1*4864410bSmrg /* $NetBSD: daemon-bozo.c,v 1.22 2020/10/15 04:21:53 mrg Exp $ */
218c80b65Stls
341f9e942Smrg /* $eterna: daemon-bozo.c,v 1.24 2011/11/18 09:21:15 mrg Exp $ */
460dbe745Stls
560dbe745Stls /*
69b91523eSmrg * Copyright (c) 1997-2019 Matthew R. Green
760dbe745Stls * All rights reserved.
860dbe745Stls *
960dbe745Stls * Redistribution and use in source and binary forms, with or without
1060dbe745Stls * modification, are permitted provided that the following conditions
1160dbe745Stls * are met:
1260dbe745Stls * 1. Redistributions of source code must retain the above copyright
1360dbe745Stls * notice, this list of conditions and the following disclaimer.
1460dbe745Stls * 2. Redistributions in binary form must reproduce the above copyright
1560dbe745Stls * notice, this list of conditions and the following disclaimer and
1660dbe745Stls * dedication in the documentation and/or other materials provided
1760dbe745Stls * with the distribution.
1860dbe745Stls *
1960dbe745Stls * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2060dbe745Stls * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2160dbe745Stls * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2260dbe745Stls * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2360dbe745Stls * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2460dbe745Stls * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2560dbe745Stls * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2660dbe745Stls * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2760dbe745Stls * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2860dbe745Stls * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2960dbe745Stls * SUCH DAMAGE.
3060dbe745Stls *
3160dbe745Stls */
3260dbe745Stls
3360dbe745Stls /* this code implements daemon mode for bozohttpd */
3460dbe745Stls
3560dbe745Stls #ifndef NO_DAEMON_MODE
3660dbe745Stls
3760dbe745Stls #include <sys/param.h>
3860dbe745Stls #include <sys/socket.h>
3960dbe745Stls #include <sys/wait.h>
4060dbe745Stls
4160dbe745Stls #include <netinet/in.h>
4260dbe745Stls
4383bb4389Sjmmv #include <assert.h>
4460dbe745Stls #include <errno.h>
4560dbe745Stls #include <netdb.h>
4660dbe745Stls #include <poll.h>
4783bb4389Sjmmv #include <stdio.h>
4860dbe745Stls #include <stdlib.h>
4960dbe745Stls #include <string.h>
5060dbe745Stls #include <unistd.h>
5160dbe745Stls
5260dbe745Stls #include "bozohttpd.h"
5360dbe745Stls
5460dbe745Stls static void sigchild(int); /* SIGCHLD handler */
5560dbe745Stls
56c6e75af2Smrg #ifndef POLLRDNORM
57c6e75af2Smrg #define POLLRDNORM 0
58c6e75af2Smrg #endif
59c6e75af2Smrg #ifndef POLLRDBAND
60c6e75af2Smrg #define POLLRDBAND 0
61c6e75af2Smrg #endif
62c6e75af2Smrg #ifndef INFTIM
63c6e75af2Smrg #define INFTIM -1
64c6e75af2Smrg #endif
65*4864410bSmrg #ifndef USE_ARG
66*4864410bSmrg #define USE_ARG(x) /*LINTED*/(void)&(x)
67*4864410bSmrg #endif
68c6e75af2Smrg
6983bb4389Sjmmv static const char* pidfile_path = NULL;
7083bb4389Sjmmv static pid_t pidfile_pid = 0;
7183bb4389Sjmmv
7260dbe745Stls static void
sigchild(int signo)73ce206308Smrg sigchild(int signo)
7460dbe745Stls {
75*4864410bSmrg USE_ARG(signo);
764cfb2183Smrg while (waitpid(-1, NULL, WNOHANG) > 0)
774cfb2183Smrg /* nothing */;
7860dbe745Stls }
7960dbe745Stls
8083bb4389Sjmmv /* Signal handler to exit in a controlled manner. This ensures that
8183bb4389Sjmmv * any atexit(3) handlers are properly executed. */
8243d06469Sjoerg BOZO_DEAD static void
controlled_exit(int signo)8383bb4389Sjmmv controlled_exit(int signo)
8483bb4389Sjmmv {
85*4864410bSmrg USE_ARG(signo);
8683bb4389Sjmmv exit(EXIT_SUCCESS);
8783bb4389Sjmmv }
8883bb4389Sjmmv
8983bb4389Sjmmv static void
remove_pidfile(void)9083bb4389Sjmmv remove_pidfile(void)
9183bb4389Sjmmv {
9283bb4389Sjmmv
9383bb4389Sjmmv if (pidfile_path != NULL && pidfile_pid == getpid()) {
9483bb4389Sjmmv (void)unlink(pidfile_path);
9583bb4389Sjmmv pidfile_path = NULL;
9683bb4389Sjmmv }
9783bb4389Sjmmv }
9883bb4389Sjmmv
9983bb4389Sjmmv static void
create_pidfile(bozohttpd_t * httpd)10083bb4389Sjmmv create_pidfile(bozohttpd_t *httpd)
10183bb4389Sjmmv {
10283bb4389Sjmmv FILE *file;
10383bb4389Sjmmv
10483bb4389Sjmmv assert(pidfile_path == NULL);
10583bb4389Sjmmv
10683bb4389Sjmmv if (httpd->pidfile == NULL)
10783bb4389Sjmmv return;
10883bb4389Sjmmv
10983bb4389Sjmmv if (atexit(remove_pidfile) == -1)
110881b8188Smrg bozoerr(httpd, 1, "Failed to install pidfile handler");
11183bb4389Sjmmv
11283bb4389Sjmmv if ((file = fopen(httpd->pidfile, "w")) == NULL)
113881b8188Smrg bozoerr(httpd, 1, "Failed to create pidfile '%s'",
11483bb4389Sjmmv httpd->pidfile);
11583bb4389Sjmmv (void)fprintf(file, "%d\n", getpid());
11683bb4389Sjmmv (void)fclose(file);
11783bb4389Sjmmv
11883bb4389Sjmmv pidfile_path = httpd->pidfile;
11983bb4389Sjmmv pidfile_pid = getpid();
12083bb4389Sjmmv
12183bb4389Sjmmv debug((httpd, DEBUG_FAT, "Created pid file '%s' for pid %d",
12283bb4389Sjmmv pidfile_path, pidfile_pid));
12383bb4389Sjmmv }
12483bb4389Sjmmv
12560dbe745Stls void
bozo_daemon_init(bozohttpd_t * httpd)126ce206308Smrg bozo_daemon_init(bozohttpd_t *httpd)
12760dbe745Stls {
12860dbe745Stls struct addrinfo h, *r, *r0;
129ce206308Smrg const char *portnum;
13060dbe745Stls int e, i, on = 1;
13160dbe745Stls
132*4864410bSmrg if (!httpd->background && !httpd->foreground)
13360dbe745Stls return;
13460dbe745Stls
135*4864410bSmrg if (!httpd->background)
136*4864410bSmrg httpd->background = 1;
137*4864410bSmrg
138ce206308Smrg portnum = (httpd->bindport) ? httpd->bindport : "http";
13960dbe745Stls
140ce206308Smrg memset(&h, 0, sizeof(h));
14160dbe745Stls h.ai_family = PF_UNSPEC;
14260dbe745Stls h.ai_socktype = SOCK_STREAM;
14360dbe745Stls h.ai_flags = AI_PASSIVE;
144ce206308Smrg e = getaddrinfo(httpd->bindaddress, portnum, &h, &r0);
14560dbe745Stls if (e)
146881b8188Smrg bozoerr(httpd, 1, "getaddrinfo([%s]:%s): %s",
147ce206308Smrg httpd->bindaddress ? httpd->bindaddress : "*",
148ce206308Smrg portnum, gai_strerror(e));
14960dbe745Stls for (r = r0; r != NULL; r = r->ai_next)
150ce206308Smrg httpd->nsock++;
151ce206308Smrg httpd->sock = bozomalloc(httpd, httpd->nsock * sizeof(*httpd->sock));
152ce206308Smrg httpd->fds = bozomalloc(httpd, httpd->nsock * sizeof(*httpd->fds));
15360dbe745Stls for (i = 0, r = r0; r != NULL; r = r->ai_next) {
154ce206308Smrg httpd->sock[i] = socket(r->ai_family, SOCK_STREAM, 0);
155ce206308Smrg if (httpd->sock[i] == -1)
15660dbe745Stls continue;
157ce206308Smrg if (setsockopt(httpd->sock[i], SOL_SOCKET, SO_REUSEADDR, &on,
15860dbe745Stls sizeof(on)) == -1)
159881b8188Smrg bozowarn(httpd, "setsockopt SO_REUSEADDR: %s",
16060dbe745Stls strerror(errno));
161ce206308Smrg if (bind(httpd->sock[i], r->ai_addr, r->ai_addrlen) == -1)
16260dbe745Stls continue;
163ce206308Smrg if (listen(httpd->sock[i], SOMAXCONN) == -1)
16460dbe745Stls continue;
165ce206308Smrg httpd->fds[i].events = POLLIN | POLLPRI | POLLRDNORM |
166c6e75af2Smrg POLLRDBAND | POLLERR;
167ce206308Smrg httpd->fds[i].fd = httpd->sock[i];
16860dbe745Stls i++;
16960dbe745Stls }
17060dbe745Stls if (i == 0)
171881b8188Smrg bozoerr(httpd, 1, "could not find any addresses to bind");
172ce206308Smrg httpd->nsock = i;
17360dbe745Stls freeaddrinfo(r0);
17460dbe745Stls
175eb725c21Spooka if (httpd->foreground == 0)
176eb725c21Spooka daemon(1, 0);
177eb725c21Spooka
17883bb4389Sjmmv create_pidfile(httpd);
17983bb4389Sjmmv
180881b8188Smrg bozowarn(httpd, "started in daemon mode as `%s' port `%s' root `%s'",
181eb725c21Spooka httpd->virthostname, portnum, httpd->slashdir);
182eb725c21Spooka
18383bb4389Sjmmv signal(SIGHUP, controlled_exit);
18483bb4389Sjmmv signal(SIGINT, controlled_exit);
18583bb4389Sjmmv signal(SIGTERM, controlled_exit);
18683bb4389Sjmmv
18760dbe745Stls signal(SIGCHLD, sigchild);
18860dbe745Stls }
18960dbe745Stls
190c6e75af2Smrg void
bozo_daemon_closefds(bozohttpd_t * httpd)191ce206308Smrg bozo_daemon_closefds(bozohttpd_t *httpd)
192c6e75af2Smrg {
193c6e75af2Smrg int i;
194c6e75af2Smrg
195ce206308Smrg for (i = 0; i < httpd->nsock; i++)
196ce206308Smrg close(httpd->sock[i]);
197c6e75af2Smrg }
198c6e75af2Smrg
199c6e75af2Smrg static void
daemon_runchild(bozohttpd_t * httpd,int fd)200ce206308Smrg daemon_runchild(bozohttpd_t *httpd, int fd)
201c6e75af2Smrg {
202ce206308Smrg httpd->request_times++;
203c6e75af2Smrg
204c6e75af2Smrg /* setup stdin/stdout/stderr */
205c6e75af2Smrg dup2(fd, 0);
206c6e75af2Smrg dup2(fd, 1);
207c6e75af2Smrg /*dup2(fd, 2);*/
208c6e75af2Smrg close(fd);
209c6e75af2Smrg }
210c6e75af2Smrg
211a07e0db3Smrg static int
daemon_poll_err(bozohttpd_t * httpd,int idx)2129c080599Smrg daemon_poll_err(bozohttpd_t *httpd, int idx)
213a07e0db3Smrg {
214a07e0db3Smrg if ((httpd->fds[idx].revents & (POLLNVAL|POLLERR|POLLHUP)) == 0)
215a07e0db3Smrg return 0;
216a07e0db3Smrg
217881b8188Smrg bozowarn(httpd, "poll on fd %d pid %d revents %d: %s",
218a07e0db3Smrg httpd->fds[idx].fd, getpid(), httpd->fds[idx].revents,
219a07e0db3Smrg strerror(errno));
220881b8188Smrg bozowarn(httpd, "nsock = %d", httpd->nsock);
221a07e0db3Smrg close(httpd->sock[idx]);
222a07e0db3Smrg httpd->nsock--;
223881b8188Smrg bozowarn(httpd, "nsock now = %d", httpd->nsock);
224a07e0db3Smrg /* no sockets left */
225a07e0db3Smrg if (httpd->nsock == 0)
226a07e0db3Smrg exit(0);
227a07e0db3Smrg /* last socket closed is the easy case */
228a07e0db3Smrg if (httpd->nsock != idx) {
229a07e0db3Smrg memmove(&httpd->fds[idx], &httpd->fds[idx+1],
230a07e0db3Smrg (httpd->nsock - idx) * sizeof(*httpd->fds));
231a07e0db3Smrg memmove(&httpd->sock[idx], &httpd->sock[idx+1],
232a07e0db3Smrg (httpd->nsock - idx) * sizeof(*httpd->sock));
233a07e0db3Smrg }
234a07e0db3Smrg
235a07e0db3Smrg return 1;
236a07e0db3Smrg }
237a07e0db3Smrg
23860dbe745Stls /*
23960dbe745Stls * the parent never returns from this function, only children that
24003387632Smrg * are ready to run... XXXMRG - still true in fork-lesser bozo?
24160dbe745Stls */
242aeb27ed4Smrg int
bozo_daemon_fork(bozohttpd_t * httpd)243ce206308Smrg bozo_daemon_fork(bozohttpd_t *httpd)
24460dbe745Stls {
245c6e75af2Smrg int i;
24660dbe745Stls
247ce206308Smrg debug((httpd, DEBUG_FAT, "%s: pid %u request_times %d",
248ce206308Smrg __func__, getpid(),
249ce206308Smrg httpd->request_times));
250c6e75af2Smrg /* if we've handled 5 files, exit and let someone else work */
251ce206308Smrg if (httpd->request_times > 5 ||
252ce206308Smrg (httpd->background == 2 && httpd->request_times > 0))
25330539536Smrg _exit(0);
25430539536Smrg
25530539536Smrg #if 1
25630539536Smrg if (httpd->request_times > 0)
25730539536Smrg _exit(0);
25830539536Smrg #endif
259bbbdac0aSmrg
260ce206308Smrg while (httpd->background) {
261bbbdac0aSmrg struct sockaddr_storage ss;
262bbbdac0aSmrg socklen_t slen;
263bbbdac0aSmrg int fd;
264bbbdac0aSmrg
265ce206308Smrg if (httpd->nsock == 0)
266bbbdac0aSmrg exit(0);
26760dbe745Stls
26860dbe745Stls /*
26960dbe745Stls * wait for a connection, then fork() and return NULL in
27060dbe745Stls * the parent, who will come back here waiting for another
27160dbe745Stls * connection. read the request in in the child, and return
27260dbe745Stls * it, for processing.
27360dbe745Stls */
27460dbe745Stls again:
275ce206308Smrg if (poll(httpd->fds, (unsigned)httpd->nsock, INFTIM) == -1) {
276bbbdac0aSmrg /* fail on programmer errors */
277bbbdac0aSmrg if (errno == EFAULT ||
278bbbdac0aSmrg errno == EINVAL)
279881b8188Smrg bozoerr(httpd, 1, "poll: %s",
280ce206308Smrg strerror(errno));
281bbbdac0aSmrg
282bbbdac0aSmrg /* sleep on some temporary kernel failures */
283bbbdac0aSmrg if (errno == ENOMEM ||
284bbbdac0aSmrg errno == EAGAIN)
285bbbdac0aSmrg sleep(1);
286bbbdac0aSmrg
28760dbe745Stls goto again;
28860dbe745Stls }
28960dbe745Stls
290ce206308Smrg for (i = 0; i < httpd->nsock; i++) {
2919c080599Smrg if (daemon_poll_err(httpd, i))
292bbbdac0aSmrg break;
293ce206308Smrg if (httpd->fds[i].revents == 0)
29460dbe745Stls continue;
29560dbe745Stls
296e20ddcffSdegroote slen = sizeof(ss);
297ce206308Smrg fd = accept(httpd->fds[i].fd,
298ce206308Smrg (struct sockaddr *)(void *)&ss, &slen);
29960dbe745Stls if (fd == -1) {
300bbbdac0aSmrg if (errno == EFAULT ||
301bbbdac0aSmrg errno == EINVAL)
302881b8188Smrg bozoerr(httpd, 1, "accept: %s",
303ce206308Smrg strerror(errno));
304bbbdac0aSmrg
305bbbdac0aSmrg if (errno == ENOMEM ||
306bbbdac0aSmrg errno == EAGAIN)
307bbbdac0aSmrg sleep(1);
308bbbdac0aSmrg
30960dbe745Stls continue;
31060dbe745Stls }
311c6e75af2Smrg
312aeb27ed4Smrg #if 0
313aeb27ed4Smrg /*
314aeb27ed4Smrg * This code doesn't work. It interacts very poorly
315aeb27ed4Smrg * with ~user translation and needs to be fixed.
316aeb27ed4Smrg */
317ce206308Smrg if (httpd->request_times > 0) {
318ce206308Smrg daemon_runchild(httpd, fd);
319aeb27ed4Smrg return 0;
320c6e75af2Smrg }
321aeb27ed4Smrg #endif
322c6e75af2Smrg
32360dbe745Stls switch (fork()) {
32460dbe745Stls case -1: /* eep, failure */
325881b8188Smrg bozowarn(httpd, "fork() failed, sleeping for "
326c6e75af2Smrg "10 seconds: %s", strerror(errno));
32760dbe745Stls close(fd);
32860dbe745Stls sleep(10);
329c6e75af2Smrg break;
33060dbe745Stls
33160dbe745Stls case 0: /* child */
332ce206308Smrg daemon_runchild(httpd, fd);
333aeb27ed4Smrg return 0;
33460dbe745Stls
33560dbe745Stls default: /* parent */
33660dbe745Stls close(fd);
337c6e75af2Smrg break;
33860dbe745Stls }
33960dbe745Stls }
34060dbe745Stls }
341aeb27ed4Smrg return 0;
34260dbe745Stls }
34360dbe745Stls
34460dbe745Stls #endif /* NO_DAEMON_MODE */
345