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