Home | History | Annotate | Line # | Download | only in httpd
daemon-bozo.c revision 1.9
      1 /*	$NetBSD: daemon-bozo.c,v 1.9 2010/05/10 14:44:19 mrg Exp $	*/
      2 
      3 /*	$eterna: daemon-bozo.c,v 1.20 2010/05/10 04:39:00 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 void
    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 	while (httpd->background) {
    193 		struct	sockaddr_storage ss;
    194 		socklen_t slen;
    195 		int fd;
    196 
    197 		if (httpd->nsock == 0)
    198 			exit(0);
    199 
    200 		/*
    201 		 * wait for a connection, then fork() and return NULL in
    202 		 * the parent, who will come back here waiting for another
    203 		 * connection.  read the request in in the child, and return
    204 		 * it, for processing.
    205 		 */
    206 again:
    207 		if (poll(httpd->fds, (unsigned)httpd->nsock, INFTIM) == -1) {
    208 			/* fail on programmer errors */
    209 			if (errno == EFAULT ||
    210 			    errno == EINVAL)
    211 				bozo_err(httpd, 1, "poll: %s",
    212 					strerror(errno));
    213 
    214 			/* sleep on some temporary kernel failures */
    215 			if (errno == ENOMEM ||
    216 			    errno == EAGAIN)
    217 				sleep(1);
    218 
    219 			goto again;
    220 		}
    221 
    222 		for (i = 0; i < httpd->nsock; i++) {
    223 			if (daemon_poll_err(httpd, fd, i))
    224 				break;
    225 			if (httpd->fds[i].revents == 0)
    226 				continue;
    227 
    228 			slen = sizeof(ss);
    229 			fd = accept(httpd->fds[i].fd,
    230 					(struct sockaddr *)(void *)&ss, &slen);
    231 			if (fd == -1) {
    232 				if (errno == EFAULT ||
    233 				    errno == EINVAL)
    234 					bozo_err(httpd, 1, "accept: %s",
    235 						strerror(errno));
    236 
    237 				if (errno == ENOMEM ||
    238 				    errno == EAGAIN)
    239 					sleep(1);
    240 
    241 				continue;
    242 			}
    243 
    244 			if (httpd->request_times > 0) {
    245 				daemon_runchild(httpd, fd);
    246 				return;
    247 			}
    248 
    249 			switch (fork()) {
    250 			case -1: /* eep, failure */
    251 				bozo_warn(httpd, "fork() failed, sleeping for "
    252 					"10 seconds: %s", strerror(errno));
    253 				close(fd);
    254 				sleep(10);
    255 				break;
    256 
    257 			case 0: /* child */
    258 				daemon_runchild(httpd, fd);
    259 				return;
    260 
    261 			default: /* parent */
    262 				close(fd);
    263 				break;
    264 			}
    265 		}
    266 	}
    267 }
    268 
    269 #endif /* NO_DAEMON_MODE */
    270