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*61084Smckusick static char sccsid[] = "@(#)init.c 6.23 (Berkeley) 06/03/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 */ 59*61084Smckusick #define GETTY_SPACING 5 /* N secs minimum getty spacing */ 60*61084Smckusick #define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ 6150394Smckusick #define WINDOW_WAIT 3 /* wait N secs after starting window */ 6250394Smckusick #define STALL_TIMEOUT 30 /* wait N secs after warning */ 6350407Smckusick #define DEATH_WATCH 10 /* wait N secs for procs to die */ 641029Sbill 6550394Smckusick void handle __P((sig_t, ...)); 6650394Smckusick void delset __P((sigset_t *, ...)); 671029Sbill 6850394Smckusick void stall __P((char *, ...)); 6950394Smckusick void warning __P((char *, ...)); 7050394Smckusick void emergency __P((char *, ...)); 7150394Smckusick void disaster __P((int)); 7258422Smckusick void badsys __P((int)); 731029Sbill 7450394Smckusick /* 7550394Smckusick * We really need a recursive typedef... 7650394Smckusick * The following at least guarantees that the return type of (*state_t)() 7750394Smckusick * is sufficiently wide to hold a function pointer. 7850394Smckusick */ 7950394Smckusick typedef long (*state_func_t) __P((void)); 8050394Smckusick typedef state_func_t (*state_t) __P((void)); 811029Sbill 8250394Smckusick state_func_t single_user __P((void)); 8350394Smckusick state_func_t runcom __P((void)); 8450394Smckusick state_func_t read_ttys __P((void)); 8550394Smckusick state_func_t multi_user __P((void)); 8650394Smckusick state_func_t clean_ttys __P((void)); 8750394Smckusick state_func_t catatonia __P((void)); 8850394Smckusick state_func_t death __P((void)); 8913021Ssam 9050394Smckusick enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 9150394Smckusick 9250394Smckusick void transition __P((state_t)); 9350394Smckusick state_t requested_transition = runcom; 9450394Smckusick 9550394Smckusick void setctty __P((char *)); 9650394Smckusick 9759441Sbostic typedef struct init_session { 9850394Smckusick int se_index; /* index of entry in ttys file */ 9950394Smckusick pid_t se_process; /* controlling process */ 10050394Smckusick time_t se_started; /* used to avoid thrashing */ 10150394Smckusick int se_flags; /* status of session */ 10250394Smckusick #define SE_SHUTDOWN 0x1 /* session won't be restarted */ 10350394Smckusick char *se_device; /* filename of port */ 10450394Smckusick char *se_getty; /* what to run on that port */ 10550394Smckusick char **se_getty_argv; /* pre-parsed argument array */ 10650394Smckusick char *se_window; /* window system (started only once) */ 10750394Smckusick char **se_window_argv; /* pre-parsed argument array */ 10859441Sbostic struct init_session *se_prev; 10959441Sbostic struct init_session *se_next; 11050394Smckusick } session_t; 11150394Smckusick 11250394Smckusick void free_session __P((session_t *)); 11350394Smckusick session_t *new_session __P((session_t *, int, struct ttyent *)); 11450394Smckusick session_t *sessions; 11550394Smckusick 11650394Smckusick char **construct_argv __P((char *)); 11750394Smckusick void start_window_system __P((session_t *)); 11860617Smckusick void collect_child __P((pid_t)); 11950394Smckusick pid_t start_getty __P((session_t *)); 12050394Smckusick void transition_handler __P((int)); 12150394Smckusick void alrm_handler __P((int)); 12261021Smckusick void setsecuritylevel __P((int)); 12361021Smckusick int getsecuritylevel __P((void)); 12461021Smckusick int setupargv __P((session_t *, struct ttyent *)); 12550394Smckusick int clang; 12650394Smckusick 12750394Smckusick void clear_session_logs __P((session_t *)); 12850394Smckusick 12950407Smckusick int start_session_db __P((void)); 13050394Smckusick void add_session __P((session_t *)); 13150394Smckusick void del_session __P((session_t *)); 13250394Smckusick session_t *find_session __P((pid_t)); 13350394Smckusick DB *session_db; 13450394Smckusick 13550407Smckusick /* 13650407Smckusick * The mother of all processes. 13750407Smckusick */ 13850394Smckusick int 13943633Skarels main(argc, argv) 14050394Smckusick int argc; 14143633Skarels char **argv; 1421029Sbill { 14350394Smckusick int c; 14450407Smckusick struct sigaction sa; 14550394Smckusick sigset_t mask; 14647659Skarels 1479869Spugs 14852795Sbostic /* Dispose of random users. */ 14952795Sbostic if (getuid() != 0) { 15052795Sbostic (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); 15152795Sbostic exit (1); 15252795Sbostic } 15352795Sbostic 15452795Sbostic /* System V users like to reexec init. */ 15552795Sbostic if (getpid() != 1) { 15652795Sbostic (void)fprintf(stderr, "init: already running\n"); 15752795Sbostic exit (1); 15852795Sbostic } 15952795Sbostic 16050394Smckusick /* 16150394Smckusick * Note that this does NOT open a file... 16250394Smckusick * Does 'init' deserve its own facility number? 16350394Smckusick */ 16450407Smckusick openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 16550394Smckusick 16650394Smckusick /* 16750407Smckusick * Create an initial session. 16850407Smckusick */ 16950407Smckusick if (setsid() < 0) 17060300Smckusick warning("initial setsid() failed: %m"); 17150407Smckusick 17250407Smckusick /* 17350394Smckusick * This code assumes that we always get arguments through flags, 17450394Smckusick * never through bits set in some random machine register. 17550394Smckusick */ 17650394Smckusick while ((c = getopt(argc, argv, "sf")) != -1) 17750394Smckusick switch (c) { 17850394Smckusick case 's': 17950394Smckusick requested_transition = single_user; 18050394Smckusick break; 18147659Skarels case 'f': 18250394Smckusick runcom_mode = FASTBOOT; 1839869Spugs break; 18450394Smckusick default: 18550394Smckusick warning("unrecognized flag '-%c'", c); 1869869Spugs break; 1879869Spugs } 18850394Smckusick 18950394Smckusick if (optind != argc) 19050394Smckusick warning("ignoring excess arguments"); 19150394Smckusick 19250394Smckusick /* 19350394Smckusick * We catch or block signals rather than ignore them, 19450394Smckusick * so that they get reset on exec. 19550394Smckusick */ 19658422Smckusick handle(badsys, SIGSYS, 0); 19750394Smckusick handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 19858422Smckusick SIGBUS, SIGXCPU, SIGXFSZ, 0); 19950394Smckusick handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); 20050394Smckusick handle(alrm_handler, SIGALRM, 0); 20150394Smckusick sigfillset(&mask); 20250407Smckusick delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 20350407Smckusick SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); 20450394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 20550407Smckusick sigemptyset(&sa.sa_mask); 20650407Smckusick sa.sa_flags = 0; 20750407Smckusick sa.sa_handler = SIG_IGN; 20850407Smckusick (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); 20950407Smckusick (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); 21050394Smckusick 21150394Smckusick /* 21250394Smckusick * Paranoia. 21350394Smckusick */ 21450394Smckusick close(0); 21550394Smckusick close(1); 21650394Smckusick close(2); 21750394Smckusick 21850394Smckusick /* 21950394Smckusick * Start the state machine. 22050394Smckusick */ 22150394Smckusick transition(requested_transition); 22250394Smckusick 22350394Smckusick /* 22450394Smckusick * Should never reach here. 22550394Smckusick */ 22650394Smckusick return 1; 22750394Smckusick } 22850394Smckusick 22950407Smckusick /* 23050407Smckusick * Associate a function with a signal handler. 23150407Smckusick */ 23250394Smckusick void 23350394Smckusick #ifdef __STDC__ 23450394Smckusick handle(sig_t handler, ...) 23550394Smckusick #else 23650394Smckusick handle(va_alist) 23750394Smckusick va_dcl 23850394Smckusick #endif 23950394Smckusick { 24050394Smckusick int sig; 24150394Smckusick struct sigaction sa; 24250394Smckusick int mask_everything; 24350394Smckusick va_list ap; 24450394Smckusick #ifndef __STDC__ 24550394Smckusick sig_t handler; 24650394Smckusick 24750394Smckusick va_start(ap); 24850394Smckusick handler = va_arg(ap, sig_t); 24950394Smckusick #else 25050394Smckusick va_start(ap, handler); 25150394Smckusick #endif 25250394Smckusick 25350394Smckusick sa.sa_handler = handler; 25450394Smckusick sigfillset(&mask_everything); 25550394Smckusick 25650394Smckusick while (sig = va_arg(ap, int)) { 25750394Smckusick sa.sa_mask = mask_everything; 25850394Smckusick /* XXX SA_RESTART? */ 25950394Smckusick sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 26050394Smckusick sigaction(sig, &sa, (struct sigaction *) 0); 26147659Skarels } 262*61084Smckusick va_end(ap); 2631029Sbill } 2641029Sbill 26550407Smckusick /* 26650407Smckusick * Delete a set of signals from a mask. 26750407Smckusick */ 26850394Smckusick void 26950394Smckusick #ifdef __STDC__ 27050394Smckusick delset(sigset_t *maskp, ...) 27150394Smckusick #else 27250394Smckusick delset(va_alist) 27350394Smckusick va_dcl 27450394Smckusick #endif 27550394Smckusick { 27650394Smckusick int sig; 27750394Smckusick va_list ap; 27850394Smckusick #ifndef __STDC__ 27950394Smckusick sigset_t *maskp; 2801429Sbill 28150394Smckusick va_start(ap); 28250394Smckusick maskp = va_arg(ap, sigset_t *); 28350394Smckusick #else 28450394Smckusick va_start(ap, maskp); 28550394Smckusick #endif 28650394Smckusick 28750394Smckusick while (sig = va_arg(ap, int)) 28850394Smckusick sigdelset(maskp, sig); 289*61084Smckusick va_end(ap); 29050394Smckusick } 29150394Smckusick 29250394Smckusick /* 29350394Smckusick * Log a message and sleep for a while (to give someone an opportunity 29450394Smckusick * to read it and to save log or hardcopy output if the problem is chronic). 29550430Smckusick * NB: should send a message to the session logger to avoid blocking. 29650394Smckusick */ 29750394Smckusick void 29850394Smckusick #ifdef __STDC__ 29950394Smckusick stall(char *message, ...) 30050394Smckusick #else 30150394Smckusick stall(va_alist) 30250394Smckusick va_dcl 30350394Smckusick #endif 3041029Sbill { 30550394Smckusick va_list ap; 30650394Smckusick #ifndef __STDC__ 30750394Smckusick char *message; 3081029Sbill 30950394Smckusick va_start(ap); 31050394Smckusick message = va_arg(ap, char *); 31150394Smckusick #else 31250394Smckusick va_start(ap, message); 31350394Smckusick #endif 31450394Smckusick 31550430Smckusick vsyslog(LOG_ALERT, message, ap); 31650394Smckusick va_end(ap); 31750394Smckusick sleep(STALL_TIMEOUT); 3181429Sbill } 3191429Sbill 32050394Smckusick /* 32150394Smckusick * Like stall(), but doesn't sleep. 32250394Smckusick * If cpp had variadic macros, the two functions could be #defines for another. 32350430Smckusick * NB: should send a message to the session logger to avoid blocking. 32450394Smckusick */ 32542407Smarc void 32650394Smckusick #ifdef __STDC__ 32750394Smckusick warning(char *message, ...) 32850394Smckusick #else 32950394Smckusick warning(va_alist) 33050394Smckusick va_dcl 33150394Smckusick #endif 3321429Sbill { 33350394Smckusick va_list ap; 33450394Smckusick #ifndef __STDC__ 33550394Smckusick char *message; 3361429Sbill 33750394Smckusick va_start(ap); 33850394Smckusick message = va_arg(ap, char *); 33950394Smckusick #else 34050394Smckusick va_start(ap, message); 34150394Smckusick #endif 34250394Smckusick 34350430Smckusick vsyslog(LOG_ALERT, message, ap); 34450394Smckusick va_end(ap); 3451429Sbill } 3461429Sbill 34750394Smckusick /* 34850430Smckusick * Log an emergency message. 34950430Smckusick * NB: should send a message to the session logger to avoid blocking. 35050394Smckusick */ 35150394Smckusick void 35250394Smckusick #ifdef __STDC__ 35350394Smckusick emergency(char *message, ...) 35450394Smckusick #else 35550394Smckusick emergency(va_alist) 35650394Smckusick va_dcl 35750394Smckusick #endif 3581429Sbill { 35950394Smckusick va_list ap; 36050394Smckusick #ifndef __STDC__ 36150394Smckusick char *message; 3621429Sbill 36350394Smckusick va_start(ap); 36450394Smckusick message = va_arg(ap, char *); 36550394Smckusick #else 36650394Smckusick va_start(ap, message); 36750394Smckusick #endif 36850394Smckusick 36950394Smckusick vsyslog(LOG_EMERG, message, ap); 37050394Smckusick va_end(ap); 3711029Sbill } 3721029Sbill 37350407Smckusick /* 37458422Smckusick * Catch a SIGSYS signal. 37558422Smckusick * 37658422Smckusick * These may arise if a system does not support sysctl. 37758422Smckusick * We tolerate up to 25 of these, then throw in the towel. 37858422Smckusick */ 37958422Smckusick void 38058422Smckusick badsys(sig) 38158422Smckusick int sig; 38258422Smckusick { 38358422Smckusick static int badcount = 0; 38458422Smckusick 38558422Smckusick if (badcount++ < 25) 38658422Smckusick return; 38758422Smckusick disaster(sig); 38858422Smckusick } 38958422Smckusick 39058422Smckusick /* 39150407Smckusick * Catch an unexpected signal. 39250407Smckusick */ 39350394Smckusick void 39450394Smckusick disaster(sig) 39550394Smckusick int sig; 3961029Sbill { 39750394Smckusick emergency("fatal signal: %s", 39850394Smckusick sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); 3991029Sbill 40050394Smckusick sleep(STALL_TIMEOUT); 40150394Smckusick _exit(sig); /* reboot */ 4021029Sbill } 4031029Sbill 40450394Smckusick /* 40558422Smckusick * Get the security level of the kernel. 40658422Smckusick */ 40758422Smckusick int 40858422Smckusick getsecuritylevel() 40958422Smckusick { 410*61084Smckusick #ifdef KERN_SECURELVL 41158600Smckusick int name[2], curlevel; 41258600Smckusick size_t len; 41358422Smckusick extern int errno; 41458422Smckusick 41558422Smckusick name[0] = CTL_KERN; 41658422Smckusick name[1] = KERN_SECURELVL; 41758422Smckusick len = sizeof curlevel; 41858422Smckusick if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 41958422Smckusick emergency("cannot get kernel security level: %s", 42058422Smckusick strerror(errno)); 42158422Smckusick return (-1); 42258422Smckusick } 42358422Smckusick return (curlevel); 424*61084Smckusick #else 425*61084Smckusick return (-1); 426*61084Smckusick #endif 42758422Smckusick } 42858422Smckusick 42958422Smckusick /* 43058422Smckusick * Set the security level of the kernel. 43158422Smckusick */ 43261021Smckusick void 43358422Smckusick setsecuritylevel(newlevel) 43458422Smckusick int newlevel; 43558422Smckusick { 436*61084Smckusick #ifdef KERN_SECURELVL 43761021Smckusick int name[2], curlevel; 43858422Smckusick extern int errno; 43958422Smckusick 44058422Smckusick curlevel = getsecuritylevel(); 44158422Smckusick if (newlevel == curlevel) 44258422Smckusick return; 44358422Smckusick name[0] = CTL_KERN; 44458422Smckusick name[1] = KERN_SECURELVL; 44558422Smckusick if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 44658422Smckusick emergency( 44758422Smckusick "cannot change kernel security level from %d to %d: %s", 44858422Smckusick curlevel, newlevel, strerror(errno)); 44958422Smckusick return; 45058422Smckusick } 45158422Smckusick #ifdef SECURE 45258422Smckusick warning("kernel security level changed from %d to %d", 45358422Smckusick curlevel, newlevel); 45458422Smckusick #endif 455*61084Smckusick #endif 45658422Smckusick } 45758422Smckusick 45858422Smckusick /* 45950394Smckusick * Change states in the finite state machine. 46050394Smckusick * The initial state is passed as an argument. 46150394Smckusick */ 46250394Smckusick void 46350394Smckusick transition(s) 46450394Smckusick state_t s; 4651029Sbill { 46650394Smckusick for (;;) 46750394Smckusick s = (state_t) (*s)(); 46850394Smckusick } 4691029Sbill 47050394Smckusick /* 47150407Smckusick * Close out the accounting files for a login session. 47250430Smckusick * NB: should send a message to the session logger to avoid blocking. 47350407Smckusick */ 47450394Smckusick void 47550394Smckusick clear_session_logs(sp) 47650394Smckusick session_t *sp; 47750394Smckusick { 47853151Storek char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 47953151Storek 48053151Storek if (logout(line)) 48153151Storek logwtmp(line, "", ""); 48250394Smckusick } 48350394Smckusick 48413021Ssam /* 48550394Smckusick * Start a session and allocate a controlling terminal. 48650394Smckusick * Only called by children of init after forking. 48713021Ssam */ 48850394Smckusick void 48950394Smckusick setctty(name) 49050394Smckusick char *name; 4911029Sbill { 49250394Smckusick int fd; 4931029Sbill 49450407Smckusick (void) revoke(name); 49560300Smckusick sleep (2); /* leave DTR low */ 49650394Smckusick if ((fd = open(name, O_RDWR)) == -1) { 49750394Smckusick stall("can't open %s: %m", name); 49850394Smckusick _exit(1); 4991029Sbill } 50050394Smckusick if (login_tty(fd) == -1) { 50150394Smckusick stall("can't get %s for controlling terminal: %m", name); 50250394Smckusick _exit(1); 50350394Smckusick } 5041029Sbill } 5051029Sbill 50650407Smckusick /* 50750407Smckusick * Bring the system up single user. 50850407Smckusick */ 50950394Smckusick state_func_t 51050394Smckusick single_user() 51113021Ssam { 51250394Smckusick pid_t pid, wpid; 51350394Smckusick int status; 51450394Smckusick sigset_t mask; 51550394Smckusick char *shell = _PATH_BSHELL; 51650394Smckusick char *argv[2]; 51750394Smckusick #ifdef SECURE 51850394Smckusick struct ttyent *typ; 51950394Smckusick struct passwd *pp; 52050394Smckusick static const char banner[] = 52150394Smckusick "Enter root password, or ^D to go multi-user\n"; 52260300Smckusick char *clear, *password; 52350394Smckusick #endif 52413021Ssam 52558422Smckusick /* 52658422Smckusick * If the kernel is in secure mode, downgrade it to insecure mode. 52758422Smckusick */ 52858422Smckusick if (getsecuritylevel() > 0) 52958422Smckusick setsecuritylevel(0); 53058422Smckusick 53150394Smckusick if ((pid = fork()) == 0) { 53250394Smckusick /* 53350394Smckusick * Start the single user session. 53450394Smckusick */ 53550394Smckusick setctty(_PATH_CONSOLE); 53650394Smckusick 53750394Smckusick #ifdef SECURE 53850394Smckusick /* 53950394Smckusick * Check the root password. 54050394Smckusick * We don't care if the console is 'on' by default; 54150394Smckusick * it's the only tty that can be 'off' and 'secure'. 54250394Smckusick */ 54350394Smckusick typ = getttynam("console"); 54450394Smckusick pp = getpwnam("root"); 54550394Smckusick if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { 54650394Smckusick write(2, banner, sizeof banner - 1); 54750394Smckusick for (;;) { 54860300Smckusick clear = getpass("Password:"); 54960300Smckusick if (clear == 0 || *clear == '\0') 55050394Smckusick _exit(0); 55160300Smckusick password = crypt(clear, pp->pw_passwd); 55260300Smckusick bzero(clear, _PASSWORD_LEN); 55350394Smckusick if (strcmp(password, pp->pw_passwd) == 0) 55450394Smckusick break; 55558422Smckusick warning("single-user login failed\n"); 55613021Ssam } 55713021Ssam } 55850394Smckusick endttyent(); 55950394Smckusick endpwent(); 56058422Smckusick #endif /* SECURE */ 56118542Sralph 56258422Smckusick #ifdef DEBUGSHELL 56358422Smckusick { 56458422Smckusick char altshell[128], *cp = altshell; 56558422Smckusick int num; 56658422Smckusick 56758422Smckusick #define SHREQUEST \ 56858422Smckusick "Enter pathname of shell or RETURN for sh: " 56958422Smckusick (void)write(STDERR_FILENO, 57058422Smckusick SHREQUEST, sizeof(SHREQUEST) - 1); 57158422Smckusick while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 57258422Smckusick num != 0 && *cp != '\n' && cp < &altshell[127]) 57358422Smckusick cp++; 57458422Smckusick *cp = '\0'; 57558422Smckusick if (altshell[0] != '\0') 57658422Smckusick shell = altshell; 57758422Smckusick } 57858422Smckusick #endif /* DEBUGSHELL */ 57958422Smckusick 58023147Sbloom /* 58150394Smckusick * Unblock signals. 58250394Smckusick * We catch all the interesting ones, 58350394Smckusick * and those are reset to SIG_DFL on exec. 58423147Sbloom */ 58550394Smckusick sigemptyset(&mask); 58650394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 58750394Smckusick 58823147Sbloom /* 58950394Smckusick * Fire off a shell. 59050394Smckusick * If the default one doesn't work, try the Bourne shell. 59123147Sbloom */ 59250394Smckusick argv[0] = "-sh"; 59350394Smckusick argv[1] = 0; 59450394Smckusick execv(shell, argv); 59550394Smckusick emergency("can't exec %s for single user: %m", shell); 59650394Smckusick execv(_PATH_BSHELL, argv); 59750394Smckusick emergency("can't exec %s for single user: %m", _PATH_BSHELL); 59850394Smckusick sleep(STALL_TIMEOUT); 59950394Smckusick _exit(1); 60050394Smckusick } 60123147Sbloom 60250394Smckusick if (pid == -1) { 60350394Smckusick /* 60450394Smckusick * We are seriously hosed. Do our best. 60550394Smckusick */ 60650394Smckusick emergency("can't fork single-user shell, trying again"); 60750394Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 60852219Storek continue; 60950394Smckusick return (state_func_t) single_user; 61050394Smckusick } 61150394Smckusick 61250539Strent requested_transition = 0; 61350407Smckusick do { 61450407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 61550407Smckusick collect_child(wpid); 61650407Smckusick if (wpid == -1) { 61750407Smckusick if (errno == EINTR) 61850407Smckusick continue; 61950394Smckusick warning("wait for single-user shell failed: %m; restarting"); 62050394Smckusick return (state_func_t) single_user; 62123147Sbloom } 62250394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 62350394Smckusick warning("init: shell stopped, restarting\n"); 62450394Smckusick kill(pid, SIGCONT); 62550407Smckusick wpid = -1; 62650394Smckusick } 62750539Strent } while (wpid != pid && !requested_transition); 62850394Smckusick 62950539Strent if (requested_transition) 63050539Strent return (state_func_t) requested_transition; 63150539Strent 63250394Smckusick if (!WIFEXITED(status)) { 63350538Strent if (WTERMSIG(status) == SIGKILL) { 63450538Strent /* 63550538Strent * reboot(8) killed shell? 63650538Strent */ 63750538Strent warning("single user shell terminated."); 63850538Strent sleep(STALL_TIMEOUT); 63950538Strent _exit(0); 64050538Strent } else { 64150538Strent warning("single user shell terminated, restarting"); 64250538Strent return (state_func_t) single_user; 64350538Strent } 64450394Smckusick } 64550394Smckusick 64650394Smckusick runcom_mode = FASTBOOT; 64750394Smckusick return (state_func_t) runcom; 64850394Smckusick } 64950394Smckusick 65050407Smckusick /* 65150407Smckusick * Run the system startup script. 65250407Smckusick */ 65350394Smckusick state_func_t 65450394Smckusick runcom() 65550394Smckusick { 65650394Smckusick pid_t pid, wpid; 65750394Smckusick int status; 65850394Smckusick char *argv[4]; 65950407Smckusick struct sigaction sa; 66050394Smckusick 66150394Smckusick if ((pid = fork()) == 0) { 66250407Smckusick sigemptyset(&sa.sa_mask); 66350407Smckusick sa.sa_flags = 0; 66450407Smckusick sa.sa_handler = SIG_IGN; 66550407Smckusick (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); 66650407Smckusick (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); 66750407Smckusick 66850394Smckusick setctty(_PATH_CONSOLE); 66950394Smckusick 67050394Smckusick argv[0] = "sh"; 67150394Smckusick argv[1] = _PATH_RUNCOM; 67250394Smckusick argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; 67350394Smckusick argv[3] = 0; 67450394Smckusick 67550407Smckusick sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 67650394Smckusick 67750394Smckusick execv(_PATH_BSHELL, argv); 67850394Smckusick stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 67950394Smckusick _exit(1); /* force single user mode */ 68050394Smckusick } 68150394Smckusick 68250394Smckusick if (pid == -1) { 68350394Smckusick emergency("can't fork for %s on %s: %m", 68450394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 68550407Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 68652219Storek continue; 68750394Smckusick sleep(STALL_TIMEOUT); 68850394Smckusick return (state_func_t) single_user; 68950394Smckusick } 69050394Smckusick 69150394Smckusick /* 69250407Smckusick * Copied from single_user(). This is a bit paranoid. 69350394Smckusick */ 69450407Smckusick do { 69550407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 69650407Smckusick collect_child(wpid); 69750407Smckusick if (wpid == -1) { 69850407Smckusick if (errno == EINTR) 69950407Smckusick continue; 70050394Smckusick warning("wait for %s on %s failed: %m; going to single user mode", 70150394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 70250394Smckusick return (state_func_t) single_user; 70313021Ssam } 70450394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 70550394Smckusick warning("init: %s on %s stopped, restarting\n", 70650394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 70750394Smckusick kill(pid, SIGCONT); 70850407Smckusick wpid = -1; 70950394Smckusick } 71050407Smckusick } while (wpid != pid); 71150394Smckusick 71260300Smckusick if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 71360300Smckusick requested_transition == catatonia) { 71460300Smckusick /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 71560300Smckusick sigset_t s; 71660300Smckusick 71760300Smckusick sigfillset(&s); 71860300Smckusick for (;;) 71960300Smckusick sigsuspend(&s); 72060300Smckusick } 72160300Smckusick 72250394Smckusick if (!WIFEXITED(status)) { 72350394Smckusick warning("%s on %s terminated abnormally, going to single user mode", 72450394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 72550394Smckusick return (state_func_t) single_user; 72650394Smckusick } 72750394Smckusick 72850394Smckusick if (WEXITSTATUS(status)) 72950394Smckusick return (state_func_t) single_user; 73050394Smckusick 73150394Smckusick runcom_mode = AUTOBOOT; /* the default */ 73250430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 73350430Smckusick logwtmp("~", "reboot", ""); 73450394Smckusick return (state_func_t) read_ttys; 73513021Ssam } 73613021Ssam 73750394Smckusick /* 73850407Smckusick * Open the session database. 73950407Smckusick * 74050407Smckusick * NB: We could pass in the size here; is it necessary? 74150394Smckusick */ 74250407Smckusick int 74350394Smckusick start_session_db() 7441029Sbill { 74550407Smckusick if (session_db && (*session_db->close)(session_db)) 74650407Smckusick emergency("session database close: %s", strerror(errno)); 74751621Sbostic if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 74850407Smckusick emergency("session database open: %s", strerror(errno)); 74950407Smckusick return (1); 75050407Smckusick } 75150407Smckusick return (0); 75250407Smckusick 75350394Smckusick } 7541029Sbill 75550407Smckusick /* 75650407Smckusick * Add a new login session. 75750407Smckusick */ 75850394Smckusick void 75950394Smckusick add_session(sp) 76050394Smckusick session_t *sp; 76150394Smckusick { 76250394Smckusick DBT key; 76350394Smckusick DBT data; 76450394Smckusick 76550394Smckusick key.data = &sp->se_process; 76650394Smckusick key.size = sizeof sp->se_process; 76750394Smckusick data.data = &sp; 76850394Smckusick data.size = sizeof sp; 76950394Smckusick 77051621Sbostic if ((*session_db->put)(session_db, &key, &data, 0)) 77150407Smckusick emergency("insert %d: %s", sp->se_process, strerror(errno)); 77250394Smckusick } 77350394Smckusick 77450407Smckusick /* 77550407Smckusick * Delete an old login session. 77650407Smckusick */ 77750394Smckusick void 77850394Smckusick del_session(sp) 77950394Smckusick session_t *sp; 78050394Smckusick { 78150394Smckusick DBT key; 78250394Smckusick 78350394Smckusick key.data = &sp->se_process; 78450394Smckusick key.size = sizeof sp->se_process; 78550394Smckusick 78650407Smckusick if ((*session_db->del)(session_db, &key, 0)) 78750407Smckusick emergency("delete %d: %s", sp->se_process, strerror(errno)); 78850394Smckusick } 78950394Smckusick 79050407Smckusick /* 79150407Smckusick * Look up a login session by pid. 79250407Smckusick */ 79350394Smckusick session_t * 79450394Smckusick #ifdef __STDC__ 79550394Smckusick find_session(pid_t pid) 79650394Smckusick #else 79750394Smckusick find_session(pid) 79850394Smckusick pid_t pid; 79950394Smckusick #endif 80050394Smckusick { 80150394Smckusick DBT key; 80250394Smckusick DBT data; 80352219Storek session_t *ret; 80450394Smckusick 80550394Smckusick key.data = &pid; 80650394Smckusick key.size = sizeof pid; 80750394Smckusick if ((*session_db->get)(session_db, &key, &data, 0) != 0) 80850394Smckusick return 0; 80952219Storek bcopy(data.data, (char *)&ret, sizeof(ret)); 81052219Storek return ret; 81150394Smckusick } 81250394Smckusick 81350407Smckusick /* 81450407Smckusick * Construct an argument vector from a command line. 81550407Smckusick */ 81650394Smckusick char ** 81750394Smckusick construct_argv(command) 81850394Smckusick char *command; 81950394Smckusick { 82050394Smckusick register int argc = 0; 82150394Smckusick register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 82250394Smckusick * sizeof (char *)); 82350394Smckusick static const char separators[] = " \t"; 82450394Smckusick 82550394Smckusick if ((argv[argc++] = strtok(command, separators)) == 0) 82650394Smckusick return 0; 82750394Smckusick while (argv[argc++] = strtok((char *) 0, separators)) 82852219Storek continue; 82950394Smckusick return argv; 83050394Smckusick } 83150394Smckusick 83250407Smckusick /* 83350407Smckusick * Deallocate a session descriptor. 83450407Smckusick */ 83550394Smckusick void 83650394Smckusick free_session(sp) 83750394Smckusick register session_t *sp; 83850394Smckusick { 83950394Smckusick free(sp->se_device); 84061021Smckusick if (sp->se_getty) { 84161021Smckusick free(sp->se_getty); 84261021Smckusick free(sp->se_getty_argv); 84361021Smckusick } 84450394Smckusick if (sp->se_window) { 84550394Smckusick free(sp->se_window); 84650394Smckusick free(sp->se_window_argv); 8471029Sbill } 84850394Smckusick free(sp); 8491029Sbill } 8501029Sbill 85150407Smckusick /* 85250407Smckusick * Allocate a new session descriptor. 85350407Smckusick */ 85450394Smckusick session_t * 85550394Smckusick new_session(sprev, session_index, typ) 85650394Smckusick session_t *sprev; 85750394Smckusick int session_index; 85850394Smckusick register struct ttyent *typ; 8591029Sbill { 86050394Smckusick register session_t *sp; 8611029Sbill 86250394Smckusick if ((typ->ty_status & TTY_ON) == 0 || 86350394Smckusick typ->ty_name == 0 || 86450394Smckusick typ->ty_getty == 0) 86550394Smckusick return 0; 86650394Smckusick 86750394Smckusick sp = (session_t *) malloc(sizeof (session_t)); 86861021Smckusick bzero(sp, sizeof *sp); 86950394Smckusick 87050394Smckusick sp->se_index = session_index; 87150394Smckusick 87253151Storek sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 87353151Storek (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 87450394Smckusick 87561021Smckusick if (setupargv(sp, typ) == 0) { 87650394Smckusick free_session(sp); 87761021Smckusick return (0); 8785971Sroot } 87950394Smckusick 88050394Smckusick sp->se_next = 0; 88150394Smckusick if (sprev == 0) { 88250394Smckusick sessions = sp; 88350394Smckusick sp->se_prev = 0; 88450394Smckusick } else { 88550394Smckusick sprev->se_next = sp; 88650394Smckusick sp->se_prev = sprev; 88750394Smckusick } 88850394Smckusick 88950394Smckusick return sp; 8901029Sbill } 8911029Sbill 89250407Smckusick /* 89361021Smckusick * Calculate getty and if useful window argv vectors. 89461021Smckusick */ 89561021Smckusick int 89661021Smckusick setupargv(sp, typ) 89761021Smckusick session_t *sp; 89861021Smckusick struct ttyent *typ; 89961021Smckusick { 90061021Smckusick 90161021Smckusick if (sp->se_getty) { 90261021Smckusick free(sp->se_getty); 90361021Smckusick free(sp->se_getty_argv); 90461021Smckusick } 90561021Smckusick sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); 90661021Smckusick (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); 90761021Smckusick sp->se_getty_argv = construct_argv(sp->se_getty); 90861021Smckusick if (sp->se_getty_argv == 0) { 90961021Smckusick warning("can't parse getty for port %s", sp->se_device); 91061021Smckusick free(sp->se_getty); 91161021Smckusick sp->se_getty = 0; 91261021Smckusick return (0); 91361021Smckusick } 91461021Smckusick if (typ->ty_window) { 91561021Smckusick if (sp->se_window) 91661021Smckusick free(sp->se_window); 91761021Smckusick sp->se_window = strdup(typ->ty_window); 91861021Smckusick sp->se_window_argv = construct_argv(sp->se_window); 91961021Smckusick if (sp->se_window_argv == 0) { 92061021Smckusick warning("can't parse window for port %s", 92161021Smckusick sp->se_device); 92261021Smckusick free(sp->se_window); 92361021Smckusick sp->se_window = 0; 92461021Smckusick return (0); 92561021Smckusick } 92661021Smckusick } 92761021Smckusick return (1); 92861021Smckusick } 92961021Smckusick 93061021Smckusick /* 93150407Smckusick * Walk the list of ttys and create sessions for each active line. 93250407Smckusick */ 93350394Smckusick state_func_t 93450394Smckusick read_ttys() 9351029Sbill { 93650394Smckusick int session_index = 0; 93750394Smckusick register session_t *sp, *snext; 93850394Smckusick register struct ttyent *typ; 93950394Smckusick 94050394Smckusick /* 94150394Smckusick * Destroy any previous session state. 94250394Smckusick * There shouldn't be any, but just in case... 94350394Smckusick */ 94450394Smckusick for (sp = sessions; sp; sp = snext) { 94550394Smckusick if (sp->se_process) 94650394Smckusick clear_session_logs(sp); 94750394Smckusick snext = sp->se_next; 94850394Smckusick free_session(sp); 9491029Sbill } 95050394Smckusick sessions = 0; 95150407Smckusick if (start_session_db()) 95250407Smckusick return (state_func_t) single_user; 95350394Smckusick 95450394Smckusick /* 95550394Smckusick * Allocate a session entry for each active port. 95650394Smckusick * Note that sp starts at 0. 95750394Smckusick */ 95850394Smckusick while (typ = getttyent()) 95950394Smckusick if (snext = new_session(sp, ++session_index, typ)) 96050394Smckusick sp = snext; 96150394Smckusick 96250394Smckusick endttyent(); 96350394Smckusick 96450394Smckusick return (state_func_t) multi_user; 9651029Sbill } 9661029Sbill 96750407Smckusick /* 96850407Smckusick * Start a window system running. 96950407Smckusick */ 97042407Smarc void 97150394Smckusick start_window_system(sp) 97250394Smckusick session_t *sp; 9731029Sbill { 97450394Smckusick pid_t pid; 97550394Smckusick sigset_t mask; 97650394Smckusick 97750394Smckusick if ((pid = fork()) == -1) { 97850394Smckusick emergency("can't fork for window system on port %s: %m", 97950394Smckusick sp->se_device); 98050394Smckusick /* hope that getty fails and we can try again */ 98150394Smckusick return; 98250394Smckusick } 98350394Smckusick 98450394Smckusick if (pid) 98550394Smckusick return; 98650394Smckusick 98750394Smckusick sigemptyset(&mask); 98850394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 98950394Smckusick 99050407Smckusick if (setsid() < 0) 99150407Smckusick emergency("setsid failed (window) %m"); 99250407Smckusick 99350394Smckusick execv(sp->se_window_argv[0], sp->se_window_argv); 99450394Smckusick stall("can't exec window system '%s' for port %s: %m", 99550394Smckusick sp->se_window_argv[0], sp->se_device); 99650394Smckusick _exit(1); 9971029Sbill } 9982821Swnj 99950407Smckusick /* 100050407Smckusick * Start a login session running. 100150407Smckusick */ 100250394Smckusick pid_t 100350394Smckusick start_getty(sp) 100450394Smckusick session_t *sp; 100550394Smckusick { 100650394Smckusick pid_t pid; 100750394Smckusick sigset_t mask; 100850394Smckusick time_t current_time = time((time_t *) 0); 100913021Ssam 101050394Smckusick /* 101150394Smckusick * fork(), not vfork() -- we can't afford to block. 101250394Smckusick */ 101350394Smckusick if ((pid = fork()) == -1) { 101450394Smckusick emergency("can't fork for getty on port %s: %m", sp->se_device); 101550394Smckusick return -1; 101650394Smckusick } 101750394Smckusick 101850394Smckusick if (pid) 101950394Smckusick return pid; 102050394Smckusick 102150394Smckusick if (current_time > sp->se_started && 102250394Smckusick current_time - sp->se_started < GETTY_SPACING) { 102350394Smckusick warning("getty repeating too quickly on port %s, sleeping", 102450394Smckusick sp->se_device); 1025*61084Smckusick sleep((unsigned) GETTY_SLEEP); 102650394Smckusick } 102750394Smckusick 102850394Smckusick if (sp->se_window) { 102950394Smckusick start_window_system(sp); 103050394Smckusick sleep(WINDOW_WAIT); 103150394Smckusick } 103250394Smckusick 103350394Smckusick sigemptyset(&mask); 103450394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 103550394Smckusick 103650394Smckusick execv(sp->se_getty_argv[0], sp->se_getty_argv); 103750394Smckusick stall("can't exec getty '%s' for port %s: %m", 103850394Smckusick sp->se_getty_argv[0], sp->se_device); 103950394Smckusick _exit(1); 104013021Ssam } 104113021Ssam 104250407Smckusick /* 104350407Smckusick * Collect exit status for a child. 104450407Smckusick * If an exiting login, start a new login running. 104550407Smckusick */ 104642407Smarc void 1047*61084Smckusick #ifdef __STDC__ 1048*61084Smckusick collect_child(pid_t pid) 1049*61084Smckusick #else 105050407Smckusick collect_child(pid) 105150407Smckusick pid_t pid; 1052*61084Smckusick #endif 10532821Swnj { 105450394Smckusick register session_t *sp, *sprev, *snext; 10552821Swnj 105650407Smckusick if (! sessions) 105750407Smckusick return; 105850394Smckusick 105950407Smckusick if (! (sp = find_session(pid))) 106050407Smckusick return; 106150394Smckusick 106250407Smckusick clear_session_logs(sp); 106350407Smckusick del_session(sp); 106450407Smckusick sp->se_process = 0; 106550394Smckusick 106650407Smckusick if (sp->se_flags & SE_SHUTDOWN) { 106750407Smckusick if (sprev = sp->se_prev) 106850407Smckusick sprev->se_next = sp->se_next; 106950407Smckusick else 107050407Smckusick sessions = sp->se_next; 107150407Smckusick if (snext = sp->se_next) 107250407Smckusick snext->se_prev = sp->se_prev; 107350407Smckusick free_session(sp); 107450407Smckusick return; 107550407Smckusick } 107650394Smckusick 107750407Smckusick if ((pid = start_getty(sp)) == -1) { 107850407Smckusick /* serious trouble */ 107950407Smckusick requested_transition = clean_ttys; 108050407Smckusick return; 10812821Swnj } 108250394Smckusick 108350407Smckusick sp->se_process = pid; 108450407Smckusick sp->se_started = time((time_t *) 0); 108550407Smckusick add_session(sp); 10862821Swnj } 108718542Sralph 108850407Smckusick /* 108950407Smckusick * Catch a signal and request a state transition. 109050407Smckusick */ 109150394Smckusick void 109250394Smckusick transition_handler(sig) 109350394Smckusick int sig; 109418542Sralph { 109550539Strent 109650394Smckusick switch (sig) { 109750394Smckusick case SIGHUP: 109850394Smckusick requested_transition = clean_ttys; 109950394Smckusick break; 110050394Smckusick case SIGTERM: 110150394Smckusick requested_transition = death; 110250394Smckusick break; 110350394Smckusick case SIGTSTP: 110450394Smckusick requested_transition = catatonia; 110550394Smckusick break; 110650394Smckusick default: 110750394Smckusick requested_transition = 0; 110850394Smckusick break; 110918542Sralph } 111018542Sralph } 111118542Sralph 111250407Smckusick /* 111350407Smckusick * Take the system multiuser. 111450407Smckusick */ 111550394Smckusick state_func_t 111650394Smckusick multi_user() 111718542Sralph { 111850394Smckusick pid_t pid; 111950394Smckusick register session_t *sp; 112018542Sralph 112150394Smckusick requested_transition = 0; 112222181Skarels 112358422Smckusick /* 112458422Smckusick * If the administrator has not set the security level to -1 112558422Smckusick * to indicate that the kernel should not run multiuser in secure 112658422Smckusick * mode, and the run script has not set a higher level of security 112758422Smckusick * than level 1, then put the kernel into secure mode. 112858422Smckusick */ 112958422Smckusick if (getsecuritylevel() == 0) 113058422Smckusick setsecuritylevel(1); 113158422Smckusick 113250407Smckusick for (sp = sessions; sp; sp = sp->se_next) { 113350407Smckusick if (sp->se_process) 113450407Smckusick continue; 113550407Smckusick if ((pid = start_getty(sp)) == -1) { 113650407Smckusick /* serious trouble */ 113750407Smckusick requested_transition = clean_ttys; 113850407Smckusick break; 113922181Skarels } 114050407Smckusick sp->se_process = pid; 114150407Smckusick sp->se_started = time((time_t *) 0); 114250407Smckusick add_session(sp); 114350407Smckusick } 114450394Smckusick 114550394Smckusick while (!requested_transition) 114650407Smckusick if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 114750407Smckusick collect_child(pid); 114850394Smckusick 114950394Smckusick return (state_func_t) requested_transition; 115018542Sralph } 115118542Sralph 115250394Smckusick /* 115350394Smckusick * This is an n-squared algorithm. We hope it isn't run often... 115450394Smckusick */ 115550394Smckusick state_func_t 115650394Smckusick clean_ttys() 115718542Sralph { 115850394Smckusick register session_t *sp, *sprev; 115950394Smckusick register struct ttyent *typ; 116050394Smckusick register int session_index = 0; 116152954Smckusick register int devlen; 116218542Sralph 116350394Smckusick if (! sessions) 116450394Smckusick return (state_func_t) multi_user; 116550394Smckusick 116653151Storek devlen = sizeof(_PATH_DEV) - 1; 116750394Smckusick while (typ = getttyent()) { 116850394Smckusick ++session_index; 116950394Smckusick 117060300Smckusick for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 117153151Storek if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 117218542Sralph break; 117350394Smckusick 117450394Smckusick if (sp) { 117550394Smckusick if (sp->se_index != session_index) { 117650394Smckusick warning("port %s changed utmp index from %d to %d", 117750394Smckusick sp->se_device, sp->se_index, 117850394Smckusick session_index); 117950394Smckusick sp->se_index = session_index; 118018542Sralph } 118160958Smckusick if ((typ->ty_status & TTY_ON) == 0 || 118260958Smckusick typ->ty_getty == 0) { 118350394Smckusick sp->se_flags |= SE_SHUTDOWN; 118450394Smckusick kill(sp->se_process, SIGHUP); 118560958Smckusick continue; 118650394Smckusick } 118760958Smckusick sp->se_flags &= ~SE_SHUTDOWN; 118861021Smckusick if (setupargv(sp, typ) == 0) { 118960958Smckusick warning("can't parse getty for port %s", 119060958Smckusick sp->se_device); 119160958Smckusick sp->se_flags |= SE_SHUTDOWN; 119260958Smckusick kill(sp->se_process, SIGHUP); 119360958Smckusick } 119450394Smckusick continue; 119518542Sralph } 119650394Smckusick 119750394Smckusick new_session(sprev, session_index, typ); 119818542Sralph } 119950394Smckusick 120050394Smckusick endttyent(); 120150394Smckusick 120250394Smckusick return (state_func_t) multi_user; 120318542Sralph } 120450394Smckusick 120550407Smckusick /* 120650407Smckusick * Block further logins. 120750407Smckusick */ 120850394Smckusick state_func_t 120950394Smckusick catatonia() 121050394Smckusick { 121150394Smckusick register session_t *sp; 121250394Smckusick 121350394Smckusick for (sp = sessions; sp; sp = sp->se_next) 121450394Smckusick sp->se_flags |= SE_SHUTDOWN; 121550394Smckusick 121650394Smckusick return (state_func_t) multi_user; 121750394Smckusick } 121850394Smckusick 121950407Smckusick /* 122050407Smckusick * Note SIGALRM. 122150407Smckusick */ 122250394Smckusick void 122350394Smckusick alrm_handler(sig) 122450394Smckusick int sig; 122550394Smckusick { 122650394Smckusick clang = 1; 122750394Smckusick } 122850394Smckusick 122950407Smckusick /* 123050407Smckusick * Bring the system down to single user. 123150407Smckusick */ 123250394Smckusick state_func_t 123350394Smckusick death() 123450394Smckusick { 123550394Smckusick register session_t *sp; 123650394Smckusick register int i; 123750407Smckusick pid_t pid; 123850394Smckusick static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 123950394Smckusick 124050394Smckusick for (sp = sessions; sp; sp = sp->se_next) 124150394Smckusick sp->se_flags |= SE_SHUTDOWN; 124250394Smckusick 124350430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 124450430Smckusick logwtmp("~", "shutdown", ""); 124550394Smckusick 124650394Smckusick for (i = 0; i < 3; ++i) { 124750394Smckusick if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 124850394Smckusick return (state_func_t) single_user; 124950394Smckusick 125050394Smckusick clang = 0; 125150394Smckusick alarm(DEATH_WATCH); 125250394Smckusick do 125350407Smckusick if ((pid = waitpid(-1, (int *)0, 0)) != -1) 125450407Smckusick collect_child(pid); 125550407Smckusick while (clang == 0 && errno != ECHILD); 125650394Smckusick 125750407Smckusick if (errno == ECHILD) 125850394Smckusick return (state_func_t) single_user; 125950394Smckusick } 126050394Smckusick 126161021Smckusick warning("some processes would not die; ps axl advised"); 126250394Smckusick 126350394Smckusick return (state_func_t) single_user; 126450394Smckusick } 1265