xref: /onnv-gate/usr/src/cmd/svc/startd/method.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 2004 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  * method.c - method execution functions
31*0Sstevel@tonic-gate  *
32*0Sstevel@tonic-gate  * This file contains the routines needed to run a method:  a fork(2)-exec(2)
33*0Sstevel@tonic-gate  * invocation monitored using either the contract filesystem or waitpid(2).
34*0Sstevel@tonic-gate  * (Plain fork1(2) support is provided in fork.c.)
35*0Sstevel@tonic-gate  *
36*0Sstevel@tonic-gate  * Contract Transfer
37*0Sstevel@tonic-gate  *   When we restart a service, we want to transfer any contracts that the old
38*0Sstevel@tonic-gate  *   service's contract inherited.  This means that (a) we must not abandon the
39*0Sstevel@tonic-gate  *   old contract when the service dies and (b) we must write the id of the old
40*0Sstevel@tonic-gate  *   contract into the terms of the new contract.  There should be limits to
41*0Sstevel@tonic-gate  *   (a), though, since we don't want to keep the contract around forever.  To
42*0Sstevel@tonic-gate  *   this end we'll say that services in the offline state may have a contract
43*0Sstevel@tonic-gate  *   to be transfered and services in the disabled or maintenance states cannot.
44*0Sstevel@tonic-gate  *   This means that when a service transitions from online (or degraded) to
45*0Sstevel@tonic-gate  *   offline, the contract should be preserved, and when the service transitions
46*0Sstevel@tonic-gate  *   from offline to online (i.e., the start method), we'll transfer inherited
47*0Sstevel@tonic-gate  *   contracts.
48*0Sstevel@tonic-gate  */
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate #include <sys/contract/process.h>
51*0Sstevel@tonic-gate #include <sys/ctfs.h>
52*0Sstevel@tonic-gate #include <sys/stat.h>
53*0Sstevel@tonic-gate #include <sys/time.h>
54*0Sstevel@tonic-gate #include <sys/types.h>
55*0Sstevel@tonic-gate #include <sys/uio.h>
56*0Sstevel@tonic-gate #include <sys/wait.h>
57*0Sstevel@tonic-gate #include <alloca.h>
58*0Sstevel@tonic-gate #include <assert.h>
59*0Sstevel@tonic-gate #include <errno.h>
60*0Sstevel@tonic-gate #include <fcntl.h>
61*0Sstevel@tonic-gate #include <libcontract.h>
62*0Sstevel@tonic-gate #include <libcontract_priv.h>
63*0Sstevel@tonic-gate #include <libgen.h>
64*0Sstevel@tonic-gate #include <librestart.h>
65*0Sstevel@tonic-gate #include <libscf.h>
66*0Sstevel@tonic-gate #include <limits.h>
67*0Sstevel@tonic-gate #include <port.h>
68*0Sstevel@tonic-gate #include <sac.h>
69*0Sstevel@tonic-gate #include <signal.h>
70*0Sstevel@tonic-gate #include <stdlib.h>
71*0Sstevel@tonic-gate #include <string.h>
72*0Sstevel@tonic-gate #include <strings.h>
73*0Sstevel@tonic-gate #include <unistd.h>
74*0Sstevel@tonic-gate 
75*0Sstevel@tonic-gate #include "startd.h"
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate #define	SBIN_SH		"/sbin/sh"
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate /*
80*0Sstevel@tonic-gate  * Mapping from restart_on method-type to contract events.  Must correspond to
81*0Sstevel@tonic-gate  * enum method_restart_t.
82*0Sstevel@tonic-gate  */
83*0Sstevel@tonic-gate static uint_t method_events[] = {
84*0Sstevel@tonic-gate 	/* METHOD_RESTART_ALL */
85*0Sstevel@tonic-gate 	CT_PR_EV_HWERR | CT_PR_EV_SIGNAL | CT_PR_EV_CORE | CT_PR_EV_EMPTY,
86*0Sstevel@tonic-gate 	/* METHOD_RESTART_EXTERNAL_FAULT */
87*0Sstevel@tonic-gate 	CT_PR_EV_HWERR | CT_PR_EV_SIGNAL,
88*0Sstevel@tonic-gate 	/* METHOD_RESTART_ANY_FAULT */
89*0Sstevel@tonic-gate 	CT_PR_EV_HWERR | CT_PR_EV_SIGNAL | CT_PR_EV_CORE
90*0Sstevel@tonic-gate };
91*0Sstevel@tonic-gate 
92*0Sstevel@tonic-gate /*
93*0Sstevel@tonic-gate  * method_record_start(restarter_inst_t *)
94*0Sstevel@tonic-gate  *   Record a service start for rate limiting.  Place the current time
95*0Sstevel@tonic-gate  *   in the circular array of instance starts.
96*0Sstevel@tonic-gate  */
97*0Sstevel@tonic-gate static void
98*0Sstevel@tonic-gate method_record_start(restarter_inst_t *inst)
99*0Sstevel@tonic-gate {
100*0Sstevel@tonic-gate 	int index = inst->ri_start_index++ % RINST_START_TIMES;
101*0Sstevel@tonic-gate 
102*0Sstevel@tonic-gate 	inst->ri_start_time[index] = gethrtime();
103*0Sstevel@tonic-gate }
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate /*
106*0Sstevel@tonic-gate  * method_rate_critical(restarter_inst_t *)
107*0Sstevel@tonic-gate  *    Return true if the average start interval is less than the permitted
108*0Sstevel@tonic-gate  *    interval.  Implicit success if insufficient measurements for an
109*0Sstevel@tonic-gate  *    average exist.
110*0Sstevel@tonic-gate  */
111*0Sstevel@tonic-gate static int
112*0Sstevel@tonic-gate method_rate_critical(restarter_inst_t *inst)
113*0Sstevel@tonic-gate {
114*0Sstevel@tonic-gate 	uint_t n = inst->ri_start_index;
115*0Sstevel@tonic-gate 	hrtime_t avg_ns = 0;
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate 	if (inst->ri_start_index < RINST_START_TIMES)
118*0Sstevel@tonic-gate 		return (0);
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 	avg_ns =
121*0Sstevel@tonic-gate 	    (inst->ri_start_time[(n - 1) % RINST_START_TIMES] -
122*0Sstevel@tonic-gate 	    inst->ri_start_time[n % RINST_START_TIMES]) /
123*0Sstevel@tonic-gate 	    (RINST_START_TIMES - 1);
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate 	return (avg_ns < RINST_FAILURE_RATE_NS);
126*0Sstevel@tonic-gate }
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate /*
129*0Sstevel@tonic-gate  * int method_is_transient()
130*0Sstevel@tonic-gate  *   Determine if the method for the given instance is transient,
131*0Sstevel@tonic-gate  *   from a contract perspective. Return 1 if it is, and 0 if it isn't.
132*0Sstevel@tonic-gate  */
133*0Sstevel@tonic-gate static int
134*0Sstevel@tonic-gate method_is_transient(restarter_inst_t *inst, int type)
135*0Sstevel@tonic-gate {
136*0Sstevel@tonic-gate 	if (instance_is_transient_style(inst) || type != METHOD_START)
137*0Sstevel@tonic-gate 		return (1);
138*0Sstevel@tonic-gate 	else
139*0Sstevel@tonic-gate 		return (0);
140*0Sstevel@tonic-gate }
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate /*
143*0Sstevel@tonic-gate  * void method_store_contract()
144*0Sstevel@tonic-gate  *   Store the newly created contract id into local structures and
145*0Sstevel@tonic-gate  *   the repository.  If the repository connection is broken it is rebound.
146*0Sstevel@tonic-gate  */
147*0Sstevel@tonic-gate static void
148*0Sstevel@tonic-gate method_store_contract(restarter_inst_t *inst, int type, ctid_t *cid)
149*0Sstevel@tonic-gate {
150*0Sstevel@tonic-gate 	int r;
151*0Sstevel@tonic-gate 	boolean_t primary;
152*0Sstevel@tonic-gate 
153*0Sstevel@tonic-gate 	if (errno = contract_latest(cid))
154*0Sstevel@tonic-gate 		uu_die("%s: Couldn't get new contract's id", inst->ri_i.i_fmri);
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 	primary = !method_is_transient(inst, type);
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 	if (!primary) {
159*0Sstevel@tonic-gate 		if (inst->ri_i.i_transient_ctid != 0) {
160*0Sstevel@tonic-gate 			log_framework(LOG_INFO,
161*0Sstevel@tonic-gate 			    "%s: transient ctid expected to be 0 but "
162*0Sstevel@tonic-gate 			    "was set to %ld\n", inst->ri_i.i_fmri,
163*0Sstevel@tonic-gate 			    inst->ri_i.i_transient_ctid);
164*0Sstevel@tonic-gate 		}
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 		inst->ri_i.i_transient_ctid = *cid;
167*0Sstevel@tonic-gate 	} else {
168*0Sstevel@tonic-gate 		if (inst->ri_i.i_primary_ctid != 0) {
169*0Sstevel@tonic-gate 			/*
170*0Sstevel@tonic-gate 			 * There was an old contract that we transferred.
171*0Sstevel@tonic-gate 			 * Remove it.
172*0Sstevel@tonic-gate 			 */
173*0Sstevel@tonic-gate 			method_remove_contract(inst, B_TRUE, B_FALSE);
174*0Sstevel@tonic-gate 		}
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate 		if (inst->ri_i.i_primary_ctid != 0) {
177*0Sstevel@tonic-gate 			log_framework(LOG_INFO,
178*0Sstevel@tonic-gate 			    "%s: primary ctid expected to be 0 but "
179*0Sstevel@tonic-gate 			    "was set to %ld\n", inst->ri_i.i_fmri,
180*0Sstevel@tonic-gate 			    inst->ri_i.i_primary_ctid);
181*0Sstevel@tonic-gate 		}
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate 		inst->ri_i.i_primary_ctid = *cid;
184*0Sstevel@tonic-gate 		inst->ri_i.i_primary_ctid_stopped = 0;
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 		contract_hash_store(*cid, inst->ri_id);
187*0Sstevel@tonic-gate 	}
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate again:
190*0Sstevel@tonic-gate 	if (inst->ri_mi_deleted)
191*0Sstevel@tonic-gate 		return;
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	r = restarter_store_contract(inst->ri_m_inst, *cid, primary ?
194*0Sstevel@tonic-gate 	    RESTARTER_CONTRACT_PRIMARY : RESTARTER_CONTRACT_TRANSIENT);
195*0Sstevel@tonic-gate 	switch (r) {
196*0Sstevel@tonic-gate 	case 0:
197*0Sstevel@tonic-gate 		break;
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate 	case ECANCELED:
200*0Sstevel@tonic-gate 		inst->ri_mi_deleted = B_TRUE;
201*0Sstevel@tonic-gate 		break;
202*0Sstevel@tonic-gate 
203*0Sstevel@tonic-gate 	case ECONNABORTED:
204*0Sstevel@tonic-gate 		libscf_handle_rebind(scf_instance_handle(inst->ri_m_inst));
205*0Sstevel@tonic-gate 		/* FALLTHROUGH */
206*0Sstevel@tonic-gate 
207*0Sstevel@tonic-gate 	case EBADF:
208*0Sstevel@tonic-gate 		libscf_reget_instance(inst);
209*0Sstevel@tonic-gate 		goto again;
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate 	case ENOMEM:
212*0Sstevel@tonic-gate 	case EPERM:
213*0Sstevel@tonic-gate 	case EACCES:
214*0Sstevel@tonic-gate 	case EROFS:
215*0Sstevel@tonic-gate 		uu_die("%s: Couldn't store contract id %ld",
216*0Sstevel@tonic-gate 		    inst->ri_i.i_fmri, *cid);
217*0Sstevel@tonic-gate 		/* NOTREACHED */
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate 	case EINVAL:
220*0Sstevel@tonic-gate 	default:
221*0Sstevel@tonic-gate 		bad_error("restarter_store_contract", r);
222*0Sstevel@tonic-gate 	}
223*0Sstevel@tonic-gate }
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate /*
226*0Sstevel@tonic-gate  * void method_remove_contract()
227*0Sstevel@tonic-gate  *   Remove any non-permanent contracts from internal structures and
228*0Sstevel@tonic-gate  *   the repository, then abandon them.
229*0Sstevel@tonic-gate  *   Returns
230*0Sstevel@tonic-gate  *     0 - success
231*0Sstevel@tonic-gate  *     ECANCELED - inst was deleted from the repository
232*0Sstevel@tonic-gate  *
233*0Sstevel@tonic-gate  *   If the repository connection was broken, it is rebound.
234*0Sstevel@tonic-gate  */
235*0Sstevel@tonic-gate void
236*0Sstevel@tonic-gate method_remove_contract(restarter_inst_t *inst, boolean_t primary,
237*0Sstevel@tonic-gate     boolean_t abandon)
238*0Sstevel@tonic-gate {
239*0Sstevel@tonic-gate 	ctid_t * const ctidp = primary ? &inst->ri_i.i_primary_ctid :
240*0Sstevel@tonic-gate 	    &inst->ri_i.i_transient_ctid;
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate 	int r;
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 	assert(*ctidp != 0);
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate 	log_framework(LOG_DEBUG, "Removing %s contract %lu for %s.\n",
247*0Sstevel@tonic-gate 	    primary ? "primary" : "transient", *ctidp, inst->ri_i.i_fmri);
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	if (abandon)
250*0Sstevel@tonic-gate 		contract_abandon(*ctidp);
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate again:
253*0Sstevel@tonic-gate 	if (inst->ri_mi_deleted) {
254*0Sstevel@tonic-gate 		r = ECANCELED;
255*0Sstevel@tonic-gate 		goto out;
256*0Sstevel@tonic-gate 	}
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate 	r = restarter_remove_contract(inst->ri_m_inst, *ctidp, primary ?
259*0Sstevel@tonic-gate 	    RESTARTER_CONTRACT_PRIMARY : RESTARTER_CONTRACT_TRANSIENT);
260*0Sstevel@tonic-gate 	switch (r) {
261*0Sstevel@tonic-gate 	case 0:
262*0Sstevel@tonic-gate 		break;
263*0Sstevel@tonic-gate 
264*0Sstevel@tonic-gate 	case ECANCELED:
265*0Sstevel@tonic-gate 		inst->ri_mi_deleted = B_TRUE;
266*0Sstevel@tonic-gate 		break;
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate 	case ECONNABORTED:
269*0Sstevel@tonic-gate 		libscf_handle_rebind(scf_instance_handle(inst->ri_m_inst));
270*0Sstevel@tonic-gate 		/* FALLTHROUGH */
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 	case EBADF:
273*0Sstevel@tonic-gate 		libscf_reget_instance(inst);
274*0Sstevel@tonic-gate 		goto again;
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate 	case ENOMEM:
277*0Sstevel@tonic-gate 	case EPERM:
278*0Sstevel@tonic-gate 	case EACCES:
279*0Sstevel@tonic-gate 	case EROFS:
280*0Sstevel@tonic-gate 		log_error(LOG_INFO, "%s: Couldn't remove contract id %ld: "
281*0Sstevel@tonic-gate 		    "%s.\n", inst->ri_i.i_fmri, *ctidp, strerror(r));
282*0Sstevel@tonic-gate 		break;
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 	case EINVAL:
285*0Sstevel@tonic-gate 	default:
286*0Sstevel@tonic-gate 		bad_error("restarter_remove_contract", r);
287*0Sstevel@tonic-gate 	}
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate out:
290*0Sstevel@tonic-gate 	if (primary)
291*0Sstevel@tonic-gate 		contract_hash_remove(*ctidp);
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 	*ctidp = 0;
294*0Sstevel@tonic-gate }
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate /*
297*0Sstevel@tonic-gate  * int method_ready_contract(restarter_inst_t *, int, method_restart_t, int)
298*0Sstevel@tonic-gate  *
299*0Sstevel@tonic-gate  *   Activate a contract template for the type method of inst.  type,
300*0Sstevel@tonic-gate  *   restart_on, and cte_mask dictate the critical events term of the contract.
301*0Sstevel@tonic-gate  *   Returns
302*0Sstevel@tonic-gate  *     0 - success
303*0Sstevel@tonic-gate  *     ECANCELED - inst has been deleted from the repository
304*0Sstevel@tonic-gate  */
305*0Sstevel@tonic-gate static int
306*0Sstevel@tonic-gate method_ready_contract(restarter_inst_t *inst, int type,
307*0Sstevel@tonic-gate     method_restart_t restart_on, uint_t cte_mask)
308*0Sstevel@tonic-gate {
309*0Sstevel@tonic-gate 	int tmpl, err, istrans, iswait, ret;
310*0Sstevel@tonic-gate 	uint_t cevents, fevents;
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 	/*
313*0Sstevel@tonic-gate 	 * Correctly supporting wait-style services is tricky without
314*0Sstevel@tonic-gate 	 * rearchitecting startd to cope with multiple event sources
315*0Sstevel@tonic-gate 	 * simultaneously trying to stop an instance.  Until a better
316*0Sstevel@tonic-gate 	 * solution is implemented, we avoid this problem for
317*0Sstevel@tonic-gate 	 * wait-style services by making contract events fatal and
318*0Sstevel@tonic-gate 	 * letting the wait code alone handle stopping the service.
319*0Sstevel@tonic-gate 	 */
320*0Sstevel@tonic-gate 	iswait = instance_is_wait_style(inst);
321*0Sstevel@tonic-gate 	istrans = method_is_transient(inst, type);
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate 	tmpl = open64(CTFS_ROOT "/process/template", O_RDWR);
324*0Sstevel@tonic-gate 	if (tmpl == -1)
325*0Sstevel@tonic-gate 		uu_die("Could not create contract template");
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 	/*
328*0Sstevel@tonic-gate 	 * We assume non-login processes are unlikely to create
329*0Sstevel@tonic-gate 	 * multiple process groups, and set CT_PR_PGRPONLY for all
330*0Sstevel@tonic-gate 	 * wait-style services' contracts.
331*0Sstevel@tonic-gate 	 */
332*0Sstevel@tonic-gate 	err = ct_pr_tmpl_set_param(tmpl, CT_PR_INHERIT | CT_PR_REGENT |
333*0Sstevel@tonic-gate 	    (iswait ? CT_PR_PGRPONLY : 0));
334*0Sstevel@tonic-gate 	assert(err == 0);
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 	if (istrans) {
337*0Sstevel@tonic-gate 		cevents = 0;
338*0Sstevel@tonic-gate 		fevents = 0;
339*0Sstevel@tonic-gate 	} else {
340*0Sstevel@tonic-gate 		assert(restart_on >= 0);
341*0Sstevel@tonic-gate 		assert(restart_on <= METHOD_RESTART_ANY_FAULT);
342*0Sstevel@tonic-gate 		cevents = method_events[restart_on] & ~cte_mask;
343*0Sstevel@tonic-gate 		fevents = iswait ?
344*0Sstevel@tonic-gate 		    (method_events[restart_on] & ~cte_mask & CT_PR_ALLFATAL) :
345*0Sstevel@tonic-gate 		    0;
346*0Sstevel@tonic-gate 	}
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 	err = ct_tmpl_set_critical(tmpl, cevents);
349*0Sstevel@tonic-gate 	assert(err == 0);
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate 	err = ct_tmpl_set_informative(tmpl, 0);
352*0Sstevel@tonic-gate 	assert(err == 0);
353*0Sstevel@tonic-gate 	err = ct_pr_tmpl_set_fatal(tmpl, fevents);
354*0Sstevel@tonic-gate 	assert(err == 0);
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate 	err = ct_tmpl_set_cookie(tmpl, istrans ?  METHOD_OTHER_COOKIE :
357*0Sstevel@tonic-gate 	    METHOD_START_COOKIE);
358*0Sstevel@tonic-gate 	assert(err == 0);
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate 	if (type == METHOD_START && inst->ri_i.i_primary_ctid != 0) {
361*0Sstevel@tonic-gate 		ret = ct_pr_tmpl_set_transfer(tmpl, inst->ri_i.i_primary_ctid);
362*0Sstevel@tonic-gate 		switch (ret) {
363*0Sstevel@tonic-gate 		case 0:
364*0Sstevel@tonic-gate 			break;
365*0Sstevel@tonic-gate 
366*0Sstevel@tonic-gate 		case ENOTEMPTY:
367*0Sstevel@tonic-gate 			/* No contracts for you! */
368*0Sstevel@tonic-gate 			method_remove_contract(inst, B_TRUE, B_TRUE);
369*0Sstevel@tonic-gate 			if (inst->ri_mi_deleted) {
370*0Sstevel@tonic-gate 				ret = ECANCELED;
371*0Sstevel@tonic-gate 				goto out;
372*0Sstevel@tonic-gate 			}
373*0Sstevel@tonic-gate 			break;
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate 		case EINVAL:
376*0Sstevel@tonic-gate 		case ESRCH:
377*0Sstevel@tonic-gate 		case EACCES:
378*0Sstevel@tonic-gate 		default:
379*0Sstevel@tonic-gate 			bad_error("ct_pr_tmpl_set_transfer", ret);
380*0Sstevel@tonic-gate 		}
381*0Sstevel@tonic-gate 	}
382*0Sstevel@tonic-gate 
383*0Sstevel@tonic-gate 	err = ct_tmpl_activate(tmpl);
384*0Sstevel@tonic-gate 	assert(err == 0);
385*0Sstevel@tonic-gate 
386*0Sstevel@tonic-gate 	ret = 0;
387*0Sstevel@tonic-gate 
388*0Sstevel@tonic-gate out:
389*0Sstevel@tonic-gate 	err = close(tmpl);
390*0Sstevel@tonic-gate 	assert(err == 0);
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate 	return (ret);
393*0Sstevel@tonic-gate }
394*0Sstevel@tonic-gate 
395*0Sstevel@tonic-gate static const char *method_names[] = { "start", "stop", "refresh" };
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate static void
398*0Sstevel@tonic-gate exec_method(const restarter_inst_t *inst, int type, const char *method,
399*0Sstevel@tonic-gate     struct method_context *mcp, uint8_t need_session)
400*0Sstevel@tonic-gate {
401*0Sstevel@tonic-gate 	char *cmd;
402*0Sstevel@tonic-gate 	const char *errf;
403*0Sstevel@tonic-gate 	char **nenv;
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate 	cmd = uu_msprintf("exec %s", method);
406*0Sstevel@tonic-gate 
407*0Sstevel@tonic-gate 	if (inst->ri_utmpx_prefix[0] != '\0' && inst->ri_utmpx_prefix != NULL)
408*0Sstevel@tonic-gate 		(void) utmpx_mark_init(getpid(), inst->ri_utmpx_prefix);
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 	setlog(inst->ri_logstem);
411*0Sstevel@tonic-gate 	log_instance(inst, B_FALSE, "Executing %s method (\"%s\")",
412*0Sstevel@tonic-gate 	    method_names[type], method);
413*0Sstevel@tonic-gate 
414*0Sstevel@tonic-gate 	if (need_session)
415*0Sstevel@tonic-gate 		(void) setpgrp();
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate 	/* Set credentials. */
418*0Sstevel@tonic-gate 	errno = restarter_set_method_context(mcp, &errf);
419*0Sstevel@tonic-gate 	if (errno != 0) {
420*0Sstevel@tonic-gate 		(void) fputs("svc.startd could not set context for method: ",
421*0Sstevel@tonic-gate 		    stderr);
422*0Sstevel@tonic-gate 
423*0Sstevel@tonic-gate 		if (errno == -1) {
424*0Sstevel@tonic-gate 			if (strcmp(errf, "core_set_process_path") == 0) {
425*0Sstevel@tonic-gate 				(void) fputs("Could not set corefile path.\n",
426*0Sstevel@tonic-gate 				    stderr);
427*0Sstevel@tonic-gate 			} else if (strcmp(errf, "setproject") == 0) {
428*0Sstevel@tonic-gate 				(void) fprintf(stderr, "%s: a resource control "
429*0Sstevel@tonic-gate 				    "assignment failed\n", errf);
430*0Sstevel@tonic-gate 			} else if (strcmp(errf, "pool_set_binding") == 0) {
431*0Sstevel@tonic-gate 				(void) fprintf(stderr, "%s: a system error "
432*0Sstevel@tonic-gate 				    "occurred\n", errf);
433*0Sstevel@tonic-gate 			} else {
434*0Sstevel@tonic-gate #ifndef NDEBUG
435*0Sstevel@tonic-gate 				uu_warn("%s:%d: Bad function name \"%s\" for "
436*0Sstevel@tonic-gate 				    "error %d from "
437*0Sstevel@tonic-gate 				    "restarter_set_method_context().\n",
438*0Sstevel@tonic-gate 				    __FILE__, __LINE__, errf, errno);
439*0Sstevel@tonic-gate #endif
440*0Sstevel@tonic-gate 				abort();
441*0Sstevel@tonic-gate 			}
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate 			exit(1);
444*0Sstevel@tonic-gate 		}
445*0Sstevel@tonic-gate 
446*0Sstevel@tonic-gate 		if (errf != NULL && strcmp(errf, "pool_set_binding") == 0) {
447*0Sstevel@tonic-gate 			switch (errno) {
448*0Sstevel@tonic-gate 			case ENOENT:
449*0Sstevel@tonic-gate 				(void) fprintf(stderr, "%s: the pool could not "
450*0Sstevel@tonic-gate 				    "be found\n", errf);
451*0Sstevel@tonic-gate 				break;
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 			case EBADF:
454*0Sstevel@tonic-gate 				(void) fprintf(stderr, "%s: the configuration "
455*0Sstevel@tonic-gate 				    "is invalid\n", errf);
456*0Sstevel@tonic-gate 				break;
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate 			default:
459*0Sstevel@tonic-gate #ifndef NDEBUG
460*0Sstevel@tonic-gate 				uu_warn("%s:%d: Bad error %d for function %s "
461*0Sstevel@tonic-gate 				    "in restarter_set_method_context().\n",
462*0Sstevel@tonic-gate 				    __FILE__, __LINE__, errno, errf);
463*0Sstevel@tonic-gate #endif
464*0Sstevel@tonic-gate 				abort();
465*0Sstevel@tonic-gate 			}
466*0Sstevel@tonic-gate 
467*0Sstevel@tonic-gate 			exit(SMF_EXIT_ERR_CONFIG);
468*0Sstevel@tonic-gate 		}
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 		if (errf != NULL) {
471*0Sstevel@tonic-gate 			perror(errf);
472*0Sstevel@tonic-gate 
473*0Sstevel@tonic-gate 			switch (errno) {
474*0Sstevel@tonic-gate 			case EINVAL:
475*0Sstevel@tonic-gate 			case EPERM:
476*0Sstevel@tonic-gate 			case ENOENT:
477*0Sstevel@tonic-gate 			case ENAMETOOLONG:
478*0Sstevel@tonic-gate 			case ERANGE:
479*0Sstevel@tonic-gate 			case ESRCH:
480*0Sstevel@tonic-gate 				exit(SMF_EXIT_ERR_CONFIG);
481*0Sstevel@tonic-gate 				/* NOTREACHED */
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate 			default:
484*0Sstevel@tonic-gate 				exit(1);
485*0Sstevel@tonic-gate 			}
486*0Sstevel@tonic-gate 		}
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate 		switch (errno) {
489*0Sstevel@tonic-gate 		case ENOMEM:
490*0Sstevel@tonic-gate 			(void) fputs("Out of memory.\n", stderr);
491*0Sstevel@tonic-gate 			exit(1);
492*0Sstevel@tonic-gate 			/* NOTREACHED */
493*0Sstevel@tonic-gate 
494*0Sstevel@tonic-gate 		case ENOENT:
495*0Sstevel@tonic-gate 			(void) fputs("Missing passwd entry for user.\n",
496*0Sstevel@tonic-gate 			    stderr);
497*0Sstevel@tonic-gate 			exit(SMF_EXIT_ERR_CONFIG);
498*0Sstevel@tonic-gate 			/* NOTREACHED */
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate 		default:
501*0Sstevel@tonic-gate #ifndef NDEBUG
502*0Sstevel@tonic-gate 			uu_warn("%s:%d: Bad miscellaneous error %d from "
503*0Sstevel@tonic-gate 			    "restarter_set_method_context().\n", __FILE__,
504*0Sstevel@tonic-gate 			    __LINE__, errno);
505*0Sstevel@tonic-gate #endif
506*0Sstevel@tonic-gate 			abort();
507*0Sstevel@tonic-gate 		}
508*0Sstevel@tonic-gate 	}
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate 	nenv = set_smf_env(mcp->env, mcp->env_sz, NULL, inst, method);
511*0Sstevel@tonic-gate 
512*0Sstevel@tonic-gate 	log_preexec();
513*0Sstevel@tonic-gate 
514*0Sstevel@tonic-gate 	(void) execle(SBIN_SH, SBIN_SH, "-c", cmd, NULL, nenv);
515*0Sstevel@tonic-gate 
516*0Sstevel@tonic-gate 	exit(10);
517*0Sstevel@tonic-gate }
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate static void
520*0Sstevel@tonic-gate write_status(restarter_inst_t *inst, const char *mname, int stat)
521*0Sstevel@tonic-gate {
522*0Sstevel@tonic-gate 	int r;
523*0Sstevel@tonic-gate 
524*0Sstevel@tonic-gate again:
525*0Sstevel@tonic-gate 	if (inst->ri_mi_deleted)
526*0Sstevel@tonic-gate 		return;
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate 	r = libscf_write_method_status(inst->ri_m_inst, mname, stat);
529*0Sstevel@tonic-gate 	switch (r) {
530*0Sstevel@tonic-gate 	case 0:
531*0Sstevel@tonic-gate 		break;
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate 	case ECONNABORTED:
534*0Sstevel@tonic-gate 		libscf_reget_instance(inst);
535*0Sstevel@tonic-gate 		goto again;
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate 	case ECANCELED:
538*0Sstevel@tonic-gate 		inst->ri_mi_deleted = 1;
539*0Sstevel@tonic-gate 		break;
540*0Sstevel@tonic-gate 
541*0Sstevel@tonic-gate 	case EPERM:
542*0Sstevel@tonic-gate 	case EACCES:
543*0Sstevel@tonic-gate 	case EROFS:
544*0Sstevel@tonic-gate 		log_framework(LOG_INFO, "Could not write exit status "
545*0Sstevel@tonic-gate 		    "for %s method of %s: %s.\n", mname,
546*0Sstevel@tonic-gate 		    inst->ri_i.i_fmri, strerror(r));
547*0Sstevel@tonic-gate 		break;
548*0Sstevel@tonic-gate 
549*0Sstevel@tonic-gate 	case ENAMETOOLONG:
550*0Sstevel@tonic-gate 	default:
551*0Sstevel@tonic-gate 		bad_error("libscf_write_method_status", r);
552*0Sstevel@tonic-gate 	}
553*0Sstevel@tonic-gate }
554*0Sstevel@tonic-gate 
555*0Sstevel@tonic-gate /*
556*0Sstevel@tonic-gate  * int method_run()
557*0Sstevel@tonic-gate  *   Execute the type method of instp.  If it requires a fork(), wait for it
558*0Sstevel@tonic-gate  *   to return and return its exit code in *exit_code.  Otherwise set
559*0Sstevel@tonic-gate  *   *exit_code to 0 if the method succeeds & -1 if it fails.  If the
560*0Sstevel@tonic-gate  *   repository connection is broken, it is rebound, but inst may not be
561*0Sstevel@tonic-gate  *   reset.
562*0Sstevel@tonic-gate  *   Returns
563*0Sstevel@tonic-gate  *     0 - success
564*0Sstevel@tonic-gate  *     EINVAL - A correct method or method context couldn't be retrieved.
565*0Sstevel@tonic-gate  *     EIO - Contract kill failed.
566*0Sstevel@tonic-gate  *     EFAULT - Method couldn't be executed successfully.
567*0Sstevel@tonic-gate  *     ELOOP - Retry threshold exceeded.
568*0Sstevel@tonic-gate  *     ECANCELED - inst was deleted from the repository before method was run
569*0Sstevel@tonic-gate  *     ERANGE - Timeout retry threshold exceeded.
570*0Sstevel@tonic-gate  *     EAGAIN - Failed due to external cause, retry.
571*0Sstevel@tonic-gate  */
572*0Sstevel@tonic-gate int
573*0Sstevel@tonic-gate method_run(restarter_inst_t **instp, int type, int *exit_code)
574*0Sstevel@tonic-gate {
575*0Sstevel@tonic-gate 	char *method;
576*0Sstevel@tonic-gate 	int ret_status;
577*0Sstevel@tonic-gate 	pid_t pid;
578*0Sstevel@tonic-gate 	method_restart_t restart_on;
579*0Sstevel@tonic-gate 	uint_t cte_mask;
580*0Sstevel@tonic-gate 	uint8_t need_session;
581*0Sstevel@tonic-gate 	scf_handle_t *h;
582*0Sstevel@tonic-gate 	scf_snapshot_t *snap;
583*0Sstevel@tonic-gate 	const char *mname;
584*0Sstevel@tonic-gate 	const char *errstr;
585*0Sstevel@tonic-gate 	struct method_context *mcp;
586*0Sstevel@tonic-gate 	int result = 0, timeout_fired = 0;
587*0Sstevel@tonic-gate 	int sig, r;
588*0Sstevel@tonic-gate 	boolean_t transient;
589*0Sstevel@tonic-gate 	uint64_t timeout;
590*0Sstevel@tonic-gate 	uint8_t timeout_retry;
591*0Sstevel@tonic-gate 	ctid_t ctid;
592*0Sstevel@tonic-gate 	int ctfd = -1;
593*0Sstevel@tonic-gate 	ct_evthdl_t ctev;
594*0Sstevel@tonic-gate 	uint_t evtype;
595*0Sstevel@tonic-gate 	restarter_inst_t *inst = *instp;
596*0Sstevel@tonic-gate 	int id = inst->ri_id;
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate 	assert(PTHREAD_MUTEX_HELD(&inst->ri_lock));
599*0Sstevel@tonic-gate 	assert(instance_in_transition(inst));
600*0Sstevel@tonic-gate 
601*0Sstevel@tonic-gate 	if (inst->ri_mi_deleted)
602*0Sstevel@tonic-gate 		return (ECANCELED);
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 	*exit_code = 0;
605*0Sstevel@tonic-gate 
606*0Sstevel@tonic-gate 	assert(0 <= type && type <= 2);
607*0Sstevel@tonic-gate 	mname = method_names[type];
608*0Sstevel@tonic-gate 
609*0Sstevel@tonic-gate 	if (type == METHOD_START)
610*0Sstevel@tonic-gate 		inst->ri_pre_online_hook();
611*0Sstevel@tonic-gate 
612*0Sstevel@tonic-gate 	h = scf_instance_handle(inst->ri_m_inst);
613*0Sstevel@tonic-gate 
614*0Sstevel@tonic-gate 	snap = scf_snapshot_create(h);
615*0Sstevel@tonic-gate 	if (snap == NULL ||
616*0Sstevel@tonic-gate 	    scf_instance_get_snapshot(inst->ri_m_inst, "running", snap) != 0) {
617*0Sstevel@tonic-gate 		log_framework(LOG_DEBUG,
618*0Sstevel@tonic-gate 		    "Could not get running snapshot for %s.  "
619*0Sstevel@tonic-gate 		    "Using editing version to run method %s.\n",
620*0Sstevel@tonic-gate 		    inst->ri_i.i_fmri, mname);
621*0Sstevel@tonic-gate 		scf_snapshot_destroy(snap);
622*0Sstevel@tonic-gate 		snap = NULL;
623*0Sstevel@tonic-gate 	}
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate 	/*
626*0Sstevel@tonic-gate 	 * After this point, we may be logging to the instance log.
627*0Sstevel@tonic-gate 	 * Make sure we've noted where that log is as a property of
628*0Sstevel@tonic-gate 	 * the instance.
629*0Sstevel@tonic-gate 	 */
630*0Sstevel@tonic-gate 	r = libscf_note_method_log(inst->ri_m_inst, st->st_log_prefix,
631*0Sstevel@tonic-gate 	    inst->ri_logstem);
632*0Sstevel@tonic-gate 	if (r != 0) {
633*0Sstevel@tonic-gate 		log_framework(LOG_WARNING,
634*0Sstevel@tonic-gate 		    "%s: couldn't note log location: %s\n",
635*0Sstevel@tonic-gate 		    inst->ri_i.i_fmri, strerror(r));
636*0Sstevel@tonic-gate 	}
637*0Sstevel@tonic-gate 
638*0Sstevel@tonic-gate 	if ((method = libscf_get_method(h, type, inst, snap, &restart_on,
639*0Sstevel@tonic-gate 	    &cte_mask, &need_session, &timeout, &timeout_retry)) == NULL) {
640*0Sstevel@tonic-gate 		if (errno == LIBSCF_PGROUP_ABSENT)  {
641*0Sstevel@tonic-gate 			log_framework(LOG_DEBUG,
642*0Sstevel@tonic-gate 			    "%s: instance has no method property group '%s'.\n",
643*0Sstevel@tonic-gate 			    inst->ri_i.i_fmri, mname);
644*0Sstevel@tonic-gate 			if (type == METHOD_REFRESH)
645*0Sstevel@tonic-gate 				log_instance(inst, B_TRUE, "No '%s' method "
646*0Sstevel@tonic-gate 				    "defined.  Treating as :true.", mname);
647*0Sstevel@tonic-gate 			else
648*0Sstevel@tonic-gate 				log_instance(inst, B_TRUE, "Method property "
649*0Sstevel@tonic-gate 				    "group '%s' is not present.", mname);
650*0Sstevel@tonic-gate 			scf_snapshot_destroy(snap);
651*0Sstevel@tonic-gate 			return (0);
652*0Sstevel@tonic-gate 		} else if (errno == LIBSCF_PROPERTY_ABSENT)  {
653*0Sstevel@tonic-gate 			log_framework(LOG_DEBUG,
654*0Sstevel@tonic-gate 			    "%s: instance has no '%s/exec' method property.\n",
655*0Sstevel@tonic-gate 			    inst->ri_i.i_fmri, mname);
656*0Sstevel@tonic-gate 			log_instance(inst, B_TRUE, "Method property '%s/exec "
657*0Sstevel@tonic-gate 			    "is not present.", mname);
658*0Sstevel@tonic-gate 			scf_snapshot_destroy(snap);
659*0Sstevel@tonic-gate 			return (0);
660*0Sstevel@tonic-gate 		} else {
661*0Sstevel@tonic-gate 			log_error(LOG_WARNING,
662*0Sstevel@tonic-gate 			    "%s: instance libscf_get_method failed\n",
663*0Sstevel@tonic-gate 			    inst->ri_i.i_fmri);
664*0Sstevel@tonic-gate 			scf_snapshot_destroy(snap);
665*0Sstevel@tonic-gate 			return (EINVAL);
666*0Sstevel@tonic-gate 		}
667*0Sstevel@tonic-gate 	}
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 	/* open service contract if stopping a non-transient service */
670*0Sstevel@tonic-gate 	if (type == METHOD_STOP && (!instance_is_transient_style(inst))) {
671*0Sstevel@tonic-gate 		if (inst->ri_i.i_primary_ctid == 0) {
672*0Sstevel@tonic-gate 			/* service is not running, nothing to stop */
673*0Sstevel@tonic-gate 			log_framework(LOG_DEBUG, "%s: instance has no primary "
674*0Sstevel@tonic-gate 			    "contract, no service to stop.\n",
675*0Sstevel@tonic-gate 			    inst->ri_i.i_fmri);
676*0Sstevel@tonic-gate 			scf_snapshot_destroy(snap);
677*0Sstevel@tonic-gate 			return (0);
678*0Sstevel@tonic-gate 		}
679*0Sstevel@tonic-gate 		if ((ctfd = contract_open(inst->ri_i.i_primary_ctid, "process",
680*0Sstevel@tonic-gate 		    "events", O_RDONLY)) < 0) {
681*0Sstevel@tonic-gate 			result = EFAULT;
682*0Sstevel@tonic-gate 			log_instance(inst, B_TRUE, "Could not open service "
683*0Sstevel@tonic-gate 			    "contract %ld.  Stop method not run.\n",
684*0Sstevel@tonic-gate 			    inst->ri_i.i_primary_ctid);
685*0Sstevel@tonic-gate 			goto out;
686*0Sstevel@tonic-gate 		}
687*0Sstevel@tonic-gate 	}
688*0Sstevel@tonic-gate 
689*0Sstevel@tonic-gate 	if (restarter_is_null_method(method)) {
690*0Sstevel@tonic-gate 		log_framework(LOG_DEBUG, "%s: null method succeeds\n",
691*0Sstevel@tonic-gate 		    inst->ri_i.i_fmri);
692*0Sstevel@tonic-gate 
693*0Sstevel@tonic-gate 		log_instance(inst, B_TRUE, "Executing %s method (null)", mname);
694*0Sstevel@tonic-gate 
695*0Sstevel@tonic-gate 		if (type == METHOD_START)
696*0Sstevel@tonic-gate 			write_status(inst, mname, 0);
697*0Sstevel@tonic-gate 		goto out;
698*0Sstevel@tonic-gate 	}
699*0Sstevel@tonic-gate 
700*0Sstevel@tonic-gate 	sig = restarter_is_kill_method(method);
701*0Sstevel@tonic-gate 	if (sig >= 0) {
702*0Sstevel@tonic-gate 
703*0Sstevel@tonic-gate 		if (inst->ri_i.i_primary_ctid == 0) {
704*0Sstevel@tonic-gate 			log_error(LOG_ERR, "%s: :kill with no contract\n",
705*0Sstevel@tonic-gate 			    inst->ri_i.i_fmri);
706*0Sstevel@tonic-gate 			result = EINVAL;
707*0Sstevel@tonic-gate 			goto out;
708*0Sstevel@tonic-gate 		}
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate 		log_framework(LOG_DEBUG,
711*0Sstevel@tonic-gate 		    "%s: :killing contract with signal %d\n",
712*0Sstevel@tonic-gate 		    inst->ri_i.i_fmri, sig);
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate 		log_instance(inst, B_TRUE, "Executing %s method (:kill)",
715*0Sstevel@tonic-gate 		    mname);
716*0Sstevel@tonic-gate 
717*0Sstevel@tonic-gate 		if (contract_kill(inst->ri_i.i_primary_ctid, sig,
718*0Sstevel@tonic-gate 		    inst->ri_i.i_fmri) != 0) {
719*0Sstevel@tonic-gate 			result = EIO;
720*0Sstevel@tonic-gate 			goto out;
721*0Sstevel@tonic-gate 		} else
722*0Sstevel@tonic-gate 			goto assured_kill;
723*0Sstevel@tonic-gate 	}
724*0Sstevel@tonic-gate 
725*0Sstevel@tonic-gate 	log_framework(LOG_DEBUG, "%s: forking to run method %s\n",
726*0Sstevel@tonic-gate 	    inst->ri_i.i_fmri, method);
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 	errstr = restarter_get_method_context(RESTARTER_METHOD_CONTEXT_VERSION,
729*0Sstevel@tonic-gate 	    inst->ri_m_inst, snap, mname, method, &mcp);
730*0Sstevel@tonic-gate 
731*0Sstevel@tonic-gate 	if (errstr != NULL) {
732*0Sstevel@tonic-gate 		log_error(LOG_WARNING, "%s: %s\n", inst->ri_i.i_fmri, errstr);
733*0Sstevel@tonic-gate 		result = EINVAL;
734*0Sstevel@tonic-gate 		goto out;
735*0Sstevel@tonic-gate 	}
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate 	r = method_ready_contract(inst, type, restart_on, cte_mask);
738*0Sstevel@tonic-gate 	if (r != 0) {
739*0Sstevel@tonic-gate 		assert(r == ECANCELED);
740*0Sstevel@tonic-gate 		assert(inst->ri_mi_deleted);
741*0Sstevel@tonic-gate 		restarter_free_method_context(mcp);
742*0Sstevel@tonic-gate 		result = ECANCELED;
743*0Sstevel@tonic-gate 		goto out;
744*0Sstevel@tonic-gate 	}
745*0Sstevel@tonic-gate 
746*0Sstevel@tonic-gate 	/*
747*0Sstevel@tonic-gate 	 * Validate safety of method contexts, to save children work.
748*0Sstevel@tonic-gate 	 */
749*0Sstevel@tonic-gate 	if (!restarter_rm_libs_loadable())
750*0Sstevel@tonic-gate 		log_framework(LOG_DEBUG, "%s: method contexts limited "
751*0Sstevel@tonic-gate 		    "to root-accessible libraries\n", inst->ri_i.i_fmri);
752*0Sstevel@tonic-gate 
753*0Sstevel@tonic-gate 	/*
754*0Sstevel@tonic-gate 	 * If the service is restarting too quickly, send it to
755*0Sstevel@tonic-gate 	 * maintenance.
756*0Sstevel@tonic-gate 	 */
757*0Sstevel@tonic-gate 	if (type == METHOD_START) {
758*0Sstevel@tonic-gate 		method_record_start(inst);
759*0Sstevel@tonic-gate 		if (method_rate_critical(inst)) {
760*0Sstevel@tonic-gate 			log_instance(inst, B_TRUE, "Restarting too quickly, "
761*0Sstevel@tonic-gate 			    "changing state to maintenance");
762*0Sstevel@tonic-gate 			result = ELOOP;
763*0Sstevel@tonic-gate 			goto out;
764*0Sstevel@tonic-gate 		}
765*0Sstevel@tonic-gate 	}
766*0Sstevel@tonic-gate 
767*0Sstevel@tonic-gate 	pid = startd_fork1(NULL);
768*0Sstevel@tonic-gate 	if (pid == 0)
769*0Sstevel@tonic-gate 		exec_method(inst, type, method, mcp, need_session);
770*0Sstevel@tonic-gate 
771*0Sstevel@tonic-gate 	if (pid == -1) {
772*0Sstevel@tonic-gate 		log_error(LOG_WARNING,
773*0Sstevel@tonic-gate 		    "%s: Couldn't fork to execute method %s\n",
774*0Sstevel@tonic-gate 		    inst->ri_i.i_fmri, method);
775*0Sstevel@tonic-gate 		result = EFAULT;
776*0Sstevel@tonic-gate 		goto out;
777*0Sstevel@tonic-gate 	}
778*0Sstevel@tonic-gate 
779*0Sstevel@tonic-gate 	restarter_free_method_context(mcp);
780*0Sstevel@tonic-gate 
781*0Sstevel@tonic-gate 	/*
782*0Sstevel@tonic-gate 	 * Get the contract id, decide whether it is primary or transient, and
783*0Sstevel@tonic-gate 	 * stash it in inst & the repository.
784*0Sstevel@tonic-gate 	 */
785*0Sstevel@tonic-gate 	method_store_contract(inst, type, &ctid);
786*0Sstevel@tonic-gate 
787*0Sstevel@tonic-gate 	/*
788*0Sstevel@tonic-gate 	 * Similarly for the start method PID.
789*0Sstevel@tonic-gate 	 */
790*0Sstevel@tonic-gate 	if (type == METHOD_START && !inst->ri_mi_deleted)
791*0Sstevel@tonic-gate 		(void) libscf_write_start_pid(inst->ri_m_inst, pid);
792*0Sstevel@tonic-gate 
793*0Sstevel@tonic-gate 	if (instance_is_wait_style(inst) && type == METHOD_START) {
794*0Sstevel@tonic-gate 		/* Wait style instances don't get timeouts on start methods. */
795*0Sstevel@tonic-gate 		if (wait_register(pid, inst->ri_i.i_fmri, 1, 0)) {
796*0Sstevel@tonic-gate 			log_error(LOG_WARNING,
797*0Sstevel@tonic-gate 			    "%s: couldn't register %ld for wait\n",
798*0Sstevel@tonic-gate 			    inst->ri_i.i_fmri, pid);
799*0Sstevel@tonic-gate 			result = EFAULT;
800*0Sstevel@tonic-gate 			goto contract_out;
801*0Sstevel@tonic-gate 		}
802*0Sstevel@tonic-gate 		write_status(inst, mname, 0);
803*0Sstevel@tonic-gate 
804*0Sstevel@tonic-gate 	} else {
805*0Sstevel@tonic-gate 		int r, err;
806*0Sstevel@tonic-gate 		time_t start_time;
807*0Sstevel@tonic-gate 		time_t end_time;
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate 		/*
810*0Sstevel@tonic-gate 		 * Because on upgrade/live-upgrade we may have no chance
811*0Sstevel@tonic-gate 		 * to override faulty timeout values on the way to
812*0Sstevel@tonic-gate 		 * manifest import, all services on the path to manifest
813*0Sstevel@tonic-gate 		 * import are treated the same as INFINITE timeout services.
814*0Sstevel@tonic-gate 		 */
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate 		start_time = time(NULL);
817*0Sstevel@tonic-gate 		if (timeout != METHOD_TIMEOUT_INFINITE && !is_timeout_ovr(inst))
818*0Sstevel@tonic-gate 			timeout_insert(inst, ctid, timeout);
819*0Sstevel@tonic-gate 		else
820*0Sstevel@tonic-gate 			timeout = METHOD_TIMEOUT_INFINITE;
821*0Sstevel@tonic-gate 
822*0Sstevel@tonic-gate 		/* Unlock the instance while waiting for the method. */
823*0Sstevel@tonic-gate 		MUTEX_UNLOCK(&inst->ri_lock);
824*0Sstevel@tonic-gate 
825*0Sstevel@tonic-gate 		do
826*0Sstevel@tonic-gate 			r = waitpid(pid, &ret_status, NULL);
827*0Sstevel@tonic-gate 		while (r == -1 && errno == EINTR);
828*0Sstevel@tonic-gate 		if (r == -1)
829*0Sstevel@tonic-gate 			err = errno;
830*0Sstevel@tonic-gate 
831*0Sstevel@tonic-gate 		/* Re-grab the lock. */
832*0Sstevel@tonic-gate 		inst = inst_lookup_by_id(id);
833*0Sstevel@tonic-gate 
834*0Sstevel@tonic-gate 		/*
835*0Sstevel@tonic-gate 		 * inst can't be removed, as the removal thread waits
836*0Sstevel@tonic-gate 		 * for completion of this one.
837*0Sstevel@tonic-gate 		 */
838*0Sstevel@tonic-gate 		assert(inst != NULL);
839*0Sstevel@tonic-gate 		*instp = inst;
840*0Sstevel@tonic-gate 
841*0Sstevel@tonic-gate 		if (inst->ri_timeout != NULL && inst->ri_timeout->te_fired)
842*0Sstevel@tonic-gate 			timeout_fired = 1;
843*0Sstevel@tonic-gate 
844*0Sstevel@tonic-gate 		timeout_remove(inst, ctid);
845*0Sstevel@tonic-gate 
846*0Sstevel@tonic-gate 		log_framework(LOG_DEBUG,
847*0Sstevel@tonic-gate 		    "%s method for %s exited with status %d.\n", mname,
848*0Sstevel@tonic-gate 		    inst->ri_i.i_fmri, WEXITSTATUS(ret_status));
849*0Sstevel@tonic-gate 
850*0Sstevel@tonic-gate 		if (r == -1) {
851*0Sstevel@tonic-gate 			log_error(LOG_WARNING,
852*0Sstevel@tonic-gate 			    "Couldn't waitpid() for %s method of %s (%s).\n",
853*0Sstevel@tonic-gate 			    mname, inst->ri_i.i_fmri, strerror(err));
854*0Sstevel@tonic-gate 			result = EFAULT;
855*0Sstevel@tonic-gate 			goto contract_out;
856*0Sstevel@tonic-gate 		}
857*0Sstevel@tonic-gate 
858*0Sstevel@tonic-gate 		if (type == METHOD_START)
859*0Sstevel@tonic-gate 			write_status(inst, mname, ret_status);
860*0Sstevel@tonic-gate 
861*0Sstevel@tonic-gate 		/* return ERANGE if this service doesn't retry on timeout */
862*0Sstevel@tonic-gate 		if (timeout_fired == 1 && timeout_retry == 0) {
863*0Sstevel@tonic-gate 			result = ERANGE;
864*0Sstevel@tonic-gate 			goto contract_out;
865*0Sstevel@tonic-gate 		}
866*0Sstevel@tonic-gate 
867*0Sstevel@tonic-gate 		if (!WIFEXITED(ret_status)) {
868*0Sstevel@tonic-gate 			/*
869*0Sstevel@tonic-gate 			 * If method didn't exit itself (it was killed by an
870*0Sstevel@tonic-gate 			 * external entity, etc.), consider the entire
871*0Sstevel@tonic-gate 			 * method_run as failed.
872*0Sstevel@tonic-gate 			 */
873*0Sstevel@tonic-gate 			if (WIFSIGNALED(ret_status)) {
874*0Sstevel@tonic-gate 				char buf[SIG2STR_MAX];
875*0Sstevel@tonic-gate 				(void) sig2str(WTERMSIG(ret_status), buf);
876*0Sstevel@tonic-gate 
877*0Sstevel@tonic-gate 				log_error(LOG_WARNING, "%s: Method \"%s\" "
878*0Sstevel@tonic-gate 				    "failed due to signal %s.\n",
879*0Sstevel@tonic-gate 				    inst->ri_i.i_fmri, method, buf);
880*0Sstevel@tonic-gate 				log_instance(inst, B_TRUE, "Method \"%s\" "
881*0Sstevel@tonic-gate 				    "failed due to signal %s", mname, buf);
882*0Sstevel@tonic-gate 			} else {
883*0Sstevel@tonic-gate 				log_error(LOG_WARNING, "%s: Method \"%s\" "
884*0Sstevel@tonic-gate 				    "failed with exit status %d.\n",
885*0Sstevel@tonic-gate 				    inst->ri_i.i_fmri, method,
886*0Sstevel@tonic-gate 				    WEXITSTATUS(ret_status));
887*0Sstevel@tonic-gate 				log_instance(inst, B_TRUE, "Method \"%s\" "
888*0Sstevel@tonic-gate 				    "failed with exit status %d", mname,
889*0Sstevel@tonic-gate 				    WEXITSTATUS(ret_status));
890*0Sstevel@tonic-gate 			}
891*0Sstevel@tonic-gate 			result = EAGAIN;
892*0Sstevel@tonic-gate 			goto contract_out;
893*0Sstevel@tonic-gate 		}
894*0Sstevel@tonic-gate 
895*0Sstevel@tonic-gate 		*exit_code = WEXITSTATUS(ret_status);
896*0Sstevel@tonic-gate 		if (*exit_code != 0) {
897*0Sstevel@tonic-gate 			log_error(LOG_WARNING,
898*0Sstevel@tonic-gate 			    "%s: Method \"%s\" failed with exit status %d.\n",
899*0Sstevel@tonic-gate 			    inst->ri_i.i_fmri, method, WEXITSTATUS(ret_status));
900*0Sstevel@tonic-gate 		}
901*0Sstevel@tonic-gate 
902*0Sstevel@tonic-gate 		log_instance(inst, B_TRUE, "Method \"%s\" exited with status "
903*0Sstevel@tonic-gate 		    "%d", mname, *exit_code);
904*0Sstevel@tonic-gate 
905*0Sstevel@tonic-gate 		if (*exit_code != 0)
906*0Sstevel@tonic-gate 			goto contract_out;
907*0Sstevel@tonic-gate 
908*0Sstevel@tonic-gate 		end_time = time(NULL);
909*0Sstevel@tonic-gate 
910*0Sstevel@tonic-gate 		/* Give service contract remaining seconds to empty */
911*0Sstevel@tonic-gate 		if (timeout != METHOD_TIMEOUT_INFINITE)
912*0Sstevel@tonic-gate 			timeout -= (end_time - start_time);
913*0Sstevel@tonic-gate 	}
914*0Sstevel@tonic-gate 
915*0Sstevel@tonic-gate assured_kill:
916*0Sstevel@tonic-gate 	/*
917*0Sstevel@tonic-gate 	 * For stop methods, assure that the service contract has emptied
918*0Sstevel@tonic-gate 	 * before returning.
919*0Sstevel@tonic-gate 	 */
920*0Sstevel@tonic-gate 	if (type == METHOD_STOP && (!instance_is_transient_style(inst)) &&
921*0Sstevel@tonic-gate 	    !(contract_is_empty(inst->ri_i.i_primary_ctid))) {
922*0Sstevel@tonic-gate 
923*0Sstevel@tonic-gate 		if (timeout != METHOD_TIMEOUT_INFINITE)
924*0Sstevel@tonic-gate 			timeout_insert(inst, inst->ri_i.i_primary_ctid,
925*0Sstevel@tonic-gate 			    timeout);
926*0Sstevel@tonic-gate 
927*0Sstevel@tonic-gate 		for (;;) {
928*0Sstevel@tonic-gate 			do {
929*0Sstevel@tonic-gate 				r = ct_event_read_critical(ctfd, &ctev);
930*0Sstevel@tonic-gate 			} while (r == EINTR);
931*0Sstevel@tonic-gate 			if (r != 0)
932*0Sstevel@tonic-gate 				break;
933*0Sstevel@tonic-gate 
934*0Sstevel@tonic-gate 			evtype = ct_event_get_type(ctev);
935*0Sstevel@tonic-gate 			ct_event_free(ctev);
936*0Sstevel@tonic-gate 			if (evtype == CT_PR_EV_EMPTY)
937*0Sstevel@tonic-gate 				break;
938*0Sstevel@tonic-gate 		}
939*0Sstevel@tonic-gate 		if (r) {
940*0Sstevel@tonic-gate 			result = EFAULT;
941*0Sstevel@tonic-gate 			log_instance(inst, B_TRUE, "Error reading service "
942*0Sstevel@tonic-gate 			    "contract %ld.\n", inst->ri_i.i_primary_ctid);
943*0Sstevel@tonic-gate 		}
944*0Sstevel@tonic-gate 
945*0Sstevel@tonic-gate 		if (timeout != METHOD_TIMEOUT_INFINITE)
946*0Sstevel@tonic-gate 			if (inst->ri_timeout->te_fired)
947*0Sstevel@tonic-gate 				result = EFAULT;
948*0Sstevel@tonic-gate 
949*0Sstevel@tonic-gate 		timeout_remove(inst, inst->ri_i.i_primary_ctid);
950*0Sstevel@tonic-gate 	}
951*0Sstevel@tonic-gate 
952*0Sstevel@tonic-gate contract_out:
953*0Sstevel@tonic-gate 	/* Abandon contracts for transient methods & methods that fail. */
954*0Sstevel@tonic-gate 	transient = method_is_transient(inst, type);
955*0Sstevel@tonic-gate 	if ((transient || *exit_code != 0 || result != 0) &&
956*0Sstevel@tonic-gate 	    (restarter_is_kill_method(method) < 0))
957*0Sstevel@tonic-gate 		method_remove_contract(inst, !transient, B_TRUE);
958*0Sstevel@tonic-gate 
959*0Sstevel@tonic-gate out:
960*0Sstevel@tonic-gate 	if (ctfd >= 0)
961*0Sstevel@tonic-gate 		(void) close(ctfd);
962*0Sstevel@tonic-gate 	scf_snapshot_destroy(snap);
963*0Sstevel@tonic-gate 	free(method);
964*0Sstevel@tonic-gate 	return (result);
965*0Sstevel@tonic-gate }
966*0Sstevel@tonic-gate 
967*0Sstevel@tonic-gate /*
968*0Sstevel@tonic-gate  * The method thread executes a service method to effect a state transition.
969*0Sstevel@tonic-gate  * The next_state of info->sf_id should be non-_NONE on entrance, and it will
970*0Sstevel@tonic-gate  * be _NONE on exit (state will either be what next_state was (on success), or
971*0Sstevel@tonic-gate  * it will be _MAINT (on error)).
972*0Sstevel@tonic-gate  *
973*0Sstevel@tonic-gate  * There are six classes of methods to consider: start & other (stop, refresh)
974*0Sstevel@tonic-gate  * for each of "normal" services, wait services, and transient services.  For
975*0Sstevel@tonic-gate  * each, the method must be fetched from the repository & executed.  fork()ed
976*0Sstevel@tonic-gate  * methods must be waited on, except for the start method of wait services
977*0Sstevel@tonic-gate  * (which must be registered with the wait subsystem via wait_register()).  If
978*0Sstevel@tonic-gate  * the method succeeded (returned 0), then for start methods its contract
979*0Sstevel@tonic-gate  * should be recorded as the primary contract for the service.  For other
980*0Sstevel@tonic-gate  * methods, it should be abandoned.  If the method fails, then depending on
981*0Sstevel@tonic-gate  * the failure, either the method should be reexecuted or the service should
982*0Sstevel@tonic-gate  * be put into maintenance.  Either way the contract should be abandoned.
983*0Sstevel@tonic-gate  */
984*0Sstevel@tonic-gate void *
985*0Sstevel@tonic-gate method_thread(void *arg)
986*0Sstevel@tonic-gate {
987*0Sstevel@tonic-gate 	fork_info_t *info = arg;
988*0Sstevel@tonic-gate 	restarter_inst_t *inst;
989*0Sstevel@tonic-gate 	scf_handle_t	*local_handle;
990*0Sstevel@tonic-gate 	scf_instance_t	*s_inst = NULL;
991*0Sstevel@tonic-gate 	int r, exit_code;
992*0Sstevel@tonic-gate 	boolean_t retryable;
993*0Sstevel@tonic-gate 	const char *aux;
994*0Sstevel@tonic-gate 
995*0Sstevel@tonic-gate 	assert(0 <= info->sf_method_type && info->sf_method_type <= 2);
996*0Sstevel@tonic-gate 
997*0Sstevel@tonic-gate 	/* Get (and lock) the restarter_inst_t. */
998*0Sstevel@tonic-gate 	inst = inst_lookup_by_id(info->sf_id);
999*0Sstevel@tonic-gate 
1000*0Sstevel@tonic-gate 	assert(inst->ri_method_thread != 0);
1001*0Sstevel@tonic-gate 	assert(instance_in_transition(inst) == 1);
1002*0Sstevel@tonic-gate 
1003*0Sstevel@tonic-gate 	/*
1004*0Sstevel@tonic-gate 	 * We cannot leave this function with inst in transition, because
1005*0Sstevel@tonic-gate 	 * protocol.c withholds messages for inst otherwise.
1006*0Sstevel@tonic-gate 	 */
1007*0Sstevel@tonic-gate 
1008*0Sstevel@tonic-gate 	log_framework(LOG_DEBUG, "method_thread() running %s method for %s.\n",
1009*0Sstevel@tonic-gate 	    method_names[info->sf_method_type], inst->ri_i.i_fmri);
1010*0Sstevel@tonic-gate 
1011*0Sstevel@tonic-gate 	local_handle = libscf_handle_create_bound_loop();
1012*0Sstevel@tonic-gate 
1013*0Sstevel@tonic-gate rebind_retry:
1014*0Sstevel@tonic-gate 	/* get scf_instance_t */
1015*0Sstevel@tonic-gate 	switch (r = libscf_fmri_get_instance(local_handle, inst->ri_i.i_fmri,
1016*0Sstevel@tonic-gate 	    &s_inst)) {
1017*0Sstevel@tonic-gate 	case 0:
1018*0Sstevel@tonic-gate 		break;
1019*0Sstevel@tonic-gate 
1020*0Sstevel@tonic-gate 	case ECONNABORTED:
1021*0Sstevel@tonic-gate 		libscf_handle_rebind(local_handle);
1022*0Sstevel@tonic-gate 		goto rebind_retry;
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate 	case ENOENT:
1025*0Sstevel@tonic-gate 		/*
1026*0Sstevel@tonic-gate 		 * It's not there, but we need to call this so protocol.c
1027*0Sstevel@tonic-gate 		 * doesn't think it's in transition anymore.
1028*0Sstevel@tonic-gate 		 */
1029*0Sstevel@tonic-gate 		(void) restarter_instance_update_states(local_handle, inst,
1030*0Sstevel@tonic-gate 		    inst->ri_i.i_state, RESTARTER_STATE_NONE, RERR_NONE,
1031*0Sstevel@tonic-gate 		    NULL);
1032*0Sstevel@tonic-gate 		goto out;
1033*0Sstevel@tonic-gate 
1034*0Sstevel@tonic-gate 	case EINVAL:
1035*0Sstevel@tonic-gate 	case ENOTSUP:
1036*0Sstevel@tonic-gate 	default:
1037*0Sstevel@tonic-gate 		bad_error("libscf_fmri_get_instance", r);
1038*0Sstevel@tonic-gate 	}
1039*0Sstevel@tonic-gate 
1040*0Sstevel@tonic-gate 	inst->ri_m_inst = s_inst;
1041*0Sstevel@tonic-gate 	inst->ri_mi_deleted = B_FALSE;
1042*0Sstevel@tonic-gate 
1043*0Sstevel@tonic-gate retry:
1044*0Sstevel@tonic-gate 	if (info->sf_method_type == METHOD_START)
1045*0Sstevel@tonic-gate 		log_transition(inst, START_REQUESTED);
1046*0Sstevel@tonic-gate 
1047*0Sstevel@tonic-gate 	r = method_run(&inst, info->sf_method_type, &exit_code);
1048*0Sstevel@tonic-gate 
1049*0Sstevel@tonic-gate 	if (r == 0 && exit_code == 0) {
1050*0Sstevel@tonic-gate 		/* Success! */
1051*0Sstevel@tonic-gate 		assert(inst->ri_i.i_next_state != RESTARTER_STATE_NONE);
1052*0Sstevel@tonic-gate 
1053*0Sstevel@tonic-gate 		/*
1054*0Sstevel@tonic-gate 		 * When a stop method succeeds, remove the primary contract of
1055*0Sstevel@tonic-gate 		 * the service, unless we're going to offline, in which case
1056*0Sstevel@tonic-gate 		 * retain the contract so we can transfer inherited contracts to
1057*0Sstevel@tonic-gate 		 * the replacement service.
1058*0Sstevel@tonic-gate 		 */
1059*0Sstevel@tonic-gate 
1060*0Sstevel@tonic-gate 		if (info->sf_method_type == METHOD_STOP &&
1061*0Sstevel@tonic-gate 		    inst->ri_i.i_primary_ctid != 0) {
1062*0Sstevel@tonic-gate 			if (inst->ri_i.i_next_state == RESTARTER_STATE_OFFLINE)
1063*0Sstevel@tonic-gate 				inst->ri_i.i_primary_ctid_stopped = 1;
1064*0Sstevel@tonic-gate 			else
1065*0Sstevel@tonic-gate 				method_remove_contract(inst, B_TRUE, B_TRUE);
1066*0Sstevel@tonic-gate 		}
1067*0Sstevel@tonic-gate 		/*
1068*0Sstevel@tonic-gate 		 * We don't care whether the handle was rebound because this is
1069*0Sstevel@tonic-gate 		 * the last thing we do with it.
1070*0Sstevel@tonic-gate 		 */
1071*0Sstevel@tonic-gate 		(void) restarter_instance_update_states(local_handle, inst,
1072*0Sstevel@tonic-gate 		    inst->ri_i.i_next_state, RESTARTER_STATE_NONE,
1073*0Sstevel@tonic-gate 		    info->sf_event_type, NULL);
1074*0Sstevel@tonic-gate 
1075*0Sstevel@tonic-gate 		(void) update_fault_count(inst, FAULT_COUNT_RESET);
1076*0Sstevel@tonic-gate 
1077*0Sstevel@tonic-gate 		goto out;
1078*0Sstevel@tonic-gate 	}
1079*0Sstevel@tonic-gate 
1080*0Sstevel@tonic-gate 	/* Failure.  Retry or go to maintenance. */
1081*0Sstevel@tonic-gate 
1082*0Sstevel@tonic-gate 	if (r != 0 && r != EAGAIN) {
1083*0Sstevel@tonic-gate 		retryable = B_FALSE;
1084*0Sstevel@tonic-gate 	} else {
1085*0Sstevel@tonic-gate 		switch (exit_code) {
1086*0Sstevel@tonic-gate 		case SMF_EXIT_ERR_CONFIG:
1087*0Sstevel@tonic-gate 		case SMF_EXIT_ERR_NOSMF:
1088*0Sstevel@tonic-gate 		case SMF_EXIT_ERR_PERM:
1089*0Sstevel@tonic-gate 		case SMF_EXIT_ERR_FATAL:
1090*0Sstevel@tonic-gate 			retryable = B_FALSE;
1091*0Sstevel@tonic-gate 			break;
1092*0Sstevel@tonic-gate 
1093*0Sstevel@tonic-gate 		default:
1094*0Sstevel@tonic-gate 			retryable = B_TRUE;
1095*0Sstevel@tonic-gate 		}
1096*0Sstevel@tonic-gate 	}
1097*0Sstevel@tonic-gate 
1098*0Sstevel@tonic-gate 	if (retryable && update_fault_count(inst, FAULT_COUNT_INCR) != 1)
1099*0Sstevel@tonic-gate 		goto retry;
1100*0Sstevel@tonic-gate 
1101*0Sstevel@tonic-gate 	/* maintenance */
1102*0Sstevel@tonic-gate 	if (r == ELOOP)
1103*0Sstevel@tonic-gate 		log_transition(inst, START_FAILED_REPEATEDLY);
1104*0Sstevel@tonic-gate 	else if (r == ERANGE)
1105*0Sstevel@tonic-gate 		log_transition(inst, START_FAILED_TIMEOUT_FATAL);
1106*0Sstevel@tonic-gate 	else if (exit_code == SMF_EXIT_ERR_CONFIG)
1107*0Sstevel@tonic-gate 		log_transition(inst, START_FAILED_CONFIGURATION);
1108*0Sstevel@tonic-gate 	else if (exit_code == SMF_EXIT_ERR_FATAL)
1109*0Sstevel@tonic-gate 		log_transition(inst, START_FAILED_FATAL);
1110*0Sstevel@tonic-gate 	else
1111*0Sstevel@tonic-gate 		log_transition(inst, START_FAILED_OTHER);
1112*0Sstevel@tonic-gate 
1113*0Sstevel@tonic-gate 	if (r == ELOOP)
1114*0Sstevel@tonic-gate 		aux = "restarting_too_quickly";
1115*0Sstevel@tonic-gate 	else if (retryable)
1116*0Sstevel@tonic-gate 		aux = "fault_threshold_reached";
1117*0Sstevel@tonic-gate 	else
1118*0Sstevel@tonic-gate 		aux = "method_failed";
1119*0Sstevel@tonic-gate 
1120*0Sstevel@tonic-gate 	(void) restarter_instance_update_states(local_handle, inst,
1121*0Sstevel@tonic-gate 	    RESTARTER_STATE_MAINT, RESTARTER_STATE_NONE, RERR_FAULT,
1122*0Sstevel@tonic-gate 	    (char *)aux);
1123*0Sstevel@tonic-gate 
1124*0Sstevel@tonic-gate 	if (!method_is_transient(inst, info->sf_method_type) &&
1125*0Sstevel@tonic-gate 	    inst->ri_i.i_primary_ctid != 0)
1126*0Sstevel@tonic-gate 		method_remove_contract(inst, B_TRUE, B_TRUE);
1127*0Sstevel@tonic-gate 
1128*0Sstevel@tonic-gate out:
1129*0Sstevel@tonic-gate 	inst->ri_method_thread = 0;
1130*0Sstevel@tonic-gate 	MUTEX_UNLOCK(&inst->ri_lock);
1131*0Sstevel@tonic-gate 	(void) pthread_cond_broadcast(&inst->ri_method_cv);
1132*0Sstevel@tonic-gate 
1133*0Sstevel@tonic-gate 	scf_instance_destroy(s_inst);
1134*0Sstevel@tonic-gate 	scf_handle_destroy(local_handle);
1135*0Sstevel@tonic-gate 	startd_free(info, sizeof (fork_info_t));
1136*0Sstevel@tonic-gate 	return (NULL);
1137*0Sstevel@tonic-gate }
1138