5 #include <sys/socket.h>
8 #include <netinet/in.h>
12 static int log_syslog;
15 static const char daemon_usage[] = "git-daemon [--verbose] [--syslog] [--inetd | --port=n]";
18 static void logreport(int priority, const char *err, va_list params)
20 /* We should do a single write so that it is atomic and output
21 * of several processes do not get intermingled. */
26 /* sizeof(buf) should be big enough for "[pid] \n" */
27 buflen = snprintf(buf, sizeof(buf), "[%ld] ", (long) getpid());
29 maxlen = sizeof(buf) - buflen - 1; /* -1 for our own LF */
30 msglen = vsnprintf(buf + buflen, maxlen, err, params);
33 syslog(priority, "%s", buf);
37 /* maxlen counted our own LF but also counts space given to
38 * vsnprintf for the terminating NUL. We want to make sure that
39 * we have space for our own LF and NUL after the "meat" of the
40 * message, so truncate it at maxlen - 1.
42 if (msglen > maxlen - 1)
45 msglen = 0; /* Protect against weird return values. */
51 write(2, buf, buflen);
54 void logerror(const char *err, ...)
57 va_start(params, err);
58 logreport(LOG_ERR, err, params);
62 void loginfo(const char *err, ...)
67 va_start(params, err);
68 logreport(LOG_INFO, err, params);
73 static int upload(char *dir, int dirlen)
75 loginfo("Request for '%s'", dir);
77 logerror("Cannot chdir('%s'): %s", dir, strerror(errno));
83 * Security on the cheap.
85 * We want a readable HEAD, usable "objects" directory, and
86 * a "git-daemon-export-ok" flag that says that the other side
87 * is ok with us doing this.
89 if (access("git-daemon-export-ok", F_OK) ||
90 access("objects/00", X_OK) ||
91 access("HEAD", R_OK)) {
92 logerror("Not a valid gitd-enabled repository: '%s'", dir);
97 * We'll ignore SIGTERM from now on, we have a
100 signal(SIGTERM, SIG_IGN);
102 /* git-upload-pack only ever reads stuff, so this is safe */
103 execlp("git-upload-pack", "git-upload-pack", ".", NULL);
107 static int execute(void)
109 static char line[1000];
112 len = packet_read_line(0, line, sizeof(line));
114 if (len && line[len-1] == '\n')
117 if (!strncmp("git-upload-pack /", line, 17))
118 return upload(line + 16, len - 16);
120 logerror("Protocol error: '%s'", line);
126 * We count spawned/reaped separately, just to avoid any
127 * races when updating them from signals. The SIGCHLD handler
128 * will only update children_reaped, and the fork logic will
129 * only update children_spawned.
131 * MAX_CHILDREN should be a power-of-two to make the modulus
132 * operation cheap. It should also be at least twice
133 * the maximum number of connections we will ever allow.
135 #define MAX_CHILDREN 128
137 static int max_connections = 25;
139 /* These are updated by the signal handler */
140 static volatile unsigned int children_reaped = 0;
141 static pid_t dead_child[MAX_CHILDREN];
143 /* These are updated by the main loop */
144 static unsigned int children_spawned = 0;
145 static unsigned int children_deleted = 0;
147 static struct child {
150 struct sockaddr_storage address;
151 } live_child[MAX_CHILDREN];
153 static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
155 live_child[idx].pid = pid;
156 live_child[idx].addrlen = addrlen;
157 memcpy(&live_child[idx].address, addr, addrlen);
161 * Walk from "deleted" to "spawned", and remove child "pid".
163 * We move everything up by one, since the new "deleted" will
166 static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
170 deleted %= MAX_CHILDREN;
171 spawned %= MAX_CHILDREN;
172 if (live_child[deleted].pid == pid) {
173 live_child[deleted].pid = -1;
176 n = live_child[deleted];
179 deleted = (deleted + 1) % MAX_CHILDREN;
180 if (deleted == spawned)
181 die("could not find dead child %d\n", pid);
182 m = live_child[deleted];
183 live_child[deleted] = n;
191 * This gets called if the number of connections grows
192 * past "max_connections".
194 * We _should_ start off by searching for connections
195 * from the same IP, and if there is some address wth
196 * multiple connections, we should kill that first.
198 * As it is, we just "randomly" kill 25% of the connections,
199 * and our pseudo-random generator sucks too. I have no
202 * Really, this is just a place-holder for a _real_ algorithm.
204 static void kill_some_children(int signo, unsigned start, unsigned stop)
206 start %= MAX_CHILDREN;
207 stop %= MAX_CHILDREN;
208 while (start != stop) {
210 kill(live_child[start].pid, signo);
211 start = (start + 1) % MAX_CHILDREN;
215 static void check_max_connections(void)
219 unsigned spawned, reaped, deleted;
221 spawned = children_spawned;
222 reaped = children_reaped;
223 deleted = children_deleted;
225 while (deleted < reaped) {
226 pid_t pid = dead_child[deleted % MAX_CHILDREN];
227 remove_child(pid, deleted, spawned);
230 children_deleted = deleted;
232 active = spawned - deleted;
233 if (active <= max_connections)
236 /* Kill some unstarted connections with SIGTERM */
237 kill_some_children(SIGTERM, deleted, spawned);
238 if (active <= max_connections << 1)
241 /* If the SIGTERM thing isn't helping use SIGKILL */
242 kill_some_children(SIGKILL, deleted, spawned);
247 static void handle(int incoming, struct sockaddr *addr, int addrlen)
250 char addrbuf[256] = "";
260 idx = children_spawned % MAX_CHILDREN;
262 add_child(idx, pid, addr, addrlen);
264 check_max_connections();
272 if (addr->sa_family == AF_INET) {
273 struct sockaddr_in *sin_addr = (void *) addr;
274 inet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
275 port = sin_addr->sin_port;
277 } else if (addr->sa_family == AF_INET6) {
278 struct sockaddr_in6 *sin6_addr = (void *) addr;
281 *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
282 inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
285 port = sin6_addr->sin6_port;
287 loginfo("Connection from %s:%d", addrbuf, port);
292 static void child_handler(int signo)
296 pid_t pid = waitpid(-1, &status, WNOHANG);
299 unsigned reaped = children_reaped;
300 dead_child[reaped % MAX_CHILDREN] = pid;
301 children_reaped = reaped + 1;
302 /* XXX: Custom logging, since we don't wanna getpid() */
305 if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
306 dead = " (with error)";
308 syslog(LOG_INFO, "[%d] Disconnected%s", pid, dead);
310 fprintf(stderr, "[%d] Disconnected%s\n", pid, dead);
318 static int serve(int port)
320 struct addrinfo hints, *ai0, *ai;
322 int socknum = 0, *socklist = NULL;
324 fd_set fds_init, fds;
325 char pbuf[NI_MAXSERV];
327 signal(SIGCHLD, child_handler);
329 sprintf(pbuf, "%d", port);
330 memset(&hints, 0, sizeof(hints));
331 hints.ai_family = AF_UNSPEC;
332 hints.ai_socktype = SOCK_STREAM;
333 hints.ai_protocol = IPPROTO_TCP;
334 hints.ai_flags = AI_PASSIVE;
336 gai = getaddrinfo(NULL, pbuf, &hints, &ai0);
338 die("getaddrinfo() failed: %s\n", gai_strerror(gai));
342 for (ai = ai0; ai; ai = ai->ai_next) {
346 sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
349 if (sockfd >= FD_SETSIZE) {
350 error("too large socket descriptor.");
356 if (ai->ai_family == AF_INET6) {
358 setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
360 /* Note: error is not fatal */
364 if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
366 continue; /* not fatal */
368 if (listen(sockfd, 5) < 0) {
370 continue; /* not fatal */
373 newlist = realloc(socklist, sizeof(int) * (socknum + 1));
375 die("memory allocation failed: %s", strerror(errno));
378 socklist[socknum++] = sockfd;
380 FD_SET(sockfd, &fds_init);
388 die("unable to allocate any listen sockets on port %u", port);
394 if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 0) {
395 if (errno != EINTR) {
396 error("select failed, resuming: %s",
403 for (i = 0; i < socknum; i++) {
404 int sockfd = socklist[i];
406 if (FD_ISSET(sockfd, &fds)) {
407 struct sockaddr_storage ss;
408 int sslen = sizeof(ss);
409 int incoming = accept(sockfd, (struct sockaddr *)&ss, &sslen);
417 die("accept returned %s", strerror(errno));
420 handle(incoming, (struct sockaddr *)&ss, sslen);
426 int main(int argc, char **argv)
428 int port = DEFAULT_GIT_PORT;
432 for (i = 1; i < argc; i++) {
435 if (!strncmp(arg, "--port=", 7)) {
438 n = strtoul(arg+7, &end, 0);
439 if (arg[7] && !*end) {
445 if (!strcmp(arg, "--inetd")) {
449 if (!strcmp(arg, "--verbose")) {
453 if (!strcmp(arg, "--syslog")) {
455 openlog("git-daemon", 0, LOG_DAEMON);
463 fclose(stderr); //FIXME: workaround