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
11static int verbose;
12
13static const char daemon_usage[] = "git-daemon [--verbose] [--inetd | --port=n]";
14
15
16static void logreport(const char *err, va_list params)
17{
18 /* We should do a single write so that it is atomic and output
19 * of several processes do not get intermingled. */
20 char buf[1024];
21 int buflen;
22 int maxlen, msglen;
23
24 /* sizeof(buf) should be big enough for "[pid] \n" */
25 buflen = snprintf(buf, sizeof(buf), "[%d] ", getpid());
26
27 maxlen = sizeof(buf) - buflen - 1; /* -1 for our own LF */
28 msglen = vsnprintf(buf + buflen, maxlen, err, params);
29
30 /* maxlen counted our own LF but also counts space given to
31 * vsnprintf for the terminating NUL. We want to make sure that
32 * we have space for our own LF and NUL after the "meat" of the
33 * message, so truncate it at maxlen - 1.
34 */
35 if (msglen > maxlen - 1)
36 msglen = maxlen - 1;
37 else if (msglen < 0)
38 msglen = 0; /* Protect against weird return values. */
39 buflen += msglen;
40
41 buf[buflen++] = '\n';
42 buf[buflen] = '\0';
43
44 write(2, buf, buflen);
45}
46
47void logerror(const char *err, ...)
48{
49 va_list params;
50 va_start(params, err);
51 logreport(err, params);
52 va_end(params);
53}
54
55void lognotice(const char *err, ...)
56{
57 va_list params;
58 if (!verbose)
59 return;
60 va_start(params, err);
61 logreport(err, params);
62 va_end(params);
63}
64
65
66static int upload(char *dir, int dirlen)
67{
68 lognotice("Request for '%s'", dir);
69 if (chdir(dir) < 0) {
70 logerror("Cannot chdir('%s'): %s", dir, strerror(errno));
71 return -1;
72 }
73 chdir(".git");
74
75 /*
76 * Security on the cheap.
77 *
78 * We want a readable HEAD, usable "objects" directory, and
79 * a "git-daemon-export-ok" flag that says that the other side
80 * is ok with us doing this.
81 */
82 if (access("git-daemon-export-ok", F_OK) ||
83 access("objects/00", X_OK) ||
84 access("HEAD", R_OK)) {
85 logerror("Not a valid gitd-enabled repository: '%s'", dir);
86 return -1;
87 }
88
89 /*
90 * We'll ignore SIGTERM from now on, we have a
91 * good client.
92 */
93 signal(SIGTERM, SIG_IGN);
94
95 /* git-upload-pack only ever reads stuff, so this is safe */
96 execlp("git-upload-pack", "git-upload-pack", ".", NULL);
97 return -1;
98}
99
100static int execute(void)
101{
102 static char line[1000];
103 int len;
104
105 len = packet_read_line(0, line, sizeof(line));
106
107 if (len && line[len-1] == '\n')
108 line[--len] = 0;
109
110 if (!strncmp("git-upload-pack /", line, 17))
111 return upload(line + 16, len - 16);
112
113 logerror("Protocol error: '%s'", line);
114 return -1;
115}
116
117
118/*
119 * We count spawned/reaped separately, just to avoid any
120 * races when updating them from signals. The SIGCHLD handler
121 * will only update children_reaped, and the fork logic will
122 * only update children_spawned.
123 *
124 * MAX_CHILDREN should be a power-of-two to make the modulus
125 * operation cheap. It should also be at least twice
126 * the maximum number of connections we will ever allow.
127 */
128#define MAX_CHILDREN 128
129
130static int max_connections = 25;
131
132/* These are updated by the signal handler */
133static volatile unsigned int children_reaped = 0;
134static pid_t dead_child[MAX_CHILDREN];
135
136/* These are updated by the main loop */
137static unsigned int children_spawned = 0;
138static unsigned int children_deleted = 0;
139
140static struct child {
141 pid_t pid;
142 int addrlen;
143 struct sockaddr_storage address;
144} live_child[MAX_CHILDREN];
145
146static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
147{
148 live_child[idx].pid = pid;
149 live_child[idx].addrlen = addrlen;
150 memcpy(&live_child[idx].address, addr, addrlen);
151}
152
153/*
154 * Walk from "deleted" to "spawned", and remove child "pid".
155 *
156 * We move everything up by one, since the new "deleted" will
157 * be one higher.
158 */
159static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
160{
161 struct child n;
162
163 deleted %= MAX_CHILDREN;
164 spawned %= MAX_CHILDREN;
165 if (live_child[deleted].pid == pid) {
166 live_child[deleted].pid = -1;
167 return;
168 }
169 n = live_child[deleted];
170 for (;;) {
171 struct child m;
172 deleted = (deleted + 1) % MAX_CHILDREN;
173 if (deleted == spawned)
174 die("could not find dead child %d\n", pid);
175 m = live_child[deleted];
176 live_child[deleted] = n;
177 if (m.pid == pid)
178 return;
179 n = m;
180 }
181}
182
183/*
184 * This gets called if the number of connections grows
185 * past "max_connections".
186 *
187 * We _should_ start off by searching for connections
188 * from the same IP, and if there is some address wth
189 * multiple connections, we should kill that first.
190 *
191 * As it is, we just "randomly" kill 25% of the connections,
192 * and our pseudo-random generator sucks too. I have no
193 * shame.
194 *
195 * Really, this is just a place-holder for a _real_ algorithm.
196 */
197static void kill_some_children(int signo, unsigned start, unsigned stop)
198{
199 start %= MAX_CHILDREN;
200 stop %= MAX_CHILDREN;
201 while (start != stop) {
202 if (!(start & 3))
203 kill(live_child[start].pid, signo);
204 start = (start + 1) % MAX_CHILDREN;
205 }
206}
207
208static void check_max_connections(void)
209{
210 for (;;) {
211 int active;
212 unsigned spawned, reaped, deleted;
213
214 spawned = children_spawned;
215 reaped = children_reaped;
216 deleted = children_deleted;
217
218 while (deleted < reaped) {
219 pid_t pid = dead_child[deleted % MAX_CHILDREN];
220 remove_child(pid, deleted, spawned);
221 deleted++;
222 }
223 children_deleted = deleted;
224
225 active = spawned - deleted;
226 if (active <= max_connections)
227 break;
228
229 /* Kill some unstarted connections with SIGTERM */
230 kill_some_children(SIGTERM, deleted, spawned);
231 if (active <= max_connections << 1)
232 break;
233
234 /* If the SIGTERM thing isn't helping use SIGKILL */
235 kill_some_children(SIGKILL, deleted, spawned);
236 sleep(1);
237 }
238}
239
240static void handle(int incoming, struct sockaddr *addr, int addrlen)
241{
242 pid_t pid = fork();
243 char addrbuf[256] = "";
244 int port = -1;
245
246 if (pid) {
247 unsigned idx;
248
249 close(incoming);
250 if (pid < 0)
251 return;
252
253 idx = children_spawned % MAX_CHILDREN;
254 children_spawned++;
255 add_child(idx, pid, addr, addrlen);
256
257 check_max_connections();
258 return;
259 }
260
261 dup2(incoming, 0);
262 dup2(incoming, 1);
263 close(incoming);
264
265 if (addr->sa_family == AF_INET) {
266 struct sockaddr_in *sin_addr = (void *) addr;
267 inet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
268 port = sin_addr->sin_port;
269
270 } else if (addr->sa_family == AF_INET6) {
271 struct sockaddr_in6 *sin6_addr = (void *) addr;
272
273 char *buf = addrbuf;
274 *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */
275 inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1);
276 strcat(buf, "]");
277
278 port = sin6_addr->sin6_port;
279 }
280 lognotice("Connection from %s:%d", addrbuf, port);
281
282 exit(execute());
283}
284
285static void child_handler(int signo)
286{
287 for (;;) {
288 pid_t pid = waitpid(-1, NULL, WNOHANG);
289
290 if (pid > 0) {
291 unsigned reaped = children_reaped;
292 dead_child[reaped % MAX_CHILDREN] = pid;
293 children_reaped = reaped + 1;
294 /* XXX: Custom logging, since we don't wanna getpid() */
295 if (verbose)
296 fprintf(stderr, "[%d] Disconnected\n", pid);
297 continue;
298 }
299 break;
300 }
301}
302
303static int serve(int port)
304{
305 struct addrinfo hints, *ai0, *ai;
306 int gai;
307 int socknum = 0, *socklist = NULL;
308 int maxfd = -1;
309 fd_set fds_init, fds;
310 char pbuf[NI_MAXSERV];
311
312 signal(SIGCHLD, child_handler);
313
314 sprintf(pbuf, "%d", port);
315 memset(&hints, 0, sizeof(hints));
316 hints.ai_family = AF_UNSPEC;
317 hints.ai_socktype = SOCK_STREAM;
318 hints.ai_protocol = IPPROTO_TCP;
319 hints.ai_flags = AI_PASSIVE;
320
321 gai = getaddrinfo(NULL, pbuf, &hints, &ai0);
322 if (gai)
323 die("getaddrinfo() failed: %s\n", gai_strerror(gai));
324
325 FD_ZERO(&fds_init);
326
327 for (ai = ai0; ai; ai = ai->ai_next) {
328 int sockfd;
329 int *newlist;
330
331 sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
332 if (sockfd < 0)
333 continue;
334 if (sockfd >= FD_SETSIZE) {
335 error("too large socket descriptor.");
336 close(sockfd);
337 continue;
338 }
339
340#ifdef IPV6_V6ONLY
341 if (ai->ai_family == AF_INET6) {
342 int on = 1;
343 setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
344 &on, sizeof(on));
345 /* Note: error is not fatal */
346 }
347#endif
348
349 if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
350 close(sockfd);
351 continue; /* not fatal */
352 }
353 if (listen(sockfd, 5) < 0) {
354 close(sockfd);
355 continue; /* not fatal */
356 }
357
358 newlist = realloc(socklist, sizeof(int) * (socknum + 1));
359 if (!newlist)
360 die("memory allocation failed: %s", strerror(errno));
361
362 socklist = newlist;
363 socklist[socknum++] = sockfd;
364
365 FD_SET(sockfd, &fds_init);
366 if (maxfd < sockfd)
367 maxfd = sockfd;
368 }
369
370 freeaddrinfo(ai0);
371
372 if (socknum == 0)
373 die("unable to allocate any listen sockets on port %u", port);
374
375 for (;;) {
376 int i;
377 fds = fds_init;
378
379 if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 0) {
380 if (errno != EINTR) {
381 error("select failed, resuming: %s",
382 strerror(errno));
383 sleep(1);
384 }
385 continue;
386 }
387
388 for (i = 0; i < socknum; i++) {
389 int sockfd = socklist[i];
390
391 if (FD_ISSET(sockfd, &fds)) {
392 struct sockaddr_storage ss;
393 int sslen = sizeof(ss);
394 int incoming = accept(sockfd, (struct sockaddr *)&ss, &sslen);
395 if (incoming < 0) {
396 switch (errno) {
397 case EAGAIN:
398 case EINTR:
399 case ECONNABORTED:
400 continue;
401 default:
402 die("accept returned %s", strerror(errno));
403 }
404 }
405 handle(incoming, (struct sockaddr *)&ss, sslen);
406 }
407 }
408 }
409}
410
411int main(int argc, char **argv)
412{
413 int port = DEFAULT_GIT_PORT;
414 int inetd_mode = 0;
415 int i;
416
417 for (i = 1; i < argc; i++) {
418 char *arg = argv[i];
419
420 if (!strncmp(arg, "--port=", 7)) {
421 char *end;
422 unsigned long n;
423 n = strtoul(arg+7, &end, 0);
424 if (arg[7] && !*end) {
425 port = n;
426 continue;
427 }
428 }
429
430 if (!strcmp(arg, "--inetd")) {
431 inetd_mode = 1;
432 continue;
433 }
434 if (!strcmp(arg, "--verbose")) {
435 verbose = 1;
436 continue;
437 }
438
439 usage(daemon_usage);
440 }
441
442 if (inetd_mode) {
443 fclose(stderr); //FIXME: workaround
444 return execute();
445 }
446
447 return serve(port);
448}