init.c revision 11910
11558Srgrimes/*-
21558Srgrimes * Copyright (c) 1991, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * This code is derived from software contributed to Berkeley by
61558Srgrimes * Donn Seeley at Berkeley Software Design, Inc.
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes * 3. All advertising materials mentioning features or use of this software
171558Srgrimes *    must display the following acknowledgement:
181558Srgrimes *	This product includes software developed by the University of
191558Srgrimes *	California, Berkeley and its contributors.
201558Srgrimes * 4. Neither the name of the University nor the names of its contributors
211558Srgrimes *    may be used to endorse or promote products derived from this software
221558Srgrimes *    without specific prior written permission.
231558Srgrimes *
241558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341558Srgrimes * SUCH DAMAGE.
351558Srgrimes */
361558Srgrimes
371558Srgrimes#ifndef lint
381558Srgrimesstatic char copyright[] =
391558Srgrimes"@(#) Copyright (c) 1991, 1993\n\
401558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
411558Srgrimes#endif /* not lint */
421558Srgrimes
431558Srgrimes#ifndef lint
441558Srgrimesstatic char sccsid[] = "@(#)init.c	8.1 (Berkeley) 7/15/93";
451558Srgrimes#endif /* not lint */
461558Srgrimes
471558Srgrimes#include <sys/param.h>
481558Srgrimes#include <sys/sysctl.h>
491558Srgrimes#include <sys/wait.h>
501558Srgrimes
511558Srgrimes#include <db.h>
521558Srgrimes#include <errno.h>
531558Srgrimes#include <fcntl.h>
541558Srgrimes#include <signal.h>
551558Srgrimes#include <stdio.h>
561558Srgrimes#include <stdlib.h>
571558Srgrimes#include <string.h>
581558Srgrimes#include <syslog.h>
591558Srgrimes#include <time.h>
601558Srgrimes#include <ttyent.h>
611558Srgrimes#include <unistd.h>
622323Snate#include <sys/reboot.h>
631558Srgrimes
641558Srgrimes#ifdef __STDC__
651558Srgrimes#include <stdarg.h>
661558Srgrimes#else
671558Srgrimes#include <varargs.h>
681558Srgrimes#endif
691558Srgrimes
701558Srgrimes#ifdef SECURE
711558Srgrimes#include <pwd.h>
721558Srgrimes#endif
731558Srgrimes
741558Srgrimes#include "pathnames.h"
751558Srgrimes
761558Srgrimes/*
771558Srgrimes * Until the mythical util.h arrives...
781558Srgrimes */
791558Srgrimesextern int login_tty __P((int));
801558Srgrimesextern int logout __P((const char *));
811558Srgrimesextern void logwtmp __P((const char *, const char *, const char *));
821558Srgrimes
831558Srgrimes/*
841558Srgrimes * Sleep times; used to prevent thrashing.
851558Srgrimes */
861558Srgrimes#define	GETTY_SPACING		 5	/* N secs minimum getty spacing */
871558Srgrimes#define	GETTY_SLEEP		30	/* sleep N secs after spacing problem */
883594Sache#define GETTY_NSPACE             3      /* max. spacing count to bring reaction */
891558Srgrimes#define	WINDOW_WAIT		 3	/* wait N secs after starting window */
901558Srgrimes#define	STALL_TIMEOUT		30	/* wait N secs after warning */
911558Srgrimes#define	DEATH_WATCH		10	/* wait N secs for procs to die */
921558Srgrimes
931558Srgrimesvoid handle __P((sig_t, ...));
941558Srgrimesvoid delset __P((sigset_t *, ...));
951558Srgrimes
961558Srgrimesvoid stall __P((char *, ...));
971558Srgrimesvoid warning __P((char *, ...));
981558Srgrimesvoid emergency __P((char *, ...));
991558Srgrimesvoid disaster __P((int));
1001558Srgrimesvoid badsys __P((int));
1011558Srgrimes
1021558Srgrimes/*
1031558Srgrimes * We really need a recursive typedef...
1041558Srgrimes * The following at least guarantees that the return type of (*state_t)()
1051558Srgrimes * is sufficiently wide to hold a function pointer.
1061558Srgrimes */
1071558Srgrimestypedef long (*state_func_t) __P((void));
1081558Srgrimestypedef state_func_t (*state_t) __P((void));
1091558Srgrimes
1101558Srgrimesstate_func_t single_user __P((void));
1111558Srgrimesstate_func_t runcom __P((void));
1121558Srgrimesstate_func_t read_ttys __P((void));
1131558Srgrimesstate_func_t multi_user __P((void));
1141558Srgrimesstate_func_t clean_ttys __P((void));
1151558Srgrimesstate_func_t catatonia __P((void));
1161558Srgrimesstate_func_t death __P((void));
1171558Srgrimes
1181558Srgrimesenum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
11911910Sphk#define FALSE	0
12011910Sphk#define TRUE	1
12111910Sphk
1222327Sjkhint Reboot = FALSE;
1231558Srgrimes
1241558Srgrimesvoid transition __P((state_t));
1251558Srgrimesstate_t requested_transition = runcom;
1261558Srgrimes
1271558Srgrimesvoid setctty __P((char *));
1281558Srgrimes
1291558Srgrimestypedef struct init_session {
1301558Srgrimes	int	se_index;		/* index of entry in ttys file */
1311558Srgrimes	pid_t	se_process;		/* controlling process */
1321558Srgrimes	time_t	se_started;		/* used to avoid thrashing */
1331558Srgrimes	int	se_flags;		/* status of session */
1341558Srgrimes#define	SE_SHUTDOWN	0x1		/* session won't be restarted */
1353594Sache	int     se_nspace;              /* spacing count */
1361558Srgrimes	char	*se_device;		/* filename of port */
1371558Srgrimes	char	*se_getty;		/* what to run on that port */
1383594Sache	char    *se_getty_argv_space;   /* pre-parsed argument array space */
1391558Srgrimes	char	**se_getty_argv;	/* pre-parsed argument array */
1401558Srgrimes	char	*se_window;		/* window system (started only once) */
1413594Sache	char    *se_window_argv_space;  /* pre-parsed argument array space */
1421558Srgrimes	char	**se_window_argv;	/* pre-parsed argument array */
1433594Sache	char    *se_type;               /* default terminal type */
1441558Srgrimes	struct	init_session *se_prev;
1451558Srgrimes	struct	init_session *se_next;
1461558Srgrimes} session_t;
1471558Srgrimes
1481558Srgrimesvoid free_session __P((session_t *));
1491558Srgrimessession_t *new_session __P((session_t *, int, struct ttyent *));
1501558Srgrimessession_t *sessions;
1511558Srgrimes
1521558Srgrimeschar **construct_argv __P((char *));
1531558Srgrimesvoid start_window_system __P((session_t *));
1541558Srgrimesvoid collect_child __P((pid_t));
1551558Srgrimespid_t start_getty __P((session_t *));
1561558Srgrimesvoid transition_handler __P((int));
1571558Srgrimesvoid alrm_handler __P((int));
1581558Srgrimesvoid setsecuritylevel __P((int));
1591558Srgrimesint getsecuritylevel __P((void));
1601558Srgrimesint setupargv __P((session_t *, struct ttyent *));
1611558Srgrimesint clang;
1621558Srgrimes
1631558Srgrimesvoid clear_session_logs __P((session_t *));
1641558Srgrimes
1651558Srgrimesint start_session_db __P((void));
1661558Srgrimesvoid add_session __P((session_t *));
1671558Srgrimesvoid del_session __P((session_t *));
1681558Srgrimessession_t *find_session __P((pid_t));
1691558SrgrimesDB *session_db;
1701558Srgrimes
1711558Srgrimes/*
1721558Srgrimes * The mother of all processes.
1731558Srgrimes */
1741558Srgrimesint
1751558Srgrimesmain(argc, argv)
1761558Srgrimes	int argc;
1771558Srgrimes	char **argv;
1781558Srgrimes{
1791558Srgrimes	int c;
1801558Srgrimes	struct sigaction sa;
1811558Srgrimes	sigset_t mask;
1821558Srgrimes
1831558Srgrimes
1841558Srgrimes	/* Dispose of random users. */
1851558Srgrimes	if (getuid() != 0) {
1861558Srgrimes		(void)fprintf(stderr, "init: %s\n", strerror(EPERM));
1871558Srgrimes		exit (1);
1881558Srgrimes	}
1891558Srgrimes
1901558Srgrimes	/* System V users like to reexec init. */
1911558Srgrimes	if (getpid() != 1) {
1921558Srgrimes		(void)fprintf(stderr, "init: already running\n");
1931558Srgrimes		exit (1);
1941558Srgrimes	}
1951558Srgrimes
1961558Srgrimes	/*
1971558Srgrimes	 * Note that this does NOT open a file...
1981558Srgrimes	 * Does 'init' deserve its own facility number?
1991558Srgrimes	 */
2001558Srgrimes	openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
2011558Srgrimes
2021558Srgrimes	/*
2031558Srgrimes	 * Create an initial session.
2041558Srgrimes	 */
2051558Srgrimes	if (setsid() < 0)
2061558Srgrimes		warning("initial setsid() failed: %m");
2071558Srgrimes
2081558Srgrimes	/*
2091558Srgrimes	 * Establish an initial user so that programs running
2101558Srgrimes	 * single user do not freak out and die (like passwd).
2111558Srgrimes	 */
2121558Srgrimes	if (setlogin("root") < 0)
2131558Srgrimes		warning("setlogin() failed: %m");
2141558Srgrimes
2151558Srgrimes	/*
2161558Srgrimes	 * This code assumes that we always get arguments through flags,
2171558Srgrimes	 * never through bits set in some random machine register.
2181558Srgrimes	 */
2191558Srgrimes	while ((c = getopt(argc, argv, "sf")) != -1)
2201558Srgrimes		switch (c) {
2211558Srgrimes		case 's':
2221558Srgrimes			requested_transition = single_user;
2231558Srgrimes			break;
2241558Srgrimes		case 'f':
2251558Srgrimes			runcom_mode = FASTBOOT;
2261558Srgrimes			break;
2271558Srgrimes		default:
2281558Srgrimes			warning("unrecognized flag '-%c'", c);
2291558Srgrimes			break;
2301558Srgrimes		}
2311558Srgrimes
2321558Srgrimes	if (optind != argc)
2331558Srgrimes		warning("ignoring excess arguments");
2341558Srgrimes
2351558Srgrimes	/*
2361558Srgrimes	 * We catch or block signals rather than ignore them,
2371558Srgrimes	 * so that they get reset on exec.
2381558Srgrimes	 */
2391558Srgrimes	handle(badsys, SIGSYS, 0);
2401558Srgrimes	handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
2411558Srgrimes	       SIGBUS, SIGXCPU, SIGXFSZ, 0);
2422323Snate	handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, 0);
2431558Srgrimes	handle(alrm_handler, SIGALRM, 0);
2441558Srgrimes	sigfillset(&mask);
2451558Srgrimes	delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
2462323Snate		SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM, 0);
2471558Srgrimes	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
2481558Srgrimes	sigemptyset(&sa.sa_mask);
2491558Srgrimes	sa.sa_flags = 0;
2501558Srgrimes	sa.sa_handler = SIG_IGN;
2511558Srgrimes	(void) sigaction(SIGTTIN, &sa, (struct sigaction *)0);
2521558Srgrimes	(void) sigaction(SIGTTOU, &sa, (struct sigaction *)0);
2531558Srgrimes
2541558Srgrimes	/*
2551558Srgrimes	 * Paranoia.
2561558Srgrimes	 */
2571558Srgrimes	close(0);
2581558Srgrimes	close(1);
2591558Srgrimes	close(2);
2601558Srgrimes
2611558Srgrimes	/*
2621558Srgrimes	 * Start the state machine.
2631558Srgrimes	 */
2641558Srgrimes	transition(requested_transition);
2651558Srgrimes
2661558Srgrimes	/*
2671558Srgrimes	 * Should never reach here.
2681558Srgrimes	 */
2691558Srgrimes	return 1;
2701558Srgrimes}
2711558Srgrimes
2721558Srgrimes/*
2731558Srgrimes * Associate a function with a signal handler.
2741558Srgrimes */
2751558Srgrimesvoid
2761558Srgrimes#ifdef __STDC__
2771558Srgrimeshandle(sig_t handler, ...)
2781558Srgrimes#else
2791558Srgrimeshandle(va_alist)
2801558Srgrimes	va_dcl
2811558Srgrimes#endif
2821558Srgrimes{
2831558Srgrimes	int sig;
2841558Srgrimes	struct sigaction sa;
2851558Srgrimes	int mask_everything;
2861558Srgrimes	va_list ap;
2871558Srgrimes#ifndef __STDC__
2881558Srgrimes	sig_t handler;
2891558Srgrimes
2901558Srgrimes	va_start(ap);
2911558Srgrimes	handler = va_arg(ap, sig_t);
2921558Srgrimes#else
2931558Srgrimes	va_start(ap, handler);
2941558Srgrimes#endif
2951558Srgrimes
2961558Srgrimes	sa.sa_handler = handler;
2971558Srgrimes	sigfillset(&mask_everything);
2981558Srgrimes
2991558Srgrimes	while (sig = va_arg(ap, int)) {
3001558Srgrimes		sa.sa_mask = mask_everything;
3011558Srgrimes		/* XXX SA_RESTART? */
3021558Srgrimes		sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
3031558Srgrimes		sigaction(sig, &sa, (struct sigaction *) 0);
3041558Srgrimes	}
3051558Srgrimes	va_end(ap);
3061558Srgrimes}
3071558Srgrimes
3081558Srgrimes/*
3091558Srgrimes * Delete a set of signals from a mask.
3101558Srgrimes */
3111558Srgrimesvoid
3121558Srgrimes#ifdef __STDC__
3131558Srgrimesdelset(sigset_t *maskp, ...)
3141558Srgrimes#else
3151558Srgrimesdelset(va_alist)
3161558Srgrimes	va_dcl
3171558Srgrimes#endif
3181558Srgrimes{
3191558Srgrimes	int sig;
3201558Srgrimes	va_list ap;
3211558Srgrimes#ifndef __STDC__
3221558Srgrimes	sigset_t *maskp;
3231558Srgrimes
3241558Srgrimes	va_start(ap);
3251558Srgrimes	maskp = va_arg(ap, sigset_t *);
3261558Srgrimes#else
3271558Srgrimes	va_start(ap, maskp);
3281558Srgrimes#endif
3291558Srgrimes
3301558Srgrimes	while (sig = va_arg(ap, int))
3311558Srgrimes		sigdelset(maskp, sig);
3321558Srgrimes	va_end(ap);
3331558Srgrimes}
3341558Srgrimes
3351558Srgrimes/*
3361558Srgrimes * Log a message and sleep for a while (to give someone an opportunity
3371558Srgrimes * to read it and to save log or hardcopy output if the problem is chronic).
3381558Srgrimes * NB: should send a message to the session logger to avoid blocking.
3391558Srgrimes */
3401558Srgrimesvoid
3411558Srgrimes#ifdef __STDC__
3421558Srgrimesstall(char *message, ...)
3431558Srgrimes#else
3441558Srgrimesstall(va_alist)
3451558Srgrimes	va_dcl
3461558Srgrimes#endif
3471558Srgrimes{
3481558Srgrimes	va_list ap;
3491558Srgrimes#ifndef __STDC__
3501558Srgrimes	char *message;
3511558Srgrimes
3521558Srgrimes	va_start(ap);
3531558Srgrimes	message = va_arg(ap, char *);
3541558Srgrimes#else
3551558Srgrimes	va_start(ap, message);
3561558Srgrimes#endif
3571558Srgrimes
3581558Srgrimes	vsyslog(LOG_ALERT, message, ap);
3591558Srgrimes	va_end(ap);
3601558Srgrimes	sleep(STALL_TIMEOUT);
3611558Srgrimes}
3621558Srgrimes
3631558Srgrimes/*
3641558Srgrimes * Like stall(), but doesn't sleep.
3651558Srgrimes * If cpp had variadic macros, the two functions could be #defines for another.
3661558Srgrimes * NB: should send a message to the session logger to avoid blocking.
3671558Srgrimes */
3681558Srgrimesvoid
3691558Srgrimes#ifdef __STDC__
3701558Srgrimeswarning(char *message, ...)
3711558Srgrimes#else
3721558Srgrimeswarning(va_alist)
3731558Srgrimes	va_dcl
3741558Srgrimes#endif
3751558Srgrimes{
3761558Srgrimes	va_list ap;
3771558Srgrimes#ifndef __STDC__
3781558Srgrimes	char *message;
3791558Srgrimes
3801558Srgrimes	va_start(ap);
3811558Srgrimes	message = va_arg(ap, char *);
3821558Srgrimes#else
3831558Srgrimes	va_start(ap, message);
3841558Srgrimes#endif
3851558Srgrimes
3861558Srgrimes	vsyslog(LOG_ALERT, message, ap);
3871558Srgrimes	va_end(ap);
3881558Srgrimes}
3891558Srgrimes
3901558Srgrimes/*
3911558Srgrimes * Log an emergency message.
3921558Srgrimes * NB: should send a message to the session logger to avoid blocking.
3931558Srgrimes */
3941558Srgrimesvoid
3951558Srgrimes#ifdef __STDC__
3961558Srgrimesemergency(char *message, ...)
3971558Srgrimes#else
3981558Srgrimesemergency(va_alist)
3991558Srgrimes	va_dcl
4001558Srgrimes#endif
4011558Srgrimes{
4021558Srgrimes	va_list ap;
4031558Srgrimes#ifndef __STDC__
4041558Srgrimes	char *message;
4051558Srgrimes
4061558Srgrimes	va_start(ap);
4071558Srgrimes	message = va_arg(ap, char *);
4081558Srgrimes#else
4091558Srgrimes	va_start(ap, message);
4101558Srgrimes#endif
4111558Srgrimes
4121558Srgrimes	vsyslog(LOG_EMERG, message, ap);
4131558Srgrimes	va_end(ap);
4141558Srgrimes}
4151558Srgrimes
4161558Srgrimes/*
4171558Srgrimes * Catch a SIGSYS signal.
4181558Srgrimes *
4191558Srgrimes * These may arise if a system does not support sysctl.
4201558Srgrimes * We tolerate up to 25 of these, then throw in the towel.
4211558Srgrimes */
4221558Srgrimesvoid
4231558Srgrimesbadsys(sig)
4241558Srgrimes	int sig;
4251558Srgrimes{
4261558Srgrimes	static int badcount = 0;
4271558Srgrimes
4281558Srgrimes	if (badcount++ < 25)
4291558Srgrimes		return;
4301558Srgrimes	disaster(sig);
4311558Srgrimes}
4321558Srgrimes
4331558Srgrimes/*
4341558Srgrimes * Catch an unexpected signal.
4351558Srgrimes */
4361558Srgrimesvoid
4371558Srgrimesdisaster(sig)
4381558Srgrimes	int sig;
4391558Srgrimes{
4401558Srgrimes	emergency("fatal signal: %s",
4411558Srgrimes		sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal");
4421558Srgrimes
4431558Srgrimes	sleep(STALL_TIMEOUT);
4441558Srgrimes	_exit(sig);		/* reboot */
4451558Srgrimes}
4461558Srgrimes
4471558Srgrimes/*
4481558Srgrimes * Get the security level of the kernel.
4491558Srgrimes */
4501558Srgrimesint
4511558Srgrimesgetsecuritylevel()
4521558Srgrimes{
4531558Srgrimes#ifdef KERN_SECURELVL
4541558Srgrimes	int name[2], curlevel;
4551558Srgrimes	size_t len;
4561558Srgrimes	extern int errno;
4571558Srgrimes
4581558Srgrimes	name[0] = CTL_KERN;
4591558Srgrimes	name[1] = KERN_SECURELVL;
4601558Srgrimes	len = sizeof curlevel;
4611558Srgrimes	if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
4621558Srgrimes		emergency("cannot get kernel security level: %s",
4631558Srgrimes		    strerror(errno));
4641558Srgrimes		return (-1);
4651558Srgrimes	}
4661558Srgrimes	return (curlevel);
4671558Srgrimes#else
4681558Srgrimes	return (-1);
4691558Srgrimes#endif
4701558Srgrimes}
4711558Srgrimes
4721558Srgrimes/*
4731558Srgrimes * Set the security level of the kernel.
4741558Srgrimes */
4751558Srgrimesvoid
4761558Srgrimessetsecuritylevel(newlevel)
4771558Srgrimes	int newlevel;
4781558Srgrimes{
4791558Srgrimes#ifdef KERN_SECURELVL
4801558Srgrimes	int name[2], curlevel;
4811558Srgrimes	extern int errno;
4821558Srgrimes
4831558Srgrimes	curlevel = getsecuritylevel();
4841558Srgrimes	if (newlevel == curlevel)
4851558Srgrimes		return;
4861558Srgrimes	name[0] = CTL_KERN;
4871558Srgrimes	name[1] = KERN_SECURELVL;
4881558Srgrimes	if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
4891558Srgrimes		emergency(
4901558Srgrimes		    "cannot change kernel security level from %d to %d: %s",
4911558Srgrimes		    curlevel, newlevel, strerror(errno));
4921558Srgrimes		return;
4931558Srgrimes	}
4941558Srgrimes#ifdef SECURE
4951558Srgrimes	warning("kernel security level changed from %d to %d",
4961558Srgrimes	    curlevel, newlevel);
4971558Srgrimes#endif
4981558Srgrimes#endif
4991558Srgrimes}
5001558Srgrimes
5011558Srgrimes/*
5021558Srgrimes * Change states in the finite state machine.
5031558Srgrimes * The initial state is passed as an argument.
5041558Srgrimes */
5051558Srgrimesvoid
5061558Srgrimestransition(s)
5071558Srgrimes	state_t s;
5081558Srgrimes{
5091558Srgrimes	for (;;)
5101558Srgrimes		s = (state_t) (*s)();
5111558Srgrimes}
5121558Srgrimes
5131558Srgrimes/*
5141558Srgrimes * Close out the accounting files for a login session.
5151558Srgrimes * NB: should send a message to the session logger to avoid blocking.
5161558Srgrimes */
5171558Srgrimesvoid
5181558Srgrimesclear_session_logs(sp)
5191558Srgrimes	session_t *sp;
5201558Srgrimes{
5211558Srgrimes	char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
5221558Srgrimes
5231558Srgrimes	if (logout(line))
5241558Srgrimes		logwtmp(line, "", "");
5251558Srgrimes}
5261558Srgrimes
5271558Srgrimes/*
5281558Srgrimes * Start a session and allocate a controlling terminal.
5291558Srgrimes * Only called by children of init after forking.
5301558Srgrimes */
5311558Srgrimesvoid
5321558Srgrimessetctty(name)
5331558Srgrimes	char *name;
5341558Srgrimes{
5351558Srgrimes	int fd;
5361558Srgrimes
5371558Srgrimes	(void) revoke(name);
5381558Srgrimes	if ((fd = open(name, O_RDWR)) == -1) {
5391558Srgrimes		stall("can't open %s: %m", name);
5401558Srgrimes		_exit(1);
5411558Srgrimes	}
5421558Srgrimes	if (login_tty(fd) == -1) {
5431558Srgrimes		stall("can't get %s for controlling terminal: %m", name);
5441558Srgrimes		_exit(1);
5451558Srgrimes	}
5461558Srgrimes}
5471558Srgrimes
5481558Srgrimes/*
5491558Srgrimes * Bring the system up single user.
5501558Srgrimes */
5511558Srgrimesstate_func_t
5521558Srgrimessingle_user()
5531558Srgrimes{
5541558Srgrimes	pid_t pid, wpid;
5551558Srgrimes	int status;
5561558Srgrimes	sigset_t mask;
5571558Srgrimes	char *shell = _PATH_BSHELL;
5581558Srgrimes	char *argv[2];
5591558Srgrimes#ifdef SECURE
5601558Srgrimes	struct ttyent *typ;
5611558Srgrimes	struct passwd *pp;
5621558Srgrimes	static const char banner[] =
5631558Srgrimes		"Enter root password, or ^D to go multi-user\n";
5641558Srgrimes	char *clear, *password;
5651558Srgrimes#endif
5661558Srgrimes
5671558Srgrimes	/*
5681558Srgrimes	 * If the kernel is in secure mode, downgrade it to insecure mode.
5691558Srgrimes	 */
5701558Srgrimes	if (getsecuritylevel() > 0)
5711558Srgrimes		setsecuritylevel(0);
5721558Srgrimes
5732327Sjkh	if (Reboot) {
5742323Snate		/* Instead of going single user, let's halt the machine */
5752323Snate		sync();
5762323Snate		alarm(2);
5772323Snate		pause();
5782323Snate		reboot(RB_AUTOBOOT);
5792323Snate		_exit(0);
5802323Snate	}
5812323Snate
5821558Srgrimes	if ((pid = fork()) == 0) {
5831558Srgrimes		/*
5841558Srgrimes		 * Start the single user session.
5851558Srgrimes		 */
5861558Srgrimes		setctty(_PATH_CONSOLE);
5871558Srgrimes
5881558Srgrimes#ifdef SECURE
5891558Srgrimes		/*
5901558Srgrimes		 * Check the root password.
5911558Srgrimes		 * We don't care if the console is 'on' by default;
5921558Srgrimes		 * it's the only tty that can be 'off' and 'secure'.
5931558Srgrimes		 */
5941558Srgrimes		typ = getttynam("console");
5951558Srgrimes		pp = getpwnam("root");
5961558Srgrimes		if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) {
5971558Srgrimes			write(2, banner, sizeof banner - 1);
5981558Srgrimes			for (;;) {
5991558Srgrimes				clear = getpass("Password:");
6001558Srgrimes				if (clear == 0 || *clear == '\0')
6011558Srgrimes					_exit(0);
6021558Srgrimes				password = crypt(clear, pp->pw_passwd);
6031558Srgrimes				bzero(clear, _PASSWORD_LEN);
6041558Srgrimes				if (strcmp(password, pp->pw_passwd) == 0)
6051558Srgrimes					break;
6061558Srgrimes				warning("single-user login failed\n");
6071558Srgrimes			}
6081558Srgrimes		}
6091558Srgrimes		endttyent();
6101558Srgrimes		endpwent();
6111558Srgrimes#endif /* SECURE */
6121558Srgrimes
6131558Srgrimes#ifdef DEBUGSHELL
6141558Srgrimes		{
6151558Srgrimes			char altshell[128], *cp = altshell;
6161558Srgrimes			int num;
6171558Srgrimes
6181558Srgrimes#define	SHREQUEST \
6191558Srgrimes	"Enter pathname of shell or RETURN for sh: "
6201558Srgrimes			(void)write(STDERR_FILENO,
6211558Srgrimes			    SHREQUEST, sizeof(SHREQUEST) - 1);
6221558Srgrimes			while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
6231558Srgrimes			    num != 0 && *cp != '\n' && cp < &altshell[127])
6241558Srgrimes					cp++;
6251558Srgrimes			*cp = '\0';
6261558Srgrimes			if (altshell[0] != '\0')
6271558Srgrimes				shell = altshell;
6281558Srgrimes		}
6291558Srgrimes#endif /* DEBUGSHELL */
6301558Srgrimes
6311558Srgrimes		/*
6321558Srgrimes		 * Unblock signals.
6331558Srgrimes		 * We catch all the interesting ones,
6341558Srgrimes		 * and those are reset to SIG_DFL on exec.
6351558Srgrimes		 */
6361558Srgrimes		sigemptyset(&mask);
6371558Srgrimes		sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
6381558Srgrimes
6391558Srgrimes		/*
6401558Srgrimes		 * Fire off a shell.
6411558Srgrimes		 * If the default one doesn't work, try the Bourne shell.
6421558Srgrimes		 */
6431558Srgrimes		argv[0] = "-sh";
6441558Srgrimes		argv[1] = 0;
6451558Srgrimes		execv(shell, argv);
6461558Srgrimes		emergency("can't exec %s for single user: %m", shell);
6471558Srgrimes		execv(_PATH_BSHELL, argv);
6481558Srgrimes		emergency("can't exec %s for single user: %m", _PATH_BSHELL);
6491558Srgrimes		sleep(STALL_TIMEOUT);
6501558Srgrimes		_exit(1);
6511558Srgrimes	}
6521558Srgrimes
6531558Srgrimes	if (pid == -1) {
6541558Srgrimes		/*
6551558Srgrimes		 * We are seriously hosed.  Do our best.
6561558Srgrimes		 */
6571558Srgrimes		emergency("can't fork single-user shell, trying again");
6581558Srgrimes		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
6591558Srgrimes			continue;
6601558Srgrimes		return (state_func_t) single_user;
6611558Srgrimes	}
6621558Srgrimes
6631558Srgrimes	requested_transition = 0;
6641558Srgrimes	do {
6651558Srgrimes		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
6661558Srgrimes			collect_child(wpid);
6671558Srgrimes		if (wpid == -1) {
6681558Srgrimes			if (errno == EINTR)
6691558Srgrimes				continue;
6701558Srgrimes			warning("wait for single-user shell failed: %m; restarting");
6711558Srgrimes			return (state_func_t) single_user;
6721558Srgrimes		}
6731558Srgrimes		if (wpid == pid && WIFSTOPPED(status)) {
6741558Srgrimes			warning("init: shell stopped, restarting\n");
6751558Srgrimes			kill(pid, SIGCONT);
6761558Srgrimes			wpid = -1;
6771558Srgrimes		}
6781558Srgrimes	} while (wpid != pid && !requested_transition);
6791558Srgrimes
6801558Srgrimes	if (requested_transition)
6811558Srgrimes		return (state_func_t) requested_transition;
6821558Srgrimes
6831558Srgrimes	if (!WIFEXITED(status)) {
6848871Srgrimes		if (WTERMSIG(status) == SIGKILL) {
6858871Srgrimes			/*
6868871Srgrimes			 *  reboot(8) killed shell?
6871558Srgrimes			 */
6881558Srgrimes			warning("single user shell terminated.");
6891558Srgrimes			sleep(STALL_TIMEOUT);
6901558Srgrimes			_exit(0);
6918871Srgrimes		} else {
6921558Srgrimes			warning("single user shell terminated, restarting");
6931558Srgrimes			return (state_func_t) single_user;
6941558Srgrimes		}
6951558Srgrimes	}
6961558Srgrimes
6971558Srgrimes	runcom_mode = FASTBOOT;
6981558Srgrimes	return (state_func_t) runcom;
6991558Srgrimes}
7001558Srgrimes
7011558Srgrimes/*
7021558Srgrimes * Run the system startup script.
7031558Srgrimes */
7041558Srgrimesstate_func_t
7051558Srgrimesruncom()
7061558Srgrimes{
7071558Srgrimes	pid_t pid, wpid;
7081558Srgrimes	int status;
7091558Srgrimes	char *argv[4];
7101558Srgrimes	struct sigaction sa;
7111558Srgrimes
7121558Srgrimes	if ((pid = fork()) == 0) {
7131558Srgrimes		sigemptyset(&sa.sa_mask);
7141558Srgrimes		sa.sa_flags = 0;
7151558Srgrimes		sa.sa_handler = SIG_IGN;
7161558Srgrimes		(void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
7171558Srgrimes		(void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
7181558Srgrimes
7191558Srgrimes		setctty(_PATH_CONSOLE);
7201558Srgrimes
7211558Srgrimes		argv[0] = "sh";
7221558Srgrimes		argv[1] = _PATH_RUNCOM;
7231558Srgrimes		argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0;
7241558Srgrimes		argv[3] = 0;
7251558Srgrimes
7261558Srgrimes		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
7271558Srgrimes
7281558Srgrimes		execv(_PATH_BSHELL, argv);
7291558Srgrimes		stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
7301558Srgrimes		_exit(1);	/* force single user mode */
7311558Srgrimes	}
7321558Srgrimes
7331558Srgrimes	if (pid == -1) {
7341558Srgrimes		emergency("can't fork for %s on %s: %m",
7351558Srgrimes			_PATH_BSHELL, _PATH_RUNCOM);
7361558Srgrimes		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
7371558Srgrimes			continue;
7381558Srgrimes		sleep(STALL_TIMEOUT);
7391558Srgrimes		return (state_func_t) single_user;
7401558Srgrimes	}
7411558Srgrimes
7421558Srgrimes	/*
7431558Srgrimes	 * Copied from single_user().  This is a bit paranoid.
7441558Srgrimes	 */
7451558Srgrimes	do {
7461558Srgrimes		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
7471558Srgrimes			collect_child(wpid);
7481558Srgrimes		if (wpid == -1) {
7491558Srgrimes			if (errno == EINTR)
7501558Srgrimes				continue;
7511558Srgrimes			warning("wait for %s on %s failed: %m; going to single user mode",
7521558Srgrimes				_PATH_BSHELL, _PATH_RUNCOM);
7531558Srgrimes			return (state_func_t) single_user;
7541558Srgrimes		}
7551558Srgrimes		if (wpid == pid && WIFSTOPPED(status)) {
7561558Srgrimes			warning("init: %s on %s stopped, restarting\n",
7571558Srgrimes				_PATH_BSHELL, _PATH_RUNCOM);
7581558Srgrimes			kill(pid, SIGCONT);
7591558Srgrimes			wpid = -1;
7601558Srgrimes		}
7611558Srgrimes	} while (wpid != pid);
7621558Srgrimes
7631558Srgrimes	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
7641558Srgrimes	    requested_transition == catatonia) {
7651558Srgrimes		/* /etc/rc executed /sbin/reboot; wait for the end quietly */
7661558Srgrimes		sigset_t s;
7671558Srgrimes
7681558Srgrimes		sigfillset(&s);
7691558Srgrimes		for (;;)
7701558Srgrimes			sigsuspend(&s);
7711558Srgrimes	}
7721558Srgrimes
7731558Srgrimes	if (!WIFEXITED(status)) {
7741558Srgrimes		warning("%s on %s terminated abnormally, going to single user mode",
7751558Srgrimes			_PATH_BSHELL, _PATH_RUNCOM);
7761558Srgrimes		return (state_func_t) single_user;
7771558Srgrimes	}
7781558Srgrimes
7791558Srgrimes	if (WEXITSTATUS(status))
7801558Srgrimes		return (state_func_t) single_user;
7811558Srgrimes
7821558Srgrimes	runcom_mode = AUTOBOOT;		/* the default */
7831558Srgrimes	/* NB: should send a message to the session logger to avoid blocking. */
7841558Srgrimes	logwtmp("~", "reboot", "");
7851558Srgrimes	return (state_func_t) read_ttys;
7861558Srgrimes}
7871558Srgrimes
7881558Srgrimes/*
7891558Srgrimes * Open the session database.
7901558Srgrimes *
7911558Srgrimes * NB: We could pass in the size here; is it necessary?
7921558Srgrimes */
7931558Srgrimesint
7941558Srgrimesstart_session_db()
7951558Srgrimes{
7961558Srgrimes	if (session_db && (*session_db->close)(session_db))
7971558Srgrimes		emergency("session database close: %s", strerror(errno));
7981558Srgrimes	if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
7991558Srgrimes		emergency("session database open: %s", strerror(errno));
8001558Srgrimes		return (1);
8011558Srgrimes	}
8021558Srgrimes	return (0);
8038871Srgrimes
8041558Srgrimes}
8051558Srgrimes
8061558Srgrimes/*
8071558Srgrimes * Add a new login session.
8081558Srgrimes */
8091558Srgrimesvoid
8101558Srgrimesadd_session(sp)
8111558Srgrimes	session_t *sp;
8121558Srgrimes{
8131558Srgrimes	DBT key;
8141558Srgrimes	DBT data;
8151558Srgrimes
8161558Srgrimes	key.data = &sp->se_process;
8171558Srgrimes	key.size = sizeof sp->se_process;
8181558Srgrimes	data.data = &sp;
8191558Srgrimes	data.size = sizeof sp;
8201558Srgrimes
8211558Srgrimes	if ((*session_db->put)(session_db, &key, &data, 0))
8221558Srgrimes		emergency("insert %d: %s", sp->se_process, strerror(errno));
8231558Srgrimes}
8241558Srgrimes
8251558Srgrimes/*
8261558Srgrimes * Delete an old login session.
8271558Srgrimes */
8281558Srgrimesvoid
8291558Srgrimesdel_session(sp)
8301558Srgrimes	session_t *sp;
8311558Srgrimes{
8321558Srgrimes	DBT key;
8331558Srgrimes
8341558Srgrimes	key.data = &sp->se_process;
8351558Srgrimes	key.size = sizeof sp->se_process;
8361558Srgrimes
8371558Srgrimes	if ((*session_db->del)(session_db, &key, 0))
8381558Srgrimes		emergency("delete %d: %s", sp->se_process, strerror(errno));
8391558Srgrimes}
8401558Srgrimes
8411558Srgrimes/*
8421558Srgrimes * Look up a login session by pid.
8431558Srgrimes */
8441558Srgrimessession_t *
8451558Srgrimes#ifdef __STDC__
8461558Srgrimesfind_session(pid_t pid)
8471558Srgrimes#else
8481558Srgrimesfind_session(pid)
8491558Srgrimes	pid_t pid;
8501558Srgrimes#endif
8511558Srgrimes{
8521558Srgrimes	DBT key;
8531558Srgrimes	DBT data;
8541558Srgrimes	session_t *ret;
8551558Srgrimes
8561558Srgrimes	key.data = &pid;
8571558Srgrimes	key.size = sizeof pid;
8581558Srgrimes	if ((*session_db->get)(session_db, &key, &data, 0) != 0)
8591558Srgrimes		return 0;
8601558Srgrimes	bcopy(data.data, (char *)&ret, sizeof(ret));
8611558Srgrimes	return ret;
8621558Srgrimes}
8631558Srgrimes
8641558Srgrimes/*
8651558Srgrimes * Construct an argument vector from a command line.
8661558Srgrimes */
8671558Srgrimeschar **
8681558Srgrimesconstruct_argv(command)
8691558Srgrimes	char *command;
8701558Srgrimes{
8715478Sache	char *strk (char *);
8721558Srgrimes	register int argc = 0;
8731558Srgrimes	register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
8741558Srgrimes						* sizeof (char *));
8751558Srgrimes
8765478Sache	if ((argv[argc++] = strk(command)) == 0)
8771558Srgrimes		return 0;
8785478Sache	while (argv[argc++] = strk((char *) 0))
8791558Srgrimes		continue;
8801558Srgrimes	return argv;
8811558Srgrimes}
8821558Srgrimes
8831558Srgrimes/*
8841558Srgrimes * Deallocate a session descriptor.
8851558Srgrimes */
8861558Srgrimesvoid
8871558Srgrimesfree_session(sp)
8881558Srgrimes	register session_t *sp;
8891558Srgrimes{
8901558Srgrimes	free(sp->se_device);
8911558Srgrimes	if (sp->se_getty) {
8921558Srgrimes		free(sp->se_getty);
8933594Sache		free(sp->se_getty_argv_space);
8941558Srgrimes		free(sp->se_getty_argv);
8951558Srgrimes	}
8961558Srgrimes	if (sp->se_window) {
8971558Srgrimes		free(sp->se_window);
8983594Sache		free(sp->se_window_argv_space);
8991558Srgrimes		free(sp->se_window_argv);
9001558Srgrimes	}
9013594Sache	if (sp->se_type)
9023594Sache		free(sp->se_type);
9031558Srgrimes	free(sp);
9041558Srgrimes}
9051558Srgrimes
9061558Srgrimes/*
9071558Srgrimes * Allocate a new session descriptor.
9081558Srgrimes */
9091558Srgrimessession_t *
9101558Srgrimesnew_session(sprev, session_index, typ)
9111558Srgrimes	session_t *sprev;
9121558Srgrimes	int session_index;
9131558Srgrimes	register struct ttyent *typ;
9141558Srgrimes{
9151558Srgrimes	register session_t *sp;
9161558Srgrimes
9171558Srgrimes	if ((typ->ty_status & TTY_ON) == 0 ||
9181558Srgrimes	    typ->ty_name == 0 ||
9191558Srgrimes	    typ->ty_getty == 0)
9201558Srgrimes		return 0;
9211558Srgrimes
9221558Srgrimes	sp = (session_t *) malloc(sizeof (session_t));
9231558Srgrimes	bzero(sp, sizeof *sp);
9241558Srgrimes
9251558Srgrimes	sp->se_index = session_index;
9261558Srgrimes
9271558Srgrimes	sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
9281558Srgrimes	(void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
9291558Srgrimes
9301558Srgrimes	if (setupargv(sp, typ) == 0) {
9311558Srgrimes		free_session(sp);
9321558Srgrimes		return (0);
9331558Srgrimes	}
9341558Srgrimes
9351558Srgrimes	sp->se_next = 0;
9361558Srgrimes	if (sprev == 0) {
9371558Srgrimes		sessions = sp;
9381558Srgrimes		sp->se_prev = 0;
9391558Srgrimes	} else {
9401558Srgrimes		sprev->se_next = sp;
9411558Srgrimes		sp->se_prev = sprev;
9421558Srgrimes	}
9431558Srgrimes
9441558Srgrimes	return sp;
9451558Srgrimes}
9461558Srgrimes
9471558Srgrimes/*
9481558Srgrimes * Calculate getty and if useful window argv vectors.
9491558Srgrimes */
9501558Srgrimesint
9511558Srgrimessetupargv(sp, typ)
9521558Srgrimes	session_t *sp;
9531558Srgrimes	struct ttyent *typ;
9541558Srgrimes{
9551558Srgrimes
9561558Srgrimes	if (sp->se_getty) {
9571558Srgrimes		free(sp->se_getty);
9583594Sache		free(sp->se_getty_argv_space);
9591558Srgrimes		free(sp->se_getty_argv);
9601558Srgrimes	}
9611558Srgrimes	sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2);
9621558Srgrimes	(void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
9633594Sache	sp->se_getty_argv_space = strdup(sp->se_getty);
9643594Sache	sp->se_getty_argv = construct_argv(sp->se_getty_argv_space);
9651558Srgrimes	if (sp->se_getty_argv == 0) {
9661558Srgrimes		warning("can't parse getty for port %s", sp->se_device);
9671558Srgrimes		free(sp->se_getty);
9683594Sache		free(sp->se_getty_argv_space);
9693594Sache		sp->se_getty = sp->se_getty_argv_space = 0;
9701558Srgrimes		return (0);
9711558Srgrimes	}
9723594Sache	if (sp->se_window) {
9733594Sache			free(sp->se_window);
9743594Sache		free(sp->se_window_argv_space);
9753594Sache		free(sp->se_window_argv);
9763594Sache	}
9773594Sache	sp->se_window = sp->se_window_argv_space = 0;
9783594Sache	sp->se_window_argv = 0;
9791558Srgrimes	if (typ->ty_window) {
9801558Srgrimes		sp->se_window = strdup(typ->ty_window);
9813594Sache		sp->se_window_argv_space = strdup(sp->se_window);
9823594Sache		sp->se_window_argv = construct_argv(sp->se_window_argv_space);
9831558Srgrimes		if (sp->se_window_argv == 0) {
9841558Srgrimes			warning("can't parse window for port %s",
9851558Srgrimes				sp->se_device);
9863594Sache			free(sp->se_window_argv_space);
9871558Srgrimes			free(sp->se_window);
9883594Sache			sp->se_window = sp->se_window_argv_space = 0;
9891558Srgrimes			return (0);
9901558Srgrimes		}
9911558Srgrimes	}
9923594Sache	if (sp->se_type)
9933594Sache		free(sp->se_type);
9943594Sache	sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0;
9951558Srgrimes	return (1);
9961558Srgrimes}
9971558Srgrimes
9981558Srgrimes/*
9991558Srgrimes * Walk the list of ttys and create sessions for each active line.
10001558Srgrimes */
10011558Srgrimesstate_func_t
10021558Srgrimesread_ttys()
10031558Srgrimes{
10041558Srgrimes	int session_index = 0;
10051558Srgrimes	register session_t *sp, *snext;
10061558Srgrimes	register struct ttyent *typ;
10071558Srgrimes
10081558Srgrimes	/*
10091558Srgrimes	 * Destroy any previous session state.
10101558Srgrimes	 * There shouldn't be any, but just in case...
10111558Srgrimes	 */
10121558Srgrimes	for (sp = sessions; sp; sp = snext) {
10131558Srgrimes		if (sp->se_process)
10141558Srgrimes			clear_session_logs(sp);
10151558Srgrimes		snext = sp->se_next;
10161558Srgrimes		free_session(sp);
10171558Srgrimes	}
10181558Srgrimes	sessions = 0;
10191558Srgrimes	if (start_session_db())
10201558Srgrimes		return (state_func_t) single_user;
10211558Srgrimes
10221558Srgrimes	/*
10231558Srgrimes	 * Allocate a session entry for each active port.
10241558Srgrimes	 * Note that sp starts at 0.
10251558Srgrimes	 */
10261558Srgrimes	while (typ = getttyent())
10271558Srgrimes		if (snext = new_session(sp, ++session_index, typ))
10281558Srgrimes			sp = snext;
10291558Srgrimes
10301558Srgrimes	endttyent();
10311558Srgrimes
10321558Srgrimes	return (state_func_t) multi_user;
10331558Srgrimes}
10341558Srgrimes
10351558Srgrimes/*
10361558Srgrimes * Start a window system running.
10371558Srgrimes */
10381558Srgrimesvoid
10391558Srgrimesstart_window_system(sp)
10401558Srgrimes	session_t *sp;
10411558Srgrimes{
10421558Srgrimes	pid_t pid;
10431558Srgrimes	sigset_t mask;
10443594Sache	char term[64], *env[2];
10451558Srgrimes
10461558Srgrimes	if ((pid = fork()) == -1) {
10471558Srgrimes		emergency("can't fork for window system on port %s: %m",
10481558Srgrimes			sp->se_device);
10491558Srgrimes		/* hope that getty fails and we can try again */
10501558Srgrimes		return;
10511558Srgrimes	}
10521558Srgrimes
10531558Srgrimes	if (pid)
10541558Srgrimes		return;
10551558Srgrimes
10561558Srgrimes	sigemptyset(&mask);
10571558Srgrimes	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
10581558Srgrimes
10591558Srgrimes	if (setsid() < 0)
10601558Srgrimes		emergency("setsid failed (window) %m");
10611558Srgrimes
10623594Sache	if (sp->se_type) {
10633594Sache		/* Don't use malloc after fork */
10643594Sache		strcpy(term, "TERM=");
10653594Sache		strcat(term, sp->se_type);
10663594Sache		env[0] = term;
10673594Sache		env[1] = 0;
10683594Sache	}
10693594Sache	else
10703594Sache		env[0] = 0;
10713594Sache	execve(sp->se_window_argv[0], sp->se_window_argv, env);
10721558Srgrimes	stall("can't exec window system '%s' for port %s: %m",
10731558Srgrimes		sp->se_window_argv[0], sp->se_device);
10741558Srgrimes	_exit(1);
10751558Srgrimes}
10761558Srgrimes
10771558Srgrimes/*
10781558Srgrimes * Start a login session running.
10791558Srgrimes */
10801558Srgrimespid_t
10811558Srgrimesstart_getty(sp)
10821558Srgrimes	session_t *sp;
10831558Srgrimes{
10841558Srgrimes	pid_t pid;
10851558Srgrimes	sigset_t mask;
10861558Srgrimes	time_t current_time = time((time_t *) 0);
10879997Sache	int too_quick = 0;
10883594Sache	char term[64], *env[2];
10891558Srgrimes
109010006Smpp	if (current_time >= sp->se_started &&
10919997Sache	    current_time - sp->se_started < GETTY_SPACING) {
10929997Sache		if (++sp->se_nspace > GETTY_NSPACE) {
10939997Sache			sp->se_nspace = 0;
10949997Sache			too_quick = 1;
10959997Sache		}
10969997Sache	} else
10979997Sache		sp->se_nspace = 0;
10989997Sache
10991558Srgrimes	/*
11001558Srgrimes	 * fork(), not vfork() -- we can't afford to block.
11011558Srgrimes	 */
11021558Srgrimes	if ((pid = fork()) == -1) {
11031558Srgrimes		emergency("can't fork for getty on port %s: %m", sp->se_device);
11041558Srgrimes		return -1;
11051558Srgrimes	}
11061558Srgrimes
11071558Srgrimes	if (pid)
11081558Srgrimes		return pid;
11091558Srgrimes
11109997Sache	if (too_quick) {
11119997Sache		warning("getty repeating too quickly on port %s, sleeping %d secs",
11129997Sache			sp->se_device, GETTY_SLEEP);
11131558Srgrimes		sleep((unsigned) GETTY_SLEEP);
11141558Srgrimes	}
11151558Srgrimes
11161558Srgrimes	if (sp->se_window) {
11171558Srgrimes		start_window_system(sp);
11181558Srgrimes		sleep(WINDOW_WAIT);
11191558Srgrimes	}
11201558Srgrimes
11211558Srgrimes	sigemptyset(&mask);
11221558Srgrimes	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
11231558Srgrimes
11243594Sache	if (sp->se_type) {
11253594Sache		/* Don't use malloc after fork */
11263594Sache		strcpy(term, "TERM=");
11273594Sache		strcat(term, sp->se_type);
11283594Sache		env[0] = term;
11293594Sache		env[1] = 0;
11303594Sache	}
11313594Sache	else
11323594Sache		env[0] = 0;
11333594Sache	execve(sp->se_getty_argv[0], sp->se_getty_argv, env);
11341558Srgrimes	stall("can't exec getty '%s' for port %s: %m",
11351558Srgrimes		sp->se_getty_argv[0], sp->se_device);
11361558Srgrimes	_exit(1);
11371558Srgrimes}
11381558Srgrimes
11391558Srgrimes/*
11401558Srgrimes * Collect exit status for a child.
11411558Srgrimes * If an exiting login, start a new login running.
11421558Srgrimes */
11431558Srgrimesvoid
11441558Srgrimes#ifdef __STDC__
11451558Srgrimescollect_child(pid_t pid)
11461558Srgrimes#else
11471558Srgrimescollect_child(pid)
11481558Srgrimes	pid_t pid;
11491558Srgrimes#endif
11501558Srgrimes{
11511558Srgrimes	register session_t *sp, *sprev, *snext;
11521558Srgrimes
11531558Srgrimes	if (! sessions)
11541558Srgrimes		return;
11551558Srgrimes
11561558Srgrimes	if (! (sp = find_session(pid)))
11571558Srgrimes		return;
11581558Srgrimes
11591558Srgrimes	clear_session_logs(sp);
11601558Srgrimes	del_session(sp);
11611558Srgrimes	sp->se_process = 0;
11621558Srgrimes
11631558Srgrimes	if (sp->se_flags & SE_SHUTDOWN) {
11641558Srgrimes		if (sprev = sp->se_prev)
11651558Srgrimes			sprev->se_next = sp->se_next;
11661558Srgrimes		else
11671558Srgrimes			sessions = sp->se_next;
11681558Srgrimes		if (snext = sp->se_next)
11691558Srgrimes			snext->se_prev = sp->se_prev;
11701558Srgrimes		free_session(sp);
11711558Srgrimes		return;
11721558Srgrimes	}
11731558Srgrimes
11741558Srgrimes	if ((pid = start_getty(sp)) == -1) {
11751558Srgrimes		/* serious trouble */
11761558Srgrimes		requested_transition = clean_ttys;
11771558Srgrimes		return;
11781558Srgrimes	}
11791558Srgrimes
11801558Srgrimes	sp->se_process = pid;
11811558Srgrimes	sp->se_started = time((time_t *) 0);
11821558Srgrimes	add_session(sp);
11831558Srgrimes}
11841558Srgrimes
11851558Srgrimes/*
11861558Srgrimes * Catch a signal and request a state transition.
11871558Srgrimes */
11881558Srgrimesvoid
11891558Srgrimestransition_handler(sig)
11901558Srgrimes	int sig;
11911558Srgrimes{
11921558Srgrimes
11931558Srgrimes	switch (sig) {
11941558Srgrimes	case SIGHUP:
11951558Srgrimes		requested_transition = clean_ttys;
11961558Srgrimes		break;
11972323Snate	case SIGINT:
11982327Sjkh		Reboot = TRUE;
11991558Srgrimes	case SIGTERM:
12001558Srgrimes		requested_transition = death;
12011558Srgrimes		break;
12021558Srgrimes	case SIGTSTP:
12031558Srgrimes		requested_transition = catatonia;
12041558Srgrimes		break;
12051558Srgrimes	default:
12061558Srgrimes		requested_transition = 0;
12071558Srgrimes		break;
12081558Srgrimes	}
12091558Srgrimes}
12101558Srgrimes
12111558Srgrimes/*
12121558Srgrimes * Take the system multiuser.
12131558Srgrimes */
12141558Srgrimesstate_func_t
12151558Srgrimesmulti_user()
12161558Srgrimes{
12171558Srgrimes	pid_t pid;
12181558Srgrimes	register session_t *sp;
12191558Srgrimes
12201558Srgrimes	requested_transition = 0;
12211558Srgrimes
12221558Srgrimes	/*
12231558Srgrimes	 * If the administrator has not set the security level to -1
12241558Srgrimes	 * to indicate that the kernel should not run multiuser in secure
12258871Srgrimes	 * mode, and the run script has not set a higher level of security
12261558Srgrimes	 * than level 1, then put the kernel into secure mode.
12271558Srgrimes	 */
12281558Srgrimes	if (getsecuritylevel() == 0)
12291558Srgrimes		setsecuritylevel(1);
12301558Srgrimes
12311558Srgrimes	for (sp = sessions; sp; sp = sp->se_next) {
12321558Srgrimes		if (sp->se_process)
12331558Srgrimes			continue;
12341558Srgrimes		if ((pid = start_getty(sp)) == -1) {
12351558Srgrimes			/* serious trouble */
12361558Srgrimes			requested_transition = clean_ttys;
12371558Srgrimes			break;
12381558Srgrimes		}
12391558Srgrimes		sp->se_process = pid;
12401558Srgrimes		sp->se_started = time((time_t *) 0);
12411558Srgrimes		add_session(sp);
12421558Srgrimes	}
12431558Srgrimes
12441558Srgrimes	while (!requested_transition)
12451558Srgrimes		if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
12461558Srgrimes			collect_child(pid);
12471558Srgrimes
12481558Srgrimes	return (state_func_t) requested_transition;
12491558Srgrimes}
12501558Srgrimes
12511558Srgrimes/*
12521558Srgrimes * This is an n-squared algorithm.  We hope it isn't run often...
12531558Srgrimes */
12541558Srgrimesstate_func_t
12551558Srgrimesclean_ttys()
12561558Srgrimes{
12571558Srgrimes	register session_t *sp, *sprev;
12581558Srgrimes	register struct ttyent *typ;
12591558Srgrimes	register int session_index = 0;
12601558Srgrimes	register int devlen;
12613594Sache	char *old_getty, *old_window, *old_type;
12621558Srgrimes
12631558Srgrimes	if (! sessions)
12641558Srgrimes		return (state_func_t) multi_user;
12651558Srgrimes
12661558Srgrimes	devlen = sizeof(_PATH_DEV) - 1;
12671558Srgrimes	while (typ = getttyent()) {
12681558Srgrimes		++session_index;
12691558Srgrimes
12701558Srgrimes		for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
12711558Srgrimes			if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
12721558Srgrimes				break;
12731558Srgrimes
12741558Srgrimes		if (sp) {
12751558Srgrimes			if (sp->se_index != session_index) {
12761558Srgrimes				warning("port %s changed utmp index from %d to %d",
12771558Srgrimes				       sp->se_device, sp->se_index,
12781558Srgrimes				       session_index);
12791558Srgrimes				sp->se_index = session_index;
12801558Srgrimes			}
12811558Srgrimes			if ((typ->ty_status & TTY_ON) == 0 ||
12821558Srgrimes			    typ->ty_getty == 0) {
12831558Srgrimes				sp->se_flags |= SE_SHUTDOWN;
12841558Srgrimes				kill(sp->se_process, SIGHUP);
12851558Srgrimes				continue;
12861558Srgrimes			}
12871558Srgrimes			sp->se_flags &= ~SE_SHUTDOWN;
12883594Sache			old_getty = sp->se_getty ? strdup(sp->se_getty) : 0;
12893594Sache			old_window = sp->se_window ? strdup(sp->se_window) : 0;
12903594Sache			old_type = sp->se_type ? strdup(sp->se_type) : 0;
12911558Srgrimes			if (setupargv(sp, typ) == 0) {
12921558Srgrimes				warning("can't parse getty for port %s",
12931558Srgrimes					sp->se_device);
12941558Srgrimes				sp->se_flags |= SE_SHUTDOWN;
12951558Srgrimes				kill(sp->se_process, SIGHUP);
12961558Srgrimes			}
12973594Sache			else if (   !old_getty
12983594Sache				 || !old_type && sp->se_type
12993594Sache				 || old_type && !sp->se_type
13003594Sache				 || !old_window && sp->se_window
13013594Sache				 || old_window && !sp->se_window
13023594Sache				 || strcmp(old_getty, sp->se_getty) != 0
13033594Sache				 || old_window && strcmp(old_window, sp->se_window) != 0
13043594Sache				 || old_type && strcmp(old_type, sp->se_type) != 0
13053594Sache				) {
13063594Sache				/* Don't set SE_SHUTDOWN here */
13073594Sache				sp->se_nspace = 0;
13083594Sache				sp->se_started = 0;
13093594Sache				kill(sp->se_process, SIGHUP);
13103594Sache			}
13113594Sache			if (old_getty)
13123594Sache				free(old_getty);
13133594Sache			if (old_getty)
13143594Sache				free(old_window);
13153594Sache			if (old_type)
13163594Sache				free(old_type);
13171558Srgrimes			continue;
13181558Srgrimes		}
13191558Srgrimes
13201558Srgrimes		new_session(sprev, session_index, typ);
13211558Srgrimes	}
13221558Srgrimes
13231558Srgrimes	endttyent();
13241558Srgrimes
13251558Srgrimes	return (state_func_t) multi_user;
13261558Srgrimes}
13271558Srgrimes
13281558Srgrimes/*
13291558Srgrimes * Block further logins.
13301558Srgrimes */
13311558Srgrimesstate_func_t
13321558Srgrimescatatonia()
13331558Srgrimes{
13341558Srgrimes	register session_t *sp;
13351558Srgrimes
13361558Srgrimes	for (sp = sessions; sp; sp = sp->se_next)
13371558Srgrimes		sp->se_flags |= SE_SHUTDOWN;
13381558Srgrimes
13391558Srgrimes	return (state_func_t) multi_user;
13401558Srgrimes}
13411558Srgrimes
13421558Srgrimes/*
13431558Srgrimes * Note SIGALRM.
13441558Srgrimes */
13451558Srgrimesvoid
13461558Srgrimesalrm_handler(sig)
13471558Srgrimes	int sig;
13481558Srgrimes{
13491558Srgrimes	clang = 1;
13501558Srgrimes}
13511558Srgrimes
13521558Srgrimes/*
13531558Srgrimes * Bring the system down to single user.
13541558Srgrimes */
13551558Srgrimesstate_func_t
13561558Srgrimesdeath()
13571558Srgrimes{
13581558Srgrimes	register session_t *sp;
13591558Srgrimes	register int i;
13601558Srgrimes	pid_t pid;
13611558Srgrimes	static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
13621558Srgrimes
13631558Srgrimes	for (sp = sessions; sp; sp = sp->se_next)
13641558Srgrimes		sp->se_flags |= SE_SHUTDOWN;
13651558Srgrimes
13661558Srgrimes	/* NB: should send a message to the session logger to avoid blocking. */
13671558Srgrimes	logwtmp("~", "shutdown", "");
13681558Srgrimes
13691558Srgrimes	for (i = 0; i < 3; ++i) {
13701558Srgrimes		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
13711558Srgrimes			return (state_func_t) single_user;
13721558Srgrimes
13731558Srgrimes		clang = 0;
13741558Srgrimes		alarm(DEATH_WATCH);
13751558Srgrimes		do
13761558Srgrimes			if ((pid = waitpid(-1, (int *)0, 0)) != -1)
13771558Srgrimes				collect_child(pid);
13781558Srgrimes		while (clang == 0 && errno != ECHILD);
13791558Srgrimes
13801558Srgrimes		if (errno == ECHILD)
13811558Srgrimes			return (state_func_t) single_user;
13821558Srgrimes	}
13831558Srgrimes
13841558Srgrimes	warning("some processes would not die; ps axl advised");
13851558Srgrimes
13861558Srgrimes	return (state_func_t) single_user;
13871558Srgrimes}
13885478Sachechar *
13895478Sachestrk (char *p)
13905478Sache{
13915478Sache    static char *t;
13925478Sache    char *q;
13935478Sache    int c;
13945478Sache
13955478Sache    if (p)
13965478Sache	t = p;
13975478Sache    if (!t)
13985478Sache	return 0;
13995478Sache
14005478Sache    c = *t;
14018871Srgrimes    while (c == ' ' || c == '\t' )
14025478Sache	c = *++t;
14035478Sache    if (!c) {
14045478Sache	t = 0;
14055478Sache	return 0;
14065478Sache    }
14075478Sache    q = t;
14085478Sache    if (c == '\'') {
14095478Sache	c = *++t;
14105478Sache	q = t;
14118871Srgrimes	while (c && c != '\'')
14125478Sache	    c = *++t;
14135478Sache	if (!c)  /* unterminated string */
14145478Sache	    q = t = 0;
14155478Sache	else
14165478Sache	    *t++ = 0;
14175478Sache    } else {
14185478Sache	while (c && c != ' ' && c != '\t' )
14195478Sache	    c = *++t;
14205478Sache	*t++ = 0;
14218871Srgrimes	if (!c)
14225478Sache	    t = 0;
14235478Sache    }
14245478Sache    return q;
14255478Sache}
1426