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