27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
505b96demjnelson * Common Development and Distribution License (the "License").
605b96demjnelson * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
207c478bdstevel@tonic-gate */
236112cecJoshua M. Clulow * Copyright 2020 Oxide Computer Company
240a1278fGary Mills * Copyright (c) 2013 Gary Mills
250a1278fGary Mills *
26861a916John Beck * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
277c478bdstevel@tonic-gate */
297c478bdstevel@tonic-gate/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
307c478bdstevel@tonic-gate/*	  All Rights Reserved  	*/
337c478bdstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988
347c478bdstevel@tonic-gate * The Regents of the University of California
357c478bdstevel@tonic-gate * All Rights Reserved
367c478bdstevel@tonic-gate *
377c478bdstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from
387c478bdstevel@tonic-gate * software developed by the University of California, Berkeley, and its
397c478bdstevel@tonic-gate * contributors.
407c478bdstevel@tonic-gate */
437c478bdstevel@tonic-gate * init(1M) is the general process spawning program.  Its primary job is to
447c478bdstevel@tonic-gate * start and restart svc.startd for smf(5).  For backwards-compatibility it also
457c478bdstevel@tonic-gate * spawns and respawns processes according to /etc/inittab and the current
467c478bdstevel@tonic-gate * run-level.  It reads /etc/default/inittab for general configuration.
477c478bdstevel@tonic-gate *
487c478bdstevel@tonic-gate * To change run-levels the system administrator runs init from the command
497c478bdstevel@tonic-gate * line with a level name.  init signals svc.startd via libscf and directs the
507c478bdstevel@tonic-gate * zone's init (pid 1 in the global zone) what to do by sending it a signal;
517c478bdstevel@tonic-gate * these signal numbers are commonly refered to in the code as 'states'.  Valid
527c478bdstevel@tonic-gate * run-levels are [sS0123456].  Additionally, init can be given directives
537c478bdstevel@tonic-gate * [qQabc], which indicate actions to be taken pertaining to /etc/inittab.
547c478bdstevel@tonic-gate *
557c478bdstevel@tonic-gate * When init processes inittab entries, it finds processes that are to be
567c478bdstevel@tonic-gate * spawned at various run-levels.  inittab contains the set of the levels for
577c478bdstevel@tonic-gate * which each inittab entry is valid.
587c478bdstevel@tonic-gate *
597c478bdstevel@tonic-gate * State File and Restartability
607c478bdstevel@tonic-gate *   Premature exit by init(1M) is handled as a special case by the kernel:
617c478bdstevel@tonic-gate *   init(1M) will be immediately re-executed, retaining its original PID.  (PID
627c478bdstevel@tonic-gate *   1 in the global zone.)  To track the processes it has previously spawned,
637c478bdstevel@tonic-gate *   as well as other mutable state, init(1M) regularly updates a state file
647c478bdstevel@tonic-gate *   such that its subsequent invocations have knowledge of its various
657c478bdstevel@tonic-gate *   dependent processes and duties.
667c478bdstevel@tonic-gate *
677c478bdstevel@tonic-gate * Process Contracts
687c478bdstevel@tonic-gate *   We start svc.startd(1M) in a contract and transfer inherited contracts when
697c478bdstevel@tonic-gate *   restarting it.  Everything else is started using the legacy contract
707c478bdstevel@tonic-gate *   template, and the created contracts are abandoned when they become empty.
717c478bdstevel@tonic-gate *
727c478bdstevel@tonic-gate * utmpx Entry Handling
737c478bdstevel@tonic-gate *   Because init(1M) no longer governs the startup process, its knowledge of
747c478bdstevel@tonic-gate *   when utmpx becomes writable is indirect.  However, spawned processes
757c478bdstevel@tonic-gate *   expect to be constructed with valid utmpx entries.  As a result, attempts
767c478bdstevel@tonic-gate *   to write normal entries will be retried until successful.
777c478bdstevel@tonic-gate *
787c478bdstevel@tonic-gate * Maintenance Mode
797c478bdstevel@tonic-gate *   In certain failure scenarios, init(1M) will enter a maintenance mode, in
807c478bdstevel@tonic-gate *   which it invokes sulogin(1M) to allow the operator an opportunity to
817c478bdstevel@tonic-gate *   repair the system.  Normally, this operation is performed as a
827c478bdstevel@tonic-gate *   fork(2)-exec(2)-waitpid(3C) sequence with the parent waiting for repair or
837c478bdstevel@tonic-gate *   diagnosis to be completed.  In the cases that fork(2) requests themselves
847c478bdstevel@tonic-gate *   fail, init(1M) will directly execute sulogin(1M), and allow the kernel to
857c478bdstevel@tonic-gate *   restart init(1M) on exit from the operator session.
867c478bdstevel@tonic-gate *
877c478bdstevel@tonic-gate *   One scenario where init(1M) enters its maintenance mode is when
887c478bdstevel@tonic-gate *   svc.startd(1M) begins to fail rapidly, defined as when the average time
897c478bdstevel@tonic-gate *   between recent failures drops below a given threshold.
907c478bdstevel@tonic-gate */
927c478bdstevel@tonic-gate#include <sys/contract/process.h>
937c478bdstevel@tonic-gate#include <sys/ctfs.h>
947c478bdstevel@tonic-gate#include <sys/stat.h>
957c478bdstevel@tonic-gate#include <sys/statvfs.h>
967c478bdstevel@tonic-gate#include <sys/stropts.h>
977c478bdstevel@tonic-gate#include <sys/systeminfo.h>
987c478bdstevel@tonic-gate#include <sys/time.h>
997c478bdstevel@tonic-gate#include <sys/termios.h>
1007c478bdstevel@tonic-gate#include <sys/tty.h>
1017c478bdstevel@tonic-gate#include <sys/types.h>
1027c478bdstevel@tonic-gate#include <sys/utsname.h>
1036112cecJoshua M. Clulow#include <sys/bootbanner.h>
1057c478bdstevel@tonic-gate#include <bsm/adt_event.h>
1067c478bdstevel@tonic-gate#include <bsm/libbsm.h>
1077c478bdstevel@tonic-gate#include <security/pam_appl.h>
1097c478bdstevel@tonic-gate#include <assert.h>
1107c478bdstevel@tonic-gate#include <ctype.h>
1117c478bdstevel@tonic-gate#include <dirent.h>
1127c478bdstevel@tonic-gate#include <errno.h>
1137c478bdstevel@tonic-gate#include <fcntl.h>
1147c478bdstevel@tonic-gate#include <libcontract.h>
1157c478bdstevel@tonic-gate#include <libcontract_priv.h>
1167c478bdstevel@tonic-gate#include <libintl.h>
1177c478bdstevel@tonic-gate#include <libscf.h>
1187c478bdstevel@tonic-gate#include <libscf_priv.h>
1197c478bdstevel@tonic-gate#include <poll.h>
1207c478bdstevel@tonic-gate#include <procfs.h>
1217c478bdstevel@tonic-gate#include <signal.h>
1227c478bdstevel@tonic-gate#include <stdarg.h>
1237c478bdstevel@tonic-gate#include <stdio.h>
1247c478bdstevel@tonic-gate#include <stdio_ext.h>
1257c478bdstevel@tonic-gate#include <stdlib.h>
1267c478bdstevel@tonic-gate#include <string.h>
1277c478bdstevel@tonic-gate#include <strings.h>
1287c478bdstevel@tonic-gate#include <syslog.h>
1297c478bdstevel@tonic-gate#include <time.h>
1307c478bdstevel@tonic-gate#include <ulimit.h>
1317c478bdstevel@tonic-gate#include <unistd.h>
1327c478bdstevel@tonic-gate#include <utmpx.h>
1337c478bdstevel@tonic-gate#include <wait.h>
1347c478bdstevel@tonic-gate#include <zone.h>
1357c478bdstevel@tonic-gate#include <ucontext.h>
1377c478bdstevel@tonic-gate#undef	sleep
1397c478bdstevel@tonic-gate#define	fioctl(p, sptr, cmd)	ioctl(fileno(p), sptr, cmd)
1407c478bdstevel@tonic-gate#define	min(a, b)		(((a) < (b)) ? (a) : (b))
1427c478bdstevel@tonic-gate#define	TRUE	1
1437c478bdstevel@tonic-gate#define	FALSE	0
1447c478bdstevel@tonic-gate#define	FAILURE	-1
1460a1278fGary Mills#define	UT_USER_SZ	32	/* Size of a utmpx ut_user field */
1477c478bdstevel@tonic-gate#define	UT_LINE_SZ	32	/* Size of a utmpx ut_line field */
1507c478bdstevel@tonic-gate * SLEEPTIME	The number of seconds "init" sleeps between wakeups if
1517c478bdstevel@tonic-gate *		nothing else requires this "init" wakeup.
1527c478bdstevel@tonic-gate */
1537c478bdstevel@tonic-gate#define	SLEEPTIME	(5 * 60)
1567c478bdstevel@tonic-gate * MAXCMDL	The maximum length of a command string in inittab.
1577c478bdstevel@tonic-gate */
1587c478bdstevel@tonic-gate#define	MAXCMDL	512
1617c478bdstevel@tonic-gate * EXEC		The length of the prefix string added to all comamnds
1627c478bdstevel@tonic-gate *		found in inittab.
1637c478bdstevel@tonic-gate */
1647c478bdstevel@tonic-gate#define	EXEC	(sizeof ("exec ") - 1)
1677c478bdstevel@tonic-gate * TWARN	The amount of time between warning signal, SIGTERM,
1687c478bdstevel@tonic-gate *		and the fatal kill signal, SIGKILL.
1697c478bdstevel@tonic-gate */
1707c478bdstevel@tonic-gate#define	TWARN	5
1727c478bdstevel@tonic-gate#define	id_eq(x, y)	((x[0] == y[0] && x[1] == y[1] && x[2] == y[2] &&\
1737c478bdstevel@tonic-gate			x[3] == y[3]) ? TRUE : FALSE)
1767c478bdstevel@tonic-gate * The kernel's default umask is 022 these days; since some processes inherit
1777c478bdstevel@tonic-gate * their umask from init, init will set it from CMASK in /etc/default/init.
1787c478bdstevel@tonic-gate * init gets the default umask from the kernel, it sets it to 022 whenever
1797c478bdstevel@tonic-gate * it wants to create a file and reverts to CMASK afterwards.
1807c478bdstevel@tonic-gate */
1827c478bdstevel@tonic-gatestatic int cmask;
1857c478bdstevel@tonic-gate * The following definitions, concluding with the 'lvls' array, provide a
1867c478bdstevel@tonic-gate * common mapping between level-name (like 'S'), signal number (state),
1877c478bdstevel@tonic-gate * run-level mask, and specific properties associated with a run-level.
1887c478bdstevel@tonic-gate * This array should be accessed using the routines lvlname_to_state(),
1897c478bdstevel@tonic-gate * lvlname_to_mask(), state_to_mask(), and state_to_flags().
1907c478bdstevel@tonic-gate */
1937c478bdstevel@tonic-gate * Correspondence of signals to init actions.
1947c478bdstevel@tonic-gate */
1957c478bdstevel@tonic-gate#define	LVLQ		SIGHUP
1967c478bdstevel@tonic-gate#define	LVL0		SIGINT
1977c478bdstevel@tonic-gate#define	LVL1		SIGQUIT
1987c478bdstevel@tonic-gate#define	LVL2		SIGILL
1997c478bdstevel@tonic-gate#define	LVL3		SIGTRAP
2007c478bdstevel@tonic-gate#define	LVL4		SIGIOT
2017c478bdstevel@tonic-gate#define	LVL5		SIGEMT
2027c478bdstevel@tonic-gate#define	LVL6		SIGFPE
2037c478bdstevel@tonic-gate#define	SINGLE_USER	SIGBUS
2047c478bdstevel@tonic-gate#define	LVLa		SIGSEGV
2057c478bdstevel@tonic-gate#define	LVLb		SIGSYS
2067c478bdstevel@tonic-gate#define	LVLc		SIGPIPE
2097c478bdstevel@tonic-gate * Bit Mask for each level.  Used to determine legal levels.
2107c478bdstevel@tonic-gate */
2117c478bdstevel@tonic-gate#define	MASK0	0x0001
2127c478bdstevel@tonic-gate#define	MASK1	0x0002
2137c478bdstevel@tonic-gate#define	MASK2	0x0004
2147c478bdstevel@tonic-gate#define	MASK3	0x0008
2157c478bdstevel@tonic-gate#define	MASK4	0x0010
2167c478bdstevel@tonic-gate#define	MASK5	0x0020
2177c478bdstevel@tonic-gate#define	MASK6	0x0040
2187c478bdstevel@tonic-gate#define	MASKSU	0x0080
2197c478bdstevel@tonic-gate#define	MASKa	0x0100
2207c478bdstevel@tonic-gate#define	MASKb	0x0200
2217c478bdstevel@tonic-gate#define	MASKc	0x0400
2237c478bdstevel@tonic-gate#define	MASK_NUMERIC (MASK0 | MASK1 | MASK2 | MASK3 | MASK4 | MASK5 | MASK6)
2247c478bdstevel@tonic-gate#define	MASK_abc (MASKa | MASKb | MASKc)
2277c478bdstevel@tonic-gate * Flags to indicate properties of various states.
2287c478bdstevel@tonic-gate */
2297c478bdstevel@tonic-gate#define	LSEL_RUNLEVEL	0x0001	/* runlevels you can transition to */
2317c478bdstevel@tonic-gatetypedef struct lvl {
2327c478bdstevel@tonic-gate	int	lvl_state;
2337c478bdstevel@tonic-gate	int	lvl_mask;
2347c478bdstevel@tonic-gate	char	lvl_name;
2357c478bdstevel@tonic-gate	int	lvl_flags;
2367c478bdstevel@tonic-gate} lvl_t;
2387c478bdstevel@tonic-gatestatic lvl_t lvls[] = {
2397c478bdstevel@tonic-gate	{ LVLQ,		0,	'Q', 0					},
2407c478bdstevel@tonic-gate	{ LVLQ,		0,	'q', 0					},
2416d59ee3paulson	{ LVL0,		MASK0,	'0', LSEL_RUNLEVEL			},
2426d59ee3paulson	{ LVL1, 	MASK1,	'1', LSEL_RUNLEVEL			},
2437c478bdstevel@tonic-gate	{ LVL2, 	MASK2,	'2', LSEL_RUNLEVEL			},
2447c478bdstevel@tonic-gate	{ LVL3, 	MASK3,	'3', LSEL_RUNLEVEL			},
2457c478bdstevel@tonic-gate	{ LVL4, 	MASK4,	'4', LSEL_RUNLEVEL			},
2466d59ee3paulson	{ LVL5, 	MASK5,	'5', LSEL_RUNLEVEL			},
2476d59ee3paulson	{ LVL6, 	MASK6, 	'6', LSEL_RUNLEVEL			},
2486d59ee3paulson	{ SINGLE_USER, 	MASKSU, 'S', LSEL_RUNLEVEL			},
2496d59ee3paulson	{ SINGLE_USER, 	MASKSU, 's', LSEL_RUNLEVEL			},
2507c478bdstevel@tonic-gate	{ LVLa,		MASKa,	'a', 0					},
2517c478bdstevel@tonic-gate	{ LVLb,		MASKb,	'b', 0					},
2527c478bdstevel@tonic-gate	{ LVLc,		MASKc,	'c', 0					}
2557c478bdstevel@tonic-gate#define	LVL_NELEMS (sizeof (lvls) / sizeof (lvl_t))
2587c478bdstevel@tonic-gate * Legal action field values.
2597c478bdstevel@tonic-gate */
2607c478bdstevel@tonic-gate#define	OFF		0	/* Kill process if on, else ignore */
2617c478bdstevel@tonic-gate#define	RESPAWN		1	/* Continuously restart process when it dies */
2627c478bdstevel@tonic-gate#define	ONDEMAND	RESPAWN	/* Respawn for a, b, c type processes */
2637c478bdstevel@tonic-gate#define	ONCE		2	/* Start process, do not respawn when dead */
2647c478bdstevel@tonic-gate#define	WAIT		3	/* Perform once and wait to complete */
2657c478bdstevel@tonic-gate#define	BOOT		4	/* Start at boot time only */
2667c478bdstevel@tonic-gate#define	BOOTWAIT	5	/* Start at boot time and wait to complete */
2677c478bdstevel@tonic-gate#define	POWERFAIL	6	/* Start on powerfail */
2687c478bdstevel@tonic-gate#define	POWERWAIT	7	/* Start and wait for complete on powerfail */
2697c478bdstevel@tonic-gate#define	INITDEFAULT	8	/* Default level "init" should start at */
2707c478bdstevel@tonic-gate#define	SYSINIT		9	/* Actions performed before init speaks */
2727c478bdstevel@tonic-gate#define	M_OFF		0001
2737c478bdstevel@tonic-gate#define	M_RESPAWN	0002
2747c478bdstevel@tonic-gate#define	M_ONDEMAND	M_RESPAWN
2757c478bdstevel@tonic-gate#define	M_ONCE		0004
2767c478bdstevel@tonic-gate#define	M_WAIT		0010
2777c478bdstevel@tonic-gate#define	M_BOOT		0020
2787c478bdstevel@tonic-gate#define	M_BOOTWAIT	0040
2797c478bdstevel@tonic-gate#define	M_PF		0100
2807c478bdstevel@tonic-gate#define	M_PWAIT		0200
2817c478bdstevel@tonic-gate#define	M_INITDEFAULT	0400
2827c478bdstevel@tonic-gate#define	M_SYSINIT	01000
2847c478bdstevel@tonic-gate/* States for the inittab parser in getcmd(). */
2857c478bdstevel@tonic-gate#define	ID	1
2867c478bdstevel@tonic-gate#define	LEVELS	2
2877c478bdstevel@tonic-gate#define	ACTION	3
2887c478bdstevel@tonic-gate#define	COMMAND	4
2897c478bdstevel@tonic-gate#define	COMMENT	5
2927b209c2acruz * inittab entry id constants
2937b209c2acruz */
2947b209c2acruz#define	INITTAB_ENTRY_ID_SIZE 4
2957b209c2acruz#define	INITTAB_ENTRY_ID_STR_FORMAT "%.4s"	/* if INITTAB_ENTRY_ID_SIZE */
2967b209c2acruz						/* changes, this should */
2977b209c2acruz						/* change accordingly */
3007c478bdstevel@tonic-gate * Init can be in any of three main states, "normal" mode where it is
3017c478bdstevel@tonic-gate * processing entries for the lines file in a normal fashion, "boot" mode,
3027c478bdstevel@tonic-gate * where it is only interested in the boot actions, and "powerfail" mode,
3037c478bdstevel@tonic-gate * where it is only interested in powerfail related actions. The following
3047c478bdstevel@tonic-gate * masks declare the legal actions for each mode.
3057c478bdstevel@tonic-gate */
3067c478bdstevel@tonic-gate#define	NORMAL_MODES	(M_OFF | M_RESPAWN | M_ONCE | M_WAIT)
3077c478bdstevel@tonic-gate#define	BOOT_MODES	(M_BOOT | M_BOOTWAIT)
3087c478bdstevel@tonic-gate#define	PF_MODES	(M_PF | M_PWAIT)
3107c478bdstevel@tonic-gatestruct PROC_TABLE {
3117b209c2acruz	char	p_id[INITTAB_ENTRY_ID_SIZE];	/* Four letter unique id of */
3127b209c2acruz						/* process */
3137c478bdstevel@tonic-gate	pid_t	p_pid;		/* Process id */
3147c478bdstevel@tonic-gate	short	p_count;	/* How many respawns of this command in */
3157c478bdstevel@tonic-gate				/*   the current series */
3167c478bdstevel@tonic-gate	long	p_time;		/* Start time for a series of respawns */
3177c478bdstevel@tonic-gate	short	p_flags;
3187c478bdstevel@tonic-gate	short	p_exit;		/* Exit status of a process which died */
3227c478bdstevel@tonic-gate * Flags for the "p_flags" word of a PROC_TABLE entry:
3237c478bdstevel@tonic-gate *
3247c478bdstevel@tonic-gate *	OCCUPIED	This slot in init's proc table is in use.
3257c478bdstevel@tonic-gate *
3267c478bdstevel@tonic-gate *	LIVING		Process is alive.
3277c478bdstevel@tonic-gate *
3287c478bdstevel@tonic-gate *	NOCLEANUP	efork() is not allowed to cleanup this entry even
3297c478bdstevel@tonic-gate *			if process is dead.
3307c478bdstevel@tonic-gate *
3317c478bdstevel@tonic-gate *	NAMED		This process has a name, i.e. came from inittab.
3327c478bdstevel@tonic-gate *
3337c478bdstevel@tonic-gate *	DEMANDREQUEST	Process started by a "telinit [abc]" command.  Processes
3347c478bdstevel@tonic-gate *			formed this way are respawnable and immune to level
3357c478bdstevel@tonic-gate *			changes as long as their entry exists in inittab.
3367c478bdstevel@tonic-gate *
3377c478bdstevel@tonic-gate *	TOUCHED		Flag used by remv() to determine whether it has looked
3387c478bdstevel@tonic-gate *			at an entry while checking for processes to be killed.
3397c478bdstevel@tonic-gate *
3407c478bdstevel@tonic-gate *	WARNED		Flag used by remv() to mark processes that have been
3417c478bdstevel@tonic-gate *			sent the SIGTERM signal.  If they don't die in 5
3427c478bdstevel@tonic-gate *			seconds, they are sent the SIGKILL signal.
3437c478bdstevel@tonic-gate *
3447c478bdstevel@tonic-gate *	KILLED		Flag used by remv() to mark procs that have been sent
3457c478bdstevel@tonic-gate *			the SIGTERM and SIGKILL signals.
3467c478bdstevel@tonic-gate *
3477c478bdstevel@tonic-gate *	PF_MASK		Bitwise or of legal flags, for sanity checking.
3487c478bdstevel@tonic-gate */
3497c478bdstevel@tonic-gate#define	OCCUPIED	01
3507c478bdstevel@tonic-gate#define	LIVING		02
3517c478bdstevel@tonic-gate#define	NOCLEANUP	04
3527c478bdstevel@tonic-gate#define	NAMED		010
3537c478bdstevel@tonic-gate#define	DEMANDREQUEST	020
3547c478bdstevel@tonic-gate#define	TOUCHED		040
3557c478bdstevel@tonic-gate#define	WARNED		0100
3567c478bdstevel@tonic-gate#define	KILLED		0200
3577c478bdstevel@tonic-gate#define	PF_MASK		0377
3607c478bdstevel@tonic-gate * Respawn limits for processes that are to be respawned:
3617c478bdstevel@tonic-gate *
3627c478bdstevel@tonic-gate *	SPAWN_INTERVAL	The number of seconds over which "init" will try to
3637c478bdstevel@tonic-gate *			respawn a process SPAWN_LIMIT times before it gets mad.
3647c478bdstevel@tonic-gate *
3657c478bdstevel@tonic-gate *	SPAWN_LIMIT	The number of respawns "init" will attempt in
3667c478bdstevel@tonic-gate *			SPAWN_INTERVAL seconds before it generates an
3677c478bdstevel@tonic-gate *			error message and inhibits further tries for
3687c478bdstevel@tonic-gate *			INHIBIT seconds.
3697c478bdstevel@tonic-gate *
3707c478bdstevel@tonic-gate *	INHIBIT		The number of seconds "init" ignores an entry it had
3717c478bdstevel@tonic-gate *			trouble spawning unless a "telinit Q" is received.
3727c478bdstevel@tonic-gate */
3747c478bdstevel@tonic-gate#define	SPAWN_INTERVAL	(2*60)
3757c478bdstevel@tonic-gate#define	SPAWN_LIMIT	10
3767c478bdstevel@tonic-gate#define	INHIBIT		(5*60)
3797c478bdstevel@tonic-gate * The maximum number of decimal digits for an id_t.  (ceil(log10 (max_id)))
3807c478bdstevel@tonic-gate */
3817c478bdstevel@tonic-gate#define	ID_MAX_STR_LEN	10
3837c478bdstevel@tonic-gate#define	NULLPROC	((struct PROC_TABLE *)(0))
3847c478bdstevel@tonic-gate#define	NO_ROOM		((struct PROC_TABLE *)(FAILURE))
3867c478bdstevel@tonic-gatestruct CMD_LINE {
3877b209c2acruz	char c_id[INITTAB_ENTRY_ID_SIZE];	/* Four letter unique id of */
3887b209c2acruz						/* process to be affected by */
3897b209c2acruz						/* action */
3907c478bdstevel@tonic-gate	short c_levels;	/* Mask of legal levels for process */
3917c478bdstevel@tonic-gate	short c_action;	/* Mask for type of action required */
3927c478bdstevel@tonic-gate	char *c_command; /* Pointer to init command */
3957c478bdstevel@tonic-gatestruct	pidrec {
3967c478bdstevel@tonic-gate	int	pd_type;	/* Command type */
3977c478bdstevel@tonic-gate	pid_t	pd_pid;		/* pid to add or remove */
4017c478bdstevel@tonic-gate * pd_type's
4027c478bdstevel@tonic-gate */
4037c478bdstevel@tonic-gate#define	ADDPID	1
4047c478bdstevel@tonic-gate#define	REMPID	2
4067c478bdstevel@tonic-gatestatic struct	pidlist {
4077c478bdstevel@tonic-gate	pid_t	pl_pid;		/* pid to watch for */
4087c478bdstevel@tonic-gate	int	pl_dflag;	/* Flag indicating SIGCLD from this pid */
4097c478bdstevel@tonic-gate	short	pl_exit;	/* Exit status of proc */
4107c478bdstevel@tonic-gate	struct	pidlist	*pl_next; /* Next in list */
4117c478bdstevel@tonic-gate} *Plhead, *Plfree;
4147c478bdstevel@tonic-gate * The following structure contains a set of modes for /dev/syscon
41539cc040Toomas Soome * and should match the default contents of /etc/ioctl.syscon.
4167c478bdstevel@tonic-gate */
4177c478bdstevel@tonic-gatestatic struct termios	dflt_termios = {
41839cc040Toomas Soome	.c_iflag = BRKINT|ICRNL|IXON|IMAXBEL,
41939cc040Toomas Soome	.c_oflag = OPOST|ONLCR|TAB3,
42039cc040Toomas Soome	.c_cflag = CS8|CREAD|B9600,
42239cc040Toomas Soome	.c_cc = { CINTR, CQUIT, CERASE, CKILL, CEOF, 0, 0, 0,
42439cc040Toomas Soome	    CSTATUS, CERASE2, 0
42539cc040Toomas Soome	}
4287c478bdstevel@tonic-gatestatic struct termios	stored_syscon_termios;
4297c478bdstevel@tonic-gatestatic int		write_ioctl = 0;	/* Rewrite /etc/ioctl.syscon */
4317c478bdstevel@tonic-gatestatic union WAKEUP {
4327c478bdstevel@tonic-gate	struct WAKEFLAGS {
4337c478bdstevel@tonic-gate		unsigned w_usersignal : 1;	/* User sent signal to "init" */
4347c478bdstevel@tonic-gate		unsigned w_childdeath : 1;	/* An "init" child died */
4357c478bdstevel@tonic-gate		unsigned w_powerhit : 1;	/* OS experienced powerfail */
4367c478bdstevel@tonic-gate	}	w_flags;
4377c478bdstevel@tonic-gate	int w_mask;
4387c478bdstevel@tonic-gate} wakeup;
4417c478bdstevel@tonic-gatestruct init_state {
4427c478bdstevel@tonic-gate	int			ist_runlevel;
4437c478bdstevel@tonic-gate	int			ist_num_proc;
4447c478bdstevel@tonic-gate	int			ist_utmpx_ok;
4457c478bdstevel@tonic-gate	struct PROC_TABLE	ist_proc_table[1];
4487c478bdstevel@tonic-gate#define	cur_state	(g_state->ist_runlevel)
4497c478bdstevel@tonic-gate#define	num_proc	(g_state->ist_num_proc)
4507c478bdstevel@tonic-gate#define	proc_table	(g_state->ist_proc_table)
4517c478bdstevel@tonic-gate#define	utmpx_ok	(g_state->ist_utmpx_ok)
4537c478bdstevel@tonic-gate/* Contract cookies. */
4547c478bdstevel@tonic-gate#define	ORDINARY_COOKIE		0
4557c478bdstevel@tonic-gate#define	STARTD_COOKIE		1
4587c478bdstevel@tonic-gate#ifndef NDEBUG
4597c478bdstevel@tonic-gate#define	bad_error(func, err)	{					\
4607c478bdstevel@tonic-gate	(void) fprintf(stderr, "%s:%d: %s() failed with unexpected "	\
4617c478bdstevel@tonic-gate	    "error %d.  Aborting.\n", __FILE__, __LINE__, (func), (err)); \
4627c478bdstevel@tonic-gate	abort();							\
4657c478bdstevel@tonic-gate#define	bad_error(func, err)	abort()
4707c478bdstevel@tonic-gate * Useful file and device names.
4717c478bdstevel@tonic-gate */
4727c478bdstevel@tonic-gatestatic char *CONSOLE	  = "/dev/console";	/* Real system console */
47392ba710eschrockstatic char *INITPIPE_DIR = "/var/run";
47492ba710eschrockstatic char *INITPIPE	  = "/var/run/initpipe";
4767c478bdstevel@tonic-gate#define	INIT_STATE_DIR "/etc/svc/volatile"
4777c478bdstevel@tonic-gatestatic const char * const init_state_file = INIT_STATE_DIR "/init.state";
4787c478bdstevel@tonic-gatestatic const char * const init_next_state_file =
4797c478bdstevel@tonic-gate	INIT_STATE_DIR "/init-next.state";
4817c478bdstevel@tonic-gatestatic const int init_num_proc = 20;	/* Initial size of process table. */
4837c478bdstevel@tonic-gatestatic char *UTMPX	 = UTMPX_FILE;		/* Snapshot record file */
4847c478bdstevel@tonic-gatestatic char *WTMPX	 = WTMPX_FILE;		/* Long term record file */
4857c478bdstevel@tonic-gatestatic char *INITTAB	 = "/etc/inittab";	/* Script file for "init" */
4867c478bdstevel@tonic-gatestatic char *SYSTTY	 = "/dev/systty";	/* System Console */
4877c478bdstevel@tonic-gatestatic char *SYSCON	 = "/dev/syscon";	/* Virtual System console */
4887c478bdstevel@tonic-gatestatic char *IOCTLSYSCON = "/etc/ioctl.syscon";	/* Last syscon modes */
4897c478bdstevel@tonic-gatestatic char *ENVFILE	 = "/etc/default/init";	/* Default env. */
4907c478bdstevel@tonic-gatestatic char *SU	= "/etc/sulogin";	/* Super-user program for single user */
4917c478bdstevel@tonic-gatestatic char *SH	= "/sbin/sh";		/* Standard shell */
4947c478bdstevel@tonic-gate * Default Path.  /sbin is included in path only during sysinit phase
4957c478bdstevel@tonic-gate */
4967c478bdstevel@tonic-gate#define	DEF_PATH	"PATH=/usr/sbin:/usr/bin"
4977c478bdstevel@tonic-gate#define	INIT_PATH	"PATH=/sbin:/usr/sbin:/usr/bin"
4997c478bdstevel@tonic-gatestatic int	prior_state;
5007c478bdstevel@tonic-gatestatic int	prev_state;	/* State "init" was in last time it woke */
5017c478bdstevel@tonic-gatestatic int	new_state;	/* State user wants "init" to go to. */
50292ba710eschrockstatic int	lvlq_received;	/* Explicit request to examine state */
5037c478bdstevel@tonic-gatestatic int	op_modes = BOOT_MODES; /* Current state of "init" */
5047c478bdstevel@tonic-gatestatic int	Gchild = 0;	/* Flag to indicate "godchild" died, set in */
5057c478bdstevel@tonic-gate				/*   childeath() and cleared in cleanaux() */
5067c478bdstevel@tonic-gatestatic int	Pfd = -1;	/* fd to receive pids thru */
5077c478bdstevel@tonic-gatestatic unsigned int	spawncnt, pausecnt;
5087c478bdstevel@tonic-gatestatic int	rsflag;		/* Set if a respawn has taken place */
5097c478bdstevel@tonic-gatestatic volatile int time_up;	/* Flag set to TRUE by the alarm interrupt */
5107c478bdstevel@tonic-gate				/* routine each time an alarm interrupt */
5117c478bdstevel@tonic-gate				/* takes place. */
5127c478bdstevel@tonic-gatestatic int	sflg = 0;	/* Set if we were booted -s to single user */
5137c478bdstevel@tonic-gatestatic int	rflg = 0;	/* Set if booted -r, reconfigure devices */
5147c478bdstevel@tonic-gatestatic int	bflg = 0;	/* Set if booted -b, don't run rc scripts */
5157c478bdstevel@tonic-gatestatic pid_t	init_pid;	/* PID of "one true" init for current zone */
5177c478bdstevel@tonic-gatestatic struct init_state *g_state = NULL;
5187c478bdstevel@tonic-gatestatic size_t	g_state_sz;
5197c478bdstevel@tonic-gatestatic int	booting = 1;	/* Set while we're booting. */
5227c478bdstevel@tonic-gate * Array for default global environment.
5237c478bdstevel@tonic-gate */
5247c478bdstevel@tonic-gate#define	MAXENVENT	24	/* Max number of default env variables + 1 */
5257c478bdstevel@tonic-gate				/* init can use three itself, so this leaves */
5267c478bdstevel@tonic-gate				/* 20 for the administrator in ENVFILE. */
5277c478bdstevel@tonic-gatestatic char	*glob_envp[MAXENVENT];	/* Array of environment strings */
5287c478bdstevel@tonic-gatestatic int	glob_envn;		/* Number of environment strings */
5317c478bdstevel@tonic-gatestatic struct pollfd	poll_fds[1];
5327c478bdstevel@tonic-gatestatic int		poll_nfds = 0;	/* poll_fds is uninitialized */
5357b209c2acruz * Contracts constants
5367b209c2acruz */
5377b209c2acruz#define	SVC_INIT_PREFIX "init:/"
5387b209c2acruz#define	SVC_AUX_SIZE (INITTAB_ENTRY_ID_SIZE + 1)
5397b209c2acruz#define	SVC_FMRI_SIZE (sizeof (SVC_INIT_PREFIX) + INITTAB_ENTRY_ID_SIZE)
5417c478bdstevel@tonic-gatestatic int	legacy_tmpl = -1;	/* fd for legacy contract template */
5427c478bdstevel@tonic-gatestatic int	startd_tmpl = -1;	/* fd for svc.startd's template */
5437b209c2acruzstatic char	startd_svc_aux[SVC_AUX_SIZE];
5457c478bdstevel@tonic-gatestatic char	startd_cline[256] = "";	/* svc.startd's command line */
5467c478bdstevel@tonic-gatestatic int	do_restart_startd = 1;	/* Whether to restart svc.startd. */
5477c478bdstevel@tonic-gatestatic char	*smf_options = NULL;	/* Options to give to startd. */
5487c478bdstevel@tonic-gatestatic int	smf_debug = 0;		/* Messages for debugging smf(5) */
5497c478bdstevel@tonic-gatestatic time_t	init_boot_time;		/* Substitute for kernel boot time. */
5517c478bdstevel@tonic-gate#define	NSTARTD_FAILURE_TIMES	3		/* trigger after 3 failures */
5527c478bdstevel@tonic-gate#define	STARTD_FAILURE_RATE_NS	5000000000LL	/* 1 failure/5 seconds */
5547c478bdstevel@tonic-gatestatic hrtime_t	startd_failure_time[NSTARTD_FAILURE_TIMES];
5557c478bdstevel@tonic-gatestatic uint_t	startd_failure_index;
5587c478bdstevel@tonic-gatestatic char	*prog_name(char *);
5597c478bdstevel@tonic-gatestatic int	state_to_mask(int);
5607c478bdstevel@tonic-gatestatic int	lvlname_to_mask(char, int *);
5617c478bdstevel@tonic-gatestatic void	lscf_set_runlevel(char);
5627c478bdstevel@tonic-gatestatic int	state_to_flags(int);
5637c478bdstevel@tonic-gatestatic char	state_to_name(int);
5647c478bdstevel@tonic-gatestatic int	lvlname_to_state(char);
5657c478bdstevel@tonic-gatestatic int	getcmd(struct CMD_LINE *, char *);
5667c478bdstevel@tonic-gatestatic int	realcon();
5677c478bdstevel@tonic-gatestatic int	spawn_processes();
5687c478bdstevel@tonic-gatestatic int	get_ioctl_syscon();
5697c478bdstevel@tonic-gatestatic int	account(short, struct PROC_TABLE *, char *);
5707c478bdstevel@tonic-gatestatic void	alarmclk();
5717c478bdstevel@tonic-gatestatic void	childeath(int);
5727c478bdstevel@tonic-gatestatic void	cleanaux();
5737c478bdstevel@tonic-gatestatic void	clearent(pid_t, short);
5747c478bdstevel@tonic-gatestatic void	console(boolean_t, char *, ...);
5757c478bdstevel@tonic-gatestatic void	init_signals(void);
5767c478bdstevel@tonic-gatestatic void	setup_pipe();
5777c478bdstevel@tonic-gatestatic void	killproc(pid_t);
5787c478bdstevel@tonic-gatestatic void	init_env();
5797c478bdstevel@tonic-gatestatic void	boot_init();
5807c478bdstevel@tonic-gatestatic void	powerfail();
5817c478bdstevel@tonic-gatestatic void	remv();
5827c478bdstevel@tonic-gatestatic void	write_ioctl_syscon();
5837c478bdstevel@tonic-gatestatic void	spawn(struct PROC_TABLE *, struct CMD_LINE *);
5847c478bdstevel@tonic-gatestatic void	setimer(int);
5857c478bdstevel@tonic-gatestatic void	siglvl(int, siginfo_t *, ucontext_t *);
5867c478bdstevel@tonic-gatestatic void	sigpoll(int);
5877c478bdstevel@tonic-gatestatic void	enter_maintenance(void);
5887c478bdstevel@tonic-gatestatic void	timer(int);
5897c478bdstevel@tonic-gatestatic void	userinit(int, char **);
5907c478bdstevel@tonic-gatestatic void	notify_pam_dead(struct utmpx *);
5917c478bdstevel@tonic-gatestatic long	waitproc(struct PROC_TABLE *);
5927c478bdstevel@tonic-gatestatic struct PROC_TABLE *efork(int, struct PROC_TABLE *, int);
5937c478bdstevel@tonic-gatestatic struct PROC_TABLE *findpslot(struct CMD_LINE *);
5947c478bdstevel@tonic-gatestatic void	increase_proc_table_size();
5957c478bdstevel@tonic-gatestatic void	st_init();
5967c478bdstevel@tonic-gatestatic void	st_write();
5977c478bdstevel@tonic-gatestatic void	contracts_init();
5987c478bdstevel@tonic-gatestatic void	contract_event(struct pollfd *);
5997c478bdstevel@tonic-gatestatic int	startd_run(const char *, int, ctid_t);
6007c478bdstevel@tonic-gatestatic void	startd_record_failure();
6017c478bdstevel@tonic-gatestatic int	startd_failure_rate_critical();
6027c478bdstevel@tonic-gatestatic char	*audit_boot_msg();
6037c478bdstevel@tonic-gatestatic int	audit_put_record(int, int, char *);
6047c478bdstevel@tonic-gatestatic void	update_boot_archive(int new_state);
6056112cecJoshua M. Clulowstatic void	init_bootbanner_print(const char *, uint_t);
6087c478bdstevel@tonic-gatemain(int argc, char *argv[])
6107c478bdstevel@tonic-gate	int	chg_lvl_flag = FALSE, print_banner = FALSE;
6117c478bdstevel@tonic-gate	int	may_need_audit = 1;
6127c478bdstevel@tonic-gate	int	c;
6137c478bdstevel@tonic-gate	char	*msg;
6157c478bdstevel@tonic-gate	/* Get a timestamp for use as boot time, if needed. */
6167c478bdstevel@tonic-gate	(void) time(&init_boot_time);
6187c478bdstevel@tonic-gate	/* Get the default umask */
6197c478bdstevel@tonic-gate	cmask = umask(022);
6207c478bdstevel@tonic-gate	(void) umask(cmask);
6227c478bdstevel@tonic-gate	/* Parse the arguments to init. Check for single user */
6237c478bdstevel@tonic-gate	opterr = 0;
6247c478bdstevel@tonic-gate	while ((c = getopt(argc, argv, "brsm:")) != EOF) {
6257c478bdstevel@tonic-gate		switch (c) {
6267c478bdstevel@tonic-gate		case 'b':
6277c478bdstevel@tonic-gate			rflg = 0;
6287c478bdstevel@tonic-gate			bflg = 1;
6297c478bdstevel@tonic-gate			if (!sflg)
6307c478bdstevel@tonic-gate				sflg++;
6317c478bdstevel@tonic-gate			break;
6327c478bdstevel@tonic-gate		case 'r':
6337c478bdstevel@tonic-gate			bflg = 0;
6347c478bdstevel@tonic-gate			rflg++;
6357c478bdstevel@tonic-gate			break;
6367c478bdstevel@tonic-gate		case 's':
6377c478bdstevel@tonic-gate			if (!bflg)
6387c478bdstevel@tonic-gate				sflg++;
6397c478bdstevel@tonic-gate			break;
6407c478bdstevel@tonic-gate		case 'm':
6417c478bdstevel@tonic-gate			smf_options = optarg;
6427c478bdstevel@tonic-gate			smf_debug = (strstr(smf_options, "debug") != NULL);
6437c478bdstevel@tonic-gate			break;
6447c478bdstevel@tonic-gate		}
6457c478bdstevel@tonic-gate	}
6477c478bdstevel@tonic-gate	/*
6487c478bdstevel@tonic-gate	 * Determine if we are the main init, or a user invoked init, whose job
6497c478bdstevel@tonic-gate	 * it is to inform init to change levels or perform some other action.
6507c478bdstevel@tonic-gate	 */
6517c478bdstevel@tonic-gate	if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid,
6527c478bdstevel@tonic-gate	    sizeof (init_pid)) != sizeof (init_pid)) {
6537c478bdstevel@tonic-gate		(void) fprintf(stderr, "could not get pid for init\n");
6547c478bdstevel@tonic-gate		return (1);
6557c478bdstevel@tonic-gate	}
6577c478bdstevel@tonic-gate	/*
6587c478bdstevel@tonic-gate	 * If this PID is not the same as the "true" init for the zone, then we
6597c478bdstevel@tonic-gate	 * must be in 'user' mode.
6607c478bdstevel@tonic-gate	 */
6617c478bdstevel@tonic-gate	if (getpid() != init_pid) {
6627c478bdstevel@tonic-gate		userinit(argc, argv);
6637c478bdstevel@tonic-gate	}
6657c478bdstevel@tonic-gate	if (getzoneid() != GLOBAL_ZONEID) {
6667c478bdstevel@tonic-gate		print_banner = TRUE;
6677c478bdstevel@tonic-gate	}
6697c478bdstevel@tonic-gate	/*
6707c478bdstevel@tonic-gate	 * Initialize state (and set "booting").
6717c478bdstevel@tonic-gate	 */
6727c478bdstevel@tonic-gate	st_init();
6747c478bdstevel@tonic-gate	if (booting && print_banner) {
6757c478bdstevel@tonic-gate		/*
6767c478bdstevel@tonic-gate		 * We want to print the boot banner as soon as
6777c478bdstevel@tonic-gate		 * possible.  In the global zone, the kernel does it,
6787c478bdstevel@tonic-gate		 * but we do not have that luxury in non-global zones,
6797c478bdstevel@tonic-gate		 * so we will print it here.
6807c478bdstevel@tonic-gate		 */
6816112cecJoshua M. Clulow#ifdef	LEGACY_BANNER
6826112cecJoshua M. Clulow		struct utsname un;
6836112cecJoshua M. Clulow		char buf[BUFSIZ];
6846112cecJoshua M. Clulow		const char *bits;
6856112cecJoshua M. Clulow		int r;
6866112cecJoshua M. Clulow
6877c478bdstevel@tonic-gate		(void) uname(&un);
6886112cecJoshua M. Clulow		if ((r = sysinfo(SI_ADDRESS_WIDTH, buf, sizeof (buf))) > 0 &&
6896112cecJoshua M. Clulow		    r < sizeof (buf)) {
6906112cecJoshua M. Clulow			bits = buf;
6916112cecJoshua M. Clulow		} else {
6926112cecJoshua M. Clulow			bits = "64";
6937c478bdstevel@tonic-gate		}
6957c478bdstevel@tonic-gate		console(B_FALSE,
6966112cecJoshua M. Clulow		    "\n\n%s Release %s Version %s %s-bit\r\n",
6977c478bdstevel@tonic-gate		    un.sysname, un.release, un.version, bits);
6987c478bdstevel@tonic-gate		console(B_FALSE,
699861a916John Beck		    "Copyright (c) 1983, 2010, Oracle and/or its affiliates."
7007c478bdstevel@tonic-gate		    " All rights reserved.\r\n");
7016112cecJoshua M. Clulow#else
7026112cecJoshua M. Clulow		bootbanner_print(init_bootbanner_print, 0);
7036112cecJoshua M. Clulow#endif
7047c478bdstevel@tonic-gate	}
7067c478bdstevel@tonic-gate	/*
7077c478bdstevel@tonic-gate	 * Get the ioctl settings for /dev/syscon from /etc/ioctl.syscon
7087c478bdstevel@tonic-gate	 * so that it can be brought up in the state it was in when the
7097c478bdstevel@tonic-gate	 * system went down; or set to defaults if ioctl.syscon isn't
7107c478bdstevel@tonic-gate	 * valid.
7117c478bdstevel@tonic-gate	 *
7127c478bdstevel@tonic-gate	 * This needs to be done even if we're restarting so reset_modes()
7137c478bdstevel@tonic-gate	 * will work in case we need to go down to single user mode.
7147c478bdstevel@tonic-gate	 */
7157c478bdstevel@tonic-gate	write_ioctl = get_ioctl_syscon();
7177c478bdstevel@tonic-gate	/*
7187c478bdstevel@tonic-gate	 * Set up all signals to be caught or ignored as appropriate.
7197c478bdstevel@tonic-gate	 */
7207c478bdstevel@tonic-gate	init_signals();
7227c478bdstevel@tonic-gate	/* Load glob_envp from ENVFILE. */
7237c478bdstevel@tonic-gate	init_env();
7257c478bdstevel@tonic-gate	contracts_init();
7277c478bdstevel@tonic-gate	if (!booting) {
7287c478bdstevel@tonic-gate		/* cur_state should have been read in. */
7307c478bdstevel@tonic-gate		op_modes = NORMAL_MODES;
7327c478bdstevel@tonic-gate		/* Rewrite the ioctl file if it was bad. */
7337c478bdstevel@tonic-gate		if (write_ioctl)
7347c478bdstevel@tonic-gate			write_ioctl_syscon();
7357c478bdstevel@tonic-gate	} else {
7367c478bdstevel@tonic-gate		/*
7377c478bdstevel@tonic-gate		 * It's fine to boot up with state as zero, because
7387c478bdstevel@tonic-gate		 * startd will later tell us the real state.
7397c478bdstevel@tonic-gate		 */
7407c478bdstevel@tonic-gate		cur_state = 0;
7417c478bdstevel@tonic-gate		op_modes = BOOT_MODES;
7437c478bdstevel@tonic-gate		boot_init();
7447c478bdstevel@tonic-gate	}
7467c478bdstevel@tonic-gate	prev_state = prior_state = cur_state;
74892ba710eschrock	setup_pipe();
7507c478bdstevel@tonic-gate	/*
7517c478bdstevel@tonic-gate	 * Here is the beginning of the main process loop.
7527c478bdstevel@tonic-gate	 */
7537c478bdstevel@tonic-gate	for (;;) {
75492ba710eschrock		if (lvlq_received) {
7557c478bdstevel@tonic-gate			setup_pipe();
75692ba710eschrock			lvlq_received = B_FALSE;
75792ba710eschrock		}
7597c478bdstevel@tonic-gate		/*
7607c478bdstevel@tonic-gate		 * Clean up any accounting records for dead "godchildren".
7617c478bdstevel@tonic-gate		 */
7627c478bdstevel@tonic-gate		if (Gchild)
7637c478bdstevel@tonic-gate			cleanaux();
7657c478bdstevel@tonic-gate		/*
7667c478bdstevel@tonic-gate		 * If in "normal" mode, check all living processes and initiate
7677c478bdstevel@tonic-gate		 * kill sequence on those that should not be there anymore.
7687c478bdstevel@tonic-gate		 */
7697c478bdstevel@tonic-gate		if (op_modes == NORMAL_MODES && cur_state != LVLa &&
7707c478bdstevel@tonic-gate		    cur_state != LVLb && cur_state != LVLc)
7717c478bdstevel@tonic-gate			remv();
7737c478bdstevel@tonic-gate		/*
7747c478bdstevel@tonic-gate		 * If a change in run levels is the reason we awoke, now do
7757c478bdstevel@tonic-gate		 * the accounting to report the change in the utmp file.
7767c478bdstevel@tonic-gate		 * Also report the change on the system console.
7777c478bdstevel@tonic-gate		 */
7787c478bdstevel@tonic-gate		if (chg_lvl_flag) {
7797c478bdstevel@tonic-gate			chg_lvl_flag = FALSE;
7817c478bdstevel@tonic-gate			if (state_to_flags(cur_state) & LSEL_RUNLEVEL) {
7827c478bdstevel@tonic-gate				char rl = state_to_name(cur_state);
7847c478bdstevel@tonic-gate				if (rl != -1)
7857c478bdstevel@tonic-gate					lscf_set_runlevel(rl);
7867c478bdstevel@tonic-gate			}
7887c478bdstevel@tonic-gate			may_need_audit = 1;
7897c478bdstevel@tonic-gate		}
7917c478bdstevel@tonic-gate		/*
7927c478bdstevel@tonic-gate		 * Scan the inittab file and spawn and respawn processes that
7937c478bdstevel@tonic-gate		 * should be alive in the current state. If inittab does not
7947c478bdstevel@tonic-gate		 * exist default to  single user mode.
7957c478bdstevel@tonic-gate		 */
7967c478bdstevel@tonic-gate		if (spawn_processes() == FAILURE) {
7977c478bdstevel@tonic-gate			prior_state = prev_state;
7987c478bdstevel@tonic-gate			cur_state = SINGLE_USER;
7997c478bdstevel@tonic-gate		}
8017c478bdstevel@tonic-gate		/* If any respawns occurred, take note. */
8027c478bdstevel@tonic-gate		if (rsflag) {
8037c478bdstevel@tonic-gate			rsflag = 0;
8047c478bdstevel@tonic-gate			spawncnt++;
8057c478bdstevel@tonic-gate		}
8077c478bdstevel@tonic-gate		/*
8087c478bdstevel@tonic-gate		 * If a powerfail signal was received during the last
8097c478bdstevel@tonic-gate		 * sequence, set mode to powerfail.  When spawn_processes() is
8107c478bdstevel@tonic-gate		 * entered the first thing it does is to check "powerhit".  If
8117c478bdstevel@tonic-gate		 * it is in PF_MODES then it clears "powerhit" and does
8127c478bdstevel@tonic-gate		 * a powerfail sequence.  If it is not in PF_MODES, then it
8137c478bdstevel@tonic-gate		 * puts itself in PF_MODES and then clears "powerhit".  Should
8147c478bdstevel@tonic-gate		 * "powerhit" get set again while spawn_processes() is working
8157c478bdstevel@tonic-gate		 * on a powerfail sequence, the following code  will see that
8167c478bdstevel@tonic-gate		 * spawn_processes() tries to execute the powerfail sequence
8177c478bdstevel@tonic-gate		 * again.  This guarantees that the powerfail sequence will be
8187c478bdstevel@tonic-gate		 * successfully completed before further processing takes
8197c478bdstevel@tonic-gate		 * place.
8207c478bdstevel@tonic-gate		 */
8217c478bdstevel@tonic-gate		if (wakeup.w_flags.w_powerhit) {
8227c478bdstevel@tonic-gate			op_modes = PF_MODES;
8237c478bdstevel@tonic-gate			/*
8247c478bdstevel@tonic-gate			 * Make sure that cur_state != prev_state so that
8257c478bdstevel@tonic-gate			 * ONCE and WAIT types work.
8267c478bdstevel@tonic-gate			 */
8277c478bdstevel@tonic-gate			prev_state = 0;
8287c478bdstevel@tonic-gate		} else if (op_modes != NORMAL_MODES) {
8297c478bdstevel@tonic-gate			/*
8307c478bdstevel@tonic-gate			 * If spawn_processes() was not just called while in
8317c478bdstevel@tonic-gate			 * normal mode, we set the mode to normal and it will
8327c478bdstevel@tonic-gate			 * be called again to check normal modes.  If we have
8337c478bdstevel@tonic-gate			 * just finished a powerfail sequence with prev_state
8347c478bdstevel@tonic-gate			 * equal to zero, we set prev_state equal to cur_state
8357c478bdstevel@tonic-gate			 * before the next pass through.
8367c478bdstevel@tonic-gate			 */
8377c478bdstevel@tonic-gate			if (op_modes == PF_MODES)
8387c478bdstevel@tonic-gate				prev_state = cur_state;
8397c478bdstevel@tonic-gate			op_modes = NORMAL_MODES;
8407c478bdstevel@tonic-gate		} else if (cur_state == LVLa || cur_state == LVLb ||
8417c478bdstevel@tonic-gate		    cur_state == LVLc) {
8427c478bdstevel@tonic-gate			/*
8437c478bdstevel@tonic-gate			 * If it was a change of levels that awakened us and the
8447c478bdstevel@tonic-gate			 * new level is one of the demand levels then reset
8457c478bdstevel@tonic-gate			 * cur_state to the previous state and do another scan
8467c478bdstevel@tonic-gate			 * to take care of the usual respawn actions.
8477c478bdstevel@tonic-gate			 */
8487c478bdstevel@tonic-gate			cur_state = prior_state;
8497c478bdstevel@tonic-gate			prior_state = prev_state;
8507c478bdstevel@tonic-gate			prev_state = cur_state;
8517c478bdstevel@tonic-gate		} else {
8527c478bdstevel@tonic-gate			prev_state = cur_state;
8547c478bdstevel@tonic-gate			if (wakeup.w_mask == 0) {
8557c478bdstevel@tonic-gate				int ret;
8577c478bdstevel@tonic-gate				if (may_need_audit && (cur_state == LVL3)) {
8587c478bdstevel@tonic-gate					msg = audit_boot_msg();
8607c478bdstevel@tonic-gate					may_need_audit = 0;
8617c478bdstevel@tonic-gate					(void) audit_put_record(ADT_SUCCESS,
8627c478bdstevel@tonic-gate					    ADT_SUCCESS, msg);
8637c478bdstevel@tonic-gate					free(msg);
8647c478bdstevel@tonic-gate				}
8667c478bdstevel@tonic-gate				/*
8677c478bdstevel@tonic-gate				 * "init" is finished with all actions for
8687c478bdstevel@tonic-gate				 * the current wakeup.
8697c478bdstevel@tonic-gate				 */
8707c478bdstevel@tonic-gate				ret = poll(poll_fds, poll_nfds,
8717c478bdstevel@tonic-gate				    SLEEPTIME * MILLISEC);
8727c478bdstevel@tonic-gate				pausecnt++;
8737c478bdstevel@tonic-gate				if (ret > 0)
8747c478bdstevel@tonic-gate					contract_event(&poll_fds[0]);
8757c478bdstevel@tonic-gate				else if (ret < 0 && errno != EINTR)
8767c478bdstevel@tonic-gate					console(B_TRUE, "poll() error: %s\n",
8777c478bdstevel@tonic-gate					    strerror(errno));
8787c478bdstevel@tonic-gate			}
8807c478bdstevel@tonic-gate			if (wakeup.w_flags.w_usersignal) {
8817c478bdstevel@tonic-gate				/*
8827c478bdstevel@tonic-gate				 * Install the new level.  This could be a real
8837c478bdstevel@tonic-gate				 * change in levels  or a telinit [Q|a|b|c] or
8847c478bdstevel@tonic-gate				 * just a telinit to the same level at which
8857c478bdstevel@tonic-gate				 * we are running.
8867c478bdstevel@tonic-gate				 */
8877c478bdstevel@tonic-gate				if (new_state != cur_state) {
8887c478bdstevel@tonic-gate					if (new_state == LVLa ||
8897c478bdstevel@tonic-gate					    new_state == LVLb ||
8907c478bdstevel@tonic-gate					    new_state == LVLc) {
8917c478bdstevel@tonic-gate						prev_state = prior_state;
8927c478bdstevel@tonic-gate						prior_state = cur_state;
8937c478bdstevel@tonic-gate						cur_state = new_state;
8947c478bdstevel@tonic-gate					} else {
8957c478bdstevel@tonic-gate						prev_state = cur_state;
8967c478bdstevel@tonic-gate						if (cur_state >= 0)
8977c478bdstevel@tonic-gate							prior_state = cur_state;
8987c478bdstevel@tonic-gate						cur_state = new_state;
8997c478bdstevel@tonic-gate						chg_lvl_flag = TRUE;
9007c478bdstevel@tonic-gate					}
9017c478bdstevel@tonic-gate				}
9037c478bdstevel@tonic-gate				new_state = 0;
9047c478bdstevel@tonic-gate			}
9067c478bdstevel@tonic-gate			if (wakeup.w_flags.w_powerhit)
9077c478bdstevel@tonic-gate				op_modes = PF_MODES;
9097c478bdstevel@tonic-gate			/*
9107c478bdstevel@tonic-gate			 * Clear all wakeup reasons.
9117c478bdstevel@tonic-gate			 */
9127c478bdstevel@tonic-gate			wakeup.w_mask = 0;
9137c478bdstevel@tonic-gate		}
9147c478bdstevel@tonic-gate	}
9167c478bdstevel@tonic-gate	/*NOTREACHED*/
9197c478bdstevel@tonic-gatestatic void
9206112cecJoshua M. Clulowinit_bootbanner_print(const char *line, uint_t num)
9216112cecJoshua M. Clulow{
9226112cecJoshua M. Clulow	const char *pfx = (num == 0) ? "\n\n" : "";
9236112cecJoshua M. Clulow
9246112cecJoshua M. Clulow	console(B_FALSE, "%s%s\r\n", pfx, line);
9256112cecJoshua M. Clulow}
9266112cecJoshua M. Clulow
9276112cecJoshua M. Clulowstatic void
9287c478bdstevel@tonic-gateupdate_boot_archive(int new_state)
9307c478bdstevel@tonic-gate	if (new_state != LVL0 && new_state != LVL5 && new_state != LVL6)
9317c478bdstevel@tonic-gate		return;
9337c478bdstevel@tonic-gate	if (getzoneid() != GLOBAL_ZONEID)
9347c478bdstevel@tonic-gate		return;
9364884749Enrico Perla - Sun Microsystems	(void) system("/sbin/bootadm -ea update_all");
9407c478bdstevel@tonic-gate * void enter_maintenance()
9417c478bdstevel@tonic-gate *   A simple invocation of sulogin(1M), with no baggage, in the case that we
9427c478bdstevel@tonic-gate *   are unable to activate svc.startd(1M).  We fork; the child runs sulogin;
9437c478bdstevel@tonic-gate *   we wait for it to exit.
9447c478bdstevel@tonic-gate */
9457c478bdstevel@tonic-gatestatic void
9487c478bdstevel@tonic-gate	struct PROC_TABLE	*su_process;
9507c478bdstevel@tonic-gate	console(B_FALSE, "Requesting maintenance mode\n"
9517c478bdstevel@tonic-gate	    "(See /lib/svc/share/README for additional information.)\n");
952c39ec06Roger A. Faulkner	(void) sighold(SIGCLD);
9537c478bdstevel@tonic-gate	while ((su_process = efork(M_OFF, NULLPROC, NOCLEANUP)) == NO_ROOM)
9547c478bdstevel@tonic-gate		(void) pause();
955c39ec06Roger A. Faulkner	(void) sigrelse(SIGCLD);
9567c478bdstevel@tonic-gate	if (su_process == NULLPROC) {
9577c478bdstevel@tonic-gate		int fd;
9597c478bdstevel@tonic-gate		(void) fclose(stdin);
9607c478bdstevel@tonic-gate		(void) fclose(stdout);
9617c478bdstevel@tonic-gate		(void) fclose(stderr);
9627c478bdstevel@tonic-gate		closefrom(0);
9647c478bdstevel@tonic-gate		fd = open(SYSCON, O_RDWR | O_NOCTTY);
9657c478bdstevel@tonic-gate		if (fd >= 0) {
9667c478bdstevel@tonic-gate			(void) dup2(fd, 1);
9677c478bdstevel@tonic-gate			(void) dup2(fd, 2);
9687c478bdstevel@tonic-gate		} else {
9697c478bdstevel@tonic-gate			/*
9707c478bdstevel@tonic-gate			 * Need to issue an error message somewhere.
9717c478bdstevel@tonic-gate			 */
9727c478bdstevel@tonic-gate			syslog(LOG_CRIT, "init[%d]: cannot open %s; %s\n",
9737c478bdstevel@tonic-gate			    getpid(), SYSCON, strerror(errno));
9747c478bdstevel@tonic-gate		}
9767c478bdstevel@tonic-gate		/*
9777c478bdstevel@tonic-gate		 * Execute the "su" program.
9787c478bdstevel@tonic-gate		 */
9797c478bdstevel@tonic-gate		(void) execle(SU, SU, "-", (char *)0, glob_envp);
9807c478bdstevel@tonic-gate		console(B_TRUE, "execle of %s failed: %s\n", SU,
9817c478bdstevel@tonic-gate		    strerror(errno));
9827c478bdstevel@tonic-gate		timer(5);
9837c478bdstevel@tonic-gate		exit(1);
9847c478bdstevel@tonic-gate	}
9867c478bdstevel@tonic-gate	/*
9877c478bdstevel@tonic-gate	 * If we are the parent, wait around for the child to die
9887c478bdstevel@tonic-gate	 * or for "init" to be signaled to change levels.
9897c478bdstevel@tonic-gate	 */
9907c478bdstevel@tonic-gate	while (waitproc(su_process) == FAILURE) {
9917c478bdstevel@tonic-gate		/*
9927c478bdstevel@tonic-gate		 * All other reasons for waking are ignored when in
9937c478bdstevel@tonic-gate		 * single-user mode.  The only child we are interested
9947c478bdstevel@tonic-gate		 * in is being waited for explicitly by waitproc().
9957c478bdstevel@tonic-gate		 */
9967c478bdstevel@tonic-gate		wakeup.w_mask = 0;
9977c478bdstevel@tonic-gate	}
10017c478bdstevel@tonic-gate * remv() scans through "proc_table" and performs cleanup.  If
10027c478bdstevel@tonic-gate * there is a process in the table, which shouldn't be here at
10037c478bdstevel@tonic-gate * the current run level, then remv() kills the process.
10047c478bdstevel@tonic-gate */
10057c478bdstevel@tonic-gatestatic void
10087c478bdstevel@tonic-gate	struct PROC_TABLE	*process;
10097c478bdstevel@tonic-gate	struct CMD_LINE		cmd;
10107c478bdstevel@tonic-gate	char			cmd_string[MAXCMDL];
10117c478bdstevel@tonic-gate	int			change_level;
10137c478bdstevel@tonic-gate	change_level = (cur_state != prev_state ? TRUE : FALSE);
10157c478bdstevel@tonic-gate	/*
10167c478bdstevel@tonic-gate	 * Clear the TOUCHED flag on all entries so that when we have
10177c478bdstevel@tonic-gate	 * finished scanning inittab, we will be able to tell if we
10187c478bdstevel@tonic-gate	 * have any processes for which there is no entry in inittab.
10197c478bdstevel@tonic-gate	 */
10207c478bdstevel@tonic-gate	for (process = proc_table;
10217c478bdstevel@tonic-gate	    (process < proc_table + num_proc); process++) {
10227c478bdstevel@tonic-gate		process->p_flags &= ~TOUCHED;
10237c478bdstevel@tonic-gate	}
10257c478bdstevel@tonic-gate	/*
10267c478bdstevel@tonic-gate	 * Scan all inittab entries.
10277c478bdstevel@tonic-gate	 */
10287c478bdstevel@tonic-gate	while (getcmd(&cmd, &cmd_string[0]) == TRUE) {
10297c478bdstevel@tonic-gate		/* Scan for process which goes with this entry in inittab. */
10307c478bdstevel@tonic-gate		for (process = proc_table;
10317c478bdstevel@tonic-gate		    (process < proc_table + num_proc); process++) {
10327c478bdstevel@tonic-gate			if ((process->p_flags & OCCUPIED) == 0 ||
10337c478bdstevel@tonic-gate			    !id_eq(process->p_id, cmd.c_id))
10347c478bdstevel@tonic-gate				continue;
10367c478bdstevel@tonic-gate			/*
10377c478bdstevel@tonic-gate			 * This slot contains the process we are looking for.
10387c478bdstevel@tonic-gate			 */
10407c478bdstevel@tonic-gate			/*
10417c478bdstevel@tonic-gate			 * Is the cur_state SINGLE_USER or is this process
10427c478bdstevel@tonic-gate			 * marked as "off" or was this proc started by some
10437c478bdstevel@tonic-gate			 * mechanism other than LVL{a|b|c} and the current level
10447c478bdstevel@tonic-gate			 * does not support this process?
10457c478bdstevel@tonic-gate			 */
10467c478bdstevel@tonic-gate			if (cur_state == SINGLE_USER ||
10477c478bdstevel@tonic-gate			    cmd.c_action == M_OFF ||
10487c478bdstevel@tonic-gate			    ((cmd.c_levels & state_to_mask(cur_state)) == 0 &&
10497c478bdstevel@tonic-gate			    (process->p_flags & DEMANDREQUEST) == 0)) {
10507c478bdstevel@tonic-gate				if (process->p_flags & LIVING) {
10517c478bdstevel@tonic-gate					/*
10527c478bdstevel@tonic-gate					 * Touch this entry so we know we have
10537c478bdstevel@tonic-gate					 * treated it.  Note that procs which
10547c478bdstevel@tonic-gate					 * are already dead at this point and
10557c478bdstevel@tonic-gate					 * should not be restarted are left
10567c478bdstevel@tonic-gate					 * untouched.  This causes their slot to
10577c478bdstevel@tonic-gate					 * be freed later after dead accounting
10587c478bdstevel@tonic-gate					 * is done.
10597c478bdstevel@tonic-gate					 */
10607c478bdstevel@tonic-gate					process->p_flags |= TOUCHED;
10627c478bdstevel@tonic-gate					if ((process->p_flags & KILLED) == 0) {
10637c478bdstevel@tonic-gate						if (change_level) {
10647c478bdstevel@tonic-gate							process->p_flags
10657c478bdstevel@tonic-gate							    |= WARNED;
10667c478bdstevel@tonic-gate							(void) kill(
10677c478bdstevel@tonic-gate							    process->p_pid,
10687c478bdstevel@tonic-gate							    SIGTERM);
10697c478bdstevel@tonic-gate						} else {
10707c478bdstevel@tonic-gate							/*
10717c478bdstevel@tonic-gate							 * Fork a killing proc
10727c478bdstevel@tonic-gate							 * so "init" can
10737c478bdstevel@tonic-gate							 * continue without
10747c478bdstevel@tonic-gate							 * having to pause for
10757c478bdstevel@tonic-gate							 * TWARN seconds.
10767c478bdstevel@tonic-gate							 */
10777c478bdstevel@tonic-gate							killproc(
10787c478bdstevel@tonic-gate							    process->p_pid);
10797c478bdstevel@tonic-gate						}
10807c478bdstevel@tonic-gate						process->p_flags |= KILLED;
10817c478bdstevel@tonic-gate					}
10827c478bdstevel@tonic-gate				}
10837c478bdstevel@tonic-gate			} else {
10847c478bdstevel@tonic-gate				/*
10857c478bdstevel@tonic-gate				 * Process can exist at current level.  If it is
10867c478bdstevel@tonic-gate				 * still alive or a DEMANDREQUEST we touch it so
10877c478bdstevel@tonic-gate				 * it will be left alone.  Otherwise we leave it
10887c478bdstevel@tonic-gate				 * untouched so it will be accounted for and
10897c478bdstevel@tonic-gate				 * cleaned up later in remv().  Dead
10907c478bdstevel@tonic-gate				 * DEMANDREQUESTs will be accounted but not
10917c478bdstevel@tonic-gate				 * freed.
10927c478bdstevel@tonic-gate				 */
10937c478bdstevel@tonic-gate				if (process->p_flags &
10947c478bdstevel@tonic-gate				    (LIVING|NOCLEANUP|DEMANDREQUEST))
10957c478bdstevel@tonic-gate					process->p_flags |= TOUCHED;
10967c478bdstevel@tonic-gate			}
10987c478bdstevel@tonic-gate			break;
10997c478bdstevel@tonic-gate		}
11007c478bdstevel@tonic-gate	}
11027c478bdstevel@tonic-gate	st_write();
11047c478bdstevel@tonic-gate	/*
11057c478bdstevel@tonic-gate	 * If this was a change of levels call, scan through the
11067c478bdstevel@tonic-gate	 * process table for processes that were warned to die.  If any
11077c478bdstevel@tonic-gate	 * are found that haven't left yet, sleep for TWARN seconds and
11087c478bdstevel@tonic-gate	 * then send final terminations to any that haven't died yet.
11097c478bdstevel@tonic-gate	 */
11107c478bdstevel@tonic-gate	if (change_level) {
11127c478bdstevel@tonic-gate		/*
11137c478bdstevel@tonic-gate		 * Set the alarm for TWARN seconds on the assumption
11147c478bdstevel@tonic-gate		 * that there will be some that need to be waited for.
11157c478bdstevel@tonic-gate		 * This won't harm anything except we are guaranteed to
11167c478bdstevel@tonic-gate		 * wakeup in TWARN seconds whether we need to or not.
11177c478bdstevel@tonic-gate		 */
11187c478bdstevel@tonic-gate		setimer(TWARN);
11207c478bdstevel@tonic-gate		/*
11217c478bdstevel@tonic-gate		 * Scan for processes which should be dying.  We hope they
11227c478bdstevel@tonic-gate		 * will die without having to be sent a SIGKILL signal.
11237c478bdstevel@tonic-gate		 */
11247c478bdstevel@tonic-gate		for (process = proc_table;
11257c478bdstevel@tonic-gate		    (process < proc_table + num_proc); process++) {
11267c478bdstevel@tonic-gate			/*
11277c478bdstevel@tonic-gate			 * If this process should die, hasn't yet, and the
11287c478bdstevel@tonic-gate			 * TWARN time hasn't expired yet, wait for process
11297c478bdstevel@tonic-gate			 * to die or for timer to expire.
11307c478bdstevel@tonic-gate			 */
11317c478bdstevel@tonic-gate			while (time_up == FALSE &&
11327c478bdstevel@tonic-gate			    (process->p_flags & (WARNED|LIVING|OCCUPIED)) ==
11337c478bdstevel@tonic-gate			    (WARNED|LIVING|OCCUPIED))
11347c478bdstevel@tonic-gate				(void) pause();
11367c478bdstevel@tonic-gate			if (time_up == TRUE)
11377c478bdstevel@tonic-gate				break;
11387c478bdstevel@tonic-gate		}
11407c478bdstevel@tonic-gate		/*
11417c478bdstevel@tonic-gate		 * If we reached the end of the table without the timer
11427c478bdstevel@tonic-gate		 * expiring, then there are no procs which will have to be
11437c478bdstevel@tonic-gate		 * sent the SIGKILL signal.  If the timer has expired, then
11447c478bdstevel@tonic-gate		 * it is necessary to scan the table again and send signals
11457c478bdstevel@tonic-gate		 * to all processes which aren't going away nicely.
11467c478bdstevel@tonic-gate		 */
11477c478bdstevel@tonic-gate		if (time_up == TRUE) {
11487c478bdstevel@tonic-gate			for (process = proc_table;
11497c478bdstevel@tonic-gate			    (process < proc_table + num_proc); process++) {
11507c478bdstevel@tonic-gate				if ((process->p_flags &
11517c478bdstevel@tonic-gate				    (WARNED|LIVING|OCCUPIED)) ==
11527c478bdstevel@tonic-gate				    (WARNED|LIVING|OCCUPIED))
11537c478bdstevel@tonic-gate					(void) kill(process->p_pid, SIGKILL);
11547c478bdstevel@tonic-gate			}
11557c478bdstevel@tonic-gate		}
11567c478bdstevel@tonic-gate		setimer(0);
11577c478bdstevel@tonic-gate	}
11597c478bdstevel@tonic-gate	/*
11607c478bdstevel@tonic-gate	 * Rescan the proc_table for two kinds of entry, those marked LIVING,
11617c478bdstevel@tonic-gate	 * NAMED, which don't have an entry in inittab (haven't been TOUCHED
11627c478bdstevel@tonic-gate	 * by the above scanning), and haven't been sent kill signals, and
11637c478bdstevel@tonic-gate	 * those entries marked not LIVING, NAMED.  The former procs are killed.
11647c478bdstevel@tonic-gate	 * The latter have DEAD_PROCESS accounting done and the slot cleared.
11657c478bdstevel@tonic-gate	 */
11667c478bdstevel@tonic-gate	for (process = proc_table;
11677c478bdstevel@tonic-gate	    (process < proc_table + num_proc); process++) {
11687c478bdstevel@tonic-gate		if ((process->p_flags & (LIVING|NAMED|TOUCHED|KILLED|OCCUPIED))
11697c478bdstevel@tonic-gate		    == (LIVING|NAMED|OCCUPIED)) {
11707c478bdstevel@tonic-gate			killproc(process->p_pid);
11717c478bdstevel@tonic-gate			process->p_flags |= KILLED;
11727c478bdstevel@tonic-gate		} else if ((process->p_flags & (LIVING|NAMED|OCCUPIED)) ==
11737c478bdstevel@tonic-gate		    (NAMED|OCCUPIED)) {
11747c478bdstevel@tonic-gate			(void) account(DEAD_PROCESS, process, NULL);
11757c478bdstevel@tonic-gate			/*
11767c478bdstevel@tonic-gate			 * If this named proc hasn't been TOUCHED, then free the
11777c478bdstevel@tonic-gate			 * space. It has either died of it's own accord, but
11787c478bdstevel@tonic-gate			 * isn't respawnable or it was killed because it
11797c478bdstevel@tonic-gate			 * shouldn't exist at this level.
11807c478bdstevel@tonic-gate			 */
11817c478bdstevel@tonic-gate			if ((process->p_flags & TOUCHED) == 0)
11827c478bdstevel@tonic-gate				process->p_flags = 0;
11837c478bdstevel@tonic-gate		}
11847c478bdstevel@tonic-gate	}
11867c478bdstevel@tonic-gate	st_write();
11907c478bdstevel@tonic-gate * Extract the svc.startd command line and whether to restart it from its
11917c478bdstevel@tonic-gate * inittab entry.
11927c478bdstevel@tonic-gate */
11947c478bdstevel@tonic-gatestatic void
11957c478bdstevel@tonic-gateprocess_startd_line(struct CMD_LINE *cmd, char *cmd_string)
11977c478bdstevel@tonic-gate	size_t sz;
11997c478bdstevel@tonic-gate	/* Save the command line. */
12007c478bdstevel@tonic-gate	if (sflg || rflg) {
12017c478bdstevel@tonic-gate		/* Also append -r or -s. */
12027c478bdstevel@tonic-gate		(void) strlcpy(startd_cline, cmd_string, sizeof (startd_cline));
12037c478bdstevel@tonic-gate		(void) strlcat(startd_cline, " -", sizeof (startd_cline));
12047c478bdstevel@tonic-gate		if (sflg)
12057c478bdstevel@tonic-gate			sz = strlcat(startd_cline, "s", sizeof (startd_cline));
12067c478bdstevel@tonic-gate		if (rflg)
12077c478bdstevel@tonic-gate			sz = strlcat(startd_cline, "r", sizeof (startd_cline));
12087c478bdstevel@tonic-gate	} else {
12097c478bdstevel@tonic-gate		sz = strlcpy(startd_cline, cmd_string, sizeof (startd_cline));
12107c478bdstevel@tonic-gate	}
12127c478bdstevel@tonic-gate	if (sz >= sizeof (startd_cline)) {
12137c478bdstevel@tonic-gate		console(B_TRUE,
12147c478bdstevel@tonic-gate		    "svc.startd command line too long.  Ignoring.\n");
12157c478bdstevel@tonic-gate		startd_cline[0] = '\0';
12167c478bdstevel@tonic-gate		return;
12177c478bdstevel@tonic-gate	}
12217c478bdstevel@tonic-gate * spawn_processes() scans inittab for entries which should be run at this
12227c478bdstevel@tonic-gate * mode.  Processes which should be running but are not, are started.
12237c478bdstevel@tonic-gate */
12247c478bdstevel@tonic-gatestatic int
12277c478bdstevel@tonic-gate	struct PROC_TABLE		*pp;
12287c478bdstevel@tonic-gate	struct CMD_LINE			cmd;
12297c478bdstevel@tonic-gate	char				cmd_string[MAXCMDL];
12307c478bdstevel@tonic-gate	short				lvl_mask;
12317c478bdstevel@tonic-gate	int				status;
12337c478bdstevel@tonic-gate	/*
12347c478bdstevel@tonic-gate	 * First check the "powerhit" flag.  If it is set, make sure the modes
12357c478bdstevel@tonic-gate	 * are PF_MODES and clear the "powerhit" flag.  Avoid the possible race
12367c478bdstevel@tonic-gate	 * on the "powerhit" flag by disallowing a new powerfail interrupt
12377c478bdstevel@tonic-gate	 * between the test of the powerhit flag and the clearing of it.
12387c478bdstevel@tonic-gate	 */
12397c478bdstevel@tonic-gate	if (wakeup.w_flags.w_powerhit) {
12407c478bdstevel@tonic-gate		wakeup.w_flags.w_powerhit = 0;
12417c478bdstevel@tonic-gate		op_modes = PF_MODES;
12427c478bdstevel@tonic-gate	}
12437c478bdstevel@tonic-gate	lvl_mask = state_to_mask(cur_state);
12457c478bdstevel@tonic-gate	/*
12467c478bdstevel@tonic-gate	 * Scan through all the entries in inittab.
12477c478bdstevel@tonic-gate	 */
12487c478bdstevel@tonic-gate	while ((status = getcmd(&cmd, &cmd_string[0])) == TRUE) {
12497c478bdstevel@tonic-gate		if (id_eq(cmd.c_id, "smf")) {
12507c478bdstevel@tonic-gate			process_startd_line(&cmd, cmd_string);
12517c478bdstevel@tonic-gate			continue;
12527c478bdstevel@tonic-gate		}
12567c478bdstevel@tonic-gate		/*
12577c478bdstevel@tonic-gate		 * Find out if there is a process slot for this entry already.
12587c478bdstevel@tonic-gate		 */
12597c478bdstevel@tonic-gate		if ((pp = findpslot(&cmd)) == NULLPROC) {
12607c478bdstevel@tonic-gate			/*
12617c478bdstevel@tonic-gate			 * we've run out of proc table entries
12627c478bdstevel@tonic-gate			 * increase proc_table.
12637c478bdstevel@tonic-gate			 */
12647c478bdstevel@tonic-gate			increase_proc_table_size();
12667c478bdstevel@tonic-gate			/*
12677c478bdstevel@tonic-gate			 * Retry now as we have an empty proc slot.
12687c478bdstevel@tonic-gate			 * In case increase_proc_table_size() fails,
12697c478bdstevel@tonic-gate			 * we will keep retrying.
12707c478bdstevel@tonic-gate			 */
12717c478bdstevel@tonic-gate			goto retry_for_proc_slot;
12727c478bdstevel@tonic-gate		}
12747c478bdstevel@tonic-gate		/*
12757c478bdstevel@tonic-gate		 * If there is an entry, and it is marked as DEMANDREQUEST,
12767c478bdstevel@tonic-gate		 * one of the levels a, b, or c is in its levels mask, and
12777c478bdstevel@tonic-gate		 * the action field is ONDEMAND and ONDEMAND is a permissable
12787c478bdstevel@tonic-gate		 * mode, and the process is dead, then respawn it.
12797c478bdstevel@tonic-gate		 */
12807c478bdstevel@tonic-gate		if (((pp->p_flags & (LIVING|DEMANDREQUEST)) == DEMANDREQUEST) &&
12817c478bdstevel@tonic-gate		    (cmd.c_levels & MASK_abc) &&
12827c478bdstevel@tonic-gate		    (cmd.c_action & op_modes) == M_ONDEMAND) {
12837c478bdstevel@tonic-gate			spawn(pp, &cmd);
12847c478bdstevel@tonic-gate			continue;
12857c478bdstevel@tonic-gate		}
12877c478bdstevel@tonic-gate		/*
12887c478bdstevel@tonic-gate		 * If the action is not an action we are interested in,
12897c478bdstevel@tonic-gate		 * skip the entry.
12907c478bdstevel@tonic-gate		 */
12917c478bdstevel@tonic-gate		if ((cmd.c_action & op_modes) == 0 || pp->p_flags & LIVING ||
12927c478bdstevel@tonic-gate		    (cmd.c_levels & lvl_mask) == 0)
12937c478bdstevel@tonic-gate			continue;
12957c478bdstevel@tonic-gate		/*
12967c478bdstevel@tonic-gate		 * If the modes are the normal modes (ONCE, WAIT, RESPAWN, OFF,
12977c478bdstevel@tonic-gate		 * ONDEMAND) and the action field is either OFF or the action
12987c478bdstevel@tonic-gate		 * field is ONCE or WAIT and the current level is the same as
12997c478bdstevel@tonic-gate		 * the last level, then skip this entry.  ONCE and WAIT only
13007c478bdstevel@tonic-gate		 * get run when the level changes.
13017c478bdstevel@tonic-gate		 */
13027c478bdstevel@tonic-gate		if (op_modes == NORMAL_MODES &&
13037c478bdstevel@tonic-gate		    (cmd.c_action == M_OFF ||
13044884749Enrico Perla - Sun Microsystems		    (cmd.c_action & (M_ONCE|M_WAIT)) &&
13054884749Enrico Perla - Sun Microsystems		    cur_state == prev_state))
13067c478bdstevel@tonic-gate			continue;
13087c478bdstevel@tonic-gate		/*
13097c478bdstevel@tonic-gate		 * At this point we are interested in performing the action for
13107c478bdstevel@tonic-gate		 * this entry.  Actions fall into two categories, spinning off
13117c478bdstevel@tonic-gate		 * a process and not waiting, and spinning off a process and
13127c478bdstevel@tonic-gate		 * waiting for it to die.  If the action is ONCE, RESPAWN,
13137c478bdstevel@tonic-gate		 * ONDEMAND, POWERFAIL, or BOOT we don't wait for the process
13147c478bdstevel@tonic-gate		 * to die, for all other actions we do wait.
13157c478bdstevel@tonic-gate		 */
13167c478bdstevel@tonic-gate		if (cmd.c_action & (M_ONCE | M_RESPAWN | M_PF | M_BOOT)) {
13177c478bdstevel@tonic-gate			spawn(pp, &cmd);
13197c478bdstevel@tonic-gate		} else {
13207c478bdstevel@tonic-gate			spawn(pp, &cmd);
13214884749Enrico Perla - Sun Microsystems			while (waitproc(pp) == FAILURE)
13224884749Enrico Perla - Sun Microsystems				;
13237c478bdstevel@tonic-gate			(void) account(DEAD_PROCESS, pp, NULL);
13247c478bdstevel@tonic-gate			pp->p_flags = 0;
13257c478bdstevel@tonic-gate		}
13267c478bdstevel@tonic-gate	}
13277c478bdstevel@tonic-gate	return (status);
13317c478bdstevel@tonic-gate * spawn() spawns a shell, inserts the information about the process
13327c478bdstevel@tonic-gate * process into the proc_table, and does the startup accounting.
13337c478bdstevel@tonic-gate */
13347c478bdstevel@tonic-gatestatic void
13357c478bdstevel@tonic-gatespawn(struct PROC_TABLE *process, struct CMD_LINE *cmd)
13377c478bdstevel@tonic-gate	int		i;
13387c478bdstevel@tonic-gate	int		modes, maxfiles;
13397c478bdstevel@tonic-gate	time_t		now;
13407c478bdstevel@tonic-gate	struct PROC_TABLE tmproc, *oprocess;
13427c478bdstevel@tonic-gate	/*
13437c478bdstevel@tonic-gate	 * The modes to be sent to efork() are 0 unless we are
13447c478bdstevel@tonic-gate	 * spawning a LVLa, LVLb, or LVLc entry or we will be
13457c478bdstevel@tonic-gate	 * waiting for the death of the child before continuing.
13467c478bdstevel@tonic-gate	 */
13477c478bdstevel@tonic-gate	modes = NAMED;
13487c478bdstevel@tonic-gate	if (process->p_flags & DEMANDREQUEST || cur_state == LVLa ||
13497c478bdstevel@tonic-gate	    cur_state == LVLb || cur_state == LVLc)
13507c478bdstevel@tonic-gate		modes |= DEMANDREQUEST;
13517c478bdstevel@tonic-gate	if ((cmd->c_action & (M_SYSINIT | M_WAIT | M_BOOTWAIT | M_PWAIT)) != 0)
13527c478bdstevel@tonic-gate		modes |= NOCLEANUP;
13547c478bdstevel@tonic-gate	/*
13557c478bdstevel@tonic-gate	 * If this is a respawnable process, check the threshold
13567c478bdstevel@tonic-gate	 * information to avoid excessive respawns.
13577c478bdstevel@tonic-gate	 */
13587c478bdstevel@tonic-gate	if (cmd->c_action & M_RESPAWN) {
13597c478bdstevel@tonic-gate		/*
13607c478bdstevel@tonic-gate		 * Add NOCLEANUP to all respawnable commands so that the
13617c478bdstevel@tonic-gate		 * information about the frequency of respawns isn't lost.
13627c478bdstevel@tonic-gate		 */
13637c478bdstevel@tonic-gate		modes |= NOCLEANUP;
13647c478bdstevel@tonic-gate		(void) time(&now);
13667c478bdstevel@tonic-gate		/*
13677c478bdstevel@tonic-gate		 * If no time is assigned, then this is the first time
13687c478bdstevel@tonic-gate		 * this command is being processed in this series.  Assign
13697c478bdstevel@tonic-gate		 * the current time.
13707c478bdstevel@tonic-gate		 */
13717c478bdstevel@tonic-gate		if (process->p_time == 0L)
13727c478bdstevel@tonic-gate			process->p_time = now;
13747c478bdstevel@tonic-gate		if (process->p_count++ == SPAWN_LIMIT) {
13767c478bdstevel@tonic-gate			if ((now - process->p_time) < SPAWN_INTERVAL) {
13777c478bdstevel@tonic-gate				/*
13787c478bdstevel@tonic-gate				 * Process is respawning too rapidly.  Print
13797c478bdstevel@tonic-gate				 * message and refuse to respawn it for now.
13807c478bdstevel@tonic-gate				 */
13817c478bdstevel@tonic-gate				console(B_TRUE, "Command is respawning too "
13827c478bdstevel@tonic-gate				    "rapidly. Check for possible errors.\n"
13837c478bdstevel@tonic-gate				    "id:%4s \"%s\"\n",
13847c478bdstevel@tonic-gate				    &cmd->c_id[0], &cmd->c_command[EXEC]);
13857c478bdstevel@tonic-gate				return;
13867c478bdstevel@tonic-gate			}
13877c478bdstevel@tonic-gate			process->p_time = now;
13887c478bdstevel@tonic-gate			process->p_count = 0;
13907c478bdstevel@tonic-gate		} else if (process->p_count > SPAWN_LIMIT) {
13917c478bdstevel@tonic-gate			/*
13927c478bdstevel@tonic-gate			 * If process has been respawning too rapidly and
13937c478bdstevel@tonic-gate			 * the inhibit time limit hasn't expired yet, we
13947c478bdstevel@tonic-gate			 * refuse to respawn.
13957c478bdstevel@tonic-gate			 */
13967c478bdstevel@tonic-gate			if (now - process->p_time < SPAWN_INTERVAL + INHIBIT)
13977c478bdstevel@tonic-gate				return;
13987c478bdstevel@tonic-gate			process->p_time = now;
13997c478bdstevel@tonic-gate			process->p_count = 0;
14007c478bdstevel@tonic-gate		}
14017c478bdstevel@tonic-gate		rsflag = TRUE;
14027c478bdstevel@tonic-gate	}
14047c478bdstevel@tonic-gate	/*
14057c478bdstevel@tonic-gate	 * Spawn a child process to execute this command.
14067c478bdstevel@tonic-gate	 */
1407c39ec06Roger A. Faulkner	(void) sighold(SIGCLD);
14087c478bdstevel@tonic-gate	oprocess = process;
14097c478bdstevel@tonic-gate	while ((process = efork(cmd->c_action, oprocess, modes)) == NO_ROOM)
14107c478bdstevel@tonic-gate		(void) pause();
14127c478bdstevel@tonic-gate	if (process == NULLPROC) {
14147c478bdstevel@tonic-gate		/*
14157c478bdstevel@tonic-gate		 * We are the child.  We must make sure we get a different
14167c478bdstevel@tonic-gate		 * file pointer for our references to utmpx.  Otherwise our
14177c478bdstevel@tonic-gate		 * seeks and reads will compete with those of the parent.
14187c478bdstevel@tonic-gate		 */
14197c478bdstevel@tonic-gate		endutxent();
14217c478bdstevel@tonic-gate		/*
14227c478bdstevel@tonic-gate		 * Perform the accounting for the beginning of a process.
14237c478bdstevel@tonic-gate		 * Note that all processes are initially "INIT_PROCESS"es.
14247c478bdstevel@tonic-gate		 */
14257c478bdstevel@tonic-gate		tmproc.p_id[0] = cmd->c_id[0];
14267c478bdstevel@tonic-gate		tmproc.p_id[1] = cmd->c_id[1];
14277c478bdstevel@tonic-gate		tmproc.p_id[2] = cmd->c_id[2];
14287c478bdstevel@tonic-gate		tmproc.p_id[3] = cmd->c_id[3];
14297c478bdstevel@tonic-gate		tmproc.p_pid = getpid();
14307c478bdstevel@tonic-gate		tmproc.p_exit = 0;
14317c478bdstevel@tonic-gate		(void) account(INIT_PROCESS, &tmproc,
14327c478bdstevel@tonic-gate		    prog_name(&cmd->c_command[EXEC]));
14337c478bdstevel@tonic-gate		maxfiles = ulimit(UL_GDESLIM, 0);
14347c478bdstevel@tonic-gate		for (i = 0; i < maxfiles; i++)
14357c478bdstevel@tonic-gate			(void) fcntl(i, F_SETFD, FD_CLOEXEC);
14377c478bdstevel@tonic-gate		/*
14387c478bdstevel@tonic-gate		 * Now exec a shell with the -c option and the command
14397c478bdstevel@tonic-gate		 * from inittab.
14407c478bdstevel@tonic-gate		 */
14417c478bdstevel@tonic-gate		(void) execle(SH, "INITSH", "-c", cmd->c_command, (char *)0,
14427c478bdstevel@tonic-gate		    glob_envp);
14437c478bdstevel@tonic-gate		console(B_TRUE, "Command\n\"%s\"\n failed to execute.  errno "
14447c478bdstevel@tonic-gate		    "= %d (exec of shell failed)\n", cmd->c_command, errno);
14467c478bdstevel@tonic-gate		/*
14477c478bdstevel@tonic-gate		 * Don't come back so quickly that "init" doesn't have a
14487c478bdstevel@tonic-gate		 * chance to finish putting this child in "proc_table".
14497c478bdstevel@tonic-gate		 */
14507c478bdstevel@tonic-gate		timer(20);
14517c478bdstevel@tonic-gate		exit(1);
14537c478bdstevel@tonic-gate	}
14557c478bdstevel@tonic-gate	/*
14567c478bdstevel@tonic-gate	 * We are the parent.  Insert the necessary
14577c478bdstevel@tonic-gate	 * information in the proc_table.
14587c478bdstevel@tonic-gate	 */
14597c478bdstevel@tonic-gate	process->p_id[0] = cmd->c_id[0];
14607c478bdstevel@tonic-gate	process->p_id[1] = cmd->c_id[1];
14617c478bdstevel@tonic-gate	process->p_id[2] = cmd->c_id[2];
14627c478bdstevel@tonic-gate	process->p_id[3] = cmd->c_id[3];
14647c478bdstevel@tonic-gate	st_write();
1466c39ec06Roger A. Faulkner	(void) sigrelse(SIGCLD);
14707c478bdstevel@tonic-gate * findpslot() finds the old slot in the process table for the
14717c478bdstevel@tonic-gate * command with the same id, or it finds an empty slot.
14727c478bdstevel@tonic-gate */
14737c478bdstevel@tonic-gatestatic struct PROC_TABLE *
14747c478bdstevel@tonic-gatefindpslot(struct CMD_LINE *cmd)
14767c478bdstevel@tonic-gate	struct PROC_TABLE	*process;
14777c478bdstevel@tonic-gate	struct PROC_TABLE	*empty = NULLPROC;
14797c478bdstevel@tonic-gate	for (process = proc_table;
14807c478bdstevel@tonic-gate	    (process < proc_table + num_proc); process++) {
14817c478bdstevel@tonic-gate		if (process->p_flags & OCCUPIED &&
14827c478bdstevel@tonic-gate		    id_eq(process->p_id, cmd->c_id))
14837c478bdstevel@tonic-gate			break;
14857c478bdstevel@tonic-gate		/*
14867c478bdstevel@tonic-gate		 * If the entry is totally empty and "empty" is still 0,
14877c478bdstevel@tonic-gate		 * remember where this hole is and make sure the slot is
14887c478bdstevel@tonic-gate		 * zeroed out.
14897c478bdstevel@tonic-gate		 */
14907c478bdstevel@tonic-gate		if (empty == NULLPROC && (process->p_flags & OCCUPIED) == 0) {
14917c478bdstevel@tonic-gate			empty = process;
14927c478bdstevel@tonic-gate			process->p_id[0] = '\0';
14937c478bdstevel@tonic-gate			process->p_id[1] = '\0';
14947c478bdstevel@tonic-gate			process->p_id[2] = '\0';
14957c478bdstevel@tonic-gate			process->p_id[3] = '\0';
14967c478bdstevel@tonic-gate			process->p_pid = 0;
14977c478bdstevel@tonic-gate			process->p_time = 0L;
14987c478bdstevel@tonic-gate			process->p_count = 0;
14997c478bdstevel@tonic-gate			process->p_flags = 0;
15007c478bdstevel@tonic-gate			process->p_exit = 0;
15017c478bdstevel@tonic-gate		}
15027c478bdstevel@tonic-gate	}
15047c478bdstevel@tonic-gate	/*
15057c478bdstevel@tonic-gate	 * If there is no entry for this slot, then there should be an
15067c478bdstevel@tonic-gate	 * empty slot.  If there is no empty slot, then we've run out
15077c478bdstevel@tonic-gate	 * of proc_table space.  If the latter is true, empty will be
15087c478bdstevel@tonic-gate	 * NULL and the caller will have to complain.
15097c478bdstevel@tonic-gate	 */
15107c478bdstevel@tonic-gate	if (process == (proc_table + num_proc))
15117c478bdstevel@tonic-gate		process = empty;
15137c478bdstevel@tonic-gate	return (process);
15177c478bdstevel@tonic-gate * getcmd() parses lines from inittab.  Each time it finds a command line
15187c478bdstevel@tonic-gate * it will return TRUE as well as fill the passed CMD_LINE structure and
15197c478bdstevel@tonic-gate * the shell command string.  When the end of inittab is reached, FALSE
15207c478bdstevel@tonic-gate * is returned inittab is automatically opened if it is not currently open
15217c478bdstevel@tonic-gate * and is closed when the end of the file is reached.
15227c478bdstevel@tonic-gate */
15237c478bdstevel@tonic-gatestatic FILE *fp_inittab = NULL;
15257c478bdstevel@tonic-gatestatic int
15267c478bdstevel@tonic-gategetcmd(struct CMD_LINE *cmd, char *shcmd)
15287c478bdstevel@tonic-gate	char	*ptr;
15297c478bdstevel@tonic-gate	int	c, lastc, state;
15307c478bdstevel@tonic-gate	char 	*ptr1;
15317c478bdstevel@tonic-gate	int	answer, i, proceed;
15327c478bdstevel@tonic-gate	struct	stat	sbuf;
15337c478bdstevel@tonic-gate	static char *actions[] = {
15347c478bdstevel@tonic-gate		"off", "respawn", "ondemand", "once", "wait", "boot",
15357c478bdstevel@tonic-gate		"bootwait", "powerfail", "powerwait", "initdefault",
15367c478bdstevel@tonic-gate		"sysinit",
15377c478bdstevel@tonic-gate	};
15387c478bdstevel@tonic-gate	static short act_masks[] = {
15397c478bdstevel@tonic-gate		M_OFF, M_RESPAWN, M_ONDEMAND, M_ONCE, M_WAIT, M_BOOT,
15407c478bdstevel@tonic-gate		M_BOOTWAIT, M_PF, M_PWAIT, M_INITDEFAULT, M_SYSINIT,
15417c478bdstevel@tonic-gate	};
15427c478bdstevel@tonic-gate	/*
15437c478bdstevel@tonic-gate	 * Only these actions will be allowed for entries which
15447c478bdstevel@tonic-gate	 * are specified for single-user mode.
15457c478bdstevel@tonic-gate	 */
15467c478bdstevel@tonic-gate	short su_acts = M_INITDEFAULT | M_PF | M_PWAIT | M_WAIT;
15487c478bdstevel@tonic-gate	if (fp_inittab == NULL) {
15497c478bdstevel@tonic-gate		/*
15507c478bdstevel@tonic-gate		 * Before attempting to open inittab we stat it to make
15517c478bdstevel@tonic-gate		 * sure it currently exists and is not empty.  We try
15527c478bdstevel@tonic-gate		 * several times because someone may have temporarily
15537c478bdstevel@tonic-gate		 * unlinked or truncated the file.
15547c478bdstevel@tonic-gate		 */
15557c478bdstevel@tonic-gate		for (i = 0; i < 3; i++) {
15567c478bdstevel@tonic-gate			if (stat(INITTAB, &sbuf) == -1) {
15577c478bdstevel@tonic-gate				if (i == 2) {
15587c478bdstevel@tonic-gate					console(B_TRUE,
15597c478bdstevel@tonic-gate					    "Cannot stat %s, errno: %d\n",
15607c478bdstevel@tonic-gate					    INITTAB, errno);
15617c478bdstevel@tonic-gate					return (FAILURE);
15627c478bdstevel@tonic-gate				} else {
15637c478bdstevel@tonic-gate					timer(3);
15647c478bdstevel@tonic-gate				}
15657c478bdstevel@tonic-gate			} else if (sbuf.st_size < 10) {
15667c478bdstevel@tonic-gate				if (i == 2) {
15677c478bdstevel@tonic-gate					console(B_TRUE,
15687c478bdstevel@tonic-gate					    "%s truncated or corrupted\n",
15697c478bdstevel@tonic-gate					    INITTAB);
15707c478bdstevel@tonic-gate					return (FAILURE);
15717c478bdstevel@tonic-gate				} else {
15727c478bdstevel@tonic-gate					timer(3);
15737c478bdstevel@tonic-gate				}
15747c478bdstevel@tonic-gate			} else {
15757c478bdstevel@tonic-gate				break;
15767c478bdstevel@tonic-gate			}
15777c478bdstevel@tonic-gate		}
15797c478bdstevel@tonic-gate		/*
15807c478bdstevel@tonic-gate		 * If unable to open inittab, print error message and
15817c478bdstevel@tonic-gate		 * return FAILURE to caller.
15827c478bdstevel@tonic-gate		 */
15837c478bdstevel@tonic-gate		if ((fp_inittab = fopen(INITTAB, "r")) == NULL) {
15847c478bdstevel@tonic-gate			console(B_TRUE, "Cannot open %s errno: %d\n", INITTAB,
15857c478bdstevel@tonic-gate			    errno);
15867c478bdstevel@tonic-gate			return (FAILURE);
15877c478bdstevel@tonic-gate		}
15887c478bdstevel@tonic-gate	}
15907c478bdstevel@tonic-gate	/*
15917c478bdstevel@tonic-gate	 * Keep getting commands from inittab until you find a
15927c478bdstevel@tonic-gate	 * good one or run out of file.
15937c478bdstevel@tonic-gate	 */
15947c478bdstevel@tonic-gate	for (answer = FALSE; answer == FALSE; ) {
15957c478bdstevel@tonic-gate		/*
15967c478bdstevel@tonic-gate		 * Zero out the cmd itself before trying next line.
15977c478bdstevel@tonic-gate		 */
15987c478bdstevel@tonic-gate		bzero(cmd, sizeof (struct CMD_LINE));
16007c478bdstevel@tonic-gate		/*
16017c478bdstevel@tonic-gate		 * Read in lines of inittab, parsing at colons, until a line is
16027c478bdstevel@tonic-gate		 * read in which doesn't end with a backslash.  Do not start if
16037c478bdstevel@tonic-gate		 * the first character read is an EOF.  Note that this means
16047c478bdstevel@tonic-gate		 * that lines which don't end in a newline are still processed,
16057c478bdstevel@tonic-gate		 * since the "for" will terminate normally once started,
16067c478bdstevel@tonic-gate		 * regardless of whether line terminates with a newline or EOF.
16077c478bdstevel@tonic-gate		 */
16087c478bdstevel@tonic-gate		state = FAILURE;
16097c478bdstevel@tonic-gate		if ((c = fgetc(fp_inittab)) == EOF) {
16107c478bdstevel@tonic-gate			answer = FALSE;
16117c478bdstevel@tonic-gate			(void) fclose(fp_inittab);
16127c478bdstevel@tonic-gate			fp_inittab = NULL;
16137c478bdstevel@tonic-gate			break;
16147c478bdstevel@tonic-gate		}
16167c478bdstevel@tonic-gate		for (proceed = TRUE, ptr = shcmd, state = ID, lastc = '\0';
16177c478bdstevel@tonic-gate		    proceed && c != EOF;
16187c478bdstevel@tonic-gate		    lastc = c, c = fgetc(fp_inittab)) {
16190a1278fGary Mills			/* If we're not in the FAILURE state and haven't */
16200a1278fGary Mills			/* yet reached the shell command field, process	 */
16210a1278fGary Mills			/* the line, otherwise just look for a real end	 */
16220a1278fGary Mills			/* of line.					 */
16230a1278fGary Mills			if (state != FAILURE && state != COMMAND) {
16247c478bdstevel@tonic-gate			/*
16257c478bdstevel@tonic-gate			 * Squeeze out spaces and tabs.
16267c478bdstevel@tonic-gate			 */
16277c478bdstevel@tonic-gate			if (c == ' ' || c == '\t')
16287c478bdstevel@tonic-gate				continue;
16307c478bdstevel@tonic-gate			/*
16317c478bdstevel@tonic-gate			 * Ignore characters in a comment, except for the \n.
16327c478bdstevel@tonic-gate			 */
16337c478bdstevel@tonic-gate			if (state == COMMENT) {
16347c478bdstevel@tonic-gate				if (c == '\n') {
16357c478bdstevel@tonic-gate					lastc = ' ';
16367c478bdstevel@tonic-gate					break;
16377c478bdstevel@tonic-gate				} else {
16387c478bdstevel@tonic-gate					continue;
16397c478bdstevel@tonic-gate				}
16407c478bdstevel@tonic-gate			}
16427c478bdstevel@tonic-gate			/*
16437c478bdstevel@tonic-gate			 * Detect comments (lines whose first non-whitespace
16447c478bdstevel@tonic-gate			 * character is '#') by checking that we're at the
16457c478bdstevel@tonic-gate			 * beginning of a line, have seen a '#', and haven't
16467c478bdstevel@tonic-gate			 * yet accumulated any characters.
16477c478bdstevel@tonic-gate			 */
16487c478bdstevel@tonic-gate			if (state == ID && c == '#' && ptr == shcmd) {
16497c478bdstevel@tonic-gate				state = COMMENT;
16507c478bdstevel@tonic-gate				continue;
16517c478bdstevel@tonic-gate			}
16537c478bdstevel@tonic-gate			/*
16547c478bdstevel@tonic-gate			 * If the character is a ':', then check the
16557c478bdstevel@tonic-gate			 * previous field for correctness and advance
16567c478bdstevel@tonic-gate			 * to the next field.
16577c478bdstevel@tonic-gate			 */
16587c478bdstevel@tonic-gate			if (c == ':') {
16590a1278fGary Mills				switch (state) {
16610a1278fGary Mills				case ID :
16627c478bdstevel@tonic-gate				/*
16637c478bdstevel@tonic-gate				 * Check to see that there are only
16647c478bdstevel@tonic-gate				 * 1 to 4 characters for the id.
16657c478bdstevel@tonic-gate				 */
16667c478bdstevel@tonic-gate				if ((i = ptr - shcmd) < 1 || i > 4) {
16677c478bdstevel@tonic-gate					state = FAILURE;
16687c478bdstevel@tonic-gate				} else {
16697c478bdstevel@tonic-gate					bcopy(shcmd, &cmd->c_id[0], i);
16707c478bdstevel@tonic-gate					ptr = shcmd;
16717c478bdstevel@tonic-gate					state = LEVELS;
16727c478bdstevel@tonic-gate				}
16737c478bdstevel@tonic-gate				break;
16750a1278fGary Mills				case LEVELS :
16767c478bdstevel@tonic-gate				/*
16777c478bdstevel@tonic-gate				 * Build a mask for all the levels for
16787c478bdstevel@tonic-gate				 * which this command will be legal.
16797c478bdstevel@tonic-gate				 */
16807c478bdstevel@tonic-gate				for (cmd->c_levels = 0, ptr1 = shcmd;
16817c478bdstevel@tonic-gate				    ptr1 < ptr; ptr1++) {
16827c478bdstevel@tonic-gate					int mask;
16837c478bdstevel@tonic-gate					if (lvlname_to_mask(*ptr1,
16847c478bdstevel@tonic-gate					    &mask) == -1) {
16857c478bdstevel@tonic-gate						state = FAILURE;
16867c478bdstevel@tonic-gate						break;
16877c478bdstevel@tonic-gate					}
16887c478bdstevel@tonic-gate					cmd->c_levels |= mask;
16897c478bdstevel@tonic-gate				}
16907c478bdstevel@tonic-gate				if (state != FAILURE) {
16917c478bdstevel@tonic-gate					state = ACTION;
16927c478bdstevel@tonic-gate					ptr = shcmd;	/* Reset the buffer */
16937c478bdstevel@tonic-gate				}
16947c478bdstevel@tonic-gate				break;
16960a1278fGary Mills				case ACTION :
16977c478bdstevel@tonic-gate				/*
16987c478bdstevel@tonic-gate				 * Null terminate the string in shcmd buffer and
16997c478bdstevel@tonic-gate				 * then try to match against legal actions.  If
17007c478bdstevel@tonic-gate				 * the field is of length 0, then the default of
17017c478bdstevel@tonic-gate				 * "RESPAWN" is used if the id is numeric,
17027c478bdstevel@tonic-gate				 * otherwise the default is "OFF".
17037c478bdstevel@tonic-gate				 */
17047c478bdstevel@tonic-gate				if (ptr == shcmd) {
17057c478bdstevel@tonic-gate					if (isdigit(cmd->c_id[0]) &&
17067c478bdstevel@tonic-gate					    (cmd->c_id[1] == '\0' ||
17070a1278fGary Mills					    isdigit(cmd->c_id[1])) &&
17087c478bdstevel@tonic-gate					    (cmd->c_id[2] == '\0' ||
17090a1278fGary Mills					    isdigit(cmd->c_id[2])) &&
17107c478bdstevel@tonic-gate					    (cmd->c_id[3] == '\0' ||
17110a1278fGary Mills					    isdigit(cmd->c_id[3])))
17120a1278fGary Mills						cmd->c_action = M_RESPAWN;
17137c478bdstevel@tonic-gate					else
17140a1278fGary Mills						cmd->c_action = M_OFF;
17157c478bdstevel@tonic-gate				} else {
17160a1278fGary Mills					for (cmd->c_action = 0, i = 0,
17170a1278fGary Mills					    *ptr = '\0';
17180a1278fGary Mills					    i <
17190a1278fGary Mills					    sizeof (actions)/sizeof (char *);
17200a1278fGary Mills					    i++) {
17217c478bdstevel@tonic-gate					if (strcmp(shcmd, actions[i]) == 0) {
17220a1278fGary Mills						if ((cmd->c_levels & MASKSU) &&
17230a1278fGary Mills						    !(act_masks[i] & su_acts))
17240a1278fGary Mills							cmd->c_action = 0;
17250a1278fGary Mills						else
17260a1278fGary Mills							cmd->c_action =
17270a1278fGary Mills							    act_masks[i];
17280a1278fGary Mills						break;
17290a1278fGary Mills					}
17307c478bdstevel@tonic-gate					}
17317c478bdstevel@tonic-gate				}
17337c478bdstevel@tonic-gate				/*
17347c478bdstevel@tonic-gate				 * If the action didn't match any legal action,
17357c478bdstevel@tonic-gate				 * set state to FAILURE.
17367c478bdstevel@tonic-gate				 */
17377c478bdstevel@tonic-gate				if (cmd->c_action == 0) {
17387c478bdstevel@tonic-gate					state = FAILURE;
17397c478bdstevel@tonic-gate				} else {
17407c478bdstevel@tonic-gate					state = COMMAND;
17417c478bdstevel@tonic-gate					(void) strcpy(shcmd, "exec ");
17427c478bdstevel@tonic-gate				}
17437c478bdstevel@tonic-gate				ptr = shcmd + EXEC;
17447c478bdstevel@tonic-gate				break;
17450a1278fGary Mills				}
17460a1278fGary Mills				continue;
17477c478bdstevel@tonic-gate			}
17480a1278fGary Mills		}
17490a1278fGary Mills
17500a1278fGary Mills		/* If the character is a '\n', then this is the end of a */
17510a1278fGary Mills		/* line.  If the '\n' wasn't preceded by a backslash, */
17520a1278fGary Mills		/* it is also the end of an inittab command.  If it was */
17530a1278fGary Mills		/* preceded by a backslash then the next line is a */
17540a1278fGary Mills		/* continuation.  Note that the continuation '\n' falls */
17550a1278fGary Mills		/* through and is treated like other characters and is */
17560a1278fGary Mills		/* stored in the shell command line. */
17570a1278fGary Mills		if (c == '\n' && lastc != '\\') {
17580a1278fGary Mills			proceed = FALSE;
17590a1278fGary Mills			*ptr = '\0';
17600a1278fGary Mills			break;
17610a1278fGary Mills		}
17630a1278fGary Mills		/* For all other characters just stuff them into the */
17640a1278fGary Mills		/* command as long as there aren't too many of them. */
17650a1278fGary Mills		/* Make sure there is room for a terminating '\0' also. */
17660a1278fGary Mills		if (ptr >= shcmd + MAXCMDL - 1)
17677c478bdstevel@tonic-gate			state = FAILURE;
17680a1278fGary Mills		else
17697c478bdstevel@tonic-gate			*ptr++ = (char)c;
17710a1278fGary Mills		/* If the character we just stored was a quoted	*/
17720a1278fGary Mills		/* backslash, then change "c" to '\0', so that this	*/
17730a1278fGary Mills		/* backslash will not cause a subsequent '\n' to appear */
17740a1278fGary Mills		/* quoted.  In otherwords '\' '\' '\n' is the real end */
17750a1278fGary Mills		/* of a command, while '\' '\n' is a continuation. */
17760a1278fGary Mills		if (c == '\\' && lastc == '\\')
17777c478bdstevel@tonic-gate			c = '\0';
17787c478bdstevel@tonic-gate		}
17807c478bdstevel@tonic-gate		/*
17817c478bdstevel@tonic-gate		 * Make sure all the fields are properly specified
17827c478bdstevel@tonic-gate		 * for a good command line.
17837c478bdstevel@tonic-gate		 */
17847c478bdstevel@tonic-gate		if (state == COMMAND) {
17857c478bdstevel@tonic-gate			answer = TRUE;
17867c478bdstevel@tonic-gate			cmd->c_command = shcmd;
17887c478bdstevel@tonic-gate			/*
17897c478bdstevel@tonic-gate			 * If no default level was supplied, insert
17907c478bdstevel@tonic-gate			 * all numerical levels.
17917c478bdstevel@tonic-gate			 */
17927c478bdstevel@tonic-gate			if (cmd->c_levels == 0)
17937c478bdstevel@tonic-gate				cmd->c_levels = MASK_NUMERIC;
17957c478bdstevel@tonic-gate			/*
17967c478bdstevel@tonic-gate			 * If no action has been supplied, declare this
17977c478bdstevel@tonic-gate			 * entry to be OFF.
17987c478bdstevel@tonic-gate			 */
17997c478bdstevel@tonic-gate			if (cmd->c_action == 0)
18007c478bdstevel@tonic-gate				cmd->c_action = M_OFF;
18027c478bdstevel@tonic-gate			/*
18037c478bdstevel@tonic-gate			 * If no shell command has been supplied, make sure
18047c478bdstevel@tonic-gate			 * there is a null string in the command field.
18057c478bdstevel@tonic-gate			 */
18067c478bdstevel@tonic-gate			if (ptr == shcmd + EXEC)
18077c478bdstevel@tonic-gate				*shcmd = '\0';
18087c478bdstevel@tonic-gate		} else
18097c478bdstevel@tonic-gate			answer = FALSE;
18117c478bdstevel@tonic-gate		/*
18127c478bdstevel@tonic-gate		 * If we have reached the end of inittab, then close it
18137c478bdstevel@tonic-gate		 * and quit trying to find a good command line.
18147c478bdstevel@tonic-gate		 */
18157c478bdstevel@tonic-gate		if (c == EOF) {
18167c478bdstevel@tonic-gate			(void) fclose(fp_inittab);
18177c478bdstevel@tonic-gate			fp_inittab = NULL;
18187c478bdstevel@tonic-gate			break;
18197c478bdstevel@tonic-gate		}
18207c478bdstevel@tonic-gate	}
18217c478bdstevel@tonic-gate	return (answer);
18257c478bdstevel@tonic-gate * lvlname_to_state(): convert the character name of a state to its level
18267c478bdstevel@tonic-gate * (its corresponding signal number).
18277c478bdstevel@tonic-gate */
18287c478bdstevel@tonic-gatestatic int
18297c478bdstevel@tonic-gatelvlname_to_state(char name)
18317c478bdstevel@tonic-gate	int i;
18327c478bdstevel@tonic-gate	for (i = 0; i < LVL_NELEMS; i++) {
18337c478bdstevel@tonic-gate		if (lvls[i].lvl_name == name)
18347c478bdstevel@tonic-gate			return (lvls[i].lvl_state);
18357c478bdstevel@tonic-gate	}
18367c478bdstevel@tonic-gate	return (-1);
18407c478bdstevel@tonic-gate * state_to_name(): convert the level to the character name.
18417c478bdstevel@tonic-gate */
18427c478bdstevel@tonic-gatestatic char
18437c478bdstevel@tonic-gatestate_to_name(int state)
18457c478bdstevel@tonic-gate	int i;
18467c478bdstevel@tonic-gate	for (i = 0; i < LVL_NELEMS; i++) {
18477c478bdstevel@tonic-gate		if (lvls[i].lvl_state == state)
18487c478bdstevel@tonic-gate			return (lvls[i].lvl_name);
18497c478bdstevel@tonic-gate	}
18507c478bdstevel@tonic-gate	return (-1);
18547c478bdstevel@tonic-gate * state_to_mask(): return the mask corresponding to a signal number
18557c478bdstevel@tonic-gate */
18567c478bdstevel@tonic-gatestatic int
18577c478bdstevel@tonic-gatestate_to_mask(int state)
18597c478bdstevel@tonic-gate	int i;
18607c478bdstevel@tonic-gate	for (i = 0; i < LVL_NELEMS; i++) {
18617c478bdstevel@tonic-gate		if (lvls[i].lvl_state == state)
18627c478bdstevel@tonic-gate			return (lvls[i].lvl_mask);
18637c478bdstevel@tonic-gate	}
18647c478bdstevel@tonic-gate	return (0);	/* return 0, since that represents an empty mask */
18687c478bdstevel@tonic-gate * lvlname_to_mask(): return the mask corresponding to a levels character name
18697c478bdstevel@tonic-gate */
18707c478bdstevel@tonic-gatestatic int
18717c478bdstevel@tonic-gatelvlname_to_mask(char name, int *mask)
18737c478bdstevel@tonic-gate	int i;
18747c478bdstevel@tonic-gate	for (i = 0; i < LVL_NELEMS; i++) {
18757c478bdstevel@tonic-gate		if (lvls[i].lvl_name == name) {
18767c478bdstevel@tonic-gate			*mask = lvls[i].lvl_mask;
18777c478bdstevel@tonic-gate			return (0);
18787c478bdstevel@tonic-gate		}
18797c478bdstevel@tonic-gate	}
18807c478bdstevel@tonic-gate	return (-1);
18847c478bdstevel@tonic-gate * state_to_flags(): return the flags corresponding to a runlevel.  These
18857c478bdstevel@tonic-gate * indicate properties of that runlevel.
18867c478bdstevel@tonic-gate */
18877c478bdstevel@tonic-gatestatic int
18887c478bdstevel@tonic-gatestate_to_flags(int state)
18907c478bdstevel@tonic-gate	int i;
18917c478bdstevel@tonic-gate	for (i = 0; i < LVL_NELEMS; i++) {
18927c478bdstevel@tonic-gate		if (lvls[i].lvl_state == state)
18937c478bdstevel@tonic-gate			return (lvls[i].lvl_flags);
18947c478bdstevel@tonic-gate	}
18957c478bdstevel@tonic-gate	return (0);
18997c478bdstevel@tonic-gate * killproc() creates a child which kills the process specified by pid.
19007c478bdstevel@tonic-gate */
19027c478bdstevel@tonic-gatekillproc(pid_t pid)
19047c478bdstevel@tonic-gate	struct PROC_TABLE	*process;
1906c39ec06Roger A. Faulkner	(void) sighold(SIGCLD);
19077c478bdstevel@tonic-gate	while ((process = efork(M_OFF, NULLPROC, 0)) == NO_ROOM)
19087c478bdstevel@tonic-gate		(void) pause();
1909c39ec06Roger A. Faulkner	(void) sigrelse(SIGCLD);
19117c478bdstevel@tonic-gate	if (process == NULLPROC) {
19127c478bdstevel@tonic-gate		/*
19137c478bdstevel@tonic-gate		 * efork() sets all signal handlers to the default, so reset
19147c478bdstevel@tonic-gate		 * the ALRM handler to make timer() work as expected.
19157c478bdstevel@tonic-gate		 */
19167c478bdstevel@tonic-gate		(void) sigset(SIGALRM, alarmclk);
19187c478bdstevel@tonic-gate		/*
19197c478bdstevel@tonic-gate		 * We are the child.  Try to terminate the process nicely
19207c478bdstevel@tonic-gate		 * first using SIGTERM and if it refuses to die in TWARN
19217c478bdstevel@tonic-gate		 * seconds kill it with SIGKILL.
19227c478bdstevel@tonic-gate		 */
19237c478bdstevel@tonic-gate		(void) kill(pid, SIGTERM);
19247c478bdstevel@tonic-gate		(void) timer(TWARN);
19257c478bdstevel@tonic-gate		(void) kill(pid, SIGKILL);
19267c478bdstevel@tonic-gate		(void) exit(0);
19277c478bdstevel@tonic-gate	}
19317c478bdstevel@tonic-gate * Set up the default environment for all procs to be forked from init.
19327c478bdstevel@tonic-gate * Read the values from the /etc/default/init file, except for PATH.  If
19337c478bdstevel@tonic-gate * there's not enough room in the environment array, the environment
19347c478bdstevel@tonic-gate * lines that don't fit are silently discarded.
19357c478bdstevel@tonic-gate */
19397c478bdstevel@tonic-gate	char	line[MAXCMDL];
19407c478bdstevel@tonic-gate	FILE	*fp;
19417c478bdstevel@tonic-gate	int	inquotes, length, wslength;
19427c478bdstevel@tonic-gate	char	*tokp, *cp1, *cp2;
19447c478bdstevel@tonic-gate	glob_envp[0] = malloc((unsigned)(strlen(DEF_PATH)+2));
19457c478bdstevel@tonic-gate	(void) strcpy(glob_envp[0], DEF_PATH);
19467c478bdstevel@tonic-gate	glob_envn = 1;
19487c478bdstevel@tonic-gate	if (rflg) {
19497c478bdstevel@tonic-gate		glob_envp[1] =
19504884749Enrico Perla - Sun Microsystems		    malloc((unsigned)(strlen("_DVFS_RECONFIG=YES")+2));
19517c478bdstevel@tonic-gate		(void) strcpy(glob_envp[1], "_DVFS_RECONFIG=YES");
19527c478bdstevel@tonic-gate		++glob_envn;
19537c478bdstevel@tonic-gate	} else if (bflg == 1) {
19547c478bdstevel@tonic-gate		glob_envp[1] =
19554884749Enrico Perla - Sun Microsystems		    malloc((unsigned)(strlen("RB_NOBOOTRC=YES")+2));
19567c478bdstevel@tonic-gate		(void) strcpy(glob_envp[1], "RB_NOBOOTRC=YES");
19577c478bdstevel@tonic-gate		++glob_envn;
19587c478bdstevel@tonic-gate	}
19607c478bdstevel@tonic-gate	if ((fp = fopen(ENVFILE, "r")) == NULL) {
19617c478bdstevel@tonic-gate		console(B_TRUE,
19627c478bdstevel@tonic-gate		    "Cannot open %s. Environment not initialized.\n",
19637c478bdstevel@tonic-gate		    ENVFILE);
19647c478bdstevel@tonic-gate	} else {
19657c478bdstevel@tonic-gate		while (fgets(line, MAXCMDL - 1, fp) != NULL &&
19667c478bdstevel@tonic-gate		    glob_envn < MAXENVENT - 2) {
19677c478bdstevel@tonic-gate			/*
19687c478bdstevel@tonic-gate			 * Toss newline
19697c478bdstevel@tonic-gate			 */
19707c478bdstevel@tonic-gate			length = strlen(line);
19717c478bdstevel@tonic-gate			if (line[length - 1] == '\n')
19727c478bdstevel@tonic-gate				line[length - 1] = '\0';
19747c478bdstevel@tonic-gate			/*
19757c478bdstevel@tonic-gate			 * Ignore blank or comment lines.
19767c478bdstevel@tonic-gate			 */
19777c478bdstevel@tonic-gate			if (line[0] == '#' || line[0] == '\0' ||
19787c478bdstevel@tonic-gate			    (wslength = strspn(line, " \t\n")) ==
19797c478bdstevel@tonic-gate			    strlen(line) ||
19807c478bdstevel@tonic-gate			    strchr(line, '#') == line + wslength)
19817c478bdstevel@tonic-gate				continue;
19837c478bdstevel@tonic-gate			/*
19847c478bdstevel@tonic-gate			 * First make a pass through the line and change
19857c478bdstevel@tonic-gate			 * any non-quoted semi-colons to blanks so they
19867c478bdstevel@tonic-gate			 * will be treated as token separators below.
19877c478bdstevel@tonic-gate			 */
19887c478bdstevel@tonic-gate			inquotes = 0;
19897c478bdstevel@tonic-gate			for (cp1 = line; *cp1 != '\0'; cp1++) {
19907c478bdstevel@tonic-gate				if (*cp1 == '"') {
19917c478bdstevel@tonic-gate					if (inquotes == 0)
19927c478bdstevel@tonic-gate						inquotes = 1;
19937c478bdstevel@tonic-gate					else
19947c478bdstevel@tonic-gate						inquotes = 0;
19957c478bdstevel@tonic-gate				} else if (*cp1 == ';') {
19967c478bdstevel@tonic-gate					if (inquotes == 0)
19977c478bdstevel@tonic-gate						*cp1 = ' ';
19987c478bdstevel@tonic-gate				}
19997c478bdstevel@tonic-gate			}
20017c478bdstevel@tonic-gate			/*
20027c478bdstevel@tonic-gate			 * Tokens within the line are separated by blanks
20037c478bdstevel@tonic-gate			 *  and tabs.  For each token in the line which
20047c478bdstevel@tonic-gate			 * contains a '=' we strip out any quotes and then
20057c478bdstevel@tonic-gate			 * stick the token in the environment array.
20067c478bdstevel@tonic-gate			 */
20077c478bdstevel@tonic-gate			if ((tokp = strtok(line, " \t")) == NULL)
20087c478bdstevel@tonic-gate				continue;
20097c478bdstevel@tonic-gate			do {
20107c478bdstevel@tonic-gate				if (strchr(tokp, '=') == NULL)
20117c478bdstevel@tonic-gate					continue;
20127c478bdstevel@tonic-gate				length = strlen(tokp);
20137c478bdstevel@tonic-gate				while ((cp1 = strpbrk(tokp, "\"\'")) != NULL) {
20147c478bdstevel@tonic-gate					for (cp2 = cp1;
20157c478bdstevel@tonic-gate					    cp2 < &tokp[length]; cp2++)
20167c478bdstevel@tonic-gate						*cp2 = *(cp2 + 1);
20177c478bdstevel@tonic-gate					length--;
20187c478bdstevel@tonic-gate				}
20207c478bdstevel@tonic-gate				if (strncmp(tokp, "CMASK=",
20217c478bdstevel@tonic-gate				    sizeof ("CMASK=") - 1) == 0) {
20227c478bdstevel@tonic-gate					long t;
20247c478bdstevel@tonic-gate					/* We know there's an = */
20257c478bdstevel@tonic-gate					t = strtol(strchr(tokp, '=') + 1, NULL,
20267c478bdstevel@tonic-gate					    8);
20287c478bdstevel@tonic-gate					/* Sanity */
20297c478bdstevel@tonic-gate					if (t <= 077 && t >= 0)
20307c478bdstevel@tonic-gate						cmask = (int)t;
20317c478bdstevel@tonic-gate					(void) umask(cmask);
20327c478bdstevel@tonic-gate					continue;
20337c478bdstevel@tonic-gate				}
20347c478bdstevel@tonic-gate				glob_envp[glob_envn] =
20357c478bdstevel@tonic-gate				    malloc((unsigned)(length + 1));
20367c478bdstevel@tonic-gate				(void) strcpy(glob_envp[glob_envn], tokp);
20377c478bdstevel@tonic-gate				if (++glob_envn >= MAXENVENT - 1)
20387c478bdstevel@tonic-gate					break;
20397c478bdstevel@tonic-gate			} while ((tokp = strtok(NULL, " \t")) != NULL);
20407c478bdstevel@tonic-gate		}
20427c478bdstevel@tonic-gate		/*
20437c478bdstevel@tonic-gate		 * Append a null pointer to the environment array
20447c478bdstevel@tonic-gate		 * to mark its end.
20457c478bdstevel@tonic-gate		 */
20467c478bdstevel@tonic-gate		glob_envp[glob_envn] = NULL;
20477c478bdstevel@tonic-gate		(void) fclose(fp);
20487c478bdstevel@tonic-gate	}
20527c478bdstevel@tonic-gate * boot_init(): Do initialization things that should be done at boot.
20537c478bdstevel@tonic-gate */
20577c478bdstevel@tonic-gate	int i;
20587c478bdstevel@tonic-gate	struct PROC_TABLE *process, *oprocess;
20597c478bdstevel@tonic-gate	struct CMD_LINE	cmd;
20607c478bdstevel@tonic-gate	char	line[MAXCMDL];
20617b209c2acruz	char	svc_aux[SVC_AUX_SIZE];
20627b209c2acruz	char	init_svc_fmri[SVC_FMRI_SIZE];
20637c478bdstevel@tonic-gate	char *old_path;
20647c478bdstevel@tonic-gate	int maxfiles;
20667c478bdstevel@tonic-gate	/* Use INIT_PATH for sysinit cmds */
20677c478bdstevel@tonic-gate	old_path = glob_envp[0];
20687c478bdstevel@tonic-gate	glob_envp[0] = malloc((unsigned)(strlen(INIT_PATH)+2));
20697c478bdstevel@tonic-gate	(void) strcpy(glob_envp[0], INIT_PATH);
20717c478bdstevel@tonic-gate	/*
20727c478bdstevel@tonic-gate	 * Scan inittab(4) and process the special svc.startd entry, initdefault
20737c478bdstevel@tonic-gate	 * and sysinit entries.
20747c478bdstevel@tonic-gate	 */
20757c478bdstevel@tonic-gate	while (getcmd(&cmd, &line[0]) == TRUE) {
20767b209c2acruz		if (startd_tmpl >= 0 && id_eq(cmd.c_id, "smf")) {
20777c478bdstevel@tonic-gate			process_startd_line(&cmd, line);
20787b209c2acruz			(void) snprintf(startd_svc_aux, SVC_AUX_SIZE,
20797b209c2acruz			    INITTAB_ENTRY_ID_STR_FORMAT, cmd.c_id);
20807b209c2acruz		} else if (cmd.c_action == M_INITDEFAULT) {
20817c478bdstevel@tonic-gate			/*
20827c478bdstevel@tonic-gate			 * initdefault is no longer meaningful, as the SMF
20837c478bdstevel@tonic-gate			 * milestone controls what (legacy) run level we
20847c478bdstevel@tonic-gate			 * boot to.
20857c478bdstevel@tonic-gate			 */
20867c478bdstevel@tonic-gate			console(B_TRUE,
20877c478bdstevel@tonic-gate			    "Ignoring legacy \"initdefault\" entry.\n");
20887c478bdstevel@tonic-gate		} else if (cmd.c_action == M_SYSINIT) {
20897c478bdstevel@tonic-gate			/*
20907c478bdstevel@tonic-gate			 * Execute the "sysinit" entry and wait for it to
20917c478bdstevel@tonic-gate			 * complete.  No bookkeeping is performed on these
20927c478bdstevel@tonic-gate			 * entries because we avoid writing to the file system
20937c478bdstevel@tonic-gate			 * until after there has been an chance to check it.
20947c478bdstevel@tonic-gate			 */
20957c478bdstevel@tonic-gate			if (process = findpslot(&cmd)) {
2096c39ec06Roger A. Faulkner				(void) sighold(SIGCLD);
20977b209c2acruz				(void) snprintf(svc_aux, SVC_AUX_SIZE,
20987b209c2acruz				    INITTAB_ENTRY_ID_STR_FORMAT, cmd.c_id);
20997b209c2acruz				(void) snprintf(init_svc_fmri, SVC_FMRI_SIZE,
21017b209c2acruz				    cmd.c_id);
21027b209c2acruz				if (legacy_tmpl >= 0) {
21037b209c2acruz					(void) ct_pr_tmpl_set_svc_fmri(
21047b209c2acruz					    legacy_tmpl, init_svc_fmri);
21057b209c2acruz					(void) ct_pr_tmpl_set_svc_aux(
21067b209c2acruz					    legacy_tmpl, svc_aux);
21077b209c2acruz				}
21097c478bdstevel@tonic-gate				for (oprocess = process;
21107c478bdstevel@tonic-gate				    (process = efork(M_OFF, oprocess,
21117c478bdstevel@tonic-gate				    (NAMED|NOCLEANUP))) == NO_ROOM;
21127c478bdstevel@tonic-gate				    /* CSTYLED */)
21137c478bdstevel@tonic-gate					;
2114c39ec06Roger A. Faulkner				(void) sigrelse(SIGCLD);
21167c478bdstevel@tonic-gate				if (process == NULLPROC) {
21177c478bdstevel@tonic-gate					maxfiles = ulimit(UL_GDESLIM, 0);
21197c478bdstevel@tonic-gate					for (i = 0; i < maxfiles; i++)
21207c478bdstevel@tonic-gate						(void) fcntl(i, F_SETFD,
21217c478bdstevel@tonic-gate						    FD_CLOEXEC);
21227c478bdstevel@tonic-gate					(void) execle(SH, "INITSH", "-c",
21237c478bdstevel@tonic-gate					    cmd.c_command,
21247c478bdstevel@tonic-gate					    (char *)0, glob_envp);
21257c478bdstevel@tonic-gate					console(B_TRUE,
21267c478bdstevel@tonic-gate"Command\n\"%s\"\n failed to execute.  errno = %d (exec of shell failed)\n",
21274884749Enrico Perla - Sun Microsystems					    cmd.c_command, errno);
21287c478bdstevel@tonic-gate					exit(1);
21290a1278fGary Mills				} else
21300a1278fGary Mills					while (waitproc(process) == FAILURE)
21310a1278fGary Mills						;
21327c478bdstevel@tonic-gate				process->p_flags = 0;
21337c478bdstevel@tonic-gate				st_write();
21347c478bdstevel@tonic-gate			}
21357c478bdstevel@tonic-gate		}
21367c478bdstevel@tonic-gate	}
21387c478bdstevel@tonic-gate	/* Restore the path. */
21397c478bdstevel@tonic-gate	free(glob_envp[0]);
21407c478bdstevel@tonic-gate	glob_envp[0] = old_path;
21427c478bdstevel@tonic-gate	/*
21437c478bdstevel@tonic-gate	 * This will enable st_write() to complain about init_state_file.
21447c478bdstevel@tonic-gate	 */
21457c478bdstevel@tonic-gate	booting = 0;
21477c478bdstevel@tonic-gate	/*
21487c478bdstevel@tonic-gate	 * If the /etc/ioctl.syscon didn't exist or had invalid contents write
21497c478bdstevel@tonic-gate	 * out a correct version.
21507c478bdstevel@tonic-gate	 */
21517c478bdstevel@tonic-gate	if (write_ioctl)
21527c478bdstevel@tonic-gate		write_ioctl_syscon();
21547c478bdstevel@tonic-gate	/*
21557c478bdstevel@tonic-gate	 * Start svc.startd(1M), which does most of the work.
21567c478bdstevel@tonic-gate	 */
21577c478bdstevel@tonic-gate	if (startd_cline[0] != '\0' && startd_tmpl >= 0) {
21587c478bdstevel@tonic-gate		/* Start svc.startd. */
21597c478bdstevel@tonic-gate		if (startd_run(startd_cline, startd_tmpl, 0) == -1)
21607c478bdstevel@tonic-gate			cur_state = SINGLE_USER;
21617c478bdstevel@tonic-gate	} else {
21627c478bdstevel@tonic-gate		console(B_TRUE, "Absent svc.startd entry or bad "
21637c478bdstevel@tonic-gate		    "contract template.  Not starting svc.startd.\n");
21647c478bdstevel@tonic-gate		enter_maintenance();
21657c478bdstevel@tonic-gate	}
21697c478bdstevel@tonic-gate * init_signals(): Initialize all signals to either be caught or ignored.
21707c478bdstevel@tonic-gate */
21747c478bdstevel@tonic-gate	struct sigaction act;
21757c478bdstevel@tonic-gate	int i;
21777c478bdstevel@tonic-gate	/*
21787c478bdstevel@tonic-gate	 * Start by ignoring all signals, then selectively re-enable some.
21797c478bdstevel@tonic-gate	 * The SIG_IGN disposition will only affect asynchronous signals:
21807c478bdstevel@tonic-gate	 * any signal that we trigger synchronously that doesn't end up
21817c478bdstevel@tonic-gate	 * being handled by siglvl() will be forcibly delivered by the kernel.
21827c478bdstevel@tonic-gate	 */
21837c478bdstevel@tonic-gate	for (i = SIGHUP; i <= SIGRTMAX; i++)
21847c478bdstevel@tonic-gate		(void) sigset(i, SIG_IGN);
21867c478bdstevel@tonic-gate	/*
21877c478bdstevel@tonic-gate	 * Handle all level-changing signals using siglvl() and set sa_mask so
21887c478bdstevel@tonic-gate	 * that all level-changing signals are blocked while in siglvl().
21897c478bdstevel@tonic-gate	 */
21907c478bdstevel@tonic-gate	act.sa_handler = siglvl;
21917c478bdstevel@tonic-gate	act.sa_flags = SA_SIGINFO;
21927c478bdstevel@tonic-gate	(void) sigemptyset(&act.sa_mask);
21947c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVLQ);
21957c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVL0);
21967c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVL1);
21977c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVL2);
21987c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVL3);
21997c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVL4);
22007c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVL5);
22017c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVL6);
22027c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, SINGLE_USER);
22037c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVLa);
22047c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVLb);
22057c478bdstevel@tonic-gate	(void) sigaddset(&act.sa_mask, LVLc);
22077c478bdstevel@tonic-gate	(void) sigaction(LVLQ, &act, NULL);
22087c478bdstevel@tonic-gate	(void) sigaction(LVL0, &act, NULL);
22097c478bdstevel@tonic-gate	(void) sigaction(LVL1, &act, NULL);
22107c478bdstevel@tonic-gate	(void) sigaction(LVL2, &act, NULL);
22117c478bdstevel@tonic-gate	(void) sigaction(LVL3, &act, NULL);
22127c478bdstevel@tonic-gate	(void) sigaction(LVL4, &act, NULL);
22137c478bdstevel@tonic-gate	(void) sigaction(LVL5, &act, NULL);
22147c478bdstevel@tonic-gate	(void) sigaction(LVL6, &act, NULL);
22157c478bdstevel@tonic-gate	(void) sigaction(SINGLE_USER, &act, NULL);
22167c478bdstevel@tonic-gate	(void) sigaction(LVLa, &act, NULL);
22177c478bdstevel@tonic-gate	(void) sigaction(LVLb, &act, NULL);
22187c478bdstevel@tonic-gate	(void) sigaction(LVLc, &act, NULL);
22207c478bdstevel@tonic-gate	(void) sigset(SIGALRM, alarmclk);
22217c478bdstevel@tonic-gate	alarmclk();
22237c478bdstevel@tonic-gate	(void) sigset(SIGCLD, childeath);
22247c478bdstevel@tonic-gate	(void) sigset(SIGPWR, powerfail);
22287c478bdstevel@tonic-gate * Set up pipe for "godchildren". If the file exists and is a pipe just open
22297c478bdstevel@tonic-gate * it. Else, if the file system is r/w create it.  Otherwise, defer its
223092ba710eschrock * creation and open until after /var/run has been mounted.  This function is
223192ba710eschrock * only called on startup and when explicitly requested via LVLQ.
22327c478bdstevel@tonic-gate */
22367c478bdstevel@tonic-gate	struct stat stat_buf;
22377c478bdstevel@tonic-gate	struct statvfs statvfs_buf;
2238dd965b2nakanon	struct sigaction act;
224092ba710eschrock	/*
224192ba710eschrock	 * Always close the previous pipe descriptor as the mounted filesystems
224292ba710eschrock	 * may have changed.
224392ba710eschrock	 */
224492ba710eschrock	if (Pfd >= 0)
224592ba710eschrock		(void) close(Pfd);
22477c478bdstevel@tonic-gate	if ((stat(INITPIPE, &stat_buf) == 0) &&
22487c478bdstevel@tonic-gate	    ((stat_buf.st_mode & (S_IFMT|S_IRUSR)) == (S_IFIFO|S_IRUSR)))
22497c478bdstevel@tonic-gate		Pfd = open(INITPIPE, O_RDWR | O_NDELAY);
22507c478bdstevel@tonic-gate	else
22517c478bdstevel@tonic-gate		if ((statvfs(INITPIPE_DIR, &statvfs_buf) == 0) &&
22527c478bdstevel@tonic-gate		    ((statvfs_buf.f_flag & ST_RDONLY) == 0)) {
22537c478bdstevel@tonic-gate			(void) unlink(INITPIPE);
22547c478bdstevel@tonic-gate			(void) mknod(INITPIPE, S_IFIFO | 0600, 0);
22557c478bdstevel@tonic-gate			Pfd = open(INITPIPE, O_RDWR | O_NDELAY);
22567c478bdstevel@tonic-gate		}
22587c478bdstevel@tonic-gate	if (Pfd >= 0) {
22597c478bdstevel@tonic-gate		(void) ioctl(Pfd, I_SETSIG, S_INPUT);
22607c478bdstevel@tonic-gate		/*
22617c478bdstevel@tonic-gate		 * Read pipe in message discard mode.
22627c478bdstevel@tonic-gate		 */
22637c478bdstevel@tonic-gate		(void) ioctl(Pfd, I_SRDOPT, RMSGD);
2265dd965b2nakanon		act.sa_handler = sigpoll;
2266dd965b2nakanon		act.sa_flags = 0;
2267dd965b2nakanon		(void) sigemptyset(&act.sa_mask);
2268dd965b2nakanon		(void) sigaddset(&act.sa_mask, SIGCLD);
2269dd965b2nakanon		(void) sigaction(SIGPOLL, &act, NULL);
22707c478bdstevel@tonic-gate	}
22747c478bdstevel@tonic-gate * siglvl - handle an asynchronous signal from init(1M) telling us that we
22757c478bdstevel@tonic-gate * should change the current run level.  We set new_state accordingly.
22767c478bdstevel@tonic-gate */
22787c478bdstevel@tonic-gatesiglvl(int sig, siginfo_t *sip, ucontext_t *ucp)
22807c478bdstevel@tonic-gate	struct PROC_TABLE *process;
22817c478bdstevel@tonic-gate	struct sigaction act;
22837c478bdstevel@tonic-gate	/*
22847c478bdstevel@tonic-gate	 * If the signal was from the kernel (rather than init(1M)) then init
22857c478bdstevel@tonic-gate	 * itself tripped the signal.  That is, we might have a bug and tripped
22867c478bdstevel@tonic-gate	 * a real SIGSEGV instead of receiving it as an alias for SIGLVLa.  In
22877c478bdstevel@tonic-gate	 * such a case we reset the disposition to SIG_DFL, block all signals
22887c478bdstevel@tonic-gate	 * in uc_mask but the current one, and return to the interrupted ucp
22897c478bdstevel@tonic-gate	 * to effect an appropriate death.  The kernel will then restart us.
22907c478bdstevel@tonic-gate	 *
22917c478bdstevel@tonic-gate	 * The one exception to SI_FROMKERNEL() is SIGFPE (a.k.a. LVL6), which
22927c478bdstevel@tonic-gate	 * the kernel can send us when it wants to effect an orderly reboot.
22937c478bdstevel@tonic-gate	 * For this case we must also verify si_code is zero, rather than a
22947c478bdstevel@tonic-gate	 * code such as FPE_INTDIV which a bug might have triggered.
22957c478bdstevel@tonic-gate	 */
22967c478bdstevel@tonic-gate	if (sip != NULL && SI_FROMKERNEL(sip) &&
22977c478bdstevel@tonic-gate	    (sig != SIGFPE || sip->si_code == 0)) {
22997c478bdstevel@tonic-gate		(void) sigemptyset(&act.sa_mask);
23007c478bdstevel@tonic-gate		act.sa_handler = SIG_DFL;
23017c478bdstevel@tonic-gate		act.sa_flags = 0;
23027c478bdstevel@tonic-gate		(void) sigaction(sig, &act, NULL);
23047c478bdstevel@tonic-gate		(void) sigfillset(&ucp->uc_sigmask);
23057c478bdstevel@tonic-gate		(void) sigdelset(&ucp->uc_sigmask, sig);
23067c478bdstevel@tonic-gate		ucp->uc_flags |= UC_SIGMASK;
23087c478bdstevel@tonic-gate		(void) setcontext(ucp);
23097c478bdstevel@tonic-gate	}
23117c478bdstevel@tonic-gate	/*
23127c478bdstevel@tonic-gate	 * If the signal received is a LVLQ signal, do not really
23137c478bdstevel@tonic-gate	 * change levels, just restate the current level.  If the
23147c478bdstevel@tonic-gate	 * signal is not a LVLQ, set the new level to the signal
23157c478bdstevel@tonic-gate	 * received.
23167c478bdstevel@tonic-gate	 */
231792ba710eschrock	if (sig == LVLQ) {
23187c478bdstevel@tonic-gate		new_state = cur_state;
231992ba710eschrock		lvlq_received = B_TRUE;
232092ba710eschrock	} else {
23217c478bdstevel@tonic-gate		new_state = sig;
232292ba710eschrock	}
23247c478bdstevel@tonic-gate	/*
23257c478bdstevel@tonic-gate	 * Clear all times and repeat counts in the process table
23267c478bdstevel@tonic-gate	 * since either the level is changing or the user has editted
23277c478bdstevel@tonic-gate	 * the inittab file and wants us to look at it again.
23287c478bdstevel@tonic-gate	 * If the user has fixed a typo, we don't want residual timing
23297c478bdstevel@tonic-gate	 * data preventing the fixed command line from executing.
23307c478bdstevel@tonic-gate	 */
23317c478bdstevel@tonic-gate	for (process = proc_table;
23324884749Enrico Perla - Sun Microsystems	    (process < proc_table + num_proc); process++) {
23337c478bdstevel@tonic-gate		process->p_time = 0L;
23347c478bdstevel@tonic-gate		process->p_count = 0;
23357c478bdstevel@tonic-gate	}
23377c478bdstevel@tonic-gate	/*
23387c478bdstevel@tonic-gate	 * Set the flag to indicate that a "user signal" was received.
23397c478bdstevel@tonic-gate	 */
23407c478bdstevel@tonic-gate	wakeup.w_flags.w_usersignal = 1;
23457c478bdstevel@tonic-gate * alarmclk
23467c478bdstevel@tonic-gate */
23477c478bdstevel@tonic-gatestatic void
23507c478bdstevel@tonic-gate	time_up = TRUE;
23547c478bdstevel@tonic-gate * childeath_single():
23557c478bdstevel@tonic-gate *
23567c478bdstevel@tonic-gate * This used to be the SIGCLD handler and it was set with signal()
23577c478bdstevel@tonic-gate * (as opposed to sigset()).  When a child exited we'd come to the
23587c478bdstevel@tonic-gate * handler, wait for the child, and reenable the handler with
23597c478bdstevel@tonic-gate * signal() just before returning.  The implementation of signal()
23607c478bdstevel@tonic-gate * checks with waitid() for waitable children and sends a SIGCLD
23617c478bdstevel@tonic-gate * if there are some.  If children are exiting faster than the
23627c478bdstevel@tonic-gate * handler can run we keep sending signals and the handler never
23637c478bdstevel@tonic-gate * gets to return and eventually the stack runs out and init dies.
23647c478bdstevel@tonic-gate * To prevent that we set the handler with sigset() so the handler
23657c478bdstevel@tonic-gate * doesn't need to be reset, and in childeath() (see below) we
23667c478bdstevel@tonic-gate * call childeath_single() as long as there are children to be
23677c478bdstevel@tonic-gate * waited for.  If a child exits while init is in the handler a
23687c478bdstevel@tonic-gate * SIGCLD will be pending and delivered on return from the handler.
23697c478bdstevel@tonic-gate * If the child was already waited for the handler will have nothing
23707c478bdstevel@tonic-gate * to do and return, otherwise the child will be waited for.
23717c478bdstevel@tonic-gate */
23727c478bdstevel@tonic-gatestatic void
2373c39ec06Roger A. Faulknerchildeath_single(pid_t pid, int status)
23757c478bdstevel@tonic-gate	struct PROC_TABLE	*process;
23767c478bdstevel@tonic-gate	struct pidlist		*pp;
23787c478bdstevel@tonic-gate	/*
2379c39ec06Roger A. Faulkner	 * Scan the process table to see if we are interested in this process.
23807c478bdstevel@tonic-gate	 */
23817c478bdstevel@tonic-gate	for (process = proc_table;
23824884749Enrico Perla - Sun Microsystems	    (process < proc_table + num_proc); process++) {
23837c478bdstevel@tonic-gate		if ((process->p_flags & (LIVING|OCCUPIED)) ==
23847c478bdstevel@tonic-gate		    (LIVING|OCCUPIED) && process->p_pid == pid) {
23867c478bdstevel@tonic-gate			/*
23877c478bdstevel@tonic-gate			 * Mark this process as having died and store the exit
23887c478bdstevel@tonic-gate			 * status.  Also set the wakeup flag for a dead child
23897c478bdstevel@tonic-gate			 * and break out of the loop.
23907c478bdstevel@tonic-gate			 */
23917c478bdstevel@tonic-gate			process->p_flags &= ~LIVING;
23927c478bdstevel@tonic-gate			process->p_exit = (short)status;
23937c478bdstevel@tonic-gate			wakeup.w_flags.w_childdeath = 1;
23957c478bdstevel@tonic-gate			return;
23967c478bdstevel@tonic-gate		}
23977c478bdstevel@tonic-gate	}
23997c478bdstevel@tonic-gate	/*
24007c478bdstevel@tonic-gate	 * No process was found above, look through auxiliary list.
24017c478bdstevel@tonic-gate	 */
24027c478bdstevel@tonic-gate	(void) sighold(SIGPOLL);
24037c478bdstevel@tonic-gate	pp = Plhead;
24047c478bdstevel@tonic-gate	while (pp) {
24057c478bdstevel@tonic-gate		if (pid > pp->pl_pid) {
24067c478bdstevel@tonic-gate			/*
24077c478bdstevel@tonic-gate			 * Keep on looking.
24087c478bdstevel@tonic-gate			 */
24097c478bdstevel@tonic-gate			pp = pp->pl_next;
24107c478bdstevel@tonic-gate			continue;
24117c478bdstevel@tonic-gate		} else if (pid < pp->pl_pid) {
24127c478bdstevel@tonic-gate			/*
24137c478bdstevel@tonic-gate			 * Not in the list.
24147c478bdstevel@tonic-gate			 */
24157c478bdstevel@tonic-gate			break;
24167c478bdstevel@tonic-gate		} else {
24177c478bdstevel@tonic-gate			/*
24187c478bdstevel@tonic-gate			 * This is a dead "godchild".
24197c478bdstevel@tonic-gate			 */
24207c478bdstevel@tonic-gate			pp->pl_dflag = 1;
24217c478bdstevel@tonic-gate			pp->pl_exit = (short)status;
24227c478bdstevel@tonic-gate			wakeup.w_flags.w_childdeath = 1;
24237c478bdstevel@tonic-gate			Gchild = 1;	/* Notice to call cleanaux(). */
24247c478bdstevel@tonic-gate			break;
24257c478bdstevel@tonic-gate		}
24267c478bdstevel@tonic-gate	}
24287c478bdstevel@tonic-gate	(void) sigrelse(SIGPOLL);
24317c478bdstevel@tonic-gate/* ARGSUSED */
24327c478bdstevel@tonic-gatestatic void
24337c478bdstevel@tonic-gatechildeath(int signo)
2435c39ec06Roger A. Faulkner	pid_t pid;
2436c39ec06Roger A. Faulkner	int status;
2438c39ec06Roger A. Faulkner	while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
2439c39ec06Roger A. Faulkner		childeath_single(pid, status);
24427c478bdstevel@tonic-gatestatic void
24457c478bdstevel@tonic-gate	(void) nice(-19);
24467c478bdstevel@tonic-gate	wakeup.w_flags.w_powerhit = 1;
24507c478bdstevel@tonic-gate * efork() forks a child and the parent inserts the process in its table
24517c478bdstevel@tonic-gate * of processes that are directly a result of forks that it has performed.
24527c478bdstevel@tonic-gate * The child just changes the "global" with the process id for this process
24537c478bdstevel@tonic-gate * to it's new value.
24547c478bdstevel@tonic-gate * If efork() is called with a pointer into the proc_table it uses that slot,
24557c478bdstevel@tonic-gate * otherwise it searches for a free slot.  Regardless of how it was called,
24567c478bdstevel@tonic-gate * it returns the pointer to the proc_table entry
24577c478bdstevel@tonic-gate *
2458c39ec06Roger A. Faulkner * The SIGCLD signal is blocked (held) before calling efork()
2459c39ec06Roger A. Faulkner * and is unblocked (released) after efork() returns.
24607c478bdstevel@tonic-gate *
24617c478bdstevel@tonic-gate * Ideally, this should be rewritten to use modern signal semantics.
24627c478bdstevel@tonic-gate */
24637c478bdstevel@tonic-gatestatic struct PROC_TABLE *
24647c478bdstevel@tonic-gateefork(int action, struct PROC_TABLE *process, int modes)
24667c478bdstevel@tonic-gate	pid_t	childpid;
24677c478bdstevel@tonic-gate	struct PROC_TABLE *proc;
24687c478bdstevel@tonic-gate	int		i;
24697c478bdstevel@tonic-gate	/*
24707c478bdstevel@tonic-gate	 * Freshen up the proc_table, removing any entries for dead processes
24717c478bdstevel@tonic-gate	 * that don't have NOCLEANUP set.  Perform the necessary accounting.
24727c478bdstevel@tonic-gate	 */
24737c478bdstevel@tonic-gate	for (proc = proc_table; (proc < proc_table + num_proc); proc++) {
24747c478bdstevel@tonic-gate		if ((proc->p_flags & (OCCUPIED|LIVING|NOCLEANUP)) ==
24757c478bdstevel@tonic-gate		    (OCCUPIED)) {
24767c478bdstevel@tonic-gate			/*
24777c478bdstevel@tonic-gate			 * Is this a named process?
24787c478bdstevel@tonic-gate			 * If so, do the necessary bookkeeping.
24797c478bdstevel@tonic-gate			 */
24807c478bdstevel@tonic-gate			if (proc->p_flags & NAMED)
24817c478bdstevel@tonic-gate				(void) account(DEAD_PROCESS, proc, NULL);
24837c478bdstevel@tonic-gate			/*
24847c478bdstevel@tonic-gate			 * Free this entry for new usage.
24857c478bdstevel@tonic-gate			 */
24867c478bdstevel@tonic-gate			proc->p_flags = 0;
24877c478bdstevel@tonic-gate		}
24887c478bdstevel@tonic-gate	}
24907c478bdstevel@tonic-gate	while ((childpid = fork()) == FAILURE) {
24917c478bdstevel@tonic-gate		/*
24927c478bdstevel@tonic-gate		 * Shorten the alarm timer in case someone else's child dies
24937c478bdstevel@tonic-gate		 * and free up a slot in the process table.
24947c478bdstevel@tonic-gate		 */
24957c478bdstevel@tonic-gate		setimer(5);
24977c478bdstevel@tonic-gate		/*
2498c39ec06Roger A. Faulkner		 * Wait for some children to die.  Since efork()
2499c39ec06Roger A. Faulkner		 * is always called with SIGCLD blocked, unblock
2500c39ec06Roger A. Faulkner		 * it here so that child death signals can come in.
25017c478bdstevel@tonic-gate		 */
2502c39ec06Roger A. Faulkner		(void) sigrelse(SIGCLD);
25037c478bdstevel@tonic-gate		(void) pause();
2504c39ec06Roger A. Faulkner		(void) sighold(SIGCLD);
25057c478bdstevel@tonic-gate		setimer(0);
25067c478bdstevel@tonic-gate	}
25087c478bdstevel@tonic-gate	if (