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 need 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 /* Initialize instance by refreshing it. */ 68 69 err = libscf_fmri_get_instance(h, v->gv_name, &inst); 70 switch (err) { 71 case 0: 72 break; 73 74 case ECONNABORTED: 75 return (ECONNABORTED); 76 77 case ENOENT: 78 return (0); 79 80 case EINVAL: 81 case ENOTSUP: 82 default: 83 bad_error("libscf_fmri_get_instance", err); 84 } 85 86 err = refresh_vertex(v, inst); 87 if (err == 0) 88 graph_enable_by_vertex(v, v->gv_flags & GV_ENABLED, 0); 89 90 scf_instance_destroy(inst); 91 92 /* If the service was running, propagate a stop event. */ 93 if (gt_running(old_state)) { 94 log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 95 v->gv_name); 96 97 graph_transition_propagate(v, PROPAGATE_STOP, rerr); 98 } 99 100 graph_transition_sulogin(RESTARTER_STATE_UNINIT, old_state); 101 return (0); 102 } 103 104 /* ARGSUSED */ 105 static int 106 gt_enter_maint(scf_handle_t *h, graph_vertex_t *v, 107 restarter_instance_state_t old_state, restarter_error_t rerr) 108 { 109 /* 110 * If the service was running, propagate a stop event. If the 111 * service was not running the maintenance transition may satisfy 112 * optional dependencies and should be propagated to determine 113 * whether new dependents are satisfiable. 114 */ 115 if (gt_running(old_state)) { 116 log_framework(LOG_DEBUG, "Propagating maintenance (stop) of " 117 "%s.\n", v->gv_name); 118 119 graph_transition_propagate(v, PROPAGATE_STOP, rerr); 120 } else { 121 log_framework(LOG_DEBUG, "Propagating maintenance of %s.\n", 122 v->gv_name); 123 124 graph_transition_propagate(v, PROPAGATE_SAT, rerr); 125 } 126 127 graph_transition_sulogin(RESTARTER_STATE_MAINT, old_state); 128 return (0); 129 } 130 131 /* ARGSUSED */ 132 static int 133 gt_enter_offline(scf_handle_t *h, graph_vertex_t *v, 134 restarter_instance_state_t old_state, restarter_error_t rerr) 135 { 136 /* 137 * If the instance should be enabled, see if we can start it. 138 * Otherwise send a disable command. 139 */ 140 if (v->gv_flags & GV_ENABLED) { 141 graph_start_if_satisfied(v); 142 } else { 143 if (gt_running(old_state) && v->gv_post_disable_f) 144 v->gv_post_disable_f(); 145 vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 146 } 147 148 if (gt_running(old_state)) { 149 log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 150 v->gv_name); 151 152 graph_transition_propagate(v, PROPAGATE_STOP, rerr); 153 } 154 155 graph_transition_sulogin(RESTARTER_STATE_OFFLINE, old_state); 156 return (0); 157 } 158 159 /* ARGSUSED */ 160 static int 161 gt_enter_disabled(scf_handle_t *h, graph_vertex_t *v, 162 restarter_instance_state_t old_state, restarter_error_t rerr) 163 { 164 /* 165 * If the instance should be disabled, no problem. Otherwise, 166 * send an enable command, which should result in the instance 167 * moving to OFFLINE. 168 */ 169 if (v->gv_flags & GV_ENABLED) { 170 vertex_send_event(v, RESTARTER_EVENT_TYPE_ENABLE); 171 } else if (gt_running(old_state) && v->gv_post_disable_f) { 172 v->gv_post_disable_f(); 173 } 174 175 /* 176 * If the service was running, propagate this as a stop. If the 177 * service was not running the disabled transition may satisfy 178 * optional dependencies and should be propagated to determine 179 * whether new dependents are satisfiable. 180 */ 181 if (gt_running(old_state)) { 182 log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 183 v->gv_name); 184 185 graph_transition_propagate(v, PROPAGATE_STOP, rerr); 186 187 } else { 188 log_framework(LOG_DEBUG, "Propagating disable of %s.\n", 189 v->gv_name); 190 191 graph_transition_propagate(v, PROPAGATE_SAT, rerr); 192 } 193 194 graph_transition_sulogin(RESTARTER_STATE_DISABLED, old_state); 195 return (0); 196 } 197 198 static int 199 gt_internal_online_or_degraded(scf_handle_t *h, graph_vertex_t *v, 200 restarter_instance_state_t old_state, restarter_error_t rerr) 201 { 202 int r; 203 204 /* 205 * If the instance has just come up, update the start 206 * snapshot. 207 */ 208 if (gt_running(old_state) == 0) { 209 /* 210 * Don't fire if we're just recovering state 211 * after a restart. 212 */ 213 if (old_state != RESTARTER_STATE_UNINIT && 214 v->gv_post_online_f) 215 v->gv_post_online_f(); 216 217 r = libscf_snapshots_poststart(h, v->gv_name, B_TRUE); 218 switch (r) { 219 case 0: 220 case ENOENT: 221 /* 222 * If ENOENT, the instance must have been 223 * deleted. Pretend we were successful since 224 * we should get a delete event later. 225 */ 226 break; 227 228 case ECONNABORTED: 229 return (ECONNABORTED); 230 231 case EACCES: 232 case ENOTSUP: 233 default: 234 bad_error("libscf_snapshots_poststart", r); 235 } 236 } 237 if (!(v->gv_flags & GV_ENABLED)) 238 vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 239 240 if (gt_running(old_state) == 0) { 241 log_framework(LOG_DEBUG, "Propagating start of %s.\n", 242 v->gv_name); 243 244 graph_transition_propagate(v, PROPAGATE_START, rerr); 245 } else if (rerr == RERR_REFRESH) { 246 /* For refresh we'll get a message sans state change */ 247 248 log_framework(LOG_DEBUG, "Propagating refresh of %s.\n", 249 v->gv_name); 250 251 graph_transition_propagate(v, PROPAGATE_STOP, rerr); 252 } 253 254 return (0); 255 } 256 257 static int 258 gt_enter_online(scf_handle_t *h, graph_vertex_t *v, 259 restarter_instance_state_t old_state, restarter_error_t rerr) 260 { 261 int r; 262 263 r = gt_internal_online_or_degraded(h, v, old_state, rerr); 264 if (r != 0) 265 return (r); 266 267 graph_transition_sulogin(RESTARTER_STATE_ONLINE, old_state); 268 return (0); 269 } 270 271 static int 272 gt_enter_degraded(scf_handle_t *h, graph_vertex_t *v, 273 restarter_instance_state_t old_state, restarter_error_t rerr) 274 { 275 int r; 276 277 r = gt_internal_online_or_degraded(h, v, old_state, rerr); 278 if (r != 0) 279 return (r); 280 281 graph_transition_sulogin(RESTARTER_STATE_DEGRADED, old_state); 282 return (0); 283 } 284 285 /* 286 * gt_transition() implements the state transition for the graph 287 * state machine. It can return: 288 * 0 success 289 * ECONNABORTED repository connection aborted 290 * 291 * v->gv_state should be set to the state we're transitioning to before 292 * calling this function. 293 */ 294 int 295 gt_transition(scf_handle_t *h, graph_vertex_t *v, restarter_error_t rerr, 296 restarter_instance_state_t old_state) 297 { 298 int err; 299 int lost_repository = 0; 300 301 /* 302 * If there's a common set of work to be done on exit from the 303 * old_state, include it as a separate set of functions here. For 304 * now there's no such work, so there are no gt_exit functions. 305 */ 306 307 err = vertex_subgraph_dependencies_shutdown(h, v, old_state); 308 switch (err) { 309 case 0: 310 break; 311 312 case ECONNABORTED: 313 lost_repository = 1; 314 break; 315 316 default: 317 bad_error("vertex_subgraph_dependencies_shutdown", err); 318 } 319 320 /* 321 * Now call the appropriate gt_enter function for the new state. 322 */ 323 switch (v->gv_state) { 324 case RESTARTER_STATE_UNINIT: 325 err = gt_enter_uninit(h, v, old_state, rerr); 326 break; 327 328 case RESTARTER_STATE_DISABLED: 329 err = gt_enter_disabled(h, v, old_state, rerr); 330 break; 331 332 case RESTARTER_STATE_OFFLINE: 333 err = gt_enter_offline(h, v, old_state, rerr); 334 break; 335 336 case RESTARTER_STATE_ONLINE: 337 err = gt_enter_online(h, v, old_state, rerr); 338 break; 339 340 case RESTARTER_STATE_DEGRADED: 341 err = gt_enter_degraded(h, v, old_state, rerr); 342 break; 343 344 case RESTARTER_STATE_MAINT: 345 err = gt_enter_maint(h, v, old_state, rerr); 346 break; 347 348 default: 349 /* Shouldn't be in an invalid state. */ 350 #ifndef NDEBUG 351 uu_warn("%s:%d: Invalid state %d.\n", __FILE__, __LINE__, 352 v->gv_state); 353 #endif 354 abort(); 355 } 356 357 switch (err) { 358 case 0: 359 break; 360 361 case ECONNABORTED: 362 lost_repository = 1; 363 break; 364 365 default: 366 #ifndef NDEBUG 367 uu_warn("%s:%d: " 368 "gt_enter_%s() failed with unexpected error %d.\n", 369 __FILE__, __LINE__, instance_state_str[v->gv_state], err); 370 #endif 371 abort(); 372 } 373 374 return (lost_repository ? ECONNABORTED : 0); 375 } 376