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; -} | 
