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, RESTARTER_EVENT_TYPE_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 if (gt_running(old_state)) { 113 log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 114 v->gv_name); 115 116 graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr); 117 } else if (v->gv_state == RESTARTER_STATE_MAINT) { 118 log_framework(LOG_DEBUG, "Propagating maintenance of %s.\n", 119 v->gv_name); 120 121 graph_transition_propagate(v, RESTARTER_EVENT_TYPE_START, rerr); 122 } 123 124 graph_transition_sulogin(RESTARTER_STATE_MAINT, old_state); 125 return (0); 126 } 127 128 static int 129 gt_enter_offline(scf_handle_t *h, graph_vertex_t *v, 130 restarter_instance_state_t old_state, restarter_error_t rerr) 131 { 132 vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 133 134 /* 135 * If the instance should be enabled, see if we can start it. 136 * Otherwise send a disable command. 137 */ 138 if (v->gv_flags & GV_ENABLED) { 139 graph_start_if_satisfied(v); 140 } else { 141 if (gt_running(old_state) && v->gv_post_disable_f) 142 v->gv_post_disable_f(); 143 vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 144 } 145 146 if (gt_running(old_state)) { 147 log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 148 v->gv_name); 149 150 graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr); 151 } 152 153 graph_transition_sulogin(RESTARTER_STATE_OFFLINE, old_state); 154 return (0); 155 } 156 157 static int 158 gt_enter_disabled(scf_handle_t *h, graph_vertex_t *v, 159 restarter_instance_state_t old_state, restarter_error_t rerr) 160 { 161 vertex_subgraph_dependencies_shutdown(h, v, gt_running(old_state)); 162 163 /* 164 * If the instance should be disabled, no problem. Otherwise, 165 * send an enable command, which should result in the instance 166 * moving to OFFLINE. 167 */ 168 if (v->gv_flags & GV_ENABLED) { 169 vertex_send_event(v, RESTARTER_EVENT_TYPE_ENABLE); 170 } else if (gt_running(old_state) && v->gv_post_disable_f) { 171 v->gv_post_disable_f(); 172 } 173 174 /* 175 * If the service was running, propagate this as a stop. 176 * Otherwise, we treat other transitions as a start propagate, 177 * since they can satisfy optional_all dependencies. 178 */ 179 if (gt_running(old_state)) { 180 log_framework(LOG_DEBUG, "Propagating stop of %s.\n", 181 v->gv_name); 182 183 graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr); 184 185 } else if (v->gv_state == RESTARTER_STATE_DISABLED) { 186 log_framework(LOG_DEBUG, "Propagating disable of %s.\n", 187 v->gv_name); 188 189 graph_transition_propagate(v, RESTARTER_EVENT_TYPE_START, rerr); 190 } 191 192 graph_transition_sulogin(RESTARTER_STATE_DISABLED, old_state); 193 return (0); 194 } 195 196 static int 197 gt_internal_online_or_degraded(scf_handle_t *h, graph_vertex_t *v, 198 restarter_instance_state_t old_state, restarter_error_t rerr) 199 { 200 int r; 201 202 /* 203 * If the instance has just come up, update the start 204 * snapshot. 205 */ 206 if (gt_running(old_state) == 0) { 207 /* 208 * Don't fire if we're just recovering state 209 * after a restart. 210 */ 211 if (old_state != RESTARTER_STATE_UNINIT && 212 v->gv_post_online_f) 213 v->gv_post_online_f(); 214 215 r = libscf_snapshots_poststart(h, v->gv_name, B_TRUE); 216 switch (r) { 217 case 0: 218 case ENOENT: 219 /* 220 * If ENOENT, the instance must have been 221 * deleted. Pretend we were successful since 222 * we should get a delete event later. 223 */ 224 break; 225 226 case ECONNABORTED: 227 return (ECONNABORTED); 228 229 case EACCES: 230 case ENOTSUP: 231 default: 232 bad_error("libscf_snapshots_poststart", r); 233 } 234 } 235 if (!(v->gv_flags & GV_ENABLED)) 236 vertex_send_event(v, RESTARTER_EVENT_TYPE_DISABLE); 237 238 if (gt_running(old_state) == 0) { 239 log_framework(LOG_DEBUG, "Propagating start of %s.\n", 240 v->gv_name); 241 242 graph_transition_propagate(v, 243 RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON, rerr); 244 } else if (rerr == RERR_REFRESH) { 245 /* For refresh we'll get a message sans state change */ 246 247 log_framework(LOG_DEBUG, "Propagating refresh of %s.\n", 248 v->gv_name); 249 250 graph_transition_propagate(v, RESTARTER_EVENT_TYPE_STOP, rerr); 251 } 252 253 return (0); 254 } 255 256 static int 257 gt_enter_online(scf_handle_t *h, graph_vertex_t *v, 258 restarter_instance_state_t old_state, restarter_error_t rerr) 259 { 260 int r; 261 262 r = gt_internal_online_or_degraded(h, v, old_state, rerr); 263 if (r != 0) 264 return (r); 265 266 graph_transition_sulogin(RESTARTER_STATE_ONLINE, old_state); 267 return (0); 268 } 269 270 static int 271 gt_enter_degraded(scf_handle_t *h, graph_vertex_t *v, 272 restarter_instance_state_t old_state, restarter_error_t rerr) 273 { 274 int r; 275 276 r = gt_internal_online_or_degraded(h, v, old_state, rerr); 277 if (r != 0) 278 return (r); 279 280 graph_transition_sulogin(RESTARTER_STATE_DEGRADED, old_state); 281 return (0); 282 } 283 284 /* 285 * gt_transition() implements the state transition for the graph 286 * state machine. It can return: 287 * 0 success 288 * ECONNABORTED repository connection aborted 289 */ 290 int 291 gt_transition(scf_handle_t *h, graph_vertex_t *v, restarter_error_t rerr, 292 restarter_instance_state_t old_state, restarter_instance_state_t new_state) 293 { 294 int err = 0; 295 296 /* 297 * If there's a common set of work to be done on exit from the 298 * old_state, include it as a separate set of functions here. For 299 * now there's no such work, so there are no gt_exit functions. 300 */ 301 302 /* 303 * Now call the appropriate gt_enter function for the new state. 304 */ 305 switch (new_state) { 306 case RESTARTER_STATE_UNINIT: 307 err = gt_enter_uninit(h, v, old_state, rerr); 308 break; 309 310 case RESTARTER_STATE_DISABLED: 311 err = gt_enter_disabled(h, v, old_state, rerr); 312 break; 313 314 case RESTARTER_STATE_OFFLINE: 315 err = gt_enter_offline(h, v, old_state, rerr); 316 break; 317 318 case RESTARTER_STATE_ONLINE: 319 err = gt_enter_online(h, v, old_state, rerr); 320 break; 321 322 case RESTARTER_STATE_DEGRADED: 323 err = gt_enter_degraded(h, v, old_state, rerr); 324 break; 325 326 case RESTARTER_STATE_MAINT: 327 err = gt_enter_maint(h, v, old_state, rerr); 328 break; 329 330 default: 331 /* Shouldn't have been passed an invalid state. */ 332 #ifndef NDEBUG 333 uu_warn("%s:%d: Uncaught case %d.\n", __FILE__, __LINE__, 334 new_state); 335 #endif 336 abort(); 337 } 338 339 return (err); 340 } 341