daemon-bozo.c revision 1.8 1 /* $NetBSD: daemon-bozo.c,v 1.8 2010/05/10 03:37:45 mrg Exp $ */
2
3 /* $eterna: daemon-bozo.c,v 1.19 2010/05/10 02:51:28 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
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 /*
150 * the parent never returns from this function, only children that
151 * are ready to run... XXXMRG - still true in fork-lesser bozo?
152 */
153 void
154 bozo_daemon_fork(bozohttpd_t *httpd)
155 {
156 int i;
157
158 debug((httpd, DEBUG_FAT, "%s: pid %u request_times %d",
159 __func__, getpid(),
160 httpd->request_times));
161 /* if we've handled 5 files, exit and let someone else work */
162 if (httpd->request_times > 5 ||
163 (httpd->background == 2 && httpd->request_times > 0))
164 exit(0);
165
166 while (httpd->background) {
167 struct sockaddr_storage ss;
168 socklen_t slen;
169 int fd;
170
171 if (httpd->nsock == 0)
172 exit(0);
173
174 /*
175 * wait for a connection, then fork() and return NULL in
176 * the parent, who will come back here waiting for another
177 * connection. read the request in in the child, and return
178 * it, for processing.
179 */
180 again:
181 if (poll(httpd->fds, (unsigned)httpd->nsock, INFTIM) == -1) {
182 /* fail on programmer errors */
183 if (errno == EFAULT ||
184 errno == EINVAL)
185 bozo_err(httpd, 1, "poll: %s",
186 strerror(errno));
187
188 /* sleep on some temporary kernel failures */
189 if (errno == ENOMEM ||
190 errno == EAGAIN)
191 sleep(1);
192
193 goto again;
194 }
195
196 for (i = 0; i < httpd->nsock; i++) {
197 if (httpd->fds[i].revents & (POLLNVAL|POLLERR|POLLHUP)) {
198 bozo_warn(httpd,
199 "poll on fd %d pid %d revents %d: %s",
200 httpd->fds[i].fd, getpid(),
201 httpd->fds[i].revents,
202 strerror(errno));
203 bozo_warn(httpd, "nsock = %d", httpd->nsock);
204 close(httpd->sock[i]);
205 httpd->nsock--;
206 bozo_warn(httpd, "nsock now = %d", httpd->nsock);
207 /* no sockets left */
208 if (httpd->nsock == 0)
209 exit(0);
210 /* last socket; easy case */
211 if (httpd->nsock == i)
212 break;
213 memmove(&httpd->fds[i], &httpd->fds[i+i],
214 (httpd->nsock - i) *
215 sizeof(*httpd->fds));
216 memmove(&httpd->sock[i], &httpd->sock[i+i],
217 (httpd->nsock - i) *
218 sizeof(*httpd->sock));
219 break;
220 }
221 if (httpd->fds[i].revents == 0)
222 continue;
223
224 slen = sizeof(ss);
225 fd = accept(httpd->fds[i].fd,
226 (struct sockaddr *)(void *)&ss, &slen);
227 if (fd == -1) {
228 if (errno == EFAULT ||
229 errno == EINVAL)
230 bozo_err(httpd, 1, "accept: %s",
231 strerror(errno));
232
233 if (errno == ENOMEM ||
234 errno == EAGAIN)
235 sleep(1);
236
237 continue;
238 }
239
240 if (httpd->request_times > 0) {
241 daemon_runchild(httpd, fd);
242 return;
243 }
244
245 switch (fork()) {
246 case -1: /* eep, failure */
247 bozo_warn(httpd, "fork() failed, sleeping for "
248 "10 seconds: %s", strerror(errno));
249 close(fd);
250 sleep(10);
251 break;
252
253 case 0: /* child */
254 daemon_runchild(httpd, fd);
255 return;
256
257 default: /* parent */
258 close(fd);
259 break;
260 }
261 }
262 }
263 }
264
265 #endif /* NO_DAEMON_MODE */
266