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$"; 451558Srgrimes#endif /* not lint */ 461558Srgrimes 471558Srgrimes#include <sys/param.h> 4827837Sdavidn#include <sys/ioctl.h> 4919227Sphk#include <sys/mount.h> 501558Srgrimes#include <sys/sysctl.h> 511558Srgrimes#include <sys/wait.h> 5228344Sdavidn#include <sys/stat.h> 53101271Smux#include <sys/uio.h> 541558Srgrimes 551558Srgrimes#include <db.h> 561558Srgrimes#include <errno.h> 571558Srgrimes#include <fcntl.h> 58166484Simp#include <kenv.h> 5927186Sache#include <libutil.h> 6069793Sobrien#include <paths.h> 611558Srgrimes#include <signal.h> 621558Srgrimes#include <stdio.h> 631558Srgrimes#include <stdlib.h> 641558Srgrimes#include <string.h> 651558Srgrimes#include <syslog.h> 661558Srgrimes#include <time.h> 671558Srgrimes#include <ttyent.h> 681558Srgrimes#include <unistd.h> 692323Snate#include <sys/reboot.h> 7026594Scharnier#include <err.h> 711558Srgrimes 721558Srgrimes#include <stdarg.h> 731558Srgrimes 741558Srgrimes#ifdef SECURE 751558Srgrimes#include <pwd.h> 761558Srgrimes#endif 771558Srgrimes 7821865Sdavidn#ifdef LOGIN_CAP 7921865Sdavidn#include <login_cap.h> 8021865Sdavidn#endif 8121865Sdavidn 821558Srgrimes#include "pathnames.h" 831558Srgrimes 841558Srgrimes/* 851558Srgrimes * Sleep times; used to prevent thrashing. 861558Srgrimes */ 871558Srgrimes#define GETTY_SPACING 5 /* N secs minimum getty spacing */ 881558Srgrimes#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ 89232841Sed#define GETTY_NSPACE 3 /* max. spacing count to bring reaction */ 901558Srgrimes#define WINDOW_WAIT 3 /* wait N secs after starting window */ 911558Srgrimes#define STALL_TIMEOUT 30 /* wait N secs after warning */ 921558Srgrimes#define DEATH_WATCH 10 /* wait N secs for procs to die */ 93173785Sobrien#define DEATH_SCRIPT 120 /* wait for 2min for /etc/rc.shutdown */ 94173785Sobrien#define RESOURCE_RC "daemon" 95232841Sed#define RESOURCE_WINDOW "default" 96173785Sobrien#define RESOURCE_GETTY "default" 971558Srgrimes 98183391Sdelphijstatic void handle(sig_t, ...); 99183391Sdelphijstatic void delset(sigset_t *, ...); 1001558Srgrimes 101183391Sdelphijstatic void stall(const char *, ...) __printflike(1, 2); 102183391Sdelphijstatic void warning(const char *, ...) __printflike(1, 2); 103183391Sdelphijstatic void emergency(const char *, ...) __printflike(1, 2); 104183391Sdelphijstatic void disaster(int); 105183391Sdelphijstatic void badsys(int); 106183391Sdelphijstatic int runshutdown(void); 107140070Sdelphijstatic char *strk(char *); 1081558Srgrimes 1091558Srgrimes/* 1101558Srgrimes * We really need a recursive typedef... 1111558Srgrimes * The following at least guarantees that the return type of (*state_t)() 1121558Srgrimes * is sufficiently wide to hold a function pointer. 1131558Srgrimes */ 11492838Simptypedef long (*state_func_t)(void); 11592838Simptypedef state_func_t (*state_t)(void); 1161558Srgrimes 117183391Sdelphijstatic state_func_t single_user(void); 118183391Sdelphijstatic state_func_t runcom(void); 119183391Sdelphijstatic state_func_t read_ttys(void); 120183391Sdelphijstatic state_func_t multi_user(void); 121183391Sdelphijstatic state_func_t clean_ttys(void); 122183391Sdelphijstatic state_func_t catatonia(void); 123183391Sdelphijstatic state_func_t death(void); 124217750Sjillesstatic state_func_t death_single(void); 1251558Srgrimes 126183391Sdelphijstatic state_func_t run_script(const char *); 127166484Simp 128227081Sedstatic enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 12911910Sphk#define FALSE 0 13011910Sphk#define TRUE 1 13111910Sphk 132227081Sedstatic int Reboot = FALSE; 133227081Sedstatic int howto = RB_AUTOBOOT; 1341558Srgrimes 135227081Sedstatic int devfs; 13619227Sphk 137183391Sdelphijstatic void transition(state_t); 138183391Sdelphijstatic state_t requested_transition; 139217750Sjillesstatic state_t current_state = death_single; 1401558Srgrimes 141232977Sedstatic void open_console(void); 142183391Sdelphijstatic const char *get_shell(void); 143183391Sdelphijstatic void write_stderr(const char *message); 1441558Srgrimes 1451558Srgrimestypedef struct init_session { 1461558Srgrimes int se_index; /* index of entry in ttys file */ 1471558Srgrimes pid_t se_process; /* controlling process */ 1481558Srgrimes time_t se_started; /* used to avoid thrashing */ 1491558Srgrimes int se_flags; /* status of session */ 1501558Srgrimes#define SE_SHUTDOWN 0x1 /* session won't be restarted */ 15157344Salfred#define SE_PRESENT 0x2 /* session is in /etc/ttys */ 152232841Sed int se_nspace; /* spacing count */ 1531558Srgrimes char *se_device; /* filename of port */ 1541558Srgrimes char *se_getty; /* what to run on that port */ 155232841Sed char *se_getty_argv_space; /* pre-parsed argument array space */ 1561558Srgrimes char **se_getty_argv; /* pre-parsed argument array */ 1571558Srgrimes char *se_window; /* window system (started only once) */ 158232841Sed char *se_window_argv_space; /* pre-parsed argument array space */ 1591558Srgrimes char **se_window_argv; /* pre-parsed argument array */ 160232841Sed char *se_type; /* default terminal type */ 1611558Srgrimes struct init_session *se_prev; 1621558Srgrimes struct init_session *se_next; 1631558Srgrimes} session_t; 1641558Srgrimes 165183391Sdelphijstatic void free_session(session_t *); 166183391Sdelphijstatic session_t *new_session(session_t *, int, struct ttyent *); 167183391Sdelphijstatic session_t *sessions; 1681558Srgrimes 169183391Sdelphijstatic char **construct_argv(char *); 170183391Sdelphijstatic void start_window_system(session_t *); 171183391Sdelphijstatic void collect_child(pid_t); 172183391Sdelphijstatic pid_t start_getty(session_t *); 173183391Sdelphijstatic void transition_handler(int); 174183391Sdelphijstatic void alrm_handler(int); 175183391Sdelphijstatic void setsecuritylevel(int); 176183391Sdelphijstatic int getsecuritylevel(void); 177183391Sdelphijstatic int setupargv(session_t *, struct ttyent *); 17821941Sdavidn#ifdef LOGIN_CAP 179183391Sdelphijstatic void setprocresources(const char *); 18021941Sdavidn#endif 181183391Sdelphijstatic int clang; 1821558Srgrimes 183183391Sdelphijstatic int start_session_db(void); 184183391Sdelphijstatic void add_session(session_t *); 185183391Sdelphijstatic void del_session(session_t *); 186183391Sdelphijstatic session_t *find_session(pid_t); 187183391Sdelphijstatic DB *session_db; 1881558Srgrimes 1891558Srgrimes/* 1901558Srgrimes * The mother of all processes. 1911558Srgrimes */ 1921558Srgrimesint 19392838Simpmain(int argc, char *argv[]) 1941558Srgrimes{ 195166484Simp state_t initial_transition = runcom; 196166484Simp char kenv_value[PATH_MAX]; 1971558Srgrimes int c; 1981558Srgrimes struct sigaction sa; 1991558Srgrimes sigset_t mask; 2001558Srgrimes 2011558Srgrimes /* Dispose of random users. */ 20226594Scharnier if (getuid() != 0) 20326594Scharnier errx(1, "%s", strerror(EPERM)); 2041558Srgrimes 2051558Srgrimes /* System V users like to reexec init. */ 20647998Sru if (getpid() != 1) { 20747998Sru#ifdef COMPAT_SYSV_INIT 20847998Sru /* So give them what they want */ 20947998Sru if (argc > 1) { 21047998Sru if (strlen(argv[1]) == 1) { 21192806Sobrien char runlevel = *argv[1]; 21292806Sobrien int sig; 2131558Srgrimes 21447998Sru switch (runlevel) { 215173785Sobrien case '0': /* halt + poweroff */ 216173785Sobrien sig = SIGUSR2; 217173785Sobrien break; 218173785Sobrien case '1': /* single-user */ 219173785Sobrien sig = SIGTERM; 220173785Sobrien break; 221173785Sobrien case '6': /* reboot */ 222173785Sobrien sig = SIGINT; 223173785Sobrien break; 224173785Sobrien case 'c': /* block further logins */ 225173785Sobrien sig = SIGTSTP; 226173785Sobrien break; 227173785Sobrien case 'q': /* rescan /etc/ttys */ 228173785Sobrien sig = SIGHUP; 229173785Sobrien break; 230173785Sobrien default: 231173785Sobrien goto invalid; 23247998Sru } 23347998Sru kill(1, sig); 23447998Sru _exit(0); 23547998Sru } else 23647998Sruinvalid: 23747998Sru errx(1, "invalid run-level ``%s''", argv[1]); 23847998Sru } else 23947998Sru#endif 24047998Sru errx(1, "already running"); 24147998Sru } 2421558Srgrimes /* 2431558Srgrimes * Note that this does NOT open a file... 2441558Srgrimes * Does 'init' deserve its own facility number? 2451558Srgrimes */ 2461558Srgrimes openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 2471558Srgrimes 2481558Srgrimes /* 2491558Srgrimes * Create an initial session. 2501558Srgrimes */ 2511558Srgrimes if (setsid() < 0) 2521558Srgrimes warning("initial setsid() failed: %m"); 2531558Srgrimes 2541558Srgrimes /* 2551558Srgrimes * Establish an initial user so that programs running 2561558Srgrimes * single user do not freak out and die (like passwd). 2571558Srgrimes */ 2581558Srgrimes if (setlogin("root") < 0) 2591558Srgrimes warning("setlogin() failed: %m"); 2601558Srgrimes 2611558Srgrimes /* 2621558Srgrimes * This code assumes that we always get arguments through flags, 2631558Srgrimes * never through bits set in some random machine register. 2641558Srgrimes */ 26519227Sphk while ((c = getopt(argc, argv, "dsf")) != -1) 2661558Srgrimes switch (c) { 26719227Sphk case 'd': 26819227Sphk devfs = 1; 26919227Sphk break; 2701558Srgrimes case 's': 271166484Simp initial_transition = single_user; 2721558Srgrimes break; 2731558Srgrimes case 'f': 2741558Srgrimes runcom_mode = FASTBOOT; 2751558Srgrimes break; 2761558Srgrimes default: 2771558Srgrimes warning("unrecognized flag '-%c'", c); 2781558Srgrimes break; 2791558Srgrimes } 2801558Srgrimes 2811558Srgrimes if (optind != argc) 2821558Srgrimes warning("ignoring excess arguments"); 2831558Srgrimes 284166484Simp /* 285166484Simp * We catch or block signals rather than ignore them, 286166484Simp * so that they get reset on exec. 287166484Simp */ 288166484Simp handle(badsys, SIGSYS, 0); 289173785Sobrien handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU, 290173785Sobrien SIGXFSZ, 0); 291173785Sobrien handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGUSR1, 292173785Sobrien SIGUSR2, 0); 293166484Simp handle(alrm_handler, SIGALRM, 0); 294166484Simp sigfillset(&mask); 295166484Simp delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 296173785Sobrien SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM, 297173785Sobrien SIGUSR1, SIGUSR2, 0); 298166484Simp sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 299166484Simp sigemptyset(&sa.sa_mask); 300166484Simp sa.sa_flags = 0; 301166484Simp sa.sa_handler = SIG_IGN; 302173785Sobrien sigaction(SIGTTIN, &sa, (struct sigaction *)0); 303173785Sobrien sigaction(SIGTTOU, &sa, (struct sigaction *)0); 304166484Simp 305166484Simp /* 306166484Simp * Paranoia. 307166484Simp */ 308166484Simp close(0); 309166484Simp close(1); 310166484Simp close(2); 311166484Simp 312166484Simp if (kenv(KENV_GET, "init_script", kenv_value, sizeof(kenv_value)) > 0) { 313166484Simp state_func_t next_transition; 314166484Simp 315166484Simp if ((next_transition = run_script(kenv_value)) != 0) 316166484Simp initial_transition = (state_t) next_transition; 317166484Simp } 318166484Simp 319166484Simp if (kenv(KENV_GET, "init_chroot", kenv_value, sizeof(kenv_value)) > 0) { 320166484Simp if (chdir(kenv_value) != 0 || chroot(".") != 0) 321166484Simp warning("Can't chroot to %s: %m", kenv_value); 322166484Simp } 323166484Simp 324166484Simp /* 325166484Simp * Additional check if devfs needs to be mounted: 326166484Simp * If "/" and "/dev" have the same device number, 327166484Simp * then it hasn't been mounted yet. 328166484Simp */ 329166484Simp if (!devfs) { 330166484Simp struct stat stst; 331166484Simp dev_t root_devno; 332166484Simp 333166484Simp stat("/", &stst); 334166484Simp root_devno = stst.st_dev; 335166484Simp if (stat("/dev", &stst) != 0) 336166484Simp warning("Can't stat /dev: %m"); 337166484Simp else if (stst.st_dev == root_devno) 338166484Simp devfs++; 339166484Simp } 340166484Simp 34119227Sphk if (devfs) { 342101271Smux struct iovec iov[4]; 34372188Sphk char *s; 34472188Sphk int i; 34572188Sphk 346140070Sdelphij char _fstype[] = "fstype"; 347140070Sdelphij char _devfs[] = "devfs"; 348140070Sdelphij char _fspath[] = "fspath"; 349140070Sdelphij char _path_dev[]= _PATH_DEV; 350140070Sdelphij 351140070Sdelphij iov[0].iov_base = _fstype; 352140070Sdelphij iov[0].iov_len = sizeof(_fstype); 353140070Sdelphij iov[1].iov_base = _devfs; 354140070Sdelphij iov[1].iov_len = sizeof(_devfs); 355140070Sdelphij iov[2].iov_base = _fspath; 356140070Sdelphij iov[2].iov_len = sizeof(_fspath); 357173787Sobrien /* 35872188Sphk * Try to avoid the trailing slash in _PATH_DEV. 35972188Sphk * Be *very* defensive. 36072188Sphk */ 36172188Sphk s = strdup(_PATH_DEV); 36272188Sphk if (s != NULL) { 36372188Sphk i = strlen(s); 36472188Sphk if (i > 0 && s[i - 1] == '/') 36572188Sphk s[i - 1] = '\0'; 366101271Smux iov[3].iov_base = s; 367101271Smux iov[3].iov_len = strlen(s) + 1; 36872188Sphk } else { 369140070Sdelphij iov[3].iov_base = _path_dev; 370140070Sdelphij iov[3].iov_len = sizeof(_path_dev); 37172188Sphk } 372101271Smux nmount(iov, 4, 0); 373101271Smux if (s != NULL) 374101271Smux free(s); 37519227Sphk } 37619227Sphk 3771558Srgrimes /* 3781558Srgrimes * Start the state machine. 3791558Srgrimes */ 380166484Simp transition(initial_transition); 3811558Srgrimes 3821558Srgrimes /* 3831558Srgrimes * Should never reach here. 3841558Srgrimes */ 3851558Srgrimes return 1; 3861558Srgrimes} 3871558Srgrimes 3881558Srgrimes/* 3891558Srgrimes * Associate a function with a signal handler. 3901558Srgrimes */ 391183391Sdelphijstatic void 3921558Srgrimeshandle(sig_t handler, ...) 3931558Srgrimes{ 3941558Srgrimes int sig; 3951558Srgrimes struct sigaction sa; 39634001Sjraynard sigset_t mask_everything; 3971558Srgrimes va_list ap; 3981558Srgrimes va_start(ap, handler); 3991558Srgrimes 4001558Srgrimes sa.sa_handler = handler; 4011558Srgrimes sigfillset(&mask_everything); 4021558Srgrimes 403126836Sbde while ((sig = va_arg(ap, int)) != 0) { 4041558Srgrimes sa.sa_mask = mask_everything; 4051558Srgrimes /* XXX SA_RESTART? */ 4061558Srgrimes sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 4071558Srgrimes sigaction(sig, &sa, (struct sigaction *) 0); 4081558Srgrimes } 4091558Srgrimes va_end(ap); 4101558Srgrimes} 4111558Srgrimes 4121558Srgrimes/* 4131558Srgrimes * Delete a set of signals from a mask. 4141558Srgrimes */ 415183391Sdelphijstatic void 4161558Srgrimesdelset(sigset_t *maskp, ...) 4171558Srgrimes{ 4181558Srgrimes int sig; 4191558Srgrimes va_list ap; 4201558Srgrimes va_start(ap, maskp); 4211558Srgrimes 422126836Sbde while ((sig = va_arg(ap, int)) != 0) 4231558Srgrimes sigdelset(maskp, sig); 4241558Srgrimes va_end(ap); 4251558Srgrimes} 4261558Srgrimes 4271558Srgrimes/* 4281558Srgrimes * Log a message and sleep for a while (to give someone an opportunity 4291558Srgrimes * to read it and to save log or hardcopy output if the problem is chronic). 4301558Srgrimes * NB: should send a message to the session logger to avoid blocking. 4311558Srgrimes */ 432183391Sdelphijstatic void 43381911Skrisstall(const char *message, ...) 4341558Srgrimes{ 4351558Srgrimes va_list ap; 4361558Srgrimes va_start(ap, message); 4371558Srgrimes 4381558Srgrimes vsyslog(LOG_ALERT, message, ap); 4391558Srgrimes va_end(ap); 4401558Srgrimes sleep(STALL_TIMEOUT); 4411558Srgrimes} 4421558Srgrimes 4431558Srgrimes/* 4441558Srgrimes * Like stall(), but doesn't sleep. 4451558Srgrimes * If cpp had variadic macros, the two functions could be #defines for another. 4461558Srgrimes * NB: should send a message to the session logger to avoid blocking. 4471558Srgrimes */ 448183391Sdelphijstatic void 44981911Skriswarning(const char *message, ...) 4501558Srgrimes{ 4511558Srgrimes va_list ap; 4521558Srgrimes va_start(ap, message); 4531558Srgrimes 4541558Srgrimes vsyslog(LOG_ALERT, message, ap); 4551558Srgrimes va_end(ap); 4561558Srgrimes} 4571558Srgrimes 4581558Srgrimes/* 4591558Srgrimes * Log an emergency message. 4601558Srgrimes * NB: should send a message to the session logger to avoid blocking. 4611558Srgrimes */ 462183391Sdelphijstatic void 46381911Skrisemergency(const char *message, ...) 4641558Srgrimes{ 4651558Srgrimes va_list ap; 4661558Srgrimes va_start(ap, message); 4671558Srgrimes 4681558Srgrimes vsyslog(LOG_EMERG, message, ap); 4691558Srgrimes va_end(ap); 4701558Srgrimes} 4711558Srgrimes 4721558Srgrimes/* 4731558Srgrimes * Catch a SIGSYS signal. 4741558Srgrimes * 4751558Srgrimes * These may arise if a system does not support sysctl. 4761558Srgrimes * We tolerate up to 25 of these, then throw in the towel. 4771558Srgrimes */ 478183391Sdelphijstatic void 47992838Simpbadsys(int sig) 4801558Srgrimes{ 4811558Srgrimes static int badcount = 0; 4821558Srgrimes 4831558Srgrimes if (badcount++ < 25) 4841558Srgrimes return; 4851558Srgrimes disaster(sig); 4861558Srgrimes} 4871558Srgrimes 4881558Srgrimes/* 4891558Srgrimes * Catch an unexpected signal. 4901558Srgrimes */ 491183391Sdelphijstatic void 49292838Simpdisaster(int sig) 4931558Srgrimes{ 494173785Sobrien 4951558Srgrimes emergency("fatal signal: %s", 496173785Sobrien (unsigned)sig < NSIG ? sys_siglist[sig] : "unknown signal"); 4971558Srgrimes 4981558Srgrimes sleep(STALL_TIMEOUT); 4991558Srgrimes _exit(sig); /* reboot */ 5001558Srgrimes} 5011558Srgrimes 5021558Srgrimes/* 5031558Srgrimes * Get the security level of the kernel. 5041558Srgrimes */ 505183391Sdelphijstatic int 50692838Simpgetsecuritylevel(void) 5071558Srgrimes{ 5081558Srgrimes#ifdef KERN_SECURELVL 5091558Srgrimes int name[2], curlevel; 5101558Srgrimes size_t len; 5111558Srgrimes 5121558Srgrimes name[0] = CTL_KERN; 5131558Srgrimes name[1] = KERN_SECURELVL; 5141558Srgrimes len = sizeof curlevel; 5151558Srgrimes if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 5161558Srgrimes emergency("cannot get kernel security level: %s", 5171558Srgrimes strerror(errno)); 5181558Srgrimes return (-1); 5191558Srgrimes } 5201558Srgrimes return (curlevel); 5211558Srgrimes#else 5221558Srgrimes return (-1); 5231558Srgrimes#endif 5241558Srgrimes} 5251558Srgrimes 5261558Srgrimes/* 5271558Srgrimes * Set the security level of the kernel. 5281558Srgrimes */ 529183391Sdelphijstatic void 53092838Simpsetsecuritylevel(int newlevel) 5311558Srgrimes{ 5321558Srgrimes#ifdef KERN_SECURELVL 5331558Srgrimes int name[2], curlevel; 5341558Srgrimes 5351558Srgrimes curlevel = getsecuritylevel(); 5361558Srgrimes if (newlevel == curlevel) 5371558Srgrimes return; 5381558Srgrimes name[0] = CTL_KERN; 5391558Srgrimes name[1] = KERN_SECURELVL; 5401558Srgrimes if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 5411558Srgrimes emergency( 5421558Srgrimes "cannot change kernel security level from %d to %d: %s", 5431558Srgrimes curlevel, newlevel, strerror(errno)); 5441558Srgrimes return; 5451558Srgrimes } 5461558Srgrimes#ifdef SECURE 5471558Srgrimes warning("kernel security level changed from %d to %d", 5481558Srgrimes curlevel, newlevel); 5491558Srgrimes#endif 5501558Srgrimes#endif 5511558Srgrimes} 5521558Srgrimes 5531558Srgrimes/* 5541558Srgrimes * Change states in the finite state machine. 5551558Srgrimes * The initial state is passed as an argument. 5561558Srgrimes */ 557183391Sdelphijstatic void 55892838Simptransition(state_t s) 5591558Srgrimes{ 560173785Sobrien 561217750Sjilles current_state = s; 5621558Srgrimes for (;;) 563217750Sjilles current_state = (state_t) (*current_state)(); 5641558Srgrimes} 5651558Srgrimes 5661558Srgrimes/* 5671558Srgrimes * Start a session and allocate a controlling terminal. 5681558Srgrimes * Only called by children of init after forking. 5691558Srgrimes */ 570183391Sdelphijstatic void 571232977Sedopen_console(void) 5721558Srgrimes{ 5731558Srgrimes int fd; 5741558Srgrimes 575233945Sed /* 576233945Sed * Try to open /dev/console. Open the device with O_NONBLOCK to 577233945Sed * prevent potential blocking on a carrier. 578233945Sed */ 579232977Sed revoke(_PATH_CONSOLE); 580232977Sed if ((fd = open(_PATH_CONSOLE, O_RDWR | O_NONBLOCK)) != -1) { 581233945Sed (void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); 582232977Sed if (login_tty(fd) == 0) 583232977Sed return; 584232977Sed close(fd); 5851558Srgrimes } 586232977Sed 587232977Sed /* No luck. Log output to file if possible. */ 588232977Sed if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 589232977Sed stall("cannot open null device."); 5901558Srgrimes _exit(1); 5911558Srgrimes } 592232977Sed if (fd != STDIN_FILENO) { 593232977Sed dup2(fd, STDIN_FILENO); 594232977Sed close(fd); 595232977Sed } 596232977Sed fd = open(_PATH_INITLOG, O_WRONLY | O_APPEND | O_CREAT, 0644); 597232977Sed if (fd == -1) 598232977Sed dup2(STDIN_FILENO, STDOUT_FILENO); 599232977Sed else if (fd != STDOUT_FILENO) { 600232977Sed dup2(fd, STDOUT_FILENO); 601232977Sed close(fd); 602232977Sed } 603232977Sed dup2(STDOUT_FILENO, STDERR_FILENO); 6041558Srgrimes} 6051558Srgrimes 606183391Sdelphijstatic const char * 607166484Simpget_shell(void) 608166484Simp{ 609166484Simp static char kenv_value[PATH_MAX]; 610166484Simp 611166484Simp if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0) 612166484Simp return kenv_value; 613166484Simp else 614166484Simp return _PATH_BSHELL; 615166484Simp} 616166484Simp 617183391Sdelphijstatic void 618166484Simpwrite_stderr(const char *message) 619166484Simp{ 620173785Sobrien 621166484Simp write(STDERR_FILENO, message, strlen(message)); 622166484Simp} 623166484Simp 6241558Srgrimes/* 6251558Srgrimes * Bring the system up single user. 6261558Srgrimes */ 627183391Sdelphijstatic state_func_t 62892838Simpsingle_user(void) 6291558Srgrimes{ 6301558Srgrimes pid_t pid, wpid; 6311558Srgrimes int status; 6321558Srgrimes sigset_t mask; 633166484Simp const char *shell; 6341558Srgrimes char *argv[2]; 6351558Srgrimes#ifdef SECURE 6361558Srgrimes struct ttyent *typ; 6371558Srgrimes struct passwd *pp; 6381558Srgrimes static const char banner[] = 6391558Srgrimes "Enter root password, or ^D to go multi-user\n"; 6401558Srgrimes char *clear, *password; 6411558Srgrimes#endif 64237814Sphk#ifdef DEBUGSHELL 64337814Sphk char altshell[128]; 64437814Sphk#endif 6451558Srgrimes 6462327Sjkh if (Reboot) { 64747962Sru /* Instead of going single user, let's reboot the machine */ 6482323Snate sync(); 64947962Sru reboot(howto); 6502323Snate _exit(0); 6512323Snate } 6522323Snate 653166484Simp shell = get_shell(); 654166484Simp 6551558Srgrimes if ((pid = fork()) == 0) { 6561558Srgrimes /* 6571558Srgrimes * Start the single user session. 6581558Srgrimes */ 659232977Sed open_console(); 6601558Srgrimes 6611558Srgrimes#ifdef SECURE 6621558Srgrimes /* 6631558Srgrimes * Check the root password. 6641558Srgrimes * We don't care if the console is 'on' by default; 6651558Srgrimes * it's the only tty that can be 'off' and 'secure'. 6661558Srgrimes */ 6671558Srgrimes typ = getttynam("console"); 6681558Srgrimes pp = getpwnam("root"); 66953550Sdillon if (typ && (typ->ty_status & TTY_SECURE) == 0 && 67053550Sdillon pp && *pp->pw_passwd) { 671166484Simp write_stderr(banner); 6721558Srgrimes for (;;) { 6731558Srgrimes clear = getpass("Password:"); 6741558Srgrimes if (clear == 0 || *clear == '\0') 6751558Srgrimes _exit(0); 6761558Srgrimes password = crypt(clear, pp->pw_passwd); 6771558Srgrimes bzero(clear, _PASSWORD_LEN); 678232841Sed if (password == NULL || 679231994Skevlo strcmp(password, pp->pw_passwd) == 0) 6801558Srgrimes break; 6811558Srgrimes warning("single-user login failed\n"); 6821558Srgrimes } 6831558Srgrimes } 6841558Srgrimes endttyent(); 6851558Srgrimes endpwent(); 6861558Srgrimes#endif /* SECURE */ 6871558Srgrimes 6881558Srgrimes#ifdef DEBUGSHELL 6891558Srgrimes { 69037814Sphk char *cp = altshell; 6911558Srgrimes int num; 6921558Srgrimes 693166484Simp#define SHREQUEST "Enter full pathname of shell or RETURN for " 694166484Simp write_stderr(SHREQUEST); 695166484Simp write_stderr(shell); 696166484Simp write_stderr(": "); 6971558Srgrimes while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 6981558Srgrimes num != 0 && *cp != '\n' && cp < &altshell[127]) 699173785Sobrien cp++; 7001558Srgrimes *cp = '\0'; 7011558Srgrimes if (altshell[0] != '\0') 7021558Srgrimes shell = altshell; 7031558Srgrimes } 7041558Srgrimes#endif /* DEBUGSHELL */ 7051558Srgrimes 7061558Srgrimes /* 7071558Srgrimes * Unblock signals. 7081558Srgrimes * We catch all the interesting ones, 7091558Srgrimes * and those are reset to SIG_DFL on exec. 7101558Srgrimes */ 7111558Srgrimes sigemptyset(&mask); 7121558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 7131558Srgrimes 7141558Srgrimes /* 7151558Srgrimes * Fire off a shell. 7161558Srgrimes * If the default one doesn't work, try the Bourne shell. 7171558Srgrimes */ 718140070Sdelphij 719140070Sdelphij char name[] = "-sh"; 720140070Sdelphij 721140070Sdelphij argv[0] = name; 7221558Srgrimes argv[1] = 0; 7231558Srgrimes execv(shell, argv); 7241558Srgrimes emergency("can't exec %s for single user: %m", shell); 7251558Srgrimes execv(_PATH_BSHELL, argv); 7261558Srgrimes emergency("can't exec %s for single user: %m", _PATH_BSHELL); 7271558Srgrimes sleep(STALL_TIMEOUT); 7281558Srgrimes _exit(1); 7291558Srgrimes } 7301558Srgrimes 7311558Srgrimes if (pid == -1) { 7321558Srgrimes /* 7331558Srgrimes * We are seriously hosed. Do our best. 7341558Srgrimes */ 7351558Srgrimes emergency("can't fork single-user shell, trying again"); 7361558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 7371558Srgrimes continue; 7381558Srgrimes return (state_func_t) single_user; 7391558Srgrimes } 7401558Srgrimes 7411558Srgrimes requested_transition = 0; 7421558Srgrimes do { 7431558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 7441558Srgrimes collect_child(wpid); 7451558Srgrimes if (wpid == -1) { 7461558Srgrimes if (errno == EINTR) 7471558Srgrimes continue; 7481558Srgrimes warning("wait for single-user shell failed: %m; restarting"); 7491558Srgrimes return (state_func_t) single_user; 7501558Srgrimes } 7511558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 7521558Srgrimes warning("init: shell stopped, restarting\n"); 7531558Srgrimes kill(pid, SIGCONT); 7541558Srgrimes wpid = -1; 7551558Srgrimes } 7561558Srgrimes } while (wpid != pid && !requested_transition); 7571558Srgrimes 7581558Srgrimes if (requested_transition) 7591558Srgrimes return (state_func_t) requested_transition; 7601558Srgrimes 7611558Srgrimes if (!WIFEXITED(status)) { 7628871Srgrimes if (WTERMSIG(status) == SIGKILL) { 7638871Srgrimes /* 7648871Srgrimes * reboot(8) killed shell? 7651558Srgrimes */ 7661558Srgrimes warning("single user shell terminated."); 7671558Srgrimes sleep(STALL_TIMEOUT); 7681558Srgrimes _exit(0); 7698871Srgrimes } else { 7701558Srgrimes warning("single user shell terminated, restarting"); 7711558Srgrimes return (state_func_t) single_user; 7721558Srgrimes } 7731558Srgrimes } 7741558Srgrimes 7751558Srgrimes runcom_mode = FASTBOOT; 7761558Srgrimes return (state_func_t) runcom; 7771558Srgrimes} 7781558Srgrimes 7791558Srgrimes/* 7801558Srgrimes * Run the system startup script. 7811558Srgrimes */ 782183391Sdelphijstatic state_func_t 78392838Simpruncom(void) 7841558Srgrimes{ 785166484Simp state_func_t next_transition; 786166484Simp 787166484Simp if ((next_transition = run_script(_PATH_RUNCOM)) != 0) 788166484Simp return next_transition; 789166484Simp 790166484Simp runcom_mode = AUTOBOOT; /* the default */ 791166484Simp return (state_func_t) read_ttys; 792166484Simp} 793166484Simp 794166484Simp/* 795166484Simp * Run a shell script. 796166484Simp * Returns 0 on success, otherwise the next transition to enter: 797166484Simp * - single_user if fork/execv/waitpid failed, or if the script 798166484Simp * terminated with a signal or exit code != 0. 799217750Sjilles * - death_single if a SIGTERM was delivered to init(8). 800166484Simp */ 801183391Sdelphijstatic state_func_t 802166484Simprun_script(const char *script) 803166484Simp{ 8041558Srgrimes pid_t pid, wpid; 8051558Srgrimes int status; 8061558Srgrimes char *argv[4]; 807166484Simp const char *shell; 8081558Srgrimes struct sigaction sa; 8091558Srgrimes 810166484Simp shell = get_shell(); 811166484Simp 8121558Srgrimes if ((pid = fork()) == 0) { 8131558Srgrimes sigemptyset(&sa.sa_mask); 8141558Srgrimes sa.sa_flags = 0; 8151558Srgrimes sa.sa_handler = SIG_IGN; 816173785Sobrien sigaction(SIGTSTP, &sa, (struct sigaction *)0); 817173785Sobrien sigaction(SIGHUP, &sa, (struct sigaction *)0); 8181558Srgrimes 819232977Sed open_console(); 8201558Srgrimes 821232841Sed char _sh[] = "sh"; 822140070Sdelphij char _autoboot[] = "autoboot"; 823140070Sdelphij 824140070Sdelphij argv[0] = _sh; 825166484Simp argv[1] = __DECONST(char *, script); 826140070Sdelphij argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0; 8271558Srgrimes argv[3] = 0; 8281558Srgrimes 8291558Srgrimes sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 8301558Srgrimes 83121865Sdavidn#ifdef LOGIN_CAP 83221865Sdavidn setprocresources(RESOURCE_RC); 83321865Sdavidn#endif 834166484Simp execv(shell, argv); 835166484Simp stall("can't exec %s for %s: %m", shell, script); 8361558Srgrimes _exit(1); /* force single user mode */ 8371558Srgrimes } 8381558Srgrimes 8391558Srgrimes if (pid == -1) { 840166484Simp emergency("can't fork for %s on %s: %m", shell, script); 8411558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 8421558Srgrimes continue; 8431558Srgrimes sleep(STALL_TIMEOUT); 8441558Srgrimes return (state_func_t) single_user; 8451558Srgrimes } 8461558Srgrimes 8471558Srgrimes /* 8481558Srgrimes * Copied from single_user(). This is a bit paranoid. 8491558Srgrimes */ 85085010Sdes requested_transition = 0; 8511558Srgrimes do { 8521558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 8531558Srgrimes collect_child(wpid); 8541558Srgrimes if (wpid == -1) { 855217750Sjilles if (requested_transition == death_single) 856217750Sjilles return (state_func_t) death_single; 8571558Srgrimes if (errno == EINTR) 8581558Srgrimes continue; 859166484Simp warning("wait for %s on %s failed: %m; going to " 860166484Simp "single user mode", shell, script); 8611558Srgrimes return (state_func_t) single_user; 8621558Srgrimes } 8631558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 8641558Srgrimes warning("init: %s on %s stopped, restarting\n", 865173785Sobrien shell, script); 8661558Srgrimes kill(pid, SIGCONT); 8671558Srgrimes wpid = -1; 8681558Srgrimes } 8691558Srgrimes } while (wpid != pid); 8701558Srgrimes 8711558Srgrimes if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 8721558Srgrimes requested_transition == catatonia) { 8731558Srgrimes /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 8741558Srgrimes sigset_t s; 8751558Srgrimes 8761558Srgrimes sigfillset(&s); 8771558Srgrimes for (;;) 8781558Srgrimes sigsuspend(&s); 8791558Srgrimes } 8801558Srgrimes 8811558Srgrimes if (!WIFEXITED(status)) { 882166484Simp warning("%s on %s terminated abnormally, going to single " 883166484Simp "user mode", shell, script); 8841558Srgrimes return (state_func_t) single_user; 8851558Srgrimes } 8861558Srgrimes 8871558Srgrimes if (WEXITSTATUS(status)) 8881558Srgrimes return (state_func_t) single_user; 8891558Srgrimes 890166484Simp return (state_func_t) 0; 8911558Srgrimes} 8921558Srgrimes 8931558Srgrimes/* 8941558Srgrimes * Open the session database. 8951558Srgrimes * 8961558Srgrimes * NB: We could pass in the size here; is it necessary? 8971558Srgrimes */ 898183391Sdelphijstatic int 89992838Simpstart_session_db(void) 9001558Srgrimes{ 9011558Srgrimes if (session_db && (*session_db->close)(session_db)) 9021558Srgrimes emergency("session database close: %s", strerror(errno)); 9031558Srgrimes if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 9041558Srgrimes emergency("session database open: %s", strerror(errno)); 9051558Srgrimes return (1); 9061558Srgrimes } 9071558Srgrimes return (0); 9088871Srgrimes 9091558Srgrimes} 9101558Srgrimes 9111558Srgrimes/* 9121558Srgrimes * Add a new login session. 9131558Srgrimes */ 914183391Sdelphijstatic void 91592838Simpadd_session(session_t *sp) 9161558Srgrimes{ 9171558Srgrimes DBT key; 9181558Srgrimes DBT data; 9191558Srgrimes 9201558Srgrimes key.data = &sp->se_process; 9211558Srgrimes key.size = sizeof sp->se_process; 9221558Srgrimes data.data = &sp; 9231558Srgrimes data.size = sizeof sp; 9241558Srgrimes 9251558Srgrimes if ((*session_db->put)(session_db, &key, &data, 0)) 9261558Srgrimes emergency("insert %d: %s", sp->se_process, strerror(errno)); 9271558Srgrimes} 9281558Srgrimes 9291558Srgrimes/* 9301558Srgrimes * Delete an old login session. 9311558Srgrimes */ 932183391Sdelphijstatic void 93392838Simpdel_session(session_t *sp) 9341558Srgrimes{ 9351558Srgrimes DBT key; 9361558Srgrimes 9371558Srgrimes key.data = &sp->se_process; 9381558Srgrimes key.size = sizeof sp->se_process; 9391558Srgrimes 9401558Srgrimes if ((*session_db->del)(session_db, &key, 0)) 9411558Srgrimes emergency("delete %d: %s", sp->se_process, strerror(errno)); 9421558Srgrimes} 9431558Srgrimes 9441558Srgrimes/* 9451558Srgrimes * Look up a login session by pid. 9461558Srgrimes */ 947183391Sdelphijstatic session_t * 9481558Srgrimesfind_session(pid_t pid) 9491558Srgrimes{ 9501558Srgrimes DBT key; 9511558Srgrimes DBT data; 9521558Srgrimes session_t *ret; 9531558Srgrimes 9541558Srgrimes key.data = &pid; 9551558Srgrimes key.size = sizeof pid; 9561558Srgrimes if ((*session_db->get)(session_db, &key, &data, 0) != 0) 9571558Srgrimes return 0; 9581558Srgrimes bcopy(data.data, (char *)&ret, sizeof(ret)); 9591558Srgrimes return ret; 9601558Srgrimes} 9611558Srgrimes 9621558Srgrimes/* 9631558Srgrimes * Construct an argument vector from a command line. 9641558Srgrimes */ 965183391Sdelphijstatic char ** 96692838Simpconstruct_argv(char *command) 9671558Srgrimes{ 96892806Sobrien int argc = 0; 96992806Sobrien char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 9701558Srgrimes * sizeof (char *)); 9711558Srgrimes 97249018Sru if ((argv[argc++] = strk(command)) == 0) { 97349018Sru free(argv); 97449018Sru return (NULL); 97549018Sru } 97627837Sdavidn while ((argv[argc++] = strk((char *) 0)) != NULL) 9771558Srgrimes continue; 9781558Srgrimes return argv; 9791558Srgrimes} 9801558Srgrimes 9811558Srgrimes/* 9821558Srgrimes * Deallocate a session descriptor. 9831558Srgrimes */ 984183391Sdelphijstatic void 98592838Simpfree_session(session_t *sp) 9861558Srgrimes{ 9871558Srgrimes free(sp->se_device); 9881558Srgrimes if (sp->se_getty) { 9891558Srgrimes free(sp->se_getty); 9903594Sache free(sp->se_getty_argv_space); 9911558Srgrimes free(sp->se_getty_argv); 9921558Srgrimes } 9931558Srgrimes if (sp->se_window) { 9941558Srgrimes free(sp->se_window); 9953594Sache free(sp->se_window_argv_space); 9961558Srgrimes free(sp->se_window_argv); 9971558Srgrimes } 9983594Sache if (sp->se_type) 9993594Sache free(sp->se_type); 10001558Srgrimes free(sp); 10011558Srgrimes} 10021558Srgrimes 10031558Srgrimes/* 10041558Srgrimes * Allocate a new session descriptor. 100557344Salfred * Mark it SE_PRESENT. 10061558Srgrimes */ 1007183391Sdelphijstatic session_t * 100892838Simpnew_session(session_t *sprev, int session_index, struct ttyent *typ) 10091558Srgrimes{ 101092806Sobrien session_t *sp; 101127029Spst int fd; 10121558Srgrimes 10131558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 10141558Srgrimes typ->ty_name == 0 || 10151558Srgrimes typ->ty_getty == 0) 10161558Srgrimes return 0; 10171558Srgrimes 101827215Sache sp = (session_t *) calloc(1, sizeof (session_t)); 10191558Srgrimes 10201558Srgrimes sp->se_index = session_index; 102157344Salfred sp->se_flags |= SE_PRESENT; 10221558Srgrimes 10231558Srgrimes sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 1024173785Sobrien sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 10251558Srgrimes 102627029Spst /* 102727029Spst * Attempt to open the device, if we get "device not configured" 102827029Spst * then don't add the device to the session list. 102927029Spst */ 103027029Spst if ((fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0)) < 0) { 1031135868Simp if (errno == ENXIO) { 103227029Spst free_session(sp); 103327029Spst return (0); 103427029Spst } 103527029Spst } else 103627029Spst close(fd); 103727029Spst 10381558Srgrimes if (setupargv(sp, typ) == 0) { 10391558Srgrimes free_session(sp); 10401558Srgrimes return (0); 10411558Srgrimes } 10421558Srgrimes 10431558Srgrimes sp->se_next = 0; 10441558Srgrimes if (sprev == 0) { 10451558Srgrimes sessions = sp; 10461558Srgrimes sp->se_prev = 0; 10471558Srgrimes } else { 10481558Srgrimes sprev->se_next = sp; 10491558Srgrimes sp->se_prev = sprev; 10501558Srgrimes } 10511558Srgrimes 10521558Srgrimes return sp; 10531558Srgrimes} 10541558Srgrimes 10551558Srgrimes/* 10561558Srgrimes * Calculate getty and if useful window argv vectors. 10571558Srgrimes */ 1058183391Sdelphijstatic int 105992838Simpsetupargv(session_t *sp, struct ttyent *typ) 10601558Srgrimes{ 10611558Srgrimes 10621558Srgrimes if (sp->se_getty) { 10631558Srgrimes free(sp->se_getty); 10643594Sache free(sp->se_getty_argv_space); 10651558Srgrimes free(sp->se_getty_argv); 10661558Srgrimes } 10671558Srgrimes sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); 1068173785Sobrien sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); 10693594Sache sp->se_getty_argv_space = strdup(sp->se_getty); 10703594Sache sp->se_getty_argv = construct_argv(sp->se_getty_argv_space); 10711558Srgrimes if (sp->se_getty_argv == 0) { 10721558Srgrimes warning("can't parse getty for port %s", sp->se_device); 10731558Srgrimes free(sp->se_getty); 10743594Sache free(sp->se_getty_argv_space); 10753594Sache sp->se_getty = sp->se_getty_argv_space = 0; 10761558Srgrimes return (0); 10771558Srgrimes } 10783594Sache if (sp->se_window) { 107949018Sru free(sp->se_window); 10803594Sache free(sp->se_window_argv_space); 10813594Sache free(sp->se_window_argv); 10823594Sache } 10833594Sache sp->se_window = sp->se_window_argv_space = 0; 10843594Sache sp->se_window_argv = 0; 10851558Srgrimes if (typ->ty_window) { 10861558Srgrimes sp->se_window = strdup(typ->ty_window); 10873594Sache sp->se_window_argv_space = strdup(sp->se_window); 10883594Sache sp->se_window_argv = construct_argv(sp->se_window_argv_space); 10891558Srgrimes if (sp->se_window_argv == 0) { 10901558Srgrimes warning("can't parse window for port %s", 1091173785Sobrien sp->se_device); 10923594Sache free(sp->se_window_argv_space); 10931558Srgrimes free(sp->se_window); 10943594Sache sp->se_window = sp->se_window_argv_space = 0; 10951558Srgrimes return (0); 10961558Srgrimes } 10971558Srgrimes } 10983594Sache if (sp->se_type) 10993594Sache free(sp->se_type); 11003594Sache sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0; 11011558Srgrimes return (1); 11021558Srgrimes} 11031558Srgrimes 11041558Srgrimes/* 11051558Srgrimes * Walk the list of ttys and create sessions for each active line. 11061558Srgrimes */ 1107183391Sdelphijstatic state_func_t 110892838Simpread_ttys(void) 11091558Srgrimes{ 11101558Srgrimes int session_index = 0; 111192806Sobrien session_t *sp, *snext; 111292806Sobrien struct ttyent *typ; 11131558Srgrimes 11141558Srgrimes /* 11151558Srgrimes * Destroy any previous session state. 11161558Srgrimes * There shouldn't be any, but just in case... 11171558Srgrimes */ 11181558Srgrimes for (sp = sessions; sp; sp = snext) { 11191558Srgrimes snext = sp->se_next; 11201558Srgrimes free_session(sp); 11211558Srgrimes } 11221558Srgrimes sessions = 0; 11231558Srgrimes if (start_session_db()) 11241558Srgrimes return (state_func_t) single_user; 11251558Srgrimes 11261558Srgrimes /* 11271558Srgrimes * Allocate a session entry for each active port. 11281558Srgrimes * Note that sp starts at 0. 11291558Srgrimes */ 113027837Sdavidn while ((typ = getttyent()) != NULL) 113127837Sdavidn if ((snext = new_session(sp, ++session_index, typ)) != NULL) 11321558Srgrimes sp = snext; 11331558Srgrimes 11341558Srgrimes endttyent(); 11351558Srgrimes 11361558Srgrimes return (state_func_t) multi_user; 11371558Srgrimes} 11381558Srgrimes 11391558Srgrimes/* 11401558Srgrimes * Start a window system running. 11411558Srgrimes */ 1142183391Sdelphijstatic void 114392838Simpstart_window_system(session_t *sp) 11441558Srgrimes{ 11451558Srgrimes pid_t pid; 11461558Srgrimes sigset_t mask; 11473594Sache char term[64], *env[2]; 1148159402Skib int status; 11491558Srgrimes 11501558Srgrimes if ((pid = fork()) == -1) { 11511558Srgrimes emergency("can't fork for window system on port %s: %m", 1152173785Sobrien sp->se_device); 11531558Srgrimes /* hope that getty fails and we can try again */ 11541558Srgrimes return; 11551558Srgrimes } 1156173785Sobrien if (pid) { 1157159402Skib waitpid(-1, &status, 0); 11581558Srgrimes return; 1159159402Skib } 11601558Srgrimes 1161159402Skib /* reparent window process to the init to not make a zombie on exit */ 1162159402Skib if ((pid = fork()) == -1) { 1163159402Skib emergency("can't fork for window system on port %s: %m", 1164173785Sobrien sp->se_device); 1165159402Skib _exit(1); 1166159402Skib } 1167159402Skib if (pid) 1168159402Skib _exit(0); 1169159402Skib 11701558Srgrimes sigemptyset(&mask); 11711558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 11721558Srgrimes 11731558Srgrimes if (setsid() < 0) 11741558Srgrimes emergency("setsid failed (window) %m"); 11751558Srgrimes 117621865Sdavidn#ifdef LOGIN_CAP 117721865Sdavidn setprocresources(RESOURCE_WINDOW); 117821865Sdavidn#endif 11793594Sache if (sp->se_type) { 11803594Sache /* Don't use malloc after fork */ 11813594Sache strcpy(term, "TERM="); 118222922Sdg strncat(term, sp->se_type, sizeof(term) - 6); 11833594Sache env[0] = term; 11843594Sache env[1] = 0; 11853594Sache } 11863594Sache else 11873594Sache env[0] = 0; 11883594Sache execve(sp->se_window_argv[0], sp->se_window_argv, env); 11891558Srgrimes stall("can't exec window system '%s' for port %s: %m", 11901558Srgrimes sp->se_window_argv[0], sp->se_device); 11911558Srgrimes _exit(1); 11921558Srgrimes} 11931558Srgrimes 11941558Srgrimes/* 11951558Srgrimes * Start a login session running. 11961558Srgrimes */ 1197183391Sdelphijstatic pid_t 119892838Simpstart_getty(session_t *sp) 11991558Srgrimes{ 12001558Srgrimes pid_t pid; 12011558Srgrimes sigset_t mask; 12021558Srgrimes time_t current_time = time((time_t *) 0); 12039997Sache int too_quick = 0; 12043594Sache char term[64], *env[2]; 12051558Srgrimes 120610006Smpp if (current_time >= sp->se_started && 12079997Sache current_time - sp->se_started < GETTY_SPACING) { 12089997Sache if (++sp->se_nspace > GETTY_NSPACE) { 12099997Sache sp->se_nspace = 0; 12109997Sache too_quick = 1; 12119997Sache } 12129997Sache } else 12139997Sache sp->se_nspace = 0; 12149997Sache 12151558Srgrimes /* 12161558Srgrimes * fork(), not vfork() -- we can't afford to block. 12171558Srgrimes */ 12181558Srgrimes if ((pid = fork()) == -1) { 12191558Srgrimes emergency("can't fork for getty on port %s: %m", sp->se_device); 12201558Srgrimes return -1; 12211558Srgrimes } 12221558Srgrimes 12231558Srgrimes if (pid) 12241558Srgrimes return pid; 12251558Srgrimes 12269997Sache if (too_quick) { 12279997Sache warning("getty repeating too quickly on port %s, sleeping %d secs", 1228173785Sobrien sp->se_device, GETTY_SLEEP); 12291558Srgrimes sleep((unsigned) GETTY_SLEEP); 12301558Srgrimes } 12311558Srgrimes 12321558Srgrimes if (sp->se_window) { 12331558Srgrimes start_window_system(sp); 12341558Srgrimes sleep(WINDOW_WAIT); 12351558Srgrimes } 12361558Srgrimes 12371558Srgrimes sigemptyset(&mask); 12381558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 12391558Srgrimes 124021865Sdavidn#ifdef LOGIN_CAP 124121865Sdavidn setprocresources(RESOURCE_GETTY); 124221865Sdavidn#endif 12433594Sache if (sp->se_type) { 12443594Sache /* Don't use malloc after fork */ 12453594Sache strcpy(term, "TERM="); 124622922Sdg strncat(term, sp->se_type, sizeof(term) - 6); 12473594Sache env[0] = term; 12483594Sache env[1] = 0; 1249173785Sobrien } else 12503594Sache env[0] = 0; 12513594Sache execve(sp->se_getty_argv[0], sp->se_getty_argv, env); 12521558Srgrimes stall("can't exec getty '%s' for port %s: %m", 12531558Srgrimes sp->se_getty_argv[0], sp->se_device); 12541558Srgrimes _exit(1); 12551558Srgrimes} 12561558Srgrimes 12571558Srgrimes/* 12581558Srgrimes * Collect exit status for a child. 12591558Srgrimes * If an exiting login, start a new login running. 12601558Srgrimes */ 1261183391Sdelphijstatic void 12621558Srgrimescollect_child(pid_t pid) 12631558Srgrimes{ 126492806Sobrien session_t *sp, *sprev, *snext; 12651558Srgrimes 12661558Srgrimes if (! sessions) 12671558Srgrimes return; 12681558Srgrimes 12691558Srgrimes if (! (sp = find_session(pid))) 12701558Srgrimes return; 12711558Srgrimes 12721558Srgrimes del_session(sp); 12731558Srgrimes sp->se_process = 0; 12741558Srgrimes 12751558Srgrimes if (sp->se_flags & SE_SHUTDOWN) { 127627837Sdavidn if ((sprev = sp->se_prev) != NULL) 12771558Srgrimes sprev->se_next = sp->se_next; 12781558Srgrimes else 12791558Srgrimes sessions = sp->se_next; 128027837Sdavidn if ((snext = sp->se_next) != NULL) 12811558Srgrimes snext->se_prev = sp->se_prev; 12821558Srgrimes free_session(sp); 12831558Srgrimes return; 12841558Srgrimes } 12851558Srgrimes 12861558Srgrimes if ((pid = start_getty(sp)) == -1) { 12871558Srgrimes /* serious trouble */ 12881558Srgrimes requested_transition = clean_ttys; 12891558Srgrimes return; 12901558Srgrimes } 12911558Srgrimes 12921558Srgrimes sp->se_process = pid; 12931558Srgrimes sp->se_started = time((time_t *) 0); 12941558Srgrimes add_session(sp); 12951558Srgrimes} 12961558Srgrimes 12971558Srgrimes/* 12981558Srgrimes * Catch a signal and request a state transition. 12991558Srgrimes */ 1300183391Sdelphijstatic void 130192838Simptransition_handler(int sig) 13021558Srgrimes{ 13031558Srgrimes 13041558Srgrimes switch (sig) { 13051558Srgrimes case SIGHUP: 1306217750Sjilles if (current_state == read_ttys || current_state == multi_user || 1307217750Sjilles current_state == clean_ttys || current_state == catatonia) 1308217750Sjilles requested_transition = clean_ttys; 13091558Srgrimes break; 131047962Sru case SIGUSR2: 131147962Sru howto = RB_POWEROFF; 131247962Sru case SIGUSR1: 131347962Sru howto |= RB_HALT; 13142323Snate case SIGINT: 13152327Sjkh Reboot = TRUE; 13161558Srgrimes case SIGTERM: 1317217750Sjilles if (current_state == read_ttys || current_state == multi_user || 1318217750Sjilles current_state == clean_ttys || current_state == catatonia) 1319217750Sjilles requested_transition = death; 1320217750Sjilles else 1321217750Sjilles requested_transition = death_single; 13221558Srgrimes break; 13231558Srgrimes case SIGTSTP: 1324217750Sjilles if (current_state == runcom || current_state == read_ttys || 1325217750Sjilles current_state == clean_ttys || 1326217750Sjilles current_state == multi_user || current_state == catatonia) 1327217750Sjilles requested_transition = catatonia; 13281558Srgrimes break; 13291558Srgrimes default: 13301558Srgrimes requested_transition = 0; 13311558Srgrimes break; 13321558Srgrimes } 13331558Srgrimes} 13341558Srgrimes 13351558Srgrimes/* 13361558Srgrimes * Take the system multiuser. 13371558Srgrimes */ 1338183391Sdelphijstatic state_func_t 133992838Simpmulti_user(void) 13401558Srgrimes{ 13411558Srgrimes pid_t pid; 134292806Sobrien session_t *sp; 13431558Srgrimes 13441558Srgrimes requested_transition = 0; 13451558Srgrimes 13461558Srgrimes /* 13471558Srgrimes * If the administrator has not set the security level to -1 13481558Srgrimes * to indicate that the kernel should not run multiuser in secure 13498871Srgrimes * mode, and the run script has not set a higher level of security 13501558Srgrimes * than level 1, then put the kernel into secure mode. 13511558Srgrimes */ 13521558Srgrimes if (getsecuritylevel() == 0) 13531558Srgrimes setsecuritylevel(1); 13541558Srgrimes 13551558Srgrimes for (sp = sessions; sp; sp = sp->se_next) { 13561558Srgrimes if (sp->se_process) 13571558Srgrimes continue; 13581558Srgrimes if ((pid = start_getty(sp)) == -1) { 13591558Srgrimes /* serious trouble */ 13601558Srgrimes requested_transition = clean_ttys; 13611558Srgrimes break; 13621558Srgrimes } 13631558Srgrimes sp->se_process = pid; 13641558Srgrimes sp->se_started = time((time_t *) 0); 13651558Srgrimes add_session(sp); 13661558Srgrimes } 13671558Srgrimes 13681558Srgrimes while (!requested_transition) 13691558Srgrimes if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 13701558Srgrimes collect_child(pid); 13711558Srgrimes 13721558Srgrimes return (state_func_t) requested_transition; 13731558Srgrimes} 13741558Srgrimes 13751558Srgrimes/* 137657344Salfred * This is an (n*2)+(n^2) algorithm. We hope it isn't run often... 13771558Srgrimes */ 1378183391Sdelphijstatic state_func_t 137992838Simpclean_ttys(void) 13801558Srgrimes{ 138192806Sobrien session_t *sp, *sprev; 138292806Sobrien struct ttyent *typ; 138392806Sobrien int session_index = 0; 138492806Sobrien int devlen; 13853594Sache char *old_getty, *old_window, *old_type; 13861558Srgrimes 1387173787Sobrien /* 1388173787Sobrien * mark all sessions for death, (!SE_PRESENT) 138957344Salfred * as we find or create new ones they'll be marked as keepers, 139057344Salfred * we'll later nuke all the ones not found in /etc/ttys 139157344Salfred */ 139257344Salfred for (sp = sessions; sp != NULL; sp = sp->se_next) 139357344Salfred sp->se_flags &= ~SE_PRESENT; 139457344Salfred 13951558Srgrimes devlen = sizeof(_PATH_DEV) - 1; 139627837Sdavidn while ((typ = getttyent()) != NULL) { 13971558Srgrimes ++session_index; 13981558Srgrimes 13991558Srgrimes for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 14001558Srgrimes if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 14011558Srgrimes break; 14021558Srgrimes 14031558Srgrimes if (sp) { 140457344Salfred /* we want this one to live */ 140557344Salfred sp->se_flags |= SE_PRESENT; 14061558Srgrimes if (sp->se_index != session_index) { 14071558Srgrimes warning("port %s changed utmp index from %d to %d", 14081558Srgrimes sp->se_device, sp->se_index, 14091558Srgrimes session_index); 14101558Srgrimes sp->se_index = session_index; 14111558Srgrimes } 14121558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 14131558Srgrimes typ->ty_getty == 0) { 14141558Srgrimes sp->se_flags |= SE_SHUTDOWN; 14151558Srgrimes kill(sp->se_process, SIGHUP); 14161558Srgrimes continue; 14171558Srgrimes } 14181558Srgrimes sp->se_flags &= ~SE_SHUTDOWN; 14193594Sache old_getty = sp->se_getty ? strdup(sp->se_getty) : 0; 14203594Sache old_window = sp->se_window ? strdup(sp->se_window) : 0; 14213594Sache old_type = sp->se_type ? strdup(sp->se_type) : 0; 14221558Srgrimes if (setupargv(sp, typ) == 0) { 14231558Srgrimes warning("can't parse getty for port %s", 14241558Srgrimes sp->se_device); 14251558Srgrimes sp->se_flags |= SE_SHUTDOWN; 14261558Srgrimes kill(sp->se_process, SIGHUP); 14271558Srgrimes } 14283594Sache else if ( !old_getty 142927837Sdavidn || (!old_type && sp->se_type) 143027837Sdavidn || (old_type && !sp->se_type) 143127837Sdavidn || (!old_window && sp->se_window) 143227837Sdavidn || (old_window && !sp->se_window) 143327837Sdavidn || (strcmp(old_getty, sp->se_getty) != 0) 143427837Sdavidn || (old_window && strcmp(old_window, sp->se_window) != 0) 143527837Sdavidn || (old_type && strcmp(old_type, sp->se_type) != 0) 14363594Sache ) { 14373594Sache /* Don't set SE_SHUTDOWN here */ 14383594Sache sp->se_nspace = 0; 14393594Sache sp->se_started = 0; 14403594Sache kill(sp->se_process, SIGHUP); 14413594Sache } 14423594Sache if (old_getty) 14433594Sache free(old_getty); 144478484Smikeh if (old_window) 14453594Sache free(old_window); 14463594Sache if (old_type) 14473594Sache free(old_type); 14481558Srgrimes continue; 14491558Srgrimes } 14501558Srgrimes 14511558Srgrimes new_session(sprev, session_index, typ); 14521558Srgrimes } 14531558Srgrimes 14541558Srgrimes endttyent(); 14551558Srgrimes 145657344Salfred /* 145757344Salfred * sweep through and kill all deleted sessions 145857344Salfred * ones who's /etc/ttys line was deleted (SE_PRESENT unset) 145957344Salfred */ 146057344Salfred for (sp = sessions; sp != NULL; sp = sp->se_next) { 146157344Salfred if ((sp->se_flags & SE_PRESENT) == 0) { 146257344Salfred sp->se_flags |= SE_SHUTDOWN; 146357344Salfred kill(sp->se_process, SIGHUP); 146457344Salfred } 146557344Salfred } 146657344Salfred 14671558Srgrimes return (state_func_t) multi_user; 14681558Srgrimes} 14691558Srgrimes 14701558Srgrimes/* 14711558Srgrimes * Block further logins. 14721558Srgrimes */ 1473183391Sdelphijstatic state_func_t 147492838Simpcatatonia(void) 14751558Srgrimes{ 147692806Sobrien session_t *sp; 14771558Srgrimes 14781558Srgrimes for (sp = sessions; sp; sp = sp->se_next) 14791558Srgrimes sp->se_flags |= SE_SHUTDOWN; 14801558Srgrimes 14811558Srgrimes return (state_func_t) multi_user; 14821558Srgrimes} 14831558Srgrimes 14841558Srgrimes/* 14851558Srgrimes * Note SIGALRM. 14861558Srgrimes */ 1487183391Sdelphijstatic void 148892838Simpalrm_handler(int sig) 14891558Srgrimes{ 1490173785Sobrien 149127837Sdavidn (void)sig; 14921558Srgrimes clang = 1; 14931558Srgrimes} 14941558Srgrimes 14951558Srgrimes/* 14961558Srgrimes * Bring the system down to single user. 14971558Srgrimes */ 1498183391Sdelphijstatic state_func_t 149992838Simpdeath(void) 15001558Srgrimes{ 150192806Sobrien session_t *sp; 15021558Srgrimes 1503194198Sed /* 1504194198Sed * Also revoke the TTY here. Because runshutdown() may reopen 1505194198Sed * the TTY whose getty we're killing here, there is no guarantee 1506194198Sed * runshutdown() will perform the initial open() call, causing 1507194198Sed * the terminal attributes to be misconfigured. 1508194198Sed */ 150927215Sache for (sp = sessions; sp; sp = sp->se_next) { 15101558Srgrimes sp->se_flags |= SE_SHUTDOWN; 151127941Sache kill(sp->se_process, SIGHUP); 1512194198Sed revoke(sp->se_device); 151327215Sache } 15141558Srgrimes 151527837Sdavidn /* Try to run the rc.shutdown script within a period of time */ 1516173785Sobrien runshutdown(); 1517173785Sobrien 1518217750Sjilles return (state_func_t) death_single; 1519217750Sjilles} 1520217750Sjilles 1521217750Sjilles/* 1522217750Sjilles * Do what is necessary to reinitialize single user mode or reboot 1523217750Sjilles * from an incomplete state. 1524217750Sjilles */ 1525217750Sjillesstatic state_func_t 1526217750Sjillesdeath_single(void) 1527217750Sjilles{ 1528217750Sjilles int i; 1529217750Sjilles pid_t pid; 1530217750Sjilles static const int death_sigs[2] = { SIGTERM, SIGKILL }; 1531217750Sjilles 1532217750Sjilles revoke(_PATH_CONSOLE); 1533217750Sjilles 153427197Sache for (i = 0; i < 2; ++i) { 15351558Srgrimes if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 15361558Srgrimes return (state_func_t) single_user; 15371558Srgrimes 15381558Srgrimes clang = 0; 15391558Srgrimes alarm(DEATH_WATCH); 15401558Srgrimes do 15411558Srgrimes if ((pid = waitpid(-1, (int *)0, 0)) != -1) 15421558Srgrimes collect_child(pid); 15431558Srgrimes while (clang == 0 && errno != ECHILD); 15441558Srgrimes 15451558Srgrimes if (errno == ECHILD) 15461558Srgrimes return (state_func_t) single_user; 15471558Srgrimes } 15481558Srgrimes 15491558Srgrimes warning("some processes would not die; ps axl advised"); 15501558Srgrimes 15511558Srgrimes return (state_func_t) single_user; 15521558Srgrimes} 155327837Sdavidn 155427837Sdavidn/* 155527837Sdavidn * Run the system shutdown script. 155627837Sdavidn * 155727837Sdavidn * Exit codes: XXX I should document more 155827837Sdavidn * -2 shutdown script terminated abnormally 155927837Sdavidn * -1 fatal error - can't run script 156027837Sdavidn * 0 good. 156127837Sdavidn * >0 some error (exit code) 156227837Sdavidn */ 1563183391Sdelphijstatic int 156492838Simprunshutdown(void) 156527837Sdavidn{ 156627837Sdavidn pid_t pid, wpid; 156727837Sdavidn int status; 156827837Sdavidn int shutdowntimeout; 156927837Sdavidn size_t len; 157053550Sdillon char *argv[4]; 1571166484Simp const char *shell; 157227837Sdavidn struct sigaction sa; 157328344Sdavidn struct stat sb; 157427837Sdavidn 157528344Sdavidn /* 157628344Sdavidn * rc.shutdown is optional, so to prevent any unnecessary 157728344Sdavidn * complaints from the shell we simply don't run it if the 157828344Sdavidn * file does not exist. If the stat() here fails for other 157928344Sdavidn * reasons, we'll let the shell complain. 158028344Sdavidn */ 158128344Sdavidn if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT) 158228344Sdavidn return 0; 158328344Sdavidn 1584166484Simp shell = get_shell(); 1585166484Simp 158627837Sdavidn if ((pid = fork()) == 0) { 158727837Sdavidn sigemptyset(&sa.sa_mask); 158827837Sdavidn sa.sa_flags = 0; 158927837Sdavidn sa.sa_handler = SIG_IGN; 1590173785Sobrien sigaction(SIGTSTP, &sa, (struct sigaction *)0); 1591173785Sobrien sigaction(SIGHUP, &sa, (struct sigaction *)0); 159227837Sdavidn 1593232977Sed open_console(); 159427837Sdavidn 1595140070Sdelphij char _sh[] = "sh"; 1596140070Sdelphij char _reboot[] = "reboot"; 1597140070Sdelphij char _single[] = "single"; 1598140070Sdelphij char _path_rundown[] = _PATH_RUNDOWN; 1599140070Sdelphij 1600140070Sdelphij argv[0] = _sh; 1601140070Sdelphij argv[1] = _path_rundown; 1602140070Sdelphij argv[2] = Reboot ? _reboot : _single; 160353550Sdillon argv[3] = 0; 160427837Sdavidn 160527837Sdavidn sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 160627837Sdavidn 160727941Sache#ifdef LOGIN_CAP 160827941Sache setprocresources(RESOURCE_RC); 160927941Sache#endif 1610166484Simp execv(shell, argv); 1611166484Simp warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN); 161227837Sdavidn _exit(1); /* force single user mode */ 161327837Sdavidn } 161427837Sdavidn 161527837Sdavidn if (pid == -1) { 1616166484Simp emergency("can't fork for %s on %s: %m", shell, _PATH_RUNDOWN); 161727837Sdavidn while (waitpid(-1, (int *) 0, WNOHANG) > 0) 161827837Sdavidn continue; 161927837Sdavidn sleep(STALL_TIMEOUT); 162027837Sdavidn return -1; 162127837Sdavidn } 162227837Sdavidn 162327837Sdavidn len = sizeof(shutdowntimeout); 1624173785Sobrien if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len, 1625173785Sobrien NULL, 0) == -1 || shutdowntimeout < 2) 1626173785Sobrien shutdowntimeout = DEATH_SCRIPT; 162727837Sdavidn alarm(shutdowntimeout); 162827837Sdavidn clang = 0; 162927837Sdavidn /* 163027837Sdavidn * Copied from single_user(). This is a bit paranoid. 163127837Sdavidn * Use the same ALRM handler. 163227837Sdavidn */ 163327837Sdavidn do { 163427837Sdavidn if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 163527837Sdavidn collect_child(wpid); 163627837Sdavidn if (clang == 1) { 163727837Sdavidn /* we were waiting for the sub-shell */ 163827837Sdavidn kill(wpid, SIGTERM); 1639166484Simp warning("timeout expired for %s on %s: %m; going to " 1640166484Simp "single user mode", shell, _PATH_RUNDOWN); 164127837Sdavidn return -1; 164227837Sdavidn } 164327837Sdavidn if (wpid == -1) { 164427837Sdavidn if (errno == EINTR) 164527837Sdavidn continue; 1646166484Simp warning("wait for %s on %s failed: %m; going to " 1647166484Simp "single user mode", shell, _PATH_RUNDOWN); 164827837Sdavidn return -1; 164927837Sdavidn } 165027837Sdavidn if (wpid == pid && WIFSTOPPED(status)) { 165127837Sdavidn warning("init: %s on %s stopped, restarting\n", 1652166484Simp shell, _PATH_RUNDOWN); 165327837Sdavidn kill(pid, SIGCONT); 165427837Sdavidn wpid = -1; 165527837Sdavidn } 165627837Sdavidn } while (wpid != pid && !clang); 165727837Sdavidn 165827837Sdavidn /* Turn off the alarm */ 165927837Sdavidn alarm(0); 166027837Sdavidn 166127837Sdavidn if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 166227837Sdavidn requested_transition == catatonia) { 166327837Sdavidn /* 166427837Sdavidn * /etc/rc.shutdown executed /sbin/reboot; 166527837Sdavidn * wait for the end quietly 166627837Sdavidn */ 166727837Sdavidn sigset_t s; 166827837Sdavidn 166927837Sdavidn sigfillset(&s); 167027837Sdavidn for (;;) 167127837Sdavidn sigsuspend(&s); 167227837Sdavidn } 167327837Sdavidn 167427837Sdavidn if (!WIFEXITED(status)) { 1675166484Simp warning("%s on %s terminated abnormally, going to " 1676166484Simp "single user mode", shell, _PATH_RUNDOWN); 167727837Sdavidn return -2; 167827837Sdavidn } 167927837Sdavidn 168027837Sdavidn if ((status = WEXITSTATUS(status)) != 0) 168127837Sdavidn warning("%s returned status %d", _PATH_RUNDOWN, status); 168227837Sdavidn 168327837Sdavidn return status; 168427837Sdavidn} 168527837Sdavidn 1686140070Sdelphijstatic char * 1687140070Sdelphijstrk(char *p) 16885478Sache{ 1689173785Sobrien static char *t; 1690173785Sobrien char *q; 1691173785Sobrien int c; 16925478Sache 1693173785Sobrien if (p) 1694173785Sobrien t = p; 1695173785Sobrien if (!t) 1696173785Sobrien return 0; 16975478Sache 1698173785Sobrien c = *t; 1699173785Sobrien while (c == ' ' || c == '\t' ) 1700173785Sobrien c = *++t; 1701173785Sobrien if (!c) { 1702173785Sobrien t = 0; 1703173785Sobrien return 0; 1704173785Sobrien } 17055478Sache q = t; 1706173785Sobrien if (c == '\'') { 1707173785Sobrien c = *++t; 1708173785Sobrien q = t; 1709173785Sobrien while (c && c != '\'') 1710173785Sobrien c = *++t; 1711173785Sobrien if (!c) /* unterminated string */ 1712173785Sobrien q = t = 0; 1713173785Sobrien else 1714173785Sobrien *t++ = 0; 1715173785Sobrien } else { 1716173785Sobrien while (c && c != ' ' && c != '\t' ) 1717173785Sobrien c = *++t; 1718173785Sobrien *t++ = 0; 1719173785Sobrien if (!c) 1720173785Sobrien t = 0; 1721173785Sobrien } 1722173785Sobrien return q; 17235478Sache} 172421865Sdavidn 172521865Sdavidn#ifdef LOGIN_CAP 1726183391Sdelphijstatic void 172792838Simpsetprocresources(const char *cname) 172821865Sdavidn{ 172921941Sdavidn login_cap_t *lc; 173027176Sache if ((lc = login_getclassbyname(cname, NULL)) != NULL) { 1731173785Sobrien setusercontext(lc, (struct passwd*)NULL, 0, 1732254288Sjilles LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | 1733254288Sjilles LOGIN_SETLOGINCLASS | LOGIN_SETCPUMASK); 173421941Sdavidn login_close(lc); 173521941Sdavidn } 173621865Sdavidn} 173721865Sdavidn#endif 1738