diff options
-rw-r--r-- | src/fileserv.c | 206 |
1 files changed, 168 insertions, 38 deletions
diff --git a/src/fileserv.c b/src/fileserv.c index f354460..fece526 100644 --- a/src/fileserv.c +++ b/src/fileserv.c @@ -18,6 +18,8 @@ #include "mimetypes.h" #include <string.h> #include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> #include <sys/select.h> #include <netdb.h> #include <signal.h> @@ -25,7 +27,6 @@ #include <time.h> #include <ctype.h> - #ifndef DEFAULT_ADDRESS # define DEFAULT_ADDRESS "0.0.0.0" #endif @@ -605,22 +606,167 @@ fileserv_error_printer (char const *msg) error("%s", msg); } +static int fatal_signals[] = { + SIGHUP, + SIGINT, + SIGQUIT, + SIGTERM, + 0 +}; + +static void +http_server(int fd, struct sockaddr *server_addr) +{ + struct MHD_Daemon *mhd; + sigset_t sigs; + int i; + + /* Block the 'fatal signals' and SIGPIPE in the handling thread */ + sigemptyset(&sigs); + for (i = 0; fatal_signals[i]; i++) + sigaddset(&sigs, fatal_signals[i]); + sigaddset(&sigs, SIGPIPE); + + pthread_sigmask(SIG_BLOCK, &sigs, NULL); + MHD_set_panic_func(fileserv_panic, NULL); + + mhd = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD + | MHD_USE_ERROR_LOG, 0, + fileserv_acl, server_addr, + fileserv_handler, NULL, + MHD_OPTION_LISTEN_SOCKET, fd, + MHD_OPTION_EXTERNAL_LOGGER, fileserv_logger, + NULL, + MHD_OPTION_END); + /* Unblock only the fatal signals */ + sigdelset(&sigs, SIGPIPE); + pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); + /* Wait for signal to arrive */ + sigwait(&sigs, &i); + MHD_stop_daemon(mhd); +} + +enum state { + RUNNING, /* Program is running */ + TERMINATING, /* Program is terminating */ + CHILD_TERM, /* SIGTERM has been sent to the child */ + CHILD_KILL +}; +volatile enum state state; + +static void +sigterm(int sig) +{ + if (state == RUNNING) + state = TERMINATING; +} + +static void +sigalrm(int sig) +{ + if (state == CHILD_TERM) + state = CHILD_KILL; +} + +static void +http_sentinel(int fd, struct sockaddr *server_addr) +{ + pid_t pid = 0; + int i; + struct sigaction act; + + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + act.sa_handler = sigalrm; + sigaction(SIGALRM, &act, NULL); + + for (i = 0; fatal_signals[i]; i++) + sigaddset(&act.sa_mask, fatal_signals[i]); + act.sa_handler = sigterm; + for (i = 0; fatal_signals[i]; i++) + sigaction(fatal_signals[i], &act, NULL); + + while (1) { + int status; + pid_t child_pid; + + if (pid == 0) { + if (state != RUNNING) + break; + pid = fork(); + if (pid == -1) { + error("fork: %s", strerror(errno)); + break; + } + if (pid == 0) { + runas(user, group); + http_server(fd, server_addr); + exit(0); + } + } + + if (child_pid > 0) { + child_pid = 0; + if (WIFEXITED(status)) { + int code = WEXITSTATUS(status); + if (code || verbose > 1) + error("child exited with status %d", + code); + } else if (WIFSIGNALED(status)) { + char const *coremsg = ""; +#ifdef WCOREDUMP + if (WCOREDUMP(status)) + coremsg = " (core dumped)"; +#endif + error("child terminated on signal %d%s", + WTERMSIG(status), coremsg); + } else if (WIFSTOPPED(status)) { + error("child stopped on signal %d", + WSTOPSIG(status)); + continue; + } else { + error("child terminated with unrecognized status %d", status); + } + /* restart the child */ + pid = 0; + continue; + } + + switch (state) { + case RUNNING: + break; + + case TERMINATING: + kill(pid, SIGTERM); + alarm(5); + state = CHILD_TERM; + break; + + case CHILD_TERM: + break; + + case CHILD_KILL: + kill(pid, SIGKILL); + return; + } + + child_pid = wait(&status); + if (child_pid == -1) { + if (errno != EINTR || verbose > 1) + error("wait: %s", strerror(errno)); + } + } +} + int main(int argc, char **argv) { int c, i; int fd = -1; - struct MHD_Daemon *mhd; struct sockaddr *server_addr; int foreground = 0; - sigset_t sigs; - static int fatal_signals[] = { - SIGHUP, - SIGINT, - SIGQUIT, - SIGTERM, - 0 - }; + int single_process = 0; progname = basename(argv[0]); @@ -629,7 +775,7 @@ main(int argc, char **argv) if (!tmpdir) tmpdir = "/tmp"; - while ((c = getopt(argc, argv, "c:fhv")) != EOF) { + while ((c = getopt(argc, argv, "c:fshv")) != EOF) { switch (c) { case 'c': config_file = optarg; @@ -637,6 +783,9 @@ main(int argc, char **argv) case 'f': foreground = 1; break; + case 's': + single_process = 1; + break; case 'h': usage(); exit(0); @@ -649,7 +798,7 @@ main(int argc, char **argv) } readconfig(); - + pidfile_check(); if (mime_types_file) { @@ -658,7 +807,8 @@ main(int argc, char **argv) fd = open_listener(address, &server_addr); - runas(user, group); + if (single_process) + runas(user, group); for (i = optind; i < argc; i++) urimap_add(argv[i]); @@ -669,8 +819,6 @@ main(int argc, char **argv) } if (!foreground) { - int i; - if (daemon(0, 1)) { error("daemon failed: %s", strerror(errno)); exit(1); @@ -680,30 +828,12 @@ main(int argc, char **argv) syslog_enable(); } pidfile_create(); - - /* Block the 'fatal signals' and SIGPIPE in the handling thread */ - sigemptyset(&sigs); - for (i = 0; fatal_signals[i]; i++) - sigaddset(&sigs, fatal_signals[i]); - sigaddset(&sigs, SIGPIPE); - - pthread_sigmask(SIG_BLOCK, &sigs, NULL); - MHD_set_panic_func(fileserv_panic, NULL); - mhd = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD - | MHD_USE_ERROR_LOG, 0, - fileserv_acl, server_addr, - fileserv_handler, NULL, - MHD_OPTION_LISTEN_SOCKET, fd, - MHD_OPTION_EXTERNAL_LOGGER, fileserv_logger, - NULL, - MHD_OPTION_END); - /* Unblock only the fatal signals */ - sigdelset(&sigs, SIGPIPE); - pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); - /* Wait for signal to arrive */ - sigwait(&sigs, &c); - MHD_stop_daemon(mhd); + if (single_process) + http_server(fd, server_addr); + else + http_sentinel(fd, server_addr); + pidfile_remove(); return 0; } |