diff options
Diffstat (limited to 'src/rc/supervise-daemon.c')
-rw-r--r-- | src/rc/supervise-daemon.c | 1250 |
1 files changed, 0 insertions, 1250 deletions
diff --git a/src/rc/supervise-daemon.c b/src/rc/supervise-daemon.c deleted file mode 100644 index 93deedcd..00000000 --- a/src/rc/supervise-daemon.c +++ /dev/null @@ -1,1250 +0,0 @@ -/* - * supervise-daemon - * This is a supervisor for daemons. - * It will start a deamon and make sure it restarts if it crashes. - */ - -/* - * Copyright (c) 2016 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -/* nano seconds */ -#define POLL_INTERVAL 20000000 -#define WAIT_PIDFILE 500000000 -#define ONE_SECOND 1000000000 -#define ONE_MS 1000000 - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <termios.h> -#include <sys/time.h> -#include <sys/wait.h> - -#ifdef __linux__ -#include <sys/syscall.h> /* For io priority */ -#include <sys/prctl.h> /* For prctl */ -#endif - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <limits.h> -#include <grp.h> -#include <pwd.h> -#include <signal.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> - -#ifdef HAVE_PAM -#include <security/pam_appl.h> - -/* We are not supporting authentication conversations */ -static struct pam_conv conv = { NULL, NULL}; -#endif - -#ifdef HAVE_CAP -#include <sys/capability.h> -#endif - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "rc-plugin.h" -#include "rc-schedules.h" -#include "_usage.h" -#include "helpers.h" - -/* Use long option value that is out of range for 8 bit getopt values. - * The exact enum value is internal and can freely change, so we keep the - * options sorted. - */ -enum { - /* This has to come first so following values stay in the 0x100+ range. */ - LONGOPT_BASE = 0x100, - - LONGOPT_CAPABILITIES, - LONGOPT_OOM_SCORE_ADJ, - LONGOPT_NO_NEW_PRIVS, - LONGOPT_SECBITS, -}; - -const char *applet = NULL; -const char *extraopts = NULL; -const char getoptstring[] = "A:a:D:d:e:g:H:I:Kk:m:N:p:R:r:s:Su:1:2:3" \ - getoptstring_COMMON; -const struct option longopts[] = { - { "healthcheck-timer", 1, NULL, 'a'}, - { "healthcheck-delay", 1, NULL, 'A'}, - { "capabilities", 1, NULL, LONGOPT_CAPABILITIES}, - { "secbits", 1, NULL, LONGOPT_SECBITS}, - { "no-new-privs", 0, NULL, LONGOPT_NO_NEW_PRIVS}, - { "respawn-delay", 1, NULL, 'D'}, - { "chdir", 1, NULL, 'd'}, - { "env", 1, NULL, 'e'}, - { "group", 1, NULL, 'g'}, - { "ionice", 1, NULL, 'I'}, - { "stop", 0, NULL, 'K'}, - { "umask", 1, NULL, 'k'}, - { "respawn-max", 1, NULL, 'm'}, - { "nicelevel", 1, NULL, 'N'}, - { "oom-score-adj",1, NULL, LONGOPT_OOM_SCORE_ADJ}, - { "pidfile", 1, NULL, 'p'}, - { "respawn-period", 1, NULL, 'P'}, - { "retry", 1, NULL, 'R'}, - { "chroot", 1, NULL, 'r'}, - { "signal", 1, NULL, 's'}, - { "start", 0, NULL, 'S'}, - { "user", 1, NULL, 'u'}, - { "stdout", 1, NULL, '1'}, - { "stderr", 1, NULL, '2'}, - { "reexec", 0, NULL, '3'}, - longopts_COMMON -}; -const char * const longopts_help[] = { - "set an initial health check delay", - "set a health check timer", - "Set the inheritable, ambient and bounding capabilities", - "Set the security-bits for the program", - "Set the No New Privs flag for the program", - "Set a respawn delay", - "Change the PWD", - "Set an environment string", - "Change the process group", - "Set an ionice class:data when starting", - "Stop daemon", - "Set the umask for the daemon", - "set maximum number of respawn attempts", - "Set a nicelevel when starting", - "Set OOM score adjustment when starting", - "Match pid found in this file", - "Set respawn time period", - "Retry schedule to use when stopping", - "Chroot to this directory", - "Send a signal to the daemon", - "Start daemon", - "Change the process user", - "Redirect stdout to file", - "Redirect stderr to file", - "reexec (used internally)", - longopts_help_COMMON -}; -const char *usagestring = NULL; - -static int healthcheckdelay = 0; -static int healthchecktimer = 0; -static volatile sig_atomic_t do_healthcheck = 0; -static volatile sig_atomic_t exiting = 0; -static int nicelevel = INT_MIN; -static int ionicec = -1; -static int ioniced = 0; -static int oom_score_adj = INT_MIN; -static char *changeuser, *ch_root, *ch_dir; -static uid_t uid = 0; -static gid_t gid = 0; -static int devnull_fd = -1; -static int stdin_fd; -static int stdout_fd; -static int stderr_fd; -static char *redirect_stderr = NULL; -static char *redirect_stdout = NULL; -#ifdef TIOCNOTTY -static int tty_fd = -1; -#endif -static pid_t child_pid; -static int respawn_count = 0; -static int respawn_delay = 0; -static int respawn_max = 10; -static int respawn_period = 0; -static char *fifopath = NULL; -static int fifo_fd = 0; -static char *pidfile = NULL; -static char *svcname = NULL; -static bool verbose = false; -#ifdef HAVE_CAP -static cap_iab_t cap_iab = NULL; -static unsigned secbits = 0; -#endif -#ifdef PR_SET_NO_NEW_PRIVS -static bool no_new_privs = false; -#endif - -extern char **environ; - -#if !defined(SYS_ioprio_set) && defined(__NR_ioprio_set) -# define SYS_ioprio_set __NR_ioprio_set -#endif -#if !defined(__DragonFly__) -static inline int ioprio_set(int which _unused, int who _unused, - int ioprio _unused) -{ -#ifdef SYS_ioprio_set - return syscall(SYS_ioprio_set, which, who, ioprio); -#else - return 0; -#endif -} -#endif - -static void cleanup(void) -{ - free(changeuser); -} - -static void re_exec_supervisor(void) -{ - syslog(LOG_WARNING, "Re-executing for %s", svcname); - execlp("supervise-daemon", "supervise-daemon", svcname, "--reexec", - (char *) NULL); - syslog(LOG_ERR, "Unable to execute supervise-daemon: %s", - strerror(errno)); - exit(EXIT_FAILURE); -} - -static void handle_signal(int sig) -{ - int serrno = errno; - pid_t pid; - - switch (sig) { - case SIGALRM: - do_healthcheck = 1; - break; - case SIGCHLD: - if (exiting) - while (waitpid((pid_t)(-1), NULL, WNOHANG) > 0) {} - else { - while ((pid = waitpid((pid_t)(-1), NULL, WNOHANG|WNOWAIT)) > 0) { - if (pid == child_pid) - break; - pid = waitpid(pid, NULL, WNOHANG); - } - } - break; - case SIGTERM: - exiting = 1; - break; - default: - syslog(LOG_WARNING, "caught signal %d", sig); - re_exec_supervisor(); - } - /* Restore errno */ - errno = serrno; -} - -static char * expand_home(const char *home, const char *path) -{ - char *opath, *ppath, *p, *nh; - struct passwd *pw; - - if (!path || *path != '~') - return xstrdup(path); - - opath = ppath = xstrdup(path); - if (ppath[1] != '/' && ppath[1] != '\0') { - p = strchr(ppath + 1, '/'); - if (p) - *p = '\0'; - pw = getpwnam(ppath + 1); - if (pw) { - home = pw->pw_dir; - ppath = p; - if (ppath) - *ppath = '/'; - } else - home = NULL; - } else - ppath++; - - if (!home) { - free(opath); - return xstrdup(path); - } - if (!ppath) { - free(opath); - return xstrdup(home); - } - - xasprintf(&nh, "%s%s", home, ppath); - free(opath); - return nh; -} - -static char *make_cmdline(char **argv) -{ - char **c; - char *cmdline = NULL; - size_t len = 0; - - for (c = argv; c && *c; c++) - len += (strlen(*c) + 1); - cmdline = xmalloc(len+1); - memset(cmdline, 0, len+1); - for (c = argv; c && *c; c++) { - strcat(cmdline, *c); - strcat(cmdline, " "); - } - return cmdline; -} - -static pid_t exec_command(const char *cmd) -{ - char *file; - pid_t pid = -1; - sigset_t full; - sigset_t old; - struct sigaction sa; - - file = rc_service_resolve(svcname); - if (!exists(file)) { - free(file); - return 0; - } - - /* We need to block signals until we have forked */ - memset(&sa, 0, sizeof (sa)); - sa.sa_handler = SIG_DFL; - sigemptyset(&sa.sa_mask); - sigfillset(&full); - sigprocmask(SIG_SETMASK, &full, &old); - - pid = fork(); - if (pid == 0) { - /* Restore default handlers */ - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGQUIT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGUSR1, &sa, NULL); - sigaction(SIGWINCH, &sa, NULL); - - /* Unmask signals */ - sigprocmask(SIG_SETMASK, &old, NULL); - - /* Safe to run now */ - execl(file, file, cmd, (char *) NULL); - syslog(LOG_ERR, "unable to exec `%s': %s\n", - file, strerror(errno)); - _exit(EXIT_FAILURE); - } - - if (pid == -1) - syslog(LOG_ERR, "fork: %s\n",strerror (errno)); - - sigprocmask(SIG_SETMASK, &old, NULL); - free(file); - return pid; -} - -static void child_process(char *exec, char **argv) -{ - RC_STRINGLIST *env_list; - RC_STRING *env; - int i; - char *p; - char *token; - size_t len; - char *newpath; - char *np; - char *cmdline = NULL; - time_t start_time; - char start_count_string[20]; - char start_time_string[20]; - FILE *fp; - -#ifdef HAVE_PAM - pam_handle_t *pamh = NULL; - int pamr; - const char *const *pamenv = NULL; -#endif - - setsid(); - - if (svcname) { - start_time = time(NULL); - from_time_t(start_time_string, start_time); - rc_service_value_set(svcname, "start_time", start_time_string); - sprintf(start_count_string, "%i", respawn_count); - rc_service_value_set(svcname, "start_count", start_count_string); - sprintf(start_count_string, "%d", getpid()); - rc_service_value_set(svcname, "child_pid", start_count_string); - } - - if (nicelevel != INT_MIN) { - if (setpriority(PRIO_PROCESS, getpid(), nicelevel) == -1) - eerrorx("%s: setpriority %d: %s", applet, nicelevel, - strerror(errno)); - } - - if (ionicec != -1 && ioprio_set(1, getpid(), ionicec | ioniced) == -1) - eerrorx("%s: ioprio_set %d %d: %s", applet, ionicec, ioniced, - strerror(errno)); - - if (oom_score_adj != INT_MIN) { - fp = fopen("/proc/self/oom_score_adj", "w"); - if (!fp) - eerrorx("%s: oom_score_adj %d: %s", applet, - oom_score_adj, strerror(errno)); - fprintf(fp, "%d\n", oom_score_adj); - fclose(fp); - } - - if (ch_root && chroot(ch_root) < 0) - eerrorx("%s: chroot `%s': %s", applet, ch_root, strerror(errno)); - - if (ch_dir && chdir(ch_dir) < 0) - eerrorx("%s: chdir `%s': %s", applet, ch_dir, strerror(errno)); - -#ifdef HAVE_PAM - if (changeuser != NULL) { - pamr = pam_start("supervise-daemon", - changeuser, &conv, &pamh); - - if (pamr == PAM_SUCCESS) - pamr = pam_acct_mgmt(pamh, PAM_SILENT); - if (pamr == PAM_SUCCESS) - pamr = pam_open_session(pamh, PAM_SILENT); - if (pamr != PAM_SUCCESS) - eerrorx("%s: pam error: %s", applet, pam_strerror(pamh, pamr)); - } -#endif - - if (gid && setgid(gid)) - eerrorx("%s: unable to set groupid to %d", applet, gid); - if (changeuser && initgroups(changeuser, gid)) - eerrorx("%s: initgroups (%s, %d)", applet, changeuser, gid); -#ifdef HAVE_CAP - if (uid && cap_setuid(uid)) -#else - if (uid && setuid(uid)) -#endif - eerrorx ("%s: unable to set userid to %d", applet, uid); - - /* Close any fd's to the passwd database */ - endpwent(); - -#ifdef HAVE_CAP - if (cap_iab != NULL) { - i = cap_iab_set_proc(cap_iab); - - if (cap_free(cap_iab) != 0) - eerrorx("Could not releasable memory: %s", strerror(errno)); - - if (i != 0) - eerrorx("Could not set iab: %s", strerror(errno)); - } - - if (secbits != 0) { - if (cap_set_secbits(secbits) < 0) - eerrorx("Could not set securebits to 0x%x: %s", secbits, strerror(errno)); - } -#endif - -#ifdef PR_SET_NO_NEW_PRIVS - if (no_new_privs) { - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) - eerrorx("Could not set No New Privs flag: %s", strerror(errno)); - } -#endif - - /* remove the controlling tty */ -#ifdef TIOCNOTTY - ioctl(tty_fd, TIOCNOTTY, 0); - close(tty_fd); -#endif - - /* Clean the environment of any RC_ variables */ - env_list = rc_stringlist_new(); - i = 0; - while (environ[i]) - rc_stringlist_add(env_list, environ[i++]); - -#ifdef HAVE_PAM - if (changeuser != NULL) { - pamenv = (const char *const *)pam_getenvlist(pamh); - if (pamenv) { - while (*pamenv) { - /* Don't add strings unless they set a var */ - if (strchr(*pamenv, '=')) - putenv(xstrdup(*pamenv)); - else - unsetenv(*pamenv); - pamenv++; - } - } - } -#endif - - TAILQ_FOREACH(env, env_list, entries) { - if ((strncmp(env->value, "RC_", 3) == 0 && - strncmp(env->value, "RC_SERVICE=", 11) != 0 && - strncmp(env->value, "RC_SVCNAME=", 11) != 0) || - strncmp(env->value, "SSD_NICELEVEL=", 14) == 0 || - strncmp(env->value, "SSD_IONICELEVEL=", 16) == 0 || - strncmp(env->value, "SSD_OOM_SCORE_ADJ=", 18) == 0) - { - p = strchr(env->value, '='); - *p = '\0'; - unsetenv(env->value); - continue; - } - } - rc_stringlist_free(env_list); - - /* For the path, remove the rcscript bin dir from it */ - if ((token = getenv("PATH"))) { - len = strlen(token); - newpath = np = xmalloc(len + 1); - while (token && *token) { - p = strchr(token, ':'); - if (p) { - *p++ = '\0'; - while (*p == ':') - p++; - } - if (strcmp(token, RC_LIBEXECDIR "/bin") != 0 && - strcmp(token, RC_LIBEXECDIR "/sbin") != 0) - { - len = strlen(token); - if (np != newpath) - *np++ = ':'; - memcpy(np, token, len); - np += len; - } - token = p; - } - *np = '\0'; - unsetenv("PATH"); - setenv("PATH", newpath, 1); - } - - stdin_fd = devnull_fd; - stdout_fd = devnull_fd; - stderr_fd = devnull_fd; - if (redirect_stdout) { - if ((stdout_fd = open(redirect_stdout, - O_WRONLY | O_CREAT | O_APPEND, - S_IRUSR | S_IWUSR)) == -1) - eerrorx("%s: unable to open the logfile" - " for stdout `%s': %s", - applet, redirect_stdout, strerror(errno)); - } - if (redirect_stderr) { - if ((stderr_fd = open(redirect_stderr, - O_WRONLY | O_CREAT | O_APPEND, - S_IRUSR | S_IWUSR)) == -1) - eerrorx("%s: unable to open the logfile" - " for stderr `%s': %s", - applet, redirect_stderr, strerror(errno)); - } - - dup2(stdin_fd, STDIN_FILENO); - if (redirect_stdout || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stdout_fd, STDOUT_FILENO); - if (redirect_stderr || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stderr_fd, STDERR_FILENO); - - for (i = getdtablesize() - 1; i >= 3; --i) - fcntl(i, F_SETFD, FD_CLOEXEC); - cmdline = make_cmdline(argv); - syslog(LOG_INFO, "Child command line: %s", cmdline); - free(cmdline); - execvp(exec, argv); - -#ifdef HAVE_PAM - if (changeuser != NULL && pamr == PAM_SUCCESS) - pam_close_session(pamh, PAM_SILENT); -#endif - eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno)); -} - -static void supervisor(char *exec, char **argv) -{ - FILE *fp; - char buf[2048]; - char cmd[2048]; - int count; - int failing; - int health_status; - int healthcheck_respawn; - int i; - int nkilled; - int sig_send; - pid_t health_pid; - pid_t wait_pid; - sigset_t old_signals; - sigset_t signals; - struct sigaction sa; - struct timespec ts; - time_t respawn_now= 0; - time_t first_spawn= 0; - - /* block all signals we do not handle */ - sigfillset(&signals); - sigdelset(&signals, SIGALRM); - sigdelset(&signals, SIGCHLD); - sigdelset(&signals, SIGTERM); - sigprocmask(SIG_SETMASK, &signals, &old_signals); - - /* install signal handler */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handle_signal; - sigaction(SIGALRM, &sa, NULL); - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - fp = fopen(pidfile, "w"); - if (!fp) - eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno)); - fprintf(fp, "%d\n", getpid()); - fclose(fp); - - if (svcname) - rc_service_daemon_set(svcname, exec, (const char * const *) argv, - pidfile, true); - - /* remove the controlling tty */ -#ifdef TIOCNOTTY - ioctl(tty_fd, TIOCNOTTY, 0); - close(tty_fd); -#endif - - /* - * Supervisor main loop - */ - if (healthcheckdelay) - alarm(healthcheckdelay); - else if (healthchecktimer) - alarm(healthchecktimer); - failing = 0; - while (!exiting) { - healthcheck_respawn = 0; - fifo_fd = open(fifopath, O_RDONLY); - if (fifo_fd > 0) { - memset(buf, 0, sizeof(buf)); - count = read(fifo_fd, buf, sizeof(buf) - 1); - close(fifo_fd); - if (count != -1) - buf[count] = 0; - if (count == 0) - continue; - if (verbose) - syslog(LOG_DEBUG, "Received %s from fifo", buf); - if (strncasecmp(buf, "sig", 3) == 0) { - if ((sscanf(buf, "%s %d", cmd, &sig_send) == 2) - && (sig_send >= 0 && sig_send < NSIG)) { - syslog(LOG_INFO, "Sending signal %d to %d", sig_send, - child_pid); - if (kill(child_pid, sig_send) == -1) - syslog(LOG_ERR, "Unable to send signal %d to %d", - sig_send, child_pid); - } - } - continue; - } - if (do_healthcheck) { - do_healthcheck = 0; - alarm(0); - if (verbose) - syslog(LOG_DEBUG, "running health check for %s", svcname); - health_pid = exec_command("healthcheck"); - health_status = rc_waitpid(health_pid); - if (WIFEXITED(health_status) && WEXITSTATUS(health_status) == 0) - alarm(healthchecktimer); - else { - syslog(LOG_WARNING, "health check for %s failed", svcname); - health_pid = exec_command("unhealthy"); - 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, "Unable to kill %d: %s", - child_pid, strerror(errno)); - else - healthcheck_respawn = 1; - } - } - 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; - } - wait_pid = waitpid(child_pid, &i, WNOHANG); - if (wait_pid == child_pid) { - if (WIFEXITED(i)) - syslog(LOG_WARNING, "%s, pid %d, exited with return code %d", - exec, child_pid, WEXITSTATUS(i)); - else if (WIFSIGNALED(i)) - syslog(LOG_WARNING, "%s, pid %d, terminated by signal %d", - exec, child_pid, WTERMSIG(i)); - } - if (wait_pid == child_pid || healthcheck_respawn) { - do_healthcheck = 0; - healthcheck_respawn = 0; - alarm(0); - respawn_now = time(NULL); - if (first_spawn == 0) - first_spawn = respawn_now; - if ((respawn_period > 0) - && (respawn_now - first_spawn > respawn_period)) { - respawn_count = 0; - first_spawn = 0; - } else - respawn_count++; - if (respawn_max > 0 && respawn_count > respawn_max) { - syslog(LOG_WARNING, "respawned \"%s\" too many times, exiting", - exec); - exiting = 1; - failing = 1; - continue; - } - ts.tv_sec = respawn_delay; - ts.tv_nsec = 0; - nanosleep(&ts, NULL); - if (exiting) - continue; - 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); - } - } - - if (svcname) { - rc_service_daemon_set(svcname, exec, (const char *const *)argv, - pidfile, false); - rc_service_value_set(svcname, "child_pid", NULL); - rc_service_mark(svcname, RC_SERVICE_STOPPED); - if (failing) - rc_service_mark(svcname, RC_SERVICE_FAILED); - } - if (pidfile && exists(pidfile)) - unlink(pidfile); - if (fifopath && exists(fifopath)) - unlink(fifopath); - exit(EXIT_SUCCESS); -} - -int main(int argc, char **argv) -{ - int opt; - char **c; - int x; - bool start = false; - bool stop = false; - bool reexec = false; - bool sendsig = false; - char *exec = NULL; - char *retry = NULL; - int sig = SIGTERM; - char *home = NULL; - int tid = 0; - pid_t pid; - char *tmp; - char *p; - char *token; - int i; - int n; - char *exec_file = NULL; - char *varbuf = NULL; - struct timespec ts; - struct passwd *pw; - struct group *gr; - FILE *fp; - mode_t numask = 022; - int child_argc = 0; - char **child_argv = NULL; - char *str = NULL; - char *cmdline = NULL; - - applet = basename_c(argv[0]); - atexit(cleanup); - svcname = getenv("RC_SVCNAME"); - if (!svcname) - eerrorx("%s: The RC_SVCNAME environment variable is not set", applet); - openlog(applet, LOG_PID, LOG_DAEMON); - - if (argc <= 1 || strcmp(argv[1], svcname)) - eerrorx("%s: the first argument is %s and must be %s", - applet, argv[1], svcname); - - if ((tmp = getenv("SSD_NICELEVEL"))) - if (sscanf(tmp, "%d", &nicelevel) != 1) - eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)", - applet, tmp); - if ((tmp = getenv("SSD_IONICELEVEL"))) { - int n = sscanf(tmp, "%d:%d", &ionicec, &ioniced); - if (n != 1 && n != 2) - eerror("%s: invalid ionice level `%s' (SSD_IONICELEVEL)", - applet, tmp); - if (ionicec == 0) - ioniced = 0; - else if (ionicec == 3) - ioniced = 7; - ionicec <<= 13; /* class shift */ - } - if ((tmp = getenv("SSD_OOM_SCORE_ADJ"))) - if (sscanf(tmp, "%d", &oom_score_adj) != 1) - eerror("%s: invalid oom_score_adj `%s' (SSD_OOM_SCORE_ADJ)", - applet, tmp); - - /* Get our user name and initial dir */ - p = getenv("USER"); - home = getenv("HOME"); - if (home == NULL || p == NULL) { - pw = getpwuid(getuid()); - if (pw != NULL) { - if (p == NULL) - setenv("USER", pw->pw_name, 1); - if (home == NULL) { - setenv("HOME", pw->pw_dir, 1); - home = pw->pw_dir; - } - } - } - - cmdline = make_cmdline(argv); - if (svcname) { - argc--; - argv++; - } - while ((opt = getopt_long(argc, argv, getoptstring, longopts, - (int *) 0)) != -1) - switch (opt) { - case 'a': /* --healthcheck-timer <time> */ - if (sscanf(optarg, "%d", &healthchecktimer) != 1 || healthchecktimer < 1) - eerrorx("%s: invalid health check timer %s", applet, optarg); - break; - - case 'A': /* --healthcheck-delay <time> */ - if (sscanf(optarg, "%d", &healthcheckdelay) != 1 || healthcheckdelay < 1) - eerrorx("%s: invalid health check delay %s", applet, optarg); - break; - - case LONGOPT_CAPABILITIES: -#ifdef HAVE_CAP - cap_iab = cap_iab_from_text(optarg); - if (cap_iab == NULL) - eerrorx("Could not parse iab: %s", strerror(errno)); -#else - eerrorx("Capabilities support not enabled"); -#endif - break; - - case LONGOPT_SECBITS: -#ifdef HAVE_CAP - if (*optarg == '\0') - eerrorx("Secbits are empty"); - - tmp = NULL; - secbits = strtoul(optarg, &tmp, 0); - if (*tmp != '\0') - eerrorx("Could not parse secbits: invalid char %c", *tmp); -#else - eerrorx("Capabilities support not enabled"); -#endif - break; - - case LONGOPT_NO_NEW_PRIVS: -#ifdef PR_SET_NO_NEW_PRIVS - no_new_privs = true; -#else - eerrorx("The No New Privs flag is only supported by Linux (since 3.5)"); -#endif - break; - - case 'D': /* --respawn-delay time */ - n = sscanf(optarg, "%d", &respawn_delay); - if (n != 1 || respawn_delay < 1) - eerrorx("Invalid respawn-delay value '%s'", optarg); - break; - - case 'I': /* --ionice */ - if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0) - eerrorx("%s: invalid ionice `%s'", - applet, optarg); - if (ionicec == 0) - ioniced = 0; - else if (ionicec == 3) - ioniced = 7; - ionicec <<= 13; /* class shift */ - break; - - case 'K': /* --stop */ - stop = true; - break; - - case 'N': /* --nice */ - if (sscanf(optarg, "%d", &nicelevel) != 1) - eerrorx("%s: invalid nice level `%s'", - applet, optarg); - break; - - case LONGOPT_OOM_SCORE_ADJ: /* --oom-score-adj */ - if (sscanf(optarg, "%d", &oom_score_adj) != 1) - eerrorx("%s: invalid oom-score-adj `%s'", - applet, optarg); - break; - - case 'P': /* --respawn-period time */ - n = sscanf(optarg, "%d", &respawn_period); - if (n != 1 || respawn_period < 1) - eerrorx("Invalid respawn-period value '%s'", optarg); - break; - - case 's': /* --signal */ - sig = parse_signal(applet, optarg); - sendsig = true; - break; - case 'S': /* --start */ - start = true; - break; - - case 'd': /* --chdir /new/dir */ - ch_dir = optarg; - break; - - case 'e': /* --env */ - putenv(optarg); - break; - - case 'g': /* --group <group>|<gid> */ - if (sscanf(optarg, "%d", &tid) != 1) - gr = getgrnam(optarg); - else - gr = getgrgid((gid_t)tid); - if (gr == NULL) - eerrorx("%s: group `%s' not found", - applet, optarg); - gid = gr->gr_gid; - break; - - case 'H': /* --healthcheck-timer <minutes> */ - if (sscanf(optarg, "%d", &healthchecktimer) != 1 || healthchecktimer < 1) - eerrorx("%s: invalid health check timer %s", applet, optarg); - break; - - case 'k': - if (parse_mode(&numask, optarg)) - eerrorx("%s: invalid mode `%s'", - applet, optarg); - break; - - case 'm': /* --respawn-max count */ - n = sscanf(optarg, "%d", &respawn_max); - if (n != 1 || respawn_max < 0) - eerrorx("Invalid respawn-max value '%s'", optarg); - break; - - case 'p': /* --pidfile <pid-file> */ - pidfile = optarg; - break; - - case 'R': /* --retry <schedule>|timeout */ - retry = optarg; - break; - case 'r': /* --chroot /new/root */ - ch_root = optarg; - break; - - case 'u': /* --user <username>|<uid> */ - { - char dummy[2]; - p = optarg; - tmp = strsep(&p, ":"); - changeuser = xstrdup(tmp); - if (sscanf(tmp, "%d%1s", &tid, dummy) != 1) - pw = getpwnam(tmp); - else - pw = getpwuid((uid_t)tid); - - if (pw == NULL) - eerrorx("%s: user `%s' not found", - applet, tmp); - uid = pw->pw_uid; - home = pw->pw_dir; - unsetenv("HOME"); - if (pw->pw_dir) - setenv("HOME", pw->pw_dir, 1); - unsetenv("USER"); - if (pw->pw_name) - setenv("USER", pw->pw_name, 1); - if (gid == 0) - gid = pw->pw_gid; - - if (p) { - tmp = strsep (&p, ":"); - if (sscanf(tmp, "%d%1s", &tid, dummy) != 1) - gr = getgrnam(tmp); - else - gr = getgrgid((gid_t) tid); - - if (gr == NULL) - eerrorx("%s: group `%s'" - " not found", - applet, tmp); - gid = gr->gr_gid; - } - } - break; - - case '1': /* --stdout /path/to/stdout.lgfile */ - redirect_stdout = optarg; - break; - - case '2': /* --stderr /path/to/stderr.logfile */ - redirect_stderr = optarg; - break; - case '3': /* --reexec */ - reexec = true; - break; - - case_RC_COMMON_GETOPT - } - - verbose = rc_yesno(getenv ("EINFO_VERBOSE")); - endpwent(); - argc -= optind; - argv += optind; - exec = *argv; - - /* Expand ~ */ - if (ch_dir && *ch_dir == '~') - ch_dir = expand_home(home, ch_dir); - if (ch_root && *ch_root == '~') - ch_root = expand_home(home, ch_root); - - umask(numask); - if (!pidfile) - xasprintf(&pidfile, "/var/run/supervise-%s.pid", svcname); - xasprintf(&fifopath, "%s/supervise-%s.ctl", RC_SVCDIR, svcname); - if (mkfifo(fifopath, 0600) == -1 && errno != EEXIST) - eerrorx("%s: unable to create control fifo: %s", - applet, strerror(errno)); - - if (reexec) { - str = rc_service_value_get(svcname, "argc"); - sscanf(str, "%d", &child_argc); - child_argv = xmalloc((child_argc + 1) * sizeof(char *)); - memset(child_argv, 0, (child_argc + 1) * sizeof(char *)); - for (x = 0; x < child_argc; x++) { - xasprintf(&varbuf, "argv_%d", x); - str = rc_service_value_get(svcname, varbuf); - child_argv[x] = str; - free(varbuf); - varbuf = NULL; - } - free(str); - str = rc_service_value_get(svcname, "child_pid"); - sscanf(str, "%d", &child_pid); - free(str); - exec = rc_service_value_get(svcname, "exec"); - pidfile = rc_service_value_get(svcname, "pidfile"); - retry = rc_service_value_get(svcname, "retry"); - if (retry) { - parse_schedule(applet, retry, sig); - rc_service_value_set(svcname, "retry", retry); - } else - parse_schedule(applet, NULL, sig); - - str = rc_service_value_get(svcname, "respawn_delay"); - sscanf(str, "%d", &respawn_delay); - str = rc_service_value_get(svcname, "respawn_max"); - sscanf(str, "%d", &respawn_max); - supervisor(exec, child_argv); - } else if (start) { - if (exec) { - if (*exec == '~') - exec = expand_home(home, exec); - - /* Validate that the binary exists if we are starting */ - if (*exec == '/' || *exec == '.') { - /* Full or relative path */ - if (ch_root) - xasprintf(&exec_file, "%s/%s", ch_root, exec); - else - xasprintf(&exec_file, "%s", exec); - } else { - /* Something in $PATH */ - p = tmp = xstrdup(getenv("PATH")); - exec_file = NULL; - while ((token = strsep(&p, ":"))) { - if (ch_root) - xasprintf(&exec_file, "%s/%s/%s", ch_root, token, exec); - else - xasprintf(&exec_file, "%s/%s", token, exec); - if (exec_file && exists(exec_file)) - break; - free(exec_file); - exec_file = NULL; - } - free(tmp); - } - if (!exists(exec_file)) { - eerror("%s: %s does not exist", applet, - exec_file ? exec_file : exec); - free(exec_file); - exit(EXIT_FAILURE); - } - } else - eerrorx("%s: nothing to start", applet); - - pid = get_pid(applet, pidfile); - if (pid != -1) - if (do_stop(applet, exec, (const char * const *)argv, pid, uid, - 0, false, true) > 0) - eerrorx("%s: %s is already running", applet, exec); - - if (respawn_period > 0 && respawn_delay * respawn_max > respawn_period) - ewarn("%s: Please increase the value of --respawn-period to more " - "than %d to avoid infinite respawning", applet, - respawn_delay * respawn_max); - - if (retry) { - parse_schedule(applet, retry, sig); - rc_service_value_set(svcname, "retry", retry); - } else - parse_schedule(applet, NULL, sig); - - einfov("Detaching to start `%s'", exec); - syslog(LOG_INFO, "Supervisor command line: %s", cmdline); - free(cmdline); - cmdline = NULL; - - /* Remove existing pidfile */ - if (pidfile) - unlink(pidfile); - - /* Make sure we can write a pid file */ - fp = fopen(pidfile, "w"); - if (!fp) - eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno)); - fclose(fp); - - rc_service_value_set(svcname, "pidfile", pidfile); - varbuf = NULL; - xasprintf(&varbuf, "%i", respawn_delay); - rc_service_value_set(svcname, "respawn_delay", varbuf); - free(varbuf); - xasprintf(&varbuf, "%i", respawn_max); - rc_service_value_set(svcname, "respawn_max", varbuf); - free(varbuf); - xasprintf(&varbuf, "%i", respawn_period); - rc_service_value_set(svcname, "respawn_period", varbuf); - free(varbuf); - child_pid = fork(); - if (child_pid == -1) - eerrorx("%s: fork: %s", applet, strerror(errno)); - if (child_pid != 0) - /* first parent process, do nothing. */ - exit(EXIT_SUCCESS); -#ifdef TIOCNOTTY - tty_fd = open("/dev/tty", O_RDWR); -#endif - devnull_fd = open("/dev/null", O_RDWR); - dup2(devnull_fd, STDIN_FILENO); - dup2(devnull_fd, STDOUT_FILENO); - dup2(devnull_fd, STDERR_FILENO); - child_pid = fork(); - if (child_pid == -1) - eerrorx("%s: fork: %s", applet, strerror(errno)); - else if (child_pid != 0) { - c = argv; - x = 0; - while (c && *c) { - varbuf = NULL; - xasprintf(&varbuf, "argv_%-d",x); - rc_service_value_set(svcname, varbuf, *c); - free(varbuf); - varbuf = NULL; - x++; - c++; - } - xasprintf(&varbuf, "%d", x); - rc_service_value_set(svcname, "argc", varbuf); - free(varbuf); - rc_service_value_set(svcname, "exec", exec); - supervisor(exec, argv); - } else - child_process(exec, argv); - } else if (stop) { - pid = get_pid(applet, pidfile); - if (pid != -1) { - i = kill(pid, SIGTERM); - if (i != 0) - /* We failed to send the signal */ - ewarn("Unable to shut down the supervisor"); - else { - /* wait for the supervisor to go down */ - while (kill(pid, 0) == 0) { - ts.tv_sec = 0; - ts.tv_nsec = 1; - nanosleep(&ts, NULL); - } - } - } - - /* Even if we have not actually killed anything, we should - * remove information about it as it may have unexpectedly - * crashed out. We should also return success as the end - * result would be the same. */ - if (pidfile && exists(pidfile)) - unlink(pidfile); - if (svcname) { - rc_service_daemon_set(svcname, exec, - (const char *const *)argv, - pidfile, false); - rc_service_mark(svcname, RC_SERVICE_STOPPED); - } - exit(EXIT_SUCCESS); - } else if (sendsig) { - fifo_fd = open(fifopath, O_WRONLY |O_NONBLOCK); - if (fifo_fd < 0) - eerrorx("%s: unable to open control fifo %s", applet, strerror(errno)); - xasprintf(&str, "sig %d", sig); - x = write(fifo_fd, str, strlen(str)); - if (x == -1) { - free(tmp); - eerrorx("%s: error writing to control fifo: %s", applet, - strerror(errno)); - } - free(tmp); - exit(EXIT_SUCCESS); - } -} |