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 6*60435Sbostic * 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*60435Sbostic static char sccsid[] = "@(#)init.c 6.18 (Berkeley) 05/26/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 *)); 11750407Smckusick void collect_child __P((int)); 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 86460306Smckusick sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); 86560306Smckusick (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); 86650394Smckusick sp->se_getty_argv = construct_argv(sp->se_getty); 86750394Smckusick if (sp->se_getty_argv == 0) { 86850394Smckusick warning("can't parse getty for port %s", 86950394Smckusick sp->se_device); 87050394Smckusick free_session(sp); 87150394Smckusick return 0; 8725971Sroot } 87350394Smckusick if (typ->ty_window) { 87450394Smckusick sp->se_window = strdup(typ->ty_window); 87550394Smckusick sp->se_window_argv = construct_argv(sp->se_window); 87650394Smckusick if (sp->se_window_argv == 0) { 87750394Smckusick warning("can't parse window for port %s", 87850394Smckusick sp->se_device); 87950394Smckusick free_session(sp); 88050394Smckusick return 0; 8815971Sroot } 8821029Sbill } 88350394Smckusick 88450394Smckusick sp->se_next = 0; 88550394Smckusick if (sprev == 0) { 88650394Smckusick sessions = sp; 88750394Smckusick sp->se_prev = 0; 88850394Smckusick } else { 88950394Smckusick sprev->se_next = sp; 89050394Smckusick sp->se_prev = sprev; 89150394Smckusick } 89250394Smckusick 89350394Smckusick return sp; 8941029Sbill } 8951029Sbill 89650407Smckusick /* 89750407Smckusick * Walk the list of ttys and create sessions for each active line. 89850407Smckusick */ 89950394Smckusick state_func_t 90050394Smckusick read_ttys() 9011029Sbill { 90250394Smckusick int session_index = 0; 90350394Smckusick register session_t *sp, *snext; 90450394Smckusick register struct ttyent *typ; 90550394Smckusick 90650394Smckusick /* 90750394Smckusick * Destroy any previous session state. 90850394Smckusick * There shouldn't be any, but just in case... 90950394Smckusick */ 91050394Smckusick for (sp = sessions; sp; sp = snext) { 91150394Smckusick if (sp->se_process) 91250394Smckusick clear_session_logs(sp); 91350394Smckusick snext = sp->se_next; 91450394Smckusick free_session(sp); 9151029Sbill } 91650394Smckusick sessions = 0; 91750407Smckusick if (start_session_db()) 91850407Smckusick return (state_func_t) single_user; 91950394Smckusick 92050394Smckusick /* 92150394Smckusick * Allocate a session entry for each active port. 92250394Smckusick * Note that sp starts at 0. 92350394Smckusick */ 92450394Smckusick while (typ = getttyent()) 92550394Smckusick if (snext = new_session(sp, ++session_index, typ)) 92650394Smckusick sp = snext; 92750394Smckusick 92850394Smckusick endttyent(); 92950394Smckusick 93050394Smckusick return (state_func_t) multi_user; 9311029Sbill } 9321029Sbill 93350407Smckusick /* 93450407Smckusick * Start a window system running. 93550407Smckusick */ 93642407Smarc void 93750394Smckusick start_window_system(sp) 93850394Smckusick session_t *sp; 9391029Sbill { 94050394Smckusick pid_t pid; 94150394Smckusick sigset_t mask; 94250394Smckusick 94350394Smckusick if ((pid = fork()) == -1) { 94450394Smckusick emergency("can't fork for window system on port %s: %m", 94550394Smckusick sp->se_device); 94650394Smckusick /* hope that getty fails and we can try again */ 94750394Smckusick return; 94850394Smckusick } 94950394Smckusick 95050394Smckusick if (pid) 95150394Smckusick return; 95250394Smckusick 95350394Smckusick sigemptyset(&mask); 95450394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 95550394Smckusick 95650407Smckusick if (setsid() < 0) 95750407Smckusick emergency("setsid failed (window) %m"); 95850407Smckusick 95950394Smckusick execv(sp->se_window_argv[0], sp->se_window_argv); 96050394Smckusick stall("can't exec window system '%s' for port %s: %m", 96150394Smckusick sp->se_window_argv[0], sp->se_device); 96250394Smckusick _exit(1); 9631029Sbill } 9642821Swnj 96550407Smckusick /* 96650407Smckusick * Start a login session running. 96750407Smckusick */ 96850394Smckusick pid_t 96950394Smckusick start_getty(sp) 97050394Smckusick session_t *sp; 97150394Smckusick { 97250394Smckusick pid_t pid; 97350394Smckusick sigset_t mask; 97450394Smckusick time_t current_time = time((time_t *) 0); 97513021Ssam 97650394Smckusick /* 97750394Smckusick * fork(), not vfork() -- we can't afford to block. 97850394Smckusick */ 97950394Smckusick if ((pid = fork()) == -1) { 98050394Smckusick emergency("can't fork for getty on port %s: %m", sp->se_device); 98150394Smckusick return -1; 98250394Smckusick } 98350394Smckusick 98450394Smckusick if (pid) 98550394Smckusick return pid; 98650394Smckusick 98750394Smckusick if (current_time > sp->se_started && 98850394Smckusick current_time - sp->se_started < GETTY_SPACING) { 98950394Smckusick warning("getty repeating too quickly on port %s, sleeping", 99050394Smckusick sp->se_device); 99160306Smckusick sleep((unsigned) GETTY_SPACING + 1 - 99250394Smckusick (current_time - sp->se_started)); 99350394Smckusick } 99450394Smckusick 99550394Smckusick if (sp->se_window) { 99650394Smckusick start_window_system(sp); 99750394Smckusick sleep(WINDOW_WAIT); 99850394Smckusick } 99950394Smckusick 100050394Smckusick setctty(sp->se_device); 100150394Smckusick 100250394Smckusick sigemptyset(&mask); 100350394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 100450394Smckusick 100550394Smckusick execv(sp->se_getty_argv[0], sp->se_getty_argv); 100650394Smckusick stall("can't exec getty '%s' for port %s: %m", 100750394Smckusick sp->se_getty_argv[0], sp->se_device); 100850394Smckusick _exit(1); 100913021Ssam } 101013021Ssam 101150407Smckusick /* 101250407Smckusick * Collect exit status for a child. 101350407Smckusick * If an exiting login, start a new login running. 101450407Smckusick */ 101542407Smarc void 101650407Smckusick collect_child(pid) 101750407Smckusick pid_t pid; 10182821Swnj { 101950394Smckusick register session_t *sp, *sprev, *snext; 10202821Swnj 102150407Smckusick if (! sessions) 102250407Smckusick return; 102350394Smckusick 102450407Smckusick if (! (sp = find_session(pid))) 102550407Smckusick return; 102650394Smckusick 102750407Smckusick clear_session_logs(sp); 102850407Smckusick del_session(sp); 102950407Smckusick sp->se_process = 0; 103050394Smckusick 103150407Smckusick if (sp->se_flags & SE_SHUTDOWN) { 103250407Smckusick if (sprev = sp->se_prev) 103350407Smckusick sprev->se_next = sp->se_next; 103450407Smckusick else 103550407Smckusick sessions = sp->se_next; 103650407Smckusick if (snext = sp->se_next) 103750407Smckusick snext->se_prev = sp->se_prev; 103850407Smckusick free_session(sp); 103950407Smckusick return; 104050407Smckusick } 104150394Smckusick 104250407Smckusick if ((pid = start_getty(sp)) == -1) { 104350407Smckusick /* serious trouble */ 104450407Smckusick requested_transition = clean_ttys; 104550407Smckusick return; 10462821Swnj } 104750394Smckusick 104850407Smckusick sp->se_process = pid; 104950407Smckusick sp->se_started = time((time_t *) 0); 105050407Smckusick add_session(sp); 10512821Swnj } 105218542Sralph 105350407Smckusick /* 105450407Smckusick * Catch a signal and request a state transition. 105550407Smckusick */ 105650394Smckusick void 105750394Smckusick transition_handler(sig) 105850394Smckusick int sig; 105918542Sralph { 106050539Strent 106150394Smckusick switch (sig) { 106250394Smckusick case SIGHUP: 106350394Smckusick requested_transition = clean_ttys; 106450394Smckusick break; 106550394Smckusick case SIGTERM: 106650394Smckusick requested_transition = death; 106750394Smckusick break; 106850394Smckusick case SIGTSTP: 106950394Smckusick requested_transition = catatonia; 107050394Smckusick break; 107150394Smckusick default: 107250394Smckusick requested_transition = 0; 107350394Smckusick break; 107418542Sralph } 107518542Sralph } 107618542Sralph 107750407Smckusick /* 107850407Smckusick * Take the system multiuser. 107950407Smckusick */ 108050394Smckusick state_func_t 108150394Smckusick multi_user() 108218542Sralph { 108350394Smckusick pid_t pid; 108450394Smckusick register session_t *sp; 108518542Sralph 108650394Smckusick requested_transition = 0; 108722181Skarels 108858422Smckusick /* 108958422Smckusick * If the administrator has not set the security level to -1 109058422Smckusick * to indicate that the kernel should not run multiuser in secure 109158422Smckusick * mode, and the run script has not set a higher level of security 109258422Smckusick * than level 1, then put the kernel into secure mode. 109358422Smckusick */ 109458422Smckusick if (getsecuritylevel() == 0) 109558422Smckusick setsecuritylevel(1); 109658422Smckusick 109750407Smckusick for (sp = sessions; sp; sp = sp->se_next) { 109850407Smckusick if (sp->se_process) 109950407Smckusick continue; 110050407Smckusick if ((pid = start_getty(sp)) == -1) { 110150407Smckusick /* serious trouble */ 110250407Smckusick requested_transition = clean_ttys; 110350407Smckusick break; 110422181Skarels } 110550407Smckusick sp->se_process = pid; 110650407Smckusick sp->se_started = time((time_t *) 0); 110750407Smckusick add_session(sp); 110850407Smckusick } 110950394Smckusick 111050394Smckusick while (!requested_transition) 111150407Smckusick if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 111250407Smckusick collect_child(pid); 111350394Smckusick 111450394Smckusick return (state_func_t) requested_transition; 111518542Sralph } 111618542Sralph 111750394Smckusick /* 111850394Smckusick * This is an n-squared algorithm. We hope it isn't run often... 111950394Smckusick */ 112050394Smckusick state_func_t 112150394Smckusick clean_ttys() 112218542Sralph { 112350394Smckusick register session_t *sp, *sprev; 112450394Smckusick register struct ttyent *typ; 112550394Smckusick register int session_index = 0; 112652954Smckusick register int devlen; 112718542Sralph 112850394Smckusick if (! sessions) 112950394Smckusick return (state_func_t) multi_user; 113050394Smckusick 113153151Storek devlen = sizeof(_PATH_DEV) - 1; 113250394Smckusick while (typ = getttyent()) { 113350394Smckusick ++session_index; 113450394Smckusick 113560300Smckusick for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 113653151Storek if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 113718542Sralph break; 113850394Smckusick 113950394Smckusick if (sp) { 114050394Smckusick if (sp->se_index != session_index) { 114150394Smckusick warning("port %s changed utmp index from %d to %d", 114250394Smckusick sp->se_device, sp->se_index, 114350394Smckusick session_index); 114450394Smckusick sp->se_index = session_index; 114518542Sralph } 114650394Smckusick if (typ->ty_status & TTY_ON) 114750394Smckusick sp->se_flags &= ~SE_SHUTDOWN; 114850394Smckusick else { 114950394Smckusick sp->se_flags |= SE_SHUTDOWN; 115050394Smckusick kill(sp->se_process, SIGHUP); 115150394Smckusick } 115250394Smckusick continue; 115318542Sralph } 115450394Smckusick 115550394Smckusick new_session(sprev, session_index, typ); 115618542Sralph } 115750394Smckusick 115850394Smckusick endttyent(); 115950394Smckusick 116050394Smckusick return (state_func_t) multi_user; 116118542Sralph } 116250394Smckusick 116350407Smckusick /* 116450407Smckusick * Block further logins. 116550407Smckusick */ 116650394Smckusick state_func_t 116750394Smckusick catatonia() 116850394Smckusick { 116950394Smckusick register session_t *sp; 117050394Smckusick 117150394Smckusick for (sp = sessions; sp; sp = sp->se_next) 117250394Smckusick sp->se_flags |= SE_SHUTDOWN; 117350394Smckusick 117450394Smckusick return (state_func_t) multi_user; 117550394Smckusick } 117650394Smckusick 117750407Smckusick /* 117850407Smckusick * Note SIGALRM. 117950407Smckusick */ 118050394Smckusick void 118150394Smckusick alrm_handler(sig) 118250394Smckusick int sig; 118350394Smckusick { 118450394Smckusick clang = 1; 118550394Smckusick } 118650394Smckusick 118750407Smckusick /* 118850407Smckusick * Bring the system down to single user. 118950407Smckusick */ 119050394Smckusick state_func_t 119150394Smckusick death() 119250394Smckusick { 119350394Smckusick register session_t *sp; 119450394Smckusick register int i; 119550407Smckusick pid_t pid; 119650394Smckusick static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 119750394Smckusick 119850394Smckusick for (sp = sessions; sp; sp = sp->se_next) 119950394Smckusick sp->se_flags |= SE_SHUTDOWN; 120050394Smckusick 120150430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 120250430Smckusick logwtmp("~", "shutdown", ""); 120350394Smckusick 120450394Smckusick for (i = 0; i < 3; ++i) { 120550394Smckusick if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 120650394Smckusick return (state_func_t) single_user; 120750394Smckusick 120850394Smckusick clang = 0; 120950394Smckusick alarm(DEATH_WATCH); 121050394Smckusick do 121150407Smckusick if ((pid = waitpid(-1, (int *)0, 0)) != -1) 121250407Smckusick collect_child(pid); 121350407Smckusick while (clang == 0 && errno != ECHILD); 121450394Smckusick 121550407Smckusick if (errno == ECHILD) 121650394Smckusick return (state_func_t) single_user; 121750394Smckusick } 121850394Smckusick 121950394Smckusick warning("some processes wouldn't die"); 122050394Smckusick 122150394Smckusick return (state_func_t) single_user; 122250394Smckusick } 1223