xref: /onnv-gate/usr/src/cmd/init/init.c (revision 12041:03c4bd206296)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51401Smjnelson  * Common Development and Distribution License (the "License").
61401Smjnelson  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211401Smjnelson 
220Sstevel@tonic-gate /*
23*12041SJohn.Beck@Sun.COM  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
310Sstevel@tonic-gate  * The Regents of the University of California
320Sstevel@tonic-gate  * All Rights Reserved
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
350Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
360Sstevel@tonic-gate  * contributors.
370Sstevel@tonic-gate  */
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /*
400Sstevel@tonic-gate  * init(1M) is the general process spawning program.  Its primary job is to
410Sstevel@tonic-gate  * start and restart svc.startd for smf(5).  For backwards-compatibility it also
420Sstevel@tonic-gate  * spawns and respawns processes according to /etc/inittab and the current
430Sstevel@tonic-gate  * run-level.  It reads /etc/default/inittab for general configuration.
440Sstevel@tonic-gate  *
450Sstevel@tonic-gate  * To change run-levels the system administrator runs init from the command
460Sstevel@tonic-gate  * line with a level name.  init signals svc.startd via libscf and directs the
470Sstevel@tonic-gate  * zone's init (pid 1 in the global zone) what to do by sending it a signal;
480Sstevel@tonic-gate  * these signal numbers are commonly refered to in the code as 'states'.  Valid
490Sstevel@tonic-gate  * run-levels are [sS0123456].  Additionally, init can be given directives
500Sstevel@tonic-gate  * [qQabc], which indicate actions to be taken pertaining to /etc/inittab.
510Sstevel@tonic-gate  *
520Sstevel@tonic-gate  * When init processes inittab entries, it finds processes that are to be
530Sstevel@tonic-gate  * spawned at various run-levels.  inittab contains the set of the levels for
540Sstevel@tonic-gate  * which each inittab entry is valid.
550Sstevel@tonic-gate  *
560Sstevel@tonic-gate  * State File and Restartability
570Sstevel@tonic-gate  *   Premature exit by init(1M) is handled as a special case by the kernel:
580Sstevel@tonic-gate  *   init(1M) will be immediately re-executed, retaining its original PID.  (PID
590Sstevel@tonic-gate  *   1 in the global zone.)  To track the processes it has previously spawned,
600Sstevel@tonic-gate  *   as well as other mutable state, init(1M) regularly updates a state file
610Sstevel@tonic-gate  *   such that its subsequent invocations have knowledge of its various
620Sstevel@tonic-gate  *   dependent processes and duties.
630Sstevel@tonic-gate  *
640Sstevel@tonic-gate  * Process Contracts
650Sstevel@tonic-gate  *   We start svc.startd(1M) in a contract and transfer inherited contracts when
660Sstevel@tonic-gate  *   restarting it.  Everything else is started using the legacy contract
670Sstevel@tonic-gate  *   template, and the created contracts are abandoned when they become empty.
680Sstevel@tonic-gate  *
690Sstevel@tonic-gate  * utmpx Entry Handling
700Sstevel@tonic-gate  *   Because init(1M) no longer governs the startup process, its knowledge of
710Sstevel@tonic-gate  *   when utmpx becomes writable is indirect.  However, spawned processes
720Sstevel@tonic-gate  *   expect to be constructed with valid utmpx entries.  As a result, attempts
730Sstevel@tonic-gate  *   to write normal entries will be retried until successful.
740Sstevel@tonic-gate  *
750Sstevel@tonic-gate  * Maintenance Mode
760Sstevel@tonic-gate  *   In certain failure scenarios, init(1M) will enter a maintenance mode, in
770Sstevel@tonic-gate  *   which it invokes sulogin(1M) to allow the operator an opportunity to
780Sstevel@tonic-gate  *   repair the system.  Normally, this operation is performed as a
790Sstevel@tonic-gate  *   fork(2)-exec(2)-waitpid(3C) sequence with the parent waiting for repair or
800Sstevel@tonic-gate  *   diagnosis to be completed.  In the cases that fork(2) requests themselves
810Sstevel@tonic-gate  *   fail, init(1M) will directly execute sulogin(1M), and allow the kernel to
820Sstevel@tonic-gate  *   restart init(1M) on exit from the operator session.
830Sstevel@tonic-gate  *
840Sstevel@tonic-gate  *   One scenario where init(1M) enters its maintenance mode is when
850Sstevel@tonic-gate  *   svc.startd(1M) begins to fail rapidly, defined as when the average time
860Sstevel@tonic-gate  *   between recent failures drops below a given threshold.
870Sstevel@tonic-gate  */
880Sstevel@tonic-gate 
890Sstevel@tonic-gate #include <sys/contract/process.h>
900Sstevel@tonic-gate #include <sys/ctfs.h>
910Sstevel@tonic-gate #include <sys/stat.h>
920Sstevel@tonic-gate #include <sys/statvfs.h>
930Sstevel@tonic-gate #include <sys/stropts.h>
940Sstevel@tonic-gate #include <sys/systeminfo.h>
950Sstevel@tonic-gate #include <sys/time.h>
960Sstevel@tonic-gate #include <sys/termios.h>
970Sstevel@tonic-gate #include <sys/tty.h>
980Sstevel@tonic-gate #include <sys/types.h>
990Sstevel@tonic-gate #include <sys/utsname.h>
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate #include <bsm/adt_event.h>
1020Sstevel@tonic-gate #include <bsm/libbsm.h>
1030Sstevel@tonic-gate #include <security/pam_appl.h>
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate #include <assert.h>
1060Sstevel@tonic-gate #include <ctype.h>
1070Sstevel@tonic-gate #include <dirent.h>
1080Sstevel@tonic-gate #include <errno.h>
1090Sstevel@tonic-gate #include <fcntl.h>
1100Sstevel@tonic-gate #include <libcontract.h>
1110Sstevel@tonic-gate #include <libcontract_priv.h>
1120Sstevel@tonic-gate #include <libintl.h>
1130Sstevel@tonic-gate #include <libscf.h>
1140Sstevel@tonic-gate #include <libscf_priv.h>
1150Sstevel@tonic-gate #include <poll.h>
1160Sstevel@tonic-gate #include <procfs.h>
1170Sstevel@tonic-gate #include <signal.h>
1180Sstevel@tonic-gate #include <stdarg.h>
1190Sstevel@tonic-gate #include <stdio.h>
1200Sstevel@tonic-gate #include <stdio_ext.h>
1210Sstevel@tonic-gate #include <stdlib.h>
1220Sstevel@tonic-gate #include <string.h>
1230Sstevel@tonic-gate #include <strings.h>
1240Sstevel@tonic-gate #include <syslog.h>
1250Sstevel@tonic-gate #include <time.h>
1260Sstevel@tonic-gate #include <ulimit.h>
1270Sstevel@tonic-gate #include <unistd.h>
1280Sstevel@tonic-gate #include <utmpx.h>
1290Sstevel@tonic-gate #include <wait.h>
1300Sstevel@tonic-gate #include <zone.h>
1310Sstevel@tonic-gate #include <ucontext.h>
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate #undef	sleep
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate #define	fioctl(p, sptr, cmd)	ioctl(fileno(p), sptr, cmd)
1360Sstevel@tonic-gate #define	min(a, b)		(((a) < (b)) ? (a) : (b))
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate #define	TRUE	1
1390Sstevel@tonic-gate #define	FALSE	0
1400Sstevel@tonic-gate #define	FAILURE	-1
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate #define	UT_LINE_SZ	32	/* Size of a utmpx ut_line field */
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate /*
1450Sstevel@tonic-gate  * SLEEPTIME	The number of seconds "init" sleeps between wakeups if
1460Sstevel@tonic-gate  *		nothing else requires this "init" wakeup.
1470Sstevel@tonic-gate  */
1480Sstevel@tonic-gate #define	SLEEPTIME	(5 * 60)
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate /*
1510Sstevel@tonic-gate  * MAXCMDL	The maximum length of a command string in inittab.
1520Sstevel@tonic-gate  */
1530Sstevel@tonic-gate #define	MAXCMDL	512
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate /*
1560Sstevel@tonic-gate  * EXEC		The length of the prefix string added to all comamnds
1570Sstevel@tonic-gate  *		found in inittab.
1580Sstevel@tonic-gate  */
1590Sstevel@tonic-gate #define	EXEC	(sizeof ("exec ") - 1)
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate /*
1620Sstevel@tonic-gate  * TWARN	The amount of time between warning signal, SIGTERM,
1630Sstevel@tonic-gate  *		and the fatal kill signal, SIGKILL.
1640Sstevel@tonic-gate  */
1650Sstevel@tonic-gate #define	TWARN	5
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate #define	id_eq(x, y)	((x[0] == y[0] && x[1] == y[1] && x[2] == y[2] &&\
1680Sstevel@tonic-gate 			x[3] == y[3]) ? TRUE : FALSE)
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate /*
1710Sstevel@tonic-gate  * The kernel's default umask is 022 these days; since some processes inherit
1720Sstevel@tonic-gate  * their umask from init, init will set it from CMASK in /etc/default/init.
1730Sstevel@tonic-gate  * init gets the default umask from the kernel, it sets it to 022 whenever
1740Sstevel@tonic-gate  * it wants to create a file and reverts to CMASK afterwards.
1750Sstevel@tonic-gate  */
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate static int cmask;
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate /*
1800Sstevel@tonic-gate  * The following definitions, concluding with the 'lvls' array, provide a
1810Sstevel@tonic-gate  * common mapping between level-name (like 'S'), signal number (state),
1820Sstevel@tonic-gate  * run-level mask, and specific properties associated with a run-level.
1830Sstevel@tonic-gate  * This array should be accessed using the routines lvlname_to_state(),
1840Sstevel@tonic-gate  * lvlname_to_mask(), state_to_mask(), and state_to_flags().
1850Sstevel@tonic-gate  */
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate /*
1880Sstevel@tonic-gate  * Correspondence of signals to init actions.
1890Sstevel@tonic-gate  */
1900Sstevel@tonic-gate #define	LVLQ		SIGHUP
1910Sstevel@tonic-gate #define	LVL0		SIGINT
1920Sstevel@tonic-gate #define	LVL1		SIGQUIT
1930Sstevel@tonic-gate #define	LVL2		SIGILL
1940Sstevel@tonic-gate #define	LVL3		SIGTRAP
1950Sstevel@tonic-gate #define	LVL4		SIGIOT
1960Sstevel@tonic-gate #define	LVL5		SIGEMT
1970Sstevel@tonic-gate #define	LVL6		SIGFPE
1980Sstevel@tonic-gate #define	SINGLE_USER	SIGBUS
1990Sstevel@tonic-gate #define	LVLa		SIGSEGV
2000Sstevel@tonic-gate #define	LVLb		SIGSYS
2010Sstevel@tonic-gate #define	LVLc		SIGPIPE
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate /*
2040Sstevel@tonic-gate  * Bit Mask for each level.  Used to determine legal levels.
2050Sstevel@tonic-gate  */
2060Sstevel@tonic-gate #define	MASK0	0x0001
2070Sstevel@tonic-gate #define	MASK1	0x0002
2080Sstevel@tonic-gate #define	MASK2	0x0004
2090Sstevel@tonic-gate #define	MASK3	0x0008
2100Sstevel@tonic-gate #define	MASK4	0x0010
2110Sstevel@tonic-gate #define	MASK5	0x0020
2120Sstevel@tonic-gate #define	MASK6	0x0040
2130Sstevel@tonic-gate #define	MASKSU	0x0080
2140Sstevel@tonic-gate #define	MASKa	0x0100
2150Sstevel@tonic-gate #define	MASKb	0x0200
2160Sstevel@tonic-gate #define	MASKc	0x0400
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate #define	MASK_NUMERIC (MASK0 | MASK1 | MASK2 | MASK3 | MASK4 | MASK5 | MASK6)
2190Sstevel@tonic-gate #define	MASK_abc (MASKa | MASKb | MASKc)
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate /*
2220Sstevel@tonic-gate  * Flags to indicate properties of various states.
2230Sstevel@tonic-gate  */
2240Sstevel@tonic-gate #define	LSEL_RUNLEVEL	0x0001	/* runlevels you can transition to */
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate typedef struct lvl {
2270Sstevel@tonic-gate 	int	lvl_state;
2280Sstevel@tonic-gate 	int	lvl_mask;
2290Sstevel@tonic-gate 	char	lvl_name;
2300Sstevel@tonic-gate 	int	lvl_flags;
2310Sstevel@tonic-gate } lvl_t;
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate static lvl_t lvls[] = {
2340Sstevel@tonic-gate 	{ LVLQ,		0,	'Q', 0					},
2350Sstevel@tonic-gate 	{ LVLQ,		0,	'q', 0					},
2362481Spaulson 	{ LVL0,		MASK0,	'0', LSEL_RUNLEVEL			},
2372481Spaulson 	{ LVL1, 	MASK1,	'1', LSEL_RUNLEVEL			},
2380Sstevel@tonic-gate 	{ LVL2, 	MASK2,	'2', LSEL_RUNLEVEL			},
2390Sstevel@tonic-gate 	{ LVL3, 	MASK3,	'3', LSEL_RUNLEVEL			},
2400Sstevel@tonic-gate 	{ LVL4, 	MASK4,	'4', LSEL_RUNLEVEL			},
2412481Spaulson 	{ LVL5, 	MASK5,	'5', LSEL_RUNLEVEL			},
2422481Spaulson 	{ LVL6, 	MASK6, 	'6', LSEL_RUNLEVEL			},
2432481Spaulson 	{ SINGLE_USER, 	MASKSU, 'S', LSEL_RUNLEVEL			},
2442481Spaulson 	{ SINGLE_USER, 	MASKSU, 's', LSEL_RUNLEVEL			},
2450Sstevel@tonic-gate 	{ LVLa,		MASKa,	'a', 0					},
2460Sstevel@tonic-gate 	{ LVLb,		MASKb,	'b', 0					},
2470Sstevel@tonic-gate 	{ LVLc,		MASKc,	'c', 0					}
2480Sstevel@tonic-gate };
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate #define	LVL_NELEMS (sizeof (lvls) / sizeof (lvl_t))
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate /*
2530Sstevel@tonic-gate  * Legal action field values.
2540Sstevel@tonic-gate  */
2550Sstevel@tonic-gate #define	OFF		0	/* Kill process if on, else ignore */
2560Sstevel@tonic-gate #define	RESPAWN		1	/* Continuously restart process when it dies */
2570Sstevel@tonic-gate #define	ONDEMAND	RESPAWN	/* Respawn for a, b, c type processes */
2580Sstevel@tonic-gate #define	ONCE		2	/* Start process, do not respawn when dead */
2590Sstevel@tonic-gate #define	WAIT		3	/* Perform once and wait to complete */
2600Sstevel@tonic-gate #define	BOOT		4	/* Start at boot time only */
2610Sstevel@tonic-gate #define	BOOTWAIT	5	/* Start at boot time and wait to complete */
2620Sstevel@tonic-gate #define	POWERFAIL	6	/* Start on powerfail */
2630Sstevel@tonic-gate #define	POWERWAIT	7	/* Start and wait for complete on powerfail */
2640Sstevel@tonic-gate #define	INITDEFAULT	8	/* Default level "init" should start at */
2650Sstevel@tonic-gate #define	SYSINIT		9	/* Actions performed before init speaks */
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate #define	M_OFF		0001
2680Sstevel@tonic-gate #define	M_RESPAWN	0002
2690Sstevel@tonic-gate #define	M_ONDEMAND	M_RESPAWN
2700Sstevel@tonic-gate #define	M_ONCE		0004
2710Sstevel@tonic-gate #define	M_WAIT		0010
2720Sstevel@tonic-gate #define	M_BOOT		0020
2730Sstevel@tonic-gate #define	M_BOOTWAIT	0040
2740Sstevel@tonic-gate #define	M_PF		0100
2750Sstevel@tonic-gate #define	M_PWAIT		0200
2760Sstevel@tonic-gate #define	M_INITDEFAULT	0400
2770Sstevel@tonic-gate #define	M_SYSINIT	01000
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate /* States for the inittab parser in getcmd(). */
2800Sstevel@tonic-gate #define	ID	1
2810Sstevel@tonic-gate #define	LEVELS	2
2820Sstevel@tonic-gate #define	ACTION	3
2830Sstevel@tonic-gate #define	COMMAND	4
2840Sstevel@tonic-gate #define	COMMENT	5
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate /*
2876073Sacruz  * inittab entry id constants
2886073Sacruz  */
2896073Sacruz #define	INITTAB_ENTRY_ID_SIZE 4
2906073Sacruz #define	INITTAB_ENTRY_ID_STR_FORMAT "%.4s"	/* if INITTAB_ENTRY_ID_SIZE */
2916073Sacruz 						/* changes, this should */
2926073Sacruz 						/* change accordingly */
2936073Sacruz 
2946073Sacruz /*
2950Sstevel@tonic-gate  * Init can be in any of three main states, "normal" mode where it is
2960Sstevel@tonic-gate  * processing entries for the lines file in a normal fashion, "boot" mode,
2970Sstevel@tonic-gate  * where it is only interested in the boot actions, and "powerfail" mode,
2980Sstevel@tonic-gate  * where it is only interested in powerfail related actions. The following
2990Sstevel@tonic-gate  * masks declare the legal actions for each mode.
3000Sstevel@tonic-gate  */
3010Sstevel@tonic-gate #define	NORMAL_MODES	(M_OFF | M_RESPAWN | M_ONCE | M_WAIT)
3020Sstevel@tonic-gate #define	BOOT_MODES	(M_BOOT | M_BOOTWAIT)
3030Sstevel@tonic-gate #define	PF_MODES	(M_PF | M_PWAIT)
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate struct PROC_TABLE {
3066073Sacruz 	char	p_id[INITTAB_ENTRY_ID_SIZE];	/* Four letter unique id of */
3076073Sacruz 						/* process */
3080Sstevel@tonic-gate 	pid_t	p_pid;		/* Process id */
3090Sstevel@tonic-gate 	short	p_count;	/* How many respawns of this command in */
3100Sstevel@tonic-gate 				/*   the current series */
3110Sstevel@tonic-gate 	long	p_time;		/* Start time for a series of respawns */
3120Sstevel@tonic-gate 	short	p_flags;
3130Sstevel@tonic-gate 	short	p_exit;		/* Exit status of a process which died */
3140Sstevel@tonic-gate };
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate /*
3170Sstevel@tonic-gate  * Flags for the "p_flags" word of a PROC_TABLE entry:
3180Sstevel@tonic-gate  *
3190Sstevel@tonic-gate  *	OCCUPIED	This slot in init's proc table is in use.
3200Sstevel@tonic-gate  *
3210Sstevel@tonic-gate  *	LIVING		Process is alive.
3220Sstevel@tonic-gate  *
3230Sstevel@tonic-gate  *	NOCLEANUP	efork() is not allowed to cleanup this entry even
3240Sstevel@tonic-gate  *			if process is dead.
3250Sstevel@tonic-gate  *
3260Sstevel@tonic-gate  *	NAMED		This process has a name, i.e. came from inittab.
3270Sstevel@tonic-gate  *
3280Sstevel@tonic-gate  *	DEMANDREQUEST	Process started by a "telinit [abc]" command.  Processes
3290Sstevel@tonic-gate  *			formed this way are respawnable and immune to level
3300Sstevel@tonic-gate  *			changes as long as their entry exists in inittab.
3310Sstevel@tonic-gate  *
3320Sstevel@tonic-gate  *	TOUCHED		Flag used by remv() to determine whether it has looked
3330Sstevel@tonic-gate  *			at an entry while checking for processes to be killed.
3340Sstevel@tonic-gate  *
3350Sstevel@tonic-gate  *	WARNED		Flag used by remv() to mark processes that have been
3360Sstevel@tonic-gate  *			sent the SIGTERM signal.  If they don't die in 5
3370Sstevel@tonic-gate  *			seconds, they are sent the SIGKILL signal.
3380Sstevel@tonic-gate  *
3390Sstevel@tonic-gate  *	KILLED		Flag used by remv() to mark procs that have been sent
3400Sstevel@tonic-gate  *			the SIGTERM and SIGKILL signals.
3410Sstevel@tonic-gate  *
3420Sstevel@tonic-gate  *	PF_MASK		Bitwise or of legal flags, for sanity checking.
3430Sstevel@tonic-gate  */
3440Sstevel@tonic-gate #define	OCCUPIED	01
3450Sstevel@tonic-gate #define	LIVING		02
3460Sstevel@tonic-gate #define	NOCLEANUP	04
3470Sstevel@tonic-gate #define	NAMED		010
3480Sstevel@tonic-gate #define	DEMANDREQUEST	020
3490Sstevel@tonic-gate #define	TOUCHED		040
3500Sstevel@tonic-gate #define	WARNED		0100
3510Sstevel@tonic-gate #define	KILLED		0200
3520Sstevel@tonic-gate #define	PF_MASK		0377
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate /*
3550Sstevel@tonic-gate  * Respawn limits for processes that are to be respawned:
3560Sstevel@tonic-gate  *
3570Sstevel@tonic-gate  *	SPAWN_INTERVAL	The number of seconds over which "init" will try to
3580Sstevel@tonic-gate  *			respawn a process SPAWN_LIMIT times before it gets mad.
3590Sstevel@tonic-gate  *
3600Sstevel@tonic-gate  *	SPAWN_LIMIT	The number of respawns "init" will attempt in
3610Sstevel@tonic-gate  *			SPAWN_INTERVAL seconds before it generates an
3620Sstevel@tonic-gate  *			error message and inhibits further tries for
3630Sstevel@tonic-gate  *			INHIBIT seconds.
3640Sstevel@tonic-gate  *
3650Sstevel@tonic-gate  *	INHIBIT		The number of seconds "init" ignores an entry it had
3660Sstevel@tonic-gate  *			trouble spawning unless a "telinit Q" is received.
3670Sstevel@tonic-gate  */
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate #define	SPAWN_INTERVAL	(2*60)
3700Sstevel@tonic-gate #define	SPAWN_LIMIT	10
3710Sstevel@tonic-gate #define	INHIBIT		(5*60)
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate /*
3740Sstevel@tonic-gate  * The maximum number of decimal digits for an id_t.  (ceil(log10 (max_id)))
3750Sstevel@tonic-gate  */
3760Sstevel@tonic-gate #define	ID_MAX_STR_LEN	10
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate #define	NULLPROC	((struct PROC_TABLE *)(0))
3790Sstevel@tonic-gate #define	NO_ROOM		((struct PROC_TABLE *)(FAILURE))
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate struct CMD_LINE {
3826073Sacruz 	char c_id[INITTAB_ENTRY_ID_SIZE];	/* Four letter unique id of */
3836073Sacruz 						/* process to be affected by */
3846073Sacruz 						/* action */
3850Sstevel@tonic-gate 	short c_levels;	/* Mask of legal levels for process */
3860Sstevel@tonic-gate 	short c_action;	/* Mask for type of action required */
3870Sstevel@tonic-gate 	char *c_command; /* Pointer to init command */
3880Sstevel@tonic-gate };
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate struct	pidrec {
3910Sstevel@tonic-gate 	int	pd_type;	/* Command type */
3920Sstevel@tonic-gate 	pid_t	pd_pid;		/* pid to add or remove */
3930Sstevel@tonic-gate };
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate /*
3960Sstevel@tonic-gate  * pd_type's
3970Sstevel@tonic-gate  */
3980Sstevel@tonic-gate #define	ADDPID	1
3990Sstevel@tonic-gate #define	REMPID	2
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate static struct	pidlist {
4020Sstevel@tonic-gate 	pid_t	pl_pid;		/* pid to watch for */
4030Sstevel@tonic-gate 	int	pl_dflag;	/* Flag indicating SIGCLD from this pid */
4040Sstevel@tonic-gate 	short	pl_exit;	/* Exit status of proc */
4050Sstevel@tonic-gate 	struct	pidlist	*pl_next; /* Next in list */
4060Sstevel@tonic-gate } *Plhead, *Plfree;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate /*
4090Sstevel@tonic-gate  * The following structure contains a set of modes for /dev/syscon
4108770SJordan.Vaughan@Sun.com  * and should match the default contents of /etc/ioctl.syscon.  It should also
4118770SJordan.Vaughan@Sun.com  * be kept in-sync with base_termios in uts/common/io/ttcompat.c.
4120Sstevel@tonic-gate  */
4130Sstevel@tonic-gate static struct termios	dflt_termios = {
4140Sstevel@tonic-gate 	BRKINT|ICRNL|IXON|IMAXBEL,			/* iflag */
4150Sstevel@tonic-gate 	OPOST|ONLCR|TAB3,				/* oflag */
4160Sstevel@tonic-gate 	CS8|CREAD|B9600,				/* cflag */
4170Sstevel@tonic-gate 	ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN, /* lflag */
4180Sstevel@tonic-gate 	CINTR, CQUIT, CERASE, CKILL, CEOF, 0, 0, 0,
4190Sstevel@tonic-gate 	0, 0, 0, 0, 0, 0, 0, 0,
4200Sstevel@tonic-gate 	0, 0, 0
4210Sstevel@tonic-gate };
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate static struct termios	stored_syscon_termios;
4240Sstevel@tonic-gate static int		write_ioctl = 0;	/* Rewrite /etc/ioctl.syscon */
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate static union WAKEUP {
4270Sstevel@tonic-gate 	struct WAKEFLAGS {
4280Sstevel@tonic-gate 		unsigned w_usersignal : 1;	/* User sent signal to "init" */
4290Sstevel@tonic-gate 		unsigned w_childdeath : 1;	/* An "init" child died */
4300Sstevel@tonic-gate 		unsigned w_powerhit : 1;	/* OS experienced powerfail */
4310Sstevel@tonic-gate 	}	w_flags;
4320Sstevel@tonic-gate 	int w_mask;
4330Sstevel@tonic-gate } wakeup;
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate struct init_state {
4370Sstevel@tonic-gate 	int			ist_runlevel;
4380Sstevel@tonic-gate 	int			ist_num_proc;
4390Sstevel@tonic-gate 	int			ist_utmpx_ok;
4400Sstevel@tonic-gate 	struct PROC_TABLE	ist_proc_table[1];
4410Sstevel@tonic-gate };
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate #define	cur_state	(g_state->ist_runlevel)
4440Sstevel@tonic-gate #define	num_proc	(g_state->ist_num_proc)
4450Sstevel@tonic-gate #define	proc_table	(g_state->ist_proc_table)
4460Sstevel@tonic-gate #define	utmpx_ok	(g_state->ist_utmpx_ok)
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate /* Contract cookies. */
4490Sstevel@tonic-gate #define	ORDINARY_COOKIE		0
4500Sstevel@tonic-gate #define	STARTD_COOKIE		1
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate #ifndef NDEBUG
4540Sstevel@tonic-gate #define	bad_error(func, err)	{					\
4550Sstevel@tonic-gate 	(void) fprintf(stderr, "%s:%d: %s() failed with unexpected "	\
4560Sstevel@tonic-gate 	    "error %d.  Aborting.\n", __FILE__, __LINE__, (func), (err)); \
4570Sstevel@tonic-gate 	abort();							\
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate #else
4600Sstevel@tonic-gate #define	bad_error(func, err)	abort()
4610Sstevel@tonic-gate #endif
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate /*
4650Sstevel@tonic-gate  * Useful file and device names.
4660Sstevel@tonic-gate  */
4670Sstevel@tonic-gate static char *CONSOLE	  = "/dev/console";	/* Real system console */
4685017Seschrock static char *INITPIPE_DIR = "/var/run";
4695017Seschrock static char *INITPIPE	  = "/var/run/initpipe";
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate #define	INIT_STATE_DIR "/etc/svc/volatile"
4720Sstevel@tonic-gate static const char * const init_state_file = INIT_STATE_DIR "/init.state";
4730Sstevel@tonic-gate static const char * const init_next_state_file =
4740Sstevel@tonic-gate 	INIT_STATE_DIR "/init-next.state";
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate static const int init_num_proc = 20;	/* Initial size of process table. */
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate static char *UTMPX	 = UTMPX_FILE;		/* Snapshot record file */
4790Sstevel@tonic-gate static char *WTMPX	 = WTMPX_FILE;		/* Long term record file */
4800Sstevel@tonic-gate static char *INITTAB	 = "/etc/inittab";	/* Script file for "init" */
4810Sstevel@tonic-gate static char *SYSTTY	 = "/dev/systty";	/* System Console */
4820Sstevel@tonic-gate static char *SYSCON	 = "/dev/syscon";	/* Virtual System console */
4830Sstevel@tonic-gate static char *IOCTLSYSCON = "/etc/ioctl.syscon";	/* Last syscon modes */
4840Sstevel@tonic-gate static char *ENVFILE	 = "/etc/default/init";	/* Default env. */
4850Sstevel@tonic-gate static char *SU	= "/etc/sulogin";	/* Super-user program for single user */
4860Sstevel@tonic-gate static char *SH	= "/sbin/sh";		/* Standard shell */
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate /*
4890Sstevel@tonic-gate  * Default Path.  /sbin is included in path only during sysinit phase
4900Sstevel@tonic-gate  */
4910Sstevel@tonic-gate #define	DEF_PATH	"PATH=/usr/sbin:/usr/bin"
4920Sstevel@tonic-gate #define	INIT_PATH	"PATH=/sbin:/usr/sbin:/usr/bin"
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate static int	prior_state;
4950Sstevel@tonic-gate static int	prev_state;	/* State "init" was in last time it woke */
4960Sstevel@tonic-gate static int	new_state;	/* State user wants "init" to go to. */
4975017Seschrock static int	lvlq_received;	/* Explicit request to examine state */
4980Sstevel@tonic-gate static int	op_modes = BOOT_MODES; /* Current state of "init" */
4990Sstevel@tonic-gate static int	Gchild = 0;	/* Flag to indicate "godchild" died, set in */
5000Sstevel@tonic-gate 				/*   childeath() and cleared in cleanaux() */
5010Sstevel@tonic-gate static int	Pfd = -1;	/* fd to receive pids thru */
5020Sstevel@tonic-gate static unsigned int	spawncnt, pausecnt;
5030Sstevel@tonic-gate static int	rsflag;		/* Set if a respawn has taken place */
5040Sstevel@tonic-gate static volatile int time_up;	/* Flag set to TRUE by the alarm interrupt */
5050Sstevel@tonic-gate 				/* routine each time an alarm interrupt */
5060Sstevel@tonic-gate 				/* takes place. */
5070Sstevel@tonic-gate static int	sflg = 0;	/* Set if we were booted -s to single user */
5080Sstevel@tonic-gate static int	rflg = 0;	/* Set if booted -r, reconfigure devices */
5090Sstevel@tonic-gate static int	bflg = 0;	/* Set if booted -b, don't run rc scripts */
5100Sstevel@tonic-gate static pid_t	init_pid;	/* PID of "one true" init for current zone */
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate static struct init_state *g_state = NULL;
5130Sstevel@tonic-gate static size_t	g_state_sz;
5140Sstevel@tonic-gate static int	booting = 1;	/* Set while we're booting. */
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate /*
5170Sstevel@tonic-gate  * Array for default global environment.
5180Sstevel@tonic-gate  */
5190Sstevel@tonic-gate #define	MAXENVENT	24	/* Max number of default env variables + 1 */
5200Sstevel@tonic-gate 				/* init can use three itself, so this leaves */
5210Sstevel@tonic-gate 				/* 20 for the administrator in ENVFILE. */
5220Sstevel@tonic-gate static char	*glob_envp[MAXENVENT];	/* Array of environment strings */
5230Sstevel@tonic-gate static int	glob_envn;		/* Number of environment strings */
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate static struct pollfd	poll_fds[1];
5270Sstevel@tonic-gate static int		poll_nfds = 0;	/* poll_fds is uninitialized */
5280Sstevel@tonic-gate 
5296073Sacruz /*
5306073Sacruz  * Contracts constants
5316073Sacruz  */
5326073Sacruz #define	SVC_INIT_PREFIX "init:/"
5336073Sacruz #define	SVC_AUX_SIZE (INITTAB_ENTRY_ID_SIZE + 1)
5346073Sacruz #define	SVC_FMRI_SIZE (sizeof (SVC_INIT_PREFIX) + INITTAB_ENTRY_ID_SIZE)
5356073Sacruz 
5360Sstevel@tonic-gate static int	legacy_tmpl = -1;	/* fd for legacy contract template */
5370Sstevel@tonic-gate static int	startd_tmpl = -1;	/* fd for svc.startd's template */
5386073Sacruz static char	startd_svc_aux[SVC_AUX_SIZE];
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate static char	startd_cline[256] = "";	/* svc.startd's command line */
5410Sstevel@tonic-gate static int	do_restart_startd = 1;	/* Whether to restart svc.startd. */
5420Sstevel@tonic-gate static char	*smf_options = NULL;	/* Options to give to startd. */
5430Sstevel@tonic-gate static int	smf_debug = 0;		/* Messages for debugging smf(5) */
5440Sstevel@tonic-gate static time_t	init_boot_time;		/* Substitute for kernel boot time. */
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate #define	NSTARTD_FAILURE_TIMES	3		/* trigger after 3 failures */
5470Sstevel@tonic-gate #define	STARTD_FAILURE_RATE_NS	5000000000LL	/* 1 failure/5 seconds */
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate static hrtime_t	startd_failure_time[NSTARTD_FAILURE_TIMES];
5500Sstevel@tonic-gate static uint_t	startd_failure_index;
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate static char	*prog_name(char *);
5540Sstevel@tonic-gate static int	state_to_mask(int);
5550Sstevel@tonic-gate static int	lvlname_to_mask(char, int *);
5560Sstevel@tonic-gate static void	lscf_set_runlevel(char);
5570Sstevel@tonic-gate static int	state_to_flags(int);
5580Sstevel@tonic-gate static char	state_to_name(int);
5590Sstevel@tonic-gate static int	lvlname_to_state(char);
5600Sstevel@tonic-gate static int	getcmd(struct CMD_LINE *, char *);
5610Sstevel@tonic-gate static int	realcon();
5620Sstevel@tonic-gate static int	spawn_processes();
5630Sstevel@tonic-gate static int	get_ioctl_syscon();
5640Sstevel@tonic-gate static int	account(short, struct PROC_TABLE *, char *);
5650Sstevel@tonic-gate static void	alarmclk();
5660Sstevel@tonic-gate static void	childeath(int);
5670Sstevel@tonic-gate static void	cleanaux();
5680Sstevel@tonic-gate static void	clearent(pid_t, short);
5690Sstevel@tonic-gate static void	console(boolean_t, char *, ...);
5700Sstevel@tonic-gate static void	init_signals(void);
5710Sstevel@tonic-gate static void	setup_pipe();
5720Sstevel@tonic-gate static void	killproc(pid_t);
5730Sstevel@tonic-gate static void	init_env();
5740Sstevel@tonic-gate static void	boot_init();
5750Sstevel@tonic-gate static void	powerfail();
5760Sstevel@tonic-gate static void	remv();
5770Sstevel@tonic-gate static void	write_ioctl_syscon();
5780Sstevel@tonic-gate static void	spawn(struct PROC_TABLE *, struct CMD_LINE *);
5790Sstevel@tonic-gate static void	setimer(int);
5800Sstevel@tonic-gate static void	siglvl(int, siginfo_t *, ucontext_t *);
5810Sstevel@tonic-gate static void	sigpoll(int);
5820Sstevel@tonic-gate static void	enter_maintenance(void);
5830Sstevel@tonic-gate static void	timer(int);
5840Sstevel@tonic-gate static void	userinit(int, char **);
5850Sstevel@tonic-gate static void	notify_pam_dead(struct utmpx *);
5860Sstevel@tonic-gate static long	waitproc(struct PROC_TABLE *);
5870Sstevel@tonic-gate static struct PROC_TABLE *efork(int, struct PROC_TABLE *, int);
5880Sstevel@tonic-gate static struct PROC_TABLE *findpslot(struct CMD_LINE *);
5890Sstevel@tonic-gate static void	increase_proc_table_size();
5900Sstevel@tonic-gate static void	st_init();
5910Sstevel@tonic-gate static void	st_write();
5920Sstevel@tonic-gate static void	contracts_init();
5930Sstevel@tonic-gate static void	contract_event(struct pollfd *);
5940Sstevel@tonic-gate static int	startd_run(const char *, int, ctid_t);
5950Sstevel@tonic-gate static void	startd_record_failure();
5960Sstevel@tonic-gate static int	startd_failure_rate_critical();
5970Sstevel@tonic-gate static char	*audit_boot_msg();
5980Sstevel@tonic-gate static int	audit_put_record(int, int, char *);
5990Sstevel@tonic-gate static void	update_boot_archive(int new_state);
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate int
main(int argc,char * argv[])6020Sstevel@tonic-gate main(int argc, char *argv[])
6030Sstevel@tonic-gate {
6040Sstevel@tonic-gate 	int	chg_lvl_flag = FALSE, print_banner = FALSE;
6050Sstevel@tonic-gate 	int	may_need_audit = 1;
6060Sstevel@tonic-gate 	int	c;
6070Sstevel@tonic-gate 	char	*msg;
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	/* Get a timestamp for use as boot time, if needed. */
6100Sstevel@tonic-gate 	(void) time(&init_boot_time);
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	/* Get the default umask */
6130Sstevel@tonic-gate 	cmask = umask(022);
6140Sstevel@tonic-gate 	(void) umask(cmask);
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 	/* Parse the arguments to init. Check for single user */
6170Sstevel@tonic-gate 	opterr = 0;
6180Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "brsm:")) != EOF) {
6190Sstevel@tonic-gate 		switch (c) {
6200Sstevel@tonic-gate 		case 'b':
6210Sstevel@tonic-gate 			rflg = 0;
6220Sstevel@tonic-gate 			bflg = 1;
6230Sstevel@tonic-gate 			if (!sflg)
6240Sstevel@tonic-gate 				sflg++;
6250Sstevel@tonic-gate 			break;
6260Sstevel@tonic-gate 		case 'r':
6270Sstevel@tonic-gate 			bflg = 0;
6280Sstevel@tonic-gate 			rflg++;
6290Sstevel@tonic-gate 			break;
6300Sstevel@tonic-gate 		case 's':
6310Sstevel@tonic-gate 			if (!bflg)
6320Sstevel@tonic-gate 				sflg++;
6330Sstevel@tonic-gate 			break;
6340Sstevel@tonic-gate 		case 'm':
6350Sstevel@tonic-gate 			smf_options = optarg;
6360Sstevel@tonic-gate 			smf_debug = (strstr(smf_options, "debug") != NULL);
6370Sstevel@tonic-gate 			break;
6380Sstevel@tonic-gate 		}
6390Sstevel@tonic-gate 	}
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	/*
6420Sstevel@tonic-gate 	 * Determine if we are the main init, or a user invoked init, whose job
6430Sstevel@tonic-gate 	 * it is to inform init to change levels or perform some other action.
6440Sstevel@tonic-gate 	 */
6450Sstevel@tonic-gate 	if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid,
6460Sstevel@tonic-gate 	    sizeof (init_pid)) != sizeof (init_pid)) {
6470Sstevel@tonic-gate 		(void) fprintf(stderr, "could not get pid for init\n");
6480Sstevel@tonic-gate 		return (1);
6490Sstevel@tonic-gate 	}
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 	/*
6520Sstevel@tonic-gate 	 * If this PID is not the same as the "true" init for the zone, then we
6530Sstevel@tonic-gate 	 * must be in 'user' mode.
6540Sstevel@tonic-gate 	 */
6550Sstevel@tonic-gate 	if (getpid() != init_pid) {
6560Sstevel@tonic-gate 		userinit(argc, argv);
6570Sstevel@tonic-gate 	}
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	if (getzoneid() != GLOBAL_ZONEID) {
6600Sstevel@tonic-gate 		print_banner = TRUE;
6610Sstevel@tonic-gate 	}
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	/*
6640Sstevel@tonic-gate 	 * Initialize state (and set "booting").
6650Sstevel@tonic-gate 	 */
6660Sstevel@tonic-gate 	st_init();
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	if (booting && print_banner) {
6690Sstevel@tonic-gate 		struct utsname un;
6700Sstevel@tonic-gate 		char buf[BUFSIZ], *isa;
6710Sstevel@tonic-gate 		long ret;
6720Sstevel@tonic-gate 		int bits = 32;
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 		/*
6750Sstevel@tonic-gate 		 * We want to print the boot banner as soon as
6760Sstevel@tonic-gate 		 * possible.  In the global zone, the kernel does it,
6770Sstevel@tonic-gate 		 * but we do not have that luxury in non-global zones,
6780Sstevel@tonic-gate 		 * so we will print it here.
6790Sstevel@tonic-gate 		 */
6800Sstevel@tonic-gate 		(void) uname(&un);
6810Sstevel@tonic-gate 		ret = sysinfo(SI_ISALIST, buf, sizeof (buf));
6820Sstevel@tonic-gate 		if (ret != -1L && ret <= sizeof (buf)) {
6830Sstevel@tonic-gate 			for (isa = strtok(buf, " "); isa;
6840Sstevel@tonic-gate 			    isa = strtok(NULL, " ")) {
6850Sstevel@tonic-gate 				if (strcmp(isa, "sparcv9") == 0 ||
6860Sstevel@tonic-gate 				    strcmp(isa, "amd64") == 0) {
6870Sstevel@tonic-gate 					bits = 64;
6880Sstevel@tonic-gate 					break;
6890Sstevel@tonic-gate 				}
6900Sstevel@tonic-gate 			}
6910Sstevel@tonic-gate 		}
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate 		console(B_FALSE,
6940Sstevel@tonic-gate 		    "\n\n%s Release %s Version %s %d-bit\r\n",
6950Sstevel@tonic-gate 		    un.sysname, un.release, un.version, bits);
6960Sstevel@tonic-gate 		console(B_FALSE,
697*12041SJohn.Beck@Sun.COM 		    "Copyright (c) 1983, 2010, Oracle and/or its affiliates."
6980Sstevel@tonic-gate 		    " All rights reserved.\r\n");
6990Sstevel@tonic-gate 	}
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	/*
7020Sstevel@tonic-gate 	 * Get the ioctl settings for /dev/syscon from /etc/ioctl.syscon
7030Sstevel@tonic-gate 	 * so that it can be brought up in the state it was in when the
7040Sstevel@tonic-gate 	 * system went down; or set to defaults if ioctl.syscon isn't
7050Sstevel@tonic-gate 	 * valid.
7060Sstevel@tonic-gate 	 *
7070Sstevel@tonic-gate 	 * This needs to be done even if we're restarting so reset_modes()
7080Sstevel@tonic-gate 	 * will work in case we need to go down to single user mode.
7090Sstevel@tonic-gate 	 */
7100Sstevel@tonic-gate 	write_ioctl = get_ioctl_syscon();
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 	/*
7130Sstevel@tonic-gate 	 * Set up all signals to be caught or ignored as appropriate.
7140Sstevel@tonic-gate 	 */
7150Sstevel@tonic-gate 	init_signals();
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	/* Load glob_envp from ENVFILE. */
7180Sstevel@tonic-gate 	init_env();
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	contracts_init();
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 	if (!booting) {
7230Sstevel@tonic-gate 		/* cur_state should have been read in. */
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate 		op_modes = NORMAL_MODES;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 		/* Rewrite the ioctl file if it was bad. */
7280Sstevel@tonic-gate 		if (write_ioctl)
7290Sstevel@tonic-gate 			write_ioctl_syscon();
7300Sstevel@tonic-gate 	} else {
7310Sstevel@tonic-gate 		/*
7320Sstevel@tonic-gate 		 * It's fine to boot up with state as zero, because
7330Sstevel@tonic-gate 		 * startd will later tell us the real state.
7340Sstevel@tonic-gate 		 */
7350Sstevel@tonic-gate 		cur_state = 0;
7360Sstevel@tonic-gate 		op_modes = BOOT_MODES;
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate 		boot_init();
7390Sstevel@tonic-gate 	}
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 	prev_state = prior_state = cur_state;
7420Sstevel@tonic-gate 
7435017Seschrock 	setup_pipe();
7445017Seschrock 
7450Sstevel@tonic-gate 	/*
7460Sstevel@tonic-gate 	 * Here is the beginning of the main process loop.
7470Sstevel@tonic-gate 	 */
7480Sstevel@tonic-gate 	for (;;) {
7495017Seschrock 		if (lvlq_received) {
7500Sstevel@tonic-gate 			setup_pipe();
7515017Seschrock 			lvlq_received = B_FALSE;
7525017Seschrock 		}
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate 		/*
7550Sstevel@tonic-gate 		 * Clean up any accounting records for dead "godchildren".
7560Sstevel@tonic-gate 		 */
7570Sstevel@tonic-gate 		if (Gchild)
7580Sstevel@tonic-gate 			cleanaux();
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 		/*
7610Sstevel@tonic-gate 		 * If in "normal" mode, check all living processes and initiate
7620Sstevel@tonic-gate 		 * kill sequence on those that should not be there anymore.
7630Sstevel@tonic-gate 		 */
7640Sstevel@tonic-gate 		if (op_modes == NORMAL_MODES && cur_state != LVLa &&
7650Sstevel@tonic-gate 		    cur_state != LVLb && cur_state != LVLc)
7660Sstevel@tonic-gate 			remv();
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate 		/*
7690Sstevel@tonic-gate 		 * If a change in run levels is the reason we awoke, now do
7700Sstevel@tonic-gate 		 * the accounting to report the change in the utmp file.
7710Sstevel@tonic-gate 		 * Also report the change on the system console.
7720Sstevel@tonic-gate 		 */
7730Sstevel@tonic-gate 		if (chg_lvl_flag) {
7740Sstevel@tonic-gate 			chg_lvl_flag = FALSE;
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 			if (state_to_flags(cur_state) & LSEL_RUNLEVEL) {
7770Sstevel@tonic-gate 				char rl = state_to_name(cur_state);
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 				if (rl != -1)
7800Sstevel@tonic-gate 					lscf_set_runlevel(rl);
7810Sstevel@tonic-gate 			}
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 			may_need_audit = 1;
7840Sstevel@tonic-gate 		}
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 		/*
7870Sstevel@tonic-gate 		 * Scan the inittab file and spawn and respawn processes that
7880Sstevel@tonic-gate 		 * should be alive in the current state. If inittab does not
7890Sstevel@tonic-gate 		 * exist default to  single user mode.
7900Sstevel@tonic-gate 		 */
7910Sstevel@tonic-gate 		if (spawn_processes() == FAILURE) {
7920Sstevel@tonic-gate 			prior_state = prev_state;
7930Sstevel@tonic-gate 			cur_state = SINGLE_USER;
7940Sstevel@tonic-gate 		}
7950Sstevel@tonic-gate 
7960Sstevel@tonic-gate 		/* If any respawns occurred, take note. */
7970Sstevel@tonic-gate 		if (rsflag) {
7980Sstevel@tonic-gate 			rsflag = 0;
7990Sstevel@tonic-gate 			spawncnt++;
8000Sstevel@tonic-gate 		}
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 		/*
8030Sstevel@tonic-gate 		 * If a powerfail signal was received during the last
8040Sstevel@tonic-gate 		 * sequence, set mode to powerfail.  When spawn_processes() is
8050Sstevel@tonic-gate 		 * entered the first thing it does is to check "powerhit".  If
8060Sstevel@tonic-gate 		 * it is in PF_MODES then it clears "powerhit" and does
8070Sstevel@tonic-gate 		 * a powerfail sequence.  If it is not in PF_MODES, then it
8080Sstevel@tonic-gate 		 * puts itself in PF_MODES and then clears "powerhit".  Should
8090Sstevel@tonic-gate 		 * "powerhit" get set again while spawn_processes() is working
8100Sstevel@tonic-gate 		 * on a powerfail sequence, the following code  will see that
8110Sstevel@tonic-gate 		 * spawn_processes() tries to execute the powerfail sequence
8120Sstevel@tonic-gate 		 * again.  This guarantees that the powerfail sequence will be
8130Sstevel@tonic-gate 		 * successfully completed before further processing takes
8140Sstevel@tonic-gate 		 * place.
8150Sstevel@tonic-gate 		 */
8160Sstevel@tonic-gate 		if (wakeup.w_flags.w_powerhit) {
8170Sstevel@tonic-gate 			op_modes = PF_MODES;
8180Sstevel@tonic-gate 			/*
8190Sstevel@tonic-gate 			 * Make sure that cur_state != prev_state so that
8200Sstevel@tonic-gate 			 * ONCE and WAIT types work.
8210Sstevel@tonic-gate 			 */
8220Sstevel@tonic-gate 			prev_state = 0;
8230Sstevel@tonic-gate 		} else if (op_modes != NORMAL_MODES) {
8240Sstevel@tonic-gate 			/*
8250Sstevel@tonic-gate 			 * If spawn_processes() was not just called while in
8260Sstevel@tonic-gate 			 * normal mode, we set the mode to normal and it will
8270Sstevel@tonic-gate 			 * be called again to check normal modes.  If we have
8280Sstevel@tonic-gate 			 * just finished a powerfail sequence with prev_state
8290Sstevel@tonic-gate 			 * equal to zero, we set prev_state equal to cur_state
8300Sstevel@tonic-gate 			 * before the next pass through.
8310Sstevel@tonic-gate 			 */
8320Sstevel@tonic-gate 			if (op_modes == PF_MODES)
8330Sstevel@tonic-gate 				prev_state = cur_state;
8340Sstevel@tonic-gate 			op_modes = NORMAL_MODES;
8350Sstevel@tonic-gate 		} else if (cur_state == LVLa || cur_state == LVLb ||
8360Sstevel@tonic-gate 		    cur_state == LVLc) {
8370Sstevel@tonic-gate 			/*
8380Sstevel@tonic-gate 			 * If it was a change of levels that awakened us and the
8390Sstevel@tonic-gate 			 * new level is one of the demand levels then reset
8400Sstevel@tonic-gate 			 * cur_state to the previous state and do another scan
8410Sstevel@tonic-gate 			 * to take care of the usual respawn actions.
8420Sstevel@tonic-gate 			 */
8430Sstevel@tonic-gate 			cur_state = prior_state;
8440Sstevel@tonic-gate 			prior_state = prev_state;
8450Sstevel@tonic-gate 			prev_state = cur_state;
8460Sstevel@tonic-gate 		} else {
8470Sstevel@tonic-gate 			prev_state = cur_state;
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 			if (wakeup.w_mask == 0) {
8500Sstevel@tonic-gate 				int ret;
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 				if (may_need_audit && (cur_state == LVL3)) {
8530Sstevel@tonic-gate 					msg = audit_boot_msg();
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 					may_need_audit = 0;
8560Sstevel@tonic-gate 					(void) audit_put_record(ADT_SUCCESS,
8570Sstevel@tonic-gate 					    ADT_SUCCESS, msg);
8580Sstevel@tonic-gate 					free(msg);
8590Sstevel@tonic-gate 				}
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate 				/*
8620Sstevel@tonic-gate 				 * "init" is finished with all actions for
8630Sstevel@tonic-gate 				 * the current wakeup.
8640Sstevel@tonic-gate 				 */
8650Sstevel@tonic-gate 				ret = poll(poll_fds, poll_nfds,
8660Sstevel@tonic-gate 				    SLEEPTIME * MILLISEC);
8670Sstevel@tonic-gate 				pausecnt++;
8680Sstevel@tonic-gate 				if (ret > 0)
8690Sstevel@tonic-gate 					contract_event(&poll_fds[0]);
8700Sstevel@tonic-gate 				else if (ret < 0 && errno != EINTR)
8710Sstevel@tonic-gate 					console(B_TRUE, "poll() error: %s\n",
8720Sstevel@tonic-gate 					    strerror(errno));
8730Sstevel@tonic-gate 			}
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 			if (wakeup.w_flags.w_usersignal) {
8760Sstevel@tonic-gate 				/*
8770Sstevel@tonic-gate 				 * Install the new level.  This could be a real
8780Sstevel@tonic-gate 				 * change in levels  or a telinit [Q|a|b|c] or
8790Sstevel@tonic-gate 				 * just a telinit to the same level at which
8800Sstevel@tonic-gate 				 * we are running.
8810Sstevel@tonic-gate 				 */
8820Sstevel@tonic-gate 				if (new_state != cur_state) {
8830Sstevel@tonic-gate 					if (new_state == LVLa ||
8840Sstevel@tonic-gate 					    new_state == LVLb ||
8850Sstevel@tonic-gate 					    new_state == LVLc) {
8860Sstevel@tonic-gate 						prev_state = prior_state;
8870Sstevel@tonic-gate 						prior_state = cur_state;
8880Sstevel@tonic-gate 						cur_state = new_state;
8890Sstevel@tonic-gate 					} else {
8900Sstevel@tonic-gate 						prev_state = cur_state;
8910Sstevel@tonic-gate 						if (cur_state >= 0)
8920Sstevel@tonic-gate 							prior_state = cur_state;
8930Sstevel@tonic-gate 						cur_state = new_state;
8940Sstevel@tonic-gate 						chg_lvl_flag = TRUE;
8950Sstevel@tonic-gate 					}
8960Sstevel@tonic-gate 				}
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 				new_state = 0;
8990Sstevel@tonic-gate 			}
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate 			if (wakeup.w_flags.w_powerhit)
9020Sstevel@tonic-gate 				op_modes = PF_MODES;
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate 			/*
9050Sstevel@tonic-gate 			 * Clear all wakeup reasons.
9060Sstevel@tonic-gate 			 */
9070Sstevel@tonic-gate 			wakeup.w_mask = 0;
9080Sstevel@tonic-gate 		}
9090Sstevel@tonic-gate 	}
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 	/*NOTREACHED*/
9120Sstevel@tonic-gate }
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate static void
update_boot_archive(int new_state)9150Sstevel@tonic-gate update_boot_archive(int new_state)
9160Sstevel@tonic-gate {
9170Sstevel@tonic-gate 	if (new_state != LVL0 && new_state != LVL5 && new_state != LVL6)
9180Sstevel@tonic-gate 		return;
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 	if (getzoneid() != GLOBAL_ZONEID)
9210Sstevel@tonic-gate 		return;
9220Sstevel@tonic-gate 
9238735SEnrico.Perla@Sun.COM 	(void) system("/sbin/bootadm -ea update_all");
9240Sstevel@tonic-gate }
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate /*
9270Sstevel@tonic-gate  * void enter_maintenance()
9280Sstevel@tonic-gate  *   A simple invocation of sulogin(1M), with no baggage, in the case that we
9290Sstevel@tonic-gate  *   are unable to activate svc.startd(1M).  We fork; the child runs sulogin;
9300Sstevel@tonic-gate  *   we wait for it to exit.
9310Sstevel@tonic-gate  */
9320Sstevel@tonic-gate static void
enter_maintenance()9330Sstevel@tonic-gate enter_maintenance()
9340Sstevel@tonic-gate {
9350Sstevel@tonic-gate 	struct PROC_TABLE	*su_process;
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 	console(B_FALSE, "Requesting maintenance mode\n"
9380Sstevel@tonic-gate 	    "(See /lib/svc/share/README for additional information.)\n");
93910083SRoger.Faulkner@Sun.COM 	(void) sighold(SIGCLD);
9400Sstevel@tonic-gate 	while ((su_process = efork(M_OFF, NULLPROC, NOCLEANUP)) == NO_ROOM)
9410Sstevel@tonic-gate 		(void) pause();
94210083SRoger.Faulkner@Sun.COM 	(void) sigrelse(SIGCLD);
9430Sstevel@tonic-gate 	if (su_process == NULLPROC) {
9440Sstevel@tonic-gate 		int fd;
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate 		(void) fclose(stdin);
9470Sstevel@tonic-gate 		(void) fclose(stdout);
9480Sstevel@tonic-gate 		(void) fclose(stderr);
9490Sstevel@tonic-gate 		closefrom(0);
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate 		fd = open(SYSCON, O_RDWR | O_NOCTTY);
9520Sstevel@tonic-gate 		if (fd >= 0) {
9530Sstevel@tonic-gate 			(void) dup2(fd, 1);
9540Sstevel@tonic-gate 			(void) dup2(fd, 2);
9550Sstevel@tonic-gate 		} else {
9560Sstevel@tonic-gate 			/*
9570Sstevel@tonic-gate 			 * Need to issue an error message somewhere.
9580Sstevel@tonic-gate 			 */
9590Sstevel@tonic-gate 			syslog(LOG_CRIT, "init[%d]: cannot open %s; %s\n",
9600Sstevel@tonic-gate 			    getpid(), SYSCON, strerror(errno));
9610Sstevel@tonic-gate 		}
9620Sstevel@tonic-gate 
9630Sstevel@tonic-gate 		/*
9640Sstevel@tonic-gate 		 * Execute the "su" program.
9650Sstevel@tonic-gate 		 */
9660Sstevel@tonic-gate 		(void) execle(SU, SU, "-", (char *)0, glob_envp);
9670Sstevel@tonic-gate 		console(B_TRUE, "execle of %s failed: %s\n", SU,
9680Sstevel@tonic-gate 		    strerror(errno));
9690Sstevel@tonic-gate 		timer(5);
9700Sstevel@tonic-gate 		exit(1);
9710Sstevel@tonic-gate 	}
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 	/*
9740Sstevel@tonic-gate 	 * If we are the parent, wait around for the child to die
9750Sstevel@tonic-gate 	 * or for "init" to be signaled to change levels.
9760Sstevel@tonic-gate 	 */
9770Sstevel@tonic-gate 	while (waitproc(su_process) == FAILURE) {
9780Sstevel@tonic-gate 		/*
9790Sstevel@tonic-gate 		 * All other reasons for waking are ignored when in
9800Sstevel@tonic-gate 		 * single-user mode.  The only child we are interested
9810Sstevel@tonic-gate 		 * in is being waited for explicitly by waitproc().
9820Sstevel@tonic-gate 		 */
9830Sstevel@tonic-gate 		wakeup.w_mask = 0;
9840Sstevel@tonic-gate 	}
9850Sstevel@tonic-gate }
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate /*
9880Sstevel@tonic-gate  * remv() scans through "proc_table" and performs cleanup.  If
9890Sstevel@tonic-gate  * there is a process in the table, which shouldn't be here at
9900Sstevel@tonic-gate  * the current run level, then remv() kills the process.
9910Sstevel@tonic-gate  */
9920Sstevel@tonic-gate static void
remv()9930Sstevel@tonic-gate remv()
9940Sstevel@tonic-gate {
9950Sstevel@tonic-gate 	struct PROC_TABLE	*process;
9960Sstevel@tonic-gate 	struct CMD_LINE		cmd;
9970Sstevel@tonic-gate 	char			cmd_string[MAXCMDL];
9980Sstevel@tonic-gate 	int			change_level;
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate 	change_level = (cur_state != prev_state ? TRUE : FALSE);
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	/*
10030Sstevel@tonic-gate 	 * Clear the TOUCHED flag on all entries so that when we have
10040Sstevel@tonic-gate 	 * finished scanning inittab, we will be able to tell if we
10050Sstevel@tonic-gate 	 * have any processes for which there is no entry in inittab.
10060Sstevel@tonic-gate 	 */
10070Sstevel@tonic-gate 	for (process = proc_table;
10080Sstevel@tonic-gate 	    (process < proc_table + num_proc); process++) {
10090Sstevel@tonic-gate 		process->p_flags &= ~TOUCHED;
10100Sstevel@tonic-gate 	}
10110Sstevel@tonic-gate 
10120Sstevel@tonic-gate 	/*
10130Sstevel@tonic-gate 	 * Scan all inittab entries.
10140Sstevel@tonic-gate 	 */
10150Sstevel@tonic-gate 	while (getcmd(&cmd, &cmd_string[0]) == TRUE) {
10160Sstevel@tonic-gate 		/* Scan for process which goes with this entry in inittab. */
10170Sstevel@tonic-gate 		for (process = proc_table;
10180Sstevel@tonic-gate 		    (process < proc_table + num_proc); process++) {
10190Sstevel@tonic-gate 			if ((process->p_flags & OCCUPIED) == 0 ||
10200Sstevel@tonic-gate 			    !id_eq(process->p_id, cmd.c_id))
10210Sstevel@tonic-gate 				continue;
10220Sstevel@tonic-gate 
10230Sstevel@tonic-gate 			/*
10240Sstevel@tonic-gate 			 * This slot contains the process we are looking for.
10250Sstevel@tonic-gate 			 */
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 			/*
10280Sstevel@tonic-gate 			 * Is the cur_state SINGLE_USER or is this process
10290Sstevel@tonic-gate 			 * marked as "off" or was this proc started by some
10300Sstevel@tonic-gate 			 * mechanism other than LVL{a|b|c} and the current level
10310Sstevel@tonic-gate 			 * does not support this process?
10320Sstevel@tonic-gate 			 */
10330Sstevel@tonic-gate 			if (cur_state == SINGLE_USER ||
10340Sstevel@tonic-gate 			    cmd.c_action == M_OFF ||
10350Sstevel@tonic-gate 			    ((cmd.c_levels & state_to_mask(cur_state)) == 0 &&
10360Sstevel@tonic-gate 			    (process->p_flags & DEMANDREQUEST) == 0)) {
10370Sstevel@tonic-gate 				if (process->p_flags & LIVING) {
10380Sstevel@tonic-gate 					/*
10390Sstevel@tonic-gate 					 * Touch this entry so we know we have
10400Sstevel@tonic-gate 					 * treated it.  Note that procs which
10410Sstevel@tonic-gate 					 * are already dead at this point and
10420Sstevel@tonic-gate 					 * should not be restarted are left
10430Sstevel@tonic-gate 					 * untouched.  This causes their slot to
10440Sstevel@tonic-gate 					 * be freed later after dead accounting
10450Sstevel@tonic-gate 					 * is done.
10460Sstevel@tonic-gate 					 */
10470Sstevel@tonic-gate 					process->p_flags |= TOUCHED;
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 					if ((process->p_flags & KILLED) == 0) {
10500Sstevel@tonic-gate 						if (change_level) {
10510Sstevel@tonic-gate 							process->p_flags
10520Sstevel@tonic-gate 							    |= WARNED;
10530Sstevel@tonic-gate 							(void) kill(
10540Sstevel@tonic-gate 							    process->p_pid,
10550Sstevel@tonic-gate 							    SIGTERM);
10560Sstevel@tonic-gate 						} else {
10570Sstevel@tonic-gate 							/*
10580Sstevel@tonic-gate 							 * Fork a killing proc
10590Sstevel@tonic-gate 							 * so "init" can
10600Sstevel@tonic-gate 							 * continue without
10610Sstevel@tonic-gate 							 * having to pause for
10620Sstevel@tonic-gate 							 * TWARN seconds.
10630Sstevel@tonic-gate 							 */
10640Sstevel@tonic-gate 							killproc(
10650Sstevel@tonic-gate 							    process->p_pid);
10660Sstevel@tonic-gate 						}
10670Sstevel@tonic-gate 						process->p_flags |= KILLED;
10680Sstevel@tonic-gate 					}
10690Sstevel@tonic-gate 				}
10700Sstevel@tonic-gate 			} else {
10710Sstevel@tonic-gate 				/*
10720Sstevel@tonic-gate 				 * Process can exist at current level.  If it is
10730Sstevel@tonic-gate 				 * still alive or a DEMANDREQUEST we touch it so
10740Sstevel@tonic-gate 				 * it will be left alone.  Otherwise we leave it
10750Sstevel@tonic-gate 				 * untouched so it will be accounted for and
10760Sstevel@tonic-gate 				 * cleaned up later in remv().  Dead
10770Sstevel@tonic-gate 				 * DEMANDREQUESTs will be accounted but not
10780Sstevel@tonic-gate 				 * freed.
10790Sstevel@tonic-gate 				 */
10800Sstevel@tonic-gate 				if (process->p_flags &
10810Sstevel@tonic-gate 				    (LIVING|NOCLEANUP|DEMANDREQUEST))
10820Sstevel@tonic-gate 					process->p_flags |= TOUCHED;
10830Sstevel@tonic-gate 			}
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 			break;
10860Sstevel@tonic-gate 		}
10870Sstevel@tonic-gate 	}
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate 	st_write();
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	/*
10920Sstevel@tonic-gate 	 * If this was a change of levels call, scan through the
10930Sstevel@tonic-gate 	 * process table for processes that were warned to die.  If any
10940Sstevel@tonic-gate 	 * are found that haven't left yet, sleep for TWARN seconds and
10950Sstevel@tonic-gate 	 * then send final terminations to any that haven't died yet.
10960Sstevel@tonic-gate 	 */
10970Sstevel@tonic-gate 	if (change_level) {
10980Sstevel@tonic-gate 
10990Sstevel@tonic-gate 		/*
11000Sstevel@tonic-gate 		 * Set the alarm for TWARN seconds on the assumption
11010Sstevel@tonic-gate 		 * that there will be some that need to be waited for.
11020Sstevel@tonic-gate 		 * This won't harm anything except we are guaranteed to
11030Sstevel@tonic-gate 		 * wakeup in TWARN seconds whether we need to or not.
11040Sstevel@tonic-gate 		 */
11050Sstevel@tonic-gate 		setimer(TWARN);
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate 		/*
11080Sstevel@tonic-gate 		 * Scan for processes which should be dying.  We hope they
11090Sstevel@tonic-gate 		 * will die without having to be sent a SIGKILL signal.
11100Sstevel@tonic-gate 		 */
11110Sstevel@tonic-gate 		for (process = proc_table;
11120Sstevel@tonic-gate 		    (process < proc_table + num_proc); process++) {
11130Sstevel@tonic-gate 			/*
11140Sstevel@tonic-gate 			 * If this process should die, hasn't yet, and the
11150Sstevel@tonic-gate 			 * TWARN time hasn't expired yet, wait for process
11160Sstevel@tonic-gate 			 * to die or for timer to expire.
11170Sstevel@tonic-gate 			 */
11180Sstevel@tonic-gate 			while (time_up == FALSE &&
11190Sstevel@tonic-gate 			    (process->p_flags & (WARNED|LIVING|OCCUPIED)) ==
11200Sstevel@tonic-gate 			    (WARNED|LIVING|OCCUPIED))
11210Sstevel@tonic-gate 				(void) pause();
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate 			if (time_up == TRUE)
11240Sstevel@tonic-gate 				break;
11250Sstevel@tonic-gate 		}
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate 		/*
11280Sstevel@tonic-gate 		 * If we reached the end of the table without the timer
11290Sstevel@tonic-gate 		 * expiring, then there are no procs which will have to be
11300Sstevel@tonic-gate 		 * sent the SIGKILL signal.  If the timer has expired, then
11310Sstevel@tonic-gate 		 * it is necessary to scan the table again and send signals
11320Sstevel@tonic-gate 		 * to all processes which aren't going away nicely.
11330Sstevel@tonic-gate 		 */
11340Sstevel@tonic-gate 		if (time_up == TRUE) {
11350Sstevel@tonic-gate 			for (process = proc_table;
11360Sstevel@tonic-gate 			    (process < proc_table + num_proc); process++) {
11370Sstevel@tonic-gate 				if ((process->p_flags &
11380Sstevel@tonic-gate 				    (WARNED|LIVING|OCCUPIED)) ==
11390Sstevel@tonic-gate 				    (WARNED|LIVING|OCCUPIED))
11400Sstevel@tonic-gate 					(void) kill(process->p_pid, SIGKILL);
11410Sstevel@tonic-gate 			}
11420Sstevel@tonic-gate 		}
11430Sstevel@tonic-gate 		setimer(0);
11440Sstevel@tonic-gate 	}
11450Sstevel@tonic-gate 
11460Sstevel@tonic-gate 	/*
11470Sstevel@tonic-gate 	 * Rescan the proc_table for two kinds of entry, those marked LIVING,
11480Sstevel@tonic-gate 	 * NAMED, which don't have an entry in inittab (haven't been TOUCHED
11490Sstevel@tonic-gate 	 * by the above scanning), and haven't been sent kill signals, and
11500Sstevel@tonic-gate 	 * those entries marked not LIVING, NAMED.  The former procs are killed.
11510Sstevel@tonic-gate 	 * The latter have DEAD_PROCESS accounting done and the slot cleared.
11520Sstevel@tonic-gate 	 */
11530Sstevel@tonic-gate 	for (process = proc_table;
11540Sstevel@tonic-gate 	    (process < proc_table + num_proc); process++) {
11550Sstevel@tonic-gate 		if ((process->p_flags & (LIVING|NAMED|TOUCHED|KILLED|OCCUPIED))
11560Sstevel@tonic-gate 		    == (LIVING|NAMED|OCCUPIED)) {
11570Sstevel@tonic-gate 			killproc(process->p_pid);
11580Sstevel@tonic-gate 			process->p_flags |= KILLED;
11590Sstevel@tonic-gate 		} else if ((process->p_flags & (LIVING|NAMED|OCCUPIED)) ==
11600Sstevel@tonic-gate 		    (NAMED|OCCUPIED)) {
11610Sstevel@tonic-gate 			(void) account(DEAD_PROCESS, process, NULL);
11620Sstevel@tonic-gate 			/*
11630Sstevel@tonic-gate 			 * If this named proc hasn't been TOUCHED, then free the
11640Sstevel@tonic-gate 			 * space. It has either died of it's own accord, but
11650Sstevel@tonic-gate 			 * isn't respawnable or it was killed because it
11660Sstevel@tonic-gate 			 * shouldn't exist at this level.
11670Sstevel@tonic-gate 			 */
11680Sstevel@tonic-gate 			if ((process->p_flags & TOUCHED) == 0)
11690Sstevel@tonic-gate 				process->p_flags = 0;
11700Sstevel@tonic-gate 		}
11710Sstevel@tonic-gate 	}
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 	st_write();
11740Sstevel@tonic-gate }
11750Sstevel@tonic-gate 
11760Sstevel@tonic-gate /*
11770Sstevel@tonic-gate  * Extract the svc.startd command line and whether to restart it from its
11780Sstevel@tonic-gate  * inittab entry.
11790Sstevel@tonic-gate  */
11800Sstevel@tonic-gate /*ARGSUSED*/
11810Sstevel@tonic-gate static void
process_startd_line(struct CMD_LINE * cmd,char * cmd_string)11820Sstevel@tonic-gate process_startd_line(struct CMD_LINE *cmd, char *cmd_string)
11830Sstevel@tonic-gate {
11840Sstevel@tonic-gate 	size_t sz;
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate 	/* Save the command line. */
11870Sstevel@tonic-gate 	if (sflg || rflg) {
11880Sstevel@tonic-gate 		/* Also append -r or -s. */
11890Sstevel@tonic-gate 		(void) strlcpy(startd_cline, cmd_string, sizeof (startd_cline));
11900Sstevel@tonic-gate 		(void) strlcat(startd_cline, " -", sizeof (startd_cline));
11910Sstevel@tonic-gate 		if (sflg)
11920Sstevel@tonic-gate 			sz = strlcat(startd_cline, "s", sizeof (startd_cline));
11930Sstevel@tonic-gate 		if (rflg)
11940Sstevel@tonic-gate 			sz = strlcat(startd_cline, "r", sizeof (startd_cline));
11950Sstevel@tonic-gate 	} else {
11960Sstevel@tonic-gate 		sz = strlcpy(startd_cline, cmd_string, sizeof (startd_cline));
11970Sstevel@tonic-gate 	}
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate 	if (sz >= sizeof (startd_cline)) {
12000Sstevel@tonic-gate 		console(B_TRUE,
12010Sstevel@tonic-gate 		    "svc.startd command line too long.  Ignoring.\n");
12020Sstevel@tonic-gate 		startd_cline[0] = '\0';
12030Sstevel@tonic-gate 		return;
12040Sstevel@tonic-gate 	}
12050Sstevel@tonic-gate }
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate /*
12080Sstevel@tonic-gate  * spawn_processes() scans inittab for entries which should be run at this
12090Sstevel@tonic-gate  * mode.  Processes which should be running but are not, are started.
12100Sstevel@tonic-gate  */
12110Sstevel@tonic-gate static int
spawn_processes()12120Sstevel@tonic-gate spawn_processes()
12130Sstevel@tonic-gate {
12140Sstevel@tonic-gate 	struct PROC_TABLE		*pp;
12150Sstevel@tonic-gate 	struct CMD_LINE			cmd;
12160Sstevel@tonic-gate 	char				cmd_string[MAXCMDL];
12170Sstevel@tonic-gate 	short				lvl_mask;
12180Sstevel@tonic-gate 	int				status;
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 	/*
12210Sstevel@tonic-gate 	 * First check the "powerhit" flag.  If it is set, make sure the modes
12220Sstevel@tonic-gate 	 * are PF_MODES and clear the "powerhit" flag.  Avoid the possible race
12230Sstevel@tonic-gate 	 * on the "powerhit" flag by disallowing a new powerfail interrupt
12240Sstevel@tonic-gate 	 * between the test of the powerhit flag and the clearing of it.
12250Sstevel@tonic-gate 	 */
12260Sstevel@tonic-gate 	if (wakeup.w_flags.w_powerhit) {
12270Sstevel@tonic-gate 		wakeup.w_flags.w_powerhit = 0;
12280Sstevel@tonic-gate 		op_modes = PF_MODES;
12290Sstevel@tonic-gate 	}
12300Sstevel@tonic-gate 	lvl_mask = state_to_mask(cur_state);
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 	/*
12330Sstevel@tonic-gate 	 * Scan through all the entries in inittab.
12340Sstevel@tonic-gate 	 */
12350Sstevel@tonic-gate 	while ((status = getcmd(&cmd, &cmd_string[0])) == TRUE) {
12360Sstevel@tonic-gate 		if (id_eq(cmd.c_id, "smf")) {
12370Sstevel@tonic-gate 			process_startd_line(&cmd, cmd_string);
12380Sstevel@tonic-gate 			continue;
12390Sstevel@tonic-gate 		}
12400Sstevel@tonic-gate 
12410Sstevel@tonic-gate retry_for_proc_slot:
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 		/*
12440Sstevel@tonic-gate 		 * Find out if there is a process slot for this entry already.
12450Sstevel@tonic-gate 		 */
12460Sstevel@tonic-gate 		if ((pp = findpslot(&cmd)) == NULLPROC) {
12470Sstevel@tonic-gate 			/*
12480Sstevel@tonic-gate 			 * we've run out of proc table entries
12490Sstevel@tonic-gate 			 * increase proc_table.
12500Sstevel@tonic-gate 			 */
12510Sstevel@tonic-gate 			increase_proc_table_size();
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 			/*
12540Sstevel@tonic-gate 			 * Retry now as we have an empty proc slot.
12550Sstevel@tonic-gate 			 * In case increase_proc_table_size() fails,
12560Sstevel@tonic-gate 			 * we will keep retrying.
12570Sstevel@tonic-gate 			 */
12580Sstevel@tonic-gate 			goto retry_for_proc_slot;
12590Sstevel@tonic-gate 		}
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 		/*
12620Sstevel@tonic-gate 		 * If there is an entry, and it is marked as DEMANDREQUEST,
12630Sstevel@tonic-gate 		 * one of the levels a, b, or c is in its levels mask, and
12640Sstevel@tonic-gate 		 * the action field is ONDEMAND and ONDEMAND is a permissable
12650Sstevel@tonic-gate 		 * mode, and the process is dead, then respawn it.
12660Sstevel@tonic-gate 		 */
12670Sstevel@tonic-gate 		if (((pp->p_flags & (LIVING|DEMANDREQUEST)) == DEMANDREQUEST) &&
12680Sstevel@tonic-gate 		    (cmd.c_levels & MASK_abc) &&
12690Sstevel@tonic-gate 		    (cmd.c_action & op_modes) == M_ONDEMAND) {
12700Sstevel@tonic-gate 			spawn(pp, &cmd);
12710Sstevel@tonic-gate 			continue;
12720Sstevel@tonic-gate 		}
12730Sstevel@tonic-gate 
12740Sstevel@tonic-gate 		/*
12750Sstevel@tonic-gate 		 * If the action is not an action we are interested in,
12760Sstevel@tonic-gate 		 * skip the entry.
12770Sstevel@tonic-gate 		 */
12780Sstevel@tonic-gate 		if ((cmd.c_action & op_modes) == 0 || pp->p_flags & LIVING ||
12790Sstevel@tonic-gate 		    (cmd.c_levels & lvl_mask) == 0)
12800Sstevel@tonic-gate 			continue;
12810Sstevel@tonic-gate 
12820Sstevel@tonic-gate 		/*
12830Sstevel@tonic-gate 		 * If the modes are the normal modes (ONCE, WAIT, RESPAWN, OFF,
12840Sstevel@tonic-gate 		 * ONDEMAND) and the action field is either OFF or the action
12850Sstevel@tonic-gate 		 * field is ONCE or WAIT and the current level is the same as
12860Sstevel@tonic-gate 		 * the last level, then skip this entry.  ONCE and WAIT only
12870Sstevel@tonic-gate 		 * get run when the level changes.
12880Sstevel@tonic-gate 		 */
12890Sstevel@tonic-gate 		if (op_modes == NORMAL_MODES &&
12900Sstevel@tonic-gate 		    (cmd.c_action == M_OFF ||
12918735SEnrico.Perla@Sun.COM 		    (cmd.c_action & (M_ONCE|M_WAIT)) &&
12928735SEnrico.Perla@Sun.COM 		    cur_state == prev_state))
12930Sstevel@tonic-gate 			continue;
12940Sstevel@tonic-gate 
12950Sstevel@tonic-gate 		/*
12960Sstevel@tonic-gate 		 * At this point we are interested in performing the action for
12970Sstevel@tonic-gate 		 * this entry.  Actions fall into two categories, spinning off
12980Sstevel@tonic-gate 		 * a process and not waiting, and spinning off a process and
12990Sstevel@tonic-gate 		 * waiting for it to die.  If the action is ONCE, RESPAWN,
13000Sstevel@tonic-gate 		 * ONDEMAND, POWERFAIL, or BOOT we don't wait for the process
13010Sstevel@tonic-gate 		 * to die, for all other actions we do wait.
13020Sstevel@tonic-gate 		 */
13030Sstevel@tonic-gate 		if (cmd.c_action & (M_ONCE | M_RESPAWN | M_PF | M_BOOT)) {
13040Sstevel@tonic-gate 			spawn(pp, &cmd);
13050Sstevel@tonic-gate 
13060Sstevel@tonic-gate 		} else {
13070Sstevel@tonic-gate 			spawn(pp, &cmd);
13088735SEnrico.Perla@Sun.COM 			while (waitproc(pp) == FAILURE)
13098735SEnrico.Perla@Sun.COM 				;
13100Sstevel@tonic-gate 			(void) account(DEAD_PROCESS, pp, NULL);
13110Sstevel@tonic-gate 			pp->p_flags = 0;
13120Sstevel@tonic-gate 		}
13130Sstevel@tonic-gate 	}
13140Sstevel@tonic-gate 	return (status);
13150Sstevel@tonic-gate }
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate /*
13180Sstevel@tonic-gate  * spawn() spawns a shell, inserts the information about the process
13190Sstevel@tonic-gate  * process into the proc_table, and does the startup accounting.
13200Sstevel@tonic-gate  */
13210Sstevel@tonic-gate static void
spawn(struct PROC_TABLE * process,struct CMD_LINE * cmd)13220Sstevel@tonic-gate spawn(struct PROC_TABLE *process, struct CMD_LINE *cmd)
13230Sstevel@tonic-gate {
13240Sstevel@tonic-gate 	int		i;
13250Sstevel@tonic-gate 	int		modes, maxfiles;
13260Sstevel@tonic-gate 	time_t		now;
13270Sstevel@tonic-gate 	struct PROC_TABLE tmproc, *oprocess;
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 	/*
13300Sstevel@tonic-gate 	 * The modes to be sent to efork() are 0 unless we are
13310Sstevel@tonic-gate 	 * spawning a LVLa, LVLb, or LVLc entry or we will be
13320Sstevel@tonic-gate 	 * waiting for the death of the child before continuing.
13330Sstevel@tonic-gate 	 */
13340Sstevel@tonic-gate 	modes = NAMED;
13350Sstevel@tonic-gate 	if (process->p_flags & DEMANDREQUEST || cur_state == LVLa ||
13360Sstevel@tonic-gate 	    cur_state == LVLb || cur_state == LVLc)
13370Sstevel@tonic-gate 		modes |= DEMANDREQUEST;
13380Sstevel@tonic-gate 	if ((cmd->c_action & (M_SYSINIT | M_WAIT | M_BOOTWAIT | M_PWAIT)) != 0)
13390Sstevel@tonic-gate 		modes |= NOCLEANUP;
13400Sstevel@tonic-gate 
13410Sstevel@tonic-gate 	/*
13420Sstevel@tonic-gate 	 * If this is a respawnable process, check the threshold
13430Sstevel@tonic-gate 	 * information to avoid excessive respawns.
13440Sstevel@tonic-gate 	 */
13450Sstevel@tonic-gate 	if (cmd->c_action & M_RESPAWN) {
13460Sstevel@tonic-gate 		/*
13470Sstevel@tonic-gate 		 * Add NOCLEANUP to all respawnable commands so that the
13480Sstevel@tonic-gate 		 * information about the frequency of respawns isn't lost.
13490Sstevel@tonic-gate 		 */
13500Sstevel@tonic-gate 		modes |= NOCLEANUP;
13510Sstevel@tonic-gate 		(void) time(&now);
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 		/*
13540Sstevel@tonic-gate 		 * If no time is assigned, then this is the first time
13550Sstevel@tonic-gate 		 * this command is being processed in this series.  Assign
13560Sstevel@tonic-gate 		 * the current time.
13570Sstevel@tonic-gate 		 */
13580Sstevel@tonic-gate 		if (process->p_time == 0L)
13590Sstevel@tonic-gate 			process->p_time = now;
13600Sstevel@tonic-gate 
13610Sstevel@tonic-gate 		if (process->p_count++ == SPAWN_LIMIT) {
13620Sstevel@tonic-gate 
13630Sstevel@tonic-gate 			if ((now - process->p_time) < SPAWN_INTERVAL) {
13640Sstevel@tonic-gate 				/*
13650Sstevel@tonic-gate 				 * Process is respawning too rapidly.  Print
13660Sstevel@tonic-gate 				 * message and refuse to respawn it for now.
13670Sstevel@tonic-gate 				 */
13680Sstevel@tonic-gate 				console(B_TRUE, "Command is respawning too "
13690Sstevel@tonic-gate 				    "rapidly. Check for possible errors.\n"
13700Sstevel@tonic-gate 				    "id:%4s \"%s\"\n",
13710Sstevel@tonic-gate 				    &cmd->c_id[0], &cmd->c_command[EXEC]);
13720Sstevel@tonic-gate 				return;
13730Sstevel@tonic-gate 			}
13740Sstevel@tonic-gate 			process->p_time = now;
13750Sstevel@tonic-gate 			process->p_count = 0;
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate 		} else if (process->p_count > SPAWN_LIMIT) {
13780Sstevel@tonic-gate 			/*
13790Sstevel@tonic-gate 			 * If process has been respawning too rapidly and
13800Sstevel@tonic-gate 			 * the inhibit time limit hasn't expired yet, we
13810Sstevel@tonic-gate 			 * refuse to respawn.
13820Sstevel@tonic-gate 			 */
13830Sstevel@tonic-gate 			if (now - process->p_time < SPAWN_INTERVAL + INHIBIT)
13840Sstevel@tonic-gate 				return;
13850Sstevel@tonic-gate 			process->p_time = now;
13860Sstevel@tonic-gate 			process->p_count = 0;
13870Sstevel@tonic-gate 		}
13880Sstevel@tonic-gate 		rsflag = TRUE;
13890Sstevel@tonic-gate 	}
13900Sstevel@tonic-gate 
13910Sstevel@tonic-gate 	/*
13920Sstevel@tonic-gate 	 * Spawn a child process to execute this command.
13930Sstevel@tonic-gate 	 */
139410083SRoger.Faulkner@Sun.COM 	(void) sighold(SIGCLD);
13950Sstevel@tonic-gate 	oprocess = process;
13960Sstevel@tonic-gate 	while ((process = efork(cmd->c_action, oprocess, modes)) == NO_ROOM)
13970Sstevel@tonic-gate 		(void) pause();
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 	if (process == NULLPROC) {
14000Sstevel@tonic-gate 
14010Sstevel@tonic-gate 		/*
14020Sstevel@tonic-gate 		 * We are the child.  We must make sure we get a different
14030Sstevel@tonic-gate 		 * file pointer for our references to utmpx.  Otherwise our
14040Sstevel@tonic-gate 		 * seeks and reads will compete with those of the parent.
14050Sstevel@tonic-gate 		 */
14060Sstevel@tonic-gate 		endutxent();
14070Sstevel@tonic-gate 
14080Sstevel@tonic-gate 		/*
14090Sstevel@tonic-gate 		 * Perform the accounting for the beginning of a process.
14100Sstevel@tonic-gate 		 * Note that all processes are initially "INIT_PROCESS"es.
14110Sstevel@tonic-gate 		 */
14120Sstevel@tonic-gate 		tmproc.p_id[0] = cmd->c_id[0];
14130Sstevel@tonic-gate 		tmproc.p_id[1] = cmd->c_id[1];
14140Sstevel@tonic-gate 		tmproc.p_id[2] = cmd->c_id[2];
14150Sstevel@tonic-gate 		tmproc.p_id[3] = cmd->c_id[3];
14160Sstevel@tonic-gate 		tmproc.p_pid = getpid();
14170Sstevel@tonic-gate 		tmproc.p_exit = 0;
14180Sstevel@tonic-gate 		(void) account(INIT_PROCESS, &tmproc,
14190Sstevel@tonic-gate 		    prog_name(&cmd->c_command[EXEC]));
14200Sstevel@tonic-gate 		maxfiles = ulimit(UL_GDESLIM, 0);
14210Sstevel@tonic-gate 		for (i = 0; i < maxfiles; i++)
14220Sstevel@tonic-gate 			(void) fcntl(i, F_SETFD, FD_CLOEXEC);
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 		/*
14250Sstevel@tonic-gate 		 * Now exec a shell with the -c option and the command
14260Sstevel@tonic-gate 		 * from inittab.
14270Sstevel@tonic-gate 		 */
14280Sstevel@tonic-gate 		(void) execle(SH, "INITSH", "-c", cmd->c_command, (char *)0,
14290Sstevel@tonic-gate 		    glob_envp);
14300Sstevel@tonic-gate 		console(B_TRUE, "Command\n\"%s\"\n failed to execute.  errno "
14310Sstevel@tonic-gate 		    "= %d (exec of shell failed)\n", cmd->c_command, errno);
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 		/*
14340Sstevel@tonic-gate 		 * Don't come back so quickly that "init" doesn't have a
14350Sstevel@tonic-gate 		 * chance to finish putting this child in "proc_table".
14360Sstevel@tonic-gate 		 */
14370Sstevel@tonic-gate 		timer(20);
14380Sstevel@tonic-gate 		exit(1);
14390Sstevel@tonic-gate 
14400Sstevel@tonic-gate 	}
14410Sstevel@tonic-gate 
14420Sstevel@tonic-gate 	/*
14430Sstevel@tonic-gate 	 * We are the parent.  Insert the necessary
14440Sstevel@tonic-gate 	 * information in the proc_table.
14450Sstevel@tonic-gate 	 */
14460Sstevel@tonic-gate 	process->p_id[0] = cmd->c_id[0];
14470Sstevel@tonic-gate 	process->p_id[1] = cmd->c_id[1];
14480Sstevel@tonic-gate 	process->p_id[2] = cmd->c_id[2];
14490Sstevel@tonic-gate 	process->p_id[3] = cmd->c_id[3];
14500Sstevel@tonic-gate 
14510Sstevel@tonic-gate 	st_write();
14520Sstevel@tonic-gate 
145310083SRoger.Faulkner@Sun.COM 	(void) sigrelse(SIGCLD);
14540Sstevel@tonic-gate }
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate /*
14570Sstevel@tonic-gate  * findpslot() finds the old slot in the process table for the
14580Sstevel@tonic-gate  * command with the same id, or it finds an empty slot.
14590Sstevel@tonic-gate  */
14600Sstevel@tonic-gate static struct PROC_TABLE *
findpslot(struct CMD_LINE * cmd)14610Sstevel@tonic-gate findpslot(struct CMD_LINE *cmd)
14620Sstevel@tonic-gate {
14630Sstevel@tonic-gate 	struct PROC_TABLE	*process;
14640Sstevel@tonic-gate 	struct PROC_TABLE	*empty = NULLPROC;
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate 	for (process = proc_table;
14670Sstevel@tonic-gate 	    (process < proc_table + num_proc); process++) {
14680Sstevel@tonic-gate 		if (process->p_flags & OCCUPIED &&
14690Sstevel@tonic-gate 		    id_eq(process->p_id, cmd->c_id))
14700Sstevel@tonic-gate 			break;
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 		/*
14730Sstevel@tonic-gate 		 * If the entry is totally empty and "empty" is still 0,
14740Sstevel@tonic-gate 		 * remember where this hole is and make sure the slot is
14750Sstevel@tonic-gate 		 * zeroed out.
14760Sstevel@tonic-gate 		 */
14770Sstevel@tonic-gate 		if (empty == NULLPROC && (process->p_flags & OCCUPIED) == 0) {
14780Sstevel@tonic-gate 			empty = process;
14790Sstevel@tonic-gate 			process->p_id[0] = '\0';
14800Sstevel@tonic-gate 			process->p_id[1] = '\0';
14810Sstevel@tonic-gate 			process->p_id[2] = '\0';
14820Sstevel@tonic-gate 			process->p_id[3] = '\0';
14830Sstevel@tonic-gate 			process->p_pid = 0;
14840Sstevel@tonic-gate 			process->p_time = 0L;
14850Sstevel@tonic-gate 			process->p_count = 0;
14860Sstevel@tonic-gate 			process->p_flags = 0;
14870Sstevel@tonic-gate 			process->p_exit = 0;
14880Sstevel@tonic-gate 		}
14890Sstevel@tonic-gate 	}
14900Sstevel@tonic-gate 
14910Sstevel@tonic-gate 	/*
14920Sstevel@tonic-gate 	 * If there is no entry for this slot, then there should be an
14930Sstevel@tonic-gate 	 * empty slot.  If there is no empty slot, then we've run out
14940Sstevel@tonic-gate 	 * of proc_table space.  If the latter is true, empty will be
14950Sstevel@tonic-gate 	 * NULL and the caller will have to complain.
14960Sstevel@tonic-gate 	 */
14970Sstevel@tonic-gate 	if (process == (proc_table + num_proc))
14980Sstevel@tonic-gate 		process = empty;
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate 	return (process);
15010Sstevel@tonic-gate }
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate /*
15040Sstevel@tonic-gate  * getcmd() parses lines from inittab.  Each time it finds a command line
15050Sstevel@tonic-gate  * it will return TRUE as well as fill the passed CMD_LINE structure and
15060Sstevel@tonic-gate  * the shell command string.  When the end of inittab is reached, FALSE
15070Sstevel@tonic-gate  * is returned inittab is automatically opened if it is not currently open
15080Sstevel@tonic-gate  * and is closed when the end of the file is reached.
15090Sstevel@tonic-gate  */
15100Sstevel@tonic-gate static FILE *fp_inittab = NULL;
15110Sstevel@tonic-gate 
15120Sstevel@tonic-gate static int
getcmd(struct CMD_LINE * cmd,char * shcmd)15130Sstevel@tonic-gate getcmd(struct CMD_LINE *cmd, char *shcmd)
15140Sstevel@tonic-gate {
15150Sstevel@tonic-gate 	char	*ptr;
15160Sstevel@tonic-gate 	int	c, lastc, state;
15170Sstevel@tonic-gate 	char 	*ptr1;
15180Sstevel@tonic-gate 	int	answer, i, proceed;
15190Sstevel@tonic-gate 	struct	stat	sbuf;
15200Sstevel@tonic-gate 	static char *actions[] = {
15210Sstevel@tonic-gate 		"off", "respawn", "ondemand", "once", "wait", "boot",
15220Sstevel@tonic-gate 		"bootwait", "powerfail", "powerwait", "initdefault",
15230Sstevel@tonic-gate 		"sysinit",
15240Sstevel@tonic-gate 	};
15250Sstevel@tonic-gate 	static short act_masks[] = {
15260Sstevel@tonic-gate 		M_OFF, M_RESPAWN, M_ONDEMAND, M_ONCE, M_WAIT, M_BOOT,
15270Sstevel@tonic-gate 		M_BOOTWAIT, M_PF, M_PWAIT, M_INITDEFAULT, M_SYSINIT,
15280Sstevel@tonic-gate 	};
15290Sstevel@tonic-gate 	/*
15300Sstevel@tonic-gate 	 * Only these actions will be allowed for entries which
15310Sstevel@tonic-gate 	 * are specified for single-user mode.
15320Sstevel@tonic-gate 	 */
15330Sstevel@tonic-gate 	short su_acts = M_INITDEFAULT | M_PF | M_PWAIT | M_WAIT;
15340Sstevel@tonic-gate 
15350Sstevel@tonic-gate 	if (fp_inittab == NULL) {
15360Sstevel@tonic-gate 		/*
15370Sstevel@tonic-gate 		 * Before attempting to open inittab we stat it to make
15380Sstevel@tonic-gate 		 * sure it currently exists and is not empty.  We try
15390Sstevel@tonic-gate 		 * several times because someone may have temporarily
15400Sstevel@tonic-gate 		 * unlinked or truncated the file.
15410Sstevel@tonic-gate 		 */
15420Sstevel@tonic-gate 		for (i = 0; i < 3; i++) {
15430Sstevel@tonic-gate 			if (stat(INITTAB, &sbuf) == -1) {
15440Sstevel@tonic-gate 				if (i == 2) {
15450Sstevel@tonic-gate 					console(B_TRUE,
15460Sstevel@tonic-gate 					    "Cannot stat %s, errno: %d\n",
15470Sstevel@tonic-gate 					    INITTAB, errno);
15480Sstevel@tonic-gate 					return (FAILURE);
15490Sstevel@tonic-gate 				} else {
15500Sstevel@tonic-gate 					timer(3);
15510Sstevel@tonic-gate 				}
15520Sstevel@tonic-gate 			} else if (sbuf.st_size < 10) {
15530Sstevel@tonic-gate 				if (i == 2) {
15540Sstevel@tonic-gate 					console(B_TRUE,
15550Sstevel@tonic-gate 					    "%s truncated or corrupted\n",
15560Sstevel@tonic-gate 					    INITTAB);
15570Sstevel@tonic-gate 					return (FAILURE);
15580Sstevel@tonic-gate 				} else {
15590Sstevel@tonic-gate 					timer(3);
15600Sstevel@tonic-gate 				}
15610Sstevel@tonic-gate 			} else {
15620Sstevel@tonic-gate 				break;
15630Sstevel@tonic-gate 			}
15640Sstevel@tonic-gate 		}
15650Sstevel@tonic-gate 
15660Sstevel@tonic-gate 		/*
15670Sstevel@tonic-gate 		 * If unable to open inittab, print error message and
15680Sstevel@tonic-gate 		 * return FAILURE to caller.
15690Sstevel@tonic-gate 		 */
15700Sstevel@tonic-gate 		if ((fp_inittab = fopen(INITTAB, "r")) == NULL) {
15710Sstevel@tonic-gate 			console(B_TRUE, "Cannot open %s errno: %d\n", INITTAB,
15720Sstevel@tonic-gate 			    errno);
15730Sstevel@tonic-gate 			return (FAILURE);
15740Sstevel@tonic-gate 		}
15750Sstevel@tonic-gate 	}
15760Sstevel@tonic-gate 
15770Sstevel@tonic-gate 	/*
15780Sstevel@tonic-gate 	 * Keep getting commands from inittab until you find a
15790Sstevel@tonic-gate 	 * good one or run out of file.
15800Sstevel@tonic-gate 	 */
15810Sstevel@tonic-gate 	for (answer = FALSE; answer == FALSE; ) {
15820Sstevel@tonic-gate 		/*
15830Sstevel@tonic-gate 		 * Zero out the cmd itself before trying next line.
15840Sstevel@tonic-gate 		 */
15850Sstevel@tonic-gate 		bzero(cmd, sizeof (struct CMD_LINE));
15860Sstevel@tonic-gate 
15870Sstevel@tonic-gate 		/*
15880Sstevel@tonic-gate 		 * Read in lines of inittab, parsing at colons, until a line is
15890Sstevel@tonic-gate 		 * read in which doesn't end with a backslash.  Do not start if
15900Sstevel@tonic-gate 		 * the first character read is an EOF.  Note that this means
15910Sstevel@tonic-gate 		 * that lines which don't end in a newline are still processed,
15920Sstevel@tonic-gate 		 * since the "for" will terminate normally once started,
15930Sstevel@tonic-gate 		 * regardless of whether line terminates with a newline or EOF.
15940Sstevel@tonic-gate 		 */
15950Sstevel@tonic-gate 		state = FAILURE;
15960Sstevel@tonic-gate 		if ((c = fgetc(fp_inittab)) == EOF) {
15970Sstevel@tonic-gate 			answer = FALSE;
15980Sstevel@tonic-gate 			(void) fclose(fp_inittab);
15990Sstevel@tonic-gate 			fp_inittab = NULL;
16000Sstevel@tonic-gate 			break;
16010Sstevel@tonic-gate 		}
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate 		for (proceed = TRUE, ptr = shcmd, state = ID, lastc = '\0';
16040Sstevel@tonic-gate 		    proceed && c != EOF;
16050Sstevel@tonic-gate 		    lastc = c, c = fgetc(fp_inittab)) {
16060Sstevel@tonic-gate 		    /* If we're not in the FAILURE state and haven't	*/
16070Sstevel@tonic-gate 		    /* yet reached the shell command field, process	*/
16080Sstevel@tonic-gate 		    /* the line, otherwise just look for a real end	*/
16090Sstevel@tonic-gate 		    /* of line.						*/
16100Sstevel@tonic-gate 		    if (state != FAILURE && state != COMMAND) {
16110Sstevel@tonic-gate 			/*
16120Sstevel@tonic-gate 			 * Squeeze out spaces and tabs.
16130Sstevel@tonic-gate 			 */
16140Sstevel@tonic-gate 			if (c == ' ' || c == '\t')
16150Sstevel@tonic-gate 				continue;
16160Sstevel@tonic-gate 
16170Sstevel@tonic-gate 			/*
16180Sstevel@tonic-gate 			 * Ignore characters in a comment, except for the \n.
16190Sstevel@tonic-gate 			 */
16200Sstevel@tonic-gate 			if (state == COMMENT) {
16210Sstevel@tonic-gate 				if (c == '\n') {
16220Sstevel@tonic-gate 					lastc = ' ';
16230Sstevel@tonic-gate 					break;
16240Sstevel@tonic-gate 				} else {
16250Sstevel@tonic-gate 					continue;
16260Sstevel@tonic-gate 				}
16270Sstevel@tonic-gate 			}
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate 			/*
16300Sstevel@tonic-gate 			 * Detect comments (lines whose first non-whitespace
16310Sstevel@tonic-gate 			 * character is '#') by checking that we're at the
16320Sstevel@tonic-gate 			 * beginning of a line, have seen a '#', and haven't
16330Sstevel@tonic-gate 			 * yet accumulated any characters.
16340Sstevel@tonic-gate 			 */
16350Sstevel@tonic-gate 			if (state == ID && c == '#' && ptr == shcmd) {
16360Sstevel@tonic-gate 				state = COMMENT;
16370Sstevel@tonic-gate 				continue;
16380Sstevel@tonic-gate 			}
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 			/*
16410Sstevel@tonic-gate 			 * If the character is a ':', then check the
16420Sstevel@tonic-gate 			 * previous field for correctness and advance
16430Sstevel@tonic-gate 			 * to the next field.
16440Sstevel@tonic-gate 			 */
16450Sstevel@tonic-gate 			if (c == ':') {
16460Sstevel@tonic-gate 			    switch (state) {
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 			    case ID :
16490Sstevel@tonic-gate 				/*
16500Sstevel@tonic-gate 				 * Check to see that there are only
16510Sstevel@tonic-gate 				 * 1 to 4 characters for the id.
16520Sstevel@tonic-gate 				 */
16530Sstevel@tonic-gate 				if ((i = ptr - shcmd) < 1 || i > 4) {
16540Sstevel@tonic-gate 					state = FAILURE;
16550Sstevel@tonic-gate 				} else {
16560Sstevel@tonic-gate 					bcopy(shcmd, &cmd->c_id[0], i);
16570Sstevel@tonic-gate 					ptr = shcmd;
16580Sstevel@tonic-gate 					state = LEVELS;
16590Sstevel@tonic-gate 				}
16600Sstevel@tonic-gate 				break;
16610Sstevel@tonic-gate 
16620Sstevel@tonic-gate 			    case LEVELS :
16630Sstevel@tonic-gate 				/*
16640Sstevel@tonic-gate 				 * Build a mask for all the levels for
16650Sstevel@tonic-gate 				 * which this command will be legal.
16660Sstevel@tonic-gate 				 */
16670Sstevel@tonic-gate 				for (cmd->c_levels = 0, ptr1 = shcmd;
16680Sstevel@tonic-gate 				    ptr1 < ptr; ptr1++) {
16690Sstevel@tonic-gate 					int mask;
16700Sstevel@tonic-gate 					if (lvlname_to_mask(*ptr1,
16710Sstevel@tonic-gate 					    &mask) == -1) {
16720Sstevel@tonic-gate 						state = FAILURE;
16730Sstevel@tonic-gate 						break;
16740Sstevel@tonic-gate 					}
16750Sstevel@tonic-gate 					cmd->c_levels |= mask;
16760Sstevel@tonic-gate 				}
16770Sstevel@tonic-gate 				if (state != FAILURE) {
16780Sstevel@tonic-gate 					state = ACTION;
16790Sstevel@tonic-gate 					ptr = shcmd;	/* Reset the buffer */
16800Sstevel@tonic-gate 				}
16810Sstevel@tonic-gate 				break;
16820Sstevel@tonic-gate 
16830Sstevel@tonic-gate 			    case ACTION :
16840Sstevel@tonic-gate 				/*
16850Sstevel@tonic-gate 				 * Null terminate the string in shcmd buffer and
16860Sstevel@tonic-gate 				 * then try to match against legal actions.  If
16870Sstevel@tonic-gate 				 * the field is of length 0, then the default of
16880Sstevel@tonic-gate 				 * "RESPAWN" is used if the id is numeric,
16890Sstevel@tonic-gate 				 * otherwise the default is "OFF".
16900Sstevel@tonic-gate 				 */
16910Sstevel@tonic-gate 				if (ptr == shcmd) {
16920Sstevel@tonic-gate 					if (isdigit(cmd->c_id[0]) &&
16930Sstevel@tonic-gate 					    (cmd->c_id[1] == '\0' ||
16940Sstevel@tonic-gate 						isdigit(cmd->c_id[1])) &&
16950Sstevel@tonic-gate 					    (cmd->c_id[2] == '\0' ||
16960Sstevel@tonic-gate 						isdigit(cmd->c_id[2])) &&
16970Sstevel@tonic-gate 					    (cmd->c_id[3] == '\0' ||
16980Sstevel@tonic-gate 						isdigit(cmd->c_id[3])))
16990Sstevel@tonic-gate 						    cmd->c_action = M_RESPAWN;
17000Sstevel@tonic-gate 					else
17010Sstevel@tonic-gate 						    cmd->c_action = M_OFF;
17020Sstevel@tonic-gate 				} else {
17030Sstevel@tonic-gate 				    for (cmd->c_action = 0, i = 0, *ptr = '\0';
17040Sstevel@tonic-gate 				    i < sizeof (actions)/sizeof (char *);
17050Sstevel@tonic-gate 				    i++) {
17060Sstevel@tonic-gate 					if (strcmp(shcmd, actions[i]) == 0) {
17070Sstevel@tonic-gate 					    if ((cmd->c_levels & MASKSU) &&
17080Sstevel@tonic-gate 						!(act_masks[i] & su_acts))
17090Sstevel@tonic-gate 						    cmd->c_action = 0;
17100Sstevel@tonic-gate 					    else
17110Sstevel@tonic-gate 						cmd->c_action = act_masks[i];
17120Sstevel@tonic-gate 					    break;
17130Sstevel@tonic-gate 					}
17140Sstevel@tonic-gate 				    }
17150Sstevel@tonic-gate 				}
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 				/*
17180Sstevel@tonic-gate 				 * If the action didn't match any legal action,
17190Sstevel@tonic-gate 				 * set state to FAILURE.
17200Sstevel@tonic-gate 				 */
17210Sstevel@tonic-gate 				if (cmd->c_action == 0) {
17220Sstevel@tonic-gate 					state = FAILURE;
17230Sstevel@tonic-gate 				} else {
17240Sstevel@tonic-gate 					state = COMMAND;
17250Sstevel@tonic-gate 					(void) strcpy(shcmd, "exec ");
17260Sstevel@tonic-gate 				}
17270Sstevel@tonic-gate 				ptr = shcmd + EXEC;
17280Sstevel@tonic-gate 				break;
17290Sstevel@tonic-gate 			    }
17300Sstevel@tonic-gate 			    continue;
17310Sstevel@tonic-gate 			}
17320Sstevel@tonic-gate 		    }
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate 		    /* If the character is a '\n', then this is the end of a */
17350Sstevel@tonic-gate 		    /* line.  If the '\n' wasn't preceded by a backslash, */
17360Sstevel@tonic-gate 		    /* it is also the end of an inittab command.  If it was */
17370Sstevel@tonic-gate 		    /* preceded by a backslash then the next line is a */
17380Sstevel@tonic-gate 		    /* continuation.  Note that the continuation '\n' falls */
17390Sstevel@tonic-gate 		    /* through and is treated like other characters and is */
17400Sstevel@tonic-gate 		    /* stored in the shell command line. */
17410Sstevel@tonic-gate 		    if (c == '\n' && lastc != '\\') {
17420Sstevel@tonic-gate 				proceed = FALSE;
17430Sstevel@tonic-gate 				*ptr = '\0';
17440Sstevel@tonic-gate 				break;
17450Sstevel@tonic-gate 		    }
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 		    /* For all other characters just stuff them into the */
17480Sstevel@tonic-gate 		    /* command as long as there aren't too many of them. */
17490Sstevel@tonic-gate 		    /* Make sure there is room for a terminating '\0' also. */
17500Sstevel@tonic-gate 		    if (ptr >= shcmd + MAXCMDL - 1)
17510Sstevel@tonic-gate 			state = FAILURE;
17520Sstevel@tonic-gate 		    else
17530Sstevel@tonic-gate 			*ptr++ = (char)c;
17540Sstevel@tonic-gate 
17550Sstevel@tonic-gate 		    /* If the character we just stored was a quoted	*/
17560Sstevel@tonic-gate 		    /* backslash, then change "c" to '\0', so that this	*/
17570Sstevel@tonic-gate 		    /* backslash will not cause a subsequent '\n' to appear */
17580Sstevel@tonic-gate 		    /* quoted.  In otherwords '\' '\' '\n' is the real end */
17590Sstevel@tonic-gate 		    /* of a command, while '\' '\n' is a continuation. */
17600Sstevel@tonic-gate 		    if (c == '\\' && lastc == '\\')
17610Sstevel@tonic-gate 			c = '\0';
17620Sstevel@tonic-gate 		}
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate 		/*
17650Sstevel@tonic-gate 		 * Make sure all the fields are properly specified
17660Sstevel@tonic-gate 		 * for a good command line.
17670Sstevel@tonic-gate 		 */
17680Sstevel@tonic-gate 		if (state == COMMAND) {
17690Sstevel@tonic-gate 			answer = TRUE;
17700Sstevel@tonic-gate 			cmd->c_command = shcmd;
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate 			/*
17730Sstevel@tonic-gate 			 * If no default level was supplied, insert
17740Sstevel@tonic-gate 			 * all numerical levels.
17750Sstevel@tonic-gate 			 */
17760Sstevel@tonic-gate 			if (cmd->c_levels == 0)
17770Sstevel@tonic-gate 				cmd->c_levels = MASK_NUMERIC;
17780Sstevel@tonic-gate 
17790Sstevel@tonic-gate 			/*
17800Sstevel@tonic-gate 			 * If no action has been supplied, declare this
17810Sstevel@tonic-gate 			 * entry to be OFF.
17820Sstevel@tonic-gate 			 */
17830Sstevel@tonic-gate 			if (cmd->c_action == 0)
17840Sstevel@tonic-gate 				cmd->c_action = M_OFF;
17850Sstevel@tonic-gate 
17860Sstevel@tonic-gate 			/*
17870Sstevel@tonic-gate 			 * If no shell command has been supplied, make sure
17880Sstevel@tonic-gate 			 * there is a null string in the command field.
17890Sstevel@tonic-gate 			 */
17900Sstevel@tonic-gate 			if (ptr == shcmd + EXEC)
17910Sstevel@tonic-gate 				*shcmd = '\0';
17920Sstevel@tonic-gate 		} else
17930Sstevel@tonic-gate 			answer = FALSE;
17940Sstevel@tonic-gate 
17950Sstevel@tonic-gate 		/*
17960Sstevel@tonic-gate 		 * If we have reached the end of inittab, then close it
17970Sstevel@tonic-gate 		 * and quit trying to find a good command line.
17980Sstevel@tonic-gate 		 */
17990Sstevel@tonic-gate 		if (c == EOF) {
18000Sstevel@tonic-gate 			(void) fclose(fp_inittab);
18010Sstevel@tonic-gate 			fp_inittab = NULL;
18020Sstevel@tonic-gate 			break;
18030Sstevel@tonic-gate 		}
18040Sstevel@tonic-gate 	}
18050Sstevel@tonic-gate 	return (answer);
18060Sstevel@tonic-gate }
18070Sstevel@tonic-gate 
18080Sstevel@tonic-gate /*
18090Sstevel@tonic-gate  * lvlname_to_state(): convert the character name of a state to its level
18100Sstevel@tonic-gate  * (its corresponding signal number).
18110Sstevel@tonic-gate  */
18120Sstevel@tonic-gate static int
lvlname_to_state(char name)18130Sstevel@tonic-gate lvlname_to_state(char name)
18140Sstevel@tonic-gate {
18150Sstevel@tonic-gate 	int i;
18160Sstevel@tonic-gate 	for (i = 0; i < LVL_NELEMS; i++) {
18170Sstevel@tonic-gate 		if (lvls[i].lvl_name == name)
18180Sstevel@tonic-gate 			return (lvls[i].lvl_state);
18190Sstevel@tonic-gate 	}
18200Sstevel@tonic-gate 	return (-1);
18210Sstevel@tonic-gate }
18220Sstevel@tonic-gate 
18230Sstevel@tonic-gate /*
18240Sstevel@tonic-gate  * state_to_name(): convert the level to the character name.
18250Sstevel@tonic-gate  */
18260Sstevel@tonic-gate static char
state_to_name(int state)18270Sstevel@tonic-gate state_to_name(int state)
18280Sstevel@tonic-gate {
18290Sstevel@tonic-gate 	int i;
18300Sstevel@tonic-gate 	for (i = 0; i < LVL_NELEMS; i++) {
18310Sstevel@tonic-gate 		if (lvls[i].lvl_state == state)
18320Sstevel@tonic-gate 			return (lvls[i].lvl_name);
18330Sstevel@tonic-gate 	}
18340Sstevel@tonic-gate 	return (-1);
18350Sstevel@tonic-gate }
18360Sstevel@tonic-gate 
18370Sstevel@tonic-gate /*
18380Sstevel@tonic-gate  * state_to_mask(): return the mask corresponding to a signal number
18390Sstevel@tonic-gate  */
18400Sstevel@tonic-gate static int
state_to_mask(int state)18410Sstevel@tonic-gate state_to_mask(int state)
18420Sstevel@tonic-gate {
18430Sstevel@tonic-gate 	int i;
18440Sstevel@tonic-gate 	for (i = 0; i < LVL_NELEMS; i++) {
18450Sstevel@tonic-gate 		if (lvls[i].lvl_state == state)
18460Sstevel@tonic-gate 			return (lvls[i].lvl_mask);
18470Sstevel@tonic-gate 	}
18480Sstevel@tonic-gate 	return (0);	/* return 0, since that represents an empty mask */
18490Sstevel@tonic-gate }
18500Sstevel@tonic-gate 
18510Sstevel@tonic-gate /*
18520Sstevel@tonic-gate  * lvlname_to_mask(): return the mask corresponding to a levels character name
18530Sstevel@tonic-gate  */
18540Sstevel@tonic-gate static int
lvlname_to_mask(char name,int * mask)18550Sstevel@tonic-gate lvlname_to_mask(char name, int *mask)
18560Sstevel@tonic-gate {
18570Sstevel@tonic-gate 	int i;
18580Sstevel@tonic-gate 	for (i = 0; i < LVL_NELEMS; i++) {
18590Sstevel@tonic-gate 		if (lvls[i].lvl_name == name) {
18600Sstevel@tonic-gate 			*mask = lvls[i].lvl_mask;
18610Sstevel@tonic-gate 			return (0);
18620Sstevel@tonic-gate 		}
18630Sstevel@tonic-gate 	}
18640Sstevel@tonic-gate 	return (-1);
18650Sstevel@tonic-gate }
18660Sstevel@tonic-gate 
18670Sstevel@tonic-gate /*
18680Sstevel@tonic-gate  * state_to_flags(): return the flags corresponding to a runlevel.  These
18690Sstevel@tonic-gate  * indicate properties of that runlevel.
18700Sstevel@tonic-gate  */
18710Sstevel@tonic-gate static int
state_to_flags(int state)18720Sstevel@tonic-gate state_to_flags(int state)
18730Sstevel@tonic-gate {
18740Sstevel@tonic-gate 	int i;
18750Sstevel@tonic-gate 	for (i = 0; i < LVL_NELEMS; i++) {
18760Sstevel@tonic-gate 		if (lvls[i].lvl_state == state)
18770Sstevel@tonic-gate 			return (lvls[i].lvl_flags);
18780Sstevel@tonic-gate 	}
18790Sstevel@tonic-gate 	return (0);
18800Sstevel@tonic-gate }
18810Sstevel@tonic-gate 
18820Sstevel@tonic-gate /*
18830Sstevel@tonic-gate  * killproc() creates a child which kills the process specified by pid.
18840Sstevel@tonic-gate  */
18850Sstevel@tonic-gate void
killproc(pid_t pid)18860Sstevel@tonic-gate killproc(pid_t pid)
18870Sstevel@tonic-gate {
18880Sstevel@tonic-gate 	struct PROC_TABLE	*process;
18890Sstevel@tonic-gate 
189010083SRoger.Faulkner@Sun.COM 	(void) sighold(SIGCLD);
18910Sstevel@tonic-gate 	while ((process = efork(M_OFF, NULLPROC, 0)) == NO_ROOM)
18920Sstevel@tonic-gate 		(void) pause();
189310083SRoger.Faulkner@Sun.COM 	(void) sigrelse(SIGCLD);
18940Sstevel@tonic-gate 
18950Sstevel@tonic-gate 	if (process == NULLPROC) {
18960Sstevel@tonic-gate 		/*
18970Sstevel@tonic-gate 		 * efork() sets all signal handlers to the default, so reset
18980Sstevel@tonic-gate 		 * the ALRM handler to make timer() work as expected.
18990Sstevel@tonic-gate 		 */
19000Sstevel@tonic-gate 		(void) sigset(SIGALRM, alarmclk);
19010Sstevel@tonic-gate 
19020Sstevel@tonic-gate 		/*
19030Sstevel@tonic-gate 		 * We are the child.  Try to terminate the process nicely
19040Sstevel@tonic-gate 		 * first using SIGTERM and if it refuses to die in TWARN
19050Sstevel@tonic-gate 		 * seconds kill it with SIGKILL.
19060Sstevel@tonic-gate 		 */
19070Sstevel@tonic-gate 		(void) kill(pid, SIGTERM);
19080Sstevel@tonic-gate 		(void) timer(TWARN);
19090Sstevel@tonic-gate 		(void) kill(pid, SIGKILL);
19100Sstevel@tonic-gate 		(void) exit(0);
19110Sstevel@tonic-gate 	}
19120Sstevel@tonic-gate }
19130Sstevel@tonic-gate 
19140Sstevel@tonic-gate /*
19150Sstevel@tonic-gate  * Set up the default environment for all procs to be forked from init.
19160Sstevel@tonic-gate  * Read the values from the /etc/default/init file, except for PATH.  If
19170Sstevel@tonic-gate  * there's not enough room in the environment array, the environment
19180Sstevel@tonic-gate  * lines that don't fit are silently discarded.
19190Sstevel@tonic-gate  */
19200Sstevel@tonic-gate void
init_env()19210Sstevel@tonic-gate init_env()
19220Sstevel@tonic-gate {
19230Sstevel@tonic-gate 	char	line[MAXCMDL];
19240Sstevel@tonic-gate 	FILE	*fp;
19250Sstevel@tonic-gate 	int	inquotes, length, wslength;
19260Sstevel@tonic-gate 	char	*tokp, *cp1, *cp2;
19270Sstevel@tonic-gate 
19280Sstevel@tonic-gate 	glob_envp[0] = malloc((unsigned)(strlen(DEF_PATH)+2));
19290Sstevel@tonic-gate 	(void) strcpy(glob_envp[0], DEF_PATH);
19300Sstevel@tonic-gate 	glob_envn = 1;
19310Sstevel@tonic-gate 
19320Sstevel@tonic-gate 	if (rflg) {
19330Sstevel@tonic-gate 		glob_envp[1] =
19348735SEnrico.Perla@Sun.COM 		    malloc((unsigned)(strlen("_DVFS_RECONFIG=YES")+2));
19350Sstevel@tonic-gate 		(void) strcpy(glob_envp[1], "_DVFS_RECONFIG=YES");
19360Sstevel@tonic-gate 		++glob_envn;
19370Sstevel@tonic-gate 	} else if (bflg == 1) {
19380Sstevel@tonic-gate 		glob_envp[1] =
19398735SEnrico.Perla@Sun.COM 		    malloc((unsigned)(strlen("RB_NOBOOTRC=YES")+2));
19400Sstevel@tonic-gate 		(void) strcpy(glob_envp[1], "RB_NOBOOTRC=YES");
19410Sstevel@tonic-gate 		++glob_envn;
19420Sstevel@tonic-gate 	}
19430Sstevel@tonic-gate 
19440Sstevel@tonic-gate 	if ((fp = fopen(ENVFILE, "r")) == NULL) {
19450Sstevel@tonic-gate 		console(B_TRUE,
19460Sstevel@tonic-gate 		    "Cannot open %s. Environment not initialized.\n",
19470Sstevel@tonic-gate 		    ENVFILE);
19480Sstevel@tonic-gate 	} else {
19490Sstevel@tonic-gate 		while (fgets(line, MAXCMDL - 1, fp) != NULL &&
19500Sstevel@tonic-gate 		    glob_envn < MAXENVENT - 2) {
19510Sstevel@tonic-gate 			/*
19520Sstevel@tonic-gate 			 * Toss newline
19530Sstevel@tonic-gate 			 */
19540Sstevel@tonic-gate 			length = strlen(line);
19550Sstevel@tonic-gate 			if (line[length - 1] == '\n')
19560Sstevel@tonic-gate 				line[length - 1] = '\0';
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 			/*
19590Sstevel@tonic-gate 			 * Ignore blank or comment lines.
19600Sstevel@tonic-gate 			 */
19610Sstevel@tonic-gate 			if (line[0] == '#' || line[0] == '\0' ||
19620Sstevel@tonic-gate 			    (wslength = strspn(line, " \t\n")) ==
19630Sstevel@tonic-gate 			    strlen(line) ||
19640Sstevel@tonic-gate 			    strchr(line, '#') == line + wslength)
19650Sstevel@tonic-gate 				continue;
19660Sstevel@tonic-gate 
19670Sstevel@tonic-gate 			/*
19680Sstevel@tonic-gate 			 * First make a pass through the line and change
19690Sstevel@tonic-gate 			 * any non-quoted semi-colons to blanks so they
19700Sstevel@tonic-gate 			 * will be treated as token separators below.
19710Sstevel@tonic-gate 			 */
19720Sstevel@tonic-gate 			inquotes = 0;
19730Sstevel@tonic-gate 			for (cp1 = line; *cp1 != '\0'; cp1++) {
19740Sstevel@tonic-gate 				if (*cp1 == '"') {
19750Sstevel@tonic-gate 					if (inquotes == 0)
19760Sstevel@tonic-gate 						inquotes = 1;
19770Sstevel@tonic-gate 					else
19780Sstevel@tonic-gate 						inquotes = 0;
19790Sstevel@tonic-gate 				} else if (*cp1 == ';') {
19800Sstevel@tonic-gate 					if (inquotes == 0)
19810Sstevel@tonic-gate 						*cp1 = ' ';
19820Sstevel@tonic-gate 				}
19830Sstevel@tonic-gate 			}
19840Sstevel@tonic-gate 
19850Sstevel@tonic-gate 			/*
19860Sstevel@tonic-gate 			 * Tokens within the line are separated by blanks
19870Sstevel@tonic-gate 			 *  and tabs.  For each token in the line which
19880Sstevel@tonic-gate 			 * contains a '=' we strip out any quotes and then
19890Sstevel@tonic-gate 			 * stick the token in the environment array.
19900Sstevel@tonic-gate 			 */
19910Sstevel@tonic-gate 			if ((tokp = strtok(line, " \t")) == NULL)
19920Sstevel@tonic-gate 				continue;
19930Sstevel@tonic-gate 			do {
19940Sstevel@tonic-gate 				if (strchr(tokp, '=') == NULL)
19950Sstevel@tonic-gate 					continue;
19960Sstevel@tonic-gate 				length = strlen(tokp);
19970Sstevel@tonic-gate 				while ((cp1 = strpbrk(tokp, "\"\'")) != NULL) {
19980Sstevel@tonic-gate 					for (cp2 = cp1;
19990Sstevel@tonic-gate 					    cp2 < &tokp[length]; cp2++)
20000Sstevel@tonic-gate 						*cp2 = *(cp2 + 1);
20010Sstevel@tonic-gate 					length--;
20020Sstevel@tonic-gate 				}
20030Sstevel@tonic-gate 
20040Sstevel@tonic-gate 				if (strncmp(tokp, "CMASK=",
20050Sstevel@tonic-gate 				    sizeof ("CMASK=") - 1) == 0) {
20060Sstevel@tonic-gate 					long t;
20070Sstevel@tonic-gate 
20080Sstevel@tonic-gate 					/* We know there's an = */
20090Sstevel@tonic-gate 					t = strtol(strchr(tokp, '=') + 1, NULL,
20100Sstevel@tonic-gate 					    8);
20110Sstevel@tonic-gate 
20120Sstevel@tonic-gate 					/* Sanity */
20130Sstevel@tonic-gate 					if (t <= 077 && t >= 0)
20140Sstevel@tonic-gate 						cmask = (int)t;
20150Sstevel@tonic-gate 					(void) umask(cmask);
20160Sstevel@tonic-gate 					continue;
20170Sstevel@tonic-gate 				}
20180Sstevel@tonic-gate 				glob_envp[glob_envn] =
20190Sstevel@tonic-gate 				    malloc((unsigned)(length + 1));
20200Sstevel@tonic-gate 				(void) strcpy(glob_envp[glob_envn], tokp);
20210Sstevel@tonic-gate 				if (++glob_envn >= MAXENVENT - 1)
20220Sstevel@tonic-gate 					break;
20230Sstevel@tonic-gate 			} while ((tokp = strtok(NULL, " \t")) != NULL);
20240Sstevel@tonic-gate 		}
20250Sstevel@tonic-gate 
20260Sstevel@tonic-gate 		/*
20270Sstevel@tonic-gate 		 * Append a null pointer to the environment array
20280Sstevel@tonic-gate 		 * to mark its end.
20290Sstevel@tonic-gate 		 */
20300Sstevel@tonic-gate 		glob_envp[glob_envn] = NULL;
20310Sstevel@tonic-gate 		(void) fclose(fp);
20320Sstevel@tonic-gate 	}
20330Sstevel@tonic-gate }
20340Sstevel@tonic-gate 
20350Sstevel@tonic-gate /*
20360Sstevel@tonic-gate  * boot_init(): Do initialization things that should be done at boot.
20370Sstevel@tonic-gate  */
20380Sstevel@tonic-gate void
boot_init()20390Sstevel@tonic-gate boot_init()
20400Sstevel@tonic-gate {
20410Sstevel@tonic-gate 	int i;
20420Sstevel@tonic-gate 	struct PROC_TABLE *process, *oprocess;
20430Sstevel@tonic-gate 	struct CMD_LINE	cmd;
20440Sstevel@tonic-gate 	char	line[MAXCMDL];
20456073Sacruz 	char	svc_aux[SVC_AUX_SIZE];
20466073Sacruz 	char	init_svc_fmri[SVC_FMRI_SIZE];
20470Sstevel@tonic-gate 	char *old_path;
20480Sstevel@tonic-gate 	int maxfiles;
20490Sstevel@tonic-gate 
20500Sstevel@tonic-gate 	/* Use INIT_PATH for sysinit cmds */
20510Sstevel@tonic-gate 	old_path = glob_envp[0];
20520Sstevel@tonic-gate 	glob_envp[0] = malloc((unsigned)(strlen(INIT_PATH)+2));
20530Sstevel@tonic-gate 	(void) strcpy(glob_envp[0], INIT_PATH);
20540Sstevel@tonic-gate 
20550Sstevel@tonic-gate 	/*
20560Sstevel@tonic-gate 	 * Scan inittab(4) and process the special svc.startd entry, initdefault
20570Sstevel@tonic-gate 	 * and sysinit entries.
20580Sstevel@tonic-gate 	 */
20590Sstevel@tonic-gate 	while (getcmd(&cmd, &line[0]) == TRUE) {
20606073Sacruz 		if (startd_tmpl >= 0 && id_eq(cmd.c_id, "smf")) {
20610Sstevel@tonic-gate 			process_startd_line(&cmd, line);
20626073Sacruz 			(void) snprintf(startd_svc_aux, SVC_AUX_SIZE,
20636073Sacruz 			    INITTAB_ENTRY_ID_STR_FORMAT, cmd.c_id);
20646073Sacruz 		} else if (cmd.c_action == M_INITDEFAULT) {
20650Sstevel@tonic-gate 			/*
20660Sstevel@tonic-gate 			 * initdefault is no longer meaningful, as the SMF
20670Sstevel@tonic-gate 			 * milestone controls what (legacy) run level we
20680Sstevel@tonic-gate 			 * boot to.
20690Sstevel@tonic-gate 			 */
20700Sstevel@tonic-gate 			console(B_TRUE,
20710Sstevel@tonic-gate 			    "Ignoring legacy \"initdefault\" entry.\n");
20720Sstevel@tonic-gate 		} else if (cmd.c_action == M_SYSINIT) {
20730Sstevel@tonic-gate 			/*
20740Sstevel@tonic-gate 			 * Execute the "sysinit" entry and wait for it to
20750Sstevel@tonic-gate 			 * complete.  No bookkeeping is performed on these
20760Sstevel@tonic-gate 			 * entries because we avoid writing to the file system
20770Sstevel@tonic-gate 			 * until after there has been an chance to check it.
20780Sstevel@tonic-gate 			 */
20790Sstevel@tonic-gate 			if (process = findpslot(&cmd)) {
208010083SRoger.Faulkner@Sun.COM 				(void) sighold(SIGCLD);
20816073Sacruz 				(void) snprintf(svc_aux, SVC_AUX_SIZE,
20826073Sacruz 				    INITTAB_ENTRY_ID_STR_FORMAT, cmd.c_id);
20836073Sacruz 				(void) snprintf(init_svc_fmri, SVC_FMRI_SIZE,
20846073Sacruz 				    SVC_INIT_PREFIX INITTAB_ENTRY_ID_STR_FORMAT,
20856073Sacruz 				    cmd.c_id);
20866073Sacruz 				if (legacy_tmpl >= 0) {
20876073Sacruz 					(void) ct_pr_tmpl_set_svc_fmri(
20886073Sacruz 					    legacy_tmpl, init_svc_fmri);
20896073Sacruz 					(void) ct_pr_tmpl_set_svc_aux(
20906073Sacruz 					    legacy_tmpl, svc_aux);
20916073Sacruz 				}
20920Sstevel@tonic-gate 
20930Sstevel@tonic-gate 				for (oprocess = process;
20940Sstevel@tonic-gate 				    (process = efork(M_OFF, oprocess,
20950Sstevel@tonic-gate 				    (NAMED|NOCLEANUP))) == NO_ROOM;
20960Sstevel@tonic-gate 				    /* CSTYLED */)
20970Sstevel@tonic-gate 					;
209810083SRoger.Faulkner@Sun.COM 				(void) sigrelse(SIGCLD);
20990Sstevel@tonic-gate 
21000Sstevel@tonic-gate 				if (process == NULLPROC) {
21010Sstevel@tonic-gate 					maxfiles = ulimit(UL_GDESLIM, 0);
21020Sstevel@tonic-gate 
21030Sstevel@tonic-gate 					for (i = 0; i < maxfiles; i++)
21040Sstevel@tonic-gate 						(void) fcntl(i, F_SETFD,
21050Sstevel@tonic-gate 						    FD_CLOEXEC);
21060Sstevel@tonic-gate 					(void) execle(SH, "INITSH", "-c",
21070Sstevel@tonic-gate 					    cmd.c_command,
21080Sstevel@tonic-gate 					    (char *)0, glob_envp);
21090Sstevel@tonic-gate 					console(B_TRUE,
21100Sstevel@tonic-gate "Command\n\"%s\"\n failed to execute.  errno = %d (exec of shell failed)\n",
21118735SEnrico.Perla@Sun.COM 					    cmd.c_command, errno);
21120Sstevel@tonic-gate 					exit(1);
21130Sstevel@tonic-gate 				} else while (waitproc(process) == FAILURE);
21140Sstevel@tonic-gate 				process->p_flags = 0;
21150Sstevel@tonic-gate 				st_write();
21160Sstevel@tonic-gate 			}
21170Sstevel@tonic-gate 		}
21180Sstevel@tonic-gate 	}
21190Sstevel@tonic-gate 
21200Sstevel@tonic-gate 	/* Restore the path. */
21210Sstevel@tonic-gate 	free(glob_envp[0]);
21220Sstevel@tonic-gate 	glob_envp[0] = old_path;
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate 	/*
21250Sstevel@tonic-gate 	 * This will enable st_write() to complain about init_state_file.
21260Sstevel@tonic-gate 	 */
21270Sstevel@tonic-gate 	booting = 0;
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 	/*
21300Sstevel@tonic-gate 	 * If the /etc/ioctl.syscon didn't exist or had invalid contents write
21310Sstevel@tonic-gate 	 * out a correct version.
21320Sstevel@tonic-gate 	 */
21330Sstevel@tonic-gate 	if (write_ioctl)
21340Sstevel@tonic-gate 		write_ioctl_syscon();
21350Sstevel@tonic-gate 
21360Sstevel@tonic-gate 	/*
21370Sstevel@tonic-gate 	 * Start svc.startd(1M), which does most of the work.
21380Sstevel@tonic-gate 	 */
21390Sstevel@tonic-gate 	if (startd_cline[0] != '\0' && startd_tmpl >= 0) {
21400Sstevel@tonic-gate 		/* Start svc.startd. */
21410Sstevel@tonic-gate 		if (startd_run(startd_cline, startd_tmpl, 0) == -1)
21420Sstevel@tonic-gate 			cur_state = SINGLE_USER;
21430Sstevel@tonic-gate 	} else {
21440Sstevel@tonic-gate 		console(B_TRUE, "Absent svc.startd entry or bad "
21450Sstevel@tonic-gate 		    "contract template.  Not starting svc.startd.\n");
21460Sstevel@tonic-gate 		enter_maintenance();
21470Sstevel@tonic-gate 	}
21480Sstevel@tonic-gate }
21490Sstevel@tonic-gate 
21500Sstevel@tonic-gate /*
21510Sstevel@tonic-gate  * init_signals(): Initialize all signals to either be caught or ignored.
21520Sstevel@tonic-gate  */
21530Sstevel@tonic-gate void
init_signals(void)21540Sstevel@tonic-gate init_signals(void)
21550Sstevel@tonic-gate {
21560Sstevel@tonic-gate 	struct sigaction act;
21570Sstevel@tonic-gate 	int i;
21580Sstevel@tonic-gate 
21590Sstevel@tonic-gate 	/*
21600Sstevel@tonic-gate 	 * Start by ignoring all signals, then selectively re-enable some.
21610Sstevel@tonic-gate 	 * The SIG_IGN disposition will only affect asynchronous signals:
21620Sstevel@tonic-gate 	 * any signal that we trigger synchronously that doesn't end up
21630Sstevel@tonic-gate 	 * being handled by siglvl() will be forcibly delivered by the kernel.
21640Sstevel@tonic-gate 	 */
21650Sstevel@tonic-gate 	for (i = SIGHUP; i <= SIGRTMAX; i++)
21660Sstevel@tonic-gate 		(void) sigset(i, SIG_IGN);
21670Sstevel@tonic-gate 
21680Sstevel@tonic-gate 	/*
21690Sstevel@tonic-gate 	 * Handle all level-changing signals using siglvl() and set sa_mask so
21700Sstevel@tonic-gate 	 * that all level-changing signals are blocked while in siglvl().
21710Sstevel@tonic-gate 	 */
21720Sstevel@tonic-gate 	act.sa_handler = siglvl;
21730Sstevel@tonic-gate 	act.sa_flags = SA_SIGINFO;
21740Sstevel@tonic-gate 	(void) sigemptyset(&act.sa_mask);
21750Sstevel@tonic-gate 
21760Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVLQ);
21770Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVL0);
21780Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVL1);
21790Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVL2);
21800Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVL3);
21810Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVL4);
21820Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVL5);
21830Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVL6);
21840Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, SINGLE_USER);
21850Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVLa);
21860Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVLb);
21870Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, LVLc);
21880Sstevel@tonic-gate 
21890Sstevel@tonic-gate 	(void) sigaction(LVLQ, &act, NULL);
21900Sstevel@tonic-gate 	(void) sigaction(LVL0, &act, NULL);
21910Sstevel@tonic-gate 	(void) sigaction(LVL1, &act, NULL);
21920Sstevel@tonic-gate 	(void) sigaction(LVL2, &act, NULL);
21930Sstevel@tonic-gate 	(void) sigaction(LVL3, &act, NULL);
21940Sstevel@tonic-gate 	(void) sigaction(LVL4, &act, NULL);
21950Sstevel@tonic-gate 	(void) sigaction(LVL5, &act, NULL);
21960Sstevel@tonic-gate 	(void) sigaction(LVL6, &act, NULL);
21970Sstevel@tonic-gate 	(void) sigaction(SINGLE_USER, &act, NULL);
21980Sstevel@tonic-gate 	(void) sigaction(LVLa, &act, NULL);
21990Sstevel@tonic-gate 	(void) sigaction(LVLb, &act, NULL);
22000Sstevel@tonic-gate 	(void) sigaction(LVLc, &act, NULL);
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate 	(void) sigset(SIGALRM, alarmclk);
22030Sstevel@tonic-gate 	alarmclk();
22040Sstevel@tonic-gate 
22050Sstevel@tonic-gate 	(void) sigset(SIGCLD, childeath);
22060Sstevel@tonic-gate 	(void) sigset(SIGPWR, powerfail);
22070Sstevel@tonic-gate }
22080Sstevel@tonic-gate 
22090Sstevel@tonic-gate /*
22100Sstevel@tonic-gate  * Set up pipe for "godchildren". If the file exists and is a pipe just open
22110Sstevel@tonic-gate  * it. Else, if the file system is r/w create it.  Otherwise, defer its
22125017Seschrock  * creation and open until after /var/run has been mounted.  This function is
22135017Seschrock  * only called on startup and when explicitly requested via LVLQ.
22140Sstevel@tonic-gate  */
22150Sstevel@tonic-gate void
setup_pipe()22160Sstevel@tonic-gate setup_pipe()
22170Sstevel@tonic-gate {
22180Sstevel@tonic-gate 	struct stat stat_buf;
22190Sstevel@tonic-gate 	struct statvfs statvfs_buf;
22202691Snakanon 	struct sigaction act;
22210Sstevel@tonic-gate 
22225017Seschrock 	/*
22235017Seschrock 	 * Always close the previous pipe descriptor as the mounted filesystems
22245017Seschrock 	 * may have changed.
22255017Seschrock 	 */
22265017Seschrock 	if (Pfd >= 0)
22275017Seschrock 		(void) close(Pfd);
22285017Seschrock 
22290Sstevel@tonic-gate 	if ((stat(INITPIPE, &stat_buf) == 0) &&
22300Sstevel@tonic-gate 	    ((stat_buf.st_mode & (S_IFMT|S_IRUSR)) == (S_IFIFO|S_IRUSR)))
22310Sstevel@tonic-gate 		Pfd = open(INITPIPE, O_RDWR | O_NDELAY);
22320Sstevel@tonic-gate 	else
22330Sstevel@tonic-gate 		if ((statvfs(INITPIPE_DIR, &statvfs_buf) == 0) &&
22340Sstevel@tonic-gate 		    ((statvfs_buf.f_flag & ST_RDONLY) == 0)) {
22350Sstevel@tonic-gate 			(void) unlink(INITPIPE);
22360Sstevel@tonic-gate 			(void) mknod(INITPIPE, S_IFIFO | 0600, 0);
22370Sstevel@tonic-gate 			Pfd = open(INITPIPE, O_RDWR | O_NDELAY);
22380Sstevel@tonic-gate 		}
22390Sstevel@tonic-gate 
22400Sstevel@tonic-gate 	if (Pfd >= 0) {
22410Sstevel@tonic-gate 		(void) ioctl(Pfd, I_SETSIG, S_INPUT);
22420Sstevel@tonic-gate 		/*
22430Sstevel@tonic-gate 		 * Read pipe in message discard mode.
22440Sstevel@tonic-gate 		 */
22450Sstevel@tonic-gate 		(void) ioctl(Pfd, I_SRDOPT, RMSGD);
22462691Snakanon 
22472691Snakanon 		act.sa_handler = sigpoll;
22482691Snakanon 		act.sa_flags = 0;
22492691Snakanon 		(void) sigemptyset(&act.sa_mask);
22502691Snakanon 		(void) sigaddset(&act.sa_mask, SIGCLD);
22512691Snakanon 		(void) sigaction(SIGPOLL, &act, NULL);
22520Sstevel@tonic-gate 	}
22530Sstevel@tonic-gate }
22540Sstevel@tonic-gate 
22550Sstevel@tonic-gate /*
22560Sstevel@tonic-gate  * siglvl - handle an asynchronous signal from init(1M) telling us that we
22570Sstevel@tonic-gate  * should change the current run level.  We set new_state accordingly.
22580Sstevel@tonic-gate  */
22590Sstevel@tonic-gate void
siglvl(int sig,siginfo_t * sip,ucontext_t * ucp)22600Sstevel@tonic-gate siglvl(int sig, siginfo_t *sip, ucontext_t *ucp)
22610Sstevel@tonic-gate {
22620Sstevel@tonic-gate 	struct PROC_TABLE *process;
22630Sstevel@tonic-gate 	struct sigaction act;
22640Sstevel@tonic-gate 
22650Sstevel@tonic-gate 	/*
22660Sstevel@tonic-gate 	 * If the signal was from the kernel (rather than init(1M)) then init
22670Sstevel@tonic-gate 	 * itself tripped the signal.  That is, we might have a bug and tripped
22680Sstevel@tonic-gate 	 * a real SIGSEGV instead of receiving it as an alias for SIGLVLa.  In
22690Sstevel@tonic-gate 	 * such a case we reset the disposition to SIG_DFL, block all signals
22700Sstevel@tonic-gate 	 * in uc_mask but the current one, and return to the interrupted ucp
22710Sstevel@tonic-gate 	 * to effect an appropriate death.  The kernel will then restart us.
22720Sstevel@tonic-gate 	 *
22730Sstevel@tonic-gate 	 * The one exception to SI_FROMKERNEL() is SIGFPE (a.k.a. LVL6), which
22740Sstevel@tonic-gate 	 * the kernel can send us when it wants to effect an orderly reboot.
22750Sstevel@tonic-gate 	 * For this case we must also verify si_code is zero, rather than a
22760Sstevel@tonic-gate 	 * code such as FPE_INTDIV which a bug might have triggered.
22770Sstevel@tonic-gate 	 */
22780Sstevel@tonic-gate 	if (sip != NULL && SI_FROMKERNEL(sip) &&
22790Sstevel@tonic-gate 	    (sig != SIGFPE || sip->si_code == 0)) {
22800Sstevel@tonic-gate 
22810Sstevel@tonic-gate 		(void) sigemptyset(&act.sa_mask);
22820Sstevel@tonic-gate 		act.sa_handler = SIG_DFL;
22830Sstevel@tonic-gate 		act.sa_flags = 0;
22840Sstevel@tonic-gate 		(void) sigaction(sig, &act, NULL);
22850Sstevel@tonic-gate 
22860Sstevel@tonic-gate 		(void) sigfillset(&ucp->uc_sigmask);
22870Sstevel@tonic-gate 		(void) sigdelset(&ucp->uc_sigmask, sig);
22880Sstevel@tonic-gate 		ucp->uc_flags |= UC_SIGMASK;
22890Sstevel@tonic-gate 
22900Sstevel@tonic-gate 		(void) setcontext(ucp);
22910Sstevel@tonic-gate 	}
22920Sstevel@tonic-gate 
22930Sstevel@tonic-gate 	/*
22940Sstevel@tonic-gate 	 * If the signal received is a LVLQ signal, do not really
22950Sstevel@tonic-gate 	 * change levels, just restate the current level.  If the
22960Sstevel@tonic-gate 	 * signal is not a LVLQ, set the new level to the signal
22970Sstevel@tonic-gate 	 * received.
22980Sstevel@tonic-gate 	 */
22995017Seschrock 	if (sig == LVLQ) {
23000Sstevel@tonic-gate 		new_state = cur_state;
23015017Seschrock 		lvlq_received = B_TRUE;
23025017Seschrock 	} else {
23030Sstevel@tonic-gate 		new_state = sig;
23045017Seschrock 	}
23050Sstevel@tonic-gate 
23060Sstevel@tonic-gate 	/*
23070Sstevel@tonic-gate 	 * Clear all times and repeat counts in the process table
23080Sstevel@tonic-gate 	 * since either the level is changing or the user has editted
23090Sstevel@tonic-gate 	 * the inittab file and wants us to look at it again.
23100Sstevel@tonic-gate 	 * If the user has fixed a typo, we don't want residual timing
23110Sstevel@tonic-gate 	 * data preventing the fixed command line from executing.
23120Sstevel@tonic-gate 	 */
23130Sstevel@tonic-gate 	for (process = proc_table;
23148735SEnrico.Perla@Sun.COM 	    (process < proc_table + num_proc); process++) {
23150Sstevel@tonic-gate 		process->p_time = 0L;
23160Sstevel@tonic-gate 		process->p_count = 0;
23170Sstevel@tonic-gate 	}
23180Sstevel@tonic-gate 
23190Sstevel@tonic-gate 	/*
23200Sstevel@tonic-gate 	 * Set the flag to indicate that a "user signal" was received.
23210Sstevel@tonic-gate 	 */
23220Sstevel@tonic-gate 	wakeup.w_flags.w_usersignal = 1;
23230Sstevel@tonic-gate }
23240Sstevel@tonic-gate 
23250Sstevel@tonic-gate 
23260Sstevel@tonic-gate /*
23270Sstevel@tonic-gate  * alarmclk
23280Sstevel@tonic-gate  */
23290Sstevel@tonic-gate static void
alarmclk()23300Sstevel@tonic-gate alarmclk()
23310Sstevel@tonic-gate {
23320Sstevel@tonic-gate 	time_up = TRUE;
23330Sstevel@tonic-gate }
23340Sstevel@tonic-gate 
23350Sstevel@tonic-gate /*
23360Sstevel@tonic-gate  * childeath_single():
23370Sstevel@tonic-gate  *
23380Sstevel@tonic-gate  * This used to be the SIGCLD handler and it was set with signal()
23390Sstevel@tonic-gate  * (as opposed to sigset()).  When a child exited we'd come to the
23400Sstevel@tonic-gate  * handler, wait for the child, and reenable the handler with
23410Sstevel@tonic-gate  * signal() just before returning.  The implementation of signal()
23420Sstevel@tonic-gate  * checks with waitid() for waitable children and sends a SIGCLD
23430Sstevel@tonic-gate  * if there are some.  If children are exiting faster than the
23440Sstevel@tonic-gate  * handler can run we keep sending signals and the handler never
23450Sstevel@tonic-gate  * gets to return and eventually the stack runs out and init dies.
23460Sstevel@tonic-gate  * To prevent that we set the handler with sigset() so the handler
23470Sstevel@tonic-gate  * doesn't need to be reset, and in childeath() (see below) we
23480Sstevel@tonic-gate  * call childeath_single() as long as there are children to be
23490Sstevel@tonic-gate  * waited for.  If a child exits while init is in the handler a
23500Sstevel@tonic-gate  * SIGCLD will be pending and delivered on return from the handler.
23510Sstevel@tonic-gate  * If the child was already waited for the handler will have nothing
23520Sstevel@tonic-gate  * to do and return, otherwise the child will be waited for.
23530Sstevel@tonic-gate  */
23540Sstevel@tonic-gate static void
childeath_single(pid_t pid,int status)235510083SRoger.Faulkner@Sun.COM childeath_single(pid_t pid, int status)
23560Sstevel@tonic-gate {
23570Sstevel@tonic-gate 	struct PROC_TABLE	*process;
23580Sstevel@tonic-gate 	struct pidlist		*pp;
23590Sstevel@tonic-gate 
23600Sstevel@tonic-gate 	/*
236110083SRoger.Faulkner@Sun.COM 	 * Scan the process table to see if we are interested in this process.
23620Sstevel@tonic-gate 	 */
23630Sstevel@tonic-gate 	for (process = proc_table;
23648735SEnrico.Perla@Sun.COM 	    (process < proc_table + num_proc); process++) {
23650Sstevel@tonic-gate 		if ((process->p_flags & (LIVING|OCCUPIED)) ==
23660Sstevel@tonic-gate 		    (LIVING|OCCUPIED) && process->p_pid == pid) {
23670Sstevel@tonic-gate 
23680Sstevel@tonic-gate 			/*
23690Sstevel@tonic-gate 			 * Mark this process as having died and store the exit
23700Sstevel@tonic-gate 			 * status.  Also set the wakeup flag for a dead child
23710Sstevel@tonic-gate 			 * and break out of the loop.
23720Sstevel@tonic-gate 			 */
23730Sstevel@tonic-gate 			process->p_flags &= ~LIVING;
23740Sstevel@tonic-gate 			process->p_exit = (short)status;
23750Sstevel@tonic-gate 			wakeup.w_flags.w_childdeath = 1;
23760Sstevel@tonic-gate 
23770Sstevel@tonic-gate 			return;
23780Sstevel@tonic-gate 		}
23790Sstevel@tonic-gate 	}
23800Sstevel@tonic-gate 
23810Sstevel@tonic-gate 	/*
23820Sstevel@tonic-gate 	 * No process was found above, look through auxiliary list.
23830Sstevel@tonic-gate 	 */
23840Sstevel@tonic-gate 	(void) sighold(SIGPOLL);
23850Sstevel@tonic-gate 	pp = Plhead;
23860Sstevel@tonic-gate 	while (pp) {
23870Sstevel@tonic-gate 		if (pid > pp->pl_pid) {
23880Sstevel@tonic-gate 			/*
23890Sstevel@tonic-gate 			 * Keep on looking.
23900Sstevel@tonic-gate 			 */
23910Sstevel@tonic-gate 			pp = pp->pl_next;
23920Sstevel@tonic-gate 			continue;
23930Sstevel@tonic-gate 		} else if (pid < pp->pl_pid) {
23940Sstevel@tonic-gate 			/*
23950Sstevel@tonic-gate 			 * Not in the list.
23960Sstevel@tonic-gate 			 */
23970Sstevel@tonic-gate 			break;
23980Sstevel@tonic-gate 		} else {
23990Sstevel@tonic-gate 			/*
24000Sstevel@tonic-gate 			 * This is a dead "godchild".
24010Sstevel@tonic-gate 			 */
24020Sstevel@tonic-gate 			pp->pl_dflag = 1;
24030Sstevel@tonic-gate 			pp->pl_exit = (short)status;
24040Sstevel@tonic-gate 			wakeup.w_flags.w_childdeath = 1;
24050Sstevel@tonic-gate 			Gchild = 1;	/* Notice to call cleanaux(). */
24060Sstevel@tonic-gate 			break;
24070Sstevel@tonic-gate 		}
24080Sstevel@tonic-gate 	}
24090Sstevel@tonic-gate 
24100Sstevel@tonic-gate 	(void) sigrelse(SIGPOLL);
24110Sstevel@tonic-gate }
24120Sstevel@tonic-gate 
24130Sstevel@tonic-gate /* ARGSUSED */
24140Sstevel@tonic-gate static void
childeath(int signo)24150Sstevel@tonic-gate childeath(int signo)
24160Sstevel@tonic-gate {
241710083SRoger.Faulkner@Sun.COM 	pid_t pid;
241810083SRoger.Faulkner@Sun.COM 	int status;
241910083SRoger.Faulkner@Sun.COM 
242010083SRoger.Faulkner@Sun.COM 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
242110083SRoger.Faulkner@Sun.COM 		childeath_single(pid, status);
24220Sstevel@tonic-gate }
24230Sstevel@tonic-gate 
24240Sstevel@tonic-gate static void
powerfail()24250Sstevel@tonic-gate powerfail()
24260Sstevel@tonic-gate {
24270Sstevel@tonic-gate 	(void) nice(-19);
24280Sstevel@tonic-gate 	wakeup.w_flags.w_powerhit = 1;
24290Sstevel@tonic-gate }
24300Sstevel@tonic-gate 
24310Sstevel@tonic-gate /*
24320Sstevel@tonic-gate  * efork() forks a child and the parent inserts the process in its table
24330Sstevel@tonic-gate  * of processes that are directly a result of forks that it has performed.
24340Sstevel@tonic-gate  * The child just changes the "global" with the process id for this process
24350Sstevel@tonic-gate  * to it's new value.
24360Sstevel@tonic-gate  * If efork() is called with a pointer into the proc_table it uses that slot,
24370Sstevel@tonic-gate  * otherwise it searches for a free slot.  Regardless of how it was called,
24380Sstevel@tonic-gate  * it returns the pointer to the proc_table entry
24390Sstevel@tonic-gate  *
244010083SRoger.Faulkner@Sun.COM  * The SIGCLD signal is blocked (held) before calling efork()
244110083SRoger.Faulkner@Sun.COM  * and is unblocked (released) after efork() returns.
24420Sstevel@tonic-gate  *
24430Sstevel@tonic-gate  * Ideally, this should be rewritten to use modern signal semantics.
24440Sstevel@tonic-gate  */
24450Sstevel@tonic-gate static struct PROC_TABLE *
efork(int action,struct PROC_TABLE * process,int modes)24460Sstevel@tonic-gate efork(int action, struct PROC_TABLE *process, int modes)
24470Sstevel@tonic-gate {
24480Sstevel@tonic-gate 	pid_t	childpid;
24490Sstevel@tonic-gate 	struct PROC_TABLE *proc;
24500Sstevel@tonic-gate 	int		i;
24510Sstevel@tonic-gate 	/*
24520Sstevel@tonic-gate 	 * Freshen up the proc_table, removing any entries for dead processes
24530Sstevel@tonic-gate 	 * that don't have NOCLEANUP set.  Perform the necessary accounting.
24540Sstevel@tonic-gate 	 */
24550Sstevel@tonic-gate 	for (proc = proc_table; (proc < proc_table + num_proc); proc++) {
24560Sstevel@tonic-gate 		if ((proc->p_flags & (OCCUPIED|LIVING|NOCLEANUP)) ==
24570Sstevel@tonic-gate 		    (OCCUPIED)) {
24580Sstevel@tonic-gate 			/*
24590Sstevel@tonic-gate 			 * Is this a named process?
24600Sstevel@tonic-gate 			 * If so, do the necessary bookkeeping.
24610Sstevel@tonic-gate 			 */
24620Sstevel@tonic-gate 			if (proc->p_flags & NAMED)
24630Sstevel@tonic-gate 				(void) account(DEAD_PROCESS, proc, NULL);
24640Sstevel@tonic-gate 
24650Sstevel@tonic-gate 			/*
24660Sstevel@tonic-gate 			 * Free this entry for new usage.
24670Sstevel@tonic-gate 			 */
24680Sstevel@tonic-gate 			proc->p_flags = 0;
24690Sstevel@tonic-gate 		}
24700Sstevel@tonic-gate 	}
24710Sstevel@tonic-gate 
24720Sstevel@tonic-gate 	while ((childpid = fork()) == FAILURE) {
24730Sstevel@tonic-gate 		/*
24740Sstevel@tonic-gate 		 * Shorten the alarm timer in case someone else's child dies
24750Sstevel@tonic-gate 		 * and free up a slot in the process table.
24760Sstevel@tonic-gate 		 */
24770Sstevel@tonic-gate 		setimer(5);
24780Sstevel@tonic-gate 
24790Sstevel@tonic-gate 		/*
248010083SRoger.Faulkner@Sun.COM 		 * Wait for some children to die.  Since efork()
248110083SRoger.Faulkner@Sun.COM 		 * is always called with SIGCLD blocked, unblock
248210083SRoger.Faulkner@Sun.COM 		 * it here so that child death signals can come in.
24830Sstevel@tonic-gate 		 */
248410083SRoger.Faulkner@Sun.COM 		(void) sigrelse(SIGCLD);
24850Sstevel@tonic-gate 		(void) pause();
248610083SRoger.Faulkner@Sun.COM 		(void) sighold(SIGCLD);
24870Sstevel@tonic-gate 		setimer(0);
24880Sstevel@tonic-gate 	}
24890Sstevel@tonic-gate 
24900Sstevel@tonic-gate 	if (childpid != 0) {
24910Sstevel@tonic-gate 
24920Sstevel@tonic-gate 		if (process == NULLPROC) {
24930Sstevel@tonic-gate 			/*
24940Sstevel@tonic-gate 			 * No proc table pointer specified so search
24950Sstevel@tonic-gate 			 * for a free slot.
24960Sstevel@tonic-gate 			 */
24970Sstevel@tonic-gate 			for (process = proc_table;  process->p_flags != 0 &&
24988735SEnrico.Perla@Sun.COM 			    (process < proc_table + num_proc); process++)
24990Sstevel@tonic-gate 					;
25000Sstevel@tonic-gate 
25010Sstevel@tonic-gate 			if (process == (proc_table + num_proc)) {
25020Sstevel@tonic-gate 				int old_proc_table_size = num_proc;
25030Sstevel@tonic-gate 
25040Sstevel@tonic-gate 				/* Increase the process table size */
25050Sstevel@tonic-gate 				increase_proc_table_size();
25060Sstevel@tonic-gate 				if (old_proc_table_size == num_proc) {
25070Sstevel@tonic-gate 					/* didn't grow: memory failure */
25080Sstevel@tonic-gate 					return (NO_ROOM);
25090Sstevel@tonic-gate 				} else {
25100Sstevel@tonic-gate 					process =
25110Sstevel@tonic-gate 					    proc_table + old_proc_table_size;
25120Sstevel@tonic-gate 				}
25130Sstevel@tonic-gate 			}
25140Sstevel@tonic-gate 
25150Sstevel@tonic-gate 			process->p_time = 0L;
25160Sstevel@tonic-gate 			process->p_count = 0;
25170Sstevel@tonic-gate 		}
25180Sstevel@tonic-gate 		process->p_id[0] = '\0';
25190Sstevel@tonic-gate 		process->p_id[1] = '\0';
25200Sstevel@tonic-gate 		process->p_id[2] = '\0';
25210Sstevel@tonic-gate 		process->p_id[3] = '\0';
25220Sstevel@tonic-gate 		process->p_pid = childpid;
25230Sstevel@tonic-gate 		process->p_flags = (LIVING | OCCUPIED | modes);
25240Sstevel@tonic-gate 		process->p_exit = 0;
25250Sstevel@tonic-gate 
25260Sstevel@tonic-gate 		st_write();
25270Sstevel@tonic-gate 	} else {
25280Sstevel@tonic-gate 		if ((action & (M_WAIT | M_BOOTWAIT)) == 0)
25290Sstevel@tonic-gate 			(void) setpgrp();
25300Sstevel@tonic-gate 
25310Sstevel@tonic-gate 		process = NULLPROC;
25320Sstevel@tonic-gate 
25330Sstevel@tonic-gate 		/*
25340Sstevel@tonic-gate 		 * Reset all signals to the system defaults.
25350Sstevel@tonic-gate 		 */
25360Sstevel@tonic-gate 		for (i = SIGHUP; i <= SIGRTMAX; i++)
25370Sstevel@tonic-gate 			(void) sigset(i, SIG_DFL);
25380Sstevel@tonic-gate 
25390Sstevel@tonic-gate 		/*
25400Sstevel@tonic-gate 		 * POSIX B.2.2.2 advises that init should set SIGTTOU,
25410Sstevel@tonic-gate 		 * SIGTTIN, and SIGTSTP to SIG_IGN.
25420Sstevel@tonic-gate 		 *
25430Sstevel@tonic-gate 		 * Make sure that SIGXCPU and SIGXFSZ also remain ignored,
25440Sstevel@tonic-gate 		 * for backward compatibility.
25450Sstevel@tonic-gate 		 */
25460Sstevel@tonic-gate 		(void) sigset(SIGTTIN, SIG_IGN);
25470Sstevel@tonic-gate 		(void) sigset(SIGTTOU, SIG_IGN);
25480Sstevel@tonic-gate 		(void) sigset(SIGTSTP, SIG_IGN);
25490Sstevel@tonic-gate 		(void) sigset(SIGXCPU, SIG_IGN);
25500Sstevel@tonic-gate 		(void) sigset(SIGXFSZ, SIG_IGN);
25510Sstevel@tonic-gate 	}
25520Sstevel@tonic-gate 	return (process);
25530Sstevel@tonic-gate }
25540Sstevel@tonic-gate 
25550Sstevel@tonic-gate 
25560Sstevel@tonic-gate /*
25570Sstevel@tonic-gate  * waitproc() waits for a specified process to die.  For this function to
25580Sstevel@tonic-gate  * work, the specified process must already in the proc_table.  waitproc()
25590Sstevel@tonic-gate  * returns the exit status of the specified process when it dies.
25600Sstevel@tonic-gate  */
25610Sstevel@tonic-gate static long
waitproc(struct PROC_TABLE * process)25620Sstevel@tonic-gate waitproc(struct PROC_TABLE *process)
25630Sstevel@tonic-gate {
25640Sstevel@tonic-gate 	int		answer;
25650Sstevel@tonic-gate 	sigset_t	oldmask, newmask, zeromask;
25660Sstevel@tonic-gate 
25670Sstevel@tonic-gate 	(void) sigemptyset(&zeromask);
25680Sstevel@tonic-gate 	(void) sigemptyset(&newmask);
25690Sstevel@tonic-gate 
25700Sstevel@tonic-gate 	(void) sigaddset(&newmask, SIGCLD);
25710Sstevel@tonic-gate 
25720Sstevel@tonic-gate 	/* Block SIGCLD and save the current signal mask */
25730Sstevel@tonic-gate 	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
25740Sstevel@tonic-gate 		perror("SIG_BLOCK error");
25750Sstevel@tonic-gate 
25760Sstevel@tonic-gate 	/*
25770Sstevel@tonic-gate 	 * Wait around until the process dies.
25780Sstevel@tonic-gate 	 */
25790Sstevel@tonic-gate 	if (process->p_flags & LIVING)
25800Sstevel@tonic-gate 		(void) sigsuspend(&zeromask);
25810Sstevel@tonic-gate 
25820Sstevel@tonic-gate 	/* Reset signal mask to unblock SIGCLD */
25830Sstevel@tonic-gate 	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
25840Sstevel@tonic-gate 		perror("SIG_SETMASK error");
25850Sstevel@tonic-gate 
25860Sstevel@tonic-gate 	if (process->p_flags & LIVING)
25870Sstevel@tonic-gate 		return (FAILURE);
25880Sstevel@tonic-gate 
25890Sstevel@tonic-gate 	/*
25900Sstevel@tonic-gate 	 * Make sure to only return 16 bits so that answer will always
25910Sstevel@tonic-gate 	 * be positive whenever the process of interest really died.
25920Sstevel@tonic-gate 	 */
25930Sstevel@tonic-gate 	answer = (process->p_exit & 0xffff);
25940Sstevel@tonic-gate 
25950Sstevel@tonic-gate 	/*
25960Sstevel@tonic-gate 	 * Free the slot in the proc_table.
25970Sstevel@tonic-gate 	 */
25980Sstevel@tonic-gate 	process->p_flags = 0;
25990Sstevel@tonic-gate 	return (answer);
26000Sstevel@tonic-gate }
26010Sstevel@tonic-gate 
26020Sstevel@tonic-gate /*
26030Sstevel@tonic-gate  * notify_pam_dead(): calls into the PAM framework to close the given session.
26040Sstevel@tonic-gate  */
26050Sstevel@tonic-gate static void
notify_pam_dead(struct utmpx * up)26060Sstevel@tonic-gate notify_pam_dead(struct utmpx *up)
26070Sstevel@tonic-gate {
26080Sstevel@tonic-gate 	pam_handle_t *pamh;
26090Sstevel@tonic-gate 	char user[sizeof (up->ut_user) + 1];
26100Sstevel@tonic-gate 	char ttyn[sizeof (up->ut_line) + 1];
26110Sstevel@tonic-gate 	char host[sizeof (up->ut_host) + 1];
26120Sstevel@tonic-gate 
26130Sstevel@tonic-gate 	/*
26140Sstevel@tonic-gate 	 * PAM does not take care of updating utmpx/wtmpx.
26150Sstevel@tonic-gate 	 */
26160Sstevel@tonic-gate 	(void) snprintf(user, sizeof (user), "%s", up->ut_user);
26170Sstevel@tonic-gate 	(void) snprintf(ttyn, sizeof (ttyn), "%s", up->ut_line);
26180Sstevel@tonic-gate 	(void) snprintf(host, sizeof (host), "%s", up->ut_host);
26190Sstevel@tonic-gate 
26200Sstevel@tonic-gate 	if (pam_start("init", user, NULL, &pamh) == PAM_SUCCESS)  {
26210Sstevel@tonic-gate 		(void) pam_set_item(pamh, PAM_TTY, ttyn);
26220Sstevel@tonic-gate 		(void) pam_set_item(pamh, PAM_RHOST, host);
26230Sstevel@tonic-gate 		(void) pam_close_session(pamh, 0);
26240Sstevel@tonic-gate 		(void) pam_end(pamh, PAM_SUCCESS);
26250Sstevel@tonic-gate 	}
26260Sstevel@tonic-gate }
26270Sstevel@tonic-gate 
26280Sstevel@tonic-gate /*
26290Sstevel@tonic-gate  * Check you can access utmpx (As / may be read-only and
26300Sstevel@tonic-gate  * /var may not be mounted yet).
26310Sstevel@tonic-gate  */
26320Sstevel@tonic-gate static int
access_utmpx(void)26330Sstevel@tonic-gate access_utmpx(void)
26340Sstevel@tonic-gate {
26350Sstevel@tonic-gate 	do {
26360Sstevel@tonic-gate 		utmpx_ok = (access(UTMPX, R_OK|W_OK) == 0);
26370Sstevel@tonic-gate 	} while (!utmpx_ok && errno == EINTR);
26380Sstevel@tonic-gate 
26390Sstevel@tonic-gate 	return (utmpx_ok);
26400Sstevel@tonic-gate }
26410Sstevel@tonic-gate 
26420Sstevel@tonic-gate /*
26430Sstevel@tonic-gate  * account() updates entries in utmpx and appends new entries to the end of
26440Sstevel@tonic-gate  * wtmpx (assuming they exist).  The program argument indicates the name of
26450Sstevel@tonic-gate  * program if INIT_PROCESS, otherwise should be NULL.
26460Sstevel@tonic-gate  *
26470Sstevel@tonic-gate  * account() only blocks for INIT_PROCESS requests.
26480Sstevel@tonic-gate  *
26490Sstevel@tonic-gate  * Returns non-zero if write failed.
26500Sstevel@tonic-gate  */
26510Sstevel@tonic-gate static int
account(short state,struct PROC_TABLE * process,char * program)26520Sstevel@tonic-gate account(short state, struct PROC_TABLE *process, char *program)
26530Sstevel@tonic-gate {
26540Sstevel@tonic-gate 	struct utmpx utmpbuf, *u, *oldu;
26550Sstevel@tonic-gate 	int tmplen;
26560Sstevel@tonic-gate 	char fail_buf[UT_LINE_SZ];
26570Sstevel@tonic-gate 	sigset_t block, unblock;
26580Sstevel@tonic-gate 
26590Sstevel@tonic-gate 	if (!utmpx_ok && !access_utmpx()) {
26600Sstevel@tonic-gate 		return (-1);
26610Sstevel@tonic-gate 	}
26620Sstevel@tonic-gate 
26630Sstevel@tonic-gate 	/*
26640Sstevel@tonic-gate 	 * Set up the prototype for the utmp structure we want to write.
26650Sstevel@tonic-gate 	 */
26660Sstevel@tonic-gate 	u = &utmpbuf;
26670Sstevel@tonic-gate 	(void) memset(u, 0, sizeof (struct utmpx));
26680Sstevel@tonic-gate 
26690Sstevel@tonic-gate 	/*
26700Sstevel@tonic-gate 	 * Fill in the various fields of the utmp structure.
26710Sstevel@tonic-gate 	 */
26720Sstevel@tonic-gate 	u->ut_id[0] = process->p_id[0];
26730Sstevel@tonic-gate 	u->ut_id[1] = process->p_id[1];
26740Sstevel@tonic-gate 	u->ut_id[2] = process->p_id[2];
26750Sstevel@tonic-gate 	u->ut_id[3] = process->p_id[3];
26760Sstevel@tonic-gate 	u->ut_pid = process->p_pid;
26770Sstevel@tonic-gate 
26780Sstevel@tonic-gate 	/*
26790Sstevel@tonic-gate 	 * Fill the "ut_exit" structure.
26800Sstevel@tonic-gate 	 */
26810Sstevel@tonic-gate 	u->ut_exit.e_termination = WTERMSIG(process->p_exit);
26820Sstevel@tonic-gate 	u->ut_exit.e_exit = WEXITSTATUS(process->p_exit);
26830Sstevel@tonic-gate 	u->ut_type = state;
26840Sstevel@tonic-gate 
26850Sstevel@tonic-gate 	(void) time(&u->ut_tv.tv_sec);
26860Sstevel@tonic-gate 
26870Sstevel@tonic-gate 	/*
26880Sstevel@tonic-gate 	 * Block signals for utmp update.
26890Sstevel@tonic-gate 	 */
26900Sstevel@tonic-gate 	(void) sigfillset(&block);
26910Sstevel@tonic-gate 	(void) sigprocmask(SIG_BLOCK, &block, &unblock);
26920Sstevel@tonic-gate 
26930Sstevel@tonic-gate 	/*
26940Sstevel@tonic-gate 	 * See if there already is such an entry in the "utmpx" file.
26950Sstevel@tonic-gate 	 */
26960Sstevel@tonic-gate 	setutxent();	/* Start at beginning of utmpx file. */
26970Sstevel@tonic-gate 
26980Sstevel@tonic-gate 	if ((oldu = getutxid(u)) != NULL) {
26990Sstevel@tonic-gate 		/*
27000Sstevel@tonic-gate 		 * Copy in the old "user", "line" and "host" fields
27010Sstevel@tonic-gate 		 * to our new structure.
27020Sstevel@tonic-gate 		 */
27030Sstevel@tonic-gate 		bcopy(oldu->ut_user, u->ut_user, sizeof (u->ut_user));
27040Sstevel@tonic-gate 		bcopy(oldu->ut_line, u->ut_line, sizeof (u->ut_line));
27050Sstevel@tonic-gate 		bcopy(oldu->ut_host, u->ut_host, sizeof (u->ut_host));
27060Sstevel@tonic-gate 		u->ut_syslen = (tmplen = strlen(u->ut_host)) ?
27078735SEnrico.Perla@Sun.COM 		    min(tmplen + 1, sizeof (u->ut_host)) : 0;
27080Sstevel@tonic-gate 
27090Sstevel@tonic-gate 		if (oldu->ut_type == USER_PROCESS && state == DEAD_PROCESS) {
27100Sstevel@tonic-gate 			notify_pam_dead(oldu);
27110Sstevel@tonic-gate 		}
27120Sstevel@tonic-gate 	}
27130Sstevel@tonic-gate 
27140Sstevel@tonic-gate 	/*
27150Sstevel@tonic-gate 	 * Perform special accounting. Insert the special string into the
27160Sstevel@tonic-gate 	 * ut_line array. For INIT_PROCESSes put in the name of the
27170Sstevel@tonic-gate 	 * program in the "ut_user" field.
27180Sstevel@tonic-gate 	 */
27190Sstevel@tonic-gate 	switch (state) {
27200Sstevel@tonic-gate 	case INIT_PROCESS:
27210Sstevel@tonic-gate 		(void) strncpy(u->ut_user, program, sizeof (u->ut_user));
27220Sstevel@tonic-gate 		(void) strcpy(fail_buf, "INIT_PROCESS");
27230Sstevel@tonic-gate 		break;
27240Sstevel@tonic-gate 
27250Sstevel@tonic-gate 	default:
27260Sstevel@tonic-gate 		(void) strlcpy(fail_buf, u->ut_id, sizeof (u->ut_id) + 1);
27270Sstevel@tonic-gate 		break;
27280Sstevel@tonic-gate 	}
27290Sstevel@tonic-gate 
27300Sstevel@tonic-gate 	/*
27310Sstevel@tonic-gate 	 * Write out the updated entry to utmpx file.
27320Sstevel@tonic-gate 	 */
27330Sstevel@tonic-gate 	if (pututxline(u) == NULL) {
27340Sstevel@tonic-gate 		console(B_TRUE, "Failed write of utmpx entry: \"%s\": %s\n",
27350Sstevel@tonic-gate 		    fail_buf, strerror(errno));
27360Sstevel@tonic-gate 		endutxent();
27370Sstevel@tonic-gate 		(void) sigprocmask(SIG_SETMASK, &unblock, NULL);
27380Sstevel@tonic-gate 		return (-1);
27390Sstevel@tonic-gate 	}
27400Sstevel@tonic-gate 
27410Sstevel@tonic-gate 	/*
27420Sstevel@tonic-gate 	 * If we're able to write to utmpx, then attempt to add to the
27430Sstevel@tonic-gate 	 * end of the wtmpx file.
27440Sstevel@tonic-gate 	 */
27450Sstevel@tonic-gate 	updwtmpx(WTMPX, u);
27460Sstevel@tonic-gate 
27470Sstevel@tonic-gate 	endutxent();
27480Sstevel@tonic-gate 
27490Sstevel@tonic-gate 	(void) sigprocmask(SIG_SETMASK, &unblock, NULL);
27500Sstevel@tonic-gate 
27510Sstevel@tonic-gate 	return (0);
27520Sstevel@tonic-gate }
27530Sstevel@tonic-gate 
27540Sstevel@tonic-gate static void
clearent(pid_t pid,short status)27550Sstevel@tonic-gate clearent(pid_t pid, short status)
27560Sstevel@tonic-gate {
27570Sstevel@tonic-gate 	struct utmpx *up;
27580Sstevel@tonic-gate 	sigset_t block, unblock;
27590Sstevel@tonic-gate 
27600Sstevel@tonic-gate 	/*
27610Sstevel@tonic-gate 	 * Block signals for utmp update.
27620Sstevel@tonic-gate 	 */
27630Sstevel@tonic-gate 	(void) sigfillset(&block);
27640Sstevel@tonic-gate 	(void) sigprocmask(SIG_BLOCK, &block, &unblock);
27650Sstevel@tonic-gate 
27660Sstevel@tonic-gate 	/*
27670Sstevel@tonic-gate 	 * No error checking for now.
27680Sstevel@tonic-gate 	 */
27690Sstevel@tonic-gate 
27700Sstevel@tonic-gate 	setutxent();
27710Sstevel@tonic-gate 	while (up = getutxent()) {
27720Sstevel@tonic-gate 		if (up->ut_pid == pid) {
27730Sstevel@tonic-gate 			if (up->ut_type == DEAD_PROCESS) {
27740Sstevel@tonic-gate 				/*
27750Sstevel@tonic-gate 				 * Cleaned up elsewhere.
27760Sstevel@tonic-gate 				 */
27770Sstevel@tonic-gate 				continue;
27780Sstevel@tonic-gate 			}
27790Sstevel@tonic-gate 
27800Sstevel@tonic-gate 			notify_pam_dead(up);
27810Sstevel@tonic-gate 
27820Sstevel@tonic-gate 			up->ut_type = DEAD_PROCESS;
27830Sstevel@tonic-gate 			up->ut_exit.e_termination = WTERMSIG(status);
27840Sstevel@tonic-gate 			up->ut_exit.e_exit = WEXITSTATUS(status);
27850Sstevel@tonic-gate 			(void) time(&up->ut_tv.tv_sec);
27860Sstevel@tonic-gate 
27870Sstevel@tonic-gate 			(void) pututxline(up);
27880Sstevel@tonic-gate 			/*
27890Sstevel@tonic-gate 			 * Now attempt to add to the end of the
27900Sstevel@tonic-gate 			 * wtmp and wtmpx files.  Do not create
27910Sstevel@tonic-gate 			 * if they don't already exist.
27920Sstevel@tonic-gate 			 */
27930Sstevel@tonic-gate 			updwtmpx(WTMPX, up);
27940Sstevel@tonic-gate 
27950Sstevel@tonic-gate 			break;
27960Sstevel@tonic-gate 		}
27970Sstevel@tonic-gate 	}
27980Sstevel@tonic-gate 
27990Sstevel@tonic-gate 	endutxent();
28000Sstevel@tonic-gate 	(void) sigprocmask(SIG_SETMASK, &unblock, NULL);
28010Sstevel@tonic-gate }
28020Sstevel@tonic-gate 
28030Sstevel@tonic-gate /*
28040Sstevel@tonic-gate  * prog_name() searches for the word or unix path name and
28050Sstevel@tonic-gate  * returns a pointer to the last element of the pathname.
28060Sstevel@tonic-gate  */
28070Sstevel@tonic-gate static char *
prog_name(char * string)28080Sstevel@tonic-gate prog_name(char *string)
28090Sstevel@tonic-gate {
28100Sstevel@tonic-gate 	char	*ptr, *ptr2;
28110Sstevel@tonic-gate 	/* XXX - utmp - fix name length */
28120Sstevel@tonic-gate 	static char word[_POSIX_LOGIN_NAME_MAX];
28130Sstevel@tonic-gate 
28140Sstevel@tonic-gate 	/*
28150Sstevel@tonic-gate 	 * Search for the first word skipping leading spaces and tabs.
28160Sstevel@tonic-gate 	 */
28170Sstevel@tonic-gate 	while (*string == ' ' || *string == '\t')
28180Sstevel@tonic-gate 		string++;
28190Sstevel@tonic-gate 
28200Sstevel@tonic-gate 	/*
28210Sstevel@tonic-gate 	 * If the first non-space non-tab character is not one allowed in
28220Sstevel@tonic-gate 	 * a word, return a pointer to a null string, otherwise parse the
28230Sstevel@tonic-gate 	 * pathname.
28240Sstevel@tonic-gate 	 */
28250Sstevel@tonic-gate 	if (*string != '.' && *string != '/' && *string != '_' &&
28260Sstevel@tonic-gate 	    (*string < 'a' || *string > 'z') &&
28270Sstevel@tonic-gate 	    (*string < 'A' || * string > 'Z') &&
28280Sstevel@tonic-gate 	    (*string < '0' || *string > '9'))
28290Sstevel@tonic-gate 		return ("");
28300Sstevel@tonic-gate 
28310Sstevel@tonic-gate 	/*
28320Sstevel@tonic-gate 	 * Parse the pathname looking forward for '/', ' ', '\t', '\n' or
28330Sstevel@tonic-gate 	 * '\0'.  Each time a '/' is found, move "ptr" to one past the
28340Sstevel@tonic-gate 	 * '/', thus when a ' ', '\t', '\n', or '\0' is found, "ptr" will
28350Sstevel@tonic-gate 	 * point to the last element of the pathname.
28360Sstevel@tonic-gate 	 */
283710083SRoger.Faulkner@Sun.COM 	for (ptr = string; *string != ' ' && *string != '\t' &&
28388735SEnrico.Perla@Sun.COM 	    *string != '\n' && *string != '\0'; string++) {
28390Sstevel@tonic-gate 		if (*string == '/')
28400Sstevel@tonic-gate 			ptr = string+1;
28410Sstevel@tonic-gate 	}
28420Sstevel@tonic-gate 
28430Sstevel@tonic-gate 	/*
28440Sstevel@tonic-gate 	 * Copy out up to the size of the "ut_user" array into "word",
28450Sstevel@tonic-gate 	 * null terminate it and return a pointer to it.
28460Sstevel@tonic-gate 	 */
28470Sstevel@tonic-gate 	/* XXX - utmp - fix name length */
28480Sstevel@tonic-gate 	for (ptr2 = &word[0]; ptr2 < &word[_POSIX_LOGIN_NAME_MAX - 1] &&
28490Sstevel@tonic-gate 	    ptr < string; /* CSTYLED */)
28500Sstevel@tonic-gate 		*ptr2++ = *ptr++;
28510Sstevel@tonic-gate 
28520Sstevel@tonic-gate 	*ptr2 = '\0';
28530Sstevel@tonic-gate 	return (&word[0]);
28540Sstevel@tonic-gate }
28550Sstevel@tonic-gate 
28560Sstevel@tonic-gate 
28570Sstevel@tonic-gate /*
28580Sstevel@tonic-gate  * realcon() returns a nonzero value if there is a character device
28590Sstevel@tonic-gate  * associated with SYSCON that has the same device number as CONSOLE.
28600Sstevel@tonic-gate  */
28610Sstevel@tonic-gate static int
realcon()28620Sstevel@tonic-gate realcon()
28630Sstevel@tonic-gate {
28640Sstevel@tonic-gate 	struct stat sconbuf, conbuf;
28650Sstevel@tonic-gate 
28660Sstevel@tonic-gate 	if (stat(SYSCON, &sconbuf) != -1 &&
28670Sstevel@tonic-gate 	    stat(CONSOLE, &conbuf) != -1 &&
2868871Scasper 	    S_ISCHR(sconbuf.st_mode) &&
2869871Scasper 	    S_ISCHR(conbuf.st_mode) &&
28700Sstevel@tonic-gate 	    sconbuf.st_rdev == conbuf.st_rdev) {
28710Sstevel@tonic-gate 		return (1);
28720Sstevel@tonic-gate 	} else {
28730Sstevel@tonic-gate 		return (0);
28740Sstevel@tonic-gate 	}
28750Sstevel@tonic-gate }
28760Sstevel@tonic-gate 
28770Sstevel@tonic-gate 
28780Sstevel@tonic-gate /*
28790Sstevel@tonic-gate  * get_ioctl_syscon() retrieves the SYSCON settings from the IOCTLSYSCON file.
28800Sstevel@tonic-gate  * Returns true if the IOCTLSYSCON file needs to be written (with
28810Sstevel@tonic-gate  * write_ioctl_syscon() below)
28820Sstevel@tonic-gate  */
28830Sstevel@tonic-gate static int
get_ioctl_syscon()28840Sstevel@tonic-gate get_ioctl_syscon()
28850Sstevel@tonic-gate {
28860Sstevel@tonic-gate 	FILE	*fp;
28870Sstevel@tonic-gate 	unsigned int	iflags, oflags, cflags, lflags, ldisc, cc[18];
28880Sstevel@tonic-gate 	int		i, valid_format = 0;
28890Sstevel@tonic-gate 
28900Sstevel@tonic-gate 	/*
28910Sstevel@tonic-gate 	 * Read in the previous modes for SYSCON from IOCTLSYSCON.
28920Sstevel@tonic-gate 	 */
28930Sstevel@tonic-gate 	if ((fp = fopen(IOCTLSYSCON, "r")) == NULL) {
28940Sstevel@tonic-gate 		stored_syscon_termios = dflt_termios;
28950Sstevel@tonic-gate 		console(B_TRUE,
28960Sstevel@tonic-gate 		    "warning:%s does not exist, default settings assumed\n",
28970Sstevel@tonic-gate 		    IOCTLSYSCON);
28980Sstevel@tonic-gate 	} else {
28990Sstevel@tonic-gate 
29000Sstevel@tonic-gate 	    i = fscanf(fp,
29010Sstevel@tonic-gate 	    "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x",
29020Sstevel@tonic-gate 		&iflags, &oflags, &cflags, &lflags,
29030Sstevel@tonic-gate 		&cc[0], &cc[1], &cc[2], &cc[3], &cc[4], &cc[5], &cc[6],
29040Sstevel@tonic-gate 		&cc[7], &cc[8], &cc[9], &cc[10], &cc[11], &cc[12], &cc[13],
29050Sstevel@tonic-gate 		&cc[14], &cc[15], &cc[16], &cc[17]);
29060Sstevel@tonic-gate 
29070Sstevel@tonic-gate 	    if (i == 22) {
29080Sstevel@tonic-gate 		stored_syscon_termios.c_iflag = iflags;
29090Sstevel@tonic-gate 		stored_syscon_termios.c_oflag = oflags;
29100Sstevel@tonic-gate 		stored_syscon_termios.c_cflag = cflags;
29110Sstevel@tonic-gate 		stored_syscon_termios.c_lflag = lflags;
29120Sstevel@tonic-gate 		for (i = 0; i < 18; i++)
29130Sstevel@tonic-gate 			stored_syscon_termios.c_cc[i] = (char)cc[i];
29140Sstevel@tonic-gate 		valid_format = 1;
29150Sstevel@tonic-gate 	    } else if (i == 13) {
29160Sstevel@tonic-gate 		rewind(fp);
29170Sstevel@tonic-gate 		i = fscanf(fp, "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x",
29180Sstevel@tonic-gate 		    &iflags, &oflags, &cflags, &lflags, &ldisc, &cc[0], &cc[1],
29190Sstevel@tonic-gate 		    &cc[2], &cc[3], &cc[4], &cc[5], &cc[6], &cc[7]);
29200Sstevel@tonic-gate 
29210Sstevel@tonic-gate 		/*
29220Sstevel@tonic-gate 		 * If the file is formatted properly, use the values to
29230Sstevel@tonic-gate 		 * initialize the console terminal condition.
29240Sstevel@tonic-gate 		 */
29250Sstevel@tonic-gate 		stored_syscon_termios.c_iflag = (ushort_t)iflags;
29260Sstevel@tonic-gate 		stored_syscon_termios.c_oflag = (ushort_t)oflags;
29270Sstevel@tonic-gate 		stored_syscon_termios.c_cflag = (ushort_t)cflags;
29280Sstevel@tonic-gate 		stored_syscon_termios.c_lflag = (ushort_t)lflags;
29290Sstevel@tonic-gate 		for (i = 0; i < 8; i++)
29300Sstevel@tonic-gate 			stored_syscon_termios.c_cc[i] = (char)cc[i];
29310Sstevel@tonic-gate 		valid_format = 1;
29320Sstevel@tonic-gate 	    }
29330Sstevel@tonic-gate 	    (void) fclose(fp);
29340Sstevel@tonic-gate 
29350Sstevel@tonic-gate 	    /* If the file is badly formatted, use the default settings. */
29360Sstevel@tonic-gate 	    if (!valid_format)
29370Sstevel@tonic-gate 		stored_syscon_termios = dflt_termios;
29380Sstevel@tonic-gate 	}
29390Sstevel@tonic-gate 
29400Sstevel@tonic-gate 	/* If the file had a bad format, rewrite it later. */
29410Sstevel@tonic-gate 	return (!valid_format);
29420Sstevel@tonic-gate }
29430Sstevel@tonic-gate 
29440Sstevel@tonic-gate 
29450Sstevel@tonic-gate static void
write_ioctl_syscon()29460Sstevel@tonic-gate write_ioctl_syscon()
29470Sstevel@tonic-gate {
29480Sstevel@tonic-gate 	FILE *fp;
29490Sstevel@tonic-gate 	int i;
29500Sstevel@tonic-gate 
29510Sstevel@tonic-gate 	(void) unlink(SYSCON);
29520Sstevel@tonic-gate 	(void) link(SYSTTY, SYSCON);
29530Sstevel@tonic-gate 	(void) umask(022);
29540Sstevel@tonic-gate 	fp = fopen(IOCTLSYSCON, "w");
29550Sstevel@tonic-gate 
29560Sstevel@tonic-gate 	(void) fprintf(fp, "%x:%x:%x:%x:0", stored_syscon_termios.c_iflag,
29570Sstevel@tonic-gate 	    stored_syscon_termios.c_oflag, stored_syscon_termios.c_cflag,
29580Sstevel@tonic-gate 	    stored_syscon_termios.c_lflag);
29590Sstevel@tonic-gate 	for (i = 0; i < 8; ++i)
29600Sstevel@tonic-gate 		(void) fprintf(fp, ":%x", stored_syscon_termios.c_cc[i]);
29610Sstevel@tonic-gate 	(void) putc('\n', fp);
29620Sstevel@tonic-gate 
29630Sstevel@tonic-gate 	(void) fflush(fp);
29640Sstevel@tonic-gate 	(void) fsync(fileno(fp));
29650Sstevel@tonic-gate 	(void) fclose(fp);
29660Sstevel@tonic-gate 	(void) umask(cmask);
29670Sstevel@tonic-gate }
29680Sstevel@tonic-gate 
29690Sstevel@tonic-gate 
29700Sstevel@tonic-gate /*
29710Sstevel@tonic-gate  * void console(boolean_t, char *, ...)
29720Sstevel@tonic-gate  *   Outputs the requested message to the system console.  Note that the number
29730Sstevel@tonic-gate  *   of arguments passed to console() should be determined by the print format.
29740Sstevel@tonic-gate  *
29750Sstevel@tonic-gate  *   The "prefix" parameter indicates whether or not "INIT: " should precede the
29760Sstevel@tonic-gate  *   message.
29770Sstevel@tonic-gate  *
29780Sstevel@tonic-gate  *   To make sure we write to the console in a sane fashion, we use the modes
29790Sstevel@tonic-gate  *   we keep in stored_syscon_termios (which we read out of /etc/ioctl.syscon).
29800Sstevel@tonic-gate  *   Afterwards we restore whatever modes were already there.
29810Sstevel@tonic-gate  */
29820Sstevel@tonic-gate /* PRINTFLIKE2 */
29830Sstevel@tonic-gate static void
console(boolean_t prefix,char * format,...)29840Sstevel@tonic-gate console(boolean_t prefix, char *format, ...)
29850Sstevel@tonic-gate {
29860Sstevel@tonic-gate 	char	outbuf[BUFSIZ];
29870Sstevel@tonic-gate 	va_list	args;
29880Sstevel@tonic-gate 	int fd, getret;
29890Sstevel@tonic-gate 	struct termios old_syscon_termios;
29900Sstevel@tonic-gate 	FILE *f;
29910Sstevel@tonic-gate 
29920Sstevel@tonic-gate 	/*
29930Sstevel@tonic-gate 	 * We open SYSCON anew each time in case it has changed (see
29940Sstevel@tonic-gate 	 * userinit()).
29950Sstevel@tonic-gate 	 */
29960Sstevel@tonic-gate 	if ((fd = open(SYSCON, O_RDWR | O_NOCTTY)) < 0 ||
29970Sstevel@tonic-gate 	    (f = fdopen(fd, "r+")) == NULL) {
29980Sstevel@tonic-gate 		if (prefix)
29990Sstevel@tonic-gate 			syslog(LOG_WARNING, "INIT: ");
30000Sstevel@tonic-gate 		va_start(args, format);
30010Sstevel@tonic-gate 		vsyslog(LOG_WARNING, format, args);
30020Sstevel@tonic-gate 		va_end(args);
30030Sstevel@tonic-gate 		if (fd >= 0)
30040Sstevel@tonic-gate 			(void) close(fd);
30050Sstevel@tonic-gate 		return;
30060Sstevel@tonic-gate 	}
30070Sstevel@tonic-gate 	setbuf(f, &outbuf[0]);
30080Sstevel@tonic-gate 
30090Sstevel@tonic-gate 	getret = tcgetattr(fd, &old_syscon_termios);
30100Sstevel@tonic-gate 	old_syscon_termios.c_cflag &= ~HUPCL;
30110Sstevel@tonic-gate 	if (realcon())
30120Sstevel@tonic-gate 		/* Don't overwrite cflag of real console. */
30130Sstevel@tonic-gate 		stored_syscon_termios.c_cflag = old_syscon_termios.c_cflag;
30140Sstevel@tonic-gate 
30150Sstevel@tonic-gate 	stored_syscon_termios.c_cflag &= ~HUPCL;
30160Sstevel@tonic-gate 
30170Sstevel@tonic-gate 	(void) tcsetattr(fd, TCSANOW, &stored_syscon_termios);
30180Sstevel@tonic-gate 
30190Sstevel@tonic-gate 	if (prefix)
30200Sstevel@tonic-gate 		(void) fprintf(f, "\nINIT: ");
30210Sstevel@tonic-gate 	va_start(args, format);
30220Sstevel@tonic-gate 	(void) vfprintf(f, format, args);
30230Sstevel@tonic-gate 	va_end(args);
30240Sstevel@tonic-gate 
30250Sstevel@tonic-gate 	if (getret == 0)
30260Sstevel@tonic-gate 		(void) tcsetattr(fd, TCSADRAIN, &old_syscon_termios);
30270Sstevel@tonic-gate 
30280Sstevel@tonic-gate 	(void) fclose(f);
30290Sstevel@tonic-gate }
30300Sstevel@tonic-gate 
30310Sstevel@tonic-gate /*
30320Sstevel@tonic-gate  * timer() is a substitute for sleep() which uses alarm() and pause().
30330Sstevel@tonic-gate  */
30340Sstevel@tonic-gate static void
timer(int waitime)30350Sstevel@tonic-gate timer(int waitime)
30360Sstevel@tonic-gate {
30370Sstevel@tonic-gate 	setimer(waitime);
30380Sstevel@tonic-gate 	while (time_up == FALSE)
30390Sstevel@tonic-gate 		(void) pause();
30400Sstevel@tonic-gate }
30410Sstevel@tonic-gate 
30420Sstevel@tonic-gate static void
setimer(int timelimit)30430Sstevel@tonic-gate setimer(int timelimit)
30440Sstevel@tonic-gate {
30450Sstevel@tonic-gate 	alarmclk();
30460Sstevel@tonic-gate 	(void) alarm(timelimit);
30470Sstevel@tonic-gate 	time_up = (timelimit ? FALSE : TRUE);
30480Sstevel@tonic-gate }
30490Sstevel@tonic-gate 
30500Sstevel@tonic-gate /*
30510Sstevel@tonic-gate  * Fails with
30520Sstevel@tonic-gate  *   ENOMEM - out of memory
30530Sstevel@tonic-gate  *   ECONNABORTED - repository connection broken
30540Sstevel@tonic-gate  *   EPERM - permission denied
30550Sstevel@tonic-gate  *   EACCES - backend access denied
30560Sstevel@tonic-gate  *   EROFS - backend readonly
30570Sstevel@tonic-gate  */
30580Sstevel@tonic-gate static int
get_or_add_startd(scf_instance_t * inst)30590Sstevel@tonic-gate get_or_add_startd(scf_instance_t *inst)
30600Sstevel@tonic-gate {
30610Sstevel@tonic-gate 	scf_handle_t *h;
30620Sstevel@tonic-gate 	scf_scope_t *scope = NULL;
30630Sstevel@tonic-gate 	scf_service_t *svc = NULL;
30640Sstevel@tonic-gate 	int ret = 0;
30650Sstevel@tonic-gate 
30660Sstevel@tonic-gate 	h = scf_instance_handle(inst);
30670Sstevel@tonic-gate 
30680Sstevel@tonic-gate 	if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL, inst,
30690Sstevel@tonic-gate 	    NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0)
30700Sstevel@tonic-gate 		return (0);
30710Sstevel@tonic-gate 
30720Sstevel@tonic-gate 	switch (scf_error()) {
30730Sstevel@tonic-gate 	case SCF_ERROR_CONNECTION_BROKEN:
30740Sstevel@tonic-gate 		return (ECONNABORTED);
30750Sstevel@tonic-gate 
30760Sstevel@tonic-gate 	case SCF_ERROR_NOT_FOUND:
30770Sstevel@tonic-gate 		break;
30780Sstevel@tonic-gate 
30790Sstevel@tonic-gate 	case SCF_ERROR_HANDLE_MISMATCH:
30800Sstevel@tonic-gate 	case SCF_ERROR_INVALID_ARGUMENT:
30810Sstevel@tonic-gate 	case SCF_ERROR_CONSTRAINT_VIOLATED:
30820Sstevel@tonic-gate 	default:
30830Sstevel@tonic-gate 		bad_error("scf_handle_decode_fmri", scf_error());
30840Sstevel@tonic-gate 	}
30850Sstevel@tonic-gate 
30860Sstevel@tonic-gate 	/* Make sure we're right, since we're adding piece-by-piece. */
30870Sstevel@tonic-gate 	assert(strcmp(SCF_SERVICE_STARTD,
30880Sstevel@tonic-gate 	    "svc:/system/svc/restarter:default") == 0);
30890Sstevel@tonic-gate 
30900Sstevel@tonic-gate 	if ((scope = scf_scope_create(h)) == NULL ||
30910Sstevel@tonic-gate 	    (svc = scf_service_create(h)) == NULL) {
30920Sstevel@tonic-gate 		ret = ENOMEM;
30930Sstevel@tonic-gate 		goto out;
30940Sstevel@tonic-gate 	}
30950Sstevel@tonic-gate 
30960Sstevel@tonic-gate get_scope:
30970Sstevel@tonic-gate 	if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0) {
30980Sstevel@tonic-gate 		switch (scf_error()) {
30990Sstevel@tonic-gate 		case SCF_ERROR_CONNECTION_BROKEN:
31000Sstevel@tonic-gate 			ret = ECONNABORTED;
31010Sstevel@tonic-gate 			goto out;
31020Sstevel@tonic-gate 
31030Sstevel@tonic-gate 		case SCF_ERROR_NOT_FOUND:
31040Sstevel@tonic-gate 			(void) fputs(gettext(
31050Sstevel@tonic-gate 			    "smf(5) repository missing local scope.\n"),
31060Sstevel@tonic-gate 			    stderr);
31070Sstevel@tonic-gate 			exit(1);
31080Sstevel@tonic-gate 			/* NOTREACHED */
31090Sstevel@tonic-gate 
31100Sstevel@tonic-gate 		case SCF_ERROR_HANDLE_MISMATCH:
31110Sstevel@tonic-gate 		case SCF_ERROR_INVALID_ARGUMENT:
31120Sstevel@tonic-gate 		default:
31130Sstevel@tonic-gate 			bad_error("scf_handle_get_scope", scf_error());
31140Sstevel@tonic-gate 		}
31150Sstevel@tonic-gate 	}
31160Sstevel@tonic-gate 
31170Sstevel@tonic-gate get_svc:
31180Sstevel@tonic-gate 	if (scf_scope_get_service(scope, "system/svc/restarter", svc) != 0) {
31190Sstevel@tonic-gate 		switch (scf_error()) {
31200Sstevel@tonic-gate 		case SCF_ERROR_CONNECTION_BROKEN:
31210Sstevel@tonic-gate 			ret = ECONNABORTED;
31220Sstevel@tonic-gate 			goto out;
31230Sstevel@tonic-gate 
31240Sstevel@tonic-gate 		case SCF_ERROR_DELETED:
31250Sstevel@tonic-gate 			goto get_scope;
31260Sstevel@tonic-gate 
31270Sstevel@tonic-gate 		case SCF_ERROR_NOT_FOUND:
31280Sstevel@tonic-gate 			break;
31290Sstevel@tonic-gate 
31300Sstevel@tonic-gate 		case SCF_ERROR_HANDLE_MISMATCH:
31310Sstevel@tonic-gate 		case SCF_ERROR_INVALID_ARGUMENT:
31320Sstevel@tonic-gate 		case SCF_ERROR_NOT_SET:
31330Sstevel@tonic-gate 		default:
31340Sstevel@tonic-gate 			bad_error("scf_scope_get_service", scf_error());
31350Sstevel@tonic-gate 		}
31360Sstevel@tonic-gate 
31370Sstevel@tonic-gate add_svc:
31380Sstevel@tonic-gate 		if (scf_scope_add_service(scope, "system/svc/restarter", svc) !=
31390Sstevel@tonic-gate 		    0) {
31400Sstevel@tonic-gate 			switch (scf_error()) {
31410Sstevel@tonic-gate 			case SCF_ERROR_CONNECTION_BROKEN:
31420Sstevel@tonic-gate 				ret = ECONNABORTED;
31430Sstevel@tonic-gate 				goto out;
31440Sstevel@tonic-gate 
31450Sstevel@tonic-gate 			case SCF_ERROR_EXISTS:
31460Sstevel@tonic-gate 				goto get_svc;
31470Sstevel@tonic-gate 
31480Sstevel@tonic-gate 			case SCF_ERROR_PERMISSION_DENIED:
31490Sstevel@tonic-gate 				ret = EPERM;
31500Sstevel@tonic-gate 				goto out;
31510Sstevel@tonic-gate 
31520Sstevel@tonic-gate 			case SCF_ERROR_BACKEND_ACCESS:
31530Sstevel@tonic-gate 				ret = EACCES;
31540Sstevel@tonic-gate 				goto out;
31550Sstevel@tonic-gate 
31560Sstevel@tonic-gate 			case SCF_ERROR_BACKEND_READONLY:
31570Sstevel@tonic-gate 				ret = EROFS;
31580Sstevel@tonic-gate 				goto out;
31590Sstevel@tonic-gate 
31600Sstevel@tonic-gate 			case SCF_ERROR_HANDLE_MISMATCH:
31610Sstevel@tonic-gate 			case SCF_ERROR_INVALID_ARGUMENT:
31620Sstevel@tonic-gate 			case SCF_ERROR_NOT_SET:
31630Sstevel@tonic-gate 			default:
31640Sstevel@tonic-gate 				bad_error("scf_scope_add_service", scf_error());
31650Sstevel@tonic-gate 			}
31660Sstevel@tonic-gate 		}
31670Sstevel@tonic-gate 	}
31680Sstevel@tonic-gate 
31690Sstevel@tonic-gate get_inst:
31700Sstevel@tonic-gate 	if (scf_service_get_instance(svc, "default", inst) != 0) {
31710Sstevel@tonic-gate 		switch (scf_error()) {
31720Sstevel@tonic-gate 		case SCF_ERROR_CONNECTION_BROKEN:
31730Sstevel@tonic-gate 			ret = ECONNABORTED;
31740Sstevel@tonic-gate 			goto out;
31750Sstevel@tonic-gate 
31760Sstevel@tonic-gate 		case SCF_ERROR_DELETED:
31770Sstevel@tonic-gate 			goto add_svc;
31780Sstevel@tonic-gate 
31790Sstevel@tonic-gate 		case SCF_ERROR_NOT_FOUND:
31800Sstevel@tonic-gate 			break;
31810Sstevel@tonic-gate 
31820Sstevel@tonic-gate 		case SCF_ERROR_HANDLE_MISMATCH:
31830Sstevel@tonic-gate 		case SCF_ERROR_INVALID_ARGUMENT:
31840Sstevel@tonic-gate 		case SCF_ERROR_NOT_SET:
31850Sstevel@tonic-gate 		default:
31860Sstevel@tonic-gate 			bad_error("scf_service_get_instance", scf_error());
31870Sstevel@tonic-gate 		}
31880Sstevel@tonic-gate 
31890Sstevel@tonic-gate 		if (scf_service_add_instance(svc, "default", inst) !=
31900Sstevel@tonic-gate 		    0) {
31910Sstevel@tonic-gate 			switch (scf_error()) {
31920Sstevel@tonic-gate 			case SCF_ERROR_CONNECTION_BROKEN:
31930Sstevel@tonic-gate 				ret = ECONNABORTED;
31940Sstevel@tonic-gate 				goto out;
31950Sstevel@tonic-gate 
31960Sstevel@tonic-gate 			case SCF_ERROR_DELETED:
31970Sstevel@tonic-gate 				goto add_svc;
31980Sstevel@tonic-gate 
31990Sstevel@tonic-gate 			case SCF_ERROR_EXISTS:
32000Sstevel@tonic-gate 				goto get_inst;
32010Sstevel@tonic-gate 
32020Sstevel@tonic-gate 			case SCF_ERROR_PERMISSION_DENIED:
32030Sstevel@tonic-gate 				ret = EPERM;
32040Sstevel@tonic-gate 				goto out;
32050Sstevel@tonic-gate 
32060Sstevel@tonic-gate 			case SCF_ERROR_BACKEND_ACCESS:
32070Sstevel@tonic-gate 				ret = EACCES;
32080Sstevel@tonic-gate 				goto out;
32090Sstevel@tonic-gate 
32100Sstevel@tonic-gate 			case SCF_ERROR_BACKEND_READONLY:
32110Sstevel@tonic-gate 				ret = EROFS;
32120Sstevel@tonic-gate 				goto out;
32130Sstevel@tonic-gate 
32140Sstevel@tonic-gate 			case SCF_ERROR_HANDLE_MISMATCH:
32150Sstevel@tonic-gate 			case SCF_ERROR_INVALID_ARGUMENT:
32160Sstevel@tonic-gate 			case SCF_ERROR_NOT_SET:
32170Sstevel@tonic-gate 			default:
32180Sstevel@tonic-gate 				bad_error("scf_service_add_instance",
32190Sstevel@tonic-gate 				    scf_error());
32200Sstevel@tonic-gate 			}
32210Sstevel@tonic-gate 		}
32220Sstevel@tonic-gate 	}
32230Sstevel@tonic-gate 
32240Sstevel@tonic-gate 	ret = 0;
32250Sstevel@tonic-gate 
32260Sstevel@tonic-gate out:
32270Sstevel@tonic-gate 	scf_service_destroy(svc);
32280Sstevel@tonic-gate 	scf_scope_destroy(scope);
32290Sstevel@tonic-gate 	return (ret);
32300Sstevel@tonic-gate }
32310Sstevel@tonic-gate 
32320Sstevel@tonic-gate /*
32330Sstevel@tonic-gate  * Fails with
32340Sstevel@tonic-gate  *   ECONNABORTED - repository connection broken
32350Sstevel@tonic-gate  *   ECANCELED - the transaction's property group was deleted
32360Sstevel@tonic-gate  */
32370Sstevel@tonic-gate static int
transaction_add_set(scf_transaction_t * tx,scf_transaction_entry_t * ent,const char * pname,scf_type_t type)32380Sstevel@tonic-gate transaction_add_set(scf_transaction_t *tx, scf_transaction_entry_t *ent,
32390Sstevel@tonic-gate     const char *pname, scf_type_t type)
32400Sstevel@tonic-gate {
32410Sstevel@tonic-gate change_type:
32420Sstevel@tonic-gate 	if (scf_transaction_property_change_type(tx, ent, pname, type) == 0)
32430Sstevel@tonic-gate 		return (0);
32440Sstevel@tonic-gate 
32450Sstevel@tonic-gate 	switch (scf_error()) {
32460Sstevel@tonic-gate 	case SCF_ERROR_CONNECTION_BROKEN:
32470Sstevel@tonic-gate 		return (ECONNABORTED);
32480Sstevel@tonic-gate 
32490Sstevel@tonic-gate 	case SCF_ERROR_DELETED:
32500Sstevel@tonic-gate 		return (ECANCELED);
32510Sstevel@tonic-gate 
32520Sstevel@tonic-gate 	case SCF_ERROR_NOT_FOUND:
32530Sstevel@tonic-gate 		goto new;
32540Sstevel@tonic-gate 
32550Sstevel@tonic-gate 	case SCF_ERROR_HANDLE_MISMATCH:
32560Sstevel@tonic-gate 	case SCF_ERROR_INVALID_ARGUMENT:
32570Sstevel@tonic-gate 	case SCF_ERROR_NOT_BOUND:
32580Sstevel@tonic-gate 	case SCF_ERROR_NOT_SET:
32590Sstevel@tonic-gate 	default:
32600Sstevel@tonic-gate 		bad_error("scf_transaction_property_change_type", scf_error());
32610Sstevel@tonic-gate 	}
32620Sstevel@tonic-gate 
32630Sstevel@tonic-gate new:
32640Sstevel@tonic-gate 	if (scf_transaction_property_new(tx, ent, pname, type) == 0)
32650Sstevel@tonic-gate 		return (0);
32660Sstevel@tonic-gate 
32670Sstevel@tonic-gate 	switch (scf_error()) {
32680Sstevel@tonic-gate 	case SCF_ERROR_CONNECTION_BROKEN:
32690Sstevel@tonic-gate 		return (ECONNABORTED);
32700Sstevel@tonic-gate 
32710Sstevel@tonic-gate 	case SCF_ERROR_DELETED:
32720Sstevel@tonic-gate 		return (ECANCELED);
32730Sstevel@tonic-gate 
32740Sstevel@tonic-gate 	case SCF_ERROR_EXISTS:
32750Sstevel@tonic-gate 		goto change_type;
32760Sstevel@tonic-gate 
32770Sstevel@tonic-gate 	case SCF_ERROR_HANDLE_MISMATCH:
32780Sstevel@tonic-gate 	case SCF_ERROR_INVALID_ARGUMENT:
32790Sstevel@tonic-gate 	case SCF_ERROR_NOT_BOUND:
32800Sstevel@tonic-gate 	case SCF_ERROR_NOT_SET:
32810Sstevel@tonic-gate 	default:
32820Sstevel@tonic-gate 		bad_error("scf_transaction_property_new", scf_error());
32830Sstevel@tonic-gate 		/* NOTREACHED */
32840Sstevel@tonic-gate 	}
32850Sstevel@tonic-gate }
32860Sstevel@tonic-gate 
32870Sstevel@tonic-gate static void
scferr(void)32880Sstevel@tonic-gate scferr(void)
32890Sstevel@tonic-gate {
32900Sstevel@tonic-gate 	switch (scf_error()) {
32910Sstevel@tonic-gate 	case SCF_ERROR_NO_MEMORY:
32920Sstevel@tonic-gate 		console(B_TRUE, gettext("Out of memory.\n"));
32930Sstevel@tonic-gate 		break;
32940Sstevel@tonic-gate 
32950Sstevel@tonic-gate 	case SCF_ERROR_CONNECTION_BROKEN:
32960Sstevel@tonic-gate 		console(B_TRUE, gettext(
32970Sstevel@tonic-gate 		    "Connection to smf(5) repository server broken.\n"));
32980Sstevel@tonic-gate 		break;
32990Sstevel@tonic-gate 
33000Sstevel@tonic-gate 	case SCF_ERROR_NO_RESOURCES:
33010Sstevel@tonic-gate 		console(B_TRUE, gettext(
33020Sstevel@tonic-gate 		    "smf(5) repository server is out of memory.\n"));
33030Sstevel@tonic-gate 		break;
33040Sstevel@tonic-gate 
33050Sstevel@tonic-gate 	case SCF_ERROR_PERMISSION_DENIED:
33060Sstevel@tonic-gate 		console(B_TRUE, gettext("Insufficient privileges.\n"));
33070Sstevel@tonic-gate 		break;
33080Sstevel@tonic-gate 
33090Sstevel@tonic-gate 	default:
33100Sstevel@tonic-gate 		console(B_TRUE, gettext("libscf error: %s\n"),
33110Sstevel@tonic-gate 		    scf_strerror(scf_error()));
33120Sstevel@tonic-gate 	}
33130Sstevel@tonic-gate }
33140Sstevel@tonic-gate 
33150Sstevel@tonic-gate static void
lscf_set_runlevel(char rl)33160Sstevel@tonic-gate lscf_set_runlevel(char rl)
33170Sstevel@tonic-gate {
33180Sstevel@tonic-gate 	scf_handle_t *h;
33190Sstevel@tonic-gate 	scf_instance_t *inst = NULL;
33200Sstevel@tonic-gate 	scf_propertygroup_t *pg = NULL;
33210Sstevel@tonic-gate 	scf_transaction_t *tx = NULL;
33220Sstevel@tonic-gate 	scf_transaction_entry_t *ent = NULL;
33230Sstevel@tonic-gate 	scf_value_t *val = NULL;
33240Sstevel@tonic-gate 	char buf[2];
33250Sstevel@tonic-gate 	int r;
33260Sstevel@tonic-gate 
33270Sstevel@tonic-gate 	h = scf_handle_create(SCF_VERSION);
33280Sstevel@tonic-gate 	if (h == NULL) {
33290Sstevel@tonic-gate 		scferr();
33300Sstevel@tonic-gate 		return;
33310Sstevel@tonic-gate 	}
33320Sstevel@tonic-gate 
33330Sstevel@tonic-gate 	if (scf_handle_bind(h) != 0) {
33340Sstevel@tonic-gate 		switch (scf_error()) {
33350Sstevel@tonic-gate 		case SCF_ERROR_NO_SERVER:
33360Sstevel@tonic-gate 			console(B_TRUE,
33370Sstevel@tonic-gate 			    gettext("smf(5) repository server not running.\n"));
33380Sstevel@tonic-gate 			goto bail;
33390Sstevel@tonic-gate 
33400Sstevel@tonic-gate 		default:
33410Sstevel@tonic-gate 			scferr();
33420Sstevel@tonic-gate 			goto bail;
33430Sstevel@tonic-gate 		}
33440Sstevel@tonic-gate 	}
33450Sstevel@tonic-gate 
33460Sstevel@tonic-gate 	if ((inst = scf_instance_create(h)) == NULL ||
33470Sstevel@tonic-gate 	    (pg = scf_pg_create(h)) == NULL ||
33480Sstevel@tonic-gate 	    (val = scf_value_create(h)) == NULL ||
33490Sstevel@tonic-gate 	    (tx = scf_transaction_create(h)) == NULL ||
33500Sstevel@tonic-gate 	    (ent = scf_entry_create(h)) == NULL) {
33510Sstevel@tonic-gate 		scferr();
33520Sstevel@tonic-gate 		goto bail;
33530Sstevel@tonic-gate 	}
33540Sstevel@tonic-gate 
33550Sstevel@tonic-gate get_inst:
33560Sstevel@tonic-gate 	r = get_or_add_startd(inst);
33570Sstevel@tonic-gate 	switch (r) {
33580Sstevel@tonic-gate 	case 0:
33590Sstevel@tonic-gate 		break;
33600Sstevel@tonic-gate 
33610Sstevel@tonic-gate 	case ENOMEM:
33620Sstevel@tonic-gate 	case ECONNABORTED:
33630Sstevel@tonic-gate 	case EPERM:
33640Sstevel@tonic-gate 	case EACCES:
33650Sstevel@tonic-gate 	case EROFS:
33660Sstevel@tonic-gate 		scferr();
33670Sstevel@tonic-gate 		goto bail;
33680Sstevel@tonic-gate 	default:
33690Sstevel@tonic-gate 		bad_error("get_or_add_startd", r);
33700Sstevel@tonic-gate 	}
33710Sstevel@tonic-gate 
33720Sstevel@tonic-gate get_pg:
33730Sstevel@tonic-gate 	if (scf_instance_get_pg(inst, SCF_PG_OPTIONS_OVR, pg) != 0) {
33740Sstevel@tonic-gate 		switch (scf_error()) {
33750Sstevel@tonic-gate 		case SCF_ERROR_CONNECTION_BROKEN:
33760Sstevel@tonic-gate 			scferr();
33770Sstevel@tonic-gate 			goto bail;
33780Sstevel@tonic-gate 
33790Sstevel@tonic-gate 		case SCF_ERROR_DELETED:
33800Sstevel@tonic-gate 			goto get_inst;
33810Sstevel@tonic-gate 
33820Sstevel@tonic-gate 		case SCF_ERROR_NOT_FOUND:
33830Sstevel@tonic-gate 			break;
33840Sstevel@tonic-gate 
33850Sstevel@tonic-gate 		case SCF_ERROR_HANDLE_MISMATCH:
33860Sstevel@tonic-gate 		case SCF_ERROR_INVALID_ARGUMENT:
33870Sstevel@tonic-gate 		case SCF_ERROR_NOT_SET:
33880Sstevel@tonic-gate 		default:
33890Sstevel@tonic-gate 			bad_error("scf_instance_get_pg", scf_error());
33900Sstevel@tonic-gate 		}
33910Sstevel@tonic-gate 
33920Sstevel@tonic-gate add_pg:
33930Sstevel@tonic-gate 		if (scf_instance_add_pg(inst, SCF_PG_OPTIONS_OVR,
33940Sstevel@tonic-gate 		    SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS, pg) !=
33950Sstevel@tonic-gate 		    0) {
33960Sstevel@tonic-gate 			switch (scf_error()) {
33970Sstevel@tonic-gate 			case SCF_ERROR_CONNECTION_BROKEN:
33980Sstevel@tonic-gate 			case SCF_ERROR_PERMISSION_DENIED:
33990Sstevel@tonic-gate 			case SCF_ERROR_BACKEND_ACCESS:
34000Sstevel@tonic-gate 				scferr();
34010Sstevel@tonic-gate 				goto bail;
34020Sstevel@tonic-gate 
34030Sstevel@tonic-gate 			case SCF_ERROR_DELETED:
34040Sstevel@tonic-gate 				goto get_inst;
34050Sstevel@tonic-gate 
34060Sstevel@tonic-gate 			case SCF_ERROR_EXISTS:
34070Sstevel@tonic-gate 				goto get_pg;
34080Sstevel@tonic-gate 
34090Sstevel@tonic-gate 			case SCF_ERROR_HANDLE_MISMATCH:
34100Sstevel@tonic-gate 			case SCF_ERROR_INVALID_ARGUMENT:
34110Sstevel@tonic-gate 			case SCF_ERROR_NOT_SET:
34120Sstevel@tonic-gate 			default:
34130Sstevel@tonic-gate 				bad_error("scf_instance_add_pg", scf_error());
34140Sstevel@tonic-gate 			}
34150Sstevel@tonic-gate 		}
34160Sstevel@tonic-gate 	}
34170Sstevel@tonic-gate 
34180Sstevel@tonic-gate 	buf[0] = rl;
34190Sstevel@tonic-gate 	buf[1] = '\0';
34200Sstevel@tonic-gate 	r = scf_value_set_astring(val, buf);
34210Sstevel@tonic-gate 	assert(r == 0);
34220Sstevel@tonic-gate 
34230Sstevel@tonic-gate 	for (;;) {
34240Sstevel@tonic-gate 		if (scf_transaction_start(tx, pg) != 0) {
34250Sstevel@tonic-gate 			switch (scf_error()) {
34260Sstevel@tonic-gate 			case SCF_ERROR_CONNECTION_BROKEN:
34270Sstevel@tonic-gate 			case SCF_ERROR_PERMISSION_DENIED:
34280Sstevel@tonic-gate 			case SCF_ERROR_BACKEND_ACCESS:
34290Sstevel@tonic-gate 				scferr();
34300Sstevel@tonic-gate 				goto bail;
34310Sstevel@tonic-gate 
34320Sstevel@tonic-gate 			case SCF_ERROR_DELETED:
34330Sstevel@tonic-gate 				goto add_pg;
34340Sstevel@tonic-gate 
34350Sstevel@tonic-gate 			case SCF_ERROR_HANDLE_MISMATCH:
34360Sstevel@tonic-gate 			case SCF_ERROR_NOT_BOUND:
34370Sstevel@tonic-gate 			case SCF_ERROR_IN_USE:
34380Sstevel@tonic-gate 			case SCF_ERROR_NOT_SET:
34390Sstevel@tonic-gate 			default:
34400Sstevel@tonic-gate 				bad_error("scf_transaction_start", scf_error());
34410Sstevel@tonic-gate 			}
34420Sstevel@tonic-gate 		}
34430Sstevel@tonic-gate 
34440Sstevel@tonic-gate 		r = transaction_add_set(tx, ent, "runlevel", SCF_TYPE_ASTRING);
34450Sstevel@tonic-gate 		switch (r) {
34460Sstevel@tonic-gate 		case 0:
34470Sstevel@tonic-gate 			break;
34480Sstevel@tonic-gate 
34490Sstevel@tonic-gate 		case ECONNABORTED:
34500Sstevel@tonic-gate 			scferr();
34510Sstevel@tonic-gate 			goto bail;
34520Sstevel@tonic-gate 
34530Sstevel@tonic-gate 		case ECANCELED:
34540Sstevel@tonic-gate 			scf_transaction_reset(tx);
34550Sstevel@tonic-gate 			goto add_pg;
34560Sstevel@tonic-gate 
34570Sstevel@tonic-gate 		default:
34580Sstevel@tonic-gate 			bad_error("transaction_add_set", r);
34590Sstevel@tonic-gate 		}
34600Sstevel@tonic-gate 
34610Sstevel@tonic-gate 		r = scf_entry_add_value(ent, val);
34620Sstevel@tonic-gate 		assert(r == 0);
34630Sstevel@tonic-gate 
34640Sstevel@tonic-gate 		r = scf_transaction_commit(tx);
34650Sstevel@tonic-gate 		if (r == 1)
34660Sstevel@tonic-gate 			break;
34670Sstevel@tonic-gate 
34680Sstevel@tonic-gate 		if (r != 0) {
34690Sstevel@tonic-gate 			switch (scf_error()) {
34700Sstevel@tonic-gate 			case SCF_ERROR_CONNECTION_BROKEN:
34710Sstevel@tonic-gate 			case SCF_ERROR_PERMISSION_DENIED:
34720Sstevel@tonic-gate 			case SCF_ERROR_BACKEND_ACCESS:
34730Sstevel@tonic-gate 			case SCF_ERROR_BACKEND_READONLY:
34740Sstevel@tonic-gate 				scferr();
34750Sstevel@tonic-gate 				goto bail;
34760Sstevel@tonic-gate 
34770Sstevel@tonic-gate 			case SCF_ERROR_DELETED:
34780Sstevel@tonic-gate 				scf_transaction_reset(tx);
34790Sstevel@tonic-gate 				goto add_pg;
34800Sstevel@tonic-gate 
34810Sstevel@tonic-gate 			case SCF_ERROR_INVALID_ARGUMENT:
34820Sstevel@tonic-gate 			case SCF_ERROR_NOT_BOUND:
34830Sstevel@tonic-gate 			case SCF_ERROR_NOT_SET:
34840Sstevel@tonic-gate 			default:
34850Sstevel@tonic-gate 				bad_error("scf_transaction_commit",
34860Sstevel@tonic-gate 				    scf_error());
34870Sstevel@tonic-gate 			}
34880Sstevel@tonic-gate 		}
34890Sstevel@tonic-gate 
34900Sstevel@tonic-gate 		scf_transaction_reset(tx);
34910Sstevel@tonic-gate 		(void) scf_pg_update(pg);
34920Sstevel@tonic-gate 	}
34930Sstevel@tonic-gate 
34940Sstevel@tonic-gate bail:
34950Sstevel@tonic-gate 	scf_transaction_destroy(tx);
34960Sstevel@tonic-gate 	scf_entry_destroy(ent);
34970Sstevel@tonic-gate 	scf_value_destroy(val);
34980Sstevel@tonic-gate 	scf_pg_destroy(pg);
34990Sstevel@tonic-gate 	scf_instance_destroy(inst);
35000Sstevel@tonic-gate 
35010Sstevel@tonic-gate 	(void) scf_handle_unbind(h);
35020Sstevel@tonic-gate 	scf_handle_destroy(h);
35030Sstevel@tonic-gate }
35040Sstevel@tonic-gate 
35050Sstevel@tonic-gate /*
35060Sstevel@tonic-gate  * Function to handle requests from users to main init running as process 1.
35070Sstevel@tonic-gate  */
35080Sstevel@tonic-gate static void
userinit(int argc,char ** argv)35090Sstevel@tonic-gate userinit(int argc, char **argv)
35100Sstevel@tonic-gate {
35110Sstevel@tonic-gate 	FILE	*fp;
35120Sstevel@tonic-gate 	char	*ln;
35130Sstevel@tonic-gate 	int	init_signal;
35140Sstevel@tonic-gate 	struct stat	sconbuf, conbuf;
35150Sstevel@tonic-gate 	const char *usage_msg = "Usage: init [0123456SsQqabc]\n";
35160Sstevel@tonic-gate 
35170Sstevel@tonic-gate 	/*
35180Sstevel@tonic-gate 	 * We are a user invoked init.  Is there an argument and is it
35190Sstevel@tonic-gate 	 * a single character?  If not, print usage message and quit.
35200Sstevel@tonic-gate 	 */
35210Sstevel@tonic-gate 	if (argc != 2 || argv[1][1] != '\0') {
35220Sstevel@tonic-gate 		(void) fprintf(stderr, usage_msg);
35230Sstevel@tonic-gate 		exit(0);
35240Sstevel@tonic-gate 	}
35250Sstevel@tonic-gate 
35260Sstevel@tonic-gate 	if ((init_signal = lvlname_to_state((char)argv[1][0])) == -1) {
35270Sstevel@tonic-gate 		(void) fprintf(stderr, usage_msg);
35280Sstevel@tonic-gate 		(void) audit_put_record(ADT_FAILURE, ADT_FAIL_VALUE_BAD_CMD,
35290Sstevel@tonic-gate 		    argv[1]);
35300Sstevel@tonic-gate 		exit(1);
35310Sstevel@tonic-gate 	}
35320Sstevel@tonic-gate 
35330Sstevel@tonic-gate 	if (init_signal == SINGLE_USER) {
35340Sstevel@tonic-gate 		/*
35350Sstevel@tonic-gate 		 * Make sure this process is talking to a legal tty line
35360Sstevel@tonic-gate 		 * and that /dev/syscon is linked to this line.
35370Sstevel@tonic-gate 		 */
35380Sstevel@tonic-gate 		ln = ttyname(0);	/* Get the name of tty */
35390Sstevel@tonic-gate 		if (ln == NULL) {
35400Sstevel@tonic-gate 			(void) fprintf(stderr,
35410Sstevel@tonic-gate 			    "Standard input not a tty line\n");
35420Sstevel@tonic-gate 			(void) audit_put_record(ADT_FAILURE,
35430Sstevel@tonic-gate 			    ADT_FAIL_VALUE_BAD_TTY, argv[1]);
35440Sstevel@tonic-gate 			exit(1);
35450Sstevel@tonic-gate 		}
35462703Svikram 
35472703Svikram 		if ((stat(ln, &sconbuf) != -1) &&
35482703Svikram 		    (stat(SYSCON, &conbuf) == -1 ||
35492703Svikram 		    sconbuf.st_rdev != conbuf.st_rdev)) {
35500Sstevel@tonic-gate 			/*
35512703Svikram 			 * /dev/syscon needs to change.
35520Sstevel@tonic-gate 			 * Unlink /dev/syscon and relink it to the current line.
35530Sstevel@tonic-gate 			 */
35542703Svikram 			if (lstat(SYSCON, &conbuf) != -1 &&
35552703Svikram 			    unlink(SYSCON) == FAILURE) {
35560Sstevel@tonic-gate 				perror("Can't unlink /dev/syscon");
35570Sstevel@tonic-gate 				(void) fprintf(stderr,
35580Sstevel@tonic-gate 				    "Run command on the system console.\n");
35590Sstevel@tonic-gate 				(void) audit_put_record(ADT_FAILURE,
35600Sstevel@tonic-gate 				    ADT_FAIL_VALUE_PROGRAM, argv[1]);
35610Sstevel@tonic-gate 				exit(1);
35620Sstevel@tonic-gate 			}
35632703Svikram 			if (symlink(ln, SYSCON) == FAILURE) {
35640Sstevel@tonic-gate 				(void) fprintf(stderr,
35652703Svikram 				    "Can't symlink /dev/syscon to %s: %s", ln,
35660Sstevel@tonic-gate 				    strerror(errno));
35670Sstevel@tonic-gate 
35680Sstevel@tonic-gate 				/* Try to leave a syscon */
35690Sstevel@tonic-gate 				(void) link(SYSTTY, SYSCON);
35700Sstevel@tonic-gate 				(void) audit_put_record(ADT_FAILURE,
35710Sstevel@tonic-gate 				    ADT_FAIL_VALUE_PROGRAM, argv[1]);
35720Sstevel@tonic-gate 				exit(1);
35730Sstevel@tonic-gate 			}
35740Sstevel@tonic-gate 
35750Sstevel@tonic-gate 			/*
35760Sstevel@tonic-gate 			 * Try to leave a message on system console saying where
35770Sstevel@tonic-gate 			 * /dev/syscon is currently connected.
35780Sstevel@tonic-gate 			 */
35790Sstevel@tonic-gate 			if ((fp = fopen(SYSTTY, "r+")) != NULL) {
35800Sstevel@tonic-gate 				(void) fprintf(fp,
35810Sstevel@tonic-gate 				    "\n****	SYSCON CHANGED TO %s	****\n",
35820Sstevel@tonic-gate 				    ln);
35830Sstevel@tonic-gate 				(void) fclose(fp);
35840Sstevel@tonic-gate 			}
35850Sstevel@tonic-gate 		}
35860Sstevel@tonic-gate 	}
35870Sstevel@tonic-gate 
35880Sstevel@tonic-gate 	update_boot_archive(init_signal);
35890Sstevel@tonic-gate 
35902481Spaulson 	(void) audit_put_record(ADT_SUCCESS, ADT_SUCCESS, argv[1]);
35910Sstevel@tonic-gate 
35920Sstevel@tonic-gate 	/*
35930Sstevel@tonic-gate 	 * Signal init; init will take care of telling svc.startd.
35940Sstevel@tonic-gate 	 */
35950Sstevel@tonic-gate 	if (kill(init_pid, init_signal) == FAILURE) {
35960Sstevel@tonic-gate 		(void) fprintf(stderr, "Must be super-user\n");
35970Sstevel@tonic-gate 		(void) audit_put_record(ADT_FAILURE,
35980Sstevel@tonic-gate 		    ADT_FAIL_VALUE_AUTH, argv[1]);
35990Sstevel@tonic-gate 		exit(1);
36000Sstevel@tonic-gate 	}
36010Sstevel@tonic-gate 
36020Sstevel@tonic-gate 	exit(0);
36030Sstevel@tonic-gate }
36040Sstevel@tonic-gate 
36050Sstevel@tonic-gate 
36060Sstevel@tonic-gate #define	DELTA	25	/* Number of pidlist elements to allocate at a time */
36070Sstevel@tonic-gate 
36080Sstevel@tonic-gate /* ARGSUSED */
36090Sstevel@tonic-gate void
sigpoll(int n)36100Sstevel@tonic-gate sigpoll(int n)
36110Sstevel@tonic-gate {
36120Sstevel@tonic-gate 	struct pidrec prec;
36130Sstevel@tonic-gate 	struct pidrec *p = &prec;
36140Sstevel@tonic-gate 	struct pidlist *plp;
36150Sstevel@tonic-gate 	struct pidlist *tp, *savetp;
36160Sstevel@tonic-gate 	int i;
36170Sstevel@tonic-gate 
36180Sstevel@tonic-gate 	if (Pfd < 0) {
36190Sstevel@tonic-gate 		return;
36200Sstevel@tonic-gate 	}
36212691Snakanon 
36220Sstevel@tonic-gate 	for (;;) {
36230Sstevel@tonic-gate 		/*
36240Sstevel@tonic-gate 		 * Important Note: Either read will really fail (in which case
36250Sstevel@tonic-gate 		 * return is all we can do) or will get EAGAIN (Pfd was opened
36260Sstevel@tonic-gate 		 * O_NDELAY), in which case we also want to return.
36270Sstevel@tonic-gate 		 * Always return from here!
36280Sstevel@tonic-gate 		 */
36290Sstevel@tonic-gate 		if (read(Pfd, p, sizeof (struct pidrec)) !=
36300Sstevel@tonic-gate 						sizeof (struct pidrec)) {
36310Sstevel@tonic-gate 			return;
36320Sstevel@tonic-gate 		}
36330Sstevel@tonic-gate 		switch (p->pd_type) {
36340Sstevel@tonic-gate 
36350Sstevel@tonic-gate 		case ADDPID:
36360Sstevel@tonic-gate 			/*
36370Sstevel@tonic-gate 			 * New "godchild", add to list.
36380Sstevel@tonic-gate 			 */
36390Sstevel@tonic-gate 			if (Plfree == NULL) {
36400Sstevel@tonic-gate 				plp = (struct pidlist *)calloc(DELTA,
36410Sstevel@tonic-gate 				    sizeof (struct pidlist));
36420Sstevel@tonic-gate 				if (plp == NULL) {
36430Sstevel@tonic-gate 					/* Can't save pid */
36440Sstevel@tonic-gate 					break;
36450Sstevel@tonic-gate 				}
36460Sstevel@tonic-gate 				/*
36470Sstevel@tonic-gate 				 * Point at 2nd record allocated, we'll use plp.
36480Sstevel@tonic-gate 				 */
36490Sstevel@tonic-gate 				tp = plp + 1;
36500Sstevel@tonic-gate 				/*
36510Sstevel@tonic-gate 				 * Link them into a chain.
36520Sstevel@tonic-gate 				 */
36530Sstevel@tonic-gate 				Plfree = tp;
36540Sstevel@tonic-gate 				for (i = 0; i < DELTA - 2; i++) {
36550Sstevel@tonic-gate 					tp->pl_next = tp + 1;
36560Sstevel@tonic-gate 					tp++;
36570Sstevel@tonic-gate 				}
36580Sstevel@tonic-gate 			} else {
36590Sstevel@tonic-gate 				plp = Plfree;
36600Sstevel@tonic-gate 				Plfree = plp->pl_next;
36610Sstevel@tonic-gate 			}
36620Sstevel@tonic-gate 			plp->pl_pid = p->pd_pid;
36630Sstevel@tonic-gate 			plp->pl_dflag = 0;
36640Sstevel@tonic-gate 			plp->pl_next = NULL;
36650Sstevel@tonic-gate 			/*
36660Sstevel@tonic-gate 			 * Note - pid list is kept in increasing order of pids.
36670Sstevel@tonic-gate 			 */
36680Sstevel@tonic-gate 			if (Plhead == NULL) {
36690Sstevel@tonic-gate 				Plhead = plp;
36700Sstevel@tonic-gate 				/* Back up to read next record */
36710Sstevel@tonic-gate 				break;
36720Sstevel@tonic-gate 			} else {
36730Sstevel@tonic-gate 				savetp = tp = Plhead;
36740Sstevel@tonic-gate 				while (tp) {
36750Sstevel@tonic-gate 					if (plp->pl_pid > tp->pl_pid) {
36760Sstevel@tonic-gate 						savetp = tp;
36770Sstevel@tonic-gate 						tp = tp->pl_next;
36780Sstevel@tonic-gate 						continue;
36790Sstevel@tonic-gate 					} else if (plp->pl_pid < tp->pl_pid) {
36800Sstevel@tonic-gate 						if (tp == Plhead) {
36810Sstevel@tonic-gate 							plp->pl_next = Plhead;
36820Sstevel@tonic-gate 							Plhead = plp;
36830Sstevel@tonic-gate 						} else {
36840Sstevel@tonic-gate 							plp->pl_next =
36850Sstevel@tonic-gate 							    savetp->pl_next;
36860Sstevel@tonic-gate 							savetp->pl_next = plp;
36870Sstevel@tonic-gate 						}
36880Sstevel@tonic-gate 						break;
36890Sstevel@tonic-gate 					} else {
36900Sstevel@tonic-gate 						/* Already in list! */
36910Sstevel@tonic-gate 						plp->pl_next = Plfree;
36920Sstevel@tonic-gate 						Plfree = plp;
36930Sstevel@tonic-gate 						break;
36940Sstevel@tonic-gate 					}
36950Sstevel@tonic-gate 				}
36960Sstevel@tonic-gate 				if (tp == NULL) {
36970Sstevel@tonic-gate 					/* Add to end of list */
36980Sstevel@tonic-gate 					savetp->pl_next = plp;
36990Sstevel@tonic-gate 				}
37000Sstevel@tonic-gate 			}
37010Sstevel@tonic-gate 			/* Back up to read next record. */
37020Sstevel@tonic-gate 			break;
37030Sstevel@tonic-gate 
37040Sstevel@tonic-gate 		case REMPID:
37050Sstevel@tonic-gate 			/*
37060Sstevel@tonic-gate 			 * This one was handled by someone else,
37070Sstevel@tonic-gate 			 * purge it from the list.
37080Sstevel@tonic-gate 			 */
37090Sstevel@tonic-gate 			if (Plhead == NULL) {
37100Sstevel@tonic-gate 				/* Back up to read next record. */
37110Sstevel@tonic-gate 				break;
37120Sstevel@tonic-gate 			}
37130Sstevel@tonic-gate 			savetp = tp = Plhead;
37140Sstevel@tonic-gate 			while (tp) {
37150Sstevel@tonic-gate 				if (p->pd_pid > tp->pl_pid) {
37160Sstevel@tonic-gate 					/* Keep on looking. */
37170Sstevel@tonic-gate 					savetp = tp;
37180Sstevel@tonic-gate 					tp = tp->pl_next;
37190Sstevel@tonic-gate 					continue;
37200Sstevel@tonic-gate 				} else if (p->pd_pid < tp->pl_pid) {
37210Sstevel@tonic-gate 					/* Not in list. */
37220Sstevel@tonic-gate 					break;
37230Sstevel@tonic-gate 				} else {
37240Sstevel@tonic-gate 					/* Found it. */
37250Sstevel@tonic-gate 					if (tp == Plhead)
37260Sstevel@tonic-gate 						Plhead = tp->pl_next;
37270Sstevel@tonic-gate 					else
37280Sstevel@tonic-gate 						savetp->pl_next = tp->pl_next;
37290Sstevel@tonic-gate 					tp->pl_next = Plfree;
37300Sstevel@tonic-gate 					Plfree = tp;
37310Sstevel@tonic-gate 					break;
37320Sstevel@tonic-gate 				}
37330Sstevel@tonic-gate 			}
37340Sstevel@tonic-gate 			/* Back up to read next record. */
37350Sstevel@tonic-gate 			break;
37360Sstevel@tonic-gate 		default:
37370Sstevel@tonic-gate 			console(B_TRUE, "Bad message on initpipe\n");
37380Sstevel@tonic-gate 			break;
37390Sstevel@tonic-gate 		}
37400Sstevel@tonic-gate 	}
37410Sstevel@tonic-gate }
37420Sstevel@tonic-gate 
37430Sstevel@tonic-gate 
37440Sstevel@tonic-gate static void
cleanaux()37450Sstevel@tonic-gate cleanaux()
37460Sstevel@tonic-gate {
37470Sstevel@tonic-gate 	struct pidlist *savep, *p;
37480Sstevel@tonic-gate 	pid_t	pid;
37490Sstevel@tonic-gate 	short	status;
37500Sstevel@tonic-gate 
375110083SRoger.Faulkner@Sun.COM 	(void) sighold(SIGCLD);
37520Sstevel@tonic-gate 	Gchild = 0;	/* Note - Safe to do this here since no SIGCLDs */
37530Sstevel@tonic-gate 	(void) sighold(SIGPOLL);
37540Sstevel@tonic-gate 	savep = p = Plhead;
37550Sstevel@tonic-gate 	while (p) {
37560Sstevel@tonic-gate 		if (p->pl_dflag) {
37570Sstevel@tonic-gate 			/*
37580Sstevel@tonic-gate 			 * Found an entry to delete,
37590Sstevel@tonic-gate 			 * remove it from list first.
37600Sstevel@tonic-gate 			 */
37610Sstevel@tonic-gate 			pid = p->pl_pid;
37620Sstevel@tonic-gate 			status = p->pl_exit;
37630Sstevel@tonic-gate 			if (p == Plhead) {
37640Sstevel@tonic-gate 				Plhead = p->pl_next;
37650Sstevel@tonic-gate 				p->pl_next = Plfree;
37660Sstevel@tonic-gate 				Plfree = p;
37670Sstevel@tonic-gate 				savep = p = Plhead;
37680Sstevel@tonic-gate 			} else {
37690Sstevel@tonic-gate 				savep->pl_next = p->pl_next;
37700Sstevel@tonic-gate 				p->pl_next = Plfree;
37710Sstevel@tonic-gate 				Plfree = p;
37720Sstevel@tonic-gate 				p = savep->pl_next;
37730Sstevel@tonic-gate 			}
37740Sstevel@tonic-gate 			clearent(pid, status);
37750Sstevel@tonic-gate 			continue;
37760Sstevel@tonic-gate 		}
37770Sstevel@tonic-gate 		savep = p;
37780Sstevel@tonic-gate 		p = p->pl_next;
37790Sstevel@tonic-gate 	}
37800Sstevel@tonic-gate 	(void) sigrelse(SIGPOLL);
378110083SRoger.Faulkner@Sun.COM 	(void) sigrelse(SIGCLD);
37820Sstevel@tonic-gate }
37830Sstevel@tonic-gate 
37840Sstevel@tonic-gate 
37850Sstevel@tonic-gate /*
37860Sstevel@tonic-gate  * /etc/inittab has more entries and we have run out of room in the proc_table
37870Sstevel@tonic-gate  * array. Double the size of proc_table to accomodate the extra entries.
37880Sstevel@tonic-gate  */
37890Sstevel@tonic-gate static void
increase_proc_table_size()37900Sstevel@tonic-gate increase_proc_table_size()
37910Sstevel@tonic-gate {
37920Sstevel@tonic-gate 	sigset_t block, unblock;
37930Sstevel@tonic-gate 	void *ptr;
37940Sstevel@tonic-gate 	size_t delta = num_proc * sizeof (struct PROC_TABLE);
37950Sstevel@tonic-gate 
37960Sstevel@tonic-gate 
37970Sstevel@tonic-gate 	/*
37980Sstevel@tonic-gate 	 * Block signals for realloc.
37990Sstevel@tonic-gate 	 */
38000Sstevel@tonic-gate 	(void) sigfillset(&block);
38010Sstevel@tonic-gate 	(void) sigprocmask(SIG_BLOCK, &block, &unblock);
38020Sstevel@tonic-gate 
38030Sstevel@tonic-gate 
38040Sstevel@tonic-gate 	/*
38050Sstevel@tonic-gate 	 * On failure we just return because callers of this function check
38060Sstevel@tonic-gate 	 * for failure.
38070Sstevel@tonic-gate 	 */
38080Sstevel@tonic-gate 	do
38090Sstevel@tonic-gate 		ptr = realloc(g_state, g_state_sz + delta);
38100Sstevel@tonic-gate 	while (ptr == NULL && errno == EAGAIN);
38110Sstevel@tonic-gate 
38120Sstevel@tonic-gate 	if (ptr != NULL) {
38130Sstevel@tonic-gate 		/* ensure that the new part is initialized to zero */
38140Sstevel@tonic-gate 		bzero((caddr_t)ptr + g_state_sz, delta);
38150Sstevel@tonic-gate 
38160Sstevel@tonic-gate 		g_state = ptr;
38170Sstevel@tonic-gate 		g_state_sz += delta;
38180Sstevel@tonic-gate 		num_proc <<= 1;
38190Sstevel@tonic-gate 	}
38200Sstevel@tonic-gate 
38210Sstevel@tonic-gate 
38220Sstevel@tonic-gate 	/* unblock our signals before returning */
38230Sstevel@tonic-gate 	(void) sigprocmask(SIG_SETMASK, &unblock, NULL);
38240Sstevel@tonic-gate }
38250Sstevel@tonic-gate 
38260Sstevel@tonic-gate 
38270Sstevel@tonic-gate 
38280Sstevel@tonic-gate /*
38290Sstevel@tonic-gate  * Sanity check g_state.
38300Sstevel@tonic-gate  */
38310Sstevel@tonic-gate static int
st_sane()38320Sstevel@tonic-gate st_sane()
38330Sstevel@tonic-gate {
38340Sstevel@tonic-gate 	int i;
38350Sstevel@tonic-gate 	struct PROC_TABLE *ptp;
38360Sstevel@tonic-gate 
38370Sstevel@tonic-gate 
38380Sstevel@tonic-gate 	/* Note: cur_state is encoded as a signal number */
38390Sstevel@tonic-gate 	if (cur_state < 1 || cur_state == 9 || cur_state > 13)
38400Sstevel@tonic-gate 		return (0);
38410Sstevel@tonic-gate 
38420Sstevel@tonic-gate 	/* Check num_proc */
38430Sstevel@tonic-gate 	if (g_state_sz != sizeof (struct init_state) + (num_proc - 1) *
38440Sstevel@tonic-gate 	    sizeof (struct PROC_TABLE))
38450Sstevel@tonic-gate 		return (0);
38460Sstevel@tonic-gate 
38470Sstevel@tonic-gate 	/* Check proc_table */
38480Sstevel@tonic-gate 	for (i = 0, ptp = proc_table; i < num_proc; ++i, ++ptp) {
38490Sstevel@tonic-gate 		/* skip unoccupied entries */
38500Sstevel@tonic-gate 		if (!(ptp->p_flags & OCCUPIED))
38510Sstevel@tonic-gate 			continue;
38520Sstevel@tonic-gate 
38530Sstevel@tonic-gate 		/* p_flags has no bits outside of PF_MASK */
38540Sstevel@tonic-gate 		if (ptp->p_flags & ~(PF_MASK))
38550Sstevel@tonic-gate 			return (0);
38560Sstevel@tonic-gate 
38570Sstevel@tonic-gate 		/* 5 <= pid <= MAXPID */
38580Sstevel@tonic-gate 		if (ptp->p_pid < 5 || ptp->p_pid > MAXPID)
38590Sstevel@tonic-gate 			return (0);
38600Sstevel@tonic-gate 
38610Sstevel@tonic-gate 		/* p_count >= 0 */
38620Sstevel@tonic-gate 		if (ptp->p_count < 0)
38630Sstevel@tonic-gate 			return (0);
38640Sstevel@tonic-gate 
38650Sstevel@tonic-gate 		/* p_time >= 0 */
38660Sstevel@tonic-gate 		if (ptp->p_time < 0)
38670Sstevel@tonic-gate 			return (0);
38680Sstevel@tonic-gate 	}
38690Sstevel@tonic-gate 
38700Sstevel@tonic-gate 	return (1);
38710Sstevel@tonic-gate }
38720Sstevel@tonic-gate 
38730Sstevel@tonic-gate /*
38740Sstevel@tonic-gate  * Initialize our state.
38750Sstevel@tonic-gate  *
38760Sstevel@tonic-gate  * If the system just booted, then init_state_file, which is located on an
38770Sstevel@tonic-gate  * everpresent tmpfs filesystem, should not exist.
38780Sstevel@tonic-gate  *
38790Sstevel@tonic-gate  * If we were restarted, then init_state_file should exist, in
38800Sstevel@tonic-gate  * which case we'll read it in, sanity check it, and use it.
38810Sstevel@tonic-gate  *
38820Sstevel@tonic-gate  * Note: You can't call console() until proc_table is ready.
38830Sstevel@tonic-gate  */
38840Sstevel@tonic-gate void
st_init()38850Sstevel@tonic-gate st_init()
38860Sstevel@tonic-gate {
38870Sstevel@tonic-gate 	struct stat stb;
38880Sstevel@tonic-gate 	int ret, st_fd, insane = 0;
38890Sstevel@tonic-gate 	size_t to_be_read;
38900Sstevel@tonic-gate 	char *ptr;
38910Sstevel@tonic-gate 
38920Sstevel@tonic-gate 
38930Sstevel@tonic-gate 	booting = 1;
38940Sstevel@tonic-gate 
38950Sstevel@tonic-gate 	do {
38960Sstevel@tonic-gate 		/*
38970Sstevel@tonic-gate 		 * If we can exclusively create the file, then we're the
38980Sstevel@tonic-gate 		 * initial invocation of init(1M).
38990Sstevel@tonic-gate 		 */
39000Sstevel@tonic-gate 		st_fd = open(init_state_file, O_RDWR | O_CREAT | O_EXCL,
39010Sstevel@tonic-gate 		    S_IRUSR | S_IWUSR);
39020Sstevel@tonic-gate 	} while (st_fd == -1 && errno == EINTR);
39030Sstevel@tonic-gate 	if (st_fd != -1)
39040Sstevel@tonic-gate 		goto new_state;
39050Sstevel@tonic-gate 
39060Sstevel@tonic-gate 	booting = 0;
39070Sstevel@tonic-gate 
39080Sstevel@tonic-gate 	do {
39090Sstevel@tonic-gate 		st_fd = open(init_state_file, O_RDWR, S_IRUSR | S_IWUSR);
39100Sstevel@tonic-gate 	} while (st_fd == -1 && errno == EINTR);
39110Sstevel@tonic-gate 	if (st_fd == -1)
39120Sstevel@tonic-gate 		goto new_state;
39130Sstevel@tonic-gate 
39140Sstevel@tonic-gate 	/* Get the size of the file. */
39150Sstevel@tonic-gate 	do
39160Sstevel@tonic-gate 		ret = fstat(st_fd, &stb);
39170Sstevel@tonic-gate 	while (ret == -1 && errno == EINTR);
39180Sstevel@tonic-gate 	if (ret == -1)
39190Sstevel@tonic-gate 		goto new_state;
39200Sstevel@tonic-gate 
39210Sstevel@tonic-gate 	do
39220Sstevel@tonic-gate 		g_state = malloc(stb.st_size);
39230Sstevel@tonic-gate 	while (g_state == NULL && errno == EAGAIN);
39240Sstevel@tonic-gate 	if (g_state == NULL)
39250Sstevel@tonic-gate 		goto new_state;
39260Sstevel@tonic-gate 
39270Sstevel@tonic-gate 	to_be_read = stb.st_size;
39280Sstevel@tonic-gate 	ptr = (char *)g_state;
39290Sstevel@tonic-gate 	while (to_be_read > 0) {
39300Sstevel@tonic-gate 		ssize_t read_ret;
39310Sstevel@tonic-gate 
39320Sstevel@tonic-gate 		read_ret = read(st_fd, ptr, to_be_read);
39330Sstevel@tonic-gate 		if (read_ret < 0) {
39340Sstevel@tonic-gate 			if (errno == EINTR)
39350Sstevel@tonic-gate 				continue;
39360Sstevel@tonic-gate 
39370Sstevel@tonic-gate 			goto new_state;
39380Sstevel@tonic-gate 		}
39390Sstevel@tonic-gate 
39400Sstevel@tonic-gate 		to_be_read -= read_ret;
39410Sstevel@tonic-gate 		ptr += read_ret;
39420Sstevel@tonic-gate 	}
39430Sstevel@tonic-gate 
39440Sstevel@tonic-gate 	(void) close(st_fd);
39450Sstevel@tonic-gate 
39460Sstevel@tonic-gate 	g_state_sz = stb.st_size;
39470Sstevel@tonic-gate 
39480Sstevel@tonic-gate 	if (st_sane()) {
39490Sstevel@tonic-gate 		console(B_TRUE, "Restarting.\n");
39500Sstevel@tonic-gate 		return;
39510Sstevel@tonic-gate 	}
39520Sstevel@tonic-gate 
39530Sstevel@tonic-gate 	insane = 1;
39540Sstevel@tonic-gate 
39550Sstevel@tonic-gate new_state:
39560Sstevel@tonic-gate 	if (st_fd >= 0)
39570Sstevel@tonic-gate 		(void) close(st_fd);
39580Sstevel@tonic-gate 	else
39590Sstevel@tonic-gate 		(void) unlink(init_state_file);
39600Sstevel@tonic-gate 
39610Sstevel@tonic-gate 	if (g_state != NULL)
39620Sstevel@tonic-gate 		free(g_state);
39630Sstevel@tonic-gate 
39640Sstevel@tonic-gate 	/* Something went wrong, so allocate new state. */
39650Sstevel@tonic-gate 	g_state_sz = sizeof (struct init_state) +
39660Sstevel@tonic-gate 	    ((init_num_proc - 1) * sizeof (struct PROC_TABLE));
39670Sstevel@tonic-gate 	do
39680Sstevel@tonic-gate 		g_state = calloc(1, g_state_sz);
39690Sstevel@tonic-gate 	while (g_state == NULL && errno == EAGAIN);
39700Sstevel@tonic-gate 	if (g_state == NULL) {
39710Sstevel@tonic-gate 		/* Fatal error! */
39720Sstevel@tonic-gate 		exit(errno);
39730Sstevel@tonic-gate 	}
39740Sstevel@tonic-gate 
39750Sstevel@tonic-gate 	g_state->ist_runlevel = -1;
39760Sstevel@tonic-gate 	num_proc = init_num_proc;
39770Sstevel@tonic-gate 
39780Sstevel@tonic-gate 	if (!booting) {
39790Sstevel@tonic-gate 		console(B_TRUE, "Restarting.\n");
39800Sstevel@tonic-gate 
39810Sstevel@tonic-gate 		/* Overwrite the bad state file. */
39820Sstevel@tonic-gate 		st_write();
39830Sstevel@tonic-gate 
39840Sstevel@tonic-gate 		if (!insane) {
39850Sstevel@tonic-gate 			console(B_TRUE,
39860Sstevel@tonic-gate 			    "Error accessing persistent state file `%s'.  "
39870Sstevel@tonic-gate 			    "Ignored.\n", init_state_file);
39880Sstevel@tonic-gate 		} else {
39890Sstevel@tonic-gate 			console(B_TRUE,
39900Sstevel@tonic-gate 			    "Persistent state file `%s' is invalid and was "
39910Sstevel@tonic-gate 			    "ignored.\n", init_state_file);
39920Sstevel@tonic-gate 		}
39930Sstevel@tonic-gate 	}
39940Sstevel@tonic-gate }
39950Sstevel@tonic-gate 
39960Sstevel@tonic-gate /*
39970Sstevel@tonic-gate  * Write g_state out to the state file.
39980Sstevel@tonic-gate  */
39990Sstevel@tonic-gate void
st_write()40000Sstevel@tonic-gate st_write()
40010Sstevel@tonic-gate {
40020Sstevel@tonic-gate 	static int complained = 0;
40030Sstevel@tonic-gate 
40040Sstevel@tonic-gate 	int st_fd;
40050Sstevel@tonic-gate 	char *cp;
40060Sstevel@tonic-gate 	size_t sz;
40070Sstevel@tonic-gate 	ssize_t ret;
40080Sstevel@tonic-gate 
40090Sstevel@tonic-gate 
40100Sstevel@tonic-gate 	do {
40110Sstevel@tonic-gate 		st_fd = open(init_next_state_file,
40120Sstevel@tonic-gate 		    O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
40130Sstevel@tonic-gate 	} while (st_fd < 0 && errno == EINTR);
40140Sstevel@tonic-gate 	if (st_fd < 0)
40150Sstevel@tonic-gate 		goto err;
40160Sstevel@tonic-gate 
40170Sstevel@tonic-gate 	cp = (char *)g_state;
40180Sstevel@tonic-gate 	sz = g_state_sz;
40190Sstevel@tonic-gate 	while (sz > 0) {
40200Sstevel@tonic-gate 		ret = write(st_fd, cp, sz);
40210Sstevel@tonic-gate 		if (ret < 0) {
40220Sstevel@tonic-gate 			if (errno == EINTR)
40230Sstevel@tonic-gate 				continue;
40240Sstevel@tonic-gate 
40250Sstevel@tonic-gate 			goto err;
40260Sstevel@tonic-gate 		}
40270Sstevel@tonic-gate 
40280Sstevel@tonic-gate 		sz -= ret;
40290Sstevel@tonic-gate 		cp += ret;
40300Sstevel@tonic-gate 	}
40310Sstevel@tonic-gate 
40320Sstevel@tonic-gate 	(void) close(st_fd);
40330Sstevel@tonic-gate 	st_fd = -1;
40340Sstevel@tonic-gate 	if (rename(init_next_state_file, init_state_file)) {
40350Sstevel@tonic-gate 		(void) unlink(init_next_state_file);
40360Sstevel@tonic-gate 		goto err;
40370Sstevel@tonic-gate 	}
40380Sstevel@tonic-gate 	complained = 0;
40390Sstevel@tonic-gate 
40400Sstevel@tonic-gate 	return;
40410Sstevel@tonic-gate 
40420Sstevel@tonic-gate err:
40430Sstevel@tonic-gate 	if (st_fd >= 0)
40440Sstevel@tonic-gate 		(void) close(st_fd);
40450Sstevel@tonic-gate 
40460Sstevel@tonic-gate 	if (!booting && !complained) {
40470Sstevel@tonic-gate 		/*
40480Sstevel@tonic-gate 		 * Only complain after the filesystem should have come up.
40490Sstevel@tonic-gate 		 * And only do it once so we don't loop between console()
40500Sstevel@tonic-gate 		 * & efork().
40510Sstevel@tonic-gate 		 */
40520Sstevel@tonic-gate 		complained = 1;
40530Sstevel@tonic-gate 		if (st_fd)
40540Sstevel@tonic-gate 			console(B_TRUE, "Couldn't write persistent state "
40550Sstevel@tonic-gate 			    "file `%s'.\n", init_state_file);
40560Sstevel@tonic-gate 		else
40570Sstevel@tonic-gate 			console(B_TRUE, "Couldn't move persistent state "
40580Sstevel@tonic-gate 			    "file `%s' to `%s'.\n", init_next_state_file,
40590Sstevel@tonic-gate 			    init_state_file);
40600Sstevel@tonic-gate 	}
40610Sstevel@tonic-gate }
40620Sstevel@tonic-gate 
40630Sstevel@tonic-gate /*
40640Sstevel@tonic-gate  * Create a contract with these parameters.
40650Sstevel@tonic-gate  */
40660Sstevel@tonic-gate static int
contract_make_template(uint_t info,uint_t critical,uint_t fatal,uint64_t cookie)40670Sstevel@tonic-gate contract_make_template(uint_t info, uint_t critical, uint_t fatal,
40680Sstevel@tonic-gate     uint64_t cookie)
40690Sstevel@tonic-gate {
40700Sstevel@tonic-gate 	int fd, err;
40710Sstevel@tonic-gate 
40720Sstevel@tonic-gate 	char *ioctl_tset_emsg =
40730Sstevel@tonic-gate 	    "Couldn't set \"%s\" contract template parameter: %s.\n";
40740Sstevel@tonic-gate 
40750Sstevel@tonic-gate 	do
40760Sstevel@tonic-gate 		fd = open64(CTFS_ROOT "/process/template", O_RDWR);
40770Sstevel@tonic-gate 	while (fd < 0 && errno == EINTR);
40780Sstevel@tonic-gate 	if (fd < 0) {
40790Sstevel@tonic-gate 		console(B_TRUE, "Couldn't create process template: %s.\n",
40800Sstevel@tonic-gate 		    strerror(errno));
40810Sstevel@tonic-gate 		return (-1);
40820Sstevel@tonic-gate 	}
40830Sstevel@tonic-gate 
40840Sstevel@tonic-gate 	if (err = ct_pr_tmpl_set_param(fd, CT_PR_INHERIT | CT_PR_REGENT))
40850Sstevel@tonic-gate 		console(B_TRUE, "Contract set template inherit, regent "
40866073Sacruz 		    "failed: %s.\n", strerror(err));
40870Sstevel@tonic-gate 
40880Sstevel@tonic-gate 	/*
40890Sstevel@tonic-gate 	 * These errors result in a misconfigured template, which is better
40900Sstevel@tonic-gate 	 * than no template at all, so warn but don't abort.
40910Sstevel@tonic-gate 	 */
40920Sstevel@tonic-gate 	if (err = ct_tmpl_set_informative(fd, info))
40930Sstevel@tonic-gate 		console(B_TRUE, ioctl_tset_emsg, "informative", strerror(err));
40940Sstevel@tonic-gate 
40950Sstevel@tonic-gate 	if (err = ct_tmpl_set_critical(fd, critical))
40960Sstevel@tonic-gate 		console(B_TRUE, ioctl_tset_emsg, "critical", strerror(err));
40970Sstevel@tonic-gate 
40980Sstevel@tonic-gate 	if (err = ct_pr_tmpl_set_fatal(fd, fatal))
40990Sstevel@tonic-gate 		console(B_TRUE, ioctl_tset_emsg, "fatal", strerror(err));
41000Sstevel@tonic-gate 
41010Sstevel@tonic-gate 	if (err = ct_tmpl_set_cookie(fd, cookie))
41020Sstevel@tonic-gate 		console(B_TRUE, ioctl_tset_emsg, "cookie", strerror(err));
41030Sstevel@tonic-gate 
41040Sstevel@tonic-gate 	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
41050Sstevel@tonic-gate 
41060Sstevel@tonic-gate 	return (fd);
41070Sstevel@tonic-gate }
41080Sstevel@tonic-gate 
41090Sstevel@tonic-gate /*
41100Sstevel@tonic-gate  * Create the templates and open an event file descriptor.  We use dup2(2) to
41110Sstevel@tonic-gate  * get these descriptors away from the stdin/stdout/stderr group.
41120Sstevel@tonic-gate  */
41130Sstevel@tonic-gate static void
contracts_init()41140Sstevel@tonic-gate contracts_init()
41150Sstevel@tonic-gate {
41160Sstevel@tonic-gate 	int err, fd;
41170Sstevel@tonic-gate 
41180Sstevel@tonic-gate 	/*
41190Sstevel@tonic-gate 	 * Create & configure a legacy template.  We only want empty events so
41200Sstevel@tonic-gate 	 * we know when to abandon them.
41210Sstevel@tonic-gate 	 */
41220Sstevel@tonic-gate 	legacy_tmpl = contract_make_template(0, CT_PR_EV_EMPTY, CT_PR_EV_HWERR,
41230Sstevel@tonic-gate 	    ORDINARY_COOKIE);
41240Sstevel@tonic-gate 	if (legacy_tmpl >= 0) {
41250Sstevel@tonic-gate 		err = ct_tmpl_activate(legacy_tmpl);
41260Sstevel@tonic-gate 		if (err != 0) {
41270Sstevel@tonic-gate 			(void) close(legacy_tmpl);
41280Sstevel@tonic-gate 			legacy_tmpl = -1;
41290Sstevel@tonic-gate 			console(B_TRUE,
41300Sstevel@tonic-gate 			    "Couldn't activate legacy template (%s); "
41310Sstevel@tonic-gate 			    "legacy services will be in init's contract.\n",
41320Sstevel@tonic-gate 			    strerror(err));
41330Sstevel@tonic-gate 		}
41340Sstevel@tonic-gate 	} else
41350Sstevel@tonic-gate 		console(B_TRUE,
41360Sstevel@tonic-gate 		    "Legacy services will be in init's contract.\n");
41370Sstevel@tonic-gate 
41380Sstevel@tonic-gate 	if (dup2(legacy_tmpl, 255) == -1) {
41390Sstevel@tonic-gate 		console(B_TRUE, "Could not duplicate legacy template: %s.\n",
41400Sstevel@tonic-gate 		    strerror(errno));
41410Sstevel@tonic-gate 	} else {
41420Sstevel@tonic-gate 		(void) close(legacy_tmpl);
41430Sstevel@tonic-gate 		legacy_tmpl = 255;
41440Sstevel@tonic-gate 	}
41450Sstevel@tonic-gate 
41460Sstevel@tonic-gate 	(void) fcntl(legacy_tmpl, F_SETFD, FD_CLOEXEC);
41470Sstevel@tonic-gate 
41480Sstevel@tonic-gate 	startd_tmpl = contract_make_template(0, CT_PR_EV_EMPTY,
41490Sstevel@tonic-gate 	    CT_PR_EV_HWERR | CT_PR_EV_SIGNAL | CT_PR_EV_CORE, STARTD_COOKIE);
41500Sstevel@tonic-gate 
41510Sstevel@tonic-gate 	if (dup2(startd_tmpl, 254) == -1) {
41520Sstevel@tonic-gate 		console(B_TRUE, "Could not duplicate startd template: %s.\n",
41530Sstevel@tonic-gate 		    strerror(errno));
41540Sstevel@tonic-gate 	} else {
41550Sstevel@tonic-gate 		(void) close(startd_tmpl);
41560Sstevel@tonic-gate 		startd_tmpl = 254;
41570Sstevel@tonic-gate 	}
41580Sstevel@tonic-gate 
41590Sstevel@tonic-gate 	(void) fcntl(startd_tmpl, F_SETFD, FD_CLOEXEC);
41600Sstevel@tonic-gate 
41610Sstevel@tonic-gate 	if (legacy_tmpl < 0 && startd_tmpl < 0) {
41620Sstevel@tonic-gate 		/* The creation errors have already been reported. */
41630Sstevel@tonic-gate 		console(B_TRUE,
41640Sstevel@tonic-gate 		    "Ignoring contract events.  Core smf(5) services will not "
41650Sstevel@tonic-gate 		    "be restarted.\n");
41660Sstevel@tonic-gate 		return;
41670Sstevel@tonic-gate 	}
41680Sstevel@tonic-gate 
41690Sstevel@tonic-gate 	/*
41700Sstevel@tonic-gate 	 * Open an event endpoint.
41710Sstevel@tonic-gate 	 */
41720Sstevel@tonic-gate 	do
41730Sstevel@tonic-gate 		fd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY);
41740Sstevel@tonic-gate 	while (fd < 0 && errno == EINTR);
41750Sstevel@tonic-gate 	if (fd < 0) {
41760Sstevel@tonic-gate 		console(B_TRUE,
41770Sstevel@tonic-gate 		    "Couldn't open process pbundle: %s.  Core smf(5) services "
41780Sstevel@tonic-gate 		    "will not be restarted.\n", strerror(errno));
41790Sstevel@tonic-gate 		return;
41800Sstevel@tonic-gate 	}
41810Sstevel@tonic-gate 
41820Sstevel@tonic-gate 	if (dup2(fd, 253) == -1) {
41830Sstevel@tonic-gate 		console(B_TRUE, "Could not duplicate process bundle: %s.\n",
41840Sstevel@tonic-gate 		    strerror(errno));
41850Sstevel@tonic-gate 	} else {
41860Sstevel@tonic-gate 		(void) close(fd);
41870Sstevel@tonic-gate 		fd = 253;
41880Sstevel@tonic-gate 	}
41890Sstevel@tonic-gate 
41900Sstevel@tonic-gate 	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
41910Sstevel@tonic-gate 
41920Sstevel@tonic-gate 	/* Reset in case we've been restarted. */
41930Sstevel@tonic-gate 	(void) ct_event_reset(fd);
41940Sstevel@tonic-gate 
41950Sstevel@tonic-gate 	poll_fds[0].fd = fd;
41960Sstevel@tonic-gate 	poll_fds[0].events = POLLIN;
41970Sstevel@tonic-gate 	poll_nfds = 1;
41980Sstevel@tonic-gate }
41990Sstevel@tonic-gate 
42000Sstevel@tonic-gate static int
contract_getfile(ctid_t id,const char * name,int oflag)42010Sstevel@tonic-gate contract_getfile(ctid_t id, const char *name, int oflag)
42020Sstevel@tonic-gate {
42030Sstevel@tonic-gate 	int fd;
42040Sstevel@tonic-gate 
42050Sstevel@tonic-gate 	do
42060Sstevel@tonic-gate 		fd = contract_open(id, "process", name, oflag);
42070Sstevel@tonic-gate 	while (fd < 0 && errno == EINTR);
42080Sstevel@tonic-gate 
42090Sstevel@tonic-gate 	if (fd < 0)
42100Sstevel@tonic-gate 		console(B_TRUE, "Couldn't open %s for contract %ld: %s.\n",
42110Sstevel@tonic-gate 		    name, id, strerror(errno));
42120Sstevel@tonic-gate 
42130Sstevel@tonic-gate 	return (fd);
42140Sstevel@tonic-gate }
42150Sstevel@tonic-gate 
42160Sstevel@tonic-gate static int
contract_cookie(ctid_t id,uint64_t * cp)42170Sstevel@tonic-gate contract_cookie(ctid_t id, uint64_t *cp)
42180Sstevel@tonic-gate {
42190Sstevel@tonic-gate 	int fd, err;
42200Sstevel@tonic-gate 	ct_stathdl_t sh;
42210Sstevel@tonic-gate 
42220Sstevel@tonic-gate 	fd = contract_getfile(id, "status", O_RDONLY);
42230Sstevel@tonic-gate 	if (fd < 0)
42240Sstevel@tonic-gate 		return (-1);
42250Sstevel@tonic-gate 
42260Sstevel@tonic-gate 	err = ct_status_read(fd, CTD_COMMON, &sh);
42270Sstevel@tonic-gate 	if (err != 0) {
42280Sstevel@tonic-gate 		console(B_TRUE, "Couldn't read status of contract %ld: %s.\n",
42290Sstevel@tonic-gate 		    id, strerror(err));
42300Sstevel@tonic-gate 		(void) close(fd);
42310Sstevel@tonic-gate 		return (-1);
42320Sstevel@tonic-gate 	}
42330Sstevel@tonic-gate 
42340Sstevel@tonic-gate 	(void) close(fd);
42350Sstevel@tonic-gate 
42360Sstevel@tonic-gate 	*cp = ct_status_get_cookie(sh);
42370Sstevel@tonic-gate 
42380Sstevel@tonic-gate 	ct_status_free(sh);
42390Sstevel@tonic-gate 	return (0);
42400Sstevel@tonic-gate }
42410Sstevel@tonic-gate 
42420Sstevel@tonic-gate static void
contract_ack(ct_evthdl_t e)42430Sstevel@tonic-gate contract_ack(ct_evthdl_t e)
42440Sstevel@tonic-gate {
42450Sstevel@tonic-gate 	int fd;
42460Sstevel@tonic-gate 
42470Sstevel@tonic-gate 	if (ct_event_get_flags(e) & CTE_INFO)
42480Sstevel@tonic-gate 		return;
42490Sstevel@tonic-gate 
42500Sstevel@tonic-gate 	fd = contract_getfile(ct_event_get_ctid(e), "ctl", O_WRONLY);
42510Sstevel@tonic-gate 	if (fd < 0)
42520Sstevel@tonic-gate 		return;
42530Sstevel@tonic-gate 
42540Sstevel@tonic-gate 	(void) ct_ctl_ack(fd, ct_event_get_evid(e));
42550Sstevel@tonic-gate 	(void) close(fd);
42560Sstevel@tonic-gate }
42570Sstevel@tonic-gate 
42580Sstevel@tonic-gate /*
42590Sstevel@tonic-gate  * Process a contract event.
42600Sstevel@tonic-gate  */
42610Sstevel@tonic-gate static void
contract_event(struct pollfd * poll)42620Sstevel@tonic-gate contract_event(struct pollfd *poll)
42630Sstevel@tonic-gate {
42640Sstevel@tonic-gate 	ct_evthdl_t e;
42650Sstevel@tonic-gate 	int err;
42660Sstevel@tonic-gate 	ctid_t ctid;
42670Sstevel@tonic-gate 
42680Sstevel@tonic-gate 	if (!(poll->revents & POLLIN)) {
42690Sstevel@tonic-gate 		if (poll->revents & POLLERR)
42700Sstevel@tonic-gate 			console(B_TRUE,
42710Sstevel@tonic-gate 			    "Unknown poll error on my process contract "
42720Sstevel@tonic-gate 			    "pbundle.\n");
42730Sstevel@tonic-gate 		return;
42740Sstevel@tonic-gate 	}
42750Sstevel@tonic-gate 
42760Sstevel@tonic-gate 	err = ct_event_read(poll->fd, &e);
42770Sstevel@tonic-gate 	if (err != 0) {
42780Sstevel@tonic-gate 		console(B_TRUE, "Error retrieving contract event: %s.\n",
42790Sstevel@tonic-gate 		    strerror(err));
42800Sstevel@tonic-gate 		return;
42810Sstevel@tonic-gate 	}
42820Sstevel@tonic-gate 
42830Sstevel@tonic-gate 	ctid = ct_event_get_ctid(e);
42840Sstevel@tonic-gate 
42850Sstevel@tonic-gate 	if (ct_event_get_type(e) == CT_PR_EV_EMPTY) {
42860Sstevel@tonic-gate 		uint64_t cookie;
42870Sstevel@tonic-gate 		int ret, abandon = 1;
42880Sstevel@tonic-gate 
42890Sstevel@tonic-gate 		/* If it's svc.startd, restart it.  Else, abandon. */
42900Sstevel@tonic-gate 		ret = contract_cookie(ctid, &cookie);
42910Sstevel@tonic-gate 
42920Sstevel@tonic-gate 		if (ret == 0) {
42930Sstevel@tonic-gate 			if (cookie == STARTD_COOKIE &&
42940Sstevel@tonic-gate 			    do_restart_startd) {
42950Sstevel@tonic-gate 				if (smf_debug)
42960Sstevel@tonic-gate 					console(B_TRUE, "Restarting "
42970Sstevel@tonic-gate 					    "svc.startd.\n");
42980Sstevel@tonic-gate 
42990Sstevel@tonic-gate 				/*
43000Sstevel@tonic-gate 				 * Account for the failure.  If the failure rate
43010Sstevel@tonic-gate 				 * exceeds a threshold, then drop to maintenance
43020Sstevel@tonic-gate 				 * mode.
43030Sstevel@tonic-gate 				 */
43040Sstevel@tonic-gate 				startd_record_failure();
43050Sstevel@tonic-gate 				if (startd_failure_rate_critical())
43060Sstevel@tonic-gate 					enter_maintenance();
43070Sstevel@tonic-gate 
43080Sstevel@tonic-gate 				if (startd_tmpl < 0)
43090Sstevel@tonic-gate 					console(B_TRUE,
43100Sstevel@tonic-gate 					    "Restarting svc.startd in "
43110Sstevel@tonic-gate 					    "improper contract (bad "
43120Sstevel@tonic-gate 					    "template).\n");
43130Sstevel@tonic-gate 
43140Sstevel@tonic-gate 				(void) startd_run(startd_cline, startd_tmpl,
43150Sstevel@tonic-gate 				    ctid);
43160Sstevel@tonic-gate 
43170Sstevel@tonic-gate 				abandon = 0;
43180Sstevel@tonic-gate 			}
43190Sstevel@tonic-gate 		}
43200Sstevel@tonic-gate 
43210Sstevel@tonic-gate 		if (abandon && (err = contract_abandon_id(ctid))) {
43220Sstevel@tonic-gate 			console(B_TRUE, "Couldn't abandon contract %ld: %s.\n",
43230Sstevel@tonic-gate 			    ctid, strerror(err));
43240Sstevel@tonic-gate 		}
43250Sstevel@tonic-gate 
43260Sstevel@tonic-gate 		/*
43270Sstevel@tonic-gate 		 * No need to acknowledge the event since either way the
43280Sstevel@tonic-gate 		 * originating contract should be abandoned.
43290Sstevel@tonic-gate 		 */
43300Sstevel@tonic-gate 	} else {
43310Sstevel@tonic-gate 		console(B_TRUE,
43320Sstevel@tonic-gate 		    "Received contract event of unexpected type %d from "
43330Sstevel@tonic-gate 		    "contract %ld.\n", ct_event_get_type(e), ctid);
43340Sstevel@tonic-gate 
43350Sstevel@tonic-gate 		if ((ct_event_get_flags(e) & (CTE_INFO | CTE_ACK)) == 0)
43360Sstevel@tonic-gate 			/* Allow unexpected critical events to be released. */
43370Sstevel@tonic-gate 			contract_ack(e);
43380Sstevel@tonic-gate 	}
43390Sstevel@tonic-gate 
43400Sstevel@tonic-gate 	ct_event_free(e);
43410Sstevel@tonic-gate }
43420Sstevel@tonic-gate 
43430Sstevel@tonic-gate /*
43440Sstevel@tonic-gate  * svc.startd(1M) Management
43450Sstevel@tonic-gate  */
43460Sstevel@tonic-gate 
43470Sstevel@tonic-gate /*
43480Sstevel@tonic-gate  * (Re)start svc.startd(1M).  old_ctid should be the contract ID of the old
43490Sstevel@tonic-gate  * contract, or 0 if we're starting it for the first time.  If wait is true
43500Sstevel@tonic-gate  * we'll wait for and return the exit value of the child.
43510Sstevel@tonic-gate  */
43520Sstevel@tonic-gate static int
startd_run(const char * cline,int tmpl,ctid_t old_ctid)43530Sstevel@tonic-gate startd_run(const char *cline, int tmpl, ctid_t old_ctid)
43540Sstevel@tonic-gate {
43550Sstevel@tonic-gate 	int err, i, ret, did_activate;
43560Sstevel@tonic-gate 	pid_t pid;
43570Sstevel@tonic-gate 	struct stat sb;
43580Sstevel@tonic-gate 
43590Sstevel@tonic-gate 	if (cline[0] == '\0')
43600Sstevel@tonic-gate 		return (-1);
43610Sstevel@tonic-gate 
43620Sstevel@tonic-gate 	/*
43630Sstevel@tonic-gate 	 * Don't restart startd if the system is rebooting or shutting down.
43640Sstevel@tonic-gate 	 */
43650Sstevel@tonic-gate 	do {
43660Sstevel@tonic-gate 		ret = stat("/etc/svc/volatile/resetting", &sb);
43670Sstevel@tonic-gate 	} while (ret == -1 && errno == EINTR);
43680Sstevel@tonic-gate 
43690Sstevel@tonic-gate 	if (ret == 0) {
43700Sstevel@tonic-gate 		if (smf_debug)
43710Sstevel@tonic-gate 			console(B_TRUE, "Quiescing for reboot.\n");
43720Sstevel@tonic-gate 		(void) pause();
43730Sstevel@tonic-gate 		return (-1);
43740Sstevel@tonic-gate 	}
43750Sstevel@tonic-gate 
43760Sstevel@tonic-gate 	err = ct_pr_tmpl_set_transfer(tmpl, old_ctid);
43770Sstevel@tonic-gate 	if (err == EINVAL) {
43780Sstevel@tonic-gate 		console(B_TRUE, "Remake startd_tmpl; reattempt transfer.\n");
43790Sstevel@tonic-gate 		tmpl = startd_tmpl = contract_make_template(0, CT_PR_EV_EMPTY,
43800Sstevel@tonic-gate 		    CT_PR_EV_HWERR, STARTD_COOKIE);
43810Sstevel@tonic-gate 
43820Sstevel@tonic-gate 		err = ct_pr_tmpl_set_transfer(tmpl, old_ctid);
43830Sstevel@tonic-gate 	}
43840Sstevel@tonic-gate 	if (err != 0) {
43850Sstevel@tonic-gate 		console(B_TRUE,
43860Sstevel@tonic-gate 		    "Couldn't set transfer parameter of contract template: "
43870Sstevel@tonic-gate 		    "%s.\n", strerror(err));
43880Sstevel@tonic-gate 	}
43890Sstevel@tonic-gate 
43906073Sacruz 	if ((err = ct_pr_tmpl_set_svc_fmri(startd_tmpl,
43916073Sacruz 	    SCF_SERVICE_STARTD)) != 0)
43926073Sacruz 		console(B_TRUE,
43936073Sacruz 		    "Can not set svc_fmri in contract template: %s\n",
43946073Sacruz 		    strerror(err));
43956073Sacruz 	if ((err = ct_pr_tmpl_set_svc_aux(startd_tmpl,
43966073Sacruz 	    startd_svc_aux)) != 0)
43976073Sacruz 		console(B_TRUE,
43986073Sacruz 		    "Can not set svc_aux in contract template: %s\n",
43996073Sacruz 		    strerror(err));
44000Sstevel@tonic-gate 	did_activate = !(ct_tmpl_activate(tmpl));
44010Sstevel@tonic-gate 	if (!did_activate)
44020Sstevel@tonic-gate 		console(B_TRUE,
44030Sstevel@tonic-gate 		    "Template activation failed; not starting \"%s\" in "
44040Sstevel@tonic-gate 		    "proper contract.\n", cline);
44050Sstevel@tonic-gate 
440610083SRoger.Faulkner@Sun.COM 	/* Hold SIGCLD so we can wait if necessary. */
440710083SRoger.Faulkner@Sun.COM 	(void) sighold(SIGCLD);
44080Sstevel@tonic-gate 
44090Sstevel@tonic-gate 	while ((pid = fork()) < 0) {
44100Sstevel@tonic-gate 		if (errno == EPERM) {
44110Sstevel@tonic-gate 			console(B_TRUE, "Insufficient permission to fork.\n");
44120Sstevel@tonic-gate 
44130Sstevel@tonic-gate 			/* Now that's a doozy. */
44140Sstevel@tonic-gate 			exit(1);
44150Sstevel@tonic-gate 		}
44160Sstevel@tonic-gate 
44170Sstevel@tonic-gate 		console(B_TRUE,
44180Sstevel@tonic-gate 		    "fork() for svc.startd failed: %s.  Will retry in 1 "
44190Sstevel@tonic-gate 		    "second...\n", strerror(errno));
44200Sstevel@tonic-gate 
44210Sstevel@tonic-gate 		(void) sleep(1);
44220Sstevel@tonic-gate 
44230Sstevel@tonic-gate 		/* Eventually give up? */
44240Sstevel@tonic-gate 	}
44250Sstevel@tonic-gate 
44260Sstevel@tonic-gate 	if (pid == 0) {
44270Sstevel@tonic-gate 		/* child */
44280Sstevel@tonic-gate 
44290Sstevel@tonic-gate 		/* See the comment in efork() */
44300Sstevel@tonic-gate 		for (i = SIGHUP; i <= SIGRTMAX; ++i) {
44310Sstevel@tonic-gate 			if (i == SIGTTOU || i == SIGTTIN || i == SIGTSTP)
44320Sstevel@tonic-gate 				(void) sigset(i, SIG_IGN);
44330Sstevel@tonic-gate 			else
44340Sstevel@tonic-gate 				(void) sigset(i, SIG_DFL);
44350Sstevel@tonic-gate 		}
44360Sstevel@tonic-gate 
44370Sstevel@tonic-gate 		if (smf_options != NULL) {
44380Sstevel@tonic-gate 			/* Put smf_options in the environment. */
44390Sstevel@tonic-gate 			glob_envp[glob_envn] =
44400Sstevel@tonic-gate 			    malloc(sizeof ("SMF_OPTIONS=") - 1 +
44418735SEnrico.Perla@Sun.COM 			    strlen(smf_options) + 1);
44420Sstevel@tonic-gate 
44430Sstevel@tonic-gate 			if (glob_envp[glob_envn] != NULL) {
44440Sstevel@tonic-gate 				/* LINTED */
44450Sstevel@tonic-gate 				(void) sprintf(glob_envp[glob_envn],
44460Sstevel@tonic-gate 				    "SMF_OPTIONS=%s", smf_options);
44470Sstevel@tonic-gate 				glob_envp[glob_envn+1] = NULL;
44480Sstevel@tonic-gate 			} else {
44490Sstevel@tonic-gate 				console(B_TRUE,
44500Sstevel@tonic-gate 				    "Could not set SMF_OPTIONS (%s).\n",
44510Sstevel@tonic-gate 				    strerror(errno));
44520Sstevel@tonic-gate 			}
44530Sstevel@tonic-gate 		}
44540Sstevel@tonic-gate 
44550Sstevel@tonic-gate 		if (smf_debug)
44560Sstevel@tonic-gate 			console(B_TRUE, "Executing svc.startd\n");
44570Sstevel@tonic-gate 
44580Sstevel@tonic-gate 		(void) execle(SH, "INITSH", "-c", cline, NULL, glob_envp);
44590Sstevel@tonic-gate 
44600Sstevel@tonic-gate 		console(B_TRUE, "Could not exec \"%s\" (%s).\n", SH,
44610Sstevel@tonic-gate 		    strerror(errno));
44620Sstevel@tonic-gate 
44630Sstevel@tonic-gate 		exit(1);
44640Sstevel@tonic-gate 	}
44650Sstevel@tonic-gate 
44660Sstevel@tonic-gate 	/* parent */
44670Sstevel@tonic-gate 
44680Sstevel@tonic-gate 	if (did_activate) {
44690Sstevel@tonic-gate 		if (legacy_tmpl < 0 || ct_tmpl_activate(legacy_tmpl) != 0)
44700Sstevel@tonic-gate 			(void) ct_tmpl_clear(tmpl);
44710Sstevel@tonic-gate 	}
44720Sstevel@tonic-gate 
44730Sstevel@tonic-gate 	/* Clear the old_ctid reference so the kernel can reclaim it. */
44740Sstevel@tonic-gate 	if (old_ctid != 0)
44750Sstevel@tonic-gate 		(void) ct_pr_tmpl_set_transfer(tmpl, 0);
44760Sstevel@tonic-gate 
447710083SRoger.Faulkner@Sun.COM 	(void) sigrelse(SIGCLD);
44780Sstevel@tonic-gate 
44790Sstevel@tonic-gate 	return (0);
44800Sstevel@tonic-gate }
44810Sstevel@tonic-gate 
44820Sstevel@tonic-gate /*
44830Sstevel@tonic-gate  * void startd_record_failure(void)
44840Sstevel@tonic-gate  *   Place the current time in our circular array of svc.startd failures.
44850Sstevel@tonic-gate  */
44860Sstevel@tonic-gate void
startd_record_failure()44870Sstevel@tonic-gate startd_record_failure()
44880Sstevel@tonic-gate {
44890Sstevel@tonic-gate 	int index = startd_failure_index++ % NSTARTD_FAILURE_TIMES;
44900Sstevel@tonic-gate 
44910Sstevel@tonic-gate 	startd_failure_time[index] = gethrtime();
44920Sstevel@tonic-gate }
44930Sstevel@tonic-gate 
44940Sstevel@tonic-gate /*
44950Sstevel@tonic-gate  * int startd_failure_rate_critical(void)
44960Sstevel@tonic-gate  *   Return true if the average failure interval is less than the permitted
44970Sstevel@tonic-gate  *   interval.  Implicit success if insufficient measurements for an average
44980Sstevel@tonic-gate  *   exist.
44990Sstevel@tonic-gate  */
45000Sstevel@tonic-gate int
startd_failure_rate_critical()45010Sstevel@tonic-gate startd_failure_rate_critical()
45020Sstevel@tonic-gate {
45030Sstevel@tonic-gate 	int n = startd_failure_index;
45040Sstevel@tonic-gate 	hrtime_t avg_ns = 0;
45050Sstevel@tonic-gate 
45060Sstevel@tonic-gate 	if (startd_failure_index < NSTARTD_FAILURE_TIMES)
45070Sstevel@tonic-gate 		return (0);
45080Sstevel@tonic-gate 
45090Sstevel@tonic-gate 	avg_ns =
45100Sstevel@tonic-gate 	    (startd_failure_time[(n - 1) % NSTARTD_FAILURE_TIMES] -
45110Sstevel@tonic-gate 	    startd_failure_time[n % NSTARTD_FAILURE_TIMES]) /
45120Sstevel@tonic-gate 	    NSTARTD_FAILURE_TIMES;
45130Sstevel@tonic-gate 
45140Sstevel@tonic-gate 	return (avg_ns < STARTD_FAILURE_RATE_NS);
45150Sstevel@tonic-gate }
45160Sstevel@tonic-gate 
45170Sstevel@tonic-gate /*
45180Sstevel@tonic-gate  * returns string that must be free'd
45190Sstevel@tonic-gate  */
45200Sstevel@tonic-gate 
45210Sstevel@tonic-gate static char
audit_boot_msg()45220Sstevel@tonic-gate *audit_boot_msg()
45230Sstevel@tonic-gate {
45240Sstevel@tonic-gate 	char		*b, *p;
45250Sstevel@tonic-gate 	char		desc[] = "booted";
45260Sstevel@tonic-gate 	zoneid_t	zid = getzoneid();
45270Sstevel@tonic-gate 
45280Sstevel@tonic-gate 	b = malloc(sizeof (desc) + MAXNAMELEN + 3);
45290Sstevel@tonic-gate 	if (b == NULL)
45300Sstevel@tonic-gate 		return (b);
45310Sstevel@tonic-gate 
45320Sstevel@tonic-gate 	p = b;
45330Sstevel@tonic-gate 	p += strlcpy(p, desc, sizeof (desc));
45340Sstevel@tonic-gate 	if (zid != GLOBAL_ZONEID) {
45350Sstevel@tonic-gate 		p += strlcpy(p, ": ", 3);
45360Sstevel@tonic-gate 		(void) getzonenamebyid(zid, p, MAXNAMELEN);
45370Sstevel@tonic-gate 	}
45380Sstevel@tonic-gate 	return (b);
45390Sstevel@tonic-gate }
45400Sstevel@tonic-gate 
45410Sstevel@tonic-gate /*
45420Sstevel@tonic-gate  * Generate AUE_init_solaris audit record.  Return 1 if
45430Sstevel@tonic-gate  * auditing is enabled in case the caller cares.
45440Sstevel@tonic-gate  *
45450Sstevel@tonic-gate  * In the case of userint() or a local zone invocation of
45460Sstevel@tonic-gate  * one_true_init, the process initially contains the audit
45470Sstevel@tonic-gate  * characteristics of the process that invoked init.  The first pass
45480Sstevel@tonic-gate  * through here uses those characteristics then for the case of
45490Sstevel@tonic-gate  * one_true_init in a local zone, clears them so subsequent system
45500Sstevel@tonic-gate  * state changes won't be attributed to the person who booted the
45510Sstevel@tonic-gate  * zone.
45520Sstevel@tonic-gate  */
45530Sstevel@tonic-gate static int
audit_put_record(int pass_fail,int status,char * msg)45540Sstevel@tonic-gate audit_put_record(int pass_fail, int status, char *msg)
45550Sstevel@tonic-gate {
45560Sstevel@tonic-gate 	adt_session_data_t	*ah;
45570Sstevel@tonic-gate 	adt_event_data_t	*event;
45580Sstevel@tonic-gate 
45590Sstevel@tonic-gate 	if (!adt_audit_enabled())
45600Sstevel@tonic-gate 		return (0);
45610Sstevel@tonic-gate 
45620Sstevel@tonic-gate 	/*
45630Sstevel@tonic-gate 	 * the PROC_DATA picks up the context to tell whether this is
45640Sstevel@tonic-gate 	 * an attributed record (auid = -2 is unattributed)
45650Sstevel@tonic-gate 	 */
45660Sstevel@tonic-gate 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) {
45670Sstevel@tonic-gate 		console(B_TRUE, "audit failure:  %s\n", strerror(errno));
45680Sstevel@tonic-gate 		return (1);
45690Sstevel@tonic-gate 	}
45700Sstevel@tonic-gate 	event = adt_alloc_event(ah, ADT_init_solaris);
45710Sstevel@tonic-gate 	if (event == NULL) {
45720Sstevel@tonic-gate 		console(B_TRUE, "audit failure:  %s\n", strerror(errno));
45730Sstevel@tonic-gate 		(void) adt_end_session(ah);
45740Sstevel@tonic-gate 		return (1);
45750Sstevel@tonic-gate 	}
45760Sstevel@tonic-gate 	event->adt_init_solaris.info = msg;	/* NULL is ok here */
45770Sstevel@tonic-gate 
45780Sstevel@tonic-gate 	if (adt_put_event(event, pass_fail, status)) {
45790Sstevel@tonic-gate 		console(B_TRUE, "audit failure:  %s\n", strerror(errno));
45800Sstevel@tonic-gate 		(void) adt_end_session(ah);
45810Sstevel@tonic-gate 		return (1);
45820Sstevel@tonic-gate 	}
45830Sstevel@tonic-gate 	adt_free_event(event);
45840Sstevel@tonic-gate 
45850Sstevel@tonic-gate 	(void) adt_end_session(ah);
45860Sstevel@tonic-gate 
45870Sstevel@tonic-gate 	return (1);
45880Sstevel@tonic-gate }
4589