diff options
author | William Hubbs <w.d.hubbs@gmail.com> | 2022-04-06 10:51:55 -0500 |
---|---|---|
committer | William Hubbs <w.d.hubbs@gmail.com> | 2022-04-06 10:51:55 -0500 |
commit | 391d12db48754861b5cecac92ee3321597ee02c1 (patch) | |
tree | b42fad5a31ca342de7b7ecf1fb78784194c1400c /src/rc/start-stop-daemon.c | |
parent | 0efc1b133e4182bd53cde78153bd8b5cc2e99448 (diff) |
migrate fully to meson build system
- drop old build system
- move shared include and source files to common directory
- drop "rc-" prefix from shared include and source files
- move executable-specific code to individual directories under src
- adjust top-level .gitignore file for new build system
This closes #489.
Diffstat (limited to 'src/rc/start-stop-daemon.c')
-rw-r--r-- | src/rc/start-stop-daemon.c | 1205 |
1 files changed, 0 insertions, 1205 deletions
diff --git a/src/rc/start-stop-daemon.c b/src/rc/start-stop-daemon.c deleted file mode 100644 index db97f3b3..00000000 --- a/src/rc/start-stop-daemon.c +++ /dev/null @@ -1,1205 +0,0 @@ -/* - start-stop-daemon - * Starts, stops, tests and signals daemons - * - * This is essentially a ground up re-write of Debians - * start-stop-daemon for cleaner code and to integrate into our RC - * system so we can monitor daemons a little. - */ - -/* - * Copyright (c) 2007-2015 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. - */ - -#define ONE_MS 1000000 - -#ifdef __linux__ -/* For extra SCHED_* defines. */ -# define _GNU_SOURCE -#endif - -#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 <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 <sched.h> - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "rc-pipes.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_SCHEDULER, - LONGOPT_SCHEDULER_PRIO, - LONGOPT_SECBITS, -}; - -const char *applet = NULL; -const char *extraopts = NULL; -const char getoptstring[] = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:3:4:" \ - getoptstring_COMMON; -const struct option longopts[] = { - { "capabilities", 1, NULL, LONGOPT_CAPABILITIES}, - { "secbits", 1, NULL, LONGOPT_SECBITS}, - { "no-new-privs", 0, NULL, LONGOPT_NO_NEW_PRIVS}, - { "ionice", 1, NULL, 'I'}, - { "stop", 0, NULL, 'K'}, - { "nicelevel", 1, NULL, 'N'}, - { "oom-score-adj",1, NULL, LONGOPT_OOM_SCORE_ADJ}, - { "retry", 1, NULL, 'R'}, - { "start", 0, NULL, 'S'}, - { "startas", 1, NULL, 'a'}, - { "background", 0, NULL, 'b'}, - { "chuid", 1, NULL, 'c'}, - { "chdir", 1, NULL, 'd'}, - { "env", 1, NULL, 'e'}, - { "umask", 1, NULL, 'k'}, - { "group", 1, NULL, 'g'}, - { "interpreted", 0, NULL, 'i'}, - { "make-pidfile", 0, NULL, 'm'}, - { "name", 1, NULL, 'n'}, - { "oknodo", 0, NULL, 'o'}, - { "pidfile", 1, NULL, 'p'}, - { "signal", 1, NULL, 's'}, - { "test", 0, NULL, 't'}, - { "user", 1, NULL, 'u'}, - { "chroot", 1, NULL, 'r'}, - { "wait", 1, NULL, 'w'}, - { "exec", 1, NULL, 'x'}, - { "stdout", 1, NULL, '1'}, - { "stderr", 1, NULL, '2'}, - { "stdout-logger",1, NULL, '3'}, - { "stderr-logger",1, NULL, '4'}, - { "progress", 0, NULL, 'P'}, - { "scheduler", 1, NULL, LONGOPT_SCHEDULER}, - { "scheduler-priority", 1, NULL, LONGOPT_SCHEDULER_PRIO}, - longopts_COMMON -}; -const char * const longopts_help[] = { - "Set the inheritable, ambient and bounding capabilities", - "Set the security-bits for the program", - "Set the No New Privs flag for the program", - "Set an ionice class:data when starting", - "Stop daemon", - "Set a nicelevel when starting", - "Set OOM score adjustment when starting", - "Retry schedule to use when stopping", - "Start daemon", - "deprecated, use --exec or --name", - "Force daemon to background", - "deprecated, use --user", - "Change the PWD", - "Set an environment string", - "Set the umask for the daemon", - "Change the process group", - "Match process name by interpreter", - "Create a pidfile", - "Match process name", - "deprecated", - "Match pid found in this file", - "Send a different signal", - "Test actions, don't do them", - "Change the process user", - "Chroot to this directory", - "Milliseconds to wait for daemon start", - "Binary to start/stop", - "Redirect stdout to file", - "Redirect stderr to file", - "Redirect stdout to process", - "Redirect stderr to process", - "Print dots each second while waiting", - "Set process scheduler", - "Set process scheduler priority", - longopts_help_COMMON -}; -const char *usagestring = NULL; - -static char **nav; - -static char *changeuser, *ch_root, *ch_dir; - -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); - free(nav); - free_schedulelist(); -} - -static void -handle_signal(int sig) -{ - int status; - int serrno = errno; - char *signame = NULL; - - switch (sig) { - case SIGINT: - if (!signame) - xasprintf(&signame, "SIGINT"); - /* FALLTHROUGH */ - case SIGTERM: - if (!signame) - xasprintf(&signame, "SIGTERM"); - /* FALLTHROUGH */ - case SIGQUIT: - if (!signame) - xasprintf(&signame, "SIGQUIT"); - eerrorx("%s: caught %s, aborting", applet, signame); - /* NOTREACHED */ - - case SIGCHLD: - for (;;) { - if (waitpid(-1, &status, WNOHANG) < 0) { - if (errno != ECHILD) - eerror("%s: waitpid: %s", - applet, strerror(errno)); - break; - } - } - break; - - default: - eerror("%s: caught unknown signal %d", applet, sig); - } - - /* free signame */ - free(signame); - - /* 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; -} - -int main(int argc, char **argv) -{ - int devnull_fd = -1; -#ifdef TIOCNOTTY - int tty_fd = -1; -#endif - -#ifdef HAVE_PAM - pam_handle_t *pamh = NULL; - int pamr; - const char *const *pamenv = NULL; -#endif - - int opt; - size_t size = 0; - bool start = false; - bool stop = false; - bool oknodo = false; - bool test = false; - char *exec = NULL; - char *startas = NULL; - char *name = NULL; - char *pidfile = NULL; - char *retry = NULL; - int sig = -1; - int nicelevel = INT_MIN, ionicec = -1, ioniced = 0; - int oom_score_adj = INT_MIN; - bool background = false; - bool makepidfile = false; - bool interpreted = false; - bool progress = false; - uid_t uid = 0; - gid_t gid = 0; - char *home = NULL; - int tid = 0; - char *redirect_stderr = NULL; - char *redirect_stdout = NULL; - char *stderr_process = NULL; - char *stdout_process = NULL; - int stdin_fd; - int stdout_fd; - int stderr_fd; - pid_t pid, spid; - RC_PIDLIST *pids; - int i; - char *svcname = getenv("RC_SVCNAME"); - RC_STRINGLIST *env_list; - RC_STRING *env; - char *tmp, *newpath, *np; - char *p; - char *token; - char *exec_file = NULL; - struct passwd *pw; - struct group *gr; - char *line = NULL; - FILE *fp; - size_t len; - mode_t numask = 022; - char **margv; - unsigned int start_wait = 0; - const char *scheduler = NULL; - int sched_prio = -1; -#ifdef HAVE_CAP - cap_iab_t cap_iab = NULL; - unsigned secbits = 0; -#endif -#ifdef PR_SET_NO_NEW_PRIVS - bool no_new_privs = false; -#endif - - applet = basename_c(argv[0]); - atexit(cleanup); - - signal_setup(SIGINT, handle_signal); - signal_setup(SIGQUIT, handle_signal); - signal_setup(SIGTERM, handle_signal); - - 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; - } - } - } - - while ((opt = getopt_long(argc, argv, getoptstring, longopts, - (int *) 0)) != -1) - switch (opt) { - 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 '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': /* --progress */ - progress = true; - break; - - case 'R': /* --retry <schedule>|<timeout> */ - retry = optarg; - break; - - case 'S': /* --start */ - start = true; - break; - - case 'b': /* --background */ - background = true; - break; - - case 'c': /* --chuid <username>|<uid> */ - /* DEPRECATED */ - ewarn("WARNING: -c/--chuid is deprecated and will be removed in the future, please use -u/--user instead"); - /* falls through */ - 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 '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 'i': /* --interpreted */ - interpreted = true; - break; - - case 'k': - if (parse_mode(&numask, optarg)) - eerrorx("%s: invalid mode `%s'", - applet, optarg); - break; - - case 'm': /* --make-pidfile */ - makepidfile = true; - break; - - case 'n': /* --name <process-name> */ - name = optarg; - break; - - case 'o': /* --oknodo */ - /* DEPRECATED */ - ewarn("WARNING: -o/--oknodo is deprecated and will be removed in the future"); - oknodo = true; - break; - - case 'p': /* --pidfile <pid-file> */ - pidfile = optarg; - break; - - case 's': /* --signal <signal> */ - sig = parse_signal(applet, optarg); - break; - - case 't': /* --test */ - test = true; - break; - - case 'r': /* --chroot /new/root */ - ch_root = optarg; - break; - - case 'a': /* --startas <name> */ - /* DEPRECATED */ - ewarn("WARNING: -a/--startas is deprecated and will be removed in the future, please use -x/--exec or -n/--name instead"); - startas = optarg; - break; - case 'w': - if (sscanf(optarg, "%u", &start_wait) != 1) - eerrorx("%s: `%s' not a number", - applet, optarg); - break; - case 'x': /* --exec <executable> */ - exec = optarg; - 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': /* --stdout-logger "command to run for stdout logging" */ - stdout_process = optarg; - break; - - case '4': /* --stderr-logger "command to run for stderr logging" */ - stderr_process = optarg; - break; - - case LONGOPT_SCHEDULER: /* --scheduler "Process scheduler policy" */ - scheduler = optarg; - break; - - case LONGOPT_SCHEDULER_PRIO: /* --scheduler-priority "Process scheduler priority" */ - sscanf(optarg, "%d", &sched_prio); - break; - - case_RC_COMMON_GETOPT - } - - endpwent(); - argc -= optind; - argv += optind; - - /* Allow start-stop-daemon --signal HUP --exec /usr/sbin/dnsmasq - * instead of forcing --stop --oknodo as well */ - if (!start && - !stop && - sig != SIGINT && - sig != SIGTERM && - sig != SIGQUIT && - sig != SIGKILL) - oknodo = true; - - if (!exec) - exec = startas; - else if (!name) - name = startas; - - if (!exec) { - exec = *argv; - if (!exec) - exec = name; - if (name && start) - *argv = name; - } else if (name) { - *--argv = name; - ++argc; - } else if (exec) { - *--argv = exec; - ++argc; - }; - - if (stop || sig != -1) { - if (sig == -1) - sig = SIGTERM; - if (!*argv && !pidfile && !name && !uid) - eerrorx("%s: --stop needs --exec, --pidfile," - " --name or --user", applet); - if (background) - eerrorx("%s: --background is only relevant with" - " --start", applet); - if (makepidfile) - eerrorx("%s: --make-pidfile is only relevant with" - " --start", applet); - if (redirect_stdout || redirect_stderr) - eerrorx("%s: --stdout and --stderr are only relevant" - " with --start", applet); - if (stdout_process || stderr_process) - eerrorx("%s: --stdout-logger and --stderr-logger are only relevant" - " with --start", applet); - if (start_wait) - ewarn("using --wait with --stop has no effect," - " use --retry instead"); - } else { - if (!exec) - eerrorx("%s: nothing to start", applet); - if (makepidfile && !pidfile) - eerrorx("%s: --make-pidfile is only relevant with" - " --pidfile", applet); - if ((redirect_stdout || redirect_stderr) && !background) - eerrorx("%s: --stdout and --stderr are only relevant" - " with --background", applet); - if ((stdout_process || stderr_process) && !background) - eerrorx("%s: --stdout-logger and --stderr-logger are only relevant" - " with --background", applet); - if (redirect_stdout && stdout_process) - eerrorx("%s: do not use --stdout and --stdout-logger together", - applet); - if (redirect_stderr && stderr_process) - eerrorx("%s: do not use --stderr and --stderr-logger together", - applet); - } - - /* 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); - 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 (start && !exists(exec_file)) { - eerror("%s: %s does not exist", applet, - exec_file ? exec_file : exec); - free(exec_file); - exit(EXIT_FAILURE); - } - if (start && retry) - ewarn("using --retry with --start has no effect," - " use --wait instead"); - - /* If we don't have a pidfile we should check if it's interpreted - * or not. If it we, we need to pass the interpreter through - * to our daemon calls to find it correctly. */ - if (interpreted && !pidfile) { - fp = fopen(exec_file, "r"); - if (fp) { - line = NULL; - if (getline(&line, &size, fp) == -1) - eerrorx("%s: %s", applet, strerror(errno)); - p = line; - fclose(fp); - if (p != NULL && line[0] == '#' && line[1] == '!') { - p = line + 2; - /* Strip leading spaces */ - while (*p == ' ' || *p == '\t') - p++; - /* Remove the trailing newline */ - len = strlen(p) - 1; - if (p[len] == '\n') - p[len] = '\0'; - token = strsep(&p, " "); - free(exec_file); - xasprintf(&exec_file, "%s", token); - opt = 0; - for (nav = argv; *nav; nav++) - opt++; - nav = xmalloc(sizeof(char *) * (opt + 3)); - nav[0] = exec_file; - len = 1; - if (p) - nav[len++] = p; - for (i = 0; i < opt; i++) - nav[i + len] = argv[i]; - nav[i + len] = NULL; - } - } - } - margv = nav ? nav : argv; - - if (stop || sig != -1) { - if (sig == -1) - sig = SIGTERM; - if (!stop) - oknodo = true; - if (retry) - parse_schedule(applet, retry, sig); - else if (test || oknodo) - parse_schedule(applet, "0", sig); - else - parse_schedule(applet, NULL, sig); - if (pidfile) { - pid = get_pid(applet, pidfile); - if (pid == -1 && errno != ENOENT) - exit(EXIT_FAILURE); - } else { - pid = 0; - } - i = run_stop_schedule(applet, exec, (const char *const *)margv, - pid, uid, test, progress, false); - - if (i < 0) - /* We failed to stop something */ - exit(EXIT_FAILURE); - if (test || oknodo) - return i > 0 ? EXIT_SUCCESS : EXIT_FAILURE; - - /* 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); - exit(EXIT_SUCCESS); - } - - if (pidfile) - pid = get_pid(applet, pidfile); - else - pid = 0; - - if (pid) - pids = rc_find_pids(NULL, NULL, 0, pid); - else - pids = rc_find_pids(exec, (const char * const *) argv, uid, 0); - if (pids) - eerrorx("%s: %s is already running", applet, exec); - - free(pids); - if (test) { - if (rc_yesno(getenv("EINFO_QUIET"))) - exit (EXIT_SUCCESS); - - einfon("Would start"); - while (argc-- > 0) - printf(" %s", *argv++); - printf("\n"); - eindent(); - if (uid != 0) - einfo("as user id %d", uid); - if (gid != 0) - einfo("as group id %d", gid); - if (ch_root) - einfo("in root `%s'", ch_root); - if (ch_dir) - einfo("in dir `%s'", ch_dir); - if (nicelevel != 0) - einfo("with a priority of %d", nicelevel); - if (name) - einfo ("with a process name of %s", name); - eoutdent(); - exit(EXIT_SUCCESS); - } - - ebeginv("Detaching to start `%s'", exec); - eindentv(); - - /* Remove existing pidfile */ - if (pidfile) - unlink(pidfile); - - if (background) - signal_setup(SIGCHLD, handle_signal); - - if ((pid = fork()) == -1) - eerrorx("%s: fork: %s", applet, strerror(errno)); - - /* Child process - lets go! */ - if (pid == 0) { - pid_t mypid = getpid(); - umask(numask); - -#ifdef TIOCNOTTY - tty_fd = open("/dev/tty", O_RDWR); -#endif - - devnull_fd = open("/dev/null", O_RDWR); - - if (nicelevel != INT_MIN) { - if (setpriority(PRIO_PROCESS, mypid, nicelevel) == -1) - eerrorx("%s: setpriority %d: %s", - applet, nicelevel, - strerror(errno)); - } - - if (ionicec != -1 && - ioprio_set(1, mypid, 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)); - - if (makepidfile && pidfile) { - fp = fopen(pidfile, "w"); - if (!fp) - eerrorx("%s: fopen `%s': %s", applet, pidfile, - strerror(errno)); - fprintf(fp, "%d\n", mypid); - fclose(fp); - } - -#ifdef HAVE_PAM - if (changeuser != NULL) { - pamr = pam_start("start-stop-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 - - -#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 | S_IRGRP | S_IWGRP)) == -1) - eerrorx("%s: unable to open the logfile" - " for stdout `%s': %s", - applet, redirect_stdout, strerror(errno)); - }else if (stdout_process) { - stdout_fd = rc_pipe_command(stdout_process); - if (stdout_fd == -1) - eerrorx("%s: unable to open the logging process" - " for stdout `%s': %s", - applet, stdout_process, strerror(errno)); - } - if (redirect_stderr) { - if ((stderr_fd = open(redirect_stderr, - O_WRONLY | O_CREAT | O_APPEND, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) - eerrorx("%s: unable to open the logfile" - " for stderr `%s': %s", - applet, redirect_stderr, strerror(errno)); - }else if (stderr_process) { - stderr_fd = rc_pipe_command(stderr_process); - if (stderr_fd == -1) - eerrorx("%s: unable to open the logging process" - " for stderr `%s': %s", - applet, stderr_process, strerror(errno)); - } - - if (background) - dup2(stdin_fd, STDIN_FILENO); - if (background || redirect_stdout || stdout_process - || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stdout_fd, STDOUT_FILENO); - if (background || redirect_stderr || stderr_process - || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stderr_fd, STDERR_FILENO); - - for (i = getdtablesize() - 1; i >= 3; --i) - close(i); - - if (scheduler != NULL) { - int scheduler_index; - struct sched_param sched = {.sched_priority = sched_prio}; - if (strcmp(scheduler, "fifo") == 0) - scheduler_index = SCHED_FIFO; - else if (strcmp(scheduler, "rr") == 0) - scheduler_index = SCHED_RR; - else if (strcmp(scheduler, "other") == 0) - scheduler_index = SCHED_OTHER; -#ifdef SCHED_BATCH - else if (strcmp(scheduler, "batch") == 0) - scheduler_index = SCHED_BATCH; -#endif -#ifdef SCHED_IDLE - else if (strcmp(scheduler, "idle") == 0) - scheduler_index = SCHED_IDLE; -#endif - else if (sscanf(scheduler, "%d", &scheduler_index) != 1) - eerrorx("Unknown scheduler: %s", scheduler); - - if (sched_prio == -1) - sched.sched_priority = sched_get_priority_min(scheduler_index); - - if (sched_setscheduler(mypid, scheduler_index, &sched)) - eerrorx("Failed to set scheduler: %s", strerror(errno)); - } else if (sched_prio != -1) { - const struct sched_param sched = {.sched_priority = sched_prio}; - if (sched_setparam(mypid, &sched)) - eerrorx("Failed to set scheduler parameters: %s", strerror(errno)); - } - - setsid(); - 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)); - } - - /* Parent process */ - if (!background) { - /* As we're not backgrounding the process, wait for our pid - * to return */ - i = 0; - spid = pid; - - do { - pid = waitpid(spid, &i, 0); - if (pid < 1) { - eerror("waitpid %d: %s", - spid, strerror(errno)); - return -1; - } - } while (!WIFEXITED(i) && !WIFSIGNALED(i)); - if (!WIFEXITED(i) || WEXITSTATUS(i) != 0) { - eerror("%s: failed to start `%s'", applet, exec); - exit(EXIT_FAILURE); - } - pid = spid; - } - - /* Wait a little bit and check that process is still running - We do this as some badly written daemons fork and then barf */ - if (start_wait == 0 && - ((p = getenv("SSD_STARTWAIT")) || - (p = rc_conf_value("rc_start_wait")))) - { - if (sscanf(p, "%u", &start_wait) != 1) - start_wait = 0; - } - - if (start_wait > 0) { - struct timespec ts; - bool alive = false; - - ts.tv_sec = start_wait / 1000; - ts.tv_nsec = (start_wait % 1000) * ONE_MS; - if (nanosleep(&ts, NULL) == -1) { - if (errno != EINTR) { - eerror("%s: nanosleep: %s", - applet, strerror(errno)); - return 0; - } - } - if (background) { - if (kill(pid, 0) == 0) - alive = true; - } else { - if (pidfile) { - pid = get_pid(applet, pidfile); - if (pid == -1) { - eerrorx("%s: did not " - "create a valid" - " pid in `%s'", - applet, pidfile); - } - } else - pid = 0; - if (do_stop(applet, exec, (const char *const *)margv, - pid, uid, 0, test, false) > 0) - alive = true; - } - - if (!alive) - eerrorx("%s: %s died", applet, exec); - } - - if (svcname) - rc_service_daemon_set(svcname, exec, - (const char *const *)margv, pidfile, true); - - exit(EXIT_SUCCESS); - /* NOTREACHED */ -} |