supervise-daemon: rework signal handling and main loop

This is needed in preparation for adding support for a fifo to allow us
to communicate with the supervisor to ask it to signal the child it is
supervising.
This commit is contained in:
William Hubbs 2018-11-30 11:43:31 -06:00
parent ff4af908a5
commit 7f23e0461d

View File

@ -181,29 +181,21 @@ static void handle_signal(int sig)
{ {
int serrno = errno; int serrno = errno;
syslog(LOG_WARNING, "caught signal %d", sig); switch(sig) {
case SIGALRM:
if (sig == SIGTERM)
exiting = 1;
/* Restore errno */
errno = serrno;
if (! exiting)
re_exec_supervisor();
}
static void healthcheck(int sig)
{
if (sig == SIGALRM)
do_healthcheck = 1; do_healthcheck = 1;
} break;
case SIGCHLD:
static void reap_zombies(int sig) while (waitpid((pid_t)(-1), NULL, WNOHANG) > 0) {}
{ break;
int serrno; case SIGTERM:
(void) sig; exiting = 1;
break;
serrno = errno; default:
while (waitpid((pid_t)(-1), NULL, WNOHANG) > 0) {} syslog(LOG_WARNING, "caught signal %d", sig);
re_exec_supervisor();
}
/* Restore errno */
errno = serrno; errno = serrno;
} }
@ -447,54 +439,32 @@ static void child_process(char *exec, char **argv)
static void supervisor(char *exec, char **argv) static void supervisor(char *exec, char **argv)
{ {
FILE *fp; FILE *fp;
pid_t wait_pid; int health_status;
int healthcheck_respawn;
int i; int i;
int nkilled; int nkilled;
pid_t health_pid;
pid_t wait_pid;
sigset_t old_signals;
sigset_t signals;
struct sigaction sa;
struct timespec ts; struct timespec ts;
time_t respawn_now= 0; time_t respawn_now= 0;
time_t first_spawn= 0; time_t first_spawn= 0;
pid_t health_pid;
int health_status;
#ifndef RC_DEBUG /* block all signals we do not handle */
signal_setup_restart(SIGHUP, handle_signal); sigfillset(&signals);
signal_setup_restart(SIGINT, handle_signal); sigdelset(&signals, SIGALRM);
signal_setup_restart(SIGQUIT, handle_signal); sigdelset(&signals, SIGCHLD);
signal_setup_restart(SIGILL, handle_signal); sigdelset(&signals, SIGTERM);
signal_setup_restart(SIGABRT, handle_signal); sigprocmask(SIG_SETMASK, &signals, &old_signals);
signal_setup_restart(SIGFPE, handle_signal);
signal_setup_restart(SIGSEGV, handle_signal); /* install signal handler */
signal_setup_restart(SIGPIPE, handle_signal); memset(&sa, 0, sizeof(sa));
signal_setup_restart(SIGALRM, handle_signal); sa.sa_handler = handle_signal;
signal_setup(SIGTERM, handle_signal); sigaction(SIGALRM, &sa, NULL);
signal_setup(SIGCHLD, reap_zombies); sigaction(SIGCHLD, &sa, NULL);
signal_setup_restart(SIGUSR1, handle_signal); sigaction(SIGTERM, &sa, NULL);
signal_setup_restart(SIGUSR2, handle_signal);
signal_setup_restart(SIGBUS, handle_signal);
#ifdef SIGPOLL
signal_setup_restart(SIGPOLL, handle_signal);
#endif
signal_setup_restart(SIGPROF, handle_signal);
signal_setup_restart(SIGSYS, handle_signal);
signal_setup_restart(SIGTRAP, handle_signal);
signal_setup_restart(SIGVTALRM, handle_signal);
signal_setup_restart(SIGXCPU, handle_signal);
signal_setup_restart(SIGXFSZ, handle_signal);
#ifdef SIGEMT
signal_setup_restart(SIGEMT, handle_signal);
#endif
signal_setup_restart(SIGIO, handle_signal);
#ifdef SIGPWR
signal_setup_restart(SIGPWR, handle_signal);
#endif
#ifdef SIGUNUSED
signal_setup_restart(SIGUNUSED, handle_signal);
#endif
#ifdef SIGRTMIN
for (i = SIGRTMIN; i <= SIGRTMAX; i++)
signal_setup_restart(i, handle_signal);
#endif
#endif
fp = fopen(pidfile, "w"); fp = fopen(pidfile, "w");
if (! fp) if (! fp)
@ -515,89 +485,94 @@ static void supervisor(char *exec, char **argv)
/* /*
* Supervisor main loop * Supervisor main loop
*/ */
i = 0; if (healthcheckdelay)
if (healthcheckdelay) {
signal_setup(SIGALRM, healthcheck);
alarm(healthcheckdelay); alarm(healthcheckdelay);
} else if (healthchecktimer) { else if (healthchecktimer)
signal_setup(SIGALRM, healthcheck);
alarm(healthchecktimer); alarm(healthchecktimer);
}
while (!exiting) { while (!exiting) {
wait_pid = wait(&i); healthcheck_respawn = 0;
if (wait_pid == -1) { wait_pid = waitpid(child_pid, &i, 0);
if (do_healthcheck) { if (do_healthcheck) {
do_healthcheck = 0; do_healthcheck = 0;
alarm(0); alarm(0);
syslog(LOG_DEBUG, "running health check for %s", svcname); syslog(LOG_DEBUG, "running health check for %s", svcname);
health_pid = exec_service(svcname, "healthcheck"); health_pid = exec_service(svcname, "healthcheck");
health_status = rc_waitpid(health_pid); health_status = rc_waitpid(health_pid);
if (WIFEXITED(health_status) && !WEXITSTATUS(health_status)) { if (WIFEXITED(health_status) && WEXITSTATUS(health_status) == 0)
alarm(healthchecktimer); alarm(healthchecktimer);
continue; else {
} else { syslog(LOG_WARNING, "health check for %s failed", svcname);
syslog(LOG_WARNING, "health check for %s failed", svcname); health_pid = exec_service(svcname, "unhealthy");
health_pid = exec_service(svcname, "unhealthy"); rc_waitpid(health_pid);
rc_waitpid(health_pid);
syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid);
nkilled = run_stop_schedule(applet, NULL, NULL, child_pid, 0,
false, false, true);
if (nkilled > 0)
syslog(LOG_INFO, "killed %d processes", nkilled);
else if (errno != 0)
syslog(LOG_INFO, "Unable to kill %d: %s",
child_pid, strerror(errno));
}
} else if (exiting ) {
alarm(0);
syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid); syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid);
nkilled = run_stop_schedule(applet, exec, NULL, child_pid, 0, nkilled = run_stop_schedule(applet, NULL, NULL, child_pid, 0,
false, false, true); false, false, true);
if (nkilled > 0) if (nkilled < 0)
syslog(LOG_INFO, "killed %d processes", nkilled); syslog(LOG_INFO, "Unable to kill %d: %s",
continue; child_pid, strerror(errno));
else
healthcheck_respawn = 1;
} }
} else if (wait_pid == child_pid) { }
if (exiting ) {
alarm(0);
syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid);
nkilled = run_stop_schedule(applet, NULL, NULL, child_pid, 0,
false, false, true);
if (nkilled > 0)
syslog(LOG_INFO, "killed %d processes", nkilled);
continue;
}
if (wait_pid == child_pid) {
if (WIFEXITED(i)) if (WIFEXITED(i))
syslog(LOG_WARNING, "%s, pid %d, exited with return code %d", syslog(LOG_WARNING, "%s, pid %d, exited with return code %d",
exec, child_pid, WEXITSTATUS(i)); exec, child_pid, WEXITSTATUS(i));
else if (WIFSIGNALED(i)) else if (WIFSIGNALED(i))
syslog(LOG_WARNING, "%s, pid %d, terminated by signal %d", syslog(LOG_WARNING, "%s, pid %d, terminated by signal %d",
exec, child_pid, WTERMSIG(i)); exec, child_pid, WTERMSIG(i));
} else
continue;
ts.tv_sec = respawn_delay;
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
if (respawn_max > 0 && respawn_period > 0) {
respawn_now = time(NULL);
if (first_spawn == 0)
first_spawn = respawn_now;
if (respawn_now - first_spawn > respawn_period) {
respawn_count = 0;
first_spawn = 0;
} else
respawn_count++;
if (respawn_count > respawn_max) {
syslog(LOG_WARNING,
"respawned \"%s\" too many times, exiting", exec);
exiting = 1;
continue;
}
} }
alarm(0); if (wait_pid == child_pid || healthcheck_respawn) {
child_pid = fork(); do_healthcheck = 0;
if (child_pid == -1) healthcheck_respawn = 0;
eerrorx("%s: fork: %s", applet, strerror(errno)); alarm(0);
if (child_pid == 0) if (respawn_max > 0 && respawn_period > 0) {
child_process(exec, argv); respawn_now = time(NULL);
if (healthcheckdelay) { if (first_spawn == 0)
signal_setup(SIGALRM, healthcheck); first_spawn = respawn_now;
alarm(healthcheckdelay); if (respawn_now - first_spawn > respawn_period) {
} else if (healthchecktimer) { respawn_count = 0;
signal_setup(SIGALRM, healthcheck); first_spawn = 0;
alarm(healthchecktimer); } else
respawn_count++;
if (respawn_count > respawn_max) {
syslog(LOG_WARNING,
"respawned \"%s\" too many times, exiting", exec);
exiting = 1;
continue;
}
}
alarm(0);
ts.tv_sec = respawn_delay;
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
child_pid = fork();
if (child_pid == -1) {
syslog(LOG_ERR, "%s: fork: %s", applet, strerror(errno));
exit(EXIT_FAILURE);
}
if (child_pid == 0) {
sigprocmask(SIG_SETMASK, &old_signals, NULL);
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
sigaction(SIGALRM, &sa, NULL);
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
child_process(exec, argv);
}
if (healthcheckdelay)
alarm(healthcheckdelay);
else if (healthchecktimer)
alarm(healthchecktimer);
} }
} }