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*3639Srm88369 * Copyright 2007 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 382747Sbustos * (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 1042747Sbustos /* 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 1312747Sbustos /* 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 148*3639Srm88369 /* 149*3639Srm88369 * If the service was running, propagate a stop event. If the 150*3639Srm88369 * service was not running the offline transition may satisfy 151*3639Srm88369 * optional dependencies and should be propagated to determine 152*3639Srm88369 * whether new dependents are satisfiable. 153*3639Srm88369 */ 1541958Slianep if (gt_running(old_state)) { 1551958Slianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 1561958Slianep v->gv_name); 1571958Slianep 1582339Slianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 159*3639Srm88369 } else { 160*3639Srm88369 log_framework(LOG_DEBUG, "Propagating offline of %s.\n", 161*3639Srm88369 v->gv_name); 162*3639Srm88369 163*3639Srm88369 graph_transition_propagate(v, PROPAGATE_SAT, rerr); 1641958Slianep } 1651958Slianep 1661958Slianep graph_transition_sulogin(RESTARTER_STATE_OFFLINE, old_state); 1671958Slianep return (0); 1681958Slianep } 1691958Slianep 1702747Sbustos /* ARGSUSED */ 1711958Slianep static int 1721958Slianep gt_enter_disabled(scf_handle_t *h, graph_vertex_t *v, 1731958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 1741958Slianep { 1751958Slianep /* 1761958Slianep * If the instance should be disabled, no problem. Otherwise, 1771958Slianep * send an enable command, which should result in the instance 1781958Slianep * moving to OFFLINE. 1791958Slianep */ 1801958Slianep if (v->gv_flags & GV_ENABLED) { 1811958Slianep vertex_send_event(v, RESTARTER_EVENT_TYPE_ENABLE); 1821958Slianep } else if (gt_running(old_state) && v->gv_post_disable_f) { 1831958Slianep v->gv_post_disable_f(); 1841958Slianep } 1851958Slianep 1861958Slianep /* 1872339Slianep * If the service was running, propagate this as a stop. If the 1882339Slianep * service was not running the disabled transition may satisfy 1892339Slianep * optional dependencies and should be propagated to determine 1902339Slianep * whether new dependents are satisfiable. 1911958Slianep */ 1921958Slianep if (gt_running(old_state)) { 1931958Slianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 1941958Slianep v->gv_name); 1951958Slianep 1962339Slianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 1971958Slianep 1982339Slianep } else { 1991958Slianep log_framework(LOG_DEBUG, "Propagating disable of %s.\n", 2001958Slianep v->gv_name); 2011958Slianep 2022339Slianep graph_transition_propagate(v, PROPAGATE_SAT, rerr); 2031958Slianep } 2041958Slianep 2051958Slianep graph_transition_sulogin(RESTARTER_STATE_DISABLED, old_state); 2061958Slianep return (0); 2071958Slianep } 2081958Slianep 2091958Slianep static int 2101958Slianep gt_internal_online_or_degraded(scf_handle_t *h, graph_vertex_t *v, 2111958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 2121958Slianep { 2131958Slianep int r; 2141958Slianep 2151958Slianep /* 2161958Slianep * If the instance has just come up, update the start 2171958Slianep * snapshot. 2181958Slianep */ 2191958Slianep if (gt_running(old_state) == 0) { 2201958Slianep /* 2211958Slianep * Don't fire if we're just recovering state 2221958Slianep * after a restart. 2231958Slianep */ 2241958Slianep if (old_state != RESTARTER_STATE_UNINIT && 2251958Slianep v->gv_post_online_f) 2261958Slianep v->gv_post_online_f(); 2271958Slianep 2281958Slianep r = libscf_snapshots_poststart(h, v->gv_name, B_TRUE); 2291958Slianep switch (r) { 2301958Slianep case 0: 2311958Slianep case ENOENT: 2321958Slianep /* 2331958Slianep * If ENOENT, the instance must have been 2341958Slianep * deleted. Pretend we were successful since 2351958Slianep * we should get a delete event later. 2361958Slianep */ 2371958Slianep break; 2381958Slianep 2391958Slianep case ECONNABORTED: 2401958Slianep return (ECONNABORTED); 2411958Slianep 2421958Slianep case EACCES: 2431958Slianep case ENOTSUP: 2441958Slianep default: 2451958Slianep bad_error("libscf_snapshots_poststart", r); 2461958Slianep } 2471958Slianep } 2481958Slianep if (!(v->gv_flags & GV_ENABLED)) 2491958Slianep vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 2501958Slianep 2511958Slianep if (gt_running(old_state) == 0) { 2521958Slianep log_framework(LOG_DEBUG, "Propagating start of %s.\n", 2531958Slianep v->gv_name); 2541958Slianep 2552339Slianep graph_transition_propagate(v, PROPAGATE_START, rerr); 2561958Slianep } else if (rerr == RERR_REFRESH) { 2571958Slianep /* For refresh we'll get a message sans state change */ 2581958Slianep 2591958Slianep log_framework(LOG_DEBUG, "Propagating refresh of %s.\n", 2601958Slianep v->gv_name); 2611958Slianep 2622339Slianep graph_transition_propagate(v, PROPAGATE_STOP, rerr); 2631958Slianep } 2641958Slianep 2651958Slianep return (0); 2661958Slianep } 2671958Slianep 2681958Slianep static int 2691958Slianep gt_enter_online(scf_handle_t *h, graph_vertex_t *v, 2701958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 2711958Slianep { 2721958Slianep int r; 2731958Slianep 2741958Slianep r = gt_internal_online_or_degraded(h, v, old_state, rerr); 2751958Slianep if (r != 0) 2761958Slianep return (r); 2771958Slianep 2781958Slianep graph_transition_sulogin(RESTARTER_STATE_ONLINE, old_state); 2791958Slianep return (0); 2801958Slianep } 2811958Slianep 2821958Slianep static int 2831958Slianep gt_enter_degraded(scf_handle_t *h, graph_vertex_t *v, 2841958Slianep restarter_instance_state_t old_state, restarter_error_t rerr) 2851958Slianep { 2861958Slianep int r; 2871958Slianep 2881958Slianep r = gt_internal_online_or_degraded(h, v, old_state, rerr); 2891958Slianep if (r != 0) 2901958Slianep return (r); 2911958Slianep 2921958Slianep graph_transition_sulogin(RESTARTER_STATE_DEGRADED, old_state); 2931958Slianep return (0); 2941958Slianep } 2951958Slianep 2961958Slianep /* 2971958Slianep * gt_transition() implements the state transition for the graph 2981958Slianep * state machine. It can return: 2991958Slianep * 0 success 3001958Slianep * ECONNABORTED repository connection aborted 3012339Slianep * 3022339Slianep * v->gv_state should be set to the state we're transitioning to before 3032339Slianep * calling this function. 3041958Slianep */ 3051958Slianep int 3061958Slianep gt_transition(scf_handle_t *h, graph_vertex_t *v, restarter_error_t rerr, 3072339Slianep restarter_instance_state_t old_state) 3081958Slianep { 3092747Sbustos int err; 3102747Sbustos int lost_repository = 0; 3111958Slianep 3121958Slianep /* 3131958Slianep * If there's a common set of work to be done on exit from the 3141958Slianep * old_state, include it as a separate set of functions here. For 3151958Slianep * now there's no such work, so there are no gt_exit functions. 3161958Slianep */ 3171958Slianep 3182747Sbustos err = vertex_subgraph_dependencies_shutdown(h, v, old_state); 3192747Sbustos switch (err) { 3202747Sbustos case 0: 3212747Sbustos break; 3222747Sbustos 3232747Sbustos case ECONNABORTED: 3242747Sbustos lost_repository = 1; 3252747Sbustos break; 3262747Sbustos 3272747Sbustos default: 3282747Sbustos bad_error("vertex_subgraph_dependencies_shutdown", err); 3292747Sbustos } 3302747Sbustos 3311958Slianep /* 3321958Slianep * Now call the appropriate gt_enter function for the new state. 3331958Slianep */ 3342339Slianep switch (v->gv_state) { 3351958Slianep case RESTARTER_STATE_UNINIT: 3361958Slianep err = gt_enter_uninit(h, v, old_state, rerr); 3371958Slianep break; 3381958Slianep 3391958Slianep case RESTARTER_STATE_DISABLED: 3401958Slianep err = gt_enter_disabled(h, v, old_state, rerr); 3411958Slianep break; 3421958Slianep 3431958Slianep case RESTARTER_STATE_OFFLINE: 3441958Slianep err = gt_enter_offline(h, v, old_state, rerr); 3451958Slianep break; 3461958Slianep 3471958Slianep case RESTARTER_STATE_ONLINE: 3481958Slianep err = gt_enter_online(h, v, old_state, rerr); 3491958Slianep break; 3501958Slianep 3511958Slianep case RESTARTER_STATE_DEGRADED: 3521958Slianep err = gt_enter_degraded(h, v, old_state, rerr); 3531958Slianep break; 3541958Slianep 3551958Slianep case RESTARTER_STATE_MAINT: 3561958Slianep err = gt_enter_maint(h, v, old_state, rerr); 3571958Slianep break; 3581958Slianep 3591958Slianep default: 3602339Slianep /* Shouldn't be in an invalid state. */ 3611958Slianep #ifndef NDEBUG 3622747Sbustos uu_warn("%s:%d: Invalid state %d.\n", __FILE__, __LINE__, 3632339Slianep v->gv_state); 3641958Slianep #endif 3651958Slianep abort(); 3661958Slianep } 3671958Slianep 3682747Sbustos switch (err) { 3692747Sbustos case 0: 3702747Sbustos break; 3712747Sbustos 3722747Sbustos case ECONNABORTED: 3732747Sbustos lost_repository = 1; 3742747Sbustos break; 3752747Sbustos 3762747Sbustos default: 3772747Sbustos #ifndef NDEBUG 3782747Sbustos uu_warn("%s:%d: " 3792747Sbustos "gt_enter_%s() failed with unexpected error %d.\n", 3802747Sbustos __FILE__, __LINE__, instance_state_str[v->gv_state], err); 3812747Sbustos #endif 3822747Sbustos abort(); 3832747Sbustos } 3842747Sbustos 3852747Sbustos return (lost_repository ? ECONNABORTED : 0); 3861958Slianep } 387