git-daemon: rewrite kindergarden, new option --max-connections
Get rid of the fixed array of children and make max-connections dynamic and configurable. Fix the killing code to actually kill the newest connections from duplicate IP-addresses. Avoid forking if too busy already. Signed-off-by: Stephen R. van den Berg <srb@cuci.nl> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Stephen R. van den Berg committed
Aug 14, 2008 at 20:02 UTC
3bd62c21760f92996569bb9335b399a9545a5c41
2 files changed
+82
-140
Documentation/git-daemon.txt
+7
-2
@@ -9,8 +9,9 @@ SYNOPSIS
9
--------
10
[verse]
11
'git daemon' [--verbose] [--syslog] [--export-all]
12
- [--timeout=n] [--init-timeout=n] [--strict-paths]
13
- [--base-path=path] [--user-path | --user-path=path]
12
+ [--timeout=n] [--init-timeout=n] [--max-connections=n]
13
+ [--strict-paths] [--base-path=path] [--base-path-relaxed]
14
+ [--user-path | --user-path=path]
15
[--interpolated-path=pathtemplate]
16
[--reuseaddr] [--detach] [--pid-file=file]
17
[--enable=service] [--disable=service]
@@ -99,6 +100,10 @@ OPTIONS
100
it takes for the server to process the sub-request and time spent
101
waiting for next client's request.
102
103
+--max-connections::
104
+ Maximum number of concurrent clients, defaults to 32. Set it to
105
+ zero for no limit.
106
+
107
--syslog::
108
Log to syslog instead of stderr. Note that this option does not imply
109
--verbose, thus by default only error conditions will be logged.
daemon.c
+75
-138
@@ -19,8 +19,8 @@ static int reuseaddr;
19
20
static const char daemon_usage[] =
21
"git daemon [--verbose] [--syslog] [--export-all]\n"
22
-" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
23
-" [--base-path=path] [--base-path-relaxed]\n"
22
+" [--timeout=n] [--init-timeout=n] [--max-connections=n]\n"
23
+" [--strict-paths] [--base-path=path] [--base-path-relaxed]\n"
24
" [--user-path | --user-path=path]\n"
25
" [--interpolated-path=path]\n"
26
" [--reuseaddr] [--detach] [--pid-file=file]\n"
@@ -584,40 +584,35 @@ static int execute(struct sockaddr *addr)
584
return -1;
585
}
586
587
+static int max_connections = 32;
588
588
-/*
589
- * We count spawned/reaped separately, just to avoid any
590
- * races when updating them from signals. The SIGCHLD handler
591
- * will only update children_reaped, and the fork logic will
592
- * only update children_spawned.
593
- *
594
- * MAX_CHILDREN should be a power-of-two to make the modulus
595
- * operation cheap. It should also be at least twice
596
- * the maximum number of connections we will ever allow.
597
- */
598
-#define MAX_CHILDREN 128
599
-
600
-static int max_connections = 25;
601
-
602
-/* These are updated by the signal handler */
603
-static volatile unsigned int children_reaped;
604
-static pid_t dead_child[MAX_CHILDREN];
605
-
606
-/* These are updated by the main loop */
607
-static unsigned int children_spawned;
608
-static unsigned int children_deleted;
589
+static unsigned int live_children;
590
591
static struct child {
592
+ struct child *next;
593
pid_t pid;
612
- int addrlen;
594
struct sockaddr_storage address;
614
-} live_child[MAX_CHILDREN];
595
+} *firstborn;
596
616
-static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
597
+static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
598
{
618
- live_child[idx].pid = pid;
619
- live_child[idx].addrlen = addrlen;
620
- memcpy(&live_child[idx].address, addr, addrlen);
599
+ struct child *newborn;
600
+ newborn = xcalloc(1, sizeof *newborn);
601
+ if (newborn) {
602
+ struct child **cradle;
603
+
604
+ live_children++;
605
+ newborn->pid = pid;
606
+ memcpy(&newborn->address, addr, addrlen);
607
+ for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
608
+ if (!memcmp(&(*cradle)->address, &newborn->address,
609
+ sizeof newborn->address))
610
+ break;
611
+ newborn->next = *cradle;
612
+ *cradle = newborn;
613
+ }
614
+ else
615
+ logerror("Out of memory spawning new child");
616
}
617
618
/*
@@ -626,142 +621,78 @@ static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
621
* We move everything up by one, since the new "deleted" will
622
* be one higher.
623
*/
629
-static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
624
+static void remove_child(pid_t pid)
625
{
631
- struct child n;
626
+ struct child **cradle, *blanket;
627
633
- deleted %= MAX_CHILDREN;
634
- spawned %= MAX_CHILDREN;
635
- if (live_child[deleted].pid == pid) {
636
- live_child[deleted].pid = -1;
637
- return;
638
- }
639
- n = live_child[deleted];
640
- for (;;) {
641
- struct child m;
642
- deleted = (deleted + 1) % MAX_CHILDREN;
643
- if (deleted == spawned)
644
- die("could not find dead child %d\n", pid);
645
- m = live_child[deleted];
646
- live_child[deleted] = n;
647
- if (m.pid == pid)
648
- return;
649
- n = m;
650
- }
628
+ for (cradle = &firstborn; (blanket = *cradle); cradle = &blanket->next)
629
+ if (blanket->pid == pid) {
630
+ *cradle = blanket->next;
631
+ live_children--;
632
+ free(blanket);
633
+ break;
634
+ }
635
}
636
637
/*
638
* This gets called if the number of connections grows
639
* past "max_connections".
640
*
657
- * We _should_ start off by searching for connections
658
- * from the same IP, and if there is some address wth
659
- * multiple connections, we should kill that first.
660
- *
661
- * As it is, we just "randomly" kill 25% of the connections,
662
- * and our pseudo-random generator sucks too. I have no
663
- * shame.
664
- *
665
- * Really, this is just a place-holder for a _real_ algorithm.
641
+ * We kill the newest connection from a duplicate IP.
642
*/
667
-static void kill_some_children(int signo, unsigned start, unsigned stop)
643
+static void kill_some_child(void)
644
{
669
- start %= MAX_CHILDREN;
670
- stop %= MAX_CHILDREN;
671
- while (start != stop) {
672
- if (!(start & 3))
673
- kill(live_child[start].pid, signo);
674
- start = (start + 1) % MAX_CHILDREN;
675
- }
676
-}
677
-
678
-static void check_dead_children(void)
679
-{
680
- unsigned spawned, reaped, deleted;
681
-
682
- for (;;) {
683
- int status;
684
- pid_t pid = waitpid(-1, &status, WNOHANG);
685
-
686
- if (pid > 0) {
687
- unsigned reaped = children_reaped;
688
- if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
689
- pid = -pid;
690
- dead_child[reaped % MAX_CHILDREN] = pid;
691
- children_reaped = reaped + 1;
692
- continue;
693
- }
694
- break;
695
- }
696
-
697
- spawned = children_spawned;
698
- reaped = children_reaped;
699
- deleted = children_deleted;
700
-
701
- while (deleted < reaped) {
702
- pid_t pid = dead_child[deleted % MAX_CHILDREN];
703
- const char *dead = pid < 0 ? " (with error)" : "";
645
+ const struct child *blanket;
646
705
- if (pid < 0)
706
- pid = -pid;
647
+ if ((blanket = firstborn)) {
648
+ const struct child *next;
649
708
- /* XXX: Custom logging, since we don't wanna getpid() */
709
- if (verbose) {
710
- if (log_syslog)
711
- syslog(LOG_INFO, "[%d] Disconnected%s",
712
- pid, dead);
713
- else
714
- fprintf(stderr, "[%d] Disconnected%s\n",
715
- pid, dead);
716
- }
717
- remove_child(pid, deleted, spawned);
718
- deleted++;
650
+ for (; (next = blanket->next); blanket = next)
651
+ if (!memcmp(&blanket->address, &next->address,
652
+ sizeof next->address)) {
653
+ kill(blanket->pid, SIGTERM);
654
+ break;
655
+ }
656
}
720
- children_deleted = deleted;
657
}
658
723
-static void check_max_connections(void)
659
+static void check_dead_children(void)
660
{
725
- for (;;) {
726
- int active;
727
- unsigned spawned, deleted;
728
-
729
- check_dead_children();
730
-
731
- spawned = children_spawned;
732
- deleted = children_deleted;
733
-
734
- active = spawned - deleted;
735
- if (active <= max_connections)
736
- break;
737
-
738
- /* Kill some unstarted connections with SIGTERM */
739
- kill_some_children(SIGTERM, deleted, spawned);
740
- if (active <= max_connections << 1)
741
- break;
661
+ int status;
662
+ pid_t pid;
663
743
- /* If the SIGTERM thing isn't helping use SIGKILL */
744
- kill_some_children(SIGKILL, deleted, spawned);
745
- sleep(1);
664
+ while ((pid = waitpid(-1, &status, WNOHANG))>0) {
665
+ const char *dead = "";
666
+ remove_child(pid);
667
+ if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
668
+ dead = " (with error)";
669
+ loginfo("[%d] Disconnected%s", (int)pid, dead);
670
}
671
}
672
673
static void handle(int incoming, struct sockaddr *addr, int addrlen)
674
{
751
- pid_t pid = fork();
675
+ pid_t pid;
676
753
- if (pid) {
754
- unsigned idx;
677
+ if (max_connections && live_children >= max_connections) {
678
+ kill_some_child();
679
+ sleep(1); /* give it some time to die */
680
+ check_dead_children();
681
+ if (live_children >= max_connections) {
682
+ close(incoming);
683
+ logerror("Too many children, dropping connection");
684
+ return;
685
+ }
686
+ }
687
688
+ if ((pid = fork())) {
689
close(incoming);
757
- if (pid < 0)
690
+ if (pid < 0) {
691
+ logerror("Couldn't fork %s", strerror(errno));
692
return;
693
+ }
694
760
- idx = children_spawned % MAX_CHILDREN;
761
- children_spawned++;
762
- add_child(idx, pid, addr, addrlen);
763
-
764
- check_max_connections();
695
+ add_child(pid, addr, addrlen);
696
return;
697
}
698
@@ -1077,6 +1008,12 @@ int main(int argc, char **argv)
1008
init_timeout = atoi(arg+15);
1009
continue;
1010
}
1011
+ if (!prefixcmp(arg, "--max-connections=")) {
1012
+ max_connections = atoi(arg+18);
1013
+ if (max_connections < 0)
1014
+ max_connections = 0; /* unlimited */
1015
+ continue;
1016
+ }
1017
if (!strcmp(arg, "--strict-paths")) {
1018
strict_paths = 1;
1019
continue;