e6fbab6bc9d6b0c2358841b0ca3b821b8a68189b
   1#include "cache.h"
   2#include "pkt-line.h"
   3#include <signal.h>
   4#include <sys/wait.h>
   5#include <sys/socket.h>
   6#include <netinet/in.h>
   7
   8static const char daemon_usage[] = "git-daemon [--inetd | --port=n]";
   9
  10/* We don't actually do anything about this yet */
  11static int max_connections = 10;
  12
  13/*
  14 * We count spawned/reaped separately, just to avoid any
  15 * races when updating them from signals. The SIGCHLD handler
  16 * will only update children_reaped, and the fork logic will
  17 * only update children_spawned.
  18 */
  19static unsigned int children_spawned = 0;
  20static unsigned int children_reaped = 0;
  21
  22static int upload(char *dir, int dirlen)
  23{
  24        if (chdir(dir) < 0)
  25                return -1;
  26        chdir(".git");
  27
  28        /*
  29         * Security on the cheap.
  30         *
  31         * We want a readable HEAD, usable "objects" directory, and 
  32         * a "git-daemon-export-ok" flag that says that the other side
  33         * is ok with us doing this.
  34         */
  35        if (access("git-daemon-export-ok", F_OK) ||
  36            access("objects/00", X_OK) ||
  37            access("HEAD", R_OK))
  38                return -1;
  39
  40        /* git-upload-pack only ever reads stuff, so this is safe */
  41        execlp("git-upload-pack", "git-upload-pack", ".", NULL);
  42        return -1;
  43}
  44
  45static int execute(void)
  46{
  47        static char line[1000];
  48        int len;
  49
  50        len = packet_read_line(0, line, sizeof(line));
  51
  52        if (len && line[len-1] == '\n')
  53                line[--len] = 0;
  54
  55        if (!strncmp("git-upload-pack /", line, 17))
  56                return upload(line + 16, len - 16);
  57
  58        fprintf(stderr, "got bad connection '%s'\n", line);
  59        return -1;
  60}
  61
  62static void handle(int incoming, struct sockaddr_in *addr, int addrlen)
  63{
  64        pid_t pid = fork();
  65
  66        if (pid) {
  67                int active;
  68
  69                close(incoming);
  70                if (pid < 0)
  71                        return;
  72
  73                active = ++children_spawned - children_reaped;
  74                if (active > max_connections) {
  75                        /*
  76                         * Fixme! This is where you'd have to do something to
  77                         * limit the number of children. Like killing off random
  78                         * ones, or at least the ones that haven't even gotten
  79                         * started yet.
  80                         */
  81                }
  82                return;
  83        }
  84
  85        dup2(incoming, 0);
  86        dup2(incoming, 1);
  87        close(incoming);
  88        exit(execute());
  89}
  90
  91static void child_handler(int signo)
  92{
  93        for (;;) {
  94                if (waitpid(-1, NULL, WNOHANG) > 0) {
  95                        children_reaped++;
  96                        continue;
  97                }
  98                break;
  99        }
 100}
 101
 102static int serve(int port)
 103{
 104        int sockfd;
 105        struct sockaddr_in addr;
 106
 107        signal(SIGCHLD, child_handler);
 108        sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
 109        if (sockfd < 0)
 110                die("unable to open socket (%s)", strerror(errno));
 111        memset(&addr, 0, sizeof(addr));
 112        addr.sin_port = htons(port);
 113        addr.sin_family = AF_INET;
 114        if (bind(sockfd, (void *)&addr, sizeof(addr)) < 0)
 115                die("unable to bind to port %d (%s)", port, strerror(errno));
 116        if (listen(sockfd, 5) < 0)
 117                die("unable to listen to port %d (%s)", port, strerror(errno));
 118
 119        for (;;) {
 120                struct sockaddr_in in;
 121                socklen_t addrlen = sizeof(in);
 122                int incoming = accept(sockfd, (void *)&in, &addrlen);
 123
 124                if (incoming < 0) {
 125                        switch (errno) {
 126                        case EAGAIN:
 127                        case EINTR:
 128                        case ECONNABORTED:
 129                                continue;
 130                        default:
 131                                die("accept returned %s", strerror(errno));
 132                        }
 133                }
 134                handle(incoming, &in, addrlen);
 135        }
 136}
 137
 138int main(int argc, char **argv)
 139{
 140        int port = DEFAULT_GIT_PORT;
 141        int inetd_mode = 0;
 142        int i;
 143
 144        for (i = 1; i < argc; i++) {
 145                char *arg = argv[i];
 146
 147                if (!strncmp(arg, "--port=", 7)) {
 148                        char *end;
 149                        unsigned long n;
 150                        n = strtoul(arg+7, &end, 0);
 151                        if (arg[7] && !*end) {
 152                                port = n;
 153                                continue;
 154                        }
 155                }
 156
 157                if (!strcmp(arg, "--inetd")) {
 158                        inetd_mode = 1;
 159                        continue;
 160                }
 161
 162                usage(daemon_usage);
 163        }
 164
 165        if (inetd_mode)
 166                return execute();
 167
 168        return serve(port);
 169}