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