init.c revision 231994
138032Speter/*- 264562Sgshapiro * Copyright (c) 1991, 1993 364562Sgshapiro * The Regents of the University of California. All rights reserved. 438032Speter * 538032Speter * This code is derived from software contributed to Berkeley by 638032Speter * Donn Seeley at Berkeley Software Design, Inc. 738032Speter * 838032Speter * Redistribution and use in source and binary forms, with or without 938032Speter * modification, are permitted provided that the following conditions 1038032Speter * are met: 1138032Speter * 1. Redistributions of source code must retain the above copyright 1238032Speter * notice, this list of conditions and the following disclaimer. 1338032Speter * 2. Redistributions in binary form must reproduce the above copyright 1464562Sgshapiro * notice, this list of conditions and the following disclaimer in the 1538032Speter * documentation and/or other materials provided with the distribution. 1638032Speter * 4. Neither the name of the University nor the names of its contributors 1764562Sgshapiro * may be used to endorse or promote products derived from this software 1871345Sgshapiro * without specific prior written permission. 1964562Sgshapiro * 2071345Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2164562Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2264562Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2364562Sgshapiro * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2464562Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2564562Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2638032Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2738032Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2838032Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2964562Sgshapiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3064562Sgshapiro * SUCH DAMAGE. 3164562Sgshapiro */ 3238032Speter 3338032Speter#ifndef lint 3438032Speterstatic const char copyright[] = 3538032Speter"@(#) Copyright (c) 1991, 1993\n\ 3638032Speter The Regents of the University of California. All rights reserved.\n"; 3738032Speter#endif /* not lint */ 3838032Speter 3964562Sgshapiro#ifndef lint 4064562Sgshapiro#if 0 4164562Sgshapirostatic char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; 4238032Speter#endif 4364562Sgshapirostatic const char rcsid[] = 4438032Speter "$FreeBSD: head/sbin/init/init.c 231994 2012-02-22 06:27:20Z kevlo $"; 4564562Sgshapiro#endif /* not lint */ 4664562Sgshapiro 4764562Sgshapiro#include <sys/param.h> 4838032Speter#include <sys/ioctl.h> 4938032Speter#include <sys/mount.h> 5038032Speter#include <sys/sysctl.h> 5138032Speter#include <sys/wait.h> 5238032Speter#include <sys/stat.h> 5338032Speter#include <sys/uio.h> 5438032Speter 5564562Sgshapiro#include <db.h> 5664562Sgshapiro#include <errno.h> 5764562Sgshapiro#include <fcntl.h> 5838032Speter#include <kenv.h> 5938032Speter#include <libutil.h> 6038032Speter#include <paths.h> 6138032Speter#include <signal.h> 6238032Speter#include <stdio.h> 6338032Speter#include <stdlib.h> 6438032Speter#include <string.h> 6538032Speter#include <syslog.h> 6638032Speter#include <time.h> 6764562Sgshapiro#include <ttyent.h> 6838032Speter#include <unistd.h> 6938032Speter#include <sys/reboot.h> 7038032Speter#include <err.h> 7164562Sgshapiro 7238032Speter#include <stdarg.h> 7338032Speter 7438032Speter#ifdef SECURE 7564562Sgshapiro#include <pwd.h> 7664562Sgshapiro#endif 7738032Speter 7864562Sgshapiro#ifdef LOGIN_CAP 7938032Speter#include <login_cap.h> 8038032Speter#endif 8164562Sgshapiro 8238032Speter#include "pathnames.h" 8338032Speter 8438032Speter/* 8538032Speter * Sleep times; used to prevent thrashing. 8638032Speter */ 8738032Speter#define GETTY_SPACING 5 /* N secs minimum getty spacing */ 8838032Speter#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ 8938032Speter#define GETTY_NSPACE 3 /* max. spacing count to bring reaction */ 9038032Speter#define WINDOW_WAIT 3 /* wait N secs after starting window */ 9138032Speter#define STALL_TIMEOUT 30 /* wait N secs after warning */ 9238032Speter#define DEATH_WATCH 10 /* wait N secs for procs to die */ 9338032Speter#define DEATH_SCRIPT 120 /* wait for 2min for /etc/rc.shutdown */ 9438032Speter#define RESOURCE_RC "daemon" 9538032Speter#define RESOURCE_WINDOW "default" 9638032Speter#define RESOURCE_GETTY "default" 9738032Speter 9838032Speterstatic void handle(sig_t, ...); 9964562Sgshapirostatic void delset(sigset_t *, ...); 10038032Speter 10138032Speterstatic void stall(const char *, ...) __printflike(1, 2); 10264562Sgshapirostatic void warning(const char *, ...) __printflike(1, 2); 10364562Sgshapirostatic void emergency(const char *, ...) __printflike(1, 2); 10464562Sgshapirostatic void disaster(int); 10538032Speterstatic void badsys(int); 10638032Speterstatic int runshutdown(void); 10764562Sgshapirostatic char *strk(char *); 10838032Speter 10938032Speter/* 11038032Speter * We really need a recursive typedef... 11164562Sgshapiro * The following at least guarantees that the return type of (*state_t)() 11238032Speter * is sufficiently wide to hold a function pointer. 11338032Speter */ 11464562Sgshapirotypedef long (*state_func_t)(void); 11538032Spetertypedef state_func_t (*state_t)(void); 11638032Speter 11738032Speterstatic state_func_t single_user(void); 11838032Speterstatic state_func_t runcom(void); 11938032Speterstatic state_func_t read_ttys(void); 12064562Sgshapirostatic state_func_t multi_user(void); 12164562Sgshapirostatic state_func_t clean_ttys(void); 12238032Speterstatic state_func_t catatonia(void); 12338032Speterstatic state_func_t death(void); 12438032Speterstatic state_func_t death_single(void); 12538032Speter 12638032Speterstatic state_func_t run_script(const char *); 12738032Speter 12838032Speterstatic enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 12938032Speter#define FALSE 0 13038032Speter#define TRUE 1 13138032Speter 13264562Sgshapirostatic int Reboot = FALSE; 13364562Sgshapirostatic int howto = RB_AUTOBOOT; 13464562Sgshapiro 13538032Speterstatic int devfs; 13638032Speter 13738032Speterstatic void transition(state_t); 13838032Speterstatic state_t requested_transition; 13938032Speterstatic state_t current_state = death_single; 14038032Speter 14138032Speterstatic void setctty(const char *); 14238032Speterstatic const char *get_shell(void); 14338032Speterstatic void write_stderr(const char *message); 14438032Speter 14538032Spetertypedef struct init_session { 14638032Speter int se_index; /* index of entry in ttys file */ 14764562Sgshapiro pid_t se_process; /* controlling process */ 14838032Speter time_t se_started; /* used to avoid thrashing */ 14938032Speter int se_flags; /* status of session */ 15064562Sgshapiro#define SE_SHUTDOWN 0x1 /* session won't be restarted */ 15138032Speter#define SE_PRESENT 0x2 /* session is in /etc/ttys */ 15238032Speter int se_nspace; /* spacing count */ 15338032Speter char *se_device; /* filename of port */ 15438032Speter char *se_getty; /* what to run on that port */ 15564562Sgshapiro char *se_getty_argv_space; /* pre-parsed argument array space */ 15638032Speter char **se_getty_argv; /* pre-parsed argument array */ 15738032Speter char *se_window; /* window system (started only once) */ 15838032Speter char *se_window_argv_space; /* pre-parsed argument array space */ 15938032Speter char **se_window_argv; /* pre-parsed argument array */ 16064562Sgshapiro char *se_type; /* default terminal type */ 16138032Speter struct init_session *se_prev; 16238032Speter struct init_session *se_next; 16338032Speter} session_t; 16438032Speter 16564562Sgshapirostatic void free_session(session_t *); 16638032Speterstatic session_t *new_session(session_t *, int, struct ttyent *); 16738032Speterstatic session_t *sessions; 16864562Sgshapiro 16964562Sgshapirostatic char **construct_argv(char *); 17064562Sgshapirostatic void start_window_system(session_t *); 17138032Speterstatic void collect_child(pid_t); 17238032Speterstatic pid_t start_getty(session_t *); 17338032Speterstatic void transition_handler(int); 17438032Speterstatic void alrm_handler(int); 17538032Speterstatic void setsecuritylevel(int); 17638032Speterstatic int getsecuritylevel(void); 17738032Speterstatic int setupargv(session_t *, struct ttyent *); 17838032Speter#ifdef LOGIN_CAP 17938032Speterstatic void setprocresources(const char *); 18038032Speter#endif 18138032Speterstatic int clang; 18238032Speter 18338032Speterstatic int start_session_db(void); 18438032Speterstatic void add_session(session_t *); 18538032Speterstatic void del_session(session_t *); 18638032Speterstatic session_t *find_session(pid_t); 18738032Speterstatic DB *session_db; 18838032Speter 18938032Speter/* 19038032Speter * The mother of all processes. 19138032Speter */ 19238032Speterint 19338032Spetermain(int argc, char *argv[]) 19438032Speter{ 19538032Speter state_t initial_transition = runcom; 19638032Speter char kenv_value[PATH_MAX]; 19738032Speter int c; 19838032Speter struct sigaction sa; 19938032Speter sigset_t mask; 20064562Sgshapiro 20138032Speter /* Dispose of random users. */ 20264562Sgshapiro if (getuid() != 0) 20338032Speter errx(1, "%s", strerror(EPERM)); 20438032Speter 20538032Speter /* System V users like to reexec init. */ 20638032Speter if (getpid() != 1) { 20738032Speter#ifdef COMPAT_SYSV_INIT 20838032Speter /* So give them what they want */ 20938032Speter if (argc > 1) { 21038032Speter if (strlen(argv[1]) == 1) { 21138032Speter char runlevel = *argv[1]; 21238032Speter int sig; 21338032Speter 21438032Speter switch (runlevel) { 21538032Speter case '0': /* halt + poweroff */ 21638032Speter sig = SIGUSR2; 21764562Sgshapiro break; 21838032Speter case '1': /* single-user */ 21938032Speter sig = SIGTERM; 22038032Speter break; 22138032Speter case '6': /* reboot */ 22238032Speter sig = SIGINT; 22338032Speter break; 22438032Speter case 'c': /* block further logins */ 22538032Speter sig = SIGTSTP; 22638032Speter break; 22738032Speter case 'q': /* rescan /etc/ttys */ 22838032Speter sig = SIGHUP; 22938032Speter break; 23038032Speter default: 23138032Speter goto invalid; 23238032Speter } 23364562Sgshapiro kill(1, sig); 23438032Speter _exit(0); 23538032Speter } else 23638032Speterinvalid: 23738032Speter errx(1, "invalid run-level ``%s''", argv[1]); 23838032Speter } else 23938032Speter#endif 24038032Speter errx(1, "already running"); 24138032Speter } 24264562Sgshapiro /* 24364562Sgshapiro * Note that this does NOT open a file... 24438032Speter * Does 'init' deserve its own facility number? 24538032Speter */ 24638032Speter openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 24738032Speter 24838032Speter /* 24938032Speter * Create an initial session. 25038032Speter */ 25138032Speter if (setsid() < 0) 25238032Speter warning("initial setsid() failed: %m"); 25338032Speter 25438032Speter /* 25538032Speter * Establish an initial user so that programs running 25638032Speter * single user do not freak out and die (like passwd). 25738032Speter */ 25838032Speter if (setlogin("root") < 0) 25938032Speter warning("setlogin() failed: %m"); 26038032Speter 26138032Speter /* 26238032Speter * This code assumes that we always get arguments through flags, 26338032Speter * never through bits set in some random machine register. 26438032Speter */ 26538032Speter while ((c = getopt(argc, argv, "dsf")) != -1) 26638032Speter switch (c) { 26738032Speter case 'd': 26864562Sgshapiro devfs = 1; 26938032Speter break; 27038032Speter case 's': 27138032Speter initial_transition = single_user; 27238032Speter break; 27338032Speter case 'f': 27438032Speter runcom_mode = FASTBOOT; 27538032Speter break; 27638032Speter default: 27738032Speter warning("unrecognized flag '-%c'", c); 27838032Speter break; 27938032Speter } 28038032Speter 28164562Sgshapiro if (optind != argc) 28238032Speter warning("ignoring excess arguments"); 28364562Sgshapiro 28464562Sgshapiro /* 28564562Sgshapiro * We catch or block signals rather than ignore them, 28664562Sgshapiro * so that they get reset on exec. 28764562Sgshapiro */ 28864562Sgshapiro handle(badsys, SIGSYS, 0); 28964562Sgshapiro handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU, 29064562Sgshapiro SIGXFSZ, 0); 29164562Sgshapiro handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGUSR1, 29264562Sgshapiro SIGUSR2, 0); 29364562Sgshapiro handle(alrm_handler, SIGALRM, 0); 29464562Sgshapiro sigfillset(&mask); 29564562Sgshapiro delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 29664562Sgshapiro SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM, 29764562Sgshapiro SIGUSR1, SIGUSR2, 0); 29864562Sgshapiro sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 29964562Sgshapiro sigemptyset(&sa.sa_mask); 30064562Sgshapiro sa.sa_flags = 0; 30164562Sgshapiro sa.sa_handler = SIG_IGN; 30264562Sgshapiro sigaction(SIGTTIN, &sa, (struct sigaction *)0); 30364562Sgshapiro sigaction(SIGTTOU, &sa, (struct sigaction *)0); 30464562Sgshapiro 30564562Sgshapiro /* 30664562Sgshapiro * Paranoia. 30764562Sgshapiro */ 30864562Sgshapiro close(0); 30964562Sgshapiro close(1); 31064562Sgshapiro close(2); 31164562Sgshapiro 31264562Sgshapiro if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) { 31364562Sgshapiro state_func_t next_transition; 31464562Sgshapiro 31564562Sgshapiro if ((next_transition = run_script(kenv_value)) != 0) 31664562Sgshapiro initial_transition = (state_t) next_transition; 31764562Sgshapiro } 31864562Sgshapiro 31964562Sgshapiro if (kenv(KENV_GET, "init_chroot", kenv_value, sizeof(kenv_value)) > 0) { 32064562Sgshapiro if (chdir(kenv_value) != 0 || chroot(".") != 0) 32164562Sgshapiro warning("Can't chroot to %s: %m", kenv_value); 32264562Sgshapiro } 32364562Sgshapiro 32464562Sgshapiro /* 32564562Sgshapiro * Additional check if devfs needs to be mounted: 32664562Sgshapiro * If "/" and "/dev" have the same device number, 32764562Sgshapiro * then it hasn't been mounted yet. 32864562Sgshapiro */ 32964562Sgshapiro if (!devfs) { 33064562Sgshapiro struct stat stst; 33164562Sgshapiro dev_t root_devno; 33264562Sgshapiro 33364562Sgshapiro stat("/", &stst); 33464562Sgshapiro root_devno = stst.st_dev; 33564562Sgshapiro if (stat("/dev", &stst) != 0) 33664562Sgshapiro warning("Can't stat /dev: %m"); 33764562Sgshapiro else if (stst.st_dev == root_devno) 33864562Sgshapiro devfs++; 33964562Sgshapiro } 34064562Sgshapiro 34164562Sgshapiro if (devfs) { 34264562Sgshapiro struct iovec iov[4]; 34364562Sgshapiro char *s; 34464562Sgshapiro int i; 34564562Sgshapiro 34664562Sgshapiro char _fstype[] = "fstype"; 34764562Sgshapiro char _devfs[] = "devfs"; 34864562Sgshapiro char _fspath[] = "fspath"; 34964562Sgshapiro char _path_dev[]= _PATH_DEV; 35038032Speter 35138032Speter iov[0].iov_base = _fstype; 35238032Speter iov[0].iov_len = sizeof(_fstype); 35338032Speter iov[1].iov_base = _devfs; 35438032Speter iov[1].iov_len = sizeof(_devfs); 35538032Speter iov[2].iov_base = _fspath; 35638032Speter iov[2].iov_len = sizeof(_fspath); 35738032Speter /* 35838032Speter * Try to avoid the trailing slash in _PATH_DEV. 35938032Speter * Be *very* defensive. 36038032Speter */ 36138032Speter s = strdup(_PATH_DEV); 36238032Speter if (s != NULL) { 36364562Sgshapiro i = strlen(s); 36438032Speter if (i > 0 && s[i - 1] == '/') 36538032Speter s[i - 1] = '\0'; 36638032Speter iov[3].iov_base = s; 36738032Speter iov[3].iov_len = strlen(s) + 1; 36838032Speter } else { 36938032Speter iov[3].iov_base = _path_dev; 37038032Speter iov[3].iov_len = sizeof(_path_dev); 37138032Speter } 37238032Speter nmount(iov, 4, 0); 37338032Speter if (s != NULL) 37464562Sgshapiro free(s); 37564562Sgshapiro } 37664562Sgshapiro 37764562Sgshapiro /* 37864562Sgshapiro * Start the state machine. 37964562Sgshapiro */ 38038032Speter transition(initial_transition); 38164562Sgshapiro 38238032Speter /* 38338032Speter * Should never reach here. 38438032Speter */ 38538032Speter return 1; 38664562Sgshapiro} 38738032Speter 38838032Speter/* 38938032Speter * Associate a function with a signal handler. 39038032Speter */ 39138032Speterstatic void 39238032Speterhandle(sig_t handler, ...) 39338032Speter{ 39438032Speter int sig; 39538032Speter struct sigaction sa; 39638032Speter sigset_t mask_everything; 39738032Speter va_list ap; 39838032Speter va_start(ap, handler); 39938032Speter 40038032Speter sa.sa_handler = handler; 40138032Speter sigfillset(&mask_everything); 40238032Speter 40338032Speter while ((sig = va_arg(ap, int)) != 0) { 40464562Sgshapiro sa.sa_mask = mask_everything; 40564562Sgshapiro /* XXX SA_RESTART? */ 40664562Sgshapiro sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 40764562Sgshapiro sigaction(sig, &sa, (struct sigaction *) 0); 40864562Sgshapiro } 40964562Sgshapiro va_end(ap); 41064562Sgshapiro} 41164562Sgshapiro 41264562Sgshapiro/* 41364562Sgshapiro * Delete a set of signals from a mask. 41464562Sgshapiro */ 41564562Sgshapirostatic void 41664562Sgshapirodelset(sigset_t *maskp, ...) 41764562Sgshapiro{ 41864562Sgshapiro int sig; 41964562Sgshapiro va_list ap; 42064562Sgshapiro va_start(ap, maskp); 42164562Sgshapiro 42264562Sgshapiro while ((sig = va_arg(ap, int)) != 0) 42364562Sgshapiro sigdelset(maskp, sig); 42464562Sgshapiro va_end(ap); 42564562Sgshapiro} 42664562Sgshapiro 42764562Sgshapiro/* 42864562Sgshapiro * Log a message and sleep for a while (to give someone an opportunity 42964562Sgshapiro * to read it and to save log or hardcopy output if the problem is chronic). 43064562Sgshapiro * NB: should send a message to the session logger to avoid blocking. 43164562Sgshapiro */ 43264562Sgshapirostatic void 43364562Sgshapirostall(const char *message, ...) 43464562Sgshapiro{ 43564562Sgshapiro va_list ap; 43664562Sgshapiro va_start(ap, message); 43764562Sgshapiro 43864562Sgshapiro vsyslog(LOG_ALERT, message, ap); 43964562Sgshapiro va_end(ap); 44064562Sgshapiro sleep(STALL_TIMEOUT); 44164562Sgshapiro} 44264562Sgshapiro 44364562Sgshapiro/* 44464562Sgshapiro * Like stall(), but doesn't sleep. 44564562Sgshapiro * If cpp had variadic macros, the two functions could be #defines for another. 44664562Sgshapiro * NB: should send a message to the session logger to avoid blocking. 44738032Speter */ 44864562Sgshapirostatic void 44964562Sgshapirowarning(const char *message, ...) 45038032Speter{ 45164562Sgshapiro va_list ap; 45264562Sgshapiro va_start(ap, message); 45364562Sgshapiro 45464562Sgshapiro vsyslog(LOG_ALERT, message, ap); 45564562Sgshapiro va_end(ap); 45664562Sgshapiro} 45764562Sgshapiro 45864562Sgshapiro/* 45964562Sgshapiro * Log an emergency message. 46064562Sgshapiro * NB: should send a message to the session logger to avoid blocking. 46164562Sgshapiro */ 46264562Sgshapirostatic void 46364562Sgshapiroemergency(const char *message, ...) 46464562Sgshapiro{ 46564562Sgshapiro va_list ap; 46664562Sgshapiro va_start(ap, message); 46764562Sgshapiro 46864562Sgshapiro vsyslog(LOG_EMERG, message, ap); 46964562Sgshapiro va_end(ap); 47064562Sgshapiro} 47164562Sgshapiro 47264562Sgshapiro/* 47364562Sgshapiro * Catch a SIGSYS signal. 47464562Sgshapiro * 47564562Sgshapiro * These may arise if a system does not support sysctl. 47664562Sgshapiro * We tolerate up to 25 of these, then throw in the towel. 47764562Sgshapiro */ 47864562Sgshapirostatic void 47964562Sgshapirobadsys(int sig) 48064562Sgshapiro{ 48164562Sgshapiro static int badcount = 0; 48264562Sgshapiro 48364562Sgshapiro if (badcount++ < 25) 48464562Sgshapiro return; 48564562Sgshapiro disaster(sig); 48664562Sgshapiro} 48764562Sgshapiro 48864562Sgshapiro/* 48964562Sgshapiro * Catch an unexpected signal. 49064562Sgshapiro */ 49164562Sgshapirostatic void 49264562Sgshapirodisaster(int sig) 49364562Sgshapiro{ 49464562Sgshapiro 49564562Sgshapiro emergency("fatal signal: %s", 49664562Sgshapiro (unsigned)sig < NSIG ? sys_siglist[sig] : "unknown signal"); 49764562Sgshapiro 49864562Sgshapiro sleep(STALL_TIMEOUT); 49964562Sgshapiro _exit(sig); /* reboot */ 50064562Sgshapiro} 50164562Sgshapiro 50264562Sgshapiro/* 50364562Sgshapiro * Get the security level of the kernel. 50464562Sgshapiro */ 50564562Sgshapirostatic int 50664562Sgshapirogetsecuritylevel(void) 50764562Sgshapiro{ 50864562Sgshapiro#ifdef KERN_SECURELVL 50964562Sgshapiro int name[2], curlevel; 51064562Sgshapiro size_t len; 51164562Sgshapiro 51264562Sgshapiro name[0] = CTL_KERN; 51364562Sgshapiro name[1] = KERN_SECURELVL; 51464562Sgshapiro len = sizeof curlevel; 51564562Sgshapiro if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 51664562Sgshapiro emergency("cannot get kernel security level: %s", 51764562Sgshapiro strerror(errno)); 51864562Sgshapiro return (-1); 51964562Sgshapiro } 52064562Sgshapiro return (curlevel); 52164562Sgshapiro#else 52264562Sgshapiro return (-1); 52364562Sgshapiro#endif 52464562Sgshapiro} 52564562Sgshapiro 52664562Sgshapiro/* 52764562Sgshapiro * Set the security level of the kernel. 52864562Sgshapiro */ 52964562Sgshapirostatic void 53064562Sgshapirosetsecuritylevel(int newlevel) 53164562Sgshapiro{ 53264562Sgshapiro#ifdef KERN_SECURELVL 53364562Sgshapiro int name[2], curlevel; 53464562Sgshapiro 53564562Sgshapiro curlevel = getsecuritylevel(); 53664562Sgshapiro if (newlevel == curlevel) 53764562Sgshapiro return; 53864562Sgshapiro name[0] = CTL_KERN; 53964562Sgshapiro name[1] = KERN_SECURELVL; 54064562Sgshapiro if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 54164562Sgshapiro emergency( 54264562Sgshapiro "cannot change kernel security level from %d to %d: %s", 54364562Sgshapiro curlevel, newlevel, strerror(errno)); 54464562Sgshapiro return; 54564562Sgshapiro } 54664562Sgshapiro#ifdef SECURE 54764562Sgshapiro warning("kernel security level changed from %d to %d", 54864562Sgshapiro curlevel, newlevel); 54964562Sgshapiro#endif 55064562Sgshapiro#endif 55164562Sgshapiro} 55264562Sgshapiro 55364562Sgshapiro/* 55464562Sgshapiro * Change states in the finite state machine. 55564562Sgshapiro * The initial state is passed as an argument. 55664562Sgshapiro */ 55764562Sgshapirostatic void 55864562Sgshapirotransition(state_t s) 55964562Sgshapiro{ 56064562Sgshapiro 56164562Sgshapiro current_state = s; 56264562Sgshapiro for (;;) 56364562Sgshapiro current_state = (state_t) (*current_state)(); 56464562Sgshapiro} 56564562Sgshapiro 56664562Sgshapiro/* 56764562Sgshapiro * Start a session and allocate a controlling terminal. 56864562Sgshapiro * Only called by children of init after forking. 56964562Sgshapiro */ 57064562Sgshapirostatic void 57164562Sgshapirosetctty(const char *name) 57264562Sgshapiro{ 57364562Sgshapiro int fd; 57464562Sgshapiro 57564562Sgshapiro revoke(name); 57664562Sgshapiro if ((fd = open(name, O_RDWR)) == -1) { 57764562Sgshapiro stall("can't open %s: %m", name); 57864562Sgshapiro _exit(1); 57964562Sgshapiro } 58064562Sgshapiro if (login_tty(fd) == -1) { 58164562Sgshapiro stall("can't get %s for controlling terminal: %m", name); 58264562Sgshapiro _exit(1); 58364562Sgshapiro } 58464562Sgshapiro} 58564562Sgshapiro 58664562Sgshapirostatic const char * 58764562Sgshapiroget_shell(void) 58864562Sgshapiro{ 58964562Sgshapiro static char kenv_value[PATH_MAX]; 59064562Sgshapiro 59164562Sgshapiro if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0) 59264562Sgshapiro return kenv_value; 59364562Sgshapiro else 59464562Sgshapiro return _PATH_BSHELL; 59564562Sgshapiro} 59664562Sgshapiro 59764562Sgshapirostatic void 59864562Sgshapirowrite_stderr(const char *message) 59964562Sgshapiro{ 60064562Sgshapiro 60164562Sgshapiro write(STDERR_FILENO, message, strlen(message)); 60264562Sgshapiro} 60364562Sgshapiro 60464562Sgshapiro/* 60564562Sgshapiro * Bring the system up single user. 60664562Sgshapiro */ 60764562Sgshapirostatic state_func_t 60864562Sgshapirosingle_user(void) 60964562Sgshapiro{ 61064562Sgshapiro pid_t pid, wpid; 61164562Sgshapiro int status; 61264562Sgshapiro sigset_t mask; 61364562Sgshapiro const char *shell; 61464562Sgshapiro char *argv[2]; 61564562Sgshapiro#ifdef SECURE 61664562Sgshapiro struct ttyent *typ; 61764562Sgshapiro struct passwd *pp; 61864562Sgshapiro static const char banner[] = 61964562Sgshapiro "Enter root password, or ^D to go multi-user\n"; 62064562Sgshapiro char *clear, *password; 62164562Sgshapiro#endif 62264562Sgshapiro#ifdef DEBUGSHELL 62364562Sgshapiro char altshell[128]; 62464562Sgshapiro#endif 62564562Sgshapiro 62664562Sgshapiro if (Reboot) { 62764562Sgshapiro /* Instead of going single user, let's reboot the machine */ 62864562Sgshapiro sync(); 62964562Sgshapiro alarm(2); 63064562Sgshapiro pause(); 63164562Sgshapiro reboot(howto); 63264562Sgshapiro _exit(0); 63364562Sgshapiro } 63464562Sgshapiro 63564562Sgshapiro shell = get_shell(); 63664562Sgshapiro 63764562Sgshapiro if ((pid = fork()) == 0) { 63864562Sgshapiro /* 63964562Sgshapiro * Start the single user session. 64064562Sgshapiro */ 64164562Sgshapiro setctty(_PATH_CONSOLE); 64264562Sgshapiro 64364562Sgshapiro#ifdef SECURE 64464562Sgshapiro /* 64564562Sgshapiro * Check the root password. 64664562Sgshapiro * We don't care if the console is 'on' by default; 64764562Sgshapiro * it's the only tty that can be 'off' and 'secure'. 64864562Sgshapiro */ 64964562Sgshapiro typ = getttynam("console"); 65064562Sgshapiro pp = getpwnam("root"); 65164562Sgshapiro if (typ && (typ->ty_status & TTY_SECURE) == 0 && 65264562Sgshapiro pp && *pp->pw_passwd) { 65364562Sgshapiro write_stderr(banner); 65464562Sgshapiro for (;;) { 65564562Sgshapiro clear = getpass("Password:"); 65664562Sgshapiro if (clear == 0 || *clear == '\0') 65764562Sgshapiro _exit(0); 65864562Sgshapiro password = crypt(clear, pp->pw_passwd); 65964562Sgshapiro bzero(clear, _PASSWORD_LEN); 66064562Sgshapiro if (password == NULL || 66164562Sgshapiro strcmp(password, pp->pw_passwd) == 0) 66264562Sgshapiro break; 66364562Sgshapiro warning("single-user login failed\n"); 66464562Sgshapiro } 66564562Sgshapiro } 66664562Sgshapiro endttyent(); 66764562Sgshapiro endpwent(); 66864562Sgshapiro#endif /* SECURE */ 66964562Sgshapiro 67064562Sgshapiro#ifdef DEBUGSHELL 67164562Sgshapiro { 67264562Sgshapiro char *cp = altshell; 67364562Sgshapiro int num; 67464562Sgshapiro 67564562Sgshapiro#define SHREQUEST "Enter full pathname of shell or RETURN for " 67664562Sgshapiro write_stderr(SHREQUEST); 67764562Sgshapiro write_stderr(shell); 67864562Sgshapiro write_stderr(": "); 67964562Sgshapiro while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 68064562Sgshapiro num != 0 && *cp != '\n' && cp < &altshell[127]) 68164562Sgshapiro cp++; 68264562Sgshapiro *cp = '\0'; 68364562Sgshapiro if (altshell[0] != '\0') 68464562Sgshapiro shell = altshell; 68564562Sgshapiro } 68664562Sgshapiro#endif /* DEBUGSHELL */ 68764562Sgshapiro 68864562Sgshapiro /* 68964562Sgshapiro * Unblock signals. 69064562Sgshapiro * We catch all the interesting ones, 69164562Sgshapiro * and those are reset to SIG_DFL on exec. 69264562Sgshapiro */ 69364562Sgshapiro sigemptyset(&mask); 69464562Sgshapiro sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 69564562Sgshapiro 69664562Sgshapiro /* 69764562Sgshapiro * Fire off a shell. 69864562Sgshapiro * If the default one doesn't work, try the Bourne shell. 69964562Sgshapiro */ 70064562Sgshapiro 70164562Sgshapiro char name[] = "-sh"; 70264562Sgshapiro 70364562Sgshapiro argv[0] = name; 70464562Sgshapiro argv[1] = 0; 70564562Sgshapiro execv(shell, argv); 70664562Sgshapiro emergency("can't exec %s for single user: %m", shell); 70764562Sgshapiro execv(_PATH_BSHELL, argv); 70864562Sgshapiro emergency("can't exec %s for single user: %m", _PATH_BSHELL); 70964562Sgshapiro sleep(STALL_TIMEOUT); 71064562Sgshapiro _exit(1); 71164562Sgshapiro } 71264562Sgshapiro 71364562Sgshapiro if (pid == -1) { 71464562Sgshapiro /* 71564562Sgshapiro * We are seriously hosed. Do our best. 71664562Sgshapiro */ 71764562Sgshapiro emergency("can't fork single-user shell, trying again"); 71864562Sgshapiro while (waitpid(-1, (int *) 0, WNOHANG) > 0) 71964562Sgshapiro continue; 72064562Sgshapiro return (state_func_t) single_user; 72164562Sgshapiro } 72264562Sgshapiro 72364562Sgshapiro requested_transition = 0; 72464562Sgshapiro do { 72564562Sgshapiro if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 72664562Sgshapiro collect_child(wpid); 72764562Sgshapiro if (wpid == -1) { 72864562Sgshapiro if (errno == EINTR) 72964562Sgshapiro continue; 73064562Sgshapiro warning("wait for single-user shell failed: %m; restarting"); 73164562Sgshapiro return (state_func_t) single_user; 73264562Sgshapiro } 73364562Sgshapiro if (wpid == pid && WIFSTOPPED(status)) { 73464562Sgshapiro warning("init: shell stopped, restarting\n"); 73564562Sgshapiro kill(pid, SIGCONT); 73664562Sgshapiro wpid = -1; 73764562Sgshapiro } 73864562Sgshapiro } while (wpid != pid && !requested_transition); 73964562Sgshapiro 74064562Sgshapiro if (requested_transition) 74164562Sgshapiro return (state_func_t) requested_transition; 74264562Sgshapiro 74364562Sgshapiro if (!WIFEXITED(status)) { 74464562Sgshapiro if (WTERMSIG(status) == SIGKILL) { 74564562Sgshapiro /* 74664562Sgshapiro * reboot(8) killed shell? 74764562Sgshapiro */ 74864562Sgshapiro warning("single user shell terminated."); 74964562Sgshapiro sleep(STALL_TIMEOUT); 75064562Sgshapiro _exit(0); 75164562Sgshapiro } else { 75264562Sgshapiro warning("single user shell terminated, restarting"); 75364562Sgshapiro return (state_func_t) single_user; 75464562Sgshapiro } 75564562Sgshapiro } 75664562Sgshapiro 75764562Sgshapiro runcom_mode = FASTBOOT; 75864562Sgshapiro return (state_func_t) runcom; 75964562Sgshapiro} 76064562Sgshapiro 76164562Sgshapiro/* 76264562Sgshapiro * Run the system startup script. 76364562Sgshapiro */ 76464562Sgshapirostatic state_func_t 76564562Sgshapiroruncom(void) 76664562Sgshapiro{ 76764562Sgshapiro state_func_t next_transition; 76864562Sgshapiro 76964562Sgshapiro if ((next_transition = run_script(_PATH_RUNCOM)) != 0) 77064562Sgshapiro return next_transition; 77164562Sgshapiro 77264562Sgshapiro runcom_mode = AUTOBOOT; /* the default */ 77364562Sgshapiro return (state_func_t) read_ttys; 77464562Sgshapiro} 77564562Sgshapiro 77664562Sgshapiro/* 77764562Sgshapiro * Run a shell script. 77864562Sgshapiro * Returns 0 on success, otherwise the next transition to enter: 77964562Sgshapiro * - single_user if fork/execv/waitpid failed, or if the script 78064562Sgshapiro * terminated with a signal or exit code != 0. 78164562Sgshapiro * - death_single if a SIGTERM was delivered to init(8). 78264562Sgshapiro */ 78364562Sgshapirostatic state_func_t 78464562Sgshapirorun_script(const char *script) 78564562Sgshapiro{ 78664562Sgshapiro pid_t pid, wpid; 78764562Sgshapiro int status; 78864562Sgshapiro char *argv[4]; 78964562Sgshapiro const char *shell; 79064562Sgshapiro struct sigaction sa; 79164562Sgshapiro 79264562Sgshapiro shell = get_shell(); 79364562Sgshapiro 79464562Sgshapiro if ((pid = fork()) == 0) { 79564562Sgshapiro sigemptyset(&sa.sa_mask); 79664562Sgshapiro sa.sa_flags = 0; 79764562Sgshapiro sa.sa_handler = SIG_IGN; 79864562Sgshapiro sigaction(SIGTSTP, &sa, (struct sigaction *)0); 79964562Sgshapiro sigaction(SIGHUP, &sa, (struct sigaction *)0); 80064562Sgshapiro 80164562Sgshapiro setctty(_PATH_CONSOLE); 80264562Sgshapiro 80364562Sgshapiro char _sh[] = "sh"; 80464562Sgshapiro char _autoboot[] = "autoboot"; 80564562Sgshapiro 80664562Sgshapiro argv[0] = _sh; 80764562Sgshapiro argv[1] = __DECONST(char *, script); 80864562Sgshapiro argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0; 80964562Sgshapiro argv[3] = 0; 81064562Sgshapiro 81164562Sgshapiro sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 81264562Sgshapiro 81364562Sgshapiro#ifdef LOGIN_CAP 81464562Sgshapiro setprocresources(RESOURCE_RC); 81564562Sgshapiro#endif 81664562Sgshapiro execv(shell, argv); 81764562Sgshapiro stall("can't exec %s for %s: %m", shell, script); 81864562Sgshapiro _exit(1); /* force single user mode */ 81964562Sgshapiro } 82064562Sgshapiro 82164562Sgshapiro if (pid == -1) { 82264562Sgshapiro emergency("can't fork for %s on %s: %m", shell, script); 82364562Sgshapiro while (waitpid(-1, (int *) 0, WNOHANG) > 0) 82464562Sgshapiro continue; 82564562Sgshapiro sleep(STALL_TIMEOUT); 82664562Sgshapiro return (state_func_t) single_user; 82764562Sgshapiro } 82864562Sgshapiro 82964562Sgshapiro /* 83064562Sgshapiro * Copied from single_user(). This is a bit paranoid. 83164562Sgshapiro */ 83264562Sgshapiro requested_transition = 0; 83364562Sgshapiro do { 83464562Sgshapiro if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 83564562Sgshapiro collect_child(wpid); 83664562Sgshapiro if (wpid == -1) { 83764562Sgshapiro if (requested_transition == death_single) 83864562Sgshapiro return (state_func_t) death_single; 83964562Sgshapiro if (errno == EINTR) 84064562Sgshapiro continue; 84164562Sgshapiro warning("wait for %s on %s failed: %m; going to " 84264562Sgshapiro "single user mode", shell, script); 84364562Sgshapiro return (state_func_t) single_user; 84464562Sgshapiro } 84564562Sgshapiro if (wpid == pid && WIFSTOPPED(status)) { 84664562Sgshapiro warning("init: %s on %s stopped, restarting\n", 84764562Sgshapiro shell, script); 84864562Sgshapiro kill(pid, SIGCONT); 84964562Sgshapiro wpid = -1; 85064562Sgshapiro } 85164562Sgshapiro } while (wpid != pid); 85264562Sgshapiro 85364562Sgshapiro if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 85464562Sgshapiro requested_transition == catatonia) { 85564562Sgshapiro /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 85664562Sgshapiro sigset_t s; 85764562Sgshapiro 85864562Sgshapiro sigfillset(&s); 85964562Sgshapiro for (;;) 86064562Sgshapiro sigsuspend(&s); 86164562Sgshapiro } 86264562Sgshapiro 86364562Sgshapiro if (!WIFEXITED(status)) { 86464562Sgshapiro warning("%s on %s terminated abnormally, going to single " 86564562Sgshapiro "user mode", shell, script); 86664562Sgshapiro return (state_func_t) single_user; 86764562Sgshapiro } 86864562Sgshapiro 86964562Sgshapiro if (WEXITSTATUS(status)) 87064562Sgshapiro return (state_func_t) single_user; 87164562Sgshapiro 87264562Sgshapiro return (state_func_t) 0; 87364562Sgshapiro} 87464562Sgshapiro 87564562Sgshapiro/* 87664562Sgshapiro * Open the session database. 87764562Sgshapiro * 87864562Sgshapiro * NB: We could pass in the size here; is it necessary? 87964562Sgshapiro */ 88064562Sgshapirostatic int 88164562Sgshapirostart_session_db(void) 88264562Sgshapiro{ 88364562Sgshapiro if (session_db && (*session_db->close)(session_db)) 88464562Sgshapiro emergency("session database close: %s", strerror(errno)); 88564562Sgshapiro if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 88664562Sgshapiro emergency("session database open: %s", strerror(errno)); 88764562Sgshapiro return (1); 88864562Sgshapiro } 88964562Sgshapiro return (0); 89064562Sgshapiro 89164562Sgshapiro} 89264562Sgshapiro 89364562Sgshapiro/* 89464562Sgshapiro * Add a new login session. 89564562Sgshapiro */ 89664562Sgshapirostatic void 89764562Sgshapiroadd_session(session_t *sp) 89864562Sgshapiro{ 89964562Sgshapiro DBT key; 90064562Sgshapiro DBT data; 90164562Sgshapiro 90264562Sgshapiro key.data = &sp->se_process; 90364562Sgshapiro key.size = sizeof sp->se_process; 90464562Sgshapiro data.data = &sp; 90564562Sgshapiro data.size = sizeof sp; 90664562Sgshapiro 90764562Sgshapiro if ((*session_db->put)(session_db, &key, &data, 0)) 90864562Sgshapiro emergency("insert %d: %s", sp->se_process, strerror(errno)); 90966494Sgshapiro} 91066494Sgshapiro 91164562Sgshapiro/* 91264562Sgshapiro * Delete an old login session. 91364562Sgshapiro */ 91464562Sgshapirostatic void 91566494Sgshapirodel_session(session_t *sp) 91664562Sgshapiro{ 91764562Sgshapiro DBT key; 91864562Sgshapiro 91964562Sgshapiro key.data = &sp->se_process; 92064562Sgshapiro key.size = sizeof sp->se_process; 92164562Sgshapiro 92264562Sgshapiro if ((*session_db->del)(session_db, &key, 0)) 92364562Sgshapiro emergency("delete %d: %s", sp->se_process, strerror(errno)); 92464562Sgshapiro} 92564562Sgshapiro 92664562Sgshapiro/* 92764562Sgshapiro * Look up a login session by pid. 92864562Sgshapiro */ 92964562Sgshapirostatic session_t * 93064562Sgshapirofind_session(pid_t pid) 93164562Sgshapiro{ 93264562Sgshapiro DBT key; 93364562Sgshapiro DBT data; 93464562Sgshapiro session_t *ret; 93564562Sgshapiro 93664562Sgshapiro key.data = &pid; 93764562Sgshapiro key.size = sizeof pid; 93864562Sgshapiro if ((*session_db->get)(session_db, &key, &data, 0) != 0) 93964562Sgshapiro return 0; 94064562Sgshapiro bcopy(data.data, (char *)&ret, sizeof(ret)); 94164562Sgshapiro return ret; 94264562Sgshapiro} 94364562Sgshapiro 94464562Sgshapiro/* 94564562Sgshapiro * Construct an argument vector from a command line. 94664562Sgshapiro */ 94764562Sgshapirostatic char ** 94864562Sgshapiroconstruct_argv(char *command) 94964562Sgshapiro{ 95064562Sgshapiro int argc = 0; 95164562Sgshapiro char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 95264562Sgshapiro * sizeof (char *)); 95364562Sgshapiro 95464562Sgshapiro if ((argv[argc++] = strk(command)) == 0) { 95564562Sgshapiro free(argv); 95664562Sgshapiro return (NULL); 95764562Sgshapiro } 95864562Sgshapiro while ((argv[argc++] = strk((char *) 0)) != NULL) 95964562Sgshapiro continue; 96064562Sgshapiro return argv; 96164562Sgshapiro} 96264562Sgshapiro 96364562Sgshapiro/* 96464562Sgshapiro * Deallocate a session descriptor. 96564562Sgshapiro */ 96664562Sgshapirostatic void 96764562Sgshapirofree_session(session_t *sp) 96864562Sgshapiro{ 96964562Sgshapiro free(sp->se_device); 97064562Sgshapiro if (sp->se_getty) { 97164562Sgshapiro free(sp->se_getty); 97264562Sgshapiro free(sp->se_getty_argv_space); 97364562Sgshapiro free(sp->se_getty_argv); 97464562Sgshapiro } 97564562Sgshapiro if (sp->se_window) { 97664562Sgshapiro free(sp->se_window); 97764562Sgshapiro free(sp->se_window_argv_space); 97864562Sgshapiro free(sp->se_window_argv); 97964562Sgshapiro } 98064562Sgshapiro if (sp->se_type) 98164562Sgshapiro free(sp->se_type); 98264562Sgshapiro free(sp); 98364562Sgshapiro} 98464562Sgshapiro 98564562Sgshapiro/* 98664562Sgshapiro * Allocate a new session descriptor. 98764562Sgshapiro * Mark it SE_PRESENT. 98864562Sgshapiro */ 98964562Sgshapirostatic session_t * 99064562Sgshapironew_session(session_t *sprev, int session_index, struct ttyent *typ) 99164562Sgshapiro{ 99264562Sgshapiro session_t *sp; 99364562Sgshapiro int fd; 99464562Sgshapiro 99564562Sgshapiro if ((typ->ty_status & TTY_ON) == 0 || 99664562Sgshapiro typ->ty_name == 0 || 99764562Sgshapiro typ->ty_getty == 0) 99864562Sgshapiro return 0; 99964562Sgshapiro 100064562Sgshapiro sp = (session_t *) calloc(1, sizeof (session_t)); 100164562Sgshapiro 100264562Sgshapiro sp->se_index = session_index; 100364562Sgshapiro sp->se_flags |= SE_PRESENT; 100464562Sgshapiro 100564562Sgshapiro sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 100664562Sgshapiro sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 100764562Sgshapiro 100864562Sgshapiro /* 100964562Sgshapiro * Attempt to open the device, if we get "device not configured" 101064562Sgshapiro * then don't add the device to the session list. 101164562Sgshapiro */ 101264562Sgshapiro if ((fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0)) < 0) { 101364562Sgshapiro if (errno == ENXIO) { 101464562Sgshapiro free_session(sp); 101564562Sgshapiro return (0); 101664562Sgshapiro } 101764562Sgshapiro } else 101864562Sgshapiro close(fd); 101964562Sgshapiro 102064562Sgshapiro if (setupargv(sp, typ) == 0) { 102164562Sgshapiro free_session(sp); 102264562Sgshapiro return (0); 102364562Sgshapiro } 102464562Sgshapiro 102564562Sgshapiro sp->se_next = 0; 102664562Sgshapiro if (sprev == 0) { 102764562Sgshapiro sessions = sp; 102864562Sgshapiro sp->se_prev = 0; 102964562Sgshapiro } else { 103064562Sgshapiro sprev->se_next = sp; 103164562Sgshapiro sp->se_prev = sprev; 103264562Sgshapiro } 103364562Sgshapiro 103464562Sgshapiro return sp; 103564562Sgshapiro} 103664562Sgshapiro 103764562Sgshapiro/* 103864562Sgshapiro * Calculate getty and if useful window argv vectors. 103964562Sgshapiro */ 104064562Sgshapirostatic int 104164562Sgshapirosetupargv(session_t *sp, struct ttyent *typ) 104264562Sgshapiro{ 104364562Sgshapiro 104464562Sgshapiro if (sp->se_getty) { 104564562Sgshapiro free(sp->se_getty); 104664562Sgshapiro free(sp->se_getty_argv_space); 104764562Sgshapiro free(sp->se_getty_argv); 104864562Sgshapiro } 104964562Sgshapiro sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); 105064562Sgshapiro sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); 105164562Sgshapiro sp->se_getty_argv_space = strdup(sp->se_getty); 105264562Sgshapiro sp->se_getty_argv = construct_argv(sp->se_getty_argv_space); 105364562Sgshapiro if (sp->se_getty_argv == 0) { 105464562Sgshapiro warning("can't parse getty for port %s", sp->se_device); 105564562Sgshapiro free(sp->se_getty); 105664562Sgshapiro free(sp->se_getty_argv_space); 105764562Sgshapiro sp->se_getty = sp->se_getty_argv_space = 0; 105864562Sgshapiro return (0); 105964562Sgshapiro } 106064562Sgshapiro if (sp->se_window) { 106164562Sgshapiro free(sp->se_window); 106264562Sgshapiro free(sp->se_window_argv_space); 106364562Sgshapiro free(sp->se_window_argv); 106464562Sgshapiro } 106564562Sgshapiro sp->se_window = sp->se_window_argv_space = 0; 106664562Sgshapiro sp->se_window_argv = 0; 106764562Sgshapiro if (typ->ty_window) { 106864562Sgshapiro sp->se_window = strdup(typ->ty_window); 106964562Sgshapiro sp->se_window_argv_space = strdup(sp->se_window); 107064562Sgshapiro sp->se_window_argv = construct_argv(sp->se_window_argv_space); 107164562Sgshapiro if (sp->se_window_argv == 0) { 107264562Sgshapiro warning("can't parse window for port %s", 107364562Sgshapiro sp->se_device); 107464562Sgshapiro free(sp->se_window_argv_space); 107564562Sgshapiro free(sp->se_window); 107664562Sgshapiro sp->se_window = sp->se_window_argv_space = 0; 107764562Sgshapiro return (0); 107864562Sgshapiro } 107964562Sgshapiro } 108064562Sgshapiro if (sp->se_type) 108164562Sgshapiro free(sp->se_type); 108264562Sgshapiro sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0; 108364562Sgshapiro return (1); 108464562Sgshapiro} 108564562Sgshapiro 108664562Sgshapiro/* 108764562Sgshapiro * Walk the list of ttys and create sessions for each active line. 108864562Sgshapiro */ 108964562Sgshapirostatic state_func_t 109064562Sgshapiroread_ttys(void) 109164562Sgshapiro{ 109264562Sgshapiro int session_index = 0; 109364562Sgshapiro session_t *sp, *snext; 109464562Sgshapiro struct ttyent *typ; 109564562Sgshapiro 109664562Sgshapiro /* 109764562Sgshapiro * Destroy any previous session state. 109864562Sgshapiro * There shouldn't be any, but just in case... 109964562Sgshapiro */ 110064562Sgshapiro for (sp = sessions; sp; sp = snext) { 110164562Sgshapiro snext = sp->se_next; 110264562Sgshapiro free_session(sp); 110364562Sgshapiro } 110464562Sgshapiro sessions = 0; 110564562Sgshapiro if (start_session_db()) 110664562Sgshapiro return (state_func_t) single_user; 110764562Sgshapiro 110864562Sgshapiro /* 110964562Sgshapiro * Allocate a session entry for each active port. 111064562Sgshapiro * Note that sp starts at 0. 111164562Sgshapiro */ 111264562Sgshapiro while ((typ = getttyent()) != NULL) 111364562Sgshapiro if ((snext = new_session(sp, ++session_index, typ)) != NULL) 111464562Sgshapiro sp = snext; 111564562Sgshapiro 111664562Sgshapiro endttyent(); 111764562Sgshapiro 111864562Sgshapiro return (state_func_t) multi_user; 111964562Sgshapiro} 112064562Sgshapiro 112164562Sgshapiro/* 112264562Sgshapiro * Start a window system running. 112364562Sgshapiro */ 112464562Sgshapirostatic void 112564562Sgshapirostart_window_system(session_t *sp) 112664562Sgshapiro{ 112764562Sgshapiro pid_t pid; 112864562Sgshapiro sigset_t mask; 112964562Sgshapiro char term[64], *env[2]; 113064562Sgshapiro int status; 113164562Sgshapiro 113264562Sgshapiro if ((pid = fork()) == -1) { 113364562Sgshapiro emergency("can't fork for window system on port %s: %m", 113464562Sgshapiro sp->se_device); 113564562Sgshapiro /* hope that getty fails and we can try again */ 113664562Sgshapiro return; 113764562Sgshapiro } 113864562Sgshapiro if (pid) { 113964562Sgshapiro waitpid(-1, &status, 0); 114064562Sgshapiro return; 114164562Sgshapiro } 114264562Sgshapiro 114364562Sgshapiro /* reparent window process to the init to not make a zombie on exit */ 114464562Sgshapiro if ((pid = fork()) == -1) { 114564562Sgshapiro emergency("can't fork for window system on port %s: %m", 114664562Sgshapiro sp->se_device); 114764562Sgshapiro _exit(1); 114864562Sgshapiro } 114964562Sgshapiro if (pid) 115064562Sgshapiro _exit(0); 115164562Sgshapiro 115264562Sgshapiro sigemptyset(&mask); 115364562Sgshapiro sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 115464562Sgshapiro 115564562Sgshapiro if (setsid() < 0) 115664562Sgshapiro emergency("setsid failed (window) %m"); 115764562Sgshapiro 115864562Sgshapiro#ifdef LOGIN_CAP 115964562Sgshapiro setprocresources(RESOURCE_WINDOW); 116064562Sgshapiro#endif 116164562Sgshapiro if (sp->se_type) { 116264562Sgshapiro /* Don't use malloc after fork */ 116364562Sgshapiro strcpy(term, "TERM="); 116464562Sgshapiro strncat(term, sp->se_type, sizeof(term) - 6); 116564562Sgshapiro env[0] = term; 116664562Sgshapiro env[1] = 0; 116764562Sgshapiro } 116864562Sgshapiro else 116964562Sgshapiro env[0] = 0; 117064562Sgshapiro execve(sp->se_window_argv[0], sp->se_window_argv, env); 117164562Sgshapiro stall("can't exec window system '%s' for port %s: %m", 117264562Sgshapiro sp->se_window_argv[0], sp->se_device); 117364562Sgshapiro _exit(1); 117464562Sgshapiro} 117564562Sgshapiro 117664562Sgshapiro/* 117764562Sgshapiro * Start a login session running. 117864562Sgshapiro */ 117964562Sgshapirostatic pid_t 118064562Sgshapirostart_getty(session_t *sp) 118164562Sgshapiro{ 118264562Sgshapiro pid_t pid; 118364562Sgshapiro sigset_t mask; 118464562Sgshapiro time_t current_time = time((time_t *) 0); 118564562Sgshapiro int too_quick = 0; 118664562Sgshapiro char term[64], *env[2]; 118764562Sgshapiro 118864562Sgshapiro if (current_time >= sp->se_started && 118964562Sgshapiro current_time - sp->se_started < GETTY_SPACING) { 119071345Sgshapiro if (++sp->se_nspace > GETTY_NSPACE) { 119164562Sgshapiro sp->se_nspace = 0; 119264562Sgshapiro too_quick = 1; 119364562Sgshapiro } 119464562Sgshapiro } else 119564562Sgshapiro sp->se_nspace = 0; 119664562Sgshapiro 119764562Sgshapiro /* 119864562Sgshapiro * fork(), not vfork() -- we can't afford to block. 119964562Sgshapiro */ 120064562Sgshapiro if ((pid = fork()) == -1) { 120164562Sgshapiro emergency("can't fork for getty on port %s: %m", sp->se_device); 120264562Sgshapiro return -1; 120364562Sgshapiro } 120464562Sgshapiro 120564562Sgshapiro if (pid) 120664562Sgshapiro return pid; 120764562Sgshapiro 120864562Sgshapiro if (too_quick) { 120964562Sgshapiro warning("getty repeating too quickly on port %s, sleeping %d secs", 121064562Sgshapiro sp->se_device, GETTY_SLEEP); 121164562Sgshapiro sleep((unsigned) GETTY_SLEEP); 121264562Sgshapiro } 121364562Sgshapiro 121464562Sgshapiro if (sp->se_window) { 121564562Sgshapiro start_window_system(sp); 121664562Sgshapiro sleep(WINDOW_WAIT); 121764562Sgshapiro } 121864562Sgshapiro 121964562Sgshapiro sigemptyset(&mask); 122064562Sgshapiro sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 122164562Sgshapiro 122264562Sgshapiro#ifdef LOGIN_CAP 122364562Sgshapiro setprocresources(RESOURCE_GETTY); 122464562Sgshapiro#endif 122564562Sgshapiro if (sp->se_type) { 122664562Sgshapiro /* Don't use malloc after fork */ 122764562Sgshapiro strcpy(term, "TERM="); 122864562Sgshapiro strncat(term, sp->se_type, sizeof(term) - 6); 122964562Sgshapiro env[0] = term; 123064562Sgshapiro env[1] = 0; 123164562Sgshapiro } else 123264562Sgshapiro env[0] = 0; 123364562Sgshapiro execve(sp->se_getty_argv[0], sp->se_getty_argv, env); 123464562Sgshapiro stall("can't exec getty '%s' for port %s: %m", 123564562Sgshapiro sp->se_getty_argv[0], sp->se_device); 123664562Sgshapiro _exit(1); 123764562Sgshapiro} 123864562Sgshapiro 123964562Sgshapiro/* 124064562Sgshapiro * Collect exit status for a child. 124164562Sgshapiro * If an exiting login, start a new login running. 124264562Sgshapiro */ 124364562Sgshapirostatic void 124464562Sgshapirocollect_child(pid_t pid) 124564562Sgshapiro{ 124664562Sgshapiro session_t *sp, *sprev, *snext; 124764562Sgshapiro 124864562Sgshapiro if (! sessions) 124964562Sgshapiro return; 125064562Sgshapiro 125164562Sgshapiro if (! (sp = find_session(pid))) 125264562Sgshapiro return; 125364562Sgshapiro 125464562Sgshapiro del_session(sp); 125564562Sgshapiro sp->se_process = 0; 125664562Sgshapiro 125764562Sgshapiro if (sp->se_flags & SE_SHUTDOWN) { 125864562Sgshapiro if ((sprev = sp->se_prev) != NULL) 125964562Sgshapiro sprev->se_next = sp->se_next; 126064562Sgshapiro else 126164562Sgshapiro sessions = sp->se_next; 126264562Sgshapiro if ((snext = sp->se_next) != NULL) 126364562Sgshapiro snext->se_prev = sp->se_prev; 126464562Sgshapiro free_session(sp); 126564562Sgshapiro return; 126664562Sgshapiro } 126764562Sgshapiro 126864562Sgshapiro if ((pid = start_getty(sp)) == -1) { 126964562Sgshapiro /* serious trouble */ 127064562Sgshapiro requested_transition = clean_ttys; 127164562Sgshapiro return; 127264562Sgshapiro } 127364562Sgshapiro 127464562Sgshapiro sp->se_process = pid; 127564562Sgshapiro sp->se_started = time((time_t *) 0); 127664562Sgshapiro add_session(sp); 127764562Sgshapiro} 127864562Sgshapiro 127964562Sgshapiro/* 128064562Sgshapiro * Catch a signal and request a state transition. 128164562Sgshapiro */ 128264562Sgshapirostatic void 128364562Sgshapirotransition_handler(int sig) 128464562Sgshapiro{ 128564562Sgshapiro 128664562Sgshapiro switch (sig) { 128764562Sgshapiro case SIGHUP: 128864562Sgshapiro if (current_state == read_ttys || current_state == multi_user || 128964562Sgshapiro current_state == clean_ttys || current_state == catatonia) 129064562Sgshapiro requested_transition = clean_ttys; 129164562Sgshapiro break; 129264562Sgshapiro case SIGUSR2: 129364562Sgshapiro howto = RB_POWEROFF; 129464562Sgshapiro case SIGUSR1: 129564562Sgshapiro howto |= RB_HALT; 129664562Sgshapiro case SIGINT: 129764562Sgshapiro Reboot = TRUE; 129864562Sgshapiro case SIGTERM: 129964562Sgshapiro if (current_state == read_ttys || current_state == multi_user || 130064562Sgshapiro current_state == clean_ttys || current_state == catatonia) 130171345Sgshapiro requested_transition = death; 130264562Sgshapiro else 130364562Sgshapiro requested_transition = death_single; 130464562Sgshapiro break; 130564562Sgshapiro case SIGTSTP: 130664562Sgshapiro if (current_state == runcom || current_state == read_ttys || 130764562Sgshapiro current_state == clean_ttys || 130864562Sgshapiro current_state == multi_user || current_state == catatonia) 130964562Sgshapiro requested_transition = catatonia; 131064562Sgshapiro break; 131164562Sgshapiro default: 131264562Sgshapiro requested_transition = 0; 131364562Sgshapiro break; 131464562Sgshapiro } 131564562Sgshapiro} 131664562Sgshapiro 131764562Sgshapiro/* 131864562Sgshapiro * Take the system multiuser. 131964562Sgshapiro */ 132064562Sgshapirostatic state_func_t 132164562Sgshapiromulti_user(void) 132264562Sgshapiro{ 132364562Sgshapiro pid_t pid; 132464562Sgshapiro session_t *sp; 132564562Sgshapiro 132664562Sgshapiro requested_transition = 0; 132764562Sgshapiro 132864562Sgshapiro /* 132964562Sgshapiro * If the administrator has not set the security level to -1 133064562Sgshapiro * to indicate that the kernel should not run multiuser in secure 133164562Sgshapiro * mode, and the run script has not set a higher level of security 133264562Sgshapiro * than level 1, then put the kernel into secure mode. 133364562Sgshapiro */ 133464562Sgshapiro if (getsecuritylevel() == 0) 133564562Sgshapiro setsecuritylevel(1); 133664562Sgshapiro 133764562Sgshapiro for (sp = sessions; sp; sp = sp->se_next) { 133864562Sgshapiro if (sp->se_process) 133964562Sgshapiro continue; 134064562Sgshapiro if ((pid = start_getty(sp)) == -1) { 134164562Sgshapiro /* serious trouble */ 134264562Sgshapiro requested_transition = clean_ttys; 134364562Sgshapiro break; 134464562Sgshapiro } 134564562Sgshapiro sp->se_process = pid; 134664562Sgshapiro sp->se_started = time((time_t *) 0); 134764562Sgshapiro add_session(sp); 134864562Sgshapiro } 134964562Sgshapiro 135064562Sgshapiro while (!requested_transition) 135164562Sgshapiro if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 135264562Sgshapiro collect_child(pid); 135364562Sgshapiro 135464562Sgshapiro return (state_func_t) requested_transition; 135564562Sgshapiro} 135664562Sgshapiro 135764562Sgshapiro/* 135864562Sgshapiro * This is an (n*2)+(n^2) algorithm. We hope it isn't run often... 135964562Sgshapiro */ 136064562Sgshapirostatic state_func_t 136164562Sgshapiroclean_ttys(void) 136264562Sgshapiro{ 136364562Sgshapiro session_t *sp, *sprev; 136464562Sgshapiro struct ttyent *typ; 136564562Sgshapiro int session_index = 0; 136664562Sgshapiro int devlen; 136764562Sgshapiro char *old_getty, *old_window, *old_type; 136864562Sgshapiro 136964562Sgshapiro /* 137064562Sgshapiro * mark all sessions for death, (!SE_PRESENT) 137164562Sgshapiro * as we find or create new ones they'll be marked as keepers, 137264562Sgshapiro * we'll later nuke all the ones not found in /etc/ttys 137364562Sgshapiro */ 137464562Sgshapiro for (sp = sessions; sp != NULL; sp = sp->se_next) 137564562Sgshapiro sp->se_flags &= ~SE_PRESENT; 137664562Sgshapiro 137764562Sgshapiro devlen = sizeof(_PATH_DEV) - 1; 137864562Sgshapiro while ((typ = getttyent()) != NULL) { 137964562Sgshapiro ++session_index; 138064562Sgshapiro 138164562Sgshapiro for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 138264562Sgshapiro if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 138364562Sgshapiro break; 138464562Sgshapiro 138564562Sgshapiro if (sp) { 138638032Speter /* we want this one to live */ 138738032Speter sp->se_flags |= SE_PRESENT; 138838032Speter if (sp->se_index != session_index) { 138938032Speter warning("port %s changed utmp index from %d to %d", 139038032Speter sp->se_device, sp->se_index, 139138032Speter session_index); 139238032Speter sp->se_index = session_index; 139338032Speter } 139438032Speter if ((typ->ty_status & TTY_ON) == 0 || 139538032Speter typ->ty_getty == 0) { 139638032Speter sp->se_flags |= SE_SHUTDOWN; 139738032Speter kill(sp->se_process, SIGHUP); 139838032Speter continue; 139938032Speter } 140038032Speter sp->se_flags &= ~SE_SHUTDOWN; 140138032Speter old_getty = sp->se_getty ? strdup(sp->se_getty) : 0; 140238032Speter old_window = sp->se_window ? strdup(sp->se_window) : 0; 140338032Speter old_type = sp->se_type ? strdup(sp->se_type) : 0; 140438032Speter if (setupargv(sp, typ) == 0) { 140564562Sgshapiro warning("can't parse getty for port %s", 140638032Speter sp->se_device); 140738032Speter sp->se_flags |= SE_SHUTDOWN; 140864562Sgshapiro kill(sp->se_process, SIGHUP); 140964562Sgshapiro } 141038032Speter else if ( !old_getty 141138032Speter || (!old_type && sp->se_type) 141238032Speter || (old_type && !sp->se_type) 141364562Sgshapiro || (!old_window && sp->se_window) 141438032Speter || (old_window && !sp->se_window) 141564562Sgshapiro || (strcmp(old_getty, sp->se_getty) != 0) 141664562Sgshapiro || (old_window && strcmp(old_window, sp->se_window) != 0) 141738032Speter || (old_type && strcmp(old_type, sp->se_type) != 0) 141864562Sgshapiro ) { 141964562Sgshapiro /* Don't set SE_SHUTDOWN here */ 142064562Sgshapiro sp->se_nspace = 0; 142164562Sgshapiro sp->se_started = 0; 142238032Speter kill(sp->se_process, SIGHUP); 142338032Speter } 142438032Speter if (old_getty) 142538032Speter free(old_getty); 142638032Speter if (old_window) 142738032Speter free(old_window); 142838032Speter if (old_type) 142938032Speter free(old_type); 143038032Speter continue; 143138032Speter } 143243730Speter 143343730Speter new_session(sprev, session_index, typ); 143438032Speter } 143543730Speter 143643730Speter endttyent(); 143743730Speter 143838032Speter /* 143938032Speter * sweep through and kill all deleted sessions 144038032Speter * ones who's /etc/ttys line was deleted (SE_PRESENT unset) 144138032Speter */ 144238032Speter for (sp = sessions; sp != NULL; sp = sp->se_next) { 144338032Speter if ((sp->se_flags & SE_PRESENT) == 0) { 144464562Sgshapiro sp->se_flags |= SE_SHUTDOWN; 144538032Speter kill(sp->se_process, SIGHUP); 144638032Speter } 144764562Sgshapiro } 144838032Speter 144938032Speter return (state_func_t) multi_user; 145038032Speter} 145138032Speter 145238032Speter/* 145338032Speter * Block further logins. 145438032Speter */ 145538032Speterstatic state_func_t 145664562Sgshapirocatatonia(void) 145738032Speter{ 145838032Speter session_t *sp; 145938032Speter 146038032Speter for (sp = sessions; sp; sp = sp->se_next) 146138032Speter sp->se_flags |= SE_SHUTDOWN; 146264562Sgshapiro 146338032Speter return (state_func_t) multi_user; 146438032Speter} 146538032Speter 146638032Speter/* 146738032Speter * Note SIGALRM. 146838032Speter */ 146943730Speterstatic void 147043730Speteralrm_handler(int sig) 147138032Speter{ 147243730Speter 147343730Speter (void)sig; 147443730Speter clang = 1; 147538032Speter} 147638032Speter 147738032Speter/* 147843730Speter * Bring the system down to single user. 147943730Speter */ 148038032Speterstatic state_func_t 148143730Speterdeath(void) 148243730Speter{ 148343730Speter session_t *sp; 148443730Speter 148543730Speter /* 148638032Speter * Also revoke the TTY here. Because runshutdown() may reopen 148738032Speter * the TTY whose getty we're killing here, there is no guarantee 148838032Speter * runshutdown() will perform the initial open() call, causing 148964562Sgshapiro * the terminal attributes to be misconfigured. 149064562Sgshapiro */ 149164562Sgshapiro for (sp = sessions; sp; sp = sp->se_next) { 149264562Sgshapiro sp->se_flags |= SE_SHUTDOWN; 149364562Sgshapiro kill(sp->se_process, SIGHUP); 149464562Sgshapiro revoke(sp->se_device); 149564562Sgshapiro } 149664562Sgshapiro 149764562Sgshapiro /* Try to run the rc.shutdown script within a period of time */ 149864562Sgshapiro runshutdown(); 149964562Sgshapiro 150064562Sgshapiro return (state_func_t) death_single; 150138032Speter} 150238032Speter 150338032Speter/* 150438032Speter * Do what is necessary to reinitialize single user mode or reboot 150538032Speter * from an incomplete state. 150638032Speter */ 150738032Speterstatic state_func_t 150838032Speterdeath_single(void) 150938032Speter{ 151064562Sgshapiro int i; 151138032Speter pid_t pid; 151238032Speter static const int death_sigs[2] = { SIGTERM, SIGKILL }; 151338032Speter 151438032Speter revoke(_PATH_CONSOLE); 151538032Speter 151638032Speter for (i = 0; i < 2; ++i) { 151738032Speter if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 151838032Speter return (state_func_t) single_user; 151938032Speter 152038032Speter clang = 0; 152138032Speter alarm(DEATH_WATCH); 152238032Speter do 152338032Speter if ((pid = waitpid(-1, (int *)0, 0)) != -1) 152438032Speter collect_child(pid); 152538032Speter while (clang == 0 && errno != ECHILD); 152638032Speter 152738032Speter if (errno == ECHILD) 152838032Speter return (state_func_t) single_user; 152938032Speter } 153038032Speter 153138032Speter warning("some processes would not die; ps axl advised"); 153238032Speter 153338032Speter return (state_func_t) single_user; 153464562Sgshapiro} 153564562Sgshapiro 153664562Sgshapiro/* 153738032Speter * Run the system shutdown script. 153838032Speter * 153938032Speter * Exit codes: XXX I should document more 154038032Speter * -2 shutdown script terminated abnormally 154138032Speter * -1 fatal error - can't run script 154238032Speter * 0 good. 154338032Speter * >0 some error (exit code) 154438032Speter */ 154571345Sgshapirostatic int 154638032Speterrunshutdown(void) 154738032Speter{ 154864562Sgshapiro pid_t pid, wpid; 154964562Sgshapiro int status; 155038032Speter int shutdowntimeout; 155138032Speter size_t len; 155238032Speter char *argv[4]; 155338032Speter const char *shell; 155438032Speter struct sigaction sa; 155564562Sgshapiro struct stat sb; 155664562Sgshapiro 155738032Speter /* 155838032Speter * rc.shutdown is optional, so to prevent any unnecessary 155938032Speter * complaints from the shell we simply don't run it if the 156038032Speter * file does not exist. If the stat() here fails for other 156138032Speter * reasons, we'll let the shell complain. 156238032Speter */ 156338032Speter if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT) 156438032Speter return 0; 156538032Speter 156664562Sgshapiro shell = get_shell(); 156764562Sgshapiro 156838032Speter if ((pid = fork()) == 0) { 156938032Speter sigemptyset(&sa.sa_mask); 157038032Speter sa.sa_flags = 0; 157138032Speter sa.sa_handler = SIG_IGN; 157238032Speter sigaction(SIGTSTP, &sa, (struct sigaction *)0); 157364562Sgshapiro sigaction(SIGHUP, &sa, (struct sigaction *)0); 157464562Sgshapiro 157538032Speter setctty(_PATH_CONSOLE); 157638032Speter 157738032Speter char _sh[] = "sh"; 157838032Speter char _reboot[] = "reboot"; 157938032Speter char _single[] = "single"; 158064562Sgshapiro char _path_rundown[] = _PATH_RUNDOWN; 158164562Sgshapiro 158238032Speter argv[0] = _sh; 158338032Speter argv[1] = _path_rundown; 158438032Speter argv[2] = Reboot ? _reboot : _single; 158538032Speter argv[3] = 0; 158638032Speter 158738032Speter sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 158838032Speter 158964562Sgshapiro#ifdef LOGIN_CAP 159064562Sgshapiro setprocresources(RESOURCE_RC); 159138032Speter#endif 159238032Speter execv(shell, argv); 159338032Speter warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN); 159438032Speter _exit(1); /* force single user mode */ 159538032Speter } 159638032Speter 159764562Sgshapiro if (pid == -1) { 159864562Sgshapiro emergency("can't fork for %s on %s: %m", shell, _PATH_RUNDOWN); 159964562Sgshapiro while (waitpid(-1, (int *) 0, WNOHANG) > 0) 160038032Speter continue; 160138032Speter sleep(STALL_TIMEOUT); 160238032Speter return -1; 160364562Sgshapiro } 160464562Sgshapiro 160538032Speter len = sizeof(shutdowntimeout); 160638032Speter if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len, 160738032Speter NULL, 0) == -1 || shutdowntimeout < 2) 160838032Speter shutdowntimeout = DEATH_SCRIPT; 160938032Speter alarm(shutdowntimeout); 161038032Speter clang = 0; 161138032Speter /* 161238032Speter * Copied from single_user(). This is a bit paranoid. 161338032Speter * Use the same ALRM handler. 161438032Speter */ 161538032Speter do { 161638032Speter if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 161738032Speter collect_child(wpid); 161838032Speter if (clang == 1) { 161938032Speter /* we were waiting for the sub-shell */ 162038032Speter kill(wpid, SIGTERM); 162138032Speter warning("timeout expired for %s on %s: %m; going to " 162238032Speter "single user mode", shell, _PATH_RUNDOWN); 162338032Speter return -1; 162438032Speter } 162538032Speter if (wpid == -1) { 162638032Speter if (errno == EINTR) 162738032Speter continue; 162838032Speter warning("wait for %s on %s failed: %m; going to " 162938032Speter "single user mode", shell, _PATH_RUNDOWN); 163038032Speter return -1; 163138032Speter } 163243730Speter if (wpid == pid && WIFSTOPPED(status)) { 163338032Speter warning("init: %s on %s stopped, restarting\n", 163464562Sgshapiro shell, _PATH_RUNDOWN); 163538032Speter kill(pid, SIGCONT); 163664562Sgshapiro wpid = -1; 163764562Sgshapiro } 163864562Sgshapiro } while (wpid != pid && !clang); 163964562Sgshapiro 164064562Sgshapiro /* Turn off the alarm */ 164164562Sgshapiro alarm(0); 164264562Sgshapiro 164364562Sgshapiro if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 164438032Speter requested_transition == catatonia) { 164538032Speter /* 164638032Speter * /etc/rc.shutdown executed /sbin/reboot; 164738032Speter * wait for the end quietly 164838032Speter */ 164938032Speter sigset_t s; 165038032Speter 165138032Speter sigfillset(&s); 165238032Speter for (;;) 165364562Sgshapiro sigsuspend(&s); 165438032Speter } 165538032Speter 165664562Sgshapiro if (!WIFEXITED(status)) { 165738032Speter warning("%s on %s terminated abnormally, going to " 165838032Speter "single user mode", shell, _PATH_RUNDOWN); 165938032Speter return -2; 166038032Speter } 166138032Speter 166264562Sgshapiro if ((status = WEXITSTATUS(status)) != 0) 166364562Sgshapiro warning("%s returned status %d", _PATH_RUNDOWN, status); 166464562Sgshapiro 166538032Speter return status; 166638032Speter} 166738032Speter 166838032Speterstatic char * 166938032Speterstrk(char *p) 167064562Sgshapiro{ 167164562Sgshapiro static char *t; 167264562Sgshapiro char *q; 167338032Speter int c; 167438032Speter 167538032Speter if (p) 167664562Sgshapiro t = p; 167743730Speter if (!t) 167838032Speter return 0; 167938032Speter 168038032Speter c = *t; 168143730Speter while (c == ' ' || c == '\t' ) 168243730Speter c = *++t; 168338032Speter if (!c) { 168443730Speter t = 0; 168543730Speter return 0; 168643730Speter } 168738032Speter q = t; 168838032Speter if (c == '\'') { 168938032Speter c = *++t; 169038032Speter q = t; 169138032Speter while (c && c != '\'') 169238032Speter c = *++t; 169364562Sgshapiro if (!c) /* unterminated string */ 169464562Sgshapiro q = t = 0; 169564562Sgshapiro else 169638032Speter *t++ = 0; 169764562Sgshapiro } else { 169864562Sgshapiro while (c && c != ' ' && c != '\t' ) 169964562Sgshapiro c = *++t; 170038032Speter *t++ = 0; 170138032Speter if (!c) 170238032Speter t = 0; 170338032Speter } 170438032Speter return q; 170538032Speter} 170664562Sgshapiro 170738032Speter#ifdef LOGIN_CAP 170838032Speterstatic void 170938032Spetersetprocresources(const char *cname) 171038032Speter{ 171164562Sgshapiro login_cap_t *lc; 171238032Speter if ((lc = login_getclassbyname(cname, NULL)) != NULL) { 171338032Speter setusercontext(lc, (struct passwd*)NULL, 0, 171438032Speter LOGIN_SETPRIORITY | LOGIN_SETRESOURCES); 171538032Speter login_close(lc); 171664562Sgshapiro } 171738032Speter} 171838032Speter#endif 171938032Speter