1*99b44c3bSlianep /* 2*99b44c3bSlianep * CDDL HEADER START 3*99b44c3bSlianep * 4*99b44c3bSlianep * The contents of this file are subject to the terms of the 5*99b44c3bSlianep * Common Development and Distribution License (the "License"). 6*99b44c3bSlianep * You may not use this file except in compliance with the License. 7*99b44c3bSlianep * 8*99b44c3bSlianep * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*99b44c3bSlianep * or http://www.opensolaris.org/os/licensing. 10*99b44c3bSlianep * See the License for the specific language governing permissions 11*99b44c3bSlianep * and limitations under the License. 12*99b44c3bSlianep * 13*99b44c3bSlianep * When distributing Covered Code, include this CDDL HEADER in each 14*99b44c3bSlianep * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*99b44c3bSlianep * If applicable, add the following below this CDDL HEADER, with the 16*99b44c3bSlianep * fields enclosed by brackets "[]" replaced with your own identifying 17*99b44c3bSlianep * information: Portions Copyright [yyyy] [name of copyright owner] 18*99b44c3bSlianep * 19*99b44c3bSlianep * CDDL HEADER END 20*99b44c3bSlianep */ 21*99b44c3bSlianep /* 22*99b44c3bSlianep * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23*99b44c3bSlianep * Use is subject to license terms. 24*99b44c3bSlianep */ 25*99b44c3bSlianep 26*99b44c3bSlianep #pragma ident "%Z%%M% %I% %E% SMI" 27*99b44c3bSlianep 28*99b44c3bSlianep /* 29*99b44c3bSlianep * transition.c - Graph State Machine 30*99b44c3bSlianep * 31*99b44c3bSlianep * The graph state machine is implemented here, with a typical approach 32*99b44c3bSlianep * of a function per state. Separating the implementation allows more 33*99b44c3bSlianep * clarity into the actions taken on notification of state change, as well 34*99b44c3bSlianep * as a place for future expansion including hooks for configurable actions. 35*99b44c3bSlianep * All functions are called with dgraph_lock held. 36*99b44c3bSlianep * 37*99b44c3bSlianep * The start action for this state machine is not explicit. The states 38*99b44c3bSlianep * (ONLINE and DEGRADED) which needs to know when they're entering the state 39*99b44c3bSlianep * due to a daemon restart implement this understanding by checking for 40*99b44c3bSlianep * transition from uninitialized. In the future, this would likely be better 41*99b44c3bSlianep * as an explicit start action instead of relying on an overloaded transition. 42*99b44c3bSlianep * 43*99b44c3bSlianep * All gt_enter functions use the same set of return codes. 44*99b44c3bSlianep * 0 success 45*99b44c3bSlianep * ECONNABORTED repository connection aborted 46*99b44c3bSlianep */ 47*99b44c3bSlianep 48*99b44c3bSlianep #include "startd.h" 49*99b44c3bSlianep 50*99b44c3bSlianep static int 51*99b44c3bSlianep gt_running(restarter_instance_state_t state) 52*99b44c3bSlianep { 53*99b44c3bSlianep if (state == RESTARTER_STATE_ONLINE || 54*99b44c3bSlianep state == RESTARTER_STATE_DEGRADED) 55*99b44c3bSlianep return (1); 56*99b44c3bSlianep 57*99b44c3bSlianep return (0); 58*99b44c3bSlianep } 59*99b44c3bSlianep 60*99b44c3bSlianep static int 61*99b44c3bSlianep gt_enter_uninit(scf_handle_t *h, graph_vertex_t *v, 62*99b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 63*99b44c3bSlianep { 64*99b44c3bSlianep int err; 65*99b44c3bSlianep scf_instance_t *inst; 66*99b44c3bSlianep 67*99b44c3bSlianep vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 68*99b44c3bSlianep 69*99b44c3bSlianep /* Initialize instance by refreshing it. */ 70*99b44c3bSlianep 71*99b44c3bSlianep err = libscf_fmri_get_instance(h, v->gv_name, &inst); 72*99b44c3bSlianep switch (err) { 73*99b44c3bSlianep case 0: 74*99b44c3bSlianep break; 75*99b44c3bSlianep 76*99b44c3bSlianep case ECONNABORTED: 77*99b44c3bSlianep return (ECONNABORTED); 78*99b44c3bSlianep 79*99b44c3bSlianep case ENOENT: 80*99b44c3bSlianep return (0); 81*99b44c3bSlianep 82*99b44c3bSlianep case EINVAL: 83*99b44c3bSlianep case ENOTSUP: 84*99b44c3bSlianep default: 85*99b44c3bSlianep bad_error("libscf_fmri_get_instance", err); 86*99b44c3bSlianep } 87*99b44c3bSlianep 88*99b44c3bSlianep err = refresh_vertex(v, inst); 89*99b44c3bSlianep if (err == 0) 90*99b44c3bSlianep graph_enable_by_vertex(v, v->gv_flags & GV_ENABLED, 0); 91*99b44c3bSlianep 92*99b44c3bSlianep scf_instance_destroy(inst); 93*99b44c3bSlianep 94*99b44c3bSlianep /* If the service was running, propagate a stop event. */ 95*99b44c3bSlianep if (gt_running(old_state)) { 96*99b44c3bSlianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 97*99b44c3bSlianep v->gv_name); 98*99b44c3bSlianep 99*99b44c3bSlianep graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr); 100*99b44c3bSlianep } 101*99b44c3bSlianep 102*99b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_UNINIT, old_state); 103*99b44c3bSlianep return (0); 104*99b44c3bSlianep } 105*99b44c3bSlianep 106*99b44c3bSlianep static int 107*99b44c3bSlianep gt_enter_maint(scf_handle_t *h, graph_vertex_t *v, 108*99b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 109*99b44c3bSlianep { 110*99b44c3bSlianep vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 111*99b44c3bSlianep 112*99b44c3bSlianep if (gt_running(old_state)) { 113*99b44c3bSlianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 114*99b44c3bSlianep v->gv_name); 115*99b44c3bSlianep 116*99b44c3bSlianep graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr); 117*99b44c3bSlianep } else if (v->gv_state == RESTARTER_STATE_MAINT) { 118*99b44c3bSlianep log_framework(LOG_DEBUG, "Propagating maintenance of %s.\n", 119*99b44c3bSlianep v->gv_name); 120*99b44c3bSlianep 121*99b44c3bSlianep graph_transition_propagate(v, RESTARTER_EVENT_TYPE_START, rerr); 122*99b44c3bSlianep } 123*99b44c3bSlianep 124*99b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_MAINT, old_state); 125*99b44c3bSlianep return (0); 126*99b44c3bSlianep } 127*99b44c3bSlianep 128*99b44c3bSlianep static int 129*99b44c3bSlianep gt_enter_offline(scf_handle_t *h, graph_vertex_t *v, 130*99b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 131*99b44c3bSlianep { 132*99b44c3bSlianep vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 133*99b44c3bSlianep 134*99b44c3bSlianep /* 135*99b44c3bSlianep * If the instance should be enabled, see if we can start it. 136*99b44c3bSlianep * Otherwise send a disable command. 137*99b44c3bSlianep */ 138*99b44c3bSlianep if (v->gv_flags & GV_ENABLED) { 139*99b44c3bSlianep graph_start_if_satisfied(v); 140*99b44c3bSlianep } else { 141*99b44c3bSlianep if (gt_running(old_state) && v->gv_post_disable_f) 142*99b44c3bSlianep v->gv_post_disable_f(); 143*99b44c3bSlianep vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 144*99b44c3bSlianep } 145*99b44c3bSlianep 146*99b44c3bSlianep if (gt_running(old_state)) { 147*99b44c3bSlianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 148*99b44c3bSlianep v->gv_name); 149*99b44c3bSlianep 150*99b44c3bSlianep graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr); 151*99b44c3bSlianep } 152*99b44c3bSlianep 153*99b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_OFFLINE, old_state); 154*99b44c3bSlianep return (0); 155*99b44c3bSlianep } 156*99b44c3bSlianep 157*99b44c3bSlianep static int 158*99b44c3bSlianep gt_enter_disabled(scf_handle_t *h, graph_vertex_t *v, 159*99b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 160*99b44c3bSlianep { 161*99b44c3bSlianep vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 162*99b44c3bSlianep 163*99b44c3bSlianep /* 164*99b44c3bSlianep * If the instance should be disabled, no problem. Otherwise, 165*99b44c3bSlianep * send an enable command, which should result in the instance 166*99b44c3bSlianep * moving to OFFLINE. 167*99b44c3bSlianep */ 168*99b44c3bSlianep if (v->gv_flags & GV_ENABLED) { 169*99b44c3bSlianep vertex_send_event(v, RESTARTER_EVENT_TYPE_ENABLE); 170*99b44c3bSlianep } else if (gt_running(old_state) && v->gv_post_disable_f) { 171*99b44c3bSlianep v->gv_post_disable_f(); 172*99b44c3bSlianep } 173*99b44c3bSlianep 174*99b44c3bSlianep /* 175*99b44c3bSlianep * If the service was running, propagate this as a stop. 176*99b44c3bSlianep * Otherwise, we treat other transitions as a start propagate, 177*99b44c3bSlianep * since they can satisfy optional_all dependencies. 178*99b44c3bSlianep */ 179*99b44c3bSlianep if (gt_running(old_state)) { 180*99b44c3bSlianep log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 181*99b44c3bSlianep v->gv_name); 182*99b44c3bSlianep 183*99b44c3bSlianep graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr); 184*99b44c3bSlianep 185*99b44c3bSlianep } else if (v->gv_state == RESTARTER_STATE_DISABLED) { 186*99b44c3bSlianep log_framework(LOG_DEBUG, "Propagating disable of %s.\n", 187*99b44c3bSlianep v->gv_name); 188*99b44c3bSlianep 189*99b44c3bSlianep graph_transition_propagate(v, RESTARTER_EVENT_TYPE_START, rerr); 190*99b44c3bSlianep } 191*99b44c3bSlianep 192*99b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_DISABLED, old_state); 193*99b44c3bSlianep return (0); 194*99b44c3bSlianep } 195*99b44c3bSlianep 196*99b44c3bSlianep static int 197*99b44c3bSlianep gt_internal_online_or_degraded(scf_handle_t *h, graph_vertex_t *v, 198*99b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 199*99b44c3bSlianep { 200*99b44c3bSlianep int r; 201*99b44c3bSlianep 202*99b44c3bSlianep /* 203*99b44c3bSlianep * If the instance has just come up, update the start 204*99b44c3bSlianep * snapshot. 205*99b44c3bSlianep */ 206*99b44c3bSlianep if (gt_running(old_state) == 0) { 207*99b44c3bSlianep /* 208*99b44c3bSlianep * Don't fire if we're just recovering state 209*99b44c3bSlianep * after a restart. 210*99b44c3bSlianep */ 211*99b44c3bSlianep if (old_state != RESTARTER_STATE_UNINIT && 212*99b44c3bSlianep v->gv_post_online_f) 213*99b44c3bSlianep v->gv_post_online_f(); 214*99b44c3bSlianep 215*99b44c3bSlianep r = libscf_snapshots_poststart(h, v->gv_name, B_TRUE); 216*99b44c3bSlianep switch (r) { 217*99b44c3bSlianep case 0: 218*99b44c3bSlianep case ENOENT: 219*99b44c3bSlianep /* 220*99b44c3bSlianep * If ENOENT, the instance must have been 221*99b44c3bSlianep * deleted. Pretend we were successful since 222*99b44c3bSlianep * we should get a delete event later. 223*99b44c3bSlianep */ 224*99b44c3bSlianep break; 225*99b44c3bSlianep 226*99b44c3bSlianep case ECONNABORTED: 227*99b44c3bSlianep return (ECONNABORTED); 228*99b44c3bSlianep 229*99b44c3bSlianep case EACCES: 230*99b44c3bSlianep case ENOTSUP: 231*99b44c3bSlianep default: 232*99b44c3bSlianep bad_error("libscf_snapshots_poststart", r); 233*99b44c3bSlianep } 234*99b44c3bSlianep } 235*99b44c3bSlianep if (!(v->gv_flags & GV_ENABLED)) 236*99b44c3bSlianep vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 237*99b44c3bSlianep 238*99b44c3bSlianep if (gt_running(old_state) == 0) { 239*99b44c3bSlianep log_framework(LOG_DEBUG, "Propagating start of %s.\n", 240*99b44c3bSlianep v->gv_name); 241*99b44c3bSlianep 242*99b44c3bSlianep graph_transition_propagate(v, 243*99b44c3bSlianep RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON, rerr); 244*99b44c3bSlianep } else if (rerr == RERR_REFRESH) { 245*99b44c3bSlianep /* For refresh we'll get a message sans state change */ 246*99b44c3bSlianep 247*99b44c3bSlianep log_framework(LOG_DEBUG, "Propagating refresh of %s.\n", 248*99b44c3bSlianep v->gv_name); 249*99b44c3bSlianep 250*99b44c3bSlianep graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr); 251*99b44c3bSlianep } 252*99b44c3bSlianep 253*99b44c3bSlianep return (0); 254*99b44c3bSlianep } 255*99b44c3bSlianep 256*99b44c3bSlianep static int 257*99b44c3bSlianep gt_enter_online(scf_handle_t *h, graph_vertex_t *v, 258*99b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 259*99b44c3bSlianep { 260*99b44c3bSlianep int r; 261*99b44c3bSlianep 262*99b44c3bSlianep r = gt_internal_online_or_degraded(h, v, old_state, rerr); 263*99b44c3bSlianep if (r != 0) 264*99b44c3bSlianep return (r); 265*99b44c3bSlianep 266*99b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_ONLINE, old_state); 267*99b44c3bSlianep return (0); 268*99b44c3bSlianep } 269*99b44c3bSlianep 270*99b44c3bSlianep static int 271*99b44c3bSlianep gt_enter_degraded(scf_handle_t *h, graph_vertex_t *v, 272*99b44c3bSlianep restarter_instance_state_t old_state, restarter_error_t rerr) 273*99b44c3bSlianep { 274*99b44c3bSlianep int r; 275*99b44c3bSlianep 276*99b44c3bSlianep r = gt_internal_online_or_degraded(h, v, old_state, rerr); 277*99b44c3bSlianep if (r != 0) 278*99b44c3bSlianep return (r); 279*99b44c3bSlianep 280*99b44c3bSlianep graph_transition_sulogin(RESTARTER_STATE_DEGRADED, old_state); 281*99b44c3bSlianep return (0); 282*99b44c3bSlianep } 283*99b44c3bSlianep 284*99b44c3bSlianep /* 285*99b44c3bSlianep * gt_transition() implements the state transition for the graph 286*99b44c3bSlianep * state machine. It can return: 287*99b44c3bSlianep * 0 success 288*99b44c3bSlianep * ECONNABORTED repository connection aborted 289*99b44c3bSlianep */ 290*99b44c3bSlianep int 291*99b44c3bSlianep gt_transition(scf_handle_t *h, graph_vertex_t *v, restarter_error_t rerr, 292*99b44c3bSlianep restarter_instance_state_t old_state, restarter_instance_state_t new_state) 293*99b44c3bSlianep { 294*99b44c3bSlianep int err = 0; 295*99b44c3bSlianep 296*99b44c3bSlianep /* 297*99b44c3bSlianep * If there's a common set of work to be done on exit from the 298*99b44c3bSlianep * old_state, include it as a separate set of functions here. For 299*99b44c3bSlianep * now there's no such work, so there are no gt_exit functions. 300*99b44c3bSlianep */ 301*99b44c3bSlianep 302*99b44c3bSlianep /* 303*99b44c3bSlianep * Now call the appropriate gt_enter function for the new state. 304*99b44c3bSlianep */ 305*99b44c3bSlianep switch (new_state) { 306*99b44c3bSlianep case RESTARTER_STATE_UNINIT: 307*99b44c3bSlianep err = gt_enter_uninit(h, v, old_state, rerr); 308*99b44c3bSlianep break; 309*99b44c3bSlianep 310*99b44c3bSlianep case RESTARTER_STATE_DISABLED: 311*99b44c3bSlianep err = gt_enter_disabled(h, v, old_state, rerr); 312*99b44c3bSlianep break; 313*99b44c3bSlianep 314*99b44c3bSlianep case RESTARTER_STATE_OFFLINE: 315*99b44c3bSlianep err = gt_enter_offline(h, v, old_state, rerr); 316*99b44c3bSlianep break; 317*99b44c3bSlianep 318*99b44c3bSlianep case RESTARTER_STATE_ONLINE: 319*99b44c3bSlianep err = gt_enter_online(h, v, old_state, rerr); 320*99b44c3bSlianep break; 321*99b44c3bSlianep 322*99b44c3bSlianep case RESTARTER_STATE_DEGRADED: 323*99b44c3bSlianep err = gt_enter_degraded(h, v, old_state, rerr); 324*99b44c3bSlianep break; 325*99b44c3bSlianep 326*99b44c3bSlianep case RESTARTER_STATE_MAINT: 327*99b44c3bSlianep err = gt_enter_maint(h, v, old_state, rerr); 328*99b44c3bSlianep break; 329*99b44c3bSlianep 330*99b44c3bSlianep default: 331*99b44c3bSlianep /* Shouldn't have been passed an invalid state. */ 332*99b44c3bSlianep #ifndef NDEBUG 333*99b44c3bSlianep uu_warn("%s:%d: Uncaught case %d.\n", __FILE__, __LINE__, 334*99b44c3bSlianep new_state); 335*99b44c3bSlianep #endif 336*99b44c3bSlianep abort(); 337*99b44c3bSlianep } 338*99b44c3bSlianep 339*99b44c3bSlianep return (err); 340*99b44c3bSlianep } 341