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