xref: /titanic_44/usr/src/cmd/svc/startd/transition.c (revision 56e2393871ebc8ef99ec6ceaded2cd17f208ee61)
199b44c3bSlianep /*
299b44c3bSlianep  * CDDL HEADER START
399b44c3bSlianep  *
499b44c3bSlianep  * The contents of this file are subject to the terms of the
599b44c3bSlianep  * Common Development and Distribution License (the "License").
699b44c3bSlianep  * You may not use this file except in compliance with the License.
799b44c3bSlianep  *
899b44c3bSlianep  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
999b44c3bSlianep  * or http://www.opensolaris.org/os/licensing.
1099b44c3bSlianep  * See the License for the specific language governing permissions
1199b44c3bSlianep  * and limitations under the License.
1299b44c3bSlianep  *
1399b44c3bSlianep  * When distributing Covered Code, include this CDDL HEADER in each
1499b44c3bSlianep  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1599b44c3bSlianep  * If applicable, add the following below this CDDL HEADER, with the
1699b44c3bSlianep  * fields enclosed by brackets "[]" replaced with your own identifying
1799b44c3bSlianep  * information: Portions Copyright [yyyy] [name of copyright owner]
1899b44c3bSlianep  *
1999b44c3bSlianep  * CDDL HEADER END
2099b44c3bSlianep  */
2199b44c3bSlianep /*
2299b44c3bSlianep  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2399b44c3bSlianep  * Use is subject to license terms.
2499b44c3bSlianep  */
2599b44c3bSlianep 
2699b44c3bSlianep #pragma ident	"%Z%%M%	%I%	%E% SMI"
2799b44c3bSlianep 
2899b44c3bSlianep /*
2999b44c3bSlianep  * transition.c - Graph State Machine
3099b44c3bSlianep  *
3199b44c3bSlianep  * The graph state machine is implemented here, with a typical approach
3299b44c3bSlianep  * of a function per state.  Separating the implementation allows more
3399b44c3bSlianep  * clarity into the actions taken on notification of state change, as well
3499b44c3bSlianep  * as a place for future expansion including hooks for configurable actions.
3599b44c3bSlianep  * All functions are called with dgraph_lock held.
3699b44c3bSlianep  *
3799b44c3bSlianep  * The start action for this state machine is not explicit.  The states
38*56e23938Sbustos  * (ONLINE and DEGRADED) which need to know when they're entering the state
3999b44c3bSlianep  * due to a daemon restart implement this understanding by checking for
4099b44c3bSlianep  * transition from uninitialized.  In the future, this would likely be better
4199b44c3bSlianep  * as an explicit start action instead of relying on an overloaded transition.
4299b44c3bSlianep  *
4399b44c3bSlianep  * All gt_enter functions use the same set of return codes.
4499b44c3bSlianep  *    0              success
4599b44c3bSlianep  *    ECONNABORTED   repository connection aborted
4699b44c3bSlianep  */
4799b44c3bSlianep 
4899b44c3bSlianep #include "startd.h"
4999b44c3bSlianep 
5099b44c3bSlianep static int
5199b44c3bSlianep gt_running(restarter_instance_state_t state)
5299b44c3bSlianep {
5399b44c3bSlianep 	if (state == RESTARTER_STATE_ONLINE ||
5499b44c3bSlianep 	    state == RESTARTER_STATE_DEGRADED)
5599b44c3bSlianep 		return (1);
5699b44c3bSlianep 
5799b44c3bSlianep 	return (0);
5899b44c3bSlianep }
5999b44c3bSlianep 
6099b44c3bSlianep static int
6199b44c3bSlianep gt_enter_uninit(scf_handle_t *h, graph_vertex_t *v,
6299b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
6399b44c3bSlianep {
6499b44c3bSlianep 	int err;
6599b44c3bSlianep 	scf_instance_t *inst;
6699b44c3bSlianep 
6799b44c3bSlianep 	/* Initialize instance by refreshing it. */
6899b44c3bSlianep 
6999b44c3bSlianep 	err = libscf_fmri_get_instance(h, v->gv_name, &inst);
7099b44c3bSlianep 	switch (err) {
7199b44c3bSlianep 	case 0:
7299b44c3bSlianep 		break;
7399b44c3bSlianep 
7499b44c3bSlianep 	case ECONNABORTED:
7599b44c3bSlianep 		return (ECONNABORTED);
7699b44c3bSlianep 
7799b44c3bSlianep 	case ENOENT:
7899b44c3bSlianep 		return (0);
7999b44c3bSlianep 
8099b44c3bSlianep 	case EINVAL:
8199b44c3bSlianep 	case ENOTSUP:
8299b44c3bSlianep 	default:
8399b44c3bSlianep 		bad_error("libscf_fmri_get_instance", err);
8499b44c3bSlianep 	}
8599b44c3bSlianep 
8699b44c3bSlianep 	err = refresh_vertex(v, inst);
8799b44c3bSlianep 	if (err == 0)
8899b44c3bSlianep 		graph_enable_by_vertex(v, v->gv_flags & GV_ENABLED, 0);
8999b44c3bSlianep 
9099b44c3bSlianep 	scf_instance_destroy(inst);
9199b44c3bSlianep 
9299b44c3bSlianep 	/* If the service was running, propagate a stop event. */
9399b44c3bSlianep 	if (gt_running(old_state)) {
9499b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
9599b44c3bSlianep 		    v->gv_name);
9699b44c3bSlianep 
97cd3bce3eSlianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
9899b44c3bSlianep 	}
9999b44c3bSlianep 
10099b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_UNINIT, old_state);
10199b44c3bSlianep 	return (0);
10299b44c3bSlianep }
10399b44c3bSlianep 
104*56e23938Sbustos /* ARGSUSED */
10599b44c3bSlianep static int
10699b44c3bSlianep gt_enter_maint(scf_handle_t *h, graph_vertex_t *v,
10799b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
10899b44c3bSlianep {
109cd3bce3eSlianep 	/*
110cd3bce3eSlianep 	 * If the service was running, propagate a stop event.  If the
111cd3bce3eSlianep 	 * service was not running the maintenance transition may satisfy
112cd3bce3eSlianep 	 * optional dependencies and should be propagated to determine
113cd3bce3eSlianep 	 * whether new dependents are satisfiable.
114cd3bce3eSlianep 	 */
11599b44c3bSlianep 	if (gt_running(old_state)) {
116cd3bce3eSlianep 		log_framework(LOG_DEBUG, "Propagating maintenance (stop) of "
117cd3bce3eSlianep 		    "%s.\n", v->gv_name);
11899b44c3bSlianep 
119cd3bce3eSlianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
120cd3bce3eSlianep 	} else {
12199b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating maintenance of %s.\n",
12299b44c3bSlianep 		    v->gv_name);
12399b44c3bSlianep 
124cd3bce3eSlianep 		graph_transition_propagate(v, PROPAGATE_SAT, rerr);
12599b44c3bSlianep 	}
12699b44c3bSlianep 
12799b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_MAINT, old_state);
12899b44c3bSlianep 	return (0);
12999b44c3bSlianep }
13099b44c3bSlianep 
131*56e23938Sbustos /* ARGSUSED */
13299b44c3bSlianep static int
13399b44c3bSlianep gt_enter_offline(scf_handle_t *h, graph_vertex_t *v,
13499b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
13599b44c3bSlianep {
13699b44c3bSlianep 	/*
13799b44c3bSlianep 	 * If the instance should be enabled, see if we can start it.
13899b44c3bSlianep 	 * Otherwise send a disable command.
13999b44c3bSlianep 	 */
14099b44c3bSlianep 	if (v->gv_flags & GV_ENABLED) {
14199b44c3bSlianep 		graph_start_if_satisfied(v);
14299b44c3bSlianep 	} else {
14399b44c3bSlianep 		if (gt_running(old_state) && v->gv_post_disable_f)
14499b44c3bSlianep 			v->gv_post_disable_f();
14599b44c3bSlianep 		vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE);
14699b44c3bSlianep 	}
14799b44c3bSlianep 
14899b44c3bSlianep 	if (gt_running(old_state)) {
14999b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
15099b44c3bSlianep 		    v->gv_name);
15199b44c3bSlianep 
152cd3bce3eSlianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
15399b44c3bSlianep 	}
15499b44c3bSlianep 
15599b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_OFFLINE, old_state);
15699b44c3bSlianep 	return (0);
15799b44c3bSlianep }
15899b44c3bSlianep 
159*56e23938Sbustos /* ARGSUSED */
16099b44c3bSlianep static int
16199b44c3bSlianep gt_enter_disabled(scf_handle_t *h, graph_vertex_t *v,
16299b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
16399b44c3bSlianep {
16499b44c3bSlianep 	/*
16599b44c3bSlianep 	 * If the instance should be disabled, no problem.  Otherwise,
16699b44c3bSlianep 	 * send an enable command, which should result in the instance
16799b44c3bSlianep 	 * moving to OFFLINE.
16899b44c3bSlianep 	 */
16999b44c3bSlianep 	if (v->gv_flags & GV_ENABLED) {
17099b44c3bSlianep 		vertex_send_event(v, RESTARTER_EVENT_TYPE_ENABLE);
17199b44c3bSlianep 	} else if (gt_running(old_state) && v->gv_post_disable_f) {
17299b44c3bSlianep 		v->gv_post_disable_f();
17399b44c3bSlianep 	}
17499b44c3bSlianep 
17599b44c3bSlianep 	/*
176cd3bce3eSlianep 	 * If the service was running, propagate this as a stop.  If the
177cd3bce3eSlianep 	 * service was not running the disabled transition may satisfy
178cd3bce3eSlianep 	 * optional dependencies and should be propagated to determine
179cd3bce3eSlianep 	 * whether new dependents are satisfiable.
18099b44c3bSlianep 	 */
18199b44c3bSlianep 	if (gt_running(old_state)) {
18299b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
18399b44c3bSlianep 		    v->gv_name);
18499b44c3bSlianep 
185cd3bce3eSlianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
18699b44c3bSlianep 
187cd3bce3eSlianep 	} else {
18899b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating disable of %s.\n",
18999b44c3bSlianep 		    v->gv_name);
19099b44c3bSlianep 
191cd3bce3eSlianep 		graph_transition_propagate(v, PROPAGATE_SAT, rerr);
19299b44c3bSlianep 	}
19399b44c3bSlianep 
19499b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_DISABLED, old_state);
19599b44c3bSlianep 	return (0);
19699b44c3bSlianep }
19799b44c3bSlianep 
19899b44c3bSlianep static int
19999b44c3bSlianep gt_internal_online_or_degraded(scf_handle_t *h, graph_vertex_t *v,
20099b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
20199b44c3bSlianep {
20299b44c3bSlianep 	int r;
20399b44c3bSlianep 
20499b44c3bSlianep 	/*
20599b44c3bSlianep 	 * If the instance has just come up, update the start
20699b44c3bSlianep 	 * snapshot.
20799b44c3bSlianep 	 */
20899b44c3bSlianep 	if (gt_running(old_state) == 0) {
20999b44c3bSlianep 		/*
21099b44c3bSlianep 		 * Don't fire if we're just recovering state
21199b44c3bSlianep 		 * after a restart.
21299b44c3bSlianep 		 */
21399b44c3bSlianep 		if (old_state != RESTARTER_STATE_UNINIT &&
21499b44c3bSlianep 		    v->gv_post_online_f)
21599b44c3bSlianep 			v->gv_post_online_f();
21699b44c3bSlianep 
21799b44c3bSlianep 		r = libscf_snapshots_poststart(h, v->gv_name, B_TRUE);
21899b44c3bSlianep 		switch (r) {
21999b44c3bSlianep 		case 0:
22099b44c3bSlianep 		case ENOENT:
22199b44c3bSlianep 			/*
22299b44c3bSlianep 			 * If ENOENT, the instance must have been
22399b44c3bSlianep 			 * deleted.  Pretend we were successful since
22499b44c3bSlianep 			 * we should get a delete event later.
22599b44c3bSlianep 			 */
22699b44c3bSlianep 			break;
22799b44c3bSlianep 
22899b44c3bSlianep 		case ECONNABORTED:
22999b44c3bSlianep 			return (ECONNABORTED);
23099b44c3bSlianep 
23199b44c3bSlianep 		case EACCES:
23299b44c3bSlianep 		case ENOTSUP:
23399b44c3bSlianep 		default:
23499b44c3bSlianep 			bad_error("libscf_snapshots_poststart", r);
23599b44c3bSlianep 		}
23699b44c3bSlianep 	}
23799b44c3bSlianep 	if (!(v->gv_flags & GV_ENABLED))
23899b44c3bSlianep 		vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE);
23999b44c3bSlianep 
24099b44c3bSlianep 	if (gt_running(old_state) == 0) {
24199b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating start of %s.\n",
24299b44c3bSlianep 		    v->gv_name);
24399b44c3bSlianep 
244cd3bce3eSlianep 		graph_transition_propagate(v, PROPAGATE_START, rerr);
24599b44c3bSlianep 	} else if (rerr == RERR_REFRESH) {
24699b44c3bSlianep 		/* For refresh we'll get a message sans state change */
24799b44c3bSlianep 
24899b44c3bSlianep 		log_framework(LOG_DEBUG, "Propagating refresh of %s.\n",
24999b44c3bSlianep 		    v->gv_name);
25099b44c3bSlianep 
251cd3bce3eSlianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
25299b44c3bSlianep 	}
25399b44c3bSlianep 
25499b44c3bSlianep 	return (0);
25599b44c3bSlianep }
25699b44c3bSlianep 
25799b44c3bSlianep static int
25899b44c3bSlianep gt_enter_online(scf_handle_t *h, graph_vertex_t *v,
25999b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
26099b44c3bSlianep {
26199b44c3bSlianep 	int r;
26299b44c3bSlianep 
26399b44c3bSlianep 	r = gt_internal_online_or_degraded(h, v, old_state, rerr);
26499b44c3bSlianep 	if (r != 0)
26599b44c3bSlianep 		return (r);
26699b44c3bSlianep 
26799b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_ONLINE, old_state);
26899b44c3bSlianep 	return (0);
26999b44c3bSlianep }
27099b44c3bSlianep 
27199b44c3bSlianep static int
27299b44c3bSlianep gt_enter_degraded(scf_handle_t *h, graph_vertex_t *v,
27399b44c3bSlianep     restarter_instance_state_t old_state, restarter_error_t rerr)
27499b44c3bSlianep {
27599b44c3bSlianep 	int r;
27699b44c3bSlianep 
27799b44c3bSlianep 	r = gt_internal_online_or_degraded(h, v, old_state, rerr);
27899b44c3bSlianep 	if (r != 0)
27999b44c3bSlianep 		return (r);
28099b44c3bSlianep 
28199b44c3bSlianep 	graph_transition_sulogin(RESTARTER_STATE_DEGRADED, old_state);
28299b44c3bSlianep 	return (0);
28399b44c3bSlianep }
28499b44c3bSlianep 
28599b44c3bSlianep /*
28699b44c3bSlianep  * gt_transition() implements the state transition for the graph
28799b44c3bSlianep  * state machine.  It can return:
28899b44c3bSlianep  *    0              success
28999b44c3bSlianep  *    ECONNABORTED   repository connection aborted
290cd3bce3eSlianep  *
291cd3bce3eSlianep  * v->gv_state should be set to the state we're transitioning to before
292cd3bce3eSlianep  * calling this function.
29399b44c3bSlianep  */
29499b44c3bSlianep int
29599b44c3bSlianep gt_transition(scf_handle_t *h, graph_vertex_t *v, restarter_error_t rerr,
296cd3bce3eSlianep     restarter_instance_state_t old_state)
29799b44c3bSlianep {
298*56e23938Sbustos 	int err;
299*56e23938Sbustos 	int lost_repository = 0;
30099b44c3bSlianep 
30199b44c3bSlianep 	/*
30299b44c3bSlianep 	 * If there's a common set of work to be done on exit from the
30399b44c3bSlianep 	 * old_state, include it as a separate set of functions here.  For
30499b44c3bSlianep 	 * now there's no such work, so there are no gt_exit functions.
30599b44c3bSlianep 	 */
30699b44c3bSlianep 
307*56e23938Sbustos 	err = vertex_subgraph_dependencies_shutdown(h, v, old_state);
308*56e23938Sbustos 	switch (err) {
309*56e23938Sbustos 	case 0:
310*56e23938Sbustos 		break;
311*56e23938Sbustos 
312*56e23938Sbustos 	case ECONNABORTED:
313*56e23938Sbustos 		lost_repository = 1;
314*56e23938Sbustos 		break;
315*56e23938Sbustos 
316*56e23938Sbustos 	default:
317*56e23938Sbustos 		bad_error("vertex_subgraph_dependencies_shutdown", err);
318*56e23938Sbustos 	}
319*56e23938Sbustos 
32099b44c3bSlianep 	/*
32199b44c3bSlianep 	 * Now call the appropriate gt_enter function for the new state.
32299b44c3bSlianep 	 */
323cd3bce3eSlianep 	switch (v->gv_state) {
32499b44c3bSlianep 	case RESTARTER_STATE_UNINIT:
32599b44c3bSlianep 		err = gt_enter_uninit(h, v, old_state, rerr);
32699b44c3bSlianep 		break;
32799b44c3bSlianep 
32899b44c3bSlianep 	case RESTARTER_STATE_DISABLED:
32999b44c3bSlianep 		err = gt_enter_disabled(h, v, old_state, rerr);
33099b44c3bSlianep 		break;
33199b44c3bSlianep 
33299b44c3bSlianep 	case RESTARTER_STATE_OFFLINE:
33399b44c3bSlianep 		err = gt_enter_offline(h, v, old_state, rerr);
33499b44c3bSlianep 		break;
33599b44c3bSlianep 
33699b44c3bSlianep 	case RESTARTER_STATE_ONLINE:
33799b44c3bSlianep 		err = gt_enter_online(h, v, old_state, rerr);
33899b44c3bSlianep 		break;
33999b44c3bSlianep 
34099b44c3bSlianep 	case RESTARTER_STATE_DEGRADED:
34199b44c3bSlianep 		err = gt_enter_degraded(h, v, old_state, rerr);
34299b44c3bSlianep 		break;
34399b44c3bSlianep 
34499b44c3bSlianep 	case RESTARTER_STATE_MAINT:
34599b44c3bSlianep 		err = gt_enter_maint(h, v, old_state, rerr);
34699b44c3bSlianep 		break;
34799b44c3bSlianep 
34899b44c3bSlianep 	default:
349cd3bce3eSlianep 		/* Shouldn't be in an invalid state. */
35099b44c3bSlianep #ifndef NDEBUG
351*56e23938Sbustos 		uu_warn("%s:%d: Invalid state %d.\n", __FILE__, __LINE__,
352cd3bce3eSlianep 		    v->gv_state);
35399b44c3bSlianep #endif
35499b44c3bSlianep 		abort();
35599b44c3bSlianep 	}
35699b44c3bSlianep 
357*56e23938Sbustos 	switch (err) {
358*56e23938Sbustos 	case 0:
359*56e23938Sbustos 		break;
360*56e23938Sbustos 
361*56e23938Sbustos 	case ECONNABORTED:
362*56e23938Sbustos 		lost_repository = 1;
363*56e23938Sbustos 		break;
364*56e23938Sbustos 
365*56e23938Sbustos 	default:
366*56e23938Sbustos #ifndef NDEBUG
367*56e23938Sbustos 		uu_warn("%s:%d: "
368*56e23938Sbustos 		    "gt_enter_%s() failed with unexpected error %d.\n",
369*56e23938Sbustos 		    __FILE__, __LINE__, instance_state_str[v->gv_state], err);
370*56e23938Sbustos #endif
371*56e23938Sbustos 		abort();
372*56e23938Sbustos 	}
373*56e23938Sbustos 
374*56e23938Sbustos 	return (lost_repository ? ECONNABORTED : 0);
37599b44c3bSlianep }
376