150394Smckusick /*- 2*63828Sbostic * Copyright (c) 1991, 1993 3*63828Sbostic * The Regents of the University of California. 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 12*63828Sbostic static char copyright[] = 13*63828Sbostic "@(#) Copyright (c) 1991, 1993\n\ 14*63828Sbostic The Regents of the University of California. All rights reserved.\n"; 1550394Smckusick #endif /* not lint */ 1612682Ssam 1750394Smckusick #ifndef lint 18*63828Sbostic static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 07/15/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 */ 5961084Smckusick #define GETTY_SPACING 5 /* N secs minimum getty spacing */ 6061084Smckusick #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 /* 17363827Smckusick * Establish an initial user so that programs running 17463827Smckusick * single user do not freak out and die (like passwd). 17563827Smckusick */ 17663827Smckusick if (setlogin("root") < 0) 17763827Smckusick warning("setlogin() failed: %m"); 17863827Smckusick 17963827Smckusick /* 18050394Smckusick * This code assumes that we always get arguments through flags, 18150394Smckusick * never through bits set in some random machine register. 18250394Smckusick */ 18350394Smckusick while ((c = getopt(argc, argv, "sf")) != -1) 18450394Smckusick switch (c) { 18550394Smckusick case 's': 18650394Smckusick requested_transition = single_user; 18750394Smckusick break; 18847659Skarels case 'f': 18950394Smckusick runcom_mode = FASTBOOT; 1909869Spugs break; 19150394Smckusick default: 19250394Smckusick warning("unrecognized flag '-%c'", c); 1939869Spugs break; 1949869Spugs } 19550394Smckusick 19650394Smckusick if (optind != argc) 19750394Smckusick warning("ignoring excess arguments"); 19850394Smckusick 19950394Smckusick /* 20050394Smckusick * We catch or block signals rather than ignore them, 20150394Smckusick * so that they get reset on exec. 20250394Smckusick */ 20358422Smckusick handle(badsys, SIGSYS, 0); 20450394Smckusick handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 20558422Smckusick SIGBUS, SIGXCPU, SIGXFSZ, 0); 20650394Smckusick handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); 20750394Smckusick handle(alrm_handler, SIGALRM, 0); 20850394Smckusick sigfillset(&mask); 20950407Smckusick delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 21050407Smckusick SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); 21150394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 21250407Smckusick sigemptyset(&sa.sa_mask); 21350407Smckusick sa.sa_flags = 0; 21450407Smckusick sa.sa_handler = SIG_IGN; 21550407Smckusick (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); 21650407Smckusick (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); 21750394Smckusick 21850394Smckusick /* 21950394Smckusick * Paranoia. 22050394Smckusick */ 22150394Smckusick close(0); 22250394Smckusick close(1); 22350394Smckusick close(2); 22450394Smckusick 22550394Smckusick /* 22650394Smckusick * Start the state machine. 22750394Smckusick */ 22850394Smckusick transition(requested_transition); 22950394Smckusick 23050394Smckusick /* 23150394Smckusick * Should never reach here. 23250394Smckusick */ 23350394Smckusick return 1; 23450394Smckusick } 23550394Smckusick 23650407Smckusick /* 23750407Smckusick * Associate a function with a signal handler. 23850407Smckusick */ 23950394Smckusick void 24050394Smckusick #ifdef __STDC__ 24150394Smckusick handle(sig_t handler, ...) 24250394Smckusick #else 24350394Smckusick handle(va_alist) 24450394Smckusick va_dcl 24550394Smckusick #endif 24650394Smckusick { 24750394Smckusick int sig; 24850394Smckusick struct sigaction sa; 24950394Smckusick int mask_everything; 25050394Smckusick va_list ap; 25150394Smckusick #ifndef __STDC__ 25250394Smckusick sig_t handler; 25350394Smckusick 25450394Smckusick va_start(ap); 25550394Smckusick handler = va_arg(ap, sig_t); 25650394Smckusick #else 25750394Smckusick va_start(ap, handler); 25850394Smckusick #endif 25950394Smckusick 26050394Smckusick sa.sa_handler = handler; 26150394Smckusick sigfillset(&mask_everything); 26250394Smckusick 26350394Smckusick while (sig = va_arg(ap, int)) { 26450394Smckusick sa.sa_mask = mask_everything; 26550394Smckusick /* XXX SA_RESTART? */ 26650394Smckusick sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 26750394Smckusick sigaction(sig, &sa, (struct sigaction *) 0); 26847659Skarels } 26961084Smckusick va_end(ap); 2701029Sbill } 2711029Sbill 27250407Smckusick /* 27350407Smckusick * Delete a set of signals from a mask. 27450407Smckusick */ 27550394Smckusick void 27650394Smckusick #ifdef __STDC__ 27750394Smckusick delset(sigset_t *maskp, ...) 27850394Smckusick #else 27950394Smckusick delset(va_alist) 28050394Smckusick va_dcl 28150394Smckusick #endif 28250394Smckusick { 28350394Smckusick int sig; 28450394Smckusick va_list ap; 28550394Smckusick #ifndef __STDC__ 28650394Smckusick sigset_t *maskp; 2871429Sbill 28850394Smckusick va_start(ap); 28950394Smckusick maskp = va_arg(ap, sigset_t *); 29050394Smckusick #else 29150394Smckusick va_start(ap, maskp); 29250394Smckusick #endif 29350394Smckusick 29450394Smckusick while (sig = va_arg(ap, int)) 29550394Smckusick sigdelset(maskp, sig); 29661084Smckusick va_end(ap); 29750394Smckusick } 29850394Smckusick 29950394Smckusick /* 30050394Smckusick * Log a message and sleep for a while (to give someone an opportunity 30150394Smckusick * to read it and to save log or hardcopy output if the problem is chronic). 30250430Smckusick * NB: should send a message to the session logger to avoid blocking. 30350394Smckusick */ 30450394Smckusick void 30550394Smckusick #ifdef __STDC__ 30650394Smckusick stall(char *message, ...) 30750394Smckusick #else 30850394Smckusick stall(va_alist) 30950394Smckusick va_dcl 31050394Smckusick #endif 3111029Sbill { 31250394Smckusick va_list ap; 31350394Smckusick #ifndef __STDC__ 31450394Smckusick char *message; 3151029Sbill 31650394Smckusick va_start(ap); 31750394Smckusick message = va_arg(ap, char *); 31850394Smckusick #else 31950394Smckusick va_start(ap, message); 32050394Smckusick #endif 32150394Smckusick 32250430Smckusick vsyslog(LOG_ALERT, message, ap); 32350394Smckusick va_end(ap); 32450394Smckusick sleep(STALL_TIMEOUT); 3251429Sbill } 3261429Sbill 32750394Smckusick /* 32850394Smckusick * Like stall(), but doesn't sleep. 32950394Smckusick * If cpp had variadic macros, the two functions could be #defines for another. 33050430Smckusick * NB: should send a message to the session logger to avoid blocking. 33150394Smckusick */ 33242407Smarc void 33350394Smckusick #ifdef __STDC__ 33450394Smckusick warning(char *message, ...) 33550394Smckusick #else 33650394Smckusick warning(va_alist) 33750394Smckusick va_dcl 33850394Smckusick #endif 3391429Sbill { 34050394Smckusick va_list ap; 34150394Smckusick #ifndef __STDC__ 34250394Smckusick char *message; 3431429Sbill 34450394Smckusick va_start(ap); 34550394Smckusick message = va_arg(ap, char *); 34650394Smckusick #else 34750394Smckusick va_start(ap, message); 34850394Smckusick #endif 34950394Smckusick 35050430Smckusick vsyslog(LOG_ALERT, message, ap); 35150394Smckusick va_end(ap); 3521429Sbill } 3531429Sbill 35450394Smckusick /* 35550430Smckusick * Log an emergency message. 35650430Smckusick * NB: should send a message to the session logger to avoid blocking. 35750394Smckusick */ 35850394Smckusick void 35950394Smckusick #ifdef __STDC__ 36050394Smckusick emergency(char *message, ...) 36150394Smckusick #else 36250394Smckusick emergency(va_alist) 36350394Smckusick va_dcl 36450394Smckusick #endif 3651429Sbill { 36650394Smckusick va_list ap; 36750394Smckusick #ifndef __STDC__ 36850394Smckusick char *message; 3691429Sbill 37050394Smckusick va_start(ap); 37150394Smckusick message = va_arg(ap, char *); 37250394Smckusick #else 37350394Smckusick va_start(ap, message); 37450394Smckusick #endif 37550394Smckusick 37650394Smckusick vsyslog(LOG_EMERG, message, ap); 37750394Smckusick va_end(ap); 3781029Sbill } 3791029Sbill 38050407Smckusick /* 38158422Smckusick * Catch a SIGSYS signal. 38258422Smckusick * 38358422Smckusick * These may arise if a system does not support sysctl. 38458422Smckusick * We tolerate up to 25 of these, then throw in the towel. 38558422Smckusick */ 38658422Smckusick void 38758422Smckusick badsys(sig) 38858422Smckusick int sig; 38958422Smckusick { 39058422Smckusick static int badcount = 0; 39158422Smckusick 39258422Smckusick if (badcount++ < 25) 39358422Smckusick return; 39458422Smckusick disaster(sig); 39558422Smckusick } 39658422Smckusick 39758422Smckusick /* 39850407Smckusick * Catch an unexpected signal. 39950407Smckusick */ 40050394Smckusick void 40150394Smckusick disaster(sig) 40250394Smckusick int sig; 4031029Sbill { 40450394Smckusick emergency("fatal signal: %s", 40550394Smckusick sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); 4061029Sbill 40750394Smckusick sleep(STALL_TIMEOUT); 40850394Smckusick _exit(sig); /* reboot */ 4091029Sbill } 4101029Sbill 41150394Smckusick /* 41258422Smckusick * Get the security level of the kernel. 41358422Smckusick */ 41458422Smckusick int 41558422Smckusick getsecuritylevel() 41658422Smckusick { 41761084Smckusick #ifdef KERN_SECURELVL 41858600Smckusick int name[2], curlevel; 41958600Smckusick size_t len; 42058422Smckusick extern int errno; 42158422Smckusick 42258422Smckusick name[0] = CTL_KERN; 42358422Smckusick name[1] = KERN_SECURELVL; 42458422Smckusick len = sizeof curlevel; 42558422Smckusick if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 42658422Smckusick emergency("cannot get kernel security level: %s", 42758422Smckusick strerror(errno)); 42858422Smckusick return (-1); 42958422Smckusick } 43058422Smckusick return (curlevel); 43161084Smckusick #else 43261084Smckusick return (-1); 43361084Smckusick #endif 43458422Smckusick } 43558422Smckusick 43658422Smckusick /* 43758422Smckusick * Set the security level of the kernel. 43858422Smckusick */ 43961021Smckusick void 44058422Smckusick setsecuritylevel(newlevel) 44158422Smckusick int newlevel; 44258422Smckusick { 44361084Smckusick #ifdef KERN_SECURELVL 44461021Smckusick int name[2], curlevel; 44558422Smckusick extern int errno; 44658422Smckusick 44758422Smckusick curlevel = getsecuritylevel(); 44858422Smckusick if (newlevel == curlevel) 44958422Smckusick return; 45058422Smckusick name[0] = CTL_KERN; 45158422Smckusick name[1] = KERN_SECURELVL; 45258422Smckusick if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 45358422Smckusick emergency( 45458422Smckusick "cannot change kernel security level from %d to %d: %s", 45558422Smckusick curlevel, newlevel, strerror(errno)); 45658422Smckusick return; 45758422Smckusick } 45858422Smckusick #ifdef SECURE 45958422Smckusick warning("kernel security level changed from %d to %d", 46058422Smckusick curlevel, newlevel); 46158422Smckusick #endif 46261084Smckusick #endif 46358422Smckusick } 46458422Smckusick 46558422Smckusick /* 46650394Smckusick * Change states in the finite state machine. 46750394Smckusick * The initial state is passed as an argument. 46850394Smckusick */ 46950394Smckusick void 47050394Smckusick transition(s) 47150394Smckusick state_t s; 4721029Sbill { 47350394Smckusick for (;;) 47450394Smckusick s = (state_t) (*s)(); 47550394Smckusick } 4761029Sbill 47750394Smckusick /* 47850407Smckusick * Close out the accounting files for a login session. 47950430Smckusick * NB: should send a message to the session logger to avoid blocking. 48050407Smckusick */ 48150394Smckusick void 48250394Smckusick clear_session_logs(sp) 48350394Smckusick session_t *sp; 48450394Smckusick { 48553151Storek char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 48653151Storek 48753151Storek if (logout(line)) 48853151Storek logwtmp(line, "", ""); 48950394Smckusick } 49050394Smckusick 49113021Ssam /* 49250394Smckusick * Start a session and allocate a controlling terminal. 49350394Smckusick * Only called by children of init after forking. 49413021Ssam */ 49550394Smckusick void 49650394Smckusick setctty(name) 49750394Smckusick char *name; 4981029Sbill { 49950394Smckusick int fd; 5001029Sbill 50150407Smckusick (void) revoke(name); 50260300Smckusick sleep (2); /* leave DTR low */ 50350394Smckusick if ((fd = open(name, O_RDWR)) == -1) { 50450394Smckusick stall("can't open %s: %m", name); 50550394Smckusick _exit(1); 5061029Sbill } 50750394Smckusick if (login_tty(fd) == -1) { 50850394Smckusick stall("can't get %s for controlling terminal: %m", name); 50950394Smckusick _exit(1); 51050394Smckusick } 5111029Sbill } 5121029Sbill 51350407Smckusick /* 51450407Smckusick * Bring the system up single user. 51550407Smckusick */ 51650394Smckusick state_func_t 51750394Smckusick single_user() 51813021Ssam { 51950394Smckusick pid_t pid, wpid; 52050394Smckusick int status; 52150394Smckusick sigset_t mask; 52250394Smckusick char *shell = _PATH_BSHELL; 52350394Smckusick char *argv[2]; 52450394Smckusick #ifdef SECURE 52550394Smckusick struct ttyent *typ; 52650394Smckusick struct passwd *pp; 52750394Smckusick static const char banner[] = 52850394Smckusick "Enter root password, or ^D to go multi-user\n"; 52960300Smckusick char *clear, *password; 53050394Smckusick #endif 53113021Ssam 53258422Smckusick /* 53358422Smckusick * If the kernel is in secure mode, downgrade it to insecure mode. 53458422Smckusick */ 53558422Smckusick if (getsecuritylevel() > 0) 53658422Smckusick setsecuritylevel(0); 53758422Smckusick 53850394Smckusick if ((pid = fork()) == 0) { 53950394Smckusick /* 54050394Smckusick * Start the single user session. 54150394Smckusick */ 54250394Smckusick setctty(_PATH_CONSOLE); 54350394Smckusick 54450394Smckusick #ifdef SECURE 54550394Smckusick /* 54650394Smckusick * Check the root password. 54750394Smckusick * We don't care if the console is 'on' by default; 54850394Smckusick * it's the only tty that can be 'off' and 'secure'. 54950394Smckusick */ 55050394Smckusick typ = getttynam("console"); 55150394Smckusick pp = getpwnam("root"); 55250394Smckusick if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { 55350394Smckusick write(2, banner, sizeof banner - 1); 55450394Smckusick for (;;) { 55560300Smckusick clear = getpass("Password:"); 55660300Smckusick if (clear == 0 || *clear == '\0') 55750394Smckusick _exit(0); 55860300Smckusick password = crypt(clear, pp->pw_passwd); 55960300Smckusick bzero(clear, _PASSWORD_LEN); 56050394Smckusick if (strcmp(password, pp->pw_passwd) == 0) 56150394Smckusick break; 56258422Smckusick warning("single-user login failed\n"); 56313021Ssam } 56413021Ssam } 56550394Smckusick endttyent(); 56650394Smckusick endpwent(); 56758422Smckusick #endif /* SECURE */ 56818542Sralph 56958422Smckusick #ifdef DEBUGSHELL 57058422Smckusick { 57158422Smckusick char altshell[128], *cp = altshell; 57258422Smckusick int num; 57358422Smckusick 57458422Smckusick #define SHREQUEST \ 57558422Smckusick "Enter pathname of shell or RETURN for sh: " 57658422Smckusick (void)write(STDERR_FILENO, 57758422Smckusick SHREQUEST, sizeof(SHREQUEST) - 1); 57858422Smckusick while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 57958422Smckusick num != 0 && *cp != '\n' && cp < &altshell[127]) 58058422Smckusick cp++; 58158422Smckusick *cp = '\0'; 58258422Smckusick if (altshell[0] != '\0') 58358422Smckusick shell = altshell; 58458422Smckusick } 58558422Smckusick #endif /* DEBUGSHELL */ 58658422Smckusick 58723147Sbloom /* 58850394Smckusick * Unblock signals. 58950394Smckusick * We catch all the interesting ones, 59050394Smckusick * and those are reset to SIG_DFL on exec. 59123147Sbloom */ 59250394Smckusick sigemptyset(&mask); 59350394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 59450394Smckusick 59523147Sbloom /* 59650394Smckusick * Fire off a shell. 59750394Smckusick * If the default one doesn't work, try the Bourne shell. 59823147Sbloom */ 59950394Smckusick argv[0] = "-sh"; 60050394Smckusick argv[1] = 0; 60150394Smckusick execv(shell, argv); 60250394Smckusick emergency("can't exec %s for single user: %m", shell); 60350394Smckusick execv(_PATH_BSHELL, argv); 60450394Smckusick emergency("can't exec %s for single user: %m", _PATH_BSHELL); 60550394Smckusick sleep(STALL_TIMEOUT); 60650394Smckusick _exit(1); 60750394Smckusick } 60823147Sbloom 60950394Smckusick if (pid == -1) { 61050394Smckusick /* 61150394Smckusick * We are seriously hosed. Do our best. 61250394Smckusick */ 61350394Smckusick emergency("can't fork single-user shell, trying again"); 61450394Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 61552219Storek continue; 61650394Smckusick return (state_func_t) single_user; 61750394Smckusick } 61850394Smckusick 61950539Strent requested_transition = 0; 62050407Smckusick do { 62150407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 62250407Smckusick collect_child(wpid); 62350407Smckusick if (wpid == -1) { 62450407Smckusick if (errno == EINTR) 62550407Smckusick continue; 62650394Smckusick warning("wait for single-user shell failed: %m; restarting"); 62750394Smckusick return (state_func_t) single_user; 62823147Sbloom } 62950394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 63050394Smckusick warning("init: shell stopped, restarting\n"); 63150394Smckusick kill(pid, SIGCONT); 63250407Smckusick wpid = -1; 63350394Smckusick } 63450539Strent } while (wpid != pid && !requested_transition); 63550394Smckusick 63650539Strent if (requested_transition) 63750539Strent return (state_func_t) requested_transition; 63850539Strent 63950394Smckusick if (!WIFEXITED(status)) { 64050538Strent if (WTERMSIG(status) == SIGKILL) { 64150538Strent /* 64250538Strent * reboot(8) killed shell? 64350538Strent */ 64450538Strent warning("single user shell terminated."); 64550538Strent sleep(STALL_TIMEOUT); 64650538Strent _exit(0); 64750538Strent } else { 64850538Strent warning("single user shell terminated, restarting"); 64950538Strent return (state_func_t) single_user; 65050538Strent } 65150394Smckusick } 65250394Smckusick 65350394Smckusick runcom_mode = FASTBOOT; 65450394Smckusick return (state_func_t) runcom; 65550394Smckusick } 65650394Smckusick 65750407Smckusick /* 65850407Smckusick * Run the system startup script. 65950407Smckusick */ 66050394Smckusick state_func_t 66150394Smckusick runcom() 66250394Smckusick { 66350394Smckusick pid_t pid, wpid; 66450394Smckusick int status; 66550394Smckusick char *argv[4]; 66650407Smckusick struct sigaction sa; 66750394Smckusick 66850394Smckusick if ((pid = fork()) == 0) { 66950407Smckusick sigemptyset(&sa.sa_mask); 67050407Smckusick sa.sa_flags = 0; 67150407Smckusick sa.sa_handler = SIG_IGN; 67250407Smckusick (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); 67350407Smckusick (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); 67450407Smckusick 67550394Smckusick setctty(_PATH_CONSOLE); 67650394Smckusick 67750394Smckusick argv[0] = "sh"; 67850394Smckusick argv[1] = _PATH_RUNCOM; 67950394Smckusick argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; 68050394Smckusick argv[3] = 0; 68150394Smckusick 68250407Smckusick sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 68350394Smckusick 68450394Smckusick execv(_PATH_BSHELL, argv); 68550394Smckusick stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 68650394Smckusick _exit(1); /* force single user mode */ 68750394Smckusick } 68850394Smckusick 68950394Smckusick if (pid == -1) { 69050394Smckusick emergency("can't fork for %s on %s: %m", 69150394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 69250407Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 69352219Storek continue; 69450394Smckusick sleep(STALL_TIMEOUT); 69550394Smckusick return (state_func_t) single_user; 69650394Smckusick } 69750394Smckusick 69850394Smckusick /* 69950407Smckusick * Copied from single_user(). This is a bit paranoid. 70050394Smckusick */ 70150407Smckusick do { 70250407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 70350407Smckusick collect_child(wpid); 70450407Smckusick if (wpid == -1) { 70550407Smckusick if (errno == EINTR) 70650407Smckusick continue; 70750394Smckusick warning("wait for %s on %s failed: %m; going to single user mode", 70850394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 70950394Smckusick return (state_func_t) single_user; 71013021Ssam } 71150394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 71250394Smckusick warning("init: %s on %s stopped, restarting\n", 71350394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 71450394Smckusick kill(pid, SIGCONT); 71550407Smckusick wpid = -1; 71650394Smckusick } 71750407Smckusick } while (wpid != pid); 71850394Smckusick 71960300Smckusick if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 72060300Smckusick requested_transition == catatonia) { 72160300Smckusick /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 72260300Smckusick sigset_t s; 72360300Smckusick 72460300Smckusick sigfillset(&s); 72560300Smckusick for (;;) 72660300Smckusick sigsuspend(&s); 72760300Smckusick } 72860300Smckusick 72950394Smckusick if (!WIFEXITED(status)) { 73050394Smckusick warning("%s on %s terminated abnormally, going to single user mode", 73150394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 73250394Smckusick return (state_func_t) single_user; 73350394Smckusick } 73450394Smckusick 73550394Smckusick if (WEXITSTATUS(status)) 73650394Smckusick return (state_func_t) single_user; 73750394Smckusick 73850394Smckusick runcom_mode = AUTOBOOT; /* the default */ 73950430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 74050430Smckusick logwtmp("~", "reboot", ""); 74150394Smckusick return (state_func_t) read_ttys; 74213021Ssam } 74313021Ssam 74450394Smckusick /* 74550407Smckusick * Open the session database. 74650407Smckusick * 74750407Smckusick * NB: We could pass in the size here; is it necessary? 74850394Smckusick */ 74950407Smckusick int 75050394Smckusick start_session_db() 7511029Sbill { 75250407Smckusick if (session_db && (*session_db->close)(session_db)) 75350407Smckusick emergency("session database close: %s", strerror(errno)); 75451621Sbostic if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 75550407Smckusick emergency("session database open: %s", strerror(errno)); 75650407Smckusick return (1); 75750407Smckusick } 75850407Smckusick return (0); 75950407Smckusick 76050394Smckusick } 7611029Sbill 76250407Smckusick /* 76350407Smckusick * Add a new login session. 76450407Smckusick */ 76550394Smckusick void 76650394Smckusick add_session(sp) 76750394Smckusick session_t *sp; 76850394Smckusick { 76950394Smckusick DBT key; 77050394Smckusick DBT data; 77150394Smckusick 77250394Smckusick key.data = &sp->se_process; 77350394Smckusick key.size = sizeof sp->se_process; 77450394Smckusick data.data = &sp; 77550394Smckusick data.size = sizeof sp; 77650394Smckusick 77751621Sbostic if ((*session_db->put)(session_db, &key, &data, 0)) 77850407Smckusick emergency("insert %d: %s", sp->se_process, strerror(errno)); 77950394Smckusick } 78050394Smckusick 78150407Smckusick /* 78250407Smckusick * Delete an old login session. 78350407Smckusick */ 78450394Smckusick void 78550394Smckusick del_session(sp) 78650394Smckusick session_t *sp; 78750394Smckusick { 78850394Smckusick DBT key; 78950394Smckusick 79050394Smckusick key.data = &sp->se_process; 79150394Smckusick key.size = sizeof sp->se_process; 79250394Smckusick 79350407Smckusick if ((*session_db->del)(session_db, &key, 0)) 79450407Smckusick emergency("delete %d: %s", sp->se_process, strerror(errno)); 79550394Smckusick } 79650394Smckusick 79750407Smckusick /* 79850407Smckusick * Look up a login session by pid. 79950407Smckusick */ 80050394Smckusick session_t * 80150394Smckusick #ifdef __STDC__ 80250394Smckusick find_session(pid_t pid) 80350394Smckusick #else 80450394Smckusick find_session(pid) 80550394Smckusick pid_t pid; 80650394Smckusick #endif 80750394Smckusick { 80850394Smckusick DBT key; 80950394Smckusick DBT data; 81052219Storek session_t *ret; 81150394Smckusick 81250394Smckusick key.data = &pid; 81350394Smckusick key.size = sizeof pid; 81450394Smckusick if ((*session_db->get)(session_db, &key, &data, 0) != 0) 81550394Smckusick return 0; 81652219Storek bcopy(data.data, (char *)&ret, sizeof(ret)); 81752219Storek return ret; 81850394Smckusick } 81950394Smckusick 82050407Smckusick /* 82150407Smckusick * Construct an argument vector from a command line. 82250407Smckusick */ 82350394Smckusick char ** 82450394Smckusick construct_argv(command) 82550394Smckusick char *command; 82650394Smckusick { 82750394Smckusick register int argc = 0; 82850394Smckusick register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 82950394Smckusick * sizeof (char *)); 83050394Smckusick static const char separators[] = " \t"; 83150394Smckusick 83250394Smckusick if ((argv[argc++] = strtok(command, separators)) == 0) 83350394Smckusick return 0; 83450394Smckusick while (argv[argc++] = strtok((char *) 0, separators)) 83552219Storek continue; 83650394Smckusick return argv; 83750394Smckusick } 83850394Smckusick 83950407Smckusick /* 84050407Smckusick * Deallocate a session descriptor. 84150407Smckusick */ 84250394Smckusick void 84350394Smckusick free_session(sp) 84450394Smckusick register session_t *sp; 84550394Smckusick { 84650394Smckusick free(sp->se_device); 84761021Smckusick if (sp->se_getty) { 84861021Smckusick free(sp->se_getty); 84961021Smckusick free(sp->se_getty_argv); 85061021Smckusick } 85150394Smckusick if (sp->se_window) { 85250394Smckusick free(sp->se_window); 85350394Smckusick free(sp->se_window_argv); 8541029Sbill } 85550394Smckusick free(sp); 8561029Sbill } 8571029Sbill 85850407Smckusick /* 85950407Smckusick * Allocate a new session descriptor. 86050407Smckusick */ 86150394Smckusick session_t * 86250394Smckusick new_session(sprev, session_index, typ) 86350394Smckusick session_t *sprev; 86450394Smckusick int session_index; 86550394Smckusick register struct ttyent *typ; 8661029Sbill { 86750394Smckusick register session_t *sp; 8681029Sbill 86950394Smckusick if ((typ->ty_status & TTY_ON) == 0 || 87050394Smckusick typ->ty_name == 0 || 87150394Smckusick typ->ty_getty == 0) 87250394Smckusick return 0; 87350394Smckusick 87450394Smckusick sp = (session_t *) malloc(sizeof (session_t)); 87561021Smckusick bzero(sp, sizeof *sp); 87650394Smckusick 87750394Smckusick sp->se_index = session_index; 87850394Smckusick 87953151Storek sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 88053151Storek (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 88150394Smckusick 88261021Smckusick if (setupargv(sp, typ) == 0) { 88350394Smckusick free_session(sp); 88461021Smckusick return (0); 8855971Sroot } 88650394Smckusick 88750394Smckusick sp->se_next = 0; 88850394Smckusick if (sprev == 0) { 88950394Smckusick sessions = sp; 89050394Smckusick sp->se_prev = 0; 89150394Smckusick } else { 89250394Smckusick sprev->se_next = sp; 89350394Smckusick sp->se_prev = sprev; 89450394Smckusick } 89550394Smckusick 89650394Smckusick return sp; 8971029Sbill } 8981029Sbill 89950407Smckusick /* 90061021Smckusick * Calculate getty and if useful window argv vectors. 90161021Smckusick */ 90261021Smckusick int 90361021Smckusick setupargv(sp, typ) 90461021Smckusick session_t *sp; 90561021Smckusick struct ttyent *typ; 90661021Smckusick { 90761021Smckusick 90861021Smckusick if (sp->se_getty) { 90961021Smckusick free(sp->se_getty); 91061021Smckusick free(sp->se_getty_argv); 91161021Smckusick } 91261021Smckusick sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); 91361021Smckusick (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); 91461021Smckusick sp->se_getty_argv = construct_argv(sp->se_getty); 91561021Smckusick if (sp->se_getty_argv == 0) { 91661021Smckusick warning("can't parse getty for port %s", sp->se_device); 91761021Smckusick free(sp->se_getty); 91861021Smckusick sp->se_getty = 0; 91961021Smckusick return (0); 92061021Smckusick } 92161021Smckusick if (typ->ty_window) { 92261021Smckusick if (sp->se_window) 92361021Smckusick free(sp->se_window); 92461021Smckusick sp->se_window = strdup(typ->ty_window); 92561021Smckusick sp->se_window_argv = construct_argv(sp->se_window); 92661021Smckusick if (sp->se_window_argv == 0) { 92761021Smckusick warning("can't parse window for port %s", 92861021Smckusick sp->se_device); 92961021Smckusick free(sp->se_window); 93061021Smckusick sp->se_window = 0; 93161021Smckusick return (0); 93261021Smckusick } 93361021Smckusick } 93461021Smckusick return (1); 93561021Smckusick } 93661021Smckusick 93761021Smckusick /* 93850407Smckusick * Walk the list of ttys and create sessions for each active line. 93950407Smckusick */ 94050394Smckusick state_func_t 94150394Smckusick read_ttys() 9421029Sbill { 94350394Smckusick int session_index = 0; 94450394Smckusick register session_t *sp, *snext; 94550394Smckusick register struct ttyent *typ; 94650394Smckusick 94750394Smckusick /* 94850394Smckusick * Destroy any previous session state. 94950394Smckusick * There shouldn't be any, but just in case... 95050394Smckusick */ 95150394Smckusick for (sp = sessions; sp; sp = snext) { 95250394Smckusick if (sp->se_process) 95350394Smckusick clear_session_logs(sp); 95450394Smckusick snext = sp->se_next; 95550394Smckusick free_session(sp); 9561029Sbill } 95750394Smckusick sessions = 0; 95850407Smckusick if (start_session_db()) 95950407Smckusick return (state_func_t) single_user; 96050394Smckusick 96150394Smckusick /* 96250394Smckusick * Allocate a session entry for each active port. 96350394Smckusick * Note that sp starts at 0. 96450394Smckusick */ 96550394Smckusick while (typ = getttyent()) 96650394Smckusick if (snext = new_session(sp, ++session_index, typ)) 96750394Smckusick sp = snext; 96850394Smckusick 96950394Smckusick endttyent(); 97050394Smckusick 97150394Smckusick return (state_func_t) multi_user; 9721029Sbill } 9731029Sbill 97450407Smckusick /* 97550407Smckusick * Start a window system running. 97650407Smckusick */ 97742407Smarc void 97850394Smckusick start_window_system(sp) 97950394Smckusick session_t *sp; 9801029Sbill { 98150394Smckusick pid_t pid; 98250394Smckusick sigset_t mask; 98350394Smckusick 98450394Smckusick if ((pid = fork()) == -1) { 98550394Smckusick emergency("can't fork for window system on port %s: %m", 98650394Smckusick sp->se_device); 98750394Smckusick /* hope that getty fails and we can try again */ 98850394Smckusick return; 98950394Smckusick } 99050394Smckusick 99150394Smckusick if (pid) 99250394Smckusick return; 99350394Smckusick 99450394Smckusick sigemptyset(&mask); 99550394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 99650394Smckusick 99750407Smckusick if (setsid() < 0) 99850407Smckusick emergency("setsid failed (window) %m"); 99950407Smckusick 100050394Smckusick execv(sp->se_window_argv[0], sp->se_window_argv); 100150394Smckusick stall("can't exec window system '%s' for port %s: %m", 100250394Smckusick sp->se_window_argv[0], sp->se_device); 100350394Smckusick _exit(1); 10041029Sbill } 10052821Swnj 100650407Smckusick /* 100750407Smckusick * Start a login session running. 100850407Smckusick */ 100950394Smckusick pid_t 101050394Smckusick start_getty(sp) 101150394Smckusick session_t *sp; 101250394Smckusick { 101350394Smckusick pid_t pid; 101450394Smckusick sigset_t mask; 101550394Smckusick time_t current_time = time((time_t *) 0); 101613021Ssam 101750394Smckusick /* 101850394Smckusick * fork(), not vfork() -- we can't afford to block. 101950394Smckusick */ 102050394Smckusick if ((pid = fork()) == -1) { 102150394Smckusick emergency("can't fork for getty on port %s: %m", sp->se_device); 102250394Smckusick return -1; 102350394Smckusick } 102450394Smckusick 102550394Smckusick if (pid) 102650394Smckusick return pid; 102750394Smckusick 102850394Smckusick if (current_time > sp->se_started && 102950394Smckusick current_time - sp->se_started < GETTY_SPACING) { 103050394Smckusick warning("getty repeating too quickly on port %s, sleeping", 103150394Smckusick sp->se_device); 103261084Smckusick sleep((unsigned) GETTY_SLEEP); 103350394Smckusick } 103450394Smckusick 103550394Smckusick if (sp->se_window) { 103650394Smckusick start_window_system(sp); 103750394Smckusick sleep(WINDOW_WAIT); 103850394Smckusick } 103950394Smckusick 104050394Smckusick sigemptyset(&mask); 104150394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 104250394Smckusick 104350394Smckusick execv(sp->se_getty_argv[0], sp->se_getty_argv); 104450394Smckusick stall("can't exec getty '%s' for port %s: %m", 104550394Smckusick sp->se_getty_argv[0], sp->se_device); 104650394Smckusick _exit(1); 104713021Ssam } 104813021Ssam 104950407Smckusick /* 105050407Smckusick * Collect exit status for a child. 105150407Smckusick * If an exiting login, start a new login running. 105250407Smckusick */ 105342407Smarc void 105461084Smckusick #ifdef __STDC__ 105561084Smckusick collect_child(pid_t pid) 105661084Smckusick #else 105750407Smckusick collect_child(pid) 105850407Smckusick pid_t pid; 105961084Smckusick #endif 10602821Swnj { 106150394Smckusick register session_t *sp, *sprev, *snext; 10622821Swnj 106350407Smckusick if (! sessions) 106450407Smckusick return; 106550394Smckusick 106650407Smckusick if (! (sp = find_session(pid))) 106750407Smckusick return; 106850394Smckusick 106950407Smckusick clear_session_logs(sp); 107050407Smckusick del_session(sp); 107150407Smckusick sp->se_process = 0; 107250394Smckusick 107350407Smckusick if (sp->se_flags & SE_SHUTDOWN) { 107450407Smckusick if (sprev = sp->se_prev) 107550407Smckusick sprev->se_next = sp->se_next; 107650407Smckusick else 107750407Smckusick sessions = sp->se_next; 107850407Smckusick if (snext = sp->se_next) 107950407Smckusick snext->se_prev = sp->se_prev; 108050407Smckusick free_session(sp); 108150407Smckusick return; 108250407Smckusick } 108350394Smckusick 108450407Smckusick if ((pid = start_getty(sp)) == -1) { 108550407Smckusick /* serious trouble */ 108650407Smckusick requested_transition = clean_ttys; 108750407Smckusick return; 10882821Swnj } 108950394Smckusick 109050407Smckusick sp->se_process = pid; 109150407Smckusick sp->se_started = time((time_t *) 0); 109250407Smckusick add_session(sp); 10932821Swnj } 109418542Sralph 109550407Smckusick /* 109650407Smckusick * Catch a signal and request a state transition. 109750407Smckusick */ 109850394Smckusick void 109950394Smckusick transition_handler(sig) 110050394Smckusick int sig; 110118542Sralph { 110250539Strent 110350394Smckusick switch (sig) { 110450394Smckusick case SIGHUP: 110550394Smckusick requested_transition = clean_ttys; 110650394Smckusick break; 110750394Smckusick case SIGTERM: 110850394Smckusick requested_transition = death; 110950394Smckusick break; 111050394Smckusick case SIGTSTP: 111150394Smckusick requested_transition = catatonia; 111250394Smckusick break; 111350394Smckusick default: 111450394Smckusick requested_transition = 0; 111550394Smckusick break; 111618542Sralph } 111718542Sralph } 111818542Sralph 111950407Smckusick /* 112050407Smckusick * Take the system multiuser. 112150407Smckusick */ 112250394Smckusick state_func_t 112350394Smckusick multi_user() 112418542Sralph { 112550394Smckusick pid_t pid; 112650394Smckusick register session_t *sp; 112718542Sralph 112850394Smckusick requested_transition = 0; 112922181Skarels 113058422Smckusick /* 113158422Smckusick * If the administrator has not set the security level to -1 113258422Smckusick * to indicate that the kernel should not run multiuser in secure 113358422Smckusick * mode, and the run script has not set a higher level of security 113458422Smckusick * than level 1, then put the kernel into secure mode. 113558422Smckusick */ 113658422Smckusick if (getsecuritylevel() == 0) 113758422Smckusick setsecuritylevel(1); 113858422Smckusick 113950407Smckusick for (sp = sessions; sp; sp = sp->se_next) { 114050407Smckusick if (sp->se_process) 114150407Smckusick continue; 114250407Smckusick if ((pid = start_getty(sp)) == -1) { 114350407Smckusick /* serious trouble */ 114450407Smckusick requested_transition = clean_ttys; 114550407Smckusick break; 114622181Skarels } 114750407Smckusick sp->se_process = pid; 114850407Smckusick sp->se_started = time((time_t *) 0); 114950407Smckusick add_session(sp); 115050407Smckusick } 115150394Smckusick 115250394Smckusick while (!requested_transition) 115350407Smckusick if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 115450407Smckusick collect_child(pid); 115550394Smckusick 115650394Smckusick return (state_func_t) requested_transition; 115718542Sralph } 115818542Sralph 115950394Smckusick /* 116050394Smckusick * This is an n-squared algorithm. We hope it isn't run often... 116150394Smckusick */ 116250394Smckusick state_func_t 116350394Smckusick clean_ttys() 116418542Sralph { 116550394Smckusick register session_t *sp, *sprev; 116650394Smckusick register struct ttyent *typ; 116750394Smckusick register int session_index = 0; 116852954Smckusick register int devlen; 116918542Sralph 117050394Smckusick if (! sessions) 117150394Smckusick return (state_func_t) multi_user; 117250394Smckusick 117353151Storek devlen = sizeof(_PATH_DEV) - 1; 117450394Smckusick while (typ = getttyent()) { 117550394Smckusick ++session_index; 117650394Smckusick 117760300Smckusick for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 117853151Storek if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 117918542Sralph break; 118050394Smckusick 118150394Smckusick if (sp) { 118250394Smckusick if (sp->se_index != session_index) { 118350394Smckusick warning("port %s changed utmp index from %d to %d", 118450394Smckusick sp->se_device, sp->se_index, 118550394Smckusick session_index); 118650394Smckusick sp->se_index = session_index; 118718542Sralph } 118860958Smckusick if ((typ->ty_status & TTY_ON) == 0 || 118960958Smckusick typ->ty_getty == 0) { 119050394Smckusick sp->se_flags |= SE_SHUTDOWN; 119150394Smckusick kill(sp->se_process, SIGHUP); 119260958Smckusick continue; 119350394Smckusick } 119460958Smckusick sp->se_flags &= ~SE_SHUTDOWN; 119561021Smckusick if (setupargv(sp, typ) == 0) { 119660958Smckusick warning("can't parse getty for port %s", 119760958Smckusick sp->se_device); 119860958Smckusick sp->se_flags |= SE_SHUTDOWN; 119960958Smckusick kill(sp->se_process, SIGHUP); 120060958Smckusick } 120150394Smckusick continue; 120218542Sralph } 120350394Smckusick 120450394Smckusick new_session(sprev, session_index, typ); 120518542Sralph } 120650394Smckusick 120750394Smckusick endttyent(); 120850394Smckusick 120950394Smckusick return (state_func_t) multi_user; 121018542Sralph } 121150394Smckusick 121250407Smckusick /* 121350407Smckusick * Block further logins. 121450407Smckusick */ 121550394Smckusick state_func_t 121650394Smckusick catatonia() 121750394Smckusick { 121850394Smckusick register session_t *sp; 121950394Smckusick 122050394Smckusick for (sp = sessions; sp; sp = sp->se_next) 122150394Smckusick sp->se_flags |= SE_SHUTDOWN; 122250394Smckusick 122350394Smckusick return (state_func_t) multi_user; 122450394Smckusick } 122550394Smckusick 122650407Smckusick /* 122750407Smckusick * Note SIGALRM. 122850407Smckusick */ 122950394Smckusick void 123050394Smckusick alrm_handler(sig) 123150394Smckusick int sig; 123250394Smckusick { 123350394Smckusick clang = 1; 123450394Smckusick } 123550394Smckusick 123650407Smckusick /* 123750407Smckusick * Bring the system down to single user. 123850407Smckusick */ 123950394Smckusick state_func_t 124050394Smckusick death() 124150394Smckusick { 124250394Smckusick register session_t *sp; 124350394Smckusick register int i; 124450407Smckusick pid_t pid; 124550394Smckusick static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 124650394Smckusick 124750394Smckusick for (sp = sessions; sp; sp = sp->se_next) 124850394Smckusick sp->se_flags |= SE_SHUTDOWN; 124950394Smckusick 125050430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 125150430Smckusick logwtmp("~", "shutdown", ""); 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 126861021Smckusick warning("some processes would not die; ps axl advised"); 126950394Smckusick 127050394Smckusick return (state_func_t) single_user; 127150394Smckusick } 1272