5 #include <sys/socket.h>
8 #include <netinet/in.h>
10 static const char daemon_usage[] = "git-daemon [--inetd | --port=n]";
12 static int upload(char *dir, int dirlen)
19 * Security on the cheap.
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.
25 if (access("git-daemon-export-ok", F_OK) ||
26 access("objects/00", X_OK) ||
31 * We'll ignore SIGTERM from now on, we have a
34 signal(SIGTERM, SIG_IGN);
36 /* git-upload-pack only ever reads stuff, so this is safe */
37 execlp("git-upload-pack", "git-upload-pack", ".", NULL);
41 static int execute(void)
43 static char line[1000];
46 len = packet_read_line(0, line, sizeof(line));
48 if (len && line[len-1] == '\n')
51 if (!strncmp("git-upload-pack /", line, 17))
52 return upload(line + 16, len - 16);
54 fprintf(stderr, "got bad connection '%s'\n", line);
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.
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.
69 #define MAX_CHILDREN 128
71 static int max_connections = 25;
73 /* These are updated by the signal handler */
74 static volatile unsigned int children_reaped = 0;
75 static pid_t dead_child[MAX_CHILDREN];
77 /* These are updated by the main loop */
78 static unsigned int children_spawned = 0;
79 static unsigned int children_deleted = 0;
84 struct sockaddr_storage address;
85 } live_child[MAX_CHILDREN];
87 static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
89 live_child[idx].pid = pid;
90 live_child[idx].addrlen = addrlen;
91 memcpy(&live_child[idx].address, addr, addrlen);
95 * Walk from "deleted" to "spawned", and remove child "pid".
97 * We move everything up by one, since the new "deleted" will
100 static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
104 deleted %= MAX_CHILDREN;
105 spawned %= MAX_CHILDREN;
106 if (live_child[deleted].pid == pid) {
107 live_child[deleted].pid = -1;
110 n = live_child[deleted];
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;
125 * This gets called if the number of connections grows
126 * past "max_connections".
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.
132 * As it is, we just "randomly" kill 25% of the connections,
133 * and our pseudo-random generator sucks too. I have no
136 * Really, this is just a place-holder for a _real_ algorithm.
138 static void kill_some_children(int signo, unsigned start, unsigned stop)
140 start %= MAX_CHILDREN;
141 stop %= MAX_CHILDREN;
142 while (start != stop) {
144 kill(live_child[start].pid, signo);
145 start = (start + 1) % MAX_CHILDREN;
149 static void check_max_connections(void)
153 unsigned spawned, reaped, deleted;
155 spawned = children_spawned;
156 reaped = children_reaped;
157 deleted = children_deleted;
159 while (deleted < reaped) {
160 pid_t pid = dead_child[deleted % MAX_CHILDREN];
161 remove_child(pid, deleted, spawned);
164 children_deleted = deleted;
166 active = spawned - deleted;
167 if (active <= max_connections)
170 /* Kill some unstarted connections with SIGTERM */
171 kill_some_children(SIGTERM, deleted, spawned);
172 if (active <= max_connections << 1)
175 /* If the SIGTERM thing isn't helping use SIGKILL */
176 kill_some_children(SIGKILL, deleted, spawned);
181 static void handle(int incoming, struct sockaddr *addr, int addrlen)
192 idx = children_spawned % MAX_CHILDREN;
194 add_child(idx, pid, addr, addrlen);
196 check_max_connections();
206 static void child_handler(int signo)
209 pid_t pid = waitpid(-1, NULL, WNOHANG);
212 unsigned reaped = children_reaped;
213 dead_child[reaped % MAX_CHILDREN] = pid;
214 children_reaped = reaped + 1;
221 static int serve(int port)
223 struct addrinfo hints, *ai0, *ai;
225 int socknum = 0, *socklist = NULL;
227 fd_set fds_init, fds;
228 char pbuf[NI_MAXSERV];
230 signal(SIGCHLD, child_handler);
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;
239 gai = getaddrinfo(NULL, pbuf, &hints, &ai0);
241 die("getaddrinfo() failed: %s\n", gai_strerror(gai));
245 for (ai = ai0; ai; ai = ai->ai_next) {
249 sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
252 if (sockfd >= FD_SETSIZE) {
253 error("too large socket descriptor.");
259 if (ai->ai_family == AF_INET6) {
261 setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
263 /* Note: error is not fatal */
267 if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
269 continue; /* not fatal */
271 if (listen(sockfd, 5) < 0) {
273 continue; /* not fatal */
276 newlist = realloc(socklist, sizeof(int) * (socknum + 1));
278 die("memory allocation failed: %s", strerror(errno));
281 socklist[socknum++] = sockfd;
283 FD_SET(sockfd, &fds_init);
291 die("unable to allocate any listen sockets on port %u", port);
297 if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 0) {
298 if (errno != EINTR) {
299 error("select failed, resuming: %s",
306 for (i = 0; i < socknum; i++) {
307 int sockfd = socklist[i];
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);
320 die("accept returned %s", strerror(errno));
323 handle(incoming, (struct sockaddr *)&ss, sslen);
329 int main(int argc, char **argv)
331 int port = DEFAULT_GIT_PORT;
335 for (i = 1; i < argc; i++) {
338 if (!strncmp(arg, "--port=", 7)) {
341 n = strtoul(arg+7, &end, 0);
342 if (arg[7] && !*end) {
348 if (!strcmp(arg, "--inetd")) {
357 fclose(stderr); //FIXME: workaround