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