daemon.con commit Move signal setting into service_loop() (9220282)
   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 <sys/poll.h>
   8#include <netdb.h>
   9#include <netinet/in.h>
  10#include <arpa/inet.h>
  11#include <syslog.h>
  12
  13static int log_syslog;
  14static int verbose;
  15
  16static const char daemon_usage[] = "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all] [directory...]";
  17
  18/* List of acceptable pathname prefixes */
  19static char **ok_paths = NULL;
  20
  21/* If this is set, git-daemon-export-ok is not required */
  22static int export_all_trees = 0;
  23
  24
  25static void logreport(int priority, const char *err, va_list params)
  26{
  27        /* We should do a single write so that it is atomic and output
  28         * of several processes do not get intermingled. */
  29        char buf[1024];
  30        int buflen;
  31        int maxlen, msglen;
  32
  33        /* sizeof(buf) should be big enough for "[pid] \n" */
  34        buflen = snprintf(buf, sizeof(buf), "[%ld] ", (long) getpid());
  35
  36        maxlen = sizeof(buf) - buflen - 1; /* -1 for our own LF */
  37        msglen = vsnprintf(buf + buflen, maxlen, err, params);
  38
  39        if (log_syslog) {
  40                syslog(priority, "%s", buf);
  41                return;
  42        }
  43
  44        /* maxlen counted our own LF but also counts space given to
  45         * vsnprintf for the terminating NUL.  We want to make sure that
  46         * we have space for our own LF and NUL after the "meat" of the
  47         * message, so truncate it at maxlen - 1.
  48         */
  49        if (msglen > maxlen - 1)
  50                msglen = maxlen - 1;
  51        else if (msglen < 0)
  52                msglen = 0; /* Protect against weird return values. */
  53        buflen += msglen;
  54
  55        buf[buflen++] = '\n';
  56        buf[buflen] = '\0';
  57
  58        write(2, buf, buflen);
  59}
  60
  61static void logerror(const char *err, ...)
  62{
  63        va_list params;
  64        va_start(params, err);
  65        logreport(LOG_ERR, err, params);
  66        va_end(params);
  67}
  68
  69static void loginfo(const char *err, ...)
  70{
  71        va_list params;
  72        if (!verbose)
  73                return;
  74        va_start(params, err);
  75        logreport(LOG_INFO, err, params);
  76        va_end(params);
  77}
  78
  79static int path_ok(const char *dir)
  80{
  81        const char *p = dir;
  82        char **pp;
  83        int sl = 1, ndot = 0;
  84
  85        for (;;) {
  86                if ( *p == '.' ) {
  87                        ndot++;
  88                } else if ( *p == '/' || *p == '\0' ) {
  89                        if ( sl && ndot > 0 && ndot < 3 )
  90                                return 0; /* . or .. in path */
  91                        sl = 1;
  92                        if ( *p == '\0' )
  93                                break; /* End of string and all is good */
  94                } else {
  95                        sl = ndot = 0;
  96                }
  97                p++;
  98        }
  99
 100        if ( ok_paths && *ok_paths ) {
 101                int ok = 0;
 102                int dirlen = strlen(dir); /* read_packet_line can return embedded \0 */
 103
 104                for ( pp = ok_paths ; *pp ; pp++ ) {
 105                        int len = strlen(*pp);
 106                        if ( len <= dirlen &&
 107                             !strncmp(*pp, dir, len) &&
 108                             (dir[len] == '/' || dir[len] == '\0') ) {
 109                                ok = 1;
 110                                break;
 111                        }
 112                }
 113
 114                if ( !ok )
 115                        return 0; /* Path not in whitelist */
 116        }
 117
 118        return 1;               /* Path acceptable */
 119}
 120
 121static int upload(char *dir, int dirlen)
 122{
 123        loginfo("Request for '%s'", dir);
 124
 125        if (!path_ok(dir)) {
 126                logerror("Forbidden directory: %s\n", dir);
 127                return -1;
 128        }
 129
 130        if (chdir(dir) < 0) {
 131                logerror("Cannot chdir('%s'): %s", dir, strerror(errno));
 132                return -1;
 133        }
 134
 135        chdir(".git");
 136
 137        /*
 138         * Security on the cheap.
 139         *
 140         * We want a readable HEAD, usable "objects" directory, and 
 141         * a "git-daemon-export-ok" flag that says that the other side
 142         * is ok with us doing this.
 143         */
 144        if ((!export_all_trees && access("git-daemon-export-ok", F_OK)) ||
 145            access("objects/00", X_OK) ||
 146            access("HEAD", R_OK)) {
 147                logerror("Not a valid git-daemon-enabled repository: '%s'", dir);
 148                return -1;
 149        }
 150
 151        /*
 152         * We'll ignore SIGTERM from now on, we have a
 153         * good client.
 154         */
 155        signal(SIGTERM, SIG_IGN);
 156
 157        /* git-upload-pack only ever reads stuff, so this is safe */
 158        execlp("git-upload-pack", "git-upload-pack", ".", NULL);
 159        return -1;
 160}
 161
 162static int execute(void)
 163{
 164        static char line[1000];
 165        int len;
 166
 167        len = packet_read_line(0, line, sizeof(line));
 168
 169        if (len && line[len-1] == '\n')
 170                line[--len] = 0;
 171
 172        if (!strncmp("git-upload-pack /", line, 17))
 173                return upload(line + 16, len - 16);
 174
 175        logerror("Protocol error: '%s'", line);
 176        return -1;
 177}
 178
 179
 180/*
 181 * We count spawned/reaped separately, just to avoid any
 182 * races when updating them from signals. The SIGCHLD handler
 183 * will only update children_reaped, and the fork logic will
 184 * only update children_spawned.
 185 *
 186 * MAX_CHILDREN should be a power-of-two to make the modulus
 187 * operation cheap. It should also be at least twice
 188 * the maximum number of connections we will ever allow.
 189 */
 190#define MAX_CHILDREN 128
 191
 192static int max_connections = 25;
 193
 194/* These are updated by the signal handler */
 195static volatile unsigned int children_reaped = 0;
 196static pid_t dead_child[MAX_CHILDREN];
 197
 198/* These are updated by the main loop */
 199static unsigned int children_spawned = 0;
 200static unsigned int children_deleted = 0;
 201
 202static struct child {
 203        pid_t pid;
 204        int addrlen;
 205        struct sockaddr_storage address;
 206} live_child[MAX_CHILDREN];
 207
 208static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
 209{
 210        live_child[idx].pid = pid;
 211        live_child[idx].addrlen = addrlen;
 212        memcpy(&live_child[idx].address, addr, addrlen);
 213}
 214
 215/*
 216 * Walk from "deleted" to "spawned", and remove child "pid".
 217 *
 218 * We move everything up by one, since the new "deleted" will
 219 * be one higher.
 220 */
 221static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
 222{
 223        struct child n;
 224
 225        deleted %= MAX_CHILDREN;
 226        spawned %= MAX_CHILDREN;
 227        if (live_child[deleted].pid == pid) {
 228                live_child[deleted].pid = -1;
 229                return;
 230        }
 231        n = live_child[deleted];
 232        for (;;) {
 233                struct child m;
 234                deleted = (deleted + 1) % MAX_CHILDREN;
 235                if (deleted == spawned)
 236                        die("could not find dead child %d\n", pid);
 237                m = live_child[deleted];
 238                live_child[deleted] = n;
 239                if (m.pid == pid)
 240                        return;
 241                n = m;
 242        }
 243}
 244
 245/*
 246 * This gets called if the number of connections grows
 247 * past "max_connections".
 248 *
 249 * We _should_ start off by searching for connections
 250 * from the same IP, and if there is some address wth
 251 * multiple connections, we should kill that first.
 252 *
 253 * As it is, we just "randomly" kill 25% of the connections,
 254 * and our pseudo-random generator sucks too. I have no
 255 * shame.
 256 *
 257 * Really, this is just a place-holder for a _real_ algorithm.
 258 */
 259static void kill_some_children(int signo, unsigned start, unsigned stop)
 260{
 261        start %= MAX_CHILDREN;
 262        stop %= MAX_CHILDREN;
 263        while (start != stop) {
 264                if (!(start & 3))
 265                        kill(live_child[start].pid, signo);
 266                start = (start + 1) % MAX_CHILDREN;
 267        }
 268}
 269
 270static void check_max_connections(void)
 271{
 272        for (;;) {
 273                int active;
 274                unsigned spawned, reaped, deleted;
 275
 276                spawned = children_spawned;
 277                reaped = children_reaped;
 278                deleted = children_deleted;
 279
 280                while (deleted < reaped) {
 281                        pid_t pid = dead_child[deleted % MAX_CHILDREN];
 282                        remove_child(pid, deleted, spawned);
 283                        deleted++;
 284                }
 285                children_deleted = deleted;
 286
 287                active = spawned - deleted;
 288                if (active <= max_connections)
 289                        break;
 290
 291                /* Kill some unstarted connections with SIGTERM */
 292                kill_some_children(SIGTERM, deleted, spawned);
 293                if (active <= max_connections << 1)
 294                        break;
 295
 296                /* If the SIGTERM thing isn't helping use SIGKILL */
 297                kill_some_children(SIGKILL, deleted, spawned);
 298                sleep(1);
 299        }
 300}
 301
 302static void handle(int incoming, struct sockaddr *addr, int addrlen)
 303{
 304        pid_t pid = fork();
 305        char addrbuf[256] = "";
 306        int port = -1;
 307
 308        if (pid) {
 309                unsigned idx;
 310
 311                close(incoming);
 312                if (pid < 0)
 313                        return;
 314
 315                idx = children_spawned % MAX_CHILDREN;
 316                children_spawned++;
 317                add_child(idx, pid, addr, addrlen);
 318
 319                check_max_connections();
 320                return;
 321        }
 322
 323        dup2(incoming, 0);
 324        dup2(incoming, 1);
 325        close(incoming);
 326
 327        if (addr->sa_family == AF_INET) {
 328                struct sockaddr_in *sin_addr = (void *) addr;
 329                inet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
 330                port = sin_addr->sin_port;
 331
 332#ifndef NO_IPV6
 333        } else if (addr->sa_family == AF_INET6) {
 334                struct sockaddr_in6 *sin6_addr = (void *) addr;
 335
 336                char *buf = addrbuf;
 337                *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
 338                inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
 339                strcat(buf, "]");
 340
 341                port = sin6_addr->sin6_port;
 342#endif
 343        }
 344        loginfo("Connection from %s:%d", addrbuf, port);
 345
 346        exit(execute());
 347}
 348
 349static void child_handler(int signo)
 350{
 351        for (;;) {
 352                int status;
 353                pid_t pid = waitpid(-1, &status, WNOHANG);
 354
 355                if (pid > 0) {
 356                        unsigned reaped = children_reaped;
 357                        dead_child[reaped % MAX_CHILDREN] = pid;
 358                        children_reaped = reaped + 1;
 359                        /* XXX: Custom logging, since we don't wanna getpid() */
 360                        if (verbose) {
 361                                char *dead = "";
 362                                if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
 363                                        dead = " (with error)";
 364                                if (log_syslog)
 365                                        syslog(LOG_INFO, "[%d] Disconnected%s", pid, dead);
 366                                else
 367                                        fprintf(stderr, "[%d] Disconnected%s\n", pid, dead);
 368                        }
 369                        continue;
 370                }
 371                break;
 372        }
 373}
 374
 375#ifndef NO_IPV6
 376
 377static int socksetup(int port, int **socklist_p)
 378{
 379        int socknum = 0, *socklist = NULL;
 380        int maxfd = -1;
 381        char pbuf[NI_MAXSERV];
 382
 383        struct addrinfo hints, *ai0, *ai;
 384        int gai;
 385
 386        sprintf(pbuf, "%d", port);
 387        memset(&hints, 0, sizeof(hints));
 388        hints.ai_family = AF_UNSPEC;
 389        hints.ai_socktype = SOCK_STREAM;
 390        hints.ai_protocol = IPPROTO_TCP;
 391        hints.ai_flags = AI_PASSIVE;
 392
 393        gai = getaddrinfo(NULL, pbuf, &hints, &ai0);
 394        if (gai)
 395                die("getaddrinfo() failed: %s\n", gai_strerror(gai));
 396
 397        for (ai = ai0; ai; ai = ai->ai_next) {
 398                int sockfd;
 399                int *newlist;
 400
 401                sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
 402                if (sockfd < 0)
 403                        continue;
 404                if (sockfd >= FD_SETSIZE) {
 405                        error("too large socket descriptor.");
 406                        close(sockfd);
 407                        continue;
 408                }
 409
 410#ifdef IPV6_V6ONLY
 411                if (ai->ai_family == AF_INET6) {
 412                        int on = 1;
 413                        setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
 414                                   &on, sizeof(on));
 415                        /* Note: error is not fatal */
 416                }
 417#endif
 418
 419                if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
 420                        close(sockfd);
 421                        continue;       /* not fatal */
 422                }
 423                if (listen(sockfd, 5) < 0) {
 424                        close(sockfd);
 425                        continue;       /* not fatal */
 426                }
 427
 428                newlist = realloc(socklist, sizeof(int) * (socknum + 1));
 429                if (!newlist)
 430                        die("memory allocation failed: %s", strerror(errno));
 431
 432                socklist = newlist;
 433                socklist[socknum++] = sockfd;
 434
 435                if (maxfd < sockfd)
 436                        maxfd = sockfd;
 437        }
 438
 439        freeaddrinfo(ai0);
 440
 441        *socklist_p = socklist;
 442        return socknum;
 443}
 444
 445#else /* NO_IPV6 */
 446
 447static int socksetup(int port, int **socklist_p)
 448{
 449        struct sockaddr_in sin;
 450        int sockfd;
 451
 452        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 453        if (sockfd < 0)
 454                return 0;
 455
 456        memset(&sin, 0, sizeof sin);
 457        sin.sin_family = AF_INET;
 458        sin.sin_addr.s_addr = htonl(INADDR_ANY);
 459        sin.sin_port = htons(port);
 460
 461        if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
 462                close(sockfd);
 463                return 0;
 464        }
 465
 466        *socklist_p = xmalloc(sizeof(int));
 467        **socklist_p = sockfd;
 468}
 469
 470#endif
 471
 472static int service_loop(int socknum, int *socklist)
 473{
 474        struct pollfd *pfd;
 475        int i;
 476
 477        pfd = xcalloc(socknum, sizeof(struct pollfd));
 478
 479        for (i = 0; i < socknum; i++) {
 480                pfd[i].fd = socklist[i];
 481                pfd[i].events = POLLIN;
 482        }
 483
 484        signal(SIGCHLD, child_handler);
 485        
 486        for (;;) {
 487                int i;
 488
 489                if (poll(pfd, socknum, 0) < 0) {
 490                        if (errno != EINTR) {
 491                                error("poll failed, resuming: %s",
 492                                      strerror(errno));
 493                                sleep(1);
 494                        }
 495                        continue;
 496                }
 497
 498                for (i = 0; i < socknum; i++) {
 499                        if (pfd[i].revents & POLLIN) {
 500                                struct sockaddr_storage ss;
 501                                unsigned int sslen = sizeof(ss);
 502                                int incoming = accept(pfd[i].fd, (struct sockaddr *)&ss, &sslen);
 503                                if (incoming < 0) {
 504                                        switch (errno) {
 505                                        case EAGAIN:
 506                                        case EINTR:
 507                                        case ECONNABORTED:
 508                                                continue;
 509                                        default:
 510                                                die("accept returned %s", strerror(errno));
 511                                        }
 512                                }
 513                                handle(incoming, (struct sockaddr *)&ss, sslen);
 514                        }
 515                }
 516        }
 517}
 518
 519static int serve(int port)
 520{
 521        int socknum, *socklist;
 522        
 523        socknum = socksetup(port, &socklist);
 524        if (socknum == 0)
 525                die("unable to allocate any listen sockets on port %u", port);
 526        
 527        return service_loop(socknum, socklist);
 528}       
 529
 530int main(int argc, char **argv)
 531{
 532        int port = DEFAULT_GIT_PORT;
 533        int inetd_mode = 0;
 534        int i;
 535
 536        for (i = 1; i < argc; i++) {
 537                char *arg = argv[i];
 538
 539                if (!strncmp(arg, "--port=", 7)) {
 540                        char *end;
 541                        unsigned long n;
 542                        n = strtoul(arg+7, &end, 0);
 543                        if (arg[7] && !*end) {
 544                                port = n;
 545                                continue;
 546                        }
 547                }
 548                if (!strcmp(arg, "--inetd")) {
 549                        inetd_mode = 1;
 550                        continue;
 551                }
 552                if (!strcmp(arg, "--verbose")) {
 553                        verbose = 1;
 554                        continue;
 555                }
 556                if (!strcmp(arg, "--syslog")) {
 557                        log_syslog = 1;
 558                        openlog("git-daemon", 0, LOG_DAEMON);
 559                        continue;
 560                }
 561                if (!strcmp(arg, "--export-all")) {
 562                        export_all_trees = 1;
 563                        continue;
 564                }
 565                if (!strcmp(arg, "--")) {
 566                        ok_paths = &argv[i+1];
 567                        break;
 568                } else if (arg[0] != '-') {
 569                        ok_paths = &argv[i];
 570                        break;
 571                }
 572
 573                usage(daemon_usage);
 574        }
 575
 576        if (inetd_mode) {
 577                fclose(stderr); //FIXME: workaround
 578                return execute();
 579        } else {
 580                return serve(port);
 581        }
 582}