diff --git a/src/Makefile.am b/src/Makefile.am index 3af10c85..24967c88 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,12 +1,14 @@ AM_CFLAGS = $(FAM_CFLAGS) noinst_PROGRAMS=proc_open lemon # simple-fcgi #graphic evalo bench ajp ssl error_test adserver gen-license -sbin_PROGRAMS=lighttpd +sbin_PROGRAMS=lighttpd lighttpd-angel bin_PROGRAMS=spawn-fcgi LEMON=$(top_builddir)/src/lemon lemon_SOURCES=lemon.c +lighttpd_angel_SOURCES=lighttpd-angel.c + #simple_fcgi_SOURCES=simple-fcgi.c #simple_fcgi_LDADD=-lfcgi @@ -254,7 +256,7 @@ hdr = server.h buffer.h network.h log.h keyvalue.h \ splaytree.h proc_open.h status_counter.h \ mod_magnet_cache.h -DEFS= @DEFS@ -DLIBRARY_DIR="\"$(libdir)\"" +DEFS= @DEFS@ -DLIBRARY_DIR="\"$(libdir)\"" -DSBIN_DIR="\"$(sbindir)\"" lighttpd_SOURCES = $(src) lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(SSL_LIB) $(FAM_LIBS) diff --git a/src/lighttpd-angel.c b/src/lighttpd-angel.c new file mode 100644 index 00000000..a6e37410 --- /dev/null +++ b/src/lighttpd-angel.c @@ -0,0 +1,154 @@ +/** + * angel process for lighttpd + * + * the purpose is the run as root all the time and handle: + * - restart on crash + * - spawn on HUP to allow graceful restart + * - ... + * + * it has to stay safe and small to be trustable + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define BINPATH SBIN_DIR"/lighttpd" + +static siginfo_t last_sigterm_info; +static siginfo_t last_sighup_info; + +static volatile sig_atomic_t start_process = 1; +static volatile sig_atomic_t graceful_restart = 0; +static volatile pid_t pid = -1; + +static void sigaction_handler(int sig, siginfo_t *si, void *context) { + int exitcode; + + switch (sig) { + case SIGINT: + case SIGTERM: + memcpy(&last_sigterm_info, si, sizeof(*si)); + + /** forward the sig to the child */ + kill(pid, sig); + break; + case SIGHUP: /** do a graceful restart */ + memcpy(&last_sighup_info, si, sizeof(*si)); + + /** do a graceful shutdown on the main process and start a new child */ + kill(pid, SIGINT); + + usleep(5 * 1000); /** wait 5 microsec */ + + start_process = 1; + break; + case SIGCHLD: + /** a child died, de-combie it */ + wait(&exitcode); + break; + } +} + +int main(int argc, char **argv) { + int is_shutdown = 0; + struct sigaction act; + + /** + * we are running as root BEWARE + */ + + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + sigaction(SIGUSR1, &act, NULL); + + act.sa_sigaction = sigaction_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGALRM, &act, NULL); + sigaction(SIGCHLD, &act, NULL); + + /* check that the compiled in path has the right user, + * + * BEWARE: there is a race between the check here and the exec later + */ + + while (!is_shutdown) { + int exitcode = 0; + + if (start_process) { + pid = fork(); + + if (0 == pid) { + /* i'm the child */ + + argv[0] = BINPATH; + + execvp(BINPATH, argv); + + exit(1); + } else if (-1 == pid) { + /** error */ + + return -1; + } + + /* I'm the angel */ + start_process = 0; + } + + if ((pid_t)-1 == waitpid(pid, &exitcode, 0)) { + switch (errno) { + case EINTR: + /* someone sent a signal ... + * do we have to shutdown or restart the process */ + break; + case ECHILD: + /** + * make sure we are not in a race between the signal handler + * and the process restart */ + if (!start_process) is_shutdown = 1; + break; + default: + break; + } + } else { + /** process went away */ + + if (WIFEXITED(exitcode)) { + /** normal exit */ + + is_shutdown = 1; + + fprintf(stderr, "%s.%d: child (pid=%d) exited normally with exitcode: %d\n", + __FILE__, __LINE__, + pid, + WEXITSTATUS(exitcode)); + + } else if (WIFSIGNALED(exitcode)) { + /** got a signal */ + + fprintf(stderr, "%s.%d: child (pid=%d) exited unexpectedly with signal %d, restarting\n", + __FILE__, __LINE__, + pid, + WTERMSIG(exitcode)); + + start_process = 1; + } + } + } + + return 0; +} +