daemon-bozo.c revision 1.12 1 /* $NetBSD: daemon-bozo.c,v 1.12 2011/02/06 19:00:53 pooka Exp $ */
2
3 /* $eterna: daemon-bozo.c,v 1.22 2010/06/21 06:45:45 mrg Exp $ */
4
5 /*
6 * Copyright (c) 1997-2010 Matthew R. Green
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer and
16 * dedication in the documentation and/or other materials provided
17 * with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 */
32
33 /* this code implements daemon mode for bozohttpd */
34
35 #ifndef NO_DAEMON_MODE
36
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/wait.h>
40
41 #include <netinet/in.h>
42
43 #include <errno.h>
44 #include <netdb.h>
45 #include <poll.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "bozohttpd.h"
51
52 static void sigchild(int); /* SIGCHLD handler */
53
54 #ifndef POLLRDNORM
55 #define POLLRDNORM 0
56 #endif
57 #ifndef POLLRDBAND
58 #define POLLRDBAND 0
59 #endif
60 #ifndef INFTIM
61 #define INFTIM -1
62 #endif
63
64 /* ARGSUSED */
65 static void
66 sigchild(int signo)
67 {
68 while (waitpid(-1, NULL, WNOHANG) > 0) {
69 }
70 }
71
72 void
73 bozo_daemon_init(bozohttpd_t *httpd)
74 {
75 struct addrinfo h, *r, *r0;
76 const char *portnum;
77 int e, i, on = 1;
78
79 if (!httpd->background)
80 return;
81
82 portnum = (httpd->bindport) ? httpd->bindport : "http";
83
84 memset(&h, 0, sizeof(h));
85 h.ai_family = PF_UNSPEC;
86 h.ai_socktype = SOCK_STREAM;
87 h.ai_flags = AI_PASSIVE;
88 e = getaddrinfo(httpd->bindaddress, portnum, &h, &r0);
89 if (e)
90 bozo_err(httpd, 1, "getaddrinfo([%s]:%s): %s",
91 httpd->bindaddress ? httpd->bindaddress : "*",
92 portnum, gai_strerror(e));
93 for (r = r0; r != NULL; r = r->ai_next)
94 httpd->nsock++;
95 httpd->sock = bozomalloc(httpd, httpd->nsock * sizeof(*httpd->sock));
96 httpd->fds = bozomalloc(httpd, httpd->nsock * sizeof(*httpd->fds));
97 for (i = 0, r = r0; r != NULL; r = r->ai_next) {
98 httpd->sock[i] = socket(r->ai_family, SOCK_STREAM, 0);
99 if (httpd->sock[i] == -1)
100 continue;
101 if (setsockopt(httpd->sock[i], SOL_SOCKET, SO_REUSEADDR, &on,
102 sizeof(on)) == -1)
103 bozo_warn(httpd, "setsockopt SO_REUSEADDR: %s",
104 strerror(errno));
105 if (bind(httpd->sock[i], r->ai_addr, r->ai_addrlen) == -1)
106 continue;
107 if (listen(httpd->sock[i], SOMAXCONN) == -1)
108 continue;
109 httpd->fds[i].events = POLLIN | POLLPRI | POLLRDNORM |
110 POLLRDBAND | POLLERR;
111 httpd->fds[i].fd = httpd->sock[i];
112 i++;
113 }
114 if (i == 0)
115 bozo_err(httpd, 1, "could not find any addresses to bind");
116 httpd->nsock = i;
117 freeaddrinfo(r0);
118
119 if (httpd->foreground == 0)
120 daemon(1, 0);
121
122 bozo_warn(httpd, "started in daemon mode as `%s' port `%s' root `%s'",
123 httpd->virthostname, portnum, httpd->slashdir);
124
125 signal(SIGCHLD, sigchild);
126 }
127
128 void
129 bozo_daemon_closefds(bozohttpd_t *httpd)
130 {
131 int i;
132
133 for (i = 0; i < httpd->nsock; i++)
134 close(httpd->sock[i]);
135 }
136
137 static void
138 daemon_runchild(bozohttpd_t *httpd, int fd)
139 {
140 httpd->request_times++;
141
142 /* setup stdin/stdout/stderr */
143 dup2(fd, 0);
144 dup2(fd, 1);
145 /*dup2(fd, 2);*/
146 close(fd);
147 }
148
149 static int
150 daemon_poll_err(bozohttpd_t *httpd, int fd, int idx)
151 {
152 if ((httpd->fds[idx].revents & (POLLNVAL|POLLERR|POLLHUP)) == 0)
153 return 0;
154
155 bozo_warn(httpd, "poll on fd %d pid %d revents %d: %s",
156 httpd->fds[idx].fd, getpid(), httpd->fds[idx].revents,
157 strerror(errno));
158 bozo_warn(httpd, "nsock = %d", httpd->nsock);
159 close(httpd->sock[idx]);
160 httpd->nsock--;
161 bozo_warn(httpd, "nsock now = %d", httpd->nsock);
162 /* no sockets left */
163 if (httpd->nsock == 0)
164 exit(0);
165 /* last socket closed is the easy case */
166 if (httpd->nsock != idx) {
167 memmove(&httpd->fds[idx], &httpd->fds[idx+1],
168 (httpd->nsock - idx) * sizeof(*httpd->fds));
169 memmove(&httpd->sock[idx], &httpd->sock[idx+1],
170 (httpd->nsock - idx) * sizeof(*httpd->sock));
171 }
172
173 return 1;
174 }
175
176 /*
177 * the parent never returns from this function, only children that
178 * are ready to run... XXXMRG - still true in fork-lesser bozo?
179 */
180 int
181 bozo_daemon_fork(bozohttpd_t *httpd)
182 {
183 int i;
184
185 debug((httpd, DEBUG_FAT, "%s: pid %u request_times %d",
186 __func__, getpid(),
187 httpd->request_times));
188 /* if we've handled 5 files, exit and let someone else work */
189 if (httpd->request_times > 5 ||
190 (httpd->background == 2 && httpd->request_times > 0))
191 _exit(0);
192
193 #if 1
194 if (httpd->request_times > 0)
195 _exit(0);
196 #endif
197
198 while (httpd->background) {
199 struct sockaddr_storage ss;
200 socklen_t slen;
201 int fd;
202
203 if (httpd->nsock == 0)
204 exit(0);
205
206 /*
207 * wait for a connection, then fork() and return NULL in
208 * the parent, who will come back here waiting for another
209 * connection. read the request in in the child, and return
210 * it, for processing.
211 */
212 again:
213 if (poll(httpd->fds, (unsigned)httpd->nsock, INFTIM) == -1) {
214 /* fail on programmer errors */
215 if (errno == EFAULT ||
216 errno == EINVAL)
217 bozo_err(httpd, 1, "poll: %s",
218 strerror(errno));
219
220 /* sleep on some temporary kernel failures */
221 if (errno == ENOMEM ||
222 errno == EAGAIN)
223 sleep(1);
224
225 goto again;
226 }
227
228 for (i = 0; i < httpd->nsock; i++) {
229 if (daemon_poll_err(httpd, fd, i))
230 break;
231 if (httpd->fds[i].revents == 0)
232 continue;
233
234 slen = sizeof(ss);
235 fd = accept(httpd->fds[i].fd,
236 (struct sockaddr *)(void *)&ss, &slen);
237 if (fd == -1) {
238 if (errno == EFAULT ||
239 errno == EINVAL)
240 bozo_err(httpd, 1, "accept: %s",
241 strerror(errno));
242
243 if (errno == ENOMEM ||
244 errno == EAGAIN)
245 sleep(1);
246
247 continue;
248 }
249
250 #if 0
251 /*
252 * This code doesn't work. It interacts very poorly
253 * with ~user translation and needs to be fixed.
254 */
255 if (httpd->request_times > 0) {
256 daemon_runchild(httpd, fd);
257 return 0;
258 }
259 #endif
260
261 switch (fork()) {
262 case -1: /* eep, failure */
263 bozo_warn(httpd, "fork() failed, sleeping for "
264 "10 seconds: %s", strerror(errno));
265 close(fd);
266 sleep(10);
267 break;
268
269 case 0: /* child */
270 daemon_runchild(httpd, fd);
271 return 0;
272
273 default: /* parent */
274 close(fd);
275 break;
276 }
277 }
278 }
279 return 0;
280 }
281
282 #endif /* NO_DAEMON_MODE */
283