xref: /netbsd/libexec/httpd/daemon-bozo.c (revision 6550d01e)
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