Added a simple program to manage multiple [FS]CGI instances.
[ashd.git] / src / multifscgi.c
CommitLineData
61a9ff68
FT
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
32static int nchildren;
33static pid_t *children;
34static char **chspec;
35static volatile int done, chdone;
36
37static void runchild(void)
38{
39 execvp(chspec[0], chspec);
40 exit(127);
41}
42
43static 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
78static 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
124static void chld(int sig)
125{
126 chdone = 1;
127}
128
129static void term(int sig)
130{
131 done = 1;
132}
133
134static void usage(FILE *out)
135{
136 fprintf(out, "usage: multifscgi NUM PROGRAM [ARGS...]\n");
137}
138
139int 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}