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