init.c revision 11910
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 * 3. All advertising materials mentioning features or use of this software 171558Srgrimes * must display the following acknowledgement: 181558Srgrimes * This product includes software developed by the University of 191558Srgrimes * California, Berkeley and its contributors. 201558Srgrimes * 4. Neither the name of the University nor the names of its contributors 211558Srgrimes * may be used to endorse or promote products derived from this software 221558Srgrimes * without specific prior written permission. 231558Srgrimes * 241558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341558Srgrimes * SUCH DAMAGE. 351558Srgrimes */ 361558Srgrimes 371558Srgrimes#ifndef lint 381558Srgrimesstatic char copyright[] = 391558Srgrimes"@(#) Copyright (c) 1991, 1993\n\ 401558Srgrimes The Regents of the University of California. All rights reserved.\n"; 411558Srgrimes#endif /* not lint */ 421558Srgrimes 431558Srgrimes#ifndef lint 441558Srgrimesstatic char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; 451558Srgrimes#endif /* not lint */ 461558Srgrimes 471558Srgrimes#include <sys/param.h> 481558Srgrimes#include <sys/sysctl.h> 491558Srgrimes#include <sys/wait.h> 501558Srgrimes 511558Srgrimes#include <db.h> 521558Srgrimes#include <errno.h> 531558Srgrimes#include <fcntl.h> 541558Srgrimes#include <signal.h> 551558Srgrimes#include <stdio.h> 561558Srgrimes#include <stdlib.h> 571558Srgrimes#include <string.h> 581558Srgrimes#include <syslog.h> 591558Srgrimes#include <time.h> 601558Srgrimes#include <ttyent.h> 611558Srgrimes#include <unistd.h> 622323Snate#include <sys/reboot.h> 631558Srgrimes 641558Srgrimes#ifdef __STDC__ 651558Srgrimes#include <stdarg.h> 661558Srgrimes#else 671558Srgrimes#include <varargs.h> 681558Srgrimes#endif 691558Srgrimes 701558Srgrimes#ifdef SECURE 711558Srgrimes#include <pwd.h> 721558Srgrimes#endif 731558Srgrimes 741558Srgrimes#include "pathnames.h" 751558Srgrimes 761558Srgrimes/* 771558Srgrimes * Until the mythical util.h arrives... 781558Srgrimes */ 791558Srgrimesextern int login_tty __P((int)); 801558Srgrimesextern int logout __P((const char *)); 811558Srgrimesextern void logwtmp __P((const char *, const char *, const char *)); 821558Srgrimes 831558Srgrimes/* 841558Srgrimes * Sleep times; used to prevent thrashing. 851558Srgrimes */ 861558Srgrimes#define GETTY_SPACING 5 /* N secs minimum getty spacing */ 871558Srgrimes#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ 883594Sache#define GETTY_NSPACE 3 /* max. spacing count to bring reaction */ 891558Srgrimes#define WINDOW_WAIT 3 /* wait N secs after starting window */ 901558Srgrimes#define STALL_TIMEOUT 30 /* wait N secs after warning */ 911558Srgrimes#define DEATH_WATCH 10 /* wait N secs for procs to die */ 921558Srgrimes 931558Srgrimesvoid handle __P((sig_t, ...)); 941558Srgrimesvoid delset __P((sigset_t *, ...)); 951558Srgrimes 961558Srgrimesvoid stall __P((char *, ...)); 971558Srgrimesvoid warning __P((char *, ...)); 981558Srgrimesvoid emergency __P((char *, ...)); 991558Srgrimesvoid disaster __P((int)); 1001558Srgrimesvoid badsys __P((int)); 1011558Srgrimes 1021558Srgrimes/* 1031558Srgrimes * We really need a recursive typedef... 1041558Srgrimes * The following at least guarantees that the return type of (*state_t)() 1051558Srgrimes * is sufficiently wide to hold a function pointer. 1061558Srgrimes */ 1071558Srgrimestypedef long (*state_func_t) __P((void)); 1081558Srgrimestypedef state_func_t (*state_t) __P((void)); 1091558Srgrimes 1101558Srgrimesstate_func_t single_user __P((void)); 1111558Srgrimesstate_func_t runcom __P((void)); 1121558Srgrimesstate_func_t read_ttys __P((void)); 1131558Srgrimesstate_func_t multi_user __P((void)); 1141558Srgrimesstate_func_t clean_ttys __P((void)); 1151558Srgrimesstate_func_t catatonia __P((void)); 1161558Srgrimesstate_func_t death __P((void)); 1171558Srgrimes 1181558Srgrimesenum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 11911910Sphk#define FALSE 0 12011910Sphk#define TRUE 1 12111910Sphk 1222327Sjkhint Reboot = FALSE; 1231558Srgrimes 1241558Srgrimesvoid transition __P((state_t)); 1251558Srgrimesstate_t requested_transition = runcom; 1261558Srgrimes 1271558Srgrimesvoid setctty __P((char *)); 1281558Srgrimes 1291558Srgrimestypedef struct init_session { 1301558Srgrimes int se_index; /* index of entry in ttys file */ 1311558Srgrimes pid_t se_process; /* controlling process */ 1321558Srgrimes time_t se_started; /* used to avoid thrashing */ 1331558Srgrimes int se_flags; /* status of session */ 1341558Srgrimes#define SE_SHUTDOWN 0x1 /* session won't be restarted */ 1353594Sache int se_nspace; /* spacing count */ 1361558Srgrimes char *se_device; /* filename of port */ 1371558Srgrimes char *se_getty; /* what to run on that port */ 1383594Sache char *se_getty_argv_space; /* pre-parsed argument array space */ 1391558Srgrimes char **se_getty_argv; /* pre-parsed argument array */ 1401558Srgrimes char *se_window; /* window system (started only once) */ 1413594Sache char *se_window_argv_space; /* pre-parsed argument array space */ 1421558Srgrimes char **se_window_argv; /* pre-parsed argument array */ 1433594Sache char *se_type; /* default terminal type */ 1441558Srgrimes struct init_session *se_prev; 1451558Srgrimes struct init_session *se_next; 1461558Srgrimes} session_t; 1471558Srgrimes 1481558Srgrimesvoid free_session __P((session_t *)); 1491558Srgrimessession_t *new_session __P((session_t *, int, struct ttyent *)); 1501558Srgrimessession_t *sessions; 1511558Srgrimes 1521558Srgrimeschar **construct_argv __P((char *)); 1531558Srgrimesvoid start_window_system __P((session_t *)); 1541558Srgrimesvoid collect_child __P((pid_t)); 1551558Srgrimespid_t start_getty __P((session_t *)); 1561558Srgrimesvoid transition_handler __P((int)); 1571558Srgrimesvoid alrm_handler __P((int)); 1581558Srgrimesvoid setsecuritylevel __P((int)); 1591558Srgrimesint getsecuritylevel __P((void)); 1601558Srgrimesint setupargv __P((session_t *, struct ttyent *)); 1611558Srgrimesint clang; 1621558Srgrimes 1631558Srgrimesvoid clear_session_logs __P((session_t *)); 1641558Srgrimes 1651558Srgrimesint start_session_db __P((void)); 1661558Srgrimesvoid add_session __P((session_t *)); 1671558Srgrimesvoid del_session __P((session_t *)); 1681558Srgrimessession_t *find_session __P((pid_t)); 1691558SrgrimesDB *session_db; 1701558Srgrimes 1711558Srgrimes/* 1721558Srgrimes * The mother of all processes. 1731558Srgrimes */ 1741558Srgrimesint 1751558Srgrimesmain(argc, argv) 1761558Srgrimes int argc; 1771558Srgrimes char **argv; 1781558Srgrimes{ 1791558Srgrimes int c; 1801558Srgrimes struct sigaction sa; 1811558Srgrimes sigset_t mask; 1821558Srgrimes 1831558Srgrimes 1841558Srgrimes /* Dispose of random users. */ 1851558Srgrimes if (getuid() != 0) { 1861558Srgrimes (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); 1871558Srgrimes exit (1); 1881558Srgrimes } 1891558Srgrimes 1901558Srgrimes /* System V users like to reexec init. */ 1911558Srgrimes if (getpid() != 1) { 1921558Srgrimes (void)fprintf(stderr, "init: already running\n"); 1931558Srgrimes exit (1); 1941558Srgrimes } 1951558Srgrimes 1961558Srgrimes /* 1971558Srgrimes * Note that this does NOT open a file... 1981558Srgrimes * Does 'init' deserve its own facility number? 1991558Srgrimes */ 2001558Srgrimes openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 2011558Srgrimes 2021558Srgrimes /* 2031558Srgrimes * Create an initial session. 2041558Srgrimes */ 2051558Srgrimes if (setsid() < 0) 2061558Srgrimes warning("initial setsid() failed: %m"); 2071558Srgrimes 2081558Srgrimes /* 2091558Srgrimes * Establish an initial user so that programs running 2101558Srgrimes * single user do not freak out and die (like passwd). 2111558Srgrimes */ 2121558Srgrimes if (setlogin("root") < 0) 2131558Srgrimes warning("setlogin() failed: %m"); 2141558Srgrimes 2151558Srgrimes /* 2161558Srgrimes * This code assumes that we always get arguments through flags, 2171558Srgrimes * never through bits set in some random machine register. 2181558Srgrimes */ 2191558Srgrimes while ((c = getopt(argc, argv, "sf")) != -1) 2201558Srgrimes switch (c) { 2211558Srgrimes case 's': 2221558Srgrimes requested_transition = single_user; 2231558Srgrimes break; 2241558Srgrimes case 'f': 2251558Srgrimes runcom_mode = FASTBOOT; 2261558Srgrimes break; 2271558Srgrimes default: 2281558Srgrimes warning("unrecognized flag '-%c'", c); 2291558Srgrimes break; 2301558Srgrimes } 2311558Srgrimes 2321558Srgrimes if (optind != argc) 2331558Srgrimes warning("ignoring excess arguments"); 2341558Srgrimes 2351558Srgrimes /* 2361558Srgrimes * We catch or block signals rather than ignore them, 2371558Srgrimes * so that they get reset on exec. 2381558Srgrimes */ 2391558Srgrimes handle(badsys, SIGSYS, 0); 2401558Srgrimes handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 2411558Srgrimes SIGBUS, SIGXCPU, SIGXFSZ, 0); 2422323Snate handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, 0); 2431558Srgrimes handle(alrm_handler, SIGALRM, 0); 2441558Srgrimes sigfillset(&mask); 2451558Srgrimes delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 2462323Snate SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM, 0); 2471558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 2481558Srgrimes sigemptyset(&sa.sa_mask); 2491558Srgrimes sa.sa_flags = 0; 2501558Srgrimes sa.sa_handler = SIG_IGN; 2511558Srgrimes (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); 2521558Srgrimes (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); 2531558Srgrimes 2541558Srgrimes /* 2551558Srgrimes * Paranoia. 2561558Srgrimes */ 2571558Srgrimes close(0); 2581558Srgrimes close(1); 2591558Srgrimes close(2); 2601558Srgrimes 2611558Srgrimes /* 2621558Srgrimes * Start the state machine. 2631558Srgrimes */ 2641558Srgrimes transition(requested_transition); 2651558Srgrimes 2661558Srgrimes /* 2671558Srgrimes * Should never reach here. 2681558Srgrimes */ 2691558Srgrimes return 1; 2701558Srgrimes} 2711558Srgrimes 2721558Srgrimes/* 2731558Srgrimes * Associate a function with a signal handler. 2741558Srgrimes */ 2751558Srgrimesvoid 2761558Srgrimes#ifdef __STDC__ 2771558Srgrimeshandle(sig_t handler, ...) 2781558Srgrimes#else 2791558Srgrimeshandle(va_alist) 2801558Srgrimes va_dcl 2811558Srgrimes#endif 2821558Srgrimes{ 2831558Srgrimes int sig; 2841558Srgrimes struct sigaction sa; 2851558Srgrimes int mask_everything; 2861558Srgrimes va_list ap; 2871558Srgrimes#ifndef __STDC__ 2881558Srgrimes sig_t handler; 2891558Srgrimes 2901558Srgrimes va_start(ap); 2911558Srgrimes handler = va_arg(ap, sig_t); 2921558Srgrimes#else 2931558Srgrimes va_start(ap, handler); 2941558Srgrimes#endif 2951558Srgrimes 2961558Srgrimes sa.sa_handler = handler; 2971558Srgrimes sigfillset(&mask_everything); 2981558Srgrimes 2991558Srgrimes while (sig = va_arg(ap, int)) { 3001558Srgrimes sa.sa_mask = mask_everything; 3011558Srgrimes /* XXX SA_RESTART? */ 3021558Srgrimes sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 3031558Srgrimes sigaction(sig, &sa, (struct sigaction *) 0); 3041558Srgrimes } 3051558Srgrimes va_end(ap); 3061558Srgrimes} 3071558Srgrimes 3081558Srgrimes/* 3091558Srgrimes * Delete a set of signals from a mask. 3101558Srgrimes */ 3111558Srgrimesvoid 3121558Srgrimes#ifdef __STDC__ 3131558Srgrimesdelset(sigset_t *maskp, ...) 3141558Srgrimes#else 3151558Srgrimesdelset(va_alist) 3161558Srgrimes va_dcl 3171558Srgrimes#endif 3181558Srgrimes{ 3191558Srgrimes int sig; 3201558Srgrimes va_list ap; 3211558Srgrimes#ifndef __STDC__ 3221558Srgrimes sigset_t *maskp; 3231558Srgrimes 3241558Srgrimes va_start(ap); 3251558Srgrimes maskp = va_arg(ap, sigset_t *); 3261558Srgrimes#else 3271558Srgrimes va_start(ap, maskp); 3281558Srgrimes#endif 3291558Srgrimes 3301558Srgrimes while (sig = va_arg(ap, int)) 3311558Srgrimes sigdelset(maskp, sig); 3321558Srgrimes va_end(ap); 3331558Srgrimes} 3341558Srgrimes 3351558Srgrimes/* 3361558Srgrimes * Log a message and sleep for a while (to give someone an opportunity 3371558Srgrimes * to read it and to save log or hardcopy output if the problem is chronic). 3381558Srgrimes * NB: should send a message to the session logger to avoid blocking. 3391558Srgrimes */ 3401558Srgrimesvoid 3411558Srgrimes#ifdef __STDC__ 3421558Srgrimesstall(char *message, ...) 3431558Srgrimes#else 3441558Srgrimesstall(va_alist) 3451558Srgrimes va_dcl 3461558Srgrimes#endif 3471558Srgrimes{ 3481558Srgrimes va_list ap; 3491558Srgrimes#ifndef __STDC__ 3501558Srgrimes char *message; 3511558Srgrimes 3521558Srgrimes va_start(ap); 3531558Srgrimes message = va_arg(ap, char *); 3541558Srgrimes#else 3551558Srgrimes va_start(ap, message); 3561558Srgrimes#endif 3571558Srgrimes 3581558Srgrimes vsyslog(LOG_ALERT, message, ap); 3591558Srgrimes va_end(ap); 3601558Srgrimes sleep(STALL_TIMEOUT); 3611558Srgrimes} 3621558Srgrimes 3631558Srgrimes/* 3641558Srgrimes * Like stall(), but doesn't sleep. 3651558Srgrimes * If cpp had variadic macros, the two functions could be #defines for another. 3661558Srgrimes * NB: should send a message to the session logger to avoid blocking. 3671558Srgrimes */ 3681558Srgrimesvoid 3691558Srgrimes#ifdef __STDC__ 3701558Srgrimeswarning(char *message, ...) 3711558Srgrimes#else 3721558Srgrimeswarning(va_alist) 3731558Srgrimes va_dcl 3741558Srgrimes#endif 3751558Srgrimes{ 3761558Srgrimes va_list ap; 3771558Srgrimes#ifndef __STDC__ 3781558Srgrimes char *message; 3791558Srgrimes 3801558Srgrimes va_start(ap); 3811558Srgrimes message = va_arg(ap, char *); 3821558Srgrimes#else 3831558Srgrimes va_start(ap, message); 3841558Srgrimes#endif 3851558Srgrimes 3861558Srgrimes vsyslog(LOG_ALERT, message, ap); 3871558Srgrimes va_end(ap); 3881558Srgrimes} 3891558Srgrimes 3901558Srgrimes/* 3911558Srgrimes * Log an emergency message. 3921558Srgrimes * NB: should send a message to the session logger to avoid blocking. 3931558Srgrimes */ 3941558Srgrimesvoid 3951558Srgrimes#ifdef __STDC__ 3961558Srgrimesemergency(char *message, ...) 3971558Srgrimes#else 3981558Srgrimesemergency(va_alist) 3991558Srgrimes va_dcl 4001558Srgrimes#endif 4011558Srgrimes{ 4021558Srgrimes va_list ap; 4031558Srgrimes#ifndef __STDC__ 4041558Srgrimes char *message; 4051558Srgrimes 4061558Srgrimes va_start(ap); 4071558Srgrimes message = va_arg(ap, char *); 4081558Srgrimes#else 4091558Srgrimes va_start(ap, message); 4101558Srgrimes#endif 4111558Srgrimes 4121558Srgrimes vsyslog(LOG_EMERG, message, ap); 4131558Srgrimes va_end(ap); 4141558Srgrimes} 4151558Srgrimes 4161558Srgrimes/* 4171558Srgrimes * Catch a SIGSYS signal. 4181558Srgrimes * 4191558Srgrimes * These may arise if a system does not support sysctl. 4201558Srgrimes * We tolerate up to 25 of these, then throw in the towel. 4211558Srgrimes */ 4221558Srgrimesvoid 4231558Srgrimesbadsys(sig) 4241558Srgrimes int sig; 4251558Srgrimes{ 4261558Srgrimes static int badcount = 0; 4271558Srgrimes 4281558Srgrimes if (badcount++ < 25) 4291558Srgrimes return; 4301558Srgrimes disaster(sig); 4311558Srgrimes} 4321558Srgrimes 4331558Srgrimes/* 4341558Srgrimes * Catch an unexpected signal. 4351558Srgrimes */ 4361558Srgrimesvoid 4371558Srgrimesdisaster(sig) 4381558Srgrimes int sig; 4391558Srgrimes{ 4401558Srgrimes emergency("fatal signal: %s", 4411558Srgrimes sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); 4421558Srgrimes 4431558Srgrimes sleep(STALL_TIMEOUT); 4441558Srgrimes _exit(sig); /* reboot */ 4451558Srgrimes} 4461558Srgrimes 4471558Srgrimes/* 4481558Srgrimes * Get the security level of the kernel. 4491558Srgrimes */ 4501558Srgrimesint 4511558Srgrimesgetsecuritylevel() 4521558Srgrimes{ 4531558Srgrimes#ifdef KERN_SECURELVL 4541558Srgrimes int name[2], curlevel; 4551558Srgrimes size_t len; 4561558Srgrimes extern int errno; 4571558Srgrimes 4581558Srgrimes name[0] = CTL_KERN; 4591558Srgrimes name[1] = KERN_SECURELVL; 4601558Srgrimes len = sizeof curlevel; 4611558Srgrimes if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 4621558Srgrimes emergency("cannot get kernel security level: %s", 4631558Srgrimes strerror(errno)); 4641558Srgrimes return (-1); 4651558Srgrimes } 4661558Srgrimes return (curlevel); 4671558Srgrimes#else 4681558Srgrimes return (-1); 4691558Srgrimes#endif 4701558Srgrimes} 4711558Srgrimes 4721558Srgrimes/* 4731558Srgrimes * Set the security level of the kernel. 4741558Srgrimes */ 4751558Srgrimesvoid 4761558Srgrimessetsecuritylevel(newlevel) 4771558Srgrimes int newlevel; 4781558Srgrimes{ 4791558Srgrimes#ifdef KERN_SECURELVL 4801558Srgrimes int name[2], curlevel; 4811558Srgrimes extern int errno; 4821558Srgrimes 4831558Srgrimes curlevel = getsecuritylevel(); 4841558Srgrimes if (newlevel == curlevel) 4851558Srgrimes return; 4861558Srgrimes name[0] = CTL_KERN; 4871558Srgrimes name[1] = KERN_SECURELVL; 4881558Srgrimes if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 4891558Srgrimes emergency( 4901558Srgrimes "cannot change kernel security level from %d to %d: %s", 4911558Srgrimes curlevel, newlevel, strerror(errno)); 4921558Srgrimes return; 4931558Srgrimes } 4941558Srgrimes#ifdef SECURE 4951558Srgrimes warning("kernel security level changed from %d to %d", 4961558Srgrimes curlevel, newlevel); 4971558Srgrimes#endif 4981558Srgrimes#endif 4991558Srgrimes} 5001558Srgrimes 5011558Srgrimes/* 5021558Srgrimes * Change states in the finite state machine. 5031558Srgrimes * The initial state is passed as an argument. 5041558Srgrimes */ 5051558Srgrimesvoid 5061558Srgrimestransition(s) 5071558Srgrimes state_t s; 5081558Srgrimes{ 5091558Srgrimes for (;;) 5101558Srgrimes s = (state_t) (*s)(); 5111558Srgrimes} 5121558Srgrimes 5131558Srgrimes/* 5141558Srgrimes * Close out the accounting files for a login session. 5151558Srgrimes * NB: should send a message to the session logger to avoid blocking. 5161558Srgrimes */ 5171558Srgrimesvoid 5181558Srgrimesclear_session_logs(sp) 5191558Srgrimes session_t *sp; 5201558Srgrimes{ 5211558Srgrimes char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 5221558Srgrimes 5231558Srgrimes if (logout(line)) 5241558Srgrimes logwtmp(line, "", ""); 5251558Srgrimes} 5261558Srgrimes 5271558Srgrimes/* 5281558Srgrimes * Start a session and allocate a controlling terminal. 5291558Srgrimes * Only called by children of init after forking. 5301558Srgrimes */ 5311558Srgrimesvoid 5321558Srgrimessetctty(name) 5331558Srgrimes char *name; 5341558Srgrimes{ 5351558Srgrimes int fd; 5361558Srgrimes 5371558Srgrimes (void) revoke(name); 5381558Srgrimes if ((fd = open(name, O_RDWR)) == -1) { 5391558Srgrimes stall("can't open %s: %m", name); 5401558Srgrimes _exit(1); 5411558Srgrimes } 5421558Srgrimes if (login_tty(fd) == -1) { 5431558Srgrimes stall("can't get %s for controlling terminal: %m", name); 5441558Srgrimes _exit(1); 5451558Srgrimes } 5461558Srgrimes} 5471558Srgrimes 5481558Srgrimes/* 5491558Srgrimes * Bring the system up single user. 5501558Srgrimes */ 5511558Srgrimesstate_func_t 5521558Srgrimessingle_user() 5531558Srgrimes{ 5541558Srgrimes pid_t pid, wpid; 5551558Srgrimes int status; 5561558Srgrimes sigset_t mask; 5571558Srgrimes char *shell = _PATH_BSHELL; 5581558Srgrimes char *argv[2]; 5591558Srgrimes#ifdef SECURE 5601558Srgrimes struct ttyent *typ; 5611558Srgrimes struct passwd *pp; 5621558Srgrimes static const char banner[] = 5631558Srgrimes "Enter root password, or ^D to go multi-user\n"; 5641558Srgrimes char *clear, *password; 5651558Srgrimes#endif 5661558Srgrimes 5671558Srgrimes /* 5681558Srgrimes * If the kernel is in secure mode, downgrade it to insecure mode. 5691558Srgrimes */ 5701558Srgrimes if (getsecuritylevel() > 0) 5711558Srgrimes setsecuritylevel(0); 5721558Srgrimes 5732327Sjkh if (Reboot) { 5742323Snate /* Instead of going single user, let's halt the machine */ 5752323Snate sync(); 5762323Snate alarm(2); 5772323Snate pause(); 5782323Snate reboot(RB_AUTOBOOT); 5792323Snate _exit(0); 5802323Snate } 5812323Snate 5821558Srgrimes if ((pid = fork()) == 0) { 5831558Srgrimes /* 5841558Srgrimes * Start the single user session. 5851558Srgrimes */ 5861558Srgrimes setctty(_PATH_CONSOLE); 5871558Srgrimes 5881558Srgrimes#ifdef SECURE 5891558Srgrimes /* 5901558Srgrimes * Check the root password. 5911558Srgrimes * We don't care if the console is 'on' by default; 5921558Srgrimes * it's the only tty that can be 'off' and 'secure'. 5931558Srgrimes */ 5941558Srgrimes typ = getttynam("console"); 5951558Srgrimes pp = getpwnam("root"); 5961558Srgrimes if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { 5971558Srgrimes write(2, banner, sizeof banner - 1); 5981558Srgrimes for (;;) { 5991558Srgrimes clear = getpass("Password:"); 6001558Srgrimes if (clear == 0 || *clear == '\0') 6011558Srgrimes _exit(0); 6021558Srgrimes password = crypt(clear, pp->pw_passwd); 6031558Srgrimes bzero(clear, _PASSWORD_LEN); 6041558Srgrimes if (strcmp(password, pp->pw_passwd) == 0) 6051558Srgrimes break; 6061558Srgrimes warning("single-user login failed\n"); 6071558Srgrimes } 6081558Srgrimes } 6091558Srgrimes endttyent(); 6101558Srgrimes endpwent(); 6111558Srgrimes#endif /* SECURE */ 6121558Srgrimes 6131558Srgrimes#ifdef DEBUGSHELL 6141558Srgrimes { 6151558Srgrimes char altshell[128], *cp = altshell; 6161558Srgrimes int num; 6171558Srgrimes 6181558Srgrimes#define SHREQUEST \ 6191558Srgrimes "Enter pathname of shell or RETURN for sh: " 6201558Srgrimes (void)write(STDERR_FILENO, 6211558Srgrimes SHREQUEST, sizeof(SHREQUEST) - 1); 6221558Srgrimes while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 6231558Srgrimes num != 0 && *cp != '\n' && cp < &altshell[127]) 6241558Srgrimes cp++; 6251558Srgrimes *cp = '\0'; 6261558Srgrimes if (altshell[0] != '\0') 6271558Srgrimes shell = altshell; 6281558Srgrimes } 6291558Srgrimes#endif /* DEBUGSHELL */ 6301558Srgrimes 6311558Srgrimes /* 6321558Srgrimes * Unblock signals. 6331558Srgrimes * We catch all the interesting ones, 6341558Srgrimes * and those are reset to SIG_DFL on exec. 6351558Srgrimes */ 6361558Srgrimes sigemptyset(&mask); 6371558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 6381558Srgrimes 6391558Srgrimes /* 6401558Srgrimes * Fire off a shell. 6411558Srgrimes * If the default one doesn't work, try the Bourne shell. 6421558Srgrimes */ 6431558Srgrimes argv[0] = "-sh"; 6441558Srgrimes argv[1] = 0; 6451558Srgrimes execv(shell, argv); 6461558Srgrimes emergency("can't exec %s for single user: %m", shell); 6471558Srgrimes execv(_PATH_BSHELL, argv); 6481558Srgrimes emergency("can't exec %s for single user: %m", _PATH_BSHELL); 6491558Srgrimes sleep(STALL_TIMEOUT); 6501558Srgrimes _exit(1); 6511558Srgrimes } 6521558Srgrimes 6531558Srgrimes if (pid == -1) { 6541558Srgrimes /* 6551558Srgrimes * We are seriously hosed. Do our best. 6561558Srgrimes */ 6571558Srgrimes emergency("can't fork single-user shell, trying again"); 6581558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 6591558Srgrimes continue; 6601558Srgrimes return (state_func_t) single_user; 6611558Srgrimes } 6621558Srgrimes 6631558Srgrimes requested_transition = 0; 6641558Srgrimes do { 6651558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 6661558Srgrimes collect_child(wpid); 6671558Srgrimes if (wpid == -1) { 6681558Srgrimes if (errno == EINTR) 6691558Srgrimes continue; 6701558Srgrimes warning("wait for single-user shell failed: %m; restarting"); 6711558Srgrimes return (state_func_t) single_user; 6721558Srgrimes } 6731558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 6741558Srgrimes warning("init: shell stopped, restarting\n"); 6751558Srgrimes kill(pid, SIGCONT); 6761558Srgrimes wpid = -1; 6771558Srgrimes } 6781558Srgrimes } while (wpid != pid && !requested_transition); 6791558Srgrimes 6801558Srgrimes if (requested_transition) 6811558Srgrimes return (state_func_t) requested_transition; 6821558Srgrimes 6831558Srgrimes if (!WIFEXITED(status)) { 6848871Srgrimes if (WTERMSIG(status) == SIGKILL) { 6858871Srgrimes /* 6868871Srgrimes * reboot(8) killed shell? 6871558Srgrimes */ 6881558Srgrimes warning("single user shell terminated."); 6891558Srgrimes sleep(STALL_TIMEOUT); 6901558Srgrimes _exit(0); 6918871Srgrimes } else { 6921558Srgrimes warning("single user shell terminated, restarting"); 6931558Srgrimes return (state_func_t) single_user; 6941558Srgrimes } 6951558Srgrimes } 6961558Srgrimes 6971558Srgrimes runcom_mode = FASTBOOT; 6981558Srgrimes return (state_func_t) runcom; 6991558Srgrimes} 7001558Srgrimes 7011558Srgrimes/* 7021558Srgrimes * Run the system startup script. 7031558Srgrimes */ 7041558Srgrimesstate_func_t 7051558Srgrimesruncom() 7061558Srgrimes{ 7071558Srgrimes pid_t pid, wpid; 7081558Srgrimes int status; 7091558Srgrimes char *argv[4]; 7101558Srgrimes struct sigaction sa; 7111558Srgrimes 7121558Srgrimes if ((pid = fork()) == 0) { 7131558Srgrimes sigemptyset(&sa.sa_mask); 7141558Srgrimes sa.sa_flags = 0; 7151558Srgrimes sa.sa_handler = SIG_IGN; 7161558Srgrimes (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); 7171558Srgrimes (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); 7181558Srgrimes 7191558Srgrimes setctty(_PATH_CONSOLE); 7201558Srgrimes 7211558Srgrimes argv[0] = "sh"; 7221558Srgrimes argv[1] = _PATH_RUNCOM; 7231558Srgrimes argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; 7241558Srgrimes argv[3] = 0; 7251558Srgrimes 7261558Srgrimes sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); 7271558Srgrimes 7281558Srgrimes execv(_PATH_BSHELL, argv); 7291558Srgrimes stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 7301558Srgrimes _exit(1); /* force single user mode */ 7311558Srgrimes } 7321558Srgrimes 7331558Srgrimes if (pid == -1) { 7341558Srgrimes emergency("can't fork for %s on %s: %m", 7351558Srgrimes _PATH_BSHELL, _PATH_RUNCOM); 7361558Srgrimes while (waitpid(-1, (int *) 0, WNOHANG) > 0) 7371558Srgrimes continue; 7381558Srgrimes sleep(STALL_TIMEOUT); 7391558Srgrimes return (state_func_t) single_user; 7401558Srgrimes } 7411558Srgrimes 7421558Srgrimes /* 7431558Srgrimes * Copied from single_user(). This is a bit paranoid. 7441558Srgrimes */ 7451558Srgrimes do { 7461558Srgrimes if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 7471558Srgrimes collect_child(wpid); 7481558Srgrimes if (wpid == -1) { 7491558Srgrimes if (errno == EINTR) 7501558Srgrimes continue; 7511558Srgrimes warning("wait for %s on %s failed: %m; going to single user mode", 7521558Srgrimes _PATH_BSHELL, _PATH_RUNCOM); 7531558Srgrimes return (state_func_t) single_user; 7541558Srgrimes } 7551558Srgrimes if (wpid == pid && WIFSTOPPED(status)) { 7561558Srgrimes warning("init: %s on %s stopped, restarting\n", 7571558Srgrimes _PATH_BSHELL, _PATH_RUNCOM); 7581558Srgrimes kill(pid, SIGCONT); 7591558Srgrimes wpid = -1; 7601558Srgrimes } 7611558Srgrimes } while (wpid != pid); 7621558Srgrimes 7631558Srgrimes if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 7641558Srgrimes requested_transition == catatonia) { 7651558Srgrimes /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 7661558Srgrimes sigset_t s; 7671558Srgrimes 7681558Srgrimes sigfillset(&s); 7691558Srgrimes for (;;) 7701558Srgrimes sigsuspend(&s); 7711558Srgrimes } 7721558Srgrimes 7731558Srgrimes if (!WIFEXITED(status)) { 7741558Srgrimes warning("%s on %s terminated abnormally, going to single user mode", 7751558Srgrimes _PATH_BSHELL, _PATH_RUNCOM); 7761558Srgrimes return (state_func_t) single_user; 7771558Srgrimes } 7781558Srgrimes 7791558Srgrimes if (WEXITSTATUS(status)) 7801558Srgrimes return (state_func_t) single_user; 7811558Srgrimes 7821558Srgrimes runcom_mode = AUTOBOOT; /* the default */ 7831558Srgrimes /* NB: should send a message to the session logger to avoid blocking. */ 7841558Srgrimes logwtmp("~", "reboot", ""); 7851558Srgrimes return (state_func_t) read_ttys; 7861558Srgrimes} 7871558Srgrimes 7881558Srgrimes/* 7891558Srgrimes * Open the session database. 7901558Srgrimes * 7911558Srgrimes * NB: We could pass in the size here; is it necessary? 7921558Srgrimes */ 7931558Srgrimesint 7941558Srgrimesstart_session_db() 7951558Srgrimes{ 7961558Srgrimes if (session_db && (*session_db->close)(session_db)) 7971558Srgrimes emergency("session database close: %s", strerror(errno)); 7981558Srgrimes if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { 7991558Srgrimes emergency("session database open: %s", strerror(errno)); 8001558Srgrimes return (1); 8011558Srgrimes } 8021558Srgrimes return (0); 8038871Srgrimes 8041558Srgrimes} 8051558Srgrimes 8061558Srgrimes/* 8071558Srgrimes * Add a new login session. 8081558Srgrimes */ 8091558Srgrimesvoid 8101558Srgrimesadd_session(sp) 8111558Srgrimes session_t *sp; 8121558Srgrimes{ 8131558Srgrimes DBT key; 8141558Srgrimes DBT data; 8151558Srgrimes 8161558Srgrimes key.data = &sp->se_process; 8171558Srgrimes key.size = sizeof sp->se_process; 8181558Srgrimes data.data = &sp; 8191558Srgrimes data.size = sizeof sp; 8201558Srgrimes 8211558Srgrimes if ((*session_db->put)(session_db, &key, &data, 0)) 8221558Srgrimes emergency("insert %d: %s", sp->se_process, strerror(errno)); 8231558Srgrimes} 8241558Srgrimes 8251558Srgrimes/* 8261558Srgrimes * Delete an old login session. 8271558Srgrimes */ 8281558Srgrimesvoid 8291558Srgrimesdel_session(sp) 8301558Srgrimes session_t *sp; 8311558Srgrimes{ 8321558Srgrimes DBT key; 8331558Srgrimes 8341558Srgrimes key.data = &sp->se_process; 8351558Srgrimes key.size = sizeof sp->se_process; 8361558Srgrimes 8371558Srgrimes if ((*session_db->del)(session_db, &key, 0)) 8381558Srgrimes emergency("delete %d: %s", sp->se_process, strerror(errno)); 8391558Srgrimes} 8401558Srgrimes 8411558Srgrimes/* 8421558Srgrimes * Look up a login session by pid. 8431558Srgrimes */ 8441558Srgrimessession_t * 8451558Srgrimes#ifdef __STDC__ 8461558Srgrimesfind_session(pid_t pid) 8471558Srgrimes#else 8481558Srgrimesfind_session(pid) 8491558Srgrimes pid_t pid; 8501558Srgrimes#endif 8511558Srgrimes{ 8521558Srgrimes DBT key; 8531558Srgrimes DBT data; 8541558Srgrimes session_t *ret; 8551558Srgrimes 8561558Srgrimes key.data = &pid; 8571558Srgrimes key.size = sizeof pid; 8581558Srgrimes if ((*session_db->get)(session_db, &key, &data, 0) != 0) 8591558Srgrimes return 0; 8601558Srgrimes bcopy(data.data, (char *)&ret, sizeof(ret)); 8611558Srgrimes return ret; 8621558Srgrimes} 8631558Srgrimes 8641558Srgrimes/* 8651558Srgrimes * Construct an argument vector from a command line. 8661558Srgrimes */ 8671558Srgrimeschar ** 8681558Srgrimesconstruct_argv(command) 8691558Srgrimes char *command; 8701558Srgrimes{ 8715478Sache char *strk (char *); 8721558Srgrimes register int argc = 0; 8731558Srgrimes register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) 8741558Srgrimes * sizeof (char *)); 8751558Srgrimes 8765478Sache if ((argv[argc++] = strk(command)) == 0) 8771558Srgrimes return 0; 8785478Sache while (argv[argc++] = strk((char *) 0)) 8791558Srgrimes continue; 8801558Srgrimes return argv; 8811558Srgrimes} 8821558Srgrimes 8831558Srgrimes/* 8841558Srgrimes * Deallocate a session descriptor. 8851558Srgrimes */ 8861558Srgrimesvoid 8871558Srgrimesfree_session(sp) 8881558Srgrimes register session_t *sp; 8891558Srgrimes{ 8901558Srgrimes free(sp->se_device); 8911558Srgrimes if (sp->se_getty) { 8921558Srgrimes free(sp->se_getty); 8933594Sache free(sp->se_getty_argv_space); 8941558Srgrimes free(sp->se_getty_argv); 8951558Srgrimes } 8961558Srgrimes if (sp->se_window) { 8971558Srgrimes free(sp->se_window); 8983594Sache free(sp->se_window_argv_space); 8991558Srgrimes free(sp->se_window_argv); 9001558Srgrimes } 9013594Sache if (sp->se_type) 9023594Sache free(sp->se_type); 9031558Srgrimes free(sp); 9041558Srgrimes} 9051558Srgrimes 9061558Srgrimes/* 9071558Srgrimes * Allocate a new session descriptor. 9081558Srgrimes */ 9091558Srgrimessession_t * 9101558Srgrimesnew_session(sprev, session_index, typ) 9111558Srgrimes session_t *sprev; 9121558Srgrimes int session_index; 9131558Srgrimes register struct ttyent *typ; 9141558Srgrimes{ 9151558Srgrimes register session_t *sp; 9161558Srgrimes 9171558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 9181558Srgrimes typ->ty_name == 0 || 9191558Srgrimes typ->ty_getty == 0) 9201558Srgrimes return 0; 9211558Srgrimes 9221558Srgrimes sp = (session_t *) malloc(sizeof (session_t)); 9231558Srgrimes bzero(sp, sizeof *sp); 9241558Srgrimes 9251558Srgrimes sp->se_index = session_index; 9261558Srgrimes 9271558Srgrimes sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); 9281558Srgrimes (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); 9291558Srgrimes 9301558Srgrimes if (setupargv(sp, typ) == 0) { 9311558Srgrimes free_session(sp); 9321558Srgrimes return (0); 9331558Srgrimes } 9341558Srgrimes 9351558Srgrimes sp->se_next = 0; 9361558Srgrimes if (sprev == 0) { 9371558Srgrimes sessions = sp; 9381558Srgrimes sp->se_prev = 0; 9391558Srgrimes } else { 9401558Srgrimes sprev->se_next = sp; 9411558Srgrimes sp->se_prev = sprev; 9421558Srgrimes } 9431558Srgrimes 9441558Srgrimes return sp; 9451558Srgrimes} 9461558Srgrimes 9471558Srgrimes/* 9481558Srgrimes * Calculate getty and if useful window argv vectors. 9491558Srgrimes */ 9501558Srgrimesint 9511558Srgrimessetupargv(sp, typ) 9521558Srgrimes session_t *sp; 9531558Srgrimes struct ttyent *typ; 9541558Srgrimes{ 9551558Srgrimes 9561558Srgrimes if (sp->se_getty) { 9571558Srgrimes free(sp->se_getty); 9583594Sache free(sp->se_getty_argv_space); 9591558Srgrimes free(sp->se_getty_argv); 9601558Srgrimes } 9611558Srgrimes sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); 9621558Srgrimes (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); 9633594Sache sp->se_getty_argv_space = strdup(sp->se_getty); 9643594Sache sp->se_getty_argv = construct_argv(sp->se_getty_argv_space); 9651558Srgrimes if (sp->se_getty_argv == 0) { 9661558Srgrimes warning("can't parse getty for port %s", sp->se_device); 9671558Srgrimes free(sp->se_getty); 9683594Sache free(sp->se_getty_argv_space); 9693594Sache sp->se_getty = sp->se_getty_argv_space = 0; 9701558Srgrimes return (0); 9711558Srgrimes } 9723594Sache if (sp->se_window) { 9733594Sache free(sp->se_window); 9743594Sache free(sp->se_window_argv_space); 9753594Sache free(sp->se_window_argv); 9763594Sache } 9773594Sache sp->se_window = sp->se_window_argv_space = 0; 9783594Sache sp->se_window_argv = 0; 9791558Srgrimes if (typ->ty_window) { 9801558Srgrimes sp->se_window = strdup(typ->ty_window); 9813594Sache sp->se_window_argv_space = strdup(sp->se_window); 9823594Sache sp->se_window_argv = construct_argv(sp->se_window_argv_space); 9831558Srgrimes if (sp->se_window_argv == 0) { 9841558Srgrimes warning("can't parse window for port %s", 9851558Srgrimes sp->se_device); 9863594Sache free(sp->se_window_argv_space); 9871558Srgrimes free(sp->se_window); 9883594Sache sp->se_window = sp->se_window_argv_space = 0; 9891558Srgrimes return (0); 9901558Srgrimes } 9911558Srgrimes } 9923594Sache if (sp->se_type) 9933594Sache free(sp->se_type); 9943594Sache sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0; 9951558Srgrimes return (1); 9961558Srgrimes} 9971558Srgrimes 9981558Srgrimes/* 9991558Srgrimes * Walk the list of ttys and create sessions for each active line. 10001558Srgrimes */ 10011558Srgrimesstate_func_t 10021558Srgrimesread_ttys() 10031558Srgrimes{ 10041558Srgrimes int session_index = 0; 10051558Srgrimes register session_t *sp, *snext; 10061558Srgrimes register struct ttyent *typ; 10071558Srgrimes 10081558Srgrimes /* 10091558Srgrimes * Destroy any previous session state. 10101558Srgrimes * There shouldn't be any, but just in case... 10111558Srgrimes */ 10121558Srgrimes for (sp = sessions; sp; sp = snext) { 10131558Srgrimes if (sp->se_process) 10141558Srgrimes clear_session_logs(sp); 10151558Srgrimes snext = sp->se_next; 10161558Srgrimes free_session(sp); 10171558Srgrimes } 10181558Srgrimes sessions = 0; 10191558Srgrimes if (start_session_db()) 10201558Srgrimes return (state_func_t) single_user; 10211558Srgrimes 10221558Srgrimes /* 10231558Srgrimes * Allocate a session entry for each active port. 10241558Srgrimes * Note that sp starts at 0. 10251558Srgrimes */ 10261558Srgrimes while (typ = getttyent()) 10271558Srgrimes if (snext = new_session(sp, ++session_index, typ)) 10281558Srgrimes sp = snext; 10291558Srgrimes 10301558Srgrimes endttyent(); 10311558Srgrimes 10321558Srgrimes return (state_func_t) multi_user; 10331558Srgrimes} 10341558Srgrimes 10351558Srgrimes/* 10361558Srgrimes * Start a window system running. 10371558Srgrimes */ 10381558Srgrimesvoid 10391558Srgrimesstart_window_system(sp) 10401558Srgrimes session_t *sp; 10411558Srgrimes{ 10421558Srgrimes pid_t pid; 10431558Srgrimes sigset_t mask; 10443594Sache char term[64], *env[2]; 10451558Srgrimes 10461558Srgrimes if ((pid = fork()) == -1) { 10471558Srgrimes emergency("can't fork for window system on port %s: %m", 10481558Srgrimes sp->se_device); 10491558Srgrimes /* hope that getty fails and we can try again */ 10501558Srgrimes return; 10511558Srgrimes } 10521558Srgrimes 10531558Srgrimes if (pid) 10541558Srgrimes return; 10551558Srgrimes 10561558Srgrimes sigemptyset(&mask); 10571558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 10581558Srgrimes 10591558Srgrimes if (setsid() < 0) 10601558Srgrimes emergency("setsid failed (window) %m"); 10611558Srgrimes 10623594Sache if (sp->se_type) { 10633594Sache /* Don't use malloc after fork */ 10643594Sache strcpy(term, "TERM="); 10653594Sache strcat(term, sp->se_type); 10663594Sache env[0] = term; 10673594Sache env[1] = 0; 10683594Sache } 10693594Sache else 10703594Sache env[0] = 0; 10713594Sache execve(sp->se_window_argv[0], sp->se_window_argv, env); 10721558Srgrimes stall("can't exec window system '%s' for port %s: %m", 10731558Srgrimes sp->se_window_argv[0], sp->se_device); 10741558Srgrimes _exit(1); 10751558Srgrimes} 10761558Srgrimes 10771558Srgrimes/* 10781558Srgrimes * Start a login session running. 10791558Srgrimes */ 10801558Srgrimespid_t 10811558Srgrimesstart_getty(sp) 10821558Srgrimes session_t *sp; 10831558Srgrimes{ 10841558Srgrimes pid_t pid; 10851558Srgrimes sigset_t mask; 10861558Srgrimes time_t current_time = time((time_t *) 0); 10879997Sache int too_quick = 0; 10883594Sache char term[64], *env[2]; 10891558Srgrimes 109010006Smpp if (current_time >= sp->se_started && 10919997Sache current_time - sp->se_started < GETTY_SPACING) { 10929997Sache if (++sp->se_nspace > GETTY_NSPACE) { 10939997Sache sp->se_nspace = 0; 10949997Sache too_quick = 1; 10959997Sache } 10969997Sache } else 10979997Sache sp->se_nspace = 0; 10989997Sache 10991558Srgrimes /* 11001558Srgrimes * fork(), not vfork() -- we can't afford to block. 11011558Srgrimes */ 11021558Srgrimes if ((pid = fork()) == -1) { 11031558Srgrimes emergency("can't fork for getty on port %s: %m", sp->se_device); 11041558Srgrimes return -1; 11051558Srgrimes } 11061558Srgrimes 11071558Srgrimes if (pid) 11081558Srgrimes return pid; 11091558Srgrimes 11109997Sache if (too_quick) { 11119997Sache warning("getty repeating too quickly on port %s, sleeping %d secs", 11129997Sache sp->se_device, GETTY_SLEEP); 11131558Srgrimes sleep((unsigned) GETTY_SLEEP); 11141558Srgrimes } 11151558Srgrimes 11161558Srgrimes if (sp->se_window) { 11171558Srgrimes start_window_system(sp); 11181558Srgrimes sleep(WINDOW_WAIT); 11191558Srgrimes } 11201558Srgrimes 11211558Srgrimes sigemptyset(&mask); 11221558Srgrimes sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); 11231558Srgrimes 11243594Sache if (sp->se_type) { 11253594Sache /* Don't use malloc after fork */ 11263594Sache strcpy(term, "TERM="); 11273594Sache strcat(term, sp->se_type); 11283594Sache env[0] = term; 11293594Sache env[1] = 0; 11303594Sache } 11313594Sache else 11323594Sache env[0] = 0; 11333594Sache execve(sp->se_getty_argv[0], sp->se_getty_argv, env); 11341558Srgrimes stall("can't exec getty '%s' for port %s: %m", 11351558Srgrimes sp->se_getty_argv[0], sp->se_device); 11361558Srgrimes _exit(1); 11371558Srgrimes} 11381558Srgrimes 11391558Srgrimes/* 11401558Srgrimes * Collect exit status for a child. 11411558Srgrimes * If an exiting login, start a new login running. 11421558Srgrimes */ 11431558Srgrimesvoid 11441558Srgrimes#ifdef __STDC__ 11451558Srgrimescollect_child(pid_t pid) 11461558Srgrimes#else 11471558Srgrimescollect_child(pid) 11481558Srgrimes pid_t pid; 11491558Srgrimes#endif 11501558Srgrimes{ 11511558Srgrimes register session_t *sp, *sprev, *snext; 11521558Srgrimes 11531558Srgrimes if (! sessions) 11541558Srgrimes return; 11551558Srgrimes 11561558Srgrimes if (! (sp = find_session(pid))) 11571558Srgrimes return; 11581558Srgrimes 11591558Srgrimes clear_session_logs(sp); 11601558Srgrimes del_session(sp); 11611558Srgrimes sp->se_process = 0; 11621558Srgrimes 11631558Srgrimes if (sp->se_flags & SE_SHUTDOWN) { 11641558Srgrimes if (sprev = sp->se_prev) 11651558Srgrimes sprev->se_next = sp->se_next; 11661558Srgrimes else 11671558Srgrimes sessions = sp->se_next; 11681558Srgrimes if (snext = sp->se_next) 11691558Srgrimes snext->se_prev = sp->se_prev; 11701558Srgrimes free_session(sp); 11711558Srgrimes return; 11721558Srgrimes } 11731558Srgrimes 11741558Srgrimes if ((pid = start_getty(sp)) == -1) { 11751558Srgrimes /* serious trouble */ 11761558Srgrimes requested_transition = clean_ttys; 11771558Srgrimes return; 11781558Srgrimes } 11791558Srgrimes 11801558Srgrimes sp->se_process = pid; 11811558Srgrimes sp->se_started = time((time_t *) 0); 11821558Srgrimes add_session(sp); 11831558Srgrimes} 11841558Srgrimes 11851558Srgrimes/* 11861558Srgrimes * Catch a signal and request a state transition. 11871558Srgrimes */ 11881558Srgrimesvoid 11891558Srgrimestransition_handler(sig) 11901558Srgrimes int sig; 11911558Srgrimes{ 11921558Srgrimes 11931558Srgrimes switch (sig) { 11941558Srgrimes case SIGHUP: 11951558Srgrimes requested_transition = clean_ttys; 11961558Srgrimes break; 11972323Snate case SIGINT: 11982327Sjkh Reboot = TRUE; 11991558Srgrimes case SIGTERM: 12001558Srgrimes requested_transition = death; 12011558Srgrimes break; 12021558Srgrimes case SIGTSTP: 12031558Srgrimes requested_transition = catatonia; 12041558Srgrimes break; 12051558Srgrimes default: 12061558Srgrimes requested_transition = 0; 12071558Srgrimes break; 12081558Srgrimes } 12091558Srgrimes} 12101558Srgrimes 12111558Srgrimes/* 12121558Srgrimes * Take the system multiuser. 12131558Srgrimes */ 12141558Srgrimesstate_func_t 12151558Srgrimesmulti_user() 12161558Srgrimes{ 12171558Srgrimes pid_t pid; 12181558Srgrimes register session_t *sp; 12191558Srgrimes 12201558Srgrimes requested_transition = 0; 12211558Srgrimes 12221558Srgrimes /* 12231558Srgrimes * If the administrator has not set the security level to -1 12241558Srgrimes * to indicate that the kernel should not run multiuser in secure 12258871Srgrimes * mode, and the run script has not set a higher level of security 12261558Srgrimes * than level 1, then put the kernel into secure mode. 12271558Srgrimes */ 12281558Srgrimes if (getsecuritylevel() == 0) 12291558Srgrimes setsecuritylevel(1); 12301558Srgrimes 12311558Srgrimes for (sp = sessions; sp; sp = sp->se_next) { 12321558Srgrimes if (sp->se_process) 12331558Srgrimes continue; 12341558Srgrimes if ((pid = start_getty(sp)) == -1) { 12351558Srgrimes /* serious trouble */ 12361558Srgrimes requested_transition = clean_ttys; 12371558Srgrimes break; 12381558Srgrimes } 12391558Srgrimes sp->se_process = pid; 12401558Srgrimes sp->se_started = time((time_t *) 0); 12411558Srgrimes add_session(sp); 12421558Srgrimes } 12431558Srgrimes 12441558Srgrimes while (!requested_transition) 12451558Srgrimes if ((pid = waitpid(-1, (int *) 0, 0)) != -1) 12461558Srgrimes collect_child(pid); 12471558Srgrimes 12481558Srgrimes return (state_func_t) requested_transition; 12491558Srgrimes} 12501558Srgrimes 12511558Srgrimes/* 12521558Srgrimes * This is an n-squared algorithm. We hope it isn't run often... 12531558Srgrimes */ 12541558Srgrimesstate_func_t 12551558Srgrimesclean_ttys() 12561558Srgrimes{ 12571558Srgrimes register session_t *sp, *sprev; 12581558Srgrimes register struct ttyent *typ; 12591558Srgrimes register int session_index = 0; 12601558Srgrimes register int devlen; 12613594Sache char *old_getty, *old_window, *old_type; 12621558Srgrimes 12631558Srgrimes if (! sessions) 12641558Srgrimes return (state_func_t) multi_user; 12651558Srgrimes 12661558Srgrimes devlen = sizeof(_PATH_DEV) - 1; 12671558Srgrimes while (typ = getttyent()) { 12681558Srgrimes ++session_index; 12691558Srgrimes 12701558Srgrimes for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) 12711558Srgrimes if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 12721558Srgrimes break; 12731558Srgrimes 12741558Srgrimes if (sp) { 12751558Srgrimes if (sp->se_index != session_index) { 12761558Srgrimes warning("port %s changed utmp index from %d to %d", 12771558Srgrimes sp->se_device, sp->se_index, 12781558Srgrimes session_index); 12791558Srgrimes sp->se_index = session_index; 12801558Srgrimes } 12811558Srgrimes if ((typ->ty_status & TTY_ON) == 0 || 12821558Srgrimes typ->ty_getty == 0) { 12831558Srgrimes sp->se_flags |= SE_SHUTDOWN; 12841558Srgrimes kill(sp->se_process, SIGHUP); 12851558Srgrimes continue; 12861558Srgrimes } 12871558Srgrimes sp->se_flags &= ~SE_SHUTDOWN; 12883594Sache old_getty = sp->se_getty ? strdup(sp->se_getty) : 0; 12893594Sache old_window = sp->se_window ? strdup(sp->se_window) : 0; 12903594Sache old_type = sp->se_type ? strdup(sp->se_type) : 0; 12911558Srgrimes if (setupargv(sp, typ) == 0) { 12921558Srgrimes warning("can't parse getty for port %s", 12931558Srgrimes sp->se_device); 12941558Srgrimes sp->se_flags |= SE_SHUTDOWN; 12951558Srgrimes kill(sp->se_process, SIGHUP); 12961558Srgrimes } 12973594Sache else if ( !old_getty 12983594Sache || !old_type && sp->se_type 12993594Sache || old_type && !sp->se_type 13003594Sache || !old_window && sp->se_window 13013594Sache || old_window && !sp->se_window 13023594Sache || strcmp(old_getty, sp->se_getty) != 0 13033594Sache || old_window && strcmp(old_window, sp->se_window) != 0 13043594Sache || old_type && strcmp(old_type, sp->se_type) != 0 13053594Sache ) { 13063594Sache /* Don't set SE_SHUTDOWN here */ 13073594Sache sp->se_nspace = 0; 13083594Sache sp->se_started = 0; 13093594Sache kill(sp->se_process, SIGHUP); 13103594Sache } 13113594Sache if (old_getty) 13123594Sache free(old_getty); 13133594Sache if (old_getty) 13143594Sache free(old_window); 13153594Sache if (old_type) 13163594Sache free(old_type); 13171558Srgrimes continue; 13181558Srgrimes } 13191558Srgrimes 13201558Srgrimes new_session(sprev, session_index, typ); 13211558Srgrimes } 13221558Srgrimes 13231558Srgrimes endttyent(); 13241558Srgrimes 13251558Srgrimes return (state_func_t) multi_user; 13261558Srgrimes} 13271558Srgrimes 13281558Srgrimes/* 13291558Srgrimes * Block further logins. 13301558Srgrimes */ 13311558Srgrimesstate_func_t 13321558Srgrimescatatonia() 13331558Srgrimes{ 13341558Srgrimes register session_t *sp; 13351558Srgrimes 13361558Srgrimes for (sp = sessions; sp; sp = sp->se_next) 13371558Srgrimes sp->se_flags |= SE_SHUTDOWN; 13381558Srgrimes 13391558Srgrimes return (state_func_t) multi_user; 13401558Srgrimes} 13411558Srgrimes 13421558Srgrimes/* 13431558Srgrimes * Note SIGALRM. 13441558Srgrimes */ 13451558Srgrimesvoid 13461558Srgrimesalrm_handler(sig) 13471558Srgrimes int sig; 13481558Srgrimes{ 13491558Srgrimes clang = 1; 13501558Srgrimes} 13511558Srgrimes 13521558Srgrimes/* 13531558Srgrimes * Bring the system down to single user. 13541558Srgrimes */ 13551558Srgrimesstate_func_t 13561558Srgrimesdeath() 13571558Srgrimes{ 13581558Srgrimes register session_t *sp; 13591558Srgrimes register int i; 13601558Srgrimes pid_t pid; 13611558Srgrimes static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 13621558Srgrimes 13631558Srgrimes for (sp = sessions; sp; sp = sp->se_next) 13641558Srgrimes sp->se_flags |= SE_SHUTDOWN; 13651558Srgrimes 13661558Srgrimes /* NB: should send a message to the session logger to avoid blocking. */ 13671558Srgrimes logwtmp("~", "shutdown", ""); 13681558Srgrimes 13691558Srgrimes for (i = 0; i < 3; ++i) { 13701558Srgrimes if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 13711558Srgrimes return (state_func_t) single_user; 13721558Srgrimes 13731558Srgrimes clang = 0; 13741558Srgrimes alarm(DEATH_WATCH); 13751558Srgrimes do 13761558Srgrimes if ((pid = waitpid(-1, (int *)0, 0)) != -1) 13771558Srgrimes collect_child(pid); 13781558Srgrimes while (clang == 0 && errno != ECHILD); 13791558Srgrimes 13801558Srgrimes if (errno == ECHILD) 13811558Srgrimes return (state_func_t) single_user; 13821558Srgrimes } 13831558Srgrimes 13841558Srgrimes warning("some processes would not die; ps axl advised"); 13851558Srgrimes 13861558Srgrimes return (state_func_t) single_user; 13871558Srgrimes} 13885478Sachechar * 13895478Sachestrk (char *p) 13905478Sache{ 13915478Sache static char *t; 13925478Sache char *q; 13935478Sache int c; 13945478Sache 13955478Sache if (p) 13965478Sache t = p; 13975478Sache if (!t) 13985478Sache return 0; 13995478Sache 14005478Sache c = *t; 14018871Srgrimes while (c == ' ' || c == '\t' ) 14025478Sache c = *++t; 14035478Sache if (!c) { 14045478Sache t = 0; 14055478Sache return 0; 14065478Sache } 14075478Sache q = t; 14085478Sache if (c == '\'') { 14095478Sache c = *++t; 14105478Sache q = t; 14118871Srgrimes while (c && c != '\'') 14125478Sache c = *++t; 14135478Sache if (!c) /* unterminated string */ 14145478Sache q = t = 0; 14155478Sache else 14165478Sache *t++ = 0; 14175478Sache } else { 14185478Sache while (c && c != ' ' && c != '\t' ) 14195478Sache c = *++t; 14205478Sache *t++ = 0; 14218871Srgrimes if (!c) 14225478Sache t = 0; 14235478Sache } 14245478Sache return q; 14255478Sache} 1426