150394Smckusick /*- 250394Smckusick * Copyright (c) 1991 The Regents of the University of California. 350394Smckusick * All rights reserved. 450394Smckusick * 550394Smckusick * This code is derived from software contributed to Berkeley by 660435Sbostic * Donn Seeley at Berkeley Software Design, Inc. 750394Smckusick * 850394Smckusick * %sccs.include.redist.c% 921135Sdist */ 1021135Sdist 1112682Ssam #ifndef lint 1250394Smckusick char copyright[] = 1350394Smckusick "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ 1450394Smckusick All rights reserved.\n"; 1550394Smckusick #endif /* not lint */ 1612682Ssam 1750394Smckusick #ifndef lint 18*60617Smckusick static char sccsid[] = "@(#)init.c 6.20 (Berkeley) 05/30/93"; 1950394Smckusick #endif /* not lint */ 2050394Smckusick 2159441Sbostic #include <sys/param.h> 2258422Smckusick #include <sys/sysctl.h> 2350394Smckusick #include <sys/wait.h> 2459441Sbostic 2550394Smckusick #include <db.h> 262821Swnj #include <errno.h> 2750394Smckusick #include <fcntl.h> 2850394Smckusick #include <signal.h> 2959441Sbostic #include <stdio.h> 3059441Sbostic #include <stdlib.h> 3159441Sbostic #include <string.h> 3250394Smckusick #include <syslog.h> 3350394Smckusick #include <time.h> 3416452Sroot #include <ttyent.h> 3550394Smckusick #include <unistd.h> 3650394Smckusick 3750394Smckusick #ifdef __STDC__ 3850394Smckusick #include <stdarg.h> 3950394Smckusick #else 4050394Smckusick #include <varargs.h> 4150394Smckusick #endif 4250394Smckusick 4350394Smckusick #ifdef SECURE 4450394Smckusick #include <pwd.h> 4550394Smckusick #endif 4650394Smckusick 4737284Sbostic #include "pathnames.h" 481029Sbill 4950394Smckusick /* 5050394Smckusick * Until the mythical util.h arrives... 5150394Smckusick */ 5250394Smckusick extern int login_tty __P((int)); 5350394Smckusick extern int logout __P((const char *)); 5450394Smckusick extern void logwtmp __P((const char *, const char *, const char *)); 551029Sbill 5650394Smckusick /* 5750394Smckusick * Sleep times; used to prevent thrashing. 5850394Smckusick */ 5950407Smckusick #define GETTY_SPACING 10 /* fork getty on a port every N secs */ 6050394Smckusick #define WINDOW_WAIT 3 /* wait N secs after starting window */ 6150394Smckusick #define STALL_TIMEOUT 30 /* wait N secs after warning */ 6250407Smckusick #define DEATH_WATCH 10 /* wait N secs for procs to die */ 631029Sbill 6450394Smckusick void handle __P((sig_t, ...)); 6550394Smckusick void delset __P((sigset_t *, ...)); 661029Sbill 6750394Smckusick void stall __P((char *, ...)); 6850394Smckusick void warning __P((char *, ...)); 6950394Smckusick void emergency __P((char *, ...)); 7050394Smckusick void disaster __P((int)); 7158422Smckusick void badsys __P((int)); 721029Sbill 7350394Smckusick /* 7450394Smckusick * We really need a recursive typedef... 7550394Smckusick * The following at least guarantees that the return type of (*state_t)() 7650394Smckusick * is sufficiently wide to hold a function pointer. 7750394Smckusick */ 7850394Smckusick typedef long (*state_func_t) __P((void)); 7950394Smckusick typedef state_func_t (*state_t) __P((void)); 801029Sbill 8150394Smckusick state_func_t single_user __P((void)); 8250394Smckusick state_func_t runcom __P((void)); 8350394Smckusick state_func_t read_ttys __P((void)); 8450394Smckusick state_func_t multi_user __P((void)); 8550394Smckusick state_func_t clean_ttys __P((void)); 8650394Smckusick state_func_t catatonia __P((void)); 8750394Smckusick state_func_t death __P((void)); 8813021Ssam 8950394Smckusick enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 9050394Smckusick 9150394Smckusick void transition __P((state_t)); 9250394Smckusick state_t requested_transition = runcom; 9350394Smckusick 9450394Smckusick void setctty __P((char *)); 9550394Smckusick 9659441Sbostic typedef struct init_session { 9750394Smckusick int se_index; /* index of entry in ttys file */ 9850394Smckusick pid_t se_process; /* controlling process */ 9950394Smckusick time_t se_started; /* used to avoid thrashing */ 10050394Smckusick int se_flags; /* status of session */ 10150394Smckusick #define SE_SHUTDOWN 0x1 /* session won't be restarted */ 10250394Smckusick char *se_device; /* filename of port */ 10350394Smckusick char *se_getty; /* what to run on that port */ 10450394Smckusick char **se_getty_argv; /* pre-parsed argument array */ 10550394Smckusick char *se_window; /* window system (started only once) */ 10650394Smckusick char **se_window_argv; /* pre-parsed argument array */ 10759441Sbostic struct init_session *se_prev; 10859441Sbostic struct init_session *se_next; 10950394Smckusick } session_t; 11050394Smckusick 11150394Smckusick void free_session __P((session_t *)); 11250394Smckusick session_t *new_session __P((session_t *, int, struct ttyent *)); 11350394Smckusick session_t *sessions; 11450394Smckusick 11550394Smckusick char **construct_argv __P((char *)); 11650394Smckusick void start_window_system __P((session_t *)); 117*60617Smckusick void collect_child __P((pid_t)); 11850394Smckusick pid_t start_getty __P((session_t *)); 11950394Smckusick void transition_handler __P((int)); 12050394Smckusick void alrm_handler __P((int)); 12150394Smckusick int clang; 12250394Smckusick 12350394Smckusick void clear_session_logs __P((session_t *)); 12450394Smckusick 12550407Smckusick int start_session_db __P((void)); 12650394Smckusick void add_session __P((session_t *)); 12750394Smckusick void del_session __P((session_t *)); 12850394Smckusick session_t *find_session __P((pid_t)); 12950394Smckusick DB *session_db; 13050394Smckusick 13150407Smckusick /* 13250407Smckusick * The mother of all processes. 13350407Smckusick */ 13450394Smckusick int 13543633Skarels main(argc, argv) 13650394Smckusick int argc; 13743633Skarels char **argv; 1381029Sbill { 13950394Smckusick int c; 14050407Smckusick struct sigaction sa; 14150394Smckusick sigset_t mask; 14247659Skarels 1439869Spugs 14452795Sbostic /* Dispose of random users. */ 14552795Sbostic if (getuid() != 0) { 14652795Sbostic (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); 14752795Sbostic exit (1); 14852795Sbostic } 14952795Sbostic 15052795Sbostic /* System V users like to reexec init. */ 15152795Sbostic if (getpid() != 1) { 15252795Sbostic (void)fprintf(stderr, "init: already running\n"); 15352795Sbostic exit (1); 15452795Sbostic } 15552795Sbostic 15650394Smckusick /* 15750394Smckusick * Note that this does NOT open a file... 15850394Smckusick * Does 'init' deserve its own facility number? 15950394Smckusick */ 16050407Smckusick openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 16150394Smckusick 16250394Smckusick /* 16350407Smckusick * Create an initial session. 16450407Smckusick */ 16550407Smckusick if (setsid() < 0) 16660300Smckusick warning("initial setsid() failed: %m"); 16750407Smckusick 16850407Smckusick /* 16950394Smckusick * This code assumes that we always get arguments through flags, 17050394Smckusick * never through bits set in some random machine register. 17150394Smckusick */ 17250394Smckusick while ((c = getopt(argc, argv, "sf")) != -1) 17350394Smckusick switch (c) { 17450394Smckusick case 's': 17550394Smckusick requested_transition = single_user; 17650394Smckusick break; 17747659Skarels case 'f': 17850394Smckusick runcom_mode = FASTBOOT; 1799869Spugs break; 18050394Smckusick default: 18150394Smckusick warning("unrecognized flag '-%c'", c); 1829869Spugs break; 1839869Spugs } 18450394Smckusick 18550394Smckusick if (optind != argc) 18650394Smckusick warning("ignoring excess arguments"); 18750394Smckusick 18850394Smckusick /* 18950394Smckusick * We catch or block signals rather than ignore them, 19050394Smckusick * so that they get reset on exec. 19150394Smckusick */ 19258422Smckusick handle(badsys, SIGSYS, 0); 19350394Smckusick handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 19458422Smckusick SIGBUS, SIGXCPU, SIGXFSZ, 0); 19550394Smckusick handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); 19650394Smckusick handle(alrm_handler, SIGALRM, 0); 19750394Smckusick sigfillset(&mask); 19850407Smckusick delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 19950407Smckusick SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); 20050394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 20150407Smckusick sigemptyset(&sa.sa_mask); 20250407Smckusick sa.sa_flags = 0; 20350407Smckusick sa.sa_handler = SIG_IGN; 20450407Smckusick (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); 20550407Smckusick (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); 20650394Smckusick 20750394Smckusick /* 20850394Smckusick * Paranoia. 20950394Smckusick */ 21050394Smckusick close(0); 21150394Smckusick close(1); 21250394Smckusick close(2); 21350394Smckusick 21450394Smckusick /* 21550394Smckusick * Start the state machine. 21650394Smckusick */ 21750394Smckusick transition(requested_transition); 21850394Smckusick 21950394Smckusick /* 22050394Smckusick * Should never reach here. 22150394Smckusick */ 22250394Smckusick return 1; 22350394Smckusick } 22450394Smckusick 22550407Smckusick /* 22650407Smckusick * Associate a function with a signal handler. 22750407Smckusick */ 22850394Smckusick void 22950394Smckusick #ifdef __STDC__ 23050394Smckusick handle(sig_t handler, ...) 23150394Smckusick #else 23250394Smckusick handle(va_alist) 23350394Smckusick va_dcl 23450394Smckusick #endif 23550394Smckusick { 23650394Smckusick int sig; 23750394Smckusick struct sigaction sa; 23850394Smckusick int mask_everything; 23950394Smckusick va_list ap; 24050394Smckusick #ifndef __STDC__ 24150394Smckusick sig_t handler; 24250394Smckusick 24350394Smckusick va_start(ap); 24450394Smckusick handler = va_arg(ap, sig_t); 24550394Smckusick #else 24650394Smckusick va_start(ap, handler); 24750394Smckusick #endif 24850394Smckusick 24950394Smckusick sa.sa_handler = handler; 25050394Smckusick sigfillset(&mask_everything); 25150394Smckusick 25250394Smckusick while (sig = va_arg(ap, int)) { 25350394Smckusick sa.sa_mask = mask_everything; 25450394Smckusick /* XXX SA_RESTART? */ 25550394Smckusick sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 25650394Smckusick sigaction(sig, &sa, (struct sigaction *) 0); 25747659Skarels } 2581029Sbill } 2591029Sbill 26050407Smckusick /* 26150407Smckusick * Delete a set of signals from a mask. 26250407Smckusick */ 26350394Smckusick void 26450394Smckusick #ifdef __STDC__ 26550394Smckusick delset(sigset_t *maskp, ...) 26650394Smckusick #else 26750394Smckusick delset(va_alist) 26850394Smckusick va_dcl 26950394Smckusick #endif 27050394Smckusick { 27150394Smckusick int sig; 27250394Smckusick va_list ap; 27350394Smckusick #ifndef __STDC__ 27450394Smckusick sigset_t *maskp; 2751429Sbill 27650394Smckusick va_start(ap); 27750394Smckusick maskp = va_arg(ap, sigset_t *); 27850394Smckusick #else 27950394Smckusick va_start(ap, maskp); 28050394Smckusick #endif 28150394Smckusick 28250394Smckusick while (sig = va_arg(ap, int)) 28350394Smckusick sigdelset(maskp, sig); 28450394Smckusick } 28550394Smckusick 28650394Smckusick /* 28750394Smckusick * Log a message and sleep for a while (to give someone an opportunity 28850394Smckusick * to read it and to save log or hardcopy output if the problem is chronic). 28950430Smckusick * NB: should send a message to the session logger to avoid blocking. 29050394Smckusick */ 29150394Smckusick void 29250394Smckusick #ifdef __STDC__ 29350394Smckusick stall(char *message, ...) 29450394Smckusick #else 29550394Smckusick stall(va_alist) 29650394Smckusick va_dcl 29750394Smckusick #endif 2981029Sbill { 29950394Smckusick pid_t pid; 30050394Smckusick va_list ap; 30150394Smckusick #ifndef __STDC__ 30250394Smckusick char *message; 3031029Sbill 30450394Smckusick va_start(ap); 30550394Smckusick message = va_arg(ap, char *); 30650394Smckusick #else 30750394Smckusick va_start(ap, message); 30850394Smckusick #endif 30950394Smckusick 31050430Smckusick vsyslog(LOG_ALERT, message, ap); 31150394Smckusick va_end(ap); 31250394Smckusick sleep(STALL_TIMEOUT); 3131429Sbill } 3141429Sbill 31550394Smckusick /* 31650394Smckusick * Like stall(), but doesn't sleep. 31750394Smckusick * If cpp had variadic macros, the two functions could be #defines for another. 31850430Smckusick * NB: should send a message to the session logger to avoid blocking. 31950394Smckusick */ 32042407Smarc void 32150394Smckusick #ifdef __STDC__ 32250394Smckusick warning(char *message, ...) 32350394Smckusick #else 32450394Smckusick warning(va_alist) 32550394Smckusick va_dcl 32650394Smckusick #endif 3271429Sbill { 32850394Smckusick va_list ap; 32950394Smckusick #ifndef __STDC__ 33050394Smckusick char *message; 3311429Sbill 33250394Smckusick va_start(ap); 33350394Smckusick message = va_arg(ap, char *); 33450394Smckusick #else 33550394Smckusick va_start(ap, message); 33650394Smckusick #endif 33750394Smckusick 33850430Smckusick vsyslog(LOG_ALERT, message, ap); 33950394Smckusick va_end(ap); 3401429Sbill } 3411429Sbill 34250394Smckusick /* 34350430Smckusick * Log an emergency message. 34450430Smckusick * NB: should send a message to the session logger to avoid blocking. 34550394Smckusick */ 34650394Smckusick void 34750394Smckusick #ifdef __STDC__ 34850394Smckusick emergency(char *message, ...) 34950394Smckusick #else 35050394Smckusick emergency(va_alist) 35150394Smckusick va_dcl 35250394Smckusick #endif 3531429Sbill { 35450394Smckusick va_list ap; 35550394Smckusick #ifndef __STDC__ 35650394Smckusick char *message; 3571429Sbill 35850394Smckusick va_start(ap); 35950394Smckusick message = va_arg(ap, char *); 36050394Smckusick #else 36150394Smckusick va_start(ap, message); 36250394Smckusick #endif 36350394Smckusick 36450394Smckusick vsyslog(LOG_EMERG, message, ap); 36550394Smckusick va_end(ap); 3661029Sbill } 3671029Sbill 36850407Smckusick /* 36958422Smckusick * Catch a SIGSYS signal. 37058422Smckusick * 37158422Smckusick * These may arise if a system does not support sysctl. 37258422Smckusick * We tolerate up to 25 of these, then throw in the towel. 37358422Smckusick */ 37458422Smckusick void 37558422Smckusick badsys(sig) 37658422Smckusick int sig; 37758422Smckusick { 37858422Smckusick static int badcount = 0; 37958422Smckusick 38058422Smckusick if (badcount++ < 25) 38158422Smckusick return; 38258422Smckusick disaster(sig); 38358422Smckusick } 38458422Smckusick 38558422Smckusick /* 38650407Smckusick * Catch an unexpected signal. 38750407Smckusick */ 38850394Smckusick void 38950394Smckusick disaster(sig) 39050394Smckusick int sig; 3911029Sbill { 39250394Smckusick emergency("fatal signal: %s", 39350394Smckusick sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); 3941029Sbill 39550394Smckusick sleep(STALL_TIMEOUT); 39650394Smckusick _exit(sig); /* reboot */ 3971029Sbill } 3981029Sbill 39950394Smckusick /* 40058422Smckusick * Get the security level of the kernel. 40158422Smckusick */ 40258422Smckusick int 40358422Smckusick getsecuritylevel() 40458422Smckusick { 40558600Smckusick int name[2], curlevel; 40658600Smckusick size_t len; 40758422Smckusick extern int errno; 40858422Smckusick 40958422Smckusick name[0] = CTL_KERN; 41058422Smckusick name[1] = KERN_SECURELVL; 41158422Smckusick len = sizeof curlevel; 41258422Smckusick if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 41358422Smckusick emergency("cannot get kernel security level: %s", 41458422Smckusick strerror(errno)); 41558422Smckusick return (-1); 41658422Smckusick } 41758422Smckusick return (curlevel); 41858422Smckusick } 41958422Smckusick 42058422Smckusick /* 42158422Smckusick * Set the security level of the kernel. 42258422Smckusick */ 42358422Smckusick setsecuritylevel(newlevel) 42458422Smckusick int newlevel; 42558422Smckusick { 42658422Smckusick int name[2], len, curlevel; 42758422Smckusick extern int errno; 42858422Smckusick 42958422Smckusick curlevel = getsecuritylevel(); 43058422Smckusick if (newlevel == curlevel) 43158422Smckusick return; 43258422Smckusick name[0] = CTL_KERN; 43358422Smckusick name[1] = KERN_SECURELVL; 43458422Smckusick if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 43558422Smckusick emergency( 43658422Smckusick "cannot change kernel security level from %d to %d: %s", 43758422Smckusick curlevel, newlevel, strerror(errno)); 43858422Smckusick return; 43958422Smckusick } 44058422Smckusick #ifdef SECURE 44158422Smckusick warning("kernel security level changed from %d to %d", 44258422Smckusick curlevel, newlevel); 44358422Smckusick #endif 44458422Smckusick } 44558422Smckusick 44658422Smckusick /* 44750394Smckusick * Change states in the finite state machine. 44850394Smckusick * The initial state is passed as an argument. 44950394Smckusick */ 45050394Smckusick void 45150394Smckusick transition(s) 45250394Smckusick state_t s; 4531029Sbill { 45450394Smckusick for (;;) 45550394Smckusick s = (state_t) (*s)(); 45650394Smckusick } 4571029Sbill 45850394Smckusick /* 45950407Smckusick * Close out the accounting files for a login session. 46050430Smckusick * NB: should send a message to the session logger to avoid blocking. 46150407Smckusick */ 46250394Smckusick void 46350394Smckusick clear_session_logs(sp) 46450394Smckusick session_t *sp; 46550394Smckusick { 46653151Storek char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 46753151Storek 46853151Storek if (logout(line)) 46953151Storek logwtmp(line, "", ""); 47050394Smckusick } 47150394Smckusick 47213021Ssam /* 47350394Smckusick * Start a session and allocate a controlling terminal. 47450394Smckusick * Only called by children of init after forking. 47513021Ssam */ 47650394Smckusick void 47750394Smckusick setctty(name) 47850394Smckusick char *name; 4791029Sbill { 48050394Smckusick int fd; 4811029Sbill 48250407Smckusick (void) revoke(name); 48360300Smckusick sleep (2); /* leave DTR low */ 48450394Smckusick if ((fd = open(name, O_RDWR)) == -1) { 48550394Smckusick stall("can't open %s: %m", name); 48650394Smckusick _exit(1); 4871029Sbill } 48850394Smckusick if (login_tty(fd) == -1) { 48950394Smckusick stall("can't get %s for controlling terminal: %m", name); 49050394Smckusick _exit(1); 49150394Smckusick } 4921029Sbill } 4931029Sbill 49450407Smckusick /* 49550407Smckusick * Bring the system up single user. 49650407Smckusick */ 49750394Smckusick state_func_t 49850394Smckusick single_user() 49913021Ssam { 50050394Smckusick pid_t pid, wpid; 50150394Smckusick int status; 50250394Smckusick sigset_t mask; 50350394Smckusick char *shell = _PATH_BSHELL; 50450394Smckusick char *argv[2]; 50550394Smckusick #ifdef SECURE 50650394Smckusick struct ttyent *typ; 50750394Smckusick struct passwd *pp; 50850394Smckusick static const char banner[] = 50950394Smckusick "Enter root password, or ^D to go multi-user\n"; 51060300Smckusick char *clear, *password; 51150394Smckusick #endif 51213021Ssam 51358422Smckusick /* 51458422Smckusick * If the kernel is in secure mode, downgrade it to insecure mode. 51558422Smckusick */ 51658422Smckusick if (getsecuritylevel() > 0) 51758422Smckusick setsecuritylevel(0); 51858422Smckusick 51950394Smckusick if ((pid = fork()) == 0) { 52050394Smckusick /* 52150394Smckusick * Start the single user session. 52250394Smckusick */ 52350394Smckusick setctty(_PATH_CONSOLE); 52450394Smckusick 52550394Smckusick #ifdef SECURE 52650394Smckusick /* 52750394Smckusick * Check the root password. 52850394Smckusick * We don't care if the console is 'on' by default; 52950394Smckusick * it's the only tty that can be 'off' and 'secure'. 53050394Smckusick */ 53150394Smckusick typ = getttynam("console"); 53250394Smckusick pp = getpwnam("root"); 53350394Smckusick if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { 53450394Smckusick write(2, banner, sizeof banner - 1); 53550394Smckusick for (;;) { 53660300Smckusick clear = getpass("Password:"); 53760300Smckusick if (clear == 0 || *clear == '\0') 53850394Smckusick _exit(0); 53960300Smckusick password = crypt(clear, pp->pw_passwd); 54060300Smckusick bzero(clear, _PASSWORD_LEN); 54150394Smckusick if (strcmp(password, pp->pw_passwd) == 0) 54250394Smckusick break; 54358422Smckusick warning("single-user login failed\n"); 54413021Ssam } 54513021Ssam } 54650394Smckusick endttyent(); 54750394Smckusick endpwent(); 54858422Smckusick #endif /* SECURE */ 54918542Sralph 55058422Smckusick #ifdef DEBUGSHELL 55158422Smckusick { 55258422Smckusick char altshell[128], *cp = altshell; 55358422Smckusick int num; 55458422Smckusick 55558422Smckusick #define SHREQUEST \ 55658422Smckusick "Enter pathname of shell or RETURN for sh: " 55758422Smckusick (void)write(STDERR_FILENO, 55858422Smckusick SHREQUEST, sizeof(SHREQUEST) - 1); 55958422Smckusick while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 56058422Smckusick num != 0 && *cp != '\n' && cp < &altshell[127]) 56158422Smckusick cp++; 56258422Smckusick *cp = '\0'; 56358422Smckusick if (altshell[0] != '\0') 56458422Smckusick shell = altshell; 56558422Smckusick } 56658422Smckusick #endif /* DEBUGSHELL */ 56758422Smckusick 56823147Sbloom /* 56950394Smckusick * Unblock signals. 57050394Smckusick * We catch all the interesting ones, 57150394Smckusick * and those are reset to SIG_DFL on exec. 57223147Sbloom */ 57350394Smckusick sigemptyset(&mask); 57450394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 57550394Smckusick 57623147Sbloom /* 57750394Smckusick * Fire off a shell. 57850394Smckusick * If the default one doesn't work, try the Bourne shell. 57923147Sbloom */ 58050394Smckusick argv[0] = "-sh"; 58150394Smckusick argv[1] = 0; 58250394Smckusick execv(shell, argv); 58350394Smckusick emergency("can't exec %s for single user: %m", shell); 58450394Smckusick execv(_PATH_BSHELL, argv); 58550394Smckusick emergency("can't exec %s for single user: %m", _PATH_BSHELL); 58650394Smckusick sleep(STALL_TIMEOUT); 58750394Smckusick _exit(1); 58850394Smckusick } 58923147Sbloom 59050394Smckusick if (pid == -1) { 59150394Smckusick /* 59250394Smckusick * We are seriously hosed. Do our best. 59350394Smckusick */ 59450394Smckusick emergency("can't fork single-user shell, trying again"); 59550394Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 59652219Storek continue; 59750394Smckusick return (state_func_t) single_user; 59850394Smckusick } 59950394Smckusick 60050539Strent requested_transition = 0; 60150407Smckusick do { 60250407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 60350407Smckusick collect_child(wpid); 60450407Smckusick if (wpid == -1) { 60550407Smckusick if (errno == EINTR) 60650407Smckusick continue; 60750394Smckusick warning("wait for single-user shell failed: %m; restarting"); 60850394Smckusick return (state_func_t) single_user; 60923147Sbloom } 61050394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 61150394Smckusick warning("init: shell stopped, restarting\n"); 61250394Smckusick kill(pid, SIGCONT); 61350407Smckusick wpid = -1; 61450394Smckusick } 61550539Strent } while (wpid != pid && !requested_transition); 61650394Smckusick 61750539Strent if (requested_transition) 61850539Strent return (state_func_t) requested_transition; 61950539Strent 62050394Smckusick if (!WIFEXITED(status)) { 62150538Strent if (WTERMSIG(status) == SIGKILL) { 62250538Strent /* 62350538Strent * reboot(8) killed shell? 62450538Strent */ 62550538Strent warning("single user shell terminated."); 62650538Strent sleep(STALL_TIMEOUT); 62750538Strent _exit(0); 62850538Strent } else { 62950538Strent warning("single user shell terminated, restarting"); 63050538Strent return (state_func_t) single_user; 63150538Strent } 63250394Smckusick } 63350394Smckusick 63450394Smckusick runcom_mode = FASTBOOT; 63550394Smckusick return (state_func_t) runcom; 63650394Smckusick } 63750394Smckusick 63850407Smckusick /* 63950407Smckusick * Run the system startup script. 64050407Smckusick */ 64150394Smckusick state_func_t 64250394Smckusick runcom() 64350394Smckusick { 64450394Smckusick pid_t pid, wpid; 64550394Smckusick int status; 64650394Smckusick char *argv[4]; 64750407Smckusick struct sigaction sa; 64850394Smckusick 64950394Smckusick if ((pid = fork()) == 0) { 65050407Smckusick sigemptyset(&sa.sa_mask); 65150407Smckusick sa.sa_flags = 0; 65250407Smckusick sa.sa_handler = SIG_IGN; 65350407Smckusick (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); 65450407Smckusick (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); 65550407Smckusick 65650394Smckusick setctty(_PATH_CONSOLE); 65750394Smckusick 65850394Smckusick argv[0] = "sh"; 65950394Smckusick argv[1] = _PATH_RUNCOM; 66050394Smckusick argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; 66150394Smckusick argv[3] = 0; 66250394Smckusick 66350407Smckusick sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 66450394Smckusick 66550394Smckusick execv(_PATH_BSHELL, argv); 66650394Smckusick stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 66750394Smckusick _exit(1); /* force single user mode */ 66850394Smckusick } 66950394Smckusick 67050394Smckusick if (pid == -1) { 67150394Smckusick emergency("can't fork for %s on %s: %m", 67250394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 67350407Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 67452219Storek continue; 67550394Smckusick sleep(STALL_TIMEOUT); 67650394Smckusick return (state_func_t) single_user; 67750394Smckusick } 67850394Smckusick 67950394Smckusick /* 68050407Smckusick * Copied from single_user(). This is a bit paranoid. 68150394Smckusick */ 68250407Smckusick do { 68350407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 68450407Smckusick collect_child(wpid); 68550407Smckusick if (wpid == -1) { 68650407Smckusick if (errno == EINTR) 68750407Smckusick continue; 68850394Smckusick warning("wait for %s on %s failed: %m; going to single user mode", 68950394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 69050394Smckusick return (state_func_t) single_user; 69113021Ssam } 69250394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 69350394Smckusick warning("init: %s on %s stopped, restarting\n", 69450394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 69550394Smckusick kill(pid, SIGCONT); 69650407Smckusick wpid = -1; 69750394Smckusick } 69850407Smckusick } while (wpid != pid); 69950394Smckusick 70060300Smckusick if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 70160300Smckusick requested_transition == catatonia) { 70260300Smckusick /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 70360300Smckusick sigset_t s; 70460300Smckusick 70560300Smckusick sigfillset(&s); 70660300Smckusick for (;;) 70760300Smckusick sigsuspend(&s); 70860300Smckusick } 70960300Smckusick 71050394Smckusick if (!WIFEXITED(status)) { 71150394Smckusick warning("%s on %s terminated abnormally, going to single user mode", 71250394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 71350394Smckusick return (state_func_t) single_user; 71450394Smckusick } 71550394Smckusick 71650394Smckusick if (WEXITSTATUS(status)) 71750394Smckusick return (state_func_t) single_user; 71850394Smckusick 71950394Smckusick runcom_mode = AUTOBOOT; /* the default */ 72050430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 72150430Smckusick logwtmp("~", "reboot", ""); 72250394Smckusick return (state_func_t) read_ttys; 72313021Ssam } 72413021Ssam 72550394Smckusick /* 72650407Smckusick * Open the session database. 72750407Smckusick * 72850407Smckusick * NB: We could pass in the size here; is it necessary? 72950394Smckusick */ 73050407Smckusick int 73150394Smckusick start_session_db() 7321029Sbill { 73350407Smckusick if (session_db && (*session_db->close)(session_db)) 73450407Smckusick emergency("session database close: %s", strerror(errno)); 73551621Sbostic if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 73650407Smckusick emergency("session database open: %s", strerror(errno)); 73750407Smckusick return (1); 73850407Smckusick } 73950407Smckusick return (0); 74050407Smckusick 74150394Smckusick } 7421029Sbill 74350407Smckusick /* 74450407Smckusick * Add a new login session. 74550407Smckusick */ 74650394Smckusick void 74750394Smckusick add_session(sp) 74850394Smckusick session_t *sp; 74950394Smckusick { 75050394Smckusick DBT key; 75150394Smckusick DBT data; 75250394Smckusick 75350394Smckusick key.data = &sp->se_process; 75450394Smckusick key.size = sizeof sp->se_process; 75550394Smckusick data.data = &sp; 75650394Smckusick data.size = sizeof sp; 75750394Smckusick 75851621Sbostic if ((*session_db->put)(session_db, &key, &data, 0)) 75950407Smckusick emergency("insert %d: %s", sp->se_process, strerror(errno)); 76050394Smckusick } 76150394Smckusick 76250407Smckusick /* 76350407Smckusick * Delete an old login session. 76450407Smckusick */ 76550394Smckusick void 76650394Smckusick del_session(sp) 76750394Smckusick session_t *sp; 76850394Smckusick { 76950394Smckusick DBT key; 77050394Smckusick 77150394Smckusick key.data = &sp->se_process; 77250394Smckusick key.size = sizeof sp->se_process; 77350394Smckusick 77450407Smckusick if ((*session_db->del)(session_db, &key, 0)) 77550407Smckusick emergency("delete %d: %s", sp->se_process, strerror(errno)); 77650394Smckusick } 77750394Smckusick 77850407Smckusick /* 77950407Smckusick * Look up a login session by pid. 78050407Smckusick */ 78150394Smckusick session_t * 78250394Smckusick #ifdef __STDC__ 78350394Smckusick find_session(pid_t pid) 78450394Smckusick #else 78550394Smckusick find_session(pid) 78650394Smckusick pid_t pid; 78750394Smckusick #endif 78850394Smckusick { 78950394Smckusick DBT key; 79050394Smckusick DBT data; 79152219Storek session_t *ret; 79250394Smckusick 79350394Smckusick key.data = &pid; 79450394Smckusick key.size = sizeof pid; 79550394Smckusick if ((*session_db->get)(session_db, &key, &data, 0) != 0) 79650394Smckusick return 0; 79752219Storek bcopy(data.data, (char *)&ret, sizeof(ret)); 79852219Storek return ret; 79950394Smckusick } 80050394Smckusick 80150407Smckusick /* 80250407Smckusick * Construct an argument vector from a command line. 80350407Smckusick */ 80450394Smckusick char ** 80550394Smckusick construct_argv(command) 80650394Smckusick char *command; 80750394Smckusick { 80850394Smckusick register int argc = 0; 80950394Smckusick register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 81050394Smckusick * sizeof (char *)); 81150394Smckusick static const char separators[] = " \t"; 81250394Smckusick 81350394Smckusick if ((argv[argc++] = strtok(command, separators)) == 0) 81450394Smckusick return 0; 81550394Smckusick while (argv[argc++] = strtok((char *) 0, separators)) 81652219Storek continue; 81750394Smckusick return argv; 81850394Smckusick } 81950394Smckusick 82050407Smckusick /* 82150407Smckusick * Deallocate a session descriptor. 82250407Smckusick */ 82350394Smckusick void 82450394Smckusick free_session(sp) 82550394Smckusick register session_t *sp; 82650394Smckusick { 82750394Smckusick free(sp->se_device); 82850394Smckusick free(sp->se_getty); 82950394Smckusick free(sp->se_getty_argv); 83050394Smckusick if (sp->se_window) { 83150394Smckusick free(sp->se_window); 83250394Smckusick free(sp->se_window_argv); 8331029Sbill } 83450394Smckusick free(sp); 8351029Sbill } 8361029Sbill 83750407Smckusick /* 83850407Smckusick * Allocate a new session descriptor. 83950407Smckusick */ 84050394Smckusick session_t * 84150394Smckusick new_session(sprev, session_index, typ) 84250394Smckusick session_t *sprev; 84350394Smckusick int session_index; 84450394Smckusick register struct ttyent *typ; 8451029Sbill { 84650394Smckusick register session_t *sp; 8471029Sbill 84850394Smckusick if ((typ->ty_status & TTY_ON) == 0 || 84950394Smckusick typ->ty_name == 0 || 85050394Smckusick typ->ty_getty == 0) 85150394Smckusick return 0; 85250394Smckusick 85350394Smckusick sp = (session_t *) malloc(sizeof (session_t)); 85450394Smckusick 85550394Smckusick sp->se_index = session_index; 85650394Smckusick sp->se_process = 0; 85750394Smckusick sp->se_started = 0; 85850394Smckusick sp->se_flags = 0; 85950394Smckusick sp->se_window = 0; 86050394Smckusick 86153151Storek sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 86253151Storek (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 86350394Smckusick 86460524Smckusick sp->se_getty = strdup(typ->ty_getty); 86550394Smckusick sp->se_getty_argv = construct_argv(sp->se_getty); 86650394Smckusick if (sp->se_getty_argv == 0) { 86750394Smckusick warning("can't parse getty for port %s", 86850394Smckusick sp->se_device); 86950394Smckusick free_session(sp); 87050394Smckusick return 0; 8715971Sroot } 87250394Smckusick if (typ->ty_window) { 87350394Smckusick sp->se_window = strdup(typ->ty_window); 87450394Smckusick sp->se_window_argv = construct_argv(sp->se_window); 87550394Smckusick if (sp->se_window_argv == 0) { 87650394Smckusick warning("can't parse window for port %s", 87750394Smckusick sp->se_device); 87850394Smckusick free_session(sp); 87950394Smckusick return 0; 8805971Sroot } 8811029Sbill } 88250394Smckusick 88350394Smckusick sp->se_next = 0; 88450394Smckusick if (sprev == 0) { 88550394Smckusick sessions = sp; 88650394Smckusick sp->se_prev = 0; 88750394Smckusick } else { 88850394Smckusick sprev->se_next = sp; 88950394Smckusick sp->se_prev = sprev; 89050394Smckusick } 89150394Smckusick 89250394Smckusick return sp; 8931029Sbill } 8941029Sbill 89550407Smckusick /* 89650407Smckusick * Walk the list of ttys and create sessions for each active line. 89750407Smckusick */ 89850394Smckusick state_func_t 89950394Smckusick read_ttys() 9001029Sbill { 90150394Smckusick int session_index = 0; 90250394Smckusick register session_t *sp, *snext; 90350394Smckusick register struct ttyent *typ; 90450394Smckusick 90550394Smckusick /* 90650394Smckusick * Destroy any previous session state. 90750394Smckusick * There shouldn't be any, but just in case... 90850394Smckusick */ 90950394Smckusick for (sp = sessions; sp; sp = snext) { 91050394Smckusick if (sp->se_process) 91150394Smckusick clear_session_logs(sp); 91250394Smckusick snext = sp->se_next; 91350394Smckusick free_session(sp); 9141029Sbill } 91550394Smckusick sessions = 0; 91650407Smckusick if (start_session_db()) 91750407Smckusick return (state_func_t) single_user; 91850394Smckusick 91950394Smckusick /* 92050394Smckusick * Allocate a session entry for each active port. 92150394Smckusick * Note that sp starts at 0. 92250394Smckusick */ 92350394Smckusick while (typ = getttyent()) 92450394Smckusick if (snext = new_session(sp, ++session_index, typ)) 92550394Smckusick sp = snext; 92650394Smckusick 92750394Smckusick endttyent(); 92850394Smckusick 92950394Smckusick return (state_func_t) multi_user; 9301029Sbill } 9311029Sbill 93250407Smckusick /* 93350407Smckusick * Start a window system running. 93450407Smckusick */ 93542407Smarc void 93650394Smckusick start_window_system(sp) 93750394Smckusick session_t *sp; 9381029Sbill { 93950394Smckusick pid_t pid; 94050394Smckusick sigset_t mask; 94150394Smckusick 94250394Smckusick if ((pid = fork()) == -1) { 94350394Smckusick emergency("can't fork for window system on port %s: %m", 94450394Smckusick sp->se_device); 94550394Smckusick /* hope that getty fails and we can try again */ 94650394Smckusick return; 94750394Smckusick } 94850394Smckusick 94950394Smckusick if (pid) 95050394Smckusick return; 95150394Smckusick 95250394Smckusick sigemptyset(&mask); 95350394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 95450394Smckusick 95550407Smckusick if (setsid() < 0) 95650407Smckusick emergency("setsid failed (window) %m"); 95750407Smckusick 95850394Smckusick execv(sp->se_window_argv[0], sp->se_window_argv); 95950394Smckusick stall("can't exec window system '%s' for port %s: %m", 96050394Smckusick sp->se_window_argv[0], sp->se_device); 96150394Smckusick _exit(1); 9621029Sbill } 9632821Swnj 96450407Smckusick /* 96550407Smckusick * Start a login session running. 96650407Smckusick */ 96750394Smckusick pid_t 96850394Smckusick start_getty(sp) 96950394Smckusick session_t *sp; 97050394Smckusick { 97150394Smckusick pid_t pid; 97250394Smckusick sigset_t mask; 97350394Smckusick time_t current_time = time((time_t *) 0); 97413021Ssam 97550394Smckusick /* 97650394Smckusick * fork(), not vfork() -- we can't afford to block. 97750394Smckusick */ 97850394Smckusick if ((pid = fork()) == -1) { 97950394Smckusick emergency("can't fork for getty on port %s: %m", sp->se_device); 98050394Smckusick return -1; 98150394Smckusick } 98250394Smckusick 98350394Smckusick if (pid) 98450394Smckusick return pid; 98550394Smckusick 98650394Smckusick if (current_time > sp->se_started && 98750394Smckusick current_time - sp->se_started < GETTY_SPACING) { 98850394Smckusick warning("getty repeating too quickly on port %s, sleeping", 98950394Smckusick sp->se_device); 99060306Smckusick sleep((unsigned) GETTY_SPACING + 1 - 99150394Smckusick (current_time - sp->se_started)); 99250394Smckusick } 99350394Smckusick 99450394Smckusick if (sp->se_window) { 99550394Smckusick start_window_system(sp); 99650394Smckusick sleep(WINDOW_WAIT); 99750394Smckusick } 99850394Smckusick 99950394Smckusick setctty(sp->se_device); 100050394Smckusick 100150394Smckusick sigemptyset(&mask); 100250394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 100350394Smckusick 100450394Smckusick execv(sp->se_getty_argv[0], sp->se_getty_argv); 100550394Smckusick stall("can't exec getty '%s' for port %s: %m", 100650394Smckusick sp->se_getty_argv[0], sp->se_device); 100750394Smckusick _exit(1); 100813021Ssam } 100913021Ssam 101050407Smckusick /* 101150407Smckusick * Collect exit status for a child. 101250407Smckusick * If an exiting login, start a new login running. 101350407Smckusick */ 101442407Smarc void 101550407Smckusick collect_child(pid) 101650407Smckusick pid_t pid; 10172821Swnj { 101850394Smckusick register session_t *sp, *sprev, *snext; 10192821Swnj 102050407Smckusick if (! sessions) 102150407Smckusick return; 102250394Smckusick 102350407Smckusick if (! (sp = find_session(pid))) 102450407Smckusick return; 102550394Smckusick 102650407Smckusick clear_session_logs(sp); 102750407Smckusick del_session(sp); 102850407Smckusick sp->se_process = 0; 102950394Smckusick 103050407Smckusick if (sp->se_flags & SE_SHUTDOWN) { 103150407Smckusick if (sprev = sp->se_prev) 103250407Smckusick sprev->se_next = sp->se_next; 103350407Smckusick else 103450407Smckusick sessions = sp->se_next; 103550407Smckusick if (snext = sp->se_next) 103650407Smckusick snext->se_prev = sp->se_prev; 103750407Smckusick free_session(sp); 103850407Smckusick return; 103950407Smckusick } 104050394Smckusick 104150407Smckusick if ((pid = start_getty(sp)) == -1) { 104250407Smckusick /* serious trouble */ 104350407Smckusick requested_transition = clean_ttys; 104450407Smckusick return; 10452821Swnj } 104650394Smckusick 104750407Smckusick sp->se_process = pid; 104850407Smckusick sp->se_started = time((time_t *) 0); 104950407Smckusick add_session(sp); 10502821Swnj } 105118542Sralph 105250407Smckusick /* 105350407Smckusick * Catch a signal and request a state transition. 105450407Smckusick */ 105550394Smckusick void 105650394Smckusick transition_handler(sig) 105750394Smckusick int sig; 105818542Sralph { 105950539Strent 106050394Smckusick switch (sig) { 106150394Smckusick case SIGHUP: 106250394Smckusick requested_transition = clean_ttys; 106350394Smckusick break; 106450394Smckusick case SIGTERM: 106550394Smckusick requested_transition = death; 106650394Smckusick break; 106750394Smckusick case SIGTSTP: 106850394Smckusick requested_transition = catatonia; 106950394Smckusick break; 107050394Smckusick default: 107150394Smckusick requested_transition = 0; 107250394Smckusick break; 107318542Sralph } 107418542Sralph } 107518542Sralph 107650407Smckusick /* 107750407Smckusick * Take the system multiuser. 107850407Smckusick */ 107950394Smckusick state_func_t 108050394Smckusick multi_user() 108118542Sralph { 108250394Smckusick pid_t pid; 108350394Smckusick register session_t *sp; 108418542Sralph 108550394Smckusick requested_transition = 0; 108622181Skarels 108758422Smckusick /* 108858422Smckusick * If the administrator has not set the security level to -1 108958422Smckusick * to indicate that the kernel should not run multiuser in secure 109058422Smckusick * mode, and the run script has not set a higher level of security 109158422Smckusick * than level 1, then put the kernel into secure mode. 109258422Smckusick */ 109358422Smckusick if (getsecuritylevel() == 0) 109458422Smckusick setsecuritylevel(1); 109558422Smckusick 109650407Smckusick for (sp = sessions; sp; sp = sp->se_next) { 109750407Smckusick if (sp->se_process) 109850407Smckusick continue; 109950407Smckusick if ((pid = start_getty(sp)) == -1) { 110050407Smckusick /* serious trouble */ 110150407Smckusick requested_transition = clean_ttys; 110250407Smckusick break; 110322181Skarels } 110450407Smckusick sp->se_process = pid; 110550407Smckusick sp->se_started = time((time_t *) 0); 110650407Smckusick add_session(sp); 110750407Smckusick } 110850394Smckusick 110950394Smckusick while (!requested_transition) 111050407Smckusick if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 111150407Smckusick collect_child(pid); 111250394Smckusick 111350394Smckusick return (state_func_t) requested_transition; 111418542Sralph } 111518542Sralph 111650394Smckusick /* 111750394Smckusick * This is an n-squared algorithm. We hope it isn't run often... 111850394Smckusick */ 111950394Smckusick state_func_t 112050394Smckusick clean_ttys() 112118542Sralph { 112250394Smckusick register session_t *sp, *sprev; 112350394Smckusick register struct ttyent *typ; 112450394Smckusick register int session_index = 0; 112552954Smckusick register int devlen; 112618542Sralph 112750394Smckusick if (! sessions) 112850394Smckusick return (state_func_t) multi_user; 112950394Smckusick 113053151Storek devlen = sizeof(_PATH_DEV) - 1; 113150394Smckusick while (typ = getttyent()) { 113250394Smckusick ++session_index; 113350394Smckusick 113460300Smckusick for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 113553151Storek if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 113618542Sralph break; 113750394Smckusick 113850394Smckusick if (sp) { 113950394Smckusick if (sp->se_index != session_index) { 114050394Smckusick warning("port %s changed utmp index from %d to %d", 114150394Smckusick sp->se_device, sp->se_index, 114250394Smckusick session_index); 114350394Smckusick sp->se_index = session_index; 114418542Sralph } 114550394Smckusick if (typ->ty_status & TTY_ON) 114650394Smckusick sp->se_flags &= ~SE_SHUTDOWN; 114750394Smckusick else { 114850394Smckusick sp->se_flags |= SE_SHUTDOWN; 114950394Smckusick kill(sp->se_process, SIGHUP); 115050394Smckusick } 115150394Smckusick continue; 115218542Sralph } 115350394Smckusick 115450394Smckusick new_session(sprev, session_index, typ); 115518542Sralph } 115650394Smckusick 115750394Smckusick endttyent(); 115850394Smckusick 115950394Smckusick return (state_func_t) multi_user; 116018542Sralph } 116150394Smckusick 116250407Smckusick /* 116350407Smckusick * Block further logins. 116450407Smckusick */ 116550394Smckusick state_func_t 116650394Smckusick catatonia() 116750394Smckusick { 116850394Smckusick register session_t *sp; 116950394Smckusick 117050394Smckusick for (sp = sessions; sp; sp = sp->se_next) 117150394Smckusick sp->se_flags |= SE_SHUTDOWN; 117250394Smckusick 117350394Smckusick return (state_func_t) multi_user; 117450394Smckusick } 117550394Smckusick 117650407Smckusick /* 117750407Smckusick * Note SIGALRM. 117850407Smckusick */ 117950394Smckusick void 118050394Smckusick alrm_handler(sig) 118150394Smckusick int sig; 118250394Smckusick { 118350394Smckusick clang = 1; 118450394Smckusick } 118550394Smckusick 118650407Smckusick /* 118750407Smckusick * Bring the system down to single user. 118850407Smckusick */ 118950394Smckusick state_func_t 119050394Smckusick death() 119150394Smckusick { 119250394Smckusick register session_t *sp; 119350394Smckusick register int i; 119450407Smckusick pid_t pid; 119550394Smckusick static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 119650394Smckusick 119750394Smckusick for (sp = sessions; sp; sp = sp->se_next) 119850394Smckusick sp->se_flags |= SE_SHUTDOWN; 119950394Smckusick 120050430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 120150430Smckusick logwtmp("~", "shutdown", ""); 120250394Smckusick 120350394Smckusick for (i = 0; i < 3; ++i) { 120450394Smckusick if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 120550394Smckusick return (state_func_t) single_user; 120650394Smckusick 120750394Smckusick clang = 0; 120850394Smckusick alarm(DEATH_WATCH); 120950394Smckusick do 121050407Smckusick if ((pid = waitpid(-1, (int *)0, 0)) != -1) 121150407Smckusick collect_child(pid); 121250407Smckusick while (clang == 0 && errno != ECHILD); 121350394Smckusick 121450407Smckusick if (errno == ECHILD) 121550394Smckusick return (state_func_t) single_user; 121650394Smckusick } 121750394Smckusick 121850394Smckusick warning("some processes wouldn't die"); 121950394Smckusick 122050394Smckusick return (state_func_t) single_user; 122150394Smckusick } 1222