[PATCH] git-daemon --syslog to log through syslog
[git/jnareb-git.git] / daemon.c
blob5547e6432e208633590dc431bf6a891055ba6bc1
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 #include <arpa/inet.h>
10 #include <syslog.h>
12 static int log_syslog;
13 static int verbose;
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. */
22 char buf[1024];
23 int buflen;
24 int maxlen, msglen;
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);
32 if (log_syslog) {
33 syslog(priority, "%s", buf);
34 return;
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)
43 msglen = maxlen - 1;
44 else if (msglen < 0)
45 msglen = 0; /* Protect against weird return values. */
46 buflen += msglen;
48 buf[buflen++] = '\n';
49 buf[buflen] = '\0';
51 write(2, buf, buflen);
54 void logerror(const char *err, ...)
56 va_list params;
57 va_start(params, err);
58 logreport(LOG_ERR, err, params);
59 va_end(params);
62 void lognotice(const char *err, ...)
64 va_list params;
65 if (!verbose)
66 return;
67 va_start(params, err);
68 logreport(LOG_INFO, err, params);
69 va_end(params);
73 static int upload(char *dir, int dirlen)
75 lognotice("Request for '%s'", dir);
76 if (chdir(dir) < 0) {
77 logerror("Cannot chdir('%s'): %s", dir, strerror(errno));
78 return -1;
80 chdir(".git");
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);
93 return -1;
97 * We'll ignore SIGTERM from now on, we have a
98 * good client.
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);
104 return -1;
107 static int execute(void)
109 static char line[1000];
110 int len;
112 len = packet_read_line(0, line, sizeof(line));
114 if (len && line[len-1] == '\n')
115 line[--len] = 0;
117 if (!strncmp("git-upload-pack /", line, 17))
118 return upload(line + 16, len - 16);
120 logerror("Protocol error: '%s'", line);
121 return -1;
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 {
148 pid_t pid;
149 int addrlen;
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
164 * be one higher.
166 static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
168 struct child n;
170 deleted %= MAX_CHILDREN;
171 spawned %= MAX_CHILDREN;
172 if (live_child[deleted].pid == pid) {
173 live_child[deleted].pid = -1;
174 return;
176 n = live_child[deleted];
177 for (;;) {
178 struct child m;
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;
184 if (m.pid == pid)
185 return;
186 n = m;
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
200 * shame.
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) {
209 if (!(start & 3))
210 kill(live_child[start].pid, signo);
211 start = (start + 1) % MAX_CHILDREN;
215 static void check_max_connections(void)
217 for (;;) {
218 int active;
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);
228 deleted++;
230 children_deleted = deleted;
232 active = spawned - deleted;
233 if (active <= max_connections)
234 break;
236 /* Kill some unstarted connections with SIGTERM */
237 kill_some_children(SIGTERM, deleted, spawned);
238 if (active <= max_connections << 1)
239 break;
241 /* If the SIGTERM thing isn't helping use SIGKILL */
242 kill_some_children(SIGKILL, deleted, spawned);
243 sleep(1);
247 static void handle(int incoming, struct sockaddr *addr, int addrlen)
249 pid_t pid = fork();
250 char addrbuf[256] = "";
251 int port = -1;
253 if (pid) {
254 unsigned idx;
256 close(incoming);
257 if (pid < 0)
258 return;
260 idx = children_spawned % MAX_CHILDREN;
261 children_spawned++;
262 add_child(idx, pid, addr, addrlen);
264 check_max_connections();
265 return;
268 dup2(incoming, 0);
269 dup2(incoming, 1);
270 close(incoming);
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;
280 char *buf = addrbuf;
281 *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
282 inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
283 strcat(buf, "]");
285 port = sin6_addr->sin6_port;
287 lognotice("Connection from %s:%d", addrbuf, port);
289 exit(execute());
292 static void child_handler(int signo)
294 for (;;) {
295 int status;
296 pid_t pid = waitpid(-1, &status, WNOHANG);
298 if (pid > 0) {
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() */
303 if (verbose) {
304 char *dead = "";
305 if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
306 dead = " (with error)";
307 if (log_syslog)
308 syslog(LOG_INFO, "[%d] Disconnected%s", pid, dead);
309 else
310 fprintf(stderr, "[%d] Disconnected%s\n", pid, dead);
312 continue;
314 break;
318 static int serve(int port)
320 struct addrinfo hints, *ai0, *ai;
321 int gai;
322 int socknum = 0, *socklist = NULL;
323 int maxfd = -1;
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);
337 if (gai)
338 die("getaddrinfo() failed: %s\n", gai_strerror(gai));
340 FD_ZERO(&fds_init);
342 for (ai = ai0; ai; ai = ai->ai_next) {
343 int sockfd;
344 int *newlist;
346 sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
347 if (sockfd < 0)
348 continue;
349 if (sockfd >= FD_SETSIZE) {
350 error("too large socket descriptor.");
351 close(sockfd);
352 continue;
355 #ifdef IPV6_V6ONLY
356 if (ai->ai_family == AF_INET6) {
357 int on = 1;
358 setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
359 &on, sizeof(on));
360 /* Note: error is not fatal */
362 #endif
364 if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
365 close(sockfd);
366 continue; /* not fatal */
368 if (listen(sockfd, 5) < 0) {
369 close(sockfd);
370 continue; /* not fatal */
373 newlist = realloc(socklist, sizeof(int) * (socknum + 1));
374 if (!newlist)
375 die("memory allocation failed: %s", strerror(errno));
377 socklist = newlist;
378 socklist[socknum++] = sockfd;
380 FD_SET(sockfd, &fds_init);
381 if (maxfd < sockfd)
382 maxfd = sockfd;
385 freeaddrinfo(ai0);
387 if (socknum == 0)
388 die("unable to allocate any listen sockets on port %u", port);
390 for (;;) {
391 int i;
392 fds = fds_init;
394 if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 0) {
395 if (errno != EINTR) {
396 error("select failed, resuming: %s",
397 strerror(errno));
398 sleep(1);
400 continue;
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);
410 if (incoming < 0) {
411 switch (errno) {
412 case EAGAIN:
413 case EINTR:
414 case ECONNABORTED:
415 continue;
416 default:
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;
429 int inetd_mode = 0;
430 int i;
432 for (i = 1; i < argc; i++) {
433 char *arg = argv[i];
435 if (!strncmp(arg, "--port=", 7)) {
436 char *end;
437 unsigned long n;
438 n = strtoul(arg+7, &end, 0);
439 if (arg[7] && !*end) {
440 port = n;
441 continue;
445 if (!strcmp(arg, "--inetd")) {
446 inetd_mode = 1;
447 continue;
449 if (!strcmp(arg, "--verbose")) {
450 verbose = 1;
451 continue;
453 if (!strcmp(arg, "--syslog")) {
454 log_syslog = 1;
455 openlog("git-daemon", 0, LOG_DAEMON);
456 continue;
459 usage(daemon_usage);
462 if (inetd_mode) {
463 fclose(stderr); //FIXME: workaround
464 return execute();
467 return serve(port);