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