]> git.ipfire.org Git - thirdparty/git.git/blame - daemon.c
Plug a small race in update-ref.c.
[thirdparty/git.git] / daemon.c
CommitLineData
a87e8be2
LT
1#include "cache.h"
2#include "pkt-line.h"
eaa94919
LT
3#include <signal.h>
4#include <sys/wait.h>
a87e8be2 5#include <sys/socket.h>
3cd6ecda 6#include <sys/time.h>
df076bdb 7#include <netdb.h>
a87e8be2 8#include <netinet/in.h>
f8ff0c06 9#include <arpa/inet.h>
9048fe1c 10#include <syslog.h>
f8ff0c06 11
9048fe1c 12static int log_syslog;
f8ff0c06
PB
13static int verbose;
14
9048fe1c 15static const char daemon_usage[] = "git-daemon [--verbose] [--syslog] [--inetd | --port=n]";
f8ff0c06
PB
16
17
9048fe1c 18static void logreport(int priority, const char *err, va_list params)
f8ff0c06
PB
19{
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;
25
26 /* sizeof(buf) should be big enough for "[pid] \n" */
1bedd4ca 27 buflen = snprintf(buf, sizeof(buf), "[%ld] ", (long) getpid());
f8ff0c06
PB
28
29 maxlen = sizeof(buf) - buflen - 1; /* -1 for our own LF */
30 msglen = vsnprintf(buf + buflen, maxlen, err, params);
31
9048fe1c
PB
32 if (log_syslog) {
33 syslog(priority, "%s", buf);
34 return;
35 }
36
f8ff0c06
PB
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.
41 */
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;
47
48 buf[buflen++] = '\n';
49 buf[buflen] = '\0';
50
51 write(2, buf, buflen);
52}
53
54void logerror(const char *err, ...)
55{
56 va_list params;
57 va_start(params, err);
9048fe1c 58 logreport(LOG_ERR, err, params);
f8ff0c06
PB
59 va_end(params);
60}
61
da38641d 62void loginfo(const char *err, ...)
f8ff0c06
PB
63{
64 va_list params;
65 if (!verbose)
66 return;
67 va_start(params, err);
9048fe1c 68 logreport(LOG_INFO, err, params);
f8ff0c06
PB
69 va_end(params);
70}
a87e8be2 71
a87e8be2
LT
72
73static int upload(char *dir, int dirlen)
74{
da38641d 75 loginfo("Request for '%s'", dir);
f8ff0c06
PB
76 if (chdir(dir) < 0) {
77 logerror("Cannot chdir('%s'): %s", dir, strerror(errno));
a87e8be2 78 return -1;
f8ff0c06 79 }
a87e8be2
LT
80 chdir(".git");
81
82 /*
83 * Security on the cheap.
84 *
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.
88 */
89 if (access("git-daemon-export-ok", F_OK) ||
90 access("objects/00", X_OK) ||
f8ff0c06
PB
91 access("HEAD", R_OK)) {
92 logerror("Not a valid gitd-enabled repository: '%s'", dir);
a87e8be2 93 return -1;
f8ff0c06 94 }
a87e8be2 95
02d57da4
LT
96 /*
97 * We'll ignore SIGTERM from now on, we have a
98 * good client.
99 */
100 signal(SIGTERM, SIG_IGN);
101
a87e8be2
LT
102 /* git-upload-pack only ever reads stuff, so this is safe */
103 execlp("git-upload-pack", "git-upload-pack", ".", NULL);
104 return -1;
105}
106
7d80694a 107static int execute(void)
a87e8be2 108{
7d80694a
LT
109 static char line[1000];
110 int len;
111
112 len = packet_read_line(0, line, sizeof(line));
113
114 if (len && line[len-1] == '\n')
115 line[--len] = 0;
116
a87e8be2
LT
117 if (!strncmp("git-upload-pack /", line, 17))
118 return upload(line + 16, len - 16);
119
f8ff0c06 120 logerror("Protocol error: '%s'", line);
a87e8be2
LT
121 return -1;
122}
123
66e631de
LT
124
125/*
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.
130 *
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.
134 */
135#define MAX_CHILDREN 128
136
137static int max_connections = 25;
138
139/* These are updated by the signal handler */
140static volatile unsigned int children_reaped = 0;
4d8fa916 141static pid_t dead_child[MAX_CHILDREN];
66e631de
LT
142
143/* These are updated by the main loop */
144static unsigned int children_spawned = 0;
145static unsigned int children_deleted = 0;
146
4d8fa916 147static struct child {
66e631de 148 pid_t pid;
7fa09084 149 int addrlen;
df076bdb 150 struct sockaddr_storage address;
66e631de
LT
151} live_child[MAX_CHILDREN];
152
7fa09084 153static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
66e631de
LT
154{
155 live_child[idx].pid = pid;
156 live_child[idx].addrlen = addrlen;
df076bdb 157 memcpy(&live_child[idx].address, addr, addrlen);
66e631de
LT
158}
159
160/*
161 * Walk from "deleted" to "spawned", and remove child "pid".
162 *
163 * We move everything up by one, since the new "deleted" will
164 * be one higher.
165 */
166static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
167{
168 struct child n;
169
170 deleted %= MAX_CHILDREN;
171 spawned %= MAX_CHILDREN;
172 if (live_child[deleted].pid == pid) {
173 live_child[deleted].pid = -1;
174 return;
175 }
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;
187 }
188}
189
190/*
191 * This gets called if the number of connections grows
192 * past "max_connections".
193 *
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.
197 *
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.
201 *
202 * Really, this is just a place-holder for a _real_ algorithm.
203 */
02d57da4 204static void kill_some_children(int signo, unsigned start, unsigned stop)
66e631de
LT
205{
206 start %= MAX_CHILDREN;
207 stop %= MAX_CHILDREN;
208 while (start != stop) {
209 if (!(start & 3))
02d57da4 210 kill(live_child[start].pid, signo);
66e631de
LT
211 start = (start + 1) % MAX_CHILDREN;
212 }
213}
214
02d57da4 215static void check_max_connections(void)
a87e8be2 216{
02d57da4 217 for (;;) {
eaa94919 218 int active;
66e631de 219 unsigned spawned, reaped, deleted;
eaa94919 220
66e631de 221 spawned = children_spawned;
66e631de
LT
222 reaped = children_reaped;
223 deleted = children_deleted;
224
225 while (deleted < reaped) {
226 pid_t pid = dead_child[deleted % MAX_CHILDREN];
227 remove_child(pid, deleted, spawned);
228 deleted++;
229 }
230 children_deleted = deleted;
231
232 active = spawned - deleted;
02d57da4
LT
233 if (active <= max_connections)
234 break;
66e631de 235
02d57da4
LT
236 /* Kill some unstarted connections with SIGTERM */
237 kill_some_children(SIGTERM, deleted, spawned);
238 if (active <= max_connections << 1)
239 break;
240
241 /* If the SIGTERM thing isn't helping use SIGKILL */
242 kill_some_children(SIGKILL, deleted, spawned);
243 sleep(1);
244 }
245}
246
7fa09084 247static void handle(int incoming, struct sockaddr *addr, int addrlen)
02d57da4
LT
248{
249 pid_t pid = fork();
f8ff0c06
PB
250 char addrbuf[256] = "";
251 int port = -1;
02d57da4
LT
252
253 if (pid) {
254 unsigned idx;
255
256 close(incoming);
257 if (pid < 0)
258 return;
259
260 idx = children_spawned % MAX_CHILDREN;
261 children_spawned++;
262 add_child(idx, pid, addr, addrlen);
66e631de 263
02d57da4 264 check_max_connections();
a87e8be2
LT
265 return;
266 }
267
268 dup2(incoming, 0);
269 dup2(incoming, 1);
270 close(incoming);
f8ff0c06
PB
271
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;
276
277 } else if (addr->sa_family == AF_INET6) {
278 struct sockaddr_in6 *sin6_addr = (void *) addr;
279
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, "]");
284
285 port = sin6_addr->sin6_port;
286 }
da38641d 287 loginfo("Connection from %s:%d", addrbuf, port);
f8ff0c06 288
7d80694a 289 exit(execute());
a87e8be2
LT
290}
291
eaa94919
LT
292static void child_handler(int signo)
293{
294 for (;;) {
9048fe1c
PB
295 int status;
296 pid_t pid = waitpid(-1, &status, WNOHANG);
66e631de
LT
297
298 if (pid > 0) {
299 unsigned reaped = children_reaped;
300 dead_child[reaped % MAX_CHILDREN] = pid;
301 children_reaped = reaped + 1;
f8ff0c06 302 /* XXX: Custom logging, since we don't wanna getpid() */
9048fe1c
PB
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);
311 }
eaa94919
LT
312 continue;
313 }
314 break;
315 }
316}
317
a87e8be2
LT
318static int serve(int port)
319{
df076bdb
YH
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];
a87e8be2 326
eaa94919 327 signal(SIGCHLD, child_handler);
df076bdb
YH
328
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;
335
336 gai = getaddrinfo(NULL, pbuf, &hints, &ai0);
337 if (gai)
338 die("getaddrinfo() failed: %s\n", gai_strerror(gai));
339
340 FD_ZERO(&fds_init);
341
342 for (ai = ai0; ai; ai = ai->ai_next) {
343 int sockfd;
344 int *newlist;
345
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;
353 }
354
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 */
361 }
362#endif
363
364 if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
365 close(sockfd);
366 continue; /* not fatal */
367 }
368 if (listen(sockfd, 5) < 0) {
369 close(sockfd);
370 continue; /* not fatal */
371 }
372
373 newlist = realloc(socklist, sizeof(int) * (socknum + 1));
374 if (!newlist)
375 die("memory allocation failed: %s", strerror(errno));
376
377 socklist = newlist;
378 socklist[socknum++] = sockfd;
379
380 FD_SET(sockfd, &fds_init);
381 if (maxfd < sockfd)
382 maxfd = sockfd;
383 }
384
385 freeaddrinfo(ai0);
386
387 if (socknum == 0)
388 die("unable to allocate any listen sockets on port %u", port);
a87e8be2
LT
389
390 for (;;) {
df076bdb
YH
391 int i;
392 fds = fds_init;
393
394 if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 0) {
1eef0b33
JH
395 if (errno != EINTR) {
396 error("select failed, resuming: %s",
397 strerror(errno));
398 sleep(1);
399 }
df076bdb
YH
400 continue;
401 }
402
403 for (i = 0; i < socknum; i++) {
404 int sockfd = socklist[i];
405
406 if (FD_ISSET(sockfd, &fds)) {
407 struct sockaddr_storage ss;
7fa09084 408 int sslen = sizeof(ss);
df076bdb
YH
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));
418 }
419 }
420 handle(incoming, (struct sockaddr *)&ss, sslen);
a87e8be2
LT
421 }
422 }
a87e8be2
LT
423 }
424}
425
426int main(int argc, char **argv)
427{
428 int port = DEFAULT_GIT_PORT;
e64e1b79 429 int inetd_mode = 0;
a87e8be2
LT
430 int i;
431
432 for (i = 1; i < argc; i++) {
433 char *arg = argv[i];
434
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;
442 }
443 }
e64e1b79
LT
444
445 if (!strcmp(arg, "--inetd")) {
446 inetd_mode = 1;
447 continue;
448 }
f8ff0c06
PB
449 if (!strcmp(arg, "--verbose")) {
450 verbose = 1;
451 continue;
452 }
9048fe1c
PB
453 if (!strcmp(arg, "--syslog")) {
454 log_syslog = 1;
455 openlog("git-daemon", 0, LOG_DAEMON);
456 continue;
457 }
e64e1b79 458
a87e8be2
LT
459 usage(daemon_usage);
460 }
461
7c3693f1
LD
462 if (inetd_mode) {
463 fclose(stderr); //FIXME: workaround
e64e1b79 464 return execute();
7c3693f1 465 }
e64e1b79 466
a87e8be2
LT
467 return serve(port);
468}