11558Srgrimes/*- 21558Srgrimes * Copyright (c) 1991, 1993 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * This code is derived from software contributed to Berkeley by 61558Srgrimes * Donn Seeley at Berkeley Software Design, Inc. 71558Srgrimes * 81558Srgrimes * Redistribution and use in source and binary forms, with or without 91558Srgrimes * modification, are permitted provided that the following conditions 101558Srgrimes * are met: 111558Srgrimes * 1. Redistributions of source code must retain the above copyright 121558Srgrimes * notice, this list of conditions and the following disclaimer. 131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141558Srgrimes * notice, this list of conditions and the following disclaimer in the 151558Srgrimes * documentation and/or other materials provided with the distribution. 161558Srgrimes * 4. Neither the name of the University nor the names of its contributors 171558Srgrimes * may be used to endorse or promote products derived from this software 181558Srgrimes * without specific prior written permission. 191558Srgrimes * 201558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301558Srgrimes * SUCH DAMAGE. 311558Srgrimes */ 321558Srgrimes 331558Srgrimes#ifndef lint 3437417Scharnierstatic const char copyright[] = 351558Srgrimes"@(#) Copyright (c) 1991, 1993\n\ 361558Srgrimes The Regents of the University of California. All rights reserved.\n"; 371558Srgrimes#endif /* not lint */ 381558Srgrimes 391558Srgrimes#ifndef lint 4037417Scharnier#if 0 411558Srgrimesstatic char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; 4237417Scharnier#endif 4337417Scharnierstatic const char rcsid[] = 4450476Speter "$FreeBSD: stable/10/sbin/init/init.c 321907 2017-08-02 05:47:26Z delphij $"; 451558Srgrimes#endif /* not lint */ 461558Srgrimes 471558Srgrimes#include <sys/param.h> 4827837Sdavidn#include <sys/ioctl.h> 49293744Strasz#include <sys/mman.h> 5019227Sphk#include <sys/mount.h> 511558Srgrimes#include <sys/sysctl.h> 521558Srgrimes#include <sys/wait.h> 5328344Sdavidn#include <sys/stat.h> 54101271Smux#include <sys/uio.h> 551558Srgrimes 561558Srgrimes#include <db.h> 571558Srgrimes#include <errno.h> 581558Srgrimes#include <fcntl.h> 59166484Simp#include <kenv.h> 6027186Sache#include <libutil.h> 6169793Sobrien#include <paths.h> 621558Srgrimes#include <signal.h> 631558Srgrimes#include <stdio.h> 641558Srgrimes#include <stdlib.h> 651558Srgrimes#include <string.h> 661558Srgrimes#include <syslog.h> 671558Srgrimes#include <time.h> 681558Srgrimes#include <ttyent.h> 691558Srgrimes#include <unistd.h> 702323Snate#include <sys/reboot.h> 7126594Scharnier#include <err.h> 721558Srgrimes 731558Srgrimes#include <stdarg.h> 741558Srgrimes 751558Srgrimes#ifdef SECURE 761558Srgrimes#include <pwd.h> 771558Srgrimes#endif 781558Srgrimes 7921865Sdavidn#ifdef LOGIN_CAP 8021865Sdavidn#include <login_cap.h> 8121865Sdavidn#endif 8221865Sdavidn 83293744Strasz#include "mntopts.h" 841558Srgrimes#include "pathnames.h" 851558Srgrimes 861558Srgrimes/* 871558Srgrimes * Sleep times; used to prevent thrashing. 881558Srgrimes */ 891558Srgrimes#define GETTY_SPACING 5 /* N secs minimum getty spacing */ 901558Srgrimes#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ 91232841Sed#define GETTY_NSPACE 3 /* max. spacing count to bring reaction */ 921558Srgrimes#define WINDOW_WAIT 3 /* wait N secs after starting window */ 931558Srgrimes#define STALL_TIMEOUT 30 /* wait N secs after warning */ 941558Srgrimes#define DEATH_WATCH 10 /* wait N secs for procs to die */ 95173785Sobrien#define DEATH_SCRIPT 120 /* wait for 2min for /etc/rc.shutdown */ 96173785Sobrien#define RESOURCE_RC "daemon" 97232841Sed#define RESOURCE_WINDOW "default" 98173785Sobrien#define RESOURCE_GETTY "default" 991558Srgrimes 100183391Sdelphijstatic void handle(sig_t, ...); 101183391Sdelphijstatic void delset(sigset_t *, ...); 1021558Srgrimes 103183391Sdelphijstatic void stall(const char *, ...) __printflike(1, 2); 104183391Sdelphijstatic void warning(const char *, ...) __printflike(1, 2); 105183391Sdelphijstatic void emergency(const char *, ...) __printflike(1, 2); 106183391Sdelphijstatic void disaster(int); 107183391Sdelphijstatic void badsys(int); 108293744Straszstatic void revoke_ttys(void); 109183391Sdelphijstatic int runshutdown(void); 110140070Sdelphijstatic char *strk(char *); 1111558Srgrimes 1121558Srgrimes/* 1131558Srgrimes * We really need a recursive typedef... 1141558Srgrimes * The following at least guarantees that the return type of (*state_t)() 1151558Srgrimes * is sufficiently wide to hold a function pointer. 1161558Srgrimes */ 11792838Simptypedef long (*state_func_t)(void); 11892838Simptypedef state_func_t (*state_t)(void); 1191558Srgrimes 120183391Sdelphijstatic state_func_t single_user(void); 121183391Sdelphijstatic state_func_t runcom(void); 122183391Sdelphijstatic state_func_t read_ttys(void); 123183391Sdelphijstatic state_func_t multi_user(void); 124183391Sdelphijstatic state_func_t clean_ttys(void); 125183391Sdelphijstatic state_func_t catatonia(void); 126183391Sdelphijstatic state_func_t death(void); 127217750Sjillesstatic state_func_t death_single(void); 128293744Straszstatic state_func_t reroot(void); 129293744Straszstatic state_func_t reroot_phase_two(void); 1301558Srgrimes 131183391Sdelphijstatic state_func_t run_script(const char *); 132166484Simp 133227081Sedstatic enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 13411910Sphk#define FALSE 0 13511910Sphk#define TRUE 1 13611910Sphk 137227081Sedstatic int Reboot = FALSE; 138227081Sedstatic int howto = RB_AUTOBOOT; 1391558Srgrimes 140227081Sedstatic int devfs; 14119227Sphk 142183391Sdelphijstatic void transition(state_t); 143183391Sdelphijstatic state_t requested_transition; 144217750Sjillesstatic state_t current_state = death_single; 1451558Srgrimes 146232977Sedstatic void open_console(void); 147183391Sdelphijstatic const char *get_shell(void); 148183391Sdelphijstatic void write_stderr(const char *message); 1491558Srgrimes 1501558Srgrimestypedef struct init_session { 1511558Srgrimes int se_index; /* index of entry in ttys file */ 1521558Srgrimes pid_t se_process; /* controlling process */ 1531558Srgrimes time_t se_started; /* used to avoid thrashing */ 1541558Srgrimes int se_flags; /* status of session */ 1551558Srgrimes#define SE_SHUTDOWN 0x1 /* session won't be restarted */ 15657344Salfred#define SE_PRESENT 0x2 /* session is in /etc/ttys */ 157232841Sed int se_nspace; /* spacing count */ 1581558Srgrimes char *se_device; /* filename of port */ 1591558Srgrimes char *se_getty; /* what to run on that port */ 160232841Sed char *se_getty_argv_space; /* pre-parsed argument array space */ 1611558Srgrimes char **se_getty_argv; /* pre-parsed argument array */ 1621558Srgrimes char *se_window; /* window system (started only once) */ 163232841Sed char *se_window_argv_space; /* pre-parsed argument array space */ 1641558Srgrimes char **se_window_argv; /* pre-parsed argument array */ 165232841Sed char *se_type; /* default terminal type */ 1661558Srgrimes struct init_session *se_prev; 1671558Srgrimes struct init_session *se_next; 1681558Srgrimes} session_t; 1691558Srgrimes 170183391Sdelphijstatic void free_session(session_t *); 171183391Sdelphijstatic session_t *new_session(session_t *, int, struct ttyent *); 172183391Sdelphijstatic session_t *sessions; 1731558Srgrimes 174183391Sdelphijstatic char **construct_argv(char *); 175183391Sdelphijstatic void start_window_system(session_t *); 176183391Sdelphijstatic void collect_child(pid_t); 177183391Sdelphijstatic pid_t start_getty(session_t *); 178183391Sdelphijstatic void transition_handler(int); 179183391Sdelphijstatic void alrm_handler(int); 180183391Sdelphijstatic void setsecuritylevel(int); 181183391Sdelphijstatic int getsecuritylevel(void); 182183391Sdelphijstatic int setupargv(session_t *, struct ttyent *); 18321941Sdavidn#ifdef LOGIN_CAP 184183391Sdelphijstatic void setprocresources(const char *); 18521941Sdavidn#endif 186183391Sdelphijstatic int clang; 1871558Srgrimes 188183391Sdelphijstatic int start_session_db(void); 189183391Sdelphijstatic void add_session(session_t *); 190183391Sdelphijstatic void del_session(session_t *); 191183391Sdelphijstatic session_t *find_session(pid_t); 192183391Sdelphijstatic DB *session_db; 1931558Srgrimes 1941558Srgrimes/* 1951558Srgrimes * The mother of all processes. 1961558Srgrimes */ 1971558Srgrimesint 19892838Simpmain(int argc, char *argv[]) 1991558Srgrimes{ 200166484Simp state_t initial_transition = runcom; 201166484Simp char kenv_value[PATH_MAX]; 202293744Strasz int c, error; 2031558Srgrimes struct sigaction sa; 2041558Srgrimes sigset_t mask; 2051558Srgrimes 2061558Srgrimes /* Dispose of random users. */ 20726594Scharnier if (getuid() != 0) 20826594Scharnier errx(1, "%s", strerror(EPERM)); 2091558Srgrimes 2101558Srgrimes /* System V users like to reexec init. */ 21147998Sru if (getpid() != 1) { 21247998Sru#ifdef COMPAT_SYSV_INIT 21347998Sru /* So give them what they want */ 21447998Sru if (argc > 1) { 21547998Sru if (strlen(argv[1]) == 1) { 21692806Sobrien char runlevel = *argv[1]; 21792806Sobrien int sig; 2181558Srgrimes 21947998Sru switch (runlevel) { 220173785Sobrien case '0': /* halt + poweroff */ 221173785Sobrien sig = SIGUSR2; 222173785Sobrien break; 223173785Sobrien case '1': /* single-user */ 224173785Sobrien sig = SIGTERM; 225173785Sobrien break; 226173785Sobrien case '6': /* reboot */ 227173785Sobrien sig = SIGINT; 228173785Sobrien break; 229173785Sobrien case 'c': /* block further logins */ 230173785Sobrien sig = SIGTSTP; 231173785Sobrien break; 232173785Sobrien case 'q': /* rescan /etc/ttys */ 233173785Sobrien sig = SIGHUP; 234173785Sobrien break; 235293744Strasz case 'r': /* remount root */ 236293744Strasz sig = SIGEMT; 237293744Strasz break; 238173785Sobrien default: 239173785Sobrien goto invalid; 24047998Sru } 24147998Sru kill(1, sig); 24247998Sru _exit(0); 24347998Sru } else 24447998Sruinvalid: 24547998Sru errx(1, "invalid run-level ``%s''", argv[1]); 24647998Sru } else 24747998Sru#endif 24847998Sru errx(1, "already running"); 24947998Sru } 2501558Srgrimes /* 2511558Srgrimes * Note that this does NOT open a file... 2521558Srgrimes * Does 'init' deserve its own facility number? 2531558Srgrimes */ 2541558Srgrimes openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 2551558Srgrimes 2561558Srgrimes /* 2571558Srgrimes * Create an initial session. 2581558Srgrimes */ 259293744Strasz if (setsid() < 0 && (errno != EPERM || getsid(0) != 1)) 2601558Srgrimes warning("initial setsid() failed: %m"); 2611558Srgrimes 2621558Srgrimes /* 2631558Srgrimes * Establish an initial user so that programs running 2641558Srgrimes * single user do not freak out and die (like passwd). 2651558Srgrimes */ 2661558Srgrimes if (setlogin("root") < 0) 2671558Srgrimes warning("setlogin() failed: %m"); 2681558Srgrimes 2691558Srgrimes /* 2701558Srgrimes * This code assumes that we always get arguments through flags, 2711558Srgrimes * never through bits set in some random machine register. 2721558Srgrimes */ 273293744Strasz while ((c = getopt(argc, argv, "dsfr")) != -1) 2741558Srgrimes switch (c) { 27519227Sphk case 'd': 27619227Sphk devfs = 1; 27719227Sphk break; 2781558Srgrimes case 's': 279166484Simp initial_transition = single_user; 2801558Srgrimes break; 2811558Srgrimes case 'f': 2821558Srgrimes runcom_mode = FASTBOOT; 2831558Srgrimes break; 284293744Strasz case 'r': 285293744Strasz initial_transition = reroot_phase_two; 286293744Strasz break; 2871558Srgrimes default: 2881558Srgrimes warning("unrecognized flag '-%c'", c); 2891558Srgrimes break; 2901558Srgrimes } 2911558Srgrimes 2921558Srgrimes if (optind != argc) 2931558Srgrimes warning("ignoring excess arguments"); 2941558Srgrimes 295166484Simp /* 296166484Simp * We catch or block signals rather than ignore them, 297166484Simp * so that they get reset on exec. 298166484Simp */ 299166484Simp handle(badsys, SIGSYS, 0); 300173785Sobrien handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU, 301173785Sobrien SIGXFSZ, 0); 302293744Strasz handle(transition_handler, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, 303293744Strasz SIGUSR1, SIGUSR2, 0); 304166484Simp handle(alrm_handler, SIGALRM, 0); 305166484Simp sigfillset(&mask); 306166484Simp delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 307293744Strasz SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, 308293744Strasz SIGALRM, SIGUSR1, SIGUSR2, 0); 309166484Simp sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 310166484Simp sigemptyset(&sa.sa_mask); 311166484Simp sa.sa_flags = 0; 312166484Simp sa.sa_handler = SIG_IGN; 313173785Sobrien sigaction(SIGTTIN, &sa, (struct sigaction *)0); 314173785Sobrien sigaction(SIGTTOU, &sa, (struct sigaction *)0); 315166484Simp 316166484Simp /* 317166484Simp * Paranoia. 318166484Simp */ 319166484Simp close(0); 320166484Simp close(1); 321166484Simp close(2); 322166484Simp 323166484Simp if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) { 324166484Simp state_func_t next_transition; 325166484Simp 326166484Simp if ((next_transition = run_script(kenv_value)) != 0) 327166484Simp initial_transition = (state_t) next_transition; 328166484Simp } 329166484Simp 330166484Simp if (kenv(KENV_GET, "init_chroot", kenv_value, sizeof(kenv_value)) > 0) { 331166484Simp if (chdir(kenv_value) != 0 || chroot(".") != 0) 332166484Simp warning("Can't chroot to %s: %m", kenv_value); 333166484Simp } 334166484Simp 335166484Simp /* 336166484Simp * Additional check if devfs needs to be mounted: 337166484Simp * If "/" and "/dev" have the same device number, 338166484Simp * then it hasn't been mounted yet. 339166484Simp */ 340166484Simp if (!devfs) { 341166484Simp struct stat stst; 342166484Simp dev_t root_devno; 343166484Simp 344166484Simp stat("/", &stst); 345166484Simp root_devno = stst.st_dev; 346166484Simp if (stat("/dev", &stst) != 0) 347166484Simp warning("Can't stat /dev: %m"); 348166484Simp else if (stst.st_dev == root_devno) 349166484Simp devfs++; 350166484Simp } 351166484Simp 35219227Sphk if (devfs) { 353101271Smux struct iovec iov[4]; 35472188Sphk char *s; 35572188Sphk int i; 35672188Sphk 357140070Sdelphij char _fstype[] = "fstype"; 358140070Sdelphij char _devfs[] = "devfs"; 359140070Sdelphij char _fspath[] = "fspath"; 360140070Sdelphij char _path_dev[]= _PATH_DEV; 361140070Sdelphij 362140070Sdelphij iov[0].iov_base = _fstype; 363140070Sdelphij iov[0].iov_len = sizeof(_fstype); 364140070Sdelphij iov[1].iov_base = _devfs; 365140070Sdelphij iov[1].iov_len = sizeof(_devfs); 366140070Sdelphij iov[2].iov_base = _fspath; 367140070Sdelphij iov[2].iov_len = sizeof(_fspath); 368173787Sobrien /* 36972188Sphk * Try to avoid the trailing slash in _PATH_DEV. 37072188Sphk * Be *very* defensive. 37172188Sphk */ 37272188Sphk s = strdup(_PATH_DEV); 37372188Sphk if (s != NULL) { 37472188Sphk i = strlen(s); 37572188Sphk if (i > 0 && s[i - 1] == '/') 37672188Sphk s[i - 1] = '\0'; 377101271Smux iov[3].iov_base = s; 378101271Smux iov[3].iov_len = strlen(s) + 1; 37972188Sphk } else { 380140070Sdelphij iov[3].iov_base = _path_dev; 381140070Sdelphij iov[3].iov_len = sizeof(_path_dev); 38272188Sphk } 383101271Smux nmount(iov, 4, 0); 384101271Smux if (s != NULL) 385101271Smux free(s); 38619227Sphk } 38719227Sphk 388293744Strasz if (initial_transition != reroot_phase_two) { 389293744Strasz /* 390293744Strasz * Unmount reroot leftovers. This runs after init(8) 391293744Strasz * gets reexecuted after reroot_phase_two() is done. 392293744Strasz */ 393293744Strasz error = unmount(_PATH_REROOT, MNT_FORCE); 394293744Strasz if (error != 0 && errno != EINVAL) 395293744Strasz warning("Cannot unmount %s: %m", _PATH_REROOT); 396293744Strasz } 397293744Strasz 3981558Srgrimes /* 3991558Srgrimes * Start the state machine. 4001558Srgrimes */ 401166484Simp transition(initial_transition); 4021558Srgrimes 4031558Srgrimes /* 4041558Srgrimes * Should never reach here. 4051558Srgrimes */ 4061558Srgrimes return 1; 4071558Srgrimes} 4081558Srgrimes 4091558Srgrimes/* 4101558Srgrimes * Associate a function with a signal handler. 4111558Srgrimes */ 412183391Sdelphijstatic void 4131558Srgrimeshandle(sig_t handler, ...) 4141558Srgrimes{ 4151558Srgrimes int sig; 4161558Srgrimes struct sigaction sa; 41734001Sjraynard sigset_t mask_everything; 4181558Srgrimes va_list ap; 4191558Srgrimes va_start(ap, handler); 4201558Srgrimes 4211558Srgrimes sa.sa_handler = handler; 4221558Srgrimes sigfillset(&mask_everything); 4231558Srgrimes 424126836Sbde while ((sig = va_arg(ap, int)) != 0) { 4251558Srgrimes sa.sa_mask = mask_everything; 4261558Srgrimes /* XXX SA_RESTART? */ 4271558Srgrimes sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 4281558Srgrimes sigaction(sig, &sa, (struct sigaction *) 0); 4291558Srgrimes } 4301558Srgrimes va_end(ap); 4311558Srgrimes} 4321558Srgrimes 4331558Srgrimes/* 4341558Srgrimes * Delete a set of signals from a mask. 4351558Srgrimes */ 436183391Sdelphijstatic void 4371558Srgrimesdelset(sigset_t *maskp, ...) 4381558Srgrimes{ 4391558Srgrimes int sig; 4401558Srgrimes va_list ap; 4411558Srgrimes va_start(ap, maskp); 4421558Srgrimes 443126836Sbde while ((sig = va_arg(ap, int)) != 0) 4441558Srgrimes sigdelset(maskp, sig); 4451558Srgrimes va_end(ap); 4461558Srgrimes} 4471558Srgrimes 4481558Srgrimes/* 4491558Srgrimes * Log a message and sleep for a while (to give someone an opportunity 4501558Srgrimes * to read it and to save log or hardcopy output if the problem is chronic). 4511558Srgrimes * NB: should send a message to the session logger to avoid blocking. 4521558Srgrimes */ 453183391Sdelphijstatic void 45481911Skrisstall(const char *message, ...) 4551558Srgrimes{ 4561558Srgrimes va_list ap; 4571558Srgrimes va_start(ap, message); 4581558Srgrimes 4591558Srgrimes vsyslog(LOG_ALERT, message, ap); 4601558Srgrimes va_end(ap); 4611558Srgrimes sleep(STALL_TIMEOUT); 4621558Srgrimes} 4631558Srgrimes 4641558Srgrimes/* 4651558Srgrimes * Like stall(), but doesn't sleep. 4661558Srgrimes * If cpp had variadic macros, the two functions could be #defines for another. 4671558Srgrimes * NB: should send a message to the session logger to avoid blocking. 4681558Srgrimes */ 469183391Sdelphijstatic void 47081911Skriswarning(const char *message, ...) 4711558Srgrimes{ 4721558Srgrimes va_list ap; 4731558Srgrimes va_start(ap, message); 4741558Srgrimes 4751558Srgrimes vsyslog(LOG_ALERT, message, ap); 4761558Srgrimes va_end(ap); 4771558Srgrimes} 4781558Srgrimes 4791558Srgrimes/* 4801558Srgrimes * Log an emergency message. 4811558Srgrimes * NB: should send a message to the session logger to avoid blocking. 4821558Srgrimes */ 483183391Sdelphijstatic void 48481911Skrisemergency(const char *message, ...) 4851558Srgrimes{ 4861558Srgrimes va_list ap; 4871558Srgrimes va_start(ap, message); 4881558Srgrimes 4891558Srgrimes vsyslog(LOG_EMERG, message, ap); 4901558Srgrimes va_end(ap); 4911558Srgrimes} 4921558Srgrimes 4931558Srgrimes/* 4941558Srgrimes * Catch a SIGSYS signal. 4951558Srgrimes * 4961558Srgrimes * These may arise if a system does not support sysctl. 4971558Srgrimes * We tolerate up to 25 of these, then throw in the towel. 4981558Srgrimes */ 499183391Sdelphijstatic void 50092838Simpbadsys(int sig) 5011558Srgrimes{ 5021558Srgrimes static int badcount = 0; 5031558Srgrimes 5041558Srgrimes if (badcount++ < 25) 5051558Srgrimes return; 5061558Srgrimes disaster(sig); 5071558Srgrimes} 5081558Srgrimes 5091558Srgrimes/* 5101558Srgrimes * Catch an unexpected signal. 5111558Srgrimes */ 512183391Sdelphijstatic void 51392838Simpdisaster(int sig) 5141558Srgrimes{ 515173785Sobrien 5161558Srgrimes emergency("fatal signal: %s", 517173785Sobrien (unsigned)sig < NSIG ? sys_siglist[sig] : "unknown signal"); 5181558Srgrimes 5191558Srgrimes sleep(STALL_TIMEOUT); 5201558Srgrimes _exit(sig); /* reboot */ 5211558Srgrimes} 5221558Srgrimes 5231558Srgrimes/* 5241558Srgrimes * Get the security level of the kernel. 5251558Srgrimes */ 526183391Sdelphijstatic int 52792838Simpgetsecuritylevel(void) 5281558Srgrimes{ 5291558Srgrimes#ifdef KERN_SECURELVL 5301558Srgrimes int name[2], curlevel; 5311558Srgrimes size_t len; 5321558Srgrimes 5331558Srgrimes name[0] = CTL_KERN; 5341558Srgrimes name[1] = KERN_SECURELVL; 5351558Srgrimes len = sizeof curlevel; 5361558Srgrimes if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 5371558Srgrimes emergency("cannot get kernel security level: %s", 5381558Srgrimes strerror(errno)); 5391558Srgrimes return (-1); 5401558Srgrimes } 5411558Srgrimes return (curlevel); 5421558Srgrimes#else 5431558Srgrimes return (-1); 5441558Srgrimes#endif 5451558Srgrimes} 5461558Srgrimes 5471558Srgrimes/* 5481558Srgrimes * Set the security level of the kernel. 5491558Srgrimes */ 550183391Sdelphijstatic void 55192838Simpsetsecuritylevel(int newlevel) 5521558Srgrimes{ 5531558Srgrimes#ifdef KERN_SECURELVL 5541558Srgrimes int name[2], curlevel; 5551558Srgrimes 5561558Srgrimes curlevel = getsecuritylevel(); 5571558Srgrimes if (newlevel == curlevel) 5581558Srgrimes return; 5591558Srgrimes name[0] = CTL_KERN; 5601558Srgrimes name[1] = KERN_SECURELVL; 5611558Srgrimes if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 5621558Srgrimes emergency( 5631558Srgrimes "cannot change kernel security level from %d to %d: %s", 5641558Srgrimes curlevel, newlevel, strerror(errno)); 5651558Srgrimes return; 5661558Srgrimes } 5671558Srgrimes#ifdef SECURE 5681558Srgrimes warning("kernel security level changed from %d to %d", 5691558Srgrimes curlevel, newlevel); 5701558Srgrimes#endif 5711558Srgrimes#endif 5721558Srgrimes} 5731558Srgrimes 5741558Srgrimes/* 5751558Srgrimes * Change states in the finite state machine. 5761558Srgrimes * The initial state is passed as an argument. 5771558Srgrimes */ 578183391Sdelphijstatic void 57992838Simptransition(state_t s) 5801558Srgrimes{ 581173785Sobrien 582217750Sjilles current_state = s; 5831558Srgrimes for (;;) 584217750Sjilles current_state = (state_t) (*current_state)(); 5851558Srgrimes} 5861558Srgrimes 5871558Srgrimes/* 5881558Srgrimes * Start a session and allocate a controlling terminal. 5891558Srgrimes * Only called by children of init after forking. 5901558Srgrimes */ 591183391Sdelphijstatic void 592232977Sedopen_console(void) 5931558Srgrimes{ 5941558Srgrimes int fd; 5951558Srgrimes 596233945Sed /* 597233945Sed * Try to open /dev/console. Open the device with O_NONBLOCK to 598233945Sed * prevent potential blocking on a carrier. 599233945Sed */ 600232977Sed revoke(_PATH_CONSOLE); 601232977Sed if ((fd = open(_PATH_CONSOLE, O_RDWR | O_NONBLOCK)) != -1) { 602233945Sed (void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); 603232977Sed if (login_tty(fd) == 0) 604232977Sed return; 605232977Sed close(fd); 6061558Srgrimes } 607232977Sed 608232977Sed /* No luck. Log output to file if possible. */ 609232977Sed if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 610232977Sed stall("cannot open null device."); 6111558Srgrimes _exit(1); 6121558Srgrimes } 613232977Sed if (fd != STDIN_FILENO) { 614232977Sed dup2(fd, STDIN_FILENO); 615232977Sed close(fd); 616232977Sed } 617232977Sed fd = open(_PATH_INITLOG, O_WRONLY | O_APPEND | O_CREAT, 0644); 618232977Sed if (fd == -1) 619232977Sed dup2(STDIN_FILENO, STDOUT_FILENO); 620232977Sed else if (fd != STDOUT_FILENO) { 621232977Sed dup2(fd, STDOUT_FILENO); 622232977Sed close(fd); 623232977Sed } 624232977Sed dup2(STDOUT_FILENO, STDERR_FILENO); 6251558Srgrimes} 6261558Srgrimes 627183391Sdelphijstatic const char * 628166484Simpget_shell(void) 629166484Simp{ 630166484Simp static char kenv_value[PATH_MAX]; 631166484Simp 632166484Simp if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0) 633166484Simp return kenv_value; 634166484Simp else 635166484Simp return _PATH_BSHELL; 636166484Simp} 637166484Simp 638183391Sdelphijstatic void 639166484Simpwrite_stderr(const char *message) 640166484Simp{ 641173785Sobrien 642166484Simp write(STDERR_FILENO, message, strlen(message)); 643166484Simp} 644166484Simp 645293744Straszstatic int 646293744Straszread_file(const char *path, void **bufp, size_t *bufsizep) 647293744Strasz{ 648293744Strasz struct stat sb; 649293744Strasz size_t bufsize; 650293744Strasz void *buf; 651293744Strasz ssize_t nbytes; 652293744Strasz int error, fd; 653293744Strasz 654293744Strasz fd = open(path, O_RDONLY); 655293744Strasz if (fd < 0) { 656293744Strasz emergency("%s: %s", path, strerror(errno)); 657293744Strasz return (-1); 658293744Strasz } 659293744Strasz 660293744Strasz error = fstat(fd, &sb); 661293744Strasz if (error != 0) { 662293744Strasz emergency("fstat: %s", strerror(errno)); 663293747Strasz close(fd); 664293744Strasz return (error); 665293744Strasz } 666293744Strasz 667293744Strasz bufsize = sb.st_size; 668293744Strasz buf = malloc(bufsize); 669293744Strasz if (buf == NULL) { 670293744Strasz emergency("malloc: %s", strerror(errno)); 671293747Strasz close(fd); 672293744Strasz return (error); 673293744Strasz } 674293744Strasz 675293744Strasz nbytes = read(fd, buf, bufsize); 676293744Strasz if (nbytes != (ssize_t)bufsize) { 677293744Strasz emergency("read: %s", strerror(errno)); 678293747Strasz close(fd); 679293744Strasz free(buf); 680293744Strasz return (error); 681293744Strasz } 682293744Strasz 683293744Strasz error = close(fd); 684293744Strasz if (error != 0) { 685293744Strasz emergency("close: %s", strerror(errno)); 686293744Strasz free(buf); 687293744Strasz return (error); 688293744Strasz } 689293744Strasz 690293744Strasz *bufp = buf; 691293744Strasz *bufsizep = bufsize; 692293744Strasz 693293744Strasz return (0); 694293744Strasz} 695293744Strasz 696293744Straszstatic int 697293747Straszcreate_file(const char *path, const void *buf, size_t bufsize) 698293744Strasz{ 699293744Strasz ssize_t nbytes; 700293744Strasz int error, fd; 701293744Strasz 702293744Strasz fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0700); 703293744Strasz if (fd < 0) { 704293744Strasz emergency("%s: %s", path, strerror(errno)); 705293744Strasz return (-1); 706293744Strasz } 707293744Strasz 708293744Strasz nbytes = write(fd, buf, bufsize); 709293744Strasz if (nbytes != (ssize_t)bufsize) { 710293744Strasz emergency("write: %s", strerror(errno)); 711293747Strasz close(fd); 712293744Strasz return (-1); 713293744Strasz } 714293744Strasz 715293744Strasz error = close(fd); 716293744Strasz if (error != 0) { 717293744Strasz emergency("close: %s", strerror(errno)); 718293744Strasz return (-1); 719293744Strasz } 720293744Strasz 721293744Strasz return (0); 722293744Strasz} 723293744Strasz 724293744Straszstatic int 725293744Straszmount_tmpfs(const char *fspath) 726293744Strasz{ 727293744Strasz struct iovec *iov; 728293744Strasz char errmsg[255]; 729293744Strasz int error, iovlen; 730293744Strasz 731293744Strasz iov = NULL; 732293744Strasz iovlen = 0; 733293744Strasz memset(errmsg, 0, sizeof(errmsg)); 734293744Strasz build_iovec(&iov, &iovlen, "fstype", 735293744Strasz __DECONST(void *, "tmpfs"), (size_t)-1); 736293744Strasz build_iovec(&iov, &iovlen, "fspath", 737293744Strasz __DECONST(void *, fspath), (size_t)-1); 738293744Strasz build_iovec(&iov, &iovlen, "errmsg", 739293744Strasz errmsg, sizeof(errmsg)); 740293744Strasz 741293744Strasz error = nmount(iov, iovlen, 0); 742293744Strasz if (error != 0) { 743293744Strasz if (*errmsg != '\0') { 744293744Strasz emergency("cannot mount tmpfs on %s: %s: %s", 745293744Strasz fspath, errmsg, strerror(errno)); 746293744Strasz } else { 747293744Strasz emergency("cannot mount tmpfs on %s: %s", 748293744Strasz fspath, strerror(errno)); 749293744Strasz } 750293744Strasz return (error); 751293744Strasz } 752293744Strasz return (0); 753293744Strasz} 754293744Strasz 755293744Straszstatic state_func_t 756293744Straszreroot(void) 757293744Strasz{ 758293744Strasz void *buf; 759293744Strasz char init_path[PATH_MAX]; 760293744Strasz size_t bufsize, init_path_len; 761293744Strasz int error, name[4]; 762293744Strasz 763293747Strasz buf = NULL; 764293747Strasz bufsize = 0; 765293747Strasz 766293744Strasz name[0] = CTL_KERN; 767293744Strasz name[1] = KERN_PROC; 768293744Strasz name[2] = KERN_PROC_PATHNAME; 769293744Strasz name[3] = -1; 770293744Strasz init_path_len = sizeof(init_path); 771293744Strasz error = sysctl(name, 4, init_path, &init_path_len, NULL, 0); 772293744Strasz if (error != 0) { 773293744Strasz emergency("failed to get kern.proc.pathname: %s", 774293744Strasz strerror(errno)); 775293744Strasz goto out; 776293744Strasz } 777293744Strasz 778293744Strasz revoke_ttys(); 779293744Strasz runshutdown(); 780293744Strasz 781293744Strasz /* 782293744Strasz * Make sure nobody can interfere with our scheme. 783293744Strasz */ 784293744Strasz error = kill(-1, SIGKILL); 785293744Strasz if (error != 0) { 786293744Strasz emergency("kill(2) failed: %s", strerror(errno)); 787293744Strasz goto out; 788293744Strasz } 789293744Strasz 790293744Strasz /* 791293744Strasz * Copy the init binary into tmpfs, so that we can unmount 792293744Strasz * the old rootfs without committing suicide. 793293744Strasz */ 794293744Strasz error = read_file(init_path, &buf, &bufsize); 795293744Strasz if (error != 0) 796293744Strasz goto out; 797293744Strasz error = mount_tmpfs(_PATH_REROOT); 798293744Strasz if (error != 0) 799293744Strasz goto out; 800293744Strasz error = create_file(_PATH_REROOT_INIT, buf, bufsize); 801293744Strasz if (error != 0) 802293744Strasz goto out; 803293744Strasz 804293744Strasz /* 805293744Strasz * Execute the temporary init. 806293744Strasz */ 807293744Strasz execl(_PATH_REROOT_INIT, _PATH_REROOT_INIT, "-r", NULL); 808293744Strasz emergency("cannot exec %s: %s", _PATH_REROOT_INIT, strerror(errno)); 809293744Strasz 810293744Straszout: 811293744Strasz emergency("reroot failed; going to single user mode"); 812293747Strasz free(buf); 813293744Strasz return (state_func_t) single_user; 814293744Strasz} 815293744Strasz 816293744Straszstatic state_func_t 817293744Straszreroot_phase_two(void) 818293744Strasz{ 819293744Strasz char init_path[PATH_MAX], *path, *path_component; 820293744Strasz size_t init_path_len; 821293744Strasz int nbytes, error; 822293744Strasz 823293744Strasz /* 824293744Strasz * Ask the kernel to mount the new rootfs. 825293744Strasz */ 826293744Strasz error = reboot(RB_REROOT); 827293744Strasz if (error != 0) { 828293744Strasz emergency("RB_REBOOT failed: %s", strerror(errno)); 829293744Strasz goto out; 830293744Strasz } 831293744Strasz 832293744Strasz /* 833293744Strasz * Figure out where the destination init(8) binary is. Note that 834293744Strasz * the path could be different than what we've started with. Use 835293744Strasz * the value from kenv, if set, or the one from sysctl otherwise. 836293744Strasz * The latter defaults to a hardcoded value, but can be overridden 837293744Strasz * by a build time option. 838293744Strasz */ 839293744Strasz nbytes = kenv(KENV_GET, "init_path", init_path, sizeof(init_path)); 840293744Strasz if (nbytes <= 0) { 841293744Strasz init_path_len = sizeof(init_path); 842293744Strasz error = sysctlbyname("kern.init_path", 843293744Strasz init_path, &init_path_len, NULL, 0); 844293744Strasz if (error != 0) { 845293744Strasz emergency("failed to retrieve kern.init_path: %s", 846293744Strasz strerror(errno)); 847293744Strasz goto out; 848293744Strasz } 849293744Strasz } 850293744Strasz 851293744Strasz /* 852293744Strasz * Repeat the init search logic from sys/kern/init_path.c 853293744Strasz */ 854293744Strasz path_component = init_path; 855293744Strasz while ((path = strsep(&path_component, ":")) != NULL) { 856293744Strasz /* 857293744Strasz * Execute init(8) from the new rootfs. 858293744Strasz */ 859293744Strasz execl(path, path, NULL); 860293744Strasz } 861293744Strasz emergency("cannot exec init from %s: %s", init_path, strerror(errno)); 862293744Strasz 863293744Straszout: 864293744Strasz emergency("reroot failed; going to single user mode"); 865293744Strasz return (state_func_t) single_user; 866293744Strasz} 867293744Strasz 8681558Srgrimes/* 8691558Srgrimes * Bring the system up single user. 8701558Srgrimes */ 871183391Sdelphijstatic state_func_t 87292838Simpsingle_user(void) 8731558Srgrimes{ 8741558Srgrimes pid_t pid, wpid; 8751558Srgrimes int status; 8761558Srgrimes sigset_t mask; 877166484Simp const char *shell; 8781558Srgrimes char *argv[2]; 879308036Skib struct timeval tv, tn; 8801558Srgrimes#ifdef SECURE 8811558Srgrimes struct ttyent *typ; 8821558Srgrimes struct passwd *pp; 8831558Srgrimes static const char banner[] = 8841558Srgrimes "Enter root password, or ^D to go multi-user\n"; 8851558Srgrimes char *clear, *password; 8861558Srgrimes#endif 88737814Sphk#ifdef DEBUGSHELL 88837814Sphk char altshell[128]; 88937814Sphk#endif 8901558Srgrimes 8912327Sjkh if (Reboot) { 89247962Sru /* Instead of going single user, let's reboot the machine */ 8932323Snate sync(); 894308037Skib if (reboot(howto) == -1) { 895308037Skib emergency("reboot(%#x) failed, %s", howto, 896308037Skib strerror(errno)); 897308037Skib _exit(1); /* panic and reboot */ 898308037Skib } 899308037Skib warning("reboot(%#x) returned", howto); 900308037Skib _exit(0); /* panic as well */ 9012323Snate } 9022323Snate 903166484Simp shell = get_shell(); 904166484Simp 9051558Srgrimes if ((pid = fork()) == 0) { 9061558Srgrimes /* 9071558Srgrimes * Start the single user session. 9081558Srgrimes */ 909232977Sed open_console(); 9101558Srgrimes 9111558Srgrimes#ifdef SECURE 9121558Srgrimes /* 9131558Srgrimes * Check the root password. 9141558Srgrimes * We don't care if the console is 'on' by default; 9151558Srgrimes * it's the only tty that can be 'off' and 'secure'. 9161558Srgrimes */ 9171558Srgrimes typ = getttynam("console"); 9181558Srgrimes pp = getpwnam("root"); 91953550Sdillon if (typ && (typ->ty_status & TTY_SECURE) == 0 && 92053550Sdillon pp && *pp->pw_passwd) { 921166484Simp write_stderr(banner); 9221558Srgrimes for (;;) { 9231558Srgrimes clear = getpass("Password:"); 9241558Srgrimes if (clear == 0 || *clear == '\0') 9251558Srgrimes _exit(0); 9261558Srgrimes password = crypt(clear, pp->pw_passwd); 9271558Srgrimes bzero(clear, _PASSWORD_LEN); 928232841Sed if (password == NULL || 929231994Skevlo strcmp(password, pp->pw_passwd) == 0) 9301558Srgrimes break; 9311558Srgrimes warning("single-user login failed\n"); 9321558Srgrimes } 9331558Srgrimes } 9341558Srgrimes endttyent(); 9351558Srgrimes endpwent(); 9361558Srgrimes#endif /* SECURE */ 9371558Srgrimes 9381558Srgrimes#ifdef DEBUGSHELL 9391558Srgrimes { 94037814Sphk char *cp = altshell; 9411558Srgrimes int num; 9421558Srgrimes 943166484Simp#define SHREQUEST "Enter full pathname of shell or RETURN for " 944166484Simp write_stderr(SHREQUEST); 945166484Simp write_stderr(shell); 946166484Simp write_stderr(": "); 9471558Srgrimes while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 9481558Srgrimes num != 0 && *cp != '\n' && cp < &altshell[127]) 949173785Sobrien cp++; 9501558Srgrimes *cp = '\0'; 9511558Srgrimes if (altshell[0] != '\0') 9521558Srgrimes shell = altshell; 9531558Srgrimes } 9541558Srgrimes#endif /* DEBUGSHELL */ 9551558Srgrimes 9561558Srgrimes /* 9571558Srgrimes * Unblock signals. 9581558Srgrimes * We catch all the interesting ones, 9591558Srgrimes * and those are reset to SIG_DFL on exec. 9601558Srgrimes */ 9611558Srgrimes sigemptyset(&mask); 9621558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 9631558Srgrimes 9641558Srgrimes /* 9651558Srgrimes * Fire off a shell. 9661558Srgrimes * If the default one doesn't work, try the Bourne shell. 9671558Srgrimes */ 968140070Sdelphij 969140070Sdelphij char name[] = "-sh"; 970140070Sdelphij 971140070Sdelphij argv[0] = name; 9721558Srgrimes argv[1] = 0; 9731558Srgrimes execv(shell, argv); 9741558Srgrimes emergency("can't exec %s for single user: %m", shell); 9751558Srgrimes execv(_PATH_BSHELL, argv); 9761558Srgrimes emergency("can't exec %s for single user: %m", _PATH_BSHELL); 9771558Srgrimes sleep(STALL_TIMEOUT); 9781558Srgrimes _exit(1); 9791558Srgrimes } 9801558Srgrimes 9811558Srgrimes if (pid == -1) { 9821558Srgrimes /* 9831558Srgrimes * We are seriously hosed. Do our best. 9841558Srgrimes */ 9851558Srgrimes emergency("can't fork single-user shell, trying again"); 9861558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 9871558Srgrimes continue; 9881558Srgrimes return (state_func_t) single_user; 9891558Srgrimes } 9901558Srgrimes 9911558Srgrimes requested_transition = 0; 9921558Srgrimes do { 9931558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 9941558Srgrimes collect_child(wpid); 9951558Srgrimes if (wpid == -1) { 9961558Srgrimes if (errno == EINTR) 9971558Srgrimes continue; 9981558Srgrimes warning("wait for single-user shell failed: %m; restarting"); 9991558Srgrimes return (state_func_t) single_user; 10001558Srgrimes } 10011558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 10021558Srgrimes warning("init: shell stopped, restarting\n"); 10031558Srgrimes kill(pid, SIGCONT); 10041558Srgrimes wpid = -1; 10051558Srgrimes } 10061558Srgrimes } while (wpid != pid && !requested_transition); 10071558Srgrimes 10081558Srgrimes if (requested_transition) 10091558Srgrimes return (state_func_t) requested_transition; 10101558Srgrimes 10111558Srgrimes if (!WIFEXITED(status)) { 10128871Srgrimes if (WTERMSIG(status) == SIGKILL) { 10138871Srgrimes /* 10148871Srgrimes * reboot(8) killed shell? 10151558Srgrimes */ 10161558Srgrimes warning("single user shell terminated."); 1017308036Skib gettimeofday(&tv, NULL); 1018308036Skib tn = tv; 1019308036Skib tv.tv_sec += STALL_TIMEOUT; 1020308036Skib while (tv.tv_sec > tn.tv_sec || (tv.tv_sec == 1021308036Skib tn.tv_sec && tv.tv_usec > tn.tv_usec)) { 1022308036Skib sleep(1); 1023308036Skib gettimeofday(&tn, NULL); 1024308036Skib } 10251558Srgrimes _exit(0); 10268871Srgrimes } else { 10271558Srgrimes warning("single user shell terminated, restarting"); 10281558Srgrimes return (state_func_t) single_user; 10291558Srgrimes } 10301558Srgrimes } 10311558Srgrimes 10321558Srgrimes runcom_mode = FASTBOOT; 10331558Srgrimes return (state_func_t) runcom; 10341558Srgrimes} 10351558Srgrimes 10361558Srgrimes/* 10371558Srgrimes * Run the system startup script. 10381558Srgrimes */ 1039183391Sdelphijstatic state_func_t 104092838Simpruncom(void) 10411558Srgrimes{ 1042166484Simp state_func_t next_transition; 1043166484Simp 1044166484Simp if ((next_transition = run_script(_PATH_RUNCOM)) != 0) 1045166484Simp return next_transition; 1046166484Simp 1047166484Simp runcom_mode = AUTOBOOT; /* the default */ 1048166484Simp return (state_func_t) read_ttys; 1049166484Simp} 1050166484Simp 1051166484Simp/* 1052166484Simp * Run a shell script. 1053166484Simp * Returns 0 on success, otherwise the next transition to enter: 1054166484Simp * - single_user if fork/execv/waitpid failed, or if the script 1055166484Simp * terminated with a signal or exit code != 0. 1056217750Sjilles * - death_single if a SIGTERM was delivered to init(8). 1057166484Simp */ 1058183391Sdelphijstatic state_func_t 1059166484Simprun_script(const char *script) 1060166484Simp{ 10611558Srgrimes pid_t pid, wpid; 10621558Srgrimes int status; 10631558Srgrimes char *argv[4]; 1064166484Simp const char *shell; 10651558Srgrimes struct sigaction sa; 10661558Srgrimes 1067166484Simp shell = get_shell(); 1068166484Simp 10691558Srgrimes if ((pid = fork()) == 0) { 10701558Srgrimes sigemptyset(&sa.sa_mask); 10711558Srgrimes sa.sa_flags = 0; 10721558Srgrimes sa.sa_handler = SIG_IGN; 1073173785Sobrien sigaction(SIGTSTP, &sa, (struct sigaction *)0); 1074173785Sobrien sigaction(SIGHUP, &sa, (struct sigaction *)0); 10751558Srgrimes 1076232977Sed open_console(); 10771558Srgrimes 1078232841Sed char _sh[] = "sh"; 1079140070Sdelphij char _autoboot[] = "autoboot"; 1080140070Sdelphij 1081140070Sdelphij argv[0] = _sh; 1082166484Simp argv[1] = __DECONST(char *, script); 1083140070Sdelphij argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0; 10841558Srgrimes argv[3] = 0; 10851558Srgrimes 10861558Srgrimes sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 10871558Srgrimes 108821865Sdavidn#ifdef LOGIN_CAP 108921865Sdavidn setprocresources(RESOURCE_RC); 109021865Sdavidn#endif 1091166484Simp execv(shell, argv); 1092166484Simp stall("can't exec %s for %s: %m", shell, script); 10931558Srgrimes _exit(1); /* force single user mode */ 10941558Srgrimes } 10951558Srgrimes 10961558Srgrimes if (pid == -1) { 1097166484Simp emergency("can't fork for %s on %s: %m", shell, script); 10981558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 10991558Srgrimes continue; 11001558Srgrimes sleep(STALL_TIMEOUT); 11011558Srgrimes return (state_func_t) single_user; 11021558Srgrimes } 11031558Srgrimes 11041558Srgrimes /* 11051558Srgrimes * Copied from single_user(). This is a bit paranoid. 11061558Srgrimes */ 110785010Sdes requested_transition = 0; 11081558Srgrimes do { 11091558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 11101558Srgrimes collect_child(wpid); 11111558Srgrimes if (wpid == -1) { 1112293744Strasz if (requested_transition == death_single || 1113293744Strasz requested_transition == reroot) 1114293744Strasz return (state_func_t) requested_transition; 11151558Srgrimes if (errno == EINTR) 11161558Srgrimes continue; 1117166484Simp warning("wait for %s on %s failed: %m; going to " 1118166484Simp "single user mode", shell, script); 11191558Srgrimes return (state_func_t) single_user; 11201558Srgrimes } 11211558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 11221558Srgrimes warning("init: %s on %s stopped, restarting\n", 1123173785Sobrien shell, script); 11241558Srgrimes kill(pid, SIGCONT); 11251558Srgrimes wpid = -1; 11261558Srgrimes } 11271558Srgrimes } while (wpid != pid); 11281558Srgrimes 11291558Srgrimes if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 11301558Srgrimes requested_transition == catatonia) { 11311558Srgrimes /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 11321558Srgrimes sigset_t s; 11331558Srgrimes 11341558Srgrimes sigfillset(&s); 11351558Srgrimes for (;;) 11361558Srgrimes sigsuspend(&s); 11371558Srgrimes } 11381558Srgrimes 11391558Srgrimes if (!WIFEXITED(status)) { 1140166484Simp warning("%s on %s terminated abnormally, going to single " 1141166484Simp "user mode", shell, script); 11421558Srgrimes return (state_func_t) single_user; 11431558Srgrimes } 11441558Srgrimes 11451558Srgrimes if (WEXITSTATUS(status)) 11461558Srgrimes return (state_func_t) single_user; 11471558Srgrimes 1148166484Simp return (state_func_t) 0; 11491558Srgrimes} 11501558Srgrimes 11511558Srgrimes/* 11521558Srgrimes * Open the session database. 11531558Srgrimes * 11541558Srgrimes * NB: We could pass in the size here; is it necessary? 11551558Srgrimes */ 1156183391Sdelphijstatic int 115792838Simpstart_session_db(void) 11581558Srgrimes{ 11591558Srgrimes if (session_db && (*session_db->close)(session_db)) 11601558Srgrimes emergency("session database close: %s", strerror(errno)); 11611558Srgrimes if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 11621558Srgrimes emergency("session database open: %s", strerror(errno)); 11631558Srgrimes return (1); 11641558Srgrimes } 11651558Srgrimes return (0); 11668871Srgrimes 11671558Srgrimes} 11681558Srgrimes 11691558Srgrimes/* 11701558Srgrimes * Add a new login session. 11711558Srgrimes */ 1172183391Sdelphijstatic void 117392838Simpadd_session(session_t *sp) 11741558Srgrimes{ 11751558Srgrimes DBT key; 11761558Srgrimes DBT data; 11771558Srgrimes 11781558Srgrimes key.data = &sp->se_process; 11791558Srgrimes key.size = sizeof sp->se_process; 11801558Srgrimes data.data = &sp; 11811558Srgrimes data.size = sizeof sp; 11821558Srgrimes 11831558Srgrimes if ((*session_db->put)(session_db, &key, &data, 0)) 11841558Srgrimes emergency("insert %d: %s", sp->se_process, strerror(errno)); 11851558Srgrimes} 11861558Srgrimes 11871558Srgrimes/* 11881558Srgrimes * Delete an old login session. 11891558Srgrimes */ 1190183391Sdelphijstatic void 119192838Simpdel_session(session_t *sp) 11921558Srgrimes{ 11931558Srgrimes DBT key; 11941558Srgrimes 11951558Srgrimes key.data = &sp->se_process; 11961558Srgrimes key.size = sizeof sp->se_process; 11971558Srgrimes 11981558Srgrimes if ((*session_db->del)(session_db, &key, 0)) 11991558Srgrimes emergency("delete %d: %s", sp->se_process, strerror(errno)); 12001558Srgrimes} 12011558Srgrimes 12021558Srgrimes/* 12031558Srgrimes * Look up a login session by pid. 12041558Srgrimes */ 1205183391Sdelphijstatic session_t * 12061558Srgrimesfind_session(pid_t pid) 12071558Srgrimes{ 12081558Srgrimes DBT key; 12091558Srgrimes DBT data; 12101558Srgrimes session_t *ret; 12111558Srgrimes 12121558Srgrimes key.data = &pid; 12131558Srgrimes key.size = sizeof pid; 12141558Srgrimes if ((*session_db->get)(session_db, &key, &data, 0) != 0) 12151558Srgrimes return 0; 12161558Srgrimes bcopy(data.data, (char *)&ret, sizeof(ret)); 12171558Srgrimes return ret; 12181558Srgrimes} 12191558Srgrimes 12201558Srgrimes/* 12211558Srgrimes * Construct an argument vector from a command line. 12221558Srgrimes */ 1223183391Sdelphijstatic char ** 122492838Simpconstruct_argv(char *command) 12251558Srgrimes{ 122692806Sobrien int argc = 0; 122792806Sobrien char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 12281558Srgrimes * sizeof (char *)); 12291558Srgrimes 123049018Sru if ((argv[argc++] = strk(command)) == 0) { 123149018Sru free(argv); 123249018Sru return (NULL); 123349018Sru } 123427837Sdavidn while ((argv[argc++] = strk((char *) 0)) != NULL) 12351558Srgrimes continue; 12361558Srgrimes return argv; 12371558Srgrimes} 12381558Srgrimes 12391558Srgrimes/* 12401558Srgrimes * Deallocate a session descriptor. 12411558Srgrimes */ 1242183391Sdelphijstatic void 124392838Simpfree_session(session_t *sp) 12441558Srgrimes{ 12451558Srgrimes free(sp->se_device); 12461558Srgrimes if (sp->se_getty) { 12471558Srgrimes free(sp->se_getty); 12483594Sache free(sp->se_getty_argv_space); 12491558Srgrimes free(sp->se_getty_argv); 12501558Srgrimes } 12511558Srgrimes if (sp->se_window) { 12521558Srgrimes free(sp->se_window); 12533594Sache free(sp->se_window_argv_space); 12541558Srgrimes free(sp->se_window_argv); 12551558Srgrimes } 12563594Sache if (sp->se_type) 12573594Sache free(sp->se_type); 12581558Srgrimes free(sp); 12591558Srgrimes} 12601558Srgrimes 12611558Srgrimes/* 12621558Srgrimes * Allocate a new session descriptor. 126357344Salfred * Mark it SE_PRESENT. 12641558Srgrimes */ 1265183391Sdelphijstatic session_t * 126692838Simpnew_session(session_t *sprev, int session_index, struct ttyent *typ) 12671558Srgrimes{ 126892806Sobrien session_t *sp; 126927029Spst int fd; 12701558Srgrimes 12711558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 12721558Srgrimes typ->ty_name == 0 || 12731558Srgrimes typ->ty_getty == 0) 12741558Srgrimes return 0; 12751558Srgrimes 127627215Sache sp = (session_t *) calloc(1, sizeof (session_t)); 12771558Srgrimes 12781558Srgrimes sp->se_index = session_index; 127957344Salfred sp->se_flags |= SE_PRESENT; 12801558Srgrimes 1281321907Sdelphij if (asprintf(&sp->se_device, "%s%s", _PATH_DEV, typ->ty_name) < 0) 1282321907Sdelphij err(1, "asprintf"); 12831558Srgrimes 128427029Spst /* 128527029Spst * Attempt to open the device, if we get "device not configured" 128627029Spst * then don't add the device to the session list. 128727029Spst */ 128827029Spst if ((fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0)) < 0) { 1289135868Simp if (errno == ENXIO) { 129027029Spst free_session(sp); 129127029Spst return (0); 129227029Spst } 129327029Spst } else 129427029Spst close(fd); 129527029Spst 12961558Srgrimes if (setupargv(sp, typ) == 0) { 12971558Srgrimes free_session(sp); 12981558Srgrimes return (0); 12991558Srgrimes } 13001558Srgrimes 13011558Srgrimes sp->se_next = 0; 13021558Srgrimes if (sprev == 0) { 13031558Srgrimes sessions = sp; 13041558Srgrimes sp->se_prev = 0; 13051558Srgrimes } else { 13061558Srgrimes sprev->se_next = sp; 13071558Srgrimes sp->se_prev = sprev; 13081558Srgrimes } 13091558Srgrimes 13101558Srgrimes return sp; 13111558Srgrimes} 13121558Srgrimes 13131558Srgrimes/* 13141558Srgrimes * Calculate getty and if useful window argv vectors. 13151558Srgrimes */ 1316183391Sdelphijstatic int 131792838Simpsetupargv(session_t *sp, struct ttyent *typ) 13181558Srgrimes{ 13191558Srgrimes 13201558Srgrimes if (sp->se_getty) { 13211558Srgrimes free(sp->se_getty); 13223594Sache free(sp->se_getty_argv_space); 13231558Srgrimes free(sp->se_getty_argv); 13241558Srgrimes } 1325321907Sdelphij if (asprintf(&sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name) < 0) 1326321907Sdelphij err(1, "asprintf"); 13273594Sache sp->se_getty_argv_space = strdup(sp->se_getty); 13283594Sache sp->se_getty_argv = construct_argv(sp->se_getty_argv_space); 13291558Srgrimes if (sp->se_getty_argv == 0) { 13301558Srgrimes warning("can't parse getty for port %s", sp->se_device); 13311558Srgrimes free(sp->se_getty); 13323594Sache free(sp->se_getty_argv_space); 13333594Sache sp->se_getty = sp->se_getty_argv_space = 0; 13341558Srgrimes return (0); 13351558Srgrimes } 13363594Sache if (sp->se_window) { 133749018Sru free(sp->se_window); 13383594Sache free(sp->se_window_argv_space); 13393594Sache free(sp->se_window_argv); 13403594Sache } 13413594Sache sp->se_window = sp->se_window_argv_space = 0; 13423594Sache sp->se_window_argv = 0; 13431558Srgrimes if (typ->ty_window) { 13441558Srgrimes sp->se_window = strdup(typ->ty_window); 13453594Sache sp->se_window_argv_space = strdup(sp->se_window); 13463594Sache sp->se_window_argv = construct_argv(sp->se_window_argv_space); 13471558Srgrimes if (sp->se_window_argv == 0) { 13481558Srgrimes warning("can't parse window for port %s", 1349173785Sobrien sp->se_device); 13503594Sache free(sp->se_window_argv_space); 13511558Srgrimes free(sp->se_window); 13523594Sache sp->se_window = sp->se_window_argv_space = 0; 13531558Srgrimes return (0); 13541558Srgrimes } 13551558Srgrimes } 13563594Sache if (sp->se_type) 13573594Sache free(sp->se_type); 13583594Sache sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0; 13591558Srgrimes return (1); 13601558Srgrimes} 13611558Srgrimes 13621558Srgrimes/* 13631558Srgrimes * Walk the list of ttys and create sessions for each active line. 13641558Srgrimes */ 1365183391Sdelphijstatic state_func_t 136692838Simpread_ttys(void) 13671558Srgrimes{ 13681558Srgrimes int session_index = 0; 136992806Sobrien session_t *sp, *snext; 137092806Sobrien struct ttyent *typ; 13711558Srgrimes 13721558Srgrimes /* 13731558Srgrimes * Destroy any previous session state. 13741558Srgrimes * There shouldn't be any, but just in case... 13751558Srgrimes */ 13761558Srgrimes for (sp = sessions; sp; sp = snext) { 13771558Srgrimes snext = sp->se_next; 13781558Srgrimes free_session(sp); 13791558Srgrimes } 13801558Srgrimes sessions = 0; 13811558Srgrimes if (start_session_db()) 13821558Srgrimes return (state_func_t) single_user; 13831558Srgrimes 13841558Srgrimes /* 13851558Srgrimes * Allocate a session entry for each active port. 13861558Srgrimes * Note that sp starts at 0. 13871558Srgrimes */ 138827837Sdavidn while ((typ = getttyent()) != NULL) 138927837Sdavidn if ((snext = new_session(sp, ++session_index, typ)) != NULL) 13901558Srgrimes sp = snext; 13911558Srgrimes 13921558Srgrimes endttyent(); 13931558Srgrimes 13941558Srgrimes return (state_func_t) multi_user; 13951558Srgrimes} 13961558Srgrimes 13971558Srgrimes/* 13981558Srgrimes * Start a window system running. 13991558Srgrimes */ 1400183391Sdelphijstatic void 140192838Simpstart_window_system(session_t *sp) 14021558Srgrimes{ 14031558Srgrimes pid_t pid; 14041558Srgrimes sigset_t mask; 14053594Sache char term[64], *env[2]; 1406159402Skib int status; 14071558Srgrimes 14081558Srgrimes if ((pid = fork()) == -1) { 14091558Srgrimes emergency("can't fork for window system on port %s: %m", 1410173785Sobrien sp->se_device); 14111558Srgrimes /* hope that getty fails and we can try again */ 14121558Srgrimes return; 14131558Srgrimes } 1414173785Sobrien if (pid) { 1415159402Skib waitpid(-1, &status, 0); 14161558Srgrimes return; 1417159402Skib } 14181558Srgrimes 1419159402Skib /* reparent window process to the init to not make a zombie on exit */ 1420159402Skib if ((pid = fork()) == -1) { 1421159402Skib emergency("can't fork for window system on port %s: %m", 1422173785Sobrien sp->se_device); 1423159402Skib _exit(1); 1424159402Skib } 1425159402Skib if (pid) 1426159402Skib _exit(0); 1427159402Skib 14281558Srgrimes sigemptyset(&mask); 14291558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 14301558Srgrimes 14311558Srgrimes if (setsid() < 0) 14321558Srgrimes emergency("setsid failed (window) %m"); 14331558Srgrimes 143421865Sdavidn#ifdef LOGIN_CAP 143521865Sdavidn setprocresources(RESOURCE_WINDOW); 143621865Sdavidn#endif 14373594Sache if (sp->se_type) { 14383594Sache /* Don't use malloc after fork */ 14393594Sache strcpy(term, "TERM="); 1440321907Sdelphij strlcat(term, sp->se_type, sizeof(term)); 14413594Sache env[0] = term; 14423594Sache env[1] = 0; 14433594Sache } 14443594Sache else 14453594Sache env[0] = 0; 14463594Sache execve(sp->se_window_argv[0], sp->se_window_argv, env); 14471558Srgrimes stall("can't exec window system '%s' for port %s: %m", 14481558Srgrimes sp->se_window_argv[0], sp->se_device); 14491558Srgrimes _exit(1); 14501558Srgrimes} 14511558Srgrimes 14521558Srgrimes/* 14531558Srgrimes * Start a login session running. 14541558Srgrimes */ 1455183391Sdelphijstatic pid_t 145692838Simpstart_getty(session_t *sp) 14571558Srgrimes{ 14581558Srgrimes pid_t pid; 14591558Srgrimes sigset_t mask; 14601558Srgrimes time_t current_time = time((time_t *) 0); 14619997Sache int too_quick = 0; 14623594Sache char term[64], *env[2]; 14631558Srgrimes 146410006Smpp if (current_time >= sp->se_started && 14659997Sache current_time - sp->se_started < GETTY_SPACING) { 14669997Sache if (++sp->se_nspace > GETTY_NSPACE) { 14679997Sache sp->se_nspace = 0; 14689997Sache too_quick = 1; 14699997Sache } 14709997Sache } else 14719997Sache sp->se_nspace = 0; 14729997Sache 14731558Srgrimes /* 14741558Srgrimes * fork(), not vfork() -- we can't afford to block. 14751558Srgrimes */ 14761558Srgrimes if ((pid = fork()) == -1) { 14771558Srgrimes emergency("can't fork for getty on port %s: %m", sp->se_device); 14781558Srgrimes return -1; 14791558Srgrimes } 14801558Srgrimes 14811558Srgrimes if (pid) 14821558Srgrimes return pid; 14831558Srgrimes 14849997Sache if (too_quick) { 14859997Sache warning("getty repeating too quickly on port %s, sleeping %d secs", 1486173785Sobrien sp->se_device, GETTY_SLEEP); 14871558Srgrimes sleep((unsigned) GETTY_SLEEP); 14881558Srgrimes } 14891558Srgrimes 14901558Srgrimes if (sp->se_window) { 14911558Srgrimes start_window_system(sp); 14921558Srgrimes sleep(WINDOW_WAIT); 14931558Srgrimes } 14941558Srgrimes 14951558Srgrimes sigemptyset(&mask); 14961558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 14971558Srgrimes 149821865Sdavidn#ifdef LOGIN_CAP 149921865Sdavidn setprocresources(RESOURCE_GETTY); 150021865Sdavidn#endif 15013594Sache if (sp->se_type) { 15023594Sache /* Don't use malloc after fork */ 15033594Sache strcpy(term, "TERM="); 1504321907Sdelphij strlcat(term, sp->se_type, sizeof(term)); 15053594Sache env[0] = term; 15063594Sache env[1] = 0; 1507173785Sobrien } else 15083594Sache env[0] = 0; 15093594Sache execve(sp->se_getty_argv[0], sp->se_getty_argv, env); 15101558Srgrimes stall("can't exec getty '%s' for port %s: %m", 15111558Srgrimes sp->se_getty_argv[0], sp->se_device); 15121558Srgrimes _exit(1); 15131558Srgrimes} 15141558Srgrimes 15151558Srgrimes/* 15161558Srgrimes * Collect exit status for a child. 15171558Srgrimes * If an exiting login, start a new login running. 15181558Srgrimes */ 1519183391Sdelphijstatic void 15201558Srgrimescollect_child(pid_t pid) 15211558Srgrimes{ 152292806Sobrien session_t *sp, *sprev, *snext; 15231558Srgrimes 15241558Srgrimes if (! sessions) 15251558Srgrimes return; 15261558Srgrimes 15271558Srgrimes if (! (sp = find_session(pid))) 15281558Srgrimes return; 15291558Srgrimes 15301558Srgrimes del_session(sp); 15311558Srgrimes sp->se_process = 0; 15321558Srgrimes 15331558Srgrimes if (sp->se_flags & SE_SHUTDOWN) { 153427837Sdavidn if ((sprev = sp->se_prev) != NULL) 15351558Srgrimes sprev->se_next = sp->se_next; 15361558Srgrimes else 15371558Srgrimes sessions = sp->se_next; 153827837Sdavidn if ((snext = sp->se_next) != NULL) 15391558Srgrimes snext->se_prev = sp->se_prev; 15401558Srgrimes free_session(sp); 15411558Srgrimes return; 15421558Srgrimes } 15431558Srgrimes 15441558Srgrimes if ((pid = start_getty(sp)) == -1) { 15451558Srgrimes /* serious trouble */ 15461558Srgrimes requested_transition = clean_ttys; 15471558Srgrimes return; 15481558Srgrimes } 15491558Srgrimes 15501558Srgrimes sp->se_process = pid; 15511558Srgrimes sp->se_started = time((time_t *) 0); 15521558Srgrimes add_session(sp); 15531558Srgrimes} 15541558Srgrimes 15551558Srgrimes/* 15561558Srgrimes * Catch a signal and request a state transition. 15571558Srgrimes */ 1558183391Sdelphijstatic void 155992838Simptransition_handler(int sig) 15601558Srgrimes{ 15611558Srgrimes 15621558Srgrimes switch (sig) { 15631558Srgrimes case SIGHUP: 1564217750Sjilles if (current_state == read_ttys || current_state == multi_user || 1565217750Sjilles current_state == clean_ttys || current_state == catatonia) 1566217750Sjilles requested_transition = clean_ttys; 15671558Srgrimes break; 156847962Sru case SIGUSR2: 156947962Sru howto = RB_POWEROFF; 157047962Sru case SIGUSR1: 157147962Sru howto |= RB_HALT; 15722323Snate case SIGINT: 15732327Sjkh Reboot = TRUE; 15741558Srgrimes case SIGTERM: 1575217750Sjilles if (current_state == read_ttys || current_state == multi_user || 1576217750Sjilles current_state == clean_ttys || current_state == catatonia) 1577217750Sjilles requested_transition = death; 1578217750Sjilles else 1579217750Sjilles requested_transition = death_single; 15801558Srgrimes break; 15811558Srgrimes case SIGTSTP: 1582217750Sjilles if (current_state == runcom || current_state == read_ttys || 1583217750Sjilles current_state == clean_ttys || 1584217750Sjilles current_state == multi_user || current_state == catatonia) 1585217750Sjilles requested_transition = catatonia; 15861558Srgrimes break; 1587293744Strasz case SIGEMT: 1588293744Strasz requested_transition = reroot; 1589293744Strasz break; 15901558Srgrimes default: 15911558Srgrimes requested_transition = 0; 15921558Srgrimes break; 15931558Srgrimes } 15941558Srgrimes} 15951558Srgrimes 15961558Srgrimes/* 15971558Srgrimes * Take the system multiuser. 15981558Srgrimes */ 1599183391Sdelphijstatic state_func_t 160092838Simpmulti_user(void) 16011558Srgrimes{ 16021558Srgrimes pid_t pid; 160392806Sobrien session_t *sp; 16041558Srgrimes 16051558Srgrimes requested_transition = 0; 16061558Srgrimes 16071558Srgrimes /* 16081558Srgrimes * If the administrator has not set the security level to -1 16091558Srgrimes * to indicate that the kernel should not run multiuser in secure 16108871Srgrimes * mode, and the run script has not set a higher level of security 16111558Srgrimes * than level 1, then put the kernel into secure mode. 16121558Srgrimes */ 16131558Srgrimes if (getsecuritylevel() == 0) 16141558Srgrimes setsecuritylevel(1); 16151558Srgrimes 16161558Srgrimes for (sp = sessions; sp; sp = sp->se_next) { 16171558Srgrimes if (sp->se_process) 16181558Srgrimes continue; 16191558Srgrimes if ((pid = start_getty(sp)) == -1) { 16201558Srgrimes /* serious trouble */ 16211558Srgrimes requested_transition = clean_ttys; 16221558Srgrimes break; 16231558Srgrimes } 16241558Srgrimes sp->se_process = pid; 16251558Srgrimes sp->se_started = time((time_t *) 0); 16261558Srgrimes add_session(sp); 16271558Srgrimes } 16281558Srgrimes 16291558Srgrimes while (!requested_transition) 16301558Srgrimes if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 16311558Srgrimes collect_child(pid); 16321558Srgrimes 16331558Srgrimes return (state_func_t) requested_transition; 16341558Srgrimes} 16351558Srgrimes 16361558Srgrimes/* 163757344Salfred * This is an (n*2)+(n^2) algorithm. We hope it isn't run often... 16381558Srgrimes */ 1639183391Sdelphijstatic state_func_t 164092838Simpclean_ttys(void) 16411558Srgrimes{ 164292806Sobrien session_t *sp, *sprev; 164392806Sobrien struct ttyent *typ; 164492806Sobrien int session_index = 0; 164592806Sobrien int devlen; 16463594Sache char *old_getty, *old_window, *old_type; 16471558Srgrimes 1648173787Sobrien /* 1649173787Sobrien * mark all sessions for death, (!SE_PRESENT) 165057344Salfred * as we find or create new ones they'll be marked as keepers, 165157344Salfred * we'll later nuke all the ones not found in /etc/ttys 165257344Salfred */ 165357344Salfred for (sp = sessions; sp != NULL; sp = sp->se_next) 165457344Salfred sp->se_flags &= ~SE_PRESENT; 165557344Salfred 16561558Srgrimes devlen = sizeof(_PATH_DEV) - 1; 165727837Sdavidn while ((typ = getttyent()) != NULL) { 16581558Srgrimes ++session_index; 16591558Srgrimes 16601558Srgrimes for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 16611558Srgrimes if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 16621558Srgrimes break; 16631558Srgrimes 16641558Srgrimes if (sp) { 166557344Salfred /* we want this one to live */ 166657344Salfred sp->se_flags |= SE_PRESENT; 16671558Srgrimes if (sp->se_index != session_index) { 16681558Srgrimes warning("port %s changed utmp index from %d to %d", 16691558Srgrimes sp->se_device, sp->se_index, 16701558Srgrimes session_index); 16711558Srgrimes sp->se_index = session_index; 16721558Srgrimes } 16731558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 16741558Srgrimes typ->ty_getty == 0) { 16751558Srgrimes sp->se_flags |= SE_SHUTDOWN; 16761558Srgrimes kill(sp->se_process, SIGHUP); 16771558Srgrimes continue; 16781558Srgrimes } 16791558Srgrimes sp->se_flags &= ~SE_SHUTDOWN; 16803594Sache old_getty = sp->se_getty ? strdup(sp->se_getty) : 0; 16813594Sache old_window = sp->se_window ? strdup(sp->se_window) : 0; 16823594Sache old_type = sp->se_type ? strdup(sp->se_type) : 0; 16831558Srgrimes if (setupargv(sp, typ) == 0) { 16841558Srgrimes warning("can't parse getty for port %s", 16851558Srgrimes sp->se_device); 16861558Srgrimes sp->se_flags |= SE_SHUTDOWN; 16871558Srgrimes kill(sp->se_process, SIGHUP); 16881558Srgrimes } 16893594Sache else if ( !old_getty 169027837Sdavidn || (!old_type && sp->se_type) 169127837Sdavidn || (old_type && !sp->se_type) 169227837Sdavidn || (!old_window && sp->se_window) 169327837Sdavidn || (old_window && !sp->se_window) 169427837Sdavidn || (strcmp(old_getty, sp->se_getty) != 0) 169527837Sdavidn || (old_window && strcmp(old_window, sp->se_window) != 0) 169627837Sdavidn || (old_type && strcmp(old_type, sp->se_type) != 0) 16973594Sache ) { 16983594Sache /* Don't set SE_SHUTDOWN here */ 16993594Sache sp->se_nspace = 0; 17003594Sache sp->se_started = 0; 17013594Sache kill(sp->se_process, SIGHUP); 17023594Sache } 17033594Sache if (old_getty) 17043594Sache free(old_getty); 170578484Smikeh if (old_window) 17063594Sache free(old_window); 17073594Sache if (old_type) 17083594Sache free(old_type); 17091558Srgrimes continue; 17101558Srgrimes } 17111558Srgrimes 17121558Srgrimes new_session(sprev, session_index, typ); 17131558Srgrimes } 17141558Srgrimes 17151558Srgrimes endttyent(); 17161558Srgrimes 171757344Salfred /* 171857344Salfred * sweep through and kill all deleted sessions 171957344Salfred * ones who's /etc/ttys line was deleted (SE_PRESENT unset) 172057344Salfred */ 172157344Salfred for (sp = sessions; sp != NULL; sp = sp->se_next) { 172257344Salfred if ((sp->se_flags & SE_PRESENT) == 0) { 172357344Salfred sp->se_flags |= SE_SHUTDOWN; 172457344Salfred kill(sp->se_process, SIGHUP); 172557344Salfred } 172657344Salfred } 172757344Salfred 17281558Srgrimes return (state_func_t) multi_user; 17291558Srgrimes} 17301558Srgrimes 17311558Srgrimes/* 17321558Srgrimes * Block further logins. 17331558Srgrimes */ 1734183391Sdelphijstatic state_func_t 173592838Simpcatatonia(void) 17361558Srgrimes{ 173792806Sobrien session_t *sp; 17381558Srgrimes 17391558Srgrimes for (sp = sessions; sp; sp = sp->se_next) 17401558Srgrimes sp->se_flags |= SE_SHUTDOWN; 17411558Srgrimes 17421558Srgrimes return (state_func_t) multi_user; 17431558Srgrimes} 17441558Srgrimes 17451558Srgrimes/* 17461558Srgrimes * Note SIGALRM. 17471558Srgrimes */ 1748183391Sdelphijstatic void 174992838Simpalrm_handler(int sig) 17501558Srgrimes{ 1751173785Sobrien 175227837Sdavidn (void)sig; 17531558Srgrimes clang = 1; 17541558Srgrimes} 17551558Srgrimes 17561558Srgrimes/* 17571558Srgrimes * Bring the system down to single user. 17581558Srgrimes */ 1759183391Sdelphijstatic state_func_t 176092838Simpdeath(void) 17611558Srgrimes{ 1762289032Scperciva int block, blocked; 1763289032Scperciva size_t len; 17641558Srgrimes 1765289032Scperciva /* Temporarily block suspend. */ 1766289032Scperciva len = sizeof(blocked); 1767289032Scperciva block = 1; 1768289032Scperciva if (sysctlbyname("kern.suspend_blocked", &blocked, &len, 1769289032Scperciva &block, sizeof(block)) == -1) 1770289032Scperciva blocked = 0; 1771289032Scperciva 1772194198Sed /* 1773194198Sed * Also revoke the TTY here. Because runshutdown() may reopen 1774194198Sed * the TTY whose getty we're killing here, there is no guarantee 1775194198Sed * runshutdown() will perform the initial open() call, causing 1776194198Sed * the terminal attributes to be misconfigured. 1777194198Sed */ 1778293744Strasz revoke_ttys(); 17791558Srgrimes 178027837Sdavidn /* Try to run the rc.shutdown script within a period of time */ 1781173785Sobrien runshutdown(); 1782173785Sobrien 1783289032Scperciva /* Unblock suspend if we blocked it. */ 1784289032Scperciva if (!blocked) 1785289032Scperciva sysctlbyname("kern.suspend_blocked", NULL, NULL, 1786289032Scperciva &blocked, sizeof(blocked)); 1787289032Scperciva 1788217750Sjilles return (state_func_t) death_single; 1789217750Sjilles} 1790217750Sjilles 1791217750Sjilles/* 1792217750Sjilles * Do what is necessary to reinitialize single user mode or reboot 1793217750Sjilles * from an incomplete state. 1794217750Sjilles */ 1795217750Sjillesstatic state_func_t 1796217750Sjillesdeath_single(void) 1797217750Sjilles{ 1798217750Sjilles int i; 1799217750Sjilles pid_t pid; 1800217750Sjilles static const int death_sigs[2] = { SIGTERM, SIGKILL }; 1801217750Sjilles 1802217750Sjilles revoke(_PATH_CONSOLE); 1803217750Sjilles 180427197Sache for (i = 0; i < 2; ++i) { 18051558Srgrimes if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 18061558Srgrimes return (state_func_t) single_user; 18071558Srgrimes 18081558Srgrimes clang = 0; 18091558Srgrimes alarm(DEATH_WATCH); 18101558Srgrimes do 18111558Srgrimes if ((pid = waitpid(-1, (int *)0, 0)) != -1) 18121558Srgrimes collect_child(pid); 18131558Srgrimes while (clang == 0 && errno != ECHILD); 18141558Srgrimes 18151558Srgrimes if (errno == ECHILD) 18161558Srgrimes return (state_func_t) single_user; 18171558Srgrimes } 18181558Srgrimes 18191558Srgrimes warning("some processes would not die; ps axl advised"); 18201558Srgrimes 18211558Srgrimes return (state_func_t) single_user; 18221558Srgrimes} 182327837Sdavidn 1824293744Straszstatic void 1825293744Straszrevoke_ttys(void) 1826293744Strasz{ 1827293744Strasz session_t *sp; 1828293744Strasz 1829293744Strasz for (sp = sessions; sp; sp = sp->se_next) { 1830293744Strasz sp->se_flags |= SE_SHUTDOWN; 1831293744Strasz kill(sp->se_process, SIGHUP); 1832293744Strasz revoke(sp->se_device); 1833293744Strasz } 1834293744Strasz} 1835293744Strasz 183627837Sdavidn/* 183727837Sdavidn * Run the system shutdown script. 183827837Sdavidn * 183927837Sdavidn * Exit codes: XXX I should document more 184027837Sdavidn * -2 shutdown script terminated abnormally 184127837Sdavidn * -1 fatal error - can't run script 184227837Sdavidn * 0 good. 184327837Sdavidn * >0 some error (exit code) 184427837Sdavidn */ 1845183391Sdelphijstatic int 184692838Simprunshutdown(void) 184727837Sdavidn{ 184827837Sdavidn pid_t pid, wpid; 184927837Sdavidn int status; 185027837Sdavidn int shutdowntimeout; 185127837Sdavidn size_t len; 185253550Sdillon char *argv[4]; 1853166484Simp const char *shell; 185427837Sdavidn struct sigaction sa; 185528344Sdavidn struct stat sb; 185627837Sdavidn 185728344Sdavidn /* 185828344Sdavidn * rc.shutdown is optional, so to prevent any unnecessary 185928344Sdavidn * complaints from the shell we simply don't run it if the 186028344Sdavidn * file does not exist. If the stat() here fails for other 186128344Sdavidn * reasons, we'll let the shell complain. 186228344Sdavidn */ 186328344Sdavidn if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT) 186428344Sdavidn return 0; 186528344Sdavidn 1866166484Simp shell = get_shell(); 1867166484Simp 186827837Sdavidn if ((pid = fork()) == 0) { 186927837Sdavidn sigemptyset(&sa.sa_mask); 187027837Sdavidn sa.sa_flags = 0; 187127837Sdavidn sa.sa_handler = SIG_IGN; 1872173785Sobrien sigaction(SIGTSTP, &sa, (struct sigaction *)0); 1873173785Sobrien sigaction(SIGHUP, &sa, (struct sigaction *)0); 187427837Sdavidn 1875232977Sed open_console(); 187627837Sdavidn 1877140070Sdelphij char _sh[] = "sh"; 1878140070Sdelphij char _reboot[] = "reboot"; 1879140070Sdelphij char _single[] = "single"; 1880140070Sdelphij char _path_rundown[] = _PATH_RUNDOWN; 1881140070Sdelphij 1882140070Sdelphij argv[0] = _sh; 1883140070Sdelphij argv[1] = _path_rundown; 1884140070Sdelphij argv[2] = Reboot ? _reboot : _single; 188553550Sdillon argv[3] = 0; 188627837Sdavidn 188727837Sdavidn sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 188827837Sdavidn 188927941Sache#ifdef LOGIN_CAP 189027941Sache setprocresources(RESOURCE_RC); 189127941Sache#endif 1892166484Simp execv(shell, argv); 1893166484Simp warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN); 189427837Sdavidn _exit(1); /* force single user mode */ 189527837Sdavidn } 189627837Sdavidn 189727837Sdavidn if (pid == -1) { 1898166484Simp emergency("can't fork for %s on %s: %m", shell, _PATH_RUNDOWN); 189927837Sdavidn while (waitpid(-1, (int *) 0, WNOHANG) > 0) 190027837Sdavidn continue; 190127837Sdavidn sleep(STALL_TIMEOUT); 190227837Sdavidn return -1; 190327837Sdavidn } 190427837Sdavidn 190527837Sdavidn len = sizeof(shutdowntimeout); 1906173785Sobrien if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len, 1907173785Sobrien NULL, 0) == -1 || shutdowntimeout < 2) 1908173785Sobrien shutdowntimeout = DEATH_SCRIPT; 190927837Sdavidn alarm(shutdowntimeout); 191027837Sdavidn clang = 0; 191127837Sdavidn /* 191227837Sdavidn * Copied from single_user(). This is a bit paranoid. 191327837Sdavidn * Use the same ALRM handler. 191427837Sdavidn */ 191527837Sdavidn do { 191627837Sdavidn if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 191727837Sdavidn collect_child(wpid); 191827837Sdavidn if (clang == 1) { 191927837Sdavidn /* we were waiting for the sub-shell */ 192027837Sdavidn kill(wpid, SIGTERM); 1921166484Simp warning("timeout expired for %s on %s: %m; going to " 1922166484Simp "single user mode", shell, _PATH_RUNDOWN); 192327837Sdavidn return -1; 192427837Sdavidn } 192527837Sdavidn if (wpid == -1) { 192627837Sdavidn if (errno == EINTR) 192727837Sdavidn continue; 1928166484Simp warning("wait for %s on %s failed: %m; going to " 1929166484Simp "single user mode", shell, _PATH_RUNDOWN); 193027837Sdavidn return -1; 193127837Sdavidn } 193227837Sdavidn if (wpid == pid && WIFSTOPPED(status)) { 193327837Sdavidn warning("init: %s on %s stopped, restarting\n", 1934166484Simp shell, _PATH_RUNDOWN); 193527837Sdavidn kill(pid, SIGCONT); 193627837Sdavidn wpid = -1; 193727837Sdavidn } 193827837Sdavidn } while (wpid != pid && !clang); 193927837Sdavidn 194027837Sdavidn /* Turn off the alarm */ 194127837Sdavidn alarm(0); 194227837Sdavidn 194327837Sdavidn if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 194427837Sdavidn requested_transition == catatonia) { 194527837Sdavidn /* 194627837Sdavidn * /etc/rc.shutdown executed /sbin/reboot; 194727837Sdavidn * wait for the end quietly 194827837Sdavidn */ 194927837Sdavidn sigset_t s; 195027837Sdavidn 195127837Sdavidn sigfillset(&s); 195227837Sdavidn for (;;) 195327837Sdavidn sigsuspend(&s); 195427837Sdavidn } 195527837Sdavidn 195627837Sdavidn if (!WIFEXITED(status)) { 1957166484Simp warning("%s on %s terminated abnormally, going to " 1958166484Simp "single user mode", shell, _PATH_RUNDOWN); 195927837Sdavidn return -2; 196027837Sdavidn } 196127837Sdavidn 196227837Sdavidn if ((status = WEXITSTATUS(status)) != 0) 196327837Sdavidn warning("%s returned status %d", _PATH_RUNDOWN, status); 196427837Sdavidn 196527837Sdavidn return status; 196627837Sdavidn} 196727837Sdavidn 1968140070Sdelphijstatic char * 1969140070Sdelphijstrk(char *p) 19705478Sache{ 1971173785Sobrien static char *t; 1972173785Sobrien char *q; 1973173785Sobrien int c; 19745478Sache 1975173785Sobrien if (p) 1976173785Sobrien t = p; 1977173785Sobrien if (!t) 1978173785Sobrien return 0; 19795478Sache 1980173785Sobrien c = *t; 1981173785Sobrien while (c == ' ' || c == '\t' ) 1982173785Sobrien c = *++t; 1983173785Sobrien if (!c) { 1984173785Sobrien t = 0; 1985173785Sobrien return 0; 1986173785Sobrien } 19875478Sache q = t; 1988173785Sobrien if (c == '\'') { 1989173785Sobrien c = *++t; 1990173785Sobrien q = t; 1991173785Sobrien while (c && c != '\'') 1992173785Sobrien c = *++t; 1993173785Sobrien if (!c) /* unterminated string */ 1994173785Sobrien q = t = 0; 1995173785Sobrien else 1996173785Sobrien *t++ = 0; 1997173785Sobrien } else { 1998173785Sobrien while (c && c != ' ' && c != '\t' ) 1999173785Sobrien c = *++t; 2000173785Sobrien *t++ = 0; 2001173785Sobrien if (!c) 2002173785Sobrien t = 0; 2003173785Sobrien } 2004173785Sobrien return q; 20055478Sache} 200621865Sdavidn 200721865Sdavidn#ifdef LOGIN_CAP 2008183391Sdelphijstatic void 200992838Simpsetprocresources(const char *cname) 201021865Sdavidn{ 201121941Sdavidn login_cap_t *lc; 201227176Sache if ((lc = login_getclassbyname(cname, NULL)) != NULL) { 2013173785Sobrien setusercontext(lc, (struct passwd*)NULL, 0, 2014254288Sjilles LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | 2015254288Sjilles LOGIN_SETLOGINCLASS | LOGIN_SETCPUMASK); 201621941Sdavidn login_close(lc); 201721941Sdavidn } 201821865Sdavidn} 201921865Sdavidn#endif 2020