xref: /openbsd/sbin/init/init.c (revision bdce9d03)
1*bdce9d03Scheloha /*	$OpenBSD: init.c,v 1.72 2022/09/10 00:49:47 cheloha Exp $	*/
24b826ba8Sderaadt /*	$NetBSD: init.c,v 1.22 1996/05/15 23:29:33 jtc Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*-
5df930be7Sderaadt  * Copyright (c) 1991, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
9df930be7Sderaadt  * Donn Seeley at Berkeley Software Design, Inc.
10df930be7Sderaadt  *
11df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt  * modification, are permitted provided that the following conditions
13df930be7Sderaadt  * are met:
14df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
191ef0d710Smillert  * 3. Neither the name of the University nor the names of its contributors
20df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
21df930be7Sderaadt  *    without specific prior written permission.
22df930be7Sderaadt  *
23df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33df930be7Sderaadt  * SUCH DAMAGE.
34df930be7Sderaadt  */
35df930be7Sderaadt 
36b9fc9a72Sderaadt #include <sys/types.h>
37ab091da3Sderaadt #include <sys/reboot.h>
38ae0936ccSguenther #include <sys/sysctl.h>
39ae0936ccSguenther #include <sys/time.h>
40ce842bbcSnicm #include <sys/tree.h>
41ae0936ccSguenther #include <sys/wait.h>
421c794bc1Shalex #include <machine/cpu.h>
43df930be7Sderaadt 
44f290c528Sdcoppa #include <err.h>
45df930be7Sderaadt #include <errno.h>
46df930be7Sderaadt #include <fcntl.h>
47ce842bbcSnicm #include <limits.h>
48bd61d3e8Smillert #include <login_cap.h>
49df930be7Sderaadt #include <signal.h>
50e7beb4a7Smillert #include <stdarg.h>
51df930be7Sderaadt #include <stdio.h>
52df930be7Sderaadt #include <stdlib.h>
53df930be7Sderaadt #include <string.h>
54df930be7Sderaadt #include <syslog.h>
55df930be7Sderaadt #include <time.h>
56df930be7Sderaadt #include <ttyent.h>
57df930be7Sderaadt #include <unistd.h>
584b826ba8Sderaadt #include <util.h>
59df930be7Sderaadt 
60df930be7Sderaadt #ifdef SECURE
61df930be7Sderaadt #include <pwd.h>
62c43387b9Sgsoares #include <readpassphrase.h>
63df930be7Sderaadt #endif
64df930be7Sderaadt 
65df930be7Sderaadt #include "pathnames.h"
66df930be7Sderaadt 
67df930be7Sderaadt /*
68df930be7Sderaadt  * Sleep times; used to prevent thrashing.
69df930be7Sderaadt  */
70df930be7Sderaadt #define	GETTY_SPACING		 5	/* N secs minimum getty spacing */
71df930be7Sderaadt #define	GETTY_SLEEP		30	/* sleep N secs after spacing problem */
72df930be7Sderaadt #define	WINDOW_WAIT		 3	/* wait N secs after starting window */
73df930be7Sderaadt #define	STALL_TIMEOUT		30	/* wait N secs after warning */
74df930be7Sderaadt #define	DEATH_WATCH		10	/* wait N secs for procs to die */
75df930be7Sderaadt 
76fed231abSmillert /*
77fed231abSmillert  * User-based resource limits.
78fed231abSmillert  */
79fed231abSmillert #define RESOURCE_RC		"daemon"
80fed231abSmillert #define RESOURCE_WINDOW		"default"
81fed231abSmillert #define RESOURCE_GETTY		"default"
82fed231abSmillert 
8323d2d19cSmillert #ifndef DEFAULT_STATE
8423d2d19cSmillert #define DEFAULT_STATE		runcom
8523d2d19cSmillert #endif
8623d2d19cSmillert 
87c72b5b24Smillert void handle(sig_t, ...);
88c72b5b24Smillert void delset(sigset_t *, ...);
89df930be7Sderaadt 
90c72b5b24Smillert void stall(char *, ...);
91c72b5b24Smillert void warning(char *, ...);
92c72b5b24Smillert void emergency(char *, ...);
93c72b5b24Smillert void disaster(int);
94df930be7Sderaadt 
955085bfb6Smillert typedef enum {
965085bfb6Smillert 	invalid_state,
975085bfb6Smillert 	single_user,
985085bfb6Smillert 	runcom,
995085bfb6Smillert 	read_ttys,
1005085bfb6Smillert 	multi_user,
1015085bfb6Smillert 	clean_ttys,
1025085bfb6Smillert 	catatonia,
1035085bfb6Smillert 	death,
1045085bfb6Smillert 	do_reboot,
1055085bfb6Smillert 	hard_death,
1065085bfb6Smillert 	nice_death
1075085bfb6Smillert } state_t;
1085085bfb6Smillert typedef state_t (*state_func_t)(void);
109df930be7Sderaadt 
1105085bfb6Smillert state_t f_single_user(void);
1115085bfb6Smillert state_t f_runcom(void);
1125085bfb6Smillert state_t f_read_ttys(void);
1135085bfb6Smillert state_t f_multi_user(void);
1145085bfb6Smillert state_t f_clean_ttys(void);
1155085bfb6Smillert state_t f_catatonia(void);
1165085bfb6Smillert state_t f_death(void);
1175085bfb6Smillert state_t f_do_reboot(void);
1185085bfb6Smillert state_t f_hard_death(void);
1195085bfb6Smillert state_t f_nice_death(void);
1205085bfb6Smillert 
1215085bfb6Smillert state_func_t state_funcs[] = {
1225085bfb6Smillert 	NULL,
1235085bfb6Smillert 	f_single_user,
1245085bfb6Smillert 	f_runcom,
1255085bfb6Smillert 	f_read_ttys,
1265085bfb6Smillert 	f_multi_user,
1275085bfb6Smillert 	f_clean_ttys,
1285085bfb6Smillert 	f_catatonia,
1295085bfb6Smillert 	f_death,
1305085bfb6Smillert 	f_do_reboot,
1315085bfb6Smillert 	f_hard_death,
1325085bfb6Smillert 	f_nice_death
1335085bfb6Smillert };
134df930be7Sderaadt 
135df930be7Sderaadt enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
136df930be7Sderaadt 
137c72b5b24Smillert void transition(state_t);
1385085bfb6Smillert volatile sig_atomic_t requested_transition = DEFAULT_STATE;
139df930be7Sderaadt 
140c72b5b24Smillert void setctty(char *);
141df930be7Sderaadt 
142df930be7Sderaadt typedef struct init_session {
143df930be7Sderaadt 	int	se_index;		/* index of entry in ttys file */
144df930be7Sderaadt 	pid_t	se_process;		/* controlling process */
145eb10f49cScheloha 	struct	timespec se_started;	/* used to avoid thrashing */
146df930be7Sderaadt 	int	se_flags;		/* status of session */
147df930be7Sderaadt #define	SE_SHUTDOWN	0x1		/* session won't be restarted */
148df930be7Sderaadt #define	SE_PRESENT	0x2		/* session is in /etc/ttys */
1498bf92784Sderaadt #define	SE_DEVEXISTS	0x4		/* open does not result in ENODEV */
150df930be7Sderaadt 	char	*se_device;		/* filename of port */
151df930be7Sderaadt 	char	*se_getty;		/* what to run on that port */
152df930be7Sderaadt 	char	**se_getty_argv;	/* pre-parsed argument array */
153df930be7Sderaadt 	char	*se_window;		/* window system (started only once) */
154df930be7Sderaadt 	char	**se_window_argv;	/* pre-parsed argument array */
155df930be7Sderaadt 	struct	init_session *se_prev;
156df930be7Sderaadt 	struct	init_session *se_next;
157ce842bbcSnicm 	RB_ENTRY(init_session) se_entry;
158df930be7Sderaadt } session_t;
159df930be7Sderaadt 
160ce842bbcSnicm static int cmp_sessions(session_t *, session_t *);
161ce842bbcSnicm RB_HEAD(session_tree, init_session) session_tree = RB_INITIALIZER(session_tree);
162ce842bbcSnicm RB_PROTOTYPE(session_tree, init_session, se_entry, cmp_sessions);
163ce842bbcSnicm RB_GENERATE(session_tree, init_session, se_entry, cmp_sessions);
164ce842bbcSnicm 
165c72b5b24Smillert void free_session(session_t *);
166c72b5b24Smillert session_t *new_session(session_t *, int, struct ttyent *);
167df930be7Sderaadt session_t *sessions;
168df930be7Sderaadt 
169c72b5b24Smillert char **construct_argv(char *);
170c72b5b24Smillert void start_window_system(session_t *);
171c72b5b24Smillert void collect_child(pid_t);
172c72b5b24Smillert pid_t start_getty(session_t *);
173c72b5b24Smillert void transition_handler(int);
174c72b5b24Smillert void alrm_handler(int);
175c72b5b24Smillert void setsecuritylevel(int);
176bd61d3e8Smillert void setprocresources(char *);
177c72b5b24Smillert int getsecuritylevel(void);
178c72b5b24Smillert int setupargv(session_t *, struct ttyent *);
179*bdce9d03Scheloha 
180*bdce9d03Scheloha volatile sig_atomic_t clang;
181df930be7Sderaadt 
182c72b5b24Smillert void clear_session_logs(session_t *);
183df930be7Sderaadt 
184c72b5b24Smillert void add_session(session_t *);
185c72b5b24Smillert void del_session(session_t *);
186c72b5b24Smillert session_t *find_session(pid_t);
187df930be7Sderaadt 
188df930be7Sderaadt /*
189df930be7Sderaadt  * The mother of all processes.
190df930be7Sderaadt  */
191df930be7Sderaadt int
main(int argc,char * argv[])192bc52e260Sderaadt main(int argc, char *argv[])
193df930be7Sderaadt {
194178d191eSbluhm 	int c, fd;
195df930be7Sderaadt 	struct sigaction sa;
196df930be7Sderaadt 	sigset_t mask;
197df930be7Sderaadt 
198df930be7Sderaadt 	/* Dispose of random users. */
199b1fde1a1Scheloha 	if (getuid() != 0)
200b1fde1a1Scheloha 		errc(1, EPERM, NULL);
201df930be7Sderaadt 
202df930be7Sderaadt 	/* System V users like to reexec init. */
203b1fde1a1Scheloha 	if (getpid() != 1)
204b1fde1a1Scheloha 		errx(1, "already running");
205df930be7Sderaadt 
206df930be7Sderaadt 	/*
207178d191eSbluhm 	 * Paranoia.
208178d191eSbluhm 	 */
209b7041c07Sderaadt 	if ((fd = open(_PATH_DEVNULL, O_RDWR)) != -1) {
210178d191eSbluhm 		(void)dup2(fd, STDIN_FILENO);
211178d191eSbluhm 		(void)dup2(fd, STDOUT_FILENO);
212178d191eSbluhm 		(void)dup2(fd, STDERR_FILENO);
213178d191eSbluhm 		if (fd > 2)
214178d191eSbluhm 			(void)close(fd);
215178d191eSbluhm 	}
216178d191eSbluhm 
217178d191eSbluhm 	/*
218df930be7Sderaadt 	 * Note that this does NOT open a file...
219df930be7Sderaadt 	 * Does 'init' deserve its own facility number?
220df930be7Sderaadt 	 */
221df930be7Sderaadt 	openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
222df930be7Sderaadt 
223df930be7Sderaadt 	/*
224df930be7Sderaadt 	 * Create an initial session.
225df930be7Sderaadt 	 */
226df69c215Sderaadt 	if (setsid() == -1)
227df930be7Sderaadt 		warning("initial setsid() failed: %m");
228df930be7Sderaadt 
229df930be7Sderaadt 	/*
230df930be7Sderaadt 	 * Establish an initial user so that programs running
231df930be7Sderaadt 	 * single user do not freak out and die (like passwd).
232df930be7Sderaadt 	 */
233df69c215Sderaadt 	if (setlogin("root") == -1)
234df930be7Sderaadt 		warning("setlogin() failed: %m");
235df930be7Sderaadt 
236df930be7Sderaadt 	/*
237df930be7Sderaadt 	 * This code assumes that we always get arguments through flags,
238df930be7Sderaadt 	 * never through bits set in some random machine register.
239df930be7Sderaadt 	 */
240df930be7Sderaadt 	while ((c = getopt(argc, argv, "sf")) != -1)
241df930be7Sderaadt 		switch (c) {
242df930be7Sderaadt 		case 's':
243df930be7Sderaadt 			requested_transition = single_user;
244df930be7Sderaadt 			break;
245df930be7Sderaadt 		case 'f':
246df930be7Sderaadt 			runcom_mode = FASTBOOT;
247df930be7Sderaadt 			break;
248df930be7Sderaadt 		default:
249df930be7Sderaadt 			warning("unrecognized flag '-%c'", c);
250df930be7Sderaadt 			break;
251df930be7Sderaadt 		}
252df930be7Sderaadt 
253df930be7Sderaadt 	if (optind != argc)
254df930be7Sderaadt 		warning("ignoring excess arguments");
255df930be7Sderaadt 
256df930be7Sderaadt 	/*
257df930be7Sderaadt 	 * We catch or block signals rather than ignore them,
258df930be7Sderaadt 	 * so that they get reset on exec.
259df930be7Sderaadt 	 */
260df930be7Sderaadt 	handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
2619a190421Scheloha 	    SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, 0);
262bff23b0cSdlg 	handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP,
263dee114d4Sdlg             SIGUSR1, SIGUSR2, 0);
264df930be7Sderaadt 	handle(alrm_handler, SIGALRM, 0);
265df930be7Sderaadt 	sigfillset(&mask);
266df930be7Sderaadt 	delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
267bff23b0cSdlg 	    SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2,
268bdbecee4Sderaadt 	    SIGTSTP, SIGALRM, 0);
2697674422fSkstailey 	sigprocmask(SIG_SETMASK, &mask, NULL);
27064fbf2d7Sderaadt 	memset(&sa, 0, sizeof sa);
271df930be7Sderaadt 	sigemptyset(&sa.sa_mask);
272df930be7Sderaadt 	sa.sa_flags = 0;
273df930be7Sderaadt 	sa.sa_handler = SIG_IGN;
2747674422fSkstailey 	(void) sigaction(SIGTTIN, &sa, NULL);
2757674422fSkstailey 	(void) sigaction(SIGTTOU, &sa, NULL);
276df930be7Sderaadt 
277df930be7Sderaadt 	/*
278df930be7Sderaadt 	 * Start the state machine.
279df930be7Sderaadt 	 */
280df930be7Sderaadt 	transition(requested_transition);
281df930be7Sderaadt 
282df930be7Sderaadt 	/*
283df930be7Sderaadt 	 * Should never reach here.
284df930be7Sderaadt 	 */
285824827eeSmillert 	exit(1);
286df930be7Sderaadt }
287df930be7Sderaadt 
288df930be7Sderaadt /*
289df930be7Sderaadt  * Associate a function with a signal handler.
290df930be7Sderaadt  */
291df930be7Sderaadt void
handle(sig_t handler,...)292df930be7Sderaadt handle(sig_t handler, ...)
293df930be7Sderaadt {
294df930be7Sderaadt 	int sig;
295df930be7Sderaadt 	struct sigaction sa;
2961db781b7Sderaadt 	sigset_t mask_everything;
297df930be7Sderaadt 	va_list ap;
298df930be7Sderaadt 
299df930be7Sderaadt 	va_start(ap, handler);
300df930be7Sderaadt 
30164fbf2d7Sderaadt 	memset(&sa, 0, sizeof sa);
302df930be7Sderaadt 	sa.sa_handler = handler;
303df930be7Sderaadt 	sigfillset(&mask_everything);
304df930be7Sderaadt 
3054158ce8dSderaadt 	while ((sig = va_arg(ap, int))) {
306df930be7Sderaadt 		sa.sa_mask = mask_everything;
307df930be7Sderaadt 		/* XXX SA_RESTART? */
308df930be7Sderaadt 		sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
3097674422fSkstailey 		sigaction(sig, &sa, NULL);
310df930be7Sderaadt 	}
311df930be7Sderaadt 	va_end(ap);
312df930be7Sderaadt }
313df930be7Sderaadt 
314df930be7Sderaadt /*
315df930be7Sderaadt  * Delete a set of signals from a mask.
316df930be7Sderaadt  */
317df930be7Sderaadt void
delset(sigset_t * maskp,...)318df930be7Sderaadt delset(sigset_t *maskp, ...)
319df930be7Sderaadt {
320df930be7Sderaadt 	int sig;
321df930be7Sderaadt 	va_list ap;
322df930be7Sderaadt 
323df930be7Sderaadt 	va_start(ap, maskp);
3244158ce8dSderaadt 	while ((sig = va_arg(ap, int)))
325df930be7Sderaadt 		sigdelset(maskp, sig);
326df930be7Sderaadt 	va_end(ap);
327df930be7Sderaadt }
328df930be7Sderaadt 
329df930be7Sderaadt /*
330df930be7Sderaadt  * Log a message and sleep for a while (to give someone an opportunity
331df930be7Sderaadt  * to read it and to save log or hardcopy output if the problem is chronic).
332df930be7Sderaadt  * NB: should send a message to the session logger to avoid blocking.
333df930be7Sderaadt  */
334df930be7Sderaadt void
stall(char * message,...)335df930be7Sderaadt stall(char *message, ...)
336df930be7Sderaadt {
337df930be7Sderaadt 	va_list ap;
338df930be7Sderaadt 
339df930be7Sderaadt 	va_start(ap, message);
340df930be7Sderaadt 	vsyslog(LOG_ALERT, message, ap);
341df930be7Sderaadt 	va_end(ap);
3422898efc9Sderaadt 	closelog();
343df930be7Sderaadt 	sleep(STALL_TIMEOUT);
344df930be7Sderaadt }
345df930be7Sderaadt 
346df930be7Sderaadt /*
347df930be7Sderaadt  * Like stall(), but doesn't sleep.
348df930be7Sderaadt  * If cpp had variadic macros, the two functions could be #defines for another.
349df930be7Sderaadt  * NB: should send a message to the session logger to avoid blocking.
350df930be7Sderaadt  */
351df930be7Sderaadt void
warning(char * message,...)352df930be7Sderaadt warning(char *message, ...)
353df930be7Sderaadt {
354df930be7Sderaadt 	va_list ap;
355df930be7Sderaadt 
356df930be7Sderaadt 	va_start(ap, message);
357df930be7Sderaadt 	vsyslog(LOG_ALERT, message, ap);
358df930be7Sderaadt 	va_end(ap);
3592898efc9Sderaadt 	closelog();
360df930be7Sderaadt }
361df930be7Sderaadt 
362df930be7Sderaadt /*
363df930be7Sderaadt  * Log an emergency message.
364df930be7Sderaadt  * NB: should send a message to the session logger to avoid blocking.
365df930be7Sderaadt  */
366df930be7Sderaadt void
emergency(char * message,...)367df930be7Sderaadt emergency(char *message, ...)
368df930be7Sderaadt {
369bc52e260Sderaadt 	struct syslog_data sdata = SYSLOG_DATA_INIT;
370df930be7Sderaadt 	va_list ap;
371df930be7Sderaadt 
372df930be7Sderaadt 	va_start(ap, message);
373bc52e260Sderaadt 	vsyslog_r(LOG_EMERG, &sdata, message, ap);
374df930be7Sderaadt 	va_end(ap);
375df930be7Sderaadt }
376df930be7Sderaadt 
377df930be7Sderaadt /*
378df930be7Sderaadt  * Catch an unexpected signal.
379df930be7Sderaadt  */
380df930be7Sderaadt void
disaster(int sig)381bc52e260Sderaadt disaster(int sig)
382df930be7Sderaadt {
383df930be7Sderaadt 	emergency("fatal signal: %s", strsignal(sig));
384df930be7Sderaadt 
385df930be7Sderaadt 	sleep(STALL_TIMEOUT);
386df930be7Sderaadt 	_exit(sig);		/* reboot */
387df930be7Sderaadt }
388df930be7Sderaadt 
389df930be7Sderaadt /*
390df930be7Sderaadt  * Get the security level of the kernel.
391df930be7Sderaadt  */
392df930be7Sderaadt int
getsecuritylevel(void)393bc52e260Sderaadt getsecuritylevel(void)
394df930be7Sderaadt {
395df930be7Sderaadt #ifdef KERN_SECURELVL
396df930be7Sderaadt 	int name[2], curlevel;
397df930be7Sderaadt 	size_t len;
398df930be7Sderaadt 
399df930be7Sderaadt 	name[0] = CTL_KERN;
400df930be7Sderaadt 	name[1] = KERN_SECURELVL;
401df930be7Sderaadt 	len = sizeof curlevel;
402df930be7Sderaadt 	if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
403df930be7Sderaadt 		emergency("cannot get kernel security level: %s",
404df930be7Sderaadt 		    strerror(errno));
405df930be7Sderaadt 		return (-1);
406df930be7Sderaadt 	}
407df930be7Sderaadt 	return (curlevel);
408df930be7Sderaadt #else
409df930be7Sderaadt 	return (-1);
410df930be7Sderaadt #endif
411df930be7Sderaadt }
412df930be7Sderaadt 
413df930be7Sderaadt /*
414df930be7Sderaadt  * Set the security level of the kernel.
415df930be7Sderaadt  */
416df930be7Sderaadt void
setsecuritylevel(int newlevel)417bc52e260Sderaadt setsecuritylevel(int newlevel)
418df930be7Sderaadt {
419df930be7Sderaadt #ifdef KERN_SECURELVL
420df930be7Sderaadt 	int name[2], curlevel;
421df930be7Sderaadt 
422df930be7Sderaadt 	curlevel = getsecuritylevel();
423df930be7Sderaadt 	if (newlevel == curlevel)
424df930be7Sderaadt 		return;
425df930be7Sderaadt 	name[0] = CTL_KERN;
426df930be7Sderaadt 	name[1] = KERN_SECURELVL;
427df930be7Sderaadt 	if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
428df930be7Sderaadt 		emergency(
429df930be7Sderaadt 		    "cannot change kernel security level from %d to %d: %s",
430df930be7Sderaadt 		    curlevel, newlevel, strerror(errno));
431df930be7Sderaadt 		return;
432df930be7Sderaadt 	}
433df930be7Sderaadt #ifdef SECURE
434df930be7Sderaadt 	warning("kernel security level changed from %d to %d",
435df930be7Sderaadt 	    curlevel, newlevel);
436df930be7Sderaadt #endif
437df930be7Sderaadt #endif
438df930be7Sderaadt }
439df930be7Sderaadt 
440df930be7Sderaadt /*
441df930be7Sderaadt  * Change states in the finite state machine.
442df930be7Sderaadt  * The initial state is passed as an argument.
443df930be7Sderaadt  */
444df930be7Sderaadt void
transition(state_t s)445bc52e260Sderaadt transition(state_t s)
446df930be7Sderaadt {
447df930be7Sderaadt 	for (;;)
4485085bfb6Smillert 		s = (*state_funcs[s])();
449df930be7Sderaadt }
450df930be7Sderaadt 
451df930be7Sderaadt /*
452df930be7Sderaadt  * Close out the accounting files for a login session.
453df930be7Sderaadt  * NB: should send a message to the session logger to avoid blocking.
454df930be7Sderaadt  */
455df930be7Sderaadt void
clear_session_logs(session_t * sp)456bc52e260Sderaadt clear_session_logs(session_t *sp)
457df930be7Sderaadt {
458df930be7Sderaadt 	char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
459df930be7Sderaadt 
460df930be7Sderaadt 	if (logout(line))
461df930be7Sderaadt 		logwtmp(line, "", "");
462df930be7Sderaadt }
463df930be7Sderaadt 
464df930be7Sderaadt /*
465df930be7Sderaadt  * Start a session and allocate a controlling terminal.
466df930be7Sderaadt  * Only called by children of init after forking.
467df930be7Sderaadt  */
468df930be7Sderaadt void
setctty(char * name)469bc52e260Sderaadt setctty(char *name)
470df930be7Sderaadt {
471df930be7Sderaadt 	int fd;
472df930be7Sderaadt 
473df930be7Sderaadt 	(void) revoke(name);
474df930be7Sderaadt 	sleep(2);			/* leave DTR low */
475df930be7Sderaadt 	if ((fd = open(name, O_RDWR)) == -1) {
476df930be7Sderaadt 		stall("can't open %s: %m", name);
477df930be7Sderaadt 		_exit(1);
478df930be7Sderaadt 	}
479df930be7Sderaadt 	if (login_tty(fd) == -1) {
480df930be7Sderaadt 		stall("can't get %s for controlling terminal: %m", name);
481df930be7Sderaadt 		_exit(1);
482df930be7Sderaadt 	}
483df930be7Sderaadt }
484df930be7Sderaadt 
485df930be7Sderaadt /*
486df930be7Sderaadt  * Bring the system up single user.
487df930be7Sderaadt  */
4885085bfb6Smillert state_t
f_single_user(void)4895085bfb6Smillert f_single_user(void)
490df930be7Sderaadt {
491df930be7Sderaadt 	pid_t pid, wpid;
492df930be7Sderaadt 	int status;
493df930be7Sderaadt 	sigset_t mask;
494b9fc9a72Sderaadt 	char shell[PATH_MAX];		/* Allocate space here */
495b9fc9a72Sderaadt 	char name[PATH_MAX];		/* Name (argv[0]) of shell */
496df930be7Sderaadt 	char *argv[2];
497df930be7Sderaadt #ifdef SECURE
498df930be7Sderaadt 	struct ttyent *typ;
499df930be7Sderaadt 	struct passwd *pp;
500df930be7Sderaadt 	static const char banner[] =
501df930be7Sderaadt 		"Enter root password, or ^D to go multi-user\n";
5029044f8b1Sjca 	char *clear;
503c43387b9Sgsoares 	char pbuf[1024];
504df930be7Sderaadt #endif
505df930be7Sderaadt 
5062ebac4d9Sweingart 	/* Init shell and name */
507dcab0d16Sderaadt 	strlcpy(shell, _PATH_BSHELL, sizeof shell);
508dcab0d16Sderaadt 	strlcpy(name, "-sh", sizeof name);
5092ebac4d9Sweingart 
510df930be7Sderaadt 	/*
511df930be7Sderaadt 	 * If the kernel is in secure mode, downgrade it to insecure mode.
512df930be7Sderaadt 	 */
513df930be7Sderaadt 	if (getsecuritylevel() > 0)
514df930be7Sderaadt 		setsecuritylevel(0);
515df930be7Sderaadt 
516df930be7Sderaadt 	if ((pid = fork()) == 0) {
517df930be7Sderaadt 		/*
518df930be7Sderaadt 		 * Start the single user session.
519df930be7Sderaadt 		 */
520df930be7Sderaadt 		setctty(_PATH_CONSOLE);
521df930be7Sderaadt 
522df930be7Sderaadt #ifdef SECURE
523df930be7Sderaadt 		/*
524df930be7Sderaadt 		 * Check the root password.
525df930be7Sderaadt 		 * We don't care if the console is 'on' by default;
526df930be7Sderaadt 		 * it's the only tty that can be 'off' and 'secure'.
527df930be7Sderaadt 		 */
528df930be7Sderaadt 		typ = getttynam("console");
529fc334d60Stedu 		pp = getpwnam_shadow("root");
530c5ee85c1Sderaadt 		if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp &&
531c5ee85c1Sderaadt 		    *pp->pw_passwd) {
5325392146dSmillert 			write(STDERR_FILENO, banner, sizeof banner - 1);
533df930be7Sderaadt 			for (;;) {
534fc334d60Stedu 				int ok = 0;
5353a7efd93Smestre 				clear = readpassphrase("Password:", pbuf,
5363a7efd93Smestre 				    sizeof(pbuf), RPP_ECHO_OFF);
537fc334d60Stedu 				if (clear == NULL || *clear == '\0')
538df930be7Sderaadt 					_exit(0);
539fc334d60Stedu 				if (crypt_checkpass(clear, pp->pw_passwd) == 0)
540fc334d60Stedu 					ok = 1;
5413a7efd93Smestre 				explicit_bzero(pbuf, sizeof(pbuf));
542fc334d60Stedu 				if (ok)
543df930be7Sderaadt 					break;
544df930be7Sderaadt 				warning("single-user login failed\n");
545df930be7Sderaadt 			}
546df930be7Sderaadt 		}
547df930be7Sderaadt 		endttyent();
548df930be7Sderaadt 		endpwent();
549df930be7Sderaadt #endif /* SECURE */
550df930be7Sderaadt 
551df930be7Sderaadt #ifdef DEBUGSHELL
552df930be7Sderaadt 		{
553df930be7Sderaadt 			char altshell[128], *cp = altshell;
554df930be7Sderaadt 			int num;
555df930be7Sderaadt 
556df930be7Sderaadt #define	SHREQUEST \
557df930be7Sderaadt 	"Enter pathname of shell or RETURN for sh: "
558dcab0d16Sderaadt 
559df930be7Sderaadt 			(void)write(STDERR_FILENO,
560df930be7Sderaadt 			    SHREQUEST, sizeof(SHREQUEST) - 1);
561df930be7Sderaadt 			while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
562df930be7Sderaadt 			    num != 0 && *cp != '\n' && cp < &altshell[127])
563df930be7Sderaadt 				cp++;
564df930be7Sderaadt 			*cp = '\0';
5652ebac4d9Sweingart 
5662ebac4d9Sweingart 			/* Copy in alternate shell */
5672ebac4d9Sweingart 			if (altshell[0] != '\0'){
5682ebac4d9Sweingart 				char *p;
5692ebac4d9Sweingart 
5702ebac4d9Sweingart 				/* Binary to exec */
571dcab0d16Sderaadt 				strlcpy(shell, altshell, sizeof shell);
5722ebac4d9Sweingart 
5732ebac4d9Sweingart 				/* argv[0] */
5742ebac4d9Sweingart 				p = strrchr(altshell, '/');
5752ebac4d9Sweingart 				if(p == NULL) p = altshell;
5762ebac4d9Sweingart 				else p++;
5772ebac4d9Sweingart 
5782ebac4d9Sweingart 				name[0] = '-';
579dcab0d16Sderaadt 				strlcpy(&name[1], p, sizeof name -1);
5802ebac4d9Sweingart 			}
581df930be7Sderaadt 		}
582df930be7Sderaadt #endif /* DEBUGSHELL */
583df930be7Sderaadt 
584df930be7Sderaadt 		/*
585df930be7Sderaadt 		 * Unblock signals.
586df930be7Sderaadt 		 * We catch all the interesting ones,
587df930be7Sderaadt 		 * and those are reset to SIG_DFL on exec.
588df930be7Sderaadt 		 */
589df930be7Sderaadt 		sigemptyset(&mask);
5907674422fSkstailey 		sigprocmask(SIG_SETMASK, &mask, NULL);
591df930be7Sderaadt 
592df930be7Sderaadt 		/*
593df930be7Sderaadt 		 * Fire off a shell.
594df930be7Sderaadt 		 * If the default one doesn't work, try the Bourne shell.
595df930be7Sderaadt 		 */
5962ebac4d9Sweingart 		argv[0] = name;
5972ebac4d9Sweingart 		argv[1] = NULL;
598df930be7Sderaadt 		setenv("PATH", _PATH_STDPATH, 1);
599df930be7Sderaadt 		execv(shell, argv);
600df930be7Sderaadt 		emergency("can't exec %s for single user: %m", shell);
6012ebac4d9Sweingart 
6022ebac4d9Sweingart 		argv[0] = "-sh";
6032ebac4d9Sweingart 		argv[1] = NULL;
604df930be7Sderaadt 		execv(_PATH_BSHELL, argv);
605df930be7Sderaadt 		emergency("can't exec %s for single user: %m", _PATH_BSHELL);
606df930be7Sderaadt 		sleep(STALL_TIMEOUT);
607df930be7Sderaadt 		_exit(1);
608df930be7Sderaadt 	}
609df930be7Sderaadt 
610df930be7Sderaadt 	if (pid == -1) {
611df930be7Sderaadt 		/*
612df930be7Sderaadt 		 * We are seriously hosed.  Do our best.
613df930be7Sderaadt 		 */
614df930be7Sderaadt 		emergency("can't fork single-user shell, trying again");
6157674422fSkstailey 		while (waitpid(-1, NULL, WNOHANG) > 0)
616df930be7Sderaadt 			continue;
6175085bfb6Smillert 		return single_user;
618df930be7Sderaadt 	}
619df930be7Sderaadt 
620df930be7Sderaadt 	requested_transition = 0;
621df930be7Sderaadt 	do {
622df930be7Sderaadt 		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
623df930be7Sderaadt 			collect_child(wpid);
624df930be7Sderaadt 		if (wpid == -1) {
625df930be7Sderaadt 			if (errno == EINTR)
626df930be7Sderaadt 				continue;
627df930be7Sderaadt 			warning("wait for single-user shell failed: %m; restarting");
6285085bfb6Smillert 			return single_user;
629df930be7Sderaadt 		}
630df930be7Sderaadt 		if (wpid == pid && WIFSTOPPED(status)) {
631df930be7Sderaadt 			warning("init: shell stopped, restarting\n");
632df930be7Sderaadt 			kill(pid, SIGCONT);
633df930be7Sderaadt 			wpid = -1;
634df930be7Sderaadt 		}
635df930be7Sderaadt 	} while (wpid != pid && !requested_transition);
636df930be7Sderaadt 
637df930be7Sderaadt 	if (requested_transition)
6385085bfb6Smillert 		return requested_transition;
639df930be7Sderaadt 
640df930be7Sderaadt 	if (!WIFEXITED(status)) {
641df930be7Sderaadt 		if (WTERMSIG(status) == SIGKILL) {
642df930be7Sderaadt 			/*
643df930be7Sderaadt 			 *  reboot(8) killed shell?
644df930be7Sderaadt 			 */
645df930be7Sderaadt 			warning("single user shell terminated.");
646df930be7Sderaadt 			sleep(STALL_TIMEOUT);
647df930be7Sderaadt 			_exit(0);
648df930be7Sderaadt 		} else {
649df930be7Sderaadt 			warning("single user shell terminated, restarting");
6505085bfb6Smillert 			return single_user;
651df930be7Sderaadt 		}
652df930be7Sderaadt 	}
653df930be7Sderaadt 
654df930be7Sderaadt 	runcom_mode = FASTBOOT;
6555085bfb6Smillert 	return runcom;
656df930be7Sderaadt }
657df930be7Sderaadt 
658df930be7Sderaadt /*
659df930be7Sderaadt  * Run the system startup script.
660df930be7Sderaadt  */
6615085bfb6Smillert state_t
f_runcom(void)6625085bfb6Smillert f_runcom(void)
663df930be7Sderaadt {
664df930be7Sderaadt 	pid_t pid, wpid;
665df930be7Sderaadt 	int status;
666df930be7Sderaadt 	char *argv[4];
667df930be7Sderaadt 	struct sigaction sa;
668df930be7Sderaadt 
669df930be7Sderaadt 	if ((pid = fork()) == 0) {
67064fbf2d7Sderaadt 		memset(&sa, 0, sizeof sa);
671df930be7Sderaadt 		sigemptyset(&sa.sa_mask);
672df930be7Sderaadt 		sa.sa_flags = 0;
673df930be7Sderaadt 		sa.sa_handler = SIG_IGN;
6747674422fSkstailey 		(void) sigaction(SIGTSTP, &sa, NULL);
6757674422fSkstailey 		(void) sigaction(SIGHUP, &sa, NULL);
676df930be7Sderaadt 
677df930be7Sderaadt 		setctty(_PATH_CONSOLE);
678df930be7Sderaadt 
679df930be7Sderaadt 		argv[0] = "sh";
680df930be7Sderaadt 		argv[1] = _PATH_RUNCOM;
681f52a7f8cSmmcc 		argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : NULL;
682f52a7f8cSmmcc 		argv[3] = NULL;
683df930be7Sderaadt 
6847674422fSkstailey 		sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL);
685df930be7Sderaadt 
686fed231abSmillert 		setprocresources(RESOURCE_RC);
687fed231abSmillert 
688df930be7Sderaadt 		execv(_PATH_BSHELL, argv);
689df930be7Sderaadt 		stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
690df930be7Sderaadt 		_exit(1);	/* force single user mode */
691df930be7Sderaadt 	}
692df930be7Sderaadt 
693df930be7Sderaadt 	if (pid == -1) {
694df930be7Sderaadt 		emergency("can't fork for %s on %s: %m",
695df930be7Sderaadt 			_PATH_BSHELL, _PATH_RUNCOM);
6967674422fSkstailey 		while (waitpid(-1, NULL, WNOHANG) > 0)
697df930be7Sderaadt 			continue;
698df930be7Sderaadt 		sleep(STALL_TIMEOUT);
6995085bfb6Smillert 		return single_user;
700df930be7Sderaadt 	}
701df930be7Sderaadt 
702df930be7Sderaadt 	/*
703df930be7Sderaadt 	 * Copied from single_user().  This is a bit paranoid.
704df930be7Sderaadt 	 */
705df930be7Sderaadt 	do {
706df930be7Sderaadt 		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
707df930be7Sderaadt 			collect_child(wpid);
708df930be7Sderaadt 		if (wpid == -1) {
709df930be7Sderaadt 			if (errno == EINTR)
710df930be7Sderaadt 				continue;
711df930be7Sderaadt 			warning("wait for %s on %s failed: %m; going to single user mode",
712df930be7Sderaadt 			    _PATH_BSHELL, _PATH_RUNCOM);
7135085bfb6Smillert 			return single_user;
714df930be7Sderaadt 		}
715df930be7Sderaadt 		if (wpid == pid && WIFSTOPPED(status)) {
716df930be7Sderaadt 			warning("init: %s on %s stopped, restarting\n",
717df930be7Sderaadt 			    _PATH_BSHELL, _PATH_RUNCOM);
718df930be7Sderaadt 			kill(pid, SIGCONT);
719df930be7Sderaadt 			wpid = -1;
720df930be7Sderaadt 		}
721df930be7Sderaadt 	} while (wpid != pid);
722df930be7Sderaadt 
723df930be7Sderaadt 	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
724df930be7Sderaadt 	    requested_transition == catatonia) {
725df930be7Sderaadt 		/* /etc/rc executed /sbin/reboot; wait for the end quietly */
726df930be7Sderaadt 		sigset_t s;
727df930be7Sderaadt 
728df930be7Sderaadt 		sigfillset(&s);
729df930be7Sderaadt 		for (;;)
730df930be7Sderaadt 			sigsuspend(&s);
731df930be7Sderaadt 	}
732df930be7Sderaadt 
733df930be7Sderaadt 	if (!WIFEXITED(status)) {
734df930be7Sderaadt 		warning("%s on %s terminated abnormally, going to single user mode",
735df930be7Sderaadt 		    _PATH_BSHELL, _PATH_RUNCOM);
7365085bfb6Smillert 		return single_user;
737df930be7Sderaadt 	}
738df930be7Sderaadt 
739df930be7Sderaadt 	if (WEXITSTATUS(status))
7405085bfb6Smillert 		return single_user;
741df930be7Sderaadt 
742df930be7Sderaadt 	runcom_mode = AUTOBOOT;		/* the default */
743df930be7Sderaadt 	/* NB: should send a message to the session logger to avoid blocking. */
744df930be7Sderaadt 	logwtmp("~", "reboot", "");
7455085bfb6Smillert 	return read_ttys;
746df930be7Sderaadt }
747df930be7Sderaadt 
748df930be7Sderaadt /*
749ce842bbcSnicm  * Compare session keys.
750df930be7Sderaadt  */
751ce842bbcSnicm static int
cmp_sessions(session_t * sp1,session_t * sp2)752ce842bbcSnicm cmp_sessions(session_t *sp1, session_t *sp2)
753df930be7Sderaadt {
754ce842bbcSnicm 	if (sp1->se_process < sp2->se_process)
755ce842bbcSnicm 		return (-1);
756ce842bbcSnicm 	if (sp1->se_process > sp2->se_process)
757df930be7Sderaadt 		return (1);
758df930be7Sderaadt 	return (0);
759df930be7Sderaadt }
760df930be7Sderaadt 
761df930be7Sderaadt /*
762df930be7Sderaadt  * Add a new login session.
763df930be7Sderaadt  */
764df930be7Sderaadt void
add_session(session_t * sp)765bc52e260Sderaadt add_session(session_t *sp)
766df930be7Sderaadt {
767ce842bbcSnicm 	if (RB_INSERT(session_tree, &session_tree, sp) != NULL)
768df930be7Sderaadt 		emergency("insert %d: %s", sp->se_process, strerror(errno));
769df930be7Sderaadt }
770df930be7Sderaadt 
771df930be7Sderaadt /*
772df930be7Sderaadt  * Delete an old login session.
773df930be7Sderaadt  */
774df930be7Sderaadt void
del_session(session_t * sp)775bc52e260Sderaadt del_session(session_t *sp)
776df930be7Sderaadt {
777ce842bbcSnicm 	RB_REMOVE(session_tree, &session_tree, sp);
778df930be7Sderaadt }
779df930be7Sderaadt 
780df930be7Sderaadt /*
781df930be7Sderaadt  * Look up a login session by pid.
782df930be7Sderaadt  */
783df930be7Sderaadt session_t *
find_session(pid_t pid)784df930be7Sderaadt find_session(pid_t pid)
785df930be7Sderaadt {
786ce842bbcSnicm 	struct init_session s;
787df930be7Sderaadt 
788ce842bbcSnicm 	s.se_process = pid;
789ce842bbcSnicm 	return (RB_FIND(session_tree, &session_tree, &s));
790df930be7Sderaadt }
791df930be7Sderaadt 
792df930be7Sderaadt /*
793df930be7Sderaadt  * Construct an argument vector from a command line.
794df930be7Sderaadt  */
795df930be7Sderaadt char **
construct_argv(char * command)796bc52e260Sderaadt construct_argv(char *command)
797df930be7Sderaadt {
798e073c79dSmpech 	int argc = 0;
7995ae94ef8Sderaadt 	char **argv = calloc((strlen(command) + 1) / 2 + 1, sizeof (char *));
800df930be7Sderaadt 	static const char separators[] = " \t";
801df930be7Sderaadt 
80298b17e93Sotto 	if (argv == NULL)
8035392146dSmillert 		return (0);
80498b17e93Sotto 
80598b17e93Sotto 	if ((argv[argc++] = strtok(command, separators)) == 0) {
80698b17e93Sotto 		free(argv);
80798b17e93Sotto 		return (0);
80898b17e93Sotto 	}
8094158ce8dSderaadt 	while ((argv[argc++] = strtok(NULL, separators)))
810df930be7Sderaadt 		continue;
8115392146dSmillert 	return (argv);
812df930be7Sderaadt }
813df930be7Sderaadt 
814df930be7Sderaadt /*
815df930be7Sderaadt  * Deallocate a session descriptor.
816df930be7Sderaadt  */
817df930be7Sderaadt void
free_session(session_t * sp)818bc52e260Sderaadt free_session(session_t *sp)
819df930be7Sderaadt {
820df930be7Sderaadt 	free(sp->se_device);
821df930be7Sderaadt 	if (sp->se_getty) {
822df930be7Sderaadt 		free(sp->se_getty);
823df930be7Sderaadt 		free(sp->se_getty_argv);
824df930be7Sderaadt 	}
825df930be7Sderaadt 	if (sp->se_window) {
826df930be7Sderaadt 		free(sp->se_window);
827df930be7Sderaadt 		free(sp->se_window_argv);
828df930be7Sderaadt 	}
829df930be7Sderaadt 	free(sp);
830df930be7Sderaadt }
831df930be7Sderaadt 
832df930be7Sderaadt /*
833df930be7Sderaadt  * Allocate a new session descriptor.
834df930be7Sderaadt  */
835df930be7Sderaadt session_t *
new_session(session_t * sprev,int session_index,struct ttyent * typ)836bc52e260Sderaadt new_session(session_t *sprev, int session_index, struct ttyent *typ)
837df930be7Sderaadt {
838e073c79dSmpech 	session_t *sp;
839df930be7Sderaadt 
840df930be7Sderaadt 	if ((typ->ty_status & TTY_ON) == 0 ||
841df930be7Sderaadt 	    typ->ty_name == 0 ||
842df930be7Sderaadt 	    typ->ty_getty == 0)
8435392146dSmillert 		return (0);
844df930be7Sderaadt 
845e7049ca9Stedu 	sp = calloc(1, sizeof (session_t));
846e7049ca9Stedu 	if (sp == NULL)
847e7049ca9Stedu 		err(1, "calloc");
848df930be7Sderaadt 
849df930be7Sderaadt 	sp->se_flags = SE_PRESENT;
850df930be7Sderaadt 	sp->se_index = session_index;
851df930be7Sderaadt 
852e19c6df5Sderaadt 	if (asprintf(&sp->se_device, "%s%s", _PATH_DEV, typ->ty_name) == -1)
853e19c6df5Sderaadt 		err(1, "asprintf");
854df930be7Sderaadt 
855df930be7Sderaadt 	if (setupargv(sp, typ) == 0) {
856df930be7Sderaadt 		free_session(sp);
857df930be7Sderaadt 		return (0);
858df930be7Sderaadt 	}
859df930be7Sderaadt 
860f52a7f8cSmmcc 	sp->se_next = NULL;
861f52a7f8cSmmcc 	if (sprev == NULL) {
862df930be7Sderaadt 		sessions = sp;
863f52a7f8cSmmcc 		sp->se_prev = NULL;
864df930be7Sderaadt 	} else {
865df930be7Sderaadt 		sprev->se_next = sp;
866df930be7Sderaadt 		sp->se_prev = sprev;
867df930be7Sderaadt 	}
868df930be7Sderaadt 
8695392146dSmillert 	return (sp);
870df930be7Sderaadt }
871df930be7Sderaadt 
872df930be7Sderaadt /*
873df930be7Sderaadt  * Calculate getty and if useful window argv vectors.
874df930be7Sderaadt  */
875df930be7Sderaadt int
setupargv(session_t * sp,struct ttyent * typ)876bc52e260Sderaadt setupargv(session_t *sp, struct ttyent *typ)
877df930be7Sderaadt {
878df930be7Sderaadt 	if (sp->se_getty) {
879df930be7Sderaadt 		free(sp->se_getty);
880df930be7Sderaadt 		free(sp->se_getty_argv);
881df930be7Sderaadt 	}
882e19c6df5Sderaadt 	if (asprintf(&sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name) == -1)
883e19c6df5Sderaadt 		err(1, "asprintf");
884df930be7Sderaadt 	sp->se_getty_argv = construct_argv(sp->se_getty);
885df930be7Sderaadt 	if (sp->se_getty_argv == 0) {
886df930be7Sderaadt 		warning("can't parse getty for port %s", sp->se_device);
887df930be7Sderaadt 		free(sp->se_getty);
888f52a7f8cSmmcc 		sp->se_getty = NULL;
889df930be7Sderaadt 		return (0);
890df930be7Sderaadt 	}
891df930be7Sderaadt 	if (typ->ty_window) {
892df930be7Sderaadt 		free(sp->se_window);
893df930be7Sderaadt 		sp->se_window = strdup(typ->ty_window);
894332a991fSderaadt 		if (sp->se_window == NULL) {
895332a991fSderaadt 			warning("can't allocate window");
896332a991fSderaadt 			return (0);
897332a991fSderaadt 		}
898df930be7Sderaadt 		sp->se_window_argv = construct_argv(sp->se_window);
899332a991fSderaadt 		if (sp->se_window_argv == NULL) {
900df930be7Sderaadt 			warning("can't parse window for port %s",
901df930be7Sderaadt 			    sp->se_device);
902df930be7Sderaadt 			free(sp->se_window);
903332a991fSderaadt 			sp->se_window = NULL;
904df930be7Sderaadt 			return (0);
905df930be7Sderaadt 		}
906df930be7Sderaadt 	}
907df930be7Sderaadt 	return (1);
908df930be7Sderaadt }
909df930be7Sderaadt 
910df930be7Sderaadt /*
911df930be7Sderaadt  * Walk the list of ttys and create sessions for each active line.
912df930be7Sderaadt  */
9135085bfb6Smillert state_t
f_read_ttys(void)9145085bfb6Smillert f_read_ttys(void)
915df930be7Sderaadt {
916df930be7Sderaadt 	int session_index = 0;
917e073c79dSmpech 	session_t *sp, *snext;
918e073c79dSmpech 	struct ttyent *typ;
919df930be7Sderaadt 
920df930be7Sderaadt 	/*
921df930be7Sderaadt 	 * Destroy any previous session state.
922df930be7Sderaadt 	 * There shouldn't be any, but just in case...
923df930be7Sderaadt 	 */
924df930be7Sderaadt 	for (sp = sessions; sp; sp = snext) {
925df930be7Sderaadt 		if (sp->se_process)
926df930be7Sderaadt 			clear_session_logs(sp);
927df930be7Sderaadt 		snext = sp->se_next;
928df930be7Sderaadt 		free_session(sp);
929df930be7Sderaadt 	}
930f52a7f8cSmmcc 	sessions = NULL;
931df930be7Sderaadt 
932df930be7Sderaadt 	/*
933df930be7Sderaadt 	 * Allocate a session entry for each active port.
934df930be7Sderaadt 	 * Note that sp starts at 0.
935df930be7Sderaadt 	 */
9364158ce8dSderaadt 	while ((typ = getttyent()))
9374158ce8dSderaadt 		if ((snext = new_session(sp, ++session_index, typ)))
938df930be7Sderaadt 			sp = snext;
939df930be7Sderaadt 
940df930be7Sderaadt 	endttyent();
941df930be7Sderaadt 
9425085bfb6Smillert 	return multi_user;
943df930be7Sderaadt }
944df930be7Sderaadt 
945df930be7Sderaadt /*
946df930be7Sderaadt  * Start a window system running.
947df930be7Sderaadt  */
948df930be7Sderaadt void
start_window_system(session_t * sp)949bc52e260Sderaadt start_window_system(session_t *sp)
950df930be7Sderaadt {
951df930be7Sderaadt 	pid_t pid;
952df930be7Sderaadt 	sigset_t mask;
953df930be7Sderaadt 
954df930be7Sderaadt 	if ((pid = fork()) == -1) {
955df930be7Sderaadt 		emergency("can't fork for window system on port %s: %m",
956df930be7Sderaadt 		    sp->se_device);
957df930be7Sderaadt 		/* hope that getty fails and we can try again */
958df930be7Sderaadt 		return;
959df930be7Sderaadt 	}
960df930be7Sderaadt 
961df930be7Sderaadt 	if (pid)
962df930be7Sderaadt 		return;
963df930be7Sderaadt 
964df930be7Sderaadt 	sigemptyset(&mask);
9657674422fSkstailey 	sigprocmask(SIG_SETMASK, &mask, NULL);
966df930be7Sderaadt 
967df69c215Sderaadt 	if (setsid() == -1)
968df930be7Sderaadt 		emergency("setsid failed (window) %m");
969df930be7Sderaadt 
970fed231abSmillert 	setprocresources(RESOURCE_WINDOW);
971fed231abSmillert 
972df930be7Sderaadt 	execv(sp->se_window_argv[0], sp->se_window_argv);
973df930be7Sderaadt 	stall("can't exec window system '%s' for port %s: %m",
974df930be7Sderaadt 	    sp->se_window_argv[0], sp->se_device);
975df930be7Sderaadt 	_exit(1);
976df930be7Sderaadt }
977df930be7Sderaadt 
978df930be7Sderaadt /*
979df930be7Sderaadt  * Start a login session running.
9808bf92784Sderaadt  * For first open, man-handle tty directly to determine if it
9818bf92784Sderaadt  * really exists. It is not efficient to spawn gettys on devices
9828bf92784Sderaadt  * that do not exist.
983df930be7Sderaadt  */
984df930be7Sderaadt pid_t
start_getty(session_t * sp)985bc52e260Sderaadt start_getty(session_t *sp)
986df930be7Sderaadt {
987df930be7Sderaadt 	pid_t pid;
988df930be7Sderaadt 	sigset_t mask;
989eb10f49cScheloha 	struct timespec current_time, elapsed;
9908bf92784Sderaadt 	int p[2], new = 1;
9918bf92784Sderaadt 
9928bf92784Sderaadt 	if (sp->se_flags & SE_DEVEXISTS)
9938bf92784Sderaadt 		new = 0;
9948bf92784Sderaadt 
9958bf92784Sderaadt 	if (new) {
9968bf92784Sderaadt 		if (pipe(p) == -1)
9975392146dSmillert 			return (-1);
9988bf92784Sderaadt 	}
999df930be7Sderaadt 
1000df930be7Sderaadt 	/*
1001df930be7Sderaadt 	 * fork(), not vfork() -- we can't afford to block.
1002df930be7Sderaadt 	 */
1003df930be7Sderaadt 	if ((pid = fork()) == -1) {
1004df930be7Sderaadt 		emergency("can't fork for getty on port %s: %m", sp->se_device);
10055392146dSmillert 		return (-1);
1006df930be7Sderaadt 	}
1007df930be7Sderaadt 
10088bf92784Sderaadt 	if (pid) {
10098bf92784Sderaadt 		if (new) {
10108bf92784Sderaadt 			char c;
10118bf92784Sderaadt 
10128bf92784Sderaadt 			close(p[1]);
10138bf92784Sderaadt 			if (read(p[0], &c, 1) != 1) {
10148bf92784Sderaadt 				close(p[0]);
10155392146dSmillert 				return (-1);
10168bf92784Sderaadt 			}
10178bf92784Sderaadt 			close(p[0]);
10188bf92784Sderaadt 			if (c == '1')
10198bf92784Sderaadt 				sp->se_flags |= SE_DEVEXISTS;
10208bf92784Sderaadt 			else
10218bf92784Sderaadt 				sp->se_flags |= SE_SHUTDOWN;
10228bf92784Sderaadt 		}
10235392146dSmillert 		return (pid);
10248bf92784Sderaadt 	}
10258bf92784Sderaadt 	if (new) {
10268bf92784Sderaadt 		int fd;
10278bf92784Sderaadt 
10288bf92784Sderaadt 		close(p[0]);
1029b7041c07Sderaadt 		fd = open(sp->se_device, O_RDONLY | O_NONBLOCK);
10308bf92784Sderaadt 		if (fd == -1 && (errno == ENXIO || errno == ENOENT ||
10318bf92784Sderaadt 		    errno == EISDIR)) {
10328bf92784Sderaadt 			(void)write(p[1], "0", 1);
10338bf92784Sderaadt 			close(p[1]);
10348bf92784Sderaadt 			_exit(1);
10358bf92784Sderaadt 		}
10368bf92784Sderaadt 		(void)write(p[1], "1", 1);
10378bf92784Sderaadt 		close(p[1]);
10388bf92784Sderaadt 		close(fd);
10398bf92784Sderaadt 		sleep(1);
10408bf92784Sderaadt 	}
1041df930be7Sderaadt 
1042eb10f49cScheloha 	if (timespecisset(&sp->se_started)) {
1043eb10f49cScheloha 		clock_gettime(CLOCK_MONOTONIC, &current_time);
1044eb10f49cScheloha 		timespecsub(&current_time, &sp->se_started, &elapsed);
1045eb10f49cScheloha 		if (elapsed.tv_sec < GETTY_SPACING) {
1046eb10f49cScheloha 			warning(
1047eb10f49cScheloha 			    "getty repeating too quickly on port %s, sleeping",
1048df930be7Sderaadt 			    sp->se_device);
10495d655d40Sderaadt 			sleep(GETTY_SLEEP);
1050df930be7Sderaadt 		}
1051eb10f49cScheloha 	}
1052df930be7Sderaadt 
1053df930be7Sderaadt 	if (sp->se_window) {
1054df930be7Sderaadt 		start_window_system(sp);
1055df930be7Sderaadt 		sleep(WINDOW_WAIT);
1056df930be7Sderaadt 	}
1057df930be7Sderaadt 
1058df930be7Sderaadt 	sigemptyset(&mask);
10597674422fSkstailey 	sigprocmask(SIG_SETMASK, &mask, NULL);
1060df930be7Sderaadt 
1061fed231abSmillert 	setprocresources(RESOURCE_GETTY);
1062fed231abSmillert 
1063df930be7Sderaadt 	execv(sp->se_getty_argv[0], sp->se_getty_argv);
1064df930be7Sderaadt 	stall("can't exec getty '%s' for port %s: %m",
1065df930be7Sderaadt 	    sp->se_getty_argv[0], sp->se_device);
1066df930be7Sderaadt 	_exit(1);
1067df930be7Sderaadt }
1068df930be7Sderaadt 
1069df930be7Sderaadt /*
1070df930be7Sderaadt  * Collect exit status for a child.
1071df930be7Sderaadt  * If an exiting login, start a new login running.
1072df930be7Sderaadt  */
1073df930be7Sderaadt void
collect_child(pid_t pid)1074df930be7Sderaadt collect_child(pid_t pid)
1075df930be7Sderaadt {
1076e073c79dSmpech 	session_t *sp, *sprev, *snext;
1077df930be7Sderaadt 
1078824827eeSmillert 	if (sessions == NULL)
1079df930be7Sderaadt 		return;
1080df930be7Sderaadt 
1081824827eeSmillert 	if ((sp = find_session(pid)) == NULL)
1082df930be7Sderaadt 		return;
1083df930be7Sderaadt 
1084df930be7Sderaadt 	clear_session_logs(sp);
108548397f5fSmillert 	login_fbtab(sp->se_device + sizeof(_PATH_DEV) - 1, 0, 0);
1086df930be7Sderaadt 	del_session(sp);
1087df930be7Sderaadt 	sp->se_process = 0;
1088df930be7Sderaadt 
1089df930be7Sderaadt 	if (sp->se_flags & SE_SHUTDOWN) {
10904158ce8dSderaadt 		if ((sprev = sp->se_prev))
1091df930be7Sderaadt 			sprev->se_next = sp->se_next;
1092df930be7Sderaadt 		else
1093df930be7Sderaadt 			sessions = sp->se_next;
10944158ce8dSderaadt 		if ((snext = sp->se_next))
1095df930be7Sderaadt 			snext->se_prev = sp->se_prev;
1096df930be7Sderaadt 		free_session(sp);
1097df930be7Sderaadt 		return;
1098df930be7Sderaadt 	}
1099df930be7Sderaadt 
1100df930be7Sderaadt 	if ((pid = start_getty(sp)) == -1) {
1101df930be7Sderaadt 		/* serious trouble */
1102df930be7Sderaadt 		requested_transition = clean_ttys;
1103df930be7Sderaadt 		return;
1104df930be7Sderaadt 	}
1105df930be7Sderaadt 
1106df930be7Sderaadt 	sp->se_process = pid;
1107eb10f49cScheloha 	clock_gettime(CLOCK_MONOTONIC, &sp->se_started);
1108df930be7Sderaadt 	add_session(sp);
1109df930be7Sderaadt }
1110df930be7Sderaadt 
1111df930be7Sderaadt /*
1112df930be7Sderaadt  * Catch a signal and request a state transition.
1113df930be7Sderaadt  */
1114df930be7Sderaadt void
transition_handler(int sig)1115bc52e260Sderaadt transition_handler(int sig)
1116df930be7Sderaadt {
1117df930be7Sderaadt 
1118df930be7Sderaadt 	switch (sig) {
1119df930be7Sderaadt 	case SIGHUP:
1120df930be7Sderaadt 		requested_transition = clean_ttys;
1121df930be7Sderaadt 		break;
1122bff23b0cSdlg 	case SIGINT:
1123dee114d4Sdlg 		requested_transition = do_reboot;
1124dee114d4Sdlg 		break;
1125df930be7Sderaadt 	case SIGTERM:
1126df930be7Sderaadt 		requested_transition = death;
1127df930be7Sderaadt 		break;
1128ab091da3Sderaadt 	case SIGUSR1:
1129ab091da3Sderaadt 		requested_transition = nice_death;
1130ab091da3Sderaadt 		break;
1131bdbecee4Sderaadt 	case SIGUSR2:
1132bdbecee4Sderaadt 		requested_transition = hard_death;
1133bdbecee4Sderaadt 		break;
1134df930be7Sderaadt 	case SIGTSTP:
1135df930be7Sderaadt 		requested_transition = catatonia;
1136df930be7Sderaadt 		break;
1137df930be7Sderaadt 	default:
1138df930be7Sderaadt 		requested_transition = 0;
1139df930be7Sderaadt 		break;
1140df930be7Sderaadt 	}
1141df930be7Sderaadt }
1142df930be7Sderaadt 
1143df930be7Sderaadt /*
1144df930be7Sderaadt  * Take the system multiuser.
1145df930be7Sderaadt  */
11465085bfb6Smillert state_t
f_multi_user(void)11475085bfb6Smillert f_multi_user(void)
1148df930be7Sderaadt {
1149df930be7Sderaadt 	pid_t pid;
1150e073c79dSmpech 	session_t *sp;
1151df930be7Sderaadt 
1152df930be7Sderaadt 	/*
1153df930be7Sderaadt 	 * If the administrator has not set the security level to -1
1154df930be7Sderaadt 	 * to indicate that the kernel should not run multiuser in secure
1155df930be7Sderaadt 	 * mode, and the run script has not set a higher level of security
1156df930be7Sderaadt 	 * than level 1, then put the kernel into secure mode.
1157df930be7Sderaadt 	 */
1158ec7f310bSmillert 	if (requested_transition != catatonia) {
1159df930be7Sderaadt 		if (getsecuritylevel() == 0)
1160df930be7Sderaadt 			setsecuritylevel(1);
1161ec7f310bSmillert 	}
1162ec7f310bSmillert 
1163ec7f310bSmillert 	requested_transition = 0;
1164df930be7Sderaadt 
1165df930be7Sderaadt 	for (sp = sessions; sp; sp = sp->se_next) {
1166df930be7Sderaadt 		if (sp->se_process)
1167df930be7Sderaadt 			continue;
1168df930be7Sderaadt 		if ((pid = start_getty(sp)) == -1) {
1169df930be7Sderaadt 			/* serious trouble */
1170df930be7Sderaadt 			requested_transition = clean_ttys;
1171df930be7Sderaadt 			break;
1172df930be7Sderaadt 		}
1173df930be7Sderaadt 		sp->se_process = pid;
1174eb10f49cScheloha 		clock_gettime(CLOCK_MONOTONIC, &sp->se_started);
1175df930be7Sderaadt 		add_session(sp);
1176df930be7Sderaadt 	}
1177df930be7Sderaadt 
1178df930be7Sderaadt 	while (!requested_transition)
11797674422fSkstailey 		if ((pid = waitpid(-1, NULL, 0)) != -1)
1180df930be7Sderaadt 			collect_child(pid);
1181df930be7Sderaadt 
11825085bfb6Smillert 	return requested_transition;
1183df930be7Sderaadt }
1184df930be7Sderaadt 
1185df930be7Sderaadt /*
1186df930be7Sderaadt  * This is an n-squared algorithm.  We hope it isn't run often...
1187df930be7Sderaadt  */
11885085bfb6Smillert state_t
f_clean_ttys(void)11895085bfb6Smillert f_clean_ttys(void)
1190df930be7Sderaadt {
1191e073c79dSmpech 	session_t *sp, *sprev;
1192e073c79dSmpech 	struct ttyent *typ;
1193e073c79dSmpech 	int session_index = 0;
1194e073c79dSmpech 	int devlen;
1195df930be7Sderaadt 
1196df930be7Sderaadt 	for (sp = sessions; sp; sp = sp->se_next)
1197df930be7Sderaadt 		sp->se_flags &= ~SE_PRESENT;
1198df930be7Sderaadt 
1199df930be7Sderaadt 	devlen = sizeof(_PATH_DEV) - 1;
12004158ce8dSderaadt 	while ((typ = getttyent())) {
1201df930be7Sderaadt 		++session_index;
1202df930be7Sderaadt 
1203f52a7f8cSmmcc 		for (sprev = NULL, sp = sessions; sp; sprev = sp, sp = sp->se_next)
1204df930be7Sderaadt 			if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
1205df930be7Sderaadt 				break;
1206df930be7Sderaadt 
1207df930be7Sderaadt 		if (sp) {
1208df930be7Sderaadt 			sp->se_flags |= SE_PRESENT;
1209df930be7Sderaadt 			if (sp->se_index != session_index) {
1210df930be7Sderaadt 				warning("port %s changed utmp index from %d to %d",
1211df930be7Sderaadt 				    sp->se_device, sp->se_index,
1212df930be7Sderaadt 				    session_index);
1213df930be7Sderaadt 				sp->se_index = session_index;
1214df930be7Sderaadt 			}
1215df930be7Sderaadt 			if ((typ->ty_status & TTY_ON) == 0 ||
1216df930be7Sderaadt 			    typ->ty_getty == 0) {
1217df930be7Sderaadt 				sp->se_flags |= SE_SHUTDOWN;
1218df930be7Sderaadt 				kill(sp->se_process, SIGHUP);
1219df930be7Sderaadt 				continue;
1220df930be7Sderaadt 			}
1221df930be7Sderaadt 			sp->se_flags &= ~SE_SHUTDOWN;
1222df930be7Sderaadt 			if (setupargv(sp, typ) == 0) {
1223df930be7Sderaadt 				warning("can't parse getty for port %s",
1224df930be7Sderaadt 				    sp->se_device);
1225df930be7Sderaadt 				sp->se_flags |= SE_SHUTDOWN;
1226df930be7Sderaadt 				kill(sp->se_process, SIGHUP);
1227df930be7Sderaadt 			}
1228df930be7Sderaadt 			continue;
1229df930be7Sderaadt 		}
1230df930be7Sderaadt 
1231df930be7Sderaadt 		new_session(sprev, session_index, typ);
1232df930be7Sderaadt 	}
1233df930be7Sderaadt 
1234df930be7Sderaadt 	endttyent();
1235df930be7Sderaadt 
1236df930be7Sderaadt 	for (sp = sessions; sp; sp = sp->se_next)
1237df930be7Sderaadt 		if ((sp->se_flags & SE_PRESENT) == 0) {
1238df930be7Sderaadt 			sp->se_flags |= SE_SHUTDOWN;
1239df930be7Sderaadt 			kill(sp->se_process, SIGHUP);
1240df930be7Sderaadt 		}
1241df930be7Sderaadt 
12425085bfb6Smillert 	return multi_user;
1243df930be7Sderaadt }
1244df930be7Sderaadt 
1245df930be7Sderaadt /*
1246df930be7Sderaadt  * Block further logins.
1247df930be7Sderaadt  */
12485085bfb6Smillert state_t
f_catatonia(void)12495085bfb6Smillert f_catatonia(void)
1250df930be7Sderaadt {
1251e073c79dSmpech 	session_t *sp;
1252df930be7Sderaadt 
1253df930be7Sderaadt 	for (sp = sessions; sp; sp = sp->se_next)
1254df930be7Sderaadt 		sp->se_flags |= SE_SHUTDOWN;
1255df930be7Sderaadt 
12565085bfb6Smillert 	return multi_user;
1257df930be7Sderaadt }
1258df930be7Sderaadt 
1259df930be7Sderaadt /*
1260df930be7Sderaadt  * Note SIGALRM.
1261df930be7Sderaadt  */
1262df930be7Sderaadt void
alrm_handler(int sig)1263bc52e260Sderaadt alrm_handler(int sig)
1264df930be7Sderaadt {
1265df930be7Sderaadt 	clang = 1;
1266df930be7Sderaadt }
1267df930be7Sderaadt 
1268bdbecee4Sderaadt int death_howto = RB_HALT;
1269bdbecee4Sderaadt 
1270bdbecee4Sderaadt /*
1271dee114d4Sdlg  * Reboot the system.
1272dee114d4Sdlg  */
12735085bfb6Smillert state_t
f_do_reboot(void)12745085bfb6Smillert f_do_reboot(void)
1275dee114d4Sdlg {
1276dee114d4Sdlg 	death_howto = RB_AUTOBOOT;
12775085bfb6Smillert 	return nice_death;
1278dee114d4Sdlg }
1279dee114d4Sdlg 
1280dee114d4Sdlg /*
1281bdbecee4Sderaadt  * Bring the system down nicely, then we must powerdown because something
1282bdbecee4Sderaadt  * is very wrong.
1283bdbecee4Sderaadt  */
12845085bfb6Smillert state_t
f_hard_death(void)12855085bfb6Smillert f_hard_death(void)
1286bdbecee4Sderaadt {
1287bdbecee4Sderaadt 	death_howto |= RB_POWERDOWN;
12885085bfb6Smillert 	return nice_death;
1289bdbecee4Sderaadt }
1290bdbecee4Sderaadt 
1291ab091da3Sderaadt /*
1292ab091da3Sderaadt  * Bring the system down to single user nicely, after run the shutdown script.
1293ab091da3Sderaadt  */
12945085bfb6Smillert state_t
f_nice_death(void)12955085bfb6Smillert f_nice_death(void)
1296ab091da3Sderaadt {
1297e073c79dSmpech 	session_t *sp;
1298e073c79dSmpech 	int i;
1299ab091da3Sderaadt 	pid_t pid;
1300ab091da3Sderaadt 	static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
1301ab091da3Sderaadt 	int status;
1302ab091da3Sderaadt 
130302638669Snatano #ifdef CPU_LIDACTION
13042d357aedSnatano 	int mib[] = {CTL_MACHDEP, CPU_LIDACTION};
13052d357aedSnatano 	int lidaction = 0;
1306d51453aaSphessler 
1307d51453aaSphessler 	if ((death_howto & RB_POWERDOWN) &&
13082d357aedSnatano 	    (sysctl(mib, 2, NULL, NULL, &lidaction,
13092d357aedSnatano 		    sizeof(lidaction)) == -1) && (errno != EOPNOTSUPP))
13102d357aedSnatano 			warning("cannot disable lid action");
1311d51453aaSphessler #endif
1312d51453aaSphessler 
1313ab091da3Sderaadt 	for (sp = sessions; sp; sp = sp->se_next) {
1314ab091da3Sderaadt 		sp->se_flags &= ~SE_PRESENT;
1315ab091da3Sderaadt 		sp->se_flags |= SE_SHUTDOWN;
1316ab091da3Sderaadt 		kill(sp->se_process, SIGHUP);
1317ab091da3Sderaadt 	}
1318ab091da3Sderaadt 
1319ae1c47a4Smarkus 	/* terminate the accounting process */
1320ae1c47a4Smarkus 	acct(NULL);
1321ae1c47a4Smarkus 
1322ab091da3Sderaadt 	/* NB: should send a message to the session logger to avoid blocking. */
1323ab091da3Sderaadt 	logwtmp("~", "shutdown", "");
1324ab091da3Sderaadt 
132575a54d2eSderaadt 	if (access(_PATH_RUNCOM, R_OK) != -1) {
1326ab091da3Sderaadt 		struct sigaction sa;
1327ab091da3Sderaadt 
1328ab091da3Sderaadt 		switch ((pid = fork())) {
1329ab091da3Sderaadt 		case -1:
1330ab091da3Sderaadt 			break;
1331ab091da3Sderaadt 		case 0:
1332ab091da3Sderaadt 
1333ab091da3Sderaadt 			memset(&sa, 0, sizeof sa);
1334ab091da3Sderaadt 			sigemptyset(&sa.sa_mask);
1335ab091da3Sderaadt 			sa.sa_flags = 0;
1336ab091da3Sderaadt 			sa.sa_handler = SIG_IGN;
1337ab091da3Sderaadt 			(void) sigaction(SIGTSTP, &sa, NULL);
1338ab091da3Sderaadt 			(void) sigaction(SIGHUP, &sa, NULL);
1339ab091da3Sderaadt 
1340ab091da3Sderaadt 			setctty(_PATH_CONSOLE);
1341ab091da3Sderaadt 
1342ab091da3Sderaadt 			sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL);
1343ab091da3Sderaadt 
134475a54d2eSderaadt 			execl(_PATH_BSHELL, "sh", _PATH_RUNCOM, "shutdown",
1345c96f6a27Sderaadt 			    (char *)NULL);
134675a54d2eSderaadt 			stall("can't exec %s for %s %s: %m", _PATH_BSHELL,
134775a54d2eSderaadt 			    _PATH_RUNCOM, "shutdown");
1348ab091da3Sderaadt 			_exit(1);
1349ab091da3Sderaadt 		default:
1350ab091da3Sderaadt 			waitpid(pid, &status, 0);
1351ab091da3Sderaadt 			if (WIFEXITED(status) && WEXITSTATUS(status) == 2)
1352bdbecee4Sderaadt 				death_howto |= RB_POWERDOWN;
1353ab091da3Sderaadt 		}
1354ab091da3Sderaadt 	}
1355ab091da3Sderaadt 
1356ab091da3Sderaadt 	for (i = 0; i < 3; ++i) {
1357ab091da3Sderaadt 		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
1358ab091da3Sderaadt 			goto die;
1359ab091da3Sderaadt 
1360ab091da3Sderaadt 		clang = 0;
1361ab091da3Sderaadt 		alarm(DEATH_WATCH);
1362b5e17218Sderaadt 		do {
1363ab091da3Sderaadt 			if ((pid = waitpid(-1, NULL, 0)) != -1)
1364ab091da3Sderaadt 				collect_child(pid);
1365b5e17218Sderaadt 		} while (clang == 0 && errno != ECHILD);
1366ab091da3Sderaadt 
1367ab091da3Sderaadt 		if (errno == ECHILD)
1368ab091da3Sderaadt 			goto die;
1369ab091da3Sderaadt 	}
1370ab091da3Sderaadt 
1371ab091da3Sderaadt 	warning("some processes would not die; ps axl advised");
1372ab091da3Sderaadt 
1373ab091da3Sderaadt die:
1374bdbecee4Sderaadt 	reboot(death_howto);
1375ab091da3Sderaadt 
1376ab091da3Sderaadt 	/* ... and if that fails.. oh well */
13775085bfb6Smillert 	return single_user;
1378ab091da3Sderaadt }
1379ab091da3Sderaadt 
1380df930be7Sderaadt /*
1381df930be7Sderaadt  * Bring the system down to single user.
1382df930be7Sderaadt  */
13835085bfb6Smillert state_t
f_death(void)13845085bfb6Smillert f_death(void)
1385df930be7Sderaadt {
1386e073c79dSmpech 	session_t *sp;
1387e073c79dSmpech 	int i;
1388df930be7Sderaadt 	pid_t pid;
1389df930be7Sderaadt 	static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
1390df930be7Sderaadt 
1391ae1c47a4Smarkus 	/* terminate the accounting process */
1392ae1c47a4Smarkus 	acct(NULL);
1393ae1c47a4Smarkus 
1394df930be7Sderaadt 	for (sp = sessions; sp; sp = sp->se_next)
1395df930be7Sderaadt 		sp->se_flags |= SE_SHUTDOWN;
1396df930be7Sderaadt 
1397df930be7Sderaadt 	/* NB: should send a message to the session logger to avoid blocking. */
1398df930be7Sderaadt 	logwtmp("~", "shutdown", "");
1399df930be7Sderaadt 
1400df930be7Sderaadt 	for (i = 0; i < 3; ++i) {
1401df930be7Sderaadt 		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
14025085bfb6Smillert 			return single_user;
1403df930be7Sderaadt 
1404df930be7Sderaadt 		clang = 0;
1405df930be7Sderaadt 		alarm(DEATH_WATCH);
1406b5e17218Sderaadt 		do {
14077674422fSkstailey 			if ((pid = waitpid(-1, NULL, 0)) != -1)
1408df930be7Sderaadt 				collect_child(pid);
1409b5e17218Sderaadt 		} while (clang == 0 && errno != ECHILD);
1410df930be7Sderaadt 
1411df930be7Sderaadt 		if (errno == ECHILD)
14125085bfb6Smillert 			return single_user;
1413df930be7Sderaadt 	}
1414df930be7Sderaadt 
1415df930be7Sderaadt 	warning("some processes would not die; ps axl advised");
1416df930be7Sderaadt 
14175085bfb6Smillert 	return single_user;
1418df930be7Sderaadt }
1419fed231abSmillert 
1420fed231abSmillert void
setprocresources(char * class)1421bc52e260Sderaadt setprocresources(char *class)
1422fed231abSmillert {
1423fed231abSmillert 	login_cap_t *lc;
1424fed231abSmillert 
1425fed231abSmillert 	if ((lc = login_getclass(class)) != NULL) {
1426fed231abSmillert 		setusercontext(lc, NULL, 0,
1427fed231abSmillert 		    LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK);
1428fed231abSmillert 		login_close(lc);
1429fed231abSmillert 	}
1430fed231abSmillert }
1431