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