daemon-bozo.c revision 1.11 1 /* $NetBSD: daemon-bozo.c,v 1.11 2010/06/22 05:24:12 mrg 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 if (httpd->foreground == 0)
83 daemon(1, 0);
84
85 portnum = (httpd->bindport) ? httpd->bindport : "http";
86 bozo_warn(httpd, "started in daemon mode as `%s' port `%s' root `%s'",
87 httpd->virthostname, portnum, httpd->slashdir);
88
89 memset(&h, 0, sizeof(h));
90 h.ai_family = PF_UNSPEC;
91 h.ai_socktype = SOCK_STREAM;
92 h.ai_flags = AI_PASSIVE;
93 e = getaddrinfo(httpd->bindaddress, portnum, &h, &r0);
94 if (e)
95 bozo_err(httpd, 1, "getaddrinfo([%s]:%s): %s",
96 httpd->bindaddress ? httpd->bindaddress : "*",
97 portnum, gai_strerror(e));
98 for (r = r0; r != NULL; r = r->ai_next)
99 httpd->nsock++;
100 httpd->sock = bozomalloc(httpd, httpd->nsock * sizeof(*httpd->sock));
101 httpd->fds = bozomalloc(httpd, httpd->nsock * sizeof(*httpd->fds));
102 for (i = 0, r = r0; r != NULL; r = r->ai_next) {
103 httpd->sock[i] = socket(r->ai_family, SOCK_STREAM, 0);
104 if (httpd->sock[i] == -1)
105 continue;
106 if (setsockopt(httpd->sock[i], SOL_SOCKET, SO_REUSEADDR, &on,
107 sizeof(on)) == -1)
108 bozo_warn(httpd, "setsockopt SO_REUSEADDR: %s",
109 strerror(errno));
110 if (bind(httpd->sock[i], r->ai_addr, r->ai_addrlen) == -1)
111 continue;
112 if (listen(httpd->sock[i], SOMAXCONN) == -1)
113 continue;
114 httpd->fds[i].events = POLLIN | POLLPRI | POLLRDNORM |
115 POLLRDBAND | POLLERR;
116 httpd->fds[i].fd = httpd->sock[i];
117 i++;
118 }
119 if (i == 0)
120 bozo_err(httpd, 1, "could not find any addresses to bind");
121 httpd->nsock = i;
122 freeaddrinfo(r0);
123
124 signal(SIGCHLD, sigchild);
125 }
126
127 void
128 bozo_daemon_closefds(bozohttpd_t *httpd)
129 {
130 int i;
131
132 for (i = 0; i < httpd->nsock; i++)
133 close(httpd->sock[i]);
134 }
135
136 static void
137 daemon_runchild(bozohttpd_t *httpd, int fd)
138 {
139 httpd->request_times++;
140
141 /* setup stdin/stdout/stderr */
142 dup2(fd, 0);
143 dup2(fd, 1);
144 /*dup2(fd, 2);*/
145 close(fd);
146 }
147
148 static int
149 daemon_poll_err(bozohttpd_t *httpd, int fd, int idx)
150 {
151 if ((httpd->fds[idx].revents & (POLLNVAL|POLLERR|POLLHUP)) == 0)
152 return 0;
153
154 bozo_warn(httpd, "poll on fd %d pid %d revents %d: %s",
155 httpd->fds[idx].fd, getpid(), httpd->fds[idx].revents,
156 strerror(errno));
157 bozo_warn(httpd, "nsock = %d", httpd->nsock);
158 close(httpd->sock[idx]);
159 httpd->nsock--;
160 bozo_warn(httpd, "nsock now = %d", httpd->nsock);
161 /* no sockets left */
162 if (httpd->nsock == 0)
163 exit(0);
164 /* last socket closed is the easy case */
165 if (httpd->nsock != idx) {
166 memmove(&httpd->fds[idx], &httpd->fds[idx+1],
167 (httpd->nsock - idx) * sizeof(*httpd->fds));
168 memmove(&httpd->sock[idx], &httpd->sock[idx+1],
169 (httpd->nsock - idx) * sizeof(*httpd->sock));
170 }
171
172 return 1;
173 }
174
175 /*
176 * the parent never returns from this function, only children that
177 * are ready to run... XXXMRG - still true in fork-lesser bozo?
178 */
179 int
180 bozo_daemon_fork(bozohttpd_t *httpd)
181 {
182 int i;
183
184 debug((httpd, DEBUG_FAT, "%s: pid %u request_times %d",
185 __func__, getpid(),
186 httpd->request_times));
187 /* if we've handled 5 files, exit and let someone else work */
188 if (httpd->request_times > 5 ||
189 (httpd->background == 2 && httpd->request_times > 0))
190 _exit(0);
191
192 #if 1
193 if (httpd->request_times > 0)
194 _exit(0);
195 #endif
196
197 while (httpd->background) {
198 struct sockaddr_storage ss;
199 socklen_t slen;
200 int fd;
201
202 if (httpd->nsock == 0)
203 exit(0);
204
205 /*
206 * wait for a connection, then fork() and return NULL in
207 * the parent, who will come back here waiting for another
208 * connection. read the request in in the child, and return
209 * it, for processing.
210 */
211 again:
212 if (poll(httpd->fds, (unsigned)httpd->nsock, INFTIM) == -1) {
213 /* fail on programmer errors */
214 if (errno == EFAULT ||
215 errno == EINVAL)
216 bozo_err(httpd, 1, "poll: %s",
217 strerror(errno));
218
219 /* sleep on some temporary kernel failures */
220 if (errno == ENOMEM ||
221 errno == EAGAIN)
222 sleep(1);
223
224 goto again;
225 }
226
227 for (i = 0; i < httpd->nsock; i++) {
228 if (daemon_poll_err(httpd, fd, i))
229 break;
230 if (httpd->fds[i].revents == 0)
231 continue;
232
233 slen = sizeof(ss);
234 fd = accept(httpd->fds[i].fd,
235 (struct sockaddr *)(void *)&ss, &slen);
236 if (fd == -1) {
237 if (errno == EFAULT ||
238 errno == EINVAL)
239 bozo_err(httpd, 1, "accept: %s",
240 strerror(errno));
241
242 if (errno == ENOMEM ||
243 errno == EAGAIN)
244 sleep(1);
245
246 continue;
247 }
248
249 #if 0
250 /*
251 * This code doesn't work. It interacts very poorly
252 * with ~user translation and needs to be fixed.
253 */
254 if (httpd->request_times > 0) {
255 daemon_runchild(httpd, fd);
256 return 0;
257 }
258 #endif
259
260 switch (fork()) {
261 case -1: /* eep, failure */
262 bozo_warn(httpd, "fork() failed, sleeping for "
263 "10 seconds: %s", strerror(errno));
264 close(fd);
265 sleep(10);
266 break;
267
268 case 0: /* child */
269 daemon_runchild(httpd, fd);
270 return 0;
271
272 default: /* parent */
273 close(fd);
274 break;
275 }
276 }
277 }
278 return 0;
279 }
280
281 #endif /* NO_DAEMON_MODE */
282