xref: /onnv-gate/usr/src/cmd/svc/startd/fork.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * fork.c - safe forking for svc.startd
31*0Sstevel@tonic-gate  *
32*0Sstevel@tonic-gate  * fork_configd() and fork_sulogin() are related, special cases that handle the
33*0Sstevel@tonic-gate  * spawning of specific client processes for svc.startd.
34*0Sstevel@tonic-gate  */
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate #include <sys/contract/process.h>
37*0Sstevel@tonic-gate #include <sys/corectl.h>
38*0Sstevel@tonic-gate #include <sys/ctfs.h>
39*0Sstevel@tonic-gate #include <sys/stat.h>
40*0Sstevel@tonic-gate #include <sys/types.h>
41*0Sstevel@tonic-gate #include <sys/uio.h>
42*0Sstevel@tonic-gate #include <sys/wait.h>
43*0Sstevel@tonic-gate #include <assert.h>
44*0Sstevel@tonic-gate #include <errno.h>
45*0Sstevel@tonic-gate #include <fcntl.h>
46*0Sstevel@tonic-gate #include <libcontract.h>
47*0Sstevel@tonic-gate #include <libcontract_priv.h>
48*0Sstevel@tonic-gate #include <limits.h>
49*0Sstevel@tonic-gate #include <port.h>
50*0Sstevel@tonic-gate #include <signal.h>
51*0Sstevel@tonic-gate #include <stdarg.h>
52*0Sstevel@tonic-gate #include <stdio.h>
53*0Sstevel@tonic-gate #include <stdlib.h>
54*0Sstevel@tonic-gate #include <string.h>
55*0Sstevel@tonic-gate #include <unistd.h>
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate #include "configd_exit.h"
58*0Sstevel@tonic-gate #include "protocol.h"
59*0Sstevel@tonic-gate #include "startd.h"
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate pid_t
62*0Sstevel@tonic-gate startd_fork1(int *forkerr)
63*0Sstevel@tonic-gate {
64*0Sstevel@tonic-gate 	pid_t p;
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate 	/*
67*0Sstevel@tonic-gate 	 * prefork stack
68*0Sstevel@tonic-gate 	 */
69*0Sstevel@tonic-gate 	wait_prefork();
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate 	p = fork1();
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate 	if (p == -1 && forkerr != NULL)
74*0Sstevel@tonic-gate 		*forkerr = errno;
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate 	/*
77*0Sstevel@tonic-gate 	 * postfork stack
78*0Sstevel@tonic-gate 	 */
79*0Sstevel@tonic-gate 	wait_postfork(p);
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate 	return (p);
82*0Sstevel@tonic-gate }
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate /*
85*0Sstevel@tonic-gate  * void fork_mount(char *, char *)
86*0Sstevel@tonic-gate  *   Run mount(1M) with the given options and mount point.  (mount(1M) has much
87*0Sstevel@tonic-gate  *   hidden knowledge; it's much less correct to reimplement that logic here to
88*0Sstevel@tonic-gate  *   save a fork(2)/exec(2) invocation.)
89*0Sstevel@tonic-gate  */
90*0Sstevel@tonic-gate int
91*0Sstevel@tonic-gate fork_mount(char *path, char *opts)
92*0Sstevel@tonic-gate {
93*0Sstevel@tonic-gate 	pid_t pid;
94*0Sstevel@tonic-gate 	uint_t tries = 0;
95*0Sstevel@tonic-gate 	int status;
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate 	for (pid = fork1(); pid == -1; pid = fork1()) {
98*0Sstevel@tonic-gate 		if (++tries > MAX_MOUNT_RETRIES)
99*0Sstevel@tonic-gate 			return (-1);
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate 		(void) sleep(tries);
102*0Sstevel@tonic-gate 	}
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate 	if (pid != 0) {
105*0Sstevel@tonic-gate 		(void) waitpid(pid, &status, 0);
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate 		/*
108*0Sstevel@tonic-gate 		 * If our mount(1M) invocation exited by peculiar means, or with
109*0Sstevel@tonic-gate 		 * a non-zero status, our mount likelihood is low.
110*0Sstevel@tonic-gate 		 */
111*0Sstevel@tonic-gate 		if (!WIFEXITED(status) ||
112*0Sstevel@tonic-gate 		    WEXITSTATUS(status) != 0)
113*0Sstevel@tonic-gate 			return (-1);
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate 		return (0);
116*0Sstevel@tonic-gate 	}
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate 	(void) execl("/sbin/mount", "mount", "-o", opts, path, NULL);
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 	return (-1);
121*0Sstevel@tonic-gate }
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate /*
124*0Sstevel@tonic-gate  * pid_t fork_common(...)
125*0Sstevel@tonic-gate  *   Common routine used by fork_sulogin and fork_configd to fork a
126*0Sstevel@tonic-gate  *   process in a contract with the provided terms.  Invokes
127*0Sstevel@tonic-gate  *   fork_sulogin (with its no-fork argument set) on errors.
128*0Sstevel@tonic-gate  */
129*0Sstevel@tonic-gate static pid_t
130*0Sstevel@tonic-gate fork_common(const char *name, int retries, ctid_t *ctidp,
131*0Sstevel@tonic-gate     uint_t inf, uint_t crit, uint_t fatal, uint_t param, uint64_t cookie)
132*0Sstevel@tonic-gate {
133*0Sstevel@tonic-gate 	uint_t tries = 0;
134*0Sstevel@tonic-gate 	int ctfd, err;
135*0Sstevel@tonic-gate 	pid_t pid;
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	/*
138*0Sstevel@tonic-gate 	 * Establish process contract terms.
139*0Sstevel@tonic-gate 	 */
140*0Sstevel@tonic-gate 	if ((ctfd = open64(CTFS_ROOT "/process/template", O_RDWR)) == -1) {
141*0Sstevel@tonic-gate 		fork_sulogin(B_TRUE, "Could not open process contract template "
142*0Sstevel@tonic-gate 		    "for %s: %s\n", name, strerror(errno));
143*0Sstevel@tonic-gate 		/* NOTREACHED */
144*0Sstevel@tonic-gate 	}
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate 	err = ct_tmpl_set_critical(ctfd, crit);
147*0Sstevel@tonic-gate 	err |= ct_pr_tmpl_set_fatal(ctfd, fatal);
148*0Sstevel@tonic-gate 	err |= ct_tmpl_set_informative(ctfd, inf);
149*0Sstevel@tonic-gate 	err |= ct_pr_tmpl_set_param(ctfd, param);
150*0Sstevel@tonic-gate 	err |= ct_tmpl_set_cookie(ctfd, cookie);
151*0Sstevel@tonic-gate 	if (err) {
152*0Sstevel@tonic-gate 		(void) close(ctfd);
153*0Sstevel@tonic-gate 		fork_sulogin(B_TRUE, "Could not set %s process contract "
154*0Sstevel@tonic-gate 		    "terms\n", name);
155*0Sstevel@tonic-gate 		/* NOTREACHED */
156*0Sstevel@tonic-gate 	}
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 	if (err = ct_tmpl_activate(ctfd)) {
159*0Sstevel@tonic-gate 		(void) close(ctfd);
160*0Sstevel@tonic-gate 		fork_sulogin(B_TRUE, "Could not activate %s process contract "
161*0Sstevel@tonic-gate 		    "template: %s\n", name, strerror(err));
162*0Sstevel@tonic-gate 		/* NOTREACHED */
163*0Sstevel@tonic-gate 	}
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate 	/*
166*0Sstevel@tonic-gate 	 * Attempt to fork "retries" times.
167*0Sstevel@tonic-gate 	 */
168*0Sstevel@tonic-gate 	for (pid = fork1(); pid == -1; pid = fork1()) {
169*0Sstevel@tonic-gate 		if (++tries > retries) {
170*0Sstevel@tonic-gate 			/*
171*0Sstevel@tonic-gate 			 * When we exit the sulogin session, init(1M)
172*0Sstevel@tonic-gate 			 * will restart svc.startd(1M).
173*0Sstevel@tonic-gate 			 */
174*0Sstevel@tonic-gate 			err = errno;
175*0Sstevel@tonic-gate 			(void) ct_tmpl_clear(ctfd);
176*0Sstevel@tonic-gate 			(void) close(ctfd);
177*0Sstevel@tonic-gate 			fork_sulogin(B_TRUE, "Could not fork to start %s: %s\n",
178*0Sstevel@tonic-gate 			    name, strerror(err));
179*0Sstevel@tonic-gate 			/* NOTREACHED */
180*0Sstevel@tonic-gate 		}
181*0Sstevel@tonic-gate 		(void) sleep(tries);
182*0Sstevel@tonic-gate 	}
183*0Sstevel@tonic-gate 
184*0Sstevel@tonic-gate 	/*
185*0Sstevel@tonic-gate 	 * Clean up, return pid and ctid.
186*0Sstevel@tonic-gate 	 */
187*0Sstevel@tonic-gate 	if (pid != 0 && (errno = contract_latest(ctidp)) != 0)
188*0Sstevel@tonic-gate 		uu_die("Could not get new contract id for %s\n", name);
189*0Sstevel@tonic-gate 	(void) ct_tmpl_clear(ctfd);
190*0Sstevel@tonic-gate 	(void) close(ctfd);
191*0Sstevel@tonic-gate 
192*0Sstevel@tonic-gate 	return (pid);
193*0Sstevel@tonic-gate }
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate /*
196*0Sstevel@tonic-gate  * void fork_sulogin(boolean_t, const char *, ...)
197*0Sstevel@tonic-gate  *   When we are invoked with the -s flag from boot (or run into an unfixable
198*0Sstevel@tonic-gate  *   situation), we run a private copy of sulogin.  When the sulogin session
199*0Sstevel@tonic-gate  *   is ended, we continue.  This is the last fallback action for system
200*0Sstevel@tonic-gate  *   maintenance.
201*0Sstevel@tonic-gate  *
202*0Sstevel@tonic-gate  *   If immediate is true, fork_sulogin() executes sulogin(1M) directly, without
203*0Sstevel@tonic-gate  *   forking.
204*0Sstevel@tonic-gate  *
205*0Sstevel@tonic-gate  *   Because fork_sulogin() is needed potentially before we daemonize, we leave
206*0Sstevel@tonic-gate  *   it outside the wait_register() framework.
207*0Sstevel@tonic-gate  */
208*0Sstevel@tonic-gate /*PRINTFLIKE2*/
209*0Sstevel@tonic-gate void
210*0Sstevel@tonic-gate fork_sulogin(boolean_t immediate, const char *format, ...)
211*0Sstevel@tonic-gate {
212*0Sstevel@tonic-gate 	va_list args;
213*0Sstevel@tonic-gate 	int i, fd_console;
214*0Sstevel@tonic-gate 
215*0Sstevel@tonic-gate 	(void) printf("Requesting System Maintenance Mode\n");
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate 	if (!booting_to_single_user)
218*0Sstevel@tonic-gate 		(void) printf("(See /lib/svc/share/README for more "
219*0Sstevel@tonic-gate 		    "information.)\n");
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 	va_start(args, format);
222*0Sstevel@tonic-gate 	(void) vprintf(format, args);
223*0Sstevel@tonic-gate 	va_end(args);
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate 	if (!immediate) {
226*0Sstevel@tonic-gate 		ctid_t	ctid;
227*0Sstevel@tonic-gate 		pid_t	pid;
228*0Sstevel@tonic-gate 
229*0Sstevel@tonic-gate 		pid = fork_common("sulogin", MAX_SULOGIN_RETRIES, &ctid,
230*0Sstevel@tonic-gate 		    CT_PR_EV_HWERR, 0, CT_PR_EV_HWERR, CT_PR_PGRPONLY,
231*0Sstevel@tonic-gate 		    SULOGIN_COOKIE);
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 		if (pid != 0) {
234*0Sstevel@tonic-gate 			(void) waitpid(pid, NULL, 0);
235*0Sstevel@tonic-gate 			contract_abandon(ctid);
236*0Sstevel@tonic-gate 			return;
237*0Sstevel@tonic-gate 		}
238*0Sstevel@tonic-gate 		/* close all inherited fds */
239*0Sstevel@tonic-gate 		closefrom(0);
240*0Sstevel@tonic-gate 	} else {
241*0Sstevel@tonic-gate 		(void) printf("Directly executing sulogin.\n");
242*0Sstevel@tonic-gate 		/*
243*0Sstevel@tonic-gate 		 * Can't call closefrom() in this MT section
244*0Sstevel@tonic-gate 		 * so safely close a minimum set of fds.
245*0Sstevel@tonic-gate 		 */
246*0Sstevel@tonic-gate 		for (i = 0; i < 3; i++)
247*0Sstevel@tonic-gate 			(void) close(i);
248*0Sstevel@tonic-gate 	}
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate 	(void) setpgrp();
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 	/* open the console for sulogin */
253*0Sstevel@tonic-gate 	if ((fd_console = open("/dev/console", O_RDWR)) >= 0) {
254*0Sstevel@tonic-gate 		if (fd_console != STDIN_FILENO)
255*0Sstevel@tonic-gate 			while (dup2(fd_console, STDIN_FILENO) < 0 &&
256*0Sstevel@tonic-gate 			    errno == EINTR)
257*0Sstevel@tonic-gate 				;
258*0Sstevel@tonic-gate 		if (fd_console != STDOUT_FILENO)
259*0Sstevel@tonic-gate 			while (dup2(fd_console, STDOUT_FILENO) < 0 &&
260*0Sstevel@tonic-gate 			    errno == EINTR)
261*0Sstevel@tonic-gate 				;
262*0Sstevel@tonic-gate 		if (fd_console != STDERR_FILENO)
263*0Sstevel@tonic-gate 			while (dup2(fd_console, STDERR_FILENO) < 0 &&
264*0Sstevel@tonic-gate 			    errno == EINTR)
265*0Sstevel@tonic-gate 				;
266*0Sstevel@tonic-gate 		if (fd_console > 2)
267*0Sstevel@tonic-gate 			(void) close(fd_console);
268*0Sstevel@tonic-gate 	}
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	(void) execl("/sbin/sulogin", "sulogin", NULL);
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 	uu_warn("Could not exec() sulogin");
273*0Sstevel@tonic-gate 
274*0Sstevel@tonic-gate 	exit(1);
275*0Sstevel@tonic-gate }
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate #define	CONFIGD_PATH	"/lib/svc/bin/svc.configd"
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate /*
280*0Sstevel@tonic-gate  * void fork_configd(int status)
281*0Sstevel@tonic-gate  *   We are interested in exit events (since the parent's exiting means configd
282*0Sstevel@tonic-gate  *   is ready to run and since the child's exiting indicates an error case) and
283*0Sstevel@tonic-gate  *   in empty events.  This means we have a unique template for initiating
284*0Sstevel@tonic-gate  *   configd.
285*0Sstevel@tonic-gate  */
286*0Sstevel@tonic-gate /*ARGSUSED*/
287*0Sstevel@tonic-gate void
288*0Sstevel@tonic-gate fork_configd(int exitstatus)
289*0Sstevel@tonic-gate {
290*0Sstevel@tonic-gate 	pid_t pid;
291*0Sstevel@tonic-gate 	ctid_t ctid = -1;
292*0Sstevel@tonic-gate 	int err;
293*0Sstevel@tonic-gate 	char path[PATH_MAX];
294*0Sstevel@tonic-gate 
295*0Sstevel@tonic-gate retry:
296*0Sstevel@tonic-gate 	log_framework(LOG_DEBUG, "fork_configd trying to start svc.configd\n");
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate 	/*
299*0Sstevel@tonic-gate 	 * If we're retrying, we will have an old contract lying around
300*0Sstevel@tonic-gate 	 * from the failure.  Since we're going to be creating a new
301*0Sstevel@tonic-gate 	 * contract shortly, we abandon the old one now.
302*0Sstevel@tonic-gate 	 */
303*0Sstevel@tonic-gate 	if (ctid != -1)
304*0Sstevel@tonic-gate 		contract_abandon(ctid);
305*0Sstevel@tonic-gate 	ctid = -1;
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	pid = fork_common("svc.configd", MAX_CONFIGD_RETRIES, &ctid,
308*0Sstevel@tonic-gate 	    0, CT_PR_EV_EXIT, 0, CT_PR_INHERIT | CT_PR_REGENT, CONFIGD_COOKIE);
309*0Sstevel@tonic-gate 
310*0Sstevel@tonic-gate 	if (pid != 0) {
311*0Sstevel@tonic-gate 		int exitstatus;
312*0Sstevel@tonic-gate 
313*0Sstevel@tonic-gate 		st->st_configd_pid = pid;
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate 		if (waitpid(pid, &exitstatus, 0) == -1) {
316*0Sstevel@tonic-gate 			fork_sulogin(B_FALSE, "waitpid on svc.configd "
317*0Sstevel@tonic-gate 			    "failed: %s\n", strerror(errno));
318*0Sstevel@tonic-gate 		} else if (WIFEXITED(exitstatus)) {
319*0Sstevel@tonic-gate 			char *errstr;
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 			/*
322*0Sstevel@tonic-gate 			 * Examine exitstatus.  This will eventually get more
323*0Sstevel@tonic-gate 			 * complicated, as we will want to teach startd how to
324*0Sstevel@tonic-gate 			 * invoke configd with alternate repositories, etc.
325*0Sstevel@tonic-gate 			 *
326*0Sstevel@tonic-gate 			 * Note that exec(2) failure results in an exit status
327*0Sstevel@tonic-gate 			 * of 1, resulting in the default clause below.
328*0Sstevel@tonic-gate 			 */
329*0Sstevel@tonic-gate 
330*0Sstevel@tonic-gate 			/*
331*0Sstevel@tonic-gate 			 * Assign readable strings to cases we don't handle, or
332*0Sstevel@tonic-gate 			 * have error outcomes that cannot be eliminated.
333*0Sstevel@tonic-gate 			 */
334*0Sstevel@tonic-gate 			switch (WEXITSTATUS(exitstatus)) {
335*0Sstevel@tonic-gate 			case CONFIGD_EXIT_BAD_ARGS:
336*0Sstevel@tonic-gate 				errstr = "bad arguments";
337*0Sstevel@tonic-gate 				break;
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate 			case CONFIGD_EXIT_DATABASE_BAD:
340*0Sstevel@tonic-gate 				errstr = "database corrupt";
341*0Sstevel@tonic-gate 				break;
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 			case CONFIGD_EXIT_DATABASE_LOCKED:
344*0Sstevel@tonic-gate 				errstr = "database locked";
345*0Sstevel@tonic-gate 				break;
346*0Sstevel@tonic-gate 			case CONFIGD_EXIT_INIT_FAILED:
347*0Sstevel@tonic-gate 				errstr = "initialization failure";
348*0Sstevel@tonic-gate 				break;
349*0Sstevel@tonic-gate 			case CONFIGD_EXIT_DOOR_INIT_FAILED:
350*0Sstevel@tonic-gate 				errstr = "door initialization failure";
351*0Sstevel@tonic-gate 				break;
352*0Sstevel@tonic-gate 			case CONFIGD_EXIT_DATABASE_INIT_FAILED:
353*0Sstevel@tonic-gate 				errstr = "database initialization failure";
354*0Sstevel@tonic-gate 				break;
355*0Sstevel@tonic-gate 			case CONFIGD_EXIT_NO_THREADS:
356*0Sstevel@tonic-gate 				errstr = "no threads available";
357*0Sstevel@tonic-gate 				break;
358*0Sstevel@tonic-gate 			case CONFIGD_EXIT_LOST_MAIN_DOOR:
359*0Sstevel@tonic-gate 				errstr = "lost door server attachment";
360*0Sstevel@tonic-gate 				break;
361*0Sstevel@tonic-gate 			case 1:
362*0Sstevel@tonic-gate 				errstr = "execution failure";
363*0Sstevel@tonic-gate 				break;
364*0Sstevel@tonic-gate 			default:
365*0Sstevel@tonic-gate 				errstr = "unknown error";
366*0Sstevel@tonic-gate 				break;
367*0Sstevel@tonic-gate 			}
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 			/*
370*0Sstevel@tonic-gate 			 * Remedial actions for various configd failures.
371*0Sstevel@tonic-gate 			 */
372*0Sstevel@tonic-gate 			switch (WEXITSTATUS(exitstatus)) {
373*0Sstevel@tonic-gate 			case CONFIGD_EXIT_OKAY:
374*0Sstevel@tonic-gate 				break;
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate 			case CONFIGD_EXIT_DATABASE_LOCKED:
377*0Sstevel@tonic-gate 				/* attempt remount of / read-write */
378*0Sstevel@tonic-gate 				if (fs_is_read_only("/", NULL) == 1) {
379*0Sstevel@tonic-gate 					if (fs_remount("/") == -1)
380*0Sstevel@tonic-gate 						fork_sulogin(B_FALSE,
381*0Sstevel@tonic-gate 						    "remount of root "
382*0Sstevel@tonic-gate 						    "filesystem failed\n");
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 					goto retry;
385*0Sstevel@tonic-gate 				}
386*0Sstevel@tonic-gate 				break;
387*0Sstevel@tonic-gate 
388*0Sstevel@tonic-gate 			default:
389*0Sstevel@tonic-gate 				fork_sulogin(B_FALSE, "svc.configd exited "
390*0Sstevel@tonic-gate 				    "with status %d (%s)\n",
391*0Sstevel@tonic-gate 				    WEXITSTATUS(exitstatus), errstr);
392*0Sstevel@tonic-gate 				goto retry;
393*0Sstevel@tonic-gate 			}
394*0Sstevel@tonic-gate 		} else if (WIFSIGNALED(exitstatus)) {
395*0Sstevel@tonic-gate 			char signame[SIG2STR_MAX];
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 			if (sig2str(WTERMSIG(exitstatus), signame))
398*0Sstevel@tonic-gate 				(void) snprintf(signame, SIG2STR_MAX,
399*0Sstevel@tonic-gate 				    "signum %d", WTERMSIG(exitstatus));
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate 			fork_sulogin(B_FALSE, "svc.configd signalled:"
402*0Sstevel@tonic-gate 			    " %s\n", signame);
403*0Sstevel@tonic-gate 
404*0Sstevel@tonic-gate 			goto retry;
405*0Sstevel@tonic-gate 		} else {
406*0Sstevel@tonic-gate 			fork_sulogin(B_FALSE, "svc.configd non-exit "
407*0Sstevel@tonic-gate 			    "condition: 0x%x\n", exitstatus);
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 			goto retry;
410*0Sstevel@tonic-gate 		}
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate 		/*
413*0Sstevel@tonic-gate 		 * Announce that we have a valid svc.configd status.
414*0Sstevel@tonic-gate 		 */
415*0Sstevel@tonic-gate 		MUTEX_LOCK(&st->st_configd_live_lock);
416*0Sstevel@tonic-gate 		st->st_configd_lives = 1;
417*0Sstevel@tonic-gate 		err = pthread_cond_broadcast(&st->st_configd_live_cv);
418*0Sstevel@tonic-gate 		assert(err == 0);
419*0Sstevel@tonic-gate 		MUTEX_UNLOCK(&st->st_configd_live_lock);
420*0Sstevel@tonic-gate 
421*0Sstevel@tonic-gate 		log_framework(LOG_DEBUG, "fork_configd broadcasts configd is "
422*0Sstevel@tonic-gate 		    "live\n");
423*0Sstevel@tonic-gate 		return;
424*0Sstevel@tonic-gate 	}
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	/*
427*0Sstevel@tonic-gate 	 * Set our per-process core file path to leave core files in
428*0Sstevel@tonic-gate 	 * /etc/svc/volatile directory, named after the PID to aid in debugging.
429*0Sstevel@tonic-gate 	 */
430*0Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path),
431*0Sstevel@tonic-gate 	    "/etc/svc/volatile/core.configd.%%p");
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 	(void) core_set_process_path(path, strlen(path) + 1, getpid());
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate 	log_framework(LOG_DEBUG, "executing svc.configd\n");
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate 	(void) execl(CONFIGD_PATH, CONFIGD_PATH, NULL);
438*0Sstevel@tonic-gate 
439*0Sstevel@tonic-gate 	/*
440*0Sstevel@tonic-gate 	 * Status code is used above to identify configd exec failure.
441*0Sstevel@tonic-gate 	 */
442*0Sstevel@tonic-gate 	exit(1);
443*0Sstevel@tonic-gate }
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate void *
446*0Sstevel@tonic-gate fork_configd_thread(void *vctid)
447*0Sstevel@tonic-gate {
448*0Sstevel@tonic-gate 	int fd, err;
449*0Sstevel@tonic-gate 	ctid_t configd_ctid = (ctid_t)vctid;
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate 	if (configd_ctid == -1) {
452*0Sstevel@tonic-gate 		log_framework(LOG_DEBUG,
453*0Sstevel@tonic-gate 		    "fork_configd_thread starting svc.configd\n");
454*0Sstevel@tonic-gate 		fork_configd(0);
455*0Sstevel@tonic-gate 	} else {
456*0Sstevel@tonic-gate 		/*
457*0Sstevel@tonic-gate 		 * configd_ctid is known:  we broadcast and continue.
458*0Sstevel@tonic-gate 		 * test contract for appropriate state by verifying that
459*0Sstevel@tonic-gate 		 * there is one or more processes within it?
460*0Sstevel@tonic-gate 		 */
461*0Sstevel@tonic-gate 		log_framework(LOG_DEBUG,
462*0Sstevel@tonic-gate 		    "fork_configd_thread accepting svc.configd with CTID %ld\n",
463*0Sstevel@tonic-gate 		    configd_ctid);
464*0Sstevel@tonic-gate 		MUTEX_LOCK(&st->st_configd_live_lock);
465*0Sstevel@tonic-gate 		st->st_configd_lives = 1;
466*0Sstevel@tonic-gate 		(void) pthread_cond_broadcast(&st->st_configd_live_cv);
467*0Sstevel@tonic-gate 		MUTEX_UNLOCK(&st->st_configd_live_lock);
468*0Sstevel@tonic-gate 	}
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	fd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY);
471*0Sstevel@tonic-gate 	if (fd == -1)
472*0Sstevel@tonic-gate 		uu_die("process bundle open failed");
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	/*
475*0Sstevel@tonic-gate 	 * Make sure we get all events (including those generated by configd
476*0Sstevel@tonic-gate 	 * before this thread was started).
477*0Sstevel@tonic-gate 	 */
478*0Sstevel@tonic-gate 	err = ct_event_reset(fd);
479*0Sstevel@tonic-gate 	assert(err == 0);
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	for (;;) {
482*0Sstevel@tonic-gate 		int efd, sfd;
483*0Sstevel@tonic-gate 		ct_evthdl_t ev;
484*0Sstevel@tonic-gate 		uint32_t type;
485*0Sstevel@tonic-gate 		ctevid_t evid;
486*0Sstevel@tonic-gate 		ct_stathdl_t status;
487*0Sstevel@tonic-gate 		ctid_t ctid;
488*0Sstevel@tonic-gate 		uint64_t cookie;
489*0Sstevel@tonic-gate 		pid_t pid;
490*0Sstevel@tonic-gate 
491*0Sstevel@tonic-gate 		if (err = ct_event_read_critical(fd, &ev)) {
492*0Sstevel@tonic-gate 			assert(err != EINVAL && err != EAGAIN);
493*0Sstevel@tonic-gate 			log_error(LOG_WARNING,
494*0Sstevel@tonic-gate 			    "Error reading next contract event: %s",
495*0Sstevel@tonic-gate 			    strerror(err));
496*0Sstevel@tonic-gate 			continue;
497*0Sstevel@tonic-gate 		}
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate 		evid = ct_event_get_evid(ev);
500*0Sstevel@tonic-gate 		ctid = ct_event_get_ctid(ev);
501*0Sstevel@tonic-gate 		type = ct_event_get_type(ev);
502*0Sstevel@tonic-gate 
503*0Sstevel@tonic-gate 		/* Fetch cookie. */
504*0Sstevel@tonic-gate 		sfd = contract_open(ctid, "process", "status", O_RDONLY);
505*0Sstevel@tonic-gate 		if (sfd < 0) {
506*0Sstevel@tonic-gate 			ct_event_free(ev);
507*0Sstevel@tonic-gate 			continue;
508*0Sstevel@tonic-gate 		}
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate 		if (err = ct_status_read(sfd, CTD_COMMON, &status)) {
511*0Sstevel@tonic-gate 			log_framework(LOG_WARNING, "Could not get status for "
512*0Sstevel@tonic-gate 			    "contract %ld: %s\n", ctid, strerror(err));
513*0Sstevel@tonic-gate 
514*0Sstevel@tonic-gate 			ct_event_free(ev);
515*0Sstevel@tonic-gate 			startd_close(sfd);
516*0Sstevel@tonic-gate 			continue;
517*0Sstevel@tonic-gate 		}
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate 		cookie = ct_status_get_cookie(status);
520*0Sstevel@tonic-gate 
521*0Sstevel@tonic-gate 		ct_status_free(status);
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate 		startd_close(sfd);
524*0Sstevel@tonic-gate 
525*0Sstevel@tonic-gate 		/*
526*0Sstevel@tonic-gate 		 * Don't process events from contracts we aren't interested in.
527*0Sstevel@tonic-gate 		 */
528*0Sstevel@tonic-gate 		if (cookie != CONFIGD_COOKIE) {
529*0Sstevel@tonic-gate 			ct_event_free(ev);
530*0Sstevel@tonic-gate 			continue;
531*0Sstevel@tonic-gate 		}
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate 		if (type == CT_PR_EV_EXIT) {
534*0Sstevel@tonic-gate 			int exitstatus;
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 			(void) ct_pr_event_get_pid(ev, &pid);
537*0Sstevel@tonic-gate 			(void) ct_pr_event_get_exitstatus(ev,
538*0Sstevel@tonic-gate 			    &exitstatus);
539*0Sstevel@tonic-gate 
540*0Sstevel@tonic-gate 			if (st->st_configd_pid != pid) {
541*0Sstevel@tonic-gate 				/*
542*0Sstevel@tonic-gate 				 * This is the child exiting, so we
543*0Sstevel@tonic-gate 				 * abandon the contract and restart
544*0Sstevel@tonic-gate 				 * configd.
545*0Sstevel@tonic-gate 				 */
546*0Sstevel@tonic-gate 				contract_abandon(ctid);
547*0Sstevel@tonic-gate 				fork_configd(exitstatus);
548*0Sstevel@tonic-gate 			}
549*0Sstevel@tonic-gate 		}
550*0Sstevel@tonic-gate 
551*0Sstevel@tonic-gate 		efd = contract_open(ctid, "process", "ctl", O_WRONLY);
552*0Sstevel@tonic-gate 		if (efd != -1) {
553*0Sstevel@tonic-gate 			(void) ct_ctl_ack(efd, evid);
554*0Sstevel@tonic-gate 			startd_close(efd);
555*0Sstevel@tonic-gate 		}
556*0Sstevel@tonic-gate 
557*0Sstevel@tonic-gate 		ct_event_free(ev);
558*0Sstevel@tonic-gate 
559*0Sstevel@tonic-gate 	}
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 	/*NOTREACHED*/
562*0Sstevel@tonic-gate 	return (NULL);
563*0Sstevel@tonic-gate }
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate void
566*0Sstevel@tonic-gate fork_rc_script(char rl, const char *arg, boolean_t wait)
567*0Sstevel@tonic-gate {
568*0Sstevel@tonic-gate 	pid_t pid;
569*0Sstevel@tonic-gate 	int tmpl, err, stat;
570*0Sstevel@tonic-gate 	char path[20] = "/sbin/rc.", log[20] = "rc..log", timebuf[20];
571*0Sstevel@tonic-gate 	time_t now;
572*0Sstevel@tonic-gate 	struct tm ltime;
573*0Sstevel@tonic-gate 	size_t sz;
574*0Sstevel@tonic-gate 	char *pathenv;
575*0Sstevel@tonic-gate 	char **nenv;
576*0Sstevel@tonic-gate 
577*0Sstevel@tonic-gate 	path[8] = rl;
578*0Sstevel@tonic-gate 
579*0Sstevel@tonic-gate 	tmpl = open64(CTFS_ROOT "/process/template", O_RDWR);
580*0Sstevel@tonic-gate 	if (tmpl >= 0) {
581*0Sstevel@tonic-gate 		err = ct_tmpl_set_critical(tmpl, 0);
582*0Sstevel@tonic-gate 		assert(err == 0);
583*0Sstevel@tonic-gate 
584*0Sstevel@tonic-gate 		err = ct_tmpl_set_informative(tmpl, 0);
585*0Sstevel@tonic-gate 		assert(err == 0);
586*0Sstevel@tonic-gate 
587*0Sstevel@tonic-gate 		err = ct_pr_tmpl_set_fatal(tmpl, 0);
588*0Sstevel@tonic-gate 		assert(err == 0);
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate 		err = ct_tmpl_activate(tmpl);
591*0Sstevel@tonic-gate 		assert(err == 0);
592*0Sstevel@tonic-gate 
593*0Sstevel@tonic-gate 		err = close(tmpl);
594*0Sstevel@tonic-gate 		assert(err == 0);
595*0Sstevel@tonic-gate 	} else {
596*0Sstevel@tonic-gate 		uu_warn("Could not create contract template for %s.\n", path);
597*0Sstevel@tonic-gate 	}
598*0Sstevel@tonic-gate 
599*0Sstevel@tonic-gate 	pid = startd_fork1(NULL);
600*0Sstevel@tonic-gate 	if (pid < 0) {
601*0Sstevel@tonic-gate 		return;
602*0Sstevel@tonic-gate 	} else if (pid != 0) {
603*0Sstevel@tonic-gate 		/* parent */
604*0Sstevel@tonic-gate 		if (wait) {
605*0Sstevel@tonic-gate 			do
606*0Sstevel@tonic-gate 				err = waitpid(pid, &stat, 0);
607*0Sstevel@tonic-gate 			while (err != 0 && errno == EINTR);
608*0Sstevel@tonic-gate 
609*0Sstevel@tonic-gate 			if (!WIFEXITED(stat)) {
610*0Sstevel@tonic-gate 				log_framework(LOG_INFO,
611*0Sstevel@tonic-gate 				    "%s terminated with waitpid() status %d.\n",
612*0Sstevel@tonic-gate 				    path, stat);
613*0Sstevel@tonic-gate 			} else if (WEXITSTATUS(stat) != 0) {
614*0Sstevel@tonic-gate 				log_framework(LOG_INFO,
615*0Sstevel@tonic-gate 				    "%s failed with status %d.\n", path,
616*0Sstevel@tonic-gate 				    WEXITSTATUS(stat));
617*0Sstevel@tonic-gate 			}
618*0Sstevel@tonic-gate 		}
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate 		return;
621*0Sstevel@tonic-gate 	}
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate 	/* child */
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate 	log[2] = rl;
626*0Sstevel@tonic-gate 
627*0Sstevel@tonic-gate 	setlog(log);
628*0Sstevel@tonic-gate 
629*0Sstevel@tonic-gate 	now = time(NULL);
630*0Sstevel@tonic-gate 	sz = strftime(timebuf, sizeof (timebuf), "%b %e %T",
631*0Sstevel@tonic-gate 	    localtime_r(&now, &ltime));
632*0Sstevel@tonic-gate 	assert(sz != 0);
633*0Sstevel@tonic-gate 
634*0Sstevel@tonic-gate 	(void) fprintf(stderr, "%s Executing %s %s\n", timebuf, path, arg);
635*0Sstevel@tonic-gate 
636*0Sstevel@tonic-gate 	if (rl == 'S')
637*0Sstevel@tonic-gate 		pathenv = "PATH=/sbin:/usr/sbin:/usr/bin";
638*0Sstevel@tonic-gate 	else
639*0Sstevel@tonic-gate 		pathenv = "PATH=/usr/sbin:/usr/bin";
640*0Sstevel@tonic-gate 
641*0Sstevel@tonic-gate 	nenv = set_smf_env(NULL, 0, pathenv, NULL, NULL);
642*0Sstevel@tonic-gate 
643*0Sstevel@tonic-gate 	(void) execle(path, path, arg, 0, nenv);
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate 	perror("exec");
646*0Sstevel@tonic-gate 	exit(0);
647*0Sstevel@tonic-gate }
648