From 110c843d9dd1d47a37f5f041c9ae048db76b2cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 28 Aug 2013 23:25:02 +0200 Subject: [PATCH] Add supervisor --- include/ucore/supervisor.h | 22 +++++ src/supervisor.c | 194 +++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 include/ucore/supervisor.h create mode 100644 src/supervisor.c diff --git a/include/ucore/supervisor.h b/include/ucore/supervisor.h new file mode 100644 index 0000000..b33aa2b --- /dev/null +++ b/include/ucore/supervisor.h @@ -0,0 +1,22 @@ +#ifndef UCORE_SUPERVISOR_H_ +#define UCORE_SUPERVISOR_H_ + +/** Supervises this process - restart if it dies. + * + * The program should call supervise() at a pristine state (all relevant file descriptors closed, etc). + * supervise() will return 0 if all is ok, and the program can continue.(returning != 0 means + * the supervisor couldn't start, and the calling process must exit) + * + * This will run the actual process as a child process. If that child process dies by any means, + * supervise will create another child process, where supervise() returns 0 again. + * + * To kill the supervisor process and the supervised process, send SIGERM/SIGHUP/SIGINT to the + * parent supervisor process. + * Sending SIGUSR1 to the parent supervisor process will terminate the current child process and + * restart it. + * + * The supervised child process must exit if it recieves a SIGTERM + * @return 0 on success, non-zero on failure (e.g. fork() fails)*/ +int uc_supervise(void); + +#endif diff --git a/src/supervisor.c b/src/supervisor.c new file mode 100644 index 0000000..c59bc91 --- /dev/null +++ b/src/supervisor.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define CLEAN_EXIT 11 +#define BAD_EXIT 22 +#define RESTART 33 + +//seconds to wait before restarting the process +#define RESTART_GRACE 1 + +static volatile sig_atomic_t restart_flag = 0; +static volatile sig_atomic_t exit_flag = 0; + +static void restart_handler(int sig) +{ + (void)sig; + restart_flag = 1; +} + +static void exit_handler(int sig) +{ + (void)sig; + exit_flag = 1; +} + +static void kill_supervised_process(pid_t pid) +{ + kill(pid, SIGTERM); + /* Todo.. verify we killed it, otherwise take more + * desperate measures*/ +} + +static int watch(pid_t pid) +{ + int status; + pid_t rc; + +restart: + rc = waitpid(pid, &status, 0); + if (rc == -1 && errno == EINTR) { + if (restart_flag) { + restart_flag = 0; + kill_supervised_process(pid); + /* next waitpid should notify us of the death and + * we'll restart the process */ + goto restart; + } else if (exit_flag) { + exit_flag = 0; + return CLEAN_EXIT; + } + + goto restart; + + } else if (rc == -1) { + return BAD_EXIT; + } + + if (WIFSIGNALED(status)) { + syslog(LOG_ERR, "supervised process(pid %ld) exited cause of signal %d, status = 0x%X", (long)pid, WTERMSIG(status), status); + return RESTART; + } else if (WIFEXITED(status)) { + syslog(LOG_ERR, "supervised process(pid %ld) exited with code %d, status = 0x%X", (long)pid, WTERMSIG(status), status); + return RESTART; + } + + /* Ok, can we ever get here ? */ + + syslog(LOG_ERR, "waitpid of supervised process(pid %ld) status = 0x%X", (long)pid, status); + if (kill(pid, 0) == 0) { + syslog(LOG_ERR, "supervised process(pid %ld) seems to still be alive, not restarting", (long)pid); + goto restart; + } + + return RESTART; +} + +static struct sigaction orig_usr1_sa; +static struct sigaction orig_term_sa; +static struct sigaction orig_hup_sa; +static struct sigaction orig_int_sa; + +static void restore_signal_handlers(void) +{ + sigaction(SIGUSR1, &orig_usr1_sa, NULL); + sigaction(SIGTERM, &orig_term_sa, NULL); + sigaction(SIGHUP, &orig_hup_sa, NULL); + sigaction(SIGINT, &orig_int_sa, NULL); +} + +static void install_signal_handlers(void) +{ + struct sigaction act; + memset(&act, 0, sizeof act); + + act.sa_handler = restart_handler; + sigaction(SIGUSR1, &act, &orig_usr1_sa); + + act.sa_handler = exit_handler; + sigaction(SIGTERM, &act, &orig_term_sa); + sigaction(SIGHUP, &act, &orig_hup_sa); + sigaction(SIGINT, &act, &orig_int_sa); +} + +/** Supervises this process - restart if it dies. + * + * The program should call supervise() at a pristine state (all relevant file descriptors closed, etc). + * supervise() will return 0 if all is ok, and the program can continue.(returning != 0 means + * the supervisor couldn't start, and the calling process must exit) + * + * This will run the actual process as a child process. If that child process dies by any means, + * supervise will create another child process, where supervise() returns 0 again. + * + * To kill the supervisor process and the supervised process, send SIGERM/SIGHUP/SIGINT to the + * parent supervisor process. + * Sending SIGUSR1 to the parent supervisor process will terminate the current child process and + * restart it. + * + * The supervised child process must exit if it recieves a SIGTERM */ +int uc_supervise(void) +{ + pid_t pid; + + pid = fork(); + if (pid == -1) { + syslog(LOG_ERR, "Cannot create supervisor : %s", strerror(errno)); + return -1; + } else if (pid == 0) { + return 0; + } + + //close_files(); + install_signal_handlers(); + + syslog(LOG_INFO, "Supervising pid %ld", (long)pid); + + for (;;) { + int cmd = watch(pid); + switch (cmd) { + + case CLEAN_EXIT: + syslog(LOG_INFO, "Terminating supervised pid %lu", (unsigned long)pid); + kill_supervised_process(pid); + wait(NULL); + syslog(LOG_INFO, "Exiting"); + exit(0); + break; + + case BAD_EXIT: + syslog(LOG_ERR, "watch() error, exiting"); + exit(55); + break; + + case RESTART: { + pid_t new_pid = -1; + + while (new_pid == -1) { + sleep(RESTART_GRACE); + syslog(LOG_INFO, "Restarting supervised pid %lu", (unsigned long)pid); + closelog(); + new_pid = fork(); + if (new_pid == -1) { + syslog(LOG_INFO, "Cannot restart. %s", strerror(errno)); + } else if (new_pid == 0) { //return so the original process continues + restore_signal_handlers(); + return 0; + } + } + + pid = new_pid; + syslog(LOG_INFO, "Supervising new pid %lu", (unsigned long)new_pid); + } + + break; + + default: + exit(38); + break; + } + + } + + return 0x76; +} + +