Documentation: Fix ssh://[user@]host.xz URL
[gitweb.git] / daemon.c
index 532bb0c325fd07e13360389297807a19a11ee33e..e096bd7ef638273b525848fbe0018863871ea93d 100644 (file)
--- a/daemon.c
+++ b/daemon.c
 
 static int log_syslog;
 static int verbose;
+static int reuseaddr;
 
 static const char daemon_usage[] =
 "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
 "           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
-"           [--base-path=path] [directory...]";
+"           [--base-path=path] [--user-path | --user-path=path]\n"
+"           [--reuseaddr] [directory...]";
 
 /* List of acceptable pathname prefixes */
 static char **ok_paths = NULL;
@@ -29,6 +31,12 @@ static int export_all_trees = 0;
 /* Take all paths relative to this one if non-NULL */
 static char *base_path = NULL;
 
+/* If defined, ~user notation is allowed and the string is inserted
+ * after ~user/.  E.g. a request to git://host/~alice/frotz would
+ * go to /home/alice/pub_git/frotz with --user-path=pub_git.
+ */
+static const char *user_path = NULL;
+
 /* Timeout, and initial timeout */
 static unsigned int timeout = 0;
 static unsigned int init_timeout = 0;
@@ -136,6 +144,7 @@ static int avoid_alias(char *p)
 
 static char *path_ok(char *dir)
 {
+       static char rpath[PATH_MAX];
        char *path;
 
        if (avoid_alias(dir)) {
@@ -143,15 +152,38 @@ static char *path_ok(char *dir)
                return NULL;
        }
 
-       if (base_path) {
-               static char rpath[PATH_MAX];
+       if (*dir == '~') {
+               if (!user_path) {
+                       logerror("'%s': User-path not allowed", dir);
+                       return NULL;
+               }
+               if (*user_path) {
+                       /* Got either "~alice" or "~alice/foo";
+                        * rewrite them to "~alice/%s" or
+                        * "~alice/%s/foo".
+                        */
+                       int namlen, restlen = strlen(dir);
+                       char *slash = strchr(dir, '/');
+                       if (!slash)
+                               slash = dir + restlen;
+                       namlen = slash - dir;
+                       restlen -= namlen;
+                       loginfo("userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>", user_path, dir, namlen, restlen, slash);
+                       snprintf(rpath, PATH_MAX, "%.*s/%s%.*s",
+                                namlen, dir, user_path, restlen, slash);
+                       dir = rpath;
+               }
+       }
+       else if (base_path) {
                if (*dir != '/') {
-                       /* Forbid possible base-path evasion using ~paths. */
+                       /* Allow only absolute */
                        logerror("'%s': Non-absolute path denied (base-path active)", dir);
                        return NULL;
                }
-               snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
-               dir = rpath;
+               else {
+                       snprintf(rpath, PATH_MAX, "%s%s", base_path, dir);
+                       dir = rpath;
+               }
        }
 
        path = enter_repo(dir, strict_paths);
@@ -232,15 +264,43 @@ static int upload(char *dir)
        return -1;
 }
 
-static int execute(void)
+static int execute(struct sockaddr *addr)
 {
        static char line[1000];
-       int len;
+       int pktlen, len;
+
+       if (addr) {
+               char addrbuf[256] = "";
+               int port = -1;
+
+               if (addr->sa_family == AF_INET) {
+                       struct sockaddr_in *sin_addr = (void *) addr;
+                       inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
+                       port = sin_addr->sin_port;
+#ifndef NO_IPV6
+               } else if (addr && addr->sa_family == AF_INET6) {
+                       struct sockaddr_in6 *sin6_addr = (void *) addr;
+
+                       char *buf = addrbuf;
+                       *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
+                       inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
+                       strcat(buf, "]");
+
+                       port = sin6_addr->sin6_port;
+#endif
+               }
+               loginfo("Connection from %s:%d", addrbuf, port);
+       }
 
        alarm(init_timeout ? init_timeout : timeout);
-       len = packet_read_line(0, line, sizeof(line));
+       pktlen = packet_read_line(0, line, sizeof(line));
        alarm(0);
 
+       len = strlen(line);
+       if (pktlen != len)
+               loginfo("Extended attributes (%d bytes) exist <%.*s>",
+                       (int) pktlen - len,
+                       (int) pktlen - len, line + len + 1);
        if (len && line[len-1] == '\n')
                line[--len] = 0;
 
@@ -377,8 +437,6 @@ static void check_max_connections(void)
 static void handle(int incoming, struct sockaddr *addr, int addrlen)
 {
        pid_t pid = fork();
-       char addrbuf[256] = "";
-       int port = -1;
 
        if (pid) {
                unsigned idx;
@@ -399,26 +457,7 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
        dup2(incoming, 1);
        close(incoming);
 
-       if (addr->sa_family == AF_INET) {
-               struct sockaddr_in *sin_addr = (void *) addr;
-               inet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
-               port = sin_addr->sin_port;
-
-#ifndef NO_IPV6
-       } else if (addr->sa_family == AF_INET6) {
-               struct sockaddr_in6 *sin6_addr = (void *) addr;
-
-               char *buf = addrbuf;
-               *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
-               inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
-               strcat(buf, "]");
-
-               port = sin6_addr->sin6_port;
-#endif
-       }
-       loginfo("Connection from %s:%d", addrbuf, port);
-
-       exit(execute());
+       exit(execute(addr));
 }
 
 static void child_handler(int signo)
@@ -433,7 +472,7 @@ static void child_handler(int signo)
                        children_reaped = reaped + 1;
                        /* XXX: Custom logging, since we don't wanna getpid() */
                        if (verbose) {
-                               char *dead = "";
+                               const char *dead = "";
                                if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
                                        dead = " (with error)";
                                if (log_syslog)
@@ -447,6 +486,16 @@ static void child_handler(int signo)
        }
 }
 
+static int set_reuse_addr(int sockfd)
+{
+       int on = 1;
+
+       if (!reuseaddr)
+               return 0;
+       return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+                         &on, sizeof(on));
+}
+
 #ifndef NO_IPV6
 
 static int socksetup(int port, int **socklist_p)
@@ -491,6 +540,11 @@ static int socksetup(int port, int **socklist_p)
                }
 #endif
 
+               if (set_reuse_addr(sockfd)) {
+                       close(sockfd);
+                       continue;
+               }
+
                if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
                        close(sockfd);
                        continue;       /* not fatal */
@@ -533,6 +587,11 @@ static int socksetup(int port, int **socklist_p)
        sin.sin_addr.s_addr = htonl(INADDR_ANY);
        sin.sin_port = htons(port);
 
+       if (set_reuse_addr(sockfd)) {
+               close(sockfd);
+               return 0;
+       }
+
        if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
                close(sockfd);
                return 0;
@@ -614,6 +673,11 @@ int main(int argc, char **argv)
        int inetd_mode = 0;
        int i;
 
+       /* Without this we cannot rely on waitpid() to tell
+        * what happened to our children.
+        */
+       signal(SIGCHLD, SIG_DFL);
+
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
 
@@ -659,6 +723,18 @@ int main(int argc, char **argv)
                        base_path = arg+12;
                        continue;
                }
+               if (!strcmp(arg, "--reuseaddr")) {
+                       reuseaddr = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--user-path")) {
+                       user_path = "";
+                       continue;
+               }
+               if (!strncmp(arg, "--user-path=", 12)) {
+                       user_path = arg + 12;
+                       continue;
+               }
                if (!strcmp(arg, "--")) {
                        ok_paths = &argv[i+1];
                        break;
@@ -682,8 +758,16 @@ int main(int argc, char **argv)
        }
 
        if (inetd_mode) {
-               fclose(stderr); //FIXME: workaround
-               return execute();
+               struct sockaddr_storage ss;
+               struct sockaddr *peer = (struct sockaddr *)&ss;
+               socklen_t slen = sizeof(ss);
+
+               freopen("/dev/null", "w", stderr);
+
+               if (getpeername(0, peer, &slen))
+                       peer = NULL;
+
+               return execute(peer);
        }
 
        return serve(port);