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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * synchronous svcadm logic 29 */ 30 31 #include <locale.h> 32 #include <libintl.h> 33 #include <libscf.h> 34 #include <libscf_priv.h> 35 #include <libuutil.h> 36 #include <stddef.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 #include <assert.h> 42 #include <errno.h> 43 #include <sys/stat.h> 44 45 46 /* 47 * Definitions from svcadm.c. 48 */ 49 extern scf_handle_t *h; 50 extern ssize_t max_scf_fmri_sz; 51 52 extern void do_scfdie(int) __NORETURN; 53 extern int inst_get_state(scf_instance_t *, char *, const char *, 54 scf_propertygroup_t **); 55 extern ssize_t get_astring_prop(const scf_propertygroup_t *, const char *, 56 scf_property_t *, scf_value_t *, char *, size_t); 57 extern int get_bool_prop(scf_propertygroup_t *, const char *, uint8_t *); 58 59 #define scfdie() do_scfdie(__LINE__) 60 61 int has_potential(scf_instance_t *, int); 62 63 /* 64 * Determines if the specified instance is enabled, composing the 65 * general and general_ovr property groups. For simplicity, we map 66 * most errors to "not enabled". 67 */ 68 int 69 is_enabled(scf_instance_t *inst) 70 { 71 scf_propertygroup_t *pg; 72 uint8_t bp; 73 74 if ((pg = scf_pg_create(h)) == NULL) 75 scfdie(); 76 77 if (scf_instance_get_pg(inst, SCF_PG_GENERAL_OVR, pg) == 0 && 78 get_bool_prop(pg, SCF_PROPERTY_ENABLED, &bp) == 0) { 79 scf_pg_destroy(pg); 80 return (bp); 81 } 82 83 if (scf_instance_get_pg(inst, SCF_PG_GENERAL, pg) == 0 && 84 get_bool_prop(pg, SCF_PROPERTY_ENABLED, &bp) == 0) { 85 scf_pg_destroy(pg); 86 return (bp); 87 } 88 89 scf_pg_destroy(pg); 90 return (B_FALSE); 91 } 92 93 /* 94 * Reads an astring property from a property group. If the named 95 * property doesn't exist, returns NULL. The result of a successful 96 * call should be freed. 97 */ 98 static char * 99 read_astring_prop(scf_propertygroup_t *pg, scf_value_t *val, 100 scf_property_t *prop, const char *name) 101 { 102 char *value; 103 size_t value_sz; 104 105 if (scf_pg_get_property(pg, name, prop) == -1) { 106 switch (scf_error()) { 107 case SCF_ERROR_NOT_FOUND: 108 case SCF_ERROR_DELETED: 109 return (NULL); 110 default: 111 scfdie(); 112 } 113 } 114 115 if (scf_property_get_value(prop, val) != 0) { 116 switch (scf_error()) { 117 case SCF_ERROR_DELETED: 118 case SCF_ERROR_NOT_FOUND: 119 case SCF_ERROR_CONSTRAINT_VIOLATED: 120 return (NULL); 121 default: 122 scfdie(); 123 } 124 } 125 126 value_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 127 if ((value = malloc(value_sz)) == NULL) 128 scfdie(); 129 130 if (scf_value_get_astring(val, value, value_sz) <= 0) { 131 free(value); 132 return (NULL); 133 } 134 135 return (value); 136 } 137 138 /* 139 * Creates and returns an scf_iter for the values of the named 140 * multi-value property. Returns NULL on failure. 141 */ 142 static scf_iter_t * 143 prop_walk_init(scf_propertygroup_t *pg, const char *name) 144 { 145 scf_iter_t *iter; 146 scf_property_t *prop; 147 148 if ((iter = scf_iter_create(h)) == NULL || 149 (prop = scf_property_create(h)) == NULL) 150 scfdie(); 151 152 if (scf_pg_get_property(pg, name, prop) != 0) { 153 switch (scf_error()) { 154 case SCF_ERROR_NOT_FOUND: 155 case SCF_ERROR_DELETED: 156 goto error; 157 default: 158 scfdie(); 159 } 160 } 161 162 if (scf_iter_property_values(iter, prop) != 0) { 163 if (scf_error() != SCF_ERROR_DELETED) 164 scfdie(); 165 goto error; 166 } 167 168 scf_property_destroy(prop); 169 return (iter); 170 error: 171 scf_property_destroy(prop); 172 scf_iter_destroy(iter); 173 return (NULL); 174 } 175 176 /* 177 * Reads the next value from the multi-value property using the 178 * scf_iter obtained by prop_walk_init, and places it in the buffer 179 * pointed to by fmri. Returns -1 on failure, 0 when done, and non-0 180 * when returning a value. 181 */ 182 static int 183 prop_walk_step(scf_iter_t *iter, char *fmri, size_t len) 184 { 185 int r; 186 scf_value_t *val; 187 188 if ((val = scf_value_create(h)) == NULL) 189 scfdie(); 190 191 r = scf_iter_next_value(iter, val); 192 if (r == 0) 193 goto out; 194 if (r == -1) { 195 if (scf_error() != SCF_ERROR_DELETED) 196 scfdie(); 197 goto out; 198 } 199 if (scf_value_get_astring(val, fmri, len) <= 0) { 200 r = -1; 201 goto out; 202 } 203 204 out: 205 scf_value_destroy(val); 206 return (r); 207 } 208 209 /* 210 * Determines if a file dependency is satisfied, taking into account 211 * whether it is an exclusion dependency or not. If we can't access 212 * the file, we err on the side of caution and assume the dependency 213 * isn't satisfied. 214 */ 215 static int 216 file_has_potential(char *fmri, int exclude) 217 { 218 const char *path; 219 struct stat st; 220 221 int good = exclude ? B_FALSE : B_TRUE; 222 223 if (scf_parse_file_fmri(fmri, NULL, &path) != 0) 224 return (good); 225 226 if (stat(path, &st) == 0) 227 return (good); 228 229 if (errno == EACCES) { 230 uu_warn(gettext("Unable to access \"%s\".\n"), path); 231 return (B_FALSE); 232 } 233 234 return (!good); 235 } 236 237 /* 238 * Determines if a dependency on a service instance is satisfiable. 239 * Returns 0 if not, 1 if it is, or 2 if it is an optional or exclude 240 * dependency and the service only "weakly" satisfies (i.e. is disabled 241 * or is in maintenance state). 242 */ 243 static int 244 inst_has_potential(scf_instance_t *inst, int enabled, int optional, int exclude) 245 { 246 char state[MAX_SCF_STATE_STRING_SZ]; 247 248 if (!enabled) 249 return ((optional || exclude) ? 2 : 0); 250 251 /* 252 * Normally we would return a positive value on failure; 253 * relying on startd to place the service in maintenance. But 254 * if we can't read a service's state, we have to assume it is 255 * out to lunch. 256 */ 257 if (inst_get_state(inst, state, NULL, NULL) != 0) 258 return (0); 259 260 /* 261 * Optional dependencies which are offline always have a possibility of 262 * coming online. 263 */ 264 if (optional && strcmp(state, SCF_STATE_STRING_OFFLINE) == 0) 265 return (2); 266 267 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) { 268 /* 269 * Enabled services in maintenance state satisfy 270 * optional-all dependencies. 271 */ 272 return ((optional || exclude) ? 2 : 0); 273 } 274 275 /* 276 * We're enabled and not in maintenance. 277 */ 278 if (exclude) 279 return (0); 280 281 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 || 282 strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) 283 return (1); 284 285 return (has_potential(inst, B_FALSE)); 286 } 287 288 /* 289 * Determines if a dependency on an fmri is satisfiable, handling the 290 * separate cases for file, service, and instance fmris. Returns false 291 * if not, or true if it is. Takes into account if the dependency is 292 * an optional or exclusive one. 293 */ 294 static int 295 fmri_has_potential(char *fmri, int isfile, int optional, int exclude, 296 int restarter) 297 { 298 scf_instance_t *inst; 299 scf_service_t *svc; 300 scf_iter_t *iter; 301 int good = exclude ? B_FALSE : B_TRUE; 302 int enabled; 303 int r, result; 304 int optbad; 305 306 assert(!optional || !exclude); 307 308 if (isfile) 309 return (file_has_potential(fmri, exclude)); 310 311 if ((inst = scf_instance_create(h)) == NULL || 312 (svc = scf_service_create(h)) == NULL || 313 (iter = scf_iter_create(h)) == NULL) 314 scfdie(); 315 316 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL, NULL, 317 SCF_DECODE_FMRI_EXACT) == 0) { 318 enabled = is_enabled(inst); 319 result = 320 (inst_has_potential(inst, enabled, optional, exclude) != 0); 321 goto out; 322 } 323 324 if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL, 325 SCF_DECODE_FMRI_EXACT) != 0) { 326 /* 327 * If we are checking a restarter dependency, a bad 328 * or nonexistent service will never be noticed. 329 */ 330 result = restarter ? B_FALSE : good; 331 goto out; 332 } 333 334 if (scf_iter_service_instances(iter, svc) != 0) { 335 if (scf_error() != SCF_ERROR_DELETED) 336 scfdie(); 337 result = good; 338 goto out; 339 } 340 341 optbad = 0; 342 for (;;) { 343 r = scf_iter_next_instance(iter, inst); 344 if (r == 0) { 345 result = exclude || (optional && !optbad); 346 goto out; 347 } 348 if (r == -1) { 349 if (scf_error() != SCF_ERROR_DELETED) 350 scfdie(); 351 result = good; 352 goto out; 353 } 354 355 enabled = is_enabled(inst); 356 r = inst_has_potential(inst, enabled, optional, exclude); 357 358 /* 359 * Exclusion dependencies over services map to 360 * require-none for its instances. 361 */ 362 if (exclude) 363 r = (r == 0); 364 365 if (r == 1) { 366 /* 367 * Remember, if this is an exclusion dependency 368 * (which means we are here because there 369 * exists an instance which wasn't satisfiable 370 * in that regard), good means bad. 371 */ 372 result = good; 373 goto out; 374 } 375 376 if (optional && r == 0) 377 optbad = 1; 378 } 379 380 out: 381 scf_instance_destroy(inst); 382 scf_service_destroy(svc); 383 scf_iter_destroy(iter); 384 return (result); 385 } 386 387 static int 388 eval_require_any(scf_iter_t *iter, char *value, size_t value_sz, int isfile) 389 { 390 int r, empty = B_TRUE; 391 392 for (;;) { 393 /* 394 * For reasons unknown, an empty require_any dependency 395 * group is considered by startd to be satisfied. 396 * This insanity fortunately doesn't extend to 397 * dependencies on services with no instances. 398 */ 399 if ((r = prop_walk_step(iter, value, value_sz)) <= 0) 400 return ((r == 0 && empty) ? B_TRUE : r); 401 if (fmri_has_potential(value, isfile, B_FALSE, B_FALSE, 402 B_FALSE)) 403 return (1); 404 empty = B_FALSE; 405 } 406 } 407 408 static int 409 eval_all(scf_iter_t *iter, char *value, size_t value_sz, 410 int isfile, int optional, int exclude) 411 { 412 int r; 413 414 for (;;) { 415 if ((r = prop_walk_step(iter, value, value_sz)) <= 0) 416 return ((r == 0) ? 1 : r); 417 if (!fmri_has_potential(value, isfile, optional, exclude, 418 B_FALSE)) 419 return (0); 420 } 421 } 422 423 static int 424 eval_require_all(scf_iter_t *iter, char *value, size_t value_sz, int isfile) 425 { 426 return (eval_all(iter, value, value_sz, isfile, B_FALSE, B_FALSE)); 427 } 428 429 static int 430 eval_optional_all(scf_iter_t *iter, char *value, size_t value_sz, int isfile) 431 { 432 return (eval_all(iter, value, value_sz, isfile, B_TRUE, B_FALSE)); 433 } 434 435 static int 436 eval_exclude_all(scf_iter_t *iter, char *value, size_t value_sz, int isfile) 437 { 438 return (eval_all(iter, value, value_sz, isfile, B_FALSE, B_TRUE)); 439 } 440 441 /* 442 * Examines the state and health of an instance's restarter and 443 * dependencies, and determines the impact of both on the instance's 444 * ability to be brought on line. A true return value indicates that 445 * instance appears to be a likely candidate for the online club. 446 * False indicates that there is no hope for the instance. 447 */ 448 int 449 has_potential(scf_instance_t *inst, int restarter_only) 450 { 451 scf_snapshot_t *snap; 452 scf_iter_t *iter, *viter = NULL; 453 scf_propertygroup_t *pg; 454 scf_property_t *prop; 455 scf_value_t *val; 456 char *type = NULL, *grouping = NULL; 457 char *value; 458 size_t value_sz; 459 int result = B_TRUE, r; 460 int isfile; 461 462 value_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 463 if ((iter = scf_iter_create(h)) == NULL || 464 (snap = scf_snapshot_create(h)) == NULL || 465 (pg = scf_pg_create(h)) == NULL || 466 (val = scf_value_create(h)) == NULL || 467 (prop = scf_property_create(h)) == NULL || 468 (value = malloc(value_sz)) == NULL) 469 scfdie(); 470 471 /* 472 * First we check our restarter as an implicit dependency. 473 */ 474 if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL, pg) != 0) 475 scfdie(); 476 477 r = get_astring_prop(pg, SCF_PROPERTY_RESTARTER, prop, val, value, 478 value_sz); 479 if (r == -ENOENT) { 480 (void) strlcpy(value, SCF_SERVICE_STARTD, value_sz); 481 } else if (r < 0 || r > max_scf_fmri_sz) { 482 /* 483 * Normally we would return true and let the restarter 484 * tell our caller there is a problem by changing the 485 * instance's state, but that's not going to happen if 486 * the restarter is invalid. 487 */ 488 result = B_FALSE; 489 goto out; 490 } 491 492 if (!fmri_has_potential(value, B_FALSE, B_FALSE, B_FALSE, B_TRUE)) { 493 result = B_FALSE; 494 goto out; 495 } 496 497 if (restarter_only) 498 goto out; 499 500 /* 501 * Now we check explicit dependencies. 502 */ 503 if (scf_instance_get_snapshot(inst, "running", snap) != 0) { 504 if (scf_error() != SCF_ERROR_NOT_FOUND) 505 scfdie(); 506 scf_snapshot_destroy(snap); 507 snap = NULL; 508 } 509 510 if (scf_iter_instance_pgs_typed_composed(iter, inst, snap, 511 SCF_GROUP_DEPENDENCY) != 0) { 512 if (scf_error() != SCF_ERROR_DELETED) 513 scfdie(); 514 goto out; 515 } 516 517 for (;;) { 518 r = scf_iter_next_pg(iter, pg); 519 if (r == 0) 520 break; 521 if (r == -1) { 522 if (scf_error() != SCF_ERROR_DELETED) 523 scfdie(); 524 goto out; 525 } 526 527 if ((grouping = read_astring_prop(pg, val, prop, 528 SCF_PROPERTY_GROUPING)) == NULL) 529 goto out; 530 531 if ((type = read_astring_prop(pg, val, prop, 532 SCF_PROPERTY_TYPE)) == NULL) 533 goto out; 534 535 if (strcmp(type, "path") == 0) { 536 isfile = B_TRUE; 537 } else if (strcmp(type, "service") == 0) { 538 isfile = B_FALSE; 539 } else { 540 free(type); 541 goto out; 542 } 543 free(type); 544 545 if ((viter = prop_walk_init(pg, SCF_PROPERTY_ENTITIES)) == NULL) 546 goto out; 547 548 if (strcmp(grouping, SCF_DEP_REQUIRE_ALL) == 0) { 549 r = eval_require_all(viter, value, value_sz, isfile); 550 } else if (strcmp(grouping, SCF_DEP_REQUIRE_ANY) == 0) { 551 r = eval_require_any(viter, value, value_sz, isfile); 552 } else if (strcmp(grouping, SCF_DEP_EXCLUDE_ALL) == 0) { 553 r = eval_exclude_all(viter, value, value_sz, isfile); 554 } else if (strcmp(grouping, SCF_DEP_OPTIONAL_ALL) == 0) { 555 r = eval_optional_all(viter, value, value_sz, isfile); 556 } else { 557 scf_iter_destroy(viter); 558 free(grouping); 559 grouping = NULL; 560 goto out; 561 } 562 563 scf_iter_destroy(viter); 564 free(grouping); 565 grouping = NULL; 566 567 if (r == 0) { 568 result = B_FALSE; 569 goto out; 570 } else if (r == -1) { 571 goto out; 572 } 573 } 574 575 out: 576 free(value); 577 scf_property_destroy(prop); 578 scf_value_destroy(val); 579 scf_pg_destroy(pg); 580 if (snap != NULL) 581 scf_snapshot_destroy(snap); 582 if (grouping != NULL) 583 free(grouping); 584 scf_iter_destroy(iter); 585 return (result); 586 } 587