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