git-am: propagate --3way options as well
[gitweb.git] / daemon.c
index 79f0388204848f1e1085c48be8e6a86bb14cef68..1cef3098d2bd2fb28e2b670ac26c41eebf37dc78 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1,7 +1,6 @@
 #include "cache.h"
 #include "pkt-line.h"
 #include "exec_cmd.h"
-#include "interpolate.h"
 
 #include <syslog.h>
 
@@ -54,26 +53,10 @@ static const char *user_path;
 static unsigned int timeout;
 static unsigned int init_timeout;
 
-/*
- * Static table for now.  Ugh.
- * Feel free to make dynamic as needed.
- */
-#define INTERP_SLOT_HOST       (0)
-#define INTERP_SLOT_CANON_HOST (1)
-#define INTERP_SLOT_IP         (2)
-#define INTERP_SLOT_PORT       (3)
-#define INTERP_SLOT_DIR                (4)
-#define INTERP_SLOT_PERCENT    (5)
-
-static struct interp interp_table[] = {
-       { "%H", 0},
-       { "%CH", 0},
-       { "%IP", 0},
-       { "%P", 0},
-       { "%D", 0},
-       { "%%", 0},
-};
-
+static char *hostname;
+static char *canon_hostname;
+static char *ip_address;
+static char *tcp_port;
 
 static void logreport(int priority, const char *err, va_list params)
 {
@@ -81,12 +64,12 @@ static void logreport(int priority, const char *err, va_list params)
                char buf[1024];
                vsnprintf(buf, sizeof(buf), err, params);
                syslog(priority, "%s", buf);
-       }
-       else {
-               /* Since stderr is set to linebuffered mode, the
+       } else {
+               /*
+                * Since stderr is set to linebuffered mode, the
                 * logging of different processes will not overlap
                 */
-               fprintf(stderr, "[%d] ", (int)getpid());
+               fprintf(stderr, "[%"PRIuMAX"] ", (uintmax_t)getpid());
                vfprintf(stderr, err, params);
                fputc('\n', stderr);
        }
@@ -163,7 +146,7 @@ static int avoid_alias(char *p)
        }
 }
 
-static char *path_ok(struct interp *itable)
+static char *path_ok(char *directory)
 {
        static char rpath[PATH_MAX];
        static char interp_path[PATH_MAX];
@@ -171,7 +154,7 @@ static char *path_ok(struct interp *itable)
        char *path;
        char *dir;
 
-       dir = itable[INTERP_SLOT_DIR].value;
+       dir = directory;
 
        if (avoid_alias(dir)) {
                logerror("'%s': aliased", dir);
@@ -201,14 +184,27 @@ static char *path_ok(struct interp *itable)
                }
        }
        else if (interpolated_path && saw_extended_args) {
+               struct strbuf expanded_path = STRBUF_INIT;
+               struct strbuf_expand_dict_entry dict[] = {
+                       { "H", hostname },
+                       { "CH", canon_hostname },
+                       { "IP", ip_address },
+                       { "P", tcp_port },
+                       { "D", directory },
+                       { "%", "%" },
+                       { NULL }
+               };
+
                if (*dir != '/') {
                        /* Allow only absolute */
                        logerror("'%s': Non-absolute path denied (interpolated-path active)", dir);
                        return NULL;
                }
 
-               interpolate(interp_path, PATH_MAX, interpolated_path,
-                           interp_table, ARRAY_SIZE(interp_table));
+               strbuf_expand(&expanded_path, interpolated_path,
+                               strbuf_expand_dict_cb, &dict);
+               strlcpy(interp_path, expanded_path.buf, PATH_MAX);
+               strbuf_release(&expanded_path);
                loginfo("Interpolated dir '%s'", interp_path);
 
                dir = interp_path;
@@ -233,7 +229,7 @@ static char *path_ok(struct interp *itable)
                 * prefixing the base path
                 */
                if (base_path && base_path_relaxed && !retried_path) {
-                       dir = itable[INTERP_SLOT_DIR].value;
+                       dir = directory;
                        retried_path = 1;
                        continue;
                }
@@ -299,14 +295,12 @@ static int git_daemon_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
-static int run_service(struct interp *itable, struct daemon_service *service)
+static int run_service(char *dir, struct daemon_service *service)
 {
        const char *path;
        int enabled = service->enabled;
 
-       loginfo("Request %s for '%s'",
-               service->name,
-               itable[INTERP_SLOT_DIR].value);
+       loginfo("Request %s for '%s'", service->name, dir);
 
        if (!enabled && !service->overridable) {
                logerror("'%s': service not enabled.", service->name);
@@ -314,7 +308,7 @@ static int run_service(struct interp *itable, struct daemon_service *service)
                return -1;
        }
 
-       if (!(path = path_ok(itable)))
+       if (!(path = path_ok(dir)))
                return -1;
 
        /*
@@ -413,13 +407,13 @@ static void make_service_overridable(const char *name, int ena)
 
 /*
  * Separate the "extra args" information as supplied by the client connection.
- * Any resulting data is squirreled away in the given interpolation table.
  */
-static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
+static void parse_extra_args(char *extra_args, int buflen)
 {
        char *val;
        int vallen;
        char *end = extra_args + buflen;
+       char *hp;
 
        while (extra_args < end && *extra_args) {
                saw_extended_args = 1;
@@ -433,25 +427,22 @@ static void parse_extra_args(struct interp *table, char *extra_args, int buflen)
                                if (port) {
                                        *port = 0;
                                        port++;
-                                       interp_set_entry(table, INTERP_SLOT_PORT, port);
+                                       free(tcp_port);
+                                       tcp_port = xstrdup(port);
                                }
-                               interp_set_entry(table, INTERP_SLOT_HOST, host);
+                               free(hostname);
+                               hostname = xstrdup(host);
                        }
 
                        /* On to the next one */
                        extra_args = val + vallen;
                }
        }
-}
-
-static void fill_in_extra_table_entries(struct interp *itable)
-{
-       char *hp;
 
        /*
         * Replace literal host with lowercase-ized hostname.
         */
-       hp = interp_table[INTERP_SLOT_HOST].value;
+       hp = hostname;
        if (!hp)
                return;
        for ( ; *hp; hp++)
@@ -470,17 +461,17 @@ static void fill_in_extra_table_entries(struct interp *itable)
                memset(&hints, 0, sizeof(hints));
                hints.ai_flags = AI_CANONNAME;
 
-               gai = getaddrinfo(interp_table[INTERP_SLOT_HOST].value, 0, &hints, &ai0);
+               gai = getaddrinfo(hostname, 0, &hints, &ai0);
                if (!gai) {
                        for (ai = ai0; ai; ai = ai->ai_next) {
                                struct sockaddr_in *sin_addr = (void *)ai->ai_addr;
 
                                inet_ntop(AF_INET, &sin_addr->sin_addr,
                                          addrbuf, sizeof(addrbuf));
-                               interp_set_entry(interp_table,
-                                                INTERP_SLOT_CANON_HOST, ai->ai_canonname);
-                               interp_set_entry(interp_table,
-                                                INTERP_SLOT_IP, addrbuf);
+                               free(canon_hostname);
+                               canon_hostname = xstrdup(ai->ai_canonname);
+                               free(ip_address);
+                               ip_address = xstrdup(addrbuf);
                                break;
                        }
                        freeaddrinfo(ai0);
@@ -493,7 +484,7 @@ static void fill_in_extra_table_entries(struct interp *itable)
                char **ap;
                static char addrbuf[HOST_NAME_MAX + 1];
 
-               hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value);
+               hent = gethostbyname(hostname);
 
                ap = hent->h_addr_list;
                memset(&sa, 0, sizeof sa);
@@ -504,8 +495,10 @@ static void fill_in_extra_table_entries(struct interp *itable)
                inet_ntop(hent->h_addrtype, &sa.sin_addr,
                          addrbuf, sizeof(addrbuf));
 
-               interp_set_entry(interp_table, INTERP_SLOT_CANON_HOST, hent->h_name);
-               interp_set_entry(interp_table, INTERP_SLOT_IP, addrbuf);
+               free(canon_hostname);
+               canon_hostname = xstrdup(hent->h_name);
+               free(ip_address);
+               ip_address = xstrdup(addrbuf);
        }
 #endif
 }
@@ -537,6 +530,10 @@ static int execute(struct sockaddr *addr)
 #endif
                }
                loginfo("Connection from %s:%d", addrbuf, port);
+               setenv("REMOTE_ADDR", addrbuf, 1);
+       }
+       else {
+               unsetenv("REMOTE_ADDR");
        }
 
        alarm(init_timeout ? init_timeout : timeout);
@@ -553,16 +550,14 @@ static int execute(struct sockaddr *addr)
                pktlen--;
        }
 
-       /*
-        * Initialize the path interpolation table for this connection.
-        */
-       interp_clear_table(interp_table, ARRAY_SIZE(interp_table));
-       interp_set_entry(interp_table, INTERP_SLOT_PERCENT, "%");
+       free(hostname);
+       free(canon_hostname);
+       free(ip_address);
+       free(tcp_port);
+       hostname = canon_hostname = ip_address = tcp_port = NULL;
 
-       if (len != pktlen) {
-           parse_extra_args(interp_table, line + len + 1, pktlen - len - 1);
-           fill_in_extra_table_entries(interp_table);
-       }
+       if (len != pktlen)
+               parse_extra_args(line + len + 1, pktlen - len - 1);
 
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
@@ -574,9 +569,7 @@ static int execute(struct sockaddr *addr)
                         * Note: The directory here is probably context sensitive,
                         * and might depend on the actual service being performed.
                         */
-                       interp_set_entry(interp_table,
-                                        INTERP_SLOT_DIR, line + namelen + 5);
-                       return run_service(interp_table, s);
+                       return run_service(line + namelen + 5, s);
                }
        }
 
@@ -596,31 +589,24 @@ static struct child {
 
 static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
 {
-       struct child *newborn;
-       newborn = xcalloc(1, sizeof *newborn);
-       if (newborn) {
-               struct child **cradle;
-
-               live_children++;
-               newborn->pid = pid;
-               memcpy(&newborn->address, addr, addrlen);
-               for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
-                       if (!memcmp(&(*cradle)->address, &newborn->address,
-                                  sizeof newborn->address))
-                               break;
-               newborn->next = *cradle;
-               *cradle = newborn;
-       }
-       else
-               logerror("Out of memory spawning new child");
+       struct child *newborn, **cradle;
+
+       /*
+        * This must be xcalloc() -- we'll compare the whole sockaddr_storage
+        * but individual address may be shorter.
+        */
+       newborn = xcalloc(1, sizeof(*newborn));
+       live_children++;
+       newborn->pid = pid;
+       memcpy(&newborn->address, addr, addrlen);
+       for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
+               if (!memcmp(&(*cradle)->address, &newborn->address,
+                           sizeof(newborn->address)))
+                       break;
+       newborn->next = *cradle;
+       *cradle = newborn;
 }
 
-/*
- * Walk from "deleted" to "spawned", and remove child "pid".
- *
- * We move everything up by one, since the new "deleted" will
- * be one higher.
- */
 static void remove_child(pid_t pid)
 {
        struct child **cradle, *blanket;
@@ -642,18 +628,17 @@ static void remove_child(pid_t pid)
  */
 static void kill_some_child(void)
 {
-       const struct child *blanket;
+       const struct child *blanket, *next;
 
-       if ((blanket = firstborn)) {
-               const struct child *next;
+       if (!(blanket = firstborn))
+               return;
 
-               for (; (next = blanket->next); blanket = next)
-                       if (!memcmp(&blanket->address, &next->address,
-                                  sizeof next->address)) {
-                               kill(blanket->pid, SIGTERM);
-                               break;
-                       }
-       }
+       for (; (next = blanket->next); blanket = next)
+               if (!memcmp(&blanket->address, &next->address,
+                           sizeof(next->address))) {
+                       kill(blanket->pid, SIGTERM);
+                       break;
+               }
 }
 
 static void check_dead_children(void)
@@ -661,12 +646,12 @@ static void check_dead_children(void)
        int status;
        pid_t pid;
 
-       while ((pid = waitpid(-1, &status, WNOHANG))>0) {
+       while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
                const char *dead = "";
                remove_child(pid);
-               if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
+               if (!WIFEXITED(status) || (WEXITSTATUS(status) > 0))
                        dead = " (with error)";
-               loginfo("[%d] Disconnected%s", (int)pid, dead);
+               loginfo("[%"PRIuMAX"] Disconnected%s", (uintmax_t)pid, dead);
        }
 }
 
@@ -676,7 +661,7 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
 
        if (max_connections && live_children >= max_connections) {
                kill_some_child();
-               sleep(1);                        /* give it some time to die */
+               sleep(1);  /* give it some time to die */
                check_dead_children();
                if (live_children >= max_connections) {
                        close(incoming);
@@ -705,7 +690,8 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
 
 static void child_handler(int signo)
 {
-       /* Otherwise empty handler because systemcalls will get interrupted
+       /*
+        * Otherwise empty handler because systemcalls will get interrupted
         * upon signal receipt
         * SysV needs the handler to be rearmed
         */
@@ -930,7 +916,7 @@ static void store_pid(const char *path)
        FILE *f = fopen(path, "w");
        if (!f)
                die("cannot open pid file %s: %s", path, strerror(errno));
-       if (fprintf(f, "%d\n", getpid()) < 0 || fclose(f) != 0)
+       if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0)
                die("failed to write pid file %s: %s", path, strerror(errno));
 }
 
@@ -1089,9 +1075,9 @@ int main(int argc, char **argv)
        if (log_syslog) {
                openlog("git-daemon", LOG_PID, LOG_DAEMON);
                set_die_routine(daemon_die);
-       }
-       else
-               setlinebuf(stderr); /* avoid splitting a message in the middle */
+       } else
+               /* avoid splitting a message in the middle */
+               setvbuf(stderr, NULL, _IOLBF, 0);
 
        if (inetd_mode && (group_name || user_name))
                die("--user and --group are incompatible with --inetd");
@@ -1123,13 +1109,9 @@ int main(int argc, char **argv)
        if (strict_paths && (!ok_paths || !*ok_paths))
                die("option --strict-paths requires a whitelist");
 
-       if (base_path) {
-               struct stat st;
-
-               if (stat(base_path, &st) || !S_ISDIR(st.st_mode))
-                       die("base-path '%s' does not exist or "
-                           "is not a directory", base_path);
-       }
+       if (base_path && !is_directory(base_path))
+               die("base-path '%s' does not exist or is not a directory",
+                   base_path);
 
        if (inetd_mode) {
                struct sockaddr_storage ss;