init.c revision 173787
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: head/sbin/init/init.c 173787 2007-11-20 21:25:58Z obrien $"; 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 */ 89173785Sobrien#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" 95173785Sobrien#define RESOURCE_WINDOW "default" 96173785Sobrien#define RESOURCE_GETTY "default" 971558Srgrimes 9892838Simpvoid handle(sig_t, ...); 9992838Simpvoid delset(sigset_t *, ...); 1001558Srgrimes 10192838Simpvoid stall(const char *, ...) __printflike(1, 2); 10292838Simpvoid warning(const char *, ...) __printflike(1, 2); 10392838Simpvoid emergency(const char *, ...) __printflike(1, 2); 10492838Simpvoid disaster(int); 10592838Simpvoid badsys(int); 10692838Simpint 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 11792838Simpstate_func_t single_user(void); 11892838Simpstate_func_t runcom(void); 11992838Simpstate_func_t read_ttys(void); 12092838Simpstate_func_t multi_user(void); 12192838Simpstate_func_t clean_ttys(void); 12292838Simpstate_func_t catatonia(void); 12392838Simpstate_func_t death(void); 1241558Srgrimes 125166484Simpstate_func_t run_script(const char *); 126166484Simp 1271558Srgrimesenum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 12811910Sphk#define FALSE 0 12911910Sphk#define TRUE 1 13011910Sphk 1312327Sjkhint Reboot = FALSE; 13247962Sruint howto = RB_AUTOBOOT; 1331558Srgrimes 13419227Sphkint devfs; 13519227Sphk 13692838Simpvoid transition(state_t); 137166484Simpstate_t requested_transition; 1381558Srgrimes 139140070Sdelphijvoid setctty(const char *); 140166484Simpconst char *get_shell(void); 141166484Simpvoid write_stderr(const char *message); 1421558Srgrimes 1431558Srgrimestypedef struct init_session { 1441558Srgrimes int se_index; /* index of entry in ttys file */ 1451558Srgrimes pid_t se_process; /* controlling process */ 1461558Srgrimes time_t se_started; /* used to avoid thrashing */ 1471558Srgrimes int se_flags; /* status of session */ 1481558Srgrimes#define SE_SHUTDOWN 0x1 /* session won't be restarted */ 14957344Salfred#define SE_PRESENT 0x2 /* session is in /etc/ttys */ 1503594Sache int se_nspace; /* spacing count */ 1511558Srgrimes char *se_device; /* filename of port */ 1521558Srgrimes char *se_getty; /* what to run on that port */ 1533594Sache char *se_getty_argv_space; /* pre-parsed argument array space */ 1541558Srgrimes char **se_getty_argv; /* pre-parsed argument array */ 1551558Srgrimes char *se_window; /* window system (started only once) */ 1563594Sache char *se_window_argv_space; /* pre-parsed argument array space */ 1571558Srgrimes char **se_window_argv; /* pre-parsed argument array */ 1583594Sache char *se_type; /* default terminal type */ 1591558Srgrimes struct init_session *se_prev; 1601558Srgrimes struct init_session *se_next; 1611558Srgrimes} session_t; 1621558Srgrimes 16392838Simpvoid free_session(session_t *); 16492838Simpsession_t *new_session(session_t *, int, struct ttyent *); 1651558Srgrimessession_t *sessions; 1661558Srgrimes 16792838Simpchar **construct_argv(char *); 16892838Simpvoid start_window_system(session_t *); 16992838Simpvoid collect_child(pid_t); 17092838Simppid_t start_getty(session_t *); 17192838Simpvoid transition_handler(int); 17292838Simpvoid alrm_handler(int); 17392838Simpvoid setsecuritylevel(int); 17492838Simpint getsecuritylevel(void); 17592838Simpint setupargv(session_t *, struct ttyent *); 17621941Sdavidn#ifdef LOGIN_CAP 17792838Simpvoid setprocresources(const char *); 17821941Sdavidn#endif 1791558Srgrimesint clang; 1801558Srgrimes 18192838Simpvoid clear_session_logs(session_t *); 1821558Srgrimes 18392838Simpint start_session_db(void); 18492838Simpvoid add_session(session_t *); 18592838Simpvoid del_session(session_t *); 18692838Simpsession_t *find_session(pid_t); 1871558SrgrimesDB *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 */ 3911558Srgrimesvoid 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 */ 4151558Srgrimesvoid 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 */ 4321558Srgrimesvoid 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 */ 4481558Srgrimesvoid 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 */ 4621558Srgrimesvoid 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 */ 4781558Srgrimesvoid 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 */ 4911558Srgrimesvoid 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 */ 5051558Srgrimesint 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 */ 5291558Srgrimesvoid 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 */ 5571558Srgrimesvoid 55892838Simptransition(state_t s) 5591558Srgrimes{ 560173785Sobrien 5611558Srgrimes for (;;) 5621558Srgrimes s = (state_t) (*s)(); 5631558Srgrimes} 5641558Srgrimes 5651558Srgrimes/* 5661558Srgrimes * Close out the accounting files for a login session. 5671558Srgrimes * NB: should send a message to the session logger to avoid blocking. 5681558Srgrimes */ 5691558Srgrimesvoid 57092838Simpclear_session_logs(session_t *sp) 5711558Srgrimes{ 5721558Srgrimes char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 5731558Srgrimes 5741558Srgrimes if (logout(line)) 5751558Srgrimes logwtmp(line, "", ""); 5761558Srgrimes} 5771558Srgrimes 5781558Srgrimes/* 5791558Srgrimes * Start a session and allocate a controlling terminal. 5801558Srgrimes * Only called by children of init after forking. 5811558Srgrimes */ 5821558Srgrimesvoid 583140070Sdelphijsetctty(const char *name) 5841558Srgrimes{ 5851558Srgrimes int fd; 5861558Srgrimes 587173785Sobrien revoke(name); 5881558Srgrimes if ((fd = open(name, O_RDWR)) == -1) { 5891558Srgrimes stall("can't open %s: %m", name); 5901558Srgrimes _exit(1); 5911558Srgrimes } 5921558Srgrimes if (login_tty(fd) == -1) { 5931558Srgrimes stall("can't get %s for controlling terminal: %m", name); 5941558Srgrimes _exit(1); 5951558Srgrimes } 5961558Srgrimes} 5971558Srgrimes 598166484Simpconst char * 599166484Simpget_shell(void) 600166484Simp{ 601166484Simp static char kenv_value[PATH_MAX]; 602166484Simp 603166484Simp if (kenv(KENV_GET, "init_shell", kenv_value, sizeof(kenv_value)) > 0) 604166484Simp return kenv_value; 605166484Simp else 606166484Simp return _PATH_BSHELL; 607166484Simp} 608166484Simp 609166484Simpvoid 610166484Simpwrite_stderr(const char *message) 611166484Simp{ 612173785Sobrien 613166484Simp write(STDERR_FILENO, message, strlen(message)); 614166484Simp} 615166484Simp 6161558Srgrimes/* 6171558Srgrimes * Bring the system up single user. 6181558Srgrimes */ 6191558Srgrimesstate_func_t 62092838Simpsingle_user(void) 6211558Srgrimes{ 6221558Srgrimes pid_t pid, wpid; 6231558Srgrimes int status; 6241558Srgrimes sigset_t mask; 625166484Simp const char *shell; 6261558Srgrimes char *argv[2]; 6271558Srgrimes#ifdef SECURE 6281558Srgrimes struct ttyent *typ; 6291558Srgrimes struct passwd *pp; 6301558Srgrimes static const char banner[] = 6311558Srgrimes "Enter root password, or ^D to go multi-user\n"; 6321558Srgrimes char *clear, *password; 6331558Srgrimes#endif 63437814Sphk#ifdef DEBUGSHELL 63537814Sphk char altshell[128]; 63637814Sphk#endif 6371558Srgrimes 6382327Sjkh if (Reboot) { 63947962Sru /* Instead of going single user, let's reboot the machine */ 6402323Snate sync(); 6412323Snate alarm(2); 6422323Snate pause(); 64347962Sru reboot(howto); 6442323Snate _exit(0); 6452323Snate } 6462323Snate 647166484Simp shell = get_shell(); 648166484Simp 6491558Srgrimes if ((pid = fork()) == 0) { 6501558Srgrimes /* 6511558Srgrimes * Start the single user session. 6521558Srgrimes */ 6531558Srgrimes setctty(_PATH_CONSOLE); 6541558Srgrimes 6551558Srgrimes#ifdef SECURE 6561558Srgrimes /* 6571558Srgrimes * Check the root password. 6581558Srgrimes * We don't care if the console is 'on' by default; 6591558Srgrimes * it's the only tty that can be 'off' and 'secure'. 6601558Srgrimes */ 6611558Srgrimes typ = getttynam("console"); 6621558Srgrimes pp = getpwnam("root"); 66353550Sdillon if (typ && (typ->ty_status & TTY_SECURE) == 0 && 66453550Sdillon pp && *pp->pw_passwd) { 665166484Simp write_stderr(banner); 6661558Srgrimes for (;;) { 6671558Srgrimes clear = getpass("Password:"); 6681558Srgrimes if (clear == 0 || *clear == '\0') 6691558Srgrimes _exit(0); 6701558Srgrimes password = crypt(clear, pp->pw_passwd); 6711558Srgrimes bzero(clear, _PASSWORD_LEN); 6721558Srgrimes if (strcmp(password, pp->pw_passwd) == 0) 6731558Srgrimes break; 6741558Srgrimes warning("single-user login failed\n"); 6751558Srgrimes } 6761558Srgrimes } 6771558Srgrimes endttyent(); 6781558Srgrimes endpwent(); 6791558Srgrimes#endif /* SECURE */ 6801558Srgrimes 6811558Srgrimes#ifdef DEBUGSHELL 6821558Srgrimes { 68337814Sphk char *cp = altshell; 6841558Srgrimes int num; 6851558Srgrimes 686166484Simp#define SHREQUEST "Enter full pathname of shell or RETURN for " 687166484Simp write_stderr(SHREQUEST); 688166484Simp write_stderr(shell); 689166484Simp write_stderr(": "); 6901558Srgrimes while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 6911558Srgrimes num != 0 && *cp != '\n' && cp < &altshell[127]) 692173785Sobrien cp++; 6931558Srgrimes *cp = '\0'; 6941558Srgrimes if (altshell[0] != '\0') 6951558Srgrimes shell = altshell; 6961558Srgrimes } 6971558Srgrimes#endif /* DEBUGSHELL */ 6981558Srgrimes 6991558Srgrimes /* 7001558Srgrimes * Unblock signals. 7011558Srgrimes * We catch all the interesting ones, 7021558Srgrimes * and those are reset to SIG_DFL on exec. 7031558Srgrimes */ 7041558Srgrimes sigemptyset(&mask); 7051558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 7061558Srgrimes 7071558Srgrimes /* 7081558Srgrimes * Fire off a shell. 7091558Srgrimes * If the default one doesn't work, try the Bourne shell. 7101558Srgrimes */ 711140070Sdelphij 712140070Sdelphij char name[] = "-sh"; 713140070Sdelphij 714140070Sdelphij argv[0] = name; 7151558Srgrimes argv[1] = 0; 7161558Srgrimes execv(shell, argv); 7171558Srgrimes emergency("can't exec %s for single user: %m", shell); 7181558Srgrimes execv(_PATH_BSHELL, argv); 7191558Srgrimes emergency("can't exec %s for single user: %m", _PATH_BSHELL); 7201558Srgrimes sleep(STALL_TIMEOUT); 7211558Srgrimes _exit(1); 7221558Srgrimes } 7231558Srgrimes 7241558Srgrimes if (pid == -1) { 7251558Srgrimes /* 7261558Srgrimes * We are seriously hosed. Do our best. 7271558Srgrimes */ 7281558Srgrimes emergency("can't fork single-user shell, trying again"); 7291558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 7301558Srgrimes continue; 7311558Srgrimes return (state_func_t) single_user; 7321558Srgrimes } 7331558Srgrimes 7341558Srgrimes requested_transition = 0; 7351558Srgrimes do { 7361558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 7371558Srgrimes collect_child(wpid); 7381558Srgrimes if (wpid == -1) { 7391558Srgrimes if (errno == EINTR) 7401558Srgrimes continue; 7411558Srgrimes warning("wait for single-user shell failed: %m; restarting"); 7421558Srgrimes return (state_func_t) single_user; 7431558Srgrimes } 7441558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 7451558Srgrimes warning("init: shell stopped, restarting\n"); 7461558Srgrimes kill(pid, SIGCONT); 7471558Srgrimes wpid = -1; 7481558Srgrimes } 7491558Srgrimes } while (wpid != pid && !requested_transition); 7501558Srgrimes 7511558Srgrimes if (requested_transition) 7521558Srgrimes return (state_func_t) requested_transition; 7531558Srgrimes 7541558Srgrimes if (!WIFEXITED(status)) { 7558871Srgrimes if (WTERMSIG(status) == SIGKILL) { 7568871Srgrimes /* 7578871Srgrimes * reboot(8) killed shell? 7581558Srgrimes */ 7591558Srgrimes warning("single user shell terminated."); 7601558Srgrimes sleep(STALL_TIMEOUT); 7611558Srgrimes _exit(0); 7628871Srgrimes } else { 7631558Srgrimes warning("single user shell terminated, restarting"); 7641558Srgrimes return (state_func_t) single_user; 7651558Srgrimes } 7661558Srgrimes } 7671558Srgrimes 7681558Srgrimes runcom_mode = FASTBOOT; 7691558Srgrimes return (state_func_t) runcom; 7701558Srgrimes} 7711558Srgrimes 7721558Srgrimes/* 7731558Srgrimes * Run the system startup script. 7741558Srgrimes */ 7751558Srgrimesstate_func_t 77692838Simpruncom(void) 7771558Srgrimes{ 778166484Simp state_func_t next_transition; 779166484Simp 780166484Simp if ((next_transition = run_script(_PATH_RUNCOM)) != 0) 781166484Simp return next_transition; 782166484Simp 783166484Simp runcom_mode = AUTOBOOT; /* the default */ 784166484Simp /* NB: should send a message to the session logger to avoid blocking. */ 785166484Simp logwtmp("~", "reboot", ""); 786166484Simp return (state_func_t) read_ttys; 787166484Simp} 788166484Simp 789166484Simp/* 790166484Simp * Run a shell script. 791166484Simp * Returns 0 on success, otherwise the next transition to enter: 792166484Simp * - single_user if fork/execv/waitpid failed, or if the script 793166484Simp * terminated with a signal or exit code != 0. 794166484Simp * - death if a SIGTERM was delivered to init(8). 795166484Simp */ 796166484Simpstate_func_t 797166484Simprun_script(const char *script) 798166484Simp{ 7991558Srgrimes pid_t pid, wpid; 8001558Srgrimes int status; 8011558Srgrimes char *argv[4]; 802166484Simp const char *shell; 8031558Srgrimes struct sigaction sa; 8041558Srgrimes 805166484Simp shell = get_shell(); 806166484Simp 8071558Srgrimes if ((pid = fork()) == 0) { 8081558Srgrimes sigemptyset(&sa.sa_mask); 8091558Srgrimes sa.sa_flags = 0; 8101558Srgrimes sa.sa_handler = SIG_IGN; 811173785Sobrien sigaction(SIGTSTP, &sa, (struct sigaction *)0); 812173785Sobrien sigaction(SIGHUP, &sa, (struct sigaction *)0); 8131558Srgrimes 8141558Srgrimes setctty(_PATH_CONSOLE); 8151558Srgrimes 816140070Sdelphij char _sh[] = "sh"; 817140070Sdelphij char _autoboot[] = "autoboot"; 818140070Sdelphij 819140070Sdelphij argv[0] = _sh; 820166484Simp argv[1] = __DECONST(char *, script); 821140070Sdelphij argv[2] = runcom_mode == AUTOBOOT ? _autoboot : 0; 8221558Srgrimes argv[3] = 0; 8231558Srgrimes 8241558Srgrimes sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 8251558Srgrimes 82621865Sdavidn#ifdef LOGIN_CAP 82721865Sdavidn setprocresources(RESOURCE_RC); 82821865Sdavidn#endif 829166484Simp execv(shell, argv); 830166484Simp stall("can't exec %s for %s: %m", shell, script); 8311558Srgrimes _exit(1); /* force single user mode */ 8321558Srgrimes } 8331558Srgrimes 8341558Srgrimes if (pid == -1) { 835166484Simp emergency("can't fork for %s on %s: %m", shell, script); 8361558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 8371558Srgrimes continue; 8381558Srgrimes sleep(STALL_TIMEOUT); 8391558Srgrimes return (state_func_t) single_user; 8401558Srgrimes } 8411558Srgrimes 8421558Srgrimes /* 8431558Srgrimes * Copied from single_user(). This is a bit paranoid. 8441558Srgrimes */ 84585010Sdes requested_transition = 0; 8461558Srgrimes do { 8471558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 8481558Srgrimes collect_child(wpid); 8491558Srgrimes if (wpid == -1) { 85085010Sdes if (requested_transition == death) 85185010Sdes return (state_func_t) death; 8521558Srgrimes if (errno == EINTR) 8531558Srgrimes continue; 854166484Simp warning("wait for %s on %s failed: %m; going to " 855166484Simp "single user mode", shell, script); 8561558Srgrimes return (state_func_t) single_user; 8571558Srgrimes } 8581558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 8591558Srgrimes warning("init: %s on %s stopped, restarting\n", 860173785Sobrien shell, script); 8611558Srgrimes kill(pid, SIGCONT); 8621558Srgrimes wpid = -1; 8631558Srgrimes } 8641558Srgrimes } while (wpid != pid); 8651558Srgrimes 8661558Srgrimes if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 8671558Srgrimes requested_transition == catatonia) { 8681558Srgrimes /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 8691558Srgrimes sigset_t s; 8701558Srgrimes 8711558Srgrimes sigfillset(&s); 8721558Srgrimes for (;;) 8731558Srgrimes sigsuspend(&s); 8741558Srgrimes } 8751558Srgrimes 8761558Srgrimes if (!WIFEXITED(status)) { 877166484Simp warning("%s on %s terminated abnormally, going to single " 878166484Simp "user mode", shell, script); 8791558Srgrimes return (state_func_t) single_user; 8801558Srgrimes } 8811558Srgrimes 8821558Srgrimes if (WEXITSTATUS(status)) 8831558Srgrimes return (state_func_t) single_user; 8841558Srgrimes 885166484Simp return (state_func_t) 0; 8861558Srgrimes} 8871558Srgrimes 8881558Srgrimes/* 8891558Srgrimes * Open the session database. 8901558Srgrimes * 8911558Srgrimes * NB: We could pass in the size here; is it necessary? 8921558Srgrimes */ 8931558Srgrimesint 89492838Simpstart_session_db(void) 8951558Srgrimes{ 8961558Srgrimes if (session_db && (*session_db->close)(session_db)) 8971558Srgrimes emergency("session database close: %s", strerror(errno)); 8981558Srgrimes if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 8991558Srgrimes emergency("session database open: %s", strerror(errno)); 9001558Srgrimes return (1); 9011558Srgrimes } 9021558Srgrimes return (0); 9038871Srgrimes 9041558Srgrimes} 9051558Srgrimes 9061558Srgrimes/* 9071558Srgrimes * Add a new login session. 9081558Srgrimes */ 9091558Srgrimesvoid 91092838Simpadd_session(session_t *sp) 9111558Srgrimes{ 9121558Srgrimes DBT key; 9131558Srgrimes DBT data; 9141558Srgrimes 9151558Srgrimes key.data = &sp->se_process; 9161558Srgrimes key.size = sizeof sp->se_process; 9171558Srgrimes data.data = &sp; 9181558Srgrimes data.size = sizeof sp; 9191558Srgrimes 9201558Srgrimes if ((*session_db->put)(session_db, &key, &data, 0)) 9211558Srgrimes emergency("insert %d: %s", sp->se_process, strerror(errno)); 9221558Srgrimes} 9231558Srgrimes 9241558Srgrimes/* 9251558Srgrimes * Delete an old login session. 9261558Srgrimes */ 9271558Srgrimesvoid 92892838Simpdel_session(session_t *sp) 9291558Srgrimes{ 9301558Srgrimes DBT key; 9311558Srgrimes 9321558Srgrimes key.data = &sp->se_process; 9331558Srgrimes key.size = sizeof sp->se_process; 9341558Srgrimes 9351558Srgrimes if ((*session_db->del)(session_db, &key, 0)) 9361558Srgrimes emergency("delete %d: %s", sp->se_process, strerror(errno)); 9371558Srgrimes} 9381558Srgrimes 9391558Srgrimes/* 9401558Srgrimes * Look up a login session by pid. 9411558Srgrimes */ 9421558Srgrimessession_t * 9431558Srgrimesfind_session(pid_t pid) 9441558Srgrimes{ 9451558Srgrimes DBT key; 9461558Srgrimes DBT data; 9471558Srgrimes session_t *ret; 9481558Srgrimes 9491558Srgrimes key.data = &pid; 9501558Srgrimes key.size = sizeof pid; 9511558Srgrimes if ((*session_db->get)(session_db, &key, &data, 0) != 0) 9521558Srgrimes return 0; 9531558Srgrimes bcopy(data.data, (char *)&ret, sizeof(ret)); 9541558Srgrimes return ret; 9551558Srgrimes} 9561558Srgrimes 9571558Srgrimes/* 9581558Srgrimes * Construct an argument vector from a command line. 9591558Srgrimes */ 9601558Srgrimeschar ** 96192838Simpconstruct_argv(char *command) 9621558Srgrimes{ 96392806Sobrien int argc = 0; 96492806Sobrien char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 9651558Srgrimes * sizeof (char *)); 9661558Srgrimes 96749018Sru if ((argv[argc++] = strk(command)) == 0) { 96849018Sru free(argv); 96949018Sru return (NULL); 97049018Sru } 97127837Sdavidn while ((argv[argc++] = strk((char *) 0)) != NULL) 9721558Srgrimes continue; 9731558Srgrimes return argv; 9741558Srgrimes} 9751558Srgrimes 9761558Srgrimes/* 9771558Srgrimes * Deallocate a session descriptor. 9781558Srgrimes */ 9791558Srgrimesvoid 98092838Simpfree_session(session_t *sp) 9811558Srgrimes{ 9821558Srgrimes free(sp->se_device); 9831558Srgrimes if (sp->se_getty) { 9841558Srgrimes free(sp->se_getty); 9853594Sache free(sp->se_getty_argv_space); 9861558Srgrimes free(sp->se_getty_argv); 9871558Srgrimes } 9881558Srgrimes if (sp->se_window) { 9891558Srgrimes free(sp->se_window); 9903594Sache free(sp->se_window_argv_space); 9911558Srgrimes free(sp->se_window_argv); 9921558Srgrimes } 9933594Sache if (sp->se_type) 9943594Sache free(sp->se_type); 9951558Srgrimes free(sp); 9961558Srgrimes} 9971558Srgrimes 9981558Srgrimes/* 9991558Srgrimes * Allocate a new session descriptor. 100057344Salfred * Mark it SE_PRESENT. 10011558Srgrimes */ 10021558Srgrimessession_t * 100392838Simpnew_session(session_t *sprev, int session_index, struct ttyent *typ) 10041558Srgrimes{ 100592806Sobrien session_t *sp; 100627029Spst int fd; 10071558Srgrimes 10081558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 10091558Srgrimes typ->ty_name == 0 || 10101558Srgrimes typ->ty_getty == 0) 10111558Srgrimes return 0; 10121558Srgrimes 101327215Sache sp = (session_t *) calloc(1, sizeof (session_t)); 10141558Srgrimes 10151558Srgrimes sp->se_index = session_index; 101657344Salfred sp->se_flags |= SE_PRESENT; 10171558Srgrimes 10181558Srgrimes sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 1019173785Sobrien sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 10201558Srgrimes 102127029Spst /* 102227029Spst * Attempt to open the device, if we get "device not configured" 102327029Spst * then don't add the device to the session list. 102427029Spst */ 102527029Spst if ((fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0)) < 0) { 1026135868Simp if (errno == ENXIO) { 102727029Spst free_session(sp); 102827029Spst return (0); 102927029Spst } 103027029Spst } else 103127029Spst close(fd); 103227029Spst 10331558Srgrimes if (setupargv(sp, typ) == 0) { 10341558Srgrimes free_session(sp); 10351558Srgrimes return (0); 10361558Srgrimes } 10371558Srgrimes 10381558Srgrimes sp->se_next = 0; 10391558Srgrimes if (sprev == 0) { 10401558Srgrimes sessions = sp; 10411558Srgrimes sp->se_prev = 0; 10421558Srgrimes } else { 10431558Srgrimes sprev->se_next = sp; 10441558Srgrimes sp->se_prev = sprev; 10451558Srgrimes } 10461558Srgrimes 10471558Srgrimes return sp; 10481558Srgrimes} 10491558Srgrimes 10501558Srgrimes/* 10511558Srgrimes * Calculate getty and if useful window argv vectors. 10521558Srgrimes */ 10531558Srgrimesint 105492838Simpsetupargv(session_t *sp, struct ttyent *typ) 10551558Srgrimes{ 10561558Srgrimes 10571558Srgrimes if (sp->se_getty) { 10581558Srgrimes free(sp->se_getty); 10593594Sache free(sp->se_getty_argv_space); 10601558Srgrimes free(sp->se_getty_argv); 10611558Srgrimes } 10621558Srgrimes sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); 1063173785Sobrien sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); 10643594Sache sp->se_getty_argv_space = strdup(sp->se_getty); 10653594Sache sp->se_getty_argv = construct_argv(sp->se_getty_argv_space); 10661558Srgrimes if (sp->se_getty_argv == 0) { 10671558Srgrimes warning("can't parse getty for port %s", sp->se_device); 10681558Srgrimes free(sp->se_getty); 10693594Sache free(sp->se_getty_argv_space); 10703594Sache sp->se_getty = sp->se_getty_argv_space = 0; 10711558Srgrimes return (0); 10721558Srgrimes } 10733594Sache if (sp->se_window) { 107449018Sru free(sp->se_window); 10753594Sache free(sp->se_window_argv_space); 10763594Sache free(sp->se_window_argv); 10773594Sache } 10783594Sache sp->se_window = sp->se_window_argv_space = 0; 10793594Sache sp->se_window_argv = 0; 10801558Srgrimes if (typ->ty_window) { 10811558Srgrimes sp->se_window = strdup(typ->ty_window); 10823594Sache sp->se_window_argv_space = strdup(sp->se_window); 10833594Sache sp->se_window_argv = construct_argv(sp->se_window_argv_space); 10841558Srgrimes if (sp->se_window_argv == 0) { 10851558Srgrimes warning("can't parse window for port %s", 1086173785Sobrien sp->se_device); 10873594Sache free(sp->se_window_argv_space); 10881558Srgrimes free(sp->se_window); 10893594Sache sp->se_window = sp->se_window_argv_space = 0; 10901558Srgrimes return (0); 10911558Srgrimes } 10921558Srgrimes } 10933594Sache if (sp->se_type) 10943594Sache free(sp->se_type); 10953594Sache sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0; 10961558Srgrimes return (1); 10971558Srgrimes} 10981558Srgrimes 10991558Srgrimes/* 11001558Srgrimes * Walk the list of ttys and create sessions for each active line. 11011558Srgrimes */ 11021558Srgrimesstate_func_t 110392838Simpread_ttys(void) 11041558Srgrimes{ 11051558Srgrimes int session_index = 0; 110692806Sobrien session_t *sp, *snext; 110792806Sobrien struct ttyent *typ; 11081558Srgrimes 11091558Srgrimes /* 11101558Srgrimes * Destroy any previous session state. 11111558Srgrimes * There shouldn't be any, but just in case... 11121558Srgrimes */ 11131558Srgrimes for (sp = sessions; sp; sp = snext) { 11141558Srgrimes if (sp->se_process) 11151558Srgrimes clear_session_logs(sp); 11161558Srgrimes snext = sp->se_next; 11171558Srgrimes free_session(sp); 11181558Srgrimes } 11191558Srgrimes sessions = 0; 11201558Srgrimes if (start_session_db()) 11211558Srgrimes return (state_func_t) single_user; 11221558Srgrimes 11231558Srgrimes /* 11241558Srgrimes * Allocate a session entry for each active port. 11251558Srgrimes * Note that sp starts at 0. 11261558Srgrimes */ 112727837Sdavidn while ((typ = getttyent()) != NULL) 112827837Sdavidn if ((snext = new_session(sp, ++session_index, typ)) != NULL) 11291558Srgrimes sp = snext; 11301558Srgrimes 11311558Srgrimes endttyent(); 11321558Srgrimes 11331558Srgrimes return (state_func_t) multi_user; 11341558Srgrimes} 11351558Srgrimes 11361558Srgrimes/* 11371558Srgrimes * Start a window system running. 11381558Srgrimes */ 11391558Srgrimesvoid 114092838Simpstart_window_system(session_t *sp) 11411558Srgrimes{ 11421558Srgrimes pid_t pid; 11431558Srgrimes sigset_t mask; 11443594Sache char term[64], *env[2]; 1145159402Skib int status; 11461558Srgrimes 11471558Srgrimes if ((pid = fork()) == -1) { 11481558Srgrimes emergency("can't fork for window system on port %s: %m", 1149173785Sobrien sp->se_device); 11501558Srgrimes /* hope that getty fails and we can try again */ 11511558Srgrimes return; 11521558Srgrimes } 1153173785Sobrien if (pid) { 1154159402Skib waitpid(-1, &status, 0); 11551558Srgrimes return; 1156159402Skib } 11571558Srgrimes 1158159402Skib /* reparent window process to the init to not make a zombie on exit */ 1159159402Skib if ((pid = fork()) == -1) { 1160159402Skib emergency("can't fork for window system on port %s: %m", 1161173785Sobrien sp->se_device); 1162159402Skib _exit(1); 1163159402Skib } 1164159402Skib if (pid) 1165159402Skib _exit(0); 1166159402Skib 11671558Srgrimes sigemptyset(&mask); 11681558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 11691558Srgrimes 11701558Srgrimes if (setsid() < 0) 11711558Srgrimes emergency("setsid failed (window) %m"); 11721558Srgrimes 117321865Sdavidn#ifdef LOGIN_CAP 117421865Sdavidn setprocresources(RESOURCE_WINDOW); 117521865Sdavidn#endif 11763594Sache if (sp->se_type) { 11773594Sache /* Don't use malloc after fork */ 11783594Sache strcpy(term, "TERM="); 117922922Sdg strncat(term, sp->se_type, sizeof(term) - 6); 11803594Sache env[0] = term; 11813594Sache env[1] = 0; 11823594Sache } 11833594Sache else 11843594Sache env[0] = 0; 11853594Sache execve(sp->se_window_argv[0], sp->se_window_argv, env); 11861558Srgrimes stall("can't exec window system '%s' for port %s: %m", 11871558Srgrimes sp->se_window_argv[0], sp->se_device); 11881558Srgrimes _exit(1); 11891558Srgrimes} 11901558Srgrimes 11911558Srgrimes/* 11921558Srgrimes * Start a login session running. 11931558Srgrimes */ 11941558Srgrimespid_t 119592838Simpstart_getty(session_t *sp) 11961558Srgrimes{ 11971558Srgrimes pid_t pid; 11981558Srgrimes sigset_t mask; 11991558Srgrimes time_t current_time = time((time_t *) 0); 12009997Sache int too_quick = 0; 12013594Sache char term[64], *env[2]; 12021558Srgrimes 120310006Smpp if (current_time >= sp->se_started && 12049997Sache current_time - sp->se_started < GETTY_SPACING) { 12059997Sache if (++sp->se_nspace > GETTY_NSPACE) { 12069997Sache sp->se_nspace = 0; 12079997Sache too_quick = 1; 12089997Sache } 12099997Sache } else 12109997Sache sp->se_nspace = 0; 12119997Sache 12121558Srgrimes /* 12131558Srgrimes * fork(), not vfork() -- we can't afford to block. 12141558Srgrimes */ 12151558Srgrimes if ((pid = fork()) == -1) { 12161558Srgrimes emergency("can't fork for getty on port %s: %m", sp->se_device); 12171558Srgrimes return -1; 12181558Srgrimes } 12191558Srgrimes 12201558Srgrimes if (pid) 12211558Srgrimes return pid; 12221558Srgrimes 12239997Sache if (too_quick) { 12249997Sache warning("getty repeating too quickly on port %s, sleeping %d secs", 1225173785Sobrien sp->se_device, GETTY_SLEEP); 12261558Srgrimes sleep((unsigned) GETTY_SLEEP); 12271558Srgrimes } 12281558Srgrimes 12291558Srgrimes if (sp->se_window) { 12301558Srgrimes start_window_system(sp); 12311558Srgrimes sleep(WINDOW_WAIT); 12321558Srgrimes } 12331558Srgrimes 12341558Srgrimes sigemptyset(&mask); 12351558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 12361558Srgrimes 123721865Sdavidn#ifdef LOGIN_CAP 123821865Sdavidn setprocresources(RESOURCE_GETTY); 123921865Sdavidn#endif 12403594Sache if (sp->se_type) { 12413594Sache /* Don't use malloc after fork */ 12423594Sache strcpy(term, "TERM="); 124322922Sdg strncat(term, sp->se_type, sizeof(term) - 6); 12443594Sache env[0] = term; 12453594Sache env[1] = 0; 1246173785Sobrien } else 12473594Sache env[0] = 0; 12483594Sache execve(sp->se_getty_argv[0], sp->se_getty_argv, env); 12491558Srgrimes stall("can't exec getty '%s' for port %s: %m", 12501558Srgrimes sp->se_getty_argv[0], sp->se_device); 12511558Srgrimes _exit(1); 12521558Srgrimes} 12531558Srgrimes 12541558Srgrimes/* 12551558Srgrimes * Collect exit status for a child. 12561558Srgrimes * If an exiting login, start a new login running. 12571558Srgrimes */ 12581558Srgrimesvoid 12591558Srgrimescollect_child(pid_t pid) 12601558Srgrimes{ 126192806Sobrien session_t *sp, *sprev, *snext; 12621558Srgrimes 12631558Srgrimes if (! sessions) 12641558Srgrimes return; 12651558Srgrimes 12661558Srgrimes if (! (sp = find_session(pid))) 12671558Srgrimes return; 12681558Srgrimes 12691558Srgrimes clear_session_logs(sp); 12701558Srgrimes del_session(sp); 12711558Srgrimes sp->se_process = 0; 12721558Srgrimes 12731558Srgrimes if (sp->se_flags & SE_SHUTDOWN) { 127427837Sdavidn if ((sprev = sp->se_prev) != NULL) 12751558Srgrimes sprev->se_next = sp->se_next; 12761558Srgrimes else 12771558Srgrimes sessions = sp->se_next; 127827837Sdavidn if ((snext = sp->se_next) != NULL) 12791558Srgrimes snext->se_prev = sp->se_prev; 12801558Srgrimes free_session(sp); 12811558Srgrimes return; 12821558Srgrimes } 12831558Srgrimes 12841558Srgrimes if ((pid = start_getty(sp)) == -1) { 12851558Srgrimes /* serious trouble */ 12861558Srgrimes requested_transition = clean_ttys; 12871558Srgrimes return; 12881558Srgrimes } 12891558Srgrimes 12901558Srgrimes sp->se_process = pid; 12911558Srgrimes sp->se_started = time((time_t *) 0); 12921558Srgrimes add_session(sp); 12931558Srgrimes} 12941558Srgrimes 12951558Srgrimes/* 12961558Srgrimes * Catch a signal and request a state transition. 12971558Srgrimes */ 12981558Srgrimesvoid 129992838Simptransition_handler(int sig) 13001558Srgrimes{ 13011558Srgrimes 13021558Srgrimes switch (sig) { 13031558Srgrimes case SIGHUP: 13041558Srgrimes requested_transition = clean_ttys; 13051558Srgrimes break; 130647962Sru case SIGUSR2: 130747962Sru howto = RB_POWEROFF; 130847962Sru case SIGUSR1: 130947962Sru howto |= RB_HALT; 13102323Snate case SIGINT: 13112327Sjkh Reboot = TRUE; 13121558Srgrimes case SIGTERM: 13131558Srgrimes requested_transition = death; 13141558Srgrimes break; 13151558Srgrimes case SIGTSTP: 13161558Srgrimes requested_transition = catatonia; 13171558Srgrimes break; 13181558Srgrimes default: 13191558Srgrimes requested_transition = 0; 13201558Srgrimes break; 13211558Srgrimes } 13221558Srgrimes} 13231558Srgrimes 13241558Srgrimes/* 13251558Srgrimes * Take the system multiuser. 13261558Srgrimes */ 13271558Srgrimesstate_func_t 132892838Simpmulti_user(void) 13291558Srgrimes{ 13301558Srgrimes pid_t pid; 133192806Sobrien session_t *sp; 13321558Srgrimes 13331558Srgrimes requested_transition = 0; 13341558Srgrimes 13351558Srgrimes /* 13361558Srgrimes * If the administrator has not set the security level to -1 13371558Srgrimes * to indicate that the kernel should not run multiuser in secure 13388871Srgrimes * mode, and the run script has not set a higher level of security 13391558Srgrimes * than level 1, then put the kernel into secure mode. 13401558Srgrimes */ 13411558Srgrimes if (getsecuritylevel() == 0) 13421558Srgrimes setsecuritylevel(1); 13431558Srgrimes 13441558Srgrimes for (sp = sessions; sp; sp = sp->se_next) { 13451558Srgrimes if (sp->se_process) 13461558Srgrimes continue; 13471558Srgrimes if ((pid = start_getty(sp)) == -1) { 13481558Srgrimes /* serious trouble */ 13491558Srgrimes requested_transition = clean_ttys; 13501558Srgrimes break; 13511558Srgrimes } 13521558Srgrimes sp->se_process = pid; 13531558Srgrimes sp->se_started = time((time_t *) 0); 13541558Srgrimes add_session(sp); 13551558Srgrimes } 13561558Srgrimes 13571558Srgrimes while (!requested_transition) 13581558Srgrimes if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 13591558Srgrimes collect_child(pid); 13601558Srgrimes 13611558Srgrimes return (state_func_t) requested_transition; 13621558Srgrimes} 13631558Srgrimes 13641558Srgrimes/* 136557344Salfred * This is an (n*2)+(n^2) algorithm. We hope it isn't run often... 13661558Srgrimes */ 13671558Srgrimesstate_func_t 136892838Simpclean_ttys(void) 13691558Srgrimes{ 137092806Sobrien session_t *sp, *sprev; 137192806Sobrien struct ttyent *typ; 137292806Sobrien int session_index = 0; 137392806Sobrien int devlen; 13743594Sache char *old_getty, *old_window, *old_type; 13751558Srgrimes 1376173787Sobrien /* 1377173787Sobrien * mark all sessions for death, (!SE_PRESENT) 137857344Salfred * as we find or create new ones they'll be marked as keepers, 137957344Salfred * we'll later nuke all the ones not found in /etc/ttys 138057344Salfred */ 138157344Salfred for (sp = sessions; sp != NULL; sp = sp->se_next) 138257344Salfred sp->se_flags &= ~SE_PRESENT; 138357344Salfred 13841558Srgrimes devlen = sizeof(_PATH_DEV) - 1; 138527837Sdavidn while ((typ = getttyent()) != NULL) { 13861558Srgrimes ++session_index; 13871558Srgrimes 13881558Srgrimes for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 13891558Srgrimes if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 13901558Srgrimes break; 13911558Srgrimes 13921558Srgrimes if (sp) { 139357344Salfred /* we want this one to live */ 139457344Salfred sp->se_flags |= SE_PRESENT; 13951558Srgrimes if (sp->se_index != session_index) { 13961558Srgrimes warning("port %s changed utmp index from %d to %d", 13971558Srgrimes sp->se_device, sp->se_index, 13981558Srgrimes session_index); 13991558Srgrimes sp->se_index = session_index; 14001558Srgrimes } 14011558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 14021558Srgrimes typ->ty_getty == 0) { 14031558Srgrimes sp->se_flags |= SE_SHUTDOWN; 14041558Srgrimes kill(sp->se_process, SIGHUP); 14051558Srgrimes continue; 14061558Srgrimes } 14071558Srgrimes sp->se_flags &= ~SE_SHUTDOWN; 14083594Sache old_getty = sp->se_getty ? strdup(sp->se_getty) : 0; 14093594Sache old_window = sp->se_window ? strdup(sp->se_window) : 0; 14103594Sache old_type = sp->se_type ? strdup(sp->se_type) : 0; 14111558Srgrimes if (setupargv(sp, typ) == 0) { 14121558Srgrimes warning("can't parse getty for port %s", 14131558Srgrimes sp->se_device); 14141558Srgrimes sp->se_flags |= SE_SHUTDOWN; 14151558Srgrimes kill(sp->se_process, SIGHUP); 14161558Srgrimes } 14173594Sache else if ( !old_getty 141827837Sdavidn || (!old_type && sp->se_type) 141927837Sdavidn || (old_type && !sp->se_type) 142027837Sdavidn || (!old_window && sp->se_window) 142127837Sdavidn || (old_window && !sp->se_window) 142227837Sdavidn || (strcmp(old_getty, sp->se_getty) != 0) 142327837Sdavidn || (old_window && strcmp(old_window, sp->se_window) != 0) 142427837Sdavidn || (old_type && strcmp(old_type, sp->se_type) != 0) 14253594Sache ) { 14263594Sache /* Don't set SE_SHUTDOWN here */ 14273594Sache sp->se_nspace = 0; 14283594Sache sp->se_started = 0; 14293594Sache kill(sp->se_process, SIGHUP); 14303594Sache } 14313594Sache if (old_getty) 14323594Sache free(old_getty); 143378484Smikeh if (old_window) 14343594Sache free(old_window); 14353594Sache if (old_type) 14363594Sache free(old_type); 14371558Srgrimes continue; 14381558Srgrimes } 14391558Srgrimes 14401558Srgrimes new_session(sprev, session_index, typ); 14411558Srgrimes } 14421558Srgrimes 14431558Srgrimes endttyent(); 14441558Srgrimes 144557344Salfred /* 144657344Salfred * sweep through and kill all deleted sessions 144757344Salfred * ones who's /etc/ttys line was deleted (SE_PRESENT unset) 144857344Salfred */ 144957344Salfred for (sp = sessions; sp != NULL; sp = sp->se_next) { 145057344Salfred if ((sp->se_flags & SE_PRESENT) == 0) { 145157344Salfred sp->se_flags |= SE_SHUTDOWN; 145257344Salfred kill(sp->se_process, SIGHUP); 145357344Salfred } 145457344Salfred } 145557344Salfred 14561558Srgrimes return (state_func_t) multi_user; 14571558Srgrimes} 14581558Srgrimes 14591558Srgrimes/* 14601558Srgrimes * Block further logins. 14611558Srgrimes */ 14621558Srgrimesstate_func_t 146392838Simpcatatonia(void) 14641558Srgrimes{ 146592806Sobrien session_t *sp; 14661558Srgrimes 14671558Srgrimes for (sp = sessions; sp; sp = sp->se_next) 14681558Srgrimes sp->se_flags |= SE_SHUTDOWN; 14691558Srgrimes 14701558Srgrimes return (state_func_t) multi_user; 14711558Srgrimes} 14721558Srgrimes 14731558Srgrimes/* 14741558Srgrimes * Note SIGALRM. 14751558Srgrimes */ 14761558Srgrimesvoid 147792838Simpalrm_handler(int sig) 14781558Srgrimes{ 1479173785Sobrien 148027837Sdavidn (void)sig; 14811558Srgrimes clang = 1; 14821558Srgrimes} 14831558Srgrimes 14841558Srgrimes/* 14851558Srgrimes * Bring the system down to single user. 14861558Srgrimes */ 14871558Srgrimesstate_func_t 148892838Simpdeath(void) 14891558Srgrimes{ 149092806Sobrien session_t *sp; 149192806Sobrien int i; 14921558Srgrimes pid_t pid; 149327197Sache static const int death_sigs[2] = { SIGTERM, SIGKILL }; 14941558Srgrimes 149527275Sache /* NB: should send a message to the session logger to avoid blocking. */ 149627275Sache logwtmp("~", "shutdown", ""); 149727275Sache 149827215Sache for (sp = sessions; sp; sp = sp->se_next) { 14991558Srgrimes sp->se_flags |= SE_SHUTDOWN; 150027941Sache kill(sp->se_process, SIGHUP); 150127215Sache } 15021558Srgrimes 150327837Sdavidn /* Try to run the rc.shutdown script within a period of time */ 1504173785Sobrien runshutdown(); 1505173785Sobrien 150627197Sache for (i = 0; i < 2; ++i) { 15071558Srgrimes if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 15081558Srgrimes return (state_func_t) single_user; 15091558Srgrimes 15101558Srgrimes clang = 0; 15111558Srgrimes alarm(DEATH_WATCH); 15121558Srgrimes do 15131558Srgrimes if ((pid = waitpid(-1, (int *)0, 0)) != -1) 15141558Srgrimes collect_child(pid); 15151558Srgrimes while (clang == 0 && errno != ECHILD); 15161558Srgrimes 15171558Srgrimes if (errno == ECHILD) 15181558Srgrimes return (state_func_t) single_user; 15191558Srgrimes } 15201558Srgrimes 15211558Srgrimes warning("some processes would not die; ps axl advised"); 15221558Srgrimes 15231558Srgrimes return (state_func_t) single_user; 15241558Srgrimes} 152527837Sdavidn 152627837Sdavidn/* 152727837Sdavidn * Run the system shutdown script. 152827837Sdavidn * 152927837Sdavidn * Exit codes: XXX I should document more 153027837Sdavidn * -2 shutdown script terminated abnormally 153127837Sdavidn * -1 fatal error - can't run script 153227837Sdavidn * 0 good. 153327837Sdavidn * >0 some error (exit code) 153427837Sdavidn */ 153527837Sdavidnint 153692838Simprunshutdown(void) 153727837Sdavidn{ 153827837Sdavidn pid_t pid, wpid; 153927837Sdavidn int status; 154027837Sdavidn int shutdowntimeout; 154127837Sdavidn size_t len; 154253550Sdillon char *argv[4]; 1543166484Simp const char *shell; 154427837Sdavidn struct sigaction sa; 154528344Sdavidn struct stat sb; 154627837Sdavidn 154728344Sdavidn /* 154828344Sdavidn * rc.shutdown is optional, so to prevent any unnecessary 154928344Sdavidn * complaints from the shell we simply don't run it if the 155028344Sdavidn * file does not exist. If the stat() here fails for other 155128344Sdavidn * reasons, we'll let the shell complain. 155228344Sdavidn */ 155328344Sdavidn if (stat(_PATH_RUNDOWN, &sb) == -1 && errno == ENOENT) 155428344Sdavidn return 0; 155528344Sdavidn 1556166484Simp shell = get_shell(); 1557166484Simp 155827837Sdavidn if ((pid = fork()) == 0) { 155927837Sdavidn int fd; 156027837Sdavidn 156127941Sache /* Assume that init already grab console as ctty before */ 156227941Sache 156327837Sdavidn sigemptyset(&sa.sa_mask); 156427837Sdavidn sa.sa_flags = 0; 156527837Sdavidn sa.sa_handler = SIG_IGN; 1566173785Sobrien sigaction(SIGTSTP, &sa, (struct sigaction *)0); 1567173785Sobrien sigaction(SIGHUP, &sa, (struct sigaction *)0); 156827837Sdavidn 156927837Sdavidn if ((fd = open(_PATH_CONSOLE, O_RDWR)) == -1) 1570173785Sobrien warning("can't open %s: %m", _PATH_CONSOLE); 157127837Sdavidn else { 1572173785Sobrien dup2(fd, 0); 1573173785Sobrien dup2(fd, 1); 1574173785Sobrien dup2(fd, 2); 1575173785Sobrien if (fd > 2) 1576173785Sobrien close(fd); 157727837Sdavidn } 157827837Sdavidn 157927837Sdavidn /* 158027837Sdavidn * Run the shutdown script. 158127837Sdavidn */ 1582140070Sdelphij 1583140070Sdelphij char _sh[] = "sh"; 1584140070Sdelphij char _reboot[] = "reboot"; 1585140070Sdelphij char _single[] = "single"; 1586140070Sdelphij char _path_rundown[] = _PATH_RUNDOWN; 1587140070Sdelphij 1588140070Sdelphij argv[0] = _sh; 1589140070Sdelphij argv[1] = _path_rundown; 1590140070Sdelphij argv[2] = Reboot ? _reboot : _single; 159153550Sdillon argv[3] = 0; 159227837Sdavidn 159327837Sdavidn sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 159427837Sdavidn 159527941Sache#ifdef LOGIN_CAP 159627941Sache setprocresources(RESOURCE_RC); 159727941Sache#endif 1598166484Simp execv(shell, argv); 1599166484Simp warning("can't exec %s for %s: %m", shell, _PATH_RUNDOWN); 160027837Sdavidn _exit(1); /* force single user mode */ 160127837Sdavidn } 160227837Sdavidn 160327837Sdavidn if (pid == -1) { 1604166484Simp emergency("can't fork for %s on %s: %m", shell, _PATH_RUNDOWN); 160527837Sdavidn while (waitpid(-1, (int *) 0, WNOHANG) > 0) 160627837Sdavidn continue; 160727837Sdavidn sleep(STALL_TIMEOUT); 160827837Sdavidn return -1; 160927837Sdavidn } 161027837Sdavidn 161127837Sdavidn len = sizeof(shutdowntimeout); 1612173785Sobrien if (sysctlbyname("kern.init_shutdown_timeout", &shutdowntimeout, &len, 1613173785Sobrien NULL, 0) == -1 || shutdowntimeout < 2) 1614173785Sobrien shutdowntimeout = DEATH_SCRIPT; 161527837Sdavidn alarm(shutdowntimeout); 161627837Sdavidn clang = 0; 161727837Sdavidn /* 161827837Sdavidn * Copied from single_user(). This is a bit paranoid. 161927837Sdavidn * Use the same ALRM handler. 162027837Sdavidn */ 162127837Sdavidn do { 162227837Sdavidn if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 162327837Sdavidn collect_child(wpid); 162427837Sdavidn if (clang == 1) { 162527837Sdavidn /* we were waiting for the sub-shell */ 162627837Sdavidn kill(wpid, SIGTERM); 1627166484Simp warning("timeout expired for %s on %s: %m; going to " 1628166484Simp "single user mode", shell, _PATH_RUNDOWN); 162927837Sdavidn return -1; 163027837Sdavidn } 163127837Sdavidn if (wpid == -1) { 163227837Sdavidn if (errno == EINTR) 163327837Sdavidn continue; 1634166484Simp warning("wait for %s on %s failed: %m; going to " 1635166484Simp "single user mode", shell, _PATH_RUNDOWN); 163627837Sdavidn return -1; 163727837Sdavidn } 163827837Sdavidn if (wpid == pid && WIFSTOPPED(status)) { 163927837Sdavidn warning("init: %s on %s stopped, restarting\n", 1640166484Simp shell, _PATH_RUNDOWN); 164127837Sdavidn kill(pid, SIGCONT); 164227837Sdavidn wpid = -1; 164327837Sdavidn } 164427837Sdavidn } while (wpid != pid && !clang); 164527837Sdavidn 164627837Sdavidn /* Turn off the alarm */ 164727837Sdavidn alarm(0); 164827837Sdavidn 164927837Sdavidn if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 165027837Sdavidn requested_transition == catatonia) { 165127837Sdavidn /* 165227837Sdavidn * /etc/rc.shutdown executed /sbin/reboot; 165327837Sdavidn * wait for the end quietly 165427837Sdavidn */ 165527837Sdavidn sigset_t s; 165627837Sdavidn 165727837Sdavidn sigfillset(&s); 165827837Sdavidn for (;;) 165927837Sdavidn sigsuspend(&s); 166027837Sdavidn } 166127837Sdavidn 166227837Sdavidn if (!WIFEXITED(status)) { 1663166484Simp warning("%s on %s terminated abnormally, going to " 1664166484Simp "single user mode", shell, _PATH_RUNDOWN); 166527837Sdavidn return -2; 166627837Sdavidn } 166727837Sdavidn 166827837Sdavidn if ((status = WEXITSTATUS(status)) != 0) 166927837Sdavidn warning("%s returned status %d", _PATH_RUNDOWN, status); 167027837Sdavidn 167127837Sdavidn return status; 167227837Sdavidn} 167327837Sdavidn 1674140070Sdelphijstatic char * 1675140070Sdelphijstrk(char *p) 16765478Sache{ 1677173785Sobrien static char *t; 1678173785Sobrien char *q; 1679173785Sobrien int c; 16805478Sache 1681173785Sobrien if (p) 1682173785Sobrien t = p; 1683173785Sobrien if (!t) 1684173785Sobrien return 0; 16855478Sache 1686173785Sobrien c = *t; 1687173785Sobrien while (c == ' ' || c == '\t' ) 1688173785Sobrien c = *++t; 1689173785Sobrien if (!c) { 1690173785Sobrien t = 0; 1691173785Sobrien return 0; 1692173785Sobrien } 16935478Sache q = t; 1694173785Sobrien if (c == '\'') { 1695173785Sobrien c = *++t; 1696173785Sobrien q = t; 1697173785Sobrien while (c && c != '\'') 1698173785Sobrien c = *++t; 1699173785Sobrien if (!c) /* unterminated string */ 1700173785Sobrien q = t = 0; 1701173785Sobrien else 1702173785Sobrien *t++ = 0; 1703173785Sobrien } else { 1704173785Sobrien while (c && c != ' ' && c != '\t' ) 1705173785Sobrien c = *++t; 1706173785Sobrien *t++ = 0; 1707173785Sobrien if (!c) 1708173785Sobrien t = 0; 1709173785Sobrien } 1710173785Sobrien return q; 17115478Sache} 171221865Sdavidn 171321865Sdavidn#ifdef LOGIN_CAP 171421941Sdavidnvoid 171592838Simpsetprocresources(const char *cname) 171621865Sdavidn{ 171721941Sdavidn login_cap_t *lc; 171827176Sache if ((lc = login_getclassbyname(cname, NULL)) != NULL) { 1719173785Sobrien setusercontext(lc, (struct passwd*)NULL, 0, 1720173785Sobrien LOGIN_SETPRIORITY | LOGIN_SETRESOURCES); 172121941Sdavidn login_close(lc); 172221941Sdavidn } 172321865Sdavidn} 172421865Sdavidn#endif 1725