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 650394Smckusick * Donn Seeley at UUNET Technologies, 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*58422Smckusick static char sccsid[] = "@(#)init.c 6.12 (Berkeley) 03/03/93"; 1950394Smckusick #endif /* not lint */ 2050394Smckusick 21*58422Smckusick #include <sys/sysctl.h> 221029Sbill #include <sys/types.h> 2350394Smckusick #include <sys/wait.h> 2450394Smckusick #include <db.h> 252821Swnj #include <errno.h> 2650394Smckusick #include <fcntl.h> 2750394Smckusick #include <signal.h> 2850394Smckusick #include <syslog.h> 2950394Smckusick #include <time.h> 3016452Sroot #include <ttyent.h> 3150394Smckusick #include <unistd.h> 3252795Sbostic #include <stdio.h> 3352795Sbostic #include <stdlib.h> 3452795Sbostic #include <string.h> 3550394Smckusick 3650394Smckusick #ifdef __STDC__ 3750394Smckusick #include <stdarg.h> 3850394Smckusick #else 3950394Smckusick #include <varargs.h> 4050394Smckusick #endif 4150394Smckusick 4250394Smckusick #ifdef SECURE 4350394Smckusick #include <pwd.h> 4450394Smckusick #endif 4550394Smckusick 4637284Sbostic #include "pathnames.h" 471029Sbill 4850394Smckusick /* 4950394Smckusick * Until the mythical util.h arrives... 5050394Smckusick */ 5150394Smckusick extern int login_tty __P((int)); 5250394Smckusick extern int logout __P((const char *)); 5350394Smckusick extern void logwtmp __P((const char *, const char *, const char *)); 541029Sbill 5550394Smckusick /* 5650394Smckusick * Sleep times; used to prevent thrashing. 5750394Smckusick */ 5850407Smckusick #define GETTY_SPACING 10 /* fork getty on a port every N secs */ 5950394Smckusick #define WINDOW_WAIT 3 /* wait N secs after starting window */ 6050394Smckusick #define STALL_TIMEOUT 30 /* wait N secs after warning */ 6150407Smckusick #define DEATH_WATCH 10 /* wait N secs for procs to die */ 621029Sbill 6350394Smckusick void handle __P((sig_t, ...)); 6450394Smckusick void delset __P((sigset_t *, ...)); 651029Sbill 6650394Smckusick void stall __P((char *, ...)); 6750394Smckusick void warning __P((char *, ...)); 6850394Smckusick void emergency __P((char *, ...)); 6950394Smckusick void disaster __P((int)); 70*58422Smckusick void badsys __P((int)); 711029Sbill 7250394Smckusick /* 7350394Smckusick * We really need a recursive typedef... 7450394Smckusick * The following at least guarantees that the return type of (*state_t)() 7550394Smckusick * is sufficiently wide to hold a function pointer. 7650394Smckusick */ 7750394Smckusick typedef long (*state_func_t) __P((void)); 7850394Smckusick typedef state_func_t (*state_t) __P((void)); 791029Sbill 8050394Smckusick state_func_t single_user __P((void)); 8150394Smckusick state_func_t runcom __P((void)); 8250394Smckusick state_func_t read_ttys __P((void)); 8350394Smckusick state_func_t multi_user __P((void)); 8450394Smckusick state_func_t clean_ttys __P((void)); 8550394Smckusick state_func_t catatonia __P((void)); 8650394Smckusick state_func_t death __P((void)); 8713021Ssam 8850394Smckusick enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 8950394Smckusick 9050394Smckusick void transition __P((state_t)); 9150394Smckusick state_t requested_transition = runcom; 9250394Smckusick 9350394Smckusick void setctty __P((char *)); 9450394Smckusick 9550394Smckusick typedef struct session { 9650394Smckusick int se_index; /* index of entry in ttys file */ 9750394Smckusick pid_t se_process; /* controlling process */ 9850394Smckusick time_t se_started; /* used to avoid thrashing */ 9950394Smckusick int se_flags; /* status of session */ 10050394Smckusick #define SE_SHUTDOWN 0x1 /* session won't be restarted */ 10150394Smckusick char *se_device; /* filename of port */ 10250394Smckusick char *se_getty; /* what to run on that port */ 10350394Smckusick char **se_getty_argv; /* pre-parsed argument array */ 10450394Smckusick char *se_window; /* window system (started only once) */ 10550394Smckusick char **se_window_argv; /* pre-parsed argument array */ 10650394Smckusick struct session *se_prev; 10750394Smckusick struct session *se_next; 10850394Smckusick } session_t; 10950394Smckusick 11050394Smckusick void free_session __P((session_t *)); 11150394Smckusick session_t *new_session __P((session_t *, int, struct ttyent *)); 11250394Smckusick session_t *sessions; 11350394Smckusick 11450394Smckusick char **construct_argv __P((char *)); 11550394Smckusick void start_window_system __P((session_t *)); 11650407Smckusick void collect_child __P((int)); 11750394Smckusick pid_t start_getty __P((session_t *)); 11850394Smckusick void transition_handler __P((int)); 11950394Smckusick void alrm_handler __P((int)); 12050394Smckusick int clang; 12150394Smckusick 12250394Smckusick int start_logger __P((void)); 12350394Smckusick void clear_session_logs __P((session_t *)); 12450394Smckusick int logger_enable; 12550394Smckusick 12650407Smckusick int start_session_db __P((void)); 12750394Smckusick void add_session __P((session_t *)); 12850394Smckusick void del_session __P((session_t *)); 12950394Smckusick session_t *find_session __P((pid_t)); 13050394Smckusick DB *session_db; 13150394Smckusick 13250407Smckusick /* 13350407Smckusick * The mother of all processes. 13450407Smckusick */ 13550394Smckusick int 13643633Skarels main(argc, argv) 13750394Smckusick int argc; 13843633Skarels char **argv; 1391029Sbill { 14050394Smckusick int c; 14150407Smckusick struct sigaction sa; 14250394Smckusick sigset_t mask; 14347659Skarels 1449869Spugs 14552795Sbostic /* Dispose of random users. */ 14652795Sbostic if (getuid() != 0) { 14752795Sbostic (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); 14852795Sbostic exit (1); 14952795Sbostic } 15052795Sbostic 15152795Sbostic /* System V users like to reexec init. */ 15252795Sbostic if (getpid() != 1) { 15352795Sbostic (void)fprintf(stderr, "init: already running\n"); 15452795Sbostic exit (1); 15552795Sbostic } 15652795Sbostic 15750394Smckusick /* 15850394Smckusick * Note that this does NOT open a file... 15950394Smckusick * Does 'init' deserve its own facility number? 16050394Smckusick */ 16150407Smckusick openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 16250394Smckusick 16350394Smckusick /* 16450407Smckusick * Create an initial session. 16550407Smckusick */ 16650407Smckusick if (setsid() < 0) 16750407Smckusick syslog(LOG_ERR, "setsid failed (initial) %m"); 16850407Smckusick 16950407Smckusick /* 17050394Smckusick * This code assumes that we always get arguments through flags, 17150394Smckusick * never through bits set in some random machine register. 17250394Smckusick */ 17350394Smckusick while ((c = getopt(argc, argv, "sf")) != -1) 17450394Smckusick switch (c) { 17550394Smckusick case 's': 17650394Smckusick requested_transition = single_user; 17750394Smckusick break; 17847659Skarels case 'f': 17950394Smckusick runcom_mode = FASTBOOT; 1809869Spugs break; 18150394Smckusick default: 18250394Smckusick warning("unrecognized flag '-%c'", c); 1839869Spugs break; 1849869Spugs } 18550394Smckusick 18650394Smckusick if (optind != argc) 18750394Smckusick warning("ignoring excess arguments"); 18850394Smckusick 18950394Smckusick /* 19050394Smckusick * We catch or block signals rather than ignore them, 19150394Smckusick * so that they get reset on exec. 19250394Smckusick */ 193*58422Smckusick handle(badsys, SIGSYS, 0); 19450394Smckusick handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 195*58422Smckusick SIGBUS, SIGXCPU, SIGXFSZ, 0); 19650394Smckusick handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); 19750394Smckusick handle(alrm_handler, SIGALRM, 0); 19850394Smckusick sigfillset(&mask); 19950407Smckusick delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 20050407Smckusick SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); 20150394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 20250407Smckusick sigemptyset(&sa.sa_mask); 20350407Smckusick sa.sa_flags = 0; 20450407Smckusick sa.sa_handler = SIG_IGN; 20550407Smckusick (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); 20650407Smckusick (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); 20750394Smckusick 20850394Smckusick /* 20950394Smckusick * Paranoia. 21050394Smckusick */ 21150394Smckusick close(0); 21250394Smckusick close(1); 21350394Smckusick close(2); 21450394Smckusick 21550394Smckusick /* 21650394Smckusick * Start the state machine. 21750394Smckusick */ 21850394Smckusick transition(requested_transition); 21950394Smckusick 22050394Smckusick /* 22150394Smckusick * Should never reach here. 22250394Smckusick */ 22350394Smckusick return 1; 22450394Smckusick } 22550394Smckusick 22650407Smckusick /* 22750407Smckusick * Associate a function with a signal handler. 22850407Smckusick */ 22950394Smckusick void 23050394Smckusick #ifdef __STDC__ 23150394Smckusick handle(sig_t handler, ...) 23250394Smckusick #else 23350394Smckusick handle(va_alist) 23450394Smckusick va_dcl 23550394Smckusick #endif 23650394Smckusick { 23750394Smckusick int sig; 23850394Smckusick struct sigaction sa; 23950394Smckusick int mask_everything; 24050394Smckusick va_list ap; 24150394Smckusick #ifndef __STDC__ 24250394Smckusick sig_t handler; 24350394Smckusick 24450394Smckusick va_start(ap); 24550394Smckusick handler = va_arg(ap, sig_t); 24650394Smckusick #else 24750394Smckusick va_start(ap, handler); 24850394Smckusick #endif 24950394Smckusick 25050394Smckusick sa.sa_handler = handler; 25150394Smckusick sigfillset(&mask_everything); 25250394Smckusick 25350394Smckusick while (sig = va_arg(ap, int)) { 25450394Smckusick sa.sa_mask = mask_everything; 25550394Smckusick /* XXX SA_RESTART? */ 25650394Smckusick sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 25750394Smckusick sigaction(sig, &sa, (struct sigaction *) 0); 25847659Skarels } 2591029Sbill } 2601029Sbill 26150407Smckusick /* 26250407Smckusick * Delete a set of signals from a mask. 26350407Smckusick */ 26450394Smckusick void 26550394Smckusick #ifdef __STDC__ 26650394Smckusick delset(sigset_t *maskp, ...) 26750394Smckusick #else 26850394Smckusick delset(va_alist) 26950394Smckusick va_dcl 27050394Smckusick #endif 27150394Smckusick { 27250394Smckusick int sig; 27350394Smckusick va_list ap; 27450394Smckusick #ifndef __STDC__ 27550394Smckusick sigset_t *maskp; 2761429Sbill 27750394Smckusick va_start(ap); 27850394Smckusick maskp = va_arg(ap, sigset_t *); 27950394Smckusick #else 28050394Smckusick va_start(ap, maskp); 28150394Smckusick #endif 28250394Smckusick 28350394Smckusick while (sig = va_arg(ap, int)) 28450394Smckusick sigdelset(maskp, sig); 28550394Smckusick } 28650394Smckusick 28750394Smckusick /* 28850394Smckusick * Log a message and sleep for a while (to give someone an opportunity 28950394Smckusick * to read it and to save log or hardcopy output if the problem is chronic). 29050430Smckusick * NB: should send a message to the session logger to avoid blocking. 29150394Smckusick */ 29250394Smckusick void 29350394Smckusick #ifdef __STDC__ 29450394Smckusick stall(char *message, ...) 29550394Smckusick #else 29650394Smckusick stall(va_alist) 29750394Smckusick va_dcl 29850394Smckusick #endif 2991029Sbill { 30050394Smckusick pid_t pid; 30150394Smckusick va_list ap; 30250394Smckusick #ifndef __STDC__ 30350394Smckusick char *message; 3041029Sbill 30550394Smckusick va_start(ap); 30650394Smckusick message = va_arg(ap, char *); 30750394Smckusick #else 30850394Smckusick va_start(ap, message); 30950394Smckusick #endif 31050394Smckusick 31150430Smckusick vsyslog(LOG_ALERT, message, ap); 31250394Smckusick va_end(ap); 31350394Smckusick sleep(STALL_TIMEOUT); 3141429Sbill } 3151429Sbill 31650394Smckusick /* 31750394Smckusick * Like stall(), but doesn't sleep. 31850394Smckusick * If cpp had variadic macros, the two functions could be #defines for another. 31950430Smckusick * NB: should send a message to the session logger to avoid blocking. 32050394Smckusick */ 32142407Smarc void 32250394Smckusick #ifdef __STDC__ 32350394Smckusick warning(char *message, ...) 32450394Smckusick #else 32550394Smckusick warning(va_alist) 32650394Smckusick va_dcl 32750394Smckusick #endif 3281429Sbill { 32950394Smckusick va_list ap; 33050394Smckusick #ifndef __STDC__ 33150394Smckusick char *message; 3321429Sbill 33350394Smckusick va_start(ap); 33450394Smckusick message = va_arg(ap, char *); 33550394Smckusick #else 33650394Smckusick va_start(ap, message); 33750394Smckusick #endif 33850394Smckusick 33950430Smckusick vsyslog(LOG_ALERT, message, ap); 34050394Smckusick va_end(ap); 3411429Sbill } 3421429Sbill 34350394Smckusick /* 34450430Smckusick * Log an emergency message. 34550430Smckusick * NB: should send a message to the session logger to avoid blocking. 34650394Smckusick */ 34750394Smckusick void 34850394Smckusick #ifdef __STDC__ 34950394Smckusick emergency(char *message, ...) 35050394Smckusick #else 35150394Smckusick emergency(va_alist) 35250394Smckusick va_dcl 35350394Smckusick #endif 3541429Sbill { 35550394Smckusick va_list ap; 35650394Smckusick #ifndef __STDC__ 35750394Smckusick char *message; 3581429Sbill 35950394Smckusick va_start(ap); 36050394Smckusick message = va_arg(ap, char *); 36150394Smckusick #else 36250394Smckusick va_start(ap, message); 36350394Smckusick #endif 36450394Smckusick 36550394Smckusick vsyslog(LOG_EMERG, message, ap); 36650394Smckusick va_end(ap); 3671029Sbill } 3681029Sbill 36950407Smckusick /* 370*58422Smckusick * Catch a SIGSYS signal. 371*58422Smckusick * 372*58422Smckusick * These may arise if a system does not support sysctl. 373*58422Smckusick * We tolerate up to 25 of these, then throw in the towel. 374*58422Smckusick */ 375*58422Smckusick void 376*58422Smckusick badsys(sig) 377*58422Smckusick int sig; 378*58422Smckusick { 379*58422Smckusick static int badcount = 0; 380*58422Smckusick 381*58422Smckusick if (badcount++ < 25) 382*58422Smckusick return; 383*58422Smckusick disaster(sig); 384*58422Smckusick } 385*58422Smckusick 386*58422Smckusick /* 38750407Smckusick * Catch an unexpected signal. 38850407Smckusick */ 38950394Smckusick void 39050394Smckusick disaster(sig) 39150394Smckusick int sig; 3921029Sbill { 39350394Smckusick emergency("fatal signal: %s", 39450394Smckusick sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); 3951029Sbill 39650394Smckusick sleep(STALL_TIMEOUT); 39750394Smckusick _exit(sig); /* reboot */ 3981029Sbill } 3991029Sbill 40050394Smckusick /* 401*58422Smckusick * Get the security level of the kernel. 402*58422Smckusick */ 403*58422Smckusick int 404*58422Smckusick getsecuritylevel() 405*58422Smckusick { 406*58422Smckusick int name[2], len, curlevel; 407*58422Smckusick extern int errno; 408*58422Smckusick 409*58422Smckusick name[0] = CTL_KERN; 410*58422Smckusick name[1] = KERN_SECURELVL; 411*58422Smckusick len = sizeof curlevel; 412*58422Smckusick if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 413*58422Smckusick emergency("cannot get kernel security level: %s", 414*58422Smckusick strerror(errno)); 415*58422Smckusick return (-1); 416*58422Smckusick } 417*58422Smckusick return (curlevel); 418*58422Smckusick } 419*58422Smckusick 420*58422Smckusick /* 421*58422Smckusick * Set the security level of the kernel. 422*58422Smckusick */ 423*58422Smckusick setsecuritylevel(newlevel) 424*58422Smckusick int newlevel; 425*58422Smckusick { 426*58422Smckusick int name[2], len, curlevel; 427*58422Smckusick extern int errno; 428*58422Smckusick 429*58422Smckusick curlevel = getsecuritylevel(); 430*58422Smckusick if (newlevel == curlevel) 431*58422Smckusick return; 432*58422Smckusick name[0] = CTL_KERN; 433*58422Smckusick name[1] = KERN_SECURELVL; 434*58422Smckusick if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 435*58422Smckusick emergency( 436*58422Smckusick "cannot change kernel security level from %d to %d: %s", 437*58422Smckusick curlevel, newlevel, strerror(errno)); 438*58422Smckusick return; 439*58422Smckusick } 440*58422Smckusick #ifdef SECURE 441*58422Smckusick warning("kernel security level changed from %d to %d", 442*58422Smckusick curlevel, newlevel); 443*58422Smckusick #endif 444*58422Smckusick } 445*58422Smckusick 446*58422Smckusick /* 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 /* 45950394Smckusick * We send requests for session logging to another process for two reasons. 46050394Smckusick * First, we don't want to block if the log files go away (e.g. because 46150394Smckusick * one or more are on hard-mounted NFS systems whose server crashes). 46250394Smckusick * Second, despite all the crud already contained in init, it still isn't 46350394Smckusick * right that init should care about session logging record formats and files. 46450394Smckusick * We could use explicit 'Unix' IPC for this, but let's try to be POSIX... 46550394Smckusick */ 46650394Smckusick int 46750394Smckusick start_logger() 46850394Smckusick { 46950394Smckusick static char *argv[] = { _PATH_SLOGGER, 0 }; 47050394Smckusick int fd, pfd[2]; 47150394Smckusick pid_t pid; 47250394Smckusick sigset_t mask; 47350394Smckusick 47450394Smckusick if (pipe(pfd) == -1) { 47550394Smckusick warning("session logging disabled: can't make pipe to %s: %m", 47650394Smckusick argv[0]); 47750394Smckusick return -1; 47850394Smckusick } 47950394Smckusick if ((pid = fork()) == -1) { 48050394Smckusick emergency("session logging disabled: can't fork for %s: %m", 48150394Smckusick argv[0]); 48250394Smckusick return -1; 48350394Smckusick } 48450394Smckusick 48513021Ssam if (pid == 0) { 48650394Smckusick close(pfd[1]); 48750394Smckusick if (pfd[0] != 0) { 48850394Smckusick dup2(pfd[0], 0); 48950394Smckusick close(pfd[0]); 49050394Smckusick } 49150394Smckusick if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != -1) { 49250394Smckusick if (fd != 1) 49350394Smckusick dup2(fd, 1); 49450394Smckusick if (fd != 2) 49550394Smckusick dup2(fd, 2); 49650394Smckusick if (fd != 1 && fd != 2) 49744284Skarels close(fd); 49850394Smckusick } else { 49950394Smckusick /* paranoid */ 50050394Smckusick close(1); 50150394Smckusick close(2); 50244284Skarels } 50350394Smckusick sigemptyset(&mask); 50450394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 50550394Smckusick execv(argv[0], argv); 50650394Smckusick stall("can't exec %s: %m", argv[0]); 50750394Smckusick _exit(1); 5081029Sbill } 50950394Smckusick 51050394Smckusick close(pfd[0]); 51150394Smckusick fcntl(pfd[1], F_SETFD, FD_CLOEXEC); 51250394Smckusick fcntl(pfd[1], F_SETFL, O_NONBLOCK); 51350394Smckusick 51450394Smckusick return pfd[1]; 5151029Sbill } 5161029Sbill 51750407Smckusick /* 51850407Smckusick * Close out the accounting files for a login session. 51950430Smckusick * NB: should send a message to the session logger to avoid blocking. 52050407Smckusick */ 52150394Smckusick void 52250394Smckusick clear_session_logs(sp) 52350394Smckusick session_t *sp; 52450394Smckusick { 52553151Storek char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 52653151Storek 52753151Storek if (logout(line)) 52853151Storek logwtmp(line, "", ""); 52950394Smckusick } 53050394Smckusick 53113021Ssam /* 53250394Smckusick * Start a session and allocate a controlling terminal. 53350394Smckusick * Only called by children of init after forking. 53413021Ssam */ 53550394Smckusick void 53650394Smckusick setctty(name) 53750394Smckusick char *name; 5381029Sbill { 53950394Smckusick int fd; 5401029Sbill 54150407Smckusick (void) revoke(name); 54250394Smckusick if ((fd = open(name, O_RDWR)) == -1) { 54350394Smckusick stall("can't open %s: %m", name); 54450394Smckusick _exit(1); 5451029Sbill } 54650394Smckusick if (login_tty(fd) == -1) { 54750394Smckusick stall("can't get %s for controlling terminal: %m", name); 54850394Smckusick _exit(1); 54950394Smckusick } 5501029Sbill } 5511029Sbill 55250407Smckusick /* 55350407Smckusick * Bring the system up single user. 55450407Smckusick */ 55550394Smckusick state_func_t 55650394Smckusick single_user() 55713021Ssam { 55850394Smckusick pid_t pid, wpid; 55950394Smckusick int status; 56050394Smckusick sigset_t mask; 56150394Smckusick char *shell = _PATH_BSHELL; 56250394Smckusick char *argv[2]; 56350394Smckusick #ifdef SECURE 56450394Smckusick struct ttyent *typ; 56550394Smckusick struct passwd *pp; 56650394Smckusick static const char banner[] = 56750394Smckusick "Enter root password, or ^D to go multi-user\n"; 56850394Smckusick char *password; 56950394Smckusick #endif 57013021Ssam 571*58422Smckusick /* 572*58422Smckusick * If the kernel is in secure mode, downgrade it to insecure mode. 573*58422Smckusick */ 574*58422Smckusick if (getsecuritylevel() > 0) 575*58422Smckusick setsecuritylevel(0); 576*58422Smckusick 57750394Smckusick if ((pid = fork()) == 0) { 57850394Smckusick /* 57950394Smckusick * Start the single user session. 58050394Smckusick */ 58150394Smckusick setctty(_PATH_CONSOLE); 58250394Smckusick 58350394Smckusick #ifdef SECURE 58450394Smckusick /* 58550394Smckusick * Check the root password. 58650394Smckusick * We don't care if the console is 'on' by default; 58750394Smckusick * it's the only tty that can be 'off' and 'secure'. 58850394Smckusick */ 58950394Smckusick typ = getttynam("console"); 59050394Smckusick pp = getpwnam("root"); 59150394Smckusick if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { 59250394Smckusick write(2, banner, sizeof banner - 1); 59350394Smckusick for (;;) { 59450394Smckusick password = getpass("Password:"); 59550394Smckusick if (password == 0 || *password == '\0') 59650394Smckusick _exit(0); 59752954Smckusick password = crypt(password, pp->pw_passwd); 59850394Smckusick if (strcmp(password, pp->pw_passwd) == 0) 59950394Smckusick break; 600*58422Smckusick warning("single-user login failed\n"); 60113021Ssam } 60213021Ssam } 60350394Smckusick endttyent(); 60450394Smckusick endpwent(); 605*58422Smckusick #endif /* SECURE */ 60618542Sralph 607*58422Smckusick #ifdef DEBUGSHELL 608*58422Smckusick { 609*58422Smckusick char altshell[128], *cp = altshell; 610*58422Smckusick int num; 611*58422Smckusick 612*58422Smckusick #define SHREQUEST \ 613*58422Smckusick "Enter pathname of shell or RETURN for sh: " 614*58422Smckusick (void)write(STDERR_FILENO, 615*58422Smckusick SHREQUEST, sizeof(SHREQUEST) - 1); 616*58422Smckusick while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 617*58422Smckusick num != 0 && *cp != '\n' && cp < &altshell[127]) 618*58422Smckusick cp++; 619*58422Smckusick *cp = '\0'; 620*58422Smckusick if (altshell[0] != '\0') 621*58422Smckusick shell = altshell; 622*58422Smckusick } 623*58422Smckusick #endif /* DEBUGSHELL */ 624*58422Smckusick 62523147Sbloom /* 62650394Smckusick * Unblock signals. 62750394Smckusick * We catch all the interesting ones, 62850394Smckusick * and those are reset to SIG_DFL on exec. 62923147Sbloom */ 63050394Smckusick sigemptyset(&mask); 63150394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 63250394Smckusick 63323147Sbloom /* 63450394Smckusick * Fire off a shell. 63550394Smckusick * If the default one doesn't work, try the Bourne shell. 63623147Sbloom */ 63750394Smckusick argv[0] = "-sh"; 63850394Smckusick argv[1] = 0; 63950394Smckusick execv(shell, argv); 64050394Smckusick emergency("can't exec %s for single user: %m", shell); 64150394Smckusick execv(_PATH_BSHELL, argv); 64250394Smckusick emergency("can't exec %s for single user: %m", _PATH_BSHELL); 64350394Smckusick sleep(STALL_TIMEOUT); 64450394Smckusick _exit(1); 64550394Smckusick } 64623147Sbloom 64750394Smckusick if (pid == -1) { 64850394Smckusick /* 64950394Smckusick * We are seriously hosed. Do our best. 65050394Smckusick */ 65150394Smckusick emergency("can't fork single-user shell, trying again"); 65250394Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 65352219Storek continue; 65450394Smckusick return (state_func_t) single_user; 65550394Smckusick } 65650394Smckusick 65750539Strent requested_transition = 0; 65850407Smckusick do { 65950407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 66050407Smckusick collect_child(wpid); 66150407Smckusick if (wpid == -1) { 66250407Smckusick if (errno == EINTR) 66350407Smckusick continue; 66450394Smckusick warning("wait for single-user shell failed: %m; restarting"); 66550394Smckusick return (state_func_t) single_user; 66623147Sbloom } 66750394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 66850394Smckusick warning("init: shell stopped, restarting\n"); 66950394Smckusick kill(pid, SIGCONT); 67050407Smckusick wpid = -1; 67150394Smckusick } 67250539Strent } while (wpid != pid && !requested_transition); 67350394Smckusick 67450539Strent if (requested_transition) 67550539Strent return (state_func_t) requested_transition; 67650539Strent 67750394Smckusick if (!WIFEXITED(status)) { 67850538Strent if (WTERMSIG(status) == SIGKILL) { 67950538Strent /* 68050538Strent * reboot(8) killed shell? 68150538Strent */ 68250538Strent warning("single user shell terminated."); 68350538Strent sleep(STALL_TIMEOUT); 68450538Strent _exit(0); 68550538Strent } else { 68650538Strent warning("single user shell terminated, restarting"); 68750538Strent return (state_func_t) single_user; 68850538Strent } 68950394Smckusick } 69050394Smckusick 69150394Smckusick runcom_mode = FASTBOOT; 69250394Smckusick return (state_func_t) runcom; 69350394Smckusick } 69450394Smckusick 69550407Smckusick /* 69650407Smckusick * Run the system startup script. 69750407Smckusick */ 69850394Smckusick state_func_t 69950394Smckusick runcom() 70050394Smckusick { 70150394Smckusick pid_t pid, wpid; 70250394Smckusick int status; 70350394Smckusick char *argv[4]; 70450407Smckusick struct sigaction sa; 70550394Smckusick 70650394Smckusick if ((pid = fork()) == 0) { 70750407Smckusick sigemptyset(&sa.sa_mask); 70850407Smckusick sa.sa_flags = 0; 70950407Smckusick sa.sa_handler = SIG_IGN; 71050407Smckusick (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); 71150407Smckusick (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); 71250407Smckusick 71350394Smckusick setctty(_PATH_CONSOLE); 71450394Smckusick 71550394Smckusick argv[0] = "sh"; 71650394Smckusick argv[1] = _PATH_RUNCOM; 71750394Smckusick argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; 71850394Smckusick argv[3] = 0; 71950394Smckusick 72050407Smckusick sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 72150394Smckusick 72250394Smckusick execv(_PATH_BSHELL, argv); 72350394Smckusick stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 72450394Smckusick _exit(1); /* force single user mode */ 72550394Smckusick } 72650394Smckusick 72750394Smckusick if (pid == -1) { 72850394Smckusick emergency("can't fork for %s on %s: %m", 72950394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 73050407Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 73152219Storek continue; 73250394Smckusick sleep(STALL_TIMEOUT); 73350394Smckusick return (state_func_t) single_user; 73450394Smckusick } 73550394Smckusick 73650394Smckusick /* 73750407Smckusick * Copied from single_user(). This is a bit paranoid. 73850394Smckusick */ 73950407Smckusick do { 74050407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 74150407Smckusick collect_child(wpid); 74250407Smckusick if (wpid == -1) { 74350407Smckusick if (errno == EINTR) 74450407Smckusick continue; 74550394Smckusick warning("wait for %s on %s failed: %m; going to single user mode", 74650394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 74750394Smckusick return (state_func_t) single_user; 74813021Ssam } 74950394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 75050394Smckusick warning("init: %s on %s stopped, restarting\n", 75150394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 75250394Smckusick kill(pid, SIGCONT); 75350407Smckusick wpid = -1; 75450394Smckusick } 75550407Smckusick } while (wpid != pid); 75650394Smckusick 75750394Smckusick if (!WIFEXITED(status)) { 75850394Smckusick warning("%s on %s terminated abnormally, going to single user mode", 75950394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 76050394Smckusick return (state_func_t) single_user; 76150394Smckusick } 76250394Smckusick 76350394Smckusick if (WEXITSTATUS(status)) 76450394Smckusick return (state_func_t) single_user; 76550394Smckusick 76650394Smckusick runcom_mode = AUTOBOOT; /* the default */ 76750430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 76850430Smckusick logwtmp("~", "reboot", ""); 76950394Smckusick return (state_func_t) read_ttys; 77013021Ssam } 77113021Ssam 77250394Smckusick /* 77350407Smckusick * Open the session database. 77450407Smckusick * 77550407Smckusick * NB: We could pass in the size here; is it necessary? 77650394Smckusick */ 77750407Smckusick int 77850394Smckusick start_session_db() 7791029Sbill { 78050407Smckusick if (session_db && (*session_db->close)(session_db)) 78150407Smckusick emergency("session database close: %s", strerror(errno)); 78251621Sbostic if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 78350407Smckusick emergency("session database open: %s", strerror(errno)); 78450407Smckusick return (1); 78550407Smckusick } 78650407Smckusick return (0); 78750407Smckusick 78850394Smckusick } 7891029Sbill 79050407Smckusick /* 79150407Smckusick * Add a new login session. 79250407Smckusick */ 79350394Smckusick void 79450394Smckusick add_session(sp) 79550394Smckusick session_t *sp; 79650394Smckusick { 79750394Smckusick DBT key; 79850394Smckusick DBT data; 79950394Smckusick 80050394Smckusick key.data = &sp->se_process; 80150394Smckusick key.size = sizeof sp->se_process; 80250394Smckusick data.data = &sp; 80350394Smckusick data.size = sizeof sp; 80450394Smckusick 80551621Sbostic if ((*session_db->put)(session_db, &key, &data, 0)) 80650407Smckusick emergency("insert %d: %s", sp->se_process, strerror(errno)); 80750394Smckusick } 80850394Smckusick 80950407Smckusick /* 81050407Smckusick * Delete an old login session. 81150407Smckusick */ 81250394Smckusick void 81350394Smckusick del_session(sp) 81450394Smckusick session_t *sp; 81550394Smckusick { 81650394Smckusick DBT key; 81750394Smckusick 81850394Smckusick key.data = &sp->se_process; 81950394Smckusick key.size = sizeof sp->se_process; 82050394Smckusick 82150407Smckusick if ((*session_db->del)(session_db, &key, 0)) 82250407Smckusick emergency("delete %d: %s", sp->se_process, strerror(errno)); 82350394Smckusick } 82450394Smckusick 82550407Smckusick /* 82650407Smckusick * Look up a login session by pid. 82750407Smckusick */ 82850394Smckusick session_t * 82950394Smckusick #ifdef __STDC__ 83050394Smckusick find_session(pid_t pid) 83150394Smckusick #else 83250394Smckusick find_session(pid) 83350394Smckusick pid_t pid; 83450394Smckusick #endif 83550394Smckusick { 83650394Smckusick DBT key; 83750394Smckusick DBT data; 83852219Storek session_t *ret; 83950394Smckusick 84050394Smckusick key.data = &pid; 84150394Smckusick key.size = sizeof pid; 84250394Smckusick if ((*session_db->get)(session_db, &key, &data, 0) != 0) 84350394Smckusick return 0; 84452219Storek bcopy(data.data, (char *)&ret, sizeof(ret)); 84552219Storek return ret; 84650394Smckusick } 84750394Smckusick 84850407Smckusick /* 84950407Smckusick * Construct an argument vector from a command line. 85050407Smckusick */ 85150394Smckusick char ** 85250394Smckusick construct_argv(command) 85350394Smckusick char *command; 85450394Smckusick { 85550394Smckusick register int argc = 0; 85650394Smckusick register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 85750394Smckusick * sizeof (char *)); 85850394Smckusick static const char separators[] = " \t"; 85950394Smckusick 86050394Smckusick if ((argv[argc++] = strtok(command, separators)) == 0) 86150394Smckusick return 0; 86250394Smckusick while (argv[argc++] = strtok((char *) 0, separators)) 86352219Storek continue; 86450394Smckusick return argv; 86550394Smckusick } 86650394Smckusick 86750407Smckusick /* 86850407Smckusick * Deallocate a session descriptor. 86950407Smckusick */ 87050394Smckusick void 87150394Smckusick free_session(sp) 87250394Smckusick register session_t *sp; 87350394Smckusick { 87450394Smckusick free(sp->se_device); 87550394Smckusick free(sp->se_getty); 87650394Smckusick free(sp->se_getty_argv); 87750394Smckusick if (sp->se_window) { 87850394Smckusick free(sp->se_window); 87950394Smckusick free(sp->se_window_argv); 8801029Sbill } 88150394Smckusick free(sp); 8821029Sbill } 8831029Sbill 88450407Smckusick /* 88550407Smckusick * Allocate a new session descriptor. 88650407Smckusick */ 88750394Smckusick session_t * 88850394Smckusick new_session(sprev, session_index, typ) 88950394Smckusick session_t *sprev; 89050394Smckusick int session_index; 89150394Smckusick register struct ttyent *typ; 8921029Sbill { 89350394Smckusick register session_t *sp; 8941029Sbill 89550394Smckusick if ((typ->ty_status & TTY_ON) == 0 || 89650394Smckusick typ->ty_name == 0 || 89750394Smckusick typ->ty_getty == 0) 89850394Smckusick return 0; 89950394Smckusick 90050394Smckusick sp = (session_t *) malloc(sizeof (session_t)); 90150394Smckusick 90250394Smckusick sp->se_index = session_index; 90350394Smckusick sp->se_process = 0; 90450394Smckusick sp->se_started = 0; 90550394Smckusick sp->se_flags = 0; 90650394Smckusick sp->se_window = 0; 90750394Smckusick 90853151Storek sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 90953151Storek (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 91050394Smckusick 91150394Smckusick sp->se_getty = strdup(typ->ty_getty); 91250394Smckusick sp->se_getty_argv = construct_argv(sp->se_getty); 91350394Smckusick if (sp->se_getty_argv == 0) { 91450394Smckusick warning("can't parse getty for port %s", 91550394Smckusick sp->se_device); 91650394Smckusick free_session(sp); 91750394Smckusick return 0; 9185971Sroot } 91950394Smckusick if (typ->ty_window) { 92050394Smckusick sp->se_window = strdup(typ->ty_window); 92150394Smckusick sp->se_window_argv = construct_argv(sp->se_window); 92250394Smckusick if (sp->se_window_argv == 0) { 92350394Smckusick warning("can't parse window for port %s", 92450394Smckusick sp->se_device); 92550394Smckusick free_session(sp); 92650394Smckusick return 0; 9275971Sroot } 9281029Sbill } 92950394Smckusick 93050394Smckusick sp->se_next = 0; 93150394Smckusick if (sprev == 0) { 93250394Smckusick sessions = sp; 93350394Smckusick sp->se_prev = 0; 93450394Smckusick } else { 93550394Smckusick sprev->se_next = sp; 93650394Smckusick sp->se_prev = sprev; 93750394Smckusick } 93850394Smckusick 93950394Smckusick return sp; 9401029Sbill } 9411029Sbill 94250407Smckusick /* 94350407Smckusick * Walk the list of ttys and create sessions for each active line. 94450407Smckusick */ 94550394Smckusick state_func_t 94650394Smckusick read_ttys() 9471029Sbill { 94850394Smckusick int session_index = 0; 94950394Smckusick register session_t *sp, *snext; 95050394Smckusick register struct ttyent *typ; 95150394Smckusick 95250394Smckusick /* 95350394Smckusick * Destroy any previous session state. 95450394Smckusick * There shouldn't be any, but just in case... 95550394Smckusick */ 95650394Smckusick for (sp = sessions; sp; sp = snext) { 95750394Smckusick if (sp->se_process) 95850394Smckusick clear_session_logs(sp); 95950394Smckusick snext = sp->se_next; 96050394Smckusick free_session(sp); 9611029Sbill } 96250394Smckusick sessions = 0; 96350407Smckusick if (start_session_db()) 96450407Smckusick return (state_func_t) single_user; 96550394Smckusick 96650394Smckusick /* 96750394Smckusick * Allocate a session entry for each active port. 96850394Smckusick * Note that sp starts at 0. 96950394Smckusick */ 97050394Smckusick while (typ = getttyent()) 97150394Smckusick if (snext = new_session(sp, ++session_index, typ)) 97250394Smckusick sp = snext; 97350394Smckusick 97450394Smckusick endttyent(); 97550394Smckusick 97650394Smckusick logger_enable = 1; 97750394Smckusick return (state_func_t) multi_user; 9781029Sbill } 9791029Sbill 98050407Smckusick /* 98150407Smckusick * Start a window system running. 98250407Smckusick */ 98342407Smarc void 98450394Smckusick start_window_system(sp) 98550394Smckusick session_t *sp; 9861029Sbill { 98750394Smckusick pid_t pid; 98850394Smckusick sigset_t mask; 98950394Smckusick 99050394Smckusick if ((pid = fork()) == -1) { 99150394Smckusick emergency("can't fork for window system on port %s: %m", 99250394Smckusick sp->se_device); 99350394Smckusick /* hope that getty fails and we can try again */ 99450394Smckusick return; 99550394Smckusick } 99650394Smckusick 99750394Smckusick if (pid) 99850394Smckusick return; 99950394Smckusick 100050394Smckusick sigemptyset(&mask); 100150394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 100250394Smckusick 100350407Smckusick if (setsid() < 0) 100450407Smckusick emergency("setsid failed (window) %m"); 100550407Smckusick 100650394Smckusick execv(sp->se_window_argv[0], sp->se_window_argv); 100750394Smckusick stall("can't exec window system '%s' for port %s: %m", 100850394Smckusick sp->se_window_argv[0], sp->se_device); 100950394Smckusick _exit(1); 10101029Sbill } 10112821Swnj 101250407Smckusick /* 101350407Smckusick * Start a login session running. 101450407Smckusick */ 101550394Smckusick pid_t 101650394Smckusick start_getty(sp) 101750394Smckusick session_t *sp; 101850394Smckusick { 101950394Smckusick pid_t pid; 102050394Smckusick sigset_t mask; 102150394Smckusick time_t current_time = time((time_t *) 0); 102213021Ssam 102350394Smckusick /* 102450394Smckusick * fork(), not vfork() -- we can't afford to block. 102550394Smckusick */ 102650394Smckusick if ((pid = fork()) == -1) { 102750394Smckusick emergency("can't fork for getty on port %s: %m", sp->se_device); 102850394Smckusick return -1; 102950394Smckusick } 103050394Smckusick 103150394Smckusick if (pid) 103250394Smckusick return pid; 103350394Smckusick 103450394Smckusick if (current_time > sp->se_started && 103550394Smckusick current_time - sp->se_started < GETTY_SPACING) { 103650394Smckusick warning("getty repeating too quickly on port %s, sleeping", 103750394Smckusick sp->se_device); 103850394Smckusick sleep((unsigned) GETTY_SPACING - 103950394Smckusick (current_time - sp->se_started)); 104050394Smckusick } 104150394Smckusick 104250394Smckusick if (sp->se_window) { 104350394Smckusick start_window_system(sp); 104450394Smckusick sleep(WINDOW_WAIT); 104550394Smckusick } 104650394Smckusick 104750394Smckusick setctty(sp->se_device); 104850394Smckusick 104950394Smckusick sigemptyset(&mask); 105050394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 105150394Smckusick 105250394Smckusick execv(sp->se_getty_argv[0], sp->se_getty_argv); 105350394Smckusick stall("can't exec getty '%s' for port %s: %m", 105450394Smckusick sp->se_getty_argv[0], sp->se_device); 105550394Smckusick _exit(1); 105613021Ssam } 105713021Ssam 105850407Smckusick /* 105950407Smckusick * Collect exit status for a child. 106050407Smckusick * If an exiting login, start a new login running. 106150407Smckusick */ 106242407Smarc void 106350407Smckusick collect_child(pid) 106450407Smckusick pid_t pid; 10652821Swnj { 106650394Smckusick register session_t *sp, *sprev, *snext; 10672821Swnj 106850407Smckusick if (! sessions) 106950407Smckusick return; 107050394Smckusick 107150407Smckusick if (! (sp = find_session(pid))) 107250407Smckusick return; 107350394Smckusick 107450407Smckusick clear_session_logs(sp); 107550407Smckusick del_session(sp); 107650407Smckusick sp->se_process = 0; 107750394Smckusick 107850407Smckusick if (sp->se_flags & SE_SHUTDOWN) { 107950407Smckusick if (sprev = sp->se_prev) 108050407Smckusick sprev->se_next = sp->se_next; 108150407Smckusick else 108250407Smckusick sessions = sp->se_next; 108350407Smckusick if (snext = sp->se_next) 108450407Smckusick snext->se_prev = sp->se_prev; 108550407Smckusick free_session(sp); 108650407Smckusick return; 108750407Smckusick } 108850394Smckusick 108950407Smckusick if ((pid = start_getty(sp)) == -1) { 109050407Smckusick /* serious trouble */ 109150407Smckusick requested_transition = clean_ttys; 109250407Smckusick return; 10932821Swnj } 109450394Smckusick 109550407Smckusick sp->se_process = pid; 109650407Smckusick sp->se_started = time((time_t *) 0); 109750407Smckusick add_session(sp); 10982821Swnj } 109918542Sralph 110050407Smckusick /* 110150407Smckusick * Catch a signal and request a state transition. 110250407Smckusick */ 110350394Smckusick void 110450394Smckusick transition_handler(sig) 110550394Smckusick int sig; 110618542Sralph { 110750539Strent 110850394Smckusick switch (sig) { 110950394Smckusick case SIGHUP: 111050394Smckusick requested_transition = clean_ttys; 111150394Smckusick break; 111250394Smckusick case SIGTERM: 111350394Smckusick requested_transition = death; 111450394Smckusick break; 111550394Smckusick case SIGTSTP: 111650394Smckusick requested_transition = catatonia; 111750394Smckusick break; 111850394Smckusick default: 111950394Smckusick requested_transition = 0; 112050394Smckusick break; 112118542Sralph } 112218542Sralph } 112318542Sralph 112450407Smckusick /* 112550407Smckusick * Take the system multiuser. 112650407Smckusick */ 112750394Smckusick state_func_t 112850394Smckusick multi_user() 112918542Sralph { 113050394Smckusick pid_t pid; 113150394Smckusick register session_t *sp; 113218542Sralph 113350394Smckusick requested_transition = 0; 113450394Smckusick logger_enable = 1; 113522181Skarels 1136*58422Smckusick /* 1137*58422Smckusick * If the administrator has not set the security level to -1 1138*58422Smckusick * to indicate that the kernel should not run multiuser in secure 1139*58422Smckusick * mode, and the run script has not set a higher level of security 1140*58422Smckusick * than level 1, then put the kernel into secure mode. 1141*58422Smckusick */ 1142*58422Smckusick if (getsecuritylevel() == 0) 1143*58422Smckusick setsecuritylevel(1); 1144*58422Smckusick 114550407Smckusick for (sp = sessions; sp; sp = sp->se_next) { 114650407Smckusick if (sp->se_process) 114750407Smckusick continue; 114850407Smckusick if ((pid = start_getty(sp)) == -1) { 114950407Smckusick /* serious trouble */ 115050407Smckusick requested_transition = clean_ttys; 115150407Smckusick break; 115222181Skarels } 115350407Smckusick sp->se_process = pid; 115450407Smckusick sp->se_started = time((time_t *) 0); 115550407Smckusick add_session(sp); 115650407Smckusick } 115750394Smckusick 115850394Smckusick while (!requested_transition) 115950407Smckusick if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 116050407Smckusick collect_child(pid); 116150394Smckusick 116250394Smckusick return (state_func_t) requested_transition; 116318542Sralph } 116418542Sralph 116550394Smckusick /* 116650394Smckusick * This is an n-squared algorithm. We hope it isn't run often... 116750394Smckusick */ 116850394Smckusick state_func_t 116950394Smckusick clean_ttys() 117018542Sralph { 117150394Smckusick register session_t *sp, *sprev; 117250394Smckusick register struct ttyent *typ; 117350394Smckusick register int session_index = 0; 117452954Smckusick register int devlen; 117518542Sralph 117650394Smckusick if (! sessions) 117750394Smckusick return (state_func_t) multi_user; 117850394Smckusick 117953151Storek devlen = sizeof(_PATH_DEV) - 1; 118050394Smckusick while (typ = getttyent()) { 118150394Smckusick ++session_index; 118250394Smckusick 118350394Smckusick for (sp = sessions; sp; sprev = sp, sp = sp->se_next) 118453151Storek if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 118518542Sralph break; 118650394Smckusick 118750394Smckusick if (sp) { 118850394Smckusick if (sp->se_index != session_index) { 118950394Smckusick warning("port %s changed utmp index from %d to %d", 119050394Smckusick sp->se_device, sp->se_index, 119150394Smckusick session_index); 119250394Smckusick sp->se_index = session_index; 119318542Sralph } 119450394Smckusick if (typ->ty_status & TTY_ON) 119550394Smckusick sp->se_flags &= ~SE_SHUTDOWN; 119650394Smckusick else { 119750394Smckusick sp->se_flags |= SE_SHUTDOWN; 119850394Smckusick kill(sp->se_process, SIGHUP); 119950394Smckusick } 120050394Smckusick continue; 120118542Sralph } 120250394Smckusick 120350394Smckusick new_session(sprev, session_index, typ); 120418542Sralph } 120550394Smckusick 120650394Smckusick endttyent(); 120750394Smckusick 120850394Smckusick return (state_func_t) multi_user; 120918542Sralph } 121050394Smckusick 121150407Smckusick /* 121250407Smckusick * Block further logins. 121350407Smckusick */ 121450394Smckusick state_func_t 121550394Smckusick catatonia() 121650394Smckusick { 121750394Smckusick register session_t *sp; 121850394Smckusick 121950394Smckusick for (sp = sessions; sp; sp = sp->se_next) 122050394Smckusick sp->se_flags |= SE_SHUTDOWN; 122150394Smckusick 122250394Smckusick return (state_func_t) multi_user; 122350394Smckusick } 122450394Smckusick 122550407Smckusick /* 122650407Smckusick * Note SIGALRM. 122750407Smckusick */ 122850394Smckusick void 122950394Smckusick alrm_handler(sig) 123050394Smckusick int sig; 123150394Smckusick { 123250394Smckusick clang = 1; 123350394Smckusick } 123450394Smckusick 123550407Smckusick /* 123650407Smckusick * Bring the system down to single user. 123750407Smckusick */ 123850394Smckusick state_func_t 123950394Smckusick death() 124050394Smckusick { 124150394Smckusick register session_t *sp; 124250394Smckusick register int i; 124350407Smckusick pid_t pid; 124450394Smckusick static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 124550394Smckusick 124650394Smckusick for (sp = sessions; sp; sp = sp->se_next) 124750394Smckusick sp->se_flags |= SE_SHUTDOWN; 124850394Smckusick 124950430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 125050430Smckusick logwtmp("~", "shutdown", ""); 125150394Smckusick logger_enable = 0; 125250394Smckusick 125350394Smckusick for (i = 0; i < 3; ++i) { 125450394Smckusick if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 125550394Smckusick return (state_func_t) single_user; 125650394Smckusick 125750394Smckusick clang = 0; 125850394Smckusick alarm(DEATH_WATCH); 125950394Smckusick do 126050407Smckusick if ((pid = waitpid(-1, (int *)0, 0)) != -1) 126150407Smckusick collect_child(pid); 126250407Smckusick while (clang == 0 && errno != ECHILD); 126350394Smckusick 126450407Smckusick if (errno == ECHILD) 126550394Smckusick return (state_func_t) single_user; 126650394Smckusick } 126750394Smckusick 126850394Smckusick warning("some processes wouldn't die"); 126950394Smckusick 127050394Smckusick return (state_func_t) single_user; 127150394Smckusick } 1272