*BSD News Article 17146


Return to BSD News archive

Xref: sserve comp.unix.bsd:12103 alt.sources:6017 comp.os.386bsd.misc:460
Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!agate!howland.reston.ans.net!noc.near.net!uunet!bsdi.com!not-for-mail
From: donn@BSDI.COM (Donn Seeley)
Newsgroups: comp.unix.bsd,alt.sources,comp.os.386bsd.misc
Subject: freely redistributable /sbin/init for BSD
Followup-To: comp.unix.bsd
Date: 14 Jun 1993 20:04:28 -0400
Organization: Berkeley Software Design Inc., Outdoor Recreation Dept.
Lines: 1688
Distribution: world
Message-ID: <1vj3mc$4r3@BSDI.COM>
NNTP-Posting-Host: bsdi.com

[Check the README file for details...  -- Donn]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  init/README init/Makefile init/init.c init/init.8
# Wrapped by donn@callao.bsdi.com on Mon Jun 14 17:49:08 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'init/README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'init/README'\"
else
echo shar: Extracting \"'init/README'\" \(1945 characters\)
sed "s/^X//" >'init/README' <<'END_OF_FILE'
XThis is the source for a version of /sbin/init that runs on 4.4
XBSD and BSD/386 1.0 systems.  I wrote it for BSDI about two years
Xago and BSDI donated the source to UC Berkeley CSRG, but CSRG didn't
Xget it working in time to include it with the Networking 2 release,
Xalthough we had hoped it would appear there.  Since that time both
XBSDI and CSRG have worked on it to make it more useful and reliable,
Xand recently the two groups have merged our efforts and produced
Xa common version once more.  Since the source is freely redistributable
Xunder the terms of the standard BSD copyright, I'm posting it to
Xthe net on behalf of BSDI so other folks might get some use from
Xit.  Keep in mind that I'm only paid to support this code if you
Xbought a system from BSDI, so I probably won't be able to make very
Xtimely responses to random bug reports, but I am interested in
Xseeing them.  (More formally: Neither BSDI nor I personally will
Xmake any guarantees about the reliability or usefulness of this
Xcode, and it's unsupported unless you have a support agreement.)
X
XI also make no promises that this code will just drop in and work
Xon any particular system.  Please don't call me at home to tell me
Xthat you compiled and installed init, and now your system won't
Xboot.  With a program as sensitive as init, you should always be
Xprepared to fall back to an earlier working version if the new one
Xfails.  ALWAYS save your old version before installing, and ALWAYS
Xbe prepared to boot from an alternate root (e.g. a floppy) if the
Xnew init fails.  I strongly advise you to read the man page carefully
Xbefore you install.  One advance warning: I don't think anyone has
Xever tested the window system start-up code.  One of these days...
XThis code is running on my systems at home, and an earlier version
Xis running on hundreds of BSDI systems, so it can't be too broken.
X
XEnjoy,
X
XDonn Seeley
XBerkeley Software Design, Inc.  (Salt Lake City)
XJune 14, 1993
END_OF_FILE
if test 1945 -ne `wc -c <'init/README'`; then
    echo shar: \"'init/README'\" unpacked with wrong size!
fi
# end of 'init/README'
fi
if test -f 'init/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'init/Makefile'\"
else
echo shar: Extracting \"'init/Makefile'\" \(260 characters\)
sed "s/^X//" >'init/Makefile' <<'END_OF_FILE'
X#	BSDI $Id: Makefile,v 1.3 1993/06/14 20:06:22 donn Exp $
X#	@(#)Makefile	5.3 (Berkeley) 5/11/90
X
XPROG=	init
XMAN8=	init.0
X# XXX fix this when we have KERN_SECURELVL
XCFLAGS+=-DSECURE -DNOSYSCTL
XDPADD=	${LIBUTIL}
XLDADD=	-lutil
XBINMODE=500
X
X.include <bsd.prog.mk>
END_OF_FILE
if test 260 -ne `wc -c <'init/Makefile'`; then
    echo shar: \"'init/Makefile'\" unpacked with wrong size!
fi
# end of 'init/Makefile'
fi
if test -f 'init/init.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'init/init.c'\"
else
echo shar: Extracting \"'init/init.c'\" \(27538 characters\)
sed "s/^X//" >'init/init.c' <<'END_OF_FILE'
X/*-
X * Copyright (c) 1991 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Donn Seeley at Berkeley Software Design, Inc.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)init.c	6.22 (Berkeley) 6/2/93";
X#endif /* not lint */
X
X#include <sys/param.h>
X#ifndef NOSYSCTL
X#include <sys/sysctl.h>
X#endif
X#include <sys/wait.h>
X
X#include <db.h>
X#include <errno.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <syslog.h>
X#include <time.h>
X#include <ttyent.h>
X#include <unistd.h>
X
X#ifdef __STDC__
X#include <stdarg.h>
X#else
X#include <varargs.h>
X#endif
X
X#ifdef SECURE
X#include <pwd.h>
X#endif
X
X#include "pathnames.h"
X
X/*
X * Until the mythical util.h arrives...
X */
Xextern int login_tty __P((int));
Xextern int logout __P((const char *));
Xextern void logwtmp __P((const char *, const char *, const char *));
X
X/*
X * Sleep times; used to prevent thrashing.
X */
X#define	GETTY_SPACING		 5	/* N secs minimum getty spacing */
X#define	GETTY_SLEEP		30	/* sleep N secs after spacing problem */
X#define	WINDOW_WAIT		 3	/* wait N secs after starting window */
X#define	STALL_TIMEOUT		30	/* wait N secs after warning */
X#define	DEATH_WATCH		10	/* wait N secs for procs to die */
X
Xvoid handle __P((sig_t, ...));
Xvoid delset __P((sigset_t *, ...));
X
Xvoid stall __P((char *, ...));
Xvoid warning __P((char *, ...));
Xvoid emergency __P((char *, ...));
Xvoid disaster __P((int));
Xvoid badsys __P((int));
X
X/*
X * We really need a recursive typedef...
X * The following at least guarantees that the return type of (*state_t)()
X * is sufficiently wide to hold a function pointer.
X */
Xtypedef long (*state_func_t) __P((void));
Xtypedef state_func_t (*state_t) __P((void));
X
Xstate_func_t single_user __P((void));
Xstate_func_t runcom __P((void));
Xstate_func_t read_ttys __P((void));
Xstate_func_t multi_user __P((void));
Xstate_func_t clean_ttys __P((void));
Xstate_func_t catatonia __P((void));
Xstate_func_t death __P((void));
X
Xenum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
X
Xvoid transition __P((state_t));
Xstate_t requested_transition = runcom;
X
Xvoid setctty __P((char *));
X
Xtypedef struct init_session {
X	int	se_index;		/* index of entry in ttys file */
X	pid_t	se_process;		/* controlling process */
X	time_t	se_started;		/* used to avoid thrashing */
X	int	se_flags;		/* status of session */
X#define	SE_SHUTDOWN	0x1		/* session won't be restarted */
X	char	*se_device;		/* filename of port */
X	char	*se_getty;		/* what to run on that port */
X	char	**se_getty_argv;	/* pre-parsed argument array */
X	char	*se_window;		/* window system (started only once) */
X	char	**se_window_argv;	/* pre-parsed argument array */
X	struct	init_session *se_prev;
X	struct	init_session *se_next;
X} session_t;
X
Xvoid free_session __P((session_t *));
Xsession_t *new_session __P((session_t *, int, struct ttyent *));
Xsession_t *sessions;
X
Xchar **construct_argv __P((char *));
Xvoid start_window_system __P((session_t *));
Xvoid collect_child __P((pid_t));
Xpid_t start_getty __P((session_t *));
Xvoid transition_handler __P((int));
Xvoid alrm_handler __P((int));
Xvoid setsecuritylevel __P((int));
Xint getsecuritylevel __P((void));
Xint setupargv __P((session_t *, struct ttyent *));
Xint clang;
X
Xvoid clear_session_logs __P((session_t *));
X
Xint start_session_db __P((void));
Xvoid add_session __P((session_t *));
Xvoid del_session __P((session_t *));
Xsession_t *find_session __P((pid_t));
XDB *session_db;
X
X/*
X * The mother of all processes.
X */
Xint
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	int c;
X	struct sigaction sa;
X	sigset_t mask;
X
X
X	/* Dispose of random users. */
X	if (getuid() != 0) {
X		(void)fprintf(stderr, "init: %s\n", strerror(EPERM));
X		exit (1);
X	}
X
X	/* System V users like to reexec init. */
X	if (getpid() != 1) {
X		(void)fprintf(stderr, "init: already running\n");
X		exit (1);
X	}
X
X	/*
X	 * Note that this does NOT open a file...
X	 * Does 'init' deserve its own facility number?
X	 */
X	openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
X
X	/*
X	 * Create an initial session.
X	 */
X	if (setsid() < 0)
X		warning("initial setsid() failed: %m");
X
X	/*
X	 * This code assumes that we always get arguments through flags,
X	 * never through bits set in some random machine register.
X	 */
X	while ((c = getopt(argc, argv, "sf")) != -1)
X		switch (c) {
X		case 's':
X			requested_transition = single_user;
X			break;
X		case 'f':
X			runcom_mode = FASTBOOT;
X			break;
X		default:
X			warning("unrecognized flag '-%c'", c);
X			break;
X		}
X
X	if (optind != argc)
X		warning("ignoring excess arguments");
X
X	/*
X	 * We catch or block signals rather than ignore them,
X	 * so that they get reset on exec.
X	 */
X	handle(badsys, SIGSYS, 0);
X	handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
X	       SIGBUS, SIGXCPU, SIGXFSZ, 0);
X	handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0);
X	handle(alrm_handler, SIGALRM, 0);
X	sigfillset(&mask);
X	delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
X		SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0);
X	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
X	sigemptyset(&sa.sa_mask);
X	sa.sa_flags = 0;
X	sa.sa_handler = SIG_IGN;
X	(void) sigaction(SIGTTIN, &sa, (struct sigaction *)0);
X	(void) sigaction(SIGTTOU, &sa, (struct sigaction *)0);
X
X	/*
X	 * Paranoia.
X	 */
X	close(0);
X	close(1);
X	close(2);
X
X	/*
X	 * Start the state machine.
X	 */
X	transition(requested_transition);
X
X	/*
X	 * Should never reach here.
X	 */
X	return 1;
X}
X
X/*
X * Associate a function with a signal handler.
X */
Xvoid
X#ifdef __STDC__
Xhandle(sig_t handler, ...)
X#else
Xhandle(va_alist)
X	va_dcl
X#endif
X{
X	int sig;
X	struct sigaction sa;
X	int mask_everything;
X	va_list ap;
X#ifndef __STDC__
X	sig_t handler;
X
X	va_start(ap);
X	handler = va_arg(ap, sig_t);
X#else
X	va_start(ap, handler);
X#endif
X
X	sa.sa_handler = handler;
X	sigfillset(&mask_everything);
X
X	while (sig = va_arg(ap, int)) {
X		sa.sa_mask = mask_everything;
X		/* XXX SA_RESTART? */
X		sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
X		sigaction(sig, &sa, (struct sigaction *) 0);
X	}
X	va_end(ap);
X}
X
X/*
X * Delete a set of signals from a mask.
X */
Xvoid
X#ifdef __STDC__
Xdelset(sigset_t *maskp, ...)
X#else
Xdelset(va_alist)
X	va_dcl
X#endif
X{
X	int sig;
X	va_list ap;
X#ifndef __STDC__
X	sigset_t *maskp;
X
X	va_start(ap);
X	maskp = va_arg(ap, sigset_t *);
X#else
X	va_start(ap, maskp);
X#endif
X
X	while (sig = va_arg(ap, int))
X		sigdelset(maskp, sig);
X	va_end(ap);
X}
X
X/*
X * Log a message and sleep for a while (to give someone an opportunity
X * to read it and to save log or hardcopy output if the problem is chronic).
X * NB: should send a message to the session logger to avoid blocking.
X */
Xvoid
X#ifdef __STDC__
Xstall(char *message, ...)
X#else
Xstall(va_alist)
X	va_dcl
X#endif
X{
X	va_list ap;
X#ifndef __STDC__
X	char *message;
X
X	va_start(ap);
X	message = va_arg(ap, char *);
X#else
X	va_start(ap, message);
X#endif
X
X	vsyslog(LOG_ALERT, message, ap);
X	va_end(ap);
X	sleep(STALL_TIMEOUT);
X}
X
X/*
X * Like stall(), but doesn't sleep.
X * If cpp had variadic macros, the two functions could be #defines for another.
X * NB: should send a message to the session logger to avoid blocking.
X */
Xvoid
X#ifdef __STDC__
Xwarning(char *message, ...)
X#else
Xwarning(va_alist)
X	va_dcl
X#endif
X{
X	va_list ap;
X#ifndef __STDC__
X	char *message;
X
X	va_start(ap);
X	message = va_arg(ap, char *);
X#else
X	va_start(ap, message);
X#endif
X
X	vsyslog(LOG_ALERT, message, ap);
X	va_end(ap);
X}
X
X/*
X * Log an emergency message.
X * NB: should send a message to the session logger to avoid blocking.
X */
Xvoid
X#ifdef __STDC__
Xemergency(char *message, ...)
X#else
Xemergency(va_alist)
X	va_dcl
X#endif
X{
X	va_list ap;
X#ifndef __STDC__
X	char *message;
X
X	va_start(ap);
X	message = va_arg(ap, char *);
X#else
X	va_start(ap, message);
X#endif
X
X	vsyslog(LOG_EMERG, message, ap);
X	va_end(ap);
X}
X
X/*
X * Catch a SIGSYS signal.
X *
X * These may arise if a system does not support sysctl.
X * We tolerate up to 25 of these, then throw in the towel.
X */
Xvoid
Xbadsys(sig)
X	int sig;
X{
X	static int badcount = 0;
X
X	if (badcount++ < 25)
X		return;
X	disaster(sig);
X}
X
X/*
X * Catch an unexpected signal.
X */
Xvoid
Xdisaster(sig)
X	int sig;
X{
X	emergency("fatal signal: %s",
X		sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal");
X
X	sleep(STALL_TIMEOUT);
X	_exit(sig);		/* reboot */
X}
X
X/*
X * Get the security level of the kernel.
X */
Xint
Xgetsecuritylevel()
X{
X#ifdef KERN_SECURELVL
X	int name[2], curlevel;
X	size_t len;
X	extern int errno;
X
X	name[0] = CTL_KERN;
X	name[1] = KERN_SECURELVL;
X	len = sizeof curlevel;
X	if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
X		emergency("cannot get kernel security level: %s",
X		    strerror(errno));
X		return (-1);
X	}
X	return (curlevel);
X#else
X	return (-1);
X#endif
X}
X
X/*
X * Set the security level of the kernel.
X */
Xvoid
Xsetsecuritylevel(newlevel)
X	int newlevel;
X{
X#ifdef KERN_SECURELVL
X	int name[2], curlevel;
X	extern int errno;
X
X	curlevel = getsecuritylevel();
X	if (newlevel == curlevel)
X		return;
X	name[0] = CTL_KERN;
X	name[1] = KERN_SECURELVL;
X	if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
X		emergency(
X		    "cannot change kernel security level from %d to %d: %s",
X		    curlevel, newlevel, strerror(errno));
X		return;
X	}
X#ifdef SECURE
X	warning("kernel security level changed from %d to %d",
X	    curlevel, newlevel);
X#endif
X#endif
X}
X
X/*
X * Change states in the finite state machine.
X * The initial state is passed as an argument.
X */
Xvoid
Xtransition(s)
X	state_t s;
X{
X	for (;;)
X		s = (state_t) (*s)();
X}
X
X/*
X * Close out the accounting files for a login session.
X * NB: should send a message to the session logger to avoid blocking.
X */
Xvoid
Xclear_session_logs(sp)
X	session_t *sp;
X{
X	char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
X
X	if (logout(line))
X		logwtmp(line, "", "");
X}
X
X/*
X * Start a session and allocate a controlling terminal.
X * Only called by children of init after forking.
X */
Xvoid
Xsetctty(name)
X	char *name;
X{
X	int fd;
X
X	(void) revoke(name);
X	sleep (2);			/* leave DTR low */
X	if ((fd = open(name, O_RDWR)) == -1) {
X		stall("can't open %s: %m", name);
X		_exit(1);
X	}
X	if (login_tty(fd) == -1) {
X		stall("can't get %s for controlling terminal: %m", name);
X		_exit(1);
X	}
X}
X
X/*
X * Bring the system up single user.
X */
Xstate_func_t
Xsingle_user()
X{
X	pid_t pid, wpid;
X	int status;
X	sigset_t mask;
X	char *shell = _PATH_BSHELL;
X	char *argv[2];
X#ifdef SECURE
X	struct ttyent *typ;
X	struct passwd *pp;
X	static const char banner[] =
X		"Enter root password, or ^D to go multi-user\n";
X	char *clear, *password;
X#endif
X
X	/*
X	 * If the kernel is in secure mode, downgrade it to insecure mode.
X	 */
X	if (getsecuritylevel() > 0)
X		setsecuritylevel(0);
X
X	if ((pid = fork()) == 0) {
X		/*
X		 * Start the single user session.
X		 */
X		setctty(_PATH_CONSOLE);
X
X#ifdef SECURE
X		/*
X		 * Check the root password.
X		 * We don't care if the console is 'on' by default;
X		 * it's the only tty that can be 'off' and 'secure'.
X		 */
X		typ = getttynam("console");
X		pp = getpwnam("root");
X		if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) {
X			write(2, banner, sizeof banner - 1);
X			for (;;) {
X				clear = getpass("Password:");
X				if (clear == 0 || *clear == '\0')
X					_exit(0);
X				password = crypt(clear, pp->pw_passwd);
X				bzero(clear, _PASSWORD_LEN);
X				if (strcmp(password, pp->pw_passwd) == 0)
X					break;
X				warning("single-user login failed\n");
X			}
X		}
X		endttyent();
X		endpwent();
X#endif /* SECURE */
X
X#ifdef DEBUGSHELL
X		{
X			char altshell[128], *cp = altshell;
X			int num;
X
X#define	SHREQUEST \
X	"Enter pathname of shell or RETURN for sh: "
X			(void)write(STDERR_FILENO,
X			    SHREQUEST, sizeof(SHREQUEST) - 1);
X			while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
X			    num != 0 && *cp != '\n' && cp < &altshell[127])
X					cp++;
X			*cp = '\0';
X			if (altshell[0] != '\0')
X				shell = altshell;
X		}
X#endif /* DEBUGSHELL */
X
X		/*
X		 * Unblock signals.
X		 * We catch all the interesting ones,
X		 * and those are reset to SIG_DFL on exec.
X		 */
X		sigemptyset(&mask);
X		sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
X
X		/*
X		 * Fire off a shell.
X		 * If the default one doesn't work, try the Bourne shell.
X		 */
X		argv[0] = "-sh";
X		argv[1] = 0;
X		execv(shell, argv);
X		emergency("can't exec %s for single user: %m", shell);
X		execv(_PATH_BSHELL, argv);
X		emergency("can't exec %s for single user: %m", _PATH_BSHELL);
X		sleep(STALL_TIMEOUT);
X		_exit(1);
X	}
X
X	if (pid == -1) {
X		/*
X		 * We are seriously hosed.  Do our best.
X		 */
X		emergency("can't fork single-user shell, trying again");
X		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
X			continue;
X		return (state_func_t) single_user;
X	}
X
X	requested_transition = 0;
X	do {
X		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
X			collect_child(wpid);
X		if (wpid == -1) {
X			if (errno == EINTR)
X				continue;
X			warning("wait for single-user shell failed: %m; restarting");
X			return (state_func_t) single_user;
X		}
X		if (wpid == pid && WIFSTOPPED(status)) {
X			warning("init: shell stopped, restarting\n");
X			kill(pid, SIGCONT);
X			wpid = -1;
X		}
X	} while (wpid != pid && !requested_transition);
X
X	if (requested_transition)
X		return (state_func_t) requested_transition;
X
X	if (!WIFEXITED(status)) {
X		if (WTERMSIG(status) == SIGKILL) { 
X			/* 
X			 *  reboot(8) killed shell? 
X			 */
X			warning("single user shell terminated.");
X			sleep(STALL_TIMEOUT);
X			_exit(0);
X		} else {	
X			warning("single user shell terminated, restarting");
X			return (state_func_t) single_user;
X		}
X	}
X
X	runcom_mode = FASTBOOT;
X	return (state_func_t) runcom;
X}
X
X/*
X * Run the system startup script.
X */
Xstate_func_t
Xruncom()
X{
X	pid_t pid, wpid;
X	int status;
X	char *argv[4];
X	struct sigaction sa;
X
X	if ((pid = fork()) == 0) {
X		sigemptyset(&sa.sa_mask);
X		sa.sa_flags = 0;
X		sa.sa_handler = SIG_IGN;
X		(void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
X		(void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
X
X		setctty(_PATH_CONSOLE);
X
X		argv[0] = "sh";
X		argv[1] = _PATH_RUNCOM;
X		argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0;
X		argv[3] = 0;
X
X		sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
X
X		execv(_PATH_BSHELL, argv);
X		stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
X		_exit(1);	/* force single user mode */
X	}
X
X	if (pid == -1) {
X		emergency("can't fork for %s on %s: %m",
X			_PATH_BSHELL, _PATH_RUNCOM);
X		while (waitpid(-1, (int *) 0, WNOHANG) > 0)
X			continue;
X		sleep(STALL_TIMEOUT);
X		return (state_func_t) single_user;
X	}
X
X	/*
X	 * Copied from single_user().  This is a bit paranoid.
X	 */
X	do {
X		if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
X			collect_child(wpid);
X		if (wpid == -1) {
X			if (errno == EINTR)
X				continue;
X			warning("wait for %s on %s failed: %m; going to single user mode",
X				_PATH_BSHELL, _PATH_RUNCOM);
X			return (state_func_t) single_user;
X		}
X		if (wpid == pid && WIFSTOPPED(status)) {
X			warning("init: %s on %s stopped, restarting\n",
X				_PATH_BSHELL, _PATH_RUNCOM);
X			kill(pid, SIGCONT);
X			wpid = -1;
X		}
X	} while (wpid != pid);
X
X	if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
X	    requested_transition == catatonia) {
X		/* /etc/rc executed /sbin/reboot; wait for the end quietly */
X		sigset_t s;
X
X		sigfillset(&s);
X		for (;;)
X			sigsuspend(&s);
X	}
X
X	if (!WIFEXITED(status)) {
X		warning("%s on %s terminated abnormally, going to single user mode",
X			_PATH_BSHELL, _PATH_RUNCOM);
X		return (state_func_t) single_user;
X	}
X
X	if (WEXITSTATUS(status))
X		return (state_func_t) single_user;
X
X	runcom_mode = AUTOBOOT;		/* the default */
X	/* NB: should send a message to the session logger to avoid blocking. */
X	logwtmp("~", "reboot", "");
X	return (state_func_t) read_ttys;
X}
X
X/*
X * Open the session database.
X *
X * NB: We could pass in the size here; is it necessary?
X */
Xint
Xstart_session_db()
X{
X	if (session_db && (*session_db->close)(session_db))
X		emergency("session database close: %s", strerror(errno));
X	if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
X		emergency("session database open: %s", strerror(errno));
X		return (1);
X	}
X	return (0);
X		
X}
X
X/*
X * Add a new login session.
X */
Xvoid
Xadd_session(sp)
X	session_t *sp;
X{
X	DBT key;
X	DBT data;
X
X	key.data = &sp->se_process;
X	key.size = sizeof sp->se_process;
X	data.data = &sp;
X	data.size = sizeof sp;
X
X	if ((*session_db->put)(session_db, &key, &data, 0))
X		emergency("insert %d: %s", sp->se_process, strerror(errno));
X}
X
X/*
X * Delete an old login session.
X */
Xvoid
Xdel_session(sp)
X	session_t *sp;
X{
X	DBT key;
X
X	key.data = &sp->se_process;
X	key.size = sizeof sp->se_process;
X
X	if ((*session_db->del)(session_db, &key, 0))
X		emergency("delete %d: %s", sp->se_process, strerror(errno));
X}
X
X/*
X * Look up a login session by pid.
X */
Xsession_t *
X#ifdef __STDC__
Xfind_session(pid_t pid)
X#else
Xfind_session(pid)
X	pid_t pid;
X#endif
X{
X	DBT key;
X	DBT data;
X	session_t *ret;
X
X	key.data = &pid;
X	key.size = sizeof pid;
X	if ((*session_db->get)(session_db, &key, &data, 0) != 0)
X		return 0;
X	bcopy(data.data, (char *)&ret, sizeof(ret));
X	return ret;
X}
X
X/*
X * Construct an argument vector from a command line.
X */
Xchar **
Xconstruct_argv(command)
X	char *command;
X{
X	register int argc = 0;
X	register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
X						* sizeof (char *));
X	static const char separators[] = " \t";
X
X	if ((argv[argc++] = strtok(command, separators)) == 0)
X		return 0;
X	while (argv[argc++] = strtok((char *) 0, separators))
X		continue;
X	return argv;
X}
X
X/*
X * Deallocate a session descriptor.
X */
Xvoid
Xfree_session(sp)
X	register session_t *sp;
X{
X	free(sp->se_device);
X	if (sp->se_getty) {
X		free(sp->se_getty);
X		free(sp->se_getty_argv);
X	}
X	if (sp->se_window) {
X		free(sp->se_window);
X		free(sp->se_window_argv);
X	}
X	free(sp);
X}
X
X/*
X * Allocate a new session descriptor.
X */
Xsession_t *
Xnew_session(sprev, session_index, typ)
X	session_t *sprev;
X	int session_index;
X	register struct ttyent *typ;
X{
X	register session_t *sp;
X
X	if ((typ->ty_status & TTY_ON) == 0 ||
X	    typ->ty_name == 0 ||
X	    typ->ty_getty == 0)
X		return 0;
X
X	sp = (session_t *) malloc(sizeof (session_t));
X	bzero(sp, sizeof *sp);
X
X	sp->se_index = session_index;
X
X	sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
X	(void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
X
X	if (setupargv(sp, typ) == 0) {
X		free_session(sp);
X		return (0);
X	}
X
X	sp->se_next = 0;
X	if (sprev == 0) {
X		sessions = sp;
X		sp->se_prev = 0;
X	} else {
X		sprev->se_next = sp;
X		sp->se_prev = sprev;
X	}
X
X	return sp;
X}
X
X/*
X * Calculate getty and if useful window argv vectors.
X */
Xint
Xsetupargv(sp, typ)
X	session_t *sp;
X	struct ttyent *typ;
X{
X
X	if (sp->se_getty) {
X		free(sp->se_getty);
X		free(sp->se_getty_argv);
X	}
X	sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2);
X	(void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
X	sp->se_getty_argv = construct_argv(sp->se_getty);
X	if (sp->se_getty_argv == 0) {
X		warning("can't parse getty for port %s", sp->se_device);
X		free(sp->se_getty);
X		sp->se_getty = 0;
X		return (0);
X	}
X	if (typ->ty_window) {
X		if (sp->se_window)
X			free(sp->se_window);
X		sp->se_window = strdup(typ->ty_window);
X		sp->se_window_argv = construct_argv(sp->se_window);
X		if (sp->se_window_argv == 0) {
X			warning("can't parse window for port %s",
X				sp->se_device);
X			free(sp->se_window);
X			sp->se_window = 0;
X			return (0);
X		}
X	}
X	return (1);
X}
X
X/*
X * Walk the list of ttys and create sessions for each active line.
X */
Xstate_func_t
Xread_ttys()
X{
X	int session_index = 0;
X	register session_t *sp, *snext;
X	register struct ttyent *typ;
X
X	/*
X	 * Destroy any previous session state.
X	 * There shouldn't be any, but just in case...
X	 */
X	for (sp = sessions; sp; sp = snext) {
X		if (sp->se_process)
X			clear_session_logs(sp);
X		snext = sp->se_next;
X		free_session(sp);
X	}
X	sessions = 0;
X	if (start_session_db())
X		return (state_func_t) single_user;
X
X	/*
X	 * Allocate a session entry for each active port.
X	 * Note that sp starts at 0.
X	 */
X	while (typ = getttyent())
X		if (snext = new_session(sp, ++session_index, typ))
X			sp = snext;
X
X	endttyent();
X
X	return (state_func_t) multi_user;
X}
X
X/*
X * Start a window system running.
X */
Xvoid
Xstart_window_system(sp)
X	session_t *sp;
X{
X	pid_t pid;
X	sigset_t mask;
X
X	if ((pid = fork()) == -1) {
X		emergency("can't fork for window system on port %s: %m",
X			sp->se_device);
X		/* hope that getty fails and we can try again */
X		return;
X	}
X
X	if (pid)
X		return;
X
X	sigemptyset(&mask);
X	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
X
X	if (setsid() < 0)
X		emergency("setsid failed (window) %m");
X
X	execv(sp->se_window_argv[0], sp->se_window_argv);
X	stall("can't exec window system '%s' for port %s: %m",
X		sp->se_window_argv[0], sp->se_device);
X	_exit(1);
X}
X
X/*
X * Start a login session running.
X */
Xpid_t
Xstart_getty(sp)
X	session_t *sp;
X{
X	pid_t pid;
X	sigset_t mask;
X	time_t current_time = time((time_t *) 0);
X
X	/*
X	 * fork(), not vfork() -- we can't afford to block.
X	 */
X	if ((pid = fork()) == -1) {
X		emergency("can't fork for getty on port %s: %m", sp->se_device);
X		return -1;
X	}
X
X	if (pid)
X		return pid;
X
X	if (current_time > sp->se_started &&
X	    current_time - sp->se_started < GETTY_SPACING) {
X		warning("getty repeating too quickly on port %s, sleeping",
X		        sp->se_device);
X		sleep((unsigned) GETTY_SLEEP);
X	}
X
X	if (sp->se_window) {
X		start_window_system(sp);
X		sleep(WINDOW_WAIT);
X	}
X
X	sigemptyset(&mask);
X	sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
X
X	execv(sp->se_getty_argv[0], sp->se_getty_argv);
X	stall("can't exec getty '%s' for port %s: %m",
X		sp->se_getty_argv[0], sp->se_device);
X	_exit(1);
X}
X
X/*
X * Collect exit status for a child.
X * If an exiting login, start a new login running.
X */
Xvoid
X#ifdef __STDC__
Xcollect_child(pid_t pid)
X#else
Xcollect_child(pid)
X	pid_t pid;
X#endif
X{
X	register session_t *sp, *sprev, *snext;
X
X	if (! sessions)
X		return;
X
X	if (! (sp = find_session(pid)))
X		return;
X
X	clear_session_logs(sp);
X	del_session(sp);
X	sp->se_process = 0;
X
X	if (sp->se_flags & SE_SHUTDOWN) {
X		if (sprev = sp->se_prev)
X			sprev->se_next = sp->se_next;
X		else
X			sessions = sp->se_next;
X		if (snext = sp->se_next)
X			snext->se_prev = sp->se_prev;
X		free_session(sp);
X		return;
X	}
X
X	if ((pid = start_getty(sp)) == -1) {
X		/* serious trouble */
X		requested_transition = clean_ttys;
X		return;
X	}
X
X	sp->se_process = pid;
X	sp->se_started = time((time_t *) 0);
X	add_session(sp);
X}
X
X/*
X * Catch a signal and request a state transition.
X */
Xvoid
Xtransition_handler(sig)
X	int sig;
X{
X
X	switch (sig) {
X	case SIGHUP:
X		requested_transition = clean_ttys;
X		break;
X	case SIGTERM:
X		requested_transition = death;
X		break;
X	case SIGTSTP:
X		requested_transition = catatonia;
X		break;
X	default:
X		requested_transition = 0;
X		break;
X	}
X}
X
X/*
X * Take the system multiuser.
X */
Xstate_func_t
Xmulti_user()
X{
X	pid_t pid;
X	register session_t *sp;
X
X	requested_transition = 0;
X
X	/*
X	 * If the administrator has not set the security level to -1
X	 * to indicate that the kernel should not run multiuser in secure
X	 * mode, and the run script has not set a higher level of security 
X	 * than level 1, then put the kernel into secure mode.
X	 */
X	if (getsecuritylevel() == 0)
X		setsecuritylevel(1);
X
X	for (sp = sessions; sp; sp = sp->se_next) {
X		if (sp->se_process)
X			continue;
X		if ((pid = start_getty(sp)) == -1) {
X			/* serious trouble */
X			requested_transition = clean_ttys;
X			break;
X		}
X		sp->se_process = pid;
X		sp->se_started = time((time_t *) 0);
X		add_session(sp);
X	}
X
X	while (!requested_transition)
X		if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
X			collect_child(pid);
X
X	return (state_func_t) requested_transition;
X}
X
X/*
X * This is an n-squared algorithm.  We hope it isn't run often...
X */
Xstate_func_t
Xclean_ttys()
X{
X	register session_t *sp, *sprev;
X	register struct ttyent *typ;
X	register int session_index = 0;
X	register int devlen;
X
X	if (! sessions)
X		return (state_func_t) multi_user;
X
X	devlen = sizeof(_PATH_DEV) - 1;
X	while (typ = getttyent()) {
X		++session_index;
X
X		for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
X			if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
X				break;
X
X		if (sp) {
X			if (sp->se_index != session_index) {
X				warning("port %s changed utmp index from %d to %d",
X				       sp->se_device, sp->se_index,
X				       session_index);
X				sp->se_index = session_index;
X			}
X			if ((typ->ty_status & TTY_ON) == 0 ||
X			    typ->ty_getty == 0) {
X				sp->se_flags |= SE_SHUTDOWN;
X				kill(sp->se_process, SIGHUP);
X				continue;
X			}
X			sp->se_flags &= ~SE_SHUTDOWN;
X			if (setupargv(sp, typ) == 0) {
X				warning("can't parse getty for port %s",
X					sp->se_device);
X				sp->se_flags |= SE_SHUTDOWN;
X				kill(sp->se_process, SIGHUP);
X			}
X			continue;
X		}
X
X		new_session(sprev, session_index, typ);
X	}
X
X	endttyent();
X
X	return (state_func_t) multi_user;
X}
X
X/*
X * Block further logins.
X */
Xstate_func_t
Xcatatonia()
X{
X	register session_t *sp;
X
X	for (sp = sessions; sp; sp = sp->se_next)
X		sp->se_flags |= SE_SHUTDOWN;
X
X	return (state_func_t) multi_user;
X}
X
X/*
X * Note SIGALRM.
X */
Xvoid
Xalrm_handler(sig)
X	int sig;
X{
X	clang = 1;
X}
X
X/*
X * Bring the system down to single user.
X */
Xstate_func_t
Xdeath()
X{
X	register session_t *sp;
X	register int i;
X	pid_t pid;
X	static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
X
X	for (sp = sessions; sp; sp = sp->se_next)
X		sp->se_flags |= SE_SHUTDOWN;
X
X	/* NB: should send a message to the session logger to avoid blocking. */
X	logwtmp("~", "shutdown", "");
X
X	for (i = 0; i < 3; ++i) {
X		if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
X			return (state_func_t) single_user;
X
X		clang = 0;
X		alarm(DEATH_WATCH);
X		do
X			if ((pid = waitpid(-1, (int *)0, 0)) != -1)
X				collect_child(pid);
X		while (clang == 0 && errno != ECHILD);
X
X		if (errno == ECHILD)
X			return (state_func_t) single_user;
X	}
X
X	warning("some processes would not die; ps axl advised");
X
X	return (state_func_t) single_user;
X}
END_OF_FILE
if test 27538 -ne `wc -c <'init/init.c'`; then
    echo shar: \"'init/init.c'\" unpacked with wrong size!
fi
# end of 'init/init.c'
fi
if test -f 'init/init.8' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'init/init.8'\"
else
echo shar: Extracting \"'init/init.8'\" \(8321 characters\)
sed "s/^X//" >'init/init.8' <<'END_OF_FILE'
X.\" Copyright (c) 1980, 1991 Regents of the University of California.
X.\" All rights reserved.
X.\"
X.\" This code is derived from software contributed to Berkeley by
X.\" Donn Seeley at Berkeley Software Design, Inc.
X.\"
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\"    notice, this list of conditions and the following disclaimer.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\"    notice, this list of conditions and the following disclaimer in the
X.\"    documentation and/or other materials provided with the distribution.
X.\" 3. All advertising materials mentioning features or use of this software
X.\"    must display the following acknowledgement:
X.\"	This product includes software developed by the University of
X.\"	California, Berkeley and its contributors.
X.\" 4. Neither the name of the University nor the names of its contributors
X.\"    may be used to endorse or promote products derived from this software
X.\"    without specific prior written permission.
X.\"
X.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X.\" SUCH DAMAGE.
X.\"
X.\"     @(#)init.8	6.7 (Berkeley) 6/2/93
X.\"
X.Dd June 2, 1993
X.Dt INIT 8
X.Os BSD 4
X.Sh NAME
X.Nm init
X.Nd process control initialization
X.Sh SYNOPSIS
X.Nm init
X.Sh DESCRIPTION
XThe
X.Nm init
Xprogram
Xis the last stage of the boot process.
XIt normally runs the automatic reboot sequence as described in
X.Xr reboot 8 ,
Xand if this succeeds, begins multi-user operation.
XIf the reboot scripts fail,
X.Nm init
Xcommences single user operation by giving
Xthe super-user a shell on the console.
XThe
X.Nm init
Xprogram may be passed parameters
Xfrom the boot program to
Xprevent the system from going multi-user and to instead execute
Xa single user shell without starting the normal daemons.
XThe system is then quiescent for maintenance work and may
Xlater be made to go to multi-user by exiting the
Xsingle-user shell (with ^D).
XThis
Xcauses
X.Nm init
Xto run the
X.Pa /etc/rc
Xstart up command file in fastboot mode (skipping disk checks).
X.Pp
XIf the
X.Nm console
Xentry in the
X.Xr ttys 5
Xfile is marked ``insecure'',
Xthen
X.Nm init
Xwill require that the superuser password be
Xentered before the system will start a single-user shell.
XThe password check is skipped if the 
X.Nm console
Xis marked as ``secure''.
X.Pp
XThe kernel runs with four different levels of security.
XAny superuser process can raise the security level, but only 
X.Nm init
Xcan lower it.
XSecurity levels are defined as follows:
X.Bl -tag -width flag
X.It Ic -1
XPermanently insecure mode \- always run system in level 0 mode.
X.It Ic 0
XInsecure mode \- immutable and append-only flags may be turned off.
XAll devices may be read or written subject to their permissions.
X.It Ic 1
XSecure mode \- immutable and append-only flags may not be changed;
Xdisks for mounted filesystems,
X.Pa /dev/mem ,
Xand
X.Pa /dev/kmem
Xare read-only.
X.It Ic 2
XHighly secure mode \- same as secure mode, plus disks are always
Xread-only whether mounted or not.
XThis level precludes tampering with filesystems by unmounting them,
Xbut also inhibits running
X.Xr newfs 8
Xwhile the system is multi-user.
X.El
X.Pp
XNormally, the system runs in level 0 mode while single user
Xand in level 1 mode while multiuser.
XIf the level 2 mode is desired while running multiuser,
Xit can be set in the startup script
X.Pa /etc/rc
Xusing
X.Xr sysctl 1 .
XIf it is desired to run the system in level 0 mode while multiuser,
Xthe administrator must build a kernel with the variable
X.Nm securelevel
Xin the kernel source file
X.Pa /sys/kern/kern_sysctl.c
Xinitialized to -1.
X.Pp
XIn multi-user operation, 
X.Nm init
Xmaintains
Xprocesses for the terminal ports found in the file
X.Xr ttys 5 .
X.Nm Init
Xreads this file, and executes the command found in the second field.
XThis command is usually
X.Xr getty 8 ;
X.Xr getty
Xopens and initializes the tty line
Xand
Xexecutes the
X.Xr login
Xprogram.
XThe
X.Xr login
Xprogram, when a valid user logs in,
Xexecutes a shell for that user.  When this shell
Xdies, either because the user logged out
Xor an abnormal termination occurred (a signal),
Xthe
X.Nm init
Xprogram wakes up, deletes the user
Xfrom the
X.Xr utmp 5
Xfile of current users and records the logout in the
X.Xr wtmp
Xfile.
XThe cycle is
Xthen restarted by
X.Nm init
Xexecuting a new
X.Xr getty
Xfor the line.
X.Pp
XLine status (on, off, secure, getty, or window information)
Xmay be changed in the
X.Xr ttys
Xfile without a reboot by sending the signal
X.Dv SIGHUP
Xto
X.Nm init
Xwith the command
X.Dq Li "kill -HUP 1" .
XOn receipt of this signal,
X.Nm init
Xre-reads the
X.Xr ttys
Xfile.
XWhen a line is turned off in
X.Xr ttys ,
X.Nm init
Xwill send a SIGHUP signal to the controlling process
Xfor the session associated with the line.
XFor any lines that were previously turned off in the
X.Xr ttys
Xfile and are now on,
X.Nm init
Xexecutes a new
X.Xr getty
Xto enable a new login.
XIf the getty or window field for a line is changed,
Xthe change takes effect at the end of the current
Xlogin session (e.g., the next time 
X.Nm init
Xstarts a process on the line).
XIf a line is commented out or deleted from
X.Xr ttys ,
X.Nm init
Xwill not do anything at all to that line.
XHowever, it will complain that the relationship between lines
Xin the
X.Xr ttys
Xfile and records in the
X.Xr utmp
Xfile is out of sync,
Xso this practice is not recommended.
X.Pp
X.Nm Init
Xwill terminate multi-user operations and resume single-user mode
Xif sent a terminate
X.Pq Dv TERM
Xsignal, for example,
X.Dq Li "kill \-TERM 1" .
XIf there are processes outstanding that are deadlocked (because of
Xhardware or software failure),
X.Xr init
Xwill not wait for them all to die (which might take forever), but
Xwill time out after 30 seconds and print a warning message.
X.Pp
X.Nm Init
Xwill cease creating new
X.Xr getty Ns 's
Xand allow the system to slowly die away, if it is sent a terminal stop
X.Pq Dv TSTP
Xsignal, i.e.
X.Dq Li "kill \-TSTP 1" .
XA later hangup will resume full
Xmulti-user operations, or a terminate will start a single user shell.
XThis hook is used by
X.Xr reboot 8
Xand
X.Xr halt 8 .
X.Pp
XThe role of
X.Nm init
Xis so critical that if it dies, the system will reboot itself
Xautomatically.
XIf, at bootstrap time, the
X.Xr init
Xprocess cannot be located, the system will panic with the message
X``panic: "init died (signal %d, exit %d)''.
X.Sh DIAGNOSTICS
X.Bl -diag
X.It "getty repeating too quickly on port %s, sleeping"
XA process being started to service a line is exiting quickly
Xeach time it is started.
XThis is often caused by a ringing or noisy terminal line.
X.Em "Init will sleep for 10 seconds" ,
X.Em "then continue trying to start the process" .
X.Pp
X.It "some processes would not die; ps axl advised."
XA process
Xis hung and could not be killed when the system was shutting down.
XThis condition is usually caused by a process
Xthat is stuck in a device driver because of
Xa persistent device error condition.
X.El
X.Sh FILES
X.Bl -tag -width /var/log/wtmp -compact
X.It Pa /dev/console
XSystem console device.
X.It Pa /dev/tty*
XTerminal ports found in
X.Xr ttys .
X.It Pa /var/run/utmp
XRecord of Current users on the system.
X.It Pa /var/log/wtmp
XRecord of all logins and logouts.
X.It Pa /etc/ttys
XThe terminal initialization information file.
X.It Pa /etc/rc
XSystem startup commands.
X.El
X.Sh SEE ALSO
X.Xr login 1 ,
X.Xr kill 1 ,
X.Xr sh 1 ,
X.Xr ttys 5 ,
X.Xr crash 8 ,
X.Xr getty 8 ,
X.Xr rc 8 ,
X.Xr reboot 8 ,
X.Xr halt 8 ,
X.Xr shutdown 8
X.Sh HISTORY
XA
X.Nm
Xcommand appeared in
X.At v6 .
X.Sh BUGS
XSystems without
X.Xr sysctl
Xbehave as though they have security level \-1.
END_OF_FILE
if test 8321 -ne `wc -c <'init/init.8'`; then
    echo shar: \"'init/init.8'\" unpacked with wrong size!
fi
# end of 'init/init.8'
fi
echo shar: End of shell archive.
exit 0