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$";
451558Srgrimes#endif /* not lint */
461558Srgrimes
471558Srgrimes#include <sys/param.h>
4827837Sdavidn#include <sys/ioctl.h>
4919227Sphk#include <sys/mount.h>
501558Srgrimes#include <sys/sysctl.h>
511558Srgrimes#include <sys/wait.h>
5228344Sdavidn#include <sys/stat.h>
53101271Smux#include <sys/uio.h>
541558Srgrimes
551558Srgrimes#include <db.h>
561558Srgrimes#include <errno.h>
571558Srgrimes#include <fcntl.h>
58166484Simp#include <kenv.h>
5927186Sache#include <libutil.h>
6069793Sobrien#include <paths.h>
611558Srgrimes#include <signal.h>
621558Srgrimes#include <stdio.h>
631558Srgrimes#include <stdlib.h>
641558Srgrimes#include <string.h>
651558Srgrimes#include <syslog.h>
661558Srgrimes#include <time.h>
671558Srgrimes#include <ttyent.h>
681558Srgrimes#include <unistd.h>
692323Snate#include <sys/reboot.h>
7026594Scharnier#include <err.h>
711558Srgrimes
721558Srgrimes#include <stdarg.h>
731558Srgrimes
741558Srgrimes#ifdef SECURE
751558Srgrimes#include <pwd.h>
761558Srgrimes#endif
771558Srgrimes
7821865Sdavidn#ifdef LOGIN_CAP
7921865Sdavidn#include <login_cap.h>
8021865Sdavidn#endif
8121865Sdavidn
821558Srgrimes#include "pathnames.h"
831558Srgrimes
841558Srgrimes/*
851558Srgrimes * Sleep times; used to prevent thrashing.
861558Srgrimes */
871558Srgrimes#define	GETTY_SPACING		 5	/* N secs minimum getty spacing */
881558Srgrimes#define	GETTY_SLEEP		30	/* sleep N secs after spacing problem */
89232841Sed#define	GETTY_NSPACE		 3	/* max. spacing count to bring reaction */
901558Srgrimes#define	WINDOW_WAIT		 3	/* wait N secs after starting window */
911558Srgrimes#define	STALL_TIMEOUT		30	/* wait N secs after warning */
921558Srgrimes#define	DEATH_WATCH		10	/* wait N secs for procs to die */
93173785Sobrien#define	DEATH_SCRIPT		120	/* wait for 2min for /etc/rc.shutdown */
94173785Sobrien#define	RESOURCE_RC		"daemon"
95232841Sed#define	RESOURCE_WINDOW		"default"
96173785Sobrien#define	RESOURCE_GETTY		"default"
971558Srgrimes
98183391Sdelphijstatic void handle(sig_t, ...);
99183391Sdelphijstatic void delset(sigset_t *, ...);
1001558Srgrimes
101183391Sdelphijstatic void stall(const char *, ...) __printflike(1, 2);
102183391Sdelphijstatic void warning(const char *, ...) __printflike(1, 2);
103183391Sdelphijstatic void emergency(const char *, ...) __printflike(1, 2);
104183391Sdelphijstatic void disaster(int);
105183391Sdelphijstatic void badsys(int);
106183391Sdelphijstatic int  runshutdown(void);
107140070Sdelphijstatic char *strk(char *);
1081558Srgrimes
1091558Srgrimes/*
1101558Srgrimes * We really need a recursive typedef...
1111558Srgrimes * The following at least guarantees that the return type of (*state_t)()
1121558Srgrimes * is sufficiently wide to hold a function pointer.
1131558Srgrimes */
11492838Simptypedef long (*state_func_t)(void);
11592838Simptypedef state_func_t (*state_t)(void);
1161558Srgrimes
117183391Sdelphijstatic state_func_t single_user(void);
118183391Sdelphijstatic state_func_t runcom(void);
119183391Sdelphijstatic state_func_t read_ttys(void);
120183391Sdelphijstatic state_func_t multi_user(void);
121183391Sdelphijstatic state_func_t clean_ttys(void);
122183391Sdelphijstatic state_func_t catatonia(void);
123183391Sdelphijstatic state_func_t death(void);
124217750Sjillesstatic state_func_t death_single(void);
1251558Srgrimes
126183391Sdelphijstatic state_func_t run_script(const char *);
127166484Simp
128227081Sedstatic enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
12911910Sphk#define FALSE	0
13011910Sphk#define TRUE	1
13111910Sphk
132227081Sedstatic int Reboot = FALSE;
133227081Sedstatic int howto = RB_AUTOBOOT;
1341558Srgrimes
135227081Sedstatic int devfs;
13619227Sphk
137183391Sdelphijstatic void transition(state_t);
138183391Sdelphijstatic state_t requested_transition;
139217750Sjillesstatic state_t current_state = death_single;
1401558Srgrimes
141232977Sedstatic void open_console(void);
142183391Sdelphijstatic const char *get_shell(void);
143183391Sdelphijstatic void write_stderr(const char *message);
1441558Srgrimes
1451558Srgrimestypedef struct init_session {
1461558Srgrimes	int	se_index;		/* index of entry in ttys file */
1471558Srgrimes	pid_t	se_process;		/* controlling process */
1481558Srgrimes	time_t	se_started;		/* used to avoid thrashing */
1491558Srgrimes	int	se_flags;		/* status of session */
1501558Srgrimes#define	SE_SHUTDOWN	0x1		/* session won't be restarted */
15157344Salfred#define	SE_PRESENT	0x2		/* session is in /etc/ttys */
152232841Sed	int	se_nspace;		/* spacing count */
1531558Srgrimes	char	*se_device;		/* filename of port */
1541558Srgrimes	char	*se_getty;		/* what to run on that port */
155232841Sed	char	*se_getty_argv_space;   /* pre-parsed argument array space */
1561558Srgrimes	char	**se_getty_argv;	/* pre-parsed argument array */
1571558Srgrimes	char	*se_window;		/* window system (started only once) */
158232841Sed	char	*se_window_argv_space;  /* pre-parsed argument array space */
1591558Srgrimes	char	**se_window_argv;	/* pre-parsed argument array */
160232841Sed	char	*se_type;		/* default terminal type */
1611558Srgrimes	struct	init_session *se_prev;
1621558Srgrimes	struct	init_session *se_next;
1631558Srgrimes} session_t;
1641558Srgrimes
165183391Sdelphijstatic void free_session(session_t *);
166183391Sdelphijstatic session_t *new_session(session_t *, int, struct ttyent *);
167183391Sdelphijstatic session_t *sessions;
1681558Srgrimes
169183391Sdelphijstatic char **construct_argv(char *);
170183391Sdelphijstatic void start_window_system(session_t *);
171183391Sdelphijstatic void collect_child(pid_t);
172183391Sdelphijstatic pid_t start_getty(session_t *);
173183391Sdelphijstatic void transition_handler(int);
174183391Sdelphijstatic void alrm_handler(int);
175183391Sdelphijstatic void setsecuritylevel(int);
176183391Sdelphijstatic int getsecuritylevel(void);
177183391Sdelphijstatic int setupargv(session_t *, struct ttyent *);
17821941Sdavidn#ifdef LOGIN_CAP
179183391Sdelphijstatic void setprocresources(const char *);
18021941Sdavidn#endif
181183391Sdelphijstatic int clang;
1821558Srgrimes
183183391Sdelphijstatic int start_session_db(void);
184183391Sdelphijstatic void add_session(session_t *);
185183391Sdelphijstatic void del_session(session_t *);
186183391Sdelphijstatic session_t *find_session(pid_t);
187183391Sdelphijstatic DB *session_db;
1881558Srgrimes
1891558Srgrimes/*
1901558Srgrimes * The mother of all processes.
1911558Srgrimes */
1921558Srgrimesint
19392838Simpmain(int argc, char *argv[])
1941558Srgrimes{
195166484Simp	state_t initial_transition = runcom;
196166484Simp	char kenv_value[PATH_MAX];
1971558Srgrimes	int c;
1981558Srgrimes	struct sigaction sa;
1991558Srgrimes	sigset_t mask;
2001558Srgrimes
2011558Srgrimes	/* Dispose of random users. */
20226594Scharnier	if (getuid() != 0)
20326594Scharnier		errx(1, "%s", strerror(EPERM));
2041558Srgrimes
2051558Srgrimes	/* System V users like to reexec init. */
20647998Sru	if (getpid() != 1) {
20747998Sru#ifdef COMPAT_SYSV_INIT
20847998Sru		/* So give them what they want */
20947998Sru		if (argc > 1) {
21047998Sru			if (strlen(argv[1]) == 1) {
21192806Sobrien				char runlevel = *argv[1];
21292806Sobrien				int sig;
2131558Srgrimes
21447998Sru				switch (runlevel) {
215173785Sobrien				case '0': /* halt + poweroff */
216173785Sobrien					sig = SIGUSR2;
217173785Sobrien					break;
218173785Sobrien				case '1': /* single-user */
219173785Sobrien					sig = SIGTERM;
220173785Sobrien					break;
221173785Sobrien				case '6': /* reboot */
222173785Sobrien					sig = SIGINT;
223173785Sobrien					break;
224173785Sobrien				case 'c': /* block further logins */
225173785Sobrien					sig = SIGTSTP;
226173785Sobrien					break;
227173785Sobrien				case 'q': /* rescan /etc/ttys */
228173785Sobrien					sig = SIGHUP;
229173785Sobrien					break;
230173785Sobrien				default:
231173785Sobrien					goto invalid;
23247998Sru				}
23347998Sru				kill(1, sig);
23447998Sru				_exit(0);
23547998Sru			} else
23647998Sruinvalid:
23747998Sru				errx(1, "invalid run-level ``%s''", argv[1]);
23847998Sru		} else
23947998Sru#endif
24047998Sru			errx(1, "already running");
24147998Sru	}
2421558Srgrimes	/*
2431558Srgrimes	 * Note that this does NOT open a file...
2441558Srgrimes	 * Does 'init' deserve its own facility number?
2451558Srgrimes	 */
2461558Srgrimes	openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
2471558Srgrimes
2481558Srgrimes	/*
2491558Srgrimes	 * Create an initial session.
2501558Srgrimes	 */
2511558Srgrimes	if (setsid() < 0)
2521558Srgrimes		warning("initial setsid() failed: %m");
2531558Srgrimes
2541558Srgrimes	/*
2551558Srgrimes	 * Establish an initial user so that programs running
2561558Srgrimes	 * single user do not freak out and die (like passwd).
2571558Srgrimes	 */
2581558Srgrimes	if (setlogin("root") < 0)
2591558Srgrimes		warning("setlogin() failed: %m");
2601558Srgrimes
2611558Srgrimes	/*
2621558Srgrimes	 * This code assumes that we always get arguments through flags,
2631558Srgrimes	 * never through bits set in some random machine register.
2641558Srgrimes	 */
26519227Sphk	while ((c = getopt(argc, argv, "dsf")) != -1)
2661558Srgrimes		switch (c) {
26719227Sphk		case 'd':
26819227Sphk			devfs = 1;
26919227Sphk			break;
2701558Srgrimes		case 's':
271166484Simp			initial_transition = single_user;
2721558Srgrimes			break;
2731558Srgrimes		case 'f':
2741558Srgrimes			runcom_mode = FASTBOOT;
2751558Srgrimes			break;
2761558Srgrimes		default:
2771558Srgrimes			warning("unrecognized flag '-%c'", c);
2781558Srgrimes			break;
2791558Srgrimes		}
2801558Srgrimes
2811558Srgrimes	if (optind != argc)
2821558Srgrimes		warning("ignoring excess arguments");
2831558Srgrimes
284166484Simp	/*
285166484Simp	 * We catch or block signals rather than ignore them,
286166484Simp	 * so that they get reset on exec.
287166484Simp	 */
288166484Simp	handle(badsys, SIGSYS, 0);
289173785Sobrien	handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU,
290173785Sobrien	    SIGXFSZ, 0);
291173785Sobrien	handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGUSR1,
292173785Sobrien	    SIGUSR2, 0);
293166484Simp	handle(alrm_handler, SIGALRM, 0);
294166484Simp	sigfillset(&mask);
295166484Simp	delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
296173785Sobrien	    SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM,
297173785Sobrien	    SIGUSR1, SIGUSR2, 0);
298166484Simp	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
299166484Simp	sigemptyset(&sa.sa_mask);
300166484Simp	sa.sa_flags = 0;
301166484Simp	sa.sa_handler = SIG_IGN;
302173785Sobrien	sigaction(SIGTTIN, &sa, (struct sigaction *)0);
303173785Sobrien	sigaction(SIGTTOU, &sa, (struct sigaction *)0);
304166484Simp
305166484Simp	/*
306166484Simp	 * Paranoia.
307166484Simp	 */
308166484Simp	close(0);
309166484Simp	close(1);
310166484Simp	close(2);
311166484Simp
312166484Simp	if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) {
313166484Simp		state_func_t next_transition;
314166484Simp
315166484Simp		if ((next_transition = run_script(kenv_value)) != 0)
316166484Simp			initial_transition = (state_t) next_transition;
317166484Simp	}
318166484Simp
319166484Simp	if (kenv(KENV_GET, "init_chroot", kenv_value, sizeof(kenv_value)) > 0) {
320166484Simp		if (chdir(kenv_value) != 0 || chroot(".") != 0)
321166484Simp			warning("Can't chroot to %s: %m", kenv_value);
322166484Simp	}
323166484Simp
324166484Simp	/*
325166484Simp	 * Additional check if devfs needs to be mounted:
326166484Simp	 * If "/" and "/dev" have the same device number,
327166484Simp	 * then it hasn't been mounted yet.
328166484Simp	 */
329166484Simp	if (!devfs) {
330166484Simp		struct stat stst;
331166484Simp		dev_t root_devno;
332166484Simp
333166484Simp		stat("/", &stst);
334166484Simp		root_devno = stst.st_dev;
335166484Simp		if (stat("/dev", &stst) != 0)
336166484Simp			warning("Can't stat /dev: %m");
337166484Simp		else if (stst.st_dev == root_devno)
338166484Simp			devfs++;
339166484Simp	}
340166484Simp
34119227Sphk	if (devfs) {
342101271Smux		struct iovec iov[4];
34372188Sphk		char *s;
34472188Sphk		int i;
34572188Sphk
346140070Sdelphij		char _fstype[]	= "fstype";
347140070Sdelphij		char _devfs[]	= "devfs";
348140070Sdelphij		char _fspath[]	= "fspath";
349140070Sdelphij		char _path_dev[]= _PATH_DEV;
350140070Sdelphij
351140070Sdelphij		iov[0].iov_base = _fstype;
352140070Sdelphij		iov[0].iov_len = sizeof(_fstype);
353140070Sdelphij		iov[1].iov_base = _devfs;
354140070Sdelphij		iov[1].iov_len = sizeof(_devfs);
355140070Sdelphij		iov[2].iov_base = _fspath;
356140070Sdelphij		iov[2].iov_len = sizeof(_fspath);
357173787Sobrien		/*
35872188Sphk		 * Try to avoid the trailing slash in _PATH_DEV.
35972188Sphk		 * Be *very* defensive.
36072188Sphk		 */
36172188Sphk		s = strdup(_PATH_DEV);
36272188Sphk		if (s != NULL) {
36372188Sphk			i = strlen(s);
36472188Sphk			if (i > 0 && s[i - 1] == '/')
36572188Sphk				s[i - 1] = '\0';
366101271Smux			iov[3].iov_base = s;
367101271Smux			iov[3].iov_len = strlen(s) + 1;
36872188Sphk		} else {
369140070Sdelphij			iov[3].iov_base = _path_dev;
370140070Sdelphij			iov[3].iov_len = sizeof(_path_dev);
37172188Sphk		}
372101271Smux		nmount(iov, 4, 0);
373101271Smux		if (s != NULL)
374101271Smux			free(s);
37519227Sphk	}
37619227Sphk
3771558Srgrimes	/*
3781558Srgrimes	 * Start the state machine.
3791558Srgrimes	 */
380166484Simp	transition(initial_transition);
3811558Srgrimes
3821558Srgrimes	/*
3831558Srgrimes	 * Should never reach here.
3841558Srgrimes	 */
3851558Srgrimes	return 1;
3861558Srgrimes}
3871558Srgrimes
3881558Srgrimes/*
3891558Srgrimes * Associate a function with a signal handler.
3901558Srgrimes */
391183391Sdelphijstatic void
3921558Srgrimeshandle(sig_t handler, ...)
3931558Srgrimes{
3941558Srgrimes	int sig;
3951558Srgrimes	struct sigaction sa;
39634001Sjraynard	sigset_t mask_everything;
3971558Srgrimes	va_list ap;
3981558Srgrimes	va_start(ap, handler);
3991558Srgrimes
4001558Srgrimes	sa.sa_handler = handler;
4011558Srgrimes	sigfillset(&mask_everything);
4021558Srgrimes
403126836Sbde	while ((sig = va_arg(ap, int)) != 0) {
4041558Srgrimes		sa.sa_mask = mask_everything;
4051558Srgrimes		/* XXX SA_RESTART? */
4061558Srgrimes		sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
4071558Srgrimes		sigaction(sig, &sa, (struct sigaction *) 0);
4081558Srgrimes	}
4091558Srgrimes	va_end(ap);
4101558Srgrimes}
4111558Srgrimes
4121558Srgrimes/*
4131558Srgrimes * Delete a set of signals from a mask.
4141558Srgrimes */
415183391Sdelphijstatic void
4161558Srgrimesdelset(sigset_t *maskp, ...)
4171558Srgrimes{
4181558Srgrimes	int sig;
4191558Srgrimes	va_list ap;
4201558Srgrimes	va_start(ap, maskp);
4211558Srgrimes
422126836Sbde	while ((sig = va_arg(ap, int)) != 0)
4231558Srgrimes		sigdelset(maskp, sig);
4241558Srgrimes	va_end(ap);
4251558Srgrimes}
4261558Srgrimes
4271558Srgrimes/*
4281558Srgrimes * Log a message and sleep for a while (to give someone an opportunity
4291558Srgrimes * to read it and to save log or hardcopy output if the problem is chronic).
4301558Srgrimes * NB: should send a message to the session logger to avoid blocking.
4311558Srgrimes */
432183391Sdelphijstatic void
43381911Skrisstall(const char *message, ...)
4341558Srgrimes{
4351558Srgrimes	va_list ap;
4361558Srgrimes	va_start(ap, message);
4371558Srgrimes
4381558Srgrimes	vsyslog(LOG_ALERT, message, ap);
4391558Srgrimes	va_end(ap);
4401558Srgrimes	sleep(STALL_TIMEOUT);
4411558Srgrimes}
4421558Srgrimes
4431558Srgrimes/*
4441558Srgrimes * Like stall(), but doesn't sleep.
4451558Srgrimes * If cpp had variadic macros, the two functions could be #defines for another.
4461558Srgrimes * NB: should send a message to the session logger to avoid blocking.
4471558Srgrimes */
448183391Sdelphijstatic void
44981911Skriswarning(const char *message, ...)
4501558Srgrimes{
4511558Srgrimes	va_list ap;
4521558Srgrimes	va_start(ap, message);
4531558Srgrimes
4541558Srgrimes	vsyslog(LOG_ALERT, message, ap);
4551558Srgrimes	va_end(ap);
4561558Srgrimes}
4571558Srgrimes
4581558Srgrimes/*
4591558Srgrimes * Log an emergency message.
4601558Srgrimes * NB: should send a message to the session logger to avoid blocking.
4611558Srgrimes */
462183391Sdelphijstatic void
46381911Skrisemergency(const char *message, ...)
4641558Srgrimes{
4651558Srgrimes	va_list ap;
4661558Srgrimes	va_start(ap, message);
4671558Srgrimes
4681558Srgrimes	vsyslog(LOG_EMERG, message, ap);
4691558Srgrimes	va_end(ap);
4701558Srgrimes}
4711558Srgrimes
4721558Srgrimes/*
4731558Srgrimes * Catch a SIGSYS signal.
4741558Srgrimes *
4751558Srgrimes * These may arise if a system does not support sysctl.
4761558Srgrimes * We tolerate up to 25 of these, then throw in the towel.
4771558Srgrimes */
478183391Sdelphijstatic void
47992838Simpbadsys(int sig)
4801558Srgrimes{
4811558Srgrimes	static int badcount = 0;
4821558Srgrimes
4831558Srgrimes	if (badcount++ < 25)
4841558Srgrimes		return;
4851558Srgrimes	disaster(sig);
4861558Srgrimes}
4871558Srgrimes
4881558Srgrimes/*
4891558Srgrimes * Catch an unexpected signal.
4901558Srgrimes */
491183391Sdelphijstatic void
49292838Simpdisaster(int sig)
4931558Srgrimes{
494173785Sobrien
4951558Srgrimes	emergency("fatal signal: %s",
496173785Sobrien	    (unsigned)sig < NSIG ? sys_siglist[sig] : "unknown signal");
4971558Srgrimes
4981558Srgrimes	sleep(STALL_TIMEOUT);
4991558Srgrimes	_exit(sig);		/* reboot */
5001558Srgrimes}
5011558Srgrimes
5021558Srgrimes/*
5031558Srgrimes * Get the security level of the kernel.
5041558Srgrimes */
505183391Sdelphijstatic int
50692838Simpgetsecuritylevel(void)
5071558Srgrimes{
5081558Srgrimes#ifdef KERN_SECURELVL
5091558Srgrimes	int name[2], curlevel;
5101558Srgrimes	size_t len;
5111558Srgrimes
5121558Srgrimes	name[0] = CTL_KERN;
5131558Srgrimes	name[1] = KERN_SECURELVL;
5141558Srgrimes	len = sizeof curlevel;
5151558Srgrimes	if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
5161558Srgrimes		emergency("cannot get kernel security level: %s",
5171558Srgrimes		    strerror(errno));
5181558Srgrimes		return (-1);
5191558Srgrimes	}
5201558Srgrimes	return (curlevel);
5211558Srgrimes#else
5221558Srgrimes	return (-1);
5231558Srgrimes#endif
5241558Srgrimes}
5251558Srgrimes
5261558Srgrimes/*
5271558Srgrimes * Set the security level of the kernel.
5281558Srgrimes */
529183391Sdelphijstatic void
53092838Simpsetsecuritylevel(int newlevel)
5311558Srgrimes{
5321558Srgrimes#ifdef KERN_SECURELVL
5331558Srgrimes	int name[2], curlevel;
5341558Srgrimes
5351558Srgrimes	curlevel = getsecuritylevel();
5361558Srgrimes	if (newlevel == curlevel)
5371558Srgrimes		return;
5381558Srgrimes	name[0] = CTL_KERN;
5391558Srgrimes	name[1] = KERN_SECURELVL;
5401558Srgrimes	if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
5411558Srgrimes		emergency(
5421558Srgrimes		    "cannot change kernel security level from %d to %d: %s",
5431558Srgrimes		    curlevel, newlevel, strerror(errno));
5441558Srgrimes		return;
5451558Srgrimes	}
5461558Srgrimes#ifdef SECURE
5471558Srgrimes	warning("kernel security level changed from %d to %d",
5481558Srgrimes	    curlevel, newlevel);
5491558Srgrimes#endif
5501558Srgrimes#endif
5511558Srgrimes}
5521558Srgrimes
5531558Srgrimes/*
5541558Srgrimes * Change states in the finite state machine.
5551558Srgrimes * The initial state is passed as an argument.
5561558Srgrimes */
557183391Sdelphijstatic void
55892838Simptransition(state_t s)
5591558Srgrimes{
560173785Sobrien
561217750Sjilles	current_state = s;
5621558Srgrimes	for (;;)
563217750Sjilles		current_state = (state_t) (*current_state)();
5641558Srgrimes}
5651558Srgrimes
5661558Srgrimes/*
5671558Srgrimes * Start a session and allocate a controlling terminal.
5681558Srgrimes * Only called by children of init after forking.
5691558Srgrimes */
570183391Sdelphijstatic void
571232977Sedopen_console(void)
5721558Srgrimes{
5731558Srgrimes	int fd;
5741558Srgrimes
575233945Sed	/*
576233945Sed	 * Try to open /dev/console.  Open the device with O_NONBLOCK to
577233945Sed	 * prevent potential blocking on a carrier.
578233945Sed	 */
579232977Sed	revoke(_PATH_CONSOLE);
580232977Sed	if ((fd = open(_PATH_CONSOLE, O_RDWR | O_NONBLOCK)) != -1) {
581233945Sed		(void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
582232977Sed		if (login_tty(fd) == 0)
583232977Sed			return;
584232977Sed		close(fd);
5851558Srgrimes	}
586232977Sed
587232977Sed	/* No luck.  Log output to file if possible. */
588232977Sed	if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
589232977Sed		stall("cannot open null device.");
5901558Srgrimes		_exit(1);
5911558Srgrimes	}
592232977Sed	if (fd != STDIN_FILENO) {
593232977Sed		dup2(fd, STDIN_FILENO);
594232977Sed		close(fd);
595232977Sed	}
596232977Sed	fd = open(_PATH_INITLOG, O_WRONLY | O_APPEND | O_CREAT, 0644);
597232977Sed	if (fd == -1)
598232977Sed		dup2(STDIN_FILENO, STDOUT_FILENO);
599232977Sed	else if (fd != STDOUT_FILENO) {
600232977Sed		dup2(fd, STDOUT_FILENO);
601232977Sed		close(fd);
602232977Sed	}
603232977Sed	dup2(STDOUT_FILENO, STDERR_FILENO);
6041558Srgrimes}
6051558Srgrimes
606183391Sdelphijstatic const char *
607166484Simpget_shell(void)
608166484Simp{
609166484Simp	static char kenv_value[PATH_MAX];
610166484Simp
611166484Simp	if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0)
612166484Simp		return kenv_value;
613166484Simp	else
614166484Simp		return _PATH_BSHELL;
615166484Simp}
616166484Simp
617183391Sdelphijstatic void
618166484Simpwrite_stderr(const char *message)
619166484Simp{
620173785Sobrien
621166484Simp	write(STDERR_FILENO, message, strlen(message));
622166484Simp}
623166484Simp
6241558Srgrimes/*
6251558Srgrimes * Bring the system up single user.
6261558Srgrimes */
627183391Sdelphijstatic state_func_t
62892838Simpsingle_user(void)
6291558Srgrimes{
6301558Srgrimes	pid_t pid, wpid;
6311558Srgrimes	int status;
6321558Srgrimes	sigset_t mask;
633166484Simp	const char *shell;
6341558Srgrimes	char *argv[2];
6351558Srgrimes#ifdef SECURE
6361558Srgrimes	struct ttyent *typ;
6371558Srgrimes	struct passwd *pp;
6381558Srgrimes	static const char banner[] =
6391558Srgrimes		"Enter root password, or ^D to go multi-user\n";
6401558Srgrimes	char *clear, *password;
6411558Srgrimes#endif
64237814Sphk#ifdef DEBUGSHELL
64337814Sphk	char altshell[128];
64437814Sphk#endif
6451558Srgrimes
6462327Sjkh	if (Reboot) {
64747962Sru		/* Instead of going single user, let's reboot the machine */
6482323Snate		sync();
64947962Sru		reboot(howto);
6502323Snate		_exit(0);
6512323Snate	}
6522323Snate
653166484Simp	shell = get_shell();
654166484Simp
6551558Srgrimes	if ((pid = fork()) == 0) {
6561558Srgrimes		/*
6571558Srgrimes		 * Start the single user session.
6581558Srgrimes		 */
659232977Sed		open_console();
6601558Srgrimes
6611558Srgrimes#ifdef SECURE
6621558Srgrimes		/*
6631558Srgrimes		 * Check the root password.
6641558Srgrimes		 * We don't care if the console is 'on' by default;
6651558Srgrimes		 * it's the only tty that can be 'off' and 'secure'.
6661558Srgrimes		 */
6671558Srgrimes		typ = getttynam("console");
6681558Srgrimes		pp = getpwnam("root");
66953550Sdillon		if (typ && (typ->ty_status & TTY_SECURE) == 0 &&
67053550Sdillon		    pp && *pp->pw_passwd) {
671166484Simp			write_stderr(banner);
6721558Srgrimes			for (;;) {
6731558Srgrimes				clear = getpass("Password:");
6741558Srgrimes				if (clear == 0 || *clear == '\0')
6751558Srgrimes					_exit(0);
6761558Srgrimes				password = crypt(clear, pp->pw_passwd);
6771558Srgrimes				bzero(clear, _PASSWORD_LEN);
678232841Sed				if (password == NULL ||
679231994Skevlo				    strcmp(password, pp->pw_passwd) == 0)
6801558Srgrimes					break;
6811558Srgrimes				warning("single-user login failed\n");
6821558Srgrimes			}
6831558Srgrimes		}
6841558Srgrimes		endttyent();
6851558Srgrimes		endpwent();
6861558Srgrimes#endif /* SECURE */
6871558Srgrimes
6881558Srgrimes#ifdef DEBUGSHELL
6891558Srgrimes		{
69037814Sphk			char *cp = altshell;
6911558Srgrimes			int num;
6921558Srgrimes
693166484Simp#define	SHREQUEST "Enter full pathname of shell or RETURN for "
694166484Simp			write_stderr(SHREQUEST);
695166484Simp			write_stderr(shell);
696166484Simp			write_stderr(": ");
6971558Srgrimes			while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
6981558Srgrimes			    num != 0 && *cp != '\n' && cp < &altshell[127])
699173785Sobrien				cp++;
7001558Srgrimes			*cp = '\0';
7011558Srgrimes			if (altshell[0] != '\0')
7021558Srgrimes				shell = altshell;
7031558Srgrimes		}
7041558Srgrimes#endif /* DEBUGSHELL */
7051558Srgrimes
7061558Srgrimes		/*
7071558Srgrimes		 * Unblock signals.
7081558Srgrimes		 * We catch all the interesting ones,
7091558Srgrimes		 * and those are reset to SIG_DFL on exec.
7101558Srgrimes		 */
7111558Srgrimes		sigemptyset(&mask);
7121558Srgrimes		sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
7131558Srgrimes
7141558Srgrimes		/*
7151558Srgrimes		 * Fire off a shell.
7161558Srgrimes		 * If the default one doesn't work, try the Bourne shell.
7171558Srgrimes		 */
718140070Sdelphij
719140070Sdelphij		char name[] = "-sh";
720140070Sdelphij
721140070Sdelphij		argv[0] = name;
7221558Srgrimes		argv[1] = 0;
7231558Srgrimes		execv(shell, argv);
7241558Srgrimes		emergency("can't exec %s for single user: %m", shell);
7251558Srgrimes		execv(_PATH_BSHELL, argv);
7261558Srgrimes		emergency("can't exec %s for single user: %m", _PATH_BSHELL);
7271558Srgrimes		sleep(STALL_TIMEOUT);
7281558Srgrimes		_exit(1);
7291558Srgrimes	}
7301558Srgrimes
7311558Srgrimes	if (pid == -1) {
7321558Srgrimes		/*
7331558Srgrimes		 * We are seriously hosed.  Do our best.
7341558Srgrimes		 */
7351558Srgrimes		emergency("can't fork single-user shell, trying again");
7361558Srgrimes		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
7371558Srgrimes			continue;
7381558Srgrimes		return (state_func_t) single_user;
7391558Srgrimes	}
7401558Srgrimes
7411558Srgrimes	requested_transition = 0;
7421558Srgrimes	do {
7431558Srgrimes		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
7441558Srgrimes			collect_child(wpid);
7451558Srgrimes		if (wpid == -1) {
7461558Srgrimes			if (errno == EINTR)
7471558Srgrimes				continue;
7481558Srgrimes			warning("wait for single-user shell failed: %m; restarting");
7491558Srgrimes			return (state_func_t) single_user;
7501558Srgrimes		}
7511558Srgrimes		if (wpid == pid && WIFSTOPPED(status)) {
7521558Srgrimes			warning("init: shell stopped, restarting\n");
7531558Srgrimes			kill(pid, SIGCONT);
7541558Srgrimes			wpid = -1;
7551558Srgrimes		}
7561558Srgrimes	} while (wpid != pid && !requested_transition);
7571558Srgrimes
7581558Srgrimes	if (requested_transition)
7591558Srgrimes		return (state_func_t) requested_transition;
7601558Srgrimes
7611558Srgrimes	if (!WIFEXITED(status)) {
7628871Srgrimes		if (WTERMSIG(status) == SIGKILL) {
7638871Srgrimes			/*
7648871Srgrimes			 *  reboot(8) killed shell?
7651558Srgrimes			 */
7661558Srgrimes			warning("single user shell terminated.");
7671558Srgrimes			sleep(STALL_TIMEOUT);
7681558Srgrimes			_exit(0);
7698871Srgrimes		} else {
7701558Srgrimes			warning("single user shell terminated, restarting");
7711558Srgrimes			return (state_func_t) single_user;
7721558Srgrimes		}
7731558Srgrimes	}
7741558Srgrimes
7751558Srgrimes	runcom_mode = FASTBOOT;
7761558Srgrimes	return (state_func_t) runcom;
7771558Srgrimes}
7781558Srgrimes
7791558Srgrimes/*
7801558Srgrimes * Run the system startup script.
7811558Srgrimes */
782183391Sdelphijstatic state_func_t
78392838Simpruncom(void)
7841558Srgrimes{
785166484Simp	state_func_t next_transition;
786166484Simp
787166484Simp	if ((next_transition = run_script(_PATH_RUNCOM)) != 0)
788166484Simp		return next_transition;
789166484Simp
790166484Simp	runcom_mode = AUTOBOOT;		/* the default */
791166484Simp	return (state_func_t) read_ttys;
792166484Simp}
793166484Simp
794166484Simp/*
795166484Simp * Run a shell script.
796166484Simp * Returns 0 on success, otherwise the next transition to enter:
797166484Simp *  - single_user if fork/execv/waitpid failed, or if the script
798166484Simp *    terminated with a signal or exit code != 0.
799217750Sjilles *  - death_single if a SIGTERM was delivered to init(8).
800166484Simp */
801183391Sdelphijstatic state_func_t
802166484Simprun_script(const char *script)
803166484Simp{
8041558Srgrimes	pid_t pid, wpid;
8051558Srgrimes	int status;
8061558Srgrimes	char *argv[4];
807166484Simp	const char *shell;
8081558Srgrimes	struct sigaction sa;
8091558Srgrimes
810166484Simp	shell = get_shell();
811166484Simp
8121558Srgrimes	if ((pid = fork()) == 0) {
8131558Srgrimes		sigemptyset(&sa.sa_mask);
8141558Srgrimes		sa.sa_flags = 0;
8151558Srgrimes		sa.sa_handler = SIG_IGN;
816173785Sobrien		sigaction(SIGTSTP, &sa, (struct sigaction *)0);
817173785Sobrien		sigaction(SIGHUP, &sa, (struct sigaction *)0);
8181558Srgrimes
819232977Sed		open_console();
8201558Srgrimes
821232841Sed		char _sh[]		= "sh";
822140070Sdelphij		char _autoboot[]	= "autoboot";
823140070Sdelphij
824140070Sdelphij		argv[0] = _sh;
825166484Simp		argv[1] = __DECONST(char *, script);
826140070Sdelphij		argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0;
8271558Srgrimes		argv[3] = 0;
8281558Srgrimes
8291558Srgrimes		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
8301558Srgrimes
83121865Sdavidn#ifdef LOGIN_CAP
83221865Sdavidn		setprocresources(RESOURCE_RC);
83321865Sdavidn#endif
834166484Simp		execv(shell, argv);
835166484Simp		stall("can't exec %s for %s: %m", shell, script);
8361558Srgrimes		_exit(1);	/* force single user mode */
8371558Srgrimes	}
8381558Srgrimes
8391558Srgrimes	if (pid == -1) {
840166484Simp		emergency("can't fork for %s on %s: %m", shell, script);
8411558Srgrimes		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
8421558Srgrimes			continue;
8431558Srgrimes		sleep(STALL_TIMEOUT);
8441558Srgrimes		return (state_func_t) single_user;
8451558Srgrimes	}
8461558Srgrimes
8471558Srgrimes	/*
8481558Srgrimes	 * Copied from single_user().  This is a bit paranoid.
8491558Srgrimes	 */
85085010Sdes	requested_transition = 0;
8511558Srgrimes	do {
8521558Srgrimes		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
8531558Srgrimes			collect_child(wpid);
8541558Srgrimes		if (wpid == -1) {
855217750Sjilles			if (requested_transition == death_single)
856217750Sjilles				return (state_func_t) death_single;
8571558Srgrimes			if (errno == EINTR)
8581558Srgrimes				continue;
859166484Simp			warning("wait for %s on %s failed: %m; going to "
860166484Simp			    "single user mode", shell, script);
8611558Srgrimes			return (state_func_t) single_user;
8621558Srgrimes		}
8631558Srgrimes		if (wpid == pid && WIFSTOPPED(status)) {
8641558Srgrimes			warning("init: %s on %s stopped, restarting\n",
865173785Sobrien			    shell, script);
8661558Srgrimes			kill(pid, SIGCONT);
8671558Srgrimes			wpid = -1;
8681558Srgrimes		}
8691558Srgrimes	} while (wpid != pid);
8701558Srgrimes
8711558Srgrimes	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
8721558Srgrimes	    requested_transition == catatonia) {
8731558Srgrimes		/* /etc/rc executed /sbin/reboot; wait for the end quietly */
8741558Srgrimes		sigset_t s;
8751558Srgrimes
8761558Srgrimes		sigfillset(&s);
8771558Srgrimes		for (;;)
8781558Srgrimes			sigsuspend(&s);
8791558Srgrimes	}
8801558Srgrimes
8811558Srgrimes	if (!WIFEXITED(status)) {
882166484Simp		warning("%s on %s terminated abnormally, going to single "
883166484Simp		    "user mode", shell, script);
8841558Srgrimes		return (state_func_t) single_user;
8851558Srgrimes	}
8861558Srgrimes
8871558Srgrimes	if (WEXITSTATUS(status))
8881558Srgrimes		return (state_func_t) single_user;
8891558Srgrimes
890166484Simp	return (state_func_t) 0;
8911558Srgrimes}
8921558Srgrimes
8931558Srgrimes/*
8941558Srgrimes * Open the session database.
8951558Srgrimes *
8961558Srgrimes * NB: We could pass in the size here; is it necessary?
8971558Srgrimes */
898183391Sdelphijstatic int
89992838Simpstart_session_db(void)
9001558Srgrimes{
9011558Srgrimes	if (session_db && (*session_db->close)(session_db))
9021558Srgrimes		emergency("session database close: %s", strerror(errno));
9031558Srgrimes	if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
9041558Srgrimes		emergency("session database open: %s", strerror(errno));
9051558Srgrimes		return (1);
9061558Srgrimes	}
9071558Srgrimes	return (0);
9088871Srgrimes
9091558Srgrimes}
9101558Srgrimes
9111558Srgrimes/*
9121558Srgrimes * Add a new login session.
9131558Srgrimes */
914183391Sdelphijstatic void
91592838Simpadd_session(session_t *sp)
9161558Srgrimes{
9171558Srgrimes	DBT key;
9181558Srgrimes	DBT data;
9191558Srgrimes
9201558Srgrimes	key.data = &sp->se_process;
9211558Srgrimes	key.size = sizeof sp->se_process;
9221558Srgrimes	data.data = &sp;
9231558Srgrimes	data.size = sizeof sp;
9241558Srgrimes
9251558Srgrimes	if ((*session_db->put)(session_db, &key, &data, 0))
9261558Srgrimes		emergency("insert %d: %s", sp->se_process, strerror(errno));
9271558Srgrimes}
9281558Srgrimes
9291558Srgrimes/*
9301558Srgrimes * Delete an old login session.
9311558Srgrimes */
932183391Sdelphijstatic void
93392838Simpdel_session(session_t *sp)
9341558Srgrimes{
9351558Srgrimes	DBT key;
9361558Srgrimes
9371558Srgrimes	key.data = &sp->se_process;
9381558Srgrimes	key.size = sizeof sp->se_process;
9391558Srgrimes
9401558Srgrimes	if ((*session_db->del)(session_db, &key, 0))
9411558Srgrimes		emergency("delete %d: %s", sp->se_process, strerror(errno));
9421558Srgrimes}
9431558Srgrimes
9441558Srgrimes/*
9451558Srgrimes * Look up a login session by pid.
9461558Srgrimes */
947183391Sdelphijstatic session_t *
9481558Srgrimesfind_session(pid_t pid)
9491558Srgrimes{
9501558Srgrimes	DBT key;
9511558Srgrimes	DBT data;
9521558Srgrimes	session_t *ret;
9531558Srgrimes
9541558Srgrimes	key.data = &pid;
9551558Srgrimes	key.size = sizeof pid;
9561558Srgrimes	if ((*session_db->get)(session_db, &key, &data, 0) != 0)
9571558Srgrimes		return 0;
9581558Srgrimes	bcopy(data.data, (char *)&ret, sizeof(ret));
9591558Srgrimes	return ret;
9601558Srgrimes}
9611558Srgrimes
9621558Srgrimes/*
9631558Srgrimes * Construct an argument vector from a command line.
9641558Srgrimes */
965183391Sdelphijstatic char **
96692838Simpconstruct_argv(char *command)
9671558Srgrimes{
96892806Sobrien	int argc = 0;
96992806Sobrien	char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
9701558Srgrimes						* sizeof (char *));
9711558Srgrimes
97249018Sru	if ((argv[argc++] = strk(command)) == 0) {
97349018Sru		free(argv);
97449018Sru		return (NULL);
97549018Sru	}
97627837Sdavidn	while ((argv[argc++] = strk((char *) 0)) != NULL)
9771558Srgrimes		continue;
9781558Srgrimes	return argv;
9791558Srgrimes}
9801558Srgrimes
9811558Srgrimes/*
9821558Srgrimes * Deallocate a session descriptor.
9831558Srgrimes */
984183391Sdelphijstatic void
98592838Simpfree_session(session_t *sp)
9861558Srgrimes{
9871558Srgrimes	free(sp->se_device);
9881558Srgrimes	if (sp->se_getty) {
9891558Srgrimes		free(sp->se_getty);
9903594Sache		free(sp->se_getty_argv_space);
9911558Srgrimes		free(sp->se_getty_argv);
9921558Srgrimes	}
9931558Srgrimes	if (sp->se_window) {
9941558Srgrimes		free(sp->se_window);
9953594Sache		free(sp->se_window_argv_space);
9961558Srgrimes		free(sp->se_window_argv);
9971558Srgrimes	}
9983594Sache	if (sp->se_type)
9993594Sache		free(sp->se_type);
10001558Srgrimes	free(sp);
10011558Srgrimes}
10021558Srgrimes
10031558Srgrimes/*
10041558Srgrimes * Allocate a new session descriptor.
100557344Salfred * Mark it SE_PRESENT.
10061558Srgrimes */
1007183391Sdelphijstatic session_t *
100892838Simpnew_session(session_t *sprev, int session_index, struct ttyent *typ)
10091558Srgrimes{
101092806Sobrien	session_t *sp;
101127029Spst	int fd;
10121558Srgrimes
10131558Srgrimes	if ((typ->ty_status & TTY_ON) == 0 ||
10141558Srgrimes	    typ->ty_name == 0 ||
10151558Srgrimes	    typ->ty_getty == 0)
10161558Srgrimes		return 0;
10171558Srgrimes
101827215Sache	sp = (session_t *) calloc(1, sizeof (session_t));
10191558Srgrimes
10201558Srgrimes	sp->se_index = session_index;
102157344Salfred	sp->se_flags |= SE_PRESENT;
10221558Srgrimes
10231558Srgrimes	sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
1024173785Sobrien	sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
10251558Srgrimes
102627029Spst	/*
102727029Spst	 * Attempt to open the device, if we get "device not configured"
102827029Spst	 * then don't add the device to the session list.
102927029Spst	 */
103027029Spst	if ((fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0)) < 0) {
1031135868Simp		if (errno == ENXIO) {
103227029Spst			free_session(sp);
103327029Spst			return (0);
103427029Spst		}
103527029Spst	} else
103627029Spst		close(fd);
103727029Spst
10381558Srgrimes	if (setupargv(sp, typ) == 0) {
10391558Srgrimes		free_session(sp);
10401558Srgrimes		return (0);
10411558Srgrimes	}
10421558Srgrimes
10431558Srgrimes	sp->se_next = 0;
10441558Srgrimes	if (sprev == 0) {
10451558Srgrimes		sessions = sp;
10461558Srgrimes		sp->se_prev = 0;
10471558Srgrimes	} else {
10481558Srgrimes		sprev->se_next = sp;
10491558Srgrimes		sp->se_prev = sprev;
10501558Srgrimes	}
10511558Srgrimes
10521558Srgrimes	return sp;
10531558Srgrimes}
10541558Srgrimes
10551558Srgrimes/*
10561558Srgrimes * Calculate getty and if useful window argv vectors.
10571558Srgrimes */
1058183391Sdelphijstatic int
105992838Simpsetupargv(session_t *sp, struct ttyent *typ)
10601558Srgrimes{
10611558Srgrimes
10621558Srgrimes	if (sp->se_getty) {
10631558Srgrimes		free(sp->se_getty);
10643594Sache		free(sp->se_getty_argv_space);
10651558Srgrimes		free(sp->se_getty_argv);
10661558Srgrimes	}
10671558Srgrimes	sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2);
1068173785Sobrien	sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
10693594Sache	sp->se_getty_argv_space = strdup(sp->se_getty);
10703594Sache	sp->se_getty_argv = construct_argv(sp->se_getty_argv_space);
10711558Srgrimes	if (sp->se_getty_argv == 0) {
10721558Srgrimes		warning("can't parse getty for port %s", sp->se_device);
10731558Srgrimes		free(sp->se_getty);
10743594Sache		free(sp->se_getty_argv_space);
10753594Sache		sp->se_getty = sp->se_getty_argv_space = 0;
10761558Srgrimes		return (0);
10771558Srgrimes	}
10783594Sache	if (sp->se_window) {
107949018Sru		free(sp->se_window);
10803594Sache		free(sp->se_window_argv_space);
10813594Sache		free(sp->se_window_argv);
10823594Sache	}
10833594Sache	sp->se_window = sp->se_window_argv_space = 0;
10843594Sache	sp->se_window_argv = 0;
10851558Srgrimes	if (typ->ty_window) {
10861558Srgrimes		sp->se_window = strdup(typ->ty_window);
10873594Sache		sp->se_window_argv_space = strdup(sp->se_window);
10883594Sache		sp->se_window_argv = construct_argv(sp->se_window_argv_space);
10891558Srgrimes		if (sp->se_window_argv == 0) {
10901558Srgrimes			warning("can't parse window for port %s",
1091173785Sobrien			    sp->se_device);
10923594Sache			free(sp->se_window_argv_space);
10931558Srgrimes			free(sp->se_window);
10943594Sache			sp->se_window = sp->se_window_argv_space = 0;
10951558Srgrimes			return (0);
10961558Srgrimes		}
10971558Srgrimes	}
10983594Sache	if (sp->se_type)
10993594Sache		free(sp->se_type);
11003594Sache	sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0;
11011558Srgrimes	return (1);
11021558Srgrimes}
11031558Srgrimes
11041558Srgrimes/*
11051558Srgrimes * Walk the list of ttys and create sessions for each active line.
11061558Srgrimes */
1107183391Sdelphijstatic state_func_t
110892838Simpread_ttys(void)
11091558Srgrimes{
11101558Srgrimes	int session_index = 0;
111192806Sobrien	session_t *sp, *snext;
111292806Sobrien	struct ttyent *typ;
11131558Srgrimes
11141558Srgrimes	/*
11151558Srgrimes	 * Destroy any previous session state.
11161558Srgrimes	 * There shouldn't be any, but just in case...
11171558Srgrimes	 */
11181558Srgrimes	for (sp = sessions; sp; sp = snext) {
11191558Srgrimes		snext = sp->se_next;
11201558Srgrimes		free_session(sp);
11211558Srgrimes	}
11221558Srgrimes	sessions = 0;
11231558Srgrimes	if (start_session_db())
11241558Srgrimes		return (state_func_t) single_user;
11251558Srgrimes
11261558Srgrimes	/*
11271558Srgrimes	 * Allocate a session entry for each active port.
11281558Srgrimes	 * Note that sp starts at 0.
11291558Srgrimes	 */
113027837Sdavidn	while ((typ = getttyent()) != NULL)
113127837Sdavidn		if ((snext = new_session(sp, ++session_index, typ)) != NULL)
11321558Srgrimes			sp = snext;
11331558Srgrimes
11341558Srgrimes	endttyent();
11351558Srgrimes
11361558Srgrimes	return (state_func_t) multi_user;
11371558Srgrimes}
11381558Srgrimes
11391558Srgrimes/*
11401558Srgrimes * Start a window system running.
11411558Srgrimes */
1142183391Sdelphijstatic void
114392838Simpstart_window_system(session_t *sp)
11441558Srgrimes{
11451558Srgrimes	pid_t pid;
11461558Srgrimes	sigset_t mask;
11473594Sache	char term[64], *env[2];
1148159402Skib	int status;
11491558Srgrimes
11501558Srgrimes	if ((pid = fork()) == -1) {
11511558Srgrimes		emergency("can't fork for window system on port %s: %m",
1152173785Sobrien		    sp->se_device);
11531558Srgrimes		/* hope that getty fails and we can try again */
11541558Srgrimes		return;
11551558Srgrimes	}
1156173785Sobrien	if (pid) {
1157159402Skib		waitpid(-1, &status, 0);
11581558Srgrimes		return;
1159159402Skib	}
11601558Srgrimes
1161159402Skib	/* reparent window process to the init to not make a zombie on exit */
1162159402Skib	if ((pid = fork()) == -1) {
1163159402Skib		emergency("can't fork for window system on port %s: %m",
1164173785Sobrien		    sp->se_device);
1165159402Skib		_exit(1);
1166159402Skib	}
1167159402Skib	if (pid)
1168159402Skib		_exit(0);
1169159402Skib
11701558Srgrimes	sigemptyset(&mask);
11711558Srgrimes	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
11721558Srgrimes
11731558Srgrimes	if (setsid() < 0)
11741558Srgrimes		emergency("setsid failed (window) %m");
11751558Srgrimes
117621865Sdavidn#ifdef LOGIN_CAP
117721865Sdavidn	setprocresources(RESOURCE_WINDOW);
117821865Sdavidn#endif
11793594Sache	if (sp->se_type) {
11803594Sache		/* Don't use malloc after fork */
11813594Sache		strcpy(term, "TERM=");
118222922Sdg		strncat(term, sp->se_type, sizeof(term) - 6);
11833594Sache		env[0] = term;
11843594Sache		env[1] = 0;
11853594Sache	}
11863594Sache	else
11873594Sache		env[0] = 0;
11883594Sache	execve(sp->se_window_argv[0], sp->se_window_argv, env);
11891558Srgrimes	stall("can't exec window system '%s' for port %s: %m",
11901558Srgrimes		sp->se_window_argv[0], sp->se_device);
11911558Srgrimes	_exit(1);
11921558Srgrimes}
11931558Srgrimes
11941558Srgrimes/*
11951558Srgrimes * Start a login session running.
11961558Srgrimes */
1197183391Sdelphijstatic pid_t
119892838Simpstart_getty(session_t *sp)
11991558Srgrimes{
12001558Srgrimes	pid_t pid;
12011558Srgrimes	sigset_t mask;
12021558Srgrimes	time_t current_time = time((time_t *) 0);
12039997Sache	int too_quick = 0;
12043594Sache	char term[64], *env[2];
12051558Srgrimes
120610006Smpp	if (current_time >= sp->se_started &&
12079997Sache	    current_time - sp->se_started < GETTY_SPACING) {
12089997Sache		if (++sp->se_nspace > GETTY_NSPACE) {
12099997Sache			sp->se_nspace = 0;
12109997Sache			too_quick = 1;
12119997Sache		}
12129997Sache	} else
12139997Sache		sp->se_nspace = 0;
12149997Sache
12151558Srgrimes	/*
12161558Srgrimes	 * fork(), not vfork() -- we can't afford to block.
12171558Srgrimes	 */
12181558Srgrimes	if ((pid = fork()) == -1) {
12191558Srgrimes		emergency("can't fork for getty on port %s: %m", sp->se_device);
12201558Srgrimes		return -1;
12211558Srgrimes	}
12221558Srgrimes
12231558Srgrimes	if (pid)
12241558Srgrimes		return pid;
12251558Srgrimes
12269997Sache	if (too_quick) {
12279997Sache		warning("getty repeating too quickly on port %s, sleeping %d secs",
1228173785Sobrien		    sp->se_device, GETTY_SLEEP);
12291558Srgrimes		sleep((unsigned) GETTY_SLEEP);
12301558Srgrimes	}
12311558Srgrimes
12321558Srgrimes	if (sp->se_window) {
12331558Srgrimes		start_window_system(sp);
12341558Srgrimes		sleep(WINDOW_WAIT);
12351558Srgrimes	}
12361558Srgrimes
12371558Srgrimes	sigemptyset(&mask);
12381558Srgrimes	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
12391558Srgrimes
124021865Sdavidn#ifdef LOGIN_CAP
124121865Sdavidn	setprocresources(RESOURCE_GETTY);
124221865Sdavidn#endif
12433594Sache	if (sp->se_type) {
12443594Sache		/* Don't use malloc after fork */
12453594Sache		strcpy(term, "TERM=");
124622922Sdg		strncat(term, sp->se_type, sizeof(term) - 6);
12473594Sache		env[0] = term;
12483594Sache		env[1] = 0;
1249173785Sobrien	} else
12503594Sache		env[0] = 0;
12513594Sache	execve(sp->se_getty_argv[0], sp->se_getty_argv, env);
12521558Srgrimes	stall("can't exec getty '%s' for port %s: %m",
12531558Srgrimes		sp->se_getty_argv[0], sp->se_device);
12541558Srgrimes	_exit(1);
12551558Srgrimes}
12561558Srgrimes
12571558Srgrimes/*
12581558Srgrimes * Collect exit status for a child.
12591558Srgrimes * If an exiting login, start a new login running.
12601558Srgrimes */
1261183391Sdelphijstatic void
12621558Srgrimescollect_child(pid_t pid)
12631558Srgrimes{
126492806Sobrien	session_t *sp, *sprev, *snext;
12651558Srgrimes
12661558Srgrimes	if (! sessions)
12671558Srgrimes		return;
12681558Srgrimes
12691558Srgrimes	if (! (sp = find_session(pid)))
12701558Srgrimes		return;
12711558Srgrimes
12721558Srgrimes	del_session(sp);
12731558Srgrimes	sp->se_process = 0;
12741558Srgrimes
12751558Srgrimes	if (sp->se_flags & SE_SHUTDOWN) {
127627837Sdavidn		if ((sprev = sp->se_prev) != NULL)
12771558Srgrimes			sprev->se_next = sp->se_next;
12781558Srgrimes		else
12791558Srgrimes			sessions = sp->se_next;
128027837Sdavidn		if ((snext = sp->se_next) != NULL)
12811558Srgrimes			snext->se_prev = sp->se_prev;
12821558Srgrimes		free_session(sp);
12831558Srgrimes		return;
12841558Srgrimes	}
12851558Srgrimes
12861558Srgrimes	if ((pid = start_getty(sp)) == -1) {
12871558Srgrimes		/* serious trouble */
12881558Srgrimes		requested_transition = clean_ttys;
12891558Srgrimes		return;
12901558Srgrimes	}
12911558Srgrimes
12921558Srgrimes	sp->se_process = pid;
12931558Srgrimes	sp->se_started = time((time_t *) 0);
12941558Srgrimes	add_session(sp);
12951558Srgrimes}
12961558Srgrimes
12971558Srgrimes/*
12981558Srgrimes * Catch a signal and request a state transition.
12991558Srgrimes */
1300183391Sdelphijstatic void
130192838Simptransition_handler(int sig)
13021558Srgrimes{
13031558Srgrimes
13041558Srgrimes	switch (sig) {
13051558Srgrimes	case SIGHUP:
1306217750Sjilles		if (current_state == read_ttys || current_state == multi_user ||
1307217750Sjilles		    current_state == clean_ttys || current_state == catatonia)
1308217750Sjilles			requested_transition = clean_ttys;
13091558Srgrimes		break;
131047962Sru	case SIGUSR2:
131147962Sru		howto = RB_POWEROFF;
131247962Sru	case SIGUSR1:
131347962Sru		howto |= RB_HALT;
13142323Snate	case SIGINT:
13152327Sjkh		Reboot = TRUE;
13161558Srgrimes	case SIGTERM:
1317217750Sjilles		if (current_state == read_ttys || current_state == multi_user ||
1318217750Sjilles		    current_state == clean_ttys || current_state == catatonia)
1319217750Sjilles			requested_transition = death;
1320217750Sjilles		else
1321217750Sjilles			requested_transition = death_single;
13221558Srgrimes		break;
13231558Srgrimes	case SIGTSTP:
1324217750Sjilles		if (current_state == runcom || current_state == read_ttys ||
1325217750Sjilles		    current_state == clean_ttys ||
1326217750Sjilles		    current_state == multi_user || current_state == catatonia)
1327217750Sjilles			requested_transition = catatonia;
13281558Srgrimes		break;
13291558Srgrimes	default:
13301558Srgrimes		requested_transition = 0;
13311558Srgrimes		break;
13321558Srgrimes	}
13331558Srgrimes}
13341558Srgrimes
13351558Srgrimes/*
13361558Srgrimes * Take the system multiuser.
13371558Srgrimes */
1338183391Sdelphijstatic state_func_t
133992838Simpmulti_user(void)
13401558Srgrimes{
13411558Srgrimes	pid_t pid;
134292806Sobrien	session_t *sp;
13431558Srgrimes
13441558Srgrimes	requested_transition = 0;
13451558Srgrimes
13461558Srgrimes	/*
13471558Srgrimes	 * If the administrator has not set the security level to -1
13481558Srgrimes	 * to indicate that the kernel should not run multiuser in secure
13498871Srgrimes	 * mode, and the run script has not set a higher level of security
13501558Srgrimes	 * than level 1, then put the kernel into secure mode.
13511558Srgrimes	 */
13521558Srgrimes	if (getsecuritylevel() == 0)
13531558Srgrimes		setsecuritylevel(1);
13541558Srgrimes
13551558Srgrimes	for (sp = sessions; sp; sp = sp->se_next) {
13561558Srgrimes		if (sp->se_process)
13571558Srgrimes			continue;
13581558Srgrimes		if ((pid = start_getty(sp)) == -1) {
13591558Srgrimes			/* serious trouble */
13601558Srgrimes			requested_transition = clean_ttys;
13611558Srgrimes			break;
13621558Srgrimes		}
13631558Srgrimes		sp->se_process = pid;
13641558Srgrimes		sp->se_started = time((time_t *) 0);
13651558Srgrimes		add_session(sp);
13661558Srgrimes	}
13671558Srgrimes
13681558Srgrimes	while (!requested_transition)
13691558Srgrimes		if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
13701558Srgrimes			collect_child(pid);
13711558Srgrimes
13721558Srgrimes	return (state_func_t) requested_transition;
13731558Srgrimes}
13741558Srgrimes
13751558Srgrimes/*
137657344Salfred * This is an (n*2)+(n^2) algorithm.  We hope it isn't run often...
13771558Srgrimes */
1378183391Sdelphijstatic state_func_t
137992838Simpclean_ttys(void)
13801558Srgrimes{
138192806Sobrien	session_t *sp, *sprev;
138292806Sobrien	struct ttyent *typ;
138392806Sobrien	int session_index = 0;
138492806Sobrien	int devlen;
13853594Sache	char *old_getty, *old_window, *old_type;
13861558Srgrimes
1387173787Sobrien	/*
1388173787Sobrien	 * mark all sessions for death, (!SE_PRESENT)
138957344Salfred	 * as we find or create new ones they'll be marked as keepers,
139057344Salfred	 * we'll later nuke all the ones not found in /etc/ttys
139157344Salfred	 */
139257344Salfred	for (sp = sessions; sp != NULL; sp = sp->se_next)
139357344Salfred		sp->se_flags &= ~SE_PRESENT;
139457344Salfred
13951558Srgrimes	devlen = sizeof(_PATH_DEV) - 1;
139627837Sdavidn	while ((typ = getttyent()) != NULL) {
13971558Srgrimes		++session_index;
13981558Srgrimes
13991558Srgrimes		for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
14001558Srgrimes			if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
14011558Srgrimes				break;
14021558Srgrimes
14031558Srgrimes		if (sp) {
140457344Salfred			/* we want this one to live */
140557344Salfred			sp->se_flags |= SE_PRESENT;
14061558Srgrimes			if (sp->se_index != session_index) {
14071558Srgrimes				warning("port %s changed utmp index from %d to %d",
14081558Srgrimes				       sp->se_device, sp->se_index,
14091558Srgrimes				       session_index);
14101558Srgrimes				sp->se_index = session_index;
14111558Srgrimes			}
14121558Srgrimes			if ((typ->ty_status & TTY_ON) == 0 ||
14131558Srgrimes			    typ->ty_getty == 0) {
14141558Srgrimes				sp->se_flags |= SE_SHUTDOWN;
14151558Srgrimes				kill(sp->se_process, SIGHUP);
14161558Srgrimes				continue;
14171558Srgrimes			}
14181558Srgrimes			sp->se_flags &= ~SE_SHUTDOWN;
14193594Sache			old_getty = sp->se_getty ? strdup(sp->se_getty) : 0;
14203594Sache			old_window = sp->se_window ? strdup(sp->se_window) : 0;
14213594Sache			old_type = sp->se_type ? strdup(sp->se_type) : 0;
14221558Srgrimes			if (setupargv(sp, typ) == 0) {
14231558Srgrimes				warning("can't parse getty for port %s",
14241558Srgrimes					sp->se_device);
14251558Srgrimes				sp->se_flags |= SE_SHUTDOWN;
14261558Srgrimes				kill(sp->se_process, SIGHUP);
14271558Srgrimes			}
14283594Sache			else if (   !old_getty
142927837Sdavidn				 || (!old_type && sp->se_type)
143027837Sdavidn				 || (old_type && !sp->se_type)
143127837Sdavidn				 || (!old_window && sp->se_window)
143227837Sdavidn				 || (old_window && !sp->se_window)
143327837Sdavidn				 || (strcmp(old_getty, sp->se_getty) != 0)
143427837Sdavidn				 || (old_window && strcmp(old_window, sp->se_window) != 0)
143527837Sdavidn				 || (old_type && strcmp(old_type, sp->se_type) != 0)
14363594Sache				) {
14373594Sache				/* Don't set SE_SHUTDOWN here */
14383594Sache				sp->se_nspace = 0;
14393594Sache				sp->se_started = 0;
14403594Sache				kill(sp->se_process, SIGHUP);
14413594Sache			}
14423594Sache			if (old_getty)
14433594Sache				free(old_getty);
144478484Smikeh			if (old_window)
14453594Sache				free(old_window);
14463594Sache			if (old_type)
14473594Sache				free(old_type);
14481558Srgrimes			continue;
14491558Srgrimes		}
14501558Srgrimes
14511558Srgrimes		new_session(sprev, session_index, typ);
14521558Srgrimes	}
14531558Srgrimes
14541558Srgrimes	endttyent();
14551558Srgrimes
145657344Salfred	/*
145757344Salfred	 * sweep through and kill all deleted sessions
145857344Salfred	 * ones who's /etc/ttys line was deleted (SE_PRESENT unset)
145957344Salfred	 */
146057344Salfred	for (sp = sessions; sp != NULL; sp = sp->se_next) {
146157344Salfred		if ((sp->se_flags & SE_PRESENT) == 0) {
146257344Salfred			sp->se_flags |= SE_SHUTDOWN;
146357344Salfred			kill(sp->se_process, SIGHUP);
146457344Salfred		}
146557344Salfred	}
146657344Salfred
14671558Srgrimes	return (state_func_t) multi_user;
14681558Srgrimes}
14691558Srgrimes
14701558Srgrimes/*
14711558Srgrimes * Block further logins.
14721558Srgrimes */
1473183391Sdelphijstatic state_func_t
147492838Simpcatatonia(void)
14751558Srgrimes{
147692806Sobrien	session_t *sp;
14771558Srgrimes
14781558Srgrimes	for (sp = sessions; sp; sp = sp->se_next)
14791558Srgrimes		sp->se_flags |= SE_SHUTDOWN;
14801558Srgrimes
14811558Srgrimes	return (state_func_t) multi_user;
14821558Srgrimes}
14831558Srgrimes
14841558Srgrimes/*
14851558Srgrimes * Note SIGALRM.
14861558Srgrimes */
1487183391Sdelphijstatic void
148892838Simpalrm_handler(int sig)
14891558Srgrimes{
1490173785Sobrien
149127837Sdavidn	(void)sig;
14921558Srgrimes	clang = 1;
14931558Srgrimes}
14941558Srgrimes
14951558Srgrimes/*
14961558Srgrimes * Bring the system down to single user.
14971558Srgrimes */
1498183391Sdelphijstatic state_func_t
149992838Simpdeath(void)
15001558Srgrimes{
150192806Sobrien	session_t *sp;
15021558Srgrimes
1503194198Sed	/*
1504194198Sed	 * Also revoke the TTY here.  Because runshutdown() may reopen
1505194198Sed	 * the TTY whose getty we're killing here, there is no guarantee
1506194198Sed	 * runshutdown() will perform the initial open() call, causing
1507194198Sed	 * the terminal attributes to be misconfigured.
1508194198Sed	 */
150927215Sache	for (sp = sessions; sp; sp = sp->se_next) {
15101558Srgrimes		sp->se_flags |= SE_SHUTDOWN;
151127941Sache		kill(sp->se_process, SIGHUP);
1512194198Sed		revoke(sp->se_device);
151327215Sache	}
15141558Srgrimes
151527837Sdavidn	/* Try to run the rc.shutdown script within a period of time */
1516173785Sobrien	runshutdown();
1517173785Sobrien
1518217750Sjilles	return (state_func_t) death_single;
1519217750Sjilles}
1520217750Sjilles
1521217750Sjilles/*
1522217750Sjilles * Do what is necessary to reinitialize single user mode or reboot
1523217750Sjilles * from an incomplete state.
1524217750Sjilles */
1525217750Sjillesstatic state_func_t
1526217750Sjillesdeath_single(void)
1527217750Sjilles{
1528217750Sjilles	int i;
1529217750Sjilles	pid_t pid;
1530217750Sjilles	static const int death_sigs[2] = { SIGTERM, SIGKILL };
1531217750Sjilles
1532217750Sjilles	revoke(_PATH_CONSOLE);
1533217750Sjilles
153427197Sache	for (i = 0; i < 2; ++i) {
15351558Srgrimes		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
15361558Srgrimes			return (state_func_t) single_user;
15371558Srgrimes
15381558Srgrimes		clang = 0;
15391558Srgrimes		alarm(DEATH_WATCH);
15401558Srgrimes		do
15411558Srgrimes			if ((pid = waitpid(-1, (int *)0, 0)) != -1)
15421558Srgrimes				collect_child(pid);
15431558Srgrimes		while (clang == 0 && errno != ECHILD);
15441558Srgrimes
15451558Srgrimes		if (errno == ECHILD)
15461558Srgrimes			return (state_func_t) single_user;
15471558Srgrimes	}
15481558Srgrimes
15491558Srgrimes	warning("some processes would not die; ps axl advised");
15501558Srgrimes
15511558Srgrimes	return (state_func_t) single_user;
15521558Srgrimes}
155327837Sdavidn
155427837Sdavidn/*
155527837Sdavidn * Run the system shutdown script.
155627837Sdavidn *
155727837Sdavidn * Exit codes:      XXX I should document more
155827837Sdavidn * -2       shutdown script terminated abnormally
155927837Sdavidn * -1       fatal error - can't run script
156027837Sdavidn * 0        good.
156127837Sdavidn * >0       some error (exit code)
156227837Sdavidn */
1563183391Sdelphijstatic int
156492838Simprunshutdown(void)
156527837Sdavidn{
156627837Sdavidn	pid_t pid, wpid;
156727837Sdavidn	int status;
156827837Sdavidn	int shutdowntimeout;
156927837Sdavidn	size_t len;
157053550Sdillon	char *argv[4];
1571166484Simp	const char *shell;
157227837Sdavidn	struct sigaction sa;
157328344Sdavidn	struct stat sb;
157427837Sdavidn
157528344Sdavidn	/*
157628344Sdavidn	 * rc.shutdown is optional, so to prevent any unnecessary
157728344Sdavidn	 * complaints from the shell we simply don't run it if the
157828344Sdavidn	 * file does not exist. If the stat() here fails for other
157928344Sdavidn	 * reasons, we'll let the shell complain.
158028344Sdavidn	 */
158128344Sdavidn	if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT)
158228344Sdavidn		return 0;
158328344Sdavidn
1584166484Simp	shell = get_shell();
1585166484Simp
158627837Sdavidn	if ((pid = fork()) == 0) {
158727837Sdavidn		sigemptyset(&sa.sa_mask);
158827837Sdavidn		sa.sa_flags = 0;
158927837Sdavidn		sa.sa_handler = SIG_IGN;
1590173785Sobrien		sigaction(SIGTSTP, &sa, (struct sigaction *)0);
1591173785Sobrien		sigaction(SIGHUP, &sa, (struct sigaction *)0);
159227837Sdavidn
1593232977Sed		open_console();
159427837Sdavidn
1595140070Sdelphij		char _sh[]	= "sh";
1596140070Sdelphij		char _reboot[]	= "reboot";
1597140070Sdelphij		char _single[]	= "single";
1598140070Sdelphij		char _path_rundown[] = _PATH_RUNDOWN;
1599140070Sdelphij
1600140070Sdelphij		argv[0] = _sh;
1601140070Sdelphij		argv[1] = _path_rundown;
1602140070Sdelphij		argv[2] = Reboot ? _reboot : _single;
160353550Sdillon		argv[3] = 0;
160427837Sdavidn
160527837Sdavidn		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
160627837Sdavidn
160727941Sache#ifdef LOGIN_CAP
160827941Sache		setprocresources(RESOURCE_RC);
160927941Sache#endif
1610166484Simp		execv(shell, argv);
1611166484Simp		warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN);
161227837Sdavidn		_exit(1);	/* force single user mode */
161327837Sdavidn	}
161427837Sdavidn
161527837Sdavidn	if (pid == -1) {
1616166484Simp		emergency("can't fork for %s on %s: %m", shell, _PATH_RUNDOWN);
161727837Sdavidn		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
161827837Sdavidn			continue;
161927837Sdavidn		sleep(STALL_TIMEOUT);
162027837Sdavidn		return -1;
162127837Sdavidn	}
162227837Sdavidn
162327837Sdavidn	len = sizeof(shutdowntimeout);
1624173785Sobrien	if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len,
1625173785Sobrien	    NULL, 0) == -1 || shutdowntimeout < 2)
1626173785Sobrien		shutdowntimeout = DEATH_SCRIPT;
162727837Sdavidn	alarm(shutdowntimeout);
162827837Sdavidn	clang = 0;
162927837Sdavidn	/*
163027837Sdavidn	 * Copied from single_user().  This is a bit paranoid.
163127837Sdavidn	 * Use the same ALRM handler.
163227837Sdavidn	 */
163327837Sdavidn	do {
163427837Sdavidn		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
163527837Sdavidn			collect_child(wpid);
163627837Sdavidn		if (clang == 1) {
163727837Sdavidn			/* we were waiting for the sub-shell */
163827837Sdavidn			kill(wpid, SIGTERM);
1639166484Simp			warning("timeout expired for %s on %s: %m; going to "
1640166484Simp			    "single user mode", shell, _PATH_RUNDOWN);
164127837Sdavidn			return -1;
164227837Sdavidn		}
164327837Sdavidn		if (wpid == -1) {
164427837Sdavidn			if (errno == EINTR)
164527837Sdavidn				continue;
1646166484Simp			warning("wait for %s on %s failed: %m; going to "
1647166484Simp			    "single user mode", shell, _PATH_RUNDOWN);
164827837Sdavidn			return -1;
164927837Sdavidn		}
165027837Sdavidn		if (wpid == pid && WIFSTOPPED(status)) {
165127837Sdavidn			warning("init: %s on %s stopped, restarting\n",
1652166484Simp				shell, _PATH_RUNDOWN);
165327837Sdavidn			kill(pid, SIGCONT);
165427837Sdavidn			wpid = -1;
165527837Sdavidn		}
165627837Sdavidn	} while (wpid != pid && !clang);
165727837Sdavidn
165827837Sdavidn	/* Turn off the alarm */
165927837Sdavidn	alarm(0);
166027837Sdavidn
166127837Sdavidn	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
166227837Sdavidn	    requested_transition == catatonia) {
166327837Sdavidn		/*
166427837Sdavidn		 * /etc/rc.shutdown executed /sbin/reboot;
166527837Sdavidn		 * wait for the end quietly
166627837Sdavidn		 */
166727837Sdavidn		sigset_t s;
166827837Sdavidn
166927837Sdavidn		sigfillset(&s);
167027837Sdavidn		for (;;)
167127837Sdavidn			sigsuspend(&s);
167227837Sdavidn	}
167327837Sdavidn
167427837Sdavidn	if (!WIFEXITED(status)) {
1675166484Simp		warning("%s on %s terminated abnormally, going to "
1676166484Simp		    "single user mode", shell, _PATH_RUNDOWN);
167727837Sdavidn		return -2;
167827837Sdavidn	}
167927837Sdavidn
168027837Sdavidn	if ((status = WEXITSTATUS(status)) != 0)
168127837Sdavidn		warning("%s returned status %d", _PATH_RUNDOWN, status);
168227837Sdavidn
168327837Sdavidn	return status;
168427837Sdavidn}
168527837Sdavidn
1686140070Sdelphijstatic char *
1687140070Sdelphijstrk(char *p)
16885478Sache{
1689173785Sobrien	static char *t;
1690173785Sobrien	char *q;
1691173785Sobrien	int c;
16925478Sache
1693173785Sobrien	if (p)
1694173785Sobrien		t = p;
1695173785Sobrien	if (!t)
1696173785Sobrien		return 0;
16975478Sache
1698173785Sobrien	c = *t;
1699173785Sobrien	while (c == ' ' || c == '\t' )
1700173785Sobrien		c = *++t;
1701173785Sobrien	if (!c) {
1702173785Sobrien		t = 0;
1703173785Sobrien		return 0;
1704173785Sobrien	}
17055478Sache	q = t;
1706173785Sobrien	if (c == '\'') {
1707173785Sobrien		c = *++t;
1708173785Sobrien		q = t;
1709173785Sobrien		while (c && c != '\'')
1710173785Sobrien			c = *++t;
1711173785Sobrien		if (!c)  /* unterminated string */
1712173785Sobrien			q = t = 0;
1713173785Sobrien		else
1714173785Sobrien			*t++ = 0;
1715173785Sobrien	} else {
1716173785Sobrien		while (c && c != ' ' && c != '\t' )
1717173785Sobrien			c = *++t;
1718173785Sobrien		*t++ = 0;
1719173785Sobrien		if (!c)
1720173785Sobrien			t = 0;
1721173785Sobrien	}
1722173785Sobrien	return q;
17235478Sache}
172421865Sdavidn
172521865Sdavidn#ifdef LOGIN_CAP
1726183391Sdelphijstatic void
172792838Simpsetprocresources(const char *cname)
172821865Sdavidn{
172921941Sdavidn	login_cap_t *lc;
173027176Sache	if ((lc = login_getclassbyname(cname, NULL)) != NULL) {
1731173785Sobrien		setusercontext(lc, (struct passwd*)NULL, 0,
1732254288Sjilles		    LOGIN_SETPRIORITY | LOGIN_SETRESOURCES |
1733254288Sjilles		    LOGIN_SETLOGINCLASS | LOGIN_SETCPUMASK);
173421941Sdavidn		login_close(lc);
173521941Sdavidn	}
173621865Sdavidn}
173721865Sdavidn#endif
1738