summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/fileserv.c206
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;
}

Return to:

Send suggestions and report system problems to the System administrator.