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