xref: /illumos-gate/usr/src/cmd/init/init.c (revision a28480fe)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
505b96de2Smjnelson  * Common Development and Distribution License (the "License").
605b96de2Smjnelson  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2105b96de2Smjnelson 
227c478bd9Sstevel@tonic-gate /*
23*a28480feSAndy Fiddaman  * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
246112cec5SJoshua M. Clulow  * Copyright 2020 Oxide Computer Company
250a1278f2SGary Mills  * Copyright (c) 2013 Gary Mills
260a1278f2SGary Mills  *
27861a9162SJohn Beck  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
31*a28480feSAndy Fiddaman /*	  All Rights Reserved	*/
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /*
347c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
357c478bd9Sstevel@tonic-gate  * The Regents of the University of California
367c478bd9Sstevel@tonic-gate  * All Rights Reserved
377c478bd9Sstevel@tonic-gate  *
387c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
397c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
407c478bd9Sstevel@tonic-gate  * contributors.
417c478bd9Sstevel@tonic-gate  */
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate /*
447c478bd9Sstevel@tonic-gate  * init(1M) is the general process spawning program.  Its primary job is to
457c478bd9Sstevel@tonic-gate  * start and restart svc.startd for smf(5).  For backwards-compatibility it also
467c478bd9Sstevel@tonic-gate  * spawns and respawns processes according to /etc/inittab and the current
477c478bd9Sstevel@tonic-gate  * run-level.  It reads /etc/default/inittab for general configuration.
487c478bd9Sstevel@tonic-gate  *
497c478bd9Sstevel@tonic-gate  * To change run-levels the system administrator runs init from the command
507c478bd9Sstevel@tonic-gate  * line with a level name.  init signals svc.startd via libscf and directs the
517c478bd9Sstevel@tonic-gate  * zone's init (pid 1 in the global zone) what to do by sending it a signal;
527c478bd9Sstevel@tonic-gate  * these signal numbers are commonly refered to in the code as 'states'.  Valid
537c478bd9Sstevel@tonic-gate  * run-levels are [sS0123456].  Additionally, init can be given directives
547c478bd9Sstevel@tonic-gate  * [qQabc], which indicate actions to be taken pertaining to /etc/inittab.
557c478bd9Sstevel@tonic-gate  *
567c478bd9Sstevel@tonic-gate  * When init processes inittab entries, it finds processes that are to be
577c478bd9Sstevel@tonic-gate  * spawned at various run-levels.  inittab contains the set of the levels for
587c478bd9Sstevel@tonic-gate  * which each inittab entry is valid.
597c478bd9Sstevel@tonic-gate  *
607c478bd9Sstevel@tonic-gate  * State File and Restartability
617c478bd9Sstevel@tonic-gate  *   Premature exit by init(1M) is handled as a special case by the kernel:
627c478bd9Sstevel@tonic-gate  *   init(1M) will be immediately re-executed, retaining its original PID.  (PID
637c478bd9Sstevel@tonic-gate  *   1 in the global zone.)  To track the processes it has previously spawned,
647c478bd9Sstevel@tonic-gate  *   as well as other mutable state, init(1M) regularly updates a state file
657c478bd9Sstevel@tonic-gate  *   such that its subsequent invocations have knowledge of its various
667c478bd9Sstevel@tonic-gate  *   dependent processes and duties.
677c478bd9Sstevel@tonic-gate  *
687c478bd9Sstevel@tonic-gate  * Process Contracts
697c478bd9Sstevel@tonic-gate  *   We start svc.startd(1M) in a contract and transfer inherited contracts when
707c478bd9Sstevel@tonic-gate  *   restarting it.  Everything else is started using the legacy contract
717c478bd9Sstevel@tonic-gate  *   template, and the created contracts are abandoned when they become empty.
727c478bd9Sstevel@tonic-gate  *
737c478bd9Sstevel@tonic-gate  * utmpx Entry Handling
747c478bd9Sstevel@tonic-gate  *   Because init(1M) no longer governs the startup process, its knowledge of
757c478bd9Sstevel@tonic-gate  *   when utmpx becomes writable is indirect.  However, spawned processes
767c478bd9Sstevel@tonic-gate  *   expect to be constructed with valid utmpx entries.  As a result, attempts
777c478bd9Sstevel@tonic-gate  *   to write normal entries will be retried until successful.
787c478bd9Sstevel@tonic-gate  *
797c478bd9Sstevel@tonic-gate  * Maintenance Mode
807c478bd9Sstevel@tonic-gate  *   In certain failure scenarios, init(1M) will enter a maintenance mode, in
817c478bd9Sstevel@tonic-gate  *   which it invokes sulogin(1M) to allow the operator an opportunity to
827c478bd9Sstevel@tonic-gate  *   repair the system.  Normally, this operation is performed as a
837c478bd9Sstevel@tonic-gate  *   fork(2)-exec(2)-waitpid(3C) sequence with the parent waiting for repair or
847c478bd9Sstevel@tonic-gate  *   diagnosis to be completed.  In the cases that fork(2) requests themselves
857c478bd9Sstevel@tonic-gate  *   fail, init(1M) will directly execute sulogin(1M), and allow the kernel to
867c478bd9Sstevel@tonic-gate  *   restart init(1M) on exit from the operator session.
877c478bd9Sstevel@tonic-gate  *
887c478bd9Sstevel@tonic-gate  *   One scenario where init(1M) enters its maintenance mode is when
897c478bd9Sstevel@tonic-gate  *   svc.startd(1M) begins to fail rapidly, defined as when the average time
907c478bd9Sstevel@tonic-gate  *   between recent failures drops below a given threshold.
917c478bd9Sstevel@tonic-gate  */
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate #include <sys/contract/process.h>
947c478bd9Sstevel@tonic-gate #include <sys/ctfs.h>
957c478bd9Sstevel@tonic-gate #include <sys/stat.h>
967c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
977c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
987c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h>
997c478bd9Sstevel@tonic-gate #include <sys/time.h>
1007c478bd9Sstevel@tonic-gate #include <sys/termios.h>
1017c478bd9Sstevel@tonic-gate #include <sys/tty.h>
1027c478bd9Sstevel@tonic-gate #include <sys/types.h>
1037c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
1046112cec5SJoshua M. Clulow #include <sys/bootbanner.h>
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate #include <bsm/adt_event.h>
1077c478bd9Sstevel@tonic-gate #include <bsm/libbsm.h>
1087c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate #include <assert.h>
1117c478bd9Sstevel@tonic-gate #include <ctype.h>
112*a28480feSAndy Fiddaman #include <definit.h>
1137c478bd9Sstevel@tonic-gate #include <dirent.h>
1147c478bd9Sstevel@tonic-gate #include <errno.h>
1157c478bd9Sstevel@tonic-gate #include <fcntl.h>
1167c478bd9Sstevel@tonic-gate #include <libcontract.h>
1177c478bd9Sstevel@tonic-gate #include <libcontract_priv.h>
1187c478bd9Sstevel@tonic-gate #include <libintl.h>
1197c478bd9Sstevel@tonic-gate #include <libscf.h>
1207c478bd9Sstevel@tonic-gate #include <libscf_priv.h>
1217c478bd9Sstevel@tonic-gate #include <poll.h>
1227c478bd9Sstevel@tonic-gate #include <procfs.h>
1237c478bd9Sstevel@tonic-gate #include <signal.h>
1247c478bd9Sstevel@tonic-gate #include <stdarg.h>
1257c478bd9Sstevel@tonic-gate #include <stdio.h>
1267c478bd9Sstevel@tonic-gate #include <stdio_ext.h>
1277c478bd9Sstevel@tonic-gate #include <stdlib.h>
1287c478bd9Sstevel@tonic-gate #include <string.h>
1297c478bd9Sstevel@tonic-gate #include <strings.h>
1307c478bd9Sstevel@tonic-gate #include <syslog.h>
1317c478bd9Sstevel@tonic-gate #include <time.h>
1327c478bd9Sstevel@tonic-gate #include <ulimit.h>
1337c478bd9Sstevel@tonic-gate #include <unistd.h>
1347c478bd9Sstevel@tonic-gate #include <utmpx.h>
1357c478bd9Sstevel@tonic-gate #include <wait.h>
1367c478bd9Sstevel@tonic-gate #include <zone.h>
1377c478bd9Sstevel@tonic-gate #include <ucontext.h>
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate #undef	sleep
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate #define	fioctl(p, sptr, cmd)	ioctl(fileno(p), sptr, cmd)
1427c478bd9Sstevel@tonic-gate #define	min(a, b)		(((a) < (b)) ? (a) : (b))
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate #define	TRUE	1
1457c478bd9Sstevel@tonic-gate #define	FALSE	0
1467c478bd9Sstevel@tonic-gate #define	FAILURE	-1
1477c478bd9Sstevel@tonic-gate 
1480a1278f2SGary Mills #define	UT_USER_SZ	32	/* Size of a utmpx ut_user field */
1497c478bd9Sstevel@tonic-gate #define	UT_LINE_SZ	32	/* Size of a utmpx ut_line field */
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate /*
1527c478bd9Sstevel@tonic-gate  * SLEEPTIME	The number of seconds "init" sleeps between wakeups if
1537c478bd9Sstevel@tonic-gate  *		nothing else requires this "init" wakeup.
1547c478bd9Sstevel@tonic-gate  */
1557c478bd9Sstevel@tonic-gate #define	SLEEPTIME	(5 * 60)
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate /*
1587c478bd9Sstevel@tonic-gate  * MAXCMDL	The maximum length of a command string in inittab.
1597c478bd9Sstevel@tonic-gate  */
1607c478bd9Sstevel@tonic-gate #define	MAXCMDL	512
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate /*
1637c478bd9Sstevel@tonic-gate  * EXEC		The length of the prefix string added to all comamnds
1647c478bd9Sstevel@tonic-gate  *		found in inittab.
1657c478bd9Sstevel@tonic-gate  */
1667c478bd9Sstevel@tonic-gate #define	EXEC	(sizeof ("exec ") - 1)
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate /*
1697c478bd9Sstevel@tonic-gate  * TWARN	The amount of time between warning signal, SIGTERM,
1707c478bd9Sstevel@tonic-gate  *		and the fatal kill signal, SIGKILL.
1717c478bd9Sstevel@tonic-gate  */
1727c478bd9Sstevel@tonic-gate #define	TWARN	5
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate #define	id_eq(x, y)	((x[0] == y[0] && x[1] == y[1] && x[2] == y[2] &&\
1757c478bd9Sstevel@tonic-gate 			x[3] == y[3]) ? TRUE : FALSE)
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate /*
1787c478bd9Sstevel@tonic-gate  * The kernel's default umask is 022 these days; since some processes inherit
1797c478bd9Sstevel@tonic-gate  * their umask from init, init will set it from CMASK in /etc/default/init.
1807c478bd9Sstevel@tonic-gate  * init gets the default umask from the kernel, it sets it to 022 whenever
1817c478bd9Sstevel@tonic-gate  * it wants to create a file and reverts to CMASK afterwards.
1827c478bd9Sstevel@tonic-gate  */
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate static int cmask;
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate /*
1877c478bd9Sstevel@tonic-gate  * The following definitions, concluding with the 'lvls' array, provide a
1887c478bd9Sstevel@tonic-gate  * common mapping between level-name (like 'S'), signal number (state),
1897c478bd9Sstevel@tonic-gate  * run-level mask, and specific properties associated with a run-level.
1907c478bd9Sstevel@tonic-gate  * This array should be accessed using the routines lvlname_to_state(),
1917c478bd9Sstevel@tonic-gate  * lvlname_to_mask(), state_to_mask(), and state_to_flags().
1927c478bd9Sstevel@tonic-gate  */
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate /*
1957c478bd9Sstevel@tonic-gate  * Correspondence of signals to init actions.
1967c478bd9Sstevel@tonic-gate  */
1977c478bd9Sstevel@tonic-gate #define	LVLQ		SIGHUP
1987c478bd9Sstevel@tonic-gate #define	LVL0		SIGINT
1997c478bd9Sstevel@tonic-gate #define	LVL1		SIGQUIT
2007c478bd9Sstevel@tonic-gate #define	LVL2		SIGILL
2017c478bd9Sstevel@tonic-gate #define	LVL3		SIGTRAP
2027c478bd9Sstevel@tonic-gate #define	LVL4		SIGIOT
2037c478bd9Sstevel@tonic-gate #define	LVL5		SIGEMT
2047c478bd9Sstevel@tonic-gate #define	LVL6		SIGFPE
2057c478bd9Sstevel@tonic-gate #define	SINGLE_USER	SIGBUS
2067c478bd9Sstevel@tonic-gate #define	LVLa		SIGSEGV
2077c478bd9Sstevel@tonic-gate #define	LVLb		SIGSYS
2087c478bd9Sstevel@tonic-gate #define	LVLc		SIGPIPE
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate /*
2117c478bd9Sstevel@tonic-gate  * Bit Mask for each level.  Used to determine legal levels.
2127c478bd9Sstevel@tonic-gate  */
2137c478bd9Sstevel@tonic-gate #define	MASK0	0x0001
2147c478bd9Sstevel@tonic-gate #define	MASK1	0x0002
2157c478bd9Sstevel@tonic-gate #define	MASK2	0x0004
2167c478bd9Sstevel@tonic-gate #define	MASK3	0x0008
2177c478bd9Sstevel@tonic-gate #define	MASK4	0x0010
2187c478bd9Sstevel@tonic-gate #define	MASK5	0x0020
2197c478bd9Sstevel@tonic-gate #define	MASK6	0x0040
2207c478bd9Sstevel@tonic-gate #define	MASKSU	0x0080
2217c478bd9Sstevel@tonic-gate #define	MASKa	0x0100
2227c478bd9Sstevel@tonic-gate #define	MASKb	0x0200
2237c478bd9Sstevel@tonic-gate #define	MASKc	0x0400
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate #define	MASK_NUMERIC (MASK0 | MASK1 | MASK2 | MASK3 | MASK4 | MASK5 | MASK6)
2267c478bd9Sstevel@tonic-gate #define	MASK_abc (MASKa | MASKb | MASKc)
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate /*
2297c478bd9Sstevel@tonic-gate  * Flags to indicate properties of various states.
2307c478bd9Sstevel@tonic-gate  */
2317c478bd9Sstevel@tonic-gate #define	LSEL_RUNLEVEL	0x0001	/* runlevels you can transition to */
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate typedef struct lvl {
2347c478bd9Sstevel@tonic-gate 	int	lvl_state;
2357c478bd9Sstevel@tonic-gate 	int	lvl_mask;
2367c478bd9Sstevel@tonic-gate 	char	lvl_name;
2377c478bd9Sstevel@tonic-gate 	int	lvl_flags;
2387c478bd9Sstevel@tonic-gate } lvl_t;
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate static lvl_t lvls[] = {
2417c478bd9Sstevel@tonic-gate 	{ LVLQ,		0,	'Q', 0					},
2427c478bd9Sstevel@tonic-gate 	{ LVLQ,		0,	'q', 0					},
2436d59ee37Spaulson 	{ LVL0,		MASK0,	'0', LSEL_RUNLEVEL			},
244*a28480feSAndy Fiddaman 	{ LVL1,		MASK1,	'1', LSEL_RUNLEVEL			},
245*a28480feSAndy Fiddaman 	{ LVL2,		MASK2,	'2', LSEL_RUNLEVEL			},
246*a28480feSAndy Fiddaman 	{ LVL3,		MASK3,	'3', LSEL_RUNLEVEL			},
247*a28480feSAndy Fiddaman 	{ LVL4,		MASK4,	'4', LSEL_RUNLEVEL			},
248*a28480feSAndy Fiddaman 	{ LVL5,		MASK5,	'5', LSEL_RUNLEVEL			},
249*a28480feSAndy Fiddaman 	{ LVL6,		MASK6,	'6', LSEL_RUNLEVEL			},
250*a28480feSAndy Fiddaman 	{ SINGLE_USER,	MASKSU, 'S', LSEL_RUNLEVEL			},
251*a28480feSAndy Fiddaman 	{ SINGLE_USER,	MASKSU, 's', LSEL_RUNLEVEL			},
2527c478bd9Sstevel@tonic-gate 	{ LVLa,		MASKa,	'a', 0					},
2537c478bd9Sstevel@tonic-gate 	{ LVLb,		MASKb,	'b', 0					},
2547c478bd9Sstevel@tonic-gate 	{ LVLc,		MASKc,	'c', 0					}
2557c478bd9Sstevel@tonic-gate };
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate #define	LVL_NELEMS (sizeof (lvls) / sizeof (lvl_t))
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate /*
2607c478bd9Sstevel@tonic-gate  * Legal action field values.
2617c478bd9Sstevel@tonic-gate  */
2627c478bd9Sstevel@tonic-gate #define	OFF		0	/* Kill process if on, else ignore */
2637c478bd9Sstevel@tonic-gate #define	RESPAWN		1	/* Continuously restart process when it dies */
2647c478bd9Sstevel@tonic-gate #define	ONDEMAND	RESPAWN	/* Respawn for a, b, c type processes */
2657c478bd9Sstevel@tonic-gate #define	ONCE		2	/* Start process, do not respawn when dead */
2667c478bd9Sstevel@tonic-gate #define	WAIT		3	/* Perform once and wait to complete */
2677c478bd9Sstevel@tonic-gate #define	BOOT		4	/* Start at boot time only */
2687c478bd9Sstevel@tonic-gate #define	BOOTWAIT	5	/* Start at boot time and wait to complete */
2697c478bd9Sstevel@tonic-gate #define	POWERFAIL	6	/* Start on powerfail */
2707c478bd9Sstevel@tonic-gate #define	POWERWAIT	7	/* Start and wait for complete on powerfail */
2717c478bd9Sstevel@tonic-gate #define	INITDEFAULT	8	/* Default level "init" should start at */
2727c478bd9Sstevel@tonic-gate #define	SYSINIT		9	/* Actions performed before init speaks */
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate #define	M_OFF		0001
2757c478bd9Sstevel@tonic-gate #define	M_RESPAWN	0002
2767c478bd9Sstevel@tonic-gate #define	M_ONDEMAND	M_RESPAWN
2777c478bd9Sstevel@tonic-gate #define	M_ONCE		0004
2787c478bd9Sstevel@tonic-gate #define	M_WAIT		0010
2797c478bd9Sstevel@tonic-gate #define	M_BOOT		0020
2807c478bd9Sstevel@tonic-gate #define	M_BOOTWAIT	0040
2817c478bd9Sstevel@tonic-gate #define	M_PF		0100
2827c478bd9Sstevel@tonic-gate #define	M_PWAIT		0200
2837c478bd9Sstevel@tonic-gate #define	M_INITDEFAULT	0400
2847c478bd9Sstevel@tonic-gate #define	M_SYSINIT	01000
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate /* States for the inittab parser in getcmd(). */
2877c478bd9Sstevel@tonic-gate #define	ID	1
2887c478bd9Sstevel@tonic-gate #define	LEVELS	2
2897c478bd9Sstevel@tonic-gate #define	ACTION	3
2907c478bd9Sstevel@tonic-gate #define	COMMAND	4
2917c478bd9Sstevel@tonic-gate #define	COMMENT	5
2927c478bd9Sstevel@tonic-gate 
2937b209c2cSacruz /*
2947b209c2cSacruz  * inittab entry id constants
2957b209c2cSacruz  */
2967b209c2cSacruz #define	INITTAB_ENTRY_ID_SIZE 4
2977b209c2cSacruz #define	INITTAB_ENTRY_ID_STR_FORMAT "%.4s"	/* if INITTAB_ENTRY_ID_SIZE */
2987b209c2cSacruz 						/* changes, this should */
2997b209c2cSacruz 						/* change accordingly */
3007b209c2cSacruz 
3017c478bd9Sstevel@tonic-gate /*
3027c478bd9Sstevel@tonic-gate  * Init can be in any of three main states, "normal" mode where it is
3037c478bd9Sstevel@tonic-gate  * processing entries for the lines file in a normal fashion, "boot" mode,
3047c478bd9Sstevel@tonic-gate  * where it is only interested in the boot actions, and "powerfail" mode,
3057c478bd9Sstevel@tonic-gate  * where it is only interested in powerfail related actions. The following
3067c478bd9Sstevel@tonic-gate  * masks declare the legal actions for each mode.
3077c478bd9Sstevel@tonic-gate  */
3087c478bd9Sstevel@tonic-gate #define	NORMAL_MODES	(M_OFF | M_RESPAWN | M_ONCE | M_WAIT)
3097c478bd9Sstevel@tonic-gate #define	BOOT_MODES	(M_BOOT | M_BOOTWAIT)
3107c478bd9Sstevel@tonic-gate #define	PF_MODES	(M_PF | M_PWAIT)
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate struct PROC_TABLE {
3137b209c2cSacruz 	char	p_id[INITTAB_ENTRY_ID_SIZE];	/* Four letter unique id of */
3147b209c2cSacruz 						/* process */
3157c478bd9Sstevel@tonic-gate 	pid_t	p_pid;		/* Process id */
3167c478bd9Sstevel@tonic-gate 	short	p_count;	/* How many respawns of this command in */
3177c478bd9Sstevel@tonic-gate 				/*   the current series */
3187c478bd9Sstevel@tonic-gate 	long	p_time;		/* Start time for a series of respawns */
3197c478bd9Sstevel@tonic-gate 	short	p_flags;
3207c478bd9Sstevel@tonic-gate 	short	p_exit;		/* Exit status of a process which died */
3217c478bd9Sstevel@tonic-gate };
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate /*
3247c478bd9Sstevel@tonic-gate  * Flags for the "p_flags" word of a PROC_TABLE entry:
3257c478bd9Sstevel@tonic-gate  *
3267c478bd9Sstevel@tonic-gate  *	OCCUPIED	This slot in init's proc table is in use.
3277c478bd9Sstevel@tonic-gate  *
3287c478bd9Sstevel@tonic-gate  *	LIVING		Process is alive.
3297c478bd9Sstevel@tonic-gate  *
3307c478bd9Sstevel@tonic-gate  *	NOCLEANUP	efork() is not allowed to cleanup this entry even
3317c478bd9Sstevel@tonic-gate  *			if process is dead.
3327c478bd9Sstevel@tonic-gate  *
3337c478bd9Sstevel@tonic-gate  *	NAMED		This process has a name, i.e. came from inittab.
3347c478bd9Sstevel@tonic-gate  *
3357c478bd9Sstevel@tonic-gate  *	DEMANDREQUEST	Process started by a "telinit [abc]" command.  Processes
3367c478bd9Sstevel@tonic-gate  *			formed this way are respawnable and immune to level
3377c478bd9Sstevel@tonic-gate  *			changes as long as their entry exists in inittab.
3387c478bd9Sstevel@tonic-gate  *
3397c478bd9Sstevel@tonic-gate  *	TOUCHED		Flag used by remv() to determine whether it has looked
3407c478bd9Sstevel@tonic-gate  *			at an entry while checking for processes to be killed.
3417c478bd9Sstevel@tonic-gate  *
3427c478bd9Sstevel@tonic-gate  *	WARNED		Flag used by remv() to mark processes that have been
3437c478bd9Sstevel@tonic-gate  *			sent the SIGTERM signal.  If they don't die in 5
3447c478bd9Sstevel@tonic-gate  *			seconds, they are sent the SIGKILL signal.
3457c478bd9Sstevel@tonic-gate  *
3467c478bd9Sstevel@tonic-gate  *	KILLED		Flag used by remv() to mark procs that have been sent
3477c478bd9Sstevel@tonic-gate  *			the SIGTERM and SIGKILL signals.
3487c478bd9Sstevel@tonic-gate  *
3497c478bd9Sstevel@tonic-gate  *	PF_MASK		Bitwise or of legal flags, for sanity checking.
3507c478bd9Sstevel@tonic-gate  */
3517c478bd9Sstevel@tonic-gate #define	OCCUPIED	01
3527c478bd9Sstevel@tonic-gate #define	LIVING		02
3537c478bd9Sstevel@tonic-gate #define	NOCLEANUP	04
3547c478bd9Sstevel@tonic-gate #define	NAMED		010
3557c478bd9Sstevel@tonic-gate #define	DEMANDREQUEST	020
3567c478bd9Sstevel@tonic-gate #define	TOUCHED		040
3577c478bd9Sstevel@tonic-gate #define	WARNED		0100
3587c478bd9Sstevel@tonic-gate #define	KILLED		0200
3597c478bd9Sstevel@tonic-gate #define	PF_MASK		0377
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate /*
3627c478bd9Sstevel@tonic-gate  * Respawn limits for processes that are to be respawned:
3637c478bd9Sstevel@tonic-gate  *
3647c478bd9Sstevel@tonic-gate  *	SPAWN_INTERVAL	The number of seconds over which "init" will try to
3657c478bd9Sstevel@tonic-gate  *			respawn a process SPAWN_LIMIT times before it gets mad.
3667c478bd9Sstevel@tonic-gate  *
3677c478bd9Sstevel@tonic-gate  *	SPAWN_LIMIT	The number of respawns "init" will attempt in
3687c478bd9Sstevel@tonic-gate  *			SPAWN_INTERVAL seconds before it generates an
3697c478bd9Sstevel@tonic-gate  *			error message and inhibits further tries for
3707c478bd9Sstevel@tonic-gate  *			INHIBIT seconds.
3717c478bd9Sstevel@tonic-gate  *
3727c478bd9Sstevel@tonic-gate  *	INHIBIT		The number of seconds "init" ignores an entry it had
3737c478bd9Sstevel@tonic-gate  *			trouble spawning unless a "telinit Q" is received.
3747c478bd9Sstevel@tonic-gate  */
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate #define	SPAWN_INTERVAL	(2*60)
3777c478bd9Sstevel@tonic-gate #define	SPAWN_LIMIT	10
3787c478bd9Sstevel@tonic-gate #define	INHIBIT		(5*60)
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate /*
3817c478bd9Sstevel@tonic-gate  * The maximum number of decimal digits for an id_t.  (ceil(log10 (max_id)))
3827c478bd9Sstevel@tonic-gate  */
3837c478bd9Sstevel@tonic-gate #define	ID_MAX_STR_LEN	10
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate #define	NULLPROC	((struct PROC_TABLE *)(0))
3867c478bd9Sstevel@tonic-gate #define	NO_ROOM		((struct PROC_TABLE *)(FAILURE))
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate struct CMD_LINE {
3897b209c2cSacruz 	char c_id[INITTAB_ENTRY_ID_SIZE];	/* Four letter unique id of */
3907b209c2cSacruz 						/* process to be affected by */
3917b209c2cSacruz 						/* action */
3927c478bd9Sstevel@tonic-gate 	short c_levels;	/* Mask of legal levels for process */
3937c478bd9Sstevel@tonic-gate 	short c_action;	/* Mask for type of action required */
3947c478bd9Sstevel@tonic-gate 	char *c_command; /* Pointer to init command */
3957c478bd9Sstevel@tonic-gate };
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate struct	pidrec {
3987c478bd9Sstevel@tonic-gate 	int	pd_type;	/* Command type */
3997c478bd9Sstevel@tonic-gate 	pid_t	pd_pid;		/* pid to add or remove */
4007c478bd9Sstevel@tonic-gate };
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate /*
4037c478bd9Sstevel@tonic-gate  * pd_type's
4047c478bd9Sstevel@tonic-gate  */
4057c478bd9Sstevel@tonic-gate #define	ADDPID	1
4067c478bd9Sstevel@tonic-gate #define	REMPID	2
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate static struct	pidlist {
4097c478bd9Sstevel@tonic-gate 	pid_t	pl_pid;		/* pid to watch for */
4107c478bd9Sstevel@tonic-gate 	int	pl_dflag;	/* Flag indicating SIGCLD from this pid */
4117c478bd9Sstevel@tonic-gate 	short	pl_exit;	/* Exit status of proc */
4127c478bd9Sstevel@tonic-gate 	struct	pidlist	*pl_next; /* Next in list */
4137c478bd9Sstevel@tonic-gate } *Plhead, *Plfree;
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate /*
4167c478bd9Sstevel@tonic-gate  * The following structure contains a set of modes for /dev/syscon
41739cc040fSToomas Soome  * and should match the default contents of /etc/ioctl.syscon.
4187c478bd9Sstevel@tonic-gate  */
4197c478bd9Sstevel@tonic-gate static struct termios	dflt_termios = {
42039cc040fSToomas Soome 	.c_iflag = BRKINT|ICRNL|IXON|IMAXBEL,
42139cc040fSToomas Soome 	.c_oflag = OPOST|ONLCR|TAB3,
42239cc040fSToomas Soome 	.c_cflag = CS8|CREAD|B9600,
42339cc040fSToomas Soome 	.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN,
42439cc040fSToomas Soome 	.c_cc = { CINTR, CQUIT, CERASE, CKILL, CEOF, 0, 0, 0,
42539cc040fSToomas Soome 	    CSTART, CSTOP, CSWTCH, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT,
42639cc040fSToomas Soome 	    CSTATUS, CERASE2, 0
42739cc040fSToomas Soome 	}
4287c478bd9Sstevel@tonic-gate };
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate static struct termios	stored_syscon_termios;
4317c478bd9Sstevel@tonic-gate static int		write_ioctl = 0;	/* Rewrite /etc/ioctl.syscon */
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate static union WAKEUP {
4347c478bd9Sstevel@tonic-gate 	struct WAKEFLAGS {
4357c478bd9Sstevel@tonic-gate 		unsigned w_usersignal : 1;	/* User sent signal to "init" */
4367c478bd9Sstevel@tonic-gate 		unsigned w_childdeath : 1;	/* An "init" child died */
4377c478bd9Sstevel@tonic-gate 		unsigned w_powerhit : 1;	/* OS experienced powerfail */
4387c478bd9Sstevel@tonic-gate 	}	w_flags;
4397c478bd9Sstevel@tonic-gate 	int w_mask;
4407c478bd9Sstevel@tonic-gate } wakeup;
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate struct init_state {
4447c478bd9Sstevel@tonic-gate 	int			ist_runlevel;
4457c478bd9Sstevel@tonic-gate 	int			ist_num_proc;
4467c478bd9Sstevel@tonic-gate 	int			ist_utmpx_ok;
4477c478bd9Sstevel@tonic-gate 	struct PROC_TABLE	ist_proc_table[1];
4487c478bd9Sstevel@tonic-gate };
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate #define	cur_state	(g_state->ist_runlevel)
4517c478bd9Sstevel@tonic-gate #define	num_proc	(g_state->ist_num_proc)
4527c478bd9Sstevel@tonic-gate #define	proc_table	(g_state->ist_proc_table)
4537c478bd9Sstevel@tonic-gate #define	utmpx_ok	(g_state->ist_utmpx_ok)
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate /* Contract cookies. */
4567c478bd9Sstevel@tonic-gate #define	ORDINARY_COOKIE		0
4577c478bd9Sstevel@tonic-gate #define	STARTD_COOKIE		1
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate #ifndef NDEBUG
4617c478bd9Sstevel@tonic-gate #define	bad_error(func, err)	{					\
4627c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "%s:%d: %s() failed with unexpected "	\
4637c478bd9Sstevel@tonic-gate 	    "error %d.  Aborting.\n", __FILE__, __LINE__, (func), (err)); \
4647c478bd9Sstevel@tonic-gate 	abort();							\
4657c478bd9Sstevel@tonic-gate }
4667c478bd9Sstevel@tonic-gate #else
4677c478bd9Sstevel@tonic-gate #define	bad_error(func, err)	abort()
4687c478bd9Sstevel@tonic-gate #endif
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate /*
4727c478bd9Sstevel@tonic-gate  * Useful file and device names.
4737c478bd9Sstevel@tonic-gate  */
4747c478bd9Sstevel@tonic-gate static char *CONSOLE	  = "/dev/console";	/* Real system console */
47592ba7109Seschrock static char *INITPIPE_DIR = "/var/run";
47692ba7109Seschrock static char *INITPIPE	  = "/var/run/initpipe";
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate #define	INIT_STATE_DIR "/etc/svc/volatile"
4797c478bd9Sstevel@tonic-gate static const char * const init_state_file = INIT_STATE_DIR "/init.state";
4807c478bd9Sstevel@tonic-gate static const char * const init_next_state_file =
4817c478bd9Sstevel@tonic-gate 	INIT_STATE_DIR "/init-next.state";
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate static const int init_num_proc = 20;	/* Initial size of process table. */
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate static char *UTMPX	 = UTMPX_FILE;		/* Snapshot record file */
4867c478bd9Sstevel@tonic-gate static char *WTMPX	 = WTMPX_FILE;		/* Long term record file */
4877c478bd9Sstevel@tonic-gate static char *INITTAB	 = "/etc/inittab";	/* Script file for "init" */
4887c478bd9Sstevel@tonic-gate static char *SYSTTY	 = "/dev/systty";	/* System Console */
4897c478bd9Sstevel@tonic-gate static char *SYSCON	 = "/dev/syscon";	/* Virtual System console */
4907c478bd9Sstevel@tonic-gate static char *IOCTLSYSCON = "/etc/ioctl.syscon";	/* Last syscon modes */
491*a28480feSAndy Fiddaman static char *ENVFILE	 = DEFINIT_DEFAULT_FILE; /* Default env. */
4927c478bd9Sstevel@tonic-gate static char *SU	= "/etc/sulogin";	/* Super-user program for single user */
4937c478bd9Sstevel@tonic-gate static char *SH	= "/sbin/sh";		/* Standard shell */
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate /*
4967c478bd9Sstevel@tonic-gate  * Default Path.  /sbin is included in path only during sysinit phase
4977c478bd9Sstevel@tonic-gate  */
4987c478bd9Sstevel@tonic-gate #define	DEF_PATH	"PATH=/usr/sbin:/usr/bin"
4997c478bd9Sstevel@tonic-gate #define	INIT_PATH	"PATH=/sbin:/usr/sbin:/usr/bin"
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate static int	prior_state;
5027c478bd9Sstevel@tonic-gate static int	prev_state;	/* State "init" was in last time it woke */
5037c478bd9Sstevel@tonic-gate static int	new_state;	/* State user wants "init" to go to. */
50492ba7109Seschrock static int	lvlq_received;	/* Explicit request to examine state */
5057c478bd9Sstevel@tonic-gate static int	op_modes = BOOT_MODES; /* Current state of "init" */
5067c478bd9Sstevel@tonic-gate static int	Gchild = 0;	/* Flag to indicate "godchild" died, set in */
5077c478bd9Sstevel@tonic-gate 				/*   childeath() and cleared in cleanaux() */
5087c478bd9Sstevel@tonic-gate static int	Pfd = -1;	/* fd to receive pids thru */
5097c478bd9Sstevel@tonic-gate static unsigned int	spawncnt, pausecnt;
5107c478bd9Sstevel@tonic-gate static int	rsflag;		/* Set if a respawn has taken place */
5117c478bd9Sstevel@tonic-gate static volatile int time_up;	/* Flag set to TRUE by the alarm interrupt */
5127c478bd9Sstevel@tonic-gate 				/* routine each time an alarm interrupt */
5137c478bd9Sstevel@tonic-gate 				/* takes place. */
5147c478bd9Sstevel@tonic-gate static int	sflg = 0;	/* Set if we were booted -s to single user */
5157c478bd9Sstevel@tonic-gate static int	rflg = 0;	/* Set if booted -r, reconfigure devices */
5167c478bd9Sstevel@tonic-gate static int	bflg = 0;	/* Set if booted -b, don't run rc scripts */
5177c478bd9Sstevel@tonic-gate static pid_t	init_pid;	/* PID of "one true" init for current zone */
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate static struct init_state *g_state = NULL;
5207c478bd9Sstevel@tonic-gate static size_t	g_state_sz;
5217c478bd9Sstevel@tonic-gate static int	booting = 1;	/* Set while we're booting. */
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate /*
5247c478bd9Sstevel@tonic-gate  * Array for default global environment.
5257c478bd9Sstevel@tonic-gate  */
5267c478bd9Sstevel@tonic-gate #define	MAXENVENT	24	/* Max number of default env variables + 1 */
5277c478bd9Sstevel@tonic-gate 				/* init can use three itself, so this leaves */
5287c478bd9Sstevel@tonic-gate 				/* 20 for the administrator in ENVFILE. */
5297c478bd9Sstevel@tonic-gate static char	*glob_envp[MAXENVENT];	/* Array of environment strings */
5307c478bd9Sstevel@tonic-gate static int	glob_envn;		/* Number of environment strings */
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate static struct pollfd	poll_fds[1];
5347c478bd9Sstevel@tonic-gate static int		poll_nfds = 0;	/* poll_fds is uninitialized */
5357c478bd9Sstevel@tonic-gate 
5367b209c2cSacruz /*
5377b209c2cSacruz  * Contracts constants
5387b209c2cSacruz  */
5397b209c2cSacruz #define	SVC_INIT_PREFIX "init:/"
5407b209c2cSacruz #define	SVC_AUX_SIZE (INITTAB_ENTRY_ID_SIZE + 1)
5417b209c2cSacruz #define	SVC_FMRI_SIZE (sizeof (SVC_INIT_PREFIX) + INITTAB_ENTRY_ID_SIZE)
5427b209c2cSacruz 
5437c478bd9Sstevel@tonic-gate static int	legacy_tmpl = -1;	/* fd for legacy contract template */
5447c478bd9Sstevel@tonic-gate static int	startd_tmpl = -1;	/* fd for svc.startd's template */
5457b209c2cSacruz static char	startd_svc_aux[SVC_AUX_SIZE];
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate static char	startd_cline[256] = "";	/* svc.startd's command line */
5487c478bd9Sstevel@tonic-gate static int	do_restart_startd = 1;	/* Whether to restart svc.startd. */
5497c478bd9Sstevel@tonic-gate static char	*smf_options = NULL;	/* Options to give to startd. */
5507c478bd9Sstevel@tonic-gate static int	smf_debug = 0;		/* Messages for debugging smf(5) */
5517c478bd9Sstevel@tonic-gate static time_t	init_boot_time;		/* Substitute for kernel boot time. */
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate #define	NSTARTD_FAILURE_TIMES	3		/* trigger after 3 failures */
5547c478bd9Sstevel@tonic-gate #define	STARTD_FAILURE_RATE_NS	5000000000LL	/* 1 failure/5 seconds */
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate static hrtime_t	startd_failure_time[NSTARTD_FAILURE_TIMES];
5577c478bd9Sstevel@tonic-gate static uint_t	startd_failure_index;
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate static char	*prog_name(char *);
5617c478bd9Sstevel@tonic-gate static int	state_to_mask(int);
5627c478bd9Sstevel@tonic-gate static int	lvlname_to_mask(char, int *);
5637c478bd9Sstevel@tonic-gate static void	lscf_set_runlevel(char);
5647c478bd9Sstevel@tonic-gate static int	state_to_flags(int);
5657c478bd9Sstevel@tonic-gate static char	state_to_name(int);
5667c478bd9Sstevel@tonic-gate static int	lvlname_to_state(char);
5677c478bd9Sstevel@tonic-gate static int	getcmd(struct CMD_LINE *, char *);
5687c478bd9Sstevel@tonic-gate static int	realcon();
5697c478bd9Sstevel@tonic-gate static int	spawn_processes();
5707c478bd9Sstevel@tonic-gate static int	get_ioctl_syscon();
5717c478bd9Sstevel@tonic-gate static int	account(short, struct PROC_TABLE *, char *);
5727c478bd9Sstevel@tonic-gate static void	alarmclk();
5737c478bd9Sstevel@tonic-gate static void	childeath(int);
5747c478bd9Sstevel@tonic-gate static void	cleanaux();
5757c478bd9Sstevel@tonic-gate static void	clearent(pid_t, short);
5767c478bd9Sstevel@tonic-gate static void	console(boolean_t, char *, ...);
5777c478bd9Sstevel@tonic-gate static void	init_signals(void);
5787c478bd9Sstevel@tonic-gate static void	setup_pipe();
5797c478bd9Sstevel@tonic-gate static void	killproc(pid_t);
5807c478bd9Sstevel@tonic-gate static void	init_env();
5817c478bd9Sstevel@tonic-gate static void	boot_init();
5827c478bd9Sstevel@tonic-gate static void	powerfail();
5837c478bd9Sstevel@tonic-gate static void	remv();
5847c478bd9Sstevel@tonic-gate static void	write_ioctl_syscon();
5857c478bd9Sstevel@tonic-gate static void	spawn(struct PROC_TABLE *, struct CMD_LINE *);
5867c478bd9Sstevel@tonic-gate static void	setimer(int);
5877c478bd9Sstevel@tonic-gate static void	siglvl(int, siginfo_t *, ucontext_t *);
5887c478bd9Sstevel@tonic-gate static void	sigpoll(int);
5897c478bd9Sstevel@tonic-gate static void	enter_maintenance(void);
5907c478bd9Sstevel@tonic-gate static void	timer(int);
5917c478bd9Sstevel@tonic-gate static void	userinit(int, char **);
5927c478bd9Sstevel@tonic-gate static void	notify_pam_dead(struct utmpx *);
5937c478bd9Sstevel@tonic-gate static long	waitproc(struct PROC_TABLE *);
5947c478bd9Sstevel@tonic-gate static struct PROC_TABLE *efork(int, struct PROC_TABLE *, int);
5957c478bd9Sstevel@tonic-gate static struct PROC_TABLE *findpslot(struct CMD_LINE *);
5967c478bd9Sstevel@tonic-gate static void	increase_proc_table_size();
5977c478bd9Sstevel@tonic-gate static void	st_init();
5987c478bd9Sstevel@tonic-gate static void	st_write();
5997c478bd9Sstevel@tonic-gate static void	contracts_init();
6007c478bd9Sstevel@tonic-gate static void	contract_event(struct pollfd *);
6017c478bd9Sstevel@tonic-gate static int	startd_run(const char *, int, ctid_t);
6027c478bd9Sstevel@tonic-gate static void	startd_record_failure();
6037c478bd9Sstevel@tonic-gate static int	startd_failure_rate_critical();
6047c478bd9Sstevel@tonic-gate static char	*audit_boot_msg();
6057c478bd9Sstevel@tonic-gate static int	audit_put_record(int, int, char *);
6067c478bd9Sstevel@tonic-gate static void	update_boot_archive(int new_state);
6076112cec5SJoshua M. Clulow static void	init_bootbanner_print(const char *, uint_t);
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate int
main(int argc,char * argv[])6107c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
6117c478bd9Sstevel@tonic-gate {
6127c478bd9Sstevel@tonic-gate 	int	chg_lvl_flag = FALSE, print_banner = FALSE;
6137c478bd9Sstevel@tonic-gate 	int	may_need_audit = 1;
6147c478bd9Sstevel@tonic-gate 	int	c;
6157c478bd9Sstevel@tonic-gate 	char	*msg;
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 	/* Get a timestamp for use as boot time, if needed. */
6187c478bd9Sstevel@tonic-gate 	(void) time(&init_boot_time);
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate 	/* Get the default umask */
6217c478bd9Sstevel@tonic-gate 	cmask = umask(022);
6227c478bd9Sstevel@tonic-gate 	(void) umask(cmask);
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	/* Parse the arguments to init. Check for single user */
6257c478bd9Sstevel@tonic-gate 	opterr = 0;
6267c478bd9Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "brsm:")) != EOF) {
6277c478bd9Sstevel@tonic-gate 		switch (c) {
6287c478bd9Sstevel@tonic-gate 		case 'b':
6297c478bd9Sstevel@tonic-gate 			rflg = 0;
6307c478bd9Sstevel@tonic-gate 			bflg = 1;
6317c478bd9Sstevel@tonic-gate 			if (!sflg)
6327c478bd9Sstevel@tonic-gate 				sflg++;
6337c478bd9Sstevel@tonic-gate 			break;
6347c478bd9Sstevel@tonic-gate 		case 'r':
6357c478bd9Sstevel@tonic-gate 			bflg = 0;
6367c478bd9Sstevel@tonic-gate 			rflg++;
6377c478bd9Sstevel@tonic-gate 			break;
6387c478bd9Sstevel@tonic-gate 		case 's':
6397c478bd9Sstevel@tonic-gate 			if (!bflg)
6407c478bd9Sstevel@tonic-gate 				sflg++;
6417c478bd9Sstevel@tonic-gate 			break;
6427c478bd9Sstevel@tonic-gate 		case 'm':
6437c478bd9Sstevel@tonic-gate 			smf_options = optarg;
6447c478bd9Sstevel@tonic-gate 			smf_debug = (strstr(smf_options, "debug") != NULL);
6457c478bd9Sstevel@tonic-gate 			break;
6467c478bd9Sstevel@tonic-gate 		}
6477c478bd9Sstevel@tonic-gate 	}
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	/*
6507c478bd9Sstevel@tonic-gate 	 * Determine if we are the main init, or a user invoked init, whose job
6517c478bd9Sstevel@tonic-gate 	 * it is to inform init to change levels or perform some other action.
6527c478bd9Sstevel@tonic-gate 	 */
6537c478bd9Sstevel@tonic-gate 	if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid,
6547c478bd9Sstevel@tonic-gate 	    sizeof (init_pid)) != sizeof (init_pid)) {
6557c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "could not get pid for init\n");
6567c478bd9Sstevel@tonic-gate 		return (1);
6577c478bd9Sstevel@tonic-gate 	}
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	/*
6607c478bd9Sstevel@tonic-gate 	 * If this PID is not the same as the "true" init for the zone, then we
6617c478bd9Sstevel@tonic-gate 	 * must be in 'user' mode.
6627c478bd9Sstevel@tonic-gate 	 */
6637c478bd9Sstevel@tonic-gate 	if (getpid() != init_pid) {
6647c478bd9Sstevel@tonic-gate 		userinit(argc, argv);
6657c478bd9Sstevel@tonic-gate 	}
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	if (getzoneid() != GLOBAL_ZONEID) {
6687c478bd9Sstevel@tonic-gate 		print_banner = TRUE;
6697c478bd9Sstevel@tonic-gate 	}
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 	/*
6727c478bd9Sstevel@tonic-gate 	 * Initialize state (and set "booting").
6737c478bd9Sstevel@tonic-gate 	 */
6747c478bd9Sstevel@tonic-gate 	st_init();
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	if (booting && print_banner) {
6777c478bd9Sstevel@tonic-gate 		/*
6787c478bd9Sstevel@tonic-gate 		 * We want to print the boot banner as soon as
6797c478bd9Sstevel@tonic-gate 		 * possible.  In the global zone, the kernel does it,
6807c478bd9Sstevel@tonic-gate 		 * but we do not have that luxury in non-global zones,
6817c478bd9Sstevel@tonic-gate 		 * so we will print it here.
6827c478bd9Sstevel@tonic-gate 		 */
6836112cec5SJoshua M. Clulow #ifdef	LEGACY_BANNER
6846112cec5SJoshua M. Clulow 		struct utsname un;
6856112cec5SJoshua M. Clulow 		char buf[BUFSIZ];
6866112cec5SJoshua M. Clulow 		const char *bits;
6876112cec5SJoshua M. Clulow 		int r;
6886112cec5SJoshua M. Clulow 
6897c478bd9Sstevel@tonic-gate 		(void) uname(&un);
6906112cec5SJoshua M. Clulow 		if ((r = sysinfo(SI_ADDRESS_WIDTH, buf, sizeof (buf))) > 0 &&
6916112cec5SJoshua M. Clulow 		    r < sizeof (buf)) {
6926112cec5SJoshua M. Clulow 			bits = buf;
6936112cec5SJoshua M. Clulow 		} else {
6946112cec5SJoshua M. Clulow 			bits = "64";
6957c478bd9Sstevel@tonic-gate 		}
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 		console(B_FALSE,
6986112cec5SJoshua M. Clulow 		    "\n\n%s Release %s Version %s %s-bit\r\n",
6997c478bd9Sstevel@tonic-gate 		    un.sysname, un.release, un.version, bits);
7007c478bd9Sstevel@tonic-gate 		console(B_FALSE,
701861a9162SJohn Beck 		    "Copyright (c) 1983, 2010, Oracle and/or its affiliates."
7027c478bd9Sstevel@tonic-gate 		    " All rights reserved.\r\n");
7036112cec5SJoshua M. Clulow #else
7046112cec5SJoshua M. Clulow 		bootbanner_print(init_bootbanner_print, 0);
7056112cec5SJoshua M. Clulow #endif
7067c478bd9Sstevel@tonic-gate 	}
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	/*
7097c478bd9Sstevel@tonic-gate 	 * Get the ioctl settings for /dev/syscon from /etc/ioctl.syscon
7107c478bd9Sstevel@tonic-gate 	 * so that it can be brought up in the state it was in when the
7117c478bd9Sstevel@tonic-gate 	 * system went down; or set to defaults if ioctl.syscon isn't
7127c478bd9Sstevel@tonic-gate 	 * valid.
7137c478bd9Sstevel@tonic-gate 	 *
7147c478bd9Sstevel@tonic-gate 	 * This needs to be done even if we're restarting so reset_modes()
7157c478bd9Sstevel@tonic-gate 	 * will work in case we need to go down to single user mode.
7167c478bd9Sstevel@tonic-gate 	 */
7177c478bd9Sstevel@tonic-gate 	write_ioctl = get_ioctl_syscon();
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 	/*
7207c478bd9Sstevel@tonic-gate 	 * Set up all signals to be caught or ignored as appropriate.
7217c478bd9Sstevel@tonic-gate 	 */
7227c478bd9Sstevel@tonic-gate 	init_signals();
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	/* Load glob_envp from ENVFILE. */
7257c478bd9Sstevel@tonic-gate 	init_env();
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	contracts_init();
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	if (!booting) {
7307c478bd9Sstevel@tonic-gate 		/* cur_state should have been read in. */
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 		op_modes = NORMAL_MODES;
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 		/* Rewrite the ioctl file if it was bad. */
7357c478bd9Sstevel@tonic-gate 		if (write_ioctl)
7367c478bd9Sstevel@tonic-gate 			write_ioctl_syscon();
7377c478bd9Sstevel@tonic-gate 	} else {
7387c478bd9Sstevel@tonic-gate 		/*
7397c478bd9Sstevel@tonic-gate 		 * It's fine to boot up with state as zero, because
7407c478bd9Sstevel@tonic-gate 		 * startd will later tell us the real state.
7417c478bd9Sstevel@tonic-gate 		 */
7427c478bd9Sstevel@tonic-gate 		cur_state = 0;
7437c478bd9Sstevel@tonic-gate 		op_modes = BOOT_MODES;
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 		boot_init();
7467c478bd9Sstevel@tonic-gate 	}
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	prev_state = prior_state = cur_state;
7497c478bd9Sstevel@tonic-gate 
75092ba7109Seschrock 	setup_pipe();
75192ba7109Seschrock 
7527c478bd9Sstevel@tonic-gate 	/*
7537c478bd9Sstevel@tonic-gate 	 * Here is the beginning of the main process loop.
7547c478bd9Sstevel@tonic-gate 	 */
7557c478bd9Sstevel@tonic-gate 	for (;;) {
75692ba7109Seschrock 		if (lvlq_received) {
7577c478bd9Sstevel@tonic-gate 			setup_pipe();
75892ba7109Seschrock 			lvlq_received = B_FALSE;
75992ba7109Seschrock 		}
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 		/*
7627c478bd9Sstevel@tonic-gate 		 * Clean up any accounting records for dead "godchildren".
7637c478bd9Sstevel@tonic-gate 		 */
7647c478bd9Sstevel@tonic-gate 		if (Gchild)
7657c478bd9Sstevel@tonic-gate 			cleanaux();
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 		/*
7687c478bd9Sstevel@tonic-gate 		 * If in "normal" mode, check all living processes and initiate
7697c478bd9Sstevel@tonic-gate 		 * kill sequence on those that should not be there anymore.
7707c478bd9Sstevel@tonic-gate 		 */
7717c478bd9Sstevel@tonic-gate 		if (op_modes == NORMAL_MODES && cur_state != LVLa &&
7727c478bd9Sstevel@tonic-gate 		    cur_state != LVLb && cur_state != LVLc)
7737c478bd9Sstevel@tonic-gate 			remv();
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 		/*
7767c478bd9Sstevel@tonic-gate 		 * If a change in run levels is the reason we awoke, now do
7777c478bd9Sstevel@tonic-gate 		 * the accounting to report the change in the utmp file.
7787c478bd9Sstevel@tonic-gate 		 * Also report the change on the system console.
7797c478bd9Sstevel@tonic-gate 		 */
7807c478bd9Sstevel@tonic-gate 		if (chg_lvl_flag) {
7817c478bd9Sstevel@tonic-gate 			chg_lvl_flag = FALSE;
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate 			if (state_to_flags(cur_state) & LSEL_RUNLEVEL) {
7847c478bd9Sstevel@tonic-gate 				char rl = state_to_name(cur_state);
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 				if (rl != -1)
7877c478bd9Sstevel@tonic-gate 					lscf_set_runlevel(rl);
7887c478bd9Sstevel@tonic-gate 			}
7897c478bd9Sstevel@tonic-gate 
7907c478bd9Sstevel@tonic-gate 			may_need_audit = 1;
7917c478bd9Sstevel@tonic-gate 		}
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate 		/*
7947c478bd9Sstevel@tonic-gate 		 * Scan the inittab file and spawn and respawn processes that
7957c478bd9Sstevel@tonic-gate 		 * should be alive in the current state. If inittab does not
7967c478bd9Sstevel@tonic-gate 		 * exist default to  single user mode.
7977c478bd9Sstevel@tonic-gate 		 */
7987c478bd9Sstevel@tonic-gate 		if (spawn_processes() == FAILURE) {
7997c478bd9Sstevel@tonic-gate 			prior_state = prev_state;
8007c478bd9Sstevel@tonic-gate 			cur_state = SINGLE_USER;
8017c478bd9Sstevel@tonic-gate 		}
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 		/* If any respawns occurred, take note. */
8047c478bd9Sstevel@tonic-gate 		if (rsflag) {
8057c478bd9Sstevel@tonic-gate 			rsflag = 0;
8067c478bd9Sstevel@tonic-gate 			spawncnt++;
8077c478bd9Sstevel@tonic-gate 		}
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate 		/*
8107c478bd9Sstevel@tonic-gate 		 * If a powerfail signal was received during the last
8117c478bd9Sstevel@tonic-gate 		 * sequence, set mode to powerfail.  When spawn_processes() is
8127c478bd9Sstevel@tonic-gate 		 * entered the first thing it does is to check "powerhit".  If
8137c478bd9Sstevel@tonic-gate 		 * it is in PF_MODES then it clears "powerhit" and does
8147c478bd9Sstevel@tonic-gate 		 * a powerfail sequence.  If it is not in PF_MODES, then it
8157c478bd9Sstevel@tonic-gate 		 * puts itself in PF_MODES and then clears "powerhit".  Should
8167c478bd9Sstevel@tonic-gate 		 * "powerhit" get set again while spawn_processes() is working
8177c478bd9Sstevel@tonic-gate 		 * on a powerfail sequence, the following code  will see that
8187c478bd9Sstevel@tonic-gate 		 * spawn_processes() tries to execute the powerfail sequence
8197c478bd9Sstevel@tonic-gate 		 * again.  This guarantees that the powerfail sequence will be
8207c478bd9Sstevel@tonic-gate 		 * successfully completed before further processing takes
8217c478bd9Sstevel@tonic-gate 		 * place.
8227c478bd9Sstevel@tonic-gate 		 */
8237c478bd9Sstevel@tonic-gate 		if (wakeup.w_flags.w_powerhit) {
8247c478bd9Sstevel@tonic-gate 			op_modes = PF_MODES;
8257c478bd9Sstevel@tonic-gate 			/*
8267c478bd9Sstevel@tonic-gate 			 * Make sure that cur_state != prev_state so that
8277c478bd9Sstevel@tonic-gate 			 * ONCE and WAIT types work.
8287c478bd9Sstevel@tonic-gate 			 */
8297c478bd9Sstevel@tonic-gate 			prev_state = 0;
8307c478bd9Sstevel@tonic-gate 		} else if (op_modes != NORMAL_MODES) {
8317c478bd9Sstevel@tonic-gate 			/*
8327c478bd9Sstevel@tonic-gate 			 * If spawn_processes() was not just called while in
8337c478bd9Sstevel@tonic-gate 			 * normal mode, we set the mode to normal and it will
8347c478bd9Sstevel@tonic-gate 			 * be called again to check normal modes.  If we have
8357c478bd9Sstevel@tonic-gate 			 * just finished a powerfail sequence with prev_state
8367c478bd9Sstevel@tonic-gate 			 * equal to zero, we set prev_state equal to cur_state
8377c478bd9Sstevel@tonic-gate 			 * before the next pass through.
8387c478bd9Sstevel@tonic-gate 			 */
8397c478bd9Sstevel@tonic-gate 			if (op_modes == PF_MODES)
8407c478bd9Sstevel@tonic-gate 				prev_state = cur_state;
8417c478bd9Sstevel@tonic-gate 			op_modes = NORMAL_MODES;
8427c478bd9Sstevel@tonic-gate 		} else if (cur_state == LVLa || cur_state == LVLb ||
8437c478bd9Sstevel@tonic-gate 		    cur_state == LVLc) {
8447c478bd9Sstevel@tonic-gate 			/*
8457c478bd9Sstevel@tonic-gate 			 * If it was a change of levels that awakened us and the
8467c478bd9Sstevel@tonic-gate 			 * new level is one of the demand levels then reset
8477c478bd9Sstevel@tonic-gate 			 * cur_state to the previous state and do another scan
8487c478bd9Sstevel@tonic-gate 			 * to take care of the usual respawn actions.
8497c478bd9Sstevel@tonic-gate 			 */
8507c478bd9Sstevel@tonic-gate 			cur_state = prior_state;
8517c478bd9Sstevel@tonic-gate 			prior_state = prev_state;
8527c478bd9Sstevel@tonic-gate 			prev_state = cur_state;
8537c478bd9Sstevel@tonic-gate 		} else {
8547c478bd9Sstevel@tonic-gate 			prev_state = cur_state;
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate 			if (wakeup.w_mask == 0) {
8577c478bd9Sstevel@tonic-gate 				int ret;
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 				if (may_need_audit && (cur_state == LVL3)) {
8607c478bd9Sstevel@tonic-gate 					msg = audit_boot_msg();
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 					may_need_audit = 0;
8637c478bd9Sstevel@tonic-gate 					(void) audit_put_record(ADT_SUCCESS,
8647c478bd9Sstevel@tonic-gate 					    ADT_SUCCESS, msg);
8657c478bd9Sstevel@tonic-gate 					free(msg);
8667c478bd9Sstevel@tonic-gate 				}
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 				/*
8697c478bd9Sstevel@tonic-gate 				 * "init" is finished with all actions for
8707c478bd9Sstevel@tonic-gate 				 * the current wakeup.
8717c478bd9Sstevel@tonic-gate 				 */
8727c478bd9Sstevel@tonic-gate 				ret = poll(poll_fds, poll_nfds,
8737c478bd9Sstevel@tonic-gate 				    SLEEPTIME * MILLISEC);
8747c478bd9Sstevel@tonic-gate 				pausecnt++;
8757c478bd9Sstevel@tonic-gate 				if (ret > 0)
8767c478bd9Sstevel@tonic-gate 					contract_event(&poll_fds[0]);
8777c478bd9Sstevel@tonic-gate 				else if (ret < 0 && errno != EINTR)
8787c478bd9Sstevel@tonic-gate 					console(B_TRUE, "poll() error: %s\n",
8797c478bd9Sstevel@tonic-gate 					    strerror(errno));
8807c478bd9Sstevel@tonic-gate 			}
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 			if (wakeup.w_flags.w_usersignal) {
8837c478bd9Sstevel@tonic-gate 				/*
8847c478bd9Sstevel@tonic-gate 				 * Install the new level.  This could be a real
8857c478bd9Sstevel@tonic-gate 				 * change in levels  or a telinit [Q|a|b|c] or
8867c478bd9Sstevel@tonic-gate 				 * just a telinit to the same level at which
8877c478bd9Sstevel@tonic-gate 				 * we are running.
8887c478bd9Sstevel@tonic-gate 				 */
8897c478bd9Sstevel@tonic-gate 				if (new_state != cur_state) {
8907c478bd9Sstevel@tonic-gate 					if (new_state == LVLa ||
8917c478bd9Sstevel@tonic-gate 					    new_state == LVLb ||
8927c478bd9Sstevel@tonic-gate 					    new_state == LVLc) {
8937c478bd9Sstevel@tonic-gate 						prev_state = prior_state;
8947c478bd9Sstevel@tonic-gate 						prior_state = cur_state;
8957c478bd9Sstevel@tonic-gate 						cur_state = new_state;
8967c478bd9Sstevel@tonic-gate 					} else {
8977c478bd9Sstevel@tonic-gate 						prev_state = cur_state;
8987c478bd9Sstevel@tonic-gate 						if (cur_state >= 0)
8997c478bd9Sstevel@tonic-gate 							prior_state = cur_state;
9007c478bd9Sstevel@tonic-gate 						cur_state = new_state;
9017c478bd9Sstevel@tonic-gate 						chg_lvl_flag = TRUE;
9027c478bd9Sstevel@tonic-gate 					}
9037c478bd9Sstevel@tonic-gate 				}
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 				new_state = 0;
9067c478bd9Sstevel@tonic-gate 			}
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate 			if (wakeup.w_flags.w_powerhit)
9097c478bd9Sstevel@tonic-gate 				op_modes = PF_MODES;
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 			/*
9127c478bd9Sstevel@tonic-gate 			 * Clear all wakeup reasons.
9137c478bd9Sstevel@tonic-gate 			 */
9147c478bd9Sstevel@tonic-gate 			wakeup.w_mask = 0;
9157c478bd9Sstevel@tonic-gate 		}
9167c478bd9Sstevel@tonic-gate 	}
9177c478bd9Sstevel@tonic-gate 
9187c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
9197c478bd9Sstevel@tonic-gate }
9207c478bd9Sstevel@tonic-gate 
9216112cec5SJoshua M. Clulow static void
init_bootbanner_print(const char * line,uint_t num)9226112cec5SJoshua M. Clulow init_bootbanner_print(const char *line, uint_t num)
9236112cec5SJoshua M. Clulow {
9246112cec5SJoshua M. Clulow 	const char *pfx = (num == 0) ? "\n\n" : "";
9256112cec5SJoshua M. Clulow 
9266112cec5SJoshua M. Clulow 	console(B_FALSE, "%s%s\r\n", pfx, line);
9276112cec5SJoshua M. Clulow }
9286112cec5SJoshua M. Clulow 
9297c478bd9Sstevel@tonic-gate static void
update_boot_archive(int new_state)9307c478bd9Sstevel@tonic-gate update_boot_archive(int new_state)
9317c478bd9Sstevel@tonic-gate {
9327c478bd9Sstevel@tonic-gate 	if (new_state != LVL0 && new_state != LVL5 && new_state != LVL6)
9337c478bd9Sstevel@tonic-gate 		return;
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	if (getzoneid() != GLOBAL_ZONEID)
9367c478bd9Sstevel@tonic-gate 		return;
9377c478bd9Sstevel@tonic-gate 
93848847494SEnrico Perla - Sun Microsystems 	(void) system("/sbin/bootadm -ea update_all");
9397c478bd9Sstevel@tonic-gate }
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate /*
9427c478bd9Sstevel@tonic-gate  * void enter_maintenance()
9437c478bd9Sstevel@tonic-gate  *   A simple invocation of sulogin(1M), with no baggage, in the case that we
9447c478bd9Sstevel@tonic-gate  *   are unable to activate svc.startd(1M).  We fork; the child runs sulogin;
9457c478bd9Sstevel@tonic-gate  *   we wait for it to exit.
9467c478bd9Sstevel@tonic-gate  */
9477c478bd9Sstevel@tonic-gate static void
enter_maintenance()9487c478bd9Sstevel@tonic-gate enter_maintenance()
9497c478bd9Sstevel@tonic-gate {
9507c478bd9Sstevel@tonic-gate 	struct PROC_TABLE	*su_process;
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 	console(B_FALSE, "Requesting maintenance mode\n"
9537c478bd9Sstevel@tonic-gate 	    "(See /lib/svc/share/README for additional information.)\n");
954c39ec064SRoger A. Faulkner 	(void) sighold(SIGCLD);
9557c478bd9Sstevel@tonic-gate 	while ((su_process = efork(M_OFF, NULLPROC, NOCLEANUP)) == NO_ROOM)
9567c478bd9Sstevel@tonic-gate 		(void) pause();
957c39ec064SRoger A. Faulkner 	(void) sigrelse(SIGCLD);
9587c478bd9Sstevel@tonic-gate 	if (su_process == NULLPROC) {
9597c478bd9Sstevel@tonic-gate 		int fd;
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 		(void) fclose(stdin);
9627c478bd9Sstevel@tonic-gate 		(void) fclose(stdout);
9637c478bd9Sstevel@tonic-gate 		(void) fclose(stderr);
9647c478bd9Sstevel@tonic-gate 		closefrom(0);
9657c478bd9Sstevel@tonic-gate 
9667c478bd9Sstevel@tonic-gate 		fd = open(SYSCON, O_RDWR | O_NOCTTY);
9677c478bd9Sstevel@tonic-gate 		if (fd >= 0) {
9687c478bd9Sstevel@tonic-gate 			(void) dup2(fd, 1);
9697c478bd9Sstevel@tonic-gate 			(void) dup2(fd, 2);
9707c478bd9Sstevel@tonic-gate 		} else {
9717c478bd9Sstevel@tonic-gate 			/*
9727c478bd9Sstevel@tonic-gate 			 * Need to issue an error message somewhere.
9737c478bd9Sstevel@tonic-gate 			 */
9747c478bd9Sstevel@tonic-gate 			syslog(LOG_CRIT, "init[%d]: cannot open %s; %s\n",
9757c478bd9Sstevel@tonic-gate 			    getpid(), SYSCON, strerror(errno));
9767c478bd9Sstevel@tonic-gate 		}
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 		/*
9797c478bd9Sstevel@tonic-gate 		 * Execute the "su" program.
9807c478bd9Sstevel@tonic-gate 		 */
9817c478bd9Sstevel@tonic-gate 		(void) execle(SU, SU, "-", (char *)0, glob_envp);
9827c478bd9Sstevel@tonic-gate 		console(B_TRUE, "execle of %s failed: %s\n", SU,
9837c478bd9Sstevel@tonic-gate 		    strerror(errno));
9847c478bd9Sstevel@tonic-gate 		timer(5);
9857c478bd9Sstevel@tonic-gate 		exit(1);
9867c478bd9Sstevel@tonic-gate 	}
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	/*
9897c478bd9Sstevel@tonic-gate 	 * If we are the parent, wait around for the child to die
9907c478bd9Sstevel@tonic-gate 	 * or for "init" to be signaled to change levels.
9917c478bd9Sstevel@tonic-gate 	 */
9927c478bd9Sstevel@tonic-gate 	while (waitproc(su_process) == FAILURE) {
9937c478bd9Sstevel@tonic-gate 		/*
9947c478bd9Sstevel@tonic-gate 		 * All other reasons for waking are ignored when in
9957c478bd9Sstevel@tonic-gate 		 * single-user mode.  The only child we are interested
9967c478bd9Sstevel@tonic-gate 		 * in is being waited for explicitly by waitproc().
9977c478bd9Sstevel@tonic-gate 		 */
9987c478bd9Sstevel@tonic-gate 		wakeup.w_mask = 0;
9997c478bd9Sstevel@tonic-gate 	}
10007c478bd9Sstevel@tonic-gate }
10017c478bd9Sstevel@tonic-gate 
10027c478bd9Sstevel@tonic-gate /*
10037c478bd9Sstevel@tonic-gate  * remv() scans through "proc_table" and performs cleanup.  If
10047c478bd9Sstevel@tonic-gate  * there is a process in the table, which shouldn't be here at
10057c478bd9Sstevel@tonic-gate  * the current run level, then remv() kills the process.
10067c478bd9Sstevel@tonic-gate  */
10077c478bd9Sstevel@tonic-gate static void
remv()10087c478bd9Sstevel@tonic-gate remv()
10097c478bd9Sstevel@tonic-gate {
10107c478bd9Sstevel@tonic-gate 	struct PROC_TABLE	*process;
10117c478bd9Sstevel@tonic-gate 	struct CMD_LINE		cmd;
10127c478bd9Sstevel@tonic-gate 	char			cmd_string[MAXCMDL];
10137c478bd9Sstevel@tonic-gate 	int			change_level;
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate 	change_level = (cur_state != prev_state ? TRUE : FALSE);
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 	/*
10187c478bd9Sstevel@tonic-gate 	 * Clear the TOUCHED flag on all entries so that when we have
10197c478bd9Sstevel@tonic-gate 	 * finished scanning inittab, we will be able to tell if we
10207c478bd9Sstevel@tonic-gate 	 * have any processes for which there is no entry in inittab.
10217c478bd9Sstevel@tonic-gate 	 */
10227c478bd9Sstevel@tonic-gate 	for (process = proc_table;
10237c478bd9Sstevel@tonic-gate 	    (process < proc_table + num_proc); process++) {
10247c478bd9Sstevel@tonic-gate 		process->p_flags &= ~TOUCHED;
10257c478bd9Sstevel@tonic-gate 	}
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	/*
10287c478bd9Sstevel@tonic-gate 	 * Scan all inittab entries.
10297c478bd9Sstevel@tonic-gate 	 */
10307c478bd9Sstevel@tonic-gate 	while (getcmd(&cmd, &cmd_string[0]) == TRUE) {
10317c478bd9Sstevel@tonic-gate 		/* Scan for process which goes with this entry in inittab. */
10327c478bd9Sstevel@tonic-gate 		for (process = proc_table;
10337c478bd9Sstevel@tonic-gate 		    (process < proc_table + num_proc); process++) {
10347c478bd9Sstevel@tonic-gate 			if ((process->p_flags & OCCUPIED) == 0 ||
10357c478bd9Sstevel@tonic-gate 			    !id_eq(process->p_id, cmd.c_id))
10367c478bd9Sstevel@tonic-gate 				continue;
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 			/*
10397c478bd9Sstevel@tonic-gate 			 * This slot contains the process we are looking for.
10407c478bd9Sstevel@tonic-gate 			 */
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate 			/*
10437c478bd9Sstevel@tonic-gate 			 * Is the cur_state SINGLE_USER or is this process
10447c478bd9Sstevel@tonic-gate 			 * marked as "off" or was this proc started by some
10457c478bd9Sstevel@tonic-gate 			 * mechanism other than LVL{a|b|c} and the current level
10467c478bd9Sstevel@tonic-gate 			 * does not support this process?
10477c478bd9Sstevel@tonic-gate 			 */
10487c478bd9Sstevel@tonic-gate 			if (cur_state == SINGLE_USER ||
10497c478bd9Sstevel@tonic-gate 			    cmd.c_action == M_OFF ||
10507c478bd9Sstevel@tonic-gate 			    ((cmd.c_levels & state_to_mask(cur_state)) == 0 &&
10517c478bd9Sstevel@tonic-gate 			    (process->p_flags & DEMANDREQUEST) == 0)) {
10527c478bd9Sstevel@tonic-gate 				if (process->p_flags & LIVING) {
10537c478bd9Sstevel@tonic-gate 					/*
10547c478bd9Sstevel@tonic-gate 					 * Touch this entry so we know we have
10557c478bd9Sstevel@tonic-gate 					 * treated it.  Note that procs which
10567c478bd9Sstevel@tonic-gate 					 * are already dead at this point and
10577c478bd9Sstevel@tonic-gate 					 * should not be restarted are left
10587c478bd9Sstevel@tonic-gate 					 * untouched.  This causes their slot to
10597c478bd9Sstevel@tonic-gate 					 * be freed later after dead accounting
10607c478bd9Sstevel@tonic-gate 					 * is done.
10617c478bd9Sstevel@tonic-gate 					 */
10627c478bd9Sstevel@tonic-gate 					process->p_flags |= TOUCHED;
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 					if ((process->p_flags & KILLED) == 0) {
10657c478bd9Sstevel@tonic-gate 						if (change_level) {
10667c478bd9Sstevel@tonic-gate 							process->p_flags
10677c478bd9Sstevel@tonic-gate 							    |= WARNED;
10687c478bd9Sstevel@tonic-gate 							(void) kill(
10697c478bd9Sstevel@tonic-gate 							    process->p_pid,
10707c478bd9Sstevel@tonic-gate 							    SIGTERM);
10717c478bd9Sstevel@tonic-gate 						} else {
10727c478bd9Sstevel@tonic-gate 							/*
10737c478bd9Sstevel@tonic-gate 							 * Fork a killing proc
10747c478bd9Sstevel@tonic-gate 							 * so "init" can
10757c478bd9Sstevel@tonic-gate 							 * continue without
10767c478bd9Sstevel@tonic-gate 							 * having to pause for
10777c478bd9Sstevel@tonic-gate 							 * TWARN seconds.
10787c478bd9Sstevel@tonic-gate 							 */
10797c478bd9Sstevel@tonic-gate 							killproc(
10807c478bd9Sstevel@tonic-gate 							    process->p_pid);
10817c478bd9Sstevel@tonic-gate 						}
10827c478bd9Sstevel@tonic-gate 						process->p_flags |= KILLED;
10837c478bd9Sstevel@tonic-gate 					}
10847c478bd9Sstevel@tonic-gate 				}
10857c478bd9Sstevel@tonic-gate 			} else {
10867c478bd9Sstevel@tonic-gate 				/*
10877c478bd9Sstevel@tonic-gate 				 * Process can exist at current level.  If it is
10887c478bd9Sstevel@tonic-gate 				 * still alive or a DEMANDREQUEST we touch it so
10897c478bd9Sstevel@tonic-gate 				 * it will be left alone.  Otherwise we leave it
10907c478bd9Sstevel@tonic-gate 				 * untouched so it will be accounted for and
10917c478bd9Sstevel@tonic-gate 				 * cleaned up later in remv().  Dead
10927c478bd9Sstevel@tonic-gate 				 * DEMANDREQUESTs will be accounted but not
10937c478bd9Sstevel@tonic-gate 				 * freed.
10947c478bd9Sstevel@tonic-gate 				 */
10957c478bd9Sstevel@tonic-gate 				if (process->p_flags &
10967c478bd9Sstevel@tonic-gate 				    (LIVING|NOCLEANUP|DEMANDREQUEST))
10977c478bd9Sstevel@tonic-gate 					process->p_flags |= TOUCHED;
10987c478bd9Sstevel@tonic-gate 			}
10997c478bd9Sstevel@tonic-gate 
11007c478bd9Sstevel@tonic-gate 			break;
11017c478bd9Sstevel@tonic-gate 		}
11027c478bd9Sstevel@tonic-gate 	}
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 	st_write();
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate 	/*
11077c478bd9Sstevel@tonic-gate 	 * If this was a change of levels call, scan through the
11087c478bd9Sstevel@tonic-gate 	 * process table for processes that were warned to die.  If any
11097c478bd9Sstevel@tonic-gate 	 * are found that haven't left yet, sleep for TWARN seconds and
11107c478bd9Sstevel@tonic-gate 	 * then send final terminations to any that haven't died yet.
11117c478bd9Sstevel@tonic-gate 	 */
11127c478bd9Sstevel@tonic-gate 	if (change_level) {
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate 		/*
11157c478bd9Sstevel@tonic-gate 		 * Set the alarm for TWARN seconds on the assumption
11167c478bd9Sstevel@tonic-gate 		 * that there will be some that need to be waited for.
11177c478bd9Sstevel@tonic-gate 		 * This won't harm anything except we are guaranteed to
11187c478bd9Sstevel@tonic-gate 		 * wakeup in TWARN seconds whether we need to or not.
11197c478bd9Sstevel@tonic-gate 		 */
11207c478bd9Sstevel@tonic-gate 		setimer(TWARN);
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate 		/*
11237c478bd9Sstevel@tonic-gate 		 * Scan for processes which should be dying.  We hope they
11247c478bd9Sstevel@tonic-gate 		 * will die without having to be sent a SIGKILL signal.
11257c478bd9Sstevel@tonic-gate 		 */
11267c478bd9Sstevel@tonic-gate 		for (process = proc_table;
11277c478bd9Sstevel@tonic-gate 		    (process < proc_table + num_proc); process++) {
11287c478bd9Sstevel@tonic-gate 			/*
11297c478bd9Sstevel@tonic-gate 			 * If this process should die, hasn't yet, and the
11307c478bd9Sstevel@tonic-gate 			 * TWARN time hasn't expired yet, wait for process
11317c478bd9Sstevel@tonic-gate 			 * to die or for timer to expire.
11327c478bd9Sstevel@tonic-gate 			 */
11337c478bd9Sstevel@tonic-gate 			while (time_up == FALSE &&
11347c478bd9Sstevel@tonic-gate 			    (process->p_flags & (WARNED|LIVING|OCCUPIED)) ==
11357c478bd9Sstevel@tonic-gate 			    (WARNED|LIVING|OCCUPIED))
11367c478bd9Sstevel@tonic-gate 				(void) pause();
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 			if (time_up == TRUE)
11397c478bd9Sstevel@tonic-gate 				break;
11407c478bd9Sstevel@tonic-gate 		}
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate 		/*
11437c478bd9Sstevel@tonic-gate 		 * If we reached the end of the table without the timer
11447c478bd9Sstevel@tonic-gate 		 * expiring, then there are no procs which will have to be
11457c478bd9Sstevel@tonic-gate 		 * sent the SIGKILL signal.  If the timer has expired, then
11467c478bd9Sstevel@tonic-gate 		 * it is necessary to scan the table again and send signals
11477c478bd9Sstevel@tonic-gate 		 * to all processes which aren't going away nicely.
11487c478bd9Sstevel@tonic-gate 		 */
11497c478bd9Sstevel@tonic-gate 		if (time_up == TRUE) {
11507c478bd9Sstevel@tonic-gate 			for (process = proc_table;
11517c478bd9Sstevel@tonic-gate 			    (process < proc_table + num_proc); process++) {
11527c478bd9Sstevel@tonic-gate 				if ((process->p_flags &
11537c478bd9Sstevel@tonic-gate 				    (WARNED|LIVING|OCCUPIED)) ==
11547c478bd9Sstevel@tonic-gate 				    (WARNED|LIVING|OCCUPIED))
11557c478bd9Sstevel@tonic-gate 					(void) kill(process->p_pid, SIGKILL);
11567c478bd9Sstevel@tonic-gate 			}
11577c478bd9Sstevel@tonic-gate 		}
11587c478bd9Sstevel@tonic-gate 		setimer(0);
11597c478bd9Sstevel@tonic-gate 	}
11607c478bd9Sstevel@tonic-gate 
11617c478bd9Sstevel@tonic-gate 	/*
11627c478bd9Sstevel@tonic-gate 	 * Rescan the proc_table for two kinds of entry, those marked LIVING,
11637c478bd9Sstevel@tonic-gate 	 * NAMED, which don't have an entry in inittab (haven't been TOUCHED
11647c478bd9Sstevel@tonic-gate 	 * by the above scanning), and haven't been sent kill signals, and
11657c478bd9Sstevel@tonic-gate 	 * those entries marked not LIVING, NAMED.  The former procs are killed.
11667c478bd9Sstevel@tonic-gate 	 * The latter have DEAD_PROCESS accounting done and the slot cleared.
11677c478bd9Sstevel@tonic-gate 	 */
11687c478bd9Sstevel@tonic-gate 	for (process = proc_table;
11697c478bd9Sstevel@tonic-gate 	    (process < proc_table + num_proc); process++) {
11707c478bd9Sstevel@tonic-gate 		if ((process->p_flags & (LIVING|NAMED|TOUCHED|KILLED|OCCUPIED))
11717c478bd9Sstevel@tonic-gate 		    == (LIVING|NAMED|OCCUPIED)) {
11727c478bd9Sstevel@tonic-gate 			killproc(process->p_pid);
11737c478bd9Sstevel@tonic-gate 			process->p_flags |= KILLED;
11747c478bd9Sstevel@tonic-gate 		} else if ((process->p_flags & (LIVING|NAMED|OCCUPIED)) ==
11757c478bd9Sstevel@tonic-gate 		    (NAMED|OCCUPIED)) {
11767c478bd9Sstevel@tonic-gate 			(void) account(DEAD_PROCESS, process, NULL);
11777c478bd9Sstevel@tonic-gate 			/*
11787c478bd9Sstevel@tonic-gate 			 * If this named proc hasn't been TOUCHED, then free the
11797c478bd9Sstevel@tonic-gate 			 * space. It has either died of it's own accord, but
11807c478bd9Sstevel@tonic-gate 			 * isn't respawnable or it was killed because it
11817c478bd9Sstevel@tonic-gate 			 * shouldn't exist at this level.
11827c478bd9Sstevel@tonic-gate 			 */
11837c478bd9Sstevel@tonic-gate 			if ((process->p_flags & TOUCHED) == 0)
11847c478bd9Sstevel@tonic-gate 				process->p_flags = 0;
11857c478bd9Sstevel@tonic-gate 		}
11867c478bd9Sstevel@tonic-gate 	}
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate 	st_write();
11897c478bd9Sstevel@tonic-gate }
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate /*
11927c478bd9Sstevel@tonic-gate  * Extract the svc.startd command line and whether to restart it from its
11937c478bd9Sstevel@tonic-gate  * inittab entry.
11947c478bd9Sstevel@tonic-gate  */
11957c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11967c478bd9Sstevel@tonic-gate static void
process_startd_line(struct CMD_LINE * cmd,char * cmd_string)11977c478bd9Sstevel@tonic-gate process_startd_line(struct CMD_LINE *cmd, char *cmd_string)
11987c478bd9Sstevel@tonic-gate {
11997c478bd9Sstevel@tonic-gate 	size_t sz;
12007c478bd9Sstevel@tonic-gate 
12017c478bd9Sstevel@tonic-gate 	/* Save the command line. */
12027c478bd9Sstevel@tonic-gate 	if (sflg || rflg) {
12037c478bd9Sstevel@tonic-gate 		/* Also append -r or -s. */
12047c478bd9Sstevel@tonic-gate 		(void) strlcpy(startd_cline, cmd_string, sizeof (startd_cline));
12057c478bd9Sstevel@tonic-gate 		(void) strlcat(startd_cline, " -", sizeof (startd_cline));
12067c478bd9Sstevel@tonic-gate 		if (sflg)
12077c478bd9Sstevel@tonic-gate 			sz = strlcat(startd_cline, "s", sizeof (startd_cline));
12087c478bd9Sstevel@tonic-gate 		if (rflg)
12097c478bd9Sstevel@tonic-gate 			sz = strlcat(startd_cline, "r", sizeof (startd_cline));
12107c478bd9Sstevel@tonic-gate 	} else {
12117c478bd9Sstevel@tonic-gate 		sz = strlcpy(startd_cline, cmd_string, sizeof (startd_cline));
12127c478bd9Sstevel@tonic-gate 	}
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 	if (sz >= sizeof (startd_cline)) {
12157c478bd9Sstevel@tonic-gate 		console(B_TRUE,
12167c478bd9Sstevel@tonic-gate 		    "svc.startd command line too long.  Ignoring.\n");
12177c478bd9Sstevel@tonic-gate 		startd_cline[0] = '\0';
12187c478bd9Sstevel@tonic-gate 		return;
12197c478bd9Sstevel@tonic-gate 	}
12207c478bd9Sstevel@tonic-gate }
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate /*
12237c478bd9Sstevel@tonic-gate  * spawn_processes() scans inittab for entries which should be run at this
12247c478bd9Sstevel@tonic-gate  * mode.  Processes which should be running but are not, are started.
12257c478bd9Sstevel@tonic-gate  */
12267c478bd9Sstevel@tonic-gate static int
spawn_processes()12277c478bd9Sstevel@tonic-gate spawn_processes()
12287c478bd9Sstevel@tonic-gate {
12297c478bd9Sstevel@tonic-gate 	struct PROC_TABLE		*pp;
12307c478bd9Sstevel@tonic-gate 	struct