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 3899b44c3bSlianep * (ONLINE and DEGRADED) which needs 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 vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 6899b44c3bSlianep 6999b44c3bSlianep /* Initialize instance by refreshing it. */ 7099b44c3bSlianep 7199b44c3bSlianep err = libscf_fmri_get_instance(h, v->gv_name, &inst); 7299b44c3bSlianep switch (err) { 7399b44c3bSlianep case 0: 7499b44c3bSlianep break; 7599b44c3bSlianep 7699b44c3bSlianep case ECONNABORTED: 7799b44c3bSlianep return (ECONNABORTED); 7899b44c3bSlianep 7999b44c3bSlianep case ENOENT: 8099b44c3bSlianep return (0); 8199b44c3bSlianep 8299b44c3bSlianep case EINVAL: 8399b44c3bSlianep case ENOTSUP: 8499b44c3bSlianep default: 8599b44c3bSlianep bad_error("libscf_fmri_get_instance", err); 8699b44c3bSlianep } 8799b44c3bSlianep 8899b44c3bSlianep err = refresh_vertex(v, inst); 8999b44c3bSlianep if (err == 0) 9099b44c3bSlianep graph_enable_by_vertex(v, v->gv_flags & GV_ENABLED, 0); 9199b44c3bSlianep 9299b44c3bSlianep scf_instance_destroy(inst); 9399b44c3bSlianep 9499b44c3bSlianep /* If the service was running, propagate a stop event. */ 9599b44c3bSlianep if (gt_running(old_state)) { 9699b44c3bSlianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 9799b44c3bSlianep v->gv_name); 9899b44c3bSlianep 99*cd3bce3eSlianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 10099b44c3bSlianep } 10199b44c3bSlianep 10299b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_UNINIT, old_state); 10399b44c3bSlianep return (0); 10499b44c3bSlianep } 10599b44c3bSlianep 10699b44c3bSlianep static int 10799b44c3bSlianep gt_enter_maint(scf_handle_t *h, graph_vertex_t *v, 10899b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 10999b44c3bSlianep { 11099b44c3bSlianep vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 11199b44c3bSlianep 112*cd3bce3eSlianep /* 113*cd3bce3eSlianep * If the service was running, propagate a stop event. If the 114*cd3bce3eSlianep * service was not running the maintenance transition may satisfy 115*cd3bce3eSlianep * optional dependencies and should be propagated to determine 116*cd3bce3eSlianep * whether new dependents are satisfiable. 117*cd3bce3eSlianep */ 11899b44c3bSlianep if (gt_running(old_state)) { 119*cd3bce3eSlianep log_framework(LOG_DEBUG, "Propagating maintenance (stop) of " 120*cd3bce3eSlianep "%s.\n", v->gv_name); 12199b44c3bSlianep 122*cd3bce3eSlianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 123*cd3bce3eSlianep } else { 12499b44c3bSlianep log_framework(LOG_DEBUG, "Propagating maintenance of %s.\n", 12599b44c3bSlianep v->gv_name); 12699b44c3bSlianep 127*cd3bce3eSlianep graph_transition_propagate(v, PROPAGATE_SAT, rerr); 12899b44c3bSlianep } 12999b44c3bSlianep 13099b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_MAINT, old_state); 13199b44c3bSlianep return (0); 13299b44c3bSlianep } 13399b44c3bSlianep 13499b44c3bSlianep static int 13599b44c3bSlianep gt_enter_offline(scf_handle_t *h, graph_vertex_t *v, 13699b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 13799b44c3bSlianep { 13899b44c3bSlianep vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 13999b44c3bSlianep 14099b44c3bSlianep /* 14199b44c3bSlianep * If the instance should be enabled, see if we can start it. 14299b44c3bSlianep * Otherwise send a disable command. 14399b44c3bSlianep */ 14499b44c3bSlianep if (v->gv_flags & GV_ENABLED) { 14599b44c3bSlianep graph_start_if_satisfied(v); 14699b44c3bSlianep } else { 14799b44c3bSlianep if (gt_running(old_state) && v->gv_post_disable_f) 14899b44c3bSlianep v->gv_post_disable_f(); 14999b44c3bSlianep vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 15099b44c3bSlianep } 15199b44c3bSlianep 15299b44c3bSlianep if (gt_running(old_state)) { 15399b44c3bSlianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 15499b44c3bSlianep v->gv_name); 15599b44c3bSlianep 156*cd3bce3eSlianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 15799b44c3bSlianep } 15899b44c3bSlianep 15999b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_OFFLINE, old_state); 16099b44c3bSlianep return (0); 16199b44c3bSlianep } 16299b44c3bSlianep 16399b44c3bSlianep static int 16499b44c3bSlianep gt_enter_disabled(scf_handle_t *h, graph_vertex_t *v, 16599b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 16699b44c3bSlianep { 16799b44c3bSlianep vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 16899b44c3bSlianep 16999b44c3bSlianep /* 17099b44c3bSlianep * If the instance should be disabled, no problem. Otherwise, 17199b44c3bSlianep * send an enable command, which should result in the instance 17299b44c3bSlianep * moving to OFFLINE. 17399b44c3bSlianep */ 17499b44c3bSlianep if (v->gv_flags & GV_ENABLED) { 17599b44c3bSlianep vertex_send_event(v, RESTARTER_EVENT_TYPE_ENABLE); 17699b44c3bSlianep } else if (gt_running(old_state) && v->gv_post_disable_f) { 17799b44c3bSlianep v->gv_post_disable_f(); 17899b44c3bSlianep } 17999b44c3bSlianep 18099b44c3bSlianep /* 181*cd3bce3eSlianep * If the service was running, propagate this as a stop. If the 182*cd3bce3eSlianep * service was not running the disabled transition may satisfy 183*cd3bce3eSlianep * optional dependencies and should be propagated to determine 184*cd3bce3eSlianep * whether new dependents are satisfiable. 18599b44c3bSlianep */ 18699b44c3bSlianep if (gt_running(old_state)) { 18799b44c3bSlianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 18899b44c3bSlianep v->gv_name); 18999b44c3bSlianep 190*cd3bce3eSlianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 19199b44c3bSlianep 192*cd3bce3eSlianep } else { 19399b44c3bSlianep log_framework(LOG_DEBUG, "Propagating disable of %s.\n", 19499b44c3bSlianep v->gv_name); 19599b44c3bSlianep 196*cd3bce3eSlianep graph_transition_propagate(v, PROPAGATE_SAT, rerr); 19799b44c3bSlianep } 19899b44c3bSlianep 19999b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_DISABLED, old_state); 20099b44c3bSlianep return (0); 20199b44c3bSlianep } 20299b44c3bSlianep 20399b44c3bSlianep static int 20499b44c3bSlianep gt_internal_online_or_degraded(scf_handle_t *h, graph_vertex_t *v, 20599b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 20699b44c3bSlianep { 20799b44c3bSlianep int r; 20899b44c3bSlianep 20999b44c3bSlianep /* 21099b44c3bSlianep * If the instance has just come up, update the start 21199b44c3bSlianep * snapshot. 21299b44c3bSlianep */ 21399b44c3bSlianep if (gt_running(old_state) == 0) { 21499b44c3bSlianep /* 21599b44c3bSlianep * Don't fire if we're just recovering state 21699b44c3bSlianep * after a restart. 21799b44c3bSlianep */ 21899b44c3bSlianep if (old_state != RESTARTER_STATE_UNINIT && 21999b44c3bSlianep v->gv_post_online_f) 22099b44c3bSlianep v->gv_post_online_f(); 22199b44c3bSlianep 22299b44c3bSlianep r = libscf_snapshots_poststart(h, v->gv_name, B_TRUE); 22399b44c3bSlianep switch (r) { 22499b44c3bSlianep case 0: 22599b44c3bSlianep case ENOENT: 22699b44c3bSlianep /* 22799b44c3bSlianep * If ENOENT, the instance must have been 22899b44c3bSlianep * deleted. Pretend we were successful since 22999b44c3bSlianep * we should get a delete event later. 23099b44c3bSlianep */ 23199b44c3bSlianep break; 23299b44c3bSlianep 23399b44c3bSlianep case ECONNABORTED: 23499b44c3bSlianep return (ECONNABORTED); 23599b44c3bSlianep 23699b44c3bSlianep case EACCES: 23799b44c3bSlianep case ENOTSUP: 23899b44c3bSlianep default: 23999b44c3bSlianep bad_error("libscf_snapshots_poststart", r); 24099b44c3bSlianep } 24199b44c3bSlianep } 24299b44c3bSlianep if (!(v->gv_flags & GV_ENABLED)) 24399b44c3bSlianep vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 24499b44c3bSlianep 24599b44c3bSlianep if (gt_running(old_state) == 0) { 24699b44c3bSlianep log_framework(LOG_DEBUG, "Propagating start of %s.\n", 24799b44c3bSlianep v->gv_name); 24899b44c3bSlianep 249*cd3bce3eSlianep graph_transition_propagate(v, PROPAGATE_START, rerr); 25099b44c3bSlianep } else if (rerr == RERR_REFRESH) { 25199b44c3bSlianep /* For refresh we'll get a message sans state change */ 25299b44c3bSlianep 25399b44c3bSlianep log_framework(LOG_DEBUG, "Propagating refresh of %s.\n", 25499b44c3bSlianep v->gv_name); 25599b44c3bSlianep 256*cd3bce3eSlianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 25799b44c3bSlianep } 25899b44c3bSlianep 25999b44c3bSlianep return (0); 26099b44c3bSlianep } 26199b44c3bSlianep 26299b44c3bSlianep static int 26399b44c3bSlianep gt_enter_online(scf_handle_t *h, graph_vertex_t *v, 26499b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 26599b44c3bSlianep { 26699b44c3bSlianep int r; 26799b44c3bSlianep 26899b44c3bSlianep r = gt_internal_online_or_degraded(h, v, old_state, rerr); 26999b44c3bSlianep if (r != 0) 27099b44c3bSlianep return (r); 27199b44c3bSlianep 27299b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_ONLINE, old_state); 27399b44c3bSlianep return (0); 27499b44c3bSlianep } 27599b44c3bSlianep 27699b44c3bSlianep static int 27799b44c3bSlianep gt_enter_degraded(scf_handle_t *h, graph_vertex_t *v, 27899b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 27999b44c3bSlianep { 28099b44c3bSlianep int r; 28199b44c3bSlianep 28299b44c3bSlianep r = gt_internal_online_or_degraded(h, v, old_state, rerr); 28399b44c3bSlianep if (r != 0) 28499b44c3bSlianep return (r); 28599b44c3bSlianep 28699b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_DEGRADED, old_state); 28799b44c3bSlianep return (0); 28899b44c3bSlianep } 28999b44c3bSlianep 29099b44c3bSlianep /* 29199b44c3bSlianep * gt_transition() implements the state transition for the graph 29299b44c3bSlianep * state machine. It can return: 29399b44c3bSlianep * 0 success 29499b44c3bSlianep * ECONNABORTED repository connection aborted 295*cd3bce3eSlianep * 296*cd3bce3eSlianep * v->gv_state should be set to the state we're transitioning to before 297*cd3bce3eSlianep * calling this function. 29899b44c3bSlianep */ 29999b44c3bSlianep int 30099b44c3bSlianep gt_transition(scf_handle_t *h, graph_vertex_t *v, restarter_error_t rerr, 301*cd3bce3eSlianep restarter_instance_state_t old_state) 30299b44c3bSlianep { 30399b44c3bSlianep int err = 0; 30499b44c3bSlianep 30599b44c3bSlianep /* 30699b44c3bSlianep * If there's a common set of work to be done on exit from the 30799b44c3bSlianep * old_state, include it as a separate set of functions here. For 30899b44c3bSlianep * now there's no such work, so there are no gt_exit functions. 30999b44c3bSlianep */ 31099b44c3bSlianep 31199b44c3bSlianep /* 31299b44c3bSlianep * Now call the appropriate gt_enter function for the new state. 31399b44c3bSlianep */ 314*cd3bce3eSlianep switch (v->gv_state) { 31599b44c3bSlianep case RESTARTER_STATE_UNINIT: 31699b44c3bSlianep err = gt_enter_uninit(h, v, old_state, rerr); 31799b44c3bSlianep break; 31899b44c3bSlianep 31999b44c3bSlianep case RESTARTER_STATE_DISABLED: 32099b44c3bSlianep err = gt_enter_disabled(h, v, old_state, rerr); 32199b44c3bSlianep break; 32299b44c3bSlianep 32399b44c3bSlianep case RESTARTER_STATE_OFFLINE: 32499b44c3bSlianep err = gt_enter_offline(h, v, old_state, rerr); 32599b44c3bSlianep break; 32699b44c3bSlianep 32799b44c3bSlianep case RESTARTER_STATE_ONLINE: 32899b44c3bSlianep err = gt_enter_online(h, v, old_state, rerr); 32999b44c3bSlianep break; 33099b44c3bSlianep 33199b44c3bSlianep case RESTARTER_STATE_DEGRADED: 33299b44c3bSlianep err = gt_enter_degraded(h, v, old_state, rerr); 33399b44c3bSlianep break; 33499b44c3bSlianep 33599b44c3bSlianep case RESTARTER_STATE_MAINT: 33699b44c3bSlianep err = gt_enter_maint(h, v, old_state, rerr); 33799b44c3bSlianep break; 33899b44c3bSlianep 33999b44c3bSlianep default: 340*cd3bce3eSlianep /* Shouldn't be in an invalid state. */ 34199b44c3bSlianep #ifndef NDEBUG 34299b44c3bSlianep uu_warn("%s:%d: Uncaught case %d.\n", __FILE__, __LINE__, 343*cd3bce3eSlianep v->gv_state); 34499b44c3bSlianep #endif 34599b44c3bSlianep abort(); 34699b44c3bSlianep } 34799b44c3bSlianep 34899b44c3bSlianep return (err); 34999b44c3bSlianep } 350