xref: /titanic_44/usr/src/cmd/svc/startd/transition.c (revision 99b44c3bba5a515aba1e56c2104f53af320a848d)
1*99b44c3bSlianep /*
2*99b44c3bSlianep  * CDDL HEADER START
3*99b44c3bSlianep  *
4*99b44c3bSlianep  * The contents of this file are subject to the terms of the
5*99b44c3bSlianep  * Common Development and Distribution License (the "License").
6*99b44c3bSlianep  * You may not use this file except in compliance with the License.
7*99b44c3bSlianep  *
8*99b44c3bSlianep  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*99b44c3bSlianep  * or http://www.opensolaris.org/os/licensing.
10*99b44c3bSlianep  * See the License for the specific language governing permissions
11*99b44c3bSlianep  * and limitations under the License.
12*99b44c3bSlianep  *
13*99b44c3bSlianep  * When distributing Covered Code, include this CDDL HEADER in each
14*99b44c3bSlianep  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*99b44c3bSlianep  * If applicable, add the following below this CDDL HEADER, with the
16*99b44c3bSlianep  * fields enclosed by brackets "[]" replaced with your own identifying
17*99b44c3bSlianep  * information: Portions Copyright [yyyy] [name of copyright owner]
18*99b44c3bSlianep  *
19*99b44c3bSlianep  * CDDL HEADER END
20*99b44c3bSlianep  */
21*99b44c3bSlianep /*
22*99b44c3bSlianep  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*99b44c3bSlianep  * Use is subject to license terms.
24*99b44c3bSlianep  */
25*99b44c3bSlianep 
26*99b44c3bSlianep #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*99b44c3bSlianep 
28*99b44c3bSlianep /*
29*99b44c3bSlianep  * transition.c - Graph State Machine
30*99b44c3bSlianep  *
31*99b44c3bSlianep  * The graph state machine is implemented here, with a typical approach
32*99b44c3bSlianep  * of a function per state.  Separating the implementation allows more
33*99b44c3bSlianep  * clarity into the actions taken on notification of state change, as well
34*99b44c3bSlianep  * as a place for future expansion including hooks for configurable actions.
35*99b44c3bSlianep  * All functions are called with dgraph_lock held.
36*99b44c3bSlianep  *
37*99b44c3bSlianep  * The start action for this state machine is not explicit.  The states
38*99b44c3bSlianep  * (ONLINE and DEGRADED) which needs to know when they're entering the state
39*99b44c3bSlianep  * due to a daemon restart implement this understanding by checking for
40*99b44c3bSlianep  * transition from uninitialized.  In the future, this would likely be better
41*99b44c3bSlianep  * as an explicit start action instead of relying on an overloaded transition.
42*99b44c3bSlianep  *
43*99b44c3bSlianep  * All gt_enter functions use the same set of return codes.
44*99b44c3bSlianep  *    0              success
45*99b44c3bSlianep  *    ECONNABORTED   repository connection aborted
46*99b44c3bSlianep  */
47*99b44c3bSlianep 
48*99b44c3bSlianep #include "startd.h"
49*99b44c3bSlianep 
50*99b44c3bSlianep static int
51*99b44c3bSlianep gt_running(restarter_instance_state_t state)
52*99b44c3bSlianep {
53*99b44c3bSlianep 	if (state == RESTARTER_STATE_ONLINE ||
54*99b44c3bSlianep 	    state == RESTARTER_STATE_DEGRADED)
55*99b44c3bSlianep 		return (1);
56*99b44c3bSlianep 
57*99b44c3bSlianep 	return (0);
58*99b44c3bSlianep }
59*99b44c3bSlianep 
60*99b44c3bSlianep static int
61*99b44c3bSlianep gt_enter_uninit(scf_handle_t *h, graph_vertex_t *v,
62*99b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
63*99b44c3bSlianep {
64*99b44c3bSlianep 	int err;
65*99b44c3bSlianep 	scf_instance_t *inst;
66*99b44c3bSlianep 
67*99b44c3bSlianep 	vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state));
68*99b44c3bSlianep 
69*99b44c3bSlianep 	/* Initialize instance by refreshing it. */
70*99b44c3bSlianep 
71*99b44c3bSlianep 	err = libscf_fmri_get_instance(h, v->gv_name, &inst);
72*99b44c3bSlianep 	switch (err) {
73*99b44c3bSlianep 	case 0:
74*99b44c3bSlianep 		break;
75*99b44c3bSlianep 
76*99b44c3bSlianep 	case ECONNABORTED:
77*99b44c3bSlianep 		return (ECONNABORTED);
78*99b44c3bSlianep 
79*99b44c3bSlianep 	case ENOENT:
80*99b44c3bSlianep 		return (0);
81*99b44c3bSlianep 
82*99b44c3bSlianep 	case EINVAL:
83*99b44c3bSlianep 	case ENOTSUP:
84*99b44c3bSlianep 	default:
85*99b44c3bSlianep 		bad_error("libscf_fmri_get_instance", err);
86*99b44c3bSlianep 	}
87*99b44c3bSlianep 
88*99b44c3bSlianep 	err = refresh_vertex(v, inst);
89*99b44c3bSlianep 	if (err == 0)
90*99b44c3bSlianep 		graph_enable_by_vertex(v, v->gv_flags & GV_ENABLED, 0);
91*99b44c3bSlianep 
92*99b44c3bSlianep 	scf_instance_destroy(inst);
93*99b44c3bSlianep 
94*99b44c3bSlianep 	/* If the service was running, propagate a stop event. */
95*99b44c3bSlianep 	if (gt_running(old_state)) {
96*99b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
97*99b44c3bSlianep 		    v->gv_name);
98*99b44c3bSlianep 
99*99b44c3bSlianep 		graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr);
100*99b44c3bSlianep 	}
101*99b44c3bSlianep 
102*99b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_UNINIT, old_state);
103*99b44c3bSlianep 	return (0);
104*99b44c3bSlianep }
105*99b44c3bSlianep 
106*99b44c3bSlianep static int
107*99b44c3bSlianep gt_enter_maint(scf_handle_t *h, graph_vertex_t *v,
108*99b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
109*99b44c3bSlianep {
110*99b44c3bSlianep 	vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state));
111*99b44c3bSlianep 
112*99b44c3bSlianep 	if (gt_running(old_state)) {
113*99b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
114*99b44c3bSlianep 		    v->gv_name);
115*99b44c3bSlianep 
116*99b44c3bSlianep 		graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr);
117*99b44c3bSlianep 	} else if (v->gv_state == RESTARTER_STATE_MAINT) {
118*99b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating maintenance of %s.\n",
119*99b44c3bSlianep 		    v->gv_name);
120*99b44c3bSlianep 
121*99b44c3bSlianep 		graph_transition_propagate(v, RESTARTER_EVENT_TYPE_START, rerr);
122*99b44c3bSlianep 	}
123*99b44c3bSlianep 
124*99b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_MAINT, old_state);
125*99b44c3bSlianep 	return (0);
126*99b44c3bSlianep }
127*99b44c3bSlianep 
128*99b44c3bSlianep static int
129*99b44c3bSlianep gt_enter_offline(scf_handle_t *h, graph_vertex_t *v,
130*99b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
131*99b44c3bSlianep {
132*99b44c3bSlianep 	vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state));
133*99b44c3bSlianep 
134*99b44c3bSlianep 	/*
135*99b44c3bSlianep 	 * If the instance should be enabled, see if we can start it.
136*99b44c3bSlianep 	 * Otherwise send a disable command.
137*99b44c3bSlianep 	 */
138*99b44c3bSlianep 	if (v->gv_flags & GV_ENABLED) {
139*99b44c3bSlianep 		graph_start_if_satisfied(v);
140*99b44c3bSlianep 	} else {
141*99b44c3bSlianep 		if (gt_running(old_state) && v->gv_post_disable_f)
142*99b44c3bSlianep 			v->gv_post_disable_f();
143*99b44c3bSlianep 		vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE);
144*99b44c3bSlianep 	}
145*99b44c3bSlianep 
146*99b44c3bSlianep 	if (gt_running(old_state)) {
147*99b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
148*99b44c3bSlianep 		    v->gv_name);
149*99b44c3bSlianep 
150*99b44c3bSlianep 		graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr);
151*99b44c3bSlianep 	}
152*99b44c3bSlianep 
153*99b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_OFFLINE, old_state);
154*99b44c3bSlianep 	return (0);
155*99b44c3bSlianep }
156*99b44c3bSlianep 
157*99b44c3bSlianep static int
158*99b44c3bSlianep gt_enter_disabled(scf_handle_t *h, graph_vertex_t *v,
159*99b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
160*99b44c3bSlianep {
161*99b44c3bSlianep 	vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state));
162*99b44c3bSlianep 
163*99b44c3bSlianep 	/*
164*99b44c3bSlianep 	 * If the instance should be disabled, no problem.  Otherwise,
165*99b44c3bSlianep 	 * send an enable command, which should result in the instance
166*99b44c3bSlianep 	 * moving to OFFLINE.
167*99b44c3bSlianep 	 */
168*99b44c3bSlianep 	if (v->gv_flags & GV_ENABLED) {
169*99b44c3bSlianep 		vertex_send_event(v, RESTARTER_EVENT_TYPE_ENABLE);
170*99b44c3bSlianep 	} else if (gt_running(old_state) && v->gv_post_disable_f) {
171*99b44c3bSlianep 		v->gv_post_disable_f();
172*99b44c3bSlianep 	}
173*99b44c3bSlianep 
174*99b44c3bSlianep 	/*
175*99b44c3bSlianep 	 * If the service was running, propagate this as a stop.
176*99b44c3bSlianep 	 * Otherwise, we treat other transitions as a start propagate,
177*99b44c3bSlianep 	 * since they can satisfy optional_all dependencies.
178*99b44c3bSlianep 	 */
179*99b44c3bSlianep 	if (gt_running(old_state)) {
180*99b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
181*99b44c3bSlianep 		    v->gv_name);
182*99b44c3bSlianep 
183*99b44c3bSlianep 		graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr);
184*99b44c3bSlianep 
185*99b44c3bSlianep 	} else if (v->gv_state == RESTARTER_STATE_DISABLED) {
186*99b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating disable of %s.\n",
187*99b44c3bSlianep 		    v->gv_name);
188*99b44c3bSlianep 
189*99b44c3bSlianep 		graph_transition_propagate(v, RESTARTER_EVENT_TYPE_START, rerr);
190*99b44c3bSlianep 	}
191*99b44c3bSlianep 
192*99b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_DISABLED, old_state);
193*99b44c3bSlianep 	return (0);
194*99b44c3bSlianep }
195*99b44c3bSlianep 
196*99b44c3bSlianep static int
197*99b44c3bSlianep gt_internal_online_or_degraded(scf_handle_t *h, graph_vertex_t *v,
198*99b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
199*99b44c3bSlianep {
200*99b44c3bSlianep 	int r;
201*99b44c3bSlianep 
202*99b44c3bSlianep 	/*
203*99b44c3bSlianep 	 * If the instance has just come up, update the start
204*99b44c3bSlianep 	 * snapshot.
205*99b44c3bSlianep 	 */
206*99b44c3bSlianep 	if (gt_running(old_state) == 0) {
207*99b44c3bSlianep 		/*
208*99b44c3bSlianep 		 * Don't fire if we're just recovering state
209*99b44c3bSlianep 		 * after a restart.
210*99b44c3bSlianep 		 */
211*99b44c3bSlianep 		if (old_state != RESTARTER_STATE_UNINIT &&
212*99b44c3bSlianep 		    v->gv_post_online_f)
213*99b44c3bSlianep 			v->gv_post_online_f();
214*99b44c3bSlianep 
215*99b44c3bSlianep 		r = libscf_snapshots_poststart(h, v->gv_name, B_TRUE);
216*99b44c3bSlianep 		switch (r) {
217*99b44c3bSlianep 		case 0:
218*99b44c3bSlianep 		case ENOENT:
219*99b44c3bSlianep 			/*
220*99b44c3bSlianep 			 * If ENOENT, the instance must have been
221*99b44c3bSlianep 			 * deleted.  Pretend we were successful since
222*99b44c3bSlianep 			 * we should get a delete event later.
223*99b44c3bSlianep 			 */
224*99b44c3bSlianep 			break;
225*99b44c3bSlianep 
226*99b44c3bSlianep 		case ECONNABORTED:
227*99b44c3bSlianep 			return (ECONNABORTED);
228*99b44c3bSlianep 
229*99b44c3bSlianep 		case EACCES:
230*99b44c3bSlianep 		case ENOTSUP:
231*99b44c3bSlianep 		default:
232*99b44c3bSlianep 			bad_error("libscf_snapshots_poststart", r);
233*99b44c3bSlianep 		}
234*99b44c3bSlianep 	}
235*99b44c3bSlianep 	if (!(v->gv_flags & GV_ENABLED))
236*99b44c3bSlianep 		vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE);
237*99b44c3bSlianep 
238*99b44c3bSlianep 	if (gt_running(old_state) == 0) {
239*99b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating start of %s.\n",
240*99b44c3bSlianep 		    v->gv_name);
241*99b44c3bSlianep 
242*99b44c3bSlianep 		graph_transition_propagate(v,
243*99b44c3bSlianep 		    RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON, rerr);
244*99b44c3bSlianep 	} else if (rerr == RERR_REFRESH) {
245*99b44c3bSlianep 		/* For refresh we'll get a message sans state change */
246*99b44c3bSlianep 
247*99b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating refresh of %s.\n",
248*99b44c3bSlianep 		    v->gv_name);
249*99b44c3bSlianep 
250*99b44c3bSlianep 		graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr);
251*99b44c3bSlianep 	}
252*99b44c3bSlianep 
253*99b44c3bSlianep 	return (0);
254*99b44c3bSlianep }
255*99b44c3bSlianep 
256*99b44c3bSlianep static int
257*99b44c3bSlianep gt_enter_online(scf_handle_t *h, graph_vertex_t *v,
258*99b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
259*99b44c3bSlianep {
260*99b44c3bSlianep 	int r;
261*99b44c3bSlianep 
262*99b44c3bSlianep 	r = gt_internal_online_or_degraded(h, v, old_state, rerr);
263*99b44c3bSlianep 	if (r != 0)
264*99b44c3bSlianep 		return (r);
265*99b44c3bSlianep 
266*99b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_ONLINE, old_state);
267*99b44c3bSlianep 	return (0);
268*99b44c3bSlianep }
269*99b44c3bSlianep 
270*99b44c3bSlianep static int
271*99b44c3bSlianep gt_enter_degraded(scf_handle_t *h, graph_vertex_t *v,
272*99b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
273*99b44c3bSlianep {
274*99b44c3bSlianep 	int r;
275*99b44c3bSlianep 
276*99b44c3bSlianep 	r = gt_internal_online_or_degraded(h, v, old_state, rerr);
277*99b44c3bSlianep 	if (r != 0)
278*99b44c3bSlianep 		return (r);
279*99b44c3bSlianep 
280*99b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_DEGRADED, old_state);
281*99b44c3bSlianep 	return (0);
282*99b44c3bSlianep }
283*99b44c3bSlianep 
284*99b44c3bSlianep /*
285*99b44c3bSlianep  * gt_transition() implements the state transition for the graph
286*99b44c3bSlianep  * state machine.  It can return:
287*99b44c3bSlianep  *    0              success
288*99b44c3bSlianep  *    ECONNABORTED   repository connection aborted
289*99b44c3bSlianep  */
290*99b44c3bSlianep int
291*99b44c3bSlianep gt_transition(scf_handle_t *h, graph_vertex_t *v, restarter_error_t rerr,
292*99b44c3bSlianep     restarter_instance_state_t old_state, restarter_instance_state_t new_state)
293*99b44c3bSlianep {
294*99b44c3bSlianep 	int err = 0;
295*99b44c3bSlianep 
296*99b44c3bSlianep 	/*
297*99b44c3bSlianep 	 * If there's a common set of work to be done on exit from the
298*99b44c3bSlianep 	 * old_state, include it as a separate set of functions here.  For
299*99b44c3bSlianep 	 * now there's no such work, so there are no gt_exit functions.
300*99b44c3bSlianep 	 */
301*99b44c3bSlianep 
302*99b44c3bSlianep 	/*
303*99b44c3bSlianep 	 * Now call the appropriate gt_enter function for the new state.
304*99b44c3bSlianep 	 */
305*99b44c3bSlianep 	switch (new_state) {
306*99b44c3bSlianep 	case RESTARTER_STATE_UNINIT:
307*99b44c3bSlianep 		err = gt_enter_uninit(h, v, old_state, rerr);
308*99b44c3bSlianep 		break;
309*99b44c3bSlianep 
310*99b44c3bSlianep 	case RESTARTER_STATE_DISABLED:
311*99b44c3bSlianep 		err = gt_enter_disabled(h, v, old_state, rerr);
312*99b44c3bSlianep 		break;
313*99b44c3bSlianep 
314*99b44c3bSlianep 	case RESTARTER_STATE_OFFLINE:
315*99b44c3bSlianep 		err = gt_enter_offline(h, v, old_state, rerr);
316*99b44c3bSlianep 		break;
317*99b44c3bSlianep 
318*99b44c3bSlianep 	case RESTARTER_STATE_ONLINE:
319*99b44c3bSlianep 		err = gt_enter_online(h, v, old_state, rerr);
320*99b44c3bSlianep 		break;
321*99b44c3bSlianep 
322*99b44c3bSlianep 	case RESTARTER_STATE_DEGRADED:
323*99b44c3bSlianep 		err = gt_enter_degraded(h, v, old_state, rerr);
324*99b44c3bSlianep 		break;
325*99b44c3bSlianep 
326*99b44c3bSlianep 	case RESTARTER_STATE_MAINT:
327*99b44c3bSlianep 		err = gt_enter_maint(h, v, old_state, rerr);
328*99b44c3bSlianep 		break;
329*99b44c3bSlianep 
330*99b44c3bSlianep 	default:
331*99b44c3bSlianep 		/* Shouldn't have been passed an invalid state. */
332*99b44c3bSlianep #ifndef NDEBUG
333*99b44c3bSlianep 		uu_warn("%s:%d: Uncaught case %d.\n", __FILE__, __LINE__,
334*99b44c3bSlianep 		    new_state);
335*99b44c3bSlianep #endif
336*99b44c3bSlianep 		abort();
337*99b44c3bSlianep 	}
338*99b44c3bSlianep 
339*99b44c3bSlianep 	return (err);
340*99b44c3bSlianep }
341