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