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