init.c revision 1.13
1/*	$OpenBSD: init.c,v 1.13 1998/04/13 15:26:46 millert Exp $	*/
2/*	$NetBSD: init.c,v 1.22 1996/05/15 23:29:33 jtc Exp $	*/
3
4/*-
5 * Copyright (c) 1991, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Donn Seeley at Berkeley Software Design, Inc.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed by the University of
22 *	California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 */
39
40#ifndef lint
41static char copyright[] =
42"@(#) Copyright (c) 1991, 1993\n\
43	The Regents of the University of California.  All rights reserved.\n";
44#endif /* not lint */
45
46#ifndef lint
47#if 0
48static char sccsid[] = "@(#)init.c	8.2 (Berkeley) 4/28/95";
49#else
50static char rcsid[] = "$OpenBSD: init.c,v 1.13 1998/04/13 15:26:46 millert Exp $";
51#endif
52#endif /* not lint */
53
54#include <sys/param.h>
55#include <sys/sysctl.h>
56#include <sys/wait.h>
57
58#include <db.h>
59#include <errno.h>
60#include <fcntl.h>
61#include <signal.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <string.h>
65#include <syslog.h>
66#include <time.h>
67#include <ttyent.h>
68#include <unistd.h>
69#include <util.h>
70
71#ifdef __STDC__
72#include <stdarg.h>
73#else
74#include <varargs.h>
75#endif
76
77#ifdef SECURE
78#include <pwd.h>
79#endif
80
81#include "pathnames.h"
82
83/*
84 * Sleep times; used to prevent thrashing.
85 */
86#define	GETTY_SPACING		 5	/* N secs minimum getty spacing */
87#define	GETTY_SLEEP		30	/* sleep N secs after spacing problem */
88#define	WINDOW_WAIT		 3	/* wait N secs after starting window */
89#define	STALL_TIMEOUT		30	/* wait N secs after warning */
90#define	DEATH_WATCH		10	/* wait N secs for procs to die */
91
92void handle __P((sig_t, ...));
93void delset __P((sigset_t *, ...));
94
95void stall __P((char *, ...));
96void warning __P((char *, ...));
97void emergency __P((char *, ...));
98void disaster __P((int));
99void badsys __P((int));
100
101/*
102 * We really need a recursive typedef...
103 * The following at least guarantees that the return type of (*state_t)()
104 * is sufficiently wide to hold a function pointer.
105 */
106typedef long (*state_func_t) __P((void));
107typedef state_func_t (*state_t) __P((void));
108
109state_func_t single_user __P((void));
110state_func_t runcom __P((void));
111state_func_t read_ttys __P((void));
112state_func_t multi_user __P((void));
113state_func_t clean_ttys __P((void));
114state_func_t catatonia __P((void));
115state_func_t death __P((void));
116
117enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
118
119void transition __P((state_t));
120#ifndef LETS_GET_SMALL
121state_t requested_transition = runcom;
122#else /* LETS_GET_SMALL */
123state_t requested_transition = single_user;
124#endif /* LETS_GET_SMALL */
125
126void setctty __P((char *));
127
128typedef struct init_session {
129	int	se_index;		/* index of entry in ttys file */
130	pid_t	se_process;		/* controlling process */
131	time_t	se_started;		/* used to avoid thrashing */
132	int	se_flags;		/* status of session */
133#define	SE_SHUTDOWN	0x1		/* session won't be restarted */
134#define	SE_PRESENT	0x2		/* session is in /etc/ttys */
135#define	SE_DEVEXISTS	0x4		/* open does not result in ENODEV */
136	char	*se_device;		/* filename of port */
137	char	*se_getty;		/* what to run on that port */
138	char	**se_getty_argv;	/* pre-parsed argument array */
139	char	*se_window;		/* window system (started only once) */
140	char	**se_window_argv;	/* pre-parsed argument array */
141	struct	init_session *se_prev;
142	struct	init_session *se_next;
143} session_t;
144
145void free_session __P((session_t *));
146session_t *new_session __P((session_t *, int, struct ttyent *));
147session_t *sessions;
148
149char **construct_argv __P((char *));
150void start_window_system __P((session_t *));
151void collect_child __P((pid_t));
152pid_t start_getty __P((session_t *));
153void transition_handler __P((int));
154void alrm_handler __P((int));
155void setsecuritylevel __P((int));
156int getsecuritylevel __P((void));
157int setupargv __P((session_t *, struct ttyent *));
158int clang;
159
160void clear_session_logs __P((session_t *));
161
162int start_session_db __P((void));
163void add_session __P((session_t *));
164void del_session __P((session_t *));
165session_t *find_session __P((pid_t));
166DB *session_db;
167
168/*
169 * The mother of all processes.
170 */
171int
172main(argc, argv)
173	int argc;
174	char **argv;
175{
176	int c;
177	struct sigaction sa;
178	sigset_t mask;
179
180#ifndef LETS_GET_SMALL
181	/* Dispose of random users. */
182	if (getuid() != 0) {
183		(void)fprintf(stderr, "init: %s\n", strerror(EPERM));
184		exit (1);
185	}
186
187	/* System V users like to reexec init. */
188	if (getpid() != 1) {
189		(void)fprintf(stderr, "init: already running\n");
190		exit (1);
191	}
192
193	/*
194	 * Note that this does NOT open a file...
195	 * Does 'init' deserve its own facility number?
196	 */
197	openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
198#endif /* LETS_GET_SMALL */
199
200	/*
201	 * Create an initial session.
202	 */
203	if (setsid() < 0)
204		warning("initial setsid() failed: %m");
205
206	/*
207	 * Establish an initial user so that programs running
208	 * single user do not freak out and die (like passwd).
209	 */
210	if (setlogin("root") < 0)
211		warning("setlogin() failed: %m");
212
213#ifndef LETS_GET_SMALL
214	/*
215	 * This code assumes that we always get arguments through flags,
216	 * never through bits set in some random machine register.
217	 */
218	while ((c = getopt(argc, argv, "sf")) != -1)
219		switch (c) {
220		case 's':
221			requested_transition = single_user;
222			break;
223		case 'f':
224			runcom_mode = FASTBOOT;
225			break;
226		default:
227			warning("unrecognized flag '-%c'", c);
228			break;
229		}
230
231	if (optind != argc)
232		warning("ignoring excess arguments");
233#else /* LETS_GET_SMALL */
234	requested_transition = single_user;
235#endif /* LETS_GET_SMALL */
236
237	/*
238	 * We catch or block signals rather than ignore them,
239	 * so that they get reset on exec.
240	 */
241	handle(badsys, SIGSYS, 0);
242	handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
243	       SIGBUS, SIGXCPU, SIGXFSZ, 0);
244	handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0);
245	handle(alrm_handler, SIGALRM, 0);
246	sigfillset(&mask);
247	delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
248		SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0);
249	sigprocmask(SIG_SETMASK, &mask, NULL);
250	sigemptyset(&sa.sa_mask);
251	sa.sa_flags = 0;
252	sa.sa_handler = SIG_IGN;
253	(void) sigaction(SIGTTIN, &sa, NULL);
254	(void) sigaction(SIGTTOU, &sa, NULL);
255
256	/*
257	 * Paranoia.
258	 */
259	close(0);
260	close(1);
261	close(2);
262
263	/*
264	 * Start the state machine.
265	 */
266	transition(requested_transition);
267
268	/*
269	 * Should never reach here.
270	 */
271	return 1;
272}
273
274/*
275 * Associate a function with a signal handler.
276 */
277void
278#ifdef __STDC__
279handle(sig_t handler, ...)
280#else
281handle(va_alist)
282	va_dcl
283#endif
284{
285	int sig;
286	struct sigaction sa;
287	sigset_t mask_everything;
288	va_list ap;
289#ifndef __STDC__
290	sig_t handler;
291
292	va_start(ap);
293	handler = va_arg(ap, sig_t);
294#else
295	va_start(ap, handler);
296#endif
297
298	sa.sa_handler = handler;
299	sigfillset(&mask_everything);
300
301	while ((sig = va_arg(ap, int))) {
302		sa.sa_mask = mask_everything;
303		/* XXX SA_RESTART? */
304		sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
305		sigaction(sig, &sa, NULL);
306	}
307	va_end(ap);
308}
309
310/*
311 * Delete a set of signals from a mask.
312 */
313void
314#ifdef __STDC__
315delset(sigset_t *maskp, ...)
316#else
317delset(va_alist)
318	va_dcl
319#endif
320{
321	int sig;
322	va_list ap;
323#ifndef __STDC__
324	sigset_t *maskp;
325
326	va_start(ap);
327	maskp = va_arg(ap, sigset_t *);
328#else
329	va_start(ap, maskp);
330#endif
331
332	while ((sig = va_arg(ap, int)))
333		sigdelset(maskp, sig);
334	va_end(ap);
335}
336
337/*
338 * Log a message and sleep for a while (to give someone an opportunity
339 * to read it and to save log or hardcopy output if the problem is chronic).
340 * NB: should send a message to the session logger to avoid blocking.
341 */
342void
343#ifdef __STDC__
344stall(char *message, ...)
345#else
346stall(va_alist)
347	va_dcl
348#endif
349{
350	va_list ap;
351#ifndef __STDC__
352	char *message;
353
354	va_start(ap);
355	message = va_arg(ap, char *);
356#else
357	va_start(ap, message);
358#endif
359
360	vsyslog(LOG_ALERT, message, ap);
361	va_end(ap);
362	closelog();
363	sleep(STALL_TIMEOUT);
364}
365
366/*
367 * Like stall(), but doesn't sleep.
368 * If cpp had variadic macros, the two functions could be #defines for another.
369 * NB: should send a message to the session logger to avoid blocking.
370 */
371void
372#ifdef __STDC__
373warning(char *message, ...)
374#else
375warning(va_alist)
376	va_dcl
377#endif
378{
379	va_list ap;
380#ifndef __STDC__
381	char *message;
382
383	va_start(ap);
384	message = va_arg(ap, char *);
385#else
386	va_start(ap, message);
387#endif
388
389	vsyslog(LOG_ALERT, message, ap);
390	va_end(ap);
391	closelog();
392}
393
394/*
395 * Log an emergency message.
396 * NB: should send a message to the session logger to avoid blocking.
397 */
398void
399#ifdef __STDC__
400emergency(char *message, ...)
401#else
402emergency(va_alist)
403	va_dcl
404#endif
405{
406	va_list ap;
407#ifndef __STDC__
408	char *message;
409
410	va_start(ap);
411	message = va_arg(ap, char *);
412#else
413	va_start(ap, message);
414#endif
415
416	vsyslog(LOG_EMERG, message, ap);
417	va_end(ap);
418	closelog();
419}
420
421/*
422 * Catch a SIGSYS signal.
423 *
424 * These may arise if a system does not support sysctl.
425 * We tolerate up to 25 of these, then throw in the towel.
426 */
427void
428badsys(sig)
429	int sig;
430{
431	static int badcount = 0;
432
433	if (badcount++ < 25)
434		return;
435	disaster(sig);
436}
437
438/*
439 * Catch an unexpected signal.
440 */
441void
442disaster(sig)
443	int sig;
444{
445	emergency("fatal signal: %s", strsignal(sig));
446
447	sleep(STALL_TIMEOUT);
448	_exit(sig);		/* reboot */
449}
450
451/*
452 * Get the security level of the kernel.
453 */
454int
455getsecuritylevel()
456{
457#ifdef KERN_SECURELVL
458	int name[2], curlevel;
459	size_t len;
460	extern int errno;
461
462	name[0] = CTL_KERN;
463	name[1] = KERN_SECURELVL;
464	len = sizeof curlevel;
465	if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
466		emergency("cannot get kernel security level: %s",
467		    strerror(errno));
468		return (-1);
469	}
470	return (curlevel);
471#else
472	return (-1);
473#endif
474}
475
476/*
477 * Set the security level of the kernel.
478 */
479void
480setsecuritylevel(newlevel)
481	int newlevel;
482{
483#ifdef KERN_SECURELVL
484	int name[2], curlevel;
485	extern int errno;
486
487	curlevel = getsecuritylevel();
488	if (newlevel == curlevel)
489		return;
490	name[0] = CTL_KERN;
491	name[1] = KERN_SECURELVL;
492	if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
493		emergency(
494		    "cannot change kernel security level from %d to %d: %s",
495		    curlevel, newlevel, strerror(errno));
496		return;
497	}
498#ifdef SECURE
499	warning("kernel security level changed from %d to %d",
500	    curlevel, newlevel);
501#endif
502#endif
503}
504
505/*
506 * Change states in the finite state machine.
507 * The initial state is passed as an argument.
508 */
509void
510transition(s)
511	state_t s;
512{
513	for (;;)
514		s = (state_t) (*s)();
515}
516
517/*
518 * Close out the accounting files for a login session.
519 * NB: should send a message to the session logger to avoid blocking.
520 */
521void
522clear_session_logs(sp)
523	session_t *sp;
524{
525	char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
526
527	if (logout(line))
528		logwtmp(line, "", "");
529}
530
531/*
532 * Start a session and allocate a controlling terminal.
533 * Only called by children of init after forking.
534 */
535void
536setctty(name)
537	char *name;
538{
539	int fd;
540
541	(void) revoke(name);
542	sleep (2);			/* leave DTR low */
543	if ((fd = open(name, O_RDWR)) == -1) {
544		stall("can't open %s: %m", name);
545		_exit(1);
546	}
547	if (login_tty(fd) == -1) {
548		stall("can't get %s for controlling terminal: %m", name);
549		_exit(1);
550	}
551}
552
553/*
554 * Bring the system up single user.
555 */
556state_func_t
557single_user()
558{
559	pid_t pid, wpid;
560	int status;
561	sigset_t mask;
562	char shell[MAXPATHLEN];		/* Allocate space here */
563	char name[MAXPATHLEN];		/* Name (argv[0]) of shell */
564	char *argv[2];
565#ifdef SECURE
566	struct ttyent *typ;
567	struct passwd *pp;
568	static const char banner[] =
569		"Enter root password, or ^D to go multi-user\n";
570	char *clear, *password;
571#endif
572
573	/* Init shell and name */
574	strcpy(shell, _PATH_BSHELL);
575	strcpy(name, "-sh");
576
577	/*
578	 * If the kernel is in secure mode, downgrade it to insecure mode.
579	 */
580	if (getsecuritylevel() > 0)
581		setsecuritylevel(0);
582
583	if ((pid = fork()) == 0) {
584		/*
585		 * Start the single user session.
586		 */
587		setctty(_PATH_CONSOLE);
588
589#ifdef SECURE
590		/*
591		 * Check the root password.
592		 * We don't care if the console is 'on' by default;
593		 * it's the only tty that can be 'off' and 'secure'.
594		 */
595		typ = getttynam("console");
596		pp = getpwnam("root");
597		if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp &&
598		    *pp->pw_passwd) {
599			write(2, banner, sizeof banner - 1);
600			for (;;) {
601				clear = getpass("Password:");
602				if (clear == 0 || *clear == '\0')
603					_exit(0);
604				password = crypt(clear, pp->pw_passwd);
605				memset(clear, 0, _PASSWORD_LEN);
606				if (strcmp(password, pp->pw_passwd) == 0)
607					break;
608				warning("single-user login failed\n");
609			}
610		}
611		endttyent();
612		endpwent();
613#endif /* SECURE */
614
615#ifdef DEBUGSHELL
616		{
617			char altshell[128], *cp = altshell;
618			int num;
619
620#define	SHREQUEST \
621	"Enter pathname of shell or RETURN for sh: "
622			(void)write(STDERR_FILENO,
623			    SHREQUEST, sizeof(SHREQUEST) - 1);
624			while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
625			    num != 0 && *cp != '\n' && cp < &altshell[127])
626					cp++;
627			*cp = '\0';
628
629			/* Copy in alternate shell */
630			if (altshell[0] != '\0'){
631				char *p;
632
633				/* Binary to exec */
634				strcpy(shell, altshell);
635
636				/* argv[0] */
637				p = strrchr(altshell, '/');
638				if(p == NULL) p = altshell;
639				else p++;
640
641				name[0] = '-';
642				strcpy(&name[1], p);
643			}
644		}
645#endif /* DEBUGSHELL */
646
647		/*
648		 * Unblock signals.
649		 * We catch all the interesting ones,
650		 * and those are reset to SIG_DFL on exec.
651		 */
652		sigemptyset(&mask);
653		sigprocmask(SIG_SETMASK, &mask, NULL);
654
655		/*
656		 * Fire off a shell.
657		 * If the default one doesn't work, try the Bourne shell.
658		 */
659		argv[0] = name;
660		argv[1] = NULL;
661		setenv("PATH", _PATH_STDPATH, 1);
662		execv(shell, argv);
663		emergency("can't exec %s for single user: %m", shell);
664
665		argv[0] = "-sh";
666		argv[1] = NULL;
667		execv(_PATH_BSHELL, argv);
668		emergency("can't exec %s for single user: %m", _PATH_BSHELL);
669		sleep(STALL_TIMEOUT);
670		_exit(1);
671	}
672
673	if (pid == -1) {
674		/*
675		 * We are seriously hosed.  Do our best.
676		 */
677		emergency("can't fork single-user shell, trying again");
678		while (waitpid(-1, NULL, WNOHANG) > 0)
679			continue;
680		return (state_func_t) single_user;
681	}
682
683	requested_transition = 0;
684	do {
685		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
686			collect_child(wpid);
687		if (wpid == -1) {
688			if (errno == EINTR)
689				continue;
690			warning("wait for single-user shell failed: %m; restarting");
691			return (state_func_t) single_user;
692		}
693		if (wpid == pid && WIFSTOPPED(status)) {
694			warning("init: shell stopped, restarting\n");
695			kill(pid, SIGCONT);
696			wpid = -1;
697		}
698	} while (wpid != pid && !requested_transition);
699
700	if (requested_transition)
701		return (state_func_t) requested_transition;
702
703	if (!WIFEXITED(status)) {
704		if (WTERMSIG(status) == SIGKILL) {
705			/*
706			 *  reboot(8) killed shell?
707			 */
708			warning("single user shell terminated.");
709			sleep(STALL_TIMEOUT);
710			_exit(0);
711		} else {
712			warning("single user shell terminated, restarting");
713			return (state_func_t) single_user;
714		}
715	}
716
717	runcom_mode = FASTBOOT;
718#ifndef LETS_GET_SMALL
719	return (state_func_t) runcom;
720#else /* LETS_GET_SMALL */
721	return (state_func_t) single_user;
722#endif /* LETS_GET_SMALL */
723}
724
725#ifndef LETS_GET_SMALL
726/*
727 * Run the system startup script.
728 */
729state_func_t
730runcom()
731{
732	pid_t pid, wpid;
733	int status;
734	char *argv[4];
735	struct sigaction sa;
736
737	if ((pid = fork()) == 0) {
738		sigemptyset(&sa.sa_mask);
739		sa.sa_flags = 0;
740		sa.sa_handler = SIG_IGN;
741		(void) sigaction(SIGTSTP, &sa, NULL);
742		(void) sigaction(SIGHUP, &sa, NULL);
743
744		setctty(_PATH_CONSOLE);
745
746		argv[0] = "sh";
747		argv[1] = _PATH_RUNCOM;
748		argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0;
749		argv[3] = 0;
750
751		sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL);
752
753		execv(_PATH_BSHELL, argv);
754		stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
755		_exit(1);	/* force single user mode */
756	}
757
758	if (pid == -1) {
759		emergency("can't fork for %s on %s: %m",
760			_PATH_BSHELL, _PATH_RUNCOM);
761		while (waitpid(-1, NULL, WNOHANG) > 0)
762			continue;
763		sleep(STALL_TIMEOUT);
764		return (state_func_t) single_user;
765	}
766
767	/*
768	 * Copied from single_user().  This is a bit paranoid.
769	 */
770	do {
771		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
772			collect_child(wpid);
773		if (wpid == -1) {
774			if (errno == EINTR)
775				continue;
776			warning("wait for %s on %s failed: %m; going to single user mode",
777				_PATH_BSHELL, _PATH_RUNCOM);
778			return (state_func_t) single_user;
779		}
780		if (wpid == pid && WIFSTOPPED(status)) {
781			warning("init: %s on %s stopped, restarting\n",
782				_PATH_BSHELL, _PATH_RUNCOM);
783			kill(pid, SIGCONT);
784			wpid = -1;
785		}
786	} while (wpid != pid);
787
788	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
789	    requested_transition == catatonia) {
790		/* /etc/rc executed /sbin/reboot; wait for the end quietly */
791		sigset_t s;
792
793		sigfillset(&s);
794		for (;;)
795			sigsuspend(&s);
796	}
797
798	if (!WIFEXITED(status)) {
799		warning("%s on %s terminated abnormally, going to single user mode",
800			_PATH_BSHELL, _PATH_RUNCOM);
801		return (state_func_t) single_user;
802	}
803
804	if (WEXITSTATUS(status))
805		return (state_func_t) single_user;
806
807	runcom_mode = AUTOBOOT;		/* the default */
808	/* NB: should send a message to the session logger to avoid blocking. */
809	logwtmp("~", "reboot", "");
810	return (state_func_t) read_ttys;
811}
812
813/*
814 * Open the session database.
815 *
816 * NB: We could pass in the size here; is it necessary?
817 */
818int
819start_session_db()
820{
821	if (session_db && (*session_db->close)(session_db))
822		emergency("session database close: %s", strerror(errno));
823	if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
824		emergency("session database open: %s", strerror(errno));
825		return (1);
826	}
827	return (0);
828
829}
830
831/*
832 * Add a new login session.
833 */
834void
835add_session(sp)
836	session_t *sp;
837{
838	DBT key;
839	DBT data;
840
841	key.data = &sp->se_process;
842	key.size = sizeof sp->se_process;
843	data.data = &sp;
844	data.size = sizeof sp;
845
846	if ((*session_db->put)(session_db, &key, &data, 0))
847		emergency("insert %d: %s", sp->se_process, strerror(errno));
848}
849
850/*
851 * Delete an old login session.
852 */
853void
854del_session(sp)
855	session_t *sp;
856{
857	DBT key;
858
859	key.data = &sp->se_process;
860	key.size = sizeof sp->se_process;
861
862	if ((*session_db->del)(session_db, &key, 0))
863		emergency("delete %d: %s", sp->se_process, strerror(errno));
864}
865
866/*
867 * Look up a login session by pid.
868 */
869session_t *
870#ifdef __STDC__
871find_session(pid_t pid)
872#else
873find_session(pid)
874	pid_t pid;
875#endif
876{
877	DBT key;
878	DBT data;
879	session_t *ret;
880
881	key.data = &pid;
882	key.size = sizeof pid;
883	if ((*session_db->get)(session_db, &key, &data, 0) != 0)
884		return 0;
885	memcpy(&ret, data.data, sizeof(ret));
886	return ret;
887}
888
889/*
890 * Construct an argument vector from a command line.
891 */
892char **
893construct_argv(command)
894	char *command;
895{
896	register int argc = 0;
897	register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
898						* sizeof (char *));
899	static const char separators[] = " \t";
900
901	if ((argv[argc++] = strtok(command, separators)) == 0)
902		return 0;
903	while ((argv[argc++] = strtok(NULL, separators)))
904		continue;
905	return argv;
906}
907
908/*
909 * Deallocate a session descriptor.
910 */
911void
912free_session(sp)
913	register session_t *sp;
914{
915	free(sp->se_device);
916	if (sp->se_getty) {
917		free(sp->se_getty);
918		free(sp->se_getty_argv);
919	}
920	if (sp->se_window) {
921		free(sp->se_window);
922		free(sp->se_window_argv);
923	}
924	free(sp);
925}
926
927/*
928 * Allocate a new session descriptor.
929 */
930session_t *
931new_session(sprev, session_index, typ)
932	session_t *sprev;
933	int session_index;
934	register struct ttyent *typ;
935{
936	register session_t *sp;
937
938	if ((typ->ty_status & TTY_ON) == 0 ||
939	    typ->ty_name == 0 ||
940	    typ->ty_getty == 0)
941		return 0;
942
943	sp = (session_t *) malloc(sizeof (session_t));
944	memset(sp, 0, sizeof *sp);
945
946	sp->se_flags = SE_PRESENT;
947	sp->se_index = session_index;
948
949	sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
950	(void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
951
952	if (setupargv(sp, typ) == 0) {
953		free_session(sp);
954		return (0);
955	}
956
957	sp->se_next = 0;
958	if (sprev == 0) {
959		sessions = sp;
960		sp->se_prev = 0;
961	} else {
962		sprev->se_next = sp;
963		sp->se_prev = sprev;
964	}
965
966	return sp;
967}
968
969/*
970 * Calculate getty and if useful window argv vectors.
971 */
972int
973setupargv(sp, typ)
974	session_t *sp;
975	struct ttyent *typ;
976{
977
978	if (sp->se_getty) {
979		free(sp->se_getty);
980		free(sp->se_getty_argv);
981	}
982	sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2);
983	(void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
984	sp->se_getty_argv = construct_argv(sp->se_getty);
985	if (sp->se_getty_argv == 0) {
986		warning("can't parse getty for port %s", sp->se_device);
987		free(sp->se_getty);
988		sp->se_getty = 0;
989		return (0);
990	}
991	if (typ->ty_window) {
992		if (sp->se_window)
993			free(sp->se_window);
994		sp->se_window = strdup(typ->ty_window);
995		sp->se_window_argv = construct_argv(sp->se_window);
996		if (sp->se_window_argv == 0) {
997			warning("can't parse window for port %s",
998				sp->se_device);
999			free(sp->se_window);
1000			sp->se_window = 0;
1001			return (0);
1002		}
1003	}
1004	return (1);
1005}
1006
1007/*
1008 * Walk the list of ttys and create sessions for each active line.
1009 */
1010state_func_t
1011read_ttys()
1012{
1013	int session_index = 0;
1014	register session_t *sp, *snext;
1015	register struct ttyent *typ;
1016
1017	/*
1018	 * Destroy any previous session state.
1019	 * There shouldn't be any, but just in case...
1020	 */
1021	for (sp = sessions; sp; sp = snext) {
1022		if (sp->se_process)
1023			clear_session_logs(sp);
1024		snext = sp->se_next;
1025		free_session(sp);
1026	}
1027	sessions = 0;
1028	if (start_session_db())
1029		return (state_func_t) single_user;
1030
1031	/*
1032	 * Allocate a session entry for each active port.
1033	 * Note that sp starts at 0.
1034	 */
1035	while ((typ = getttyent()))
1036		if ((snext = new_session(sp, ++session_index, typ)))
1037			sp = snext;
1038
1039	endttyent();
1040
1041	return (state_func_t) multi_user;
1042}
1043
1044/*
1045 * Start a window system running.
1046 */
1047void
1048start_window_system(sp)
1049	session_t *sp;
1050{
1051	pid_t pid;
1052	sigset_t mask;
1053
1054	if ((pid = fork()) == -1) {
1055		emergency("can't fork for window system on port %s: %m",
1056			sp->se_device);
1057		/* hope that getty fails and we can try again */
1058		return;
1059	}
1060
1061	if (pid)
1062		return;
1063
1064	sigemptyset(&mask);
1065	sigprocmask(SIG_SETMASK, &mask, NULL);
1066
1067	if (setsid() < 0)
1068		emergency("setsid failed (window) %m");
1069
1070	execv(sp->se_window_argv[0], sp->se_window_argv);
1071	stall("can't exec window system '%s' for port %s: %m",
1072		sp->se_window_argv[0], sp->se_device);
1073	_exit(1);
1074}
1075
1076/*
1077 * Start a login session running.
1078 * For first open, man-handle tty directly to determine if it
1079 * really exists. It is not efficient to spawn gettys on devices
1080 * that do not exist.
1081 */
1082pid_t
1083start_getty(sp)
1084	session_t *sp;
1085{
1086	pid_t pid;
1087	sigset_t mask;
1088	time_t current_time = time(NULL);
1089	int p[2], new = 1;
1090
1091	if (sp->se_flags & SE_DEVEXISTS)
1092		new = 0;
1093
1094	if (new) {
1095		if (pipe(p) == -1)
1096			return -1;
1097	}
1098
1099	/*
1100	 * fork(), not vfork() -- we can't afford to block.
1101	 */
1102	if ((pid = fork()) == -1) {
1103		emergency("can't fork for getty on port %s: %m", sp->se_device);
1104		return -1;
1105	}
1106
1107	if (pid) {
1108		if (new) {
1109			char c;
1110
1111			close(p[1]);
1112			if (read(p[0], &c, 1) != 1) {
1113				close(p[0]);
1114				return -1;
1115			}
1116			close(p[0]);
1117			if (c == '1')
1118				sp->se_flags |= SE_DEVEXISTS;
1119			else
1120				sp->se_flags |= SE_SHUTDOWN;
1121		}
1122		return pid;
1123	}
1124	if (new) {
1125		int fd;
1126
1127		close(p[0]);
1128		fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0666);
1129		if (fd == -1 && (errno == ENXIO || errno == ENOENT ||
1130		    errno == EISDIR)) {
1131			(void)write(p[1], "0", 1);
1132			close(p[1]);
1133			_exit(1);
1134		}
1135		(void)write(p[1], "1", 1);
1136		close(p[1]);
1137		close(fd);
1138		sleep(1);
1139	}
1140
1141	if (current_time > sp->se_started &&
1142	    current_time - sp->se_started < GETTY_SPACING) {
1143		warning("getty repeating too quickly on port %s, sleeping",
1144		        sp->se_device);
1145		sleep((unsigned) GETTY_SLEEP);
1146	}
1147
1148	if (sp->se_window) {
1149		start_window_system(sp);
1150		sleep(WINDOW_WAIT);
1151	}
1152
1153	sigemptyset(&mask);
1154	sigprocmask(SIG_SETMASK, &mask, NULL);
1155
1156	execv(sp->se_getty_argv[0], sp->se_getty_argv);
1157	stall("can't exec getty '%s' for port %s: %m",
1158		sp->se_getty_argv[0], sp->se_device);
1159	_exit(1);
1160}
1161#endif /* LETS_GET_SMALL */
1162
1163/*
1164 * Collect exit status for a child.
1165 * If an exiting login, start a new login running.
1166 */
1167void
1168#ifdef __STDC__
1169collect_child(pid_t pid)
1170#else
1171collect_child(pid)
1172	pid_t pid;
1173#endif
1174{
1175#ifndef LETS_GET_SMALL
1176	register session_t *sp, *sprev, *snext;
1177
1178	if (! sessions)
1179		return;
1180
1181	if (! (sp = find_session(pid)))
1182		return;
1183
1184	clear_session_logs(sp);
1185	login_fbtab(sp->se_device + sizeof(_PATH_DEV) - 1, 0, 0);
1186	del_session(sp);
1187	sp->se_process = 0;
1188
1189	if (sp->se_flags & SE_SHUTDOWN) {
1190		if ((sprev = sp->se_prev))
1191			sprev->se_next = sp->se_next;
1192		else
1193			sessions = sp->se_next;
1194		if ((snext = sp->se_next))
1195			snext->se_prev = sp->se_prev;
1196		free_session(sp);
1197		return;
1198	}
1199
1200	if ((pid = start_getty(sp)) == -1) {
1201		/* serious trouble */
1202		requested_transition = clean_ttys;
1203		return;
1204	}
1205
1206	sp->se_process = pid;
1207	sp->se_started = time(NULL);
1208	add_session(sp);
1209#endif /* LETS_GET_SMALL */
1210}
1211
1212/*
1213 * Catch a signal and request a state transition.
1214 */
1215void
1216transition_handler(sig)
1217	int sig;
1218{
1219
1220	switch (sig) {
1221#ifndef LETS_GET_SMALL
1222	case SIGHUP:
1223		requested_transition = clean_ttys;
1224		break;
1225	case SIGTERM:
1226		requested_transition = death;
1227		break;
1228	case SIGTSTP:
1229		requested_transition = catatonia;
1230		break;
1231#endif /* LETS_GET_SMALL */
1232	default:
1233		requested_transition = 0;
1234		break;
1235	}
1236}
1237
1238#ifndef LETS_GET_SMALL
1239/*
1240 * Take the system multiuser.
1241 */
1242state_func_t
1243multi_user()
1244{
1245	pid_t pid;
1246	register session_t *sp;
1247
1248	requested_transition = 0;
1249
1250	/*
1251	 * If the administrator has not set the security level to -1
1252	 * to indicate that the kernel should not run multiuser in secure
1253	 * mode, and the run script has not set a higher level of security
1254	 * than level 1, then put the kernel into secure mode.
1255	 */
1256	if (getsecuritylevel() == 0)
1257		setsecuritylevel(1);
1258
1259	for (sp = sessions; sp; sp = sp->se_next) {
1260		if (sp->se_process)
1261			continue;
1262		if ((pid = start_getty(sp)) == -1) {
1263			/* serious trouble */
1264			requested_transition = clean_ttys;
1265			break;
1266		}
1267		sp->se_process = pid;
1268		sp->se_started = time(NULL);
1269		add_session(sp);
1270	}
1271
1272	while (!requested_transition)
1273		if ((pid = waitpid(-1, NULL, 0)) != -1)
1274			collect_child(pid);
1275
1276	return (state_func_t) requested_transition;
1277}
1278
1279/*
1280 * This is an n-squared algorithm.  We hope it isn't run often...
1281 */
1282state_func_t
1283clean_ttys()
1284{
1285	register session_t *sp, *sprev;
1286	register struct ttyent *typ;
1287	register int session_index = 0;
1288	register int devlen;
1289
1290	for (sp = sessions; sp; sp = sp->se_next)
1291		sp->se_flags &= ~SE_PRESENT;
1292
1293	devlen = sizeof(_PATH_DEV) - 1;
1294	while ((typ = getttyent())) {
1295		++session_index;
1296
1297		for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
1298			if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
1299				break;
1300
1301		if (sp) {
1302			sp->se_flags |= SE_PRESENT;
1303			if (sp->se_index != session_index) {
1304				warning("port %s changed utmp index from %d to %d",
1305				       sp->se_device, sp->se_index,
1306				       session_index);
1307				sp->se_index = session_index;
1308			}
1309			if ((typ->ty_status & TTY_ON) == 0 ||
1310			    typ->ty_getty == 0) {
1311				sp->se_flags |= SE_SHUTDOWN;
1312				kill(sp->se_process, SIGHUP);
1313				continue;
1314			}
1315			sp->se_flags &= ~SE_SHUTDOWN;
1316			if (setupargv(sp, typ) == 0) {
1317				warning("can't parse getty for port %s",
1318					sp->se_device);
1319				sp->se_flags |= SE_SHUTDOWN;
1320				kill(sp->se_process, SIGHUP);
1321			}
1322			continue;
1323		}
1324
1325		new_session(sprev, session_index, typ);
1326	}
1327
1328	endttyent();
1329
1330	for (sp = sessions; sp; sp = sp->se_next)
1331		if ((sp->se_flags & SE_PRESENT) == 0) {
1332			sp->se_flags |= SE_SHUTDOWN;
1333			kill(sp->se_process, SIGHUP);
1334		}
1335
1336	return (state_func_t) multi_user;
1337}
1338
1339/*
1340 * Block further logins.
1341 */
1342state_func_t
1343catatonia()
1344{
1345	register session_t *sp;
1346
1347	for (sp = sessions; sp; sp = sp->se_next)
1348		sp->se_flags |= SE_SHUTDOWN;
1349
1350	return (state_func_t) multi_user;
1351}
1352#endif /* LETS_GET_SMALL */
1353
1354/*
1355 * Note SIGALRM.
1356 */
1357void
1358alrm_handler(sig)
1359	int sig;
1360{
1361	clang = 1;
1362}
1363
1364#ifndef LETS_GET_SMALL
1365/*
1366 * Bring the system down to single user.
1367 */
1368state_func_t
1369death()
1370{
1371	register session_t *sp;
1372	register int i;
1373	pid_t pid;
1374	static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
1375
1376	for (sp = sessions; sp; sp = sp->se_next)
1377		sp->se_flags |= SE_SHUTDOWN;
1378
1379	/* NB: should send a message to the session logger to avoid blocking. */
1380	logwtmp("~", "shutdown", "");
1381
1382	for (i = 0; i < 3; ++i) {
1383		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
1384			return (state_func_t) single_user;
1385
1386		clang = 0;
1387		alarm(DEATH_WATCH);
1388		do
1389			if ((pid = waitpid(-1, NULL, 0)) != -1)
1390				collect_child(pid);
1391		while (clang == 0 && errno != ECHILD);
1392
1393		if (errno == ECHILD)
1394			return (state_func_t) single_user;
1395	}
1396
1397	warning("some processes would not die; ps axl advised");
1398
1399	return (state_func_t) single_user;
1400}
1401#endif /* LETS_GET_SMALL */
1402