diff options
Diffstat (limited to 'src/rc/rc.c')
-rw-r--r-- | src/rc/rc.c | 1118 |
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, ®en)) == 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; -} |