doc: Documented htpipe.
[ashd.git] / src / httrcall.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 <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <signal.h>
25 #include <sys/wait.h>
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 #include <utils.h>
31 #include <log.h>
32 #include <req.h>
33 #include <proc.h>
34 #include <resp.h>
35
36 struct current {
37     struct current *next, *prev;
38     pid_t pid;
39 };
40
41 static char **prog;
42 static struct current *running = NULL;
43 static int nrunning = 0, limit = 0;
44 static volatile int exited;
45
46 static void checkexit(int block)
47 {
48     pid_t pid;
49     int st;
50     struct current *rec;
51     
52     exited = 0;
53     while((pid = waitpid(-1, &st, block?0:WNOHANG)) > 0) {
54         if(WCOREDUMP(st))
55             flog(LOG_WARNING, "child process %i dumped core", pid);
56         for(rec = running; rec != NULL; rec = rec->next) {
57             if(rec->pid == pid) {
58                 if(rec->next)
59                     rec->next->prev = rec->prev;
60                 if(rec->prev)
61                     rec->prev->next = rec->next;
62                 if(rec == running)
63                     running = rec->next;
64                 free(rec);
65                 nrunning--;
66                 break;
67             }
68         }
69     }
70 }
71
72 static void serve(struct hthead *req, int fd)
73 {
74     pid_t new;
75     struct current *rec;
76     
77     while((limit > 0) && (nrunning >= limit))
78         checkexit(1);
79     if((new = stdforkserve(prog, req, fd, NULL, NULL)) < 0) {
80         simpleerror(fd, 500, "Server Error", "The server appears to be overloaded.");
81         return;
82     }
83     omalloc(rec);
84     rec->pid = new;
85     rec->next = running;
86     if(running != NULL)
87         running->prev = rec;
88     running = rec;
89     nrunning++;
90 }
91
92 static void chldhandler(int sig)
93 {
94     exited = 1;
95 }
96
97 static void usage(FILE *out)
98 {
99     fprintf(out, "usage: httrcall [-h] [-l LIMIT] PROGRAM [ARGS...]\n");
100 }
101
102 int main(int argc, char **argv)
103 {
104     int c;
105     struct hthead *req;
106     int fd;
107     
108     while((c = getopt(argc, argv, "+hl:")) >= 0) {
109         switch(c) {
110         case 'h':
111             usage(stdout);
112             exit(0);
113         case 'l':
114             limit = atoi(optarg);
115             break;
116         default:
117             usage(stderr);
118             exit(1);
119         }
120     }
121     if(argc < optind - 1) {
122         usage(stderr);
123         exit(1);
124     }
125     prog = argv + optind;
126     sigaction(SIGCHLD, &(struct sigaction) {
127             .sa_handler = chldhandler,
128         }, NULL);
129     while(1) {
130         if(exited)
131             checkexit(0);
132         if((fd = recvreq(0, &req)) < 0) {
133             if(errno == EINTR)
134                 continue;
135             if(errno != 0)
136                 flog(LOG_ERR, "recvreq: %s", strerror(errno));
137             break;
138         }
139         serve(req, fd);
140         freehthead(req);
141         close(fd);
142     }
143     return(0);
144 }