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*53151Storek static char sccsid[] = "@(#)init.c 6.10 (Berkeley) 04/10/92"; 1950394Smckusick #endif /* not lint */ 2050394Smckusick 211029Sbill #include <sys/types.h> 2250394Smckusick #include <sys/wait.h> 2350394Smckusick #include <db.h> 242821Swnj #include <errno.h> 2550394Smckusick #include <fcntl.h> 2650394Smckusick #include <signal.h> 2750394Smckusick #include <syslog.h> 2850394Smckusick #include <time.h> 2916452Sroot #include <ttyent.h> 3050394Smckusick #include <unistd.h> 3152795Sbostic #include <stdio.h> 3252795Sbostic #include <stdlib.h> 3352795Sbostic #include <string.h> 3450394Smckusick 3550394Smckusick #ifdef __STDC__ 3650394Smckusick #include <stdarg.h> 3750394Smckusick #else 3850394Smckusick #include <varargs.h> 3950394Smckusick #endif 4050394Smckusick 4150394Smckusick #ifdef SECURE 4250394Smckusick #include <pwd.h> 4350394Smckusick #endif 4450394Smckusick 4537284Sbostic #include "pathnames.h" 461029Sbill 4750394Smckusick /* 4850394Smckusick * Until the mythical util.h arrives... 4950394Smckusick */ 5050394Smckusick extern int login_tty __P((int)); 5150394Smckusick extern int logout __P((const char *)); 5250394Smckusick extern void logwtmp __P((const char *, const char *, const char *)); 531029Sbill 5450394Smckusick /* 5550394Smckusick * Sleep times; used to prevent thrashing. 5650394Smckusick */ 5750407Smckusick #define GETTY_SPACING 10 /* fork getty on a port every N secs */ 5850394Smckusick #define WINDOW_WAIT 3 /* wait N secs after starting window */ 5950394Smckusick #define STALL_TIMEOUT 30 /* wait N secs after warning */ 6050407Smckusick #define DEATH_WATCH 10 /* wait N secs for procs to die */ 611029Sbill 6250394Smckusick void handle __P((sig_t, ...)); 6350394Smckusick void delset __P((sigset_t *, ...)); 641029Sbill 6550394Smckusick void stall __P((char *, ...)); 6650394Smckusick void warning __P((char *, ...)); 6750394Smckusick void emergency __P((char *, ...)); 6850394Smckusick void disaster __P((int)); 691029Sbill 7050394Smckusick /* 7150394Smckusick * We really need a recursive typedef... 7250394Smckusick * The following at least guarantees that the return type of (*state_t)() 7350394Smckusick * is sufficiently wide to hold a function pointer. 7450394Smckusick */ 7550394Smckusick typedef long (*state_func_t) __P((void)); 7650394Smckusick typedef state_func_t (*state_t) __P((void)); 771029Sbill 7850394Smckusick state_func_t single_user __P((void)); 7950394Smckusick state_func_t runcom __P((void)); 8050394Smckusick state_func_t read_ttys __P((void)); 8150394Smckusick state_func_t multi_user __P((void)); 8250394Smckusick state_func_t clean_ttys __P((void)); 8350394Smckusick state_func_t catatonia __P((void)); 8450394Smckusick state_func_t death __P((void)); 8513021Ssam 8650394Smckusick enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 8750394Smckusick 8850394Smckusick void transition __P((state_t)); 8950394Smckusick state_t requested_transition = runcom; 9050394Smckusick 9150394Smckusick void setctty __P((char *)); 9250394Smckusick 9350394Smckusick typedef struct session { 9450394Smckusick int se_index; /* index of entry in ttys file */ 9550394Smckusick pid_t se_process; /* controlling process */ 9650394Smckusick time_t se_started; /* used to avoid thrashing */ 9750394Smckusick int se_flags; /* status of session */ 9850394Smckusick #define SE_SHUTDOWN 0x1 /* session won't be restarted */ 9950394Smckusick char *se_device; /* filename of port */ 10050394Smckusick char *se_getty; /* what to run on that port */ 10150394Smckusick char **se_getty_argv; /* pre-parsed argument array */ 10250394Smckusick char *se_window; /* window system (started only once) */ 10350394Smckusick char **se_window_argv; /* pre-parsed argument array */ 10450394Smckusick struct session *se_prev; 10550394Smckusick struct session *se_next; 10650394Smckusick } session_t; 10750394Smckusick 10850394Smckusick void free_session __P((session_t *)); 10950394Smckusick session_t *new_session __P((session_t *, int, struct ttyent *)); 11050394Smckusick session_t *sessions; 11150394Smckusick 11250394Smckusick char **construct_argv __P((char *)); 11350394Smckusick void start_window_system __P((session_t *)); 11450407Smckusick void collect_child __P((int)); 11550394Smckusick pid_t start_getty __P((session_t *)); 11650394Smckusick void transition_handler __P((int)); 11750394Smckusick void alrm_handler __P((int)); 11850394Smckusick int clang; 11950394Smckusick 12050394Smckusick int start_logger __P((void)); 12150394Smckusick void clear_session_logs __P((session_t *)); 12250394Smckusick int logger_enable; 12350394Smckusick 12450407Smckusick int start_session_db __P((void)); 12550394Smckusick void add_session __P((session_t *)); 12650394Smckusick void del_session __P((session_t *)); 12750394Smckusick session_t *find_session __P((pid_t)); 12850394Smckusick DB *session_db; 12950394Smckusick 13050407Smckusick /* 13150407Smckusick * The mother of all processes. 13250407Smckusick */ 13350394Smckusick int 13443633Skarels main(argc, argv) 13550394Smckusick int argc; 13643633Skarels char **argv; 1371029Sbill { 13850394Smckusick int c; 13950407Smckusick struct sigaction sa; 14050394Smckusick sigset_t mask; 14147659Skarels 1429869Spugs 14352795Sbostic /* Dispose of random users. */ 14452795Sbostic if (getuid() != 0) { 14552795Sbostic (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); 14652795Sbostic exit (1); 14752795Sbostic } 14852795Sbostic 14952795Sbostic /* System V users like to reexec init. */ 15052795Sbostic if (getpid() != 1) { 15152795Sbostic (void)fprintf(stderr, "init: already running\n"); 15252795Sbostic exit (1); 15352795Sbostic } 15452795Sbostic 15550394Smckusick /* 15650394Smckusick * Note that this does NOT open a file... 15750394Smckusick * Does 'init' deserve its own facility number? 15850394Smckusick */ 15950407Smckusick openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 16050394Smckusick 16150394Smckusick /* 16250407Smckusick * Create an initial session. 16350407Smckusick */ 16450407Smckusick if (setsid() < 0) 16550407Smckusick syslog(LOG_ERR, "setsid failed (initial) %m"); 16650407Smckusick 16750407Smckusick /* 16850394Smckusick * This code assumes that we always get arguments through flags, 16950394Smckusick * never through bits set in some random machine register. 17050394Smckusick */ 17150394Smckusick while ((c = getopt(argc, argv, "sf")) != -1) 17250394Smckusick switch (c) { 17350394Smckusick case 's': 17450394Smckusick requested_transition = single_user; 17550394Smckusick break; 17647659Skarels case 'f': 17750394Smckusick runcom_mode = FASTBOOT; 1789869Spugs break; 17950394Smckusick default: 18050394Smckusick warning("unrecognized flag '-%c'", c); 1819869Spugs break; 1829869Spugs } 18350394Smckusick 18450394Smckusick if (optind != argc) 18550394Smckusick warning("ignoring excess arguments"); 18650394Smckusick 18750394Smckusick /* 18850394Smckusick * We catch or block signals rather than ignore them, 18950394Smckusick * so that they get reset on exec. 19050394Smckusick */ 19150394Smckusick handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 19250394Smckusick SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, 0); 19350394Smckusick handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); 19450394Smckusick handle(alrm_handler, SIGALRM, 0); 19550394Smckusick sigfillset(&mask); 19650407Smckusick delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 19750407Smckusick SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); 19850394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 19950407Smckusick sigemptyset(&sa.sa_mask); 20050407Smckusick sa.sa_flags = 0; 20150407Smckusick sa.sa_handler = SIG_IGN; 20250407Smckusick (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); 20350407Smckusick (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); 20450394Smckusick 20550394Smckusick /* 20650394Smckusick * Paranoia. 20750394Smckusick */ 20850394Smckusick close(0); 20950394Smckusick close(1); 21050394Smckusick close(2); 21150394Smckusick 21250394Smckusick /* 21350394Smckusick * Start the state machine. 21450394Smckusick */ 21550394Smckusick transition(requested_transition); 21650394Smckusick 21750394Smckusick /* 21850394Smckusick * Should never reach here. 21950394Smckusick */ 22050394Smckusick return 1; 22150394Smckusick } 22250394Smckusick 22350407Smckusick /* 22450407Smckusick * Associate a function with a signal handler. 22550407Smckusick */ 22650394Smckusick void 22750394Smckusick #ifdef __STDC__ 22850394Smckusick handle(sig_t handler, ...) 22950394Smckusick #else 23050394Smckusick handle(va_alist) 23150394Smckusick va_dcl 23250394Smckusick #endif 23350394Smckusick { 23450394Smckusick int sig; 23550394Smckusick struct sigaction sa; 23650394Smckusick int mask_everything; 23750394Smckusick va_list ap; 23850394Smckusick #ifndef __STDC__ 23950394Smckusick sig_t handler; 24050394Smckusick 24150394Smckusick va_start(ap); 24250394Smckusick handler = va_arg(ap, sig_t); 24350394Smckusick #else 24450394Smckusick va_start(ap, handler); 24550394Smckusick #endif 24650394Smckusick 24750394Smckusick sa.sa_handler = handler; 24850394Smckusick sigfillset(&mask_everything); 24950394Smckusick 25050394Smckusick while (sig = va_arg(ap, int)) { 25150394Smckusick sa.sa_mask = mask_everything; 25250394Smckusick /* XXX SA_RESTART? */ 25350394Smckusick sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 25450394Smckusick sigaction(sig, &sa, (struct sigaction *) 0); 25547659Skarels } 2561029Sbill } 2571029Sbill 25850407Smckusick /* 25950407Smckusick * Delete a set of signals from a mask. 26050407Smckusick */ 26150394Smckusick void 26250394Smckusick #ifdef __STDC__ 26350394Smckusick delset(sigset_t *maskp, ...) 26450394Smckusick #else 26550394Smckusick delset(va_alist) 26650394Smckusick va_dcl 26750394Smckusick #endif 26850394Smckusick { 26950394Smckusick int sig; 27050394Smckusick va_list ap; 27150394Smckusick #ifndef __STDC__ 27250394Smckusick sigset_t *maskp; 2731429Sbill 27450394Smckusick va_start(ap); 27550394Smckusick maskp = va_arg(ap, sigset_t *); 27650394Smckusick #else 27750394Smckusick va_start(ap, maskp); 27850394Smckusick #endif 27950394Smckusick 28050394Smckusick while (sig = va_arg(ap, int)) 28150394Smckusick sigdelset(maskp, sig); 28250394Smckusick } 28350394Smckusick 28450394Smckusick /* 28550394Smckusick * Log a message and sleep for a while (to give someone an opportunity 28650394Smckusick * to read it and to save log or hardcopy output if the problem is chronic). 28750430Smckusick * NB: should send a message to the session logger to avoid blocking. 28850394Smckusick */ 28950394Smckusick void 29050394Smckusick #ifdef __STDC__ 29150394Smckusick stall(char *message, ...) 29250394Smckusick #else 29350394Smckusick stall(va_alist) 29450394Smckusick va_dcl 29550394Smckusick #endif 2961029Sbill { 29750394Smckusick pid_t pid; 29850394Smckusick va_list ap; 29950394Smckusick #ifndef __STDC__ 30050394Smckusick char *message; 3011029Sbill 30250394Smckusick va_start(ap); 30350394Smckusick message = va_arg(ap, char *); 30450394Smckusick #else 30550394Smckusick va_start(ap, message); 30650394Smckusick #endif 30750394Smckusick 30850430Smckusick vsyslog(LOG_ALERT, message, ap); 30950394Smckusick va_end(ap); 31050394Smckusick sleep(STALL_TIMEOUT); 3111429Sbill } 3121429Sbill 31350394Smckusick /* 31450394Smckusick * Like stall(), but doesn't sleep. 31550394Smckusick * If cpp had variadic macros, the two functions could be #defines for another. 31650430Smckusick * NB: should send a message to the session logger to avoid blocking. 31750394Smckusick */ 31842407Smarc void 31950394Smckusick #ifdef __STDC__ 32050394Smckusick warning(char *message, ...) 32150394Smckusick #else 32250394Smckusick warning(va_alist) 32350394Smckusick va_dcl 32450394Smckusick #endif 3251429Sbill { 32650394Smckusick va_list ap; 32750394Smckusick #ifndef __STDC__ 32850394Smckusick char *message; 3291429Sbill 33050394Smckusick va_start(ap); 33150394Smckusick message = va_arg(ap, char *); 33250394Smckusick #else 33350394Smckusick va_start(ap, message); 33450394Smckusick #endif 33550394Smckusick 33650430Smckusick vsyslog(LOG_ALERT, message, ap); 33750394Smckusick va_end(ap); 3381429Sbill } 3391429Sbill 34050394Smckusick /* 34150430Smckusick * Log an emergency message. 34250430Smckusick * NB: should send a message to the session logger to avoid blocking. 34350394Smckusick */ 34450394Smckusick void 34550394Smckusick #ifdef __STDC__ 34650394Smckusick emergency(char *message, ...) 34750394Smckusick #else 34850394Smckusick emergency(va_alist) 34950394Smckusick va_dcl 35050394Smckusick #endif 3511429Sbill { 35250394Smckusick va_list ap; 35350394Smckusick #ifndef __STDC__ 35450394Smckusick char *message; 3551429Sbill 35650394Smckusick va_start(ap); 35750394Smckusick message = va_arg(ap, char *); 35850394Smckusick #else 35950394Smckusick va_start(ap, message); 36050394Smckusick #endif 36150394Smckusick 36250394Smckusick vsyslog(LOG_EMERG, message, ap); 36350394Smckusick va_end(ap); 3641029Sbill } 3651029Sbill 36650407Smckusick /* 36750407Smckusick * Catch an unexpected signal. 36850407Smckusick */ 36950394Smckusick void 37050394Smckusick disaster(sig) 37150394Smckusick int sig; 3721029Sbill { 37350394Smckusick emergency("fatal signal: %s", 37450394Smckusick sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); 3751029Sbill 37650394Smckusick sleep(STALL_TIMEOUT); 37750394Smckusick _exit(sig); /* reboot */ 3781029Sbill } 3791029Sbill 38050394Smckusick /* 38150394Smckusick * Change states in the finite state machine. 38250394Smckusick * The initial state is passed as an argument. 38350394Smckusick */ 38450394Smckusick void 38550394Smckusick transition(s) 38650394Smckusick state_t s; 3871029Sbill { 38850394Smckusick for (;;) 38950394Smckusick s = (state_t) (*s)(); 39050394Smckusick } 3911029Sbill 39250394Smckusick /* 39350394Smckusick * We send requests for session logging to another process for two reasons. 39450394Smckusick * First, we don't want to block if the log files go away (e.g. because 39550394Smckusick * one or more are on hard-mounted NFS systems whose server crashes). 39650394Smckusick * Second, despite all the crud already contained in init, it still isn't 39750394Smckusick * right that init should care about session logging record formats and files. 39850394Smckusick * We could use explicit 'Unix' IPC for this, but let's try to be POSIX... 39950394Smckusick */ 40050394Smckusick int 40150394Smckusick start_logger() 40250394Smckusick { 40350394Smckusick static char *argv[] = { _PATH_SLOGGER, 0 }; 40450394Smckusick int fd, pfd[2]; 40550394Smckusick pid_t pid; 40650394Smckusick sigset_t mask; 40750394Smckusick 40850394Smckusick if (pipe(pfd) == -1) { 40950394Smckusick warning("session logging disabled: can't make pipe to %s: %m", 41050394Smckusick argv[0]); 41150394Smckusick return -1; 41250394Smckusick } 41350394Smckusick if ((pid = fork()) == -1) { 41450394Smckusick emergency("session logging disabled: can't fork for %s: %m", 41550394Smckusick argv[0]); 41650394Smckusick return -1; 41750394Smckusick } 41850394Smckusick 41913021Ssam if (pid == 0) { 42050394Smckusick close(pfd[1]); 42150394Smckusick if (pfd[0] != 0) { 42250394Smckusick dup2(pfd[0], 0); 42350394Smckusick close(pfd[0]); 42450394Smckusick } 42550394Smckusick if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != -1) { 42650394Smckusick if (fd != 1) 42750394Smckusick dup2(fd, 1); 42850394Smckusick if (fd != 2) 42950394Smckusick dup2(fd, 2); 43050394Smckusick if (fd != 1 && fd != 2) 43144284Skarels close(fd); 43250394Smckusick } else { 43350394Smckusick /* paranoid */ 43450394Smckusick close(1); 43550394Smckusick close(2); 43644284Skarels } 43750394Smckusick sigemptyset(&mask); 43850394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 43950394Smckusick execv(argv[0], argv); 44050394Smckusick stall("can't exec %s: %m", argv[0]); 44150394Smckusick _exit(1); 4421029Sbill } 44350394Smckusick 44450394Smckusick close(pfd[0]); 44550394Smckusick fcntl(pfd[1], F_SETFD, FD_CLOEXEC); 44650394Smckusick fcntl(pfd[1], F_SETFL, O_NONBLOCK); 44750394Smckusick 44850394Smckusick return pfd[1]; 4491029Sbill } 4501029Sbill 45150407Smckusick /* 45250407Smckusick * Close out the accounting files for a login session. 45350430Smckusick * NB: should send a message to the session logger to avoid blocking. 45450407Smckusick */ 45550394Smckusick void 45650394Smckusick clear_session_logs(sp) 45750394Smckusick session_t *sp; 45850394Smckusick { 459*53151Storek char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 460*53151Storek 461*53151Storek if (logout(line)) 462*53151Storek logwtmp(line, "", ""); 46350394Smckusick } 46450394Smckusick 46513021Ssam /* 46650394Smckusick * Start a session and allocate a controlling terminal. 46750394Smckusick * Only called by children of init after forking. 46813021Ssam */ 46950394Smckusick void 47050394Smckusick setctty(name) 47150394Smckusick char *name; 4721029Sbill { 47350394Smckusick int fd; 4741029Sbill 47550407Smckusick (void) revoke(name); 47650394Smckusick if ((fd = open(name, O_RDWR)) == -1) { 47750394Smckusick stall("can't open %s: %m", name); 47850394Smckusick _exit(1); 4791029Sbill } 48050394Smckusick if (login_tty(fd) == -1) { 48150394Smckusick stall("can't get %s for controlling terminal: %m", name); 48250394Smckusick _exit(1); 48350394Smckusick } 4841029Sbill } 4851029Sbill 48650407Smckusick /* 48750407Smckusick * Bring the system up single user. 48850407Smckusick */ 48950394Smckusick state_func_t 49050394Smckusick single_user() 49113021Ssam { 49250394Smckusick pid_t pid, wpid; 49350394Smckusick int status; 49450394Smckusick sigset_t mask; 49550394Smckusick char *shell = _PATH_BSHELL; 49650394Smckusick char *argv[2]; 49750394Smckusick #ifdef SECURE 49850394Smckusick struct ttyent *typ; 49950394Smckusick struct passwd *pp; 50050394Smckusick static const char banner[] = 50150394Smckusick "Enter root password, or ^D to go multi-user\n"; 50250394Smckusick char *password; 50350394Smckusick #endif 50413021Ssam 50550394Smckusick if ((pid = fork()) == 0) { 50650394Smckusick /* 50750394Smckusick * Start the single user session. 50850394Smckusick */ 50950394Smckusick setctty(_PATH_CONSOLE); 51050394Smckusick 51150394Smckusick #ifdef SECURE 51250394Smckusick /* 51350394Smckusick * Check the root password. 51450394Smckusick * We don't care if the console is 'on' by default; 51550394Smckusick * it's the only tty that can be 'off' and 'secure'. 51650394Smckusick */ 51750394Smckusick typ = getttynam("console"); 51850394Smckusick pp = getpwnam("root"); 51950394Smckusick if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { 52050394Smckusick write(2, banner, sizeof banner - 1); 52150394Smckusick for (;;) { 52250394Smckusick password = getpass("Password:"); 52350394Smckusick if (password == 0 || *password == '\0') 52450394Smckusick _exit(0); 52552954Smckusick password = crypt(password, pp->pw_passwd); 52650394Smckusick if (strcmp(password, pp->pw_passwd) == 0) 52750394Smckusick break; 52813021Ssam } 52913021Ssam } 53050394Smckusick #if 0 53150394Smckusick /* 53250394Smckusick * Make the single-user shell be root's standard shell? 53350394Smckusick */ 53450394Smckusick if (pp && pp->pw_shell) 53550394Smckusick shell = pp->pw_shell; 53650394Smckusick #endif 53750394Smckusick endttyent(); 53850394Smckusick endpwent(); 53950394Smckusick #endif 54018542Sralph 54123147Sbloom /* 54250394Smckusick * Unblock signals. 54350394Smckusick * We catch all the interesting ones, 54450394Smckusick * and those are reset to SIG_DFL on exec. 54523147Sbloom */ 54650394Smckusick sigemptyset(&mask); 54750394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 54850394Smckusick 54923147Sbloom /* 55050394Smckusick * Fire off a shell. 55150394Smckusick * If the default one doesn't work, try the Bourne shell. 55223147Sbloom */ 55350394Smckusick argv[0] = "-sh"; 55450394Smckusick argv[1] = 0; 55550394Smckusick execv(shell, argv); 55650394Smckusick emergency("can't exec %s for single user: %m", shell); 55750394Smckusick execv(_PATH_BSHELL, argv); 55850394Smckusick emergency("can't exec %s for single user: %m", _PATH_BSHELL); 55950394Smckusick sleep(STALL_TIMEOUT); 56050394Smckusick _exit(1); 56150394Smckusick } 56223147Sbloom 56350394Smckusick if (pid == -1) { 56450394Smckusick /* 56550394Smckusick * We are seriously hosed. Do our best. 56650394Smckusick */ 56750394Smckusick emergency("can't fork single-user shell, trying again"); 56850394Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 56952219Storek continue; 57050394Smckusick return (state_func_t) single_user; 57150394Smckusick } 57250394Smckusick 57350539Strent requested_transition = 0; 57450407Smckusick do { 57550407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 57650407Smckusick collect_child(wpid); 57750407Smckusick if (wpid == -1) { 57850407Smckusick if (errno == EINTR) 57950407Smckusick continue; 58050394Smckusick warning("wait for single-user shell failed: %m; restarting"); 58150394Smckusick return (state_func_t) single_user; 58223147Sbloom } 58350394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 58450394Smckusick warning("init: shell stopped, restarting\n"); 58550394Smckusick kill(pid, SIGCONT); 58650407Smckusick wpid = -1; 58750394Smckusick } 58850539Strent } while (wpid != pid && !requested_transition); 58950394Smckusick 59050539Strent if (requested_transition) 59150539Strent return (state_func_t) requested_transition; 59250539Strent 59350394Smckusick if (!WIFEXITED(status)) { 59450538Strent if (WTERMSIG(status) == SIGKILL) { 59550538Strent /* 59650538Strent * reboot(8) killed shell? 59750538Strent */ 59850538Strent warning("single user shell terminated."); 59950538Strent sleep(STALL_TIMEOUT); 60050538Strent _exit(0); 60150538Strent } else { 60250538Strent warning("single user shell terminated, restarting"); 60350538Strent return (state_func_t) single_user; 60450538Strent } 60550394Smckusick } 60650394Smckusick 60750394Smckusick runcom_mode = FASTBOOT; 60850394Smckusick return (state_func_t) runcom; 60950394Smckusick } 61050394Smckusick 61150407Smckusick /* 61250407Smckusick * Run the system startup script. 61350407Smckusick */ 61450394Smckusick state_func_t 61550394Smckusick runcom() 61650394Smckusick { 61750394Smckusick pid_t pid, wpid; 61850394Smckusick int status; 61950394Smckusick char *argv[4]; 62050407Smckusick struct sigaction sa; 62150394Smckusick 62250394Smckusick if ((pid = fork()) == 0) { 62350407Smckusick sigemptyset(&sa.sa_mask); 62450407Smckusick sa.sa_flags = 0; 62550407Smckusick sa.sa_handler = SIG_IGN; 62650407Smckusick (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); 62750407Smckusick (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); 62850407Smckusick 62950394Smckusick setctty(_PATH_CONSOLE); 63050394Smckusick 63150394Smckusick argv[0] = "sh"; 63250394Smckusick argv[1] = _PATH_RUNCOM; 63350394Smckusick argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; 63450394Smckusick argv[3] = 0; 63550394Smckusick 63650407Smckusick sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 63750394Smckusick 63850394Smckusick execv(_PATH_BSHELL, argv); 63950394Smckusick stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 64050394Smckusick _exit(1); /* force single user mode */ 64150394Smckusick } 64250394Smckusick 64350394Smckusick if (pid == -1) { 64450394Smckusick emergency("can't fork for %s on %s: %m", 64550394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 64650407Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 64752219Storek continue; 64850394Smckusick sleep(STALL_TIMEOUT); 64950394Smckusick return (state_func_t) single_user; 65050394Smckusick } 65150394Smckusick 65250394Smckusick /* 65350407Smckusick * Copied from single_user(). This is a bit paranoid. 65450394Smckusick */ 65550407Smckusick do { 65650407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 65750407Smckusick collect_child(wpid); 65850407Smckusick if (wpid == -1) { 65950407Smckusick if (errno == EINTR) 66050407Smckusick continue; 66150394Smckusick warning("wait for %s on %s failed: %m; going to single user mode", 66250394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 66350394Smckusick return (state_func_t) single_user; 66413021Ssam } 66550394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 66650394Smckusick warning("init: %s on %s stopped, restarting\n", 66750394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 66850394Smckusick kill(pid, SIGCONT); 66950407Smckusick wpid = -1; 67050394Smckusick } 67150407Smckusick } while (wpid != pid); 67250394Smckusick 67350394Smckusick if (!WIFEXITED(status)) { 67450394Smckusick warning("%s on %s terminated abnormally, going to single user mode", 67550394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 67650394Smckusick return (state_func_t) single_user; 67750394Smckusick } 67850394Smckusick 67950394Smckusick if (WEXITSTATUS(status)) 68050394Smckusick return (state_func_t) single_user; 68150394Smckusick 68250394Smckusick runcom_mode = AUTOBOOT; /* the default */ 68350430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 68450430Smckusick logwtmp("~", "reboot", ""); 68550394Smckusick return (state_func_t) read_ttys; 68613021Ssam } 68713021Ssam 68850394Smckusick /* 68950407Smckusick * Open the session database. 69050407Smckusick * 69150407Smckusick * NB: We could pass in the size here; is it necessary? 69250394Smckusick */ 69350407Smckusick int 69450394Smckusick start_session_db() 6951029Sbill { 69650407Smckusick if (session_db && (*session_db->close)(session_db)) 69750407Smckusick emergency("session database close: %s", strerror(errno)); 69851621Sbostic if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 69950407Smckusick emergency("session database open: %s", strerror(errno)); 70050407Smckusick return (1); 70150407Smckusick } 70250407Smckusick return (0); 70350407Smckusick 70450394Smckusick } 7051029Sbill 70650407Smckusick /* 70750407Smckusick * Add a new login session. 70850407Smckusick */ 70950394Smckusick void 71050394Smckusick add_session(sp) 71150394Smckusick session_t *sp; 71250394Smckusick { 71350394Smckusick DBT key; 71450394Smckusick DBT data; 71550394Smckusick 71650394Smckusick key.data = &sp->se_process; 71750394Smckusick key.size = sizeof sp->se_process; 71850394Smckusick data.data = &sp; 71950394Smckusick data.size = sizeof sp; 72050394Smckusick 72151621Sbostic if ((*session_db->put)(session_db, &key, &data, 0)) 72250407Smckusick emergency("insert %d: %s", sp->se_process, strerror(errno)); 72350394Smckusick } 72450394Smckusick 72550407Smckusick /* 72650407Smckusick * Delete an old login session. 72750407Smckusick */ 72850394Smckusick void 72950394Smckusick del_session(sp) 73050394Smckusick session_t *sp; 73150394Smckusick { 73250394Smckusick DBT key; 73350394Smckusick 73450394Smckusick key.data = &sp->se_process; 73550394Smckusick key.size = sizeof sp->se_process; 73650394Smckusick 73750407Smckusick if ((*session_db->del)(session_db, &key, 0)) 73850407Smckusick emergency("delete %d: %s", sp->se_process, strerror(errno)); 73950394Smckusick } 74050394Smckusick 74150407Smckusick /* 74250407Smckusick * Look up a login session by pid. 74350407Smckusick */ 74450394Smckusick session_t * 74550394Smckusick #ifdef __STDC__ 74650394Smckusick find_session(pid_t pid) 74750394Smckusick #else 74850394Smckusick find_session(pid) 74950394Smckusick pid_t pid; 75050394Smckusick #endif 75150394Smckusick { 75250394Smckusick DBT key; 75350394Smckusick DBT data; 75452219Storek session_t *ret; 75550394Smckusick 75650394Smckusick key.data = &pid; 75750394Smckusick key.size = sizeof pid; 75850394Smckusick if ((*session_db->get)(session_db, &key, &data, 0) != 0) 75950394Smckusick return 0; 76052219Storek bcopy(data.data, (char *)&ret, sizeof(ret)); 76152219Storek return ret; 76250394Smckusick } 76350394Smckusick 76450407Smckusick /* 76550407Smckusick * Construct an argument vector from a command line. 76650407Smckusick */ 76750394Smckusick char ** 76850394Smckusick construct_argv(command) 76950394Smckusick char *command; 77050394Smckusick { 77150394Smckusick register int argc = 0; 77250394Smckusick register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 77350394Smckusick * sizeof (char *)); 77450394Smckusick static const char separators[] = " \t"; 77550394Smckusick 77650394Smckusick if ((argv[argc++] = strtok(command, separators)) == 0) 77750394Smckusick return 0; 77850394Smckusick while (argv[argc++] = strtok((char *) 0, separators)) 77952219Storek continue; 78050394Smckusick return argv; 78150394Smckusick } 78250394Smckusick 78350407Smckusick /* 78450407Smckusick * Deallocate a session descriptor. 78550407Smckusick */ 78650394Smckusick void 78750394Smckusick free_session(sp) 78850394Smckusick register session_t *sp; 78950394Smckusick { 79050394Smckusick free(sp->se_device); 79150394Smckusick free(sp->se_getty); 79250394Smckusick free(sp->se_getty_argv); 79350394Smckusick if (sp->se_window) { 79450394Smckusick free(sp->se_window); 79550394Smckusick free(sp->se_window_argv); 7961029Sbill } 79750394Smckusick free(sp); 7981029Sbill } 7991029Sbill 80050407Smckusick /* 80150407Smckusick * Allocate a new session descriptor. 80250407Smckusick */ 80350394Smckusick session_t * 80450394Smckusick new_session(sprev, session_index, typ) 80550394Smckusick session_t *sprev; 80650394Smckusick int session_index; 80750394Smckusick register struct ttyent *typ; 8081029Sbill { 80950394Smckusick register session_t *sp; 8101029Sbill 81150394Smckusick if ((typ->ty_status & TTY_ON) == 0 || 81250394Smckusick typ->ty_name == 0 || 81350394Smckusick typ->ty_getty == 0) 81450394Smckusick return 0; 81550394Smckusick 81650394Smckusick sp = (session_t *) malloc(sizeof (session_t)); 81750394Smckusick 81850394Smckusick sp->se_index = session_index; 81950394Smckusick sp->se_process = 0; 82050394Smckusick sp->se_started = 0; 82150394Smckusick sp->se_flags = 0; 82250394Smckusick sp->se_window = 0; 82350394Smckusick 824*53151Storek sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 825*53151Storek (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 82650394Smckusick 82750394Smckusick sp->se_getty = strdup(typ->ty_getty); 82850394Smckusick sp->se_getty_argv = construct_argv(sp->se_getty); 82950394Smckusick if (sp->se_getty_argv == 0) { 83050394Smckusick warning("can't parse getty for port %s", 83150394Smckusick sp->se_device); 83250394Smckusick free_session(sp); 83350394Smckusick return 0; 8345971Sroot } 83550394Smckusick if (typ->ty_window) { 83650394Smckusick sp->se_window = strdup(typ->ty_window); 83750394Smckusick sp->se_window_argv = construct_argv(sp->se_window); 83850394Smckusick if (sp->se_window_argv == 0) { 83950394Smckusick warning("can't parse window for port %s", 84050394Smckusick sp->se_device); 84150394Smckusick free_session(sp); 84250394Smckusick return 0; 8435971Sroot } 8441029Sbill } 84550394Smckusick 84650394Smckusick sp->se_next = 0; 84750394Smckusick if (sprev == 0) { 84850394Smckusick sessions = sp; 84950394Smckusick sp->se_prev = 0; 85050394Smckusick } else { 85150394Smckusick sprev->se_next = sp; 85250394Smckusick sp->se_prev = sprev; 85350394Smckusick } 85450394Smckusick 85550394Smckusick return sp; 8561029Sbill } 8571029Sbill 85850407Smckusick /* 85950407Smckusick * Walk the list of ttys and create sessions for each active line. 86050407Smckusick */ 86150394Smckusick state_func_t 86250394Smckusick read_ttys() 8631029Sbill { 86450394Smckusick int session_index = 0; 86550394Smckusick register session_t *sp, *snext; 86650394Smckusick register struct ttyent *typ; 86750394Smckusick 86850394Smckusick /* 86950394Smckusick * Destroy any previous session state. 87050394Smckusick * There shouldn't be any, but just in case... 87150394Smckusick */ 87250394Smckusick for (sp = sessions; sp; sp = snext) { 87350394Smckusick if (sp->se_process) 87450394Smckusick clear_session_logs(sp); 87550394Smckusick snext = sp->se_next; 87650394Smckusick free_session(sp); 8771029Sbill } 87850394Smckusick sessions = 0; 87950407Smckusick if (start_session_db()) 88050407Smckusick return (state_func_t) single_user; 88150394Smckusick 88250394Smckusick /* 88350394Smckusick * Allocate a session entry for each active port. 88450394Smckusick * Note that sp starts at 0. 88550394Smckusick */ 88650394Smckusick while (typ = getttyent()) 88750394Smckusick if (snext = new_session(sp, ++session_index, typ)) 88850394Smckusick sp = snext; 88950394Smckusick 89050394Smckusick endttyent(); 89150394Smckusick 89250394Smckusick logger_enable = 1; 89350394Smckusick return (state_func_t) multi_user; 8941029Sbill } 8951029Sbill 89650407Smckusick /* 89750407Smckusick * Start a window system running. 89850407Smckusick */ 89942407Smarc void 90050394Smckusick start_window_system(sp) 90150394Smckusick session_t *sp; 9021029Sbill { 90350394Smckusick pid_t pid; 90450394Smckusick sigset_t mask; 90550394Smckusick 90650394Smckusick if ((pid = fork()) == -1) { 90750394Smckusick emergency("can't fork for window system on port %s: %m", 90850394Smckusick sp->se_device); 90950394Smckusick /* hope that getty fails and we can try again */ 91050394Smckusick return; 91150394Smckusick } 91250394Smckusick 91350394Smckusick if (pid) 91450394Smckusick return; 91550394Smckusick 91650394Smckusick sigemptyset(&mask); 91750394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 91850394Smckusick 91950407Smckusick if (setsid() < 0) 92050407Smckusick emergency("setsid failed (window) %m"); 92150407Smckusick 92250394Smckusick execv(sp->se_window_argv[0], sp->se_window_argv); 92350394Smckusick stall("can't exec window system '%s' for port %s: %m", 92450394Smckusick sp->se_window_argv[0], sp->se_device); 92550394Smckusick _exit(1); 9261029Sbill } 9272821Swnj 92850407Smckusick /* 92950407Smckusick * Start a login session running. 93050407Smckusick */ 93150394Smckusick pid_t 93250394Smckusick start_getty(sp) 93350394Smckusick session_t *sp; 93450394Smckusick { 93550394Smckusick pid_t pid; 93650394Smckusick sigset_t mask; 93750394Smckusick time_t current_time = time((time_t *) 0); 93813021Ssam 93950394Smckusick /* 94050394Smckusick * fork(), not vfork() -- we can't afford to block. 94150394Smckusick */ 94250394Smckusick if ((pid = fork()) == -1) { 94350394Smckusick emergency("can't fork for getty on port %s: %m", sp->se_device); 94450394Smckusick return -1; 94550394Smckusick } 94650394Smckusick 94750394Smckusick if (pid) 94850394Smckusick return pid; 94950394Smckusick 95050394Smckusick if (current_time > sp->se_started && 95150394Smckusick current_time - sp->se_started < GETTY_SPACING) { 95250394Smckusick warning("getty repeating too quickly on port %s, sleeping", 95350394Smckusick sp->se_device); 95450394Smckusick sleep((unsigned) GETTY_SPACING - 95550394Smckusick (current_time - sp->se_started)); 95650394Smckusick } 95750394Smckusick 95850394Smckusick if (sp->se_window) { 95950394Smckusick start_window_system(sp); 96050394Smckusick sleep(WINDOW_WAIT); 96150394Smckusick } 96250394Smckusick 96350394Smckusick setctty(sp->se_device); 96450394Smckusick 96550394Smckusick sigemptyset(&mask); 96650394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 96750394Smckusick 96850394Smckusick execv(sp->se_getty_argv[0], sp->se_getty_argv); 96950394Smckusick stall("can't exec getty '%s' for port %s: %m", 97050394Smckusick sp->se_getty_argv[0], sp->se_device); 97150394Smckusick _exit(1); 97213021Ssam } 97313021Ssam 97450407Smckusick /* 97550407Smckusick * Collect exit status for a child. 97650407Smckusick * If an exiting login, start a new login running. 97750407Smckusick */ 97842407Smarc void 97950407Smckusick collect_child(pid) 98050407Smckusick pid_t pid; 9812821Swnj { 98250394Smckusick register session_t *sp, *sprev, *snext; 9832821Swnj 98450407Smckusick if (! sessions) 98550407Smckusick return; 98650394Smckusick 98750407Smckusick if (! (sp = find_session(pid))) 98850407Smckusick return; 98950394Smckusick 99050407Smckusick clear_session_logs(sp); 99150407Smckusick del_session(sp); 99250407Smckusick sp->se_process = 0; 99350394Smckusick 99450407Smckusick if (sp->se_flags & SE_SHUTDOWN) { 99550407Smckusick if (sprev = sp->se_prev) 99650407Smckusick sprev->se_next = sp->se_next; 99750407Smckusick else 99850407Smckusick sessions = sp->se_next; 99950407Smckusick if (snext = sp->se_next) 100050407Smckusick snext->se_prev = sp->se_prev; 100150407Smckusick free_session(sp); 100250407Smckusick return; 100350407Smckusick } 100450394Smckusick 100550407Smckusick if ((pid = start_getty(sp)) == -1) { 100650407Smckusick /* serious trouble */ 100750407Smckusick requested_transition = clean_ttys; 100850407Smckusick return; 10092821Swnj } 101050394Smckusick 101150407Smckusick sp->se_process = pid; 101250407Smckusick sp->se_started = time((time_t *) 0); 101350407Smckusick add_session(sp); 10142821Swnj } 101518542Sralph 101650407Smckusick /* 101750407Smckusick * Catch a signal and request a state transition. 101850407Smckusick */ 101950394Smckusick void 102050394Smckusick transition_handler(sig) 102150394Smckusick int sig; 102218542Sralph { 102350539Strent 102450394Smckusick switch (sig) { 102550394Smckusick case SIGHUP: 102650394Smckusick requested_transition = clean_ttys; 102750394Smckusick break; 102850394Smckusick case SIGTERM: 102950394Smckusick requested_transition = death; 103050394Smckusick break; 103150394Smckusick case SIGTSTP: 103250394Smckusick requested_transition = catatonia; 103350394Smckusick break; 103450394Smckusick default: 103550394Smckusick requested_transition = 0; 103650394Smckusick break; 103718542Sralph } 103818542Sralph } 103918542Sralph 104050407Smckusick /* 104150407Smckusick * Take the system multiuser. 104250407Smckusick */ 104350394Smckusick state_func_t 104450394Smckusick multi_user() 104518542Sralph { 104650394Smckusick pid_t pid; 104750394Smckusick register session_t *sp; 104818542Sralph 104950394Smckusick requested_transition = 0; 105050394Smckusick logger_enable = 1; 105122181Skarels 105250407Smckusick for (sp = sessions; sp; sp = sp->se_next) { 105350407Smckusick if (sp->se_process) 105450407Smckusick continue; 105550407Smckusick if ((pid = start_getty(sp)) == -1) { 105650407Smckusick /* serious trouble */ 105750407Smckusick requested_transition = clean_ttys; 105850407Smckusick break; 105922181Skarels } 106050407Smckusick sp->se_process = pid; 106150407Smckusick sp->se_started = time((time_t *) 0); 106250407Smckusick add_session(sp); 106350407Smckusick } 106450394Smckusick 106550394Smckusick while (!requested_transition) 106650407Smckusick if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 106750407Smckusick collect_child(pid); 106850394Smckusick 106950394Smckusick return (state_func_t) requested_transition; 107018542Sralph } 107118542Sralph 107250394Smckusick /* 107350394Smckusick * This is an n-squared algorithm. We hope it isn't run often... 107450394Smckusick */ 107550394Smckusick state_func_t 107650394Smckusick clean_ttys() 107718542Sralph { 107850394Smckusick register session_t *sp, *sprev; 107950394Smckusick register struct ttyent *typ; 108050394Smckusick register int session_index = 0; 108152954Smckusick register int devlen; 108218542Sralph 108350394Smckusick if (! sessions) 108450394Smckusick return (state_func_t) multi_user; 108550394Smckusick 1086*53151Storek devlen = sizeof(_PATH_DEV) - 1; 108750394Smckusick while (typ = getttyent()) { 108850394Smckusick ++session_index; 108950394Smckusick 109050394Smckusick for (sp = sessions; sp; sprev = sp, sp = sp->se_next) 1091*53151Storek if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 109218542Sralph break; 109350394Smckusick 109450394Smckusick if (sp) { 109550394Smckusick if (sp->se_index != session_index) { 109650394Smckusick warning("port %s changed utmp index from %d to %d", 109750394Smckusick sp->se_device, sp->se_index, 109850394Smckusick session_index); 109950394Smckusick sp->se_index = session_index; 110018542Sralph } 110150394Smckusick if (typ->ty_status & TTY_ON) 110250394Smckusick sp->se_flags &= ~SE_SHUTDOWN; 110350394Smckusick else { 110450394Smckusick sp->se_flags |= SE_SHUTDOWN; 110550394Smckusick kill(sp->se_process, SIGHUP); 110650394Smckusick } 110750394Smckusick continue; 110818542Sralph } 110950394Smckusick 111050394Smckusick new_session(sprev, session_index, typ); 111118542Sralph } 111250394Smckusick 111350394Smckusick endttyent(); 111450394Smckusick 111550394Smckusick return (state_func_t) multi_user; 111618542Sralph } 111750394Smckusick 111850407Smckusick /* 111950407Smckusick * Block further logins. 112050407Smckusick */ 112150394Smckusick state_func_t 112250394Smckusick catatonia() 112350394Smckusick { 112450394Smckusick register session_t *sp; 112550394Smckusick 112650394Smckusick for (sp = sessions; sp; sp = sp->se_next) 112750394Smckusick sp->se_flags |= SE_SHUTDOWN; 112850394Smckusick 112950394Smckusick return (state_func_t) multi_user; 113050394Smckusick } 113150394Smckusick 113250407Smckusick /* 113350407Smckusick * Note SIGALRM. 113450407Smckusick */ 113550394Smckusick void 113650394Smckusick alrm_handler(sig) 113750394Smckusick int sig; 113850394Smckusick { 113950394Smckusick clang = 1; 114050394Smckusick } 114150394Smckusick 114250407Smckusick /* 114350407Smckusick * Bring the system down to single user. 114450407Smckusick */ 114550394Smckusick state_func_t 114650394Smckusick death() 114750394Smckusick { 114850394Smckusick register session_t *sp; 114950394Smckusick register int i; 115050407Smckusick pid_t pid; 115150394Smckusick static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 115250394Smckusick 115350394Smckusick for (sp = sessions; sp; sp = sp->se_next) 115450394Smckusick sp->se_flags |= SE_SHUTDOWN; 115550394Smckusick 115650430Smckusick /* NB: should send a message to the session logger to avoid blocking. */ 115750430Smckusick logwtmp("~", "shutdown", ""); 115850394Smckusick logger_enable = 0; 115950394Smckusick 116050394Smckusick for (i = 0; i < 3; ++i) { 116150394Smckusick if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 116250394Smckusick return (state_func_t) single_user; 116350394Smckusick 116450394Smckusick clang = 0; 116550394Smckusick alarm(DEATH_WATCH); 116650394Smckusick do 116750407Smckusick if ((pid = waitpid(-1, (int *)0, 0)) != -1) 116850407Smckusick collect_child(pid); 116950407Smckusick while (clang == 0 && errno != ECHILD); 117050394Smckusick 117150407Smckusick if (errno == ECHILD) 117250394Smckusick return (state_func_t) single_user; 117350394Smckusick } 117450394Smckusick 117550394Smckusick warning("some processes wouldn't die"); 117650394Smckusick 117750394Smckusick return (state_func_t) single_user; 117850394Smckusick } 1179