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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2017 RackTop Systems. 26 * Copyright 2020 Joyent, Inc. 27 */ 28 29 /* 30 * Service state explanation. For select services, display a description, the 31 * state, and possibly why the service is in that state, what's causing it to 32 * be in that state, and what other services it is keeping offline (impact). 33 * 34 * Explaining states other than offline is easy. For maintenance and 35 * degraded, we just use the auxiliary state. For offline, we must determine 36 * which dependencies are unsatisfied and recurse. If a causal service is not 37 * offline, then a svcptr to it is added to the offline service's causes list. 38 * If a causal service is offline, then we recurse to determine its causes and 39 * merge them into the causes list of the service in question (see 40 * add_causes()). Note that by adding a self-pointing svcptr to the causes 41 * lists of services which are not offline or are offline for unknown reasons, 42 * we can always merge the unsatisfied dependency's causes into the 43 * dependent's list. 44 * 45 * Computing an impact list is more involved because the dependencies in the 46 * repository are unidirectional; it requires determining the causes of all 47 * offline services. For each unsatisfied dependency of an offline service, 48 * a svcptr to the dependent is added to the dependency's impact_dependents 49 * list (see add_causes()). determine_impact() uses the lists to build an 50 * impact list. The direct dependency is used so that a path from the 51 * affected service to the causal service can be constructed (see 52 * print_dependency_reasons()). 53 * 54 * Because we always need at least impact counts, we always run 55 * determine_causes() on all services. 56 * 57 * If no arguments are given, we must select the services which are causing 58 * other services to be offline. We do so by adding services which are not 59 * running for any reason other than another service to the g_causes list in 60 * determine_causes(). 61 * 62 * Since all services must be examined, and their states may be consulted 63 * a lot, it is important that we only read volatile data (like states) from 64 * the repository once. add_instance() reads data for an instance from the 65 * repository into an inst_t and puts it into the "services" cache, which is 66 * organized as a hash table of svc_t's, each of which has a list of inst_t's. 67 */ 68 69 #include "svcs.h" 70 71 #include <sys/stat.h> 72 #include <sys/wait.h> 73 74 #include <assert.h> 75 #include <errno.h> 76 #include <libintl.h> 77 #include <libuutil.h> 78 #include <libscf.h> 79 #include <libscf_priv.h> 80 #include <string.h> 81 #include <stdio.h> 82 #include <stdlib.h> 83 #include <time.h> 84 85 86 #define DC_DISABLED "SMF-8000-05" 87 #define DC_TEMPDISABLED "SMF-8000-1S" 88 #define DC_RSTRINVALID "SMF-8000-2A" 89 #define DC_RSTRABSENT "SMF-8000-3P" 90 #define DC_UNINIT "SMF-8000-4D" 91 #define DC_RSTRDEAD "SMF-8000-5H" 92 #define DC_ADMINMAINT "SMF-8000-63" 93 #define DC_SVCREQMAINT "SMF-8000-R4" 94 #define DC_REPTFAIL "SMF-8000-7Y" 95 #define DC_METHFAIL "SMF-8000-8Q" 96 #define DC_NONE "SMF-8000-9C" 97 #define DC_UNKNOWN "SMF-8000-AR" 98 #define DC_STARTING "SMF-8000-C4" 99 #define DC_ADMINDEGR "SMF-8000-DX" 100 #define DC_DEPABSENT "SMF-8000-E2" 101 #define DC_DEPRUNNING "SMF-8000-FJ" 102 #define DC_DEPOTHER "SMF-8000-GE" 103 #define DC_DEPCYCLE "SMF-8000-HP" 104 #define DC_INVALIDDEP "SMF-8000-JA" 105 #define DC_STARTFAIL "SMF-8000-KS" 106 #define DC_TOOQUICKLY "SMF-8000-L5" 107 #define DC_INVALIDSTATE "SMF-8000-N3" 108 #define DC_TRANSITION "SMF-8000-PH" 109 110 #define DEFAULT_MAN_PATH "/usr/share/man" 111 112 #define AUX_STATE_INVALID "invalid_aux_state" 113 114 #define uu_list_append(lst, e) uu_list_insert_before(lst, NULL, e) 115 116 #define bad_error(func, err) \ 117 uu_panic("%s:%d: %s() failed with unknown error %d.\n", \ 118 __FILE__, __LINE__, func, err); 119 120 typedef struct { 121 const char *svcname; 122 const char *instname; 123 124 /* restarter pg properties */ 125 char state[MAX_SCF_STATE_STRING_SZ]; 126 char next_state[MAX_SCF_STATE_STRING_SZ]; 127 struct timeval stime; 128 const char *aux_state; 129 const char *aux_fmri; 130 int64_t start_method_waitstatus; 131 132 uint8_t enabled; 133 int temporary; 134 const char *restarter; 135 uu_list_t *dependencies; /* list of dependency_group's */ 136 137 char comment[SCF_COMMENT_MAX_LENGTH]; 138 139 int active; /* In use? (cycle detection) */ 140 int restarter_bad; 141 const char *summary; 142 uu_list_t *baddeps; /* list of dependency's */ 143 uu_list_t *causes; /* list of svcptrs */ 144 uu_list_t *impact_dependents; /* list of svcptrs */ 145 uu_list_t *impact; /* list of svcptrs */ 146 147 uu_list_node_t node; 148 } inst_t; 149 150 typedef struct service { 151 const char *svcname; 152 uu_list_t *instances; 153 struct service *next; 154 } svc_t; 155 156 struct svcptr { 157 inst_t *svcp; 158 inst_t *next_hop; 159 uu_list_node_t node; 160 }; 161 162 struct dependency_group { 163 enum { DGG_REQALL, DGG_REQANY, DGG_OPTALL, DGG_EXCALL } grouping; 164 const char *type; 165 uu_list_t *entities; /* List of struct dependency's */ 166 uu_list_node_t node; 167 }; 168 169 struct dependency { 170 const char *fmri; 171 uu_list_node_t node; 172 }; 173 174 /* Hash table of service names -> svc_t's */ 175 #define SVC_HASH_NBUCKETS 256 176 #define SVC_HASH_MASK (SVC_HASH_NBUCKETS - 1) 177 178 static svc_t **services; 179 180 static uu_list_pool_t *insts, *svcptrs, *depgroups, *deps; 181 static uu_list_t *g_causes; /* list of svcptrs */ 182 183 static scf_scope_t *g_local_scope; 184 static scf_service_t *g_svc; 185 static scf_instance_t *g_inst; 186 static scf_snapshot_t *g_snap; 187 static scf_propertygroup_t *g_pg; 188 static scf_property_t *g_prop; 189 static scf_value_t *g_val; 190 static scf_iter_t *g_iter, *g_viter; 191 static char *g_fmri, *g_value; 192 static size_t g_fmri_sz, g_value_sz; 193 static const char *g_msgbase = "http://illumos.org/msg/"; 194 195 static char *emsg_nomem; 196 static char *emsg_invalid_dep; 197 198 extern scf_handle_t *h; 199 extern char *g_zonename; 200 201 /* ARGSUSED */ 202 static int 203 svcptr_compare(struct svcptr *a, struct svcptr *b, void *data) 204 { 205 return (b->svcp - a->svcp); 206 } 207 208 static uint32_t 209 hash_name(const char *name) 210 { 211 uint32_t h = 0, g; 212 const char *p; 213 214 for (p = name; *p != '\0'; ++p) { 215 h = (h << 4) + *p; 216 if ((g = (h & 0xf0000000)) != 0) { 217 h ^= (g >> 24); 218 h ^= g; 219 } 220 } 221 222 return (h); 223 } 224 225 static void 226 x_init(void) 227 { 228 emsg_nomem = gettext("Out of memory.\n"); 229 emsg_invalid_dep = 230 gettext("svc:/%s:%s has invalid dependency \"%s\".\n"); 231 232 services = calloc(SVC_HASH_NBUCKETS, sizeof (*services)); 233 if (services == NULL) 234 uu_die(emsg_nomem); 235 236 insts = uu_list_pool_create("insts", sizeof (inst_t), 237 offsetof(inst_t, node), NULL, UU_LIST_POOL_DEBUG); 238 svcptrs = uu_list_pool_create("svcptrs", sizeof (struct svcptr), 239 offsetof(struct svcptr, node), (uu_compare_fn_t *)svcptr_compare, 240 UU_LIST_POOL_DEBUG); 241 depgroups = uu_list_pool_create("depgroups", 242 sizeof (struct dependency_group), 243 offsetof(struct dependency_group, node), NULL, UU_LIST_POOL_DEBUG); 244 deps = uu_list_pool_create("deps", sizeof (struct dependency), 245 offsetof(struct dependency, node), NULL, UU_LIST_POOL_DEBUG); 246 g_causes = uu_list_create(svcptrs, NULL, UU_LIST_DEBUG); 247 if (insts == NULL || svcptrs == NULL || depgroups == NULL || 248 deps == NULL || g_causes == NULL) 249 uu_die(emsg_nomem); 250 251 if ((g_local_scope = scf_scope_create(h)) == NULL || 252 (g_svc = scf_service_create(h)) == NULL || 253 (g_inst = scf_instance_create(h)) == NULL || 254 (g_snap = scf_snapshot_create(h)) == NULL || 255 (g_pg = scf_pg_create(h)) == NULL || 256 (g_prop = scf_property_create(h)) == NULL || 257 (g_val = scf_value_create(h)) == NULL || 258 (g_iter = scf_iter_create(h)) == NULL || 259 (g_viter = scf_iter_create(h)) == NULL) 260 scfdie(); 261 262 if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, g_local_scope) != 0) 263 scfdie(); 264 265 g_fmri_sz = max_scf_fmri_length + 1; 266 g_fmri = safe_malloc(g_fmri_sz); 267 268 g_value_sz = max_scf_value_length + 1; 269 g_value = safe_malloc(g_value_sz); 270 } 271 272 /* 273 * Repository loading routines. 274 */ 275 276 /* 277 * Returns 278 * 0 - success 279 * ECANCELED - inst was deleted 280 * EINVAL - inst is invalid 281 */ 282 static int 283 load_dependencies(inst_t *svcp, scf_instance_t *inst) 284 { 285 scf_snapshot_t *snap; 286 struct dependency_group *dg; 287 struct dependency *d; 288 int r; 289 290 assert(svcp->dependencies == NULL); 291 svcp->dependencies = uu_list_create(depgroups, svcp, UU_LIST_DEBUG); 292 if (svcp->dependencies == NULL) 293 uu_die(emsg_nomem); 294 295 if (scf_instance_get_snapshot(inst, "running", g_snap) == 0) { 296 snap = g_snap; 297 } else { 298 if (scf_error() != SCF_ERROR_NOT_FOUND) 299 scfdie(); 300 301 snap = NULL; 302 } 303 304 if (scf_iter_instance_pgs_typed_composed(g_iter, inst, snap, 305 SCF_GROUP_DEPENDENCY) != 0) { 306 if (scf_error() != SCF_ERROR_DELETED) 307 scfdie(); 308 return (ECANCELED); 309 } 310 311 for (;;) { 312 r = scf_iter_next_pg(g_iter, g_pg); 313 if (r == 0) 314 break; 315 if (r != 1) { 316 if (scf_error() != SCF_ERROR_DELETED) 317 scfdie(); 318 return (ECANCELED); 319 } 320 321 dg = safe_malloc(sizeof (*dg)); 322 (void) memset(dg, 0, sizeof (*dg)); 323 dg->entities = uu_list_create(deps, dg, UU_LIST_DEBUG); 324 if (dg->entities == NULL) 325 uu_die(emsg_nomem); 326 327 if (pg_get_single_val(g_pg, SCF_PROPERTY_GROUPING, 328 SCF_TYPE_ASTRING, g_value, g_value_sz, 0) != 0) 329 return (EINVAL); 330 331 if (strcmp(g_value, "require_all") == 0) 332 dg->grouping = DGG_REQALL; 333 else if (strcmp(g_value, "require_any") == 0) 334 dg->grouping = DGG_REQANY; 335 else if (strcmp(g_value, "optional_all") == 0) 336 dg->grouping = DGG_OPTALL; 337 else if (strcmp(g_value, "exclude_all") == 0) 338 dg->grouping = DGG_EXCALL; 339 else { 340 (void) fprintf(stderr, gettext("svc:/%s:%s has " 341 "dependency with unknown type \"%s\".\n"), 342 svcp->svcname, svcp->instname, g_value); 343 return (EINVAL); 344 } 345 346 if (pg_get_single_val(g_pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING, 347 g_value, g_value_sz, 0) != 0) 348 return (EINVAL); 349 dg->type = safe_strdup(g_value); 350 351 if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) != 352 0) { 353 switch (scf_error()) { 354 case SCF_ERROR_NOT_FOUND: 355 (void) fprintf(stderr, gettext("svc:/%s:%s has " 356 "dependency without an entities " 357 "property.\n"), svcp->svcname, 358 svcp->instname); 359 return (EINVAL); 360 361 case SCF_ERROR_DELETED: 362 return (ECANCELED); 363 364 default: 365 scfdie(); 366 } 367 } 368 369 if (scf_iter_property_values(g_viter, g_prop) != 0) { 370 if (scf_error() != SCF_ERROR_DELETED) 371 scfdie(); 372 return (ECANCELED); 373 } 374 375 for (;;) { 376 r = scf_iter_next_value(g_viter, g_val); 377 if (r == 0) 378 break; 379 if (r != 1) { 380 if (scf_error() != SCF_ERROR_DELETED) 381 scfdie(); 382 return (ECANCELED); 383 } 384 385 d = safe_malloc(sizeof (*d)); 386 d->fmri = safe_malloc(max_scf_fmri_length + 1); 387 388 if (scf_value_get_astring(g_val, (char *)d->fmri, 389 max_scf_fmri_length + 1) < 0) 390 scfdie(); 391 392 uu_list_node_init(d, &d->node, deps); 393 (void) uu_list_append(dg->entities, d); 394 } 395 396 uu_list_node_init(dg, &dg->node, depgroups); 397 r = uu_list_append(svcp->dependencies, dg); 398 assert(r == 0); 399 } 400 401 return (0); 402 } 403 404 static void 405 add_instance(const char *svcname, const char *instname, scf_instance_t *inst) 406 { 407 inst_t *instp; 408 svc_t *svcp; 409 int ovr_set = 0; 410 uint8_t i; 411 uint32_t h; 412 int r; 413 414 h = hash_name(svcname) & SVC_HASH_MASK; 415 for (svcp = services[h]; svcp != NULL; svcp = svcp->next) { 416 if (strcmp(svcp->svcname, svcname) == 0) 417 break; 418 } 419 420 if (svcp == NULL) { 421 svcp = safe_malloc(sizeof (*svcp)); 422 svcp->svcname = safe_strdup(svcname); 423 svcp->instances = uu_list_create(insts, svcp, UU_LIST_DEBUG); 424 if (svcp->instances == NULL) 425 uu_die(emsg_nomem); 426 svcp->next = services[h]; 427 services[h] = svcp; 428 } 429 430 instp = safe_malloc(sizeof (*instp)); 431 (void) memset(instp, 0, sizeof (*instp)); 432 instp->svcname = svcp->svcname; 433 instp->instname = safe_strdup(instname); 434 instp->impact_dependents = 435 uu_list_create(svcptrs, instp, UU_LIST_DEBUG); 436 if (instp->impact_dependents == NULL) 437 uu_die(emsg_nomem); 438 439 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) != 0) { 440 switch (scf_error()) { 441 case SCF_ERROR_DELETED: 442 return; 443 444 case SCF_ERROR_NOT_FOUND: 445 (void) fprintf(stderr, gettext("svc:/%s:%s has no " 446 "\"%s\" property group; ignoring.\n"), 447 instp->svcname, instp->instname, SCF_PG_RESTARTER); 448 return; 449 450 default: 451 scfdie(); 452 } 453 } 454 455 if (pg_get_single_val(g_pg, SCF_PROPERTY_STATE, SCF_TYPE_ASTRING, 456 (void *)instp->state, sizeof (instp->state), 0) != 0) 457 return; 458 459 if (pg_get_single_val(g_pg, SCF_PROPERTY_NEXT_STATE, SCF_TYPE_ASTRING, 460 (void *)instp->next_state, sizeof (instp->next_state), 0) != 0) 461 return; 462 463 if (pg_get_single_val(g_pg, SCF_PROPERTY_STATE_TIMESTAMP, 464 SCF_TYPE_TIME, &instp->stime, 0, 0) != 0) 465 return; 466 467 /* restarter may not set aux_state, allow to continue in that case */ 468 if (pg_get_single_val(g_pg, SCF_PROPERTY_AUX_STATE, SCF_TYPE_ASTRING, 469 g_fmri, g_fmri_sz, 0) == 0) 470 instp->aux_state = safe_strdup(g_fmri); 471 else 472 instp->aux_state = safe_strdup(AUX_STATE_INVALID); 473 474 (void) pg_get_single_val(g_pg, SCF_PROPERTY_START_METHOD_WAITSTATUS, 475 SCF_TYPE_INTEGER, &instp->start_method_waitstatus, 0, 0); 476 477 /* Get the optional auxiliary_fmri */ 478 if (pg_get_single_val(g_pg, SCF_PROPERTY_AUX_FMRI, SCF_TYPE_ASTRING, 479 g_fmri, g_fmri_sz, 0) == 0) 480 instp->aux_fmri = safe_strdup(g_fmri); 481 482 if (scf_instance_get_pg(inst, SCF_PG_GENERAL_OVR, g_pg) == 0) { 483 if (pg_get_single_val(g_pg, SCF_PROPERTY_ENABLED, 484 SCF_TYPE_BOOLEAN, &instp->enabled, 0, 0) == 0) 485 ovr_set = 1; 486 (void) pg_get_single_val(g_pg, SCF_PROPERTY_COMMENT, 487 SCF_TYPE_ASTRING, instp->comment, 488 sizeof (instp->comment), EMPTY_OK); 489 } else { 490 switch (scf_error()) { 491 case SCF_ERROR_NOT_FOUND: 492 break; 493 494 case SCF_ERROR_DELETED: 495 return; 496 497 default: 498 scfdie(); 499 } 500 } 501 502 if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL, g_pg) != 503 0) { 504 switch (scf_error()) { 505 case SCF_ERROR_DELETED: 506 case SCF_ERROR_NOT_FOUND: 507 return; 508 509 default: 510 scfdie(); 511 } 512 } 513 514 if (pg_get_single_val(g_pg, SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 515 &i, 0, 0) != 0) 516 return; 517 518 if (ovr_set) { 519 instp->temporary = (instp->enabled != i); 520 } else { 521 instp->enabled = i; 522 instp->temporary = 0; 523 } 524 525 if (!instp->temporary) { 526 (void) pg_get_single_val(g_pg, SCF_PROPERTY_COMMENT, 527 SCF_TYPE_ASTRING, instp->comment, 528 sizeof (instp->comment), EMPTY_OK); 529 } 530 531 if (pg_get_single_val(g_pg, SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, 532 g_fmri, g_fmri_sz, 0) == 0) 533 instp->restarter = safe_strdup(g_fmri); 534 else 535 instp->restarter = SCF_SERVICE_STARTD; 536 537 if (strcmp(instp->state, SCF_STATE_STRING_OFFLINE) == 0 && 538 load_dependencies(instp, inst) != 0) 539 return; 540 541 uu_list_node_init(instp, &instp->node, insts); 542 r = uu_list_append(svcp->instances, instp); 543 assert(r == 0); 544 } 545 546 static void 547 load_services(void) 548 { 549 scf_iter_t *siter, *iiter; 550 int r; 551 char *svcname, *instname; 552 553 if ((siter = scf_iter_create(h)) == NULL || 554 (iiter = scf_iter_create(h)) == NULL) 555 scfdie(); 556 557 svcname = safe_malloc(max_scf_name_length + 1); 558 instname = safe_malloc(max_scf_name_length + 1); 559 560 if (scf_iter_scope_services(siter, g_local_scope) != 0) 561 scfdie(); 562 563 for (;;) { 564 r = scf_iter_next_service(siter, g_svc); 565 if (r == 0) 566 break; 567 if (r != 1) 568 scfdie(); 569 570 if (scf_service_get_name(g_svc, svcname, 571 max_scf_name_length + 1) < 0) { 572 if (scf_error() != SCF_ERROR_DELETED) 573 scfdie(); 574 continue; 575 } 576 577 if (scf_iter_service_instances(iiter, g_svc) != 0) { 578 if (scf_error() != SCF_ERROR_DELETED) 579 scfdie(); 580 continue; 581 } 582 583 for (;;) { 584 r = scf_iter_next_instance(iiter, g_inst); 585 if (r == 0) 586 break; 587 if (r != 1) { 588 if (scf_error() != SCF_ERROR_DELETED) 589 scfdie(); 590 break; 591 } 592 593 if (scf_instance_get_name(g_inst, instname, 594 max_scf_name_length + 1) < 0) { 595 if (scf_error() != SCF_ERROR_DELETED) 596 scfdie(); 597 continue; 598 } 599 600 add_instance(svcname, instname, g_inst); 601 } 602 } 603 604 free(svcname); 605 free(instname); 606 scf_iter_destroy(siter); 607 scf_iter_destroy(iiter); 608 } 609 610 /* 611 * Dependency analysis routines. 612 */ 613 614 static void 615 add_svcptr(uu_list_t *lst, inst_t *svcp) 616 { 617 struct svcptr *spp; 618 uu_list_index_t idx; 619 int r; 620 621 spp = safe_malloc(sizeof (*spp)); 622 spp->svcp = svcp; 623 spp->next_hop = NULL; 624 625 if (uu_list_find(lst, spp, NULL, &idx) != NULL) { 626 free(spp); 627 return; 628 } 629 630 uu_list_node_init(spp, &spp->node, svcptrs); 631 r = uu_list_append(lst, spp); 632 assert(r == 0); 633 } 634 635 static int determine_causes(inst_t *, void *); 636 637 /* 638 * Determine the causes of src and add them to the causes list of dst. 639 * Returns ELOOP if src is active, and 0 otherwise. 640 */ 641 static int 642 add_causes(inst_t *dst, inst_t *src) 643 { 644 struct svcptr *spp, *copy; 645 uu_list_index_t idx; 646 647 if (determine_causes(src, (void *)1) != UU_WALK_NEXT) { 648 /* Dependency cycle. */ 649 (void) fprintf(stderr, " svc:/%s:%s\n", dst->svcname, 650 dst->instname); 651 return (ELOOP); 652 } 653 654 add_svcptr(src->impact_dependents, dst); 655 656 for (spp = uu_list_first(src->causes); 657 spp != NULL; 658 spp = uu_list_next(src->causes, spp)) { 659 if (uu_list_find(dst->causes, spp, NULL, &idx) != NULL) 660 continue; 661 662 copy = safe_malloc(sizeof (*copy)); 663 copy->svcp = spp->svcp; 664 copy->next_hop = src; 665 uu_list_node_init(copy, ©->node, svcptrs); 666 uu_list_insert(dst->causes, copy, idx); 667 668 add_svcptr(g_causes, spp->svcp); 669 } 670 671 return (0); 672 } 673 674 static int 675 inst_running(inst_t *ip) 676 { 677 return (strcmp(ip->state, SCF_STATE_STRING_ONLINE) == 0 || 678 strcmp(ip->state, SCF_STATE_STRING_DEGRADED) == 0); 679 } 680 681 static int 682 inst_running_or_maint(inst_t *ip) 683 { 684 return (inst_running(ip) || 685 strcmp(ip->state, SCF_STATE_STRING_MAINT) == 0); 686 } 687 688 static svc_t * 689 get_svc(const char *sn) 690 { 691 uint32_t h; 692 svc_t *svcp; 693 694 h = hash_name(sn) & SVC_HASH_MASK; 695 696 for (svcp = services[h]; svcp != NULL; svcp = svcp->next) { 697 if (strcmp(svcp->svcname, sn) == 0) 698 break; 699 } 700 701 return (svcp); 702 } 703 704 /* ARGSUSED */ 705 static inst_t * 706 get_inst(svc_t *svcp, const char *in) 707 { 708 inst_t *instp; 709 710 for (instp = uu_list_first(svcp->instances); 711 instp != NULL; 712 instp = uu_list_next(svcp->instances, instp)) { 713 if (strcmp(instp->instname, in) == 0) 714 return (instp); 715 } 716 717 return (NULL); 718 } 719 720 static int 721 get_fmri(const char *fmri, svc_t **spp, inst_t **ipp) 722 { 723 const char *sn, *in; 724 svc_t *sp; 725 inst_t *ip; 726 727 if (strlcpy(g_fmri, fmri, g_fmri_sz) >= g_fmri_sz) 728 return (EINVAL); 729 730 if (scf_parse_svc_fmri(g_fmri, NULL, &sn, &in, NULL, NULL) != 0) 731 return (EINVAL); 732 733 if (sn == NULL) 734 return (EINVAL); 735 736 sp = get_svc(sn); 737 if (sp == NULL) 738 return (ENOENT); 739 740 if (in != NULL) { 741 ip = get_inst(sp, in); 742 if (ip == NULL) 743 return (ENOENT); 744 } 745 746 if (spp != NULL) 747 *spp = sp; 748 if (ipp != NULL) 749 *ipp = ((in == NULL) ? NULL : ip); 750 751 return (0); 752 } 753 754 static int 755 process_reqall(inst_t *svcp, struct dependency_group *dg) 756 { 757 uu_list_walk_t *walk; 758 struct dependency *d; 759 int r, svcrunning; 760 svc_t *sp; 761 inst_t *ip; 762 763 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST); 764 if (walk == NULL) 765 uu_die(emsg_nomem); 766 767 while ((d = uu_list_walk_next(walk)) != NULL) { 768 r = get_fmri(d->fmri, &sp, &ip); 769 switch (r) { 770 case EINVAL: 771 /* LINTED */ 772 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 773 svcp->instname, d->fmri); 774 continue; 775 776 case ENOENT: 777 uu_list_remove(dg->entities, d); 778 r = uu_list_append(svcp->baddeps, d); 779 assert(r == 0); 780 continue; 781 782 case 0: 783 break; 784 785 default: 786 bad_error("get_fmri", r); 787 } 788 789 if (ip != NULL) { 790 if (inst_running(ip)) 791 continue; 792 r = add_causes(svcp, ip); 793 if (r != 0) { 794 assert(r == ELOOP); 795 return (r); 796 } 797 continue; 798 } 799 800 svcrunning = 0; 801 802 for (ip = uu_list_first(sp->instances); 803 ip != NULL; 804 ip = uu_list_next(sp->instances, ip)) { 805 if (inst_running(ip)) 806 svcrunning = 1; 807 } 808 809 if (!svcrunning) { 810 for (ip = uu_list_first(sp->instances); 811 ip != NULL; 812 ip = uu_list_next(sp->instances, ip)) { 813 r = add_causes(svcp, ip); 814 if (r != 0) { 815 assert(r == ELOOP); 816 uu_list_walk_end(walk); 817 return (r); 818 } 819 } 820 } 821 } 822 823 uu_list_walk_end(walk); 824 return (0); 825 } 826 827 static int 828 process_reqany(inst_t *svcp, struct dependency_group *dg) 829 { 830 svc_t *sp; 831 inst_t *ip; 832 struct dependency *d; 833 int r; 834 uu_list_walk_t *walk; 835 836 for (d = uu_list_first(dg->entities); 837 d != NULL; 838 d = uu_list_next(dg->entities, d)) { 839 r = get_fmri(d->fmri, &sp, &ip); 840 switch (r) { 841 case 0: 842 break; 843 844 case EINVAL: 845 /* LINTED */ 846 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 847 svcp->instname, d->fmri); 848 continue; 849 850 case ENOENT: 851 continue; 852 853 default: 854 bad_error("eval_svc_dep", r); 855 } 856 857 if (ip != NULL) { 858 if (inst_running(ip)) 859 return (0); 860 continue; 861 } 862 863 for (ip = uu_list_first(sp->instances); 864 ip != NULL; 865 ip = uu_list_next(sp->instances, ip)) { 866 if (inst_running(ip)) 867 return (0); 868 } 869 } 870 871 /* 872 * The dependency group is not satisfied. Add all unsatisfied members 873 * to the cause list. 874 */ 875 876 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST); 877 if (walk == NULL) 878 uu_die(emsg_nomem); 879 880 while ((d = uu_list_walk_next(walk)) != NULL) { 881 r = get_fmri(d->fmri, &sp, &ip); 882 switch (r) { 883 case 0: 884 break; 885 886 case ENOENT: 887 uu_list_remove(dg->entities, d); 888 r = uu_list_append(svcp->baddeps, d); 889 assert(r == 0); 890 continue; 891 892 case EINVAL: 893 /* Should have caught above. */ 894 default: 895 bad_error("eval_svc_dep", r); 896 } 897 898 if (ip != NULL) { 899 if (inst_running(ip)) 900 continue; 901 r = add_causes(svcp, ip); 902 if (r != 0) { 903 assert(r == ELOOP); 904 return (r); 905 } 906 continue; 907 } 908 909 for (ip = uu_list_first(sp->instances); 910 ip != NULL; 911 ip = uu_list_next(sp->instances, ip)) { 912 if (inst_running(ip)) 913 continue; 914 r = add_causes(svcp, ip); 915 if (r != 0) { 916 assert(r == ELOOP); 917 return (r); 918 } 919 } 920 } 921 922 return (0); 923 } 924 925 static int 926 process_optall(inst_t *svcp, struct dependency_group *dg) 927 { 928 uu_list_walk_t *walk; 929 struct dependency *d; 930 int r; 931 inst_t *ip; 932 svc_t *sp; 933 934 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST); 935 if (walk == NULL) 936 uu_die(emsg_nomem); 937 938 while ((d = uu_list_walk_next(walk)) != NULL) { 939 r = get_fmri(d->fmri, &sp, &ip); 940 941 switch (r) { 942 case 0: 943 break; 944 945 case EINVAL: 946 /* LINTED */ 947 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 948 svcp->instname, d->fmri); 949 continue; 950 951 case ENOENT: 952 continue; 953 954 default: 955 bad_error("get_fmri", r); 956 } 957 958 if (ip != NULL) { 959 if ((ip->enabled != 0) && !inst_running_or_maint(ip)) { 960 r = add_causes(svcp, ip); 961 if (r != 0) { 962 assert(r == ELOOP); 963 uu_list_walk_end(walk); 964 return (r); 965 } 966 } 967 continue; 968 } 969 970 for (ip = uu_list_first(sp->instances); 971 ip != NULL; 972 ip = uu_list_next(sp->instances, ip)) { 973 if ((ip->enabled != 0) && !inst_running_or_maint(ip)) { 974 r = add_causes(svcp, ip); 975 if (r != 0) { 976 assert(r == ELOOP); 977 uu_list_walk_end(walk); 978 return (r); 979 } 980 } 981 } 982 } 983 984 uu_list_walk_end(walk); 985 return (0); 986 } 987 988 static int 989 process_excall(inst_t *svcp, struct dependency_group *dg) 990 { 991 struct dependency *d; 992 int r; 993 svc_t *sp; 994 inst_t *ip; 995 996 for (d = uu_list_first(dg->entities); 997 d != NULL; 998 d = uu_list_next(dg->entities, d)) { 999 r = get_fmri(d->fmri, &sp, &ip); 1000 1001 switch (r) { 1002 case 0: 1003 break; 1004 1005 case EINVAL: 1006 /* LINTED */ 1007 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 1008 svcp->instname, d->fmri); 1009 continue; 1010 1011 case ENOENT: 1012 continue; 1013 1014 default: 1015 bad_error("eval_svc_dep", r); 1016 } 1017 1018 if (ip != NULL) { 1019 if (inst_running(ip)) { 1020 r = add_causes(svcp, ip); 1021 if (r != 0) { 1022 assert(r == ELOOP); 1023 return (r); 1024 } 1025 } 1026 continue; 1027 } 1028 1029 for (ip = uu_list_first(sp->instances); 1030 ip != NULL; 1031 ip = uu_list_next(sp->instances, ip)) { 1032 if (inst_running(ip)) { 1033 r = add_causes(svcp, ip); 1034 if (r != 0) { 1035 assert(r == ELOOP); 1036 return (r); 1037 } 1038 } 1039 } 1040 } 1041 1042 return (0); 1043 } 1044 1045 static int 1046 process_svc_dg(inst_t *svcp, struct dependency_group *dg) 1047 { 1048 switch (dg->grouping) { 1049 case DGG_REQALL: 1050 return (process_reqall(svcp, dg)); 1051 1052 case DGG_REQANY: 1053 return (process_reqany(svcp, dg)); 1054 1055 case DGG_OPTALL: 1056 return (process_optall(svcp, dg)); 1057 1058 case DGG_EXCALL: 1059 return (process_excall(svcp, dg)); 1060 1061 default: 1062 #ifndef NDEBUG 1063 (void) fprintf(stderr, 1064 "%s:%d: Unknown dependency grouping %d.\n", __FILE__, 1065 __LINE__, dg->grouping); 1066 #endif 1067 abort(); 1068 /* NOTREACHED */ 1069 } 1070 } 1071 1072 /* 1073 * Returns 1074 * EINVAL - fmri is not a valid FMRI 1075 * 0 - the file indicated by fmri is missing 1076 * 1 - the file indicated by fmri is present 1077 */ 1078 static int 1079 eval_file_dep(const char *fmri) 1080 { 1081 const char *path; 1082 struct stat st; 1083 1084 if (strncmp(fmri, "file:", sizeof ("file:") - 1) != 0) 1085 return (EINVAL); 1086 1087 path = fmri + (sizeof ("file:") - 1); 1088 1089 if (path[0] != '/') 1090 return (EINVAL); 1091 1092 if (path[1] == '/') { 1093 path += 2; 1094 if (strncmp(path, "localhost/", sizeof ("localhost/") - 1) == 0) 1095 path += sizeof ("localhost") - 1; 1096 else if (path[0] != '/') 1097 return (EINVAL); 1098 } 1099 1100 return (stat(path, &st) == 0 ? 1 : 0); 1101 } 1102 1103 static void 1104 process_file_dg(inst_t *svcp, struct dependency_group *dg) 1105 { 1106 uu_list_walk_t *walk; 1107 struct dependency *d, **deps; 1108 int r, i = 0, any_satisfied = 0; 1109 1110 if (dg->grouping == DGG_REQANY) { 1111 deps = calloc(uu_list_numnodes(dg->entities), sizeof (*deps)); 1112 if (deps == NULL) 1113 uu_die(emsg_nomem); 1114 } 1115 1116 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST); 1117 if (walk == NULL) 1118 uu_die(emsg_nomem); 1119 1120 while ((d = uu_list_walk_next(walk)) != NULL) { 1121 r = eval_file_dep(d->fmri); 1122 if (r == EINVAL) { 1123 /* LINTED */ 1124 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 1125 svcp->instname, d->fmri); 1126 continue; 1127 } 1128 1129 assert(r == 0 || r == 1); 1130 1131 switch (dg->grouping) { 1132 case DGG_REQALL: 1133 case DGG_OPTALL: 1134 if (r == 0) { 1135 uu_list_remove(dg->entities, d); 1136 r = uu_list_append(svcp->baddeps, d); 1137 assert(r == 0); 1138 } 1139 break; 1140 1141 case DGG_REQANY: 1142 if (r == 1) 1143 any_satisfied = 1; 1144 else 1145 deps[i++] = d; 1146 break; 1147 1148 case DGG_EXCALL: 1149 if (r == 1) { 1150 uu_list_remove(dg->entities, d); 1151 r = uu_list_append(svcp->baddeps, d); 1152 assert(r == 0); 1153 } 1154 break; 1155 1156 default: 1157 #ifndef NDEBUG 1158 (void) fprintf(stderr, "%s:%d: Unknown grouping %d.\n", 1159 __FILE__, __LINE__, dg->grouping); 1160 #endif 1161 abort(); 1162 } 1163 } 1164 1165 uu_list_walk_end(walk); 1166 1167 if (dg->grouping != DGG_REQANY) 1168 return; 1169 1170 if (!any_satisfied) { 1171 while (--i >= 0) { 1172 uu_list_remove(dg->entities, deps[i]); 1173 r = uu_list_append(svcp->baddeps, deps[i]); 1174 assert(r == 0); 1175 } 1176 } 1177 1178 free(deps); 1179 } 1180 1181 /* 1182 * Populate the causes list of svcp. This function should not return with 1183 * causes empty. 1184 */ 1185 static int 1186 determine_causes(inst_t *svcp, void *canfailp) 1187 { 1188 struct dependency_group *dg; 1189 int r; 1190 1191 if (svcp->active) { 1192 (void) fprintf(stderr, gettext("Dependency cycle detected:\n" 1193 " svc:/%s:%s\n"), svcp->svcname, svcp->instname); 1194 return ((int)canfailp != 0 ? UU_WALK_ERROR : UU_WALK_NEXT); 1195 } 1196 1197 if (svcp->causes != NULL) 1198 return (UU_WALK_NEXT); 1199 1200 svcp->causes = uu_list_create(svcptrs, svcp, UU_LIST_DEBUG); 1201 svcp->baddeps = uu_list_create(deps, svcp, UU_LIST_DEBUG); 1202 if (svcp->causes == NULL || svcp->baddeps == NULL) 1203 uu_die(emsg_nomem); 1204 1205 if (inst_running(svcp) || 1206 strcmp(svcp->state, SCF_STATE_STRING_UNINIT) == 0) { 1207 /* 1208 * If we're running, add a self-pointer in case we're 1209 * excluding another service. 1210 */ 1211 add_svcptr(svcp->causes, svcp); 1212 if (strcmp(svcp->state, SCF_STATE_STRING_DEGRADED) == 0) 1213 add_svcptr(g_causes, svcp); 1214 1215 return (UU_WALK_NEXT); 1216 } 1217 1218 if (strcmp(svcp->state, SCF_STATE_STRING_MAINT) == 0) { 1219 add_svcptr(svcp->causes, svcp); 1220 add_svcptr(g_causes, svcp); 1221 return (UU_WALK_NEXT); 1222 } 1223 1224 if (strcmp(svcp->state, SCF_STATE_STRING_DISABLED) == 0) { 1225 add_svcptr(svcp->causes, svcp); 1226 if (svcp->enabled != 0) 1227 add_svcptr(g_causes, svcp); 1228 1229 return (UU_WALK_NEXT); 1230 } 1231 1232 if (strcmp(svcp->state, SCF_STATE_STRING_OFFLINE) != 0) { 1233 (void) fprintf(stderr, 1234 gettext("svc:/%s:%s has invalid state \"%s\".\n"), 1235 svcp->svcname, svcp->instname, svcp->state); 1236 add_svcptr(svcp->causes, svcp); 1237 add_svcptr(g_causes, svcp); 1238 return (UU_WALK_NEXT); 1239 } 1240 1241 if (strcmp(svcp->next_state, SCF_STATE_STRING_NONE) != 0) { 1242 add_svcptr(svcp->causes, svcp); 1243 add_svcptr(g_causes, svcp); 1244 return (UU_WALK_NEXT); 1245 } 1246 1247 svcp->active = 1; 1248 1249 /* 1250 * Dependency analysis can add elements to our baddeps list (absent 1251 * dependency, unsatisfied file dependency), or to our cause list 1252 * (unsatisfied dependency). 1253 */ 1254 for (dg = uu_list_first(svcp->dependencies); 1255 dg != NULL; 1256 dg = uu_list_next(svcp->dependencies, dg)) { 1257 if (strcmp(dg->type, "path") == 0) { 1258 process_file_dg(svcp, dg); 1259 } else if (strcmp(dg->type, "service") == 0) { 1260 int r; 1261 1262 r = process_svc_dg(svcp, dg); 1263 if (r != 0) { 1264 assert(r == ELOOP); 1265 svcp->active = 0; 1266 return ((int)canfailp != 0 ? 1267 UU_WALK_ERROR : UU_WALK_NEXT); 1268 } 1269 } else { 1270 (void) fprintf(stderr, gettext("svc:/%s:%s has " 1271 "dependency group with invalid type \"%s\".\n"), 1272 svcp->svcname, svcp->instname, dg->type); 1273 } 1274 } 1275 1276 if (uu_list_numnodes(svcp->causes) == 0) { 1277 if (uu_list_numnodes(svcp->baddeps) > 0) { 1278 add_svcptr(g_causes, svcp); 1279 add_svcptr(svcp->causes, svcp); 1280 } else { 1281 inst_t *restarter; 1282 1283 r = get_fmri(svcp->restarter, NULL, &restarter); 1284 if (r == 0 && !inst_running(restarter)) { 1285 r = add_causes(svcp, restarter); 1286 if (r != 0) { 1287 assert(r == ELOOP); 1288 svcp->active = 0; 1289 return ((int)canfailp != 0 ? 1290 UU_WALK_ERROR : UU_WALK_NEXT); 1291 } 1292 } else { 1293 svcp->restarter_bad = r; 1294 add_svcptr(svcp->causes, svcp); 1295 add_svcptr(g_causes, svcp); 1296 } 1297 } 1298 } 1299 1300 assert(uu_list_numnodes(svcp->causes) > 0); 1301 1302 svcp->active = 0; 1303 return (UU_WALK_NEXT); 1304 } 1305 1306 static void 1307 determine_all_causes(void) 1308 { 1309 svc_t *svcp; 1310 int i; 1311 1312 for (i = 0; i < SVC_HASH_NBUCKETS; ++i) { 1313 for (svcp = services[i]; svcp != NULL; svcp = svcp->next) 1314 (void) uu_list_walk(svcp->instances, 1315 (uu_walk_fn_t *)determine_causes, 0, 0); 1316 } 1317 } 1318 1319 /* 1320 * Returns 1321 * 0 - success 1322 * ELOOP - dependency cycle detected 1323 */ 1324 static int 1325 determine_impact(inst_t *ip) 1326 { 1327 struct svcptr *idsp, *spp, *copy; 1328 uu_list_index_t idx; 1329 1330 if (ip->active) { 1331 (void) fprintf(stderr, gettext("Dependency cycle detected:\n" 1332 " svc:/%s:%s\n"), ip->svcname, ip->instname); 1333 return (ELOOP); 1334 } 1335 1336 if (ip->impact != NULL) 1337 return (0); 1338 1339 ip->impact = uu_list_create(svcptrs, ip, UU_LIST_DEBUG); 1340 if (ip->impact == NULL) 1341 uu_die(emsg_nomem); 1342 ip->active = 1; 1343 1344 for (idsp = uu_list_first(ip->impact_dependents); 1345 idsp != NULL; 1346 idsp = uu_list_next(ip->impact_dependents, idsp)) { 1347 if (determine_impact(idsp->svcp) != 0) { 1348 (void) fprintf(stderr, " svc:/%s:%s\n", 1349 ip->svcname, ip->instname); 1350 return (ELOOP); 1351 } 1352 1353 add_svcptr(ip->impact, idsp->svcp); 1354 1355 for (spp = uu_list_first(idsp->svcp->impact); 1356 spp != NULL; 1357 spp = uu_list_next(idsp->svcp->impact, spp)) { 1358 if (uu_list_find(ip->impact, spp, NULL, &idx) != NULL) 1359 continue; 1360 1361 copy = safe_malloc(sizeof (*copy)); 1362 copy->svcp = spp->svcp; 1363 copy->next_hop = NULL; 1364 uu_list_node_init(copy, ©->node, svcptrs); 1365 uu_list_insert(ip->impact, copy, idx); 1366 } 1367 } 1368 1369 ip->active = 0; 1370 return (0); 1371 } 1372 1373 /* 1374 * Printing routines. 1375 */ 1376 1377 static void 1378 check_msgbase(void) 1379 { 1380 if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL, g_inst, 1381 NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0) { 1382 if (scf_error() != SCF_ERROR_NOT_FOUND) 1383 scfdie(); 1384 1385 return; 1386 } 1387 1388 if (scf_instance_get_pg_composed(g_inst, NULL, "msg", g_pg) != 0) { 1389 switch (scf_error()) { 1390 case SCF_ERROR_NOT_FOUND: 1391 case SCF_ERROR_DELETED: 1392 return; 1393 1394 default: 1395 scfdie(); 1396 } 1397 } 1398 1399 if (scf_pg_get_property(g_pg, "base", g_prop) != 0) { 1400 switch (scf_error()) { 1401 case SCF_ERROR_NOT_FOUND: 1402 case SCF_ERROR_DELETED: 1403 return; 1404 1405 default: 1406 scfdie(); 1407 } 1408 } 1409 1410 if (scf_property_get_value(g_prop, g_val) != 0) { 1411 switch (scf_error()) { 1412 case SCF_ERROR_NOT_FOUND: 1413 case SCF_ERROR_CONSTRAINT_VIOLATED: 1414 case SCF_ERROR_PERMISSION_DENIED: 1415 g_msgbase = NULL; 1416 return; 1417 1418 case SCF_ERROR_DELETED: 1419 return; 1420 1421 default: 1422 scfdie(); 1423 } 1424 } 1425 1426 if (scf_value_get_astring(g_val, g_value, g_value_sz) < 0) { 1427 if (scf_error() != SCF_ERROR_TYPE_MISMATCH) 1428 scfdie(); 1429 return; 1430 } 1431 1432 g_msgbase = safe_strdup(g_value); 1433 } 1434 1435 static void 1436 determine_summary(inst_t *ip) 1437 { 1438 if (ip->summary != NULL) 1439 return; 1440 1441 if (inst_running(ip)) { 1442 ip->summary = gettext("is running."); 1443 return; 1444 } 1445 1446 if (strcmp(ip->state, SCF_STATE_STRING_UNINIT) == 0) { 1447 ip->summary = gettext("is uninitialized."); 1448 } else if (strcmp(ip->state, SCF_STATE_STRING_DISABLED) == 0) { 1449 if (!ip->temporary) 1450 ip->summary = gettext("is disabled."); 1451 else 1452 ip->summary = gettext("is temporarily disabled."); 1453 } else if (strcmp(ip->state, SCF_STATE_STRING_OFFLINE) == 0) { 1454 if (uu_list_numnodes(ip->baddeps) != 0) 1455 ip->summary = gettext("has missing dependencies."); 1456 else if (strcmp(ip->next_state, SCF_STATE_STRING_ONLINE) == 0) 1457 ip->summary = gettext("is starting."); 1458 else 1459 ip->summary = gettext("is offline."); 1460 } else if (strcmp(ip->state, SCF_STATE_STRING_MAINT) == 0) { 1461 if (strcmp(ip->aux_state, "administrative_request") == 0) { 1462 ip->summary = gettext("was taken down for maintenace " 1463 "by an administrator."); 1464 } else if (strcmp(ip->aux_state, "dependency_cycle") == 0) { 1465 ip->summary = gettext("completed a dependency cycle."); 1466 } else if (strcmp(ip->aux_state, "fault_threshold_reached") == 1467 0) { 1468 ip->summary = gettext("is not running because " 1469 "a method failed repeatedly."); 1470 } else if (strcmp(ip->aux_state, "invalid_dependency") == 0) { 1471 ip->summary = gettext("has an invalid dependency."); 1472 } else if (strcmp(ip->aux_state, "invalid_restarter") == 0) { 1473 ip->summary = gettext("has an invalid restarter."); 1474 } else if (strcmp(ip->aux_state, "method_failed") == 0) { 1475 ip->summary = gettext("is not running because " 1476 "a method failed."); 1477 } else if (strcmp(ip->aux_state, "none") == 0) { 1478 ip->summary = 1479 gettext("is not running for an unknown reason."); 1480 } else if (strcmp(ip->aux_state, "restarting_too_quickly") == 1481 0) { 1482 ip->summary = gettext("was restarting too quickly."); 1483 } else { 1484 ip->summary = gettext("requires maintenance."); 1485 } 1486 } else { 1487 ip->summary = gettext("is in an invalid state."); 1488 } 1489 } 1490 1491 static void 1492 print_method_failure(const inst_t *ip, const char **dcp) 1493 { 1494 char buf[50]; 1495 int stat = ip->start_method_waitstatus; 1496 1497 if (stat != 0) { 1498 if (WIFEXITED(stat)) { 1499 if (WEXITSTATUS(stat) == SMF_EXIT_ERR_CONFIG) { 1500 (void) strlcpy(buf, gettext( 1501 "exited with $SMF_EXIT_ERR_CONFIG"), 1502 sizeof (buf)); 1503 } else if (WEXITSTATUS(stat) == SMF_EXIT_ERR_FATAL) { 1504 (void) strlcpy(buf, gettext( 1505 "exited with $SMF_EXIT_ERR_FATAL"), 1506 sizeof (buf)); 1507 } else if (WEXITSTATUS(stat) == SMF_EXIT_MON_DEGRADE) { 1508 (void) strlcpy(buf, gettext( 1509 "exited with $SMF_EXIT_MON_DEGRADE"), 1510 sizeof (buf)); 1511 } else { 1512 (void) snprintf(buf, sizeof (buf), 1513 gettext("exited with status %d"), 1514 WEXITSTATUS(stat)); 1515 } 1516 } else if (WIFSIGNALED(stat)) { 1517 if (WCOREDUMP(stat)) { 1518 if (strsignal(WTERMSIG(stat)) != NULL) 1519 (void) snprintf(buf, sizeof (buf), 1520 gettext("dumped core on %s (%d)"), 1521 strsignal(WTERMSIG(stat)), 1522 WTERMSIG(stat)); 1523 else 1524 (void) snprintf(buf, sizeof (buf), 1525 gettext("dumped core signal %d"), 1526 WTERMSIG(stat)); 1527 } else { 1528 if (strsignal(WTERMSIG(stat)) != NULL) { 1529 (void) snprintf(buf, sizeof (buf), 1530 gettext("died on %s (%d)"), 1531 strsignal(WTERMSIG(stat)), 1532 WTERMSIG(stat)); 1533 } else { 1534 (void) snprintf(buf, sizeof (buf), 1535 gettext("died on signal %d"), 1536 WTERMSIG(stat)); 1537 } 1538 } 1539 } else { 1540 goto fail; 1541 } 1542 1543 if (strcmp(ip->aux_state, "fault_threshold_reached") != 0) 1544 (void) printf(gettext("Reason: Start method %s.\n"), 1545 buf); 1546 else 1547 (void) printf(gettext("Reason: " 1548 "Start method failed repeatedly, last %s.\n"), buf); 1549 *dcp = DC_STARTFAIL; 1550 } else { 1551 fail: 1552 if (strcmp(ip->aux_state, "fault_threshold_reached") == 0) 1553 (void) puts(gettext( 1554 "Reason: Method failed repeatedly.")); 1555 else 1556 (void) puts(gettext("Reason: Method failed.")); 1557 *dcp = DC_METHFAIL; 1558 } 1559 } 1560 1561 static void 1562 print_dependency_reasons(const inst_t *svcp, int verbose) 1563 { 1564 struct dependency *d; 1565 struct svcptr *spp; 1566 const char *dc; 1567 1568 /* 1569 * If we couldn't determine why the service is offline, then baddeps 1570 * will be empty and causes will have a pointer to self. 1571 */ 1572 if (uu_list_numnodes(svcp->baddeps) == 0 && 1573 uu_list_numnodes(svcp->causes) == 1) { 1574 spp = uu_list_first(svcp->causes); 1575 if (spp->svcp == svcp) { 1576 switch (svcp->restarter_bad) { 1577 case 0: 1578 (void) puts(gettext("Reason: Unknown.")); 1579 dc = DC_UNKNOWN; 1580 break; 1581 1582 case EINVAL: 1583 (void) printf(gettext("Reason: " 1584 "Restarter \"%s\" is invalid.\n"), 1585 svcp->restarter); 1586 dc = DC_RSTRINVALID; 1587 break; 1588 1589 case ENOENT: 1590 (void) printf(gettext("Reason: " 1591 "Restarter \"%s\" does not exist.\n"), 1592 svcp->restarter); 1593 dc = DC_RSTRABSENT; 1594 break; 1595 1596 default: 1597 #ifndef NDEBUG 1598 (void) fprintf(stderr, "%s:%d: Bad " 1599 "restarter_bad value %d. Aborting.\n", 1600 __FILE__, __LINE__, svcp->restarter_bad); 1601 #endif 1602 abort(); 1603 } 1604 1605 if (g_msgbase) 1606 (void) printf(gettext(" See: %s%s\n"), 1607 g_msgbase, dc); 1608 return; 1609 } 1610 } 1611 1612 for (d = uu_list_first(svcp->baddeps); 1613 d != NULL; 1614 d = uu_list_next(svcp->baddeps, d)) { 1615 (void) printf(gettext("Reason: Dependency %s is absent.\n"), 1616 d->fmri); 1617 if (g_msgbase) 1618 (void) printf(gettext(" See: %s%s\n"), g_msgbase, 1619 DC_DEPABSENT); 1620 } 1621 1622 for (spp = uu_list_first(svcp->causes); 1623 spp != NULL && spp->svcp != svcp; 1624 spp = uu_list_next(svcp->causes, spp)) { 1625 determine_summary(spp->svcp); 1626 1627 if (inst_running(spp->svcp)) { 1628 (void) printf(gettext("Reason: " 1629 "Service svc:/%s:%s is running.\n"), 1630 spp->svcp->svcname, spp->svcp->instname); 1631 dc = DC_DEPRUNNING; 1632 } else { 1633 if (snprintf(NULL, 0, 1634 gettext("Reason: Service svc:/%s:%s %s"), 1635 spp->svcp->svcname, spp->svcp->instname, 1636 spp->svcp->summary) <= 80) { 1637 (void) printf(gettext( 1638 "Reason: Service svc:/%s:%s %s\n"), 1639 spp->svcp->svcname, spp->svcp->instname, 1640 spp->svcp->summary); 1641 } else { 1642 (void) printf(gettext( 1643 "Reason: Service svc:/%s:%s\n" 1644 " %s\n"), spp->svcp->svcname, 1645 spp->svcp->instname, spp->svcp->summary); 1646 } 1647 1648 dc = DC_DEPOTHER; 1649 } 1650 1651 if (g_msgbase != NULL) 1652 (void) printf(gettext(" See: %s%s\n"), g_msgbase, dc); 1653 1654 if (verbose) { 1655 inst_t *pp; 1656 int indent; 1657 1658 (void) printf(gettext(" Path: svc:/%s:%s\n"), 1659 svcp->svcname, svcp->instname); 1660 1661 indent = 1; 1662 for (pp = spp->next_hop; ; ) { 1663 struct svcptr *tmp; 1664 1665 (void) printf(gettext("%6s %*ssvc:/%s:%s\n"), 1666 "", indent++ * 2, "", pp->svcname, 1667 pp->instname); 1668 1669 if (pp == spp->svcp) 1670 break; 1671 1672 /* set pp to next_hop of cause with same svcp */ 1673 tmp = uu_list_find(pp->causes, spp, NULL, NULL); 1674 pp = tmp->next_hop; 1675 } 1676 } 1677 } 1678 } 1679 1680 static void 1681 print_logs(scf_instance_t *inst) 1682 { 1683 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) != 0) 1684 return; 1685 1686 if (pg_get_single_val(g_pg, SCF_PROPERTY_ALT_LOGFILE, 1687 SCF_TYPE_ASTRING, (void *)g_value, g_value_sz, 0) == 0) 1688 (void) printf(gettext(" See: %s\n"), g_value); 1689 1690 if (pg_get_single_val(g_pg, SCF_PROPERTY_LOGFILE, 1691 SCF_TYPE_ASTRING, (void *)g_value, g_value_sz, 0) == 0) 1692 (void) printf(gettext(" See: %s\n"), g_value); 1693 } 1694 1695 static void 1696 print_aux_fmri_logs(const char *fmri) 1697 { 1698 scf_instance_t *scf_inst = scf_instance_create(h); 1699 if (scf_inst == NULL) 1700 return; 1701 1702 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, scf_inst, 1703 NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0) 1704 print_logs(scf_inst); 1705 1706 scf_instance_destroy(scf_inst); 1707 } 1708 1709 static void 1710 print_reasons(const inst_t *svcp, int verbose) 1711 { 1712 int r; 1713 const char *dc = NULL; 1714 1715 if (strcmp(svcp->state, SCF_STATE_STRING_ONLINE) == 0) 1716 return; 1717 1718 if (strcmp(svcp->state, SCF_STATE_STRING_UNINIT) == 0) { 1719 inst_t *rsp; 1720 1721 r = get_fmri(svcp->restarter, NULL, &rsp); 1722 switch (r) { 1723 case 0: 1724 if (rsp != NULL) 1725 break; 1726 /* FALLTHROUGH */ 1727 1728 case EINVAL: 1729 (void) printf(gettext("Reason: " 1730 "Restarter \"%s\" is invalid.\n"), svcp->restarter); 1731 dc = DC_RSTRINVALID; 1732 goto diagcode; 1733 1734 case ENOENT: 1735 (void) printf(gettext("Reason: " 1736 "Restarter \"%s\" does not exist.\n"), 1737 svcp->restarter); 1738 dc = DC_RSTRABSENT; 1739 goto diagcode; 1740 1741 default: 1742 bad_error("get_fmri", r); 1743 } 1744 1745 if (inst_running(rsp)) { 1746 (void) printf(gettext("Reason: Restarter %s " 1747 "has not initialized service state.\n"), 1748 svcp->restarter); 1749 dc = DC_UNINIT; 1750 } else { 1751 (void) printf(gettext( 1752 "Reason: Restarter %s is not running.\n"), 1753 svcp->restarter); 1754 dc = DC_RSTRDEAD; 1755 } 1756 1757 } else if (strcmp(svcp->state, SCF_STATE_STRING_DISABLED) == 0) { 1758 if (!svcp->temporary) { 1759 if (svcp->comment[0] != '\0') { 1760 (void) printf(gettext("Reason: Disabled by " 1761 "an administrator: %s\n"), svcp->comment); 1762 } else { 1763 (void) printf(gettext("Reason: Disabled by " 1764 "an administrator.\n")); 1765 } 1766 dc = DC_DISABLED; 1767 } else { 1768 if (svcp->comment[0] != '\0') { 1769 (void) printf(gettext("Reason: Temporarily " 1770 "disabled by an administrator: %s\n"), 1771 svcp->comment); 1772 } else { 1773 (void) printf(gettext("Reason: Temporarily " 1774 "disabled by an administrator.\n")); 1775 } 1776 dc = DC_TEMPDISABLED; 1777 } 1778 1779 } else if (strcmp(svcp->state, SCF_STATE_STRING_MAINT) == 0) { 1780 if (strcmp(svcp->aux_state, "administrative_request") == 0) { 1781 (void) puts(gettext("Reason: " 1782 "Maintenance requested by an administrator.")); 1783 dc = DC_ADMINMAINT; 1784 } else if (strcmp(svcp->aux_state, "dependency_cycle") == 0) { 1785 (void) puts(gettext( 1786 "Reason: Completes a dependency cycle.")); 1787 dc = DC_DEPCYCLE; 1788 } else if (strcmp(svcp->aux_state, "fault_threshold_reached") == 1789 0) { 1790 print_method_failure(svcp, &dc); 1791 } else if (strcmp(svcp->aux_state, "service_request") == 0) { 1792 if (svcp->aux_fmri) { 1793 (void) printf(gettext("Reason: Maintenance " 1794 "requested by \"%s\"\n"), svcp->aux_fmri); 1795 print_aux_fmri_logs(svcp->aux_fmri); 1796 } else { 1797 (void) puts(gettext("Reason: Maintenance " 1798 "requested by another service.")); 1799 } 1800 dc = DC_SVCREQMAINT; 1801 } else if (strcmp(svcp->aux_state, "invalid_dependency") == 0) { 1802 (void) puts(gettext("Reason: Has invalid dependency.")); 1803 dc = DC_INVALIDDEP; 1804 } else if (strcmp(svcp->aux_state, "invalid_restarter") == 0) { 1805 (void) printf(gettext("Reason: Restarter \"%s\" is " 1806 "invalid.\n"), svcp->restarter); 1807 dc = DC_RSTRINVALID; 1808 } else if (strcmp(svcp->aux_state, "method_failed") == 0) { 1809 print_method_failure(svcp, &dc); 1810 } else if (strcmp(svcp->aux_state, "restarting_too_quickly") == 1811 0) { 1812 (void) puts(gettext("Reason: Restarting too quickly.")); 1813 dc = DC_TOOQUICKLY; 1814 } else if (strcmp(svcp->aux_state, "none") == 0) { 1815 (void) printf(gettext( 1816 "Reason: Restarter %s gave no explanation.\n"), 1817 svcp->restarter); 1818 dc = DC_NONE; 1819 } else { 1820 (void) puts(gettext("Reason: Unknown.")); 1821 dc = DC_UNKNOWN; 1822 } 1823 1824 } else if (strcmp(svcp->state, SCF_STATE_STRING_OFFLINE) == 0) { 1825 if (strcmp(svcp->next_state, SCF_STATE_STRING_ONLINE) == 0) { 1826 (void) puts(gettext( 1827 "Reason: Start method is running.")); 1828 dc = DC_STARTING; 1829 } else if (strcmp(svcp->next_state, SCF_STATE_STRING_NONE) == 1830 0) { 1831 print_dependency_reasons(svcp, verbose); 1832 /* Function prints diagcodes. */ 1833 return; 1834 } else { 1835 (void) printf(gettext( 1836 "Reason: Transitioning to state %s.\n"), 1837 svcp->next_state); 1838 dc = DC_TRANSITION; 1839 } 1840 1841 } else if (strcmp(svcp->state, SCF_STATE_STRING_DEGRADED) == 0) { 1842 if (strcmp(svcp->aux_state, "administrative_request") == 0) { 1843 (void) puts(gettext( 1844 "Reason: Degraded by an administrator.")); 1845 dc = DC_ADMINDEGR; 1846 } else if (strcmp(svcp->aux_state, "service_request") == 0) { 1847 if (svcp->aux_fmri) { 1848 (void) printf(gettext( 1849 "Reason: Degraded by \"%s\"\n"), 1850 svcp->aux_fmri); 1851 print_aux_fmri_logs(svcp->aux_fmri); 1852 } else { 1853 (void) puts(gettext( 1854 "Reason: Degraded by another service.")); 1855 } 1856 } else if (strcmp(svcp->aux_state, "method_failed") == 0) { 1857 print_method_failure(svcp, &dc); 1858 } else { 1859 (void) puts(gettext("Reason: Unknown.")); 1860 dc = DC_UNKNOWN; 1861 } 1862 1863 } else { 1864 (void) printf(gettext("Reason: Not in valid state (%s).\n"), 1865 svcp->state); 1866 dc = DC_INVALIDSTATE; 1867 } 1868 1869 diagcode: 1870 if (g_msgbase != NULL && dc != NULL) 1871 (void) printf(gettext(" See: %s%s\n"), g_msgbase, dc); 1872 } 1873 1874 static void 1875 print_manpage(int verbose) 1876 { 1877 static char *title = NULL; 1878 static char *section = NULL; 1879 1880 if (title == NULL) { 1881 title = safe_malloc(g_value_sz); 1882 section = safe_malloc(g_value_sz); 1883 } 1884 1885 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_TITLE, SCF_TYPE_ASTRING, 1886 (void *)title, g_value_sz, 0) != 0) 1887 return; 1888 1889 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_SECTION, 1890 SCF_TYPE_ASTRING, (void *)section, g_value_sz, 0) != 0) 1891 return; 1892 1893 if (!verbose) { 1894 (void) printf(gettext(" See: %s(%s)\n"), title, section); 1895 return; 1896 } 1897 1898 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_MANPATH, SCF_TYPE_ASTRING, 1899 (void *)g_value, g_value_sz, 0) != 0) 1900 return; 1901 1902 if (strcmp(g_value, ":default") == 0) { 1903 assert(sizeof (DEFAULT_MAN_PATH) < g_value_sz); 1904 (void) strcpy(g_value, DEFAULT_MAN_PATH); 1905 } 1906 1907 (void) printf(gettext(" See: man -M %s -s %s %s\n"), g_value, 1908 section, title); 1909 } 1910 1911 static void 1912 print_doclink() 1913 { 1914 static char *uri = NULL; 1915 1916 if (uri == NULL) { 1917 uri = safe_malloc(g_value_sz); 1918 } 1919 1920 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_URI, SCF_TYPE_ASTRING, 1921 (void *)uri, g_value_sz, 0) != 0) 1922 return; 1923 1924 (void) printf(gettext(" See: %s\n"), uri); 1925 } 1926 1927 1928 /* 1929 * Returns 1930 * 0 - success 1931 * 1 - inst was deleted 1932 */ 1933 static int 1934 print_docs(scf_instance_t *inst, int verbose) 1935 { 1936 scf_snapshot_t *snap; 1937 int r; 1938 1939 if (scf_instance_get_snapshot(inst, "running", g_snap) != 0) { 1940 switch (scf_error()) { 1941 case SCF_ERROR_NOT_FOUND: 1942 break; 1943 1944 case SCF_ERROR_DELETED: 1945 return (1); 1946 1947 default: 1948 scfdie(); 1949 } 1950 1951 snap = NULL; 1952 } else { 1953 snap = g_snap; 1954 } 1955 1956 if (scf_iter_instance_pgs_typed_composed(g_iter, inst, snap, 1957 SCF_GROUP_TEMPLATE) != 0) { 1958 if (scf_error() != SCF_ERROR_DELETED) 1959 scfdie(); 1960 1961 return (1); 1962 } 1963 1964 for (;;) { 1965 r = scf_iter_next_pg(g_iter, g_pg); 1966 if (r == 0) 1967 break; 1968 if (r != 1) { 1969 if (scf_error() != SCF_ERROR_DELETED) 1970 scfdie(); 1971 1972 return (1); 1973 } 1974 1975 if (scf_pg_get_name(g_pg, g_fmri, g_fmri_sz) < 0) { 1976 if (scf_error() != SCF_ERROR_DELETED) 1977 scfdie(); 1978 1979 continue; 1980 } 1981 1982 if (strncmp(g_fmri, SCF_PG_TM_MAN_PREFIX, 1983 strlen(SCF_PG_TM_MAN_PREFIX)) == 0) { 1984 print_manpage(verbose); 1985 continue; 1986 } 1987 1988 if (strncmp(g_fmri, SCF_PG_TM_DOC_PREFIX, 1989 strlen(SCF_PG_TM_DOC_PREFIX)) == 0) { 1990 print_doclink(); 1991 continue; 1992 } 1993 } 1994 return (0); 1995 } 1996 1997 static int first = 1; 1998 1999 /* 2000 * Explain why the given service is in the state it's in. 2001 */ 2002 static void 2003 print_service(inst_t *svcp, int verbose) 2004 { 2005 struct svcptr *spp; 2006 time_t stime; 2007 char *timebuf; 2008 size_t tbsz; 2009 struct tm *tmp; 2010 int deleted = 0; 2011 2012 if (first) 2013 first = 0; 2014 else 2015 (void) putchar('\n'); 2016 2017 (void) printf(gettext("svc:/%s:%s"), svcp->svcname, svcp->instname); 2018 2019 if (scf_scope_get_service(g_local_scope, svcp->svcname, g_svc) != 0) { 2020 if (scf_error() != SCF_ERROR_NOT_FOUND) 2021 scfdie(); 2022 deleted = 1; 2023 } else if (scf_service_get_instance(g_svc, svcp->instname, g_inst) != 2024 0) { 2025 if (scf_error() != SCF_ERROR_NOT_FOUND) 2026 scfdie(); 2027 deleted = 1; 2028 } 2029 2030 if (!deleted) { 2031 if (inst_get_single_val(g_inst, SCF_PG_TM_COMMON_NAME, locale, 2032 SCF_TYPE_USTRING, g_value, g_value_sz, 0, 0, 1) == 0) 2033 /* EMPTY */; 2034 else if (inst_get_single_val(g_inst, SCF_PG_TM_COMMON_NAME, "C", 2035 SCF_TYPE_USTRING, g_value, g_value_sz, 0, 0, 1) != 0) 2036 (void) strcpy(g_value, "?"); 2037 2038 (void) printf(gettext(" (%s)\n"), g_value); 2039 } else { 2040 (void) putchar('\n'); 2041 } 2042 2043 if (g_zonename != NULL) 2044 (void) printf(gettext(" Zone: %s\n"), g_zonename); 2045 2046 stime = svcp->stime.tv_sec; 2047 tmp = localtime(&stime); 2048 2049 for (tbsz = 50; ; tbsz *= 2) { 2050 timebuf = safe_malloc(tbsz); 2051 if (strftime(timebuf, tbsz, NULL, tmp) != 0) 2052 break; 2053 free(timebuf); 2054 } 2055 2056 (void) printf(gettext(" State: %s since %s\n"), svcp->state, timebuf); 2057 2058 free(timebuf); 2059 2060 /* Reasons */ 2061 print_reasons(svcp, verbose); 2062 2063 if (!deleted) 2064 deleted = print_docs(g_inst, verbose); 2065 if (!deleted) 2066 print_logs(g_inst); 2067 2068 (void) determine_impact(svcp); 2069 2070 switch (uu_list_numnodes(svcp->impact)) { 2071 case 0: 2072 if (inst_running(svcp)) 2073 (void) puts(gettext("Impact: None.")); 2074 else 2075 (void) puts(gettext( 2076 "Impact: This service is not running.")); 2077 break; 2078 2079 case 1: 2080 if (!verbose) 2081 (void) puts(gettext("Impact: 1 dependent service " 2082 "is not running. (Use -v for list.)")); 2083 else 2084 (void) puts(gettext( 2085 "Impact: 1 dependent service is not running:")); 2086 break; 2087 2088 default: 2089 if (!verbose) 2090 (void) printf(gettext("Impact: %d dependent services " 2091 "are not running. (Use -v for list.)\n"), 2092 uu_list_numnodes(svcp->impact)); 2093 else 2094 (void) printf(gettext( 2095 "Impact: %d dependent services are not running:\n"), 2096 uu_list_numnodes(svcp->impact)); 2097 } 2098 2099 if (verbose) { 2100 for (spp = uu_list_first(svcp->impact); 2101 spp != NULL; 2102 spp = uu_list_next(svcp->impact, spp)) 2103 (void) printf(gettext(" svc:/%s:%s\n"), 2104 spp->svcp->svcname, spp->svcp->instname); 2105 } 2106 } 2107 2108 /* 2109 * Top level routine. 2110 */ 2111 2112 static int 2113 impact_compar(const void *a, const void *b) 2114 { 2115 int n, m; 2116 2117 n = uu_list_numnodes((*(inst_t **)a)->impact); 2118 m = uu_list_numnodes((*(inst_t **)b)->impact); 2119 2120 return (m - n); 2121 } 2122 2123 static int 2124 print_service_cb(void *verbose, scf_walkinfo_t *wip) 2125 { 2126 int r; 2127 inst_t *ip; 2128 2129 assert(wip->pg == NULL); 2130 2131 r = get_fmri(wip->fmri, NULL, &ip); 2132 assert(r != EINVAL); 2133 if (r == ENOENT) 2134 return (0); 2135 2136 assert(r == 0); 2137 assert(ip != NULL); 2138 2139 print_service(ip, (int)verbose); 2140 2141 return (0); 2142 } 2143 2144 void 2145 explain(int verbose, int argc, char **argv) 2146 { 2147 /* 2148 * Initialize globals. If we have been called before (e.g., for a 2149 * different zone), this will clobber the previous globals -- keeping 2150 * with the proud svcs(1) tradition of not bothering to ever clean 2151 * anything up. 2152 */ 2153 x_init(); 2154 2155 /* Walk the graph and populate services with inst_t's */ 2156 load_services(); 2157 2158 /* Populate causes for services. */ 2159 determine_all_causes(); 2160 2161 if (argc > 0) { 2162 scf_error_t err; 2163 2164 check_msgbase(); 2165 2166 /* Call print_service() for each operand. */ 2167 2168 err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE, 2169 print_service_cb, (void *)verbose, &exit_status, uu_warn); 2170 if (err != 0) { 2171 uu_warn(gettext( 2172 "failed to iterate over instances: %s\n"), 2173 scf_strerror(err)); 2174 exit_status = UU_EXIT_FATAL; 2175 } 2176 } else { 2177 struct svcptr *spp; 2178 int n, i; 2179 inst_t **ary; 2180 2181 /* Sort g_causes. */ 2182 2183 n = uu_list_numnodes(g_causes); 2184 if (n == 0) 2185 return; 2186 2187 check_msgbase(); 2188 2189 ary = calloc(n, sizeof (*ary)); 2190 if (ary == NULL) 2191 uu_die(emsg_nomem); 2192 2193 i = 0; 2194 for (spp = uu_list_first(g_causes); 2195 spp != NULL; 2196 spp = uu_list_next(g_causes, spp)) { 2197 (void) determine_impact(spp->svcp); 2198 ary[i++] = spp->svcp; 2199 } 2200 2201 qsort(ary, n, sizeof (*ary), impact_compar); 2202 2203 /* Call print_service() for each service. */ 2204 2205 for (i = 0; i < n; ++i) 2206 print_service(ary[i], verbose); 2207 } 2208 } 2209