1 /* $NetBSD: daemon-bozo.c,v 1.16 2014/01/02 08:21:38 mrg Exp $ */
3 /* $eterna: daemon-bozo.c,v 1.24 2011/11/18 09:21:15 mrg Exp $ */
6 * Copyright (c) 1997-2014 Matthew R. Green
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
33 /* this code implements daemon mode for bozohttpd */
35 #ifndef NO_DAEMON_MODE
37 #include <sys/param.h>
38 #include <sys/socket.h>
41 #include <netinet/in.h>
52 #include "bozohttpd.h"
54 static void sigchild(int); /* SIGCHLD handler */
66 static const char* pidfile_path
= NULL
;
67 static pid_t pidfile_pid
= 0;
73 while (waitpid(-1, NULL
, WNOHANG
) > 0) {
77 /* Signal handler to exit in a controlled manner. This ensures that
78 * any atexit(3) handlers are properly executed. */
81 controlled_exit(int signo
)
91 if (pidfile_path
!= NULL
&& pidfile_pid
== getpid()) {
92 (void)unlink(pidfile_path
);
98 create_pidfile(bozohttpd_t
*httpd
)
102 assert(pidfile_path
== NULL
);
104 if (httpd
->pidfile
== NULL
)
107 if (atexit(remove_pidfile
) == -1)
108 bozo_err(httpd
, 1, "Failed to install pidfile handler");
110 if ((file
= fopen(httpd
->pidfile
, "w")) == NULL
)
111 bozo_err(httpd
, 1, "Failed to create pidfile '%s'",
113 (void)fprintf(file
, "%d\n", getpid());
116 pidfile_path
= httpd
->pidfile
;
117 pidfile_pid
= getpid();
119 debug((httpd
, DEBUG_FAT
, "Created pid file '%s' for pid %d",
120 pidfile_path
, pidfile_pid
));
124 bozo_daemon_init(bozohttpd_t
*httpd
)
126 struct addrinfo h
, *r
, *r0
;
130 if (!httpd
->background
)
133 portnum
= (httpd
->bindport
) ? httpd
->bindport
: "http";
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
);
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
)
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)
152 if (setsockopt(httpd
->sock
[i
], SOL_SOCKET
, SO_REUSEADDR
, &on
,
154 bozo_warn(httpd
, "setsockopt SO_REUSEADDR: %s",
156 if (bind(httpd
->sock
[i
], r
->ai_addr
, r
->ai_addrlen
) == -1)
158 if (listen(httpd
->sock
[i
], SOMAXCONN
) == -1)
160 httpd
->fds
[i
].events
= POLLIN
| POLLPRI
| POLLRDNORM
|
161 POLLRDBAND
| POLLERR
;
162 httpd
->fds
[i
].fd
= httpd
->sock
[i
];
166 bozo_err(httpd
, 1, "could not find any addresses to bind");
170 if (httpd
->foreground
== 0)
173 create_pidfile(httpd
);
175 bozo_warn(httpd
, "started in daemon mode as `%s' port `%s' root `%s'",
176 httpd
->virthostname
, portnum
, httpd
->slashdir
);
178 signal(SIGHUP
, controlled_exit
);
179 signal(SIGINT
, controlled_exit
);
180 signal(SIGTERM
, controlled_exit
);
182 signal(SIGCHLD
, sigchild
);
186 bozo_daemon_closefds(bozohttpd_t
*httpd
)
190 for (i
= 0; i
< httpd
->nsock
; i
++)
191 close(httpd
->sock
[i
]);
195 daemon_runchild(bozohttpd_t
*httpd
, int fd
)
197 httpd
->request_times
++;
199 /* setup stdin/stdout/stderr */
207 daemon_poll_err(bozohttpd_t
*httpd
, int fd
, int idx
)
209 if ((httpd
->fds
[idx
].revents
& (POLLNVAL
|POLLERR
|POLLHUP
)) == 0)
212 bozo_warn(httpd
, "poll on fd %d pid %d revents %d: %s",
213 httpd
->fds
[idx
].fd
, getpid(), httpd
->fds
[idx
].revents
,
215 bozo_warn(httpd
, "nsock = %d", httpd
->nsock
);
216 close(httpd
->sock
[idx
]);
218 bozo_warn(httpd
, "nsock now = %d", httpd
->nsock
);
219 /* no sockets left */
220 if (httpd
->nsock
== 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
));
234 * the parent never returns from this function, only children that
235 * are ready to run... XXXMRG - still true in fork-lesser bozo?
238 bozo_daemon_fork(bozohttpd_t
*httpd
)
242 debug((httpd
, DEBUG_FAT
, "%s: pid %u request_times %d",
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))
251 if (httpd
->request_times
> 0)
255 while (httpd
->background
) {
256 struct sockaddr_storage ss
;
260 if (httpd
->nsock
== 0)
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.
270 if (poll(httpd
->fds
, (unsigned)httpd
->nsock
, INFTIM
) == -1) {
271 /* fail on programmer errors */
272 if (errno
== EFAULT
||
274 bozo_err(httpd
, 1, "poll: %s",
277 /* sleep on some temporary kernel failures */
278 if (errno
== ENOMEM
||
285 for (i
= 0; i
< httpd
->nsock
; i
++) {
286 if (daemon_poll_err(httpd
, fd
, i
))
288 if (httpd
->fds
[i
].revents
== 0)
292 fd
= accept(httpd
->fds
[i
].fd
,
293 (struct sockaddr
*)(void *)&ss
, &slen
);
295 if (errno
== EFAULT
||
297 bozo_err(httpd
, 1, "accept: %s",
300 if (errno
== ENOMEM
||
309 * This code doesn't work. It interacts very poorly
310 * with ~user translation and needs to be fixed.
312 if (httpd
->request_times
> 0) {
313 daemon_runchild(httpd
, fd
);
319 case -1: /* eep, failure */
320 bozo_warn(httpd
, "fork() failed, sleeping for "
321 "10 seconds: %s", strerror(errno
));
327 daemon_runchild(httpd
, fd
);
330 default: /* parent */
339 #endif /* NO_DAEMON_MODE */