daemon.con commit [PATCH] git-daemon --inetd (7c3693f)
   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 <sys/time.h>
   7#include <netdb.h>
   8#include <netinet/in.h>
   9
  10static const char daemon_usage[] = "git-daemon [--inetd | --port=n]";
  11
  12static int upload(char *dir, int dirlen)
  13{
  14        if (chdir(dir) < 0)
  15                return -1;
  16        chdir(".git");
  17
  18        /*
  19         * Security on the cheap.
  20         *
  21         * We want a readable HEAD, usable "objects" directory, and 
  22         * a "git-daemon-export-ok" flag that says that the other side
  23         * is ok with us doing this.
  24         */
  25        if (access("git-daemon-export-ok", F_OK) ||
  26            access("objects/00", X_OK) ||
  27            access("HEAD", R_OK))
  28                return -1;
  29
  30        /*
  31         * We'll ignore SIGTERM from now on, we have a
  32         * good client.
  33         */
  34        signal(SIGTERM, SIG_IGN);
  35
  36        /* git-upload-pack only ever reads stuff, so this is safe */
  37        execlp("git-upload-pack", "git-upload-pack", ".", NULL);
  38        return -1;
  39}
  40
  41static int execute(void)
  42{
  43        static char line[1000];
  44        int len;
  45
  46        len = packet_read_line(0, line, sizeof(line));
  47
  48        if (len && line[len-1] == '\n')
  49                line[--len] = 0;
  50
  51        if (!strncmp("git-upload-pack /", line, 17))
  52                return upload(line + 16, len - 16);
  53
  54        fprintf(stderr, "got bad connection '%s'\n", line);
  55        return -1;
  56}
  57
  58
  59/*
  60 * We count spawned/reaped separately, just to avoid any
  61 * races when updating them from signals. The SIGCHLD handler
  62 * will only update children_reaped, and the fork logic will
  63 * only update children_spawned.
  64 *
  65 * MAX_CHILDREN should be a power-of-two to make the modulus
  66 * operation cheap. It should also be at least twice
  67 * the maximum number of connections we will ever allow.
  68 */
  69#define MAX_CHILDREN 128
  70
  71static int max_connections = 25;
  72
  73/* These are updated by the signal handler */
  74static volatile unsigned int children_reaped = 0;
  75static pid_t dead_child[MAX_CHILDREN];
  76
  77/* These are updated by the main loop */
  78static unsigned int children_spawned = 0;
  79static unsigned int children_deleted = 0;
  80
  81static struct child {
  82        pid_t pid;
  83        socklen_t addrlen;
  84        struct sockaddr_storage address;
  85} live_child[MAX_CHILDREN];
  86
  87static void add_child(int idx, pid_t pid, struct sockaddr *addr, socklen_t addrlen)
  88{
  89        live_child[idx].pid = pid;
  90        live_child[idx].addrlen = addrlen;
  91        memcpy(&live_child[idx].address, addr, addrlen);
  92}
  93
  94/*
  95 * Walk from "deleted" to "spawned", and remove child "pid".
  96 *
  97 * We move everything up by one, since the new "deleted" will
  98 * be one higher.
  99 */
 100static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
 101{
 102        struct child n;
 103
 104        deleted %= MAX_CHILDREN;
 105        spawned %= MAX_CHILDREN;
 106        if (live_child[deleted].pid == pid) {
 107                live_child[deleted].pid = -1;
 108                return;
 109        }
 110        n = live_child[deleted];
 111        for (;;) {
 112                struct child m;
 113                deleted = (deleted + 1) % MAX_CHILDREN;
 114                if (deleted == spawned)
 115                        die("could not find dead child %d\n", pid);
 116                m = live_child[deleted];
 117                live_child[deleted] = n;
 118                if (m.pid == pid)
 119                        return;
 120                n = m;
 121        }
 122}
 123
 124/*
 125 * This gets called if the number of connections grows
 126 * past "max_connections".
 127 *
 128 * We _should_ start off by searching for connections
 129 * from the same IP, and if there is some address wth
 130 * multiple connections, we should kill that first.
 131 *
 132 * As it is, we just "randomly" kill 25% of the connections,
 133 * and our pseudo-random generator sucks too. I have no
 134 * shame.
 135 *
 136 * Really, this is just a place-holder for a _real_ algorithm.
 137 */
 138static void kill_some_children(int signo, unsigned start, unsigned stop)
 139{
 140        start %= MAX_CHILDREN;
 141        stop %= MAX_CHILDREN;
 142        while (start != stop) {
 143                if (!(start & 3))
 144                        kill(live_child[start].pid, signo);
 145                start = (start + 1) % MAX_CHILDREN;
 146        }
 147}
 148
 149static void check_max_connections(void)
 150{
 151        for (;;) {
 152                int active;
 153                unsigned spawned, reaped, deleted;
 154
 155                spawned = children_spawned;
 156                reaped = children_reaped;
 157                deleted = children_deleted;
 158
 159                while (deleted < reaped) {
 160                        pid_t pid = dead_child[deleted % MAX_CHILDREN];
 161                        remove_child(pid, deleted, spawned);
 162                        deleted++;
 163                }
 164                children_deleted = deleted;
 165
 166                active = spawned - deleted;
 167                if (active <= max_connections)
 168                        break;
 169
 170                /* Kill some unstarted connections with SIGTERM */
 171                kill_some_children(SIGTERM, deleted, spawned);
 172                if (active <= max_connections << 1)
 173                        break;
 174
 175                /* If the SIGTERM thing isn't helping use SIGKILL */
 176                kill_some_children(SIGKILL, deleted, spawned);
 177                sleep(1);
 178        }
 179}
 180
 181static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
 182{
 183        pid_t pid = fork();
 184
 185        if (pid) {
 186                unsigned idx;
 187
 188                close(incoming);
 189                if (pid < 0)
 190                        return;
 191
 192                idx = children_spawned % MAX_CHILDREN;
 193                children_spawned++;
 194                add_child(idx, pid, addr, addrlen);
 195
 196                check_max_connections();
 197                return;
 198        }
 199
 200        dup2(incoming, 0);
 201        dup2(incoming, 1);
 202        close(incoming);
 203        exit(execute());
 204}
 205
 206static void child_handler(int signo)
 207{
 208        for (;;) {
 209                pid_t pid = waitpid(-1, NULL, WNOHANG);
 210
 211                if (pid > 0) {
 212                        unsigned reaped = children_reaped;
 213                        dead_child[reaped % MAX_CHILDREN] = pid;
 214                        children_reaped = reaped + 1;
 215                        continue;
 216                }
 217                break;
 218        }
 219}
 220
 221static int serve(int port)
 222{
 223        struct addrinfo hints, *ai0, *ai;
 224        int gai;
 225        int socknum = 0, *socklist = NULL;
 226        int maxfd = -1;
 227        fd_set fds_init, fds;
 228        char pbuf[NI_MAXSERV];
 229
 230        signal(SIGCHLD, child_handler);
 231
 232        sprintf(pbuf, "%d", port);
 233        memset(&hints, 0, sizeof(hints));
 234        hints.ai_family = AF_UNSPEC;
 235        hints.ai_socktype = SOCK_STREAM;
 236        hints.ai_protocol = IPPROTO_TCP;
 237        hints.ai_flags = AI_PASSIVE;
 238
 239        gai = getaddrinfo(NULL, pbuf, &hints, &ai0);
 240        if (gai)
 241                die("getaddrinfo() failed: %s\n", gai_strerror(gai));
 242
 243        FD_ZERO(&fds_init);
 244
 245        for (ai = ai0; ai; ai = ai->ai_next) {
 246                int sockfd;
 247                int *newlist;
 248
 249                sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
 250                if (sockfd < 0)
 251                        continue;
 252                if (sockfd >= FD_SETSIZE) {
 253                        error("too large socket descriptor.");
 254                        close(sockfd);
 255                        continue;
 256                }
 257
 258#ifdef IPV6_V6ONLY
 259                if (ai->ai_family == AF_INET6) {
 260                        int on = 1;
 261                        setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
 262                                   &on, sizeof(on));
 263                        /* Note: error is not fatal */
 264                }
 265#endif
 266
 267                if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
 268                        close(sockfd);
 269                        continue;       /* not fatal */
 270                }
 271                if (listen(sockfd, 5) < 0) {
 272                        close(sockfd);
 273                        continue;       /* not fatal */
 274                }
 275
 276                newlist = realloc(socklist, sizeof(int) * (socknum + 1));
 277                if (!newlist)
 278                        die("memory allocation failed: %s", strerror(errno));
 279
 280                socklist = newlist;
 281                socklist[socknum++] = sockfd;
 282
 283                FD_SET(sockfd, &fds_init);
 284                if (maxfd < sockfd)
 285                        maxfd = sockfd;
 286        }
 287
 288        freeaddrinfo(ai0);
 289
 290        if (socknum == 0)
 291                die("unable to allocate any listen sockets on port %u", port);
 292
 293        for (;;) {
 294                int i;
 295                fds = fds_init;
 296                
 297                if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 0) {
 298                        if (errno != EINTR) {
 299                                error("select failed, resuming: %s",
 300                                      strerror(errno));
 301                                sleep(1);
 302                        }
 303                        continue;
 304                }
 305
 306                for (i = 0; i < socknum; i++) {
 307                        int sockfd = socklist[i];
 308
 309                        if (FD_ISSET(sockfd, &fds)) {
 310                                struct sockaddr_storage ss;
 311                                socklen_t sslen = sizeof(ss);
 312                                int incoming = accept(sockfd, (struct sockaddr *)&ss, &sslen);
 313                                if (incoming < 0) {
 314                                        switch (errno) {
 315                                        case EAGAIN:
 316                                        case EINTR:
 317                                        case ECONNABORTED:
 318                                                continue;
 319                                        default:
 320                                                die("accept returned %s", strerror(errno));
 321                                        }
 322                                }
 323                                handle(incoming, (struct sockaddr *)&ss, sslen);
 324                        }
 325                }
 326        }
 327}
 328
 329int main(int argc, char **argv)
 330{
 331        int port = DEFAULT_GIT_PORT;
 332        int inetd_mode = 0;
 333        int i;
 334
 335        for (i = 1; i < argc; i++) {
 336                char *arg = argv[i];
 337
 338                if (!strncmp(arg, "--port=", 7)) {
 339                        char *end;
 340                        unsigned long n;
 341                        n = strtoul(arg+7, &end, 0);
 342                        if (arg[7] && !*end) {
 343                                port = n;
 344                                continue;
 345                        }
 346                }
 347
 348                if (!strcmp(arg, "--inetd")) {
 349                        inetd_mode = 1;
 350                        continue;
 351                }
 352
 353                usage(daemon_usage);
 354        }
 355
 356        if (inetd_mode) {
 357                fclose(stderr); //FIXME: workaround
 358                return execute();
 359        }
 360
 361        return serve(port);
 362}