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