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