python: Ignore EPIPE in hredir and serve-ssi.
[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                 for(i = 0; i < nchildren; i++) {
70                     if(children[i] == ch)
71                         children[i] = 0;
72                 }
73             }
74             chdone = 0;
75         }
76     }
77     sigprocmask(SIG_SETMASK, &ns, NULL);
78 }
79
80 static void killall(void)
81 {
82     int i, try, left, st;
83     sigset_t ss, ns;
84     struct timespec to;
85     pid_t ch;
86     time_t b;
87     
88     signal(SIGINT, SIG_DFL);
89     signal(SIGTERM, SIG_DFL);
90     sigemptyset(&ss);
91     sigaddset(&ss, SIGCHLD);
92     sigprocmask(SIG_BLOCK, &ss, &ns);
93     for(try = 0; try < 2; try++) {
94         for(i = 0; i < nchildren; i++) {
95             if(children[i] != 0)
96                 kill(children[i], SIGTERM);
97         }
98         b = time(NULL);
99         while(time(NULL) - b < 5) {
100             for(i = 0, left = 0; i < nchildren; i++) {
101                 if(children[i] != 0)
102                     left++;
103             }
104             if(!left)
105                 return;
106             to.tv_sec = 1;
107             to.tv_nsec = 0;
108             pselect(0, NULL, NULL, NULL, &to, &ns);
109             if(chdone) {
110                 while((ch = waitpid(-1, &st, WNOHANG)) > 0) {
111                     for(i = 0; i < nchildren; i++) {
112                         if(children[i] == ch)
113                             children[i] = 0;
114                     }
115                 }
116                 chdone = 0;
117             }
118         }
119     }
120     for(i = 0; i < nchildren; i++) {
121         if(children[i] != 0)
122             kill(children[i], SIGKILL);
123     }
124 }
125
126 static void chld(int sig)
127 {
128     chdone = 1;
129 }
130
131 static void term(int sig)
132 {
133     done = 1;
134 }
135
136 static void usage(FILE *out)
137 {
138     fprintf(out, "usage: multifscgi [-h] NUM PROGRAM [ARGS...]\n");
139 }
140
141 int main(int argc, char **argv)
142 {
143     int c;
144     
145     while((c = getopt(argc, argv, "+h")) >= 0) {
146         switch(c) {
147         case 'h':
148             usage(stdout);
149             return(0);
150         default:
151             usage(stderr);
152             return(1);
153         }
154     }
155     if(argc - optind < 2) {
156         usage(stderr);
157         return(1);
158     }
159     nchildren = atoi(argv[optind]);
160     if(nchildren < 1) {
161         usage(stderr);
162         return(1);
163     }
164     children = szmalloc(sizeof(pid_t) * nchildren);
165     chspec = argv + optind + 1;
166     signal(SIGINT, term);
167     signal(SIGTERM, term);
168     signal(SIGCHLD, chld);
169     manage();
170     killall();
171     return(0);
172 }