669240281820245e3624407368884b78faff135f
[ashd.git] / src / multifscgi.c
1 /*
2     ashd - A Sane HTTP Daemon
3     Copyright (C) 2008  Fredrik Tolf <fredrik@dolda2000.com>
4
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <signal.h>
24 #include <sys/wait.h>
25 #include <time.h>
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 #include <utils.h>
31
32 static int nchildren;
33 static pid_t *children;
34 static char **chspec;
35 static volatile int done, chdone;
36
37 static void runchild(void)
38 {
39     execvp(chspec[0], chspec);
40     exit(127);
41 }
42
43 static void manage(void)
44 {
45     int i, st;
46     sigset_t ss, ns;
47     pid_t ch;
48     
49     sigemptyset(&ss);
50     sigaddset(&ss, SIGCHLD);
51     sigaddset(&ss, SIGTERM);
52     sigaddset(&ss, SIGINT);
53     sigprocmask(SIG_BLOCK, &ss, &ns);
54     while(!done) {
55         for(i = 0; i < nchildren; i++) {
56             if(children[i] == 0) {
57                 if((ch = fork()) < 0)
58                     break;
59                 if(ch == 0)
60                     runchild();
61                 children[i] = ch;
62             }
63         }
64         pselect(0, NULL, NULL, NULL, NULL, &ns);
65         if(chdone) {
66             while((ch = waitpid(-1, &st, WNOHANG)) > 0) {
67                 for(i = 0; i < nchildren; i++) {
68                     if(children[i] == ch)
69                         children[i] = 0;
70                 }
71             }
72             chdone = 0;
73         }
74     }
75     sigprocmask(SIG_SETMASK, &ns, NULL);
76 }
77
78 static void killall(void)
79 {
80     int i, try, left, st;
81     sigset_t ss, ns;
82     struct timespec to;
83     pid_t ch;
84     time_t b;
85     
86     signal(SIGINT, SIG_DFL);
87     signal(SIGTERM, SIG_DFL);
88     sigemptyset(&ss);
89     sigaddset(&ss, SIGCHLD);
90     sigprocmask(SIG_BLOCK, &ss, &ns);
91     for(try = 0; try < 2; try++) {
92         for(i = 0; i < nchildren; i++) {
93             if(children[i] != 0)
94                 kill(children[i], SIGTERM);
95         }
96         b = time(NULL);
97         while(time(NULL) - b < 5) {
98             for(i = 0, left = 0; i < nchildren; i++) {
99                 if(children[i] != 0)
100                     left++;
101             }
102             if(!left)
103                 return;
104             to.tv_sec = 1;
105             to.tv_nsec = 0;
106             pselect(0, NULL, NULL, NULL, &to, &ns);
107             if(chdone) {
108                 while((ch = waitpid(-1, &st, WNOHANG)) > 0) {
109                     for(i = 0; i < nchildren; i++) {
110                         if(children[i] == ch)
111                             children[i] = 0;
112                     }
113                 }
114                 chdone = 0;
115             }
116         }
117     }
118     for(i = 0; i < nchildren; i++) {
119         if(children[i] != 0)
120             kill(children[i], SIGKILL);
121     }
122 }
123
124 static void chld(int sig)
125 {
126     chdone = 1;
127 }
128
129 static void term(int sig)
130 {
131     done = 1;
132 }
133
134 static void usage(FILE *out)
135 {
136     fprintf(out, "usage: multifscgi NUM PROGRAM [ARGS...]\n");
137 }
138
139 int main(int argc, char **argv)
140 {
141     int c;
142     
143     while((c = getopt(argc, argv, "h")) >= 0) {
144         switch(c) {
145         case 'h':
146             usage(stdout);
147             return(0);
148         default:
149             usage(stderr);
150             return(1);
151         }
152     }
153     if(argc - optind < 2) {
154         usage(stderr);
155         return(1);
156     }
157     nchildren = atoi(argv[optind]);
158     if(nchildren < 1) {
159         usage(stderr);
160         return(1);
161     }
162     children = szmalloc(sizeof(pid_t) * nchildren);
163     chspec = argv + optind + 1;
164     signal(SIGINT, term);
165     signal(SIGTERM, term);
166     signal(SIGCHLD, chld);
167     manage();
168     killall();
169     return(0);
170 }