aboutsummaryrefslogtreecommitdiff
path: root/src/rc/rc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rc/rc.c')
-rw-r--r--src/rc/rc.c1118
1 files changed, 0 insertions, 1118 deletions
diff --git a/src/rc/rc.c b/src/rc/rc.c
deleted file mode 100644
index 53c75bb6..00000000
--- a/src/rc/rc.c
+++ /dev/null
@@ -1,1118 +0,0 @@
-/*
- * rc.c
- * rc - manager for init scripts which control the startup, shutdown
- * and the running of daemons.
- *
- * Also a multicall binary for various commands that can be used in shell
- * scripts to query service state, mark service state and provide the
- * einfo family of informational functions.
- */
-
-/*
- * 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.
- */
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/utsname.h>
-#include <sys/wait.h>
-
-#include <errno.h>
-#include <dirent.h>
-#include <ctype.h>
-#include <getopt.h>
-#include <libgen.h>
-#include <limits.h>
-#include <pwd.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <strings.h>
-#include <termios.h>
-#include <unistd.h>
-
-#include "einfo.h"
-#include "queue.h"
-#include "rc.h"
-#include "rc-logger.h"
-#include "rc-misc.h"
-#include "rc-plugin.h"
-
-#include "version.h"
-#include "_usage.h"
-
-const char *extraopts = NULL;
-const char getoptstring[] = "a:no:s:S" getoptstring_COMMON;
-const struct option longopts[] = {
- { "no-stop", 0, NULL, 'n' },
- { "override", 1, NULL, 'o' },
- { "service", 1, NULL, 's' },
- { "sys", 0, NULL, 'S' },
- longopts_COMMON
-};
-const char * const longopts_help[] = {
- "do not stop any services",
- "override the next runlevel to change into\n",
- "when leaving single user or boot runlevels",
- "runs the service specified with the rest\nof the arguments",
- "output the RC system type, if any",
- longopts_help_COMMON
-};
-const char *usagestring = "" \
- "Usage: openrc [options] [<runlevel>]";
-
-#define INITSH RC_LIBEXECDIR "/sh/init.sh"
-#define INITEARLYSH RC_LIBEXECDIR "/sh/init-early.sh"
-
-#define INTERACTIVE RC_SVCDIR "/interactive"
-
-#define DEVBOOT "/dev/.rcboot"
-
-const char *applet = NULL;
-static RC_STRINGLIST *main_hotplugged_services;
-static RC_STRINGLIST *main_stop_services;
-static RC_STRINGLIST *main_start_services;
-static RC_STRINGLIST *main_types_nw;
-static RC_STRINGLIST *main_types_nwua;
-static RC_DEPTREE *main_deptree;
-static char *runlevel;
-static RC_HOOK hook_out;
-
-struct termios *termios_orig = NULL;
-
-RC_PIDLIST service_pids;
-
-static void
-clean_failed(void)
-{
- DIR *dp;
- struct dirent *d;
- char *path;
-
- /* Clean the failed services state dir now */
- if ((dp = opendir(RC_SVCDIR "/failed"))) {
- while ((d = readdir(dp))) {
- if (d->d_name[0] == '.' &&
- (d->d_name[1] == '\0' ||
- (d->d_name[1] == '.' && d->d_name[2] == '\0')))
- continue;
-
- xasprintf(&path, RC_SVCDIR "/failed/%s", d->d_name);
- if (unlink(path))
- eerror("%s: unlink `%s': %s",
- applet, path, strerror(errno));
- free(path);
- }
- closedir(dp);
- }
-}
-
-static void
-cleanup(void)
-{
- RC_PID *p1 = LIST_FIRST(&service_pids);
- RC_PID *p2;
-
- if (!rc_in_logger && !rc_in_plugin &&
- applet && (strcmp(applet, "rc") == 0 || strcmp(applet, "openrc") == 0))
- {
- if (hook_out)
- rc_plugin_run(hook_out, runlevel);
-
- rc_plugin_unload();
-
- if (termios_orig) {
- tcsetattr(STDIN_FILENO, TCSANOW, termios_orig);
- free(termios_orig);
- }
-
- /* Clean runlevel start, stop markers */
- rmdir(RC_STARTING);
- rmdir(RC_STOPPING);
- clean_failed();
- rc_logger_close();
- }
-
- while (p1) {
- p2 = LIST_NEXT(p1, entries);
- free(p1);
- p1 = p2;
- }
-
- rc_stringlist_free(main_hotplugged_services);
- rc_stringlist_free(main_stop_services);
- rc_stringlist_free(main_start_services);
- rc_stringlist_free(main_types_nw);
- rc_stringlist_free(main_types_nwua);
- rc_deptree_free(main_deptree);
- free(runlevel);
-}
-
-static char
-read_key(bool block)
-{
- struct termios termios;
- char c = 0;
- int fd = STDIN_FILENO;
-
- if (!isatty(fd))
- return false;
-
- /* Now save our terminal settings. We need to restore them at exit as
- we will be changing it for non-blocking reads for Interactive */
- if (!termios_orig) {
- termios_orig = xmalloc(sizeof(*termios_orig));
- tcgetattr(fd, termios_orig);
- }
-
- tcgetattr(fd, &termios);
- termios.c_lflag &= ~(ICANON | ECHO);
- if (block)
- termios.c_cc[VMIN] = 1;
- else {
- termios.c_cc[VMIN] = 0;
- termios.c_cc[VTIME] = 0;
- }
- tcsetattr(fd, TCSANOW, &termios);
- if (read(fd, &c, 1) == -1)
- eerror("read: %s", strerror(errno));
- tcsetattr(fd, TCSANOW, termios_orig);
- return c;
-}
-
-static bool
-want_interactive(void)
-{
- char c;
- static bool gotinteractive;
- static bool interactive;
-
- if (rc_yesno(getenv("EINFO_QUIET")))
- return false;
- if (!gotinteractive) {
- gotinteractive = true;
- interactive = rc_conf_yesno("rc_interactive");
- }
- if (!interactive)
- return false;
- c = read_key(false);
- return (c == 'I' || c == 'i') ? true : false;
-}
-
-static void
-mark_interactive(void)
-{
- FILE *fp = fopen(INTERACTIVE, "w");
- if (fp)
- fclose(fp);
-}
-
-static void
-run_program(const char *prog)
-{
- struct sigaction sa;
- sigset_t full;
- sigset_t old;
- pid_t pid;
-
- /* 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 == -1)
- eerrorx("%s: fork: %s", applet, strerror(errno));
- 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);
-
- if (termios_orig)
- tcsetattr(STDIN_FILENO, TCSANOW, termios_orig);
-
- execl(prog, prog, (char *)NULL);
- eerror("%s: unable to exec `%s': %s", applet, prog,
- strerror(errno));
- _exit(EXIT_FAILURE);
- }
-
- /* Unmask signals and wait for child */
- sigprocmask(SIG_SETMASK, &old, NULL);
- if (rc_waitpid(pid) == -1)
- eerrorx("%s: failed to exec `%s'", applet, prog);
-}
-
-static void
-open_shell(void)
-{
- const char *shell;
- struct passwd *pw;
-
-#ifdef __linux__
- const char *sys = rc_sys();
-
- /* VSERVER systems cannot really drop to shells */
- if (sys && strcmp(sys, RC_SYS_VSERVER) == 0)
- {
- execlp("halt", "halt", "-f", (char *) NULL);
- eerrorx("%s: unable to exec `halt -f': %s",
- applet, strerror(errno));
- }
-#endif
-
- shell = rc_conf_value("rc_shell");
- /* No shell set, so obey env, then passwd, then default to /bin/sh */
- if (shell == NULL) {
- shell = getenv("SHELL");
- if (shell == NULL) {
- pw = getpwuid(getuid());
- if (pw)
- shell = pw->pw_shell;
- if (shell == NULL)
- shell = "/bin/sh";
- }
- }
- run_program(shell);
-}
-
-static bool
-set_krunlevel(const char *level)
-{
- FILE *fp;
-
- if (!level ||
- strcmp(level, getenv ("RC_BOOTLEVEL")) == 0 ||
- strcmp(level, RC_LEVEL_SINGLE) == 0 ||
- strcmp(level, RC_LEVEL_SYSINIT) == 0)
- {
- if (exists(RC_KRUNLEVEL) &&
- unlink(RC_KRUNLEVEL) != 0)
- eerror("unlink `%s': %s", RC_KRUNLEVEL,
- strerror(errno));
- return false;
- }
-
- if (!(fp = fopen(RC_KRUNLEVEL, "w"))) {
- eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno));
- return false;
- }
-
- fprintf(fp, "%s", level);
- fclose(fp);
- return true;
-}
-
-static char *get_krunlevel(void)
-{
- char *buffer = NULL;
- FILE *fp;
- size_t i = 0;
-
- if (!exists(RC_KRUNLEVEL))
- return NULL;
- if (!(fp = fopen(RC_KRUNLEVEL, "r"))) {
- eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno));
- return NULL;
- }
-
- if (getline(&buffer, &i, fp) != -1) {
- i = strlen(buffer);
- if (buffer[i - 1] == '\n')
- buffer[i - 1] = 0;
- }
- fclose(fp);
- return buffer;
-}
-
-static void
-add_pid(pid_t pid)
-{
- RC_PID *p = xmalloc(sizeof(*p));
- p->pid = pid;
- LIST_INSERT_HEAD(&service_pids, p, entries);
-}
-
-static void
-remove_pid(pid_t pid)
-{
- RC_PID *p;
-
- LIST_FOREACH(p, &service_pids, entries)
- if (p->pid == pid) {
- LIST_REMOVE(p, entries);
- free(p);
- return;
- }
-}
-
-static void
-wait_for_services(void)
-{
- for (;;) {
- while (waitpid(0, 0, 0) != -1)
- ;
- if (errno != EINTR)
- break;
- }
-}
-
-static void
-handle_signal(int sig)
-{
- int serrno = errno;
- char *signame = NULL;
- pid_t pid;
- RC_PID *pi;
- int status = 0;
- struct winsize ws;
- sigset_t sset;
-
- switch (sig) {
- case SIGCHLD:
- do {
- pid = waitpid(-1, &status, WNOHANG);
- if (pid < 0) {
- if (errno != ECHILD)
- eerror("waitpid: %s", strerror(errno));
- return;
- }
- } while (!WIFEXITED(status) && !WIFSIGNALED(status));
-
- /* Remove that pid from our list */
- if (pid > 0)
- remove_pid(pid);
- break;
-
- case SIGWINCH:
- if (rc_logger_tty >= 0) {
- ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
- ioctl(rc_logger_tty, TIOCSWINSZ, &ws);
- }
- break;
-
- 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 SIGUSR1:
- eerror("rc: Aborting!");
-
- /* Block child signals */
- sigemptyset(&sset);
- sigaddset(&sset, SIGCHLD);
- sigprocmask(SIG_BLOCK, &sset, NULL);
-
- /* Kill any running services we have started */
- LIST_FOREACH(pi, &service_pids, entries)
- kill(pi->pid, SIGTERM);
-
- /* Notify plugins we are aborting */
- rc_plugin_run(RC_HOOK_ABORT, NULL);
-
- exit(EXIT_FAILURE);
- /* NOTREACHED */
-
- default:
- eerror("%s: caught unknown signal %d", applet, sig);
- }
-
- /* Restore errno */
- errno = serrno;
-}
-
-static void
-do_sysinit()
-{
- struct utsname uts;
- const char *sys;
-
- /* exec init-early.sh if it exists
- * This should just setup the console to use the correct
- * font. Maybe it should setup the keyboard too? */
- if (exists(INITEARLYSH))
- run_program(INITEARLYSH);
-
- uname(&uts);
- printf("\n %sOpenRC %s" VERSION "%s is starting up %s",
- ecolor(ECOLOR_GOOD), ecolor(ECOLOR_HILITE),
- ecolor(ECOLOR_NORMAL), ecolor(ECOLOR_BRACKET));
-#ifdef BRANDING
- printf(BRANDING " (%s)", uts.machine);
-#else
- printf("%s %s (%s)",
- uts.sysname,
- uts.release,
- uts.machine);
-#endif
-
- if ((sys = rc_sys()))
- printf(" [%s]", sys);
-
- printf("%s\n\n", ecolor(ECOLOR_NORMAL));
-
- if (!rc_yesno(getenv ("EINFO_QUIET")) &&
- rc_conf_yesno("rc_interactive"))
- printf("Press %sI%s to enter interactive boot mode\n\n",
- ecolor(ECOLOR_GOOD), ecolor(ECOLOR_NORMAL));
-
- setenv("RC_RUNLEVEL", RC_LEVEL_SYSINIT, 1);
- run_program(INITSH);
-
- /* init may have mounted /proc so we can now detect or real
- * sys */
- if ((sys = rc_sys()))
- setenv("RC_SYS", sys, 1);
- /* force an update of the dependency tree */
- if ((main_deptree = _rc_deptree_load(1, NULL)) == NULL)
- eerrorx("failed to load deptree");
-}
-
-static bool
-runlevel_config(const char *service, const char *level)
-{
- char *init = rc_service_resolve(service);
- char *conf, *dir;
- bool retval;
-
- dir = dirname(init);
- dir = dirname(init);
- xasprintf(&conf, "%s/conf.d/%s.%s", dir, service, level);
- retval = exists(conf);
- free(conf);
- free(init);
- return retval;
-}
-
-static void
-do_stop_services(RC_STRINGLIST *types_nw, RC_STRINGLIST *start_services,
- const RC_STRINGLIST *stop_services, const RC_DEPTREE *deptree,
- const char *newlevel, bool parallel, bool going_down)
-{
- pid_t pid;
- RC_STRING *service, *svc1, *svc2;
- RC_STRINGLIST *deporder, *tmplist, *kwords;
- RC_SERVICE state;
- RC_STRINGLIST *nostop;
- bool crashed, nstop;
-
- if (!types_nw) {
- types_nw = rc_stringlist_new();
- rc_stringlist_add(types_nw, "needsme");
- rc_stringlist_add(types_nw, "wantsme");
- }
-
- crashed = rc_conf_yesno("rc_crashed_stop");
-
- nostop = rc_stringlist_split(rc_conf_value("rc_nostop"), " ");
- TAILQ_FOREACH_REVERSE(service, stop_services, rc_stringlist, entries)
- {
- state = rc_service_state(service->value);
- if (state & RC_SERVICE_STOPPED || state & RC_SERVICE_FAILED)
- continue;
-
- /* Sometimes we don't ever want to stop a service. */
- if (rc_stringlist_find(nostop, service->value)) {
- rc_service_mark(service->value, RC_SERVICE_FAILED);
- continue;
- }
- kwords = rc_deptree_depend(deptree, service->value, "keyword");
- if (rc_stringlist_find(kwords, "-stop") ||
- rc_stringlist_find(kwords, "nostop") ||
- (going_down &&
- (rc_stringlist_find(kwords, "-shutdown") ||
- rc_stringlist_find(kwords, "noshutdown"))))
- nstop = true;
- else
- nstop = false;
- rc_stringlist_free(kwords);
- if (nstop) {
- rc_service_mark(service->value, RC_SERVICE_FAILED);
- continue;
- }
-
- /* If the service has crashed, skip futher checks and just stop
- it */
- if (crashed &&
- rc_service_daemons_crashed(service->value))
- goto stop;
-
- /* If we're in the start list then don't bother stopping us */
- svc1 = rc_stringlist_find(start_services, service->value);
- if (svc1) {
- if (newlevel && strcmp(runlevel, newlevel) != 0) {
- /* So we're in the start list. But we should
- * be stopped if we have a runlevel
- * configuration file for either the current
- * or next so we use the correct one. */
- if (!runlevel_config(service->value,runlevel) &&
- !runlevel_config(service->value,newlevel))
- continue;
- }
- else
- continue;
- }
-
- /* We got this far. Last check is to see if any any service
- * that going to be started depends on us */
- if (!svc1) {
- tmplist = rc_stringlist_new();
- rc_stringlist_add(tmplist, service->value);
- deporder = rc_deptree_depends(deptree, types_nw,
- tmplist, newlevel ? newlevel : runlevel,
- RC_DEP_STRICT | RC_DEP_TRACE);
- rc_stringlist_free(tmplist);
- svc2 = NULL;
- TAILQ_FOREACH(svc1, deporder, entries) {
- svc2 = rc_stringlist_find(start_services,
- svc1->value);
- if (svc2)
- break;
- }
- rc_stringlist_free(deporder);
-
- if (svc2)
- continue;
- }
-
-stop:
- /* After all that we can finally stop the blighter! */
- pid = service_stop(service->value);
- if (pid > 0) {
- add_pid(pid);
- if (!parallel) {
- rc_waitpid(pid);
- remove_pid(pid);
- }
- }
- }
-
- rc_stringlist_free(nostop);
-}
-
-static void
-do_start_services(const RC_STRINGLIST *start_services, bool parallel)
-{
- RC_STRING *service;
- pid_t pid;
- bool interactive = false;
- RC_SERVICE state;
- bool crashed = false;
-
- if (!rc_yesno(getenv("EINFO_QUIET")))
- interactive = exists(INTERACTIVE);
- errno = 0;
- crashed = rc_conf_yesno("rc_crashed_start");
- if (errno == ENOENT)
- crashed = true;
-
- TAILQ_FOREACH(service, start_services, entries) {
- state = rc_service_state(service->value);
- if (state & RC_SERVICE_FAILED)
- continue;
- if (!(state & RC_SERVICE_STOPPED)) {
- if (crashed &&
- rc_service_daemons_crashed(service->value))
- rc_service_mark(service->value,
- RC_SERVICE_STOPPED);
- else
- continue;
- }
- if (!interactive)
- interactive = want_interactive();
-
- if (interactive) {
- interactive_retry:
- printf("\n");
- einfo("About to start the service %s",
- service->value);
- eindent();
- einfo("1) Start the service\t\t2) Skip the service");
- einfo("3) Continue boot process\t\t4) Exit to shell");
- eoutdent();
- interactive_option:
- switch (read_key(true)) {
- case '1': break;
- case '2': continue;
- case '3': interactive = false; break;
- case '4': open_shell(); goto interactive_retry;
- default: goto interactive_option;
- }
- }
-
- pid = service_start(service->value);
- if (pid == -1)
- break;
- /* Remember the pid if we're running in parallel */
- if (pid > 0) {
- add_pid(pid);
- if (!parallel) {
- rc_waitpid(pid);
- remove_pid(pid);
- }
- }
- }
-
- /* Store our interactive status for boot */
- if (interactive &&
- (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 ||
- strcmp(runlevel, getenv("RC_BOOTLEVEL")) == 0))
- mark_interactive();
- else {
- if (exists(INTERACTIVE))
- unlink(INTERACTIVE);
- }
-
-}
-
-#ifdef RC_DEBUG
-static void
-handle_bad_signal(int sig)
-{
- char pid[10];
- int status;
- pid_t crashed_pid = getpid();
-
- switch (fork()) {
- case -1:
- _exit(sig);
- /* NOTREACHED */
- case 0:
- sprintf(pid, "%i", crashed_pid);
- printf("\nAuto launching gdb!\n\n");
- _exit(execlp("gdb", "gdb", "--quiet", "--pid", pid,
- "-ex", "bt full", NULL));
- /* NOTREACHED */
- default:
- wait(&status);
- }
- _exit(1);
- /* NOTREACHED */
-}
-#endif
-
-int main(int argc, char **argv)
-{
- const char *bootlevel = NULL;
- char *newlevel = NULL;
- const char *systype = NULL;
- RC_STRINGLIST *deporder = NULL;
- RC_STRINGLIST *tmplist;
- RC_STRING *service;
- bool going_down = false;
- int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
- char *krunlevel = NULL;
- char *pidstr = NULL;
- int opt;
- bool parallel;
- int regen = 0;
- bool nostop = false;
-#ifdef __linux__
- char *proc;
- char *p;
- char *token;
-#endif
-
-#ifdef RC_DEBUG
- signal_setup(SIGBUS, handle_bad_signal);
- signal_setup(SIGILL, handle_bad_signal);
- signal_setup(SIGSEGV, handle_bad_signal);
-#endif
-
- applet = basename_c(argv[0]);
- LIST_INIT(&service_pids);
- atexit(cleanup);
- if (!applet)
- eerrorx("arguments required");
-
- argc--;
- argv++;
-
- /* Change dir to / to ensure all scripts don't use stuff in pwd */
- if (chdir("/") == -1)
- eerror("chdir: %s", strerror(errno));
-
- /* Ensure our environment is pure
- * Also, add our configuration to it */
- env_filter();
- env_config();
-
- /* complain about old configuration settings if they exist */
- if (exists(RC_CONF_OLD)) {
- ewarn("%s still exists on your system and should be removed.",
- RC_CONF_OLD);
- ewarn("Please migrate to the appropriate settings in %s", RC_CONF);
- }
-
- argc++;
- argv--;
- while ((opt = getopt_long(argc, argv, getoptstring,
- longopts, (int *) 0)) != -1)
- {
- switch (opt) {
- case 'n':
- nostop = true;
- break;
- case 'o':
- if (*optarg == '\0')
- optarg = NULL;
- if (!rc_runlevel_exists(optarg)) {
- eerror("runlevel `%s' does not exist", optarg);
- exit(EXIT_FAILURE);
- }
- if (!set_krunlevel(optarg))
- exit(EXIT_FAILURE);
- einfo("Overriding next runlevel to %s", optarg);
- exit(EXIT_SUCCESS);
- /* NOTREACHED */
- case 's':
- newlevel = rc_service_resolve(optarg);
- if (!newlevel)
- eerrorx("%s: service `%s' does not exist",
- applet, optarg);
- argv += optind - 1;
- *argv = newlevel;
- execv(*argv, argv);
- eerrorx("%s: %s", applet, strerror(errno));
- /* NOTREACHED */
- case 'S':
- systype = rc_sys();
- if (systype)
- printf("%s\n", systype);
- exit(EXIT_SUCCESS);
- /* NOTREACHED */
- case_RC_COMMON_GETOPT
- }
- }
-
- if (strcmp(applet, "rc") == 0)
- ewarn("rc is deprecated, please use openrc instead.");
- newlevel = argv[optind++];
- /* To make life easier, we only have the shutdown runlevel as
- * nothing really needs to know that we're rebooting.
- * But for those that do, you can test against RC_REBOOT. */
- if (newlevel) {
- if (strcmp(newlevel, "reboot") == 0) {
- newlevel = UNCONST(RC_LEVEL_SHUTDOWN);
- setenv("RC_REBOOT", "YES", 1);
- }
- }
-
- /* Enable logging */
- setenv("EINFO_LOG", "openrc", 1);
-
- /* Export our PID */
- xasprintf(&pidstr, "%d", getpid());
- setenv("RC_PID", pidstr, 1);
- free(pidstr);
-
- /* Create a list of all services which should be started for the new or
- * current runlevel including those in boot, sysinit and hotplugged
- * runlevels. Clearly, some of these will already be started so we
- * won't actually be starting them all.
- */
- bootlevel = getenv("RC_BOOTLEVEL");
- runlevel = rc_runlevel_get();
-
- rc_logger_open(newlevel ? newlevel : runlevel);
-
- /* Setup a signal handler */
- signal_setup(SIGINT, handle_signal);
- signal_setup(SIGQUIT, handle_signal);
- signal_setup(SIGTERM, handle_signal);
- signal_setup(SIGUSR1, handle_signal);
- signal_setup(SIGWINCH, handle_signal);
-
- /* Run any special sysinit foo */
- if (newlevel && strcmp(newlevel, RC_LEVEL_SYSINIT) == 0) {
- do_sysinit();
- free(runlevel);
- runlevel = rc_runlevel_get();
- }
-
- rc_plugin_load();
-
- /* Now we start handling our children */
- signal_setup(SIGCHLD, handle_signal);
-
- if (newlevel &&
- (strcmp(newlevel, RC_LEVEL_SHUTDOWN) == 0 ||
- strcmp(newlevel, RC_LEVEL_SINGLE) == 0))
- {
- going_down = true;
- if (!exists(RC_KRUNLEVEL))
- set_krunlevel(runlevel);
- rc_runlevel_set(newlevel);
- setenv("RC_RUNLEVEL", newlevel, 1);
- setenv("RC_GOINGDOWN", "YES", 1);
- } else {
- /* We should not use krunevel in sysinit or boot runlevels */
- if (!newlevel ||
- (strcmp(newlevel, RC_LEVEL_SYSINIT) != 0 &&
- strcmp(newlevel, getenv("RC_BOOTLEVEL")) != 0))
- {
- krunlevel = get_krunlevel();
- if (krunlevel) {
- newlevel = krunlevel;
- set_krunlevel(NULL);
- }
- }
-
- if (newlevel) {
- if (strcmp(runlevel, newlevel) != 0 &&
- !rc_runlevel_exists(newlevel))
- eerrorx("%s: not a valid runlevel", newlevel);
-
-#ifdef __linux__
- if (strcmp(newlevel, RC_LEVEL_SYSINIT) == 0) {
- /* If we requested a runlevel, save it now */
- p = rc_proc_getent("rc_runlevel");
- if (p == NULL)
- p = rc_proc_getent("softlevel");
- if (p != NULL) {
- set_krunlevel(p);
- free(p);
- }
- }
-#endif
- }
- }
-
- if (going_down) {
-#ifdef __FreeBSD__
- /* FIXME: we shouldn't have todo this */
- /* For some reason, wait_for_services waits for the logger
- * proccess to finish as well, but only on FreeBSD.
- * We cannot allow this so we stop logging now. */
- rc_logger_close();
-#endif
-
- rc_plugin_run(RC_HOOK_RUNLEVEL_STOP_IN, newlevel);
- } else {
- rc_plugin_run(RC_HOOK_RUNLEVEL_STOP_IN, runlevel);
- }
- hook_out = RC_HOOK_RUNLEVEL_STOP_OUT;
-
- /* Check if runlevel is valid if we're changing */
- if (newlevel && strcmp(runlevel, newlevel) != 0 && !going_down) {
- if (!rc_runlevel_exists(newlevel))
- eerrorx("%s: is not a valid runlevel", newlevel);
- }
-
- /* Load our deptree */
- if ((main_deptree = _rc_deptree_load(0, &regen)) == NULL)
- eerrorx("failed to load deptree");
- if (exists(RC_DEPTREE_SKEWED))
- ewarn("WARNING: clock skew detected!");
-
- /* Clean the failed services state dir */
- clean_failed();
-
- if (mkdir(RC_STOPPING, 0755) != 0) {
- if (errno == EACCES)
- eerrorx("%s: superuser access required", applet);
- eerrorx("%s: failed to create stopping dir `%s': %s",
- applet, RC_STOPPING, strerror(errno));
- }
-
- /* Create a list of all services which we could stop (assuming
- * they won't be active in the new or current runlevel) including
- * all those services which have been started, are inactive or
- * are currently starting. Clearly, some of these will be listed
- * in the new or current runlevel so we won't actually be stopping
- * them all.
- */
- main_stop_services = rc_services_in_state(RC_SERVICE_STARTED);
- tmplist = rc_services_in_state(RC_SERVICE_INACTIVE);
- TAILQ_CONCAT(main_stop_services, tmplist, entries);
- free(tmplist);
- tmplist = rc_services_in_state(RC_SERVICE_STARTING);
- TAILQ_CONCAT(main_stop_services, tmplist, entries);
- free(tmplist);
- if (main_stop_services)
- rc_stringlist_sort(&main_stop_services);
-
- main_types_nwua = rc_stringlist_new();
- rc_stringlist_add(main_types_nwua, "ineed");
- rc_stringlist_add(main_types_nwua, "iwant");
- rc_stringlist_add(main_types_nwua, "iuse");
- rc_stringlist_add(main_types_nwua, "iafter");
-
- if (main_stop_services) {
- tmplist = rc_deptree_depends(main_deptree, main_types_nwua, main_stop_services,
- runlevel, depoptions | RC_DEP_STOP);
- rc_stringlist_free(main_stop_services);
- main_stop_services = tmplist;
- }
-
- /* Create a list of all services which should be started for the new or
- * current runlevel including those in boot, sysinit and hotplugged
- * runlevels. Clearly, some of these will already be started so we
- * won't actually be starting them all.
- */
- main_hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED);
- main_start_services = rc_services_in_runlevel_stacked(newlevel ?
- newlevel : runlevel);
- if (strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 &&
- strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SYSINIT) != 0)
- {
- tmplist = rc_services_in_runlevel(RC_LEVEL_SYSINIT);
- TAILQ_CONCAT(main_start_services, tmplist, entries);
- free(tmplist);
- /* If we are NOT headed for the single-user runlevel... */
- if (strcmp(newlevel ? newlevel : runlevel,
- RC_LEVEL_SINGLE) != 0)
- {
- /* If we are NOT headed for the boot runlevel... */
- if (strcmp(newlevel ? newlevel : runlevel,
- bootlevel) != 0)
- {
- tmplist = rc_services_in_runlevel(bootlevel);
- TAILQ_CONCAT(main_start_services, tmplist, entries);
- free(tmplist);
- }
- if (main_hotplugged_services) {
- TAILQ_FOREACH(service, main_hotplugged_services,
- entries)
- rc_stringlist_addu(main_start_services,
- service->value);
- }
- }
- }
-
- parallel = rc_conf_yesno("rc_parallel");
-
- /* Now stop the services that shouldn't be running */
- if (main_stop_services && !nostop)
- do_stop_services(main_types_nw, main_start_services, main_stop_services, main_deptree, newlevel, parallel, going_down);
-
- /* Wait for our services to finish */
- wait_for_services();
-
- /* Notify the plugins we have finished */
- rc_plugin_run(RC_HOOK_RUNLEVEL_STOP_OUT,
- going_down ? newlevel : runlevel);
- hook_out = 0;
-
- rmdir(RC_STOPPING);
-
- /* Store the new runlevel */
- if (newlevel) {
- rc_runlevel_set(newlevel);
- free(runlevel);
- runlevel = xstrdup(newlevel);
- setenv("RC_RUNLEVEL", runlevel, 1);
- }
-
-#ifdef __linux__
- /* We can't log beyond this point as the shutdown runlevel
- * will mount / readonly. */
- if (strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0)
- rc_logger_close();
-#endif
-
- mkdir(RC_STARTING, 0755);
- rc_plugin_run(RC_HOOK_RUNLEVEL_START_IN, runlevel);
- hook_out = RC_HOOK_RUNLEVEL_START_OUT;
-
- /* Re-add our hotplugged services if they stopped */
- if (main_hotplugged_services)
- TAILQ_FOREACH(service, main_hotplugged_services, entries)
- rc_service_mark(service->value, RC_SERVICE_HOTPLUGGED);
-
-#ifdef __linux__
- /* If the "noinit" parameter was passed on the kernel command line then
- * mark the specified services as started so they will not be started
- * by us. */
- proc = p = rc_proc_getent("noinit");
- if (proc) {
- while ((token = strsep(&p, ",")))
- rc_service_mark(token, RC_SERVICE_STARTED);
- free(proc);
- }
-#endif
-
- /* If we have a list of services to start then... */
- if (main_start_services) {
- /* Get a list of the chained runlevels which compose the target runlevel */
- RC_STRINGLIST *runlevel_chain = rc_runlevel_stacks(runlevel);
-
- /* Loop through them in reverse order. */
- RC_STRING *rlevel;
- TAILQ_FOREACH_REVERSE(rlevel, runlevel_chain, rc_stringlist, entries)
- {
- /* Get a list of all the services in that runlevel */
- RC_STRINGLIST *run_services = rc_services_in_runlevel(rlevel->value);
-
- /* Start those services. */
- rc_stringlist_sort(&run_services);
- deporder = rc_deptree_depends(main_deptree, main_types_nwua, run_services, rlevel->value, depoptions | RC_DEP_START);
- rc_stringlist_free(run_services);
- run_services = deporder;
- do_start_services(run_services, parallel);
-
- /* Wait for our services to finish */
- wait_for_services();
-
- /* Free the list of services, we're done with it. */
- rc_stringlist_free(run_services);
- }
- rc_stringlist_free(runlevel_chain);
- }
-
-#ifdef __linux__
- /* If the "noinit" parameter was passed on the kernel command line then
- * mark the specified services as stopped so that our records reflect
- * reality. */
- proc = p = rc_proc_getent("noinit");
- if (proc) {
- while ((token = strsep(&p, ",")))
- rc_service_mark(token, RC_SERVICE_STOPPED);
- free(proc);
- }
-
-#endif
-
- rc_plugin_run(RC_HOOK_RUNLEVEL_START_OUT, runlevel);
- hook_out = 0;
-
- /* If we're in the boot runlevel and we regenerated our dependencies
- * we need to delete them so that they are regenerated again in the
- * default runlevel as they may depend on things that are now
- * available */
- if (regen && strcmp(runlevel, bootlevel) == 0)
- unlink(RC_DEPTREE_CACHE);
-
- return EXIT_SUCCESS;
-}