xref: /onnv-gate/usr/src/cmd/svc/startd/transition.c (revision 2747:87ae3ca2e4c8)
11958Slianep /*
21958Slianep  * CDDL HEADER START
31958Slianep  *
41958Slianep  * The contents of this file are subject to the terms of the
51958Slianep  * Common Development and Distribution License (the "License").
61958Slianep  * You may not use this file except in compliance with the License.
71958Slianep  *
81958Slianep  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91958Slianep  * or http://www.opensolaris.org/os/licensing.
101958Slianep  * See the License for the specific language governing permissions
111958Slianep  * and limitations under the License.
121958Slianep  *
131958Slianep  * When distributing Covered Code, include this CDDL HEADER in each
141958Slianep  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151958Slianep  * If applicable, add the following below this CDDL HEADER, with the
161958Slianep  * fields enclosed by brackets "[]" replaced with your own identifying
171958Slianep  * information: Portions Copyright [yyyy] [name of copyright owner]
181958Slianep  *
191958Slianep  * CDDL HEADER END
201958Slianep  */
211958Slianep /*
221958Slianep  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
231958Slianep  * Use is subject to license terms.
241958Slianep  */
251958Slianep 
261958Slianep #pragma ident	"%Z%%M%	%I%	%E% SMI"
271958Slianep 
281958Slianep /*
291958Slianep  * transition.c - Graph State Machine
301958Slianep  *
311958Slianep  * The graph state machine is implemented here, with a typical approach
321958Slianep  * of a function per state.  Separating the implementation allows more
331958Slianep  * clarity into the actions taken on notification of state change, as well
341958Slianep  * as a place for future expansion including hooks for configurable actions.
351958Slianep  * All functions are called with dgraph_lock held.
361958Slianep  *
371958Slianep  * The start action for this state machine is not explicit.  The states
38*2747Sbustos  * (ONLINE and DEGRADED) which need to know when they're entering the state
391958Slianep  * due to a daemon restart implement this understanding by checking for
401958Slianep  * transition from uninitialized.  In the future, this would likely be better
411958Slianep  * as an explicit start action instead of relying on an overloaded transition.
421958Slianep  *
431958Slianep  * All gt_enter functions use the same set of return codes.
441958Slianep  *    0              success
451958Slianep  *    ECONNABORTED   repository connection aborted
461958Slianep  */
471958Slianep 
481958Slianep #include "startd.h"
491958Slianep 
501958Slianep static int
511958Slianep gt_running(restarter_instance_state_t state)
521958Slianep {
531958Slianep 	if (state == RESTARTER_STATE_ONLINE ||
541958Slianep 	    state == RESTARTER_STATE_DEGRADED)
551958Slianep 		return (1);
561958Slianep 
571958Slianep 	return (0);
581958Slianep }
591958Slianep 
601958Slianep static int
611958Slianep gt_enter_uninit(scf_handle_t *h, graph_vertex_t *v,
621958Slianep     restarter_instance_state_t old_state, restarter_error_t rerr)
631958Slianep {
641958Slianep 	int err;
651958Slianep 	scf_instance_t *inst;
661958Slianep 
671958Slianep 	/* Initialize instance by refreshing it. */
681958Slianep 
691958Slianep 	err = libscf_fmri_get_instance(h, v->gv_name, &inst);
701958Slianep 	switch (err) {
711958Slianep 	case 0:
721958Slianep 		break;
731958Slianep 
741958Slianep 	case ECONNABORTED:
751958Slianep 		return (ECONNABORTED);
761958Slianep 
771958Slianep 	case ENOENT:
781958Slianep 		return (0);
791958Slianep 
801958Slianep 	case EINVAL:
811958Slianep 	case ENOTSUP:
821958Slianep 	default:
831958Slianep 		bad_error("libscf_fmri_get_instance", err);
841958Slianep 	}
851958Slianep 
861958Slianep 	err = refresh_vertex(v, inst);
871958Slianep 	if (err == 0)
881958Slianep 		graph_enable_by_vertex(v, v->gv_flags & GV_ENABLED, 0);
891958Slianep 
901958Slianep 	scf_instance_destroy(inst);
911958Slianep 
921958Slianep 	/* If the service was running, propagate a stop event. */
931958Slianep 	if (gt_running(old_state)) {
941958Slianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
951958Slianep 		    v->gv_name);
961958Slianep 
972339Slianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
981958Slianep 	}
991958Slianep 
1001958Slianep 	graph_transition_sulogin(RESTARTER_STATE_UNINIT, old_state);
1011958Slianep 	return (0);
1021958Slianep }
1031958Slianep 
104*2747Sbustos /* ARGSUSED */
1051958Slianep static int
1061958Slianep gt_enter_maint(scf_handle_t *h, graph_vertex_t *v,
1071958Slianep     restarter_instance_state_t old_state, restarter_error_t rerr)
1081958Slianep {
1092339Slianep 	/*
1102339Slianep 	 * If the service was running, propagate a stop event.  If the
1112339Slianep 	 * service was not running the maintenance transition may satisfy
1122339Slianep 	 * optional dependencies and should be propagated to determine
1132339Slianep 	 * whether new dependents are satisfiable.
1142339Slianep 	 */
1151958Slianep 	if (gt_running(old_state)) {
1162339Slianep 		log_framework(LOG_DEBUG, "Propagating maintenance (stop) of "
1172339Slianep 		    "%s.\n", v->gv_name);
1181958Slianep 
1192339Slianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
1202339Slianep 	} else {
1211958Slianep 		log_framework(LOG_DEBUG, "Propagating maintenance of %s.\n",
1221958Slianep 		    v->gv_name);
1231958Slianep 
1242339Slianep 		graph_transition_propagate(v, PROPAGATE_SAT, rerr);
1251958Slianep 	}
1261958Slianep 
1271958Slianep 	graph_transition_sulogin(RESTARTER_STATE_MAINT, old_state);
1281958Slianep 	return (0);
1291958Slianep }
1301958Slianep 
131*2747Sbustos /* ARGSUSED */
1321958Slianep static int
1331958Slianep gt_enter_offline(scf_handle_t *h, graph_vertex_t *v,
1341958Slianep     restarter_instance_state_t old_state, restarter_error_t rerr)
1351958Slianep {
1361958Slianep 	/*
1371958Slianep 	 * If the instance should be enabled, see if we can start it.
1381958Slianep 	 * Otherwise send a disable command.
1391958Slianep 	 */
1401958Slianep 	if (v->gv_flags & GV_ENABLED) {
1411958Slianep 		graph_start_if_satisfied(v);
1421958Slianep 	} else {
1431958Slianep 		if (gt_running(old_state) && v->gv_post_disable_f)
1441958Slianep 			v->gv_post_disable_f();
1451958Slianep 		vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE);
1461958Slianep 	}
1471958Slianep 
1481958Slianep 	if (gt_running(old_state)) {
1491958Slianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
1501958Slianep 		    v->gv_name);
1511958Slianep 
1522339Slianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
1531958Slianep 	}
1541958Slianep 
1551958Slianep 	graph_transition_sulogin(RESTARTER_STATE_OFFLINE, old_state);
1561958Slianep 	return (0);
1571958Slianep }
1581958Slianep 
159*2747Sbustos /* ARGSUSED */
1601958Slianep static int
1611958Slianep gt_enter_disabled(scf_handle_t *h, graph_vertex_t *v,
1621958Slianep     restarter_instance_state_t old_state, restarter_error_t rerr)
1631958Slianep {
1641958Slianep 	/*
1651958Slianep 	 * If the instance should be disabled, no problem.  Otherwise,
1661958Slianep 	 * send an enable command, which should result in the instance
1671958Slianep 	 * moving to OFFLINE.
1681958Slianep 	 */
1691958Slianep 	if (v->gv_flags & GV_ENABLED) {
1701958Slianep 		vertex_send_event(v, RESTARTER_EVENT_TYPE_ENABLE);
1711958Slianep 	} else if (gt_running(old_state) && v->gv_post_disable_f) {
1721958Slianep 		v->gv_post_disable_f();
1731958Slianep 	}
1741958Slianep 
1751958Slianep 	/*
1762339Slianep 	 * If the service was running, propagate this as a stop.  If the
1772339Slianep 	 * service was not running the disabled transition may satisfy
1782339Slianep 	 * optional dependencies and should be propagated to determine
1792339Slianep 	 * whether new dependents are satisfiable.
1801958Slianep 	 */
1811958Slianep 	if (gt_running(old_state)) {
1821958Slianep 		log_framework(LOG_DEBUG, "Propagating stop of %s.\n",
1831958Slianep 		    v->gv_name);
1841958Slianep 
1852339Slianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
1861958Slianep 
1872339Slianep 	} else {
1881958Slianep 		log_framework(LOG_DEBUG, "Propagating disable of %s.\n",
1891958Slianep 		    v->gv_name);
1901958Slianep 
1912339Slianep 		graph_transition_propagate(v, PROPAGATE_SAT, rerr);
1921958Slianep 	}
1931958Slianep 
1941958Slianep 	graph_transition_sulogin(RESTARTER_STATE_DISABLED, old_state);
1951958Slianep 	return (0);
1961958Slianep }
1971958Slianep 
1981958Slianep static int
1991958Slianep gt_internal_online_or_degraded(scf_handle_t *h, graph_vertex_t *v,
2001958Slianep     restarter_instance_state_t old_state, restarter_error_t rerr)
2011958Slianep {
2021958Slianep 	int r;
2031958Slianep 
2041958Slianep 	/*
2051958Slianep 	 * If the instance has just come up, update the start
2061958Slianep 	 * snapshot.
2071958Slianep 	 */
2081958Slianep 	if (gt_running(old_state) == 0) {
2091958Slianep 		/*
2101958Slianep 		 * Don't fire if we're just recovering state
2111958Slianep 		 * after a restart.
2121958Slianep 		 */
2131958Slianep 		if (old_state != RESTARTER_STATE_UNINIT &&
2141958Slianep 		    v->gv_post_online_f)
2151958Slianep 			v->gv_post_online_f();
2161958Slianep 
2171958Slianep 		r = libscf_snapshots_poststart(h, v->gv_name, B_TRUE);
2181958Slianep 		switch (r) {
2191958Slianep 		case 0:
2201958Slianep 		case ENOENT:
2211958Slianep 			/*
2221958Slianep 			 * If ENOENT, the instance must have been
2231958Slianep 			 * deleted.  Pretend we were successful since
2241958Slianep 			 * we should get a delete event later.
2251958Slianep 			 */
2261958Slianep 			break;
2271958Slianep 
2281958Slianep 		case ECONNABORTED:
2291958Slianep 			return (ECONNABORTED);
2301958Slianep 
2311958Slianep 		case EACCES:
2321958Slianep 		case ENOTSUP:
2331958Slianep 		default:
2341958Slianep 			bad_error("libscf_snapshots_poststart", r);
2351958Slianep 		}
2361958Slianep 	}
2371958Slianep 	if (!(v->gv_flags & GV_ENABLED))
2381958Slianep 		vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE);
2391958Slianep 
2401958Slianep 	if (gt_running(old_state) == 0) {
2411958Slianep 		log_framework(LOG_DEBUG, "Propagating start of %s.\n",
2421958Slianep 		    v->gv_name);
2431958Slianep 
2442339Slianep 		graph_transition_propagate(v, PROPAGATE_START, rerr);
2451958Slianep 	} else if (rerr == RERR_REFRESH) {
2461958Slianep 		/* For refresh we'll get a message sans state change */
2471958Slianep 
2481958Slianep 		log_framework(LOG_DEBUG, "Propagating refresh of %s.\n",
2491958Slianep 		    v->gv_name);
2501958Slianep 
2512339Slianep 		graph_transition_propagate(v, PROPAGATE_STOP, rerr);
2521958Slianep 	}
2531958Slianep 
2541958Slianep 	return (0);
2551958Slianep }
2561958Slianep 
2571958Slianep static int
2581958Slianep gt_enter_online(scf_handle_t *h, graph_vertex_t *v,
2591958Slianep     restarter_instance_state_t old_state, restarter_error_t rerr)
2601958Slianep {
2611958Slianep 	int r;
2621958Slianep 
2631958Slianep 	r = gt_internal_online_or_degraded(h, v, old_state, rerr);
2641958Slianep 	if (r != 0)
2651958Slianep 		return (r);
2661958Slianep 
2671958Slianep 	graph_transition_sulogin(RESTARTER_STATE_ONLINE, old_state);
2681958Slianep 	return (0);
2691958Slianep }
2701958Slianep 
2711958Slianep static int
2721958Slianep gt_enter_degraded(scf_handle_t *h, graph_vertex_t *v,
2731958Slianep     restarter_instance_state_t old_state, restarter_error_t rerr)
2741958Slianep {
2751958Slianep 	int r;
2761958Slianep 
2771958Slianep 	r = gt_internal_online_or_degraded(h, v, old_state, rerr);
2781958Slianep 	if (r != 0)
2791958Slianep 		return (r);
2801958Slianep 
2811958Slianep 	graph_transition_sulogin(RESTARTER_STATE_DEGRADED, old_state);
2821958Slianep 	return (0);
2831958Slianep }
2841958Slianep 
2851958Slianep /*
2861958Slianep  * gt_transition() implements the state transition for the graph
2871958Slianep  * state machine.  It can return:
2881958Slianep  *    0              success
2891958Slianep  *    ECONNABORTED   repository connection aborted
2902339Slianep  *
2912339Slianep  * v->gv_state should be set to the state we're transitioning to before
2922339Slianep  * calling this function.
2931958Slianep  */
2941958Slianep int
2951958Slianep gt_transition(scf_handle_t *h, graph_vertex_t *v, restarter_error_t rerr,
2962339Slianep     restarter_instance_state_t old_state)
2971958Slianep {
298*2747Sbustos 	int err;
299*2747Sbustos 	int lost_repository = 0;
3001958Slianep 
3011958Slianep 	/*
3021958Slianep 	 * If there's a common set of work to be done on exit from the
3031958Slianep 	 * old_state, include it as a separate set of functions here.  For
3041958Slianep 	 * now there's no such work, so there are no gt_exit functions.
3051958Slianep 	 */
3061958Slianep 
307*2747Sbustos 	err = vertex_subgraph_dependencies_shutdown(h, v, old_state);
308*2747Sbustos 	switch (err) {
309*2747Sbustos 	case 0:
310*2747Sbustos 		break;
311*2747Sbustos 
312*2747Sbustos 	case ECONNABORTED:
313*2747Sbustos 		lost_repository = 1;
314*2747Sbustos 		break;
315*2747Sbustos 
316*2747Sbustos 	default:
317*2747Sbustos 		bad_error("vertex_subgraph_dependencies_shutdown", err);
318*2747Sbustos 	}
319*2747Sbustos 
3201958Slianep 	/*
3211958Slianep 	 * Now call the appropriate gt_enter function for the new state.
3221958Slianep 	 */
3232339Slianep 	switch (v->gv_state) {
3241958Slianep 	case RESTARTER_STATE_UNINIT:
3251958Slianep 		err = gt_enter_uninit(h, v, old_state, rerr);
3261958Slianep 		break;
3271958Slianep 
3281958Slianep 	case RESTARTER_STATE_DISABLED:
3291958Slianep 		err = gt_enter_disabled(h, v, old_state, rerr);
3301958Slianep 		break;
3311958Slianep 
3321958Slianep 	case RESTARTER_STATE_OFFLINE:
3331958Slianep 		err = gt_enter_offline(h, v, old_state, rerr);
3341958Slianep 		break;
3351958Slianep 
3361958Slianep 	case RESTARTER_STATE_ONLINE:
3371958Slianep 		err = gt_enter_online(h, v, old_state, rerr);
3381958Slianep 		break;
3391958Slianep 
3401958Slianep 	case RESTARTER_STATE_DEGRADED:
3411958Slianep 		err = gt_enter_degraded(h, v, old_state, rerr);
3421958Slianep 		break;
3431958Slianep 
3441958Slianep 	case RESTARTER_STATE_MAINT:
3451958Slianep 		err = gt_enter_maint(h, v, old_state, rerr);
3461958Slianep 		break;
3471958Slianep 
3481958Slianep 	default:
3492339Slianep 		/* Shouldn't be in an invalid state. */
3501958Slianep #ifndef NDEBUG
351*2747Sbustos 		uu_warn("%s:%d: Invalid state %d.\n", __FILE__, __LINE__,
3522339Slianep 		    v->gv_state);
3531958Slianep #endif
3541958Slianep 		abort();
3551958Slianep 	}
3561958Slianep 
357*2747Sbustos 	switch (err) {
358*2747Sbustos 	case 0:
359*2747Sbustos 		break;
360*2747Sbustos 
361*2747Sbustos 	case ECONNABORTED:
362*2747Sbustos 		lost_repository = 1;
363*2747Sbustos 		break;
364*2747Sbustos 
365*2747Sbustos 	default:
366*2747Sbustos #ifndef NDEBUG
367*2747Sbustos 		uu_warn("%s:%d: "
368*2747Sbustos 		    "gt_enter_%s() failed with unexpected error %d.\n",
369*2747Sbustos 		    __FILE__, __LINE__, instance_state_str[v->gv_state], err);
370*2747Sbustos #endif
371*2747Sbustos 		abort();
372*2747Sbustos 	}
373*2747Sbustos 
374*2747Sbustos 	return (lost_repository ? ECONNABORTED : 0);
3751958Slianep }
376