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: releng/10.3/sbin/init/init.c 293747 2016-01-12 10:24:08Z trasz $"; 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]; 8791558Srgrimes#ifdef SECURE 8801558Srgrimes struct ttyent *typ; 8811558Srgrimes struct passwd *pp; 8821558Srgrimes static const char banner[] = 8831558Srgrimes "Enter root password, or ^D to go multi-user\n"; 8841558Srgrimes char *clear, *password; 8851558Srgrimes#endif 88637814Sphk#ifdef DEBUGSHELL 88737814Sphk char altshell[128]; 88837814Sphk#endif 8891558Srgrimes 8902327Sjkh if (Reboot) { 89147962Sru /* Instead of going single user, let's reboot the machine */ 8922323Snate sync(); 89347962Sru reboot(howto); 8942323Snate _exit(0); 8952323Snate } 8962323Snate 897166484Simp shell = get_shell(); 898166484Simp 8991558Srgrimes if ((pid = fork()) == 0) { 9001558Srgrimes /* 9011558Srgrimes * Start the single user session. 9021558Srgrimes */ 903232977Sed open_console(); 9041558Srgrimes 9051558Srgrimes#ifdef SECURE 9061558Srgrimes /* 9071558Srgrimes * Check the root password. 9081558Srgrimes * We don't care if the console is 'on' by default; 9091558Srgrimes * it's the only tty that can be 'off' and 'secure'. 9101558Srgrimes */ 9111558Srgrimes typ = getttynam("console"); 9121558Srgrimes pp = getpwnam("root"); 91353550Sdillon if (typ && (typ->ty_status & TTY_SECURE) == 0 && 91453550Sdillon pp && *pp->pw_passwd) { 915166484Simp write_stderr(banner); 9161558Srgrimes for (;;) { 9171558Srgrimes clear = getpass("Password:"); 9181558Srgrimes if (clear == 0 || *clear == '\0') 9191558Srgrimes _exit(0); 9201558Srgrimes password = crypt(clear, pp->pw_passwd); 9211558Srgrimes bzero(clear, _PASSWORD_LEN); 922232841Sed if (password == NULL || 923231994Skevlo strcmp(password, pp->pw_passwd) == 0) 9241558Srgrimes break; 9251558Srgrimes warning("single-user login failed\n"); 9261558Srgrimes } 9271558Srgrimes } 9281558Srgrimes endttyent(); 9291558Srgrimes endpwent(); 9301558Srgrimes#endif /* SECURE */ 9311558Srgrimes 9321558Srgrimes#ifdef DEBUGSHELL 9331558Srgrimes { 93437814Sphk char *cp = altshell; 9351558Srgrimes int num; 9361558Srgrimes 937166484Simp#define SHREQUEST "Enter full pathname of shell or RETURN for " 938166484Simp write_stderr(SHREQUEST); 939166484Simp write_stderr(shell); 940166484Simp write_stderr(": "); 9411558Srgrimes while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 9421558Srgrimes num != 0 && *cp != '\n' && cp < &altshell[127]) 943173785Sobrien cp++; 9441558Srgrimes *cp = '\0'; 9451558Srgrimes if (altshell[0] != '\0') 9461558Srgrimes shell = altshell; 9471558Srgrimes } 9481558Srgrimes#endif /* DEBUGSHELL */ 9491558Srgrimes 9501558Srgrimes /* 9511558Srgrimes * Unblock signals. 9521558Srgrimes * We catch all the interesting ones, 9531558Srgrimes * and those are reset to SIG_DFL on exec. 9541558Srgrimes */ 9551558Srgrimes sigemptyset(&mask); 9561558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 9571558Srgrimes 9581558Srgrimes /* 9591558Srgrimes * Fire off a shell. 9601558Srgrimes * If the default one doesn't work, try the Bourne shell. 9611558Srgrimes */ 962140070Sdelphij 963140070Sdelphij char name[] = "-sh"; 964140070Sdelphij 965140070Sdelphij argv[0] = name; 9661558Srgrimes argv[1] = 0; 9671558Srgrimes execv(shell, argv); 9681558Srgrimes emergency("can't exec %s for single user: %m", shell); 9691558Srgrimes execv(_PATH_BSHELL, argv); 9701558Srgrimes emergency("can't exec %s for single user: %m", _PATH_BSHELL); 9711558Srgrimes sleep(STALL_TIMEOUT); 9721558Srgrimes _exit(1); 9731558Srgrimes } 9741558Srgrimes 9751558Srgrimes if (pid == -1) { 9761558Srgrimes /* 9771558Srgrimes * We are seriously hosed. Do our best. 9781558Srgrimes */ 9791558Srgrimes emergency("can't fork single-user shell, trying again"); 9801558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 9811558Srgrimes continue; 9821558Srgrimes return (state_func_t) single_user; 9831558Srgrimes } 9841558Srgrimes 9851558Srgrimes requested_transition = 0; 9861558Srgrimes do { 9871558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 9881558Srgrimes collect_child(wpid); 9891558Srgrimes if (wpid == -1) { 9901558Srgrimes if (errno == EINTR) 9911558Srgrimes continue; 9921558Srgrimes warning("wait for single-user shell failed: %m; restarting"); 9931558Srgrimes return (state_func_t) single_user; 9941558Srgrimes } 9951558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 9961558Srgrimes warning("init: shell stopped, restarting\n"); 9971558Srgrimes kill(pid, SIGCONT); 9981558Srgrimes wpid = -1; 9991558Srgrimes } 10001558Srgrimes } while (wpid != pid && !requested_transition); 10011558Srgrimes 10021558Srgrimes if (requested_transition) 10031558Srgrimes return (state_func_t) requested_transition; 10041558Srgrimes 10051558Srgrimes if (!WIFEXITED(status)) { 10068871Srgrimes if (WTERMSIG(status) == SIGKILL) { 10078871Srgrimes /* 10088871Srgrimes * reboot(8) killed shell? 10091558Srgrimes */ 10101558Srgrimes warning("single user shell terminated."); 10111558Srgrimes sleep(STALL_TIMEOUT); 10121558Srgrimes _exit(0); 10138871Srgrimes } else { 10141558Srgrimes warning("single user shell terminated, restarting"); 10151558Srgrimes return (state_func_t) single_user; 10161558Srgrimes } 10171558Srgrimes } 10181558Srgrimes 10191558Srgrimes runcom_mode = FASTBOOT; 10201558Srgrimes return (state_func_t) runcom; 10211558Srgrimes} 10221558Srgrimes 10231558Srgrimes/* 10241558Srgrimes * Run the system startup script. 10251558Srgrimes */ 1026183391Sdelphijstatic state_func_t 102792838Simpruncom(void) 10281558Srgrimes{ 1029166484Simp state_func_t next_transition; 1030166484Simp 1031166484Simp if ((next_transition = run_script(_PATH_RUNCOM)) != 0) 1032166484Simp return next_transition; 1033166484Simp 1034166484Simp runcom_mode = AUTOBOOT; /* the default */ 1035166484Simp return (state_func_t) read_ttys; 1036166484Simp} 1037166484Simp 1038166484Simp/* 1039166484Simp * Run a shell script. 1040166484Simp * Returns 0 on success, otherwise the next transition to enter: 1041166484Simp * - single_user if fork/execv/waitpid failed, or if the script 1042166484Simp * terminated with a signal or exit code != 0. 1043217750Sjilles * - death_single if a SIGTERM was delivered to init(8). 1044166484Simp */ 1045183391Sdelphijstatic state_func_t 1046166484Simprun_script(const char *script) 1047166484Simp{ 10481558Srgrimes pid_t pid, wpid; 10491558Srgrimes int status; 10501558Srgrimes char *argv[4]; 1051166484Simp const char *shell; 10521558Srgrimes struct sigaction sa; 10531558Srgrimes 1054166484Simp shell = get_shell(); 1055166484Simp 10561558Srgrimes if ((pid = fork()) == 0) { 10571558Srgrimes sigemptyset(&sa.sa_mask); 10581558Srgrimes sa.sa_flags = 0; 10591558Srgrimes sa.sa_handler = SIG_IGN; 1060173785Sobrien sigaction(SIGTSTP, &sa, (struct sigaction *)0); 1061173785Sobrien sigaction(SIGHUP, &sa, (struct sigaction *)0); 10621558Srgrimes 1063232977Sed open_console(); 10641558Srgrimes 1065232841Sed char _sh[] = "sh"; 1066140070Sdelphij char _autoboot[] = "autoboot"; 1067140070Sdelphij 1068140070Sdelphij argv[0] = _sh; 1069166484Simp argv[1] = __DECONST(char *, script); 1070140070Sdelphij argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0; 10711558Srgrimes argv[3] = 0; 10721558Srgrimes 10731558Srgrimes sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 10741558Srgrimes 107521865Sdavidn#ifdef LOGIN_CAP 107621865Sdavidn setprocresources(RESOURCE_RC); 107721865Sdavidn#endif 1078166484Simp execv(shell, argv); 1079166484Simp stall("can't exec %s for %s: %m", shell, script); 10801558Srgrimes _exit(1); /* force single user mode */ 10811558Srgrimes } 10821558Srgrimes 10831558Srgrimes if (pid == -1) { 1084166484Simp emergency("can't fork for %s on %s: %m", shell, script); 10851558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 10861558Srgrimes continue; 10871558Srgrimes sleep(STALL_TIMEOUT); 10881558Srgrimes return (state_func_t) single_user; 10891558Srgrimes } 10901558Srgrimes 10911558Srgrimes /* 10921558Srgrimes * Copied from single_user(). This is a bit paranoid. 10931558Srgrimes */ 109485010Sdes requested_transition = 0; 10951558Srgrimes do { 10961558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 10971558Srgrimes collect_child(wpid); 10981558Srgrimes if (wpid == -1) { 1099293744Strasz if (requested_transition == death_single || 1100293744Strasz requested_transition == reroot) 1101293744Strasz return (state_func_t) requested_transition; 11021558Srgrimes if (errno == EINTR) 11031558Srgrimes continue; 1104166484Simp warning("wait for %s on %s failed: %m; going to " 1105166484Simp "single user mode", shell, script); 11061558Srgrimes return (state_func_t) single_user; 11071558Srgrimes } 11081558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 11091558Srgrimes warning("init: %s on %s stopped, restarting\n", 1110173785Sobrien shell, script); 11111558Srgrimes kill(pid, SIGCONT); 11121558Srgrimes wpid = -1; 11131558Srgrimes } 11141558Srgrimes } while (wpid != pid); 11151558Srgrimes 11161558Srgrimes if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 11171558Srgrimes requested_transition == catatonia) { 11181558Srgrimes /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 11191558Srgrimes sigset_t s; 11201558Srgrimes 11211558Srgrimes sigfillset(&s); 11221558Srgrimes for (;;) 11231558Srgrimes sigsuspend(&s); 11241558Srgrimes } 11251558Srgrimes 11261558Srgrimes if (!WIFEXITED(status)) { 1127166484Simp warning("%s on %s terminated abnormally, going to single " 1128166484Simp "user mode", shell, script); 11291558Srgrimes return (state_func_t) single_user; 11301558Srgrimes } 11311558Srgrimes 11321558Srgrimes if (WEXITSTATUS(status)) 11331558Srgrimes return (state_func_t) single_user; 11341558Srgrimes 1135166484Simp return (state_func_t) 0; 11361558Srgrimes} 11371558Srgrimes 11381558Srgrimes/* 11391558Srgrimes * Open the session database. 11401558Srgrimes * 11411558Srgrimes * NB: We could pass in the size here; is it necessary? 11421558Srgrimes */ 1143183391Sdelphijstatic int 114492838Simpstart_session_db(void) 11451558Srgrimes{ 11461558Srgrimes if (session_db && (*session_db->close)(session_db)) 11471558Srgrimes emergency("session database close: %s", strerror(errno)); 11481558Srgrimes if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 11491558Srgrimes emergency("session database open: %s", strerror(errno)); 11501558Srgrimes return (1); 11511558Srgrimes } 11521558Srgrimes return (0); 11538871Srgrimes 11541558Srgrimes} 11551558Srgrimes 11561558Srgrimes/* 11571558Srgrimes * Add a new login session. 11581558Srgrimes */ 1159183391Sdelphijstatic void 116092838Simpadd_session(session_t *sp) 11611558Srgrimes{ 11621558Srgrimes DBT key; 11631558Srgrimes DBT data; 11641558Srgrimes 11651558Srgrimes key.data = &sp->se_process; 11661558Srgrimes key.size = sizeof sp->se_process; 11671558Srgrimes data.data = &sp; 11681558Srgrimes data.size = sizeof sp; 11691558Srgrimes 11701558Srgrimes if ((*session_db->put)(session_db, &key, &data, 0)) 11711558Srgrimes emergency("insert %d: %s", sp->se_process, strerror(errno)); 11721558Srgrimes} 11731558Srgrimes 11741558Srgrimes/* 11751558Srgrimes * Delete an old login session. 11761558Srgrimes */ 1177183391Sdelphijstatic void 117892838Simpdel_session(session_t *sp) 11791558Srgrimes{ 11801558Srgrimes DBT key; 11811558Srgrimes 11821558Srgrimes key.data = &sp->se_process; 11831558Srgrimes key.size = sizeof sp->se_process; 11841558Srgrimes 11851558Srgrimes if ((*session_db->del)(session_db, &key, 0)) 11861558Srgrimes emergency("delete %d: %s", sp->se_process, strerror(errno)); 11871558Srgrimes} 11881558Srgrimes 11891558Srgrimes/* 11901558Srgrimes * Look up a login session by pid. 11911558Srgrimes */ 1192183391Sdelphijstatic session_t * 11931558Srgrimesfind_session(pid_t pid) 11941558Srgrimes{ 11951558Srgrimes DBT key; 11961558Srgrimes DBT data; 11971558Srgrimes session_t *ret; 11981558Srgrimes 11991558Srgrimes key.data = &pid; 12001558Srgrimes key.size = sizeof pid; 12011558Srgrimes if ((*session_db->get)(session_db, &key, &data, 0) != 0) 12021558Srgrimes return 0; 12031558Srgrimes bcopy(data.data, (char *)&ret, sizeof(ret)); 12041558Srgrimes return ret; 12051558Srgrimes} 12061558Srgrimes 12071558Srgrimes/* 12081558Srgrimes * Construct an argument vector from a command line. 12091558Srgrimes */ 1210183391Sdelphijstatic char ** 121192838Simpconstruct_argv(char *command) 12121558Srgrimes{ 121392806Sobrien int argc = 0; 121492806Sobrien char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 12151558Srgrimes * sizeof (char *)); 12161558Srgrimes 121749018Sru if ((argv[argc++] = strk(command)) == 0) { 121849018Sru free(argv); 121949018Sru return (NULL); 122049018Sru } 122127837Sdavidn while ((argv[argc++] = strk((char *) 0)) != NULL) 12221558Srgrimes continue; 12231558Srgrimes return argv; 12241558Srgrimes} 12251558Srgrimes 12261558Srgrimes/* 12271558Srgrimes * Deallocate a session descriptor. 12281558Srgrimes */ 1229183391Sdelphijstatic void 123092838Simpfree_session(session_t *sp) 12311558Srgrimes{ 12321558Srgrimes free(sp->se_device); 12331558Srgrimes if (sp->se_getty) { 12341558Srgrimes free(sp->se_getty); 12353594Sache free(sp->se_getty_argv_space); 12361558Srgrimes free(sp->se_getty_argv); 12371558Srgrimes } 12381558Srgrimes if (sp->se_window) { 12391558Srgrimes free(sp->se_window); 12403594Sache free(sp->se_window_argv_space); 12411558Srgrimes free(sp->se_window_argv); 12421558Srgrimes } 12433594Sache if (sp->se_type) 12443594Sache free(sp->se_type); 12451558Srgrimes free(sp); 12461558Srgrimes} 12471558Srgrimes 12481558Srgrimes/* 12491558Srgrimes * Allocate a new session descriptor. 125057344Salfred * Mark it SE_PRESENT. 12511558Srgrimes */ 1252183391Sdelphijstatic session_t * 125392838Simpnew_session(session_t *sprev, int session_index, struct ttyent *typ) 12541558Srgrimes{ 125592806Sobrien session_t *sp; 125627029Spst int fd; 12571558Srgrimes 12581558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 12591558Srgrimes typ->ty_name == 0 || 12601558Srgrimes typ->ty_getty == 0) 12611558Srgrimes return 0; 12621558Srgrimes 126327215Sache sp = (session_t *) calloc(1, sizeof (session_t)); 12641558Srgrimes 12651558Srgrimes sp->se_index = session_index; 126657344Salfred sp->se_flags |= SE_PRESENT; 12671558Srgrimes 12681558Srgrimes sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 1269173785Sobrien sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 12701558Srgrimes 127127029Spst /* 127227029Spst * Attempt to open the device, if we get "device not configured" 127327029Spst * then don't add the device to the session list. 127427029Spst */ 127527029Spst if ((fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0)) < 0) { 1276135868Simp if (errno == ENXIO) { 127727029Spst free_session(sp); 127827029Spst return (0); 127927029Spst } 128027029Spst } else 128127029Spst close(fd); 128227029Spst 12831558Srgrimes if (setupargv(sp, typ) == 0) { 12841558Srgrimes free_session(sp); 12851558Srgrimes return (0); 12861558Srgrimes } 12871558Srgrimes 12881558Srgrimes sp->se_next = 0; 12891558Srgrimes if (sprev == 0) { 12901558Srgrimes sessions = sp; 12911558Srgrimes sp->se_prev = 0; 12921558Srgrimes } else { 12931558Srgrimes sprev->se_next = sp; 12941558Srgrimes sp->se_prev = sprev; 12951558Srgrimes } 12961558Srgrimes 12971558Srgrimes return sp; 12981558Srgrimes} 12991558Srgrimes 13001558Srgrimes/* 13011558Srgrimes * Calculate getty and if useful window argv vectors. 13021558Srgrimes */ 1303183391Sdelphijstatic int 130492838Simpsetupargv(session_t *sp, struct ttyent *typ) 13051558Srgrimes{ 13061558Srgrimes 13071558Srgrimes if (sp->se_getty) { 13081558Srgrimes free(sp->se_getty); 13093594Sache free(sp->se_getty_argv_space); 13101558Srgrimes free(sp->se_getty_argv); 13111558Srgrimes } 13121558Srgrimes sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); 1313173785Sobrien sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); 13143594Sache sp->se_getty_argv_space = strdup(sp->se_getty); 13153594Sache sp->se_getty_argv = construct_argv(sp->se_getty_argv_space); 13161558Srgrimes if (sp->se_getty_argv == 0) { 13171558Srgrimes warning("can't parse getty for port %s", sp->se_device); 13181558Srgrimes free(sp->se_getty); 13193594Sache free(sp->se_getty_argv_space); 13203594Sache sp->se_getty = sp->se_getty_argv_space = 0; 13211558Srgrimes return (0); 13221558Srgrimes } 13233594Sache if (sp->se_window) { 132449018Sru free(sp->se_window); 13253594Sache free(sp->se_window_argv_space); 13263594Sache free(sp->se_window_argv); 13273594Sache } 13283594Sache sp->se_window = sp->se_window_argv_space = 0; 13293594Sache sp->se_window_argv = 0; 13301558Srgrimes if (typ->ty_window) { 13311558Srgrimes sp->se_window = strdup(typ->ty_window); 13323594Sache sp->se_window_argv_space = strdup(sp->se_window); 13333594Sache sp->se_window_argv = construct_argv(sp->se_window_argv_space); 13341558Srgrimes if (sp->se_window_argv == 0) { 13351558Srgrimes warning("can't parse window for port %s", 1336173785Sobrien sp->se_device); 13373594Sache free(sp->se_window_argv_space); 13381558Srgrimes free(sp->se_window); 13393594Sache sp->se_window = sp->se_window_argv_space = 0; 13401558Srgrimes return (0); 13411558Srgrimes } 13421558Srgrimes } 13433594Sache if (sp->se_type) 13443594Sache free(sp->se_type); 13453594Sache sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0; 13461558Srgrimes return (1); 13471558Srgrimes} 13481558Srgrimes 13491558Srgrimes/* 13501558Srgrimes * Walk the list of ttys and create sessions for each active line. 13511558Srgrimes */ 1352183391Sdelphijstatic state_func_t 135392838Simpread_ttys(void) 13541558Srgrimes{ 13551558Srgrimes int session_index = 0; 135692806Sobrien session_t *sp, *snext; 135792806Sobrien struct ttyent *typ; 13581558Srgrimes 13591558Srgrimes /* 13601558Srgrimes * Destroy any previous session state. 13611558Srgrimes * There shouldn't be any, but just in case... 13621558Srgrimes */ 13631558Srgrimes for (sp = sessions; sp; sp = snext) { 13641558Srgrimes snext = sp->se_next; 13651558Srgrimes free_session(sp); 13661558Srgrimes } 13671558Srgrimes sessions = 0; 13681558Srgrimes if (start_session_db()) 13691558Srgrimes return (state_func_t) single_user; 13701558Srgrimes 13711558Srgrimes /* 13721558Srgrimes * Allocate a session entry for each active port. 13731558Srgrimes * Note that sp starts at 0. 13741558Srgrimes */ 137527837Sdavidn while ((typ = getttyent()) != NULL) 137627837Sdavidn if ((snext = new_session(sp, ++session_index, typ)) != NULL) 13771558Srgrimes sp = snext; 13781558Srgrimes 13791558Srgrimes endttyent(); 13801558Srgrimes 13811558Srgrimes return (state_func_t) multi_user; 13821558Srgrimes} 13831558Srgrimes 13841558Srgrimes/* 13851558Srgrimes * Start a window system running. 13861558Srgrimes */ 1387183391Sdelphijstatic void 138892838Simpstart_window_system(session_t *sp) 13891558Srgrimes{ 13901558Srgrimes pid_t pid; 13911558Srgrimes sigset_t mask; 13923594Sache char term[64], *env[2]; 1393159402Skib int status; 13941558Srgrimes 13951558Srgrimes if ((pid = fork()) == -1) { 13961558Srgrimes emergency("can't fork for window system on port %s: %m", 1397173785Sobrien sp->se_device); 13981558Srgrimes /* hope that getty fails and we can try again */ 13991558Srgrimes return; 14001558Srgrimes } 1401173785Sobrien if (pid) { 1402159402Skib waitpid(-1, &status, 0); 14031558Srgrimes return; 1404159402Skib } 14051558Srgrimes 1406159402Skib /* reparent window process to the init to not make a zombie on exit */ 1407159402Skib if ((pid = fork()) == -1) { 1408159402Skib emergency("can't fork for window system on port %s: %m", 1409173785Sobrien sp->se_device); 1410159402Skib _exit(1); 1411159402Skib } 1412159402Skib if (pid) 1413159402Skib _exit(0); 1414159402Skib 14151558Srgrimes sigemptyset(&mask); 14161558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 14171558Srgrimes 14181558Srgrimes if (setsid() < 0) 14191558Srgrimes emergency("setsid failed (window) %m"); 14201558Srgrimes 142121865Sdavidn#ifdef LOGIN_CAP 142221865Sdavidn setprocresources(RESOURCE_WINDOW); 142321865Sdavidn#endif 14243594Sache if (sp->se_type) { 14253594Sache /* Don't use malloc after fork */ 14263594Sache strcpy(term, "TERM="); 142722922Sdg strncat(term, sp->se_type, sizeof(term) - 6); 14283594Sache env[0] = term; 14293594Sache env[1] = 0; 14303594Sache } 14313594Sache else 14323594Sache env[0] = 0; 14333594Sache execve(sp->se_window_argv[0], sp->se_window_argv, env); 14341558Srgrimes stall("can't exec window system '%s' for port %s: %m", 14351558Srgrimes sp->se_window_argv[0], sp->se_device); 14361558Srgrimes _exit(1); 14371558Srgrimes} 14381558Srgrimes 14391558Srgrimes/* 14401558Srgrimes * Start a login session running. 14411558Srgrimes */ 1442183391Sdelphijstatic pid_t 144392838Simpstart_getty(session_t *sp) 14441558Srgrimes{ 14451558Srgrimes pid_t pid; 14461558Srgrimes sigset_t mask; 14471558Srgrimes time_t current_time = time((time_t *) 0); 14489997Sache int too_quick = 0; 14493594Sache char term[64], *env[2]; 14501558Srgrimes 145110006Smpp if (current_time >= sp->se_started && 14529997Sache current_time - sp->se_started < GETTY_SPACING) { 14539997Sache if (++sp->se_nspace > GETTY_NSPACE) { 14549997Sache sp->se_nspace = 0; 14559997Sache too_quick = 1; 14569997Sache } 14579997Sache } else 14589997Sache sp->se_nspace = 0; 14599997Sache 14601558Srgrimes /* 14611558Srgrimes * fork(), not vfork() -- we can't afford to block. 14621558Srgrimes */ 14631558Srgrimes if ((pid = fork()) == -1) { 14641558Srgrimes emergency("can't fork for getty on port %s: %m", sp->se_device); 14651558Srgrimes return -1; 14661558Srgrimes } 14671558Srgrimes 14681558Srgrimes if (pid) 14691558Srgrimes return pid; 14701558Srgrimes 14719997Sache if (too_quick) { 14729997Sache warning("getty repeating too quickly on port %s, sleeping %d secs", 1473173785Sobrien sp->se_device, GETTY_SLEEP); 14741558Srgrimes sleep((unsigned) GETTY_SLEEP); 14751558Srgrimes } 14761558Srgrimes 14771558Srgrimes if (sp->se_window) { 14781558Srgrimes start_window_system(sp); 14791558Srgrimes sleep(WINDOW_WAIT); 14801558Srgrimes } 14811558Srgrimes 14821558Srgrimes sigemptyset(&mask); 14831558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 14841558Srgrimes 148521865Sdavidn#ifdef LOGIN_CAP 148621865Sdavidn setprocresources(RESOURCE_GETTY); 148721865Sdavidn#endif 14883594Sache if (sp->se_type) { 14893594Sache /* Don't use malloc after fork */ 14903594Sache strcpy(term, "TERM="); 149122922Sdg strncat(term, sp->se_type, sizeof(term) - 6); 14923594Sache env[0] = term; 14933594Sache env[1] = 0; 1494173785Sobrien } else 14953594Sache env[0] = 0; 14963594Sache execve(sp->se_getty_argv[0], sp->se_getty_argv, env); 14971558Srgrimes stall("can't exec getty '%s' for port %s: %m", 14981558Srgrimes sp->se_getty_argv[0], sp->se_device); 14991558Srgrimes _exit(1); 15001558Srgrimes} 15011558Srgrimes 15021558Srgrimes/* 15031558Srgrimes * Collect exit status for a child. 15041558Srgrimes * If an exiting login, start a new login running. 15051558Srgrimes */ 1506183391Sdelphijstatic void 15071558Srgrimescollect_child(pid_t pid) 15081558Srgrimes{ 150992806Sobrien session_t *sp, *sprev, *snext; 15101558Srgrimes 15111558Srgrimes if (! sessions) 15121558Srgrimes return; 15131558Srgrimes 15141558Srgrimes if (! (sp = find_session(pid))) 15151558Srgrimes return; 15161558Srgrimes 15171558Srgrimes del_session(sp); 15181558Srgrimes sp->se_process = 0; 15191558Srgrimes 15201558Srgrimes if (sp->se_flags & SE_SHUTDOWN) { 152127837Sdavidn if ((sprev = sp->se_prev) != NULL) 15221558Srgrimes sprev->se_next = sp->se_next; 15231558Srgrimes else 15241558Srgrimes sessions = sp->se_next; 152527837Sdavidn if ((snext = sp->se_next) != NULL) 15261558Srgrimes snext->se_prev = sp->se_prev; 15271558Srgrimes free_session(sp); 15281558Srgrimes return; 15291558Srgrimes } 15301558Srgrimes 15311558Srgrimes if ((pid = start_getty(sp)) == -1) { 15321558Srgrimes /* serious trouble */ 15331558Srgrimes requested_transition = clean_ttys; 15341558Srgrimes return; 15351558Srgrimes } 15361558Srgrimes 15371558Srgrimes sp->se_process = pid; 15381558Srgrimes sp->se_started = time((time_t *) 0); 15391558Srgrimes add_session(sp); 15401558Srgrimes} 15411558Srgrimes 15421558Srgrimes/* 15431558Srgrimes * Catch a signal and request a state transition. 15441558Srgrimes */ 1545183391Sdelphijstatic void 154692838Simptransition_handler(int sig) 15471558Srgrimes{ 15481558Srgrimes 15491558Srgrimes switch (sig) { 15501558Srgrimes case SIGHUP: 1551217750Sjilles if (current_state == read_ttys || current_state == multi_user || 1552217750Sjilles current_state == clean_ttys || current_state == catatonia) 1553217750Sjilles requested_transition = clean_ttys; 15541558Srgrimes break; 155547962Sru case SIGUSR2: 155647962Sru howto = RB_POWEROFF; 155747962Sru case SIGUSR1: 155847962Sru howto |= RB_HALT; 15592323Snate case SIGINT: 15602327Sjkh Reboot = TRUE; 15611558Srgrimes case SIGTERM: 1562217750Sjilles if (current_state == read_ttys || current_state == multi_user || 1563217750Sjilles current_state == clean_ttys || current_state == catatonia) 1564217750Sjilles requested_transition = death; 1565217750Sjilles else 1566217750Sjilles requested_transition = death_single; 15671558Srgrimes break; 15681558Srgrimes case SIGTSTP: 1569217750Sjilles if (current_state == runcom || current_state == read_ttys || 1570217750Sjilles current_state == clean_ttys || 1571217750Sjilles current_state == multi_user || current_state == catatonia) 1572217750Sjilles requested_transition = catatonia; 15731558Srgrimes break; 1574293744Strasz case SIGEMT: 1575293744Strasz requested_transition = reroot; 1576293744Strasz break; 15771558Srgrimes default: 15781558Srgrimes requested_transition = 0; 15791558Srgrimes break; 15801558Srgrimes } 15811558Srgrimes} 15821558Srgrimes 15831558Srgrimes/* 15841558Srgrimes * Take the system multiuser. 15851558Srgrimes */ 1586183391Sdelphijstatic state_func_t 158792838Simpmulti_user(void) 15881558Srgrimes{ 15891558Srgrimes pid_t pid; 159092806Sobrien session_t *sp; 15911558Srgrimes 15921558Srgrimes requested_transition = 0; 15931558Srgrimes 15941558Srgrimes /* 15951558Srgrimes * If the administrator has not set the security level to -1 15961558Srgrimes * to indicate that the kernel should not run multiuser in secure 15978871Srgrimes * mode, and the run script has not set a higher level of security 15981558Srgrimes * than level 1, then put the kernel into secure mode. 15991558Srgrimes */ 16001558Srgrimes if (getsecuritylevel() == 0) 16011558Srgrimes setsecuritylevel(1); 16021558Srgrimes 16031558Srgrimes for (sp = sessions; sp; sp = sp->se_next) { 16041558Srgrimes if (sp->se_process) 16051558Srgrimes continue; 16061558Srgrimes if ((pid = start_getty(sp)) == -1) { 16071558Srgrimes /* serious trouble */ 16081558Srgrimes requested_transition = clean_ttys; 16091558Srgrimes break; 16101558Srgrimes } 16111558Srgrimes sp->se_process = pid; 16121558Srgrimes sp->se_started = time((time_t *) 0); 16131558Srgrimes add_session(sp); 16141558Srgrimes } 16151558Srgrimes 16161558Srgrimes while (!requested_transition) 16171558Srgrimes if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 16181558Srgrimes collect_child(pid); 16191558Srgrimes 16201558Srgrimes return (state_func_t) requested_transition; 16211558Srgrimes} 16221558Srgrimes 16231558Srgrimes/* 162457344Salfred * This is an (n*2)+(n^2) algorithm. We hope it isn't run often... 16251558Srgrimes */ 1626183391Sdelphijstatic state_func_t 162792838Simpclean_ttys(void) 16281558Srgrimes{ 162992806Sobrien session_t *sp, *sprev; 163092806Sobrien struct ttyent *typ; 163192806Sobrien int session_index = 0; 163292806Sobrien int devlen; 16333594Sache char *old_getty, *old_window, *old_type; 16341558Srgrimes 1635173787Sobrien /* 1636173787Sobrien * mark all sessions for death, (!SE_PRESENT) 163757344Salfred * as we find or create new ones they'll be marked as keepers, 163857344Salfred * we'll later nuke all the ones not found in /etc/ttys 163957344Salfred */ 164057344Salfred for (sp = sessions; sp != NULL; sp = sp->se_next) 164157344Salfred sp->se_flags &= ~SE_PRESENT; 164257344Salfred 16431558Srgrimes devlen = sizeof(_PATH_DEV) - 1; 164427837Sdavidn while ((typ = getttyent()) != NULL) { 16451558Srgrimes ++session_index; 16461558Srgrimes 16471558Srgrimes for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 16481558Srgrimes if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 16491558Srgrimes break; 16501558Srgrimes 16511558Srgrimes if (sp) { 165257344Salfred /* we want this one to live */ 165357344Salfred sp->se_flags |= SE_PRESENT; 16541558Srgrimes if (sp->se_index != session_index) { 16551558Srgrimes warning("port %s changed utmp index from %d to %d", 16561558Srgrimes sp->se_device, sp->se_index, 16571558Srgrimes session_index); 16581558Srgrimes sp->se_index = session_index; 16591558Srgrimes } 16601558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 16611558Srgrimes typ->ty_getty == 0) { 16621558Srgrimes sp->se_flags |= SE_SHUTDOWN; 16631558Srgrimes kill(sp->se_process, SIGHUP); 16641558Srgrimes continue; 16651558Srgrimes } 16661558Srgrimes sp->se_flags &= ~SE_SHUTDOWN; 16673594Sache old_getty = sp->se_getty ? strdup(sp->se_getty) : 0; 16683594Sache old_window = sp->se_window ? strdup(sp->se_window) : 0; 16693594Sache old_type = sp->se_type ? strdup(sp->se_type) : 0; 16701558Srgrimes if (setupargv(sp, typ) == 0) { 16711558Srgrimes warning("can't parse getty for port %s", 16721558Srgrimes sp->se_device); 16731558Srgrimes sp->se_flags |= SE_SHUTDOWN; 16741558Srgrimes kill(sp->se_process, SIGHUP); 16751558Srgrimes } 16763594Sache else if ( !old_getty 167727837Sdavidn || (!old_type && sp->se_type) 167827837Sdavidn || (old_type && !sp->se_type) 167927837Sdavidn || (!old_window && sp->se_window) 168027837Sdavidn || (old_window && !sp->se_window) 168127837Sdavidn || (strcmp(old_getty, sp->se_getty) != 0) 168227837Sdavidn || (old_window && strcmp(old_window, sp->se_window) != 0) 168327837Sdavidn || (old_type && strcmp(old_type, sp->se_type) != 0) 16843594Sache ) { 16853594Sache /* Don't set SE_SHUTDOWN here */ 16863594Sache sp->se_nspace = 0; 16873594Sache sp->se_started = 0; 16883594Sache kill(sp->se_process, SIGHUP); 16893594Sache } 16903594Sache if (old_getty) 16913594Sache free(old_getty); 169278484Smikeh if (old_window) 16933594Sache free(old_window); 16943594Sache if (old_type) 16953594Sache free(old_type); 16961558Srgrimes continue; 16971558Srgrimes } 16981558Srgrimes 16991558Srgrimes new_session(sprev, session_index, typ); 17001558Srgrimes } 17011558Srgrimes 17021558Srgrimes endttyent(); 17031558Srgrimes 170457344Salfred /* 170557344Salfred * sweep through and kill all deleted sessions 170657344Salfred * ones who's /etc/ttys line was deleted (SE_PRESENT unset) 170757344Salfred */ 170857344Salfred for (sp = sessions; sp != NULL; sp = sp->se_next) { 170957344Salfred if ((sp->se_flags & SE_PRESENT) == 0) { 171057344Salfred sp->se_flags |= SE_SHUTDOWN; 171157344Salfred kill(sp->se_process, SIGHUP); 171257344Salfred } 171357344Salfred } 171457344Salfred 17151558Srgrimes return (state_func_t) multi_user; 17161558Srgrimes} 17171558Srgrimes 17181558Srgrimes/* 17191558Srgrimes * Block further logins. 17201558Srgrimes */ 1721183391Sdelphijstatic state_func_t 172292838Simpcatatonia(void) 17231558Srgrimes{ 172492806Sobrien session_t *sp; 17251558Srgrimes 17261558Srgrimes for (sp = sessions; sp; sp = sp->se_next) 17271558Srgrimes sp->se_flags |= SE_SHUTDOWN; 17281558Srgrimes 17291558Srgrimes return (state_func_t) multi_user; 17301558Srgrimes} 17311558Srgrimes 17321558Srgrimes/* 17331558Srgrimes * Note SIGALRM. 17341558Srgrimes */ 1735183391Sdelphijstatic void 173692838Simpalrm_handler(int sig) 17371558Srgrimes{ 1738173785Sobrien 173927837Sdavidn (void)sig; 17401558Srgrimes clang = 1; 17411558Srgrimes} 17421558Srgrimes 17431558Srgrimes/* 17441558Srgrimes * Bring the system down to single user. 17451558Srgrimes */ 1746183391Sdelphijstatic state_func_t 174792838Simpdeath(void) 17481558Srgrimes{ 1749289032Scperciva int block, blocked; 1750289032Scperciva size_t len; 17511558Srgrimes 1752289032Scperciva /* Temporarily block suspend. */ 1753289032Scperciva len = sizeof(blocked); 1754289032Scperciva block = 1; 1755289032Scperciva if (sysctlbyname("kern.suspend_blocked", &blocked, &len, 1756289032Scperciva &block, sizeof(block)) == -1) 1757289032Scperciva blocked = 0; 1758289032Scperciva 1759194198Sed /* 1760194198Sed * Also revoke the TTY here. Because runshutdown() may reopen 1761194198Sed * the TTY whose getty we're killing here, there is no guarantee 1762194198Sed * runshutdown() will perform the initial open() call, causing 1763194198Sed * the terminal attributes to be misconfigured. 1764194198Sed */ 1765293744Strasz revoke_ttys(); 17661558Srgrimes 176727837Sdavidn /* Try to run the rc.shutdown script within a period of time */ 1768173785Sobrien runshutdown(); 1769173785Sobrien 1770289032Scperciva /* Unblock suspend if we blocked it. */ 1771289032Scperciva if (!blocked) 1772289032Scperciva sysctlbyname("kern.suspend_blocked", NULL, NULL, 1773289032Scperciva &blocked, sizeof(blocked)); 1774289032Scperciva 1775217750Sjilles return (state_func_t) death_single; 1776217750Sjilles} 1777217750Sjilles 1778217750Sjilles/* 1779217750Sjilles * Do what is necessary to reinitialize single user mode or reboot 1780217750Sjilles * from an incomplete state. 1781217750Sjilles */ 1782217750Sjillesstatic state_func_t 1783217750Sjillesdeath_single(void) 1784217750Sjilles{ 1785217750Sjilles int i; 1786217750Sjilles pid_t pid; 1787217750Sjilles static const int death_sigs[2] = { SIGTERM, SIGKILL }; 1788217750Sjilles 1789217750Sjilles revoke(_PATH_CONSOLE); 1790217750Sjilles 179127197Sache for (i = 0; i < 2; ++i) { 17921558Srgrimes if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 17931558Srgrimes return (state_func_t) single_user; 17941558Srgrimes 17951558Srgrimes clang = 0; 17961558Srgrimes alarm(DEATH_WATCH); 17971558Srgrimes do 17981558Srgrimes if ((pid = waitpid(-1, (int *)0, 0)) != -1) 17991558Srgrimes collect_child(pid); 18001558Srgrimes while (clang == 0 && errno != ECHILD); 18011558Srgrimes 18021558Srgrimes if (errno == ECHILD) 18031558Srgrimes return (state_func_t) single_user; 18041558Srgrimes } 18051558Srgrimes 18061558Srgrimes warning("some processes would not die; ps axl advised"); 18071558Srgrimes 18081558Srgrimes return (state_func_t) single_user; 18091558Srgrimes} 181027837Sdavidn 1811293744Straszstatic void 1812293744Straszrevoke_ttys(void) 1813293744Strasz{ 1814293744Strasz session_t *sp; 1815293744Strasz 1816293744Strasz for (sp = sessions; sp; sp = sp->se_next) { 1817293744Strasz sp->se_flags |= SE_SHUTDOWN; 1818293744Strasz kill(sp->se_process, SIGHUP); 1819293744Strasz revoke(sp->se_device); 1820293744Strasz } 1821293744Strasz} 1822293744Strasz 182327837Sdavidn/* 182427837Sdavidn * Run the system shutdown script. 182527837Sdavidn * 182627837Sdavidn * Exit codes: XXX I should document more 182727837Sdavidn * -2 shutdown script terminated abnormally 182827837Sdavidn * -1 fatal error - can't run script 182927837Sdavidn * 0 good. 183027837Sdavidn * >0 some error (exit code) 183127837Sdavidn */ 1832183391Sdelphijstatic int 183392838Simprunshutdown(void) 183427837Sdavidn{ 183527837Sdavidn pid_t pid, wpid; 183627837Sdavidn int status; 183727837Sdavidn int shutdowntimeout; 183827837Sdavidn size_t len; 183953550Sdillon char *argv[4]; 1840166484Simp const char *shell; 184127837Sdavidn struct sigaction sa; 184228344Sdavidn struct stat sb; 184327837Sdavidn 184428344Sdavidn /* 184528344Sdavidn * rc.shutdown is optional, so to prevent any unnecessary 184628344Sdavidn * complaints from the shell we simply don't run it if the 184728344Sdavidn * file does not exist. If the stat() here fails for other 184828344Sdavidn * reasons, we'll let the shell complain. 184928344Sdavidn */ 185028344Sdavidn if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT) 185128344Sdavidn return 0; 185228344Sdavidn 1853166484Simp shell = get_shell(); 1854166484Simp 185527837Sdavidn if ((pid = fork()) == 0) { 185627837Sdavidn sigemptyset(&sa.sa_mask); 185727837Sdavidn sa.sa_flags = 0; 185827837Sdavidn sa.sa_handler = SIG_IGN; 1859173785Sobrien sigaction(SIGTSTP, &sa, (struct sigaction *)0); 1860173785Sobrien sigaction(SIGHUP, &sa, (struct sigaction *)0); 186127837Sdavidn 1862232977Sed open_console(); 186327837Sdavidn 1864140070Sdelphij char _sh[] = "sh"; 1865140070Sdelphij char _reboot[] = "reboot"; 1866140070Sdelphij char _single[] = "single"; 1867140070Sdelphij char _path_rundown[] = _PATH_RUNDOWN; 1868140070Sdelphij 1869140070Sdelphij argv[0] = _sh; 1870140070Sdelphij argv[1] = _path_rundown; 1871140070Sdelphij argv[2] = Reboot ? _reboot : _single; 187253550Sdillon argv[3] = 0; 187327837Sdavidn 187427837Sdavidn sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 187527837Sdavidn 187627941Sache#ifdef LOGIN_CAP 187727941Sache setprocresources(RESOURCE_RC); 187827941Sache#endif 1879166484Simp execv(shell, argv); 1880166484Simp warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN); 188127837Sdavidn _exit(1); /* force single user mode */ 188227837Sdavidn } 188327837Sdavidn 188427837Sdavidn if (pid == -1) { 1885166484Simp emergency("can't fork for %s on %s: %m", shell, _PATH_RUNDOWN); 188627837Sdavidn while (waitpid(-1, (int *) 0, WNOHANG) > 0) 188727837Sdavidn continue; 188827837Sdavidn sleep(STALL_TIMEOUT); 188927837Sdavidn return -1; 189027837Sdavidn } 189127837Sdavidn 189227837Sdavidn len = sizeof(shutdowntimeout); 1893173785Sobrien if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len, 1894173785Sobrien NULL, 0) == -1 || shutdowntimeout < 2) 1895173785Sobrien shutdowntimeout = DEATH_SCRIPT; 189627837Sdavidn alarm(shutdowntimeout); 189727837Sdavidn clang = 0; 189827837Sdavidn /* 189927837Sdavidn * Copied from single_user(). This is a bit paranoid. 190027837Sdavidn * Use the same ALRM handler. 190127837Sdavidn */ 190227837Sdavidn do { 190327837Sdavidn if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 190427837Sdavidn collect_child(wpid); 190527837Sdavidn if (clang == 1) { 190627837Sdavidn /* we were waiting for the sub-shell */ 190727837Sdavidn kill(wpid, SIGTERM); 1908166484Simp warning("timeout expired for %s on %s: %m; going to " 1909166484Simp "single user mode", shell, _PATH_RUNDOWN); 191027837Sdavidn return -1; 191127837Sdavidn } 191227837Sdavidn if (wpid == -1) { 191327837Sdavidn if (errno == EINTR) 191427837Sdavidn continue; 1915166484Simp warning("wait for %s on %s failed: %m; going to " 1916166484Simp "single user mode", shell, _PATH_RUNDOWN); 191727837Sdavidn return -1; 191827837Sdavidn } 191927837Sdavidn if (wpid == pid && WIFSTOPPED(status)) { 192027837Sdavidn warning("init: %s on %s stopped, restarting\n", 1921166484Simp shell, _PATH_RUNDOWN); 192227837Sdavidn kill(pid, SIGCONT); 192327837Sdavidn wpid = -1; 192427837Sdavidn } 192527837Sdavidn } while (wpid != pid && !clang); 192627837Sdavidn 192727837Sdavidn /* Turn off the alarm */ 192827837Sdavidn alarm(0); 192927837Sdavidn 193027837Sdavidn if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 193127837Sdavidn requested_transition == catatonia) { 193227837Sdavidn /* 193327837Sdavidn * /etc/rc.shutdown executed /sbin/reboot; 193427837Sdavidn * wait for the end quietly 193527837Sdavidn */ 193627837Sdavidn sigset_t s; 193727837Sdavidn 193827837Sdavidn sigfillset(&s); 193927837Sdavidn for (;;) 194027837Sdavidn sigsuspend(&s); 194127837Sdavidn } 194227837Sdavidn 194327837Sdavidn if (!WIFEXITED(status)) { 1944166484Simp warning("%s on %s terminated abnormally, going to " 1945166484Simp "single user mode", shell, _PATH_RUNDOWN); 194627837Sdavidn return -2; 194727837Sdavidn } 194827837Sdavidn 194927837Sdavidn if ((status = WEXITSTATUS(status)) != 0) 195027837Sdavidn warning("%s returned status %d", _PATH_RUNDOWN, status); 195127837Sdavidn 195227837Sdavidn return status; 195327837Sdavidn} 195427837Sdavidn 1955140070Sdelphijstatic char * 1956140070Sdelphijstrk(char *p) 19575478Sache{ 1958173785Sobrien static char *t; 1959173785Sobrien char *q; 1960173785Sobrien int c; 19615478Sache 1962173785Sobrien if (p) 1963173785Sobrien t = p; 1964173785Sobrien if (!t) 1965173785Sobrien return 0; 19665478Sache 1967173785Sobrien c = *t; 1968173785Sobrien while (c == ' ' || c == '\t' ) 1969173785Sobrien c = *++t; 1970173785Sobrien if (!c) { 1971173785Sobrien t = 0; 1972173785Sobrien return 0; 1973173785Sobrien } 19745478Sache q = t; 1975173785Sobrien if (c == '\'') { 1976173785Sobrien c = *++t; 1977173785Sobrien q = t; 1978173785Sobrien while (c && c != '\'') 1979173785Sobrien c = *++t; 1980173785Sobrien if (!c) /* unterminated string */ 1981173785Sobrien q = t = 0; 1982173785Sobrien else 1983173785Sobrien *t++ = 0; 1984173785Sobrien } else { 1985173785Sobrien while (c && c != ' ' && c != '\t' ) 1986173785Sobrien c = *++t; 1987173785Sobrien *t++ = 0; 1988173785Sobrien if (!c) 1989173785Sobrien t = 0; 1990173785Sobrien } 1991173785Sobrien return q; 19925478Sache} 199321865Sdavidn 199421865Sdavidn#ifdef LOGIN_CAP 1995183391Sdelphijstatic void 199692838Simpsetprocresources(const char *cname) 199721865Sdavidn{ 199821941Sdavidn login_cap_t *lc; 199927176Sache if ((lc = login_getclassbyname(cname, NULL)) != NULL) { 2000173785Sobrien setusercontext(lc, (struct passwd*)NULL, 0, 2001254288Sjilles LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | 2002254288Sjilles LOGIN_SETLOGINCLASS | LOGIN_SETCPUMASK); 200321941Sdavidn login_close(lc); 200421941Sdavidn } 200521865Sdavidn} 200621865Sdavidn#endif 2007