2016-03-19 15:14:35 +00:00
|
|
|
#include "first.h"
|
|
|
|
|
2007-09-02 22:23:53 +00:00
|
|
|
/**
|
|
|
|
* 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 <sys/wait.h>
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#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 pid_t pid = -1;
|
|
|
|
|
2008-04-29 11:03:26 +00:00
|
|
|
#define UNUSED(x) ( (void)(x) )
|
|
|
|
|
2007-09-02 22:23:53 +00:00
|
|
|
static void sigaction_handler(int sig, siginfo_t *si, void *context) {
|
|
|
|
int exitcode;
|
|
|
|
|
2008-04-29 11:03:26 +00:00
|
|
|
UNUSED(context);
|
2007-09-02 22:23:53 +00:00
|
|
|
switch (sig) {
|
|
|
|
case SIGINT:
|
|
|
|
case SIGTERM:
|
2017-01-24 23:40:28 +00:00
|
|
|
case SIGUSR1:
|
|
|
|
if (pid <= 0) break;
|
2007-09-02 22:23:53 +00:00
|
|
|
memcpy(&last_sigterm_info, si, sizeof(*si));
|
|
|
|
|
|
|
|
/** forward the sig to the child */
|
|
|
|
kill(pid, sig);
|
|
|
|
break;
|
|
|
|
case SIGHUP: /** do a graceful restart */
|
2017-01-24 23:40:28 +00:00
|
|
|
if (pid <= 0) break;
|
2007-09-02 22:23:53 +00:00
|
|
|
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;
|
|
|
|
|
2008-04-29 11:03:26 +00:00
|
|
|
UNUSED(argc);
|
2017-08-13 01:43:04 +00:00
|
|
|
*(const char **)&argv[0] = BINPATH;
|
|
|
|
#ifdef __COVERITY__
|
|
|
|
__coverity_tainted_data_sanitize__(argv);
|
|
|
|
#endif
|
2008-04-29 11:03:26 +00:00
|
|
|
|
2007-09-02 22:23:53 +00:00
|
|
|
/**
|
|
|
|
* 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);
|
2017-01-24 23:40:28 +00:00
|
|
|
sigaction(SIGUSR1, &act, NULL);
|
2007-09-02 22:23:53 +00:00
|
|
|
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 */
|
|
|
|
|
2017-08-12 19:39:12 +00:00
|
|
|
/* intentionally pass argv params */
|
|
|
|
/* coverity[tainted_string : FALSE] */
|
2017-08-13 01:43:04 +00:00
|
|
|
execvp(argv[0], argv);
|
|
|
|
_exit(1);
|
2007-09-02 22:23:53 +00:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|