xref: /minix3/sbin/init/init.c (revision d2532d3d42d764c9ef9816851cdb17eda7e08d36)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: init.c,v 1.106 2015/06/16 23:18:55 christos Exp $	*/
2a06e2ab3SBen Gras 
3a06e2ab3SBen Gras /*-
4a06e2ab3SBen Gras  * Copyright (c) 1991, 1993
5a06e2ab3SBen Gras  *	The Regents of the University of California.  All rights reserved.
6a06e2ab3SBen Gras  *
7a06e2ab3SBen Gras  * This code is derived from software contributed to Berkeley by
8a06e2ab3SBen Gras  * Donn Seeley at Berkeley Software Design, Inc.
9a06e2ab3SBen Gras  *
10a06e2ab3SBen Gras  * Redistribution and use in source and binary forms, with or without
11a06e2ab3SBen Gras  * modification, are permitted provided that the following conditions
12a06e2ab3SBen Gras  * are met:
13a06e2ab3SBen Gras  * 1. Redistributions of source code must retain the above copyright
14a06e2ab3SBen Gras  *    notice, this list of conditions and the following disclaimer.
15a06e2ab3SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
16a06e2ab3SBen Gras  *    notice, this list of conditions and the following disclaimer in the
17a06e2ab3SBen Gras  *    documentation and/or other materials provided with the distribution.
18a06e2ab3SBen Gras  * 3. Neither the name of the University nor the names of its contributors
19a06e2ab3SBen Gras  *    may be used to endorse or promote products derived from this software
20a06e2ab3SBen Gras  *    without specific prior written permission.
21a06e2ab3SBen Gras  *
22a06e2ab3SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23a06e2ab3SBen Gras  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24a06e2ab3SBen Gras  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25a06e2ab3SBen Gras  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26a06e2ab3SBen Gras  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27a06e2ab3SBen Gras  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28a06e2ab3SBen Gras  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29a06e2ab3SBen Gras  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30a06e2ab3SBen Gras  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31a06e2ab3SBen Gras  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32a06e2ab3SBen Gras  * SUCH DAMAGE.
33a06e2ab3SBen Gras  */
34a06e2ab3SBen Gras 
35a06e2ab3SBen Gras #include <sys/cdefs.h>
36a06e2ab3SBen Gras #ifndef lint
37a06e2ab3SBen Gras __COPYRIGHT("@(#) Copyright (c) 1991, 1993\
38a06e2ab3SBen Gras  The Regents of the University of California.  All rights reserved.");
39a06e2ab3SBen Gras #endif /* not lint */
40a06e2ab3SBen Gras 
41a06e2ab3SBen Gras #ifndef lint
42a06e2ab3SBen Gras #if 0
43a06e2ab3SBen Gras static char sccsid[] = "@(#)init.c	8.2 (Berkeley) 4/28/95";
44a06e2ab3SBen Gras #else
45*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: init.c,v 1.106 2015/06/16 23:18:55 christos Exp $");
46a06e2ab3SBen Gras #endif
47a06e2ab3SBen Gras #endif /* not lint */
48a06e2ab3SBen Gras 
49a06e2ab3SBen Gras #include <sys/param.h>
50a06e2ab3SBen Gras #include <sys/sysctl.h>
51a06e2ab3SBen Gras #include <sys/wait.h>
52a06e2ab3SBen Gras #include <sys/mman.h>
53a06e2ab3SBen Gras #include <sys/stat.h>
54a06e2ab3SBen Gras #include <sys/mount.h>
55a06e2ab3SBen Gras #include <machine/cpu.h>
56a06e2ab3SBen Gras 
57a06e2ab3SBen Gras #include <db.h>
58a06e2ab3SBen Gras #include <errno.h>
59a06e2ab3SBen Gras #include <fcntl.h>
60a06e2ab3SBen Gras #include <signal.h>
61a06e2ab3SBen Gras #include <stdio.h>
62a06e2ab3SBen Gras #include <stdlib.h>
63a06e2ab3SBen Gras #include <string.h>
64a06e2ab3SBen Gras #include <syslog.h>
65a06e2ab3SBen Gras #include <time.h>
66a06e2ab3SBen Gras #include <ttyent.h>
67a06e2ab3SBen Gras #include <unistd.h>
68a06e2ab3SBen Gras #include <util.h>
69a06e2ab3SBen Gras #include <paths.h>
70a06e2ab3SBen Gras #include <err.h>
7184d9c625SLionel Sambuc #ifdef SUPPORT_UTMP
7284d9c625SLionel Sambuc #include <utmp.h>
7384d9c625SLionel Sambuc #endif
7484d9c625SLionel Sambuc #ifdef SUPPORT_UTMPX
7584d9c625SLionel Sambuc #include <utmpx.h>
7684d9c625SLionel Sambuc #endif
77a06e2ab3SBen Gras 
78a06e2ab3SBen Gras #include <stdarg.h>
79a06e2ab3SBen Gras 
80a06e2ab3SBen Gras #ifdef SECURE
81a06e2ab3SBen Gras #include <pwd.h>
82a06e2ab3SBen Gras #endif
83a06e2ab3SBen Gras 
84a06e2ab3SBen Gras #include "pathnames.h"
85a06e2ab3SBen Gras 
86a06e2ab3SBen Gras #define XSTR(x) #x
87a06e2ab3SBen Gras #define STR(x) XSTR(x)
88a06e2ab3SBen Gras 
89a06e2ab3SBen Gras /*
90a06e2ab3SBen Gras  * Sleep times; used to prevent thrashing.
91a06e2ab3SBen Gras  */
92a06e2ab3SBen Gras #define	GETTY_SPACING		 5	/* N secs minimum getty spacing */
93a06e2ab3SBen Gras #define	GETTY_SLEEP		30	/* sleep N secs after spacing problem */
94a06e2ab3SBen Gras #define	WINDOW_WAIT		 3	/* wait N secs after starting window */
95a06e2ab3SBen Gras #define	STALL_TIMEOUT		30	/* wait N secs after warning */
96a06e2ab3SBen Gras #define	DEATH_WATCH		10	/* wait N secs for procs to die */
97a06e2ab3SBen Gras 
98a06e2ab3SBen Gras static const struct timespec dtrtime = {.tv_sec = 0, .tv_nsec = 250000};
99a06e2ab3SBen Gras 
100a06e2ab3SBen Gras #if defined(RESCUEDIR)
101a06e2ab3SBen Gras #define	INIT_BSHELL	RESCUEDIR "/sh"
102a06e2ab3SBen Gras #define	INIT_MOUNT_MFS	RESCUEDIR "/mount_mfs"
103a06e2ab3SBen Gras #define	INIT_PATH	RESCUEDIR ":" _PATH_STDPATH
104a06e2ab3SBen Gras #else
105a06e2ab3SBen Gras #define	INIT_BSHELL	_PATH_BSHELL
106a06e2ab3SBen Gras #define	INIT_MOUNT_MFS	"/sbin/mount_mfs"
107a06e2ab3SBen Gras #define	INIT_PATH	_PATH_STDPATH
108a06e2ab3SBen Gras #endif
109a06e2ab3SBen Gras 
110a06e2ab3SBen Gras static void handle(sig_t, ...);
111a06e2ab3SBen Gras static void delset(sigset_t *, ...);
112a06e2ab3SBen Gras 
113a06e2ab3SBen Gras static void stall(const char *, ...) __printflike(1, 2);
114a06e2ab3SBen Gras static void warning(const char *, ...) __printflike(1, 2);
115a06e2ab3SBen Gras static void emergency(const char *, ...) __printflike(1, 2);
116a06e2ab3SBen Gras __dead static void disaster(int);
117a06e2ab3SBen Gras 
11884d9c625SLionel Sambuc #if defined(__minix)
119a06e2ab3SBen Gras static void minixreboot(int);
120a06e2ab3SBen Gras static void minixpowerdown(int);
12184d9c625SLionel Sambuc #else
122a06e2ab3SBen Gras static void badsys(int);
12384d9c625SLionel Sambuc #endif /* defined(__minix) */
124a06e2ab3SBen Gras 
125a06e2ab3SBen Gras /*
126a06e2ab3SBen Gras  * We really need a recursive typedef...
127a06e2ab3SBen Gras  * The following at least guarantees that the return type of (*state_t)()
128a06e2ab3SBen Gras  * is sufficiently wide to hold a function pointer.
129a06e2ab3SBen Gras  */
130a06e2ab3SBen Gras typedef long (*state_func_t)(void);
131a06e2ab3SBen Gras typedef state_func_t (*state_t)(void);
132a06e2ab3SBen Gras 
133a06e2ab3SBen Gras #define	DEATH		'd'
134a06e2ab3SBen Gras #define	SINGLE_USER	's'
135a06e2ab3SBen Gras #define	RUNCOM		'r'
136a06e2ab3SBen Gras #define	READ_TTYS	't'
137a06e2ab3SBen Gras #define	MULTI_USER	'm'
138a06e2ab3SBen Gras #define	CLEAN_TTYS	'T'
139a06e2ab3SBen Gras #define	CATATONIA	'c'
140a06e2ab3SBen Gras 
141a06e2ab3SBen Gras static state_func_t single_user(void);
142a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
143a06e2ab3SBen Gras static state_func_t runcom(void);
144a06e2ab3SBen Gras static state_func_t read_ttys(void);
145a06e2ab3SBen Gras static state_func_t multi_user(void);
146a06e2ab3SBen Gras static state_func_t clean_ttys(void);
147a06e2ab3SBen Gras static state_func_t catatonia(void);
148a06e2ab3SBen Gras static state_func_t death(void);
149a06e2ab3SBen Gras #endif
150a06e2ab3SBen Gras 
151a06e2ab3SBen Gras static enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
152a06e2ab3SBen Gras 
153a06e2ab3SBen Gras static void transition(state_t);
154a06e2ab3SBen Gras static void setctty(const char *);
155a06e2ab3SBen Gras 
156a06e2ab3SBen Gras typedef struct init_session {
157a06e2ab3SBen Gras 	int	se_index;		/* index of entry in ttys file */
158a06e2ab3SBen Gras 	pid_t	se_process;		/* controlling process */
159a06e2ab3SBen Gras 	struct timeval	se_started;	/* used to avoid thrashing */
160a06e2ab3SBen Gras 	int	se_flags;		/* status of session */
161a06e2ab3SBen Gras #define	SE_SHUTDOWN	0x1		/* session won't be restarted */
162a06e2ab3SBen Gras #define	SE_PRESENT	0x2		/* session is in /etc/ttys */
163a06e2ab3SBen Gras 	char	*se_device;		/* filename of port */
164a06e2ab3SBen Gras 	char	*se_getty;		/* what to run on that port */
165a06e2ab3SBen Gras 	char	**se_getty_argv;	/* pre-parsed argument array */
166a06e2ab3SBen Gras 	char	*se_window;		/* window system (started only once) */
167a06e2ab3SBen Gras 	char	**se_window_argv;	/* pre-parsed argument array */
168a06e2ab3SBen Gras 	struct	init_session *se_prev;
169a06e2ab3SBen Gras 	struct	init_session *se_next;
170a06e2ab3SBen Gras } session_t;
171a06e2ab3SBen Gras 
172a06e2ab3SBen Gras static void collect_child(pid_t, int);
173a06e2ab3SBen Gras static int clang;
174a06e2ab3SBen Gras static void transition_handler(int);
175a06e2ab3SBen Gras static void alrm_handler(int);
176a06e2ab3SBen Gras static int has_securelevel(void);
177a06e2ab3SBen Gras static int securelevel_present;
178a06e2ab3SBen Gras 
179a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
180a06e2ab3SBen Gras static int do_setttyent(void);
181a06e2ab3SBen Gras static void start_window_system(session_t *);
182a06e2ab3SBen Gras static char **construct_argv(char *);
183a06e2ab3SBen Gras static int setupargv(session_t *, struct ttyent *);
184a06e2ab3SBen Gras static pid_t start_getty(session_t *);
185a06e2ab3SBen Gras static void free_session(session_t *);
186a06e2ab3SBen Gras static session_t *new_session(session_t *, int, struct ttyent *);
187a06e2ab3SBen Gras static session_t *sessions;
188a06e2ab3SBen Gras static void setsecuritylevel(int);
189a06e2ab3SBen Gras static int getsecuritylevel(void);
190a06e2ab3SBen Gras static int start_session_db(void);
191a06e2ab3SBen Gras static void add_session(session_t *);
192a06e2ab3SBen Gras static void del_session(session_t *);
193a06e2ab3SBen Gras static session_t *find_session(pid_t);
194a06e2ab3SBen Gras static DB *session_db;
195a06e2ab3SBen Gras static state_t requested_transition = runcom;
196a06e2ab3SBen Gras 
197a06e2ab3SBen Gras static void clear_session_logs(session_t *, int);
198a06e2ab3SBen Gras static state_func_t runetcrc(int);
199a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
200a06e2ab3SBen Gras static struct timeval boot_time;
201a06e2ab3SBen Gras static state_t current_state = death;
202a06e2ab3SBen Gras static void session_utmpx(const session_t *, int);
203a06e2ab3SBen Gras static void make_utmpx(const char *, const char *, int, pid_t,
204a06e2ab3SBen Gras     const struct timeval *, int);
205a06e2ab3SBen Gras static char get_runlevel(const state_t);
206a06e2ab3SBen Gras static void utmpx_set_runlevel(char, char);
207a06e2ab3SBen Gras #endif
208a06e2ab3SBen Gras 
209a06e2ab3SBen Gras #ifdef CHROOT
210a06e2ab3SBen Gras static int did_multiuser_chroot = 0;
211a06e2ab3SBen Gras static char rootdir[PATH_MAX];
212a06e2ab3SBen Gras static int shouldchroot(void);
213a06e2ab3SBen Gras static int createsysctlnode(void);
214a06e2ab3SBen Gras #endif /* CHROOT */
215a06e2ab3SBen Gras 
216a06e2ab3SBen Gras #else /* LETS_GET_SMALL */
217a06e2ab3SBen Gras static state_t requested_transition = single_user;
218a06e2ab3SBen Gras #endif /* !LETS_GET_SMALL */
219a06e2ab3SBen Gras 
220a06e2ab3SBen Gras #ifdef MFS_DEV_IF_NO_CONSOLE
221a06e2ab3SBen Gras 
222a06e2ab3SBen Gras static int mfs_dev(void);
223a06e2ab3SBen Gras 
224a06e2ab3SBen Gras #endif
225a06e2ab3SBen Gras 
226a06e2ab3SBen Gras /*
227a06e2ab3SBen Gras  * The mother of all processes.
228a06e2ab3SBen Gras  */
229a06e2ab3SBen Gras int
main(int argc,char ** argv)230a06e2ab3SBen Gras main(int argc, char **argv)
231a06e2ab3SBen Gras {
232a06e2ab3SBen Gras 	struct sigaction sa;
233a06e2ab3SBen Gras 	sigset_t mask;
234a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
235a06e2ab3SBen Gras 	int c;
236a06e2ab3SBen Gras 
237a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
238a06e2ab3SBen Gras 	(void)gettimeofday(&boot_time, NULL);
239a06e2ab3SBen Gras #endif /* SUPPORT_UTMPX */
240a06e2ab3SBen Gras 
241a06e2ab3SBen Gras 	/* Dispose of random users. */
242a06e2ab3SBen Gras 	if (getuid() != 0) {
243a06e2ab3SBen Gras 		errno = EPERM;
244a06e2ab3SBen Gras 		err(1, NULL);
245a06e2ab3SBen Gras 	}
246a06e2ab3SBen Gras 
247a06e2ab3SBen Gras 	/* System V users like to reexec init. */
248a06e2ab3SBen Gras 	if (getpid() != 1)
249a06e2ab3SBen Gras 		errx(1, "already running");
250a06e2ab3SBen Gras #endif
251a06e2ab3SBen Gras 
252a06e2ab3SBen Gras 	/*
253a06e2ab3SBen Gras 	 * Create an initial session.
254a06e2ab3SBen Gras 	 */
255a06e2ab3SBen Gras 	if (setsid() < 0)
256a06e2ab3SBen Gras 		warn("initial setsid() failed");
257a06e2ab3SBen Gras 
258a06e2ab3SBen Gras 	/*
259a06e2ab3SBen Gras 	 * Establish an initial user so that programs running
260a06e2ab3SBen Gras 	 * single user do not freak out and die (like passwd).
261a06e2ab3SBen Gras 	 */
26284d9c625SLionel Sambuc #if !defined(__minix)
263a06e2ab3SBen Gras 	if (setlogin("root") < 0)
264a06e2ab3SBen Gras 		warn("setlogin() failed");
26584d9c625SLionel Sambuc #endif /* !defined(__minix) */
266a06e2ab3SBen Gras 
267a06e2ab3SBen Gras 
268a06e2ab3SBen Gras #ifdef MFS_DEV_IF_NO_CONSOLE
269a06e2ab3SBen Gras 	if (mfs_dev() == -1)
270a06e2ab3SBen Gras 		requested_transition = single_user;
271a06e2ab3SBen Gras #endif
272a06e2ab3SBen Gras 
273a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
274a06e2ab3SBen Gras 	/*
275a06e2ab3SBen Gras 	 * Note that this does NOT open a file...
276a06e2ab3SBen Gras 	 * Does 'init' deserve its own facility number?
277a06e2ab3SBen Gras 	 */
278a06e2ab3SBen Gras 	openlog("init", LOG_CONS, LOG_AUTH);
279a06e2ab3SBen Gras #endif /* LETS_GET_SMALL */
280a06e2ab3SBen Gras 
281a06e2ab3SBen Gras 
282a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
283a06e2ab3SBen Gras 	/*
284a06e2ab3SBen Gras 	 * This code assumes that we always get arguments through flags,
285a06e2ab3SBen Gras 	 * never through bits set in some random machine register.
286a06e2ab3SBen Gras 	 */
287a06e2ab3SBen Gras 	while ((c = getopt(argc, argv, "sf")) != -1)
288a06e2ab3SBen Gras 		switch (c) {
289a06e2ab3SBen Gras 		case 's':
290a06e2ab3SBen Gras 			requested_transition = single_user;
291a06e2ab3SBen Gras 			break;
292a06e2ab3SBen Gras 		case 'f':
293a06e2ab3SBen Gras 			runcom_mode = FASTBOOT;
294a06e2ab3SBen Gras 			break;
295a06e2ab3SBen Gras 		default:
296a06e2ab3SBen Gras 			warning("unrecognized flag `%c'", c);
297a06e2ab3SBen Gras 			break;
298a06e2ab3SBen Gras 		}
299a06e2ab3SBen Gras 
300a06e2ab3SBen Gras 	if (optind != argc)
301a06e2ab3SBen Gras 		warning("ignoring excess arguments");
302a06e2ab3SBen Gras #else /* LETS_GET_SMALL */
303a06e2ab3SBen Gras 	requested_transition = single_user;
304a06e2ab3SBen Gras #endif /* LETS_GET_SMALL */
305a06e2ab3SBen Gras 
306a06e2ab3SBen Gras 	/*
307a06e2ab3SBen Gras 	 * We catch or block signals rather than ignore them,
308a06e2ab3SBen Gras 	 * so that they get reset on exec.
309a06e2ab3SBen Gras 	 */
31084d9c625SLionel Sambuc #if !defined(__minix)
311a06e2ab3SBen Gras 	handle(badsys, SIGSYS, 0);
312a06e2ab3SBen Gras 	handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
313a06e2ab3SBen Gras 	       SIGBUS, SIGXCPU, SIGXFSZ, 0);
314a06e2ab3SBen Gras #else
315a06e2ab3SBen Gras 	handle(minixreboot, SIGABRT, 0);
316a06e2ab3SBen Gras 	handle(minixpowerdown, SIGUSR1, 0);
317a06e2ab3SBen Gras 	handle(disaster, SIGFPE, SIGILL, SIGSEGV, SIGBUS, 0);
31884d9c625SLionel Sambuc #endif /* !defined(__minix) */
319a06e2ab3SBen Gras 	handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0);
320a06e2ab3SBen Gras 	handle(alrm_handler, SIGALRM, 0);
321a06e2ab3SBen Gras 	(void)sigfillset(&mask);
32284d9c625SLionel Sambuc #if !defined(__minix)
323a06e2ab3SBen Gras 	delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
324a06e2ab3SBen Gras 	    SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0);
325a06e2ab3SBen Gras #else
326a06e2ab3SBen Gras 	delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS,
327a06e2ab3SBen Gras 	    SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0);
32884d9c625SLionel Sambuc #endif /* !defined(__minix) */
329a06e2ab3SBen Gras 	(void)sigprocmask(SIG_SETMASK, &mask, NULL);
330a06e2ab3SBen Gras 	(void)sigemptyset(&sa.sa_mask);
331a06e2ab3SBen Gras 	sa.sa_flags = 0;
332a06e2ab3SBen Gras 	sa.sa_handler = SIG_IGN;
333a06e2ab3SBen Gras 	(void)sigaction(SIGTTIN, &sa, NULL);
334a06e2ab3SBen Gras 	(void)sigaction(SIGTTOU, &sa, NULL);
335a06e2ab3SBen Gras 
336a06e2ab3SBen Gras 	/*
337a06e2ab3SBen Gras 	 * Paranoia.
338a06e2ab3SBen Gras 	 */
339a06e2ab3SBen Gras 	(void)close(0);
340a06e2ab3SBen Gras 	(void)close(1);
341a06e2ab3SBen Gras 	(void)close(2);
342a06e2ab3SBen Gras 
343a06e2ab3SBen Gras #if !defined(LETS_GET_SMALL) && defined(CHROOT)
344a06e2ab3SBen Gras 	/* Create "init.root" sysctl node. */
345a06e2ab3SBen Gras 	(void)createsysctlnode();
346a06e2ab3SBen Gras #endif /* !LETS_GET_SMALL && CHROOT*/
347a06e2ab3SBen Gras 
348a06e2ab3SBen Gras 	/*
349a06e2ab3SBen Gras 	 * Securelevel might not be supported by the kernel. Query for it, and
350a06e2ab3SBen Gras 	 * set a variable indicating whether we should attempt anything with it
351a06e2ab3SBen Gras 	 * or not.
352a06e2ab3SBen Gras 	 */
353a06e2ab3SBen Gras 	securelevel_present = has_securelevel();
354a06e2ab3SBen Gras 
355a06e2ab3SBen Gras 	/*
356a06e2ab3SBen Gras 	 * Start the state machine.
357a06e2ab3SBen Gras 	 */
358a06e2ab3SBen Gras 	transition(requested_transition);
359a06e2ab3SBen Gras 
360a06e2ab3SBen Gras 	/*
361a06e2ab3SBen Gras 	 * Should never reach here.
362a06e2ab3SBen Gras 	 */
363a06e2ab3SBen Gras 	return 1;
364a06e2ab3SBen Gras }
365a06e2ab3SBen Gras 
366a06e2ab3SBen Gras /*
367a06e2ab3SBen Gras  * Associate a function with a signal handler.
368a06e2ab3SBen Gras  */
369a06e2ab3SBen Gras static void
handle(sig_t handler,...)370a06e2ab3SBen Gras handle(sig_t handler, ...)
371a06e2ab3SBen Gras {
372a06e2ab3SBen Gras 	int sig;
373a06e2ab3SBen Gras 	struct sigaction sa;
374a06e2ab3SBen Gras 	sigset_t mask_everything;
375a06e2ab3SBen Gras 	va_list ap;
376a06e2ab3SBen Gras 
377a06e2ab3SBen Gras 	va_start(ap, handler);
378a06e2ab3SBen Gras 
379a06e2ab3SBen Gras 	sa.sa_handler = handler;
380a06e2ab3SBen Gras 	(void)sigfillset(&mask_everything);
381a06e2ab3SBen Gras 
382a06e2ab3SBen Gras 	while ((sig = va_arg(ap, int)) != 0) {
383a06e2ab3SBen Gras 		sa.sa_mask = mask_everything;
384a06e2ab3SBen Gras 		/* XXX SA_RESTART? */
385a06e2ab3SBen Gras 		sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
386a06e2ab3SBen Gras 		(void)sigaction(sig, &sa, NULL);
387a06e2ab3SBen Gras 	}
388a06e2ab3SBen Gras 	va_end(ap);
389a06e2ab3SBen Gras }
390a06e2ab3SBen Gras 
391a06e2ab3SBen Gras /*
392a06e2ab3SBen Gras  * Delete a set of signals from a mask.
393a06e2ab3SBen Gras  */
394a06e2ab3SBen Gras static void
delset(sigset_t * maskp,...)395a06e2ab3SBen Gras delset(sigset_t *maskp, ...)
396a06e2ab3SBen Gras {
397a06e2ab3SBen Gras 	int sig;
398a06e2ab3SBen Gras 	va_list ap;
399a06e2ab3SBen Gras 
400a06e2ab3SBen Gras 	va_start(ap, maskp);
401a06e2ab3SBen Gras 
402a06e2ab3SBen Gras 	while ((sig = va_arg(ap, int)) != 0)
403a06e2ab3SBen Gras 		(void)sigdelset(maskp, sig);
404a06e2ab3SBen Gras 	va_end(ap);
405a06e2ab3SBen Gras }
406a06e2ab3SBen Gras 
407a06e2ab3SBen Gras #if 0	/* Enable to get error messages from init ! */
408a06e2ab3SBen Gras #define vsyslog(level, fmt, ap) print_console(level, fmt, ap)
409a06e2ab3SBen Gras #define closelog()
410a06e2ab3SBen Gras 
411a06e2ab3SBen Gras static void
412a06e2ab3SBen Gras print_console(int level, const char *message, va_list ap)
413a06e2ab3SBen Gras {
414a06e2ab3SBen Gras 	/*
415a06e2ab3SBen Gras 	 * XXX: syslog seems to just plain not work in console-only
416a06e2ab3SBen Gras 	 * XXX: situation... that should be fixed.  Let's leave this
417a06e2ab3SBen Gras 	 * XXX: note + code here in case someone gets in trouble and
418a06e2ab3SBen Gras 	 * XXX: wants to debug. -- Jachym Holecek <freza@liberouter.org>
419a06e2ab3SBen Gras 	 */
420a06e2ab3SBen Gras 	char errbuf[1024];
421a06e2ab3SBen Gras 	int fd, len;
422a06e2ab3SBen Gras 
423a06e2ab3SBen Gras 	/* We can't do anything on errors, anyway... */
424a06e2ab3SBen Gras 	fd = open(_PATH_CONSOLE, O_WRONLY);
425a06e2ab3SBen Gras 	if (fd == -1)
426a06e2ab3SBen Gras 		return ;
427a06e2ab3SBen Gras 
428a06e2ab3SBen Gras 	/* %m will get lost... */
429a06e2ab3SBen Gras 	len = vsnprintf(errbuf, sizeof(errbuf), message, ap);
430a06e2ab3SBen Gras 	(void)write(fd, (void *)errbuf, len);
431a06e2ab3SBen Gras 	(void)close(fd);
432a06e2ab3SBen Gras }
433a06e2ab3SBen Gras #endif
434a06e2ab3SBen Gras 
435a06e2ab3SBen Gras /*
436a06e2ab3SBen Gras  * Log a message and sleep for a while (to give someone an opportunity
437a06e2ab3SBen Gras  * to read it and to save log or hardcopy output if the problem is chronic).
438a06e2ab3SBen Gras  * NB: should send a message to the session logger to avoid blocking.
439a06e2ab3SBen Gras  */
440a06e2ab3SBen Gras static void
stall(const char * message,...)441a06e2ab3SBen Gras stall(const char *message, ...)
442a06e2ab3SBen Gras {
443a06e2ab3SBen Gras 	va_list ap;
444a06e2ab3SBen Gras 
445a06e2ab3SBen Gras 	va_start(ap, message);
446a06e2ab3SBen Gras 	vsyslog(LOG_ALERT, message, ap);
447a06e2ab3SBen Gras 	va_end(ap);
448a06e2ab3SBen Gras 	closelog();
449a06e2ab3SBen Gras 	(void)sleep(STALL_TIMEOUT);
450a06e2ab3SBen Gras }
451a06e2ab3SBen Gras 
452a06e2ab3SBen Gras /*
453a06e2ab3SBen Gras  * Like stall(), but doesn't sleep.
454a06e2ab3SBen Gras  * If cpp had variadic macros, the two functions could be #defines for another.
455a06e2ab3SBen Gras  * NB: should send a message to the session logger to avoid blocking.
456a06e2ab3SBen Gras  */
457a06e2ab3SBen Gras static void
warning(const char * message,...)458a06e2ab3SBen Gras warning(const char *message, ...)
459a06e2ab3SBen Gras {
460a06e2ab3SBen Gras 	va_list ap;
461a06e2ab3SBen Gras 
462a06e2ab3SBen Gras 	va_start(ap, message);
463a06e2ab3SBen Gras 	vsyslog(LOG_ALERT, message, ap);
464a06e2ab3SBen Gras 	va_end(ap);
465a06e2ab3SBen Gras 	closelog();
466a06e2ab3SBen Gras }
467a06e2ab3SBen Gras 
468a06e2ab3SBen Gras /*
469a06e2ab3SBen Gras  * Log an emergency message.
470a06e2ab3SBen Gras  * NB: should send a message to the session logger to avoid blocking.
471a06e2ab3SBen Gras  */
472a06e2ab3SBen Gras static void
emergency(const char * message,...)473a06e2ab3SBen Gras emergency(const char *message, ...)
474a06e2ab3SBen Gras {
475a06e2ab3SBen Gras 	va_list ap;
476a06e2ab3SBen Gras 
477a06e2ab3SBen Gras 	va_start(ap, message);
478a06e2ab3SBen Gras 	vsyslog(LOG_EMERG, message, ap);
479a06e2ab3SBen Gras 	va_end(ap);
480a06e2ab3SBen Gras 	closelog();
481a06e2ab3SBen Gras }
482a06e2ab3SBen Gras 
48384d9c625SLionel Sambuc #if !defined(__minix)
484a06e2ab3SBen Gras /*
485a06e2ab3SBen Gras  * Catch a SIGSYS signal.
486a06e2ab3SBen Gras  *
487a06e2ab3SBen Gras  * These may arise if a system does not support sysctl.
488a06e2ab3SBen Gras  * We tolerate up to 25 of these, then throw in the towel.
489a06e2ab3SBen Gras  */
490a06e2ab3SBen Gras static void
badsys(int sig)491a06e2ab3SBen Gras badsys(int sig)
492a06e2ab3SBen Gras {
493a06e2ab3SBen Gras 	static int badcount = 0;
494a06e2ab3SBen Gras 
495a06e2ab3SBen Gras 	if (badcount++ < 25)
496a06e2ab3SBen Gras 		return;
497a06e2ab3SBen Gras 	disaster(sig);
498a06e2ab3SBen Gras }
49984d9c625SLionel Sambuc #endif /* !defined(__minix) */
500a06e2ab3SBen Gras 
501a06e2ab3SBen Gras /*
502a06e2ab3SBen Gras  * Catch an unexpected signal.
503a06e2ab3SBen Gras  */
504a06e2ab3SBen Gras static void
disaster(int sig)505a06e2ab3SBen Gras disaster(int sig)
506a06e2ab3SBen Gras {
507a06e2ab3SBen Gras 
508a06e2ab3SBen Gras 	emergency("fatal signal: %s", strsignal(sig));
509a06e2ab3SBen Gras 	(void)sleep(STALL_TIMEOUT);
510a06e2ab3SBen Gras 	_exit(sig);		/* reboot */
511a06e2ab3SBen Gras }
512a06e2ab3SBen Gras 
51384d9c625SLionel Sambuc #if defined(__minix)
514a06e2ab3SBen Gras /*
515a06e2ab3SBen Gras  * controlled reboot - minix tradition, SIGABRT by tty
516a06e2ab3SBen Gras  */
517a06e2ab3SBen Gras static void
minixreboot(int sig)518a06e2ab3SBen Gras minixreboot(int sig)
519a06e2ab3SBen Gras {
520a06e2ab3SBen Gras 	if(fork() == 0) {
521a06e2ab3SBen Gras 		(void)execl("/sbin/shutdown",
522a06e2ab3SBen Gras 			"shutdown", "-r", "now", "CTRL-ALT_DEL", NULL);
523a06e2ab3SBen Gras 		_exit(1);
524a06e2ab3SBen Gras 	}
525a06e2ab3SBen Gras }
526a06e2ab3SBen Gras 
527a06e2ab3SBen Gras /*
528a06e2ab3SBen Gras  * controlled powerdown
529a06e2ab3SBen Gras  */
530a06e2ab3SBen Gras static void
minixpowerdown(int sig)531a06e2ab3SBen Gras minixpowerdown(int sig)
532a06e2ab3SBen Gras {
533a06e2ab3SBen Gras 	if(fork() == 0) {
534a06e2ab3SBen Gras 		(void)execl("/sbin/shutdown",
535a06e2ab3SBen Gras 			"shutdown", "-p", "now", "CTRL-ALT_DEL", NULL);
536a06e2ab3SBen Gras 		_exit(1);
537a06e2ab3SBen Gras 	}
538a06e2ab3SBen Gras }
53984d9c625SLionel Sambuc #endif /* defined(__minix) */
540a06e2ab3SBen Gras 
541a06e2ab3SBen Gras /*
542a06e2ab3SBen Gras  * Check if securelevel is present.
543a06e2ab3SBen Gras  */
544a06e2ab3SBen Gras static int
has_securelevel(void)545a06e2ab3SBen Gras has_securelevel(void)
546a06e2ab3SBen Gras {
547a06e2ab3SBen Gras #ifdef KERN_SECURELVL
548a06e2ab3SBen Gras 	int name[2], curlevel;
549a06e2ab3SBen Gras 	size_t len;
550a06e2ab3SBen Gras 
551a06e2ab3SBen Gras 	name[0] = CTL_KERN;
552a06e2ab3SBen Gras 	name[1] = KERN_SECURELVL;
553a06e2ab3SBen Gras 	len = sizeof curlevel;
554a06e2ab3SBen Gras 	if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
555a06e2ab3SBen Gras 		/* If it doesn't exist, it's okay. */
556a06e2ab3SBen Gras 		if (errno == ENOENT)
557a06e2ab3SBen Gras 			return 0;
558a06e2ab3SBen Gras 	}
559a06e2ab3SBen Gras 	return 1;
560a06e2ab3SBen Gras #else
561a06e2ab3SBen Gras 	return 0;
562a06e2ab3SBen Gras #endif
563a06e2ab3SBen Gras }
564a06e2ab3SBen Gras 
565a06e2ab3SBen Gras /*
566a06e2ab3SBen Gras  * Get the security level of the kernel.
567a06e2ab3SBen Gras  */
568a06e2ab3SBen Gras static int
getsecuritylevel(void)569a06e2ab3SBen Gras getsecuritylevel(void)
570a06e2ab3SBen Gras {
571a06e2ab3SBen Gras #ifdef KERN_SECURELVL
572a06e2ab3SBen Gras 	int name[2], curlevel;
573a06e2ab3SBen Gras 	size_t len;
574a06e2ab3SBen Gras 
575a06e2ab3SBen Gras 	if (!securelevel_present)
576a06e2ab3SBen Gras 		return -1;
577a06e2ab3SBen Gras 
578a06e2ab3SBen Gras 	name[0] = CTL_KERN;
579a06e2ab3SBen Gras 	name[1] = KERN_SECURELVL;
580a06e2ab3SBen Gras 	len = sizeof curlevel;
581a06e2ab3SBen Gras 	if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
582a06e2ab3SBen Gras 		emergency("cannot get kernel security level: %m");
583a06e2ab3SBen Gras 		return -1;
584a06e2ab3SBen Gras 	}
585a06e2ab3SBen Gras 	return curlevel;
586a06e2ab3SBen Gras #else
587a06e2ab3SBen Gras 	return -1;
588a06e2ab3SBen Gras #endif
589a06e2ab3SBen Gras }
590a06e2ab3SBen Gras 
591a06e2ab3SBen Gras /*
592a06e2ab3SBen Gras  * Set the security level of the kernel.
593a06e2ab3SBen Gras  */
594a06e2ab3SBen Gras static void
setsecuritylevel(int newlevel)595a06e2ab3SBen Gras setsecuritylevel(int newlevel)
596a06e2ab3SBen Gras {
597a06e2ab3SBen Gras #ifdef KERN_SECURELVL
598a06e2ab3SBen Gras 	int name[2], curlevel;
599a06e2ab3SBen Gras 
600a06e2ab3SBen Gras 	if (!securelevel_present)
601a06e2ab3SBen Gras 		return;
602a06e2ab3SBen Gras 
603a06e2ab3SBen Gras 	curlevel = getsecuritylevel();
604a06e2ab3SBen Gras 	if (newlevel == curlevel)
605a06e2ab3SBen Gras 		return;
606a06e2ab3SBen Gras 	name[0] = CTL_KERN;
607a06e2ab3SBen Gras 	name[1] = KERN_SECURELVL;
608a06e2ab3SBen Gras 	if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
609a06e2ab3SBen Gras 		emergency("cannot change kernel security level from"
610a06e2ab3SBen Gras 		    " %d to %d: %m", curlevel, newlevel);
611a06e2ab3SBen Gras 		return;
612a06e2ab3SBen Gras 	}
613a06e2ab3SBen Gras #ifdef SECURE
614a06e2ab3SBen Gras 	warning("kernel security level changed from %d to %d",
615a06e2ab3SBen Gras 	    curlevel, newlevel);
616a06e2ab3SBen Gras #endif
617a06e2ab3SBen Gras #endif
618a06e2ab3SBen Gras }
619a06e2ab3SBen Gras 
620a06e2ab3SBen Gras /*
621a06e2ab3SBen Gras  * Change states in the finite state machine.
622a06e2ab3SBen Gras  * The initial state is passed as an argument.
623a06e2ab3SBen Gras  */
624a06e2ab3SBen Gras static void
transition(state_t s)625a06e2ab3SBen Gras transition(state_t s)
626a06e2ab3SBen Gras {
627a06e2ab3SBen Gras 
628a06e2ab3SBen Gras 	if (s == NULL)
629a06e2ab3SBen Gras 		return;
630a06e2ab3SBen Gras 	for (;;) {
631a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
632a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
633a06e2ab3SBen Gras 		utmpx_set_runlevel(get_runlevel(current_state),
634a06e2ab3SBen Gras 		    get_runlevel(s));
635a06e2ab3SBen Gras 		current_state = s;
636a06e2ab3SBen Gras #endif
637a06e2ab3SBen Gras #endif
638a06e2ab3SBen Gras 		s = (state_t)(*s)();
639a06e2ab3SBen Gras 	}
640a06e2ab3SBen Gras }
641a06e2ab3SBen Gras 
642a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
643a06e2ab3SBen Gras /*
644a06e2ab3SBen Gras  * Close out the accounting files for a login session.
645a06e2ab3SBen Gras  * NB: should send a message to the session logger to avoid blocking.
646a06e2ab3SBen Gras  */
647a06e2ab3SBen Gras static void
clear_session_logs(session_t * sp,int status)648a06e2ab3SBen Gras clear_session_logs(session_t *sp, int status)
649a06e2ab3SBen Gras {
650a06e2ab3SBen Gras #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
651a06e2ab3SBen Gras 	char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
652a06e2ab3SBen Gras #endif
653a06e2ab3SBen Gras 
654a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
655a06e2ab3SBen Gras 	if (logoutx(line, status, DEAD_PROCESS))
656a06e2ab3SBen Gras 		logwtmpx(line, "", "", status, DEAD_PROCESS);
657a06e2ab3SBen Gras #endif
658a06e2ab3SBen Gras #ifdef SUPPORT_UTMP
659a06e2ab3SBen Gras 	if (logout(line))
660a06e2ab3SBen Gras 		logwtmp(line, "", "");
661a06e2ab3SBen Gras #endif
662a06e2ab3SBen Gras }
663a06e2ab3SBen Gras #endif
664a06e2ab3SBen Gras 
665a06e2ab3SBen Gras /*
666a06e2ab3SBen Gras  * Start a session and allocate a controlling terminal.
667a06e2ab3SBen Gras  * Only called by children of init after forking.
668a06e2ab3SBen Gras  */
669a06e2ab3SBen Gras static void
setctty(const char * name)670a06e2ab3SBen Gras setctty(const char *name)
671a06e2ab3SBen Gras {
672a06e2ab3SBen Gras 	int fd;
673a06e2ab3SBen Gras 
67484d9c625SLionel Sambuc #if !defined(__minix)
675a06e2ab3SBen Gras 	(void)revoke(name);
676a06e2ab3SBen Gras #else
677a06e2ab3SBen Gras 	if (setsid() < 0)
678a06e2ab3SBen Gras 		warn("child setsid() failed");
67984d9c625SLionel Sambuc #endif /* !defined(__minix) */
680a06e2ab3SBen Gras 	(void)nanosleep(&dtrtime, NULL);	/* leave DTR low for a bit */
681a06e2ab3SBen Gras 	if ((fd = open(name, O_RDWR)) == -1) {
682a06e2ab3SBen Gras 		stall("can't open %s: %m", name);
683a06e2ab3SBen Gras 		_exit(1);
684a06e2ab3SBen Gras 	}
685a06e2ab3SBen Gras 	if (login_tty(fd) == -1) {
686a06e2ab3SBen Gras 		stall("can't get %s for controlling terminal: %m", name);
687a06e2ab3SBen Gras 		_exit(2);
688a06e2ab3SBen Gras 	}
689a06e2ab3SBen Gras }
690a06e2ab3SBen Gras 
691a06e2ab3SBen Gras /*
692a06e2ab3SBen Gras  * Bring the system up single user.
693a06e2ab3SBen Gras  */
694a06e2ab3SBen Gras static state_func_t
single_user(void)695a06e2ab3SBen Gras single_user(void)
696a06e2ab3SBen Gras {
697a06e2ab3SBen Gras 	pid_t pid, wpid;
698a06e2ab3SBen Gras 	int status;
699a06e2ab3SBen Gras 	int from_securitylevel;
700a06e2ab3SBen Gras 	sigset_t mask;
701a06e2ab3SBen Gras 	struct sigaction sa, satstp, sahup;
702a06e2ab3SBen Gras #ifdef ALTSHELL
703a06e2ab3SBen Gras 	const char *shell = INIT_BSHELL;
704a06e2ab3SBen Gras #endif
705a06e2ab3SBen Gras 	const char *argv[2];
706a06e2ab3SBen Gras #ifdef SECURE
707a06e2ab3SBen Gras 	struct ttyent *typ;
708a06e2ab3SBen Gras 	struct passwd *pp;
709a06e2ab3SBen Gras 	char *clear, *password;
710a06e2ab3SBen Gras #endif
711a06e2ab3SBen Gras #ifdef ALTSHELL
712a06e2ab3SBen Gras 	char altshell[128];
713a06e2ab3SBen Gras #endif /* ALTSHELL */
714a06e2ab3SBen Gras 
715a06e2ab3SBen Gras #if !defined(LETS_GET_SMALL) && defined(CHROOT)
716a06e2ab3SBen Gras 	/* Clear previous idea, just in case. */
717a06e2ab3SBen Gras 	did_multiuser_chroot = 0;
718a06e2ab3SBen Gras #endif /* !LETS_GET_SMALL && CHROOT */
719a06e2ab3SBen Gras 
720a06e2ab3SBen Gras 	/*
721a06e2ab3SBen Gras 	 * If the kernel is in secure mode, downgrade it to insecure mode.
722a06e2ab3SBen Gras 	 */
723a06e2ab3SBen Gras 	from_securitylevel = getsecuritylevel();
724a06e2ab3SBen Gras 	if (from_securitylevel > 0)
725a06e2ab3SBen Gras 		setsecuritylevel(0);
726a06e2ab3SBen Gras 
727a06e2ab3SBen Gras 	(void)sigemptyset(&sa.sa_mask);
728a06e2ab3SBen Gras 	sa.sa_flags = 0;
729a06e2ab3SBen Gras 	sa.sa_handler = SIG_IGN;
730a06e2ab3SBen Gras 	(void)sigaction(SIGTSTP, &sa, &satstp);
731a06e2ab3SBen Gras 	(void)sigaction(SIGHUP, &sa, &sahup);
732a06e2ab3SBen Gras 	if ((pid = fork()) == 0) {
733a06e2ab3SBen Gras 		/*
734a06e2ab3SBen Gras 		 * Start the single user session.
735a06e2ab3SBen Gras 		 */
736a06e2ab3SBen Gras 		if (access(_PATH_CONSTTY, F_OK) == 0)
737a06e2ab3SBen Gras 			setctty(_PATH_CONSTTY);
738a06e2ab3SBen Gras 		else
739a06e2ab3SBen Gras 			setctty(_PATH_CONSOLE);
740a06e2ab3SBen Gras 
741a06e2ab3SBen Gras #ifdef SECURE
742a06e2ab3SBen Gras 		/*
743a06e2ab3SBen Gras 		 * Check the root password.
744a06e2ab3SBen Gras 		 * We don't care if the console is 'on' by default;
745a06e2ab3SBen Gras 		 * it's the only tty that can be 'off' and 'secure'.
746a06e2ab3SBen Gras 		 */
747a06e2ab3SBen Gras 		typ = getttynam("console");
748a06e2ab3SBen Gras 		pp = getpwnam("root");
749a06e2ab3SBen Gras 		if (typ && (from_securitylevel >=2 || (typ->ty_status
750a06e2ab3SBen Gras 		    & TTY_SECURE) == 0) && pp && *pp->pw_passwd != '\0') {
751a06e2ab3SBen Gras 			(void)fprintf(stderr,
752a06e2ab3SBen Gras 			    "Enter root password, or ^D to go multi-user\n");
753a06e2ab3SBen Gras 			for (;;) {
754a06e2ab3SBen Gras 				clear = getpass("Password:");
755a06e2ab3SBen Gras 				if (clear == 0 || *clear == '\0')
756a06e2ab3SBen Gras 					_exit(0);
757a06e2ab3SBen Gras 				password = crypt(clear, pp->pw_passwd);
758a06e2ab3SBen Gras 				(void)memset(clear, 0, _PASSWORD_LEN);
759a06e2ab3SBen Gras 				if (strcmp(password, pp->pw_passwd) == 0)
760a06e2ab3SBen Gras 					break;
761a06e2ab3SBen Gras 				warning("single-user login failed");
762a06e2ab3SBen Gras 			}
763a06e2ab3SBen Gras 		}
764a06e2ab3SBen Gras 		(void)endttyent();
765a06e2ab3SBen Gras 		endpwent();
766a06e2ab3SBen Gras #endif /* SECURE */
767a06e2ab3SBen Gras 
768a06e2ab3SBen Gras #ifdef ALTSHELL
769a06e2ab3SBen Gras 		(void)fprintf(stderr,
770a06e2ab3SBen Gras 		    "Enter pathname of shell or RETURN for %s: ", shell);
771a06e2ab3SBen Gras 		if (fgets(altshell, sizeof(altshell), stdin) == NULL) {
772a06e2ab3SBen Gras 			altshell[0] = '\0';
773a06e2ab3SBen Gras 		} else {
774a06e2ab3SBen Gras 			/* nuke \n */
775a06e2ab3SBen Gras 			char *p;
776a06e2ab3SBen Gras 
777a06e2ab3SBen Gras 			if ((p = strchr(altshell, '\n')) != NULL)
778a06e2ab3SBen Gras 				*p = '\0';
779a06e2ab3SBen Gras 		}
780a06e2ab3SBen Gras 
781a06e2ab3SBen Gras 		if (altshell[0])
782a06e2ab3SBen Gras 			shell = altshell;
783a06e2ab3SBen Gras #endif /* ALTSHELL */
784a06e2ab3SBen Gras 
785a06e2ab3SBen Gras 		/*
786a06e2ab3SBen Gras 		 * Unblock signals.
787a06e2ab3SBen Gras 		 * We catch all the interesting ones,
788a06e2ab3SBen Gras 		 * and those are reset to SIG_DFL on exec.
789a06e2ab3SBen Gras 		 */
790a06e2ab3SBen Gras 		(void)sigemptyset(&mask);
791a06e2ab3SBen Gras 		(void)sigprocmask(SIG_SETMASK, &mask, NULL);
792a06e2ab3SBen Gras 
793a06e2ab3SBen Gras 		/*
794a06e2ab3SBen Gras 		 * Fire off a shell.
795a06e2ab3SBen Gras 		 * If the default one doesn't work, try the Bourne shell.
796a06e2ab3SBen Gras 		 */
797a06e2ab3SBen Gras 		argv[0] = "-sh";
798a06e2ab3SBen Gras 		argv[1] = 0;
799a06e2ab3SBen Gras 		(void)setenv("PATH", INIT_PATH, 1);
800a06e2ab3SBen Gras #ifdef ALTSHELL
801a06e2ab3SBen Gras 		if (altshell[0])
802a06e2ab3SBen Gras 			argv[0] = altshell;
803a06e2ab3SBen Gras 		(void)execv(shell, __UNCONST(argv));
804a06e2ab3SBen Gras 		emergency("can't exec `%s' for single user: %m", shell);
805a06e2ab3SBen Gras 		argv[0] = "-sh";
806a06e2ab3SBen Gras #endif /* ALTSHELL */
807a06e2ab3SBen Gras 		(void)execv(INIT_BSHELL, __UNCONST(argv));
808a06e2ab3SBen Gras 		emergency("can't exec `%s' for single user: %m", INIT_BSHELL);
809a06e2ab3SBen Gras 		(void)sleep(STALL_TIMEOUT);
810a06e2ab3SBen Gras 		_exit(3);
811a06e2ab3SBen Gras 	}
812a06e2ab3SBen Gras 
813a06e2ab3SBen Gras 	if (pid == -1) {
814a06e2ab3SBen Gras 		/*
815a06e2ab3SBen Gras 		 * We are seriously hosed.  Do our best.
816a06e2ab3SBen Gras 		 */
817a06e2ab3SBen Gras 		emergency("can't fork single-user shell: %m, trying again");
818a06e2ab3SBen Gras 		while (waitpid(-1, NULL, WNOHANG) > 0)
819a06e2ab3SBen Gras 			continue;
820a06e2ab3SBen Gras 		(void)sigaction(SIGTSTP, &satstp, NULL);
821a06e2ab3SBen Gras 		(void)sigaction(SIGHUP, &sahup, NULL);
822a06e2ab3SBen Gras 		return (state_func_t)single_user;
823a06e2ab3SBen Gras 	}
824a06e2ab3SBen Gras 
825a06e2ab3SBen Gras 	requested_transition = 0;
826a06e2ab3SBen Gras 	do {
827a06e2ab3SBen Gras 		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
828a06e2ab3SBen Gras 			collect_child(wpid, status);
829a06e2ab3SBen Gras 		if (wpid == -1) {
830a06e2ab3SBen Gras 			if (errno == EINTR)
831a06e2ab3SBen Gras 				continue;
832a06e2ab3SBen Gras 			warning("wait for single-user shell failed: %m; "
833a06e2ab3SBen Gras 			    "restarting");
834a06e2ab3SBen Gras 			return (state_func_t)single_user;
835a06e2ab3SBen Gras 		}
836a06e2ab3SBen Gras 		if (wpid == pid && WIFSTOPPED(status)) {
837a06e2ab3SBen Gras 			warning("shell stopped, restarting");
838a06e2ab3SBen Gras 			(void)kill(pid, SIGCONT);
839a06e2ab3SBen Gras 			wpid = -1;
840a06e2ab3SBen Gras 		}
841a06e2ab3SBen Gras 	} while (wpid != pid && !requested_transition);
842a06e2ab3SBen Gras 
843a06e2ab3SBen Gras 	if (requested_transition) {
844a06e2ab3SBen Gras 		(void)sigaction(SIGTSTP, &satstp, NULL);
845a06e2ab3SBen Gras 		(void)sigaction(SIGHUP, &sahup, NULL);
846a06e2ab3SBen Gras 		return (state_func_t)requested_transition;
847a06e2ab3SBen Gras 	}
848a06e2ab3SBen Gras 
849a06e2ab3SBen Gras 	if (WIFSIGNALED(status)) {
850a06e2ab3SBen Gras 		if (WTERMSIG(status) == SIGKILL) {
851a06e2ab3SBen Gras 			/* executed /sbin/reboot; wait for the end quietly */
852a06e2ab3SBen Gras 			sigset_t s;
853a06e2ab3SBen Gras 
854a06e2ab3SBen Gras 			(void)sigfillset(&s);
855a06e2ab3SBen Gras 			for (;;)
856a06e2ab3SBen Gras 				(void)sigsuspend(&s);
857a06e2ab3SBen Gras 		} else {
858a06e2ab3SBen Gras 			warning("single user shell terminated (%x), restarting",
859a06e2ab3SBen Gras 				status);
860a06e2ab3SBen Gras 			(void)sigaction(SIGTSTP, &satstp, NULL);
861a06e2ab3SBen Gras 			(void)sigaction(SIGHUP, &sahup, NULL);
862a06e2ab3SBen Gras 			return (state_func_t)single_user;
863a06e2ab3SBen Gras 		}
864a06e2ab3SBen Gras 	}
865a06e2ab3SBen Gras 
866a06e2ab3SBen Gras 	runcom_mode = FASTBOOT;
867a06e2ab3SBen Gras 	(void)sigaction(SIGTSTP, &satstp, NULL);
868a06e2ab3SBen Gras 	(void)sigaction(SIGHUP, &sahup, NULL);
869a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
870a06e2ab3SBen Gras 	return (state_func_t)runcom;
871a06e2ab3SBen Gras #else /* LETS_GET_SMALL */
872a06e2ab3SBen Gras 	return (state_func_t)single_user;
873a06e2ab3SBen Gras #endif /* LETS_GET_SMALL */
874a06e2ab3SBen Gras }
875a06e2ab3SBen Gras 
876a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
877a06e2ab3SBen Gras 
878a06e2ab3SBen Gras /* ARGSUSED */
879a06e2ab3SBen Gras static state_func_t
runetcrc(int trychroot)880a06e2ab3SBen Gras runetcrc(int trychroot)
881a06e2ab3SBen Gras {
882a06e2ab3SBen Gras 	pid_t pid, wpid;
883a06e2ab3SBen Gras 	int status;
884a06e2ab3SBen Gras 	const char *argv[4];
885a06e2ab3SBen Gras 	struct sigaction sa;
886a06e2ab3SBen Gras 
887a06e2ab3SBen Gras 	switch ((pid = fork())) {
888a06e2ab3SBen Gras 	case 0:
889a06e2ab3SBen Gras 		(void)sigemptyset(&sa.sa_mask);
890a06e2ab3SBen Gras 		sa.sa_flags = 0;
891a06e2ab3SBen Gras 		sa.sa_handler = SIG_IGN;
892a06e2ab3SBen Gras 		(void)sigaction(SIGTSTP, &sa, NULL);
893a06e2ab3SBen Gras 		(void)sigaction(SIGHUP, &sa, NULL);
894a06e2ab3SBen Gras 
895a06e2ab3SBen Gras 		setctty(_PATH_CONSOLE);
896a06e2ab3SBen Gras 
897a06e2ab3SBen Gras 		argv[0] = "sh";
898a06e2ab3SBen Gras 		argv[1] = _PATH_RUNCOM;
899a06e2ab3SBen Gras 		argv[2] = (runcom_mode == AUTOBOOT ? "autoboot" : 0);
900a06e2ab3SBen Gras 		argv[3] = 0;
901a06e2ab3SBen Gras 
902a06e2ab3SBen Gras 		(void)sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL);
903a06e2ab3SBen Gras 
904a06e2ab3SBen Gras #ifdef CHROOT
905a06e2ab3SBen Gras 		if (trychroot)
906a06e2ab3SBen Gras 			if (chroot(rootdir) != 0) {
907a06e2ab3SBen Gras 				warning("failed to chroot to `%s': %m",
908a06e2ab3SBen Gras 				    rootdir);
909a06e2ab3SBen Gras 				_exit(4); 	/* force single user mode */
910a06e2ab3SBen Gras 			}
911a06e2ab3SBen Gras #endif /* CHROOT */
912a06e2ab3SBen Gras 
913a06e2ab3SBen Gras 		(void)execv(INIT_BSHELL, __UNCONST(argv));
914a06e2ab3SBen Gras 		stall("can't exec `%s' for `%s': %m", INIT_BSHELL, _PATH_RUNCOM);
915a06e2ab3SBen Gras 		_exit(5);	/* force single user mode */
916a06e2ab3SBen Gras 		/*NOTREACHED*/
917a06e2ab3SBen Gras 	case -1:
918a06e2ab3SBen Gras 		emergency("can't fork for `%s' on `%s': %m", INIT_BSHELL,
919a06e2ab3SBen Gras 		    _PATH_RUNCOM);
920a06e2ab3SBen Gras 		while (waitpid(-1, NULL, WNOHANG) > 0)
921a06e2ab3SBen Gras 			continue;
922a06e2ab3SBen Gras 		(void)sleep(STALL_TIMEOUT);
923a06e2ab3SBen Gras 		return (state_func_t)single_user;
924a06e2ab3SBen Gras 	default:
925a06e2ab3SBen Gras 		break;
926a06e2ab3SBen Gras 	}
927a06e2ab3SBen Gras 
928a06e2ab3SBen Gras 	/*
929a06e2ab3SBen Gras 	 * Copied from single_user().  This is a bit paranoid.
930a06e2ab3SBen Gras 	 */
931a06e2ab3SBen Gras 	do {
932a06e2ab3SBen Gras 		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
933a06e2ab3SBen Gras 			collect_child(wpid, status);
934a06e2ab3SBen Gras 		if (wpid == -1) {
935a06e2ab3SBen Gras 			if (errno == EINTR)
936a06e2ab3SBen Gras 				continue;
937a06e2ab3SBen Gras 			warning("wait for `%s' on `%s' failed: %m; going to "
938a06e2ab3SBen Gras 			    "single user mode", INIT_BSHELL, _PATH_RUNCOM);
939a06e2ab3SBen Gras 			return (state_func_t)single_user;
940a06e2ab3SBen Gras 		}
941a06e2ab3SBen Gras 		if (wpid == pid && WIFSTOPPED(status)) {
942a06e2ab3SBen Gras 			warning("`%s' on `%s' stopped, restarting",
943a06e2ab3SBen Gras 			    INIT_BSHELL, _PATH_RUNCOM);
944a06e2ab3SBen Gras 			(void)kill(pid, SIGCONT);
945a06e2ab3SBen Gras 			wpid = -1;
946a06e2ab3SBen Gras 		}
947a06e2ab3SBen Gras 	} while (wpid != pid);
948a06e2ab3SBen Gras 
949a06e2ab3SBen Gras 	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
950a06e2ab3SBen Gras 	    requested_transition == catatonia) {
951a06e2ab3SBen Gras 		/* /etc/rc executed /sbin/reboot; wait for the end quietly */
952a06e2ab3SBen Gras 		sigset_t s;
953a06e2ab3SBen Gras 
954a06e2ab3SBen Gras 		(void)sigfillset(&s);
955a06e2ab3SBen Gras 		for (;;)
956a06e2ab3SBen Gras 			(void)sigsuspend(&s);
957a06e2ab3SBen Gras 	}
958a06e2ab3SBen Gras 
959a06e2ab3SBen Gras 	if (!WIFEXITED(status)) {
960a06e2ab3SBen Gras 		warning("`%s' on `%s' terminated abnormally, going to "
961a06e2ab3SBen Gras 		    "single user mode", INIT_BSHELL, _PATH_RUNCOM);
962a06e2ab3SBen Gras 		return (state_func_t)single_user;
963a06e2ab3SBen Gras 	}
964a06e2ab3SBen Gras 
965a06e2ab3SBen Gras 	if (WEXITSTATUS(status))
966a06e2ab3SBen Gras 		return (state_func_t)single_user;
967a06e2ab3SBen Gras 
968a06e2ab3SBen Gras 	return (state_func_t)read_ttys;
969a06e2ab3SBen Gras }
970a06e2ab3SBen Gras 
971a06e2ab3SBen Gras /*
972a06e2ab3SBen Gras  * Run the system startup script.
973a06e2ab3SBen Gras  */
974a06e2ab3SBen Gras static state_func_t
runcom(void)975a06e2ab3SBen Gras runcom(void)
976a06e2ab3SBen Gras {
977a06e2ab3SBen Gras 	state_func_t next_step;
978a06e2ab3SBen Gras 
979a06e2ab3SBen Gras 	/* Run /etc/rc and choose next state depending on the result. */
980a06e2ab3SBen Gras 	next_step = runetcrc(0);
981a06e2ab3SBen Gras 	if (next_step != (state_func_t)read_ttys)
982a06e2ab3SBen Gras 		return (state_func_t)next_step;
983a06e2ab3SBen Gras 
984a06e2ab3SBen Gras #ifdef CHROOT
985a06e2ab3SBen Gras 	/*
986a06e2ab3SBen Gras 	 * If init.root sysctl does not point to "/", we'll chroot and run
987a06e2ab3SBen Gras 	 * The Real(tm) /etc/rc now.  Global variable rootdir will tell us
988a06e2ab3SBen Gras 	 * where to go.
989a06e2ab3SBen Gras 	 */
990a06e2ab3SBen Gras 	if (shouldchroot()) {
991a06e2ab3SBen Gras 		next_step = runetcrc(1);
992a06e2ab3SBen Gras 		if (next_step != (state_func_t)read_ttys)
993a06e2ab3SBen Gras 			return (state_func_t)next_step;
994a06e2ab3SBen Gras 
995a06e2ab3SBen Gras 		did_multiuser_chroot = 1;
996a06e2ab3SBen Gras 	} else {
997a06e2ab3SBen Gras 		did_multiuser_chroot = 0;
998a06e2ab3SBen Gras 	}
999a06e2ab3SBen Gras #endif /* CHROOT */
1000a06e2ab3SBen Gras 
1001a06e2ab3SBen Gras 	/*
1002a06e2ab3SBen Gras 	 * Regardless of whether in chroot or not, we booted successfuly.
1003a06e2ab3SBen Gras 	 * It's time to spawn gettys (ie. next_step's value at this point).
1004a06e2ab3SBen Gras 	 */
1005a06e2ab3SBen Gras 	runcom_mode = AUTOBOOT;		/* the default */
1006a06e2ab3SBen Gras 	/* NB: should send a message to the session logger to avoid blocking. */
1007a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
1008a06e2ab3SBen Gras 	logwtmpx("~", "reboot", "", 0, INIT_PROCESS);
1009a06e2ab3SBen Gras #endif
1010a06e2ab3SBen Gras #ifdef SUPPORT_UTMP
1011a06e2ab3SBen Gras 	logwtmp("~", "reboot", "");
1012a06e2ab3SBen Gras #endif
1013a06e2ab3SBen Gras 	return (state_func_t)read_ttys;
1014a06e2ab3SBen Gras }
1015a06e2ab3SBen Gras 
1016a06e2ab3SBen Gras /*
1017a06e2ab3SBen Gras  * Open the session database.
1018a06e2ab3SBen Gras  *
1019a06e2ab3SBen Gras  * NB: We could pass in the size here; is it necessary?
1020a06e2ab3SBen Gras  */
1021a06e2ab3SBen Gras static int
start_session_db(void)1022a06e2ab3SBen Gras start_session_db(void)
1023a06e2ab3SBen Gras {
1024a06e2ab3SBen Gras 
1025a06e2ab3SBen Gras 	if (session_db && (*session_db->close)(session_db))
1026a06e2ab3SBen Gras 		emergency("session database close: %m");
1027a06e2ab3SBen Gras 	if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
1028a06e2ab3SBen Gras 		emergency("session database open: %m");
1029a06e2ab3SBen Gras 		return 1;
1030a06e2ab3SBen Gras 	}
1031a06e2ab3SBen Gras 	return 0;
1032a06e2ab3SBen Gras 
1033a06e2ab3SBen Gras }
1034a06e2ab3SBen Gras 
1035a06e2ab3SBen Gras /*
1036a06e2ab3SBen Gras  * Add a new login session.
1037a06e2ab3SBen Gras  */
1038a06e2ab3SBen Gras static void
add_session(session_t * sp)1039a06e2ab3SBen Gras add_session(session_t *sp)
1040a06e2ab3SBen Gras {
1041a06e2ab3SBen Gras 	DBT key;
1042a06e2ab3SBen Gras 	DBT data;
1043a06e2ab3SBen Gras 
1044a06e2ab3SBen Gras 	if (session_db == NULL)
1045a06e2ab3SBen Gras 		return;
1046a06e2ab3SBen Gras 
1047a06e2ab3SBen Gras 	key.data = &sp->se_process;
1048a06e2ab3SBen Gras 	key.size = sizeof sp->se_process;
1049a06e2ab3SBen Gras 	data.data = &sp;
1050a06e2ab3SBen Gras 	data.size = sizeof sp;
1051a06e2ab3SBen Gras 
1052a06e2ab3SBen Gras 	if ((*session_db->put)(session_db, &key, &data, 0))
1053a06e2ab3SBen Gras 		emergency("insert %d: %m", sp->se_process);
1054a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
1055a06e2ab3SBen Gras 	session_utmpx(sp, 1);
1056a06e2ab3SBen Gras #endif
1057a06e2ab3SBen Gras }
1058a06e2ab3SBen Gras 
1059a06e2ab3SBen Gras /*
1060a06e2ab3SBen Gras  * Delete an old login session.
1061a06e2ab3SBen Gras  */
1062a06e2ab3SBen Gras static void
del_session(session_t * sp)1063a06e2ab3SBen Gras del_session(session_t *sp)
1064a06e2ab3SBen Gras {
1065a06e2ab3SBen Gras 	DBT key;
1066a06e2ab3SBen Gras 
1067a06e2ab3SBen Gras 	key.data = &sp->se_process;
1068a06e2ab3SBen Gras 	key.size = sizeof sp->se_process;
1069a06e2ab3SBen Gras 
1070a06e2ab3SBen Gras 	if ((*session_db->del)(session_db, &key, 0))
1071a06e2ab3SBen Gras 		emergency("delete %d: %m", sp->se_process);
1072a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
1073a06e2ab3SBen Gras 	session_utmpx(sp, 0);
1074a06e2ab3SBen Gras #endif
1075a06e2ab3SBen Gras }
1076a06e2ab3SBen Gras 
1077a06e2ab3SBen Gras /*
1078a06e2ab3SBen Gras  * Look up a login session by pid.
1079a06e2ab3SBen Gras  */
1080a06e2ab3SBen Gras static session_t *
find_session(pid_t pid)1081a06e2ab3SBen Gras find_session(pid_t pid)
1082a06e2ab3SBen Gras {
1083a06e2ab3SBen Gras 	DBT key;
1084a06e2ab3SBen Gras 	DBT data;
1085a06e2ab3SBen Gras 	session_t *ret;
1086a06e2ab3SBen Gras 
1087a06e2ab3SBen Gras 	if (session_db == NULL)
1088a06e2ab3SBen Gras 		return NULL;
1089a06e2ab3SBen Gras 
1090a06e2ab3SBen Gras 	key.data = &pid;
1091a06e2ab3SBen Gras 	key.size = sizeof pid;
1092a06e2ab3SBen Gras 	if ((*session_db->get)(session_db, &key, &data, 0) != 0)
1093a06e2ab3SBen Gras 		return 0;
1094a06e2ab3SBen Gras 	(void)memmove(&ret, data.data, sizeof(ret));
1095a06e2ab3SBen Gras 	return ret;
1096a06e2ab3SBen Gras }
1097a06e2ab3SBen Gras 
1098a06e2ab3SBen Gras /*
1099a06e2ab3SBen Gras  * Construct an argument vector from a command line.
1100a06e2ab3SBen Gras  */
1101a06e2ab3SBen Gras static char **
construct_argv(char * command)1102a06e2ab3SBen Gras construct_argv(char *command)
1103a06e2ab3SBen Gras {
1104a06e2ab3SBen Gras 	int argc = 0;
1105a06e2ab3SBen Gras 	char **argv = malloc(((strlen(command) + 1) / 2 + 1) * sizeof (char *));
1106a06e2ab3SBen Gras 	static const char separators[] = " \t";
1107a06e2ab3SBen Gras 
1108a06e2ab3SBen Gras 	if (argv == NULL)
1109a06e2ab3SBen Gras 		return NULL;
1110a06e2ab3SBen Gras 
1111a06e2ab3SBen Gras 	if ((argv[argc++] = strtok(command, separators)) == 0) {
1112a06e2ab3SBen Gras 		free(argv);
1113a06e2ab3SBen Gras 		return NULL;
1114a06e2ab3SBen Gras 	}
1115a06e2ab3SBen Gras 	while ((argv[argc++] = strtok(NULL, separators)) != NULL)
1116a06e2ab3SBen Gras 		continue;
1117a06e2ab3SBen Gras 	return argv;
1118a06e2ab3SBen Gras }
1119a06e2ab3SBen Gras 
1120a06e2ab3SBen Gras /*
1121a06e2ab3SBen Gras  * Deallocate a session descriptor.
1122a06e2ab3SBen Gras  */
1123a06e2ab3SBen Gras static void
free_session(session_t * sp)1124a06e2ab3SBen Gras free_session(session_t *sp)
1125a06e2ab3SBen Gras {
1126a06e2ab3SBen Gras 
1127a06e2ab3SBen Gras 	free(sp->se_device);
1128a06e2ab3SBen Gras 	if (sp->se_getty) {
1129a06e2ab3SBen Gras 		free(sp->se_getty);
1130a06e2ab3SBen Gras 		free(sp->se_getty_argv);
1131a06e2ab3SBen Gras 	}
1132a06e2ab3SBen Gras 	if (sp->se_window) {
1133a06e2ab3SBen Gras 		free(sp->se_window);
1134a06e2ab3SBen Gras 		free(sp->se_window_argv);
1135a06e2ab3SBen Gras 	}
1136a06e2ab3SBen Gras 	free(sp);
1137a06e2ab3SBen Gras }
1138a06e2ab3SBen Gras 
1139a06e2ab3SBen Gras /*
1140a06e2ab3SBen Gras  * Allocate a new session descriptor.
1141a06e2ab3SBen Gras  */
1142a06e2ab3SBen Gras static session_t *
new_session(session_t * sprev,int session_index,struct ttyent * typ)1143a06e2ab3SBen Gras new_session(session_t *sprev, int session_index, struct ttyent *typ)
1144a06e2ab3SBen Gras {
1145a06e2ab3SBen Gras 	session_t *sp;
1146a06e2ab3SBen Gras 
1147a06e2ab3SBen Gras 	if ((typ->ty_status & TTY_ON) == 0 || typ->ty_name == NULL ||
1148a06e2ab3SBen Gras 	    typ->ty_getty == NULL)
1149a06e2ab3SBen Gras 		return NULL;
1150a06e2ab3SBen Gras 
1151a06e2ab3SBen Gras 	sp = malloc(sizeof (session_t));
1152a06e2ab3SBen Gras 	if (sp == NULL)
1153a06e2ab3SBen Gras 		return NULL;
1154a06e2ab3SBen Gras 	(void)memset(sp, 0, sizeof *sp);
1155a06e2ab3SBen Gras 
1156a06e2ab3SBen Gras 	sp->se_flags = SE_PRESENT;
1157a06e2ab3SBen Gras 	sp->se_index = session_index;
1158a06e2ab3SBen Gras 
1159a06e2ab3SBen Gras 	(void)asprintf(&sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
116084d9c625SLionel Sambuc 	if (!sp->se_device) {
116184d9c625SLionel Sambuc 		free(sp);
1162a06e2ab3SBen Gras 		return NULL;
116384d9c625SLionel Sambuc 	}
1164a06e2ab3SBen Gras 
1165a06e2ab3SBen Gras 	if (setupargv(sp, typ) == 0) {
1166a06e2ab3SBen Gras 		free_session(sp);
1167a06e2ab3SBen Gras 		return NULL;
1168a06e2ab3SBen Gras 	}
1169a06e2ab3SBen Gras 
1170a06e2ab3SBen Gras 	sp->se_next = NULL;
1171a06e2ab3SBen Gras 	if (sprev == NULL) {
1172a06e2ab3SBen Gras 		sessions = sp;
1173a06e2ab3SBen Gras 		sp->se_prev = NULL;
1174a06e2ab3SBen Gras 	} else {
1175a06e2ab3SBen Gras 		sprev->se_next = sp;
1176a06e2ab3SBen Gras 		sp->se_prev = sprev;
1177a06e2ab3SBen Gras 	}
1178a06e2ab3SBen Gras 
1179a06e2ab3SBen Gras 	return sp;
1180a06e2ab3SBen Gras }
1181a06e2ab3SBen Gras 
1182a06e2ab3SBen Gras /*
1183a06e2ab3SBen Gras  * Calculate getty and if useful window argv vectors.
1184a06e2ab3SBen Gras  */
1185a06e2ab3SBen Gras static int
setupargv(session_t * sp,struct ttyent * typ)1186a06e2ab3SBen Gras setupargv(session_t *sp, struct ttyent *typ)
1187a06e2ab3SBen Gras {
1188a06e2ab3SBen Gras 
1189a06e2ab3SBen Gras 	if (sp->se_getty) {
1190a06e2ab3SBen Gras 		free(sp->se_getty);
1191a06e2ab3SBen Gras 		free(sp->se_getty_argv);
1192a06e2ab3SBen Gras 	}
1193a06e2ab3SBen Gras 	(void)asprintf(&sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
1194a06e2ab3SBen Gras 	if (!sp->se_getty)
1195a06e2ab3SBen Gras 		return 0;
1196a06e2ab3SBen Gras 	sp->se_getty_argv = construct_argv(sp->se_getty);
1197a06e2ab3SBen Gras 	if (sp->se_getty_argv == NULL) {
1198a06e2ab3SBen Gras 		warning("can't parse getty for port `%s'", sp->se_device);
1199a06e2ab3SBen Gras 		free(sp->se_getty);
1200a06e2ab3SBen Gras 		sp->se_getty = NULL;
1201a06e2ab3SBen Gras 		return 0;
1202a06e2ab3SBen Gras 	}
1203a06e2ab3SBen Gras 	if (typ->ty_window) {
1204a06e2ab3SBen Gras 		if (sp->se_window)
1205a06e2ab3SBen Gras 			free(sp->se_window);
1206a06e2ab3SBen Gras 		sp->se_window = strdup(typ->ty_window);
1207a06e2ab3SBen Gras 		sp->se_window_argv = construct_argv(sp->se_window);
1208a06e2ab3SBen Gras 		if (sp->se_window_argv == NULL) {
1209a06e2ab3SBen Gras 			warning("can't parse window for port `%s'",
1210a06e2ab3SBen Gras 			    sp->se_device);
1211a06e2ab3SBen Gras 			free(sp->se_window);
1212a06e2ab3SBen Gras 			sp->se_window = NULL;
1213a06e2ab3SBen Gras 			return 0;
1214a06e2ab3SBen Gras 		}
1215a06e2ab3SBen Gras 	}
1216a06e2ab3SBen Gras 	return 1;
1217a06e2ab3SBen Gras }
1218a06e2ab3SBen Gras 
1219a06e2ab3SBen Gras /*
1220a06e2ab3SBen Gras  * Walk the list of ttys and create sessions for each active line.
1221a06e2ab3SBen Gras  */
1222a06e2ab3SBen Gras static state_func_t
read_ttys(void)1223a06e2ab3SBen Gras read_ttys(void)
1224a06e2ab3SBen Gras {
1225a06e2ab3SBen Gras 	int session_index = 0;
1226a06e2ab3SBen Gras 	session_t *sp, *snext;
1227a06e2ab3SBen Gras 	struct ttyent *typ;
1228a06e2ab3SBen Gras 
1229a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
1230a06e2ab3SBen Gras 	if (sessions == NULL) {
1231a06e2ab3SBen Gras 		struct stat st;
1232a06e2ab3SBen Gras 
1233a06e2ab3SBen Gras 		make_utmpx("", BOOT_MSG, BOOT_TIME, 0, &boot_time, 0);
1234a06e2ab3SBen Gras 
1235a06e2ab3SBen Gras 		/*
1236a06e2ab3SBen Gras 		 * If wtmpx is not empty, pick the down time from there
1237a06e2ab3SBen Gras 		 */
1238a06e2ab3SBen Gras 		if (stat(_PATH_WTMPX, &st) != -1 && st.st_size != 0) {
1239a06e2ab3SBen Gras 			struct timeval down_time;
1240a06e2ab3SBen Gras 
1241a06e2ab3SBen Gras 			TIMESPEC_TO_TIMEVAL(&down_time,
1242a06e2ab3SBen Gras 			    st.st_atime > st.st_mtime ?
1243a06e2ab3SBen Gras 			    &st.st_atimespec : &st.st_mtimespec);
1244a06e2ab3SBen Gras 			make_utmpx("", DOWN_MSG, DOWN_TIME, 0, &down_time, 0);
1245a06e2ab3SBen Gras 		}
1246a06e2ab3SBen Gras 	}
1247a06e2ab3SBen Gras #endif
1248a06e2ab3SBen Gras 	/*
1249a06e2ab3SBen Gras 	 * Destroy any previous session state.
1250a06e2ab3SBen Gras 	 * There shouldn't be any, but just in case...
1251a06e2ab3SBen Gras 	 */
1252a06e2ab3SBen Gras 	for (sp = sessions; sp; sp = snext) {
1253a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
1254a06e2ab3SBen Gras 		if (sp->se_process)
1255a06e2ab3SBen Gras 			clear_session_logs(sp, 0);
1256a06e2ab3SBen Gras #endif
1257a06e2ab3SBen Gras 		snext = sp->se_next;
1258a06e2ab3SBen Gras 		free_session(sp);
1259a06e2ab3SBen Gras 	}
1260a06e2ab3SBen Gras 	sessions = NULL;
1261a06e2ab3SBen Gras 
1262a06e2ab3SBen Gras 	if (start_session_db()) {
1263a06e2ab3SBen Gras 		warning("start_session_db failed, death");
1264a06e2ab3SBen Gras #ifdef CHROOT
1265a06e2ab3SBen Gras 		/* If /etc/rc ran in chroot, we want to kill any survivors. */
1266a06e2ab3SBen Gras 		if (did_multiuser_chroot)
1267a06e2ab3SBen Gras 			return (state_func_t)death;
1268a06e2ab3SBen Gras 		else
1269a06e2ab3SBen Gras #endif /* CHROOT */
1270a06e2ab3SBen Gras 			return (state_func_t)single_user;
1271a06e2ab3SBen Gras 	}
1272a06e2ab3SBen Gras 
1273a06e2ab3SBen Gras 	(void)do_setttyent();
1274a06e2ab3SBen Gras 
1275a06e2ab3SBen Gras 	/*
1276a06e2ab3SBen Gras 	 * Allocate a session entry for each active port.
1277a06e2ab3SBen Gras 	 * Note that sp starts at 0.
1278a06e2ab3SBen Gras 	 */
1279a06e2ab3SBen Gras 	while ((typ = getttyent()) != NULL)
1280a06e2ab3SBen Gras 		if ((snext = new_session(sp, ++session_index, typ)) != NULL)
1281a06e2ab3SBen Gras 			sp = snext;
1282a06e2ab3SBen Gras 	(void)endttyent();
1283a06e2ab3SBen Gras 
1284a06e2ab3SBen Gras 	return (state_func_t)multi_user;
1285a06e2ab3SBen Gras }
1286a06e2ab3SBen Gras 
1287a06e2ab3SBen Gras /*
1288a06e2ab3SBen Gras  * Start a window system running.
1289a06e2ab3SBen Gras  */
1290a06e2ab3SBen Gras static void
start_window_system(session_t * sp)1291a06e2ab3SBen Gras start_window_system(session_t *sp)
1292a06e2ab3SBen Gras {
1293a06e2ab3SBen Gras 	pid_t pid;
1294a06e2ab3SBen Gras 	sigset_t mask;
1295a06e2ab3SBen Gras 
1296a06e2ab3SBen Gras 	if ((pid = fork()) == -1) {
1297a06e2ab3SBen Gras 		emergency("can't fork for window system on port `%s': %m",
1298a06e2ab3SBen Gras 		    sp->se_device);
1299a06e2ab3SBen Gras 		/* hope that getty fails and we can try again */
1300a06e2ab3SBen Gras 		return;
1301a06e2ab3SBen Gras 	}
1302a06e2ab3SBen Gras 
1303a06e2ab3SBen Gras 	if (pid)
1304a06e2ab3SBen Gras 		return;
1305a06e2ab3SBen Gras 
1306a06e2ab3SBen Gras 	(void)sigemptyset(&mask);
1307a06e2ab3SBen Gras 	(void)sigprocmask(SIG_SETMASK, &mask, NULL);
1308a06e2ab3SBen Gras 
1309a06e2ab3SBen Gras 	if (setsid() < 0)
1310a06e2ab3SBen Gras 		emergency("setsid failed (window): %m");
1311a06e2ab3SBen Gras 
1312a06e2ab3SBen Gras 	(void)execv(sp->se_window_argv[0], sp->se_window_argv);
1313a06e2ab3SBen Gras 	stall("can't exec window system `%s' for port `%s': %m",
1314a06e2ab3SBen Gras 	    sp->se_window_argv[0], sp->se_device);
1315a06e2ab3SBen Gras 	_exit(6);
1316a06e2ab3SBen Gras }
1317a06e2ab3SBen Gras 
1318a06e2ab3SBen Gras /*
1319a06e2ab3SBen Gras  * Start a login session running.
1320a06e2ab3SBen Gras  */
1321a06e2ab3SBen Gras static pid_t
start_getty(session_t * sp)1322a06e2ab3SBen Gras start_getty(session_t *sp)
1323a06e2ab3SBen Gras {
1324a06e2ab3SBen Gras 	pid_t pid;
1325a06e2ab3SBen Gras 	sigset_t mask;
1326a06e2ab3SBen Gras 	time_t current_time = time(NULL);
1327a06e2ab3SBen Gras 
1328a06e2ab3SBen Gras 	/*
1329a06e2ab3SBen Gras 	 * fork(), not vfork() -- we can't afford to block.
1330a06e2ab3SBen Gras 	 */
1331a06e2ab3SBen Gras 	if ((pid = fork()) == -1) {
1332a06e2ab3SBen Gras 		emergency("can't fork for getty on port `%s': %m",
1333a06e2ab3SBen Gras 		    sp->se_device);
1334a06e2ab3SBen Gras 		return -1;
1335a06e2ab3SBen Gras 	}
1336a06e2ab3SBen Gras 
1337a06e2ab3SBen Gras 	if (pid)
1338a06e2ab3SBen Gras 		return pid;
1339a06e2ab3SBen Gras 
1340a06e2ab3SBen Gras #ifdef CHROOT
1341a06e2ab3SBen Gras 	/* If /etc/rc did proceed inside chroot, we have to try as well. */
1342a06e2ab3SBen Gras 	if (did_multiuser_chroot)
1343a06e2ab3SBen Gras 		if (chroot(rootdir) != 0) {
1344a06e2ab3SBen Gras 			stall("can't chroot getty `%s' inside `%s': %m",
1345a06e2ab3SBen Gras 			    sp->se_getty_argv[0], rootdir);
1346a06e2ab3SBen Gras 			_exit(7);
1347a06e2ab3SBen Gras 		}
1348a06e2ab3SBen Gras #endif /* CHROOT */
1349a06e2ab3SBen Gras 
1350a06e2ab3SBen Gras 	if (current_time > sp->se_started.tv_sec &&
1351a06e2ab3SBen Gras 	    current_time - sp->se_started.tv_sec < GETTY_SPACING) {
1352a06e2ab3SBen Gras 		warning("getty repeating too quickly on port `%s', sleeping",
1353a06e2ab3SBen Gras 		    sp->se_device);
1354a06e2ab3SBen Gras 		(void)sleep(GETTY_SLEEP);
1355a06e2ab3SBen Gras 	}
1356a06e2ab3SBen Gras 
1357a06e2ab3SBen Gras 	if (sp->se_window) {
1358a06e2ab3SBen Gras 		start_window_system(sp);
1359a06e2ab3SBen Gras 		(void)sleep(WINDOW_WAIT);
1360a06e2ab3SBen Gras 	}
1361a06e2ab3SBen Gras 
1362a06e2ab3SBen Gras 	(void)sigemptyset(&mask);
1363a06e2ab3SBen Gras 	(void)sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
1364a06e2ab3SBen Gras 
1365a06e2ab3SBen Gras 	(void)execv(sp->se_getty_argv[0], sp->se_getty_argv);
1366a06e2ab3SBen Gras 	stall("can't exec getty `%s' for port `%s': %m",
1367a06e2ab3SBen Gras 	    sp->se_getty_argv[0], sp->se_device);
1368a06e2ab3SBen Gras 	_exit(8);
1369a06e2ab3SBen Gras 	/*NOTREACHED*/
1370a06e2ab3SBen Gras }
1371a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
1372a06e2ab3SBen Gras static void
session_utmpx(const session_t * sp,int add)1373a06e2ab3SBen Gras session_utmpx(const session_t *sp, int add)
1374a06e2ab3SBen Gras {
1375a06e2ab3SBen Gras 	const char *name = sp->se_getty ? sp->se_getty :
1376a06e2ab3SBen Gras 	    (sp->se_window ? sp->se_window : "");
1377a06e2ab3SBen Gras 	const char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
1378a06e2ab3SBen Gras 
1379a06e2ab3SBen Gras 	make_utmpx(name, line, add ? LOGIN_PROCESS : DEAD_PROCESS,
1380a06e2ab3SBen Gras 	    sp->se_process, &sp->se_started, sp->se_index);
1381a06e2ab3SBen Gras }
1382a06e2ab3SBen Gras 
1383a06e2ab3SBen Gras static void
make_utmpx(const char * name,const char * line,int type,pid_t pid,const struct timeval * tv,int session)1384a06e2ab3SBen Gras make_utmpx(const char *name, const char *line, int type, pid_t pid,
1385a06e2ab3SBen Gras     const struct timeval *tv, int session)
1386a06e2ab3SBen Gras {
1387a06e2ab3SBen Gras 	struct utmpx ut;
1388a06e2ab3SBen Gras 	const char *eline;
1389a06e2ab3SBen Gras 
1390a06e2ab3SBen Gras 	(void)memset(&ut, 0, sizeof(ut));
1391a06e2ab3SBen Gras 	(void)strlcpy(ut.ut_name, name, sizeof(ut.ut_name));
1392a06e2ab3SBen Gras 	ut.ut_type = type;
1393a06e2ab3SBen Gras 	(void)strlcpy(ut.ut_line, line, sizeof(ut.ut_line));
1394a06e2ab3SBen Gras 	ut.ut_pid = pid;
1395a06e2ab3SBen Gras 	if (tv)
1396a06e2ab3SBen Gras 		ut.ut_tv = *tv;
1397a06e2ab3SBen Gras 	else
1398a06e2ab3SBen Gras 		(void)gettimeofday(&ut.ut_tv, NULL);
1399a06e2ab3SBen Gras 	ut.ut_session = session;
1400a06e2ab3SBen Gras 
1401a06e2ab3SBen Gras 	eline = line + strlen(line);
1402a06e2ab3SBen Gras 	if ((size_t)(eline - line) >= sizeof(ut.ut_id))
1403a06e2ab3SBen Gras 		line = eline - sizeof(ut.ut_id);
1404a06e2ab3SBen Gras 	(void)strncpy(ut.ut_id, line, sizeof(ut.ut_id));
1405a06e2ab3SBen Gras 
1406a06e2ab3SBen Gras 	if (pututxline(&ut) == NULL)
1407a06e2ab3SBen Gras 		warning("can't add utmpx record for `%s': %m", ut.ut_line);
1408a06e2ab3SBen Gras 	endutxent();
1409a06e2ab3SBen Gras }
1410a06e2ab3SBen Gras 
1411a06e2ab3SBen Gras static char
get_runlevel(const state_t s)1412a06e2ab3SBen Gras get_runlevel(const state_t s)
1413a06e2ab3SBen Gras {
1414a06e2ab3SBen Gras 	if (s == (state_t)single_user)
1415a06e2ab3SBen Gras 		return SINGLE_USER;
1416a06e2ab3SBen Gras 	if (s == (state_t)runcom)
1417a06e2ab3SBen Gras 		return RUNCOM;
1418a06e2ab3SBen Gras 	if (s == (state_t)read_ttys)
1419a06e2ab3SBen Gras 		return READ_TTYS;
1420a06e2ab3SBen Gras 	if (s == (state_t)multi_user)
1421a06e2ab3SBen Gras 		return MULTI_USER;
1422a06e2ab3SBen Gras 	if (s == (state_t)clean_ttys)
1423a06e2ab3SBen Gras 		return CLEAN_TTYS;
1424a06e2ab3SBen Gras 	if (s == (state_t)catatonia)
1425a06e2ab3SBen Gras 		return CATATONIA;
1426a06e2ab3SBen Gras 	return DEATH;
1427a06e2ab3SBen Gras }
1428a06e2ab3SBen Gras 
1429a06e2ab3SBen Gras static void
utmpx_set_runlevel(char old,char new)1430a06e2ab3SBen Gras utmpx_set_runlevel(char old, char new)
1431a06e2ab3SBen Gras {
1432a06e2ab3SBen Gras 	struct utmpx ut;
1433a06e2ab3SBen Gras 
1434a06e2ab3SBen Gras 	/*
1435a06e2ab3SBen Gras 	 * Don't record any transitions until we did the first transition
1436a06e2ab3SBen Gras 	 * to read ttys, which is when we are guaranteed to have a read-write
1437a06e2ab3SBen Gras 	 * /var. Perhaps use a different variable for this?
1438a06e2ab3SBen Gras 	 */
1439a06e2ab3SBen Gras 	if (sessions == NULL)
1440a06e2ab3SBen Gras 		return;
1441a06e2ab3SBen Gras 
1442a06e2ab3SBen Gras 	(void)memset(&ut, 0, sizeof(ut));
1443a06e2ab3SBen Gras 	(void)snprintf(ut.ut_line, sizeof(ut.ut_line), RUNLVL_MSG, new);
1444a06e2ab3SBen Gras 	ut.ut_type = RUN_LVL;
1445a06e2ab3SBen Gras 	(void)gettimeofday(&ut.ut_tv, NULL);
1446a06e2ab3SBen Gras 	ut.ut_exit.e_exit = old;
1447a06e2ab3SBen Gras 	ut.ut_exit.e_termination = new;
1448a06e2ab3SBen Gras 	if (pututxline(&ut) == NULL)
1449a06e2ab3SBen Gras 		warning("can't add utmpx record for `runlevel': %m");
1450a06e2ab3SBen Gras 	endutxent();
1451a06e2ab3SBen Gras }
1452a06e2ab3SBen Gras #endif /* SUPPORT_UTMPX */
1453a06e2ab3SBen Gras 
1454a06e2ab3SBen Gras #endif /* LETS_GET_SMALL */
1455a06e2ab3SBen Gras 
1456a06e2ab3SBen Gras /*
1457a06e2ab3SBen Gras  * Collect exit status for a child.
1458a06e2ab3SBen Gras  * If an exiting login, start a new login running.
1459a06e2ab3SBen Gras  */
1460a06e2ab3SBen Gras static void
collect_child(pid_t pid,int status)1461a06e2ab3SBen Gras collect_child(pid_t pid, int status)
1462a06e2ab3SBen Gras {
1463a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
1464a06e2ab3SBen Gras 	session_t *sp, *sprev, *snext;
1465a06e2ab3SBen Gras 
1466a06e2ab3SBen Gras 	if (! sessions)
1467a06e2ab3SBen Gras 		return;
1468a06e2ab3SBen Gras 
1469a06e2ab3SBen Gras 	if ((sp = find_session(pid)) == NULL)
1470a06e2ab3SBen Gras 		return;
1471a06e2ab3SBen Gras 
1472a06e2ab3SBen Gras 	clear_session_logs(sp, status);
1473a06e2ab3SBen Gras 	del_session(sp);
1474a06e2ab3SBen Gras 	sp->se_process = 0;
1475a06e2ab3SBen Gras 
1476a06e2ab3SBen Gras 	if (sp->se_flags & SE_SHUTDOWN) {
1477a06e2ab3SBen Gras 		if ((sprev = sp->se_prev) != NULL)
1478a06e2ab3SBen Gras 			sprev->se_next = sp->se_next;
1479a06e2ab3SBen Gras 		else
1480a06e2ab3SBen Gras 			sessions = sp->se_next;
1481a06e2ab3SBen Gras 		if ((snext = sp->se_next) != NULL)
1482a06e2ab3SBen Gras 			snext->se_prev = sp->se_prev;
1483a06e2ab3SBen Gras 		free_session(sp);
1484a06e2ab3SBen Gras 		return;
1485a06e2ab3SBen Gras 	}
1486a06e2ab3SBen Gras 
1487a06e2ab3SBen Gras 	if ((pid = start_getty(sp)) == -1) {
1488a06e2ab3SBen Gras 		/* serious trouble */
1489a06e2ab3SBen Gras 		requested_transition = clean_ttys;
1490a06e2ab3SBen Gras 		return;
1491a06e2ab3SBen Gras 	}
1492a06e2ab3SBen Gras 
1493a06e2ab3SBen Gras 	sp->se_process = pid;
1494a06e2ab3SBen Gras 	(void)gettimeofday(&sp->se_started, NULL);
1495a06e2ab3SBen Gras 	add_session(sp);
1496a06e2ab3SBen Gras #endif /* LETS_GET_SMALL */
1497a06e2ab3SBen Gras }
1498a06e2ab3SBen Gras 
1499a06e2ab3SBen Gras /*
1500a06e2ab3SBen Gras  * Catch a signal and request a state transition.
1501a06e2ab3SBen Gras  */
1502a06e2ab3SBen Gras static void
transition_handler(int sig)1503a06e2ab3SBen Gras transition_handler(int sig)
1504a06e2ab3SBen Gras {
1505a06e2ab3SBen Gras 
1506a06e2ab3SBen Gras 	switch (sig) {
1507a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
1508a06e2ab3SBen Gras 	case SIGHUP:
1509a06e2ab3SBen Gras 		requested_transition = clean_ttys;
1510a06e2ab3SBen Gras 		break;
1511a06e2ab3SBen Gras 	case SIGTERM:
1512a06e2ab3SBen Gras 		requested_transition = death;
1513a06e2ab3SBen Gras 		break;
1514a06e2ab3SBen Gras 	case SIGTSTP:
1515a06e2ab3SBen Gras 		requested_transition = catatonia;
1516a06e2ab3SBen Gras 		break;
1517a06e2ab3SBen Gras #endif /* LETS_GET_SMALL */
1518a06e2ab3SBen Gras 	default:
1519a06e2ab3SBen Gras 		requested_transition = 0;
1520a06e2ab3SBen Gras 		break;
1521a06e2ab3SBen Gras 	}
1522a06e2ab3SBen Gras }
1523a06e2ab3SBen Gras 
1524a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
1525a06e2ab3SBen Gras /*
1526a06e2ab3SBen Gras  * Take the system multiuser.
1527a06e2ab3SBen Gras  */
1528a06e2ab3SBen Gras static state_func_t
multi_user(void)1529a06e2ab3SBen Gras multi_user(void)
1530a06e2ab3SBen Gras {
1531a06e2ab3SBen Gras 	pid_t pid;
1532a06e2ab3SBen Gras 	int status;
1533a06e2ab3SBen Gras 	session_t *sp;
1534a06e2ab3SBen Gras 
1535a06e2ab3SBen Gras 	requested_transition = 0;
1536a06e2ab3SBen Gras 
1537a06e2ab3SBen Gras 	/*
1538a06e2ab3SBen Gras 	 * If the administrator has not set the security level to -1
1539a06e2ab3SBen Gras 	 * to indicate that the kernel should not run multiuser in secure
1540a06e2ab3SBen Gras 	 * mode, and the run script has not set a higher level of security
1541a06e2ab3SBen Gras 	 * than level 1, then put the kernel into secure mode.
1542a06e2ab3SBen Gras 	 */
1543a06e2ab3SBen Gras 	if (getsecuritylevel() == 0)
1544a06e2ab3SBen Gras 		setsecuritylevel(1);
1545a06e2ab3SBen Gras 
1546a06e2ab3SBen Gras 	for (sp = sessions; sp; sp = sp->se_next) {
1547a06e2ab3SBen Gras 		if (sp->se_process)
1548a06e2ab3SBen Gras 			continue;
1549a06e2ab3SBen Gras 		if ((pid = start_getty(sp)) == -1) {
1550a06e2ab3SBen Gras 			/* serious trouble */
1551a06e2ab3SBen Gras 			requested_transition = clean_ttys;
1552a06e2ab3SBen Gras 			break;
1553a06e2ab3SBen Gras 		}
1554a06e2ab3SBen Gras 		sp->se_process = pid;
1555a06e2ab3SBen Gras 		(void)gettimeofday(&sp->se_started, NULL);
1556a06e2ab3SBen Gras 		add_session(sp);
1557a06e2ab3SBen Gras 	}
1558a06e2ab3SBen Gras 
1559a06e2ab3SBen Gras 	while (!requested_transition)
1560a06e2ab3SBen Gras 		if ((pid = waitpid(-1, &status, 0)) != -1)
1561a06e2ab3SBen Gras 			collect_child(pid, status);
1562a06e2ab3SBen Gras 
1563a06e2ab3SBen Gras 	return (state_func_t)requested_transition;
1564a06e2ab3SBen Gras }
1565a06e2ab3SBen Gras 
1566a06e2ab3SBen Gras /*
1567a06e2ab3SBen Gras  * This is an n-squared algorithm.  We hope it isn't run often...
1568a06e2ab3SBen Gras  */
1569a06e2ab3SBen Gras static state_func_t
clean_ttys(void)1570a06e2ab3SBen Gras clean_ttys(void)
1571a06e2ab3SBen Gras {
1572a06e2ab3SBen Gras 	session_t *sp, *sprev;
1573a06e2ab3SBen Gras 	struct ttyent *typ;
1574a06e2ab3SBen Gras 	int session_index = 0;
1575a06e2ab3SBen Gras 	int devlen;
1576a06e2ab3SBen Gras 
1577a06e2ab3SBen Gras 	for (sp = sessions; sp; sp = sp->se_next)
1578a06e2ab3SBen Gras 		sp->se_flags &= ~SE_PRESENT;
1579a06e2ab3SBen Gras 
1580a06e2ab3SBen Gras 	(void)do_setttyent();
1581a06e2ab3SBen Gras 
1582a06e2ab3SBen Gras 	devlen = sizeof(_PATH_DEV) - 1;
1583a06e2ab3SBen Gras 	while ((typ = getttyent()) != NULL) {
1584a06e2ab3SBen Gras 		++session_index;
1585a06e2ab3SBen Gras 
1586a06e2ab3SBen Gras 		for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
1587a06e2ab3SBen Gras 			if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
1588a06e2ab3SBen Gras 				break;
1589a06e2ab3SBen Gras 
1590a06e2ab3SBen Gras 		if (sp) {
1591a06e2ab3SBen Gras 			sp->se_flags |= SE_PRESENT;
1592a06e2ab3SBen Gras 			if (sp->se_index != session_index) {
1593a06e2ab3SBen Gras 				warning("port `%s' changed utmp index from "
1594a06e2ab3SBen Gras 				    "%d to %d", sp->se_device, sp->se_index,
1595a06e2ab3SBen Gras 				    session_index);
1596a06e2ab3SBen Gras 				sp->se_index = session_index;
1597a06e2ab3SBen Gras 			}
1598a06e2ab3SBen Gras 			if ((typ->ty_status & TTY_ON) == 0 ||
1599a06e2ab3SBen Gras 			    typ->ty_getty == 0) {
1600a06e2ab3SBen Gras 				sp->se_flags |= SE_SHUTDOWN;
1601a06e2ab3SBen Gras 				if (sp->se_process != 0)
1602a06e2ab3SBen Gras 					(void)kill(sp->se_process, SIGHUP);
1603a06e2ab3SBen Gras 				continue;
1604a06e2ab3SBen Gras 			}
1605a06e2ab3SBen Gras 			sp->se_flags &= ~SE_SHUTDOWN;
1606a06e2ab3SBen Gras 			if (setupargv(sp, typ) == 0) {
1607a06e2ab3SBen Gras 				warning("can't parse getty for port `%s'",
1608a06e2ab3SBen Gras 				    sp->se_device);
1609a06e2ab3SBen Gras 				sp->se_flags |= SE_SHUTDOWN;
1610a06e2ab3SBen Gras 				if (sp->se_process != 0)
1611a06e2ab3SBen Gras 					(void)kill(sp->se_process, SIGHUP);
1612a06e2ab3SBen Gras 			}
1613a06e2ab3SBen Gras 			continue;
1614a06e2ab3SBen Gras 		}
1615a06e2ab3SBen Gras 
1616a06e2ab3SBen Gras 		(void)new_session(sprev, session_index, typ);
1617a06e2ab3SBen Gras 	}
1618a06e2ab3SBen Gras 
1619a06e2ab3SBen Gras 	(void)endttyent();
1620a06e2ab3SBen Gras 
1621a06e2ab3SBen Gras 	for (sp = sessions; sp; sp = sp->se_next)
1622a06e2ab3SBen Gras 		if ((sp->se_flags & SE_PRESENT) == 0) {
1623a06e2ab3SBen Gras 			sp->se_flags |= SE_SHUTDOWN;
1624a06e2ab3SBen Gras 			if (sp->se_process != 0)
1625a06e2ab3SBen Gras 				(void)kill(sp->se_process, SIGHUP);
1626a06e2ab3SBen Gras 		}
1627a06e2ab3SBen Gras 
1628a06e2ab3SBen Gras 	return (state_func_t)multi_user;
1629a06e2ab3SBen Gras }
1630a06e2ab3SBen Gras 
1631a06e2ab3SBen Gras /*
1632a06e2ab3SBen Gras  * Block further logins.
1633a06e2ab3SBen Gras  */
1634a06e2ab3SBen Gras static state_func_t
catatonia(void)1635a06e2ab3SBen Gras catatonia(void)
1636a06e2ab3SBen Gras {
1637a06e2ab3SBen Gras 	session_t *sp;
1638a06e2ab3SBen Gras 
1639a06e2ab3SBen Gras 	for (sp = sessions; sp; sp = sp->se_next)
1640a06e2ab3SBen Gras 		sp->se_flags |= SE_SHUTDOWN;
1641a06e2ab3SBen Gras 
1642a06e2ab3SBen Gras 	return (state_func_t)multi_user;
1643a06e2ab3SBen Gras }
1644a06e2ab3SBen Gras #endif /* LETS_GET_SMALL */
1645a06e2ab3SBen Gras 
1646a06e2ab3SBen Gras /*
1647a06e2ab3SBen Gras  * Note SIGALRM.
1648a06e2ab3SBen Gras  */
1649a06e2ab3SBen Gras static void
1650a06e2ab3SBen Gras /*ARGSUSED*/
alrm_handler(int sig)1651a06e2ab3SBen Gras alrm_handler(int sig)
1652a06e2ab3SBen Gras {
1653a06e2ab3SBen Gras 
1654a06e2ab3SBen Gras 	clang = 1;
1655a06e2ab3SBen Gras }
1656a06e2ab3SBen Gras 
1657a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
1658a06e2ab3SBen Gras /*
1659a06e2ab3SBen Gras  * Bring the system down to single user.
1660a06e2ab3SBen Gras  */
1661a06e2ab3SBen Gras static state_func_t
death(void)1662a06e2ab3SBen Gras death(void)
1663a06e2ab3SBen Gras {
1664a06e2ab3SBen Gras 	session_t *sp;
1665a06e2ab3SBen Gras 	int i, status;
1666a06e2ab3SBen Gras 	pid_t pid;
1667a06e2ab3SBen Gras 	static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
1668a06e2ab3SBen Gras 
1669a06e2ab3SBen Gras 	for (sp = sessions; sp; sp = sp->se_next)
1670a06e2ab3SBen Gras 		sp->se_flags |= SE_SHUTDOWN;
1671a06e2ab3SBen Gras 
1672a06e2ab3SBen Gras 	/* NB: should send a message to the session logger to avoid blocking. */
1673a06e2ab3SBen Gras #ifdef SUPPORT_UTMPX
1674a06e2ab3SBen Gras 	logwtmpx("~", "shutdown", "", 0, INIT_PROCESS);
1675a06e2ab3SBen Gras #endif
1676a06e2ab3SBen Gras #ifdef SUPPORT_UTMP
1677a06e2ab3SBen Gras 	logwtmp("~", "shutdown", "");
1678a06e2ab3SBen Gras #endif
1679a06e2ab3SBen Gras 
1680a06e2ab3SBen Gras 	for (i = 0; i < 3; ++i) {
1681a06e2ab3SBen Gras 		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
1682a06e2ab3SBen Gras 			return (state_func_t)single_user;
1683a06e2ab3SBen Gras 
1684a06e2ab3SBen Gras 		clang = 0;
1685a06e2ab3SBen Gras 		(void)alarm(DEATH_WATCH);
1686a06e2ab3SBen Gras 		do
1687a06e2ab3SBen Gras 			if ((pid = waitpid(-1, &status, 0)) != -1)
1688a06e2ab3SBen Gras 				collect_child(pid, status);
1689a06e2ab3SBen Gras 		while (clang == 0 && errno != ECHILD);
1690a06e2ab3SBen Gras 
1691a06e2ab3SBen Gras 		if (errno == ECHILD)
1692a06e2ab3SBen Gras 			return (state_func_t)single_user;
1693a06e2ab3SBen Gras 	}
1694a06e2ab3SBen Gras 
1695a06e2ab3SBen Gras 	warning("some processes would not die; ps axl advised");
1696a06e2ab3SBen Gras 
1697a06e2ab3SBen Gras 	return (state_func_t)single_user;
1698a06e2ab3SBen Gras }
1699a06e2ab3SBen Gras #endif /* LETS_GET_SMALL */
1700a06e2ab3SBen Gras 
1701a06e2ab3SBen Gras #ifdef MFS_DEV_IF_NO_CONSOLE
1702a06e2ab3SBen Gras 
1703a06e2ab3SBen Gras static int
mfs_dev(void)1704a06e2ab3SBen Gras mfs_dev(void)
1705a06e2ab3SBen Gras {
1706a06e2ab3SBen Gras 	/*
1707a06e2ab3SBen Gras 	 * We cannot print errors so we bail out silently...
1708a06e2ab3SBen Gras 	 */
1709a06e2ab3SBen Gras 	pid_t pid;
1710a06e2ab3SBen Gras 	int status;
1711a06e2ab3SBen Gras 
1712a06e2ab3SBen Gras 	/* If we have /dev/console, assume all is OK  */
1713a06e2ab3SBen Gras 	if (access(_PATH_CONSOLE, F_OK) == 0)
1714a06e2ab3SBen Gras 		return 0;
1715a06e2ab3SBen Gras 
1716a06e2ab3SBen Gras #if 0	/* Useful for testing MAKEDEV */
1717a06e2ab3SBen Gras 	/* Mount an mfs over /mnt so we can create a console entry */
1718a06e2ab3SBen Gras 	switch ((pid = fork())) {
1719a06e2ab3SBen Gras 	case 0:
1720a06e2ab3SBen Gras 		(void)execl(INIT_MOUNT_MFS, "mount_mfs",
1721a06e2ab3SBen Gras 		    "-b", "4096", "-f", "512",
1722a06e2ab3SBen Gras 		    "-s", 64, "-n", 10,
1723a06e2ab3SBen Gras 		    "-p", "0755",
1724a06e2ab3SBen Gras 		    "swap", "/mnt", NULL);
1725a06e2ab3SBen Gras 		_exit(9);
1726a06e2ab3SBen Gras 		/*NOTREACHED*/
1727a06e2ab3SBen Gras 
1728a06e2ab3SBen Gras 	case -1:
1729a06e2ab3SBen Gras 		return(-1);
1730a06e2ab3SBen Gras 
1731a06e2ab3SBen Gras 	default:
1732a06e2ab3SBen Gras 		if (waitpid(pid, &status, 0) == -1)
1733a06e2ab3SBen Gras 			return(-1);
1734a06e2ab3SBen Gras 		if (status != 0)
1735a06e2ab3SBen Gras 			return(-1);
1736a06e2ab3SBen Gras 		break;
1737a06e2ab3SBen Gras 	}
1738a06e2ab3SBen Gras 
1739a06e2ab3SBen Gras 	{
1740a06e2ab3SBen Gras 		dev_t dev;
1741a06e2ab3SBen Gras #ifdef CPU_CONSDEV
1742a06e2ab3SBen Gras 		static int name[2] = { CTL_MACHDEP, CPU_CONSDEV };
1743a06e2ab3SBen Gras 		size_t olen;
1744a06e2ab3SBen Gras 		olen = sizeof(dev);
1745a06e2ab3SBen Gras 		if (sysctl(name, sizeof(name) / sizeof(name[0]), &dev, &olen,
1746a06e2ab3SBen Gras 		    NULL, 0) == -1)
1747a06e2ab3SBen Gras #endif
1748a06e2ab3SBen Gras 			dev = makedev(0, 0);
1749a06e2ab3SBen Gras 
1750a06e2ab3SBen Gras 		/* Make a console for us, so we can see things happening */
1751a06e2ab3SBen Gras 		if (mknod("/mnt/console", 0666 | S_IFCHR, dev) == -1)
1752a06e2ab3SBen Gras 			return(-1);
1753a06e2ab3SBen Gras 		(void)freopen("/mnt/console", "a", stderr);
1754a06e2ab3SBen Gras 	}
1755a06e2ab3SBen Gras 
1756a06e2ab3SBen Gras #endif
1757a06e2ab3SBen Gras 
1758a06e2ab3SBen Gras 	/* Run the makedev script to create devices */
1759a06e2ab3SBen Gras 	switch ((pid = fork())) {
1760a06e2ab3SBen Gras 	case 0:
1761a06e2ab3SBen Gras 		(void)dup2(2, 1);	/* Give the script stdout */
1762a06e2ab3SBen Gras 		if (chdir("/dev") == 0)
1763a06e2ab3SBen Gras 			(void)execl(INIT_BSHELL, "sh",
1764a06e2ab3SBen Gras 			    access("./MAKEDEV", X_OK) == 0
1765a06e2ab3SBen Gras 				? "./MAKEDEV" : "/etc/MAKEDEV",
1766a06e2ab3SBen Gras 			    "-MM", "init", NULL);
1767a06e2ab3SBen Gras 		_exit(10);
1768a06e2ab3SBen Gras 		/* NOTREACHED */
1769a06e2ab3SBen Gras 
1770a06e2ab3SBen Gras 	case -1:
1771a06e2ab3SBen Gras 		break;
1772a06e2ab3SBen Gras 
1773a06e2ab3SBen Gras 	default:
1774a06e2ab3SBen Gras 		if (waitpid(pid, &status, 0) == -1)
1775a06e2ab3SBen Gras 			break;
1776a06e2ab3SBen Gras 		if (status != 0)
1777*0a6a1f1dSLionel Sambuc 			warn("MAKEDEV exit status %d", status);
1778a06e2ab3SBen Gras 		/*
1779a06e2ab3SBen Gras 		 * If /dev/console got created, then return 0
1780a06e2ab3SBen Gras 		 * regardless of MAKEDEV exit status.
1781a06e2ab3SBen Gras 		 */
1782a06e2ab3SBen Gras 		if (access(_PATH_CONSOLE, F_OK) == 0)
1783a06e2ab3SBen Gras 			return 0;
1784a06e2ab3SBen Gras 		_exit(11);
1785a06e2ab3SBen Gras 	}
1786a06e2ab3SBen Gras 	warn("Unable to run MAKEDEV");
1787a06e2ab3SBen Gras 	_exit(12);
1788a06e2ab3SBen Gras }
1789a06e2ab3SBen Gras #endif
1790a06e2ab3SBen Gras 
1791a06e2ab3SBen Gras #ifndef LETS_GET_SMALL
1792a06e2ab3SBen Gras static int
do_setttyent(void)1793a06e2ab3SBen Gras do_setttyent(void)
1794a06e2ab3SBen Gras {
1795a06e2ab3SBen Gras 	(void)endttyent();
1796a06e2ab3SBen Gras #ifdef CHROOT
1797a06e2ab3SBen Gras 	if (did_multiuser_chroot) {
1798a06e2ab3SBen Gras 		char path[PATH_MAX];
1799a06e2ab3SBen Gras 
1800a06e2ab3SBen Gras 		(void)snprintf(path, sizeof(path), "%s/%s", rootdir, _PATH_TTYS);
1801a06e2ab3SBen Gras 
1802a06e2ab3SBen Gras 		return setttyentpath(path);
1803a06e2ab3SBen Gras 	} else
1804a06e2ab3SBen Gras #endif /* CHROOT */
1805a06e2ab3SBen Gras 		return setttyent();
1806a06e2ab3SBen Gras }
1807a06e2ab3SBen Gras #endif
1808a06e2ab3SBen Gras 
1809a06e2ab3SBen Gras #if !defined(LETS_GET_SMALL) && defined(CHROOT)
1810a06e2ab3SBen Gras 
1811a06e2ab3SBen Gras static int
createsysctlnode(void)1812a06e2ab3SBen Gras createsysctlnode(void)
1813a06e2ab3SBen Gras {
1814a06e2ab3SBen Gras 	struct sysctlnode node;
1815a06e2ab3SBen Gras 	int mib[2];
1816a06e2ab3SBen Gras 	size_t len;
1817a06e2ab3SBen Gras 
1818a06e2ab3SBen Gras 	/*
1819a06e2ab3SBen Gras 	 * Create top-level dynamic sysctl node.  Its child nodes will only
1820a06e2ab3SBen Gras 	 * be readable by the superuser, since regular mortals should not
1821a06e2ab3SBen Gras 	 * care ("Sssh, it's a secret!").
1822a06e2ab3SBen Gras 	 */
1823a06e2ab3SBen Gras 	len = sizeof(struct sysctlnode);
1824a06e2ab3SBen Gras 	mib[0] = CTL_CREATE;
1825a06e2ab3SBen Gras 
1826a06e2ab3SBen Gras 	(void)memset(&node, 0, len);
1827a06e2ab3SBen Gras 	node.sysctl_flags = SYSCTL_VERSION | CTLFLAG_READWRITE |
1828a06e2ab3SBen Gras 	    CTLFLAG_PRIVATE | CTLTYPE_NODE;
1829a06e2ab3SBen Gras 	node.sysctl_num = CTL_CREATE;
1830a06e2ab3SBen Gras 	(void)snprintf(node.sysctl_name, SYSCTL_NAMELEN, "init");
1831a06e2ab3SBen Gras 	if (sysctl(&mib[0], 1, &node, &len, &node, len) == -1) {
1832a06e2ab3SBen Gras 		warning("could not create init node: %m");
1833a06e2ab3SBen Gras 		return -1;
1834a06e2ab3SBen Gras 	}
1835a06e2ab3SBen Gras 
1836a06e2ab3SBen Gras 	/*
1837a06e2ab3SBen Gras 	 * Create second level dynamic node capable of holding pathname.
1838a06e2ab3SBen Gras 	 * Provide "/" as the default value.
1839a06e2ab3SBen Gras 	 */
1840a06e2ab3SBen Gras 	len = sizeof(struct sysctlnode);
1841a06e2ab3SBen Gras 	mib[0] = node.sysctl_num;
1842a06e2ab3SBen Gras 	mib[1] = CTL_CREATE;
1843a06e2ab3SBen Gras 
1844a06e2ab3SBen Gras 	(void)memset(&node, 0, len);
1845a06e2ab3SBen Gras 	node.sysctl_flags = SYSCTL_VERSION | CTLFLAG_READWRITE |
1846a06e2ab3SBen Gras 	    CTLTYPE_STRING | CTLFLAG_OWNDATA;
1847a06e2ab3SBen Gras 	node.sysctl_size = _POSIX_PATH_MAX;
1848a06e2ab3SBen Gras 	node.sysctl_data = __UNCONST("/");
1849a06e2ab3SBen Gras 	node.sysctl_num = CTL_CREATE;
1850a06e2ab3SBen Gras 	(void)snprintf(node.sysctl_name, SYSCTL_NAMELEN, "root");
1851a06e2ab3SBen Gras 	if (sysctl(&mib[0], 2, NULL, NULL, &node, len) == -1) {
1852a06e2ab3SBen Gras 		warning("could not create init.root node: %m");
1853a06e2ab3SBen Gras 		return -1;
1854a06e2ab3SBen Gras 	}
1855a06e2ab3SBen Gras 
1856a06e2ab3SBen Gras 	return 0;
1857a06e2ab3SBen Gras }
1858a06e2ab3SBen Gras 
1859a06e2ab3SBen Gras static int
shouldchroot(void)1860a06e2ab3SBen Gras shouldchroot(void)
1861a06e2ab3SBen Gras {
1862a06e2ab3SBen Gras 	struct sysctlnode node;
1863a06e2ab3SBen Gras 	size_t len, cnt;
1864a06e2ab3SBen Gras 	int mib;
1865a06e2ab3SBen Gras 
1866a06e2ab3SBen Gras 	len = sizeof(struct sysctlnode);
1867a06e2ab3SBen Gras 
1868a06e2ab3SBen Gras 	if (sysctlbyname("init.root", rootdir, &len, NULL, 0) == -1) {
1869a06e2ab3SBen Gras 		warning("could not read init.root: %m");
1870a06e2ab3SBen Gras 
1871a06e2ab3SBen Gras 		/* Child killed our node. Recreate it. */
1872a06e2ab3SBen Gras 		if (errno == ENOENT) {
1873a06e2ab3SBen Gras 			/* Destroy whatever is left, recreate from scratch. */
1874a06e2ab3SBen Gras 			if (sysctlnametomib("init", &mib, &cnt) != -1) {
1875a06e2ab3SBen Gras 				(void)memset(&node, 0, sizeof(node));
1876a06e2ab3SBen Gras 				node.sysctl_flags = SYSCTL_VERSION;
1877a06e2ab3SBen Gras 				node.sysctl_num = mib;
1878a06e2ab3SBen Gras 				mib = CTL_DESTROY;
1879a06e2ab3SBen Gras 
1880a06e2ab3SBen Gras 				(void)sysctl(&mib, 1, NULL, NULL, &node,
1881a06e2ab3SBen Gras 				    sizeof(node));
1882a06e2ab3SBen Gras 			}
1883a06e2ab3SBen Gras 
1884a06e2ab3SBen Gras 			(void)createsysctlnode();
1885a06e2ab3SBen Gras 		}
1886a06e2ab3SBen Gras 
1887a06e2ab3SBen Gras 		/* We certainly won't chroot. */
1888a06e2ab3SBen Gras 		return 0;
1889a06e2ab3SBen Gras 	}
1890a06e2ab3SBen Gras 
1891a06e2ab3SBen Gras 	if (rootdir[len] != '\0' || strlen(rootdir) != len - 1) {
1892a06e2ab3SBen Gras 		warning("init.root is not a string");
1893a06e2ab3SBen Gras 		return 0;
1894a06e2ab3SBen Gras 	}
1895a06e2ab3SBen Gras 
1896a06e2ab3SBen Gras 	if (strcmp(rootdir, "/") == 0)
1897a06e2ab3SBen Gras 		return 0;
189884d9c625SLionel Sambuc 
1899a06e2ab3SBen Gras 	return 1;
1900a06e2ab3SBen Gras }
1901a06e2ab3SBen Gras 
1902a06e2ab3SBen Gras #endif /* !LETS_GET_SMALL && CHROOT */
1903