callcgi: Fixed possible deadlock problem on aborted requests.
[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 #include <errno.h>
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <utils.h>
32
33 static int nchildren;
34 static pid_t *children;
35 static char **chspec;
36 static volatile int done, chdone;
37
38 static void runchild(void)
39 {
40     execvp(chspec[0], chspec);
41     fprintf(stderr, "%s: %s", chspec[0], strerror(errno));
42     exit(127);
43 }
44
45 static void manage(void)
46 {
47     int i, st;
48     sigset_t ss, ns;
49     pid_t ch;
50     
51     sigemptyset(&ss);
52     sigaddset(&ss, SIGCHLD);
53     sigaddset(&ss, SIGTERM);
54     sigaddset(&ss, SIGINT);
55     sigprocmask(SIG_BLOCK, &ss, &ns);
56     while(!done) {
57         for(i = 0; i < nchildren; i++) {
58             if(children[i] == 0) {
59                 if((ch = fork()) < 0)
60                     break;
61                 if(ch == 0)
62                     runchild();
63                 children[i] = ch;
64             }
65         }
66         pselect(0, NULL, NULL, NULL, NULL, &ns);
67         if(chdone) {
68             while((ch = waitpid(-1, &st, WNOHANG)) > 0) {
69                 if(WCOREDUMP(st))
70                     fprintf(stderr, "multifscgi: child %i (%s) dumped core\n", ch, chspec[0]);
71                 for(i = 0; i < nchildren; i++) {
72                     if(children[i] == ch)
73                         children[i] = 0;
74                 }
75             }
76             chdone = 0;
77         }
78     }
79     sigprocmask(SIG_SETMASK, &ns, NULL);
80 }
81
82 static void killall(void)
83 {
84     int i, try, left, st;
85     sigset_t ss, ns;
86     struct timespec to;
87     pid_t ch;
88     time_t b;
89     
90     signal(SIGINT, SIG_DFL);
91     signal(SIGTERM, SIG_DFL);
92     sigemptyset(&ss);
93     sigaddset(&ss, SIGCHLD);
94     sigprocmask(SIG_BLOCK, &ss, &ns);
95     for(try = 0; try < 2; try++) {
96         for(i = 0; i < nchildren; i++) {
97             if(children[i] != 0)
98                 kill(children[i], SIGTERM);
99         }
100         b = time(NULL);
101         while(time(NULL) - b < 5) {
102             for(i = 0, left = 0; i < nchildren; i++) {
103                 if(children[i] != 0)
104                     left++;
105             }
106             if(!left)
107                 return;
108             to.tv_sec = 1;
109             to.tv_nsec = 0;
110             pselect(0, NULL, NULL, NULL, &to, &ns);
111             if(chdone) {
112                 while((ch = waitpid(-1, &st, WNOHANG)) > 0) {
113                     if(WCOREDUMP(st))
114                         fprintf(stderr, "multifscgi: child %i (%s) dumped core\n", ch, chspec[0]);
115                     for(i = 0; i < nchildren; i++) {
116                         if(children[i] == ch)
117                             children[i] = 0;
118                     }
119                 }
120                 chdone = 0;
121             }
122         }
123     }
124     for(i = 0; i < nchildren; i++) {
125         if(children[i] != 0)
126             kill(children[i], SIGKILL);
127     }
128 }
129
130 static void chld(int sig)
131 {
132     chdone = 1;
133 }
134
135 static void term(int sig)
136 {
137     done = 1;
138 }
139
140 static void usage(FILE *out)
141 {
142     fprintf(out, "usage: multifscgi [-h] NUM PROGRAM [ARGS...]\n");
143 }
144
145 int main(int argc, char **argv)
146 {
147     int c;
148     
149     while((c = getopt(argc, argv, "+h")) >= 0) {
150         switch(c) {
151         case 'h':
152             usage(stdout);
153             return(0);
154         default:
155             usage(stderr);
156             return(1);
157         }
158     }
159     if(argc - optind < 2) {
160         usage(stderr);
161         return(1);
162     }
163     nchildren = atoi(argv[optind]);
164     if(nchildren < 1) {
165         usage(stderr);
166         return(1);
167     }
168     children = szmalloc(sizeof(pid_t) * nchildren);
169     chspec = argv + optind + 1;
170     signal(SIGINT, term);
171     signal(SIGTERM, term);
172     signal(SIGCHLD, chld);
173     manage();
174     killall();
175     return(0);
176 }