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 /* 22*7630SRenaud.Manus@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 231958Slianep * Use is subject to license terms. 241958Slianep */ 251958Slianep 261958Slianep 271958Slianep /* 281958Slianep * transition.c - Graph State Machine 291958Slianep * 301958Slianep * The graph state machine is implemented here, with a typical approach 311958Slianep * of a function per state. Separating the implementation allows more 321958Slianep * clarity into the actions taken on notification of state change, as well 331958Slianep * as a place for future expansion including hooks for configurable actions. 341958Slianep * All functions are called with dgraph_lock held. 351958Slianep * 361958Slianep * The start action for this state machine is not explicit. The states 372747Sbustos * (ONLINE and DEGRADED) which need to know when they're entering the state 381958Slianep * due to a daemon restart implement this understanding by checking for 391958Slianep * transition from uninitialized. In the future, this would likely be better 401958Slianep * as an explicit start action instead of relying on an overloaded transition. 411958Slianep * 421958Slianep * All gt_enter functions use the same set of return codes. 431958Slianep * 0 success 441958Slianep * ECONNABORTED repository connection aborted 451958Slianep */ 461958Slianep 471958Slianep #include "startd.h" 481958Slianep 491958Slianep static int 501958Slianep gt_running(restarter_instance_state_t state) 511958Slianep { 521958Slianep if (state == RESTARTER_STATE_ONLINE || 531958Slianep state == RESTARTER_STATE_DEGRADED) 541958Slianep return (1); 551958Slianep 561958Slianep return (0); 571958Slianep } 581958Slianep 591958Slianep static int 601958Slianep gt_enter_uninit(scf_handle_t *h, graph_vertex_t *v, 611958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 621958Slianep { 631958Slianep int err; 641958Slianep scf_instance_t *inst; 651958Slianep 661958Slianep /* Initialize instance by refreshing it. */ 671958Slianep 681958Slianep err = libscf_fmri_get_instance(h, v->gv_name, &inst); 691958Slianep switch (err) { 701958Slianep case 0: 711958Slianep break; 721958Slianep 731958Slianep case ECONNABORTED: 741958Slianep return (ECONNABORTED); 751958Slianep 761958Slianep case ENOENT: 771958Slianep return (0); 781958Slianep 791958Slianep case EINVAL: 801958Slianep case ENOTSUP: 811958Slianep default: 821958Slianep bad_error("libscf_fmri_get_instance", err); 831958Slianep } 841958Slianep 851958Slianep err = refresh_vertex(v, inst); 861958Slianep if (err == 0) 871958Slianep graph_enable_by_vertex(v, v->gv_flags & GV_ENABLED, 0); 881958Slianep 891958Slianep scf_instance_destroy(inst); 901958Slianep 911958Slianep /* If the service was running, propagate a stop event. */ 921958Slianep if (gt_running(old_state)) { 931958Slianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 941958Slianep v->gv_name); 951958Slianep 962339Slianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 971958Slianep } 981958Slianep 991958Slianep graph_transition_sulogin(RESTARTER_STATE_UNINIT, old_state); 1001958Slianep return (0); 1011958Slianep } 1021958Slianep 1032747Sbustos /* ARGSUSED */ 1041958Slianep static int 1051958Slianep gt_enter_maint(scf_handle_t *h, graph_vertex_t *v, 1061958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 1071958Slianep { 1082339Slianep /* 1092339Slianep * If the service was running, propagate a stop event. If the 1102339Slianep * service was not running the maintenance transition may satisfy 1112339Slianep * optional dependencies and should be propagated to determine 1122339Slianep * whether new dependents are satisfiable. 113*7630SRenaud.Manus@Sun.COM * Instances that transition to maintenance and have the GV_TOOFFLINE 114*7630SRenaud.Manus@Sun.COM * flag are special because they can expose new subtree leaves so 115*7630SRenaud.Manus@Sun.COM * propagate the offline to the instance dependencies. 1162339Slianep */ 1171958Slianep if (gt_running(old_state)) { 118*7630SRenaud.Manus@Sun.COM /* 119*7630SRenaud.Manus@Sun.COM * Handle state change during instance disabling. 120*7630SRenaud.Manus@Sun.COM * Propagate offline to the new exposed leaves. 121*7630SRenaud.Manus@Sun.COM */ 122*7630SRenaud.Manus@Sun.COM if (v->gv_flags & GV_TOOFFLINE) { 123*7630SRenaud.Manus@Sun.COM v->gv_flags &= ~GV_TOOFFLINE; 124*7630SRenaud.Manus@Sun.COM log_framework(LOG_DEBUG, "%s removed from subtree\n", 125*7630SRenaud.Manus@Sun.COM v->gv_name); 126*7630SRenaud.Manus@Sun.COM graph_offline_subtree_leaves(v, (void *)h); 127*7630SRenaud.Manus@Sun.COM } 128*7630SRenaud.Manus@Sun.COM 1292339Slianep log_framework(LOG_DEBUG, "Propagating maintenance (stop) of " 1302339Slianep "%s.\n", v->gv_name); 1311958Slianep 1322339Slianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 1332339Slianep } else { 1341958Slianep log_framework(LOG_DEBUG, "Propagating maintenance of %s.\n", 1351958Slianep v->gv_name); 1361958Slianep 1372339Slianep graph_transition_propagate(v, PROPAGATE_SAT, rerr); 1381958Slianep } 1391958Slianep 1401958Slianep graph_transition_sulogin(RESTARTER_STATE_MAINT, old_state); 1411958Slianep return (0); 1421958Slianep } 1431958Slianep 1442747Sbustos /* ARGSUSED */ 1451958Slianep static int 1461958Slianep gt_enter_offline(scf_handle_t *h, graph_vertex_t *v, 1471958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 1481958Slianep { 1491958Slianep /* 1501958Slianep * If the instance should be enabled, see if we can start it. 1511958Slianep * Otherwise send a disable command. 152*7630SRenaud.Manus@Sun.COM * If a instance has the GV_TOOFFLINE flag set then it must 153*7630SRenaud.Manus@Sun.COM * remains offline until the disable process completes. 1541958Slianep */ 1551958Slianep if (v->gv_flags & GV_ENABLED) { 156*7630SRenaud.Manus@Sun.COM if (!(v->gv_flags & GV_TOOFFLINE)) 157*7630SRenaud.Manus@Sun.COM graph_start_if_satisfied(v); 1581958Slianep } else { 1591958Slianep if (gt_running(old_state) && v->gv_post_disable_f) 1601958Slianep v->gv_post_disable_f(); 161*7630SRenaud.Manus@Sun.COM 1621958Slianep vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 1631958Slianep } 1641958Slianep 1653639Srm88369 /* 1663639Srm88369 * If the service was running, propagate a stop event. If the 1673639Srm88369 * service was not running the offline transition may satisfy 1683639Srm88369 * optional dependencies and should be propagated to determine 1693639Srm88369 * whether new dependents are satisfiable. 170*7630SRenaud.Manus@Sun.COM * Instances that transition to offline and have the GV_TOOFFLINE flag 171*7630SRenaud.Manus@Sun.COM * are special because they can expose new subtree leaves so propagate 172*7630SRenaud.Manus@Sun.COM * the offline to the instance dependencies. 1733639Srm88369 */ 1741958Slianep if (gt_running(old_state)) { 175*7630SRenaud.Manus@Sun.COM /* 176*7630SRenaud.Manus@Sun.COM * Handle state change during instance disabling. 177*7630SRenaud.Manus@Sun.COM * Propagate offline to the new exposed leaves. 178*7630SRenaud.Manus@Sun.COM */ 179*7630SRenaud.Manus@Sun.COM if (v->gv_flags & GV_TOOFFLINE) { 180*7630SRenaud.Manus@Sun.COM v->gv_flags &= ~GV_TOOFFLINE; 181*7630SRenaud.Manus@Sun.COM log_framework(LOG_DEBUG, "%s removed from subtree\n", 182*7630SRenaud.Manus@Sun.COM v->gv_name); 183*7630SRenaud.Manus@Sun.COM graph_offline_subtree_leaves(v, (void *)h); 184*7630SRenaud.Manus@Sun.COM } 185*7630SRenaud.Manus@Sun.COM 1861958Slianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 1871958Slianep v->gv_name); 1881958Slianep 1892339Slianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 1903639Srm88369 } else { 1913639Srm88369 log_framework(LOG_DEBUG, "Propagating offline of %s.\n", 1923639Srm88369 v->gv_name); 1933639Srm88369 1943639Srm88369 graph_transition_propagate(v, PROPAGATE_SAT, rerr); 1951958Slianep } 1961958Slianep 1971958Slianep graph_transition_sulogin(RESTARTER_STATE_OFFLINE, old_state); 1981958Slianep return (0); 1991958Slianep } 2001958Slianep 2012747Sbustos /* ARGSUSED */ 2021958Slianep static int 2031958Slianep gt_enter_disabled(scf_handle_t *h, graph_vertex_t *v, 2041958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 2051958Slianep { 206*7630SRenaud.Manus@Sun.COM 2071958Slianep /* 2081958Slianep * If the instance should be disabled, no problem. Otherwise, 2091958Slianep * send an enable command, which should result in the instance 210*7630SRenaud.Manus@Sun.COM * moving to OFFLINE unless the instance is part of a subtree 211*7630SRenaud.Manus@Sun.COM * (non root) and in this case the result is unpredictable. 2121958Slianep */ 2131958Slianep if (v->gv_flags & GV_ENABLED) { 2141958Slianep vertex_send_event(v, RESTARTER_EVENT_TYPE_ENABLE); 2151958Slianep } else if (gt_running(old_state) && v->gv_post_disable_f) { 2161958Slianep v->gv_post_disable_f(); 2171958Slianep } 2181958Slianep 2191958Slianep /* 2202339Slianep * If the service was running, propagate this as a stop. If the 2212339Slianep * service was not running the disabled transition may satisfy 2222339Slianep * optional dependencies and should be propagated to determine 2232339Slianep * whether new dependents are satisfiable. 2241958Slianep */ 2251958Slianep if (gt_running(old_state)) { 226*7630SRenaud.Manus@Sun.COM /* 227*7630SRenaud.Manus@Sun.COM * We need to propagate the offline to new exposed leaves in 228*7630SRenaud.Manus@Sun.COM * case we've just disabled an instance that was part of a 229*7630SRenaud.Manus@Sun.COM * subtree. 230*7630SRenaud.Manus@Sun.COM */ 231*7630SRenaud.Manus@Sun.COM if (v->gv_flags & GV_TOOFFLINE) { 232*7630SRenaud.Manus@Sun.COM /* 233*7630SRenaud.Manus@Sun.COM * If the vertex is in the subtree and is transitionning 234*7630SRenaud.Manus@Sun.COM * to DISABLED then remove the GV_TODISABLE flag also. 235*7630SRenaud.Manus@Sun.COM */ 236*7630SRenaud.Manus@Sun.COM v->gv_flags &= ~GV_TODISABLE; 237*7630SRenaud.Manus@Sun.COM v->gv_flags &= ~GV_TOOFFLINE; 238*7630SRenaud.Manus@Sun.COM 239*7630SRenaud.Manus@Sun.COM log_framework(LOG_DEBUG, "%s removed from subtree\n", 240*7630SRenaud.Manus@Sun.COM v->gv_name); 241*7630SRenaud.Manus@Sun.COM 242*7630SRenaud.Manus@Sun.COM /* 243*7630SRenaud.Manus@Sun.COM * Handle state change during instance disabling. 244*7630SRenaud.Manus@Sun.COM * Propagate offline to the new exposed leaves. 245*7630SRenaud.Manus@Sun.COM */ 246*7630SRenaud.Manus@Sun.COM graph_offline_subtree_leaves(v, (void *)h); 247*7630SRenaud.Manus@Sun.COM } 248*7630SRenaud.Manus@Sun.COM 249*7630SRenaud.Manus@Sun.COM 2501958Slianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 2511958Slianep v->gv_name); 2521958Slianep 2532339Slianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 2541958Slianep 2552339Slianep } else { 2561958Slianep log_framework(LOG_DEBUG, "Propagating disable of %s.\n", 2571958Slianep v->gv_name); 2581958Slianep 2592339Slianep graph_transition_propagate(v, PROPAGATE_SAT, rerr); 2601958Slianep } 2611958Slianep 2621958Slianep graph_transition_sulogin(RESTARTER_STATE_DISABLED, old_state); 2631958Slianep return (0); 2641958Slianep } 2651958Slianep 2661958Slianep static int 2671958Slianep gt_internal_online_or_degraded(scf_handle_t *h, graph_vertex_t *v, 2681958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 2691958Slianep { 2701958Slianep int r; 2711958Slianep 2721958Slianep /* 2731958Slianep * If the instance has just come up, update the start 2741958Slianep * snapshot. 2751958Slianep */ 2761958Slianep if (gt_running(old_state) == 0) { 2771958Slianep /* 2781958Slianep * Don't fire if we're just recovering state 2791958Slianep * after a restart. 2801958Slianep */ 2811958Slianep if (old_state != RESTARTER_STATE_UNINIT && 2821958Slianep v->gv_post_online_f) 2831958Slianep v->gv_post_online_f(); 2841958Slianep 2851958Slianep r = libscf_snapshots_poststart(h, v->gv_name, B_TRUE); 2861958Slianep switch (r) { 2871958Slianep case 0: 2881958Slianep case ENOENT: 2891958Slianep /* 2901958Slianep * If ENOENT, the instance must have been 2911958Slianep * deleted. Pretend we were successful since 2921958Slianep * we should get a delete event later. 2931958Slianep */ 2941958Slianep break; 2951958Slianep 2961958Slianep case ECONNABORTED: 2971958Slianep return (ECONNABORTED); 2981958Slianep 2991958Slianep case EACCES: 3001958Slianep case ENOTSUP: 3011958Slianep default: 3021958Slianep bad_error("libscf_snapshots_poststart", r); 3031958Slianep } 3041958Slianep } 3051958Slianep if (!(v->gv_flags & GV_ENABLED)) 3061958Slianep vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 3071958Slianep 3081958Slianep if (gt_running(old_state) == 0) { 3091958Slianep log_framework(LOG_DEBUG, "Propagating start of %s.\n", 3101958Slianep v->gv_name); 3111958Slianep 3122339Slianep graph_transition_propagate(v, PROPAGATE_START, rerr); 3131958Slianep } else if (rerr == RERR_REFRESH) { 3141958Slianep /* For refresh we'll get a message sans state change */ 3151958Slianep 3161958Slianep log_framework(LOG_DEBUG, "Propagating refresh of %s.\n", 3171958Slianep v->gv_name); 3181958Slianep 3192339Slianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 3201958Slianep } 3211958Slianep 3221958Slianep return (0); 3231958Slianep } 3241958Slianep 3251958Slianep static int 3261958Slianep gt_enter_online(scf_handle_t *h, graph_vertex_t *v, 3271958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 3281958Slianep { 3291958Slianep int r; 3301958Slianep 3311958Slianep r = gt_internal_online_or_degraded(h, v, old_state, rerr); 3321958Slianep if (r != 0) 3331958Slianep return (r); 3341958Slianep 3351958Slianep graph_transition_sulogin(RESTARTER_STATE_ONLINE, old_state); 3361958Slianep return (0); 3371958Slianep } 3381958Slianep 3391958Slianep static int 3401958Slianep gt_enter_degraded(scf_handle_t *h, graph_vertex_t *v, 3411958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 3421958Slianep { 3431958Slianep int r; 3441958Slianep 3451958Slianep r = gt_internal_online_or_degraded(h, v, old_state, rerr); 3461958Slianep if (r != 0) 3471958Slianep return (r); 3481958Slianep 3491958Slianep graph_transition_sulogin(RESTARTER_STATE_DEGRADED, old_state); 3501958Slianep return (0); 3511958Slianep } 3521958Slianep 3531958Slianep /* 3541958Slianep * gt_transition() implements the state transition for the graph 3551958Slianep * state machine. It can return: 3561958Slianep * 0 success 3571958Slianep * ECONNABORTED repository connection aborted 3582339Slianep * 3592339Slianep * v->gv_state should be set to the state we're transitioning to before 3602339Slianep * calling this function. 3611958Slianep */ 3621958Slianep int 3631958Slianep gt_transition(scf_handle_t *h, graph_vertex_t *v, restarter_error_t rerr, 3642339Slianep restarter_instance_state_t old_state) 3651958Slianep { 3662747Sbustos int err; 3672747Sbustos int lost_repository = 0; 3681958Slianep 3691958Slianep /* 3701958Slianep * If there's a common set of work to be done on exit from the 3711958Slianep * old_state, include it as a separate set of functions here. For 3721958Slianep * now there's no such work, so there are no gt_exit functions. 3731958Slianep */ 3741958Slianep 3752747Sbustos err = vertex_subgraph_dependencies_shutdown(h, v, old_state); 3762747Sbustos switch (err) { 3772747Sbustos case 0: 3782747Sbustos break; 3792747Sbustos 3802747Sbustos case ECONNABORTED: 3812747Sbustos lost_repository = 1; 3822747Sbustos break; 3832747Sbustos 3842747Sbustos default: 3852747Sbustos bad_error("vertex_subgraph_dependencies_shutdown", err); 3862747Sbustos } 3872747Sbustos 3881958Slianep /* 3891958Slianep * Now call the appropriate gt_enter function for the new state. 3901958Slianep */ 3912339Slianep switch (v->gv_state) { 3921958Slianep case RESTARTER_STATE_UNINIT: 3931958Slianep err = gt_enter_uninit(h, v, old_state, rerr); 3941958Slianep break; 3951958Slianep 3961958Slianep case RESTARTER_STATE_DISABLED: 3971958Slianep err = gt_enter_disabled(h, v, old_state, rerr); 3981958Slianep break; 3991958Slianep 4001958Slianep case RESTARTER_STATE_OFFLINE: 4011958Slianep err = gt_enter_offline(h, v, old_state, rerr); 4021958Slianep break; 4031958Slianep 4041958Slianep case RESTARTER_STATE_ONLINE: 4051958Slianep err = gt_enter_online(h, v, old_state, rerr); 4061958Slianep break; 4071958Slianep 4081958Slianep case RESTARTER_STATE_DEGRADED: 4091958Slianep err = gt_enter_degraded(h, v, old_state, rerr); 4101958Slianep break; 4111958Slianep 4121958Slianep case RESTARTER_STATE_MAINT: 4131958Slianep err = gt_enter_maint(h, v, old_state, rerr); 4141958Slianep break; 4151958Slianep 4161958Slianep default: 4172339Slianep /* Shouldn't be in an invalid state. */ 4181958Slianep #ifndef NDEBUG 4192747Sbustos uu_warn("%s:%d: Invalid state %d.\n", __FILE__, __LINE__, 4202339Slianep v->gv_state); 4211958Slianep #endif 4221958Slianep abort(); 4231958Slianep } 4241958Slianep 4252747Sbustos switch (err) { 4262747Sbustos case 0: 4272747Sbustos break; 4282747Sbustos 4292747Sbustos case ECONNABORTED: 4302747Sbustos lost_repository = 1; 4312747Sbustos break; 4322747Sbustos 4332747Sbustos default: 4342747Sbustos #ifndef NDEBUG 4352747Sbustos uu_warn("%s:%d: " 4362747Sbustos "gt_enter_%s() failed with unexpected error %d.\n", 4372747Sbustos __FILE__, __LINE__, instance_state_str[v->gv_state], err); 4382747Sbustos #endif 4392747Sbustos abort(); 4402747Sbustos } 4412747Sbustos 4422747Sbustos return (lost_repository ? ECONNABORTED : 0); 4431958Slianep } 444