python: Added a request limiter to ashd-wsgi.
[ashd.git] / lib / proc.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 <string.h>
21 #include <unistd.h>
22 #include <sys/socket.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <fcntl.h>
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 #include <utils.h>
31 #include <log.h>
32 #include <proc.h>
33 #include <req.h>
34
35 int stdmkchild(char **argv, void (*chinit)(void *), void *idata)
36 {
37     pid_t pid;
38     int fd[2];
39     
40     if(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fd))
41         return(-1);
42     if((pid = fork()) < 0)
43         return(-1);
44     if(pid == 0) {
45         if(chinit != NULL)
46             chinit(idata);
47         dup2(fd[0], 0);
48         close(fd[0]);
49         close(fd[1]);
50         execvp(argv[0], argv);
51         flog(LOG_WARNING, "could not exec child program %s: %s", argv[0], strerror(errno));
52         exit(127);
53     }
54     close(fd[0]);
55     fcntl(fd[1], F_SETFD, FD_CLOEXEC);
56     return(fd[1]);
57 }
58
59 int sendfd(int sock, int fd, char *data, size_t datalen)
60 {
61     struct msghdr msg;
62     struct cmsghdr *cmsg;
63     char cmbuf[CMSG_SPACE(sizeof(int))];
64     struct iovec bufvec;
65     
66     memset(&msg, 0, sizeof(msg));
67     msg.msg_iov = &bufvec;
68     msg.msg_iovlen = 1;
69     bufvec.iov_base = data;
70     bufvec.iov_len = datalen;
71     
72     msg.msg_control = cmbuf;
73     msg.msg_controllen = sizeof(cmbuf);
74     cmsg = CMSG_FIRSTHDR(&msg);
75     cmsg->cmsg_level = SOL_SOCKET;
76     cmsg->cmsg_type = SCM_RIGHTS;
77     cmsg->cmsg_len = CMSG_LEN(sizeof(int));
78     *((int *)CMSG_DATA(cmsg)) = fd;
79     msg.msg_controllen = cmsg->cmsg_len;
80     
81     return(sendmsg(sock, &msg, MSG_NOSIGNAL | MSG_DONTWAIT));
82 }
83
84 int recvfd(int sock, char **data, size_t *datalen)
85 {
86     int ret, fd;
87     char *buf, cbuf[1024];
88     struct msghdr msg;
89     struct cmsghdr *cmsg;
90     struct iovec bufvec;
91     
92     buf = smalloc(65536);
93     memset(&msg, 0, sizeof(msg));
94
95     msg.msg_iov = &bufvec;
96     msg.msg_iovlen = 1;
97     bufvec.iov_base = buf;
98     bufvec.iov_len = 65536;
99     msg.msg_control = cbuf;
100     msg.msg_controllen = sizeof(cbuf);
101     
102     ret = recvmsg(sock, &msg, 0);
103     if(ret <= 0) {
104         free(buf);
105         if(ret == 0)
106             errno = 0;
107         return(-1);
108     }
109     
110     fd = -1;
111     for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
112         if((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_RIGHTS)) {
113             fd = *((int *)CMSG_DATA(cmsg));
114         }
115     }
116     if(fd < 0) {
117         free(buf);
118         errno = EPROTO;
119         return(-1);
120     }
121     buf = realloc(buf, ret);
122     *data = buf;
123     *datalen = ret;
124     return(fd);
125 }
126
127 pid_t stdforkserve(char **argv, struct hthead *req, int fd, void (*chinit)(void *), void *idata)
128 {
129     int i;
130     char *ebuf, *p;
131     pid_t pid;
132     struct charvbuf args;
133     
134     if((pid = fork()) < 0)
135         return(-1);
136     if(pid == 0) {
137         if(chinit != NULL)
138             chinit(idata);
139         
140         dup2(fd, 0);
141         dup2(fd, 1);
142         close(fd);
143         
144         bufinit(args);
145         for(i = 0; argv[i]; i++)
146             bufadd(args, argv[i]);
147         bufadd(args, req->method);
148         bufadd(args, req->url);
149         bufadd(args, req->rest);
150         bufadd(args, NULL);
151         
152         for(i = 0; i < req->noheaders; i++) {
153             ebuf = sstrdup(req->headers[i][0]);
154             for(p = ebuf; *p; p++) {
155                 if(isalnum(*p))
156                     *p = toupper(*p);
157                 else
158                     *p = '_';
159             }
160             putenv(sprintf2("REQ_%s=%s", ebuf, req->headers[i][1]));
161         }
162         putenv(sprintf2("HTTP_VERSION=%s", req->ver));
163         
164         execvp(args.b[0], args.b);
165         flog(LOG_WARNING, "could not exec child program %s: %s", argv[0], strerror(errno));
166         exit(127);
167     }
168     return(pid);
169 }