daemon.con commit Fix potential send-pack SIGSEGV (bdf2514)
   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#include <arpa/inet.h>
   8
   9static const char daemon_usage[] = "git-daemon [--inetd | --port=n]";
  10
  11static int upload(char *dir, int dirlen)
  12{
  13        if (chdir(dir) < 0)
  14                return -1;
  15        chdir(".git");
  16
  17        /*
  18         * Security on the cheap.
  19         *
  20         * We want a readable HEAD, usable "objects" directory, and 
  21         * a "git-daemon-export-ok" flag that says that the other side
  22         * is ok with us doing this.
  23         */
  24        if (access("git-daemon-export-ok", F_OK) ||
  25            access("objects/00", X_OK) ||
  26            access("HEAD", R_OK))
  27                return -1;
  28
  29        /*
  30         * We'll ignore SIGTERM from now on, we have a
  31         * good client.
  32         */
  33        signal(SIGTERM, SIG_IGN);
  34
  35        /* git-upload-pack only ever reads stuff, so this is safe */
  36        execlp("git-upload-pack", "git-upload-pack", ".", NULL);
  37        return -1;
  38}
  39
  40static int execute(void)
  41{
  42        static char line[1000];
  43        int len;
  44
  45        len = packet_read_line(0, line, sizeof(line));
  46
  47        if (len && line[len-1] == '\n')
  48                line[--len] = 0;
  49
  50        if (!strncmp("git-upload-pack /", line, 17))
  51                return upload(line + 16, len - 16);
  52
  53        fprintf(stderr, "got bad connection '%s'\n", line);
  54        return -1;
  55}
  56
  57
  58/*
  59 * We count spawned/reaped separately, just to avoid any
  60 * races when updating them from signals. The SIGCHLD handler
  61 * will only update children_reaped, and the fork logic will
  62 * only update children_spawned.
  63 *
  64 * MAX_CHILDREN should be a power-of-two to make the modulus
  65 * operation cheap. It should also be at least twice
  66 * the maximum number of connections we will ever allow.
  67 */
  68#define MAX_CHILDREN 128
  69
  70static int max_connections = 25;
  71
  72/* These are updated by the signal handler */
  73static volatile unsigned int children_reaped = 0;
  74pid_t dead_child[MAX_CHILDREN];
  75
  76/* These are updated by the main loop */
  77static unsigned int children_spawned = 0;
  78static unsigned int children_deleted = 0;
  79
  80struct child {
  81        pid_t pid;
  82        int addrlen;
  83        struct sockaddr_in address;
  84} live_child[MAX_CHILDREN];
  85
  86static void add_child(int idx, pid_t pid, struct sockaddr_in *addr, int addrlen)
  87{
  88        live_child[idx].pid = pid;
  89        live_child[idx].addrlen = addrlen;
  90        live_child[idx].address = *addr;
  91}
  92
  93/*
  94 * Walk from "deleted" to "spawned", and remove child "pid".
  95 *
  96 * We move everything up by one, since the new "deleted" will
  97 * be one higher.
  98 */
  99static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
 100{
 101        struct child n;
 102
 103        deleted %= MAX_CHILDREN;
 104        spawned %= MAX_CHILDREN;
 105        if (live_child[deleted].pid == pid) {
 106                live_child[deleted].pid = -1;
 107                return;
 108        }
 109        n = live_child[deleted];
 110        for (;;) {
 111                struct child m;
 112                deleted = (deleted + 1) % MAX_CHILDREN;
 113                if (deleted == spawned)
 114                        die("could not find dead child %d\n", pid);
 115                m = live_child[deleted];
 116                live_child[deleted] = n;
 117                if (m.pid == pid)
 118                        return;
 119                n = m;
 120        }
 121}
 122
 123/*
 124 * This gets called if the number of connections grows
 125 * past "max_connections".
 126 *
 127 * We _should_ start off by searching for connections
 128 * from the same IP, and if there is some address wth
 129 * multiple connections, we should kill that first.
 130 *
 131 * As it is, we just "randomly" kill 25% of the connections,
 132 * and our pseudo-random generator sucks too. I have no
 133 * shame.
 134 *
 135 * Really, this is just a place-holder for a _real_ algorithm.
 136 */
 137static void kill_some_children(int signo, unsigned start, unsigned stop)
 138{
 139        start %= MAX_CHILDREN;
 140        stop %= MAX_CHILDREN;
 141        while (start != stop) {
 142                if (!(start & 3))
 143                        kill(live_child[start].pid, signo);
 144                start = (start + 1) % MAX_CHILDREN;
 145        }
 146}
 147
 148static void check_max_connections(void)
 149{
 150        for (;;) {
 151                int active;
 152                unsigned spawned, reaped, deleted;
 153
 154                spawned = children_spawned;
 155                reaped = children_reaped;
 156                deleted = children_deleted;
 157
 158                while (deleted < reaped) {
 159                        pid_t pid = dead_child[deleted % MAX_CHILDREN];
 160                        remove_child(pid, deleted, spawned);
 161                        deleted++;
 162                }
 163                children_deleted = deleted;
 164
 165                active = spawned - deleted;
 166                if (active <= max_connections)
 167                        break;
 168
 169                /* Kill some unstarted connections with SIGTERM */
 170                kill_some_children(SIGTERM, deleted, spawned);
 171                if (active <= max_connections << 1)
 172                        break;
 173
 174                /* If the SIGTERM thing isn't helping use SIGKILL */
 175                kill_some_children(SIGKILL, deleted, spawned);
 176                sleep(1);
 177        }
 178}
 179
 180static void handle(int incoming, struct sockaddr_in *addr, int addrlen)
 181{
 182        pid_t pid = fork();
 183
 184        if (pid) {
 185                unsigned idx;
 186
 187                close(incoming);
 188                if (pid < 0)
 189                        return;
 190
 191                idx = children_spawned % MAX_CHILDREN;
 192                children_spawned++;
 193                add_child(idx, pid, addr, addrlen);
 194
 195                check_max_connections();
 196                return;
 197        }
 198
 199        dup2(incoming, 0);
 200        dup2(incoming, 1);
 201        close(incoming);
 202        exit(execute());
 203}
 204
 205static void child_handler(int signo)
 206{
 207        for (;;) {
 208                pid_t pid = waitpid(-1, NULL, WNOHANG);
 209
 210                if (pid > 0) {
 211                        unsigned reaped = children_reaped;
 212                        dead_child[reaped % MAX_CHILDREN] = pid;
 213                        children_reaped = reaped + 1;
 214                        continue;
 215                }
 216                break;
 217        }
 218}
 219
 220static int serve(int port)
 221{
 222        int sockfd;
 223        struct sockaddr_in addr;
 224
 225        signal(SIGCHLD, child_handler);
 226        sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
 227        if (sockfd < 0)
 228                die("unable to open socket (%s)", strerror(errno));
 229        memset(&addr, 0, sizeof(addr));
 230        addr.sin_port = htons(port);
 231        addr.sin_family = AF_INET;
 232        if (bind(sockfd, (void *)&addr, sizeof(addr)) < 0)
 233                die("unable to bind to port %d (%s)", port, strerror(errno));
 234        if (listen(sockfd, 5) < 0)
 235                die("unable to listen to port %d (%s)", port, strerror(errno));
 236
 237        for (;;) {
 238                struct sockaddr_in in;
 239                socklen_t addrlen = sizeof(in);
 240                int incoming = accept(sockfd, (void *)&in, &addrlen);
 241
 242                if (incoming < 0) {
 243                        switch (errno) {
 244                        case EAGAIN:
 245                        case EINTR:
 246                        case ECONNABORTED:
 247                                continue;
 248                        default:
 249                                die("accept returned %s", strerror(errno));
 250                        }
 251                }
 252                handle(incoming, &in, addrlen);
 253        }
 254}
 255
 256int main(int argc, char **argv)
 257{
 258        int port = DEFAULT_GIT_PORT;
 259        int inetd_mode = 0;
 260        int i;
 261
 262        for (i = 1; i < argc; i++) {
 263                char *arg = argv[i];
 264
 265                if (!strncmp(arg, "--port=", 7)) {
 266                        char *end;
 267                        unsigned long n;
 268                        n = strtoul(arg+7, &end, 0);
 269                        if (arg[7] && !*end) {
 270                                port = n;
 271                                continue;
 272                        }
 273                }
 274
 275                if (!strcmp(arg, "--inetd")) {
 276                        inetd_mode = 1;
 277                        continue;
 278                }
 279
 280                usage(daemon_usage);
 281        }
 282
 283        if (inetd_mode)
 284                return execute();
 285
 286        return serve(port);
 287}