aboutsummaryrefslogtreecommitdiff
path: root/src/rc/start-stop-daemon.c
diff options
context:
space:
mode:
authorWilliam Hubbs <w.d.hubbs@gmail.com>2022-04-06 10:51:55 -0500
committerWilliam Hubbs <w.d.hubbs@gmail.com>2022-04-06 10:51:55 -0500
commit391d12db48754861b5cecac92ee3321597ee02c1 (patch)
treeb42fad5a31ca342de7b7ecf1fb78784194c1400c /src/rc/start-stop-daemon.c
parent0efc1b133e4182bd53cde78153bd8b5cc2e99448 (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.c1205
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 */
-}