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