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*59441Sbostic static char sccsid[] = "@(#)init.c 6.15 (Berkeley) 04/28/93"; 1950394Smckusick #endif /* not lint */ 2050394Smckusick 21*59441Sbostic #include <sys/param.h> 2258422Smckusick #include <sys/sysctl.h> 2350394Smckusick #include <sys/wait.h> 24*59441Sbostic 2550394Smckusick #include <db.h> 262821Swnj #include <errno.h> 2750394Smckusick #include <fcntl.h> 2850394Smckusick #include <signal.h> 29*59441Sbostic #include <stdio.h> 30*59441Sbostic #include <stdlib.h> 31*59441Sbostic #include <string.h> 3250394Smckusick #include <syslog.h> 3350394Smckusick #include <time.h> 3416452Sroot #include <ttyent.h> 3550394Smckusick #include <unistd.h> 3650394Smckusick 3750394Smckusick #ifdef __STDC__ 3850394Smckusick #include <stdarg.h> 3950394Smckusick #else 4050394Smckusick #include <varargs.h> 4150394Smckusick #endif 4250394Smckusick 4350394Smckusick #ifdef SECURE 4450394Smckusick #include <pwd.h> 4550394Smckusick #endif 4650394Smckusick 4737284Sbostic #include "pathnames.h" 481029Sbill 4950394Smckusick /* 5050394Smckusick * Until the mythical util.h arrives... 5150394Smckusick */ 5250394Smckusick extern int login_tty __P((int)); 5350394Smckusick extern int logout __P((const char *)); 5450394Smckusick extern void logwtmp __P((const char *, const char *, const char *)); 551029Sbill 5650394Smckusick /* 5750394Smckusick * Sleep times; used to prevent thrashing. 5850394Smckusick */ 5950407Smckusick #define GETTY_SPACING 10 /* fork getty on a port every N secs */ 6050394Smckusick #define WINDOW_WAIT 3 /* wait N secs after starting window */ 6150394Smckusick #define STALL_TIMEOUT 30 /* wait N secs after warning */ 6250407Smckusick #define DEATH_WATCH 10 /* wait N secs for procs to die */ 631029Sbill 6450394Smckusick void handle __P((sig_t, ...)); 6550394Smckusick void delset __P((sigset_t *, ...)); 661029Sbill 6750394Smckusick void stall __P((char *, ...)); 6850394Smckusick void warning __P((char *, ...)); 6950394Smckusick void emergency __P((char *, ...)); 7050394Smckusick void disaster __P((int)); 7158422Smckusick void badsys __P((int)); 721029Sbill 7350394Smckusick /* 7450394Smckusick * We really need a recursive typedef... 7550394Smckusick * The following at least guarantees that the return type of (*state_t)() 7650394Smckusick * is sufficiently wide to hold a function pointer. 7750394Smckusick */ 7850394Smckusick typedef long (*state_func_t) __P((void)); 7950394Smckusick typedef state_func_t (*state_t) __P((void)); 801029Sbill 8150394Smckusick state_func_t single_user __P((void)); 8250394Smckusick state_func_t runcom __P((void)); 8350394Smckusick state_func_t read_ttys __P((void)); 8450394Smckusick state_func_t multi_user __P((void)); 8550394Smckusick state_func_t clean_ttys __P((void)); 8650394Smckusick state_func_t catatonia __P((void)); 8750394Smckusick state_func_t death __P((void)); 8813021Ssam 8950394Smckusick enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 9050394Smckusick 9150394Smckusick void transition __P((state_t)); 9250394Smckusick state_t requested_transition = runcom; 9350394Smckusick 9450394Smckusick void setctty __P((char *)); 9550394Smckusick 96*59441Sbostic typedef struct init_session { 9750394Smckusick int se_index; /* index of entry in ttys file */ 9850394Smckusick pid_t se_process; /* controlling process */ 9950394Smckusick time_t se_started; /* used to avoid thrashing */ 10050394Smckusick int se_flags; /* status of session */ 10150394Smckusick #define SE_SHUTDOWN 0x1 /* session won't be restarted */ 10250394Smckusick char *se_device; /* filename of port */ 10350394Smckusick char *se_getty; /* what to run on that port */ 10450394Smckusick char **se_getty_argv; /* pre-parsed argument array */ 10550394Smckusick char *se_window; /* window system (started only once) */ 10650394Smckusick char **se_window_argv; /* pre-parsed argument array */ 107*59441Sbostic struct init_session *se_prev; 108*59441Sbostic struct init_session *se_next; 10950394Smckusick } session_t; 11050394Smckusick 11150394Smckusick void free_session __P((session_t *)); 11250394Smckusick session_t *new_session __P((session_t *, int, struct ttyent *)); 11350394Smckusick session_t *sessions; 11450394Smckusick 11550394Smckusick char **construct_argv __P((char *)); 11650394Smckusick void start_window_system __P((session_t *)); 11750407Smckusick void collect_child __P((int)); 11850394Smckusick pid_t start_getty __P((session_t *)); 11950394Smckusick void transition_handler __P((int)); 12050394Smckusick void alrm_handler __P((int)); 12150394Smckusick int clang; 12250394Smckusick 12350394Smckusick int start_logger __P((void)); 12450394Smckusick void clear_session_logs __P((session_t *)); 12550394Smckusick int logger_enable; 12650394Smckusick 12750407Smckusick int start_session_db __P((void)); 12850394Smckusick void add_session __P((session_t *)); 12950394Smckusick void del_session __P((session_t *)); 13050394Smckusick session_t *find_session __P((pid_t)); 13150394Smckusick DB *session_db; 13250394Smckusick 13350407Smckusick /* 13450407Smckusick * The mother of all processes. 13550407Smckusick */ 13650394Smckusick int 13743633Skarels main(argc, argv) 13850394Smckusick int argc; 13943633Skarels char **argv; 1401029Sbill { 14150394Smckusick int c; 14250407Smckusick struct sigaction sa; 14350394Smckusick sigset_t mask; 14447659Skarels 1459869Spugs 14652795Sbostic /* Dispose of random users. */ 14752795Sbostic if (getuid() != 0) { 14852795Sbostic (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); 14952795Sbostic exit (1); 15052795Sbostic } 15152795Sbostic 15252795Sbostic /* System V users like to reexec init. */ 15352795Sbostic if (getpid() != 1) { 15452795Sbostic (void)fprintf(stderr, "init: already running\n"); 15552795Sbostic exit (1); 15652795Sbostic } 15752795Sbostic 15850394Smckusick /* 15950394Smckusick * Note that this does NOT open a file... 16050394Smckusick * Does 'init' deserve its own facility number? 16150394Smckusick */ 16250407Smckusick openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 16350394Smckusick 16450394Smckusick /* 16550407Smckusick * Create an initial session. 16650407Smckusick */ 16750407Smckusick if (setsid() < 0) 16850407Smckusick syslog(LOG_ERR, "setsid failed (initial) %m"); 16950407Smckusick 17050407Smckusick /* 17150394Smckusick * This code assumes that we always get arguments through flags, 17250394Smckusick * never through bits set in some random machine register. 17350394Smckusick */ 17450394Smckusick while ((c = getopt(argc, argv, "sf")) != -1) 17550394Smckusick switch (c) { 17650394Smckusick case 's': 17750394Smckusick requested_transition = single_user; 17850394Smckusick break; 17947659Skarels case 'f': 18050394Smckusick runcom_mode = FASTBOOT; 1819869Spugs break; 18250394Smckusick default: 18350394Smckusick warning("unrecognized flag '-%c'", c); 1849869Spugs break; 1859869Spugs } 18650394Smckusick 18750394Smckusick if (optind != argc) 18850394Smckusick warning("ignoring excess arguments"); 18950394Smckusick 19050394Smckusick /* 19150394Smckusick * We catch or block signals rather than ignore them, 19250394Smckusick * so that they get reset on exec. 19350394Smckusick */ 19458422Smckusick handle(badsys, SIGSYS, 0); 19550394Smckusick handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 19658422Smckusick SIGBUS, SIGXCPU, SIGXFSZ, 0); 19750394Smckusick handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); 19850394Smckusick handle(alrm_handler, SIGALRM, 0); 19950394Smckusick sigfillset(&mask); 20050407Smckusick delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 20150407Smckusick SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); 20250394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 20350407Smckusick sigemptyset(&sa.sa_mask); 20450407Smckusick sa.sa_flags = 0; 20550407Smckusick sa.sa_handler = SIG_IGN; 20650407Smckusick (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); 20750407Smckusick (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); 20850394Smckusick 20950394Smckusick /* 21050394Smckusick * Paranoia. 21150394Smckusick */ 21250394Smckusick close(0); 21350394Smckusick close(1); 21450394Smckusick close(2); 21550394Smckusick 21650394Smckusick /* 21750394Smckusick * Start the state machine. 21850394Smckusick */ 21950394Smckusick transition(requested_transition); 22050394Smckusick 22150394Smckusick /* 22250394Smckusick * Should never reach here. 22350394Smckusick */ 22450394Smckusick return 1; 22550394Smckusick } 22650394Smckusick 22750407Smckusick /* 22850407Smckusick * Associate a function with a signal handler. 22950407Smckusick */ 23050394Smckusick void 23150394Smckusick #ifdef __STDC__ 23250394Smckusick handle(sig_t handler, ...) 23350394Smckusick #else 23450394Smckusick handle(va_alist) 23550394Smckusick va_dcl 23650394Smckusick #endif 23750394Smckusick { 23850394Smckusick int sig; 23950394Smckusick struct sigaction sa; 24050394Smckusick int mask_everything; 24150394Smckusick va_list ap; 24250394Smckusick #ifndef __STDC__ 24350394Smckusick sig_t handler; 24450394Smckusick 24550394Smckusick va_start(ap); 24650394Smckusick handler = va_arg(ap, sig_t); 24750394Smckusick #else 24850394Smckusick va_start(ap, handler); 24950394Smckusick #endif 25050394Smckusick 25150394Smckusick sa.sa_handler = handler; 25250394Smckusick sigfillset(&mask_everything); 25350394Smckusick 25450394Smckusick while (sig = va_arg(ap, int)) { 25550394Smckusick sa.sa_mask = mask_everything; 25650394Smckusick /* XXX SA_RESTART? */ 25750394Smckusick sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 25850394Smckusick sigaction(sig, &sa, (struct sigaction *) 0); 25947659Skarels } 2601029Sbill } 2611029Sbill 26250407Smckusick /* 26350407Smckusick * Delete a set of signals from a mask. 26450407Smckusick */ 26550394Smckusick void 26650394Smckusick #ifdef __STDC__ 26750394Smckusick delset(sigset_t *maskp, ...) 26850394Smckusick #else 26950394Smckusick delset(va_alist) 27050394Smckusick va_dcl 27150394Smckusick #endif 27250394Smckusick { 27350394Smckusick int sig; 27450394Smckusick va_list ap; 27550394Smckusick #ifndef __STDC__ 27650394Smckusick sigset_t *maskp; 2771429Sbill 27850394Smckusick va_start(ap); 27950394Smckusick maskp = va_arg(ap, sigset_t *); 28050394Smckusick #else 28150394Smckusick va_start(ap, maskp); 28250394Smckusick #endif 28350394Smckusick 28450394Smckusick while (sig = va_arg(ap, int)) 28550394Smckusick sigdelset(maskp, sig); 28650394Smckusick } 28750394Smckusick 28850394Smckusick /* 28950394Smckusick * Log a message and sleep for a while (to give someone an opportunity 29050394Smckusick * to read it and to save log or hardcopy output if the problem is chronic). 29150430Smckusick * NB: should send a message to the session logger to avoid blocking. 29250394Smckusick */ 29350394Smckusick void 29450394Smckusick #ifdef __STDC__ 29550394Smckusick stall(char *message, ...) 29650394Smckusick #else 29750394Smckusick stall(va_alist) 29850394Smckusick va_dcl 29950394Smckusick #endif 3001029Sbill { 30150394Smckusick pid_t pid; 30250394Smckusick va_list ap; 30350394Smckusick #ifndef __STDC__ 30450394Smckusick char *message; 3051029Sbill 30650394Smckusick va_start(ap); 30750394Smckusick message = va_arg(ap, char *); 30850394Smckusick #else 30950394Smckusick va_start(ap, message); 31050394Smckusick #endif 31150394Smckusick 31250430Smckusick vsyslog(LOG_ALERT, message, ap); 31350394Smckusick va_end(ap); 31450394Smckusick sleep(STALL_TIMEOUT); 3151429Sbill } 3161429Sbill 31750394Smckusick /* 31850394Smckusick * Like stall(), but doesn't sleep. 31950394Smckusick * If cpp had variadic macros, the two functions could be #defines for another. 32050430Smckusick * NB: should send a message to the session logger to avoid blocking. 32150394Smckusick */ 32242407Smarc void 32350394Smckusick #ifdef __STDC__ 32450394Smckusick warning(char *message, ...) 32550394Smckusick #else 32650394Smckusick warning(va_alist) 32750394Smckusick va_dcl 32850394Smckusick #endif 3291429Sbill { 33050394Smckusick va_list ap; 33150394Smckusick #ifndef __STDC__ 33250394Smckusick char *message; 3331429Sbill 33450394Smckusick va_start(ap); 33550394Smckusick message = va_arg(ap, char *); 33650394Smckusick #else 33750394Smckusick va_start(ap, message); 33850394Smckusick #endif 33950394Smckusick 34050430Smckusick vsyslog(LOG_ALERT, message, ap); 34150394Smckusick va_end(ap); 3421429Sbill } 3431429Sbill 34450394Smckusick /* 34550430Smckusick * Log an emergency message. 34650430Smckusick * NB: should send a message to the session logger to avoid blocking. 34750394Smckusick */ 34850394Smckusick void 34950394Smckusick #ifdef __STDC__ 35050394Smckusick emergency(char *message, ...) 35150394Smckusick #else 35250394Smckusick emergency(va_alist) 35350394Smckusick va_dcl 35450394Smckusick #endif 3551429Sbill { 35650394Smckusick va_list ap; 35750394Smckusick #ifndef __STDC__ 35850394Smckusick char *message; 3591429Sbill 36050394Smckusick va_start(ap); 36150394Smckusick message = va_arg(ap, char *); 36250394Smckusick #else 36350394Smckusick va_start(ap, message); 36450394Smckusick #endif 36550394Smckusick 36650394Smckusick vsyslog(LOG_EMERG, message, ap); 36750394Smckusick va_end(ap); 3681029Sbill } 3691029Sbill 37050407Smckusick /* 37158422Smckusick * Catch a SIGSYS signal. 37258422Smckusick * 37358422Smckusick * These may arise if a system does not support sysctl. 37458422Smckusick * We tolerate up to 25 of these, then throw in the towel. 37558422Smckusick */ 37658422Smckusick void 37758422Smckusick badsys(sig) 37858422Smckusick int sig; 37958422Smckusick { 38058422Smckusick static int badcount = 0; 38158422Smckusick 38258422Smckusick if (badcount++ < 25) 38358422Smckusick return; 38458422Smckusick disaster(sig); 38558422Smckusick } 38658422Smckusick 38758422Smckusick /* 38850407Smckusick * Catch an unexpected signal. 38950407Smckusick */ 39050394Smckusick void 39150394Smckusick disaster(sig) 39250394Smckusick int sig; 3931029Sbill { 39450394Smckusick emergency("fatal signal: %s", 39550394Smckusick sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); 3961029Sbill 39750394Smckusick sleep(STALL_TIMEOUT); 39850394Smckusick _exit(sig); /* reboot */ 3991029Sbill } 4001029Sbill 40150394Smckusick /* 40258422Smckusick * Get the security level of the kernel. 40358422Smckusick */ 40458422Smckusick int 40558422Smckusick getsecuritylevel() 40658422Smckusick { 40758600Smckusick int name[2], curlevel; 40858600Smckusick size_t len; 40958422Smckusick extern int errno; 41058422Smckusick 41158422Smckusick name[0] = CTL_KERN; 41258422Smckusick name[1] = KERN_SECURELVL; 41358422Smckusick len = sizeof curlevel; 41458422Smckusick if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 41558422Smckusick emergency("cannot get kernel security level: %s", 41658422Smckusick strerror(errno)); 41758422Smckusick return (-1); 41858422Smckusick } 41958422Smckusick return (curlevel); 42058422Smckusick } 42158422Smckusick 42258422Smckusick /* 42358422Smckusick * Set the security level of the kernel. 42458422Smckusick */ 42558422Smckusick setsecuritylevel(newlevel) 42658422Smckusick int newlevel; 42758422Smckusick { 42858422Smckusick int name[2], len, curlevel; 42958422Smckusick extern int errno; 43058422Smckusick 43158422Smckusick curlevel = getsecuritylevel(); 43258422Smckusick if (newlevel == curlevel) 43358422Smckusick return; 43458422Smckusick name[0] = CTL_KERN; 43558422Smckusick name[1] = KERN_SECURELVL; 43658422Smckusick if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 43758422Smckusick emergency( 43858422Smckusick "cannot change kernel security level from %d to %d: %s", 43958422Smckusick curlevel, newlevel, strerror(errno)); 44058422Smckusick return; 44158422Smckusick } 44258422Smckusick #ifdef SECURE 44358422Smckusick warning("kernel security level changed from %d to %d", 44458422Smckusick curlevel, newlevel); 44558422Smckusick #endif 44658422Smckusick } 44758422Smckusick 44858422Smckusick /* 44950394Smckusick * Change states in the finite state machine. 45050394Smckusick * The initial state is passed as an argument. 45150394Smckusick */ 45250394Smckusick void 45350394Smckusick transition(s) 45450394Smckusick state_t s; 4551029Sbill { 45650394Smckusick for (;;) 45750394Smckusick s = (state_t) (*s)(); 45850394Smckusick } 4591029Sbill 46050394Smckusick /* 46150394Smckusick * We send requests for session logging to another process for two reasons. 46250394Smckusick * First, we don't want to block if the log files go away (e.g. because 46350394Smckusick * one or more are on hard-mounted NFS systems whose server crashes). 46450394Smckusick * Second, despite all the crud already contained in init, it still isn't 46550394Smckusick * right that init should care about session logging record formats and files. 46650394Smckusick * We could use explicit 'Unix' IPC for this, but let's try to be POSIX... 46750394Smckusick */ 46850394Smckusick int 46950394Smckusick start_logger() 47050394Smckusick { 47150394Smckusick static char *argv[] = { _PATH_SLOGGER, 0 }; 47250394Smckusick int fd, pfd[2]; 47350394Smckusick pid_t pid; 47450394Smckusick sigset_t mask; 47550394Smckusick 47650394Smckusick if (pipe(pfd) == -1) { 47750394Smckusick warning("session logging disabled: can't make pipe to %s: %m", 47850394Smckusick argv[0]); 47950394Smckusick return -1; 48050394Smckusick } 48150394Smckusick if ((pid = fork()) == -1) { 48250394Smckusick emergency("session logging disabled: can't fork for %s: %m", 48350394Smckusick argv[0]); 48450394Smckusick return -1; 48550394Smckusick } 48650394Smckusick 48713021Ssam if (pid == 0) { 48850394Smckusick close(pfd[1]); 48950394Smckusick if (pfd[0] != 0) { 49050394Smckusick dup2(pfd[0], 0); 49150394Smckusick close(pfd[0]); 49250394Smckusick } 49350394Smckusick if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != -1) { 49450394Smckusick if (fd != 1) 49550394Smckusick dup2(fd, 1); 49650394Smckusick if (fd != 2) 49750394Smckusick dup2(fd, 2); 49850394Smckusick if (fd != 1 && fd != 2) 49944284Skarels close(fd); 50050394Smckusick } else { 50150394Smckusick /* paranoid */ 50250394Smckusick close(1); 50350394Smckusick close(2); 50444284Skarels } 50550394Smckusick sigemptyset(&mask); 50650394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 50750394Smckusick execv(argv[0], argv); 50850394Smckusick stall("can't exec %s: %m", argv[0]); 50950394Smckusick _exit(1); 5101029Sbill } 51150394Smckusick 51250394Smckusick close(pfd[0]); 51350394Smckusick fcntl(pfd[1], F_SETFD, FD_CLOEXEC); 51450394Smckusick fcntl(pfd[1], F_SETFL, O_NONBLOCK); 51550394Smckusick 51650394Smckusick return pfd[1]; 5171029Sbill } 5181029Sbill 51950407Smckusick /* 52050407Smckusick * Close out the accounting files for a login session. 52150430Smckusick * NB: should send a message to the session logger to avoid blocking. 52250407Smckusick */ 52350394Smckusick void 52450394Smckusick clear_session_logs(sp) 52550394Smckusick session_t *sp; 52650394Smckusick { 52753151Storek char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 52853151Storek 52953151Storek if (logout(line)) 53053151Storek logwtmp(line, "", ""); 53150394Smckusick } 53250394Smckusick 53313021Ssam /* 53450394Smckusick * Start a session and allocate a controlling terminal. 53550394Smckusick * Only called by children of init after forking. 53613021Ssam */ 53750394Smckusick void 53850394Smckusick setctty(name) 53950394Smckusick char *name; 5401029Sbill { 54150394Smckusick int fd; 5421029Sbill 54350407Smckusick (void) revoke(name); 54450394Smckusick if ((fd = open(name, O_RDWR)) == -1) { 54550394Smckusick stall("can't open %s: %m", name); 54650394Smckusick _exit(1); 5471029Sbill } 54850394Smckusick if (login_tty(fd) == -1) { 54950394Smckusick stall("can't get %s for controlling terminal: %m", name); 55050394Smckusick _exit(1); 55150394Smckusick } 5521029Sbill } 5531029Sbill 55450407Smckusick /* 55550407Smckusick * Bring the system up single user. 55650407Smckusick */ 55750394Smckusick state_func_t 55850394Smckusick single_user() 55913021Ssam { 56050394Smckusick pid_t pid, wpid; 56150394Smckusick int status; 56250394Smckusick sigset_t mask; 56350394Smckusick char *shell = _PATH_BSHELL; 56450394Smckusick char *argv[2]; 56550394Smckusick #ifdef SECURE 56650394Smckusick struct ttyent *typ; 56750394Smckusick struct passwd *pp; 56850394Smckusick static const char banner[] = 56950394Smckusick "Enter root password, or ^D to go multi-user\n"; 57050394Smckusick char *password; 57150394Smckusick #endif 57213021Ssam 57358422Smckusick /* 57458422Smckusick * If the kernel is in secure mode, downgrade it to insecure mode. 57558422Smckusick */ 57658422Smckusick if (getsecuritylevel() > 0) 57758422Smckusick setsecuritylevel(0); 57858422Smckusick 57950394Smckusick if ((pid = fork()) == 0) { 58050394Smckusick /* 58150394Smckusick * Start the single user session. 58250394Smckusick */ 58350394Smckusick setctty(_PATH_CONSOLE); 58450394Smckusick 58550394Smckusick #ifdef SECURE 58650394Smckusick /* 58750394Smckusick * Check the root password. 58850394Smckusick * We don't care if the console is 'on' by default; 58950394Smckusick * it's the only tty that can be 'off' and 'secure'. 59050394Smckusick */ 59150394Smckusick typ = getttynam("console"); 59250394Smckusick pp = getpwnam("root"); 59350394Smckusick if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { 59450394Smckusick write(2, banner, sizeof banner - 1); 59550394Smckusick for (;;) { 59650394Smckusick password = getpass("Password:"); 59750394Smckusick if (password == 0 || *password == '\0') 59850394Smckusick _exit(0); 59952954Smckusick password = crypt(password, pp->pw_passwd); 60050394Smckusick if (strcmp(password, pp->pw_passwd) == 0) 60150394Smckusick break; 60258422Smckusick warning("single-user login failed\n"); 60313021Ssam } 60413021Ssam } 60550394Smckusick endttyent(); 60650394Smckusick endpwent(); 60758422Smckusick #endif /* SECURE */ 60818542Sralph 60958422Smckusick #ifdef DEBUGSHELL 61058422Smckusick { 61158422Smckusick char altshell[128], *cp = altshell; 61258422Smckusick int num; 61358422Smckusick 61458422Smckusick #define SHREQUEST \ 61558422Smckusick "Enter pathname of shell or RETURN for sh: " 61658422Smckusick (void)write(STDERR_FILENO, 61758422Smckusick SHREQUEST, sizeof(SHREQUEST) - 1); 61858422Smckusick while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 61958422Smckusick num != 0 && *cp != '\n' && cp < &altshell[127]) 62058422Smckusick cp++; 62158422Smckusick *cp = '\0'; 62258422Smckusick if (altshell[0] != '\0') 62358422Smckusick shell = altshell; 62458422Smckusick } 62558422Smckusick #endif /* DEBUGSHELL */ 62658422Smckusick 62723147Sbloom /* 62850394Smckusick * Unblock signals. 62950394Smckusick * We catch all the interesting ones, 63050394Smckusick * and those are reset to SIG_DFL on exec. 63123147Sbloom */ 63250394Smckusick sigemptyset(&mask); 63350394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 63450394Smckusick 63523147Sbloom /* 63650394Smckusick * Fire off a shell. 63750394Smckusick * If the default one doesn't work, try the Bourne shell. 63823147Sbloom */ 63950394Smckusick argv[0] = "-sh"; 64050394Smckusick argv[1] = 0; 64150394Smckusick execv(shell, argv); 64250394Smckusick emergency("can't exec %s for single user: %m", shell); 64350394Smckusick execv(_PATH_BSHELL, argv); 64450394Smckusick emergency("can't exec %s for single user: %m", _PATH_BSHELL); 64550394Smckusick sleep(STALL_TIMEOUT); 64650394Smckusick _exit(1); 64750394Smckusick } 64823147Sbloom 64950394Smckusick if (pid == -1) { 65050394Smckusick /* 65150394Smckusick * We are seriously hosed. Do our best. 65250394Smckusick */ 65350394Smckusick emergency("can't fork single-user shell, trying again"); 65450394Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 65552219Storek continue; 65650394Smckusick return (state_func_t) single_user; 65750394Smckusick } 65850394Smckusick 65950539Strent requested_transition = 0; 66050407Smckusick do { 66150407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 66250407Smckusick collect_child(wpid); 66350407Smckusick if (wpid == -1) { 66450407Smckusick if (errno == EINTR) 66550407Smckusick continue; 66650394Smckusick warning("wait for single-user shell failed: %m; restarting"); 66750394Smckusick return (state_func_t) single_user; 66823147Sbloom } 66950394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 67050394Smckusick warning("init: shell stopped, restarting\n"); 67150394Smckusick kill(pid, SIGCONT); 67250407Smckusick wpid = -1; 67350394Smckusick } 67450539Strent } while (wpid != pid && !requested_transition); 67550394Smckusick 67650539Strent if (requested_transition) 67750539Strent return (state_func_t) requested_transition; 67850539Strent 67950394Smckusick if (!WIFEXITED(status)) { 68050538Strent if (WTERMSIG(status) == SIGKILL) { 68150538Strent /* 68250538Strent * reboot(8) killed shell? 68350538Strent */ 68450538Strent warning("single user shell terminated."); 68550538Strent sleep(STALL_TIMEOUT); 68650538Strent _exit(0); 68750538Strent } else { 68850538Strent warning("single user shell terminated, restarting"); 68950538Strent return (state_func_t) single_user; 69050538Strent } 69150394Smckusick } 69250394Smckusick 69350394Smckusick runcom_mode = FASTBOOT; 69450394Smckusick return (state_func_t) runcom; 69550394Smckusick } 69650394Smckusick 69750407Smckusick /* 69850407Smckusick * Run the system startup script. 69950407Smckusick */ 70050394Smckusick state_func_t 70150394Smckusick runcom() 70250394Smckusick { 70350394Smckusick pid_t pid, wpid; 70450394Smckusick int status; 70550394Smckusick char *argv[4]; 70650407Smckusick struct sigaction sa; 70750394Smckusick 70850394Smckusick if ((pid = fork()) == 0) { 70950407Smckusick sigemptyset(&sa.sa_mask); 71050407Smckusick sa.sa_flags = 0; 71150407Smckusick sa.sa_handler = SIG_IGN; 71250407Smckusick (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); 71350407Smckusick (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); 71450407Smckusick 71550394Smckusick setctty(_PATH_CONSOLE); 71650394Smckusick 71750394Smckusick argv[0] = "sh"; 71850394Smckusick argv[1] = _PATH_RUNCOM; 71950394Smckusick argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; 72050394Smckusick argv[3] = 0; 72150394Smckusick 72250407Smckusick sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 72350394Smckusick 72450394Smckusick execv(_PATH_BSHELL, argv); 72550394Smckusick stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 72650394Smckusick _exit(1); /* force single user mode */ 72750394Smckusick } 72850394Smckusick 72950394Smckusick if (pid == -1) { 73050394Smckusick emergency("can't fork for %s on %s: %m", 73150394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 73250407Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 73352219Storek continue; 73450394Smckusick sleep(STALL_TIMEOUT); 73550394Smckusick return (state_func_t) single_user; 73650394Smckusick } 73750394Smckusick 73850394Smckusick /* 73950407Smckusick * Copied from single_user(). This is a bit paranoid. 74050394Smckusick */ 74150407Smckusick do { 74250407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 74350407Smckusick collect_child(wpid); 74450407Smckusick if (wpid == -1) { 74550407Smckusick if (errno == EINTR) 74650407Smckusick continue; 74750394Smckusick warning("wait for %s on %s failed: %m; going to single user mode", 74850394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 74950394Smckusick return (state_func_t) single_user; 75013021Ssam } 75150394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 75250394Smckusick warning("init: %s on %s stopped, restarting\n", 75350394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 75450394Smckusick kill(pid, SIGCONT); 75550407Smckusick wpid = -1; 75650394Smckusick } 75750407Smckusick } while (wpid != pid); 75850394Smckusick 75950394Smckusick if (!WIFEXITED(status)) { 76050394Smckusick warning("%s on %s terminated abnormally, going to single user mode", 76150394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 76250394Smckusick return (state_func_t) single_user; 76350394Smckusick } 76450394Smckusick 76550394Smckusick if (WEXITSTATUS(status)) 76650394Smckusick return (state_func_t) single_user; 76750394Smckusick 76850394Smckusick runcom_mode = AUTOBOOT; /* the default */ 76950430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 77050430Smckusick logwtmp("~", "reboot", ""); 77150394Smckusick return (state_func_t) read_ttys; 77213021Ssam } 77313021Ssam 77450394Smckusick /* 77550407Smckusick * Open the session database. 77650407Smckusick * 77750407Smckusick * NB: We could pass in the size here; is it necessary? 77850394Smckusick */ 77950407Smckusick int 78050394Smckusick start_session_db() 7811029Sbill { 78250407Smckusick if (session_db && (*session_db->close)(session_db)) 78350407Smckusick emergency("session database close: %s", strerror(errno)); 78451621Sbostic if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 78550407Smckusick emergency("session database open: %s", strerror(errno)); 78650407Smckusick return (1); 78750407Smckusick } 78850407Smckusick return (0); 78950407Smckusick 79050394Smckusick } 7911029Sbill 79250407Smckusick /* 79350407Smckusick * Add a new login session. 79450407Smckusick */ 79550394Smckusick void 79650394Smckusick add_session(sp) 79750394Smckusick session_t *sp; 79850394Smckusick { 79950394Smckusick DBT key; 80050394Smckusick DBT data; 80150394Smckusick 80250394Smckusick key.data = &sp->se_process; 80350394Smckusick key.size = sizeof sp->se_process; 80450394Smckusick data.data = &sp; 80550394Smckusick data.size = sizeof sp; 80650394Smckusick 80751621Sbostic if ((*session_db->put)(session_db, &key, &data, 0)) 80850407Smckusick emergency("insert %d: %s", sp->se_process, strerror(errno)); 80950394Smckusick } 81050394Smckusick 81150407Smckusick /* 81250407Smckusick * Delete an old login session. 81350407Smckusick */ 81450394Smckusick void 81550394Smckusick del_session(sp) 81650394Smckusick session_t *sp; 81750394Smckusick { 81850394Smckusick DBT key; 81950394Smckusick 82050394Smckusick key.data = &sp->se_process; 82150394Smckusick key.size = sizeof sp->se_process; 82250394Smckusick 82350407Smckusick if ((*session_db->del)(session_db, &key, 0)) 82450407Smckusick emergency("delete %d: %s", sp->se_process, strerror(errno)); 82550394Smckusick } 82650394Smckusick 82750407Smckusick /* 82850407Smckusick * Look up a login session by pid. 82950407Smckusick */ 83050394Smckusick session_t * 83150394Smckusick #ifdef __STDC__ 83250394Smckusick find_session(pid_t pid) 83350394Smckusick #else 83450394Smckusick find_session(pid) 83550394Smckusick pid_t pid; 83650394Smckusick #endif 83750394Smckusick { 83850394Smckusick DBT key; 83950394Smckusick DBT data; 84052219Storek session_t *ret; 84150394Smckusick 84250394Smckusick key.data = &pid; 84350394Smckusick key.size = sizeof pid; 84450394Smckusick if ((*session_db->get)(session_db, &key, &data, 0) != 0) 84550394Smckusick return 0; 84652219Storek bcopy(data.data, (char *)&ret, sizeof(ret)); 84752219Storek return ret; 84850394Smckusick } 84950394Smckusick 85050407Smckusick /* 85150407Smckusick * Construct an argument vector from a command line. 85250407Smckusick */ 85350394Smckusick char ** 85450394Smckusick construct_argv(command) 85550394Smckusick char *command; 85650394Smckusick { 85750394Smckusick register int argc = 0; 85850394Smckusick register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 85950394Smckusick * sizeof (char *)); 86050394Smckusick static const char separators[] = " \t"; 86150394Smckusick 86250394Smckusick if ((argv[argc++] = strtok(command, separators)) == 0) 86350394Smckusick return 0; 86450394Smckusick while (argv[argc++] = strtok((char *) 0, separators)) 86552219Storek continue; 86650394Smckusick return argv; 86750394Smckusick } 86850394Smckusick 86950407Smckusick /* 87050407Smckusick * Deallocate a session descriptor. 87150407Smckusick */ 87250394Smckusick void 87350394Smckusick free_session(sp) 87450394Smckusick register session_t *sp; 87550394Smckusick { 87650394Smckusick free(sp->se_device); 87750394Smckusick free(sp->se_getty); 87850394Smckusick free(sp->se_getty_argv); 87950394Smckusick if (sp->se_window) { 88050394Smckusick free(sp->se_window); 88150394Smckusick free(sp->se_window_argv); 8821029Sbill } 88350394Smckusick free(sp); 8841029Sbill } 8851029Sbill 88650407Smckusick /* 88750407Smckusick * Allocate a new session descriptor. 88850407Smckusick */ 88950394Smckusick session_t * 89050394Smckusick new_session(sprev, session_index, typ) 89150394Smckusick session_t *sprev; 89250394Smckusick int session_index; 89350394Smckusick register struct ttyent *typ; 8941029Sbill { 89550394Smckusick register session_t *sp; 8961029Sbill 89750394Smckusick if ((typ->ty_status & TTY_ON) == 0 || 89850394Smckusick typ->ty_name == 0 || 89950394Smckusick typ->ty_getty == 0) 90050394Smckusick return 0; 90150394Smckusick 90250394Smckusick sp = (session_t *) malloc(sizeof (session_t)); 90350394Smckusick 90450394Smckusick sp->se_index = session_index; 90550394Smckusick sp->se_process = 0; 90650394Smckusick sp->se_started = 0; 90750394Smckusick sp->se_flags = 0; 90850394Smckusick sp->se_window = 0; 90950394Smckusick 91053151Storek sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 91153151Storek (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 91250394Smckusick 91350394Smckusick sp->se_getty = strdup(typ->ty_getty); 91450394Smckusick sp->se_getty_argv = construct_argv(sp->se_getty); 91550394Smckusick if (sp->se_getty_argv == 0) { 91650394Smckusick warning("can't parse getty for port %s", 91750394Smckusick sp->se_device); 91850394Smckusick free_session(sp); 91950394Smckusick return 0; 9205971Sroot } 92150394Smckusick if (typ->ty_window) { 92250394Smckusick sp->se_window = strdup(typ->ty_window); 92350394Smckusick sp->se_window_argv = construct_argv(sp->se_window); 92450394Smckusick if (sp->se_window_argv == 0) { 92550394Smckusick warning("can't parse window for port %s", 92650394Smckusick sp->se_device); 92750394Smckusick free_session(sp); 92850394Smckusick return 0; 9295971Sroot } 9301029Sbill } 93150394Smckusick 93250394Smckusick sp->se_next = 0; 93350394Smckusick if (sprev == 0) { 93450394Smckusick sessions = sp; 93550394Smckusick sp->se_prev = 0; 93650394Smckusick } else { 93750394Smckusick sprev->se_next = sp; 93850394Smckusick sp->se_prev = sprev; 93950394Smckusick } 94050394Smckusick 94150394Smckusick return sp; 9421029Sbill } 9431029Sbill 94450407Smckusick /* 94550407Smckusick * Walk the list of ttys and create sessions for each active line. 94650407Smckusick */ 94750394Smckusick state_func_t 94850394Smckusick read_ttys() 9491029Sbill { 95050394Smckusick int session_index = 0; 95150394Smckusick register session_t *sp, *snext; 95250394Smckusick register struct ttyent *typ; 95350394Smckusick 95450394Smckusick /* 95550394Smckusick * Destroy any previous session state. 95650394Smckusick * There shouldn't be any, but just in case... 95750394Smckusick */ 95850394Smckusick for (sp = sessions; sp; sp = snext) { 95950394Smckusick if (sp->se_process) 96050394Smckusick clear_session_logs(sp); 96150394Smckusick snext = sp->se_next; 96250394Smckusick free_session(sp); 9631029Sbill } 96450394Smckusick sessions = 0; 96550407Smckusick if (start_session_db()) 96650407Smckusick return (state_func_t) single_user; 96750394Smckusick 96850394Smckusick /* 96950394Smckusick * Allocate a session entry for each active port. 97050394Smckusick * Note that sp starts at 0. 97150394Smckusick */ 97250394Smckusick while (typ = getttyent()) 97350394Smckusick if (snext = new_session(sp, ++session_index, typ)) 97450394Smckusick sp = snext; 97550394Smckusick 97650394Smckusick endttyent(); 97750394Smckusick 97850394Smckusick logger_enable = 1; 97950394Smckusick return (state_func_t) multi_user; 9801029Sbill } 9811029Sbill 98250407Smckusick /* 98350407Smckusick * Start a window system running. 98450407Smckusick */ 98542407Smarc void 98650394Smckusick start_window_system(sp) 98750394Smckusick session_t *sp; 9881029Sbill { 98950394Smckusick pid_t pid; 99050394Smckusick sigset_t mask; 99150394Smckusick 99250394Smckusick if ((pid = fork()) == -1) { 99350394Smckusick emergency("can't fork for window system on port %s: %m", 99450394Smckusick sp->se_device); 99550394Smckusick /* hope that getty fails and we can try again */ 99650394Smckusick return; 99750394Smckusick } 99850394Smckusick 99950394Smckusick if (pid) 100050394Smckusick return; 100150394Smckusick 100250394Smckusick sigemptyset(&mask); 100350394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 100450394Smckusick 100550407Smckusick if (setsid() < 0) 100650407Smckusick emergency("setsid failed (window) %m"); 100750407Smckusick 100850394Smckusick execv(sp->se_window_argv[0], sp->se_window_argv); 100950394Smckusick stall("can't exec window system '%s' for port %s: %m", 101050394Smckusick sp->se_window_argv[0], sp->se_device); 101150394Smckusick _exit(1); 10121029Sbill } 10132821Swnj 101450407Smckusick /* 101550407Smckusick * Start a login session running. 101650407Smckusick */ 101750394Smckusick pid_t 101850394Smckusick start_getty(sp) 101950394Smckusick session_t *sp; 102050394Smckusick { 102150394Smckusick pid_t pid; 102250394Smckusick sigset_t mask; 102350394Smckusick time_t current_time = time((time_t *) 0); 102413021Ssam 102550394Smckusick /* 102650394Smckusick * fork(), not vfork() -- we can't afford to block. 102750394Smckusick */ 102850394Smckusick if ((pid = fork()) == -1) { 102950394Smckusick emergency("can't fork for getty on port %s: %m", sp->se_device); 103050394Smckusick return -1; 103150394Smckusick } 103250394Smckusick 103350394Smckusick if (pid) 103450394Smckusick return pid; 103550394Smckusick 103650394Smckusick if (current_time > sp->se_started && 103750394Smckusick current_time - sp->se_started < GETTY_SPACING) { 103850394Smckusick warning("getty repeating too quickly on port %s, sleeping", 103950394Smckusick sp->se_device); 104050394Smckusick sleep((unsigned) GETTY_SPACING - 104150394Smckusick (current_time - sp->se_started)); 104250394Smckusick } 104350394Smckusick 104450394Smckusick if (sp->se_window) { 104550394Smckusick start_window_system(sp); 104650394Smckusick sleep(WINDOW_WAIT); 104750394Smckusick } 104850394Smckusick 104950394Smckusick setctty(sp->se_device); 105050394Smckusick 105150394Smckusick sigemptyset(&mask); 105250394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 105350394Smckusick 105450394Smckusick execv(sp->se_getty_argv[0], sp->se_getty_argv); 105550394Smckusick stall("can't exec getty '%s' for port %s: %m", 105650394Smckusick sp->se_getty_argv[0], sp->se_device); 105750394Smckusick _exit(1); 105813021Ssam } 105913021Ssam 106050407Smckusick /* 106150407Smckusick * Collect exit status for a child. 106250407Smckusick * If an exiting login, start a new login running. 106350407Smckusick */ 106442407Smarc void 106550407Smckusick collect_child(pid) 106650407Smckusick pid_t pid; 10672821Swnj { 106850394Smckusick register session_t *sp, *sprev, *snext; 10692821Swnj 107050407Smckusick if (! sessions) 107150407Smckusick return; 107250394Smckusick 107350407Smckusick if (! (sp = find_session(pid))) 107450407Smckusick return; 107550394Smckusick 107650407Smckusick clear_session_logs(sp); 107750407Smckusick del_session(sp); 107850407Smckusick sp->se_process = 0; 107950394Smckusick 108050407Smckusick if (sp->se_flags & SE_SHUTDOWN) { 108150407Smckusick if (sprev = sp->se_prev) 108250407Smckusick sprev->se_next = sp->se_next; 108350407Smckusick else 108450407Smckusick sessions = sp->se_next; 108550407Smckusick if (snext = sp->se_next) 108650407Smckusick snext->se_prev = sp->se_prev; 108750407Smckusick free_session(sp); 108850407Smckusick return; 108950407Smckusick } 109050394Smckusick 109150407Smckusick if ((pid = start_getty(sp)) == -1) { 109250407Smckusick /* serious trouble */ 109350407Smckusick requested_transition = clean_ttys; 109450407Smckusick return; 10952821Swnj } 109650394Smckusick 109750407Smckusick sp->se_process = pid; 109850407Smckusick sp->se_started = time((time_t *) 0); 109950407Smckusick add_session(sp); 11002821Swnj } 110118542Sralph 110250407Smckusick /* 110350407Smckusick * Catch a signal and request a state transition. 110450407Smckusick */ 110550394Smckusick void 110650394Smckusick transition_handler(sig) 110750394Smckusick int sig; 110818542Sralph { 110950539Strent 111050394Smckusick switch (sig) { 111150394Smckusick case SIGHUP: 111250394Smckusick requested_transition = clean_ttys; 111350394Smckusick break; 111450394Smckusick case SIGTERM: 111550394Smckusick requested_transition = death; 111650394Smckusick break; 111750394Smckusick case SIGTSTP: 111850394Smckusick requested_transition = catatonia; 111950394Smckusick break; 112050394Smckusick default: 112150394Smckusick requested_transition = 0; 112250394Smckusick break; 112318542Sralph } 112418542Sralph } 112518542Sralph 112650407Smckusick /* 112750407Smckusick * Take the system multiuser. 112850407Smckusick */ 112950394Smckusick state_func_t 113050394Smckusick multi_user() 113118542Sralph { 113250394Smckusick pid_t pid; 113350394Smckusick register session_t *sp; 113418542Sralph 113550394Smckusick requested_transition = 0; 113650394Smckusick logger_enable = 1; 113722181Skarels 113858422Smckusick /* 113958422Smckusick * If the administrator has not set the security level to -1 114058422Smckusick * to indicate that the kernel should not run multiuser in secure 114158422Smckusick * mode, and the run script has not set a higher level of security 114258422Smckusick * than level 1, then put the kernel into secure mode. 114358422Smckusick */ 114458422Smckusick if (getsecuritylevel() == 0) 114558422Smckusick setsecuritylevel(1); 114658422Smckusick 114750407Smckusick for (sp = sessions; sp; sp = sp->se_next) { 114850407Smckusick if (sp->se_process) 114950407Smckusick continue; 115050407Smckusick if ((pid = start_getty(sp)) == -1) { 115150407Smckusick /* serious trouble */ 115250407Smckusick requested_transition = clean_ttys; 115350407Smckusick break; 115422181Skarels } 115550407Smckusick sp->se_process = pid; 115650407Smckusick sp->se_started = time((time_t *) 0); 115750407Smckusick add_session(sp); 115850407Smckusick } 115950394Smckusick 116050394Smckusick while (!requested_transition) 116150407Smckusick if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 116250407Smckusick collect_child(pid); 116350394Smckusick 116450394Smckusick return (state_func_t) requested_transition; 116518542Sralph } 116618542Sralph 116750394Smckusick /* 116850394Smckusick * This is an n-squared algorithm. We hope it isn't run often... 116950394Smckusick */ 117050394Smckusick state_func_t 117150394Smckusick clean_ttys() 117218542Sralph { 117350394Smckusick register session_t *sp, *sprev; 117450394Smckusick register struct ttyent *typ; 117550394Smckusick register int session_index = 0; 117652954Smckusick register int devlen; 117718542Sralph 117850394Smckusick if (! sessions) 117950394Smckusick return (state_func_t) multi_user; 118050394Smckusick 118153151Storek devlen = sizeof(_PATH_DEV) - 1; 118250394Smckusick while (typ = getttyent()) { 118350394Smckusick ++session_index; 118450394Smckusick 118550394Smckusick for (sp = sessions; sp; sprev = sp, sp = sp->se_next) 118653151Storek if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 118718542Sralph break; 118850394Smckusick 118950394Smckusick if (sp) { 119050394Smckusick if (sp->se_index != session_index) { 119150394Smckusick warning("port %s changed utmp index from %d to %d", 119250394Smckusick sp->se_device, sp->se_index, 119350394Smckusick session_index); 119450394Smckusick sp->se_index = session_index; 119518542Sralph } 119650394Smckusick if (typ->ty_status & TTY_ON) 119750394Smckusick sp->se_flags &= ~SE_SHUTDOWN; 119850394Smckusick else { 119950394Smckusick sp->se_flags |= SE_SHUTDOWN; 120050394Smckusick kill(sp->se_process, SIGHUP); 120150394Smckusick } 120250394Smckusick continue; 120318542Sralph } 120450394Smckusick 120550394Smckusick new_session(sprev, session_index, typ); 120618542Sralph } 120750394Smckusick 120850394Smckusick endttyent(); 120950394Smckusick 121050394Smckusick return (state_func_t) multi_user; 121118542Sralph } 121250394Smckusick 121350407Smckusick /* 121450407Smckusick * Block further logins. 121550407Smckusick */ 121650394Smckusick state_func_t 121750394Smckusick catatonia() 121850394Smckusick { 121950394Smckusick register session_t *sp; 122050394Smckusick 122150394Smckusick for (sp = sessions; sp; sp = sp->se_next) 122250394Smckusick sp->se_flags |= SE_SHUTDOWN; 122350394Smckusick 122450394Smckusick return (state_func_t) multi_user; 122550394Smckusick } 122650394Smckusick 122750407Smckusick /* 122850407Smckusick * Note SIGALRM. 122950407Smckusick */ 123050394Smckusick void 123150394Smckusick alrm_handler(sig) 123250394Smckusick int sig; 123350394Smckusick { 123450394Smckusick clang = 1; 123550394Smckusick } 123650394Smckusick 123750407Smckusick /* 123850407Smckusick * Bring the system down to single user. 123950407Smckusick */ 124050394Smckusick state_func_t 124150394Smckusick death() 124250394Smckusick { 124350394Smckusick register session_t *sp; 124450394Smckusick register int i; 124550407Smckusick pid_t pid; 124650394Smckusick static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 124750394Smckusick 124850394Smckusick for (sp = sessions; sp; sp = sp->se_next) 124950394Smckusick sp->se_flags |= SE_SHUTDOWN; 125050394Smckusick 125150430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 125250430Smckusick logwtmp("~", "shutdown", ""); 125350394Smckusick logger_enable = 0; 125450394Smckusick 125550394Smckusick for (i = 0; i < 3; ++i) { 125650394Smckusick if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 125750394Smckusick return (state_func_t) single_user; 125850394Smckusick 125950394Smckusick clang = 0; 126050394Smckusick alarm(DEATH_WATCH); 126150394Smckusick do 126250407Smckusick if ((pid = waitpid(-1, (int *)0, 0)) != -1) 126350407Smckusick collect_child(pid); 126450407Smckusick while (clang == 0 && errno != ECHILD); 126550394Smckusick 126650407Smckusick if (errno == ECHILD) 126750394Smckusick return (state_func_t) single_user; 126850394Smckusick } 126950394Smckusick 127050394Smckusick warning("some processes wouldn't die"); 127150394Smckusick 127250394Smckusick return (state_func_t) single_user; 127350394Smckusick } 1274