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*50407Smckusick static char sccsid[] = "@(#)init.c 6.2 (Berkeley) 07/05/91"; 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 <stdlib.h> 2850394Smckusick #include <string.h> 2950394Smckusick #include <syslog.h> 3050394Smckusick #include <time.h> 3116452Sroot #include <ttyent.h> 3250394Smckusick #include <unistd.h> 3350394Smckusick 3450394Smckusick #ifdef __STDC__ 3550394Smckusick #include <stdarg.h> 3650394Smckusick #else 3750394Smckusick #include <varargs.h> 3850394Smckusick #endif 3950394Smckusick 4050394Smckusick #ifdef SECURE 4150394Smckusick #include <pwd.h> 4250394Smckusick #endif 4350394Smckusick 4437284Sbostic #include "pathnames.h" 451029Sbill 4650394Smckusick /* 4750394Smckusick * Until the mythical util.h arrives... 4850394Smckusick */ 4950394Smckusick extern int login_tty __P((int)); 5050394Smckusick extern int logout __P((const char *)); 5150394Smckusick extern void logwtmp __P((const char *, const char *, const char *)); 521029Sbill 5350394Smckusick /* 5450394Smckusick * Sleep times; used to prevent thrashing. 5550394Smckusick */ 56*50407Smckusick #define GETTY_SPACING 10 /* fork getty on a port every N secs */ 5750394Smckusick #define WINDOW_WAIT 3 /* wait N secs after starting window */ 5850394Smckusick #define STALL_TIMEOUT 30 /* wait N secs after warning */ 59*50407Smckusick #define DEATH_WATCH 10 /* wait N secs for procs to die */ 601029Sbill 6150394Smckusick void handle __P((sig_t, ...)); 6250394Smckusick void delset __P((sigset_t *, ...)); 631029Sbill 6450394Smckusick void stall __P((char *, ...)); 6550394Smckusick void warning __P((char *, ...)); 6650394Smckusick void emergency __P((char *, ...)); 6750394Smckusick void disaster __P((int)); 681029Sbill 6950394Smckusick /* 7050394Smckusick * We really need a recursive typedef... 7150394Smckusick * The following at least guarantees that the return type of (*state_t)() 7250394Smckusick * is sufficiently wide to hold a function pointer. 7350394Smckusick */ 7450394Smckusick typedef long (*state_func_t) __P((void)); 7550394Smckusick typedef state_func_t (*state_t) __P((void)); 761029Sbill 7750394Smckusick state_func_t single_user __P((void)); 7850394Smckusick state_func_t runcom __P((void)); 7950394Smckusick state_func_t read_ttys __P((void)); 8050394Smckusick state_func_t multi_user __P((void)); 8150394Smckusick state_func_t clean_ttys __P((void)); 8250394Smckusick state_func_t catatonia __P((void)); 8350394Smckusick state_func_t death __P((void)); 8413021Ssam 8550394Smckusick enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 8650394Smckusick 8750394Smckusick void transition __P((state_t)); 8850394Smckusick state_t requested_transition = runcom; 8950394Smckusick 9050394Smckusick void setctty __P((char *)); 9150394Smckusick 9250394Smckusick typedef struct session { 9350394Smckusick int se_index; /* index of entry in ttys file */ 9450394Smckusick pid_t se_process; /* controlling process */ 9550394Smckusick time_t se_started; /* used to avoid thrashing */ 9650394Smckusick int se_flags; /* status of session */ 9750394Smckusick #define SE_SHUTDOWN 0x1 /* session won't be restarted */ 9850394Smckusick char *se_device; /* filename of port */ 9950394Smckusick char *se_getty; /* what to run on that port */ 10050394Smckusick char **se_getty_argv; /* pre-parsed argument array */ 10150394Smckusick char *se_window; /* window system (started only once) */ 10250394Smckusick char **se_window_argv; /* pre-parsed argument array */ 10350394Smckusick struct session *se_prev; 10450394Smckusick struct session *se_next; 10550394Smckusick } session_t; 10650394Smckusick 10750394Smckusick void free_session __P((session_t *)); 10850394Smckusick session_t *new_session __P((session_t *, int, struct ttyent *)); 10950394Smckusick session_t *sessions; 11050394Smckusick 11150394Smckusick char **construct_argv __P((char *)); 11250394Smckusick void start_window_system __P((session_t *)); 113*50407Smckusick void collect_child __P((int)); 11450394Smckusick pid_t start_getty __P((session_t *)); 11550394Smckusick void transition_handler __P((int)); 11650394Smckusick void alrm_handler __P((int)); 11750394Smckusick int clang; 11850394Smckusick 11950394Smckusick int start_logger __P((void)); 12050394Smckusick void clear_session_logs __P((session_t *)); 12150394Smckusick int logger_enable; 12250394Smckusick 123*50407Smckusick int start_session_db __P((void)); 12450394Smckusick void add_session __P((session_t *)); 12550394Smckusick void del_session __P((session_t *)); 12650394Smckusick session_t *find_session __P((pid_t)); 12750394Smckusick DB *session_db; 12850394Smckusick 129*50407Smckusick /* 130*50407Smckusick * The mother of all processes. 131*50407Smckusick */ 13250394Smckusick int 13343633Skarels main(argc, argv) 13450394Smckusick int argc; 13543633Skarels char **argv; 1361029Sbill { 13750394Smckusick int c; 138*50407Smckusick struct sigaction sa; 13950394Smckusick sigset_t mask; 14047659Skarels 14147659Skarels /* 14250394Smckusick * Silently dispose of random users running this program. 14347659Skarels */ 14450394Smckusick if (getuid() != 0) 14550394Smckusick return 1; 1469869Spugs 14750394Smckusick /* 14850394Smckusick * Note that this does NOT open a file... 14950394Smckusick * Does 'init' deserve its own facility number? 15050394Smckusick */ 151*50407Smckusick openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 15250394Smckusick 15350394Smckusick /* 154*50407Smckusick * Create an initial session. 155*50407Smckusick */ 156*50407Smckusick if (setsid() < 0) 157*50407Smckusick syslog(LOG_ERR, "setsid failed (initial) %m"); 158*50407Smckusick 159*50407Smckusick /* 16050394Smckusick * This code assumes that we always get arguments through flags, 16150394Smckusick * never through bits set in some random machine register. 16250394Smckusick */ 16350394Smckusick while ((c = getopt(argc, argv, "sf")) != -1) 16450394Smckusick switch (c) { 16550394Smckusick case 's': 16650394Smckusick requested_transition = single_user; 16750394Smckusick break; 16847659Skarels case 'f': 16950394Smckusick runcom_mode = FASTBOOT; 1709869Spugs break; 17150394Smckusick default: 17250394Smckusick warning("unrecognized flag '-%c'", c); 1739869Spugs break; 1749869Spugs } 17550394Smckusick 17650394Smckusick if (optind != argc) 17750394Smckusick warning("ignoring excess arguments"); 17850394Smckusick 17950394Smckusick /* 18050394Smckusick * We catch or block signals rather than ignore them, 18150394Smckusick * so that they get reset on exec. 18250394Smckusick */ 18350394Smckusick handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 18450394Smckusick SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, 0); 18550394Smckusick handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); 18650394Smckusick handle(alrm_handler, SIGALRM, 0); 18750394Smckusick sigfillset(&mask); 188*50407Smckusick delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 189*50407Smckusick SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); 19050394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 191*50407Smckusick sigemptyset(&sa.sa_mask); 192*50407Smckusick sa.sa_flags = 0; 193*50407Smckusick sa.sa_handler = SIG_IGN; 194*50407Smckusick (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); 195*50407Smckusick (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); 19650394Smckusick 19750394Smckusick /* 19850394Smckusick * Paranoia. 19950394Smckusick */ 20050394Smckusick close(0); 20150394Smckusick close(1); 20250394Smckusick close(2); 20350394Smckusick 20450394Smckusick /* 20550394Smckusick * Start the state machine. 20650394Smckusick */ 20750394Smckusick transition(requested_transition); 20850394Smckusick 20950394Smckusick /* 21050394Smckusick * Should never reach here. 21150394Smckusick */ 21250394Smckusick return 1; 21350394Smckusick } 21450394Smckusick 215*50407Smckusick /* 216*50407Smckusick * Associate a function with a signal handler. 217*50407Smckusick */ 21850394Smckusick void 21950394Smckusick #ifdef __STDC__ 22050394Smckusick handle(sig_t handler, ...) 22150394Smckusick #else 22250394Smckusick handle(va_alist) 22350394Smckusick va_dcl 22450394Smckusick #endif 22550394Smckusick { 22650394Smckusick int sig; 22750394Smckusick struct sigaction sa; 22850394Smckusick int mask_everything; 22950394Smckusick va_list ap; 23050394Smckusick #ifndef __STDC__ 23150394Smckusick sig_t handler; 23250394Smckusick 23350394Smckusick va_start(ap); 23450394Smckusick handler = va_arg(ap, sig_t); 23550394Smckusick #else 23650394Smckusick va_start(ap, handler); 23750394Smckusick #endif 23850394Smckusick 23950394Smckusick sa.sa_handler = handler; 24050394Smckusick sigfillset(&mask_everything); 24150394Smckusick 24250394Smckusick while (sig = va_arg(ap, int)) { 24350394Smckusick sa.sa_mask = mask_everything; 24450394Smckusick /* XXX SA_RESTART? */ 24550394Smckusick sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 24650394Smckusick sigaction(sig, &sa, (struct sigaction *) 0); 24747659Skarels } 2481029Sbill } 2491029Sbill 250*50407Smckusick /* 251*50407Smckusick * Delete a set of signals from a mask. 252*50407Smckusick */ 25350394Smckusick void 25450394Smckusick #ifdef __STDC__ 25550394Smckusick delset(sigset_t *maskp, ...) 25650394Smckusick #else 25750394Smckusick delset(va_alist) 25850394Smckusick va_dcl 25950394Smckusick #endif 26050394Smckusick { 26150394Smckusick int sig; 26250394Smckusick va_list ap; 26350394Smckusick #ifndef __STDC__ 26450394Smckusick sigset_t *maskp; 2651429Sbill 26650394Smckusick va_start(ap); 26750394Smckusick maskp = va_arg(ap, sigset_t *); 26850394Smckusick #else 26950394Smckusick va_start(ap, maskp); 27050394Smckusick #endif 27150394Smckusick 27250394Smckusick while (sig = va_arg(ap, int)) 27350394Smckusick sigdelset(maskp, sig); 27450394Smckusick } 27550394Smckusick 27650394Smckusick /* 27750394Smckusick * Log a message and sleep for a while (to give someone an opportunity 27850394Smckusick * to read it and to save log or hardcopy output if the problem is chronic). 27950394Smckusick * We fork so that we can't block on I/O when writing the message, 28050394Smckusick * and so that init proper doesn't acquire another open file. 28150394Smckusick * If the fork fails, or the child can't finish, too bad. 28250394Smckusick */ 28350394Smckusick void 28450394Smckusick #ifdef __STDC__ 28550394Smckusick stall(char *message, ...) 28650394Smckusick #else 28750394Smckusick stall(va_alist) 28850394Smckusick va_dcl 28950394Smckusick #endif 2901029Sbill { 29150394Smckusick pid_t pid; 29250394Smckusick va_list ap; 29350394Smckusick #ifndef __STDC__ 29450394Smckusick char *message; 2951029Sbill 29650394Smckusick va_start(ap); 29750394Smckusick message = va_arg(ap, char *); 29850394Smckusick #else 29950394Smckusick va_start(ap, message); 30050394Smckusick #endif 30150394Smckusick 30250394Smckusick if ((pid = fork()) == 0) { 30350394Smckusick vsyslog(LOG_ALERT, message, ap); 30450394Smckusick _exit(0); 3051029Sbill } 30650394Smckusick va_end(ap); 30750394Smckusick sleep(STALL_TIMEOUT); 30850394Smckusick if (pid != -1) 30950394Smckusick waitpid(pid, (int *) 0, WNOHANG); 3101429Sbill } 3111429Sbill 31250394Smckusick /* 31350394Smckusick * Like stall(), but doesn't sleep. 31450394Smckusick * If cpp had variadic macros, the two functions could be #defines for another. 31550394Smckusick */ 31642407Smarc void 31750394Smckusick #ifdef __STDC__ 31850394Smckusick warning(char *message, ...) 31950394Smckusick #else 32050394Smckusick warning(va_alist) 32150394Smckusick va_dcl 32250394Smckusick #endif 3231429Sbill { 32450394Smckusick va_list ap; 32550394Smckusick #ifndef __STDC__ 32650394Smckusick char *message; 3271429Sbill 32850394Smckusick va_start(ap); 32950394Smckusick message = va_arg(ap, char *); 33050394Smckusick #else 33150394Smckusick va_start(ap, message); 33250394Smckusick #endif 33350394Smckusick 3341429Sbill if (fork() == 0) { 33550394Smckusick vsyslog(LOG_ALERT, message, ap); 33650394Smckusick _exit(0); 3371429Sbill } 33850394Smckusick va_end(ap); 3391429Sbill } 3401429Sbill 34150394Smckusick /* 34250394Smckusick * Log a message, no forking, no waiting. 34350394Smckusick * We take care to close syslog's file descriptor, however. 34450394Smckusick */ 34550394Smckusick void 34650394Smckusick #ifdef __STDC__ 34750394Smckusick emergency(char *message, ...) 34850394Smckusick #else 34950394Smckusick emergency(va_alist) 35050394Smckusick va_dcl 35150394Smckusick #endif 3521429Sbill { 35350394Smckusick va_list ap; 35450394Smckusick #ifndef __STDC__ 35550394Smckusick char *message; 3561429Sbill 35750394Smckusick va_start(ap); 35850394Smckusick message = va_arg(ap, char *); 35950394Smckusick #else 36050394Smckusick va_start(ap, message); 36150394Smckusick #endif 36250394Smckusick 36350394Smckusick vsyslog(LOG_EMERG, message, ap); 36450394Smckusick va_end(ap); 36550394Smckusick closelog(); 366*50407Smckusick openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 3671029Sbill } 3681029Sbill 369*50407Smckusick /* 370*50407Smckusick * Catch an unexpected signal. 371*50407Smckusick */ 37250394Smckusick void 37350394Smckusick disaster(sig) 37450394Smckusick int sig; 3751029Sbill { 37650394Smckusick emergency("fatal signal: %s", 37750394Smckusick sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); 3781029Sbill 37950394Smckusick sleep(STALL_TIMEOUT); 38050394Smckusick _exit(sig); /* reboot */ 3811029Sbill } 3821029Sbill 38350394Smckusick /* 38450394Smckusick * Change states in the finite state machine. 38550394Smckusick * The initial state is passed as an argument. 38650394Smckusick */ 38750394Smckusick void 38850394Smckusick transition(s) 38950394Smckusick state_t s; 3901029Sbill { 39150394Smckusick for (;;) 39250394Smckusick s = (state_t) (*s)(); 39350394Smckusick } 3941029Sbill 39550394Smckusick /* 39650394Smckusick * We send requests for session logging to another process for two reasons. 39750394Smckusick * First, we don't want to block if the log files go away (e.g. because 39850394Smckusick * one or more are on hard-mounted NFS systems whose server crashes). 39950394Smckusick * Second, despite all the crud already contained in init, it still isn't 40050394Smckusick * right that init should care about session logging record formats and files. 40150394Smckusick * We could use explicit 'Unix' IPC for this, but let's try to be POSIX... 40250394Smckusick */ 40350394Smckusick int 40450394Smckusick start_logger() 40550394Smckusick { 40650394Smckusick static char *argv[] = { _PATH_SLOGGER, 0 }; 40750394Smckusick int fd, pfd[2]; 40850394Smckusick pid_t pid; 40950394Smckusick sigset_t mask; 41050394Smckusick 41150394Smckusick if (pipe(pfd) == -1) { 41250394Smckusick warning("session logging disabled: can't make pipe to %s: %m", 41350394Smckusick argv[0]); 41450394Smckusick return -1; 41550394Smckusick } 41650394Smckusick if ((pid = fork()) == -1) { 41750394Smckusick emergency("session logging disabled: can't fork for %s: %m", 41850394Smckusick argv[0]); 41950394Smckusick return -1; 42050394Smckusick } 42150394Smckusick 42213021Ssam if (pid == 0) { 42350394Smckusick close(pfd[1]); 42450394Smckusick if (pfd[0] != 0) { 42550394Smckusick dup2(pfd[0], 0); 42650394Smckusick close(pfd[0]); 42750394Smckusick } 42850394Smckusick if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != -1) { 42950394Smckusick if (fd != 1) 43050394Smckusick dup2(fd, 1); 43150394Smckusick if (fd != 2) 43250394Smckusick dup2(fd, 2); 43350394Smckusick if (fd != 1 && fd != 2) 43444284Skarels close(fd); 43550394Smckusick } else { 43650394Smckusick /* paranoid */ 43750394Smckusick close(1); 43850394Smckusick close(2); 43944284Skarels } 44050394Smckusick sigemptyset(&mask); 44150394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 44250394Smckusick execv(argv[0], argv); 44350394Smckusick stall("can't exec %s: %m", argv[0]); 44450394Smckusick _exit(1); 4451029Sbill } 44650394Smckusick 44750394Smckusick close(pfd[0]); 44850394Smckusick fcntl(pfd[1], F_SETFD, FD_CLOEXEC); 44950394Smckusick fcntl(pfd[1], F_SETFL, O_NONBLOCK); 45050394Smckusick 45150394Smckusick return pfd[1]; 4521029Sbill } 4531029Sbill 454*50407Smckusick /* 455*50407Smckusick * Close out the accounting files for a login session. 456*50407Smckusick */ 45750394Smckusick void 45850394Smckusick clear_session_logs(sp) 45950394Smckusick session_t *sp; 46050394Smckusick { 46150394Smckusick if (fork() == 0 && logout(sp->se_device)) 46250394Smckusick logwtmp(sp->se_device, "", ""); 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 475*50407Smckusick (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 486*50407Smckusick /* 487*50407Smckusick * Bring the system up single user. 488*50407Smckusick */ 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); 52550394Smckusick if (strcmp(password, pp->pw_passwd) == 0) 52650394Smckusick break; 52713021Ssam } 52813021Ssam } 52950394Smckusick #if 0 53050394Smckusick /* 53150394Smckusick * Make the single-user shell be root's standard shell? 53250394Smckusick */ 53350394Smckusick if (pp && pp->pw_shell) 53450394Smckusick shell = pp->pw_shell; 53550394Smckusick #endif 53650394Smckusick endttyent(); 53750394Smckusick endpwent(); 53850394Smckusick #endif 53918542Sralph 54023147Sbloom /* 54150394Smckusick * Unblock signals. 54250394Smckusick * We catch all the interesting ones, 54350394Smckusick * and those are reset to SIG_DFL on exec. 54423147Sbloom */ 54550394Smckusick sigemptyset(&mask); 54650394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 54750394Smckusick 54823147Sbloom /* 54950394Smckusick * Fire off a shell. 55050394Smckusick * If the default one doesn't work, try the Bourne shell. 55123147Sbloom */ 55250394Smckusick argv[0] = "-sh"; 55350394Smckusick argv[1] = 0; 55450394Smckusick execv(shell, argv); 55550394Smckusick emergency("can't exec %s for single user: %m", shell); 55650394Smckusick execv(_PATH_BSHELL, argv); 55750394Smckusick emergency("can't exec %s for single user: %m", _PATH_BSHELL); 55850394Smckusick sleep(STALL_TIMEOUT); 55950394Smckusick _exit(1); 56050394Smckusick } 56123147Sbloom 56250394Smckusick if (pid == -1) { 56350394Smckusick /* 56450394Smckusick * We are seriously hosed. Do our best. 56550394Smckusick */ 56650394Smckusick emergency("can't fork single-user shell, trying again"); 56750394Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 56850394Smckusick ; 56950394Smckusick return (state_func_t) single_user; 57050394Smckusick } 57150394Smckusick 572*50407Smckusick do { 573*50407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 574*50407Smckusick collect_child(wpid); 575*50407Smckusick if (wpid == -1) { 576*50407Smckusick if (errno == EINTR) 577*50407Smckusick continue; 57850394Smckusick warning("wait for single-user shell failed: %m; restarting"); 57950394Smckusick return (state_func_t) single_user; 58023147Sbloom } 58150394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 58250394Smckusick warning("init: shell stopped, restarting\n"); 58350394Smckusick kill(pid, SIGCONT); 584*50407Smckusick wpid = -1; 58550394Smckusick } 586*50407Smckusick } while (wpid != pid); 58750394Smckusick 58850394Smckusick if (!WIFEXITED(status)) { 58950394Smckusick warning("single user shell terminated abnormally, restarting"); 59050394Smckusick return (state_func_t) single_user; 59150394Smckusick } 59250394Smckusick 59350394Smckusick runcom_mode = FASTBOOT; 59450394Smckusick return (state_func_t) runcom; 59550394Smckusick } 59650394Smckusick 597*50407Smckusick /* 598*50407Smckusick * Run the system startup script. 599*50407Smckusick */ 60050394Smckusick state_func_t 60150394Smckusick runcom() 60250394Smckusick { 60350394Smckusick pid_t pid, wpid; 60450394Smckusick int status; 60550394Smckusick char *argv[4]; 606*50407Smckusick struct sigaction sa; 60750394Smckusick 60850394Smckusick if ((pid = fork()) == 0) { 609*50407Smckusick sigemptyset(&sa.sa_mask); 610*50407Smckusick sa.sa_flags = 0; 611*50407Smckusick sa.sa_handler = SIG_IGN; 612*50407Smckusick (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); 613*50407Smckusick (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); 614*50407Smckusick 61550394Smckusick setctty(_PATH_CONSOLE); 61650394Smckusick 61750394Smckusick argv[0] = "sh"; 61850394Smckusick argv[1] = _PATH_RUNCOM; 61950394Smckusick argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; 62050394Smckusick argv[3] = 0; 62150394Smckusick 622*50407Smckusick sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 62350394Smckusick 62450394Smckusick execv(_PATH_BSHELL, argv); 62550394Smckusick stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 62650394Smckusick _exit(1); /* force single user mode */ 62750394Smckusick } 62850394Smckusick 62950394Smckusick if (pid == -1) { 63050394Smckusick emergency("can't fork for %s on %s: %m", 63150394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 632*50407Smckusick while (waitpid(-1, (int *) 0, WNOHANG) > 0) 633*50407Smckusick ; 63450394Smckusick sleep(STALL_TIMEOUT); 63550394Smckusick return (state_func_t) single_user; 63650394Smckusick } 63750394Smckusick 63850394Smckusick /* 639*50407Smckusick * Copied from single_user(). This is a bit paranoid. 64050394Smckusick */ 641*50407Smckusick do { 642*50407Smckusick if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 643*50407Smckusick collect_child(wpid); 644*50407Smckusick if (wpid == -1) { 645*50407Smckusick if (errno == EINTR) 646*50407Smckusick continue; 64750394Smckusick warning("wait for %s on %s failed: %m; going to single user mode", 64850394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 64950394Smckusick return (state_func_t) single_user; 65013021Ssam } 65150394Smckusick if (wpid == pid && WIFSTOPPED(status)) { 65250394Smckusick warning("init: %s on %s stopped, restarting\n", 65350394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 65450394Smckusick kill(pid, SIGCONT); 655*50407Smckusick wpid = -1; 65650394Smckusick } 657*50407Smckusick } while (wpid != pid); 65850394Smckusick 65950394Smckusick if (!WIFEXITED(status)) { 66050394Smckusick warning("%s on %s terminated abnormally, going to single user mode", 66150394Smckusick _PATH_BSHELL, _PATH_RUNCOM); 66250394Smckusick return (state_func_t) single_user; 66350394Smckusick } 66450394Smckusick 66550394Smckusick if (WEXITSTATUS(status)) 66650394Smckusick return (state_func_t) single_user; 66750394Smckusick 66850394Smckusick runcom_mode = AUTOBOOT; /* the default */ 669*50407Smckusick logwtmp("~", "reboot", ""); /* XXX */ 67050394Smckusick return (state_func_t) read_ttys; 67113021Ssam } 67213021Ssam 67350394Smckusick /* 674*50407Smckusick * Open the session database. 675*50407Smckusick * 676*50407Smckusick * NB: We could pass in the size here; is it necessary? 67750394Smckusick */ 678*50407Smckusick int 67950394Smckusick start_session_db() 6801029Sbill { 681*50407Smckusick if (session_db && (*session_db->close)(session_db)) 682*50407Smckusick emergency("session database close: %s", strerror(errno)); 683*50407Smckusick if ((session_db = hash_open(NULL, O_RDWR, 0, NULL)) == 0) { 684*50407Smckusick emergency("session database open: %s", strerror(errno)); 685*50407Smckusick return (1); 686*50407Smckusick } 687*50407Smckusick return (0); 688*50407Smckusick 68950394Smckusick } 6901029Sbill 691*50407Smckusick /* 692*50407Smckusick * Add a new login session. 693*50407Smckusick */ 69450394Smckusick void 69550394Smckusick add_session(sp) 69650394Smckusick session_t *sp; 69750394Smckusick { 69850394Smckusick DBT key; 69950394Smckusick DBT data; 70050394Smckusick 70150394Smckusick key.data = &sp->se_process; 70250394Smckusick key.size = sizeof sp->se_process; 70350394Smckusick data.data = &sp; 70450394Smckusick data.size = sizeof sp; 70550394Smckusick 706*50407Smckusick if ((*session_db->put)(session_db, &key, &data, R_PUT)) 707*50407Smckusick emergency("insert %d: %s", sp->se_process, strerror(errno)); 70850394Smckusick } 70950394Smckusick 710*50407Smckusick /* 711*50407Smckusick * Delete an old login session. 712*50407Smckusick */ 71350394Smckusick void 71450394Smckusick del_session(sp) 71550394Smckusick session_t *sp; 71650394Smckusick { 71750394Smckusick DBT key; 71850394Smckusick 71950394Smckusick key.data = &sp->se_process; 72050394Smckusick key.size = sizeof sp->se_process; 72150394Smckusick 722*50407Smckusick if ((*session_db->del)(session_db, &key, 0)) 723*50407Smckusick emergency("delete %d: %s", sp->se_process, strerror(errno)); 72450394Smckusick } 72550394Smckusick 726*50407Smckusick /* 727*50407Smckusick * Look up a login session by pid. 728*50407Smckusick */ 72950394Smckusick session_t * 73050394Smckusick #ifdef __STDC__ 73150394Smckusick find_session(pid_t pid) 73250394Smckusick #else 73350394Smckusick find_session(pid) 73450394Smckusick pid_t pid; 73550394Smckusick #endif 73650394Smckusick { 73750394Smckusick DBT key; 73850394Smckusick DBT data; 73950394Smckusick 74050394Smckusick key.data = &pid; 74150394Smckusick key.size = sizeof pid; 74250394Smckusick if ((*session_db->get)(session_db, &key, &data, 0) != 0) 74350394Smckusick return 0; 74450394Smckusick return *(session_t **)data.data; 74550394Smckusick } 74650394Smckusick 747*50407Smckusick /* 748*50407Smckusick * Construct an argument vector from a command line. 749*50407Smckusick */ 75050394Smckusick char ** 75150394Smckusick construct_argv(command) 75250394Smckusick char *command; 75350394Smckusick { 75450394Smckusick register int argc = 0; 75550394Smckusick register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 75650394Smckusick * sizeof (char *)); 75750394Smckusick static const char separators[] = " \t"; 75850394Smckusick 75950394Smckusick if ((argv[argc++] = strtok(command, separators)) == 0) 76050394Smckusick return 0; 76150394Smckusick while (argv[argc++] = strtok((char *) 0, separators)) 76250394Smckusick ; 76350394Smckusick return argv; 76450394Smckusick } 76550394Smckusick 766*50407Smckusick /* 767*50407Smckusick * Deallocate a session descriptor. 768*50407Smckusick */ 76950394Smckusick void 77050394Smckusick free_session(sp) 77150394Smckusick register session_t *sp; 77250394Smckusick { 77350394Smckusick free(sp->se_device); 77450394Smckusick free(sp->se_getty); 77550394Smckusick free(sp->se_getty_argv); 77650394Smckusick if (sp->se_window) { 77750394Smckusick free(sp->se_window); 77850394Smckusick free(sp->se_window_argv); 7791029Sbill } 78050394Smckusick free(sp); 7811029Sbill } 7821029Sbill 783*50407Smckusick /* 784*50407Smckusick * Allocate a new session descriptor. 785*50407Smckusick */ 78650394Smckusick session_t * 78750394Smckusick new_session(sprev, session_index, typ) 78850394Smckusick session_t *sprev; 78950394Smckusick int session_index; 79050394Smckusick register struct ttyent *typ; 7911029Sbill { 79250394Smckusick register session_t *sp; 7931029Sbill 79450394Smckusick if ((typ->ty_status & TTY_ON) == 0 || 79550394Smckusick typ->ty_name == 0 || 79650394Smckusick typ->ty_getty == 0) 79750394Smckusick return 0; 79850394Smckusick 79950394Smckusick sp = (session_t *) malloc(sizeof (session_t)); 80050394Smckusick 80150394Smckusick sp->se_index = session_index; 80250394Smckusick sp->se_process = 0; 80350394Smckusick sp->se_started = 0; 80450394Smckusick sp->se_flags = 0; 80550394Smckusick sp->se_window = 0; 80650394Smckusick 80750394Smckusick sp->se_device = malloc(6 + strlen(typ->ty_name)); 80850394Smckusick memcpy(sp->se_device, _PATH_DEV, 5); 80950394Smckusick strcpy(sp->se_device + 5, typ->ty_name); 81050394Smckusick 81150394Smckusick sp->se_getty = strdup(typ->ty_getty); 81250394Smckusick sp->se_getty_argv = construct_argv(sp->se_getty); 81350394Smckusick if (sp->se_getty_argv == 0) { 81450394Smckusick warning("can't parse getty for port %s", 81550394Smckusick sp->se_device); 81650394Smckusick free_session(sp); 81750394Smckusick return 0; 8185971Sroot } 81950394Smckusick if (typ->ty_window) { 82050394Smckusick sp->se_window = strdup(typ->ty_window); 82150394Smckusick sp->se_window_argv = construct_argv(sp->se_window); 82250394Smckusick if (sp->se_window_argv == 0) { 82350394Smckusick warning("can't parse window for port %s", 82450394Smckusick sp->se_device); 82550394Smckusick free_session(sp); 82650394Smckusick return 0; 8275971Sroot } 8281029Sbill } 82950394Smckusick 83050394Smckusick sp->se_next = 0; 83150394Smckusick if (sprev == 0) { 83250394Smckusick sessions = sp; 83350394Smckusick sp->se_prev = 0; 83450394Smckusick } else { 83550394Smckusick sprev->se_next = sp; 83650394Smckusick sp->se_prev = sprev; 83750394Smckusick } 83850394Smckusick 83950394Smckusick return sp; 8401029Sbill } 8411029Sbill 842*50407Smckusick /* 843*50407Smckusick * Walk the list of ttys and create sessions for each active line. 844*50407Smckusick */ 84550394Smckusick state_func_t 84650394Smckusick read_ttys() 8471029Sbill { 84850394Smckusick int session_index = 0; 84950394Smckusick register session_t *sp, *snext; 85050394Smckusick register struct ttyent *typ; 85150394Smckusick 85250394Smckusick /* 85350394Smckusick * Destroy any previous session state. 85450394Smckusick * There shouldn't be any, but just in case... 85550394Smckusick */ 85650394Smckusick for (sp = sessions; sp; sp = snext) { 85750394Smckusick if (sp->se_process) 85850394Smckusick clear_session_logs(sp); 85950394Smckusick snext = sp->se_next; 86050394Smckusick free_session(sp); 8611029Sbill } 86250394Smckusick sessions = 0; 863*50407Smckusick if (start_session_db()) 864*50407Smckusick return (state_func_t) single_user; 86550394Smckusick 86650394Smckusick /* 86750394Smckusick * Allocate a session entry for each active port. 86850394Smckusick * Note that sp starts at 0. 86950394Smckusick */ 87050394Smckusick while (typ = getttyent()) 87150394Smckusick if (snext = new_session(sp, ++session_index, typ)) 87250394Smckusick sp = snext; 87350394Smckusick 87450394Smckusick endttyent(); 87550394Smckusick 87650394Smckusick logger_enable = 1; 87750394Smckusick return (state_func_t) multi_user; 8781029Sbill } 8791029Sbill 880*50407Smckusick /* 881*50407Smckusick * Start a window system running. 882*50407Smckusick */ 88342407Smarc void 88450394Smckusick start_window_system(sp) 88550394Smckusick session_t *sp; 8861029Sbill { 88750394Smckusick pid_t pid; 88850394Smckusick sigset_t mask; 88950394Smckusick 89050394Smckusick if ((pid = fork()) == -1) { 89150394Smckusick emergency("can't fork for window system on port %s: %m", 89250394Smckusick sp->se_device); 89350394Smckusick /* hope that getty fails and we can try again */ 89450394Smckusick return; 89550394Smckusick } 89650394Smckusick 89750394Smckusick if (pid) 89850394Smckusick return; 89950394Smckusick 90050394Smckusick sigemptyset(&mask); 90150394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 90250394Smckusick 903*50407Smckusick if (setsid() < 0) 904*50407Smckusick emergency("setsid failed (window) %m"); 905*50407Smckusick 90650394Smckusick execv(sp->se_window_argv[0], sp->se_window_argv); 90750394Smckusick stall("can't exec window system '%s' for port %s: %m", 90850394Smckusick sp->se_window_argv[0], sp->se_device); 90950394Smckusick _exit(1); 9101029Sbill } 9112821Swnj 912*50407Smckusick /* 913*50407Smckusick * Start a login session running. 914*50407Smckusick */ 91550394Smckusick pid_t 91650394Smckusick start_getty(sp) 91750394Smckusick session_t *sp; 91850394Smckusick { 91950394Smckusick pid_t pid; 92050394Smckusick sigset_t mask; 92150394Smckusick time_t current_time = time((time_t *) 0); 92213021Ssam 92350394Smckusick /* 92450394Smckusick * fork(), not vfork() -- we can't afford to block. 92550394Smckusick */ 92650394Smckusick if ((pid = fork()) == -1) { 92750394Smckusick emergency("can't fork for getty on port %s: %m", sp->se_device); 92850394Smckusick return -1; 92950394Smckusick } 93050394Smckusick 93150394Smckusick if (pid) 93250394Smckusick return pid; 93350394Smckusick 93450394Smckusick if (current_time > sp->se_started && 93550394Smckusick current_time - sp->se_started < GETTY_SPACING) { 93650394Smckusick warning("getty repeating too quickly on port %s, sleeping", 93750394Smckusick sp->se_device); 93850394Smckusick sleep((unsigned) GETTY_SPACING - 93950394Smckusick (current_time - sp->se_started)); 94050394Smckusick } 94150394Smckusick 94250394Smckusick if (sp->se_window) { 94350394Smckusick start_window_system(sp); 94450394Smckusick sleep(WINDOW_WAIT); 94550394Smckusick } 94650394Smckusick 94750394Smckusick setctty(sp->se_device); 94850394Smckusick 94950394Smckusick sigemptyset(&mask); 95050394Smckusick sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 95150394Smckusick 95250394Smckusick execv(sp->se_getty_argv[0], sp->se_getty_argv); 95350394Smckusick stall("can't exec getty '%s' for port %s: %m", 95450394Smckusick sp->se_getty_argv[0], sp->se_device); 95550394Smckusick _exit(1); 95613021Ssam } 95713021Ssam 958*50407Smckusick /* 959*50407Smckusick * Collect exit status for a child. 960*50407Smckusick * If an exiting login, start a new login running. 961*50407Smckusick */ 96242407Smarc void 963*50407Smckusick collect_child(pid) 964*50407Smckusick pid_t pid; 9652821Swnj { 96650394Smckusick register session_t *sp, *sprev, *snext; 9672821Swnj 968*50407Smckusick if (! sessions) 969*50407Smckusick return; 97050394Smckusick 971*50407Smckusick if (! (sp = find_session(pid))) 972*50407Smckusick return; 97350394Smckusick 974*50407Smckusick clear_session_logs(sp); 975*50407Smckusick del_session(sp); 976*50407Smckusick sp->se_process = 0; 97750394Smckusick 978*50407Smckusick if (sp->se_flags & SE_SHUTDOWN) { 979*50407Smckusick if (sprev = sp->se_prev) 980*50407Smckusick sprev->se_next = sp->se_next; 981*50407Smckusick else 982*50407Smckusick sessions = sp->se_next; 983*50407Smckusick if (snext = sp->se_next) 984*50407Smckusick snext->se_prev = sp->se_prev; 985*50407Smckusick free_session(sp); 986*50407Smckusick return; 987*50407Smckusick } 98850394Smckusick 989*50407Smckusick if ((pid = start_getty(sp)) == -1) { 990*50407Smckusick /* serious trouble */ 991*50407Smckusick requested_transition = clean_ttys; 992*50407Smckusick return; 9932821Swnj } 99450394Smckusick 995*50407Smckusick sp->se_process = pid; 996*50407Smckusick sp->se_started = time((time_t *) 0); 997*50407Smckusick add_session(sp); 9982821Swnj } 99918542Sralph 1000*50407Smckusick /* 1001*50407Smckusick * Catch a signal and request a state transition. 1002*50407Smckusick */ 100350394Smckusick void 100450394Smckusick transition_handler(sig) 100550394Smckusick int sig; 100618542Sralph { 100750394Smckusick switch (sig) { 100850394Smckusick case SIGHUP: 100950394Smckusick requested_transition = clean_ttys; 101050394Smckusick break; 101150394Smckusick case SIGTERM: 101250394Smckusick requested_transition = death; 101350394Smckusick break; 101450394Smckusick case SIGTSTP: 101550394Smckusick requested_transition = catatonia; 101650394Smckusick break; 101750394Smckusick default: 101850394Smckusick requested_transition = 0; 101950394Smckusick break; 102018542Sralph } 102118542Sralph } 102218542Sralph 1023*50407Smckusick /* 1024*50407Smckusick * Take the system multiuser. 1025*50407Smckusick */ 102650394Smckusick state_func_t 102750394Smckusick multi_user() 102818542Sralph { 102950394Smckusick pid_t pid; 103050394Smckusick register session_t *sp; 103118542Sralph 103250394Smckusick requested_transition = 0; 103350394Smckusick logger_enable = 1; 103422181Skarels 1035*50407Smckusick for (sp = sessions; sp; sp = sp->se_next) { 1036*50407Smckusick if (sp->se_process) 1037*50407Smckusick continue; 1038*50407Smckusick if ((pid = start_getty(sp)) == -1) { 1039*50407Smckusick /* serious trouble */ 1040*50407Smckusick requested_transition = clean_ttys; 1041*50407Smckusick break; 104222181Skarels } 1043*50407Smckusick sp->se_process = pid; 1044*50407Smckusick sp->se_started = time((time_t *) 0); 1045*50407Smckusick add_session(sp); 1046*50407Smckusick } 104750394Smckusick 104850394Smckusick while (!requested_transition) 1049*50407Smckusick if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 1050*50407Smckusick collect_child(pid); 105150394Smckusick 105250394Smckusick return (state_func_t) requested_transition; 105318542Sralph } 105418542Sralph 105550394Smckusick /* 105650394Smckusick * This is an n-squared algorithm. We hope it isn't run often... 105750394Smckusick */ 105850394Smckusick state_func_t 105950394Smckusick clean_ttys() 106018542Sralph { 106150394Smckusick register session_t *sp, *sprev; 106250394Smckusick register struct ttyent *typ; 106350394Smckusick register int session_index = 0; 106418542Sralph 106550394Smckusick if (! sessions) 106650394Smckusick return (state_func_t) multi_user; 106750394Smckusick 106850394Smckusick while (typ = getttyent()) { 106950394Smckusick ++session_index; 107050394Smckusick 107150394Smckusick for (sp = sessions; sp; sprev = sp, sp = sp->se_next) 107250394Smckusick if (strcmp(typ->ty_name, sp->se_device) == 0) 107318542Sralph break; 107450394Smckusick 107550394Smckusick if (sp) { 107650394Smckusick if (sp->se_index != session_index) { 107750394Smckusick warning("port %s changed utmp index from %d to %d", 107850394Smckusick sp->se_device, sp->se_index, 107950394Smckusick session_index); 108050394Smckusick sp->se_index = session_index; 108118542Sralph } 108250394Smckusick if (typ->ty_status & TTY_ON) 108350394Smckusick sp->se_flags &= ~SE_SHUTDOWN; 108450394Smckusick else { 108550394Smckusick sp->se_flags |= SE_SHUTDOWN; 108650394Smckusick kill(sp->se_process, SIGHUP); 108750394Smckusick } 108850394Smckusick continue; 108918542Sralph } 109050394Smckusick 109150394Smckusick new_session(sprev, session_index, typ); 109218542Sralph } 109350394Smckusick 109450394Smckusick endttyent(); 109550394Smckusick 109650394Smckusick return (state_func_t) multi_user; 109718542Sralph } 109850394Smckusick 1099*50407Smckusick /* 1100*50407Smckusick * Block further logins. 1101*50407Smckusick */ 110250394Smckusick state_func_t 110350394Smckusick catatonia() 110450394Smckusick { 110550394Smckusick register session_t *sp; 110650394Smckusick 110750394Smckusick for (sp = sessions; sp; sp = sp->se_next) 110850394Smckusick sp->se_flags |= SE_SHUTDOWN; 110950394Smckusick 111050394Smckusick return (state_func_t) multi_user; 111150394Smckusick } 111250394Smckusick 1113*50407Smckusick /* 1114*50407Smckusick * Note SIGALRM. 1115*50407Smckusick */ 111650394Smckusick void 111750394Smckusick alrm_handler(sig) 111850394Smckusick int sig; 111950394Smckusick { 112050394Smckusick clang = 1; 112150394Smckusick } 112250394Smckusick 1123*50407Smckusick /* 1124*50407Smckusick * Bring the system down to single user. 1125*50407Smckusick */ 112650394Smckusick state_func_t 112750394Smckusick death() 112850394Smckusick { 112950394Smckusick register session_t *sp; 113050394Smckusick register int i; 1131*50407Smckusick pid_t pid; 113250394Smckusick static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 113350394Smckusick 113450394Smckusick for (sp = sessions; sp; sp = sp->se_next) 113550394Smckusick sp->se_flags |= SE_SHUTDOWN; 113650394Smckusick 1137*50407Smckusick logwtmp("~", "shutdown", ""); /* XXX */ 113850394Smckusick logger_enable = 0; 113950394Smckusick 114050394Smckusick for (i = 0; i < 3; ++i) { 114150394Smckusick if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 114250394Smckusick return (state_func_t) single_user; 114350394Smckusick 114450394Smckusick clang = 0; 114550394Smckusick alarm(DEATH_WATCH); 114650394Smckusick do 1147*50407Smckusick if ((pid = waitpid(-1, (int *)0, 0)) != -1) 1148*50407Smckusick collect_child(pid); 1149*50407Smckusick while (clang == 0 && errno != ECHILD); 115050394Smckusick 1151*50407Smckusick if (errno == ECHILD) 115250394Smckusick return (state_func_t) single_user; 115350394Smckusick } 115450394Smckusick 115550394Smckusick warning("some processes wouldn't die"); 115650394Smckusick 115750394Smckusick return (state_func_t) single_user; 115850394Smckusick } 1159