gitignore.txt: fix documentation of "**" patterns
[gitweb.git] / daemon.c
index 34d95c1674930aa6ea429f7ee9173c239aae6bd5..34916c5e105812e36d659dfeeb8341e003e746bd 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -26,6 +26,7 @@ static const char daemon_usage[] =
 "           [--interpolated-path=<path>]\n"
 "           [--reuseaddr] [--pid-file=<file>]\n"
 "           [--(enable|disable|allow-override|forbid-override)=<service>]\n"
+"           [--access-hook=<path>]\n"
 "           [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>]\n"
 "                      [--detach] [--user=<user> [--group=<group>]]\n"
 "           [<directory>...]";
@@ -252,6 +253,71 @@ static int daemon_error(const char *dir, const char *msg)
        return -1;
 }
 
+static char *access_hook;
+
+static int run_access_hook(struct daemon_service *service, const char *dir, const char *path)
+{
+       struct child_process child;
+       struct strbuf buf = STRBUF_INIT;
+       const char *argv[8];
+       const char **arg = argv;
+       char *eol;
+       int seen_errors = 0;
+
+#define STRARG(x) ((x) ? (x) : "")
+       *arg++ = access_hook;
+       *arg++ = service->name;
+       *arg++ = path;
+       *arg++ = STRARG(hostname);
+       *arg++ = STRARG(canon_hostname);
+       *arg++ = STRARG(ip_address);
+       *arg++ = STRARG(tcp_port);
+       *arg = NULL;
+#undef STRARG
+
+       memset(&child, 0, sizeof(child));
+       child.use_shell = 1;
+       child.argv = argv;
+       child.no_stdin = 1;
+       child.no_stderr = 1;
+       child.out = -1;
+       if (start_command(&child)) {
+               logerror("daemon access hook '%s' failed to start",
+                        access_hook);
+               goto error_return;
+       }
+       if (strbuf_read(&buf, child.out, 0) < 0) {
+               logerror("failed to read from pipe to daemon access hook '%s'",
+                        access_hook);
+               strbuf_reset(&buf);
+               seen_errors = 1;
+       }
+       if (close(child.out) < 0) {
+               logerror("failed to close pipe to daemon access hook '%s'",
+                        access_hook);
+               seen_errors = 1;
+       }
+       if (finish_command(&child))
+               seen_errors = 1;
+
+       if (!seen_errors) {
+               strbuf_release(&buf);
+               return 0;
+       }
+
+error_return:
+       strbuf_ltrim(&buf);
+       if (!buf.len)
+               strbuf_addstr(&buf, "service rejected");
+       eol = strchr(buf.buf, '\n');
+       if (eol)
+               *eol = '\0';
+       errno = EACCES;
+       daemon_error(dir, buf.buf);
+       strbuf_release(&buf);
+       return -1;
+}
+
 static int run_service(char *dir, struct daemon_service *service)
 {
        const char *path;
@@ -299,6 +365,13 @@ static int run_service(char *dir, struct daemon_service *service)
                return daemon_error(dir, "service not enabled");
        }
 
+       /*
+        * Optionally, a hook can choose to deny access to the
+        * repository depending on the phase of the moon.
+        */
+       if (access_hook && run_access_hook(service, dir, path))
+               return -1;
+
        /*
         * We'll ignore SIGTERM from now on, we have a
         * good client.
@@ -527,7 +600,7 @@ static void parse_host_arg(char *extra_args, int buflen)
 
 static int execute(void)
 {
-       static char line[1000];
+       char *line = packet_buffer;
        int pktlen, len, i;
        char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
 
@@ -535,7 +608,7 @@ static int execute(void)
                loginfo("Connection from %s:%s", addr, port);
 
        alarm(init_timeout ? init_timeout : timeout);
-       pktlen = packet_read_line(0, line, sizeof(line));
+       pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
        alarm(0);
 
        len = strlen(line);
@@ -687,7 +760,7 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
                snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
                    ntohs(sin_addr->sin_port));
 #ifndef NO_IPV6
-       } else if (addr && addr->sa_family == AF_INET6) {
+       } else if (addr->sa_family == AF_INET6) {
                struct sockaddr_in6 *sin6_addr = (void *) addr;
 
                char *buf = addrbuf + 12;
@@ -974,18 +1047,6 @@ static int service_loop(struct socketlist *socklist)
        }
 }
 
-/* if any standard file descriptor is missing open it to /dev/null */
-static void sanitize_stdfds(void)
-{
-       int fd = open("/dev/null", O_RDWR, 0);
-       while (fd != -1 && fd < 2)
-               fd = dup(fd);
-       if (fd == -1)
-               die_errno("open /dev/null or dup failed");
-       if (fd > 2)
-               close(fd);
-}
-
 #ifdef NO_POSIX_GOODIES
 
 struct credentials;
@@ -1138,6 +1199,10 @@ int main(int argc, char **argv)
                        export_all_trees = 1;
                        continue;
                }
+               if (!prefixcmp(arg, "--access-hook=")) {
+                       access_hook = arg + 14;
+                       continue;
+               }
                if (!prefixcmp(arg, "--timeout=")) {
                        timeout = atoi(arg+10);
                        continue;