11558Srgrimes/*-
21558Srgrimes * Copyright (c) 1991, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * This code is derived from software contributed to Berkeley by
61558Srgrimes * Donn Seeley at Berkeley Software Design, Inc.
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes * 4. Neither the name of the University nor the names of its contributors
171558Srgrimes *    may be used to endorse or promote products derived from this software
181558Srgrimes *    without specific prior written permission.
191558Srgrimes *
201558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301558Srgrimes * SUCH DAMAGE.
311558Srgrimes */
321558Srgrimes
331558Srgrimes#ifndef lint
3437417Scharnierstatic const char copyright[] =
351558Srgrimes"@(#) Copyright (c) 1991, 1993\n\
361558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
371558Srgrimes#endif /* not lint */
381558Srgrimes
391558Srgrimes#ifndef lint
4037417Scharnier#if 0
411558Srgrimesstatic char sccsid[] = "@(#)init.c	8.1 (Berkeley) 7/15/93";
4237417Scharnier#endif
4337417Scharnierstatic const char rcsid[] =
4450476Speter  "$FreeBSD: releng/10.3/sbin/init/init.c 293747 2016-01-12 10:24:08Z trasz $";
451558Srgrimes#endif /* not lint */
461558Srgrimes
471558Srgrimes#include <sys/param.h>
4827837Sdavidn#include <sys/ioctl.h>
49293744Strasz#include <sys/mman.h>
5019227Sphk#include <sys/mount.h>
511558Srgrimes#include <sys/sysctl.h>
521558Srgrimes#include <sys/wait.h>
5328344Sdavidn#include <sys/stat.h>
54101271Smux#include <sys/uio.h>
551558Srgrimes
561558Srgrimes#include <db.h>
571558Srgrimes#include <errno.h>
581558Srgrimes#include <fcntl.h>
59166484Simp#include <kenv.h>
6027186Sache#include <libutil.h>
6169793Sobrien#include <paths.h>
621558Srgrimes#include <signal.h>
631558Srgrimes#include <stdio.h>
641558Srgrimes#include <stdlib.h>
651558Srgrimes#include <string.h>
661558Srgrimes#include <syslog.h>
671558Srgrimes#include <time.h>
681558Srgrimes#include <ttyent.h>
691558Srgrimes#include <unistd.h>
702323Snate#include <sys/reboot.h>
7126594Scharnier#include <err.h>
721558Srgrimes
731558Srgrimes#include <stdarg.h>
741558Srgrimes
751558Srgrimes#ifdef SECURE
761558Srgrimes#include <pwd.h>
771558Srgrimes#endif
781558Srgrimes
7921865Sdavidn#ifdef LOGIN_CAP
8021865Sdavidn#include <login_cap.h>
8121865Sdavidn#endif
8221865Sdavidn
83293744Strasz#include "mntopts.h"
841558Srgrimes#include "pathnames.h"
851558Srgrimes
861558Srgrimes/*
871558Srgrimes * Sleep times; used to prevent thrashing.
881558Srgrimes */
891558Srgrimes#define	GETTY_SPACING		 5	/* N secs minimum getty spacing */
901558Srgrimes#define	GETTY_SLEEP		30	/* sleep N secs after spacing problem */
91232841Sed#define	GETTY_NSPACE		 3	/* max. spacing count to bring reaction */
921558Srgrimes#define	WINDOW_WAIT		 3	/* wait N secs after starting window */
931558Srgrimes#define	STALL_TIMEOUT		30	/* wait N secs after warning */
941558Srgrimes#define	DEATH_WATCH		10	/* wait N secs for procs to die */
95173785Sobrien#define	DEATH_SCRIPT		120	/* wait for 2min for /etc/rc.shutdown */
96173785Sobrien#define	RESOURCE_RC		"daemon"
97232841Sed#define	RESOURCE_WINDOW		"default"
98173785Sobrien#define	RESOURCE_GETTY		"default"
991558Srgrimes
100183391Sdelphijstatic void handle(sig_t, ...);
101183391Sdelphijstatic void delset(sigset_t *, ...);
1021558Srgrimes
103183391Sdelphijstatic void stall(const char *, ...) __printflike(1, 2);
104183391Sdelphijstatic void warning(const char *, ...) __printflike(1, 2);
105183391Sdelphijstatic void emergency(const char *, ...) __printflike(1, 2);
106183391Sdelphijstatic void disaster(int);
107183391Sdelphijstatic void badsys(int);
108293744Straszstatic void revoke_ttys(void);
109183391Sdelphijstatic int  runshutdown(void);
110140070Sdelphijstatic char *strk(char *);
1111558Srgrimes
1121558Srgrimes/*
1131558Srgrimes * We really need a recursive typedef...
1141558Srgrimes * The following at least guarantees that the return type of (*state_t)()
1151558Srgrimes * is sufficiently wide to hold a function pointer.
1161558Srgrimes */
11792838Simptypedef long (*state_func_t)(void);
11892838Simptypedef state_func_t (*state_t)(void);
1191558Srgrimes
120183391Sdelphijstatic state_func_t single_user(void);
121183391Sdelphijstatic state_func_t runcom(void);
122183391Sdelphijstatic state_func_t read_ttys(void);
123183391Sdelphijstatic state_func_t multi_user(void);
124183391Sdelphijstatic state_func_t clean_ttys(void);
125183391Sdelphijstatic state_func_t catatonia(void);
126183391Sdelphijstatic state_func_t death(void);
127217750Sjillesstatic state_func_t death_single(void);
128293744Straszstatic state_func_t reroot(void);
129293744Straszstatic state_func_t reroot_phase_two(void);
1301558Srgrimes
131183391Sdelphijstatic state_func_t run_script(const char *);
132166484Simp
133227081Sedstatic enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
13411910Sphk#define FALSE	0
13511910Sphk#define TRUE	1
13611910Sphk
137227081Sedstatic int Reboot = FALSE;
138227081Sedstatic int howto = RB_AUTOBOOT;
1391558Srgrimes
140227081Sedstatic int devfs;
14119227Sphk
142183391Sdelphijstatic void transition(state_t);
143183391Sdelphijstatic state_t requested_transition;
144217750Sjillesstatic state_t current_state = death_single;
1451558Srgrimes
146232977Sedstatic void open_console(void);
147183391Sdelphijstatic const char *get_shell(void);
148183391Sdelphijstatic void write_stderr(const char *message);
1491558Srgrimes
1501558Srgrimestypedef struct init_session {
1511558Srgrimes	int	se_index;		/* index of entry in ttys file */
1521558Srgrimes	pid_t	se_process;		/* controlling process */
1531558Srgrimes	time_t	se_started;		/* used to avoid thrashing */
1541558Srgrimes	int	se_flags;		/* status of session */
1551558Srgrimes#define	SE_SHUTDOWN	0x1		/* session won't be restarted */
15657344Salfred#define	SE_PRESENT	0x2		/* session is in /etc/ttys */
157232841Sed	int	se_nspace;		/* spacing count */
1581558Srgrimes	char	*se_device;		/* filename of port */
1591558Srgrimes	char	*se_getty;		/* what to run on that port */
160232841Sed	char	*se_getty_argv_space;   /* pre-parsed argument array space */
1611558Srgrimes	char	**se_getty_argv;	/* pre-parsed argument array */
1621558Srgrimes	char	*se_window;		/* window system (started only once) */
163232841Sed	char	*se_window_argv_space;  /* pre-parsed argument array space */
1641558Srgrimes	char	**se_window_argv;	/* pre-parsed argument array */
165232841Sed	char	*se_type;		/* default terminal type */
1661558Srgrimes	struct	init_session *se_prev;
1671558Srgrimes	struct	init_session *se_next;
1681558Srgrimes} session_t;
1691558Srgrimes
170183391Sdelphijstatic void free_session(session_t *);
171183391Sdelphijstatic session_t *new_session(session_t *, int, struct ttyent *);
172183391Sdelphijstatic session_t *sessions;
1731558Srgrimes
174183391Sdelphijstatic char **construct_argv(char *);
175183391Sdelphijstatic void start_window_system(session_t *);
176183391Sdelphijstatic void collect_child(pid_t);
177183391Sdelphijstatic pid_t start_getty(session_t *);
178183391Sdelphijstatic void transition_handler(int);
179183391Sdelphijstatic void alrm_handler(int);
180183391Sdelphijstatic void setsecuritylevel(int);
181183391Sdelphijstatic int getsecuritylevel(void);
182183391Sdelphijstatic int setupargv(session_t *, struct ttyent *);
18321941Sdavidn#ifdef LOGIN_CAP
184183391Sdelphijstatic void setprocresources(const char *);
18521941Sdavidn#endif
186183391Sdelphijstatic int clang;
1871558Srgrimes
188183391Sdelphijstatic int start_session_db(void);
189183391Sdelphijstatic void add_session(session_t *);
190183391Sdelphijstatic void del_session(session_t *);
191183391Sdelphijstatic session_t *find_session(pid_t);
192183391Sdelphijstatic DB *session_db;
1931558Srgrimes
1941558Srgrimes/*
1951558Srgrimes * The mother of all processes.
1961558Srgrimes */
1971558Srgrimesint
19892838Simpmain(int argc, char *argv[])
1991558Srgrimes{
200166484Simp	state_t initial_transition = runcom;
201166484Simp	char kenv_value[PATH_MAX];
202293744Strasz	int c, error;
2031558Srgrimes	struct sigaction sa;
2041558Srgrimes	sigset_t mask;
2051558Srgrimes
2061558Srgrimes	/* Dispose of random users. */
20726594Scharnier	if (getuid() != 0)
20826594Scharnier		errx(1, "%s", strerror(EPERM));
2091558Srgrimes
2101558Srgrimes	/* System V users like to reexec init. */
21147998Sru	if (getpid() != 1) {
21247998Sru#ifdef COMPAT_SYSV_INIT
21347998Sru		/* So give them what they want */
21447998Sru		if (argc > 1) {
21547998Sru			if (strlen(argv[1]) == 1) {
21692806Sobrien				char runlevel = *argv[1];
21792806Sobrien				int sig;
2181558Srgrimes
21947998Sru				switch (runlevel) {
220173785Sobrien				case '0': /* halt + poweroff */
221173785Sobrien					sig = SIGUSR2;
222173785Sobrien					break;
223173785Sobrien				case '1': /* single-user */
224173785Sobrien					sig = SIGTERM;
225173785Sobrien					break;
226173785Sobrien				case '6': /* reboot */
227173785Sobrien					sig = SIGINT;
228173785Sobrien					break;
229173785Sobrien				case 'c': /* block further logins */
230173785Sobrien					sig = SIGTSTP;
231173785Sobrien					break;
232173785Sobrien				case 'q': /* rescan /etc/ttys */
233173785Sobrien					sig = SIGHUP;
234173785Sobrien					break;
235293744Strasz				case 'r': /* remount root */
236293744Strasz					sig = SIGEMT;
237293744Strasz					break;
238173785Sobrien				default:
239173785Sobrien					goto invalid;
24047998Sru				}
24147998Sru				kill(1, sig);
24247998Sru				_exit(0);
24347998Sru			} else
24447998Sruinvalid:
24547998Sru				errx(1, "invalid run-level ``%s''", argv[1]);
24647998Sru		} else
24747998Sru#endif
24847998Sru			errx(1, "already running");
24947998Sru	}
2501558Srgrimes	/*
2511558Srgrimes	 * Note that this does NOT open a file...
2521558Srgrimes	 * Does 'init' deserve its own facility number?
2531558Srgrimes	 */
2541558Srgrimes	openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
2551558Srgrimes
2561558Srgrimes	/*
2571558Srgrimes	 * Create an initial session.
2581558Srgrimes	 */
259293744Strasz	if (setsid() < 0 && (errno != EPERM || getsid(0) != 1))
2601558Srgrimes		warning("initial setsid() failed: %m");
2611558Srgrimes
2621558Srgrimes	/*
2631558Srgrimes	 * Establish an initial user so that programs running
2641558Srgrimes	 * single user do not freak out and die (like passwd).
2651558Srgrimes	 */
2661558Srgrimes	if (setlogin("root") < 0)
2671558Srgrimes		warning("setlogin() failed: %m");
2681558Srgrimes
2691558Srgrimes	/*
2701558Srgrimes	 * This code assumes that we always get arguments through flags,
2711558Srgrimes	 * never through bits set in some random machine register.
2721558Srgrimes	 */
273293744Strasz	while ((c = getopt(argc, argv, "dsfr")) != -1)
2741558Srgrimes		switch (c) {
27519227Sphk		case 'd':
27619227Sphk			devfs = 1;
27719227Sphk			break;
2781558Srgrimes		case 's':
279166484Simp			initial_transition = single_user;
2801558Srgrimes			break;
2811558Srgrimes		case 'f':
2821558Srgrimes			runcom_mode = FASTBOOT;
2831558Srgrimes			break;
284293744Strasz		case 'r':
285293744Strasz			initial_transition = reroot_phase_two;
286293744Strasz			break;
2871558Srgrimes		default:
2881558Srgrimes			warning("unrecognized flag '-%c'", c);
2891558Srgrimes			break;
2901558Srgrimes		}
2911558Srgrimes
2921558Srgrimes	if (optind != argc)
2931558Srgrimes		warning("ignoring excess arguments");
2941558Srgrimes
295166484Simp	/*
296166484Simp	 * We catch or block signals rather than ignore them,
297166484Simp	 * so that they get reset on exec.
298166484Simp	 */
299166484Simp	handle(badsys, SIGSYS, 0);
300173785Sobrien	handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU,
301173785Sobrien	    SIGXFSZ, 0);
302293744Strasz	handle(transition_handler, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP,
303293744Strasz	    SIGUSR1, SIGUSR2, 0);
304166484Simp	handle(alrm_handler, SIGALRM, 0);
305166484Simp	sigfillset(&mask);
306166484Simp	delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
307293744Strasz	    SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP,
308293744Strasz	    SIGALRM, SIGUSR1, SIGUSR2, 0);
309166484Simp	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
310166484Simp	sigemptyset(&sa.sa_mask);
311166484Simp	sa.sa_flags = 0;
312166484Simp	sa.sa_handler = SIG_IGN;
313173785Sobrien	sigaction(SIGTTIN, &sa, (struct sigaction *)0);
314173785Sobrien	sigaction(SIGTTOU, &sa, (struct sigaction *)0);
315166484Simp
316166484Simp	/*
317166484Simp	 * Paranoia.
318166484Simp	 */
319166484Simp	close(0);
320166484Simp	close(1);
321166484Simp	close(2);
322166484Simp
323166484Simp	if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) {
324166484Simp		state_func_t next_transition;
325166484Simp
326166484Simp		if ((next_transition = run_script(kenv_value)) != 0)
327166484Simp			initial_transition = (state_t) next_transition;
328166484Simp	}
329166484Simp
330166484Simp	if (kenv(KENV_GET, "init_chroot", kenv_value, sizeof(kenv_value)) > 0) {
331166484Simp		if (chdir(kenv_value) != 0 || chroot(".") != 0)
332166484Simp			warning("Can't chroot to %s: %m", kenv_value);
333166484Simp	}
334166484Simp
335166484Simp	/*
336166484Simp	 * Additional check if devfs needs to be mounted:
337166484Simp	 * If "/" and "/dev" have the same device number,
338166484Simp	 * then it hasn't been mounted yet.
339166484Simp	 */
340166484Simp	if (!devfs) {
341166484Simp		struct stat stst;
342166484Simp		dev_t root_devno;
343166484Simp
344166484Simp		stat("/", &stst);
345166484Simp		root_devno = stst.st_dev;
346166484Simp		if (stat("/dev", &stst) != 0)
347166484Simp			warning("Can't stat /dev: %m");
348166484Simp		else if (stst.st_dev == root_devno)
349166484Simp			devfs++;
350166484Simp	}
351166484Simp
35219227Sphk	if (devfs) {
353101271Smux		struct iovec iov[4];
35472188Sphk		char *s;
35572188Sphk		int i;
35672188Sphk
357140070Sdelphij		char _fstype[]	= "fstype";
358140070Sdelphij		char _devfs[]	= "devfs";
359140070Sdelphij		char _fspath[]	= "fspath";
360140070Sdelphij		char _path_dev[]= _PATH_DEV;
361140070Sdelphij
362140070Sdelphij		iov[0].iov_base = _fstype;
363140070Sdelphij		iov[0].iov_len = sizeof(_fstype);
364140070Sdelphij		iov[1].iov_base = _devfs;
365140070Sdelphij		iov[1].iov_len = sizeof(_devfs);
366140070Sdelphij		iov[2].iov_base = _fspath;
367140070Sdelphij		iov[2].iov_len = sizeof(_fspath);
368173787Sobrien		/*
36972188Sphk		 * Try to avoid the trailing slash in _PATH_DEV.
37072188Sphk		 * Be *very* defensive.
37172188Sphk		 */
37272188Sphk		s = strdup(_PATH_DEV);
37372188Sphk		if (s != NULL) {
37472188Sphk			i = strlen(s);
37572188Sphk			if (i > 0 && s[i - 1] == '/')
37672188Sphk				s[i - 1] = '\0';
377101271Smux			iov[3].iov_base = s;
378101271Smux			iov[3].iov_len = strlen(s) + 1;
37972188Sphk		} else {
380140070Sdelphij			iov[3].iov_base = _path_dev;
381140070Sdelphij			iov[3].iov_len = sizeof(_path_dev);
38272188Sphk		}
383101271Smux		nmount(iov, 4, 0);
384101271Smux		if (s != NULL)
385101271Smux			free(s);
38619227Sphk	}
38719227Sphk
388293744Strasz	if (initial_transition != reroot_phase_two) {
389293744Strasz		/*
390293744Strasz		 * Unmount reroot leftovers.  This runs after init(8)
391293744Strasz		 * gets reexecuted after reroot_phase_two() is done.
392293744Strasz		 */
393293744Strasz		error = unmount(_PATH_REROOT, MNT_FORCE);
394293744Strasz		if (error != 0 && errno != EINVAL)
395293744Strasz			warning("Cannot unmount %s: %m", _PATH_REROOT);
396293744Strasz	}
397293744Strasz
3981558Srgrimes	/*
3991558Srgrimes	 * Start the state machine.
4001558Srgrimes	 */
401166484Simp	transition(initial_transition);
4021558Srgrimes
4031558Srgrimes	/*
4041558Srgrimes	 * Should never reach here.
4051558Srgrimes	 */
4061558Srgrimes	return 1;
4071558Srgrimes}
4081558Srgrimes
4091558Srgrimes/*
4101558Srgrimes * Associate a function with a signal handler.
4111558Srgrimes */
412183391Sdelphijstatic void
4131558Srgrimeshandle(sig_t handler, ...)
4141558Srgrimes{
4151558Srgrimes	int sig;
4161558Srgrimes	struct sigaction sa;
41734001Sjraynard	sigset_t mask_everything;
4181558Srgrimes	va_list ap;
4191558Srgrimes	va_start(ap, handler);
4201558Srgrimes
4211558Srgrimes	sa.sa_handler = handler;
4221558Srgrimes	sigfillset(&mask_everything);
4231558Srgrimes
424126836Sbde	while ((sig = va_arg(ap, int)) != 0) {
4251558Srgrimes		sa.sa_mask = mask_everything;
4261558Srgrimes		/* XXX SA_RESTART? */
4271558Srgrimes		sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
4281558Srgrimes		sigaction(sig, &sa, (struct sigaction *) 0);
4291558Srgrimes	}
4301558Srgrimes	va_end(ap);
4311558Srgrimes}
4321558Srgrimes
4331558Srgrimes/*
4341558Srgrimes * Delete a set of signals from a mask.
4351558Srgrimes */
436183391Sdelphijstatic void
4371558Srgrimesdelset(sigset_t *maskp, ...)
4381558Srgrimes{
4391558Srgrimes	int sig;
4401558Srgrimes	va_list ap;
4411558Srgrimes	va_start(ap, maskp);
4421558Srgrimes
443126836Sbde	while ((sig = va_arg(ap, int)) != 0)
4441558Srgrimes		sigdelset(maskp, sig);
4451558Srgrimes	va_end(ap);
4461558Srgrimes}
4471558Srgrimes
4481558Srgrimes/*
4491558Srgrimes * Log a message and sleep for a while (to give someone an opportunity
4501558Srgrimes * to read it and to save log or hardcopy output if the problem is chronic).
4511558Srgrimes * NB: should send a message to the session logger to avoid blocking.
4521558Srgrimes */
453183391Sdelphijstatic void
45481911Skrisstall(const char *message, ...)
4551558Srgrimes{
4561558Srgrimes	va_list ap;
4571558Srgrimes	va_start(ap, message);
4581558Srgrimes
4591558Srgrimes	vsyslog(LOG_ALERT, message, ap);
4601558Srgrimes	va_end(ap);
4611558Srgrimes	sleep(STALL_TIMEOUT);
4621558Srgrimes}
4631558Srgrimes
4641558Srgrimes/*
4651558Srgrimes * Like stall(), but doesn't sleep.
4661558Srgrimes * If cpp had variadic macros, the two functions could be #defines for another.
4671558Srgrimes * NB: should send a message to the session logger to avoid blocking.
4681558Srgrimes */
469183391Sdelphijstatic void
47081911Skriswarning(const char *message, ...)
4711558Srgrimes{
4721558Srgrimes	va_list ap;
4731558Srgrimes	va_start(ap, message);
4741558Srgrimes
4751558Srgrimes	vsyslog(LOG_ALERT, message, ap);
4761558Srgrimes	va_end(ap);
4771558Srgrimes}
4781558Srgrimes
4791558Srgrimes/*
4801558Srgrimes * Log an emergency message.
4811558Srgrimes * NB: should send a message to the session logger to avoid blocking.
4821558Srgrimes */
483183391Sdelphijstatic void
48481911Skrisemergency(const char *message, ...)
4851558Srgrimes{
4861558Srgrimes	va_list ap;
4871558Srgrimes	va_start(ap, message);
4881558Srgrimes
4891558Srgrimes	vsyslog(LOG_EMERG, message, ap);
4901558Srgrimes	va_end(ap);
4911558Srgrimes}
4921558Srgrimes
4931558Srgrimes/*
4941558Srgrimes * Catch a SIGSYS signal.
4951558Srgrimes *
4961558Srgrimes * These may arise if a system does not support sysctl.
4971558Srgrimes * We tolerate up to 25 of these, then throw in the towel.
4981558Srgrimes */
499183391Sdelphijstatic void
50092838Simpbadsys(int sig)
5011558Srgrimes{
5021558Srgrimes	static int badcount = 0;
5031558Srgrimes
5041558Srgrimes	if (badcount++ < 25)
5051558Srgrimes		return;
5061558Srgrimes	disaster(sig);
5071558Srgrimes}
5081558Srgrimes
5091558Srgrimes/*
5101558Srgrimes * Catch an unexpected signal.
5111558Srgrimes */
512183391Sdelphijstatic void
51392838Simpdisaster(int sig)
5141558Srgrimes{
515173785Sobrien
5161558Srgrimes	emergency("fatal signal: %s",
517173785Sobrien	    (unsigned)sig < NSIG ? sys_siglist[sig] : "unknown signal");
5181558Srgrimes
5191558Srgrimes	sleep(STALL_TIMEOUT);
5201558Srgrimes	_exit(sig);		/* reboot */
5211558Srgrimes}
5221558Srgrimes
5231558Srgrimes/*
5241558Srgrimes * Get the security level of the kernel.
5251558Srgrimes */
526183391Sdelphijstatic int
52792838Simpgetsecuritylevel(void)
5281558Srgrimes{
5291558Srgrimes#ifdef KERN_SECURELVL
5301558Srgrimes	int name[2], curlevel;
5311558Srgrimes	size_t len;
5321558Srgrimes
5331558Srgrimes	name[0] = CTL_KERN;
5341558Srgrimes	name[1] = KERN_SECURELVL;
5351558Srgrimes	len = sizeof curlevel;
5361558Srgrimes	if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
5371558Srgrimes		emergency("cannot get kernel security level: %s",
5381558Srgrimes		    strerror(errno));
5391558Srgrimes		return (-1);
5401558Srgrimes	}
5411558Srgrimes	return (curlevel);
5421558Srgrimes#else
5431558Srgrimes	return (-1);
5441558Srgrimes#endif
5451558Srgrimes}
5461558Srgrimes
5471558Srgrimes/*
5481558Srgrimes * Set the security level of the kernel.
5491558Srgrimes */
550183391Sdelphijstatic void
55192838Simpsetsecuritylevel(int newlevel)
5521558Srgrimes{
5531558Srgrimes#ifdef KERN_SECURELVL
5541558Srgrimes	int name[2], curlevel;
5551558Srgrimes
5561558Srgrimes	curlevel = getsecuritylevel();
5571558Srgrimes	if (newlevel == curlevel)
5581558Srgrimes		return;
5591558Srgrimes	name[0] = CTL_KERN;
5601558Srgrimes	name[1] = KERN_SECURELVL;
5611558Srgrimes	if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
5621558Srgrimes		emergency(
5631558Srgrimes		    "cannot change kernel security level from %d to %d: %s",
5641558Srgrimes		    curlevel, newlevel, strerror(errno));
5651558Srgrimes		return;
5661558Srgrimes	}
5671558Srgrimes#ifdef SECURE
5681558Srgrimes	warning("kernel security level changed from %d to %d",
5691558Srgrimes	    curlevel, newlevel);
5701558Srgrimes#endif
5711558Srgrimes#endif
5721558Srgrimes}
5731558Srgrimes
5741558Srgrimes/*
5751558Srgrimes * Change states in the finite state machine.
5761558Srgrimes * The initial state is passed as an argument.
5771558Srgrimes */
578183391Sdelphijstatic void
57992838Simptransition(state_t s)
5801558Srgrimes{
581173785Sobrien
582217750Sjilles	current_state = s;
5831558Srgrimes	for (;;)
584217750Sjilles		current_state = (state_t) (*current_state)();
5851558Srgrimes}
5861558Srgrimes
5871558Srgrimes/*
5881558Srgrimes * Start a session and allocate a controlling terminal.
5891558Srgrimes * Only called by children of init after forking.
5901558Srgrimes */
591183391Sdelphijstatic void
592232977Sedopen_console(void)
5931558Srgrimes{
5941558Srgrimes	int fd;
5951558Srgrimes
596233945Sed	/*
597233945Sed	 * Try to open /dev/console.  Open the device with O_NONBLOCK to
598233945Sed	 * prevent potential blocking on a carrier.
599233945Sed	 */
600232977Sed	revoke(_PATH_CONSOLE);
601232977Sed	if ((fd = open(_PATH_CONSOLE, O_RDWR | O_NONBLOCK)) != -1) {
602233945Sed		(void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
603232977Sed		if (login_tty(fd) == 0)
604232977Sed			return;
605232977Sed		close(fd);
6061558Srgrimes	}
607232977Sed
608232977Sed	/* No luck.  Log output to file if possible. */
609232977Sed	if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
610232977Sed		stall("cannot open null device.");
6111558Srgrimes		_exit(1);
6121558Srgrimes	}
613232977Sed	if (fd != STDIN_FILENO) {
614232977Sed		dup2(fd, STDIN_FILENO);
615232977Sed		close(fd);
616232977Sed	}
617232977Sed	fd = open(_PATH_INITLOG, O_WRONLY | O_APPEND | O_CREAT, 0644);
618232977Sed	if (fd == -1)
619232977Sed		dup2(STDIN_FILENO, STDOUT_FILENO);
620232977Sed	else if (fd != STDOUT_FILENO) {
621232977Sed		dup2(fd, STDOUT_FILENO);
622232977Sed		close(fd);
623232977Sed	}
624232977Sed	dup2(STDOUT_FILENO, STDERR_FILENO);
6251558Srgrimes}
6261558Srgrimes
627183391Sdelphijstatic const char *
628166484Simpget_shell(void)
629166484Simp{
630166484Simp	static char kenv_value[PATH_MAX];
631166484Simp
632166484Simp	if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0)
633166484Simp		return kenv_value;
634166484Simp	else
635166484Simp		return _PATH_BSHELL;
636166484Simp}
637166484Simp
638183391Sdelphijstatic void
639166484Simpwrite_stderr(const char *message)
640166484Simp{
641173785Sobrien
642166484Simp	write(STDERR_FILENO, message, strlen(message));
643166484Simp}
644166484Simp
645293744Straszstatic int
646293744Straszread_file(const char *path, void **bufp, size_t *bufsizep)
647293744Strasz{
648293744Strasz	struct stat sb;
649293744Strasz	size_t bufsize;
650293744Strasz	void *buf;
651293744Strasz	ssize_t nbytes;
652293744Strasz	int error, fd;
653293744Strasz
654293744Strasz	fd = open(path, O_RDONLY);
655293744Strasz	if (fd < 0) {
656293744Strasz		emergency("%s: %s", path, strerror(errno));
657293744Strasz		return (-1);
658293744Strasz	}
659293744Strasz
660293744Strasz	error = fstat(fd, &sb);
661293744Strasz	if (error != 0) {
662293744Strasz		emergency("fstat: %s", strerror(errno));
663293747Strasz		close(fd);
664293744Strasz		return (error);
665293744Strasz	}
666293744Strasz
667293744Strasz	bufsize = sb.st_size;
668293744Strasz	buf = malloc(bufsize);
669293744Strasz	if (buf == NULL) {
670293744Strasz		emergency("malloc: %s", strerror(errno));
671293747Strasz		close(fd);
672293744Strasz		return (error);
673293744Strasz	}
674293744Strasz
675293744Strasz	nbytes = read(fd, buf, bufsize);
676293744Strasz	if (nbytes != (ssize_t)bufsize) {
677293744Strasz		emergency("read: %s", strerror(errno));
678293747Strasz		close(fd);
679293744Strasz		free(buf);
680293744Strasz		return (error);
681293744Strasz	}
682293744Strasz
683293744Strasz	error = close(fd);
684293744Strasz	if (error != 0) {
685293744Strasz		emergency("close: %s", strerror(errno));
686293744Strasz		free(buf);
687293744Strasz		return (error);
688293744Strasz	}
689293744Strasz
690293744Strasz	*bufp = buf;
691293744Strasz	*bufsizep = bufsize;
692293744Strasz
693293744Strasz	return (0);
694293744Strasz}
695293744Strasz
696293744Straszstatic int
697293747Straszcreate_file(const char *path, const void *buf, size_t bufsize)
698293744Strasz{
699293744Strasz	ssize_t nbytes;
700293744Strasz	int error, fd;
701293744Strasz
702293744Strasz	fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0700);
703293744Strasz	if (fd < 0) {
704293744Strasz		emergency("%s: %s", path, strerror(errno));
705293744Strasz		return (-1);
706293744Strasz	}
707293744Strasz
708293744Strasz	nbytes = write(fd, buf, bufsize);
709293744Strasz	if (nbytes != (ssize_t)bufsize) {
710293744Strasz		emergency("write: %s", strerror(errno));
711293747Strasz		close(fd);
712293744Strasz		return (-1);
713293744Strasz	}
714293744Strasz
715293744Strasz	error = close(fd);
716293744Strasz	if (error != 0) {
717293744Strasz		emergency("close: %s", strerror(errno));
718293744Strasz		return (-1);
719293744Strasz	}
720293744Strasz
721293744Strasz	return (0);
722293744Strasz}
723293744Strasz
724293744Straszstatic int
725293744Straszmount_tmpfs(const char *fspath)
726293744Strasz{
727293744Strasz	struct iovec *iov;
728293744Strasz	char errmsg[255];
729293744Strasz	int error, iovlen;
730293744Strasz
731293744Strasz	iov = NULL;
732293744Strasz	iovlen = 0;
733293744Strasz	memset(errmsg, 0, sizeof(errmsg));
734293744Strasz	build_iovec(&iov, &iovlen, "fstype",
735293744Strasz	    __DECONST(void *, "tmpfs"), (size_t)-1);
736293744Strasz	build_iovec(&iov, &iovlen, "fspath",
737293744Strasz	    __DECONST(void *, fspath), (size_t)-1);
738293744Strasz	build_iovec(&iov, &iovlen, "errmsg",
739293744Strasz	    errmsg, sizeof(errmsg));
740293744Strasz
741293744Strasz	error = nmount(iov, iovlen, 0);
742293744Strasz	if (error != 0) {
743293744Strasz		if (*errmsg != '\0') {
744293744Strasz			emergency("cannot mount tmpfs on %s: %s: %s",
745293744Strasz			    fspath, errmsg, strerror(errno));
746293744Strasz		} else {
747293744Strasz			emergency("cannot mount tmpfs on %s: %s",
748293744Strasz			    fspath, strerror(errno));
749293744Strasz		}
750293744Strasz		return (error);
751293744Strasz	}
752293744Strasz	return (0);
753293744Strasz}
754293744Strasz
755293744Straszstatic state_func_t
756293744Straszreroot(void)
757293744Strasz{
758293744Strasz	void *buf;
759293744Strasz	char init_path[PATH_MAX];
760293744Strasz	size_t bufsize, init_path_len;
761293744Strasz	int error, name[4];
762293744Strasz
763293747Strasz	buf = NULL;
764293747Strasz	bufsize = 0;
765293747Strasz
766293744Strasz	name[0] = CTL_KERN;
767293744Strasz	name[1] = KERN_PROC;
768293744Strasz	name[2] = KERN_PROC_PATHNAME;
769293744Strasz	name[3] = -1;
770293744Strasz	init_path_len = sizeof(init_path);
771293744Strasz	error = sysctl(name, 4, init_path, &init_path_len, NULL, 0);
772293744Strasz	if (error != 0) {
773293744Strasz		emergency("failed to get kern.proc.pathname: %s",
774293744Strasz		    strerror(errno));
775293744Strasz		goto out;
776293744Strasz	}
777293744Strasz
778293744Strasz	revoke_ttys();
779293744Strasz	runshutdown();
780293744Strasz
781293744Strasz	/*
782293744Strasz	 * Make sure nobody can interfere with our scheme.
783293744Strasz	 */
784293744Strasz	error = kill(-1, SIGKILL);
785293744Strasz	if (error != 0) {
786293744Strasz		emergency("kill(2) failed: %s", strerror(errno));
787293744Strasz		goto out;
788293744Strasz	}
789293744Strasz
790293744Strasz	/*
791293744Strasz	 * Copy the init binary into tmpfs, so that we can unmount
792293744Strasz	 * the old rootfs without committing suicide.
793293744Strasz	 */
794293744Strasz	error = read_file(init_path, &buf, &bufsize);
795293744Strasz	if (error != 0)
796293744Strasz		goto out;
797293744Strasz	error = mount_tmpfs(_PATH_REROOT);
798293744Strasz	if (error != 0)
799293744Strasz		goto out;
800293744Strasz	error = create_file(_PATH_REROOT_INIT, buf, bufsize);
801293744Strasz	if (error != 0)
802293744Strasz		goto out;
803293744Strasz
804293744Strasz	/*
805293744Strasz	 * Execute the temporary init.
806293744Strasz	 */
807293744Strasz	execl(_PATH_REROOT_INIT, _PATH_REROOT_INIT, "-r", NULL);
808293744Strasz	emergency("cannot exec %s: %s", _PATH_REROOT_INIT, strerror(errno));
809293744Strasz
810293744Straszout:
811293744Strasz	emergency("reroot failed; going to single user mode");
812293747Strasz	free(buf);
813293744Strasz	return (state_func_t) single_user;
814293744Strasz}
815293744Strasz
816293744Straszstatic state_func_t
817293744Straszreroot_phase_two(void)
818293744Strasz{
819293744Strasz	char init_path[PATH_MAX], *path, *path_component;
820293744Strasz	size_t init_path_len;
821293744Strasz	int nbytes, error;
822293744Strasz
823293744Strasz	/*
824293744Strasz	 * Ask the kernel to mount the new rootfs.
825293744Strasz	 */
826293744Strasz	error = reboot(RB_REROOT);
827293744Strasz	if (error != 0) {
828293744Strasz		emergency("RB_REBOOT failed: %s", strerror(errno));
829293744Strasz		goto out;
830293744Strasz	}
831293744Strasz
832293744Strasz	/*
833293744Strasz	 * Figure out where the destination init(8) binary is.  Note that
834293744Strasz	 * the path could be different than what we've started with.  Use
835293744Strasz	 * the value from kenv, if set, or the one from sysctl otherwise.
836293744Strasz	 * The latter defaults to a hardcoded value, but can be overridden
837293744Strasz	 * by a build time option.
838293744Strasz	 */
839293744Strasz	nbytes = kenv(KENV_GET, "init_path", init_path, sizeof(init_path));
840293744Strasz	if (nbytes <= 0) {
841293744Strasz		init_path_len = sizeof(init_path);
842293744Strasz		error = sysctlbyname("kern.init_path",
843293744Strasz		    init_path, &init_path_len, NULL, 0);
844293744Strasz		if (error != 0) {
845293744Strasz			emergency("failed to retrieve kern.init_path: %s",
846293744Strasz			    strerror(errno));
847293744Strasz			goto out;
848293744Strasz		}
849293744Strasz	}
850293744Strasz
851293744Strasz	/*
852293744Strasz	 * Repeat the init search logic from sys/kern/init_path.c
853293744Strasz	 */
854293744Strasz	path_component = init_path;
855293744Strasz	while ((path = strsep(&path_component, ":")) != NULL) {
856293744Strasz		/*
857293744Strasz		 * Execute init(8) from the new rootfs.
858293744Strasz		 */
859293744Strasz		execl(path, path, NULL);
860293744Strasz	}
861293744Strasz	emergency("cannot exec init from %s: %s", init_path, strerror(errno));
862293744Strasz
863293744Straszout:
864293744Strasz	emergency("reroot failed; going to single user mode");
865293744Strasz	return (state_func_t) single_user;
866293744Strasz}
867293744Strasz
8681558Srgrimes/*
8691558Srgrimes * Bring the system up single user.
8701558Srgrimes */
871183391Sdelphijstatic state_func_t
87292838Simpsingle_user(void)
8731558Srgrimes{
8741558Srgrimes	pid_t pid, wpid;
8751558Srgrimes	int status;
8761558Srgrimes	sigset_t mask;
877166484Simp	const char *shell;
8781558Srgrimes	char *argv[2];
8791558Srgrimes#ifdef SECURE
8801558Srgrimes	struct ttyent *typ;
8811558Srgrimes	struct passwd *pp;
8821558Srgrimes	static const char banner[] =
8831558Srgrimes		"Enter root password, or ^D to go multi-user\n";
8841558Srgrimes	char *clear, *password;
8851558Srgrimes#endif
88637814Sphk#ifdef DEBUGSHELL
88737814Sphk	char altshell[128];
88837814Sphk#endif
8891558Srgrimes
8902327Sjkh	if (Reboot) {
89147962Sru		/* Instead of going single user, let's reboot the machine */
8922323Snate		sync();
89347962Sru		reboot(howto);
8942323Snate		_exit(0);
8952323Snate	}
8962323Snate
897166484Simp	shell = get_shell();
898166484Simp
8991558Srgrimes	if ((pid = fork()) == 0) {
9001558Srgrimes		/*
9011558Srgrimes		 * Start the single user session.
9021558Srgrimes		 */
903232977Sed		open_console();
9041558Srgrimes
9051558Srgrimes#ifdef SECURE
9061558Srgrimes		/*
9071558Srgrimes		 * Check the root password.
9081558Srgrimes		 * We don't care if the console is 'on' by default;
9091558Srgrimes		 * it's the only tty that can be 'off' and 'secure'.
9101558Srgrimes		 */
9111558Srgrimes		typ = getttynam("console");
9121558Srgrimes		pp = getpwnam("root");
91353550Sdillon		if (typ && (typ->ty_status & TTY_SECURE) == 0 &&
91453550Sdillon		    pp && *pp->pw_passwd) {
915166484Simp			write_stderr(banner);
9161558Srgrimes			for (;;) {
9171558Srgrimes				clear = getpass("Password:");
9181558Srgrimes				if (clear == 0 || *clear == '\0')
9191558Srgrimes					_exit(0);
9201558Srgrimes				password = crypt(clear, pp->pw_passwd);
9211558Srgrimes				bzero(clear, _PASSWORD_LEN);
922232841Sed				if (password == NULL ||
923231994Skevlo				    strcmp(password, pp->pw_passwd) == 0)
9241558Srgrimes					break;
9251558Srgrimes				warning("single-user login failed\n");
9261558Srgrimes			}
9271558Srgrimes		}
9281558Srgrimes		endttyent();
9291558Srgrimes		endpwent();
9301558Srgrimes#endif /* SECURE */
9311558Srgrimes
9321558Srgrimes#ifdef DEBUGSHELL
9331558Srgrimes		{
93437814Sphk			char *cp = altshell;
9351558Srgrimes			int num;
9361558Srgrimes
937166484Simp#define	SHREQUEST "Enter full pathname of shell or RETURN for "
938166484Simp			write_stderr(SHREQUEST);
939166484Simp			write_stderr(shell);
940166484Simp			write_stderr(": ");
9411558Srgrimes			while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
9421558Srgrimes			    num != 0 && *cp != '\n' && cp < &altshell[127])
943173785Sobrien				cp++;
9441558Srgrimes			*cp = '\0';
9451558Srgrimes			if (altshell[0] != '\0')
9461558Srgrimes				shell = altshell;
9471558Srgrimes		}
9481558Srgrimes#endif /* DEBUGSHELL */
9491558Srgrimes
9501558Srgrimes		/*
9511558Srgrimes		 * Unblock signals.
9521558Srgrimes		 * We catch all the interesting ones,
9531558Srgrimes		 * and those are reset to SIG_DFL on exec.
9541558Srgrimes		 */
9551558Srgrimes		sigemptyset(&mask);
9561558Srgrimes		sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
9571558Srgrimes
9581558Srgrimes		/*
9591558Srgrimes		 * Fire off a shell.
9601558Srgrimes		 * If the default one doesn't work, try the Bourne shell.
9611558Srgrimes		 */
962140070Sdelphij
963140070Sdelphij		char name[] = "-sh";
964140070Sdelphij
965140070Sdelphij		argv[0] = name;
9661558Srgrimes		argv[1] = 0;
9671558Srgrimes		execv(shell, argv);
9681558Srgrimes		emergency("can't exec %s for single user: %m", shell);
9691558Srgrimes		execv(_PATH_BSHELL, argv);
9701558Srgrimes		emergency("can't exec %s for single user: %m", _PATH_BSHELL);
9711558Srgrimes		sleep(STALL_TIMEOUT);
9721558Srgrimes		_exit(1);
9731558Srgrimes	}
9741558Srgrimes
9751558Srgrimes	if (pid == -1) {
9761558Srgrimes		/*
9771558Srgrimes		 * We are seriously hosed.  Do our best.
9781558Srgrimes		 */
9791558Srgrimes		emergency("can't fork single-user shell, trying again");
9801558Srgrimes		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
9811558Srgrimes			continue;
9821558Srgrimes		return (state_func_t) single_user;
9831558Srgrimes	}
9841558Srgrimes
9851558Srgrimes	requested_transition = 0;
9861558Srgrimes	do {
9871558Srgrimes		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
9881558Srgrimes			collect_child(wpid);
9891558Srgrimes		if (wpid == -1) {
9901558Srgrimes			if (errno == EINTR)
9911558Srgrimes				continue;
9921558Srgrimes			warning("wait for single-user shell failed: %m; restarting");
9931558Srgrimes			return (state_func_t) single_user;
9941558Srgrimes		}
9951558Srgrimes		if (wpid == pid && WIFSTOPPED(status)) {
9961558Srgrimes			warning("init: shell stopped, restarting\n");
9971558Srgrimes			kill(pid, SIGCONT);
9981558Srgrimes			wpid = -1;
9991558Srgrimes		}
10001558Srgrimes	} while (wpid != pid && !requested_transition);
10011558Srgrimes
10021558Srgrimes	if (requested_transition)
10031558Srgrimes		return (state_func_t) requested_transition;
10041558Srgrimes
10051558Srgrimes	if (!WIFEXITED(status)) {
10068871Srgrimes		if (WTERMSIG(status) == SIGKILL) {
10078871Srgrimes			/*
10088871Srgrimes			 *  reboot(8) killed shell?
10091558Srgrimes			 */
10101558Srgrimes			warning("single user shell terminated.");
10111558Srgrimes			sleep(STALL_TIMEOUT);
10121558Srgrimes			_exit(0);
10138871Srgrimes		} else {
10141558Srgrimes			warning("single user shell terminated, restarting");
10151558Srgrimes			return (state_func_t) single_user;
10161558Srgrimes		}
10171558Srgrimes	}
10181558Srgrimes
10191558Srgrimes	runcom_mode = FASTBOOT;
10201558Srgrimes	return (state_func_t) runcom;
10211558Srgrimes}
10221558Srgrimes
10231558Srgrimes/*
10241558Srgrimes * Run the system startup script.
10251558Srgrimes */
1026183391Sdelphijstatic state_func_t
102792838Simpruncom(void)
10281558Srgrimes{
1029166484Simp	state_func_t next_transition;
1030166484Simp
1031166484Simp	if ((next_transition = run_script(_PATH_RUNCOM)) != 0)
1032166484Simp		return next_transition;
1033166484Simp
1034166484Simp	runcom_mode = AUTOBOOT;		/* the default */
1035166484Simp	return (state_func_t) read_ttys;
1036166484Simp}
1037166484Simp
1038166484Simp/*
1039166484Simp * Run a shell script.
1040166484Simp * Returns 0 on success, otherwise the next transition to enter:
1041166484Simp *  - single_user if fork/execv/waitpid failed, or if the script
1042166484Simp *    terminated with a signal or exit code != 0.
1043217750Sjilles *  - death_single if a SIGTERM was delivered to init(8).
1044166484Simp */
1045183391Sdelphijstatic state_func_t
1046166484Simprun_script(const char *script)
1047166484Simp{
10481558Srgrimes	pid_t pid, wpid;
10491558Srgrimes	int status;
10501558Srgrimes	char *argv[4];
1051166484Simp	const char *shell;
10521558Srgrimes	struct sigaction sa;
10531558Srgrimes
1054166484Simp	shell = get_shell();
1055166484Simp
10561558Srgrimes	if ((pid = fork()) == 0) {
10571558Srgrimes		sigemptyset(&sa.sa_mask);
10581558Srgrimes		sa.sa_flags = 0;
10591558Srgrimes		sa.sa_handler = SIG_IGN;
1060173785Sobrien		sigaction(SIGTSTP, &sa, (struct sigaction *)0);
1061173785Sobrien		sigaction(SIGHUP, &sa, (struct sigaction *)0);
10621558Srgrimes
1063232977Sed		open_console();
10641558Srgrimes
1065232841Sed		char _sh[]		= "sh";
1066140070Sdelphij		char _autoboot[]	= "autoboot";
1067140070Sdelphij
1068140070Sdelphij		argv[0] = _sh;
1069166484Simp		argv[1] = __DECONST(char *, script);
1070140070Sdelphij		argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0;
10711558Srgrimes		argv[3] = 0;
10721558Srgrimes
10731558Srgrimes		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
10741558Srgrimes
107521865Sdavidn#ifdef LOGIN_CAP
107621865Sdavidn		setprocresources(RESOURCE_RC);
107721865Sdavidn#endif
1078166484Simp		execv(shell, argv);
1079166484Simp		stall("can't exec %s for %s: %m", shell, script);
10801558Srgrimes		_exit(1);	/* force single user mode */
10811558Srgrimes	}
10821558Srgrimes
10831558Srgrimes	if (pid == -1) {
1084166484Simp		emergency("can't fork for %s on %s: %m", shell, script);
10851558Srgrimes		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
10861558Srgrimes			continue;
10871558Srgrimes		sleep(STALL_TIMEOUT);
10881558Srgrimes		return (state_func_t) single_user;
10891558Srgrimes	}
10901558Srgrimes
10911558Srgrimes	/*
10921558Srgrimes	 * Copied from single_user().  This is a bit paranoid.
10931558Srgrimes	 */
109485010Sdes	requested_transition = 0;
10951558Srgrimes	do {
10961558Srgrimes		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
10971558Srgrimes			collect_child(wpid);
10981558Srgrimes		if (wpid == -1) {
1099293744Strasz			if (requested_transition == death_single ||
1100293744Strasz			    requested_transition == reroot)
1101293744Strasz				return (state_func_t) requested_transition;
11021558Srgrimes			if (errno == EINTR)
11031558Srgrimes				continue;
1104166484Simp			warning("wait for %s on %s failed: %m; going to "
1105166484Simp			    "single user mode", shell, script);
11061558Srgrimes			return (state_func_t) single_user;
11071558Srgrimes		}
11081558Srgrimes		if (wpid == pid && WIFSTOPPED(status)) {
11091558Srgrimes			warning("init: %s on %s stopped, restarting\n",
1110173785Sobrien			    shell, script);
11111558Srgrimes			kill(pid, SIGCONT);
11121558Srgrimes			wpid = -1;
11131558Srgrimes		}
11141558Srgrimes	} while (wpid != pid);
11151558Srgrimes
11161558Srgrimes	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
11171558Srgrimes	    requested_transition == catatonia) {
11181558Srgrimes		/* /etc/rc executed /sbin/reboot; wait for the end quietly */
11191558Srgrimes		sigset_t s;
11201558Srgrimes
11211558Srgrimes		sigfillset(&s);
11221558Srgrimes		for (;;)
11231558Srgrimes			sigsuspend(&s);
11241558Srgrimes	}
11251558Srgrimes
11261558Srgrimes	if (!WIFEXITED(status)) {
1127166484Simp		warning("%s on %s terminated abnormally, going to single "
1128166484Simp		    "user mode", shell, script);
11291558Srgrimes		return (state_func_t) single_user;
11301558Srgrimes	}
11311558Srgrimes
11321558Srgrimes	if (WEXITSTATUS(status))
11331558Srgrimes		return (state_func_t) single_user;
11341558Srgrimes
1135166484Simp	return (state_func_t) 0;
11361558Srgrimes}
11371558Srgrimes
11381558Srgrimes/*
11391558Srgrimes * Open the session database.
11401558Srgrimes *
11411558Srgrimes * NB: We could pass in the size here; is it necessary?
11421558Srgrimes */
1143183391Sdelphijstatic int
114492838Simpstart_session_db(void)
11451558Srgrimes{
11461558Srgrimes	if (session_db && (*session_db->close)(session_db))
11471558Srgrimes		emergency("session database close: %s", strerror(errno));
11481558Srgrimes	if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
11491558Srgrimes		emergency("session database open: %s", strerror(errno));
11501558Srgrimes		return (1);
11511558Srgrimes	}
11521558Srgrimes	return (0);
11538871Srgrimes
11541558Srgrimes}
11551558Srgrimes
11561558Srgrimes/*
11571558Srgrimes * Add a new login session.
11581558Srgrimes */
1159183391Sdelphijstatic void
116092838Simpadd_session(session_t *sp)
11611558Srgrimes{
11621558Srgrimes	DBT key;
11631558Srgrimes	DBT data;
11641558Srgrimes
11651558Srgrimes	key.data = &sp->se_process;
11661558Srgrimes	key.size = sizeof sp->se_process;
11671558Srgrimes	data.data = &sp;
11681558Srgrimes	data.size = sizeof sp;
11691558Srgrimes
11701558Srgrimes	if ((*session_db->put)(session_db, &key, &data, 0))
11711558Srgrimes		emergency("insert %d: %s", sp->se_process, strerror(errno));
11721558Srgrimes}
11731558Srgrimes
11741558Srgrimes/*
11751558Srgrimes * Delete an old login session.
11761558Srgrimes */
1177183391Sdelphijstatic void
117892838Simpdel_session(session_t *sp)
11791558Srgrimes{
11801558Srgrimes	DBT key;
11811558Srgrimes
11821558Srgrimes	key.data = &sp->se_process;
11831558Srgrimes	key.size = sizeof sp->se_process;
11841558Srgrimes
11851558Srgrimes	if ((*session_db->del)(session_db, &key, 0))
11861558Srgrimes		emergency("delete %d: %s", sp->se_process, strerror(errno));
11871558Srgrimes}
11881558Srgrimes
11891558Srgrimes/*
11901558Srgrimes * Look up a login session by pid.
11911558Srgrimes */
1192183391Sdelphijstatic session_t *
11931558Srgrimesfind_session(pid_t pid)
11941558Srgrimes{
11951558Srgrimes	DBT key;
11961558Srgrimes	DBT data;
11971558Srgrimes	session_t *ret;
11981558Srgrimes
11991558Srgrimes	key.data = &pid;
12001558Srgrimes	key.size = sizeof pid;
12011558Srgrimes	if ((*session_db->get)(session_db, &key, &data, 0) != 0)
12021558Srgrimes		return 0;
12031558Srgrimes	bcopy(data.data, (char *)&ret, sizeof(ret));
12041558Srgrimes	return ret;
12051558Srgrimes}
12061558Srgrimes
12071558Srgrimes/*
12081558Srgrimes * Construct an argument vector from a command line.
12091558Srgrimes */
1210183391Sdelphijstatic char **
121192838Simpconstruct_argv(char *command)
12121558Srgrimes{
121392806Sobrien	int argc = 0;
121492806Sobrien	char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
12151558Srgrimes						* sizeof (char *));
12161558Srgrimes
121749018Sru	if ((argv[argc++] = strk(command)) == 0) {
121849018Sru		free(argv);
121949018Sru		return (NULL);
122049018Sru	}
122127837Sdavidn	while ((argv[argc++] = strk((char *) 0)) != NULL)
12221558Srgrimes		continue;
12231558Srgrimes	return argv;
12241558Srgrimes}
12251558Srgrimes
12261558Srgrimes/*
12271558Srgrimes * Deallocate a session descriptor.
12281558Srgrimes */
1229183391Sdelphijstatic void
123092838Simpfree_session(session_t *sp)
12311558Srgrimes{
12321558Srgrimes	free(sp->se_device);
12331558Srgrimes	if (sp->se_getty) {
12341558Srgrimes		free(sp->se_getty);
12353594Sache		free(sp->se_getty_argv_space);
12361558Srgrimes		free(sp->se_getty_argv);
12371558Srgrimes	}
12381558Srgrimes	if (sp->se_window) {
12391558Srgrimes		free(sp->se_window);
12403594Sache		free(sp->se_window_argv_space);
12411558Srgrimes		free(sp->se_window_argv);
12421558Srgrimes	}
12433594Sache	if (sp->se_type)
12443594Sache		free(sp->se_type);
12451558Srgrimes	free(sp);
12461558Srgrimes}
12471558Srgrimes
12481558Srgrimes/*
12491558Srgrimes * Allocate a new session descriptor.
125057344Salfred * Mark it SE_PRESENT.
12511558Srgrimes */
1252183391Sdelphijstatic session_t *
125392838Simpnew_session(session_t *sprev, int session_index, struct ttyent *typ)
12541558Srgrimes{
125592806Sobrien	session_t *sp;
125627029Spst	int fd;
12571558Srgrimes
12581558Srgrimes	if ((typ->ty_status & TTY_ON) == 0 ||
12591558Srgrimes	    typ->ty_name == 0 ||
12601558Srgrimes	    typ->ty_getty == 0)
12611558Srgrimes		return 0;
12621558Srgrimes
126327215Sache	sp = (session_t *) calloc(1, sizeof (session_t));
12641558Srgrimes
12651558Srgrimes	sp->se_index = session_index;
126657344Salfred	sp->se_flags |= SE_PRESENT;
12671558Srgrimes
12681558Srgrimes	sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
1269173785Sobrien	sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
12701558Srgrimes
127127029Spst	/*
127227029Spst	 * Attempt to open the device, if we get "device not configured"
127327029Spst	 * then don't add the device to the session list.
127427029Spst	 */
127527029Spst	if ((fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0)) < 0) {
1276135868Simp		if (errno == ENXIO) {
127727029Spst			free_session(sp);
127827029Spst			return (0);
127927029Spst		}
128027029Spst	} else
128127029Spst		close(fd);
128227029Spst
12831558Srgrimes	if (setupargv(sp, typ) == 0) {
12841558Srgrimes		free_session(sp);
12851558Srgrimes		return (0);
12861558Srgrimes	}
12871558Srgrimes
12881558Srgrimes	sp->se_next = 0;
12891558Srgrimes	if (sprev == 0) {
12901558Srgrimes		sessions = sp;
12911558Srgrimes		sp->se_prev = 0;
12921558Srgrimes	} else {
12931558Srgrimes		sprev->se_next = sp;
12941558Srgrimes		sp->se_prev = sprev;
12951558Srgrimes	}
12961558Srgrimes
12971558Srgrimes	return sp;
12981558Srgrimes}
12991558Srgrimes
13001558Srgrimes/*
13011558Srgrimes * Calculate getty and if useful window argv vectors.
13021558Srgrimes */
1303183391Sdelphijstatic int
130492838Simpsetupargv(session_t *sp, struct ttyent *typ)
13051558Srgrimes{
13061558Srgrimes
13071558Srgrimes	if (sp->se_getty) {
13081558Srgrimes		free(sp->se_getty);
13093594Sache		free(sp->se_getty_argv_space);
13101558Srgrimes		free(sp->se_getty_argv);
13111558Srgrimes	}
13121558Srgrimes	sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2);
1313173785Sobrien	sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
13143594Sache	sp->se_getty_argv_space = strdup(sp->se_getty);
13153594Sache	sp->se_getty_argv = construct_argv(sp->se_getty_argv_space);
13161558Srgrimes	if (sp->se_getty_argv == 0) {
13171558Srgrimes		warning("can't parse getty for port %s", sp->se_device);
13181558Srgrimes		free(sp->se_getty);
13193594Sache		free(sp->se_getty_argv_space);
13203594Sache		sp->se_getty = sp->se_getty_argv_space = 0;
13211558Srgrimes		return (0);
13221558Srgrimes	}
13233594Sache	if (sp->se_window) {
132449018Sru		free(sp->se_window);
13253594Sache		free(sp->se_window_argv_space);
13263594Sache		free(sp->se_window_argv);
13273594Sache	}
13283594Sache	sp->se_window = sp->se_window_argv_space = 0;
13293594Sache	sp->se_window_argv = 0;
13301558Srgrimes	if (typ->ty_window) {
13311558Srgrimes		sp->se_window = strdup(typ->ty_window);
13323594Sache		sp->se_window_argv_space = strdup(sp->se_window);
13333594Sache		sp->se_window_argv = construct_argv(sp->se_window_argv_space);
13341558Srgrimes		if (sp->se_window_argv == 0) {
13351558Srgrimes			warning("can't parse window for port %s",
1336173785Sobrien			    sp->se_device);
13373594Sache			free(sp->se_window_argv_space);
13381558Srgrimes			free(sp->se_window);
13393594Sache			sp->se_window = sp->se_window_argv_space = 0;
13401558Srgrimes			return (0);
13411558Srgrimes		}
13421558Srgrimes	}
13433594Sache	if (sp->se_type)
13443594Sache		free(sp->se_type);
13453594Sache	sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0;
13461558Srgrimes	return (1);
13471558Srgrimes}
13481558Srgrimes
13491558Srgrimes/*
13501558Srgrimes * Walk the list of ttys and create sessions for each active line.
13511558Srgrimes */
1352183391Sdelphijstatic state_func_t
135392838Simpread_ttys(void)
13541558Srgrimes{
13551558Srgrimes	int session_index = 0;
135692806Sobrien	session_t *sp, *snext;
135792806Sobrien	struct ttyent *typ;
13581558Srgrimes
13591558Srgrimes	/*
13601558Srgrimes	 * Destroy any previous session state.
13611558Srgrimes	 * There shouldn't be any, but just in case...
13621558Srgrimes	 */
13631558Srgrimes	for (sp = sessions; sp; sp = snext) {
13641558Srgrimes		snext = sp->se_next;
13651558Srgrimes		free_session(sp);
13661558Srgrimes	}
13671558Srgrimes	sessions = 0;
13681558Srgrimes	if (start_session_db())
13691558Srgrimes		return (state_func_t) single_user;
13701558Srgrimes
13711558Srgrimes	/*
13721558Srgrimes	 * Allocate a session entry for each active port.
13731558Srgrimes	 * Note that sp starts at 0.
13741558Srgrimes	 */
137527837Sdavidn	while ((typ = getttyent()) != NULL)
137627837Sdavidn		if ((snext = new_session(sp, ++session_index, typ)) != NULL)
13771558Srgrimes			sp = snext;
13781558Srgrimes
13791558Srgrimes	endttyent();
13801558Srgrimes
13811558Srgrimes	return (state_func_t) multi_user;
13821558Srgrimes}
13831558Srgrimes
13841558Srgrimes/*
13851558Srgrimes * Start a window system running.
13861558Srgrimes */
1387183391Sdelphijstatic void
138892838Simpstart_window_system(session_t *sp)
13891558Srgrimes{
13901558Srgrimes	pid_t pid;
13911558Srgrimes	sigset_t mask;
13923594Sache	char term[64], *env[2];
1393159402Skib	int status;
13941558Srgrimes
13951558Srgrimes	if ((pid = fork()) == -1) {
13961558Srgrimes		emergency("can't fork for window system on port %s: %m",
1397173785Sobrien		    sp->se_device);
13981558Srgrimes		/* hope that getty fails and we can try again */
13991558Srgrimes		return;
14001558Srgrimes	}
1401173785Sobrien	if (pid) {
1402159402Skib		waitpid(-1, &status, 0);
14031558Srgrimes		return;
1404159402Skib	}
14051558Srgrimes
1406159402Skib	/* reparent window process to the init to not make a zombie on exit */
1407159402Skib	if ((pid = fork()) == -1) {
1408159402Skib		emergency("can't fork for window system on port %s: %m",
1409173785Sobrien		    sp->se_device);
1410159402Skib		_exit(1);
1411159402Skib	}
1412159402Skib	if (pid)
1413159402Skib		_exit(0);
1414159402Skib
14151558Srgrimes	sigemptyset(&mask);
14161558Srgrimes	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
14171558Srgrimes
14181558Srgrimes	if (setsid() < 0)
14191558Srgrimes		emergency("setsid failed (window) %m");
14201558Srgrimes
142121865Sdavidn#ifdef LOGIN_CAP
142221865Sdavidn	setprocresources(RESOURCE_WINDOW);
142321865Sdavidn#endif
14243594Sache	if (sp->se_type) {
14253594Sache		/* Don't use malloc after fork */
14263594Sache		strcpy(term, "TERM=");
142722922Sdg		strncat(term, sp->se_type, sizeof(term) - 6);
14283594Sache		env[0] = term;
14293594Sache		env[1] = 0;
14303594Sache	}
14313594Sache	else
14323594Sache		env[0] = 0;
14333594Sache	execve(sp->se_window_argv[0], sp->se_window_argv, env);
14341558Srgrimes	stall("can't exec window system '%s' for port %s: %m",
14351558Srgrimes		sp->se_window_argv[0], sp->se_device);
14361558Srgrimes	_exit(1);
14371558Srgrimes}
14381558Srgrimes
14391558Srgrimes/*
14401558Srgrimes * Start a login session running.
14411558Srgrimes */
1442183391Sdelphijstatic pid_t
144392838Simpstart_getty(session_t *sp)
14441558Srgrimes{
14451558Srgrimes	pid_t pid;
14461558Srgrimes	sigset_t mask;
14471558Srgrimes	time_t current_time = time((time_t *) 0);
14489997Sache	int too_quick = 0;
14493594Sache	char term[64], *env[2];
14501558Srgrimes
145110006Smpp	if (current_time >= sp->se_started &&
14529997Sache	    current_time - sp->se_started < GETTY_SPACING) {
14539997Sache		if (++sp->se_nspace > GETTY_NSPACE) {
14549997Sache			sp->se_nspace = 0;
14559997Sache			too_quick = 1;
14569997Sache		}
14579997Sache	} else
14589997Sache		sp->se_nspace = 0;
14599997Sache
14601558Srgrimes	/*
14611558Srgrimes	 * fork(), not vfork() -- we can't afford to block.
14621558Srgrimes	 */
14631558Srgrimes	if ((pid = fork()) == -1) {
14641558Srgrimes		emergency("can't fork for getty on port %s: %m", sp->se_device);
14651558Srgrimes		return -1;
14661558Srgrimes	}
14671558Srgrimes
14681558Srgrimes	if (pid)
14691558Srgrimes		return pid;
14701558Srgrimes
14719997Sache	if (too_quick) {
14729997Sache		warning("getty repeating too quickly on port %s, sleeping %d secs",
1473173785Sobrien		    sp->se_device, GETTY_SLEEP);
14741558Srgrimes		sleep((unsigned) GETTY_SLEEP);
14751558Srgrimes	}
14761558Srgrimes
14771558Srgrimes	if (sp->se_window) {
14781558Srgrimes		start_window_system(sp);
14791558Srgrimes		sleep(WINDOW_WAIT);
14801558Srgrimes	}
14811558Srgrimes
14821558Srgrimes	sigemptyset(&mask);
14831558Srgrimes	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
14841558Srgrimes
148521865Sdavidn#ifdef LOGIN_CAP
148621865Sdavidn	setprocresources(RESOURCE_GETTY);
148721865Sdavidn#endif
14883594Sache	if (sp->se_type) {
14893594Sache		/* Don't use malloc after fork */
14903594Sache		strcpy(term, "TERM=");
149122922Sdg		strncat(term, sp->se_type, sizeof(term) - 6);
14923594Sache		env[0] = term;
14933594Sache		env[1] = 0;
1494173785Sobrien	} else
14953594Sache		env[0] = 0;
14963594Sache	execve(sp->se_getty_argv[0], sp->se_getty_argv, env);
14971558Srgrimes	stall("can't exec getty '%s' for port %s: %m",
14981558Srgrimes		sp->se_getty_argv[0], sp->se_device);
14991558Srgrimes	_exit(1);
15001558Srgrimes}
15011558Srgrimes
15021558Srgrimes/*
15031558Srgrimes * Collect exit status for a child.
15041558Srgrimes * If an exiting login, start a new login running.
15051558Srgrimes */
1506183391Sdelphijstatic void
15071558Srgrimescollect_child(pid_t pid)
15081558Srgrimes{
150992806Sobrien	session_t *sp, *sprev, *snext;
15101558Srgrimes
15111558Srgrimes	if (! sessions)
15121558Srgrimes		return;
15131558Srgrimes
15141558Srgrimes	if (! (sp = find_session(pid)))
15151558Srgrimes		return;
15161558Srgrimes
15171558Srgrimes	del_session(sp);
15181558Srgrimes	sp->se_process = 0;
15191558Srgrimes
15201558Srgrimes	if (sp->se_flags & SE_SHUTDOWN) {
152127837Sdavidn		if ((sprev = sp->se_prev) != NULL)
15221558Srgrimes			sprev->se_next = sp->se_next;
15231558Srgrimes		else
15241558Srgrimes			sessions = sp->se_next;
152527837Sdavidn		if ((snext = sp->se_next) != NULL)
15261558Srgrimes			snext->se_prev = sp->se_prev;
15271558Srgrimes		free_session(sp);
15281558Srgrimes		return;
15291558Srgrimes	}
15301558Srgrimes
15311558Srgrimes	if ((pid = start_getty(sp)) == -1) {
15321558Srgrimes		/* serious trouble */
15331558Srgrimes		requested_transition = clean_ttys;
15341558Srgrimes		return;
15351558Srgrimes	}
15361558Srgrimes
15371558Srgrimes	sp->se_process = pid;
15381558Srgrimes	sp->se_started = time((time_t *) 0);
15391558Srgrimes	add_session(sp);
15401558Srgrimes}
15411558Srgrimes
15421558Srgrimes/*
15431558Srgrimes * Catch a signal and request a state transition.
15441558Srgrimes */
1545183391Sdelphijstatic void
154692838Simptransition_handler(int sig)
15471558Srgrimes{
15481558Srgrimes
15491558Srgrimes	switch (sig) {
15501558Srgrimes	case SIGHUP:
1551217750Sjilles		if (current_state == read_ttys || current_state == multi_user ||
1552217750Sjilles		    current_state == clean_ttys || current_state == catatonia)
1553217750Sjilles			requested_transition = clean_ttys;
15541558Srgrimes		break;
155547962Sru	case SIGUSR2:
155647962Sru		howto = RB_POWEROFF;
155747962Sru	case SIGUSR1:
155847962Sru		howto |= RB_HALT;
15592323Snate	case SIGINT:
15602327Sjkh		Reboot = TRUE;
15611558Srgrimes	case SIGTERM:
1562217750Sjilles		if (current_state == read_ttys || current_state == multi_user ||
1563217750Sjilles		    current_state == clean_ttys || current_state == catatonia)
1564217750Sjilles			requested_transition = death;
1565217750Sjilles		else
1566217750Sjilles			requested_transition = death_single;
15671558Srgrimes		break;
15681558Srgrimes	case SIGTSTP:
1569217750Sjilles		if (current_state == runcom || current_state == read_ttys ||
1570217750Sjilles		    current_state == clean_ttys ||
1571217750Sjilles		    current_state == multi_user || current_state == catatonia)
1572217750Sjilles			requested_transition = catatonia;
15731558Srgrimes		break;
1574293744Strasz	case SIGEMT:
1575293744Strasz		requested_transition = reroot;
1576293744Strasz		break;
15771558Srgrimes	default:
15781558Srgrimes		requested_transition = 0;
15791558Srgrimes		break;
15801558Srgrimes	}
15811558Srgrimes}
15821558Srgrimes
15831558Srgrimes/*
15841558Srgrimes * Take the system multiuser.
15851558Srgrimes */
1586183391Sdelphijstatic state_func_t
158792838Simpmulti_user(void)
15881558Srgrimes{
15891558Srgrimes	pid_t pid;
159092806Sobrien	session_t *sp;
15911558Srgrimes
15921558Srgrimes	requested_transition = 0;
15931558Srgrimes
15941558Srgrimes	/*
15951558Srgrimes	 * If the administrator has not set the security level to -1
15961558Srgrimes	 * to indicate that the kernel should not run multiuser in secure
15978871Srgrimes	 * mode, and the run script has not set a higher level of security
15981558Srgrimes	 * than level 1, then put the kernel into secure mode.
15991558Srgrimes	 */
16001558Srgrimes	if (getsecuritylevel() == 0)
16011558Srgrimes		setsecuritylevel(1);
16021558Srgrimes
16031558Srgrimes	for (sp = sessions; sp; sp = sp->se_next) {
16041558Srgrimes		if (sp->se_process)
16051558Srgrimes			continue;
16061558Srgrimes		if ((pid = start_getty(sp)) == -1) {
16071558Srgrimes			/* serious trouble */
16081558Srgrimes			requested_transition = clean_ttys;
16091558Srgrimes			break;
16101558Srgrimes		}
16111558Srgrimes		sp->se_process = pid;
16121558Srgrimes		sp->se_started = time((time_t *) 0);
16131558Srgrimes		add_session(sp);
16141558Srgrimes	}
16151558Srgrimes
16161558Srgrimes	while (!requested_transition)
16171558Srgrimes		if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
16181558Srgrimes			collect_child(pid);
16191558Srgrimes
16201558Srgrimes	return (state_func_t) requested_transition;
16211558Srgrimes}
16221558Srgrimes
16231558Srgrimes/*
162457344Salfred * This is an (n*2)+(n^2) algorithm.  We hope it isn't run often...
16251558Srgrimes */
1626183391Sdelphijstatic state_func_t
162792838Simpclean_ttys(void)
16281558Srgrimes{
162992806Sobrien	session_t *sp, *sprev;
163092806Sobrien	struct ttyent *typ;
163192806Sobrien	int session_index = 0;
163292806Sobrien	int devlen;
16333594Sache	char *old_getty, *old_window, *old_type;
16341558Srgrimes
1635173787Sobrien	/*
1636173787Sobrien	 * mark all sessions for death, (!SE_PRESENT)
163757344Salfred	 * as we find or create new ones they'll be marked as keepers,
163857344Salfred	 * we'll later nuke all the ones not found in /etc/ttys
163957344Salfred	 */
164057344Salfred	for (sp = sessions; sp != NULL; sp = sp->se_next)
164157344Salfred		sp->se_flags &= ~SE_PRESENT;
164257344Salfred
16431558Srgrimes	devlen = sizeof(_PATH_DEV) - 1;
164427837Sdavidn	while ((typ = getttyent()) != NULL) {
16451558Srgrimes		++session_index;
16461558Srgrimes
16471558Srgrimes		for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
16481558Srgrimes			if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
16491558Srgrimes				break;
16501558Srgrimes
16511558Srgrimes		if (sp) {
165257344Salfred			/* we want this one to live */
165357344Salfred			sp->se_flags |= SE_PRESENT;
16541558Srgrimes			if (sp->se_index != session_index) {
16551558Srgrimes				warning("port %s changed utmp index from %d to %d",
16561558Srgrimes				       sp->se_device, sp->se_index,
16571558Srgrimes				       session_index);
16581558Srgrimes				sp->se_index = session_index;
16591558Srgrimes			}
16601558Srgrimes			if ((typ->ty_status & TTY_ON) == 0 ||
16611558Srgrimes			    typ->ty_getty == 0) {
16621558Srgrimes				sp->se_flags |= SE_SHUTDOWN;
16631558Srgrimes				kill(sp->se_process, SIGHUP);
16641558Srgrimes				continue;
16651558Srgrimes			}
16661558Srgrimes			sp->se_flags &= ~SE_SHUTDOWN;
16673594Sache			old_getty = sp->se_getty ? strdup(sp->se_getty) : 0;
16683594Sache			old_window = sp->se_window ? strdup(sp->se_window) : 0;
16693594Sache			old_type = sp->se_type ? strdup(sp->se_type) : 0;
16701558Srgrimes			if (setupargv(sp, typ) == 0) {
16711558Srgrimes				warning("can't parse getty for port %s",
16721558Srgrimes					sp->se_device);
16731558Srgrimes				sp->se_flags |= SE_SHUTDOWN;
16741558Srgrimes				kill(sp->se_process, SIGHUP);
16751558Srgrimes			}
16763594Sache			else if (   !old_getty
167727837Sdavidn				 || (!old_type && sp->se_type)
167827837Sdavidn				 || (old_type && !sp->se_type)
167927837Sdavidn				 || (!old_window && sp->se_window)
168027837Sdavidn				 || (old_window && !sp->se_window)
168127837Sdavidn				 || (strcmp(old_getty, sp->se_getty) != 0)
168227837Sdavidn				 || (old_window && strcmp(old_window, sp->se_window) != 0)
168327837Sdavidn				 || (old_type && strcmp(old_type, sp->se_type) != 0)
16843594Sache				) {
16853594Sache				/* Don't set SE_SHUTDOWN here */
16863594Sache				sp->se_nspace = 0;
16873594Sache				sp->se_started = 0;
16883594Sache				kill(sp->se_process, SIGHUP);
16893594Sache			}
16903594Sache			if (old_getty)
16913594Sache				free(old_getty);
169278484Smikeh			if (old_window)
16933594Sache				free(old_window);
16943594Sache			if (old_type)
16953594Sache				free(old_type);
16961558Srgrimes			continue;
16971558Srgrimes		}
16981558Srgrimes
16991558Srgrimes		new_session(sprev, session_index, typ);
17001558Srgrimes	}
17011558Srgrimes
17021558Srgrimes	endttyent();
17031558Srgrimes
170457344Salfred	/*
170557344Salfred	 * sweep through and kill all deleted sessions
170657344Salfred	 * ones who's /etc/ttys line was deleted (SE_PRESENT unset)
170757344Salfred	 */
170857344Salfred	for (sp = sessions; sp != NULL; sp = sp->se_next) {
170957344Salfred		if ((sp->se_flags & SE_PRESENT) == 0) {
171057344Salfred			sp->se_flags |= SE_SHUTDOWN;
171157344Salfred			kill(sp->se_process, SIGHUP);
171257344Salfred		}
171357344Salfred	}
171457344Salfred
17151558Srgrimes	return (state_func_t) multi_user;
17161558Srgrimes}
17171558Srgrimes
17181558Srgrimes/*
17191558Srgrimes * Block further logins.
17201558Srgrimes */
1721183391Sdelphijstatic state_func_t
172292838Simpcatatonia(void)
17231558Srgrimes{
172492806Sobrien	session_t *sp;
17251558Srgrimes
17261558Srgrimes	for (sp = sessions; sp; sp = sp->se_next)
17271558Srgrimes		sp->se_flags |= SE_SHUTDOWN;
17281558Srgrimes
17291558Srgrimes	return (state_func_t) multi_user;
17301558Srgrimes}
17311558Srgrimes
17321558Srgrimes/*
17331558Srgrimes * Note SIGALRM.
17341558Srgrimes */
1735183391Sdelphijstatic void
173692838Simpalrm_handler(int sig)
17371558Srgrimes{
1738173785Sobrien
173927837Sdavidn	(void)sig;
17401558Srgrimes	clang = 1;
17411558Srgrimes}
17421558Srgrimes
17431558Srgrimes/*
17441558Srgrimes * Bring the system down to single user.
17451558Srgrimes */
1746183391Sdelphijstatic state_func_t
174792838Simpdeath(void)
17481558Srgrimes{
1749289032Scperciva	int block, blocked;
1750289032Scperciva	size_t len;
17511558Srgrimes
1752289032Scperciva	/* Temporarily block suspend. */
1753289032Scperciva	len = sizeof(blocked);
1754289032Scperciva	block = 1;
1755289032Scperciva	if (sysctlbyname("kern.suspend_blocked", &blocked, &len,
1756289032Scperciva	    &block, sizeof(block)) == -1)
1757289032Scperciva		blocked = 0;
1758289032Scperciva
1759194198Sed	/*
1760194198Sed	 * Also revoke the TTY here.  Because runshutdown() may reopen
1761194198Sed	 * the TTY whose getty we're killing here, there is no guarantee
1762194198Sed	 * runshutdown() will perform the initial open() call, causing
1763194198Sed	 * the terminal attributes to be misconfigured.
1764194198Sed	 */
1765293744Strasz	revoke_ttys();
17661558Srgrimes
176727837Sdavidn	/* Try to run the rc.shutdown script within a period of time */
1768173785Sobrien	runshutdown();
1769173785Sobrien
1770289032Scperciva	/* Unblock suspend if we blocked it. */
1771289032Scperciva	if (!blocked)
1772289032Scperciva		sysctlbyname("kern.suspend_blocked", NULL, NULL,
1773289032Scperciva		    &blocked, sizeof(blocked));
1774289032Scperciva
1775217750Sjilles	return (state_func_t) death_single;
1776217750Sjilles}
1777217750Sjilles
1778217750Sjilles/*
1779217750Sjilles * Do what is necessary to reinitialize single user mode or reboot
1780217750Sjilles * from an incomplete state.
1781217750Sjilles */
1782217750Sjillesstatic state_func_t
1783217750Sjillesdeath_single(void)
1784217750Sjilles{
1785217750Sjilles	int i;
1786217750Sjilles	pid_t pid;
1787217750Sjilles	static const int death_sigs[2] = { SIGTERM, SIGKILL };
1788217750Sjilles
1789217750Sjilles	revoke(_PATH_CONSOLE);
1790217750Sjilles
179127197Sache	for (i = 0; i < 2; ++i) {
17921558Srgrimes		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
17931558Srgrimes			return (state_func_t) single_user;
17941558Srgrimes
17951558Srgrimes		clang = 0;
17961558Srgrimes		alarm(DEATH_WATCH);
17971558Srgrimes		do
17981558Srgrimes			if ((pid = waitpid(-1, (int *)0, 0)) != -1)
17991558Srgrimes				collect_child(pid);
18001558Srgrimes		while (clang == 0 && errno != ECHILD);
18011558Srgrimes
18021558Srgrimes		if (errno == ECHILD)
18031558Srgrimes			return (state_func_t) single_user;
18041558Srgrimes	}
18051558Srgrimes
18061558Srgrimes	warning("some processes would not die; ps axl advised");
18071558Srgrimes
18081558Srgrimes	return (state_func_t) single_user;
18091558Srgrimes}
181027837Sdavidn
1811293744Straszstatic void
1812293744Straszrevoke_ttys(void)
1813293744Strasz{
1814293744Strasz	session_t *sp;
1815293744Strasz
1816293744Strasz	for (sp = sessions; sp; sp = sp->se_next) {
1817293744Strasz		sp->se_flags |= SE_SHUTDOWN;
1818293744Strasz		kill(sp->se_process, SIGHUP);
1819293744Strasz		revoke(sp->se_device);
1820293744Strasz	}
1821293744Strasz}
1822293744Strasz
182327837Sdavidn/*
182427837Sdavidn * Run the system shutdown script.
182527837Sdavidn *
182627837Sdavidn * Exit codes:      XXX I should document more
182727837Sdavidn * -2       shutdown script terminated abnormally
182827837Sdavidn * -1       fatal error - can't run script
182927837Sdavidn * 0        good.
183027837Sdavidn * >0       some error (exit code)
183127837Sdavidn */
1832183391Sdelphijstatic int
183392838Simprunshutdown(void)
183427837Sdavidn{
183527837Sdavidn	pid_t pid, wpid;
183627837Sdavidn	int status;
183727837Sdavidn	int shutdowntimeout;
183827837Sdavidn	size_t len;
183953550Sdillon	char *argv[4];
1840166484Simp	const char *shell;
184127837Sdavidn	struct sigaction sa;
184228344Sdavidn	struct stat sb;
184327837Sdavidn
184428344Sdavidn	/*
184528344Sdavidn	 * rc.shutdown is optional, so to prevent any unnecessary
184628344Sdavidn	 * complaints from the shell we simply don't run it if the
184728344Sdavidn	 * file does not exist. If the stat() here fails for other
184828344Sdavidn	 * reasons, we'll let the shell complain.
184928344Sdavidn	 */
185028344Sdavidn	if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT)
185128344Sdavidn		return 0;
185228344Sdavidn
1853166484Simp	shell = get_shell();
1854166484Simp
185527837Sdavidn	if ((pid = fork()) == 0) {
185627837Sdavidn		sigemptyset(&sa.sa_mask);
185727837Sdavidn		sa.sa_flags = 0;
185827837Sdavidn		sa.sa_handler = SIG_IGN;
1859173785Sobrien		sigaction(SIGTSTP, &sa, (struct sigaction *)0);
1860173785Sobrien		sigaction(SIGHUP, &sa, (struct sigaction *)0);
186127837Sdavidn
1862232977Sed		open_console();
186327837Sdavidn
1864140070Sdelphij		char _sh[]	= "sh";
1865140070Sdelphij		char _reboot[]	= "reboot";
1866140070Sdelphij		char _single[]	= "single";
1867140070Sdelphij		char _path_rundown[] = _PATH_RUNDOWN;
1868140070Sdelphij
1869140070Sdelphij		argv[0] = _sh;
1870140070Sdelphij		argv[1] = _path_rundown;
1871140070Sdelphij		argv[2] = Reboot ? _reboot : _single;
187253550Sdillon		argv[3] = 0;
187327837Sdavidn
187427837Sdavidn		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
187527837Sdavidn
187627941Sache#ifdef LOGIN_CAP
187727941Sache		setprocresources(RESOURCE_RC);
187827941Sache#endif
1879166484Simp		execv(shell, argv);
1880166484Simp		warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN);
188127837Sdavidn		_exit(1);	/* force single user mode */
188227837Sdavidn	}
188327837Sdavidn
188427837Sdavidn	if (pid == -1) {
1885166484Simp		emergency("can't fork for %s on %s: %m", shell, _PATH_RUNDOWN);
188627837Sdavidn		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
188727837Sdavidn			continue;
188827837Sdavidn		sleep(STALL_TIMEOUT);
188927837Sdavidn		return -1;
189027837Sdavidn	}
189127837Sdavidn
189227837Sdavidn	len = sizeof(shutdowntimeout);
1893173785Sobrien	if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len,
1894173785Sobrien	    NULL, 0) == -1 || shutdowntimeout < 2)
1895173785Sobrien		shutdowntimeout = DEATH_SCRIPT;
189627837Sdavidn	alarm(shutdowntimeout);
189727837Sdavidn	clang = 0;
189827837Sdavidn	/*
189927837Sdavidn	 * Copied from single_user().  This is a bit paranoid.
190027837Sdavidn	 * Use the same ALRM handler.
190127837Sdavidn	 */
190227837Sdavidn	do {
190327837Sdavidn		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
190427837Sdavidn			collect_child(wpid);
190527837Sdavidn		if (clang == 1) {
190627837Sdavidn			/* we were waiting for the sub-shell */
190727837Sdavidn			kill(wpid, SIGTERM);
1908166484Simp			warning("timeout expired for %s on %s: %m; going to "
1909166484Simp			    "single user mode", shell, _PATH_RUNDOWN);
191027837Sdavidn			return -1;
191127837Sdavidn		}
191227837Sdavidn		if (wpid == -1) {
191327837Sdavidn			if (errno == EINTR)
191427837Sdavidn				continue;
1915166484Simp			warning("wait for %s on %s failed: %m; going to "
1916166484Simp			    "single user mode", shell, _PATH_RUNDOWN);
191727837Sdavidn			return -1;
191827837Sdavidn		}
191927837Sdavidn		if (wpid == pid && WIFSTOPPED(status)) {
192027837Sdavidn			warning("init: %s on %s stopped, restarting\n",
1921166484Simp				shell, _PATH_RUNDOWN);
192227837Sdavidn			kill(pid, SIGCONT);
192327837Sdavidn			wpid = -1;
192427837Sdavidn		}
192527837Sdavidn	} while (wpid != pid && !clang);
192627837Sdavidn
192727837Sdavidn	/* Turn off the alarm */
192827837Sdavidn	alarm(0);
192927837Sdavidn
193027837Sdavidn	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
193127837Sdavidn	    requested_transition == catatonia) {
193227837Sdavidn		/*
193327837Sdavidn		 * /etc/rc.shutdown executed /sbin/reboot;
193427837Sdavidn		 * wait for the end quietly
193527837Sdavidn		 */
193627837Sdavidn		sigset_t s;
193727837Sdavidn
193827837Sdavidn		sigfillset(&s);
193927837Sdavidn		for (;;)
194027837Sdavidn			sigsuspend(&s);
194127837Sdavidn	}
194227837Sdavidn
194327837Sdavidn	if (!WIFEXITED(status)) {
1944166484Simp		warning("%s on %s terminated abnormally, going to "
1945166484Simp		    "single user mode", shell, _PATH_RUNDOWN);
194627837Sdavidn		return -2;
194727837Sdavidn	}
194827837Sdavidn
194927837Sdavidn	if ((status = WEXITSTATUS(status)) != 0)
195027837Sdavidn		warning("%s returned status %d", _PATH_RUNDOWN, status);
195127837Sdavidn
195227837Sdavidn	return status;
195327837Sdavidn}
195427837Sdavidn
1955140070Sdelphijstatic char *
1956140070Sdelphijstrk(char *p)
19575478Sache{
1958173785Sobrien	static char *t;
1959173785Sobrien	char *q;
1960173785Sobrien	int c;
19615478Sache
1962173785Sobrien	if (p)
1963173785Sobrien		t = p;
1964173785Sobrien	if (!t)
1965173785Sobrien		return 0;
19665478Sache
1967173785Sobrien	c = *t;
1968173785Sobrien	while (c == ' ' || c == '\t' )
1969173785Sobrien		c = *++t;
1970173785Sobrien	if (!c) {
1971173785Sobrien		t = 0;
1972173785Sobrien		return 0;
1973173785Sobrien	}
19745478Sache	q = t;
1975173785Sobrien	if (c == '\'') {
1976173785Sobrien		c = *++t;
1977173785Sobrien		q = t;
1978173785Sobrien		while (c && c != '\'')
1979173785Sobrien			c = *++t;
1980173785Sobrien		if (!c)  /* unterminated string */
1981173785Sobrien			q = t = 0;
1982173785Sobrien		else
1983173785Sobrien			*t++ = 0;
1984173785Sobrien	} else {
1985173785Sobrien		while (c && c != ' ' && c != '\t' )
1986173785Sobrien			c = *++t;
1987173785Sobrien		*t++ = 0;
1988173785Sobrien		if (!c)
1989173785Sobrien			t = 0;
1990173785Sobrien	}
1991173785Sobrien	return q;
19925478Sache}
199321865Sdavidn
199421865Sdavidn#ifdef LOGIN_CAP
1995183391Sdelphijstatic void
199692838Simpsetprocresources(const char *cname)
199721865Sdavidn{
199821941Sdavidn	login_cap_t *lc;
199927176Sache	if ((lc = login_getclassbyname(cname, NULL)) != NULL) {
2000173785Sobrien		setusercontext(lc, (struct passwd*)NULL, 0,
2001254288Sjilles		    LOGIN_SETPRIORITY | LOGIN_SETRESOURCES |
2002254288Sjilles		    LOGIN_SETLOGINCLASS | LOGIN_SETCPUMASK);
200321941Sdavidn		login_close(lc);
200421941Sdavidn	}
200521865Sdavidn}
200621865Sdavidn#endif
2007