submodule: convert stage_updated_gitmodules to take a struct index_state
[gitweb.git] / daemon.c
index ff0fa583b0497d886c54ff93d97cc45fd909e7a5..e37e343d0aec37fd74e7bdf694b99f59d022690a 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1,13 +1,10 @@
 #include "cache.h"
+#include "config.h"
 #include "pkt-line.h"
 #include "run-command.h"
 #include "strbuf.h"
 #include "string-list.h"
 
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX 256
-#endif
-
 #ifdef NO_INITGROUPS
 #define initgroups(x, y) (0) /* nothing */
 #endif
@@ -285,7 +282,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi)
        return NULL;            /* Fallthrough. Deny by default */
 }
 
-typedef int (*daemon_service_fn)(void);
+typedef int (*daemon_service_fn)(const struct argv_array *env);
 struct daemon_service {
        const char *name;
        const char *config_name;
@@ -298,7 +295,7 @@ static int daemon_error(const char *dir, const char *msg)
 {
        if (!informative_errors)
                msg = "access denied or repository not exported";
-       packet_write(1, "ERR %s: %s", msg, dir);
+       packet_write_fmt(1, "ERR %s: %s", msg, dir);
        return -1;
 }
 
@@ -366,7 +363,7 @@ static int run_access_hook(struct daemon_service *service, const char *dir,
 }
 
 static int run_service(const char *dir, struct daemon_service *service,
-                      struct hostinfo *hi)
+                      struct hostinfo *hi, const struct argv_array *env)
 {
        const char *path;
        int enabled = service->enabled;
@@ -425,7 +422,7 @@ static int run_service(const char *dir, struct daemon_service *service,
         */
        signal(SIGTERM, SIG_IGN);
 
-       return service->fn();
+       return service->fn(env);
 }
 
 static void copy_to_log(int fd)
@@ -449,46 +446,51 @@ static void copy_to_log(int fd)
        fclose(fp);
 }
 
-static int run_service_command(const char **argv)
+static int run_service_command(struct child_process *cld)
 {
-       struct child_process cld = CHILD_PROCESS_INIT;
-
-       cld.argv = argv;
-       cld.git_cmd = 1;
-       cld.err = -1;
-       if (start_command(&cld))
+       argv_array_push(&cld->args, ".");
+       cld->git_cmd = 1;
+       cld->err = -1;
+       if (start_command(cld))
                return -1;
 
        close(0);
        close(1);
 
-       copy_to_log(cld.err);
+       copy_to_log(cld->err);
 
-       return finish_command(&cld);
+       return finish_command(cld);
 }
 
-static int upload_pack(void)
+static int upload_pack(const struct argv_array *env)
 {
-       /* Timeout as string */
-       char timeout_buf[64];
-       const char *argv[] = { "upload-pack", "--strict", NULL, ".", NULL };
+       struct child_process cld = CHILD_PROCESS_INIT;
+       argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
+       argv_array_pushf(&cld.args, "--timeout=%u", timeout);
 
-       argv[2] = timeout_buf;
+       argv_array_pushv(&cld.env_array, env->argv);
 
-       snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
-       return run_service_command(argv);
+       return run_service_command(&cld);
 }
 
-static int upload_archive(void)
+static int upload_archive(const struct argv_array *env)
 {
-       static const char *argv[] = { "upload-archive", ".", NULL };
-       return run_service_command(argv);
+       struct child_process cld = CHILD_PROCESS_INIT;
+       argv_array_push(&cld.args, "upload-archive");
+
+       argv_array_pushv(&cld.env_array, env->argv);
+
+       return run_service_command(&cld);
 }
 
-static int receive_pack(void)
+static int receive_pack(const struct argv_array *env)
 {
-       static const char *argv[] = { "receive-pack", ".", NULL };
-       return run_service_command(argv);
+       struct child_process cld = CHILD_PROCESS_INIT;
+       argv_array_push(&cld.args, "receive-pack");
+
+       argv_array_pushv(&cld.env_array, env->argv);
+
+       return run_service_command(&cld);
 }
 
 static struct daemon_service daemon_service[] = {
@@ -580,8 +582,11 @@ static void canonicalize_client(struct strbuf *out, const char *in)
 
 /*
  * Read the host as supplied by the client connection.
+ *
+ * Returns a pointer to the character after the NUL byte terminating the host
+ * arguemnt, or 'extra_args' if there is no host arguemnt.
  */
-static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
+static char *parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
 {
        char *val;
        int vallen;
@@ -609,6 +614,43 @@ static void parse_host_arg(struct hostinfo *hi, char *extra_args, int buflen)
                if (extra_args < end && *extra_args)
                        die("Invalid request");
        }
+
+       return extra_args;
+}
+
+static void parse_extra_args(struct hostinfo *hi, struct argv_array *env,
+                            char *extra_args, int buflen)
+{
+       const char *end = extra_args + buflen;
+       struct strbuf git_protocol = STRBUF_INIT;
+
+       /* First look for the host argument */
+       extra_args = parse_host_arg(hi, extra_args, buflen);
+
+       /* Look for additional arguments places after a second NUL byte */
+       for (; extra_args < end; extra_args += strlen(extra_args) + 1) {
+               const char *arg = extra_args;
+
+               /*
+                * Parse the extra arguments, adding most to 'git_protocol'
+                * which will be used to set the 'GIT_PROTOCOL' envvar in the
+                * service that will be run.
+                *
+                * If there ends up being a particular arg in the future that
+                * git-daemon needs to parse specificly (like the 'host' arg)
+                * then it can be parsed here and not added to 'git_protocol'.
+                */
+               if (*arg) {
+                       if (git_protocol.len > 0)
+                               strbuf_addch(&git_protocol, ':');
+                       strbuf_addstr(&git_protocol, arg);
+               }
+       }
+
+       if (git_protocol.len > 0)
+               argv_array_pushf(env, GIT_PROTOCOL_ENVIRONMENT "=%s",
+                                git_protocol.buf);
+       strbuf_release(&git_protocol);
 }
 
 /*
@@ -702,6 +744,7 @@ static int execute(void)
        int pktlen, len, i;
        char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
        struct hostinfo hi;
+       struct argv_array env = ARGV_ARRAY_INIT;
 
        hostinfo_init(&hi);
 
@@ -723,8 +766,9 @@ static int execute(void)
                pktlen--;
        }
 
+       /* parse additional args hidden behind a NUL byte */
        if (len != pktlen)
-               parse_host_arg(&hi, line + len + 1, pktlen - len - 1);
+               parse_extra_args(&hi, &env, line + len + 1, pktlen - len - 1);
 
        for (i = 0; i < ARRAY_SIZE(daemon_service); i++) {
                struct daemon_service *s = &(daemon_service[i]);
@@ -737,13 +781,15 @@ static int execute(void)
                         * Note: The directory here is probably context sensitive,
                         * and might depend on the actual service being performed.
                         */
-                       int rc = run_service(arg, s, &hi);
+                       int rc = run_service(arg, s, &hi, &env);
                        hostinfo_clear(&hi);
+                       argv_array_clear(&env);
                        return rc;
                }
        }
 
        hostinfo_clear(&hi);
+       argv_array_clear(&env);
        logerror("Protocol error: '%s'", line);
        return -1;
 }