init.c revision 173787
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: head/sbin/init/init.c 173787 2007-11-20 21:25:58Z obrien $";
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 */
89173785Sobrien#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"
95173785Sobrien#define	RESOURCE_WINDOW 	"default"
96173785Sobrien#define	RESOURCE_GETTY		"default"
971558Srgrimes
9892838Simpvoid handle(sig_t, ...);
9992838Simpvoid delset(sigset_t *, ...);
1001558Srgrimes
10192838Simpvoid stall(const char *, ...) __printflike(1, 2);
10292838Simpvoid warning(const char *, ...) __printflike(1, 2);
10392838Simpvoid emergency(const char *, ...) __printflike(1, 2);
10492838Simpvoid disaster(int);
10592838Simpvoid badsys(int);
10692838Simpint  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
11792838Simpstate_func_t single_user(void);
11892838Simpstate_func_t runcom(void);
11992838Simpstate_func_t read_ttys(void);
12092838Simpstate_func_t multi_user(void);
12192838Simpstate_func_t clean_ttys(void);
12292838Simpstate_func_t catatonia(void);
12392838Simpstate_func_t death(void);
1241558Srgrimes
125166484Simpstate_func_t run_script(const char *);
126166484Simp
1271558Srgrimesenum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
12811910Sphk#define FALSE	0
12911910Sphk#define TRUE	1
13011910Sphk
1312327Sjkhint Reboot = FALSE;
13247962Sruint howto = RB_AUTOBOOT;
1331558Srgrimes
13419227Sphkint devfs;
13519227Sphk
13692838Simpvoid transition(state_t);
137166484Simpstate_t requested_transition;
1381558Srgrimes
139140070Sdelphijvoid setctty(const char *);
140166484Simpconst char *get_shell(void);
141166484Simpvoid write_stderr(const char *message);
1421558Srgrimes
1431558Srgrimestypedef struct init_session {
1441558Srgrimes	int	se_index;		/* index of entry in ttys file */
1451558Srgrimes	pid_t	se_process;		/* controlling process */
1461558Srgrimes	time_t	se_started;		/* used to avoid thrashing */
1471558Srgrimes	int	se_flags;		/* status of session */
1481558Srgrimes#define	SE_SHUTDOWN	0x1		/* session won't be restarted */
14957344Salfred#define	SE_PRESENT	0x2		/* session is in /etc/ttys */
1503594Sache	int     se_nspace;              /* spacing count */
1511558Srgrimes	char	*se_device;		/* filename of port */
1521558Srgrimes	char	*se_getty;		/* what to run on that port */
1533594Sache	char    *se_getty_argv_space;   /* pre-parsed argument array space */
1541558Srgrimes	char	**se_getty_argv;	/* pre-parsed argument array */
1551558Srgrimes	char	*se_window;		/* window system (started only once) */
1563594Sache	char    *se_window_argv_space;  /* pre-parsed argument array space */
1571558Srgrimes	char	**se_window_argv;	/* pre-parsed argument array */
1583594Sache	char    *se_type;               /* default terminal type */
1591558Srgrimes	struct	init_session *se_prev;
1601558Srgrimes	struct	init_session *se_next;
1611558Srgrimes} session_t;
1621558Srgrimes
16392838Simpvoid free_session(session_t *);
16492838Simpsession_t *new_session(session_t *, int, struct ttyent *);
1651558Srgrimessession_t *sessions;
1661558Srgrimes
16792838Simpchar **construct_argv(char *);
16892838Simpvoid start_window_system(session_t *);
16992838Simpvoid collect_child(pid_t);
17092838Simppid_t start_getty(session_t *);
17192838Simpvoid transition_handler(int);
17292838Simpvoid alrm_handler(int);
17392838Simpvoid setsecuritylevel(int);
17492838Simpint getsecuritylevel(void);
17592838Simpint setupargv(session_t *, struct ttyent *);
17621941Sdavidn#ifdef LOGIN_CAP
17792838Simpvoid setprocresources(const char *);
17821941Sdavidn#endif
1791558Srgrimesint clang;
1801558Srgrimes
18192838Simpvoid clear_session_logs(session_t *);
1821558Srgrimes
18392838Simpint start_session_db(void);
18492838Simpvoid add_session(session_t *);
18592838Simpvoid del_session(session_t *);
18692838Simpsession_t *find_session(pid_t);
1871558SrgrimesDB *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 */
3911558Srgrimesvoid
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 */
4151558Srgrimesvoid
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 */
4321558Srgrimesvoid
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 */
4481558Srgrimesvoid
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 */
4621558Srgrimesvoid
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 */
4781558Srgrimesvoid
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 */
4911558Srgrimesvoid
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 */
5051558Srgrimesint
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 */
5291558Srgrimesvoid
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 */
5571558Srgrimesvoid
55892838Simptransition(state_t s)
5591558Srgrimes{
560173785Sobrien
5611558Srgrimes	for (;;)
5621558Srgrimes		s = (state_t) (*s)();
5631558Srgrimes}
5641558Srgrimes
5651558Srgrimes/*
5661558Srgrimes * Close out the accounting files for a login session.
5671558Srgrimes * NB: should send a message to the session logger to avoid blocking.
5681558Srgrimes */
5691558Srgrimesvoid
57092838Simpclear_session_logs(session_t *sp)
5711558Srgrimes{
5721558Srgrimes	char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
5731558Srgrimes
5741558Srgrimes	if (logout(line))
5751558Srgrimes		logwtmp(line, "", "");
5761558Srgrimes}
5771558Srgrimes
5781558Srgrimes/*
5791558Srgrimes * Start a session and allocate a controlling terminal.
5801558Srgrimes * Only called by children of init after forking.
5811558Srgrimes */
5821558Srgrimesvoid
583140070Sdelphijsetctty(const char *name)
5841558Srgrimes{
5851558Srgrimes	int fd;
5861558Srgrimes
587173785Sobrien	revoke(name);
5881558Srgrimes	if ((fd = open(name, O_RDWR)) == -1) {
5891558Srgrimes		stall("can't open %s: %m", name);
5901558Srgrimes		_exit(1);
5911558Srgrimes	}
5921558Srgrimes	if (login_tty(fd) == -1) {
5931558Srgrimes		stall("can't get %s for controlling terminal: %m", name);
5941558Srgrimes		_exit(1);
5951558Srgrimes	}
5961558Srgrimes}
5971558Srgrimes
598166484Simpconst char *
599166484Simpget_shell(void)
600166484Simp{
601166484Simp	static char kenv_value[PATH_MAX];
602166484Simp
603166484Simp	if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0)
604166484Simp		return kenv_value;
605166484Simp	else
606166484Simp		return _PATH_BSHELL;
607166484Simp}
608166484Simp
609166484Simpvoid
610166484Simpwrite_stderr(const char *message)
611166484Simp{
612173785Sobrien
613166484Simp	write(STDERR_FILENO, message, strlen(message));
614166484Simp}
615166484Simp
6161558Srgrimes/*
6171558Srgrimes * Bring the system up single user.
6181558Srgrimes */
6191558Srgrimesstate_func_t
62092838Simpsingle_user(void)
6211558Srgrimes{
6221558Srgrimes	pid_t pid, wpid;
6231558Srgrimes	int status;
6241558Srgrimes	sigset_t mask;
625166484Simp	const char *shell;
6261558Srgrimes	char *argv[2];
6271558Srgrimes#ifdef SECURE
6281558Srgrimes	struct ttyent *typ;
6291558Srgrimes	struct passwd *pp;
6301558Srgrimes	static const char banner[] =
6311558Srgrimes		"Enter root password, or ^D to go multi-user\n";
6321558Srgrimes	char *clear, *password;
6331558Srgrimes#endif
63437814Sphk#ifdef DEBUGSHELL
63537814Sphk	char altshell[128];
63637814Sphk#endif
6371558Srgrimes
6382327Sjkh	if (Reboot) {
63947962Sru		/* Instead of going single user, let's reboot the machine */
6402323Snate		sync();
6412323Snate		alarm(2);
6422323Snate		pause();
64347962Sru		reboot(howto);
6442323Snate		_exit(0);
6452323Snate	}
6462323Snate
647166484Simp	shell = get_shell();
648166484Simp
6491558Srgrimes	if ((pid = fork()) == 0) {
6501558Srgrimes		/*
6511558Srgrimes		 * Start the single user session.
6521558Srgrimes		 */
6531558Srgrimes		setctty(_PATH_CONSOLE);
6541558Srgrimes
6551558Srgrimes#ifdef SECURE
6561558Srgrimes		/*
6571558Srgrimes		 * Check the root password.
6581558Srgrimes		 * We don't care if the console is 'on' by default;
6591558Srgrimes		 * it's the only tty that can be 'off' and 'secure'.
6601558Srgrimes		 */
6611558Srgrimes		typ = getttynam("console");
6621558Srgrimes		pp = getpwnam("root");
66353550Sdillon		if (typ && (typ->ty_status & TTY_SECURE) == 0 &&
66453550Sdillon		    pp && *pp->pw_passwd) {
665166484Simp			write_stderr(banner);
6661558Srgrimes			for (;;) {
6671558Srgrimes				clear = getpass("Password:");
6681558Srgrimes				if (clear == 0 || *clear == '\0')
6691558Srgrimes					_exit(0);
6701558Srgrimes				password = crypt(clear, pp->pw_passwd);
6711558Srgrimes				bzero(clear, _PASSWORD_LEN);
6721558Srgrimes				if (strcmp(password, pp->pw_passwd) == 0)
6731558Srgrimes					break;
6741558Srgrimes				warning("single-user login failed\n");
6751558Srgrimes			}
6761558Srgrimes		}
6771558Srgrimes		endttyent();
6781558Srgrimes		endpwent();
6791558Srgrimes#endif /* SECURE */
6801558Srgrimes
6811558Srgrimes#ifdef DEBUGSHELL
6821558Srgrimes		{
68337814Sphk			char *cp = altshell;
6841558Srgrimes			int num;
6851558Srgrimes
686166484Simp#define	SHREQUEST "Enter full pathname of shell or RETURN for "
687166484Simp			write_stderr(SHREQUEST);
688166484Simp			write_stderr(shell);
689166484Simp			write_stderr(": ");
6901558Srgrimes			while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
6911558Srgrimes			    num != 0 && *cp != '\n' && cp < &altshell[127])
692173785Sobrien				cp++;
6931558Srgrimes			*cp = '\0';
6941558Srgrimes			if (altshell[0] != '\0')
6951558Srgrimes				shell = altshell;
6961558Srgrimes		}
6971558Srgrimes#endif /* DEBUGSHELL */
6981558Srgrimes
6991558Srgrimes		/*
7001558Srgrimes		 * Unblock signals.
7011558Srgrimes		 * We catch all the interesting ones,
7021558Srgrimes		 * and those are reset to SIG_DFL on exec.
7031558Srgrimes		 */
7041558Srgrimes		sigemptyset(&mask);
7051558Srgrimes		sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
7061558Srgrimes
7071558Srgrimes		/*
7081558Srgrimes		 * Fire off a shell.
7091558Srgrimes		 * If the default one doesn't work, try the Bourne shell.
7101558Srgrimes		 */
711140070Sdelphij
712140070Sdelphij		char name[] = "-sh";
713140070Sdelphij
714140070Sdelphij		argv[0] = name;
7151558Srgrimes		argv[1] = 0;
7161558Srgrimes		execv(shell, argv);
7171558Srgrimes		emergency("can't exec %s for single user: %m", shell);
7181558Srgrimes		execv(_PATH_BSHELL, argv);
7191558Srgrimes		emergency("can't exec %s for single user: %m", _PATH_BSHELL);
7201558Srgrimes		sleep(STALL_TIMEOUT);
7211558Srgrimes		_exit(1);
7221558Srgrimes	}
7231558Srgrimes
7241558Srgrimes	if (pid == -1) {
7251558Srgrimes		/*
7261558Srgrimes		 * We are seriously hosed.  Do our best.
7271558Srgrimes		 */
7281558Srgrimes		emergency("can't fork single-user shell, trying again");
7291558Srgrimes		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
7301558Srgrimes			continue;
7311558Srgrimes		return (state_func_t) single_user;
7321558Srgrimes	}
7331558Srgrimes
7341558Srgrimes	requested_transition = 0;
7351558Srgrimes	do {
7361558Srgrimes		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
7371558Srgrimes			collect_child(wpid);
7381558Srgrimes		if (wpid == -1) {
7391558Srgrimes			if (errno == EINTR)
7401558Srgrimes				continue;
7411558Srgrimes			warning("wait for single-user shell failed: %m; restarting");
7421558Srgrimes			return (state_func_t) single_user;
7431558Srgrimes		}
7441558Srgrimes		if (wpid == pid && WIFSTOPPED(status)) {
7451558Srgrimes			warning("init: shell stopped, restarting\n");
7461558Srgrimes			kill(pid, SIGCONT);
7471558Srgrimes			wpid = -1;
7481558Srgrimes		}
7491558Srgrimes	} while (wpid != pid && !requested_transition);
7501558Srgrimes
7511558Srgrimes	if (requested_transition)
7521558Srgrimes		return (state_func_t) requested_transition;
7531558Srgrimes
7541558Srgrimes	if (!WIFEXITED(status)) {
7558871Srgrimes		if (WTERMSIG(status) == SIGKILL) {
7568871Srgrimes			/*
7578871Srgrimes			 *  reboot(8) killed shell?
7581558Srgrimes			 */
7591558Srgrimes			warning("single user shell terminated.");
7601558Srgrimes			sleep(STALL_TIMEOUT);
7611558Srgrimes			_exit(0);
7628871Srgrimes		} else {
7631558Srgrimes			warning("single user shell terminated, restarting");
7641558Srgrimes			return (state_func_t) single_user;
7651558Srgrimes		}
7661558Srgrimes	}
7671558Srgrimes
7681558Srgrimes	runcom_mode = FASTBOOT;
7691558Srgrimes	return (state_func_t) runcom;
7701558Srgrimes}
7711558Srgrimes
7721558Srgrimes/*
7731558Srgrimes * Run the system startup script.
7741558Srgrimes */
7751558Srgrimesstate_func_t
77692838Simpruncom(void)
7771558Srgrimes{
778166484Simp	state_func_t next_transition;
779166484Simp
780166484Simp	if ((next_transition = run_script(_PATH_RUNCOM)) != 0)
781166484Simp		return next_transition;
782166484Simp
783166484Simp	runcom_mode = AUTOBOOT;		/* the default */
784166484Simp	/* NB: should send a message to the session logger to avoid blocking. */
785166484Simp	logwtmp("~", "reboot", "");
786166484Simp	return (state_func_t) read_ttys;
787166484Simp}
788166484Simp
789166484Simp/*
790166484Simp * Run a shell script.
791166484Simp * Returns 0 on success, otherwise the next transition to enter:
792166484Simp *  - single_user if fork/execv/waitpid failed, or if the script
793166484Simp *    terminated with a signal or exit code != 0.
794166484Simp *  - death if a SIGTERM was delivered to init(8).
795166484Simp */
796166484Simpstate_func_t
797166484Simprun_script(const char *script)
798166484Simp{
7991558Srgrimes	pid_t pid, wpid;
8001558Srgrimes	int status;
8011558Srgrimes	char *argv[4];
802166484Simp	const char *shell;
8031558Srgrimes	struct sigaction sa;
8041558Srgrimes
805166484Simp	shell = get_shell();
806166484Simp
8071558Srgrimes	if ((pid = fork()) == 0) {
8081558Srgrimes		sigemptyset(&sa.sa_mask);
8091558Srgrimes		sa.sa_flags = 0;
8101558Srgrimes		sa.sa_handler = SIG_IGN;
811173785Sobrien		sigaction(SIGTSTP, &sa, (struct sigaction *)0);
812173785Sobrien		sigaction(SIGHUP, &sa, (struct sigaction *)0);
8131558Srgrimes
8141558Srgrimes		setctty(_PATH_CONSOLE);
8151558Srgrimes
816140070Sdelphij		char _sh[]	 	= "sh";
817140070Sdelphij		char _autoboot[]	= "autoboot";
818140070Sdelphij
819140070Sdelphij		argv[0] = _sh;
820166484Simp		argv[1] = __DECONST(char *, script);
821140070Sdelphij		argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0;
8221558Srgrimes		argv[3] = 0;
8231558Srgrimes
8241558Srgrimes		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
8251558Srgrimes
82621865Sdavidn#ifdef LOGIN_CAP
82721865Sdavidn		setprocresources(RESOURCE_RC);
82821865Sdavidn#endif
829166484Simp		execv(shell, argv);
830166484Simp		stall("can't exec %s for %s: %m", shell, script);
8311558Srgrimes		_exit(1);	/* force single user mode */
8321558Srgrimes	}
8331558Srgrimes
8341558Srgrimes	if (pid == -1) {
835166484Simp		emergency("can't fork for %s on %s: %m", shell, script);
8361558Srgrimes		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
8371558Srgrimes			continue;
8381558Srgrimes		sleep(STALL_TIMEOUT);
8391558Srgrimes		return (state_func_t) single_user;
8401558Srgrimes	}
8411558Srgrimes
8421558Srgrimes	/*
8431558Srgrimes	 * Copied from single_user().  This is a bit paranoid.
8441558Srgrimes	 */
84585010Sdes	requested_transition = 0;
8461558Srgrimes	do {
8471558Srgrimes		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
8481558Srgrimes			collect_child(wpid);
8491558Srgrimes		if (wpid == -1) {
85085010Sdes			if (requested_transition == death)
85185010Sdes				return (state_func_t) death;
8521558Srgrimes			if (errno == EINTR)
8531558Srgrimes				continue;
854166484Simp			warning("wait for %s on %s failed: %m; going to "
855166484Simp			    "single user mode", shell, script);
8561558Srgrimes			return (state_func_t) single_user;
8571558Srgrimes		}
8581558Srgrimes		if (wpid == pid && WIFSTOPPED(status)) {
8591558Srgrimes			warning("init: %s on %s stopped, restarting\n",
860173785Sobrien			    shell, script);
8611558Srgrimes			kill(pid, SIGCONT);
8621558Srgrimes			wpid = -1;
8631558Srgrimes		}
8641558Srgrimes	} while (wpid != pid);
8651558Srgrimes
8661558Srgrimes	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
8671558Srgrimes	    requested_transition == catatonia) {
8681558Srgrimes		/* /etc/rc executed /sbin/reboot; wait for the end quietly */
8691558Srgrimes		sigset_t s;
8701558Srgrimes
8711558Srgrimes		sigfillset(&s);
8721558Srgrimes		for (;;)
8731558Srgrimes			sigsuspend(&s);
8741558Srgrimes	}
8751558Srgrimes
8761558Srgrimes	if (!WIFEXITED(status)) {
877166484Simp		warning("%s on %s terminated abnormally, going to single "
878166484Simp		    "user mode", shell, script);
8791558Srgrimes		return (state_func_t) single_user;
8801558Srgrimes	}
8811558Srgrimes
8821558Srgrimes	if (WEXITSTATUS(status))
8831558Srgrimes		return (state_func_t) single_user;
8841558Srgrimes
885166484Simp	return (state_func_t) 0;
8861558Srgrimes}
8871558Srgrimes
8881558Srgrimes/*
8891558Srgrimes * Open the session database.
8901558Srgrimes *
8911558Srgrimes * NB: We could pass in the size here; is it necessary?
8921558Srgrimes */
8931558Srgrimesint
89492838Simpstart_session_db(void)
8951558Srgrimes{
8961558Srgrimes	if (session_db && (*session_db->close)(session_db))
8971558Srgrimes		emergency("session database close: %s", strerror(errno));
8981558Srgrimes	if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
8991558Srgrimes		emergency("session database open: %s", strerror(errno));
9001558Srgrimes		return (1);
9011558Srgrimes	}
9021558Srgrimes	return (0);
9038871Srgrimes
9041558Srgrimes}
9051558Srgrimes
9061558Srgrimes/*
9071558Srgrimes * Add a new login session.
9081558Srgrimes */
9091558Srgrimesvoid
91092838Simpadd_session(session_t *sp)
9111558Srgrimes{
9121558Srgrimes	DBT key;
9131558Srgrimes	DBT data;
9141558Srgrimes
9151558Srgrimes	key.data = &sp->se_process;
9161558Srgrimes	key.size = sizeof sp->se_process;
9171558Srgrimes	data.data = &sp;
9181558Srgrimes	data.size = sizeof sp;
9191558Srgrimes
9201558Srgrimes	if ((*session_db->put)(session_db, &key, &data, 0))
9211558Srgrimes		emergency("insert %d: %s", sp->se_process, strerror(errno));
9221558Srgrimes}
9231558Srgrimes
9241558Srgrimes/*
9251558Srgrimes * Delete an old login session.
9261558Srgrimes */
9271558Srgrimesvoid
92892838Simpdel_session(session_t *sp)
9291558Srgrimes{
9301558Srgrimes	DBT key;
9311558Srgrimes
9321558Srgrimes	key.data = &sp->se_process;
9331558Srgrimes	key.size = sizeof sp->se_process;
9341558Srgrimes
9351558Srgrimes	if ((*session_db->del)(session_db, &key, 0))
9361558Srgrimes		emergency("delete %d: %s", sp->se_process, strerror(errno));
9371558Srgrimes}
9381558Srgrimes
9391558Srgrimes/*
9401558Srgrimes * Look up a login session by pid.
9411558Srgrimes */
9421558Srgrimessession_t *
9431558Srgrimesfind_session(pid_t pid)
9441558Srgrimes{
9451558Srgrimes	DBT key;
9461558Srgrimes	DBT data;
9471558Srgrimes	session_t *ret;
9481558Srgrimes
9491558Srgrimes	key.data = &pid;
9501558Srgrimes	key.size = sizeof pid;
9511558Srgrimes	if ((*session_db->get)(session_db, &key, &data, 0) != 0)
9521558Srgrimes		return 0;
9531558Srgrimes	bcopy(data.data, (char *)&ret, sizeof(ret));
9541558Srgrimes	return ret;
9551558Srgrimes}
9561558Srgrimes
9571558Srgrimes/*
9581558Srgrimes * Construct an argument vector from a command line.
9591558Srgrimes */
9601558Srgrimeschar **
96192838Simpconstruct_argv(char *command)
9621558Srgrimes{
96392806Sobrien	int argc = 0;
96492806Sobrien	char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
9651558Srgrimes						* sizeof (char *));
9661558Srgrimes
96749018Sru	if ((argv[argc++] = strk(command)) == 0) {
96849018Sru		free(argv);
96949018Sru		return (NULL);
97049018Sru	}
97127837Sdavidn	while ((argv[argc++] = strk((char *) 0)) != NULL)
9721558Srgrimes		continue;
9731558Srgrimes	return argv;
9741558Srgrimes}
9751558Srgrimes
9761558Srgrimes/*
9771558Srgrimes * Deallocate a session descriptor.
9781558Srgrimes */
9791558Srgrimesvoid
98092838Simpfree_session(session_t *sp)
9811558Srgrimes{
9821558Srgrimes	free(sp->se_device);
9831558Srgrimes	if (sp->se_getty) {
9841558Srgrimes		free(sp->se_getty);
9853594Sache		free(sp->se_getty_argv_space);
9861558Srgrimes		free(sp->se_getty_argv);
9871558Srgrimes	}
9881558Srgrimes	if (sp->se_window) {
9891558Srgrimes		free(sp->se_window);
9903594Sache		free(sp->se_window_argv_space);
9911558Srgrimes		free(sp->se_window_argv);
9921558Srgrimes	}
9933594Sache	if (sp->se_type)
9943594Sache		free(sp->se_type);
9951558Srgrimes	free(sp);
9961558Srgrimes}
9971558Srgrimes
9981558Srgrimes/*
9991558Srgrimes * Allocate a new session descriptor.
100057344Salfred * Mark it SE_PRESENT.
10011558Srgrimes */
10021558Srgrimessession_t *
100392838Simpnew_session(session_t *sprev, int session_index, struct ttyent *typ)
10041558Srgrimes{
100592806Sobrien	session_t *sp;
100627029Spst	int fd;
10071558Srgrimes
10081558Srgrimes	if ((typ->ty_status & TTY_ON) == 0 ||
10091558Srgrimes	    typ->ty_name == 0 ||
10101558Srgrimes	    typ->ty_getty == 0)
10111558Srgrimes		return 0;
10121558Srgrimes
101327215Sache	sp = (session_t *) calloc(1, sizeof (session_t));
10141558Srgrimes
10151558Srgrimes	sp->se_index = session_index;
101657344Salfred	sp->se_flags |= SE_PRESENT;
10171558Srgrimes
10181558Srgrimes	sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
1019173785Sobrien	sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
10201558Srgrimes
102127029Spst	/*
102227029Spst	 * Attempt to open the device, if we get "device not configured"
102327029Spst	 * then don't add the device to the session list.
102427029Spst	 */
102527029Spst	if ((fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0)) < 0) {
1026135868Simp		if (errno == ENXIO) {
102727029Spst			free_session(sp);
102827029Spst			return (0);
102927029Spst		}
103027029Spst	} else
103127029Spst		close(fd);
103227029Spst
10331558Srgrimes	if (setupargv(sp, typ) == 0) {
10341558Srgrimes		free_session(sp);
10351558Srgrimes		return (0);
10361558Srgrimes	}
10371558Srgrimes
10381558Srgrimes	sp->se_next = 0;
10391558Srgrimes	if (sprev == 0) {
10401558Srgrimes		sessions = sp;
10411558Srgrimes		sp->se_prev = 0;
10421558Srgrimes	} else {
10431558Srgrimes		sprev->se_next = sp;
10441558Srgrimes		sp->se_prev = sprev;
10451558Srgrimes	}
10461558Srgrimes
10471558Srgrimes	return sp;
10481558Srgrimes}
10491558Srgrimes
10501558Srgrimes/*
10511558Srgrimes * Calculate getty and if useful window argv vectors.
10521558Srgrimes */
10531558Srgrimesint
105492838Simpsetupargv(session_t *sp, struct ttyent *typ)
10551558Srgrimes{
10561558Srgrimes
10571558Srgrimes	if (sp->se_getty) {
10581558Srgrimes		free(sp->se_getty);
10593594Sache		free(sp->se_getty_argv_space);
10601558Srgrimes		free(sp->se_getty_argv);
10611558Srgrimes	}
10621558Srgrimes	sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2);
1063173785Sobrien	sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
10643594Sache	sp->se_getty_argv_space = strdup(sp->se_getty);
10653594Sache	sp->se_getty_argv = construct_argv(sp->se_getty_argv_space);
10661558Srgrimes	if (sp->se_getty_argv == 0) {
10671558Srgrimes		warning("can't parse getty for port %s", sp->se_device);
10681558Srgrimes		free(sp->se_getty);
10693594Sache		free(sp->se_getty_argv_space);
10703594Sache		sp->se_getty = sp->se_getty_argv_space = 0;
10711558Srgrimes		return (0);
10721558Srgrimes	}
10733594Sache	if (sp->se_window) {
107449018Sru		free(sp->se_window);
10753594Sache		free(sp->se_window_argv_space);
10763594Sache		free(sp->se_window_argv);
10773594Sache	}
10783594Sache	sp->se_window = sp->se_window_argv_space = 0;
10793594Sache	sp->se_window_argv = 0;
10801558Srgrimes	if (typ->ty_window) {
10811558Srgrimes		sp->se_window = strdup(typ->ty_window);
10823594Sache		sp->se_window_argv_space = strdup(sp->se_window);
10833594Sache		sp->se_window_argv = construct_argv(sp->se_window_argv_space);
10841558Srgrimes		if (sp->se_window_argv == 0) {
10851558Srgrimes			warning("can't parse window for port %s",
1086173785Sobrien			    sp->se_device);
10873594Sache			free(sp->se_window_argv_space);
10881558Srgrimes			free(sp->se_window);
10893594Sache			sp->se_window = sp->se_window_argv_space = 0;
10901558Srgrimes			return (0);
10911558Srgrimes		}
10921558Srgrimes	}
10933594Sache	if (sp->se_type)
10943594Sache		free(sp->se_type);
10953594Sache	sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0;
10961558Srgrimes	return (1);
10971558Srgrimes}
10981558Srgrimes
10991558Srgrimes/*
11001558Srgrimes * Walk the list of ttys and create sessions for each active line.
11011558Srgrimes */
11021558Srgrimesstate_func_t
110392838Simpread_ttys(void)
11041558Srgrimes{
11051558Srgrimes	int session_index = 0;
110692806Sobrien	session_t *sp, *snext;
110792806Sobrien	struct ttyent *typ;
11081558Srgrimes
11091558Srgrimes	/*
11101558Srgrimes	 * Destroy any previous session state.
11111558Srgrimes	 * There shouldn't be any, but just in case...
11121558Srgrimes	 */
11131558Srgrimes	for (sp = sessions; sp; sp = snext) {
11141558Srgrimes		if (sp->se_process)
11151558Srgrimes			clear_session_logs(sp);
11161558Srgrimes		snext = sp->se_next;
11171558Srgrimes		free_session(sp);
11181558Srgrimes	}
11191558Srgrimes	sessions = 0;
11201558Srgrimes	if (start_session_db())
11211558Srgrimes		return (state_func_t) single_user;
11221558Srgrimes
11231558Srgrimes	/*
11241558Srgrimes	 * Allocate a session entry for each active port.
11251558Srgrimes	 * Note that sp starts at 0.
11261558Srgrimes	 */
112727837Sdavidn	while ((typ = getttyent()) != NULL)
112827837Sdavidn		if ((snext = new_session(sp, ++session_index, typ)) != NULL)
11291558Srgrimes			sp = snext;
11301558Srgrimes
11311558Srgrimes	endttyent();
11321558Srgrimes
11331558Srgrimes	return (state_func_t) multi_user;
11341558Srgrimes}
11351558Srgrimes
11361558Srgrimes/*
11371558Srgrimes * Start a window system running.
11381558Srgrimes */
11391558Srgrimesvoid
114092838Simpstart_window_system(session_t *sp)
11411558Srgrimes{
11421558Srgrimes	pid_t pid;
11431558Srgrimes	sigset_t mask;
11443594Sache	char term[64], *env[2];
1145159402Skib	int status;
11461558Srgrimes
11471558Srgrimes	if ((pid = fork()) == -1) {
11481558Srgrimes		emergency("can't fork for window system on port %s: %m",
1149173785Sobrien		    sp->se_device);
11501558Srgrimes		/* hope that getty fails and we can try again */
11511558Srgrimes		return;
11521558Srgrimes	}
1153173785Sobrien	if (pid) {
1154159402Skib		waitpid(-1, &status, 0);
11551558Srgrimes		return;
1156159402Skib	}
11571558Srgrimes
1158159402Skib	/* reparent window process to the init to not make a zombie on exit */
1159159402Skib	if ((pid = fork()) == -1) {
1160159402Skib		emergency("can't fork for window system on port %s: %m",
1161173785Sobrien		    sp->se_device);
1162159402Skib		_exit(1);
1163159402Skib	}
1164159402Skib	if (pid)
1165159402Skib		_exit(0);
1166159402Skib
11671558Srgrimes	sigemptyset(&mask);
11681558Srgrimes	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
11691558Srgrimes
11701558Srgrimes	if (setsid() < 0)
11711558Srgrimes		emergency("setsid failed (window) %m");
11721558Srgrimes
117321865Sdavidn#ifdef LOGIN_CAP
117421865Sdavidn	setprocresources(RESOURCE_WINDOW);
117521865Sdavidn#endif
11763594Sache	if (sp->se_type) {
11773594Sache		/* Don't use malloc after fork */
11783594Sache		strcpy(term, "TERM=");
117922922Sdg		strncat(term, sp->se_type, sizeof(term) - 6);
11803594Sache		env[0] = term;
11813594Sache		env[1] = 0;
11823594Sache	}
11833594Sache	else
11843594Sache		env[0] = 0;
11853594Sache	execve(sp->se_window_argv[0], sp->se_window_argv, env);
11861558Srgrimes	stall("can't exec window system '%s' for port %s: %m",
11871558Srgrimes		sp->se_window_argv[0], sp->se_device);
11881558Srgrimes	_exit(1);
11891558Srgrimes}
11901558Srgrimes
11911558Srgrimes/*
11921558Srgrimes * Start a login session running.
11931558Srgrimes */
11941558Srgrimespid_t
119592838Simpstart_getty(session_t *sp)
11961558Srgrimes{
11971558Srgrimes	pid_t pid;
11981558Srgrimes	sigset_t mask;
11991558Srgrimes	time_t current_time = time((time_t *) 0);
12009997Sache	int too_quick = 0;
12013594Sache	char term[64], *env[2];
12021558Srgrimes
120310006Smpp	if (current_time >= sp->se_started &&
12049997Sache	    current_time - sp->se_started < GETTY_SPACING) {
12059997Sache		if (++sp->se_nspace > GETTY_NSPACE) {
12069997Sache			sp->se_nspace = 0;
12079997Sache			too_quick = 1;
12089997Sache		}
12099997Sache	} else
12109997Sache		sp->se_nspace = 0;
12119997Sache
12121558Srgrimes	/*
12131558Srgrimes	 * fork(), not vfork() -- we can't afford to block.
12141558Srgrimes	 */
12151558Srgrimes	if ((pid = fork()) == -1) {
12161558Srgrimes		emergency("can't fork for getty on port %s: %m", sp->se_device);
12171558Srgrimes		return -1;
12181558Srgrimes	}
12191558Srgrimes
12201558Srgrimes	if (pid)
12211558Srgrimes		return pid;
12221558Srgrimes
12239997Sache	if (too_quick) {
12249997Sache		warning("getty repeating too quickly on port %s, sleeping %d secs",
1225173785Sobrien		    sp->se_device, GETTY_SLEEP);
12261558Srgrimes		sleep((unsigned) GETTY_SLEEP);
12271558Srgrimes	}
12281558Srgrimes
12291558Srgrimes	if (sp->se_window) {
12301558Srgrimes		start_window_system(sp);
12311558Srgrimes		sleep(WINDOW_WAIT);
12321558Srgrimes	}
12331558Srgrimes
12341558Srgrimes	sigemptyset(&mask);
12351558Srgrimes	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
12361558Srgrimes
123721865Sdavidn#ifdef LOGIN_CAP
123821865Sdavidn	setprocresources(RESOURCE_GETTY);
123921865Sdavidn#endif
12403594Sache	if (sp->se_type) {
12413594Sache		/* Don't use malloc after fork */
12423594Sache		strcpy(term, "TERM=");
124322922Sdg		strncat(term, sp->se_type, sizeof(term) - 6);
12443594Sache		env[0] = term;
12453594Sache		env[1] = 0;
1246173785Sobrien	} else
12473594Sache		env[0] = 0;
12483594Sache	execve(sp->se_getty_argv[0], sp->se_getty_argv, env);
12491558Srgrimes	stall("can't exec getty '%s' for port %s: %m",
12501558Srgrimes		sp->se_getty_argv[0], sp->se_device);
12511558Srgrimes	_exit(1);
12521558Srgrimes}
12531558Srgrimes
12541558Srgrimes/*
12551558Srgrimes * Collect exit status for a child.
12561558Srgrimes * If an exiting login, start a new login running.
12571558Srgrimes */
12581558Srgrimesvoid
12591558Srgrimescollect_child(pid_t pid)
12601558Srgrimes{
126192806Sobrien	session_t *sp, *sprev, *snext;
12621558Srgrimes
12631558Srgrimes	if (! sessions)
12641558Srgrimes		return;
12651558Srgrimes
12661558Srgrimes	if (! (sp = find_session(pid)))
12671558Srgrimes		return;
12681558Srgrimes
12691558Srgrimes	clear_session_logs(sp);
12701558Srgrimes	del_session(sp);
12711558Srgrimes	sp->se_process = 0;
12721558Srgrimes
12731558Srgrimes	if (sp->se_flags & SE_SHUTDOWN) {
127427837Sdavidn		if ((sprev = sp->se_prev) != NULL)
12751558Srgrimes			sprev->se_next = sp->se_next;
12761558Srgrimes		else
12771558Srgrimes			sessions = sp->se_next;
127827837Sdavidn		if ((snext = sp->se_next) != NULL)
12791558Srgrimes			snext->se_prev = sp->se_prev;
12801558Srgrimes		free_session(sp);
12811558Srgrimes		return;
12821558Srgrimes	}
12831558Srgrimes
12841558Srgrimes	if ((pid = start_getty(sp)) == -1) {
12851558Srgrimes		/* serious trouble */
12861558Srgrimes		requested_transition = clean_ttys;
12871558Srgrimes		return;
12881558Srgrimes	}
12891558Srgrimes
12901558Srgrimes	sp->se_process = pid;
12911558Srgrimes	sp->se_started = time((time_t *) 0);
12921558Srgrimes	add_session(sp);
12931558Srgrimes}
12941558Srgrimes
12951558Srgrimes/*
12961558Srgrimes * Catch a signal and request a state transition.
12971558Srgrimes */
12981558Srgrimesvoid
129992838Simptransition_handler(int sig)
13001558Srgrimes{
13011558Srgrimes
13021558Srgrimes	switch (sig) {
13031558Srgrimes	case SIGHUP:
13041558Srgrimes		requested_transition = clean_ttys;
13051558Srgrimes		break;
130647962Sru	case SIGUSR2:
130747962Sru		howto = RB_POWEROFF;
130847962Sru	case SIGUSR1:
130947962Sru		howto |= RB_HALT;
13102323Snate	case SIGINT:
13112327Sjkh		Reboot = TRUE;
13121558Srgrimes	case SIGTERM:
13131558Srgrimes		requested_transition = death;
13141558Srgrimes		break;
13151558Srgrimes	case SIGTSTP:
13161558Srgrimes		requested_transition = catatonia;
13171558Srgrimes		break;
13181558Srgrimes	default:
13191558Srgrimes		requested_transition = 0;
13201558Srgrimes		break;
13211558Srgrimes	}
13221558Srgrimes}
13231558Srgrimes
13241558Srgrimes/*
13251558Srgrimes * Take the system multiuser.
13261558Srgrimes */
13271558Srgrimesstate_func_t
132892838Simpmulti_user(void)
13291558Srgrimes{
13301558Srgrimes	pid_t pid;
133192806Sobrien	session_t *sp;
13321558Srgrimes
13331558Srgrimes	requested_transition = 0;
13341558Srgrimes
13351558Srgrimes	/*
13361558Srgrimes	 * If the administrator has not set the security level to -1
13371558Srgrimes	 * to indicate that the kernel should not run multiuser in secure
13388871Srgrimes	 * mode, and the run script has not set a higher level of security
13391558Srgrimes	 * than level 1, then put the kernel into secure mode.
13401558Srgrimes	 */
13411558Srgrimes	if (getsecuritylevel() == 0)
13421558Srgrimes		setsecuritylevel(1);
13431558Srgrimes
13441558Srgrimes	for (sp = sessions; sp; sp = sp->se_next) {
13451558Srgrimes		if (sp->se_process)
13461558Srgrimes			continue;
13471558Srgrimes		if ((pid = start_getty(sp)) == -1) {
13481558Srgrimes			/* serious trouble */
13491558Srgrimes			requested_transition = clean_ttys;
13501558Srgrimes			break;
13511558Srgrimes		}
13521558Srgrimes		sp->se_process = pid;
13531558Srgrimes		sp->se_started = time((time_t *) 0);
13541558Srgrimes		add_session(sp);
13551558Srgrimes	}
13561558Srgrimes
13571558Srgrimes	while (!requested_transition)
13581558Srgrimes		if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
13591558Srgrimes			collect_child(pid);
13601558Srgrimes
13611558Srgrimes	return (state_func_t) requested_transition;
13621558Srgrimes}
13631558Srgrimes
13641558Srgrimes/*
136557344Salfred * This is an (n*2)+(n^2) algorithm.  We hope it isn't run often...
13661558Srgrimes */
13671558Srgrimesstate_func_t
136892838Simpclean_ttys(void)
13691558Srgrimes{
137092806Sobrien	session_t *sp, *sprev;
137192806Sobrien	struct ttyent *typ;
137292806Sobrien	int session_index = 0;
137392806Sobrien	int devlen;
13743594Sache	char *old_getty, *old_window, *old_type;
13751558Srgrimes
1376173787Sobrien	/*
1377173787Sobrien	 * mark all sessions for death, (!SE_PRESENT)
137857344Salfred	 * as we find or create new ones they'll be marked as keepers,
137957344Salfred	 * we'll later nuke all the ones not found in /etc/ttys
138057344Salfred	 */
138157344Salfred	for (sp = sessions; sp != NULL; sp = sp->se_next)
138257344Salfred		sp->se_flags &= ~SE_PRESENT;
138357344Salfred
13841558Srgrimes	devlen = sizeof(_PATH_DEV) - 1;
138527837Sdavidn	while ((typ = getttyent()) != NULL) {
13861558Srgrimes		++session_index;
13871558Srgrimes
13881558Srgrimes		for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
13891558Srgrimes			if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
13901558Srgrimes				break;
13911558Srgrimes
13921558Srgrimes		if (sp) {
139357344Salfred			/* we want this one to live */
139457344Salfred			sp->se_flags |= SE_PRESENT;
13951558Srgrimes			if (sp->se_index != session_index) {
13961558Srgrimes				warning("port %s changed utmp index from %d to %d",
13971558Srgrimes				       sp->se_device, sp->se_index,
13981558Srgrimes				       session_index);
13991558Srgrimes				sp->se_index = session_index;
14001558Srgrimes			}
14011558Srgrimes			if ((typ->ty_status & TTY_ON) == 0 ||
14021558Srgrimes			    typ->ty_getty == 0) {
14031558Srgrimes				sp->se_flags |= SE_SHUTDOWN;
14041558Srgrimes				kill(sp->se_process, SIGHUP);
14051558Srgrimes				continue;
14061558Srgrimes			}
14071558Srgrimes			sp->se_flags &= ~SE_SHUTDOWN;
14083594Sache			old_getty = sp->se_getty ? strdup(sp->se_getty) : 0;
14093594Sache			old_window = sp->se_window ? strdup(sp->se_window) : 0;
14103594Sache			old_type = sp->se_type ? strdup(sp->se_type) : 0;
14111558Srgrimes			if (setupargv(sp, typ) == 0) {
14121558Srgrimes				warning("can't parse getty for port %s",
14131558Srgrimes					sp->se_device);
14141558Srgrimes				sp->se_flags |= SE_SHUTDOWN;
14151558Srgrimes				kill(sp->se_process, SIGHUP);
14161558Srgrimes			}
14173594Sache			else if (   !old_getty
141827837Sdavidn				 || (!old_type && sp->se_type)
141927837Sdavidn				 || (old_type && !sp->se_type)
142027837Sdavidn				 || (!old_window && sp->se_window)
142127837Sdavidn				 || (old_window && !sp->se_window)
142227837Sdavidn				 || (strcmp(old_getty, sp->se_getty) != 0)
142327837Sdavidn				 || (old_window && strcmp(old_window, sp->se_window) != 0)
142427837Sdavidn				 || (old_type && strcmp(old_type, sp->se_type) != 0)
14253594Sache				) {
14263594Sache				/* Don't set SE_SHUTDOWN here */
14273594Sache				sp->se_nspace = 0;
14283594Sache				sp->se_started = 0;
14293594Sache				kill(sp->se_process, SIGHUP);
14303594Sache			}
14313594Sache			if (old_getty)
14323594Sache				free(old_getty);
143378484Smikeh			if (old_window)
14343594Sache				free(old_window);
14353594Sache			if (old_type)
14363594Sache				free(old_type);
14371558Srgrimes			continue;
14381558Srgrimes		}
14391558Srgrimes
14401558Srgrimes		new_session(sprev, session_index, typ);
14411558Srgrimes	}
14421558Srgrimes
14431558Srgrimes	endttyent();
14441558Srgrimes
144557344Salfred	/*
144657344Salfred	 * sweep through and kill all deleted sessions
144757344Salfred	 * ones who's /etc/ttys line was deleted (SE_PRESENT unset)
144857344Salfred	 */
144957344Salfred	for (sp = sessions; sp != NULL; sp = sp->se_next) {
145057344Salfred		if ((sp->se_flags & SE_PRESENT) == 0) {
145157344Salfred			sp->se_flags |= SE_SHUTDOWN;
145257344Salfred			kill(sp->se_process, SIGHUP);
145357344Salfred		}
145457344Salfred	}
145557344Salfred
14561558Srgrimes	return (state_func_t) multi_user;
14571558Srgrimes}
14581558Srgrimes
14591558Srgrimes/*
14601558Srgrimes * Block further logins.
14611558Srgrimes */
14621558Srgrimesstate_func_t
146392838Simpcatatonia(void)
14641558Srgrimes{
146592806Sobrien	session_t *sp;
14661558Srgrimes
14671558Srgrimes	for (sp = sessions; sp; sp = sp->se_next)
14681558Srgrimes		sp->se_flags |= SE_SHUTDOWN;
14691558Srgrimes
14701558Srgrimes	return (state_func_t) multi_user;
14711558Srgrimes}
14721558Srgrimes
14731558Srgrimes/*
14741558Srgrimes * Note SIGALRM.
14751558Srgrimes */
14761558Srgrimesvoid
147792838Simpalrm_handler(int sig)
14781558Srgrimes{
1479173785Sobrien
148027837Sdavidn	(void)sig;
14811558Srgrimes	clang = 1;
14821558Srgrimes}
14831558Srgrimes
14841558Srgrimes/*
14851558Srgrimes * Bring the system down to single user.
14861558Srgrimes */
14871558Srgrimesstate_func_t
148892838Simpdeath(void)
14891558Srgrimes{
149092806Sobrien	session_t *sp;
149192806Sobrien	int i;
14921558Srgrimes	pid_t pid;
149327197Sache	static const int death_sigs[2] = { SIGTERM, SIGKILL };
14941558Srgrimes
149527275Sache	/* NB: should send a message to the session logger to avoid blocking. */
149627275Sache	logwtmp("~", "shutdown", "");
149727275Sache
149827215Sache	for (sp = sessions; sp; sp = sp->se_next) {
14991558Srgrimes		sp->se_flags |= SE_SHUTDOWN;
150027941Sache		kill(sp->se_process, SIGHUP);
150127215Sache	}
15021558Srgrimes
150327837Sdavidn	/* Try to run the rc.shutdown script within a period of time */
1504173785Sobrien	runshutdown();
1505173785Sobrien
150627197Sache	for (i = 0; i < 2; ++i) {
15071558Srgrimes		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
15081558Srgrimes			return (state_func_t) single_user;
15091558Srgrimes
15101558Srgrimes		clang = 0;
15111558Srgrimes		alarm(DEATH_WATCH);
15121558Srgrimes		do
15131558Srgrimes			if ((pid = waitpid(-1, (int *)0, 0)) != -1)
15141558Srgrimes				collect_child(pid);
15151558Srgrimes		while (clang == 0 && errno != ECHILD);
15161558Srgrimes
15171558Srgrimes		if (errno == ECHILD)
15181558Srgrimes			return (state_func_t) single_user;
15191558Srgrimes	}
15201558Srgrimes
15211558Srgrimes	warning("some processes would not die; ps axl advised");
15221558Srgrimes
15231558Srgrimes	return (state_func_t) single_user;
15241558Srgrimes}
152527837Sdavidn
152627837Sdavidn/*
152727837Sdavidn * Run the system shutdown script.
152827837Sdavidn *
152927837Sdavidn * Exit codes:      XXX I should document more
153027837Sdavidn * -2       shutdown script terminated abnormally
153127837Sdavidn * -1       fatal error - can't run script
153227837Sdavidn * 0        good.
153327837Sdavidn * >0       some error (exit code)
153427837Sdavidn */
153527837Sdavidnint
153692838Simprunshutdown(void)
153727837Sdavidn{
153827837Sdavidn	pid_t pid, wpid;
153927837Sdavidn	int status;
154027837Sdavidn	int shutdowntimeout;
154127837Sdavidn	size_t len;
154253550Sdillon	char *argv[4];
1543166484Simp	const char *shell;
154427837Sdavidn	struct sigaction sa;
154528344Sdavidn	struct stat sb;
154627837Sdavidn
154728344Sdavidn	/*
154828344Sdavidn	 * rc.shutdown is optional, so to prevent any unnecessary
154928344Sdavidn	 * complaints from the shell we simply don't run it if the
155028344Sdavidn	 * file does not exist. If the stat() here fails for other
155128344Sdavidn	 * reasons, we'll let the shell complain.
155228344Sdavidn	 */
155328344Sdavidn	if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT)
155428344Sdavidn		return 0;
155528344Sdavidn
1556166484Simp	shell = get_shell();
1557166484Simp
155827837Sdavidn	if ((pid = fork()) == 0) {
155927837Sdavidn		int	fd;
156027837Sdavidn
156127941Sache		/* Assume that init already grab console as ctty before */
156227941Sache
156327837Sdavidn		sigemptyset(&sa.sa_mask);
156427837Sdavidn		sa.sa_flags = 0;
156527837Sdavidn		sa.sa_handler = SIG_IGN;
1566173785Sobrien		sigaction(SIGTSTP, &sa, (struct sigaction *)0);
1567173785Sobrien		sigaction(SIGHUP, &sa, (struct sigaction *)0);
156827837Sdavidn
156927837Sdavidn		if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1)
1570173785Sobrien			warning("can't open %s: %m", _PATH_CONSOLE);
157127837Sdavidn		else {
1572173785Sobrien			dup2(fd, 0);
1573173785Sobrien			dup2(fd, 1);
1574173785Sobrien			dup2(fd, 2);
1575173785Sobrien			if (fd > 2)
1576173785Sobrien				close(fd);
157727837Sdavidn		}
157827837Sdavidn
157927837Sdavidn		/*
158027837Sdavidn		 * Run the shutdown script.
158127837Sdavidn		 */
1582140070Sdelphij
1583140070Sdelphij		char _sh[]	= "sh";
1584140070Sdelphij		char _reboot[]	= "reboot";
1585140070Sdelphij		char _single[]	= "single";
1586140070Sdelphij		char _path_rundown[] = _PATH_RUNDOWN;
1587140070Sdelphij
1588140070Sdelphij		argv[0] = _sh;
1589140070Sdelphij		argv[1] = _path_rundown;
1590140070Sdelphij		argv[2] = Reboot ? _reboot : _single;
159153550Sdillon		argv[3] = 0;
159227837Sdavidn
159327837Sdavidn		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
159427837Sdavidn
159527941Sache#ifdef LOGIN_CAP
159627941Sache		setprocresources(RESOURCE_RC);
159727941Sache#endif
1598166484Simp		execv(shell, argv);
1599166484Simp		warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN);
160027837Sdavidn		_exit(1);	/* force single user mode */
160127837Sdavidn	}
160227837Sdavidn
160327837Sdavidn	if (pid == -1) {
1604166484Simp		emergency("can't fork for %s on %s: %m", shell, _PATH_RUNDOWN);
160527837Sdavidn		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
160627837Sdavidn			continue;
160727837Sdavidn		sleep(STALL_TIMEOUT);
160827837Sdavidn		return -1;
160927837Sdavidn	}
161027837Sdavidn
161127837Sdavidn	len = sizeof(shutdowntimeout);
1612173785Sobrien	if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len,
1613173785Sobrien	    NULL, 0) == -1 || shutdowntimeout < 2)
1614173785Sobrien		shutdowntimeout = DEATH_SCRIPT;
161527837Sdavidn	alarm(shutdowntimeout);
161627837Sdavidn	clang = 0;
161727837Sdavidn	/*
161827837Sdavidn	 * Copied from single_user().  This is a bit paranoid.
161927837Sdavidn	 * Use the same ALRM handler.
162027837Sdavidn	 */
162127837Sdavidn	do {
162227837Sdavidn		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
162327837Sdavidn			collect_child(wpid);
162427837Sdavidn		if (clang == 1) {
162527837Sdavidn			/* we were waiting for the sub-shell */
162627837Sdavidn			kill(wpid, SIGTERM);
1627166484Simp			warning("timeout expired for %s on %s: %m; going to "
1628166484Simp			    "single user mode", shell, _PATH_RUNDOWN);
162927837Sdavidn			return -1;
163027837Sdavidn		}
163127837Sdavidn		if (wpid == -1) {
163227837Sdavidn			if (errno == EINTR)
163327837Sdavidn				continue;
1634166484Simp			warning("wait for %s on %s failed: %m; going to "
1635166484Simp			    "single user mode", shell, _PATH_RUNDOWN);
163627837Sdavidn			return -1;
163727837Sdavidn		}
163827837Sdavidn		if (wpid == pid && WIFSTOPPED(status)) {
163927837Sdavidn			warning("init: %s on %s stopped, restarting\n",
1640166484Simp				shell, _PATH_RUNDOWN);
164127837Sdavidn			kill(pid, SIGCONT);
164227837Sdavidn			wpid = -1;
164327837Sdavidn		}
164427837Sdavidn	} while (wpid != pid && !clang);
164527837Sdavidn
164627837Sdavidn	/* Turn off the alarm */
164727837Sdavidn	alarm(0);
164827837Sdavidn
164927837Sdavidn	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
165027837Sdavidn	    requested_transition == catatonia) {
165127837Sdavidn		/*
165227837Sdavidn		 * /etc/rc.shutdown executed /sbin/reboot;
165327837Sdavidn		 * wait for the end quietly
165427837Sdavidn		 */
165527837Sdavidn		sigset_t s;
165627837Sdavidn
165727837Sdavidn		sigfillset(&s);
165827837Sdavidn		for (;;)
165927837Sdavidn			sigsuspend(&s);
166027837Sdavidn	}
166127837Sdavidn
166227837Sdavidn	if (!WIFEXITED(status)) {
1663166484Simp		warning("%s on %s terminated abnormally, going to "
1664166484Simp		    "single user mode", shell, _PATH_RUNDOWN);
166527837Sdavidn		return -2;
166627837Sdavidn	}
166727837Sdavidn
166827837Sdavidn	if ((status = WEXITSTATUS(status)) != 0)
166927837Sdavidn		warning("%s returned status %d", _PATH_RUNDOWN, status);
167027837Sdavidn
167127837Sdavidn	return status;
167227837Sdavidn}
167327837Sdavidn
1674140070Sdelphijstatic char *
1675140070Sdelphijstrk(char *p)
16765478Sache{
1677173785Sobrien	static char *t;
1678173785Sobrien	char *q;
1679173785Sobrien	int c;
16805478Sache
1681173785Sobrien	if (p)
1682173785Sobrien		t = p;
1683173785Sobrien	if (!t)
1684173785Sobrien		return 0;
16855478Sache
1686173785Sobrien	c = *t;
1687173785Sobrien	while (c == ' ' || c == '\t' )
1688173785Sobrien		c = *++t;
1689173785Sobrien	if (!c) {
1690173785Sobrien		t = 0;
1691173785Sobrien		return 0;
1692173785Sobrien	}
16935478Sache	q = t;
1694173785Sobrien	if (c == '\'') {
1695173785Sobrien		c = *++t;
1696173785Sobrien		q = t;
1697173785Sobrien		while (c && c != '\'')
1698173785Sobrien			c = *++t;
1699173785Sobrien		if (!c)  /* unterminated string */
1700173785Sobrien			q = t = 0;
1701173785Sobrien		else
1702173785Sobrien			*t++ = 0;
1703173785Sobrien	} else {
1704173785Sobrien		while (c && c != ' ' && c != '\t' )
1705173785Sobrien			c = *++t;
1706173785Sobrien		*t++ = 0;
1707173785Sobrien		if (!c)
1708173785Sobrien			t = 0;
1709173785Sobrien	}
1710173785Sobrien	return q;
17115478Sache}
171221865Sdavidn
171321865Sdavidn#ifdef LOGIN_CAP
171421941Sdavidnvoid
171592838Simpsetprocresources(const char *cname)
171621865Sdavidn{
171721941Sdavidn	login_cap_t *lc;
171827176Sache	if ((lc = login_getclassbyname(cname, NULL)) != NULL) {
1719173785Sobrien		setusercontext(lc, (struct passwd*)NULL, 0,
1720173785Sobrien		    LOGIN_SETPRIORITY | LOGIN_SETRESOURCES);
172121941Sdavidn		login_close(lc);
172221941Sdavidn	}
172321865Sdavidn}
172421865Sdavidn#endif
1725