diff --git a/sbin/init/Makefile b/sbin/init/Makefile index 81ecd371dea0..2d4d2d92047f 100644 --- a/sbin/init/Makefile +++ b/sbin/init/Makefile @@ -1,15 +1,13 @@ -# $Id: Makefile,v 1.6 1993/06/10 01:02:59 cgd Exp $ +# from: @(#)Makefile 5.3 (Berkeley) 5/11/90 +# from: BSDI Id: Makefile,v 1.3 1993/06/14 20:06:22 donn Exp +# $Id: Makefile,v 1.7 1993/06/18 20:51:45 cgd Exp $ PROG= init -#SRCS= init.c -CFLAGS+= -DSECURE_CONSOLE -DUSE_DEVFS +MAN8= init.0 +# XXX fix this when we have KERN_SECURELVL +CFLAGS+=-DSECURE -DNOSYSCTL DPADD= ${LIBUTIL} LDADD= -lutil -NOMAN=noman -LDFLAGS= -.ifndef EXPORTABLE_SYSTEM -DPADD+= ${LIBCRYPT} -LDADD+= -lcrypt -.endif +BINMODE=500 .include diff --git a/sbin/init/README b/sbin/init/README new file mode 100644 index 000000000000..ede363db84f3 --- /dev/null +++ b/sbin/init/README @@ -0,0 +1,35 @@ +This is the source for a version of /sbin/init that runs on 4.4 +BSD and BSD/386 1.0 systems. I wrote it for BSDI about two years +ago and BSDI donated the source to UC Berkeley CSRG, but CSRG didn't +get it working in time to include it with the Networking 2 release, +although we had hoped it would appear there. Since that time both +BSDI and CSRG have worked on it to make it more useful and reliable, +and recently the two groups have merged our efforts and produced +a common version once more. Since the source is freely redistributable +under the terms of the standard BSD copyright, I'm posting it to +the net on behalf of BSDI so other folks might get some use from +it. Keep in mind that I'm only paid to support this code if you +bought a system from BSDI, so I probably won't be able to make very +timely responses to random bug reports, but I am interested in +seeing them. (More formally: Neither BSDI nor I personally will +make any guarantees about the reliability or usefulness of this +code, and it's unsupported unless you have a support agreement.) + +I also make no promises that this code will just drop in and work +on any particular system. Please don't call me at home to tell me +that you compiled and installed init, and now your system won't +boot. With a program as sensitive as init, you should always be +prepared to fall back to an earlier working version if the new one +fails. ALWAYS save your old version before installing, and ALWAYS +be prepared to boot from an alternate root (e.g. a floppy) if the +new init fails. I strongly advise you to read the man page carefully +before you install. One advance warning: I don't think anyone has +ever tested the window system start-up code. One of these days... +This code is running on my systems at home, and an earlier version +is running on hundreds of BSDI systems, so it can't be too broken. + +Enjoy, + +Donn Seeley +Berkeley Software Design, Inc. (Salt Lake City) +June 14, 1993 diff --git a/sbin/init/init.8 b/sbin/init/init.8 new file mode 100644 index 000000000000..bdf1a4f111b9 --- /dev/null +++ b/sbin/init/init.8 @@ -0,0 +1,292 @@ +.\" Copyright (c) 1980, 1991 Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Donn Seeley at Berkeley Software Design, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)init.8 6.7 (Berkeley) 6/2/93 +.\" $Id: init.8,v 1.1 1993/06/18 20:51:47 cgd Exp $ +.\" +.Dd June 2, 1993 +.Dt INIT 8 +.Os BSD 4 +.Sh NAME +.Nm init +.Nd process control initialization +.Sh SYNOPSIS +.Nm init +.Sh DESCRIPTION +The +.Nm init +program +is the last stage of the boot process. +It normally runs the automatic reboot sequence as described in +.Xr reboot 8 , +and if this succeeds, begins multi-user operation. +If the reboot scripts fail, +.Nm init +commences single user operation by giving +the super-user a shell on the console. +The +.Nm init +program may be passed parameters +from the boot program to +prevent the system from going multi-user and to instead execute +a single user shell without starting the normal daemons. +The system is then quiescent for maintenance work and may +later be made to go to multi-user by exiting the +single-user shell (with ^D). +This +causes +.Nm init +to run the +.Pa /etc/rc +start up command file in fastboot mode (skipping disk checks). +.Pp +If the +.Nm console +entry in the +.Xr ttys 5 +file is marked ``insecure'', +then +.Nm init +will require that the superuser password be +entered before the system will start a single-user shell. +The password check is skipped if the +.Nm console +is marked as ``secure''. +.Pp +The kernel runs with four different levels of security. +Any superuser process can raise the security level, but only +.Nm init +can lower it. +Security levels are defined as follows: +.Bl -tag -width flag +.It Ic -1 +Permanently insecure mode \- always run system in level 0 mode. +.It Ic 0 +Insecure mode \- immutable and append-only flags may be turned off. +All devices may be read or written subject to their permissions. +.It Ic 1 +Secure mode \- immutable and append-only flags may not be changed; +disks for mounted filesystems, +.Pa /dev/mem , +and +.Pa /dev/kmem +are read-only. +.It Ic 2 +Highly secure mode \- same as secure mode, plus disks are always +read-only whether mounted or not. +This level precludes tampering with filesystems by unmounting them, +but also inhibits running +.Xr newfs 8 +while the system is multi-user. +.El +.Pp +Normally, the system runs in level 0 mode while single user +and in level 1 mode while multiuser. +If the level 2 mode is desired while running multiuser, +it can be set in the startup script +.Pa /etc/rc +using +.Xr sysctl 1 . +If it is desired to run the system in level 0 mode while multiuser, +the administrator must build a kernel with the variable +.Nm securelevel +in the kernel source file +.Pa /sys/kern/kern_sysctl.c +initialized to -1. +.Pp +In multi-user operation, +.Nm init +maintains +processes for the terminal ports found in the file +.Xr ttys 5 . +.Nm Init +reads this file, and executes the command found in the second field. +This command is usually +.Xr getty 8 ; +.Xr getty +opens and initializes the tty line +and +executes the +.Xr login +program. +The +.Xr login +program, when a valid user logs in, +executes a shell for that user. When this shell +dies, either because the user logged out +or an abnormal termination occurred (a signal), +the +.Nm init +program wakes up, deletes the user +from the +.Xr utmp 5 +file of current users and records the logout in the +.Xr wtmp +file. +The cycle is +then restarted by +.Nm init +executing a new +.Xr getty +for the line. +.Pp +Line status (on, off, secure, getty, or window information) +may be changed in the +.Xr ttys +file without a reboot by sending the signal +.Dv SIGHUP +to +.Nm init +with the command +.Dq Li "kill -HUP 1" . +On receipt of this signal, +.Nm init +re-reads the +.Xr ttys +file. +When a line is turned off in +.Xr ttys , +.Nm init +will send a SIGHUP signal to the controlling process +for the session associated with the line. +For any lines that were previously turned off in the +.Xr ttys +file and are now on, +.Nm init +executes a new +.Xr getty +to enable a new login. +If the getty or window field for a line is changed, +the change takes effect at the end of the current +login session (e.g., the next time +.Nm init +starts a process on the line). +If a line is commented out or deleted from +.Xr ttys , +.Nm init +will not do anything at all to that line. +However, it will complain that the relationship between lines +in the +.Xr ttys +file and records in the +.Xr utmp +file is out of sync, +so this practice is not recommended. +.Pp +.Nm Init +will terminate multi-user operations and resume single-user mode +if sent a terminate +.Pq Dv TERM +signal, for example, +.Dq Li "kill \-TERM 1" . +If there are processes outstanding that are deadlocked (because of +hardware or software failure), +.Xr init +will not wait for them all to die (which might take forever), but +will time out after 30 seconds and print a warning message. +.Pp +.Nm Init +will cease creating new +.Xr getty Ns 's +and allow the system to slowly die away, if it is sent a terminal stop +.Pq Dv TSTP +signal, i.e. +.Dq Li "kill \-TSTP 1" . +A later hangup will resume full +multi-user operations, or a terminate will start a single user shell. +This hook is used by +.Xr reboot 8 +and +.Xr halt 8 . +.Pp +The role of +.Nm init +is so critical that if it dies, the system will reboot itself +automatically. +If, at bootstrap time, the +.Xr init +process cannot be located, the system will panic with the message +``panic: "init died (signal %d, exit %d)''. +.Sh DIAGNOSTICS +.Bl -diag +.It "getty repeating too quickly on port %s, sleeping" +A process being started to service a line is exiting quickly +each time it is started. +This is often caused by a ringing or noisy terminal line. +.Em "Init will sleep for 10 seconds" , +.Em "then continue trying to start the process" . +.Pp +.It "some processes would not die; ps axl advised." +A process +is hung and could not be killed when the system was shutting down. +This condition is usually caused by a process +that is stuck in a device driver because of +a persistent device error condition. +.El +.Sh FILES +.Bl -tag -width /var/log/wtmp -compact +.It Pa /dev/console +System console device. +.It Pa /dev/tty* +Terminal ports found in +.Xr ttys . +.It Pa /var/run/utmp +Record of Current users on the system. +.It Pa /var/log/wtmp +Record of all logins and logouts. +.It Pa /etc/ttys +The terminal initialization information file. +.It Pa /etc/rc +System startup commands. +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr kill 1 , +.Xr sh 1 , +.Xr ttys 5 , +.Xr crash 8 , +.Xr getty 8 , +.Xr rc 8 , +.Xr reboot 8 , +.Xr halt 8 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +.Sh BUGS +Systems without +.Xr sysctl +behave as though they have security level \-1. diff --git a/sbin/init/init.c b/sbin/init/init.c index 4015f403fa4a..9c7096efa0ba 100644 --- a/sbin/init/init.c +++ b/sbin/init/init.c @@ -1,7 +1,10 @@ -/* - * Copyright (c) 1986, 1987, 1992 Daniel D. Lanciani. +/*- + * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -12,16 +15,16 @@ * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by - * Daniel D. Lanciani. - * 4. The name of the author may not - * be used to endorse or promote products derived from this software + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY Daniel D. Lanciani ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL Daniel D. Lanciani BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -29,435 +32,1262 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $Id: init.c,v 1.7 1993/06/10 01:03:02 cgd Exp $ */ -#include -#include -#include +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/* from: static char sccsid[] = "@(#)init.c 6.22 (Berkeley) 6/2/93"; */ +static char rcsid[] = "$Id: init.c,v 1.8 1993/06/18 20:51:49 cgd Exp $"; +#endif /* not lint */ + +#include +#ifndef NOSYSCTL +#include +#endif #include -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#ifdef SECURE_CONSOLE +#ifdef __STDC__ +#include +#else +#include +#endif + +#ifdef SECURE #include #endif -#ifdef USE_DEVFS -#include -#endif +#include "pathnames.h" -#define NTTY 32 /* max ttys */ -#define NARG 16 /* max args to login/getty */ +/* + * Until the mythical util.h arrives... + */ +extern int login_tty __P((int)); +extern int logout __P((const char *)); +extern void logwtmp __P((const char *, const char *, const char *)); -/* internal flags */ -#define TTY_SEEN 0x8000 -#define TTY_DIFF 0x4000 -#define TTY_LOGIN 0x2000 +/* + * Sleep times; used to prevent thrashing. + */ +#define GETTY_SPACING 5 /* N secs minimum getty spacing */ +#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ +#define WINDOW_WAIT 3 /* wait N secs after starting window */ +#define STALL_TIMEOUT 30 /* wait N secs after warning */ +#define DEATH_WATCH 10 /* wait N secs for procs to die */ -/* non-standard tty_logout: rerun login/getty with -o switch to clean line */ -#ifndef TTY_LOGOUT -#define TTY_LOGOUT 0x1000 -#endif +void handle __P((sig_t, ...)); +void delset __P((sigset_t *, ...)); -/* non-standard tty_open: open device for login/getty */ -#ifndef TTY_OPEN -#define TTY_OPEN 0x0800 -#endif +void stall __P((char *, ...)); +void warning __P((char *, ...)); +void emergency __P((char *, ...)); +void disaster __P((int)); +void badsys __P((int)); -#define isspace(c) ((c) == ' ' || (c) == '\t') +/* + * We really need a recursive typedef... + * The following at least guarantees that the return type of (*state_t)() + * is sufficiently wide to hold a function pointer. + */ +typedef long (*state_func_t) __P((void)); +typedef state_func_t (*state_t) __P((void)); -struct ttytab { - char *tt_name; - char *tt_getty; - char *tt_type; - int tt_status; - int tt_pid; -} ttytab[NTTY], *ttytabend = ttytab; -int drain, sflag; -char arg[128], nam[64], term[64], *env[] = { term, 0 }; -jmp_buf single, reread; -char *Reboot = "autoboot"; +state_func_t single_user __P((void)); +state_func_t runcom __P((void)); +state_func_t read_ttys __P((void)); +state_func_t multi_user __P((void)); +state_func_t clean_ttys __P((void)); +state_func_t catatonia __P((void)); +state_func_t death __P((void)); -char *newstring(), *malloc(); -extern int errno; +enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; -/* signal state of child process */ -#define SIGNALSFORCHILD \ - signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); \ - signal(SIGTERM, SIG_DFL); signal(SIGALRM, SIG_DFL); \ - signal(SIGTSTP, SIG_DFL); signal(SIGCHLD, SIG_DFL); \ - signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); \ - sigsetmask( 0); /* 04 Sep 92*/ +void transition __P((state_t)); +state_t requested_transition = runcom; -/* SIGHUP: reread /etc/ttys */ -void -shup(sig) -{ - longjmp(reread, 1); -} +void setctty __P((char *)); -/* SIGALRM: abort wait and go single user */ -void -salrm(sig) -{ - signal(SIGALRM, SIG_DFL); - warn("process hung"); - longjmp(single, 1); -} +typedef struct init_session { + int se_index; /* index of entry in ttys file */ + pid_t se_process; /* controlling process */ + time_t se_started; /* used to avoid thrashing */ + int se_flags; /* status of session */ +#define SE_SHUTDOWN 0x1 /* session won't be restarted */ + char *se_device; /* filename of port */ + char *se_getty; /* what to run on that port */ + char **se_getty_argv; /* pre-parsed argument array */ + char *se_window; /* window system (started only once) */ + char **se_window_argv; /* pre-parsed argument array */ + struct init_session *se_prev; + struct init_session *se_next; +} session_t; -/* SIGTERM: go single user */ -void -sterm(sig) -{ - register struct ttytab *tt; +void free_session __P((session_t *)); +session_t *new_session __P((session_t *, int, struct ttyent *)); +session_t *sessions; - if (!Reboot) { - for(tt = ttytab; tt < ttytabend; tt++) { - free(tt->tt_name); - free(tt->tt_getty); - free(tt->tt_type); - } - ttytabend = ttytab; - /* give processes time to exit cleanly */ /* 15 Aug 92*/ - kill(-1, SIGTERM); - sleep(10); - /* Now murder them */ - kill(-1, SIGKILL); - kill(-1, SIGCONT); - signal(SIGALRM, salrm); - alarm(30); - while(wait((int *)0) > 0); - alarm(0); - signal(SIGALRM, SIG_DFL); - longjmp(single, 1); - } -} +char **construct_argv __P((char *)); +void start_window_system __P((session_t *)); +void collect_child __P((pid_t)); +pid_t start_getty __P((session_t *)); +void transition_handler __P((int)); +void alrm_handler __P((int)); +void setsecuritylevel __P((int)); +int getsecuritylevel __P((void)); +int setupargv __P((session_t *, struct ttyent *)); +int clang; -/* SIGTSTP: drain system */ -void -ststp(sig) -{ - drain = 1; -} +void clear_session_logs __P((session_t *)); -/* init [-s] [-f] */ +int start_session_db __P((void)); +void add_session __P((session_t *)); +void del_session __P((session_t *)); +session_t *find_session __P((pid_t)); +DB *session_db; +/* + * The mother of all processes. + */ +int main(argc, argv) -char **argv; + int argc; + char **argv; { - register int pid; - register struct ttytab *tt; - struct ttyent *ty; - int status; - long mask = sigblock(sigmask(SIGHUP) | sigmask(SIGTERM)); + int c; + struct sigaction sa; + sigset_t mask; - /* did some idiot try to run us? */ - if(getpid() != 1) { - writes(2,"init: sorry, system daemon, runnable only by system\n"); - exit(0xff); + + /* Dispose of random users. */ + if (getuid() != 0) { + (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); + exit (1); } - /* allocate a session for init */ - (void) setsid(); - - /* protect against signals, listen for outside requests */ - signal(SIGHUP, shup); - signal(SIGTSTP, ststp); - - signal (SIGTTIN, SIG_IGN); - signal (SIGTTOU, SIG_IGN); - signal (SIGCHLD, SIG_IGN); - signal (SIGINT, SIG_IGN); - - /* handle arguments, if any */ - if(argc > 1) - if(!strcmp(argv[1], "-s")) - sflag++; - else if(!strcmp(argv[1], "-f")) - Reboot = 0; - -#ifdef USE_DEVFS - if (mount(MOUNT_DEVFS, "/dev", 0, (caddr_t) 0) < 0) { - writes(2, "init: couldn't mount /dev\n"); - perror( " mount"); - writes(2, " trying to continue...\n"); + /* System V users like to reexec init. */ + if (getpid() != 1) { + (void)fprintf(stderr, "init: already running\n"); + exit (1); } -#endif -top: - /* Single user mode? */ - if(sflag) { - sflag = 0; - status = 1; - } else { - /* otherwise, execute /etc/rc */ - if (access("/etc/rc", F_OK) == 0) { - - signal(SIGTERM, SIG_IGN); /* XXX */ - if((pid = fork()) < 0) - fatal("fork"); - else if(!pid) { - /* signals, to default state */ - SIGNALSFORCHILD; + /* + * Note that this does NOT open a file... + * Does 'init' deserve its own facility number? + */ + openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); - /* clean off console */ - revoke("/dev/console"); + /* + * Create an initial session. + */ + if (setsid() < 0) + warning("initial setsid() failed: %m"); - /* create a shell */ - login_tty(open("/dev/console", 2)); - execl("/bin/sh", "sh", "/etc/rc", Reboot, (char *)0); - _exit(127); - } - Reboot = 0; /* 31 Jul 92*/ - while(wait(&status) != pid); - - /* if we are about to be rebooted, then wait for it */ - if (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - pause(); - } else { status = 1; sflag = 1; goto top; } - } - signal(SIGTERM, sterm); - Reboot = 0; - - /* do single user shell on console */ - if (setjmp(single) || status) { -#ifdef SECURE_CONSOLE - struct ttyent *ttyp; - struct passwd *passp; - char *pass; - static const char banner[] = - "Enter root password, or Control-D to go multi-user\n"; -#endif - - if((pid = fork()) < 0) - fatal("fork"); - else if(!pid) { - /* signals, to default state */ - SIGNALSFORCHILD; - - /* clean off console */ - revoke("/dev/console"); - - /* do open and configuration of console */ - login_tty(open("/dev/console", 2)); -#ifdef SECURE_CONSOLE - /* if the console isn't secure, check the root PW */ - ttyp = getttynam("console"); - if (!ttyp) { - /* don't have an entry for "console", probably - * have one for /dev/vga - */ - ttyp = getttynam("vga"); - } - passp = getpwnam("root"); - if (ttyp && ((ttyp->ty_status & TTY_SECURE) == 0) && - passp) { - write(2, banner, sizeof(banner) - 1); - do { - pass = getpass("Password:"); - if ((pass == 0) || (*pass == '\0')) - _exit(0); /* got control-d */ - pass = crypt(pass, passp->pw_passwd); - } while (strcmp(pass, passp->pw_passwd) != 0); - } -#endif - execl("/bin/sh", "-", (char *)0); - _exit(127); + /* + * This code assumes that we always get arguments through flags, + * never through bits set in some random machine register. + */ + while ((c = getopt(argc, argv, "sf")) != -1) + switch (c) { + case 's': + requested_transition = single_user; + break; + case 'f': + runcom_mode = FASTBOOT; + break; + default: + warning("unrecognized flag '-%c'", c); + break; } - while(wait(&status) != pid); - while(drain) /* 31 Jul 92*/ - pause(); - goto top; + + if (optind != argc) + warning("ignoring excess arguments"); + + /* + * We catch or block signals rather than ignore them, + * so that they get reset on exec. + */ + handle(badsys, SIGSYS, 0); + handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, + SIGBUS, SIGXCPU, SIGXFSZ, 0); + handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); + handle(alrm_handler, SIGALRM, 0); + sigfillset(&mask); + delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, + SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); + (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); + + /* + * Paranoia. + */ + close(0); + close(1); + close(2); + + /* + * Start the state machine. + */ + transition(requested_transition); + + /* + * Should never reach here. + */ + return 1; +} + +/* + * Associate a function with a signal handler. + */ +void +#ifdef __STDC__ +handle(sig_t handler, ...) +#else +handle(va_alist) + va_dcl +#endif +{ + int sig; + struct sigaction sa; + int mask_everything; + va_list ap; +#ifndef __STDC__ + sig_t handler; + + va_start(ap); + handler = va_arg(ap, sig_t); +#else + va_start(ap, handler); +#endif + + sa.sa_handler = handler; + sigfillset(&mask_everything); + + while (sig = va_arg(ap, int)) { + sa.sa_mask = mask_everything; + /* XXX SA_RESTART? */ + sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; + sigaction(sig, &sa, (struct sigaction *) 0); } + va_end(ap); +} - /* multiuser mode, traipse through table */ - setttyent(); - for(tt = ttytab; (ty = getttyent()) && tt < &ttytab[NTTY]; tt++) { - tt->tt_name = newstring(ty->ty_name); - tt->tt_getty = newstring(ty->ty_getty); - tt->tt_type = newstring(ty->ty_type); - tt->tt_status = ty->ty_status; +/* + * Delete a set of signals from a mask. + */ +void +#ifdef __STDC__ +delset(sigset_t *maskp, ...) +#else +delset(va_alist) + va_dcl +#endif +{ + int sig; + va_list ap; +#ifndef __STDC__ + sigset_t *maskp; + + va_start(ap); + maskp = va_arg(ap, sigset_t *); +#else + va_start(ap, maskp); +#endif + + while (sig = va_arg(ap, int)) + sigdelset(maskp, sig); + va_end(ap); +} + +/* + * Log a message and sleep for a while (to give someone an opportunity + * to read it and to save log or hardcopy output if the problem is chronic). + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +stall(char *message, ...) +#else +stall(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); + sleep(STALL_TIMEOUT); +} + +/* + * Like stall(), but doesn't sleep. + * If cpp had variadic macros, the two functions could be #defines for another. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +warning(char *message, ...) +#else +warning(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); +} + +/* + * Log an emergency message. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +emergency(char *message, ...) +#else +emergency(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_EMERG, message, ap); + va_end(ap); +} + +/* + * Catch a SIGSYS signal. + * + * These may arise if a system does not support sysctl. + * We tolerate up to 25 of these, then throw in the towel. + */ +void +badsys(sig) + int sig; +{ + static int badcount = 0; + + if (badcount++ < 25) + return; + disaster(sig); +} + +/* + * Catch an unexpected signal. + */ +void +disaster(sig) + int sig; +{ + emergency("fatal signal: %s", + sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); + + sleep(STALL_TIMEOUT); + _exit(sig); /* reboot */ +} + +/* + * Get the security level of the kernel. + */ +int +getsecuritylevel() +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + size_t len; + extern int errno; + + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + len = sizeof curlevel; + if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { + emergency("cannot get kernel security level: %s", + strerror(errno)); + return (-1); } - ttytabend = tt; - endttyent(); - for(tt = ttytab; tt < ttytabend; getty(tt++)); + return (curlevel); +#else + return (-1); +#endif +} - /* if we receive a request to reread the table, come here */ - if(setjmp(reread)) { +/* + * Set the security level of the kernel. + */ +void +setsecuritylevel(newlevel) + int newlevel; +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + extern int errno; - /* first pass. find and clean the entries that have changed */ - setttyent(); - while(ty = getttyent()) { - for(tt = ttytab; tt < ttytabend; tt++) - if(!strcmp(tt->tt_name, ty->ty_name)) { - /* if a process present, mark */ - if((tt->tt_status & ~TTY_LOGIN) !=ty->ty_status) - tt->tt_status = ty->ty_status |TTY_DIFF; - if(strcmp(tt->tt_getty, ty->ty_getty)) { - free(tt->tt_getty); - tt->tt_getty = newstring(ty->ty_getty); - tt->tt_status |= TTY_DIFF; - } - if(strcmp(tt->tt_type, ty->ty_type)) { - free(tt->tt_type); - tt->tt_type = newstring(ty->ty_type); - tt->tt_status |= TTY_DIFF; - } - if(((tt->tt_status |= TTY_SEEN) & TTY_DIFF) - && tt->tt_pid > 1) - kill(tt->tt_pid, 9); - break; - } - if(tt == ttytabend && tt < &ttytab[NTTY]) { - tt->tt_name = newstring(ty->ty_name); - tt->tt_getty = newstring(ty->ty_getty); - tt->tt_type = newstring(ty->ty_type); - tt->tt_status = ty->ty_status | - TTY_SEEN | TTY_DIFF; - ttytabend++; + curlevel = getsecuritylevel(); + if (newlevel == curlevel) + return; + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { + emergency( + "cannot change kernel security level from %d to %d: %s", + curlevel, newlevel, strerror(errno)); + return; + } +#ifdef SECURE + warning("kernel security level changed from %d to %d", + curlevel, newlevel); +#endif +#endif +} + +/* + * Change states in the finite state machine. + * The initial state is passed as an argument. + */ +void +transition(s) + state_t s; +{ + for (;;) + s = (state_t) (*s)(); +} + +/* + * Close out the accounting files for a login session. + * NB: should send a message to the session logger to avoid blocking. + */ +void +clear_session_logs(sp) + session_t *sp; +{ + char *line = sp->se_device + sizeof(_PATH_DEV) - 1; + + if (logout(line)) + logwtmp(line, "", ""); +} + +/* + * Start a session and allocate a controlling terminal. + * Only called by children of init after forking. + */ +void +setctty(name) + char *name; +{ + int fd; + + (void) revoke(name); + sleep (2); /* leave DTR low */ + if ((fd = open(name, O_RDWR)) == -1) { + stall("can't open %s: %m", name); + _exit(1); + } + if (login_tty(fd) == -1) { + stall("can't get %s for controlling terminal: %m", name); + _exit(1); + } +} + +/* + * Bring the system up single user. + */ +state_func_t +single_user() +{ + pid_t pid, wpid; + int status; + sigset_t mask; + char *shell = _PATH_BSHELL; + char *argv[2]; +#ifdef SECURE + struct ttyent *typ; + struct passwd *pp; + static const char banner[] = + "Enter root password, or ^D to go multi-user\n"; + char *clear, *password; +#endif + + /* + * If the kernel is in secure mode, downgrade it to insecure mode. + */ + if (getsecuritylevel() > 0) + setsecuritylevel(0); + + if ((pid = fork()) == 0) { + /* + * Start the single user session. + */ + setctty(_PATH_CONSOLE); + +#ifdef SECURE + /* + * Check the root password. + * We don't care if the console is 'on' by default; + * it's the only tty that can be 'off' and 'secure'. + */ + typ = getttynam("console"); + pp = getpwnam("root"); + if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { + write(2, banner, sizeof banner - 1); + for (;;) { + clear = getpass("Password:"); + if (clear == 0 || *clear == '\0') + _exit(0); + password = crypt(clear, pp->pw_passwd); + bzero(clear, _PASSWORD_LEN); + if (strcmp(password, pp->pw_passwd) == 0) + break; + warning("single-user login failed\n"); } } endttyent(); - /* second pass. offer gettys on previously cleaned entries, - and garbage collect "dead" entries */ - for(tt = ttytab; tt < ttytabend; tt++) - if(tt->tt_status & TTY_SEEN) { - tt->tt_status &= ~TTY_SEEN; - if(tt->tt_status & TTY_DIFF) { - tt->tt_status &= ~TTY_DIFF; - getty(tt); - } - } - else { - if(tt->tt_pid > 1) - kill(tt->tt_pid, 9); - free(tt->tt_name); - free(tt->tt_getty); - free(tt->tt_type); - pid = tt - ttytab; - for(tt++; tt < ttytabend; tt++) - tt[-1] = *tt; - ttytabend--; - tt = &ttytab[pid]; - } - } - drain = 0; + endpwent(); +#endif /* SECURE */ - /* listen for terminating gettys and sessions, and process them */ - while(1) { - sigsetmask(mask); - pid = wait(&status); - sigblock(sigmask(SIGHUP) | sigmask(SIGTERM)); - if(pid < 0) { - sleep(5); +#ifdef DEBUGSHELL + { + char altshell[128], *cp = altshell; + int num; + +#define SHREQUEST \ + "Enter pathname of shell or RETURN for sh: " + (void)write(STDERR_FILENO, + SHREQUEST, sizeof(SHREQUEST) - 1); + while ((num = read(STDIN_FILENO, cp, 1)) != -1 && + num != 0 && *cp != '\n' && cp < &altshell[127]) + cp++; + *cp = '\0'; + if (altshell[0] != '\0') + shell = altshell; + } +#endif /* DEBUGSHELL */ + + /* + * Unblock signals. + * We catch all the interesting ones, + * and those are reset to SIG_DFL on exec. + */ + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + /* + * Fire off a shell. + * If the default one doesn't work, try the Bourne shell. + */ + argv[0] = "-sh"; + argv[1] = 0; + execv(shell, argv); + emergency("can't exec %s for single user: %m", shell); + execv(_PATH_BSHELL, argv); + emergency("can't exec %s for single user: %m", _PATH_BSHELL); + sleep(STALL_TIMEOUT); + _exit(1); + } + + if (pid == -1) { + /* + * We are seriously hosed. Do our best. + */ + emergency("can't fork single-user shell, trying again"); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + return (state_func_t) single_user; + } + + requested_transition = 0; + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for single-user shell failed: %m; restarting"); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: shell stopped, restarting\n"); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid && !requested_transition); + + if (requested_transition) + return (state_func_t) requested_transition; + + if (!WIFEXITED(status)) { + if (WTERMSIG(status) == SIGKILL) { + /* + * reboot(8) killed shell? + */ + warning("single user shell terminated."); + sleep(STALL_TIMEOUT); + _exit(0); + } else { + warning("single user shell terminated, restarting"); + return (state_func_t) single_user; + } + } + + runcom_mode = FASTBOOT; + return (state_func_t) runcom; +} + +/* + * Run the system startup script. + */ +state_func_t +runcom() +{ + pid_t pid, wpid; + int status; + char *argv[4]; + struct sigaction sa; + + if ((pid = fork()) == 0) { + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); + (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); + + setctty(_PATH_CONSOLE); + + argv[0] = "sh"; + argv[1] = _PATH_RUNCOM; + argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; + argv[3] = 0; + + sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); + + execv(_PATH_BSHELL, argv); + stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); + _exit(1); /* force single user mode */ + } + + if (pid == -1) { + emergency("can't fork for %s on %s: %m", + _PATH_BSHELL, _PATH_RUNCOM); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + sleep(STALL_TIMEOUT); + return (state_func_t) single_user; + } + + /* + * Copied from single_user(). This is a bit paranoid. + */ + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for %s on %s failed: %m; going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: %s on %s stopped, restarting\n", + _PATH_BSHELL, _PATH_RUNCOM); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && + requested_transition == catatonia) { + /* /etc/rc executed /sbin/reboot; wait for the end quietly */ + sigset_t s; + + sigfillset(&s); + for (;;) + sigsuspend(&s); + } + + if (!WIFEXITED(status)) { + warning("%s on %s terminated abnormally, going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + + if (WEXITSTATUS(status)) + return (state_func_t) single_user; + + runcom_mode = AUTOBOOT; /* the default */ + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "reboot", ""); + return (state_func_t) read_ttys; +} + +/* + * Open the session database. + * + * NB: We could pass in the size here; is it necessary? + */ +int +start_session_db() +{ + if (session_db && (*session_db->close)(session_db)) + emergency("session database close: %s", strerror(errno)); + if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { + emergency("session database open: %s", strerror(errno)); + return (1); + } + return (0); + +} + +/* + * Add a new login session. + */ +void +add_session(sp) + session_t *sp; +{ + DBT key; + DBT data; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + data.data = &sp; + data.size = sizeof sp; + + if ((*session_db->put)(session_db, &key, &data, 0)) + emergency("insert %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Delete an old login session. + */ +void +del_session(sp) + session_t *sp; +{ + DBT key; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + + if ((*session_db->del)(session_db, &key, 0)) + emergency("delete %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Look up a login session by pid. + */ +session_t * +#ifdef __STDC__ +find_session(pid_t pid) +#else +find_session(pid) + pid_t pid; +#endif +{ + DBT key; + DBT data; + session_t *ret; + + key.data = &pid; + key.size = sizeof pid; + if ((*session_db->get)(session_db, &key, &data, 0) != 0) + return 0; + bcopy(data.data, (char *)&ret, sizeof(ret)); + return ret; +} + +/* + * Construct an argument vector from a command line. + */ +char ** +construct_argv(command) + char *command; +{ + register int argc = 0; + register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) + * sizeof (char *)); + static const char separators[] = " \t"; + + if ((argv[argc++] = strtok(command, separators)) == 0) + return 0; + while (argv[argc++] = strtok((char *) 0, separators)) + continue; + return argv; +} + +/* + * Deallocate a session descriptor. + */ +void +free_session(sp) + register session_t *sp; +{ + free(sp->se_device); + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + if (sp->se_window) { + free(sp->se_window); + free(sp->se_window_argv); + } + free(sp); +} + +/* + * Allocate a new session descriptor. + */ +session_t * +new_session(sprev, session_index, typ) + session_t *sprev; + int session_index; + register struct ttyent *typ; +{ + register session_t *sp; + + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_name == 0 || + typ->ty_getty == 0) + return 0; + + sp = (session_t *) malloc(sizeof (session_t)); + bzero(sp, sizeof *sp); + + sp->se_index = session_index; + + sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); + (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); + + if (setupargv(sp, typ) == 0) { + free_session(sp); + return (0); + } + + sp->se_next = 0; + if (sprev == 0) { + sessions = sp; + sp->se_prev = 0; + } else { + sprev->se_next = sp; + sp->se_prev = sprev; + } + + return sp; +} + +/* + * Calculate getty and if useful window argv vectors. + */ +int +setupargv(sp, typ) + session_t *sp; + struct ttyent *typ; +{ + + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); + (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); + sp->se_getty_argv = construct_argv(sp->se_getty); + if (sp->se_getty_argv == 0) { + warning("can't parse getty for port %s", sp->se_device); + free(sp->se_getty); + sp->se_getty = 0; + return (0); + } + if (typ->ty_window) { + if (sp->se_window) + free(sp->se_window); + sp->se_window = strdup(typ->ty_window); + sp->se_window_argv = construct_argv(sp->se_window); + if (sp->se_window_argv == 0) { + warning("can't parse window for port %s", + sp->se_device); + free(sp->se_window); + sp->se_window = 0; + return (0); + } + } + return (1); +} + +/* + * Walk the list of ttys and create sessions for each active line. + */ +state_func_t +read_ttys() +{ + int session_index = 0; + register session_t *sp, *snext; + register struct ttyent *typ; + + /* + * Destroy any previous session state. + * There shouldn't be any, but just in case... + */ + for (sp = sessions; sp; sp = snext) { + if (sp->se_process) + clear_session_logs(sp); + snext = sp->se_next; + free_session(sp); + } + sessions = 0; + if (start_session_db()) + return (state_func_t) single_user; + + /* + * Allocate a session entry for each active port. + * Note that sp starts at 0. + */ + while (typ = getttyent()) + if (snext = new_session(sp, ++session_index, typ)) + sp = snext; + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Start a window system running. + */ +void +start_window_system(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + + if ((pid = fork()) == -1) { + emergency("can't fork for window system on port %s: %m", + sp->se_device); + /* hope that getty fails and we can try again */ + return; + } + + if (pid) + return; + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + if (setsid() < 0) + emergency("setsid failed (window) %m"); + + execv(sp->se_window_argv[0], sp->se_window_argv); + stall("can't exec window system '%s' for port %s: %m", + sp->se_window_argv[0], sp->se_device); + _exit(1); +} + +/* + * Start a login session running. + */ +pid_t +start_getty(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + time_t current_time = time((time_t *) 0); + + /* + * fork(), not vfork() -- we can't afford to block. + */ + if ((pid = fork()) == -1) { + emergency("can't fork for getty on port %s: %m", sp->se_device); + return -1; + } + + if (pid) + return pid; + + if (current_time > sp->se_started && + current_time - sp->se_started < GETTY_SPACING) { + warning("getty repeating too quickly on port %s, sleeping", + sp->se_device); + sleep((unsigned) GETTY_SLEEP); + } + + if (sp->se_window) { + start_window_system(sp); + sleep(WINDOW_WAIT); + } + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + execv(sp->se_getty_argv[0], sp->se_getty_argv); + stall("can't exec getty '%s' for port %s: %m", + sp->se_getty_argv[0], sp->se_device); + _exit(1); +} + +/* + * Collect exit status for a child. + * If an exiting login, start a new login running. + */ +void +#ifdef __STDC__ +collect_child(pid_t pid) +#else +collect_child(pid) + pid_t pid; +#endif +{ + register session_t *sp, *sprev, *snext; + + if (! sessions) + return; + + if (! (sp = find_session(pid))) + return; + + clear_session_logs(sp); + del_session(sp); + sp->se_process = 0; + + if (sp->se_flags & SE_SHUTDOWN) { + if (sprev = sp->se_prev) + sprev->se_next = sp->se_next; + else + sessions = sp->se_next; + if (snext = sp->se_next) + snext->se_prev = sp->se_prev; + free_session(sp); + return; + } + + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + return; + } + + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); +} + +/* + * Catch a signal and request a state transition. + */ +void +transition_handler(sig) + int sig; +{ + + switch (sig) { + case SIGHUP: + requested_transition = clean_ttys; + break; + case SIGTERM: + requested_transition = death; + break; + case SIGTSTP: + requested_transition = catatonia; + break; + default: + requested_transition = 0; + break; + } +} + +/* + * Take the system multiuser. + */ +state_func_t +multi_user() +{ + pid_t pid; + register session_t *sp; + + requested_transition = 0; + + /* + * If the administrator has not set the security level to -1 + * to indicate that the kernel should not run multiuser in secure + * mode, and the run script has not set a higher level of security + * than level 1, then put the kernel into secure mode. + */ + if (getsecuritylevel() == 0) + setsecuritylevel(1); + + for (sp = sessions; sp; sp = sp->se_next) { + if (sp->se_process) + continue; + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + break; + } + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); + } + + while (!requested_transition) + if ((pid = waitpid(-1, (int *) 0, 0)) != -1) + collect_child(pid); + + return (state_func_t) requested_transition; +} + +/* + * This is an n-squared algorithm. We hope it isn't run often... + */ +state_func_t +clean_ttys() +{ + register session_t *sp, *sprev; + register struct ttyent *typ; + register int session_index = 0; + register int devlen; + + if (! sessions) + return (state_func_t) multi_user; + + devlen = sizeof(_PATH_DEV) - 1; + while (typ = getttyent()) { + ++session_index; + + for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) + if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) + break; + + if (sp) { + if (sp->se_index != session_index) { + warning("port %s changed utmp index from %d to %d", + sp->se_device, sp->se_index, + session_index); + sp->se_index = session_index; + } + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_getty == 0) { + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + continue; + } + sp->se_flags &= ~SE_SHUTDOWN; + if (setupargv(sp, typ) == 0) { + warning("can't parse getty for port %s", + sp->se_device); + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + } continue; } - for(tt = ttytab; tt < ttytabend; tt++) - if(pid == tt->tt_pid) { -/* 24 Jul 92*/ if (logout(tt->tt_name)) logwtmp(tt->tt_name,"",""); - if(drain && !(tt->tt_status & TTY_LOGIN)) { - free(tt->tt_name); - free(tt->tt_getty); - free(tt->tt_type); - for(tt++; tt < ttytabend; tt++) - tt[-1] = *tt; - ttytabend--; - } - else - getty(tt); - break; - } + + new_session(sprev, session_index, typ); } + + endttyent(); + + return (state_func_t) multi_user; } -/* process a getty for a "line". N.B. by having getty do open, init - is not limited by filedescriptors for number of possible users */ -getty(tt) -struct ttytab *tt; +/* + * Block further logins. + */ +state_func_t +catatonia() { - char *sargv[NARG]; - register char *p = arg, **sp = sargv; + register session_t *sp; - if(!(tt->tt_status & TTY_ON)) { - tt->tt_pid = -1; - return; + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + return (state_func_t) multi_user; +} + +/* + * Note SIGALRM. + */ +void +alrm_handler(sig) + int sig; +{ + clang = 1; +} + +/* + * Bring the system down to single user. + */ +state_func_t +death() +{ + register session_t *sp; + register int i; + pid_t pid; + static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "shutdown", ""); + + for (i = 0; i < 3; ++i) { + if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) + return (state_func_t) single_user; + + clang = 0; + alarm(DEATH_WATCH); + do + if ((pid = waitpid(-1, (int *)0, 0)) != -1) + collect_child(pid); + while (clang == 0 && errno != ECHILD); + + if (errno == ECHILD) + return (state_func_t) single_user; } - if((tt->tt_pid = fork()) < 0) - fatal("getty fork"); - else if(tt->tt_pid) { - if(tt->tt_status & TTY_LOGOUT) - tt->tt_status ^= TTY_LOGIN; - return; - } - signal(SIGHUP, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGTSTP, SIG_DFL); - sigsetmask(0); - strcpy(p, tt->tt_getty); - while(sp < &sargv[NARG - 2]) { - while(isspace(*p)) - p++; - if(!*p) - break; - *sp++ = p; - while(!isspace(*p) && *p) - p++; - if(!*p) - break; - *p++ = 0; - } - strcpy(nam, tt->tt_name); - *sp++ = nam; - *sp = 0; - p = *sargv; - strcpy(term, "TERM="); - strcat(term, tt->tt_type); - execve(p, sargv, env); -bad: - sleep(30); - fatal(tt->tt_name); -} - -char * -newstring(s) -register char *s; -{ - register char *n; - - if(!(n = malloc(strlen(s) + 1))) - fatal("out of memory"); - strcpy(n, s); - return(n); -} - -warn(s) -char *s; -{ - register int pid; - int fd; - - fd = open("/dev/console", 2); - writes(fd, "init WARNING: "); - writes(fd, s); - write(fd, "\n", 1); - close(fd); -} - -fatal(s) -char *s; -{ - login_tty(open("/dev/console", 2)); - writes(2, "init FATAL error: "); - perror(s); - _exit(1); /* 04 Sep 92*/ - /* panic: init died */ -} - -writes(n, s) -char *s; -{ - write(n, s, strlen(s)); + + warning("some processes would not die; ps axl advised"); + + return (state_func_t) single_user; } diff --git a/sbin/init/pathnames.h b/sbin/init/pathnames.h new file mode 100644 index 000000000000..46b3e88572a2 --- /dev/null +++ b/sbin/init/pathnames.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at UUNET Technologies, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)pathnames.h 6.1 (Berkeley) 7/2/91 + * $Id: pathnames.h,v 1.1 1993/06/18 20:51:50 cgd Exp $ + */ + +#include + +#define _PATH_SLOGGER "/sbin/session_logger" +#define _PATH_RUNCOM "/etc/rc"