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