1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 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_REPTFAIL "SMF-8000-7Y" 94 #define DC_METHFAIL "SMF-8000-8Q" 95 #define DC_NONE "SMF-8000-9C" 96 #define DC_UNKNOWN "SMF-8000-AR" 97 #define DC_STARTING "SMF-8000-C4" 98 #define DC_ADMINDEGR "SMF-8000-DX" 99 #define DC_DEPABSENT "SMF-8000-E2" 100 #define DC_DEPRUNNING "SMF-8000-FJ" 101 #define DC_DEPOTHER "SMF-8000-GE" 102 #define DC_DEPCYCLE "SMF-8000-HP" 103 #define DC_INVALIDDEP "SMF-8000-JA" 104 #define DC_STARTFAIL "SMF-8000-KS" 105 #define DC_TOOQUICKLY "SMF-8000-L5" 106 #define DC_INVALIDSTATE "SMF-8000-N3" 107 #define DC_TRANSITION "SMF-8000-PH" 108 109 #define DEFAULT_MAN_PATH "/usr/share/man" 110 111 112 #define uu_list_append(lst, e) uu_list_insert_before(lst, NULL, e) 113 114 #ifdef NDEBUG 115 #define bad_error(func, err) abort() 116 #else 117 #define bad_error(func, err) \ 118 (void) fprintf(stderr, "%s:%d: %s() failed with unknown error %d.\n", \ 119 __FILE__, __LINE__, func, err); \ 120 abort(); 121 #endif 122 123 124 typedef struct { 125 const char *svcname; 126 const char *instname; 127 128 /* restarter pg properties */ 129 char state[MAX_SCF_STATE_STRING_SZ]; 130 char next_state[MAX_SCF_STATE_STRING_SZ]; 131 struct timeval stime; 132 const char *aux_state; 133 int64_t start_method_waitstatus; 134 135 int enabled; 136 int temporary; 137 const char *restarter; 138 uu_list_t *dependencies; /* list of dependency_group's */ 139 140 int active; /* In use? (cycle detection) */ 141 int restarter_bad; 142 const char *summary; 143 uu_list_t *baddeps; /* list of dependency's */ 144 uu_list_t *causes; /* list of svcptrs */ 145 uu_list_t *impact_dependents; /* list of svcptrs */ 146 uu_list_t *impact; /* list of svcptrs */ 147 148 uu_list_node_t node; 149 } inst_t; 150 151 typedef struct service { 152 const char *svcname; 153 uu_list_t *instances; 154 struct service *next; 155 } svc_t; 156 157 struct svcptr { 158 inst_t *svcp; 159 inst_t *next_hop; 160 uu_list_node_t node; 161 }; 162 163 struct dependency_group { 164 enum { DGG_REQALL, DGG_REQANY, DGG_OPTALL, DGG_EXCALL } grouping; 165 const char *type; 166 uu_list_t *entities; /* List of struct dependency's */ 167 uu_list_node_t node; 168 }; 169 170 struct dependency { 171 const char *fmri; 172 uu_list_node_t node; 173 }; 174 175 /* Hash table of service names -> svc_t's */ 176 #define SVC_HASH_NBUCKETS 256 177 #define SVC_HASH_MASK (SVC_HASH_NBUCKETS - 1) 178 179 static svc_t **services; 180 181 static uu_list_pool_t *insts, *svcptrs, *depgroups, *deps; 182 static uu_list_t *g_causes; /* list of svcptrs */ 183 184 static scf_scope_t *g_local_scope; 185 static scf_service_t *g_svc; 186 static scf_instance_t *g_inst; 187 static scf_snapshot_t *g_snap; 188 static scf_propertygroup_t *g_pg; 189 static scf_property_t *g_prop; 190 static scf_value_t *g_val; 191 static scf_iter_t *g_iter, *g_viter; 192 static char *g_fmri, *g_value; 193 static size_t g_fmri_sz, g_value_sz; 194 static const char *g_msgbase = "http://sun.com/msg/"; 195 196 static char *emsg_nomem; 197 static char *emsg_invalid_dep; 198 199 extern scf_handle_t *h; 200 201 /* ARGSUSED */ 202 static int 203 svcptr_compare(struct svcptr *a, struct svcptr *b, void *data) 204 { 205 return (b->svcp - a->svcp); 206 } 207 208 static uint32_t 209 hash_name(const char *name) 210 { 211 uint32_t h = 0, g; 212 const char *p; 213 214 for (p = name; *p != '\0'; ++p) { 215 h = (h << 4) + *p; 216 if ((g = (h & 0xf0000000)) != 0) { 217 h ^= (g >> 24); 218 h ^= g; 219 } 220 } 221 222 return (h); 223 } 224 225 226 static void 227 x_init(void) 228 { 229 emsg_nomem = gettext("Out of memory.\n"); 230 emsg_invalid_dep = 231 gettext("svc:/%s:%s has invalid dependency \"%s\".\n"); 232 233 services = calloc(SVC_HASH_NBUCKETS, sizeof (*services)); 234 if (services == NULL) 235 uu_die(emsg_nomem); 236 237 insts = uu_list_pool_create("insts", sizeof (inst_t), 238 offsetof(inst_t, node), NULL, UU_LIST_POOL_DEBUG); 239 svcptrs = uu_list_pool_create("svcptrs", sizeof (struct svcptr), 240 offsetof(struct svcptr, node), (uu_compare_fn_t *)svcptr_compare, 241 UU_LIST_POOL_DEBUG); 242 depgroups = uu_list_pool_create("depgroups", 243 sizeof (struct dependency_group), 244 offsetof(struct dependency_group, node), NULL, UU_LIST_POOL_DEBUG); 245 deps = uu_list_pool_create("deps", sizeof (struct dependency), 246 offsetof(struct dependency, node), NULL, UU_LIST_POOL_DEBUG); 247 g_causes = uu_list_create(svcptrs, NULL, UU_LIST_DEBUG); 248 if (insts == NULL || svcptrs == NULL || depgroups == NULL || 249 deps == NULL || g_causes == NULL) 250 uu_die(emsg_nomem); 251 252 if ((g_local_scope = scf_scope_create(h)) == NULL || 253 (g_svc = scf_service_create(h)) == NULL || 254 (g_inst = scf_instance_create(h)) == NULL || 255 (g_snap = scf_snapshot_create(h)) == NULL || 256 (g_pg = scf_pg_create(h)) == NULL || 257 (g_prop = scf_property_create(h)) == NULL || 258 (g_val = scf_value_create(h)) == NULL || 259 (g_iter = scf_iter_create(h)) == NULL || 260 (g_viter = scf_iter_create(h)) == NULL) 261 scfdie(); 262 263 if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, g_local_scope) != 0) 264 scfdie(); 265 266 g_fmri_sz = max_scf_fmri_length + 1; 267 g_fmri = safe_malloc(g_fmri_sz); 268 269 g_value_sz = max_scf_value_length + 1; 270 g_value = safe_malloc(g_value_sz); 271 } 272 273 /* 274 * Repository loading routines. 275 */ 276 277 /* 278 * Returns 279 * 0 - success 280 * ECANCELED - inst was deleted 281 * EINVAL - inst is invalid 282 */ 283 static int 284 load_dependencies(inst_t *svcp, scf_instance_t *inst) 285 { 286 scf_snapshot_t *snap; 287 struct dependency_group *dg; 288 struct dependency *d; 289 int r; 290 291 assert(svcp->dependencies == NULL); 292 svcp->dependencies = uu_list_create(depgroups, svcp, UU_LIST_DEBUG); 293 if (svcp->dependencies == NULL) 294 uu_die(emsg_nomem); 295 296 if (scf_instance_get_snapshot(inst, "running", g_snap) == 0) { 297 snap = g_snap; 298 } else { 299 if (scf_error() != SCF_ERROR_NOT_FOUND) 300 scfdie(); 301 302 snap = NULL; 303 } 304 305 if (scf_iter_instance_pgs_typed_composed(g_iter, inst, snap, 306 SCF_GROUP_DEPENDENCY) != 0) { 307 if (scf_error() != SCF_ERROR_DELETED) 308 scfdie(); 309 return (ECANCELED); 310 } 311 312 for (;;) { 313 r = scf_iter_next_pg(g_iter, g_pg); 314 if (r == 0) 315 break; 316 if (r != 1) { 317 if (scf_error() != SCF_ERROR_DELETED) 318 scfdie(); 319 return (ECANCELED); 320 } 321 322 dg = safe_malloc(sizeof (*dg)); 323 (void) memset(dg, 0, sizeof (*dg)); 324 dg->entities = uu_list_create(deps, dg, UU_LIST_DEBUG); 325 if (dg->entities == NULL) 326 uu_die(emsg_nomem); 327 328 if (pg_get_single_val(g_pg, SCF_PROPERTY_GROUPING, 329 SCF_TYPE_ASTRING, g_value, g_value_sz, 0) != 0) 330 return (EINVAL); 331 332 if (strcmp(g_value, "require_all") == 0) 333 dg->grouping = DGG_REQALL; 334 else if (strcmp(g_value, "require_any") == 0) 335 dg->grouping = DGG_REQANY; 336 else if (strcmp(g_value, "optional_all") == 0) 337 dg->grouping = DGG_OPTALL; 338 else if (strcmp(g_value, "exclude_all") == 0) 339 dg->grouping = DGG_EXCALL; 340 else { 341 (void) fprintf(stderr, gettext("svc:/%s:%s has " 342 "dependency with unknown type \"%s\".\n"), 343 svcp->svcname, svcp->instname, g_value); 344 return (EINVAL); 345 } 346 347 if (pg_get_single_val(g_pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING, 348 g_value, g_value_sz, 0) != 0) 349 return (EINVAL); 350 dg->type = safe_strdup(g_value); 351 352 if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) != 353 0) { 354 switch (scf_error()) { 355 case SCF_ERROR_NOT_FOUND: 356 (void) fprintf(stderr, gettext("svc:/%s:%s has " 357 "dependency without an entities " 358 "property.\n"), svcp->svcname, 359 svcp->instname); 360 return (EINVAL); 361 362 case SCF_ERROR_DELETED: 363 return (ECANCELED); 364 365 default: 366 scfdie(); 367 } 368 } 369 370 if (scf_iter_property_values(g_viter, g_prop) != 0) { 371 if (scf_error() != SCF_ERROR_DELETED) 372 scfdie(); 373 return (ECANCELED); 374 } 375 376 for (;;) { 377 r = scf_iter_next_value(g_viter, g_val); 378 if (r == 0) 379 break; 380 if (r != 1) { 381 if (scf_error() != SCF_ERROR_DELETED) 382 scfdie(); 383 return (ECANCELED); 384 } 385 386 d = safe_malloc(sizeof (*d)); 387 d->fmri = safe_malloc(max_scf_fmri_length + 1); 388 389 if (scf_value_get_astring(g_val, (char *)d->fmri, 390 max_scf_fmri_length + 1) < 0) 391 scfdie(); 392 393 uu_list_node_init(d, &d->node, deps); 394 (void) uu_list_append(dg->entities, d); 395 } 396 397 uu_list_node_init(dg, &dg->node, depgroups); 398 r = uu_list_append(svcp->dependencies, dg); 399 assert(r == 0); 400 } 401 402 return (0); 403 } 404 405 static void 406 add_instance(const char *svcname, const char *instname, scf_instance_t *inst) 407 { 408 inst_t *instp; 409 svc_t *svcp; 410 int have_enabled = 0; 411 int i; 412 uint32_t h; 413 414 h = hash_name(svcname) & SVC_HASH_MASK; 415 for (svcp = services[h]; svcp != NULL; svcp = svcp->next) { 416 if (strcmp(svcp->svcname, svcname) == 0) 417 break; 418 } 419 420 if (svcp == NULL) { 421 svcp = safe_malloc(sizeof (*svcp)); 422 svcp->svcname = safe_strdup(svcname); 423 svcp->instances = uu_list_create(insts, svcp, UU_LIST_DEBUG); 424 if (svcp->instances == NULL) 425 uu_die(emsg_nomem); 426 svcp->next = services[h]; 427 services[h] = svcp; 428 } 429 430 instp = safe_malloc(sizeof (*instp)); 431 (void) memset(instp, 0, sizeof (*instp)); 432 instp->svcname = svcp->svcname; 433 instp->instname = safe_strdup(instname); 434 instp->impact_dependents = 435 uu_list_create(svcptrs, instp, UU_LIST_DEBUG); 436 if (instp->impact_dependents == NULL) 437 uu_die(emsg_nomem); 438 439 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) != 0) { 440 switch (scf_error()) { 441 case SCF_ERROR_DELETED: 442 return; 443 444 case SCF_ERROR_NOT_FOUND: 445 (void) fprintf(stderr, gettext("svc:/%s:%s has no " 446 "\"%s\" property group; ignoring.\n"), 447 instp->svcname, instp->instname, SCF_PG_RESTARTER); 448 return; 449 450 default: 451 scfdie(); 452 } 453 } 454 455 if (pg_get_single_val(g_pg, SCF_PROPERTY_STATE, SCF_TYPE_ASTRING, 456 (void *)instp->state, sizeof (instp->state), 0) != 0) 457 return; 458 459 if (pg_get_single_val(g_pg, SCF_PROPERTY_NEXT_STATE, SCF_TYPE_ASTRING, 460 (void *)instp->next_state, sizeof (instp->next_state), 0) != 0) 461 return; 462 463 if (pg_get_single_val(g_pg, SCF_PROPERTY_STATE_TIMESTAMP, 464 SCF_TYPE_TIME, &instp->stime, 0, 0) != 0) 465 return; 466 467 if (pg_get_single_val(g_pg, SCF_PROPERTY_AUX_STATE, SCF_TYPE_ASTRING, 468 g_fmri, g_fmri_sz, 0) != 0) 469 return; 470 instp->aux_state = safe_strdup(g_fmri); 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 if (scf_instance_get_pg(inst, SCF_PG_GENERAL_OVR, g_pg) == 0) { 476 if (pg_get_single_val(g_pg, SCF_PROPERTY_ENABLED, 477 SCF_TYPE_BOOLEAN, &instp->enabled, 0, 0) == 0) 478 have_enabled = 1; 479 } else { 480 switch (scf_error()) { 481 case SCF_ERROR_NOT_FOUND: 482 break; 483 484 case SCF_ERROR_DELETED: 485 return; 486 487 default: 488 scfdie(); 489 } 490 } 491 492 if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL, g_pg) != 493 0) { 494 switch (scf_error()) { 495 case SCF_ERROR_DELETED: 496 case SCF_ERROR_NOT_FOUND: 497 return; 498 499 default: 500 scfdie(); 501 } 502 } 503 504 if (pg_get_single_val(g_pg, SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 505 &i, 0, 0) != 0) 506 return; 507 if (!have_enabled) { 508 instp->enabled = i; 509 instp->temporary = 0; 510 } else { 511 instp->temporary = (instp->enabled != i); 512 } 513 514 if (pg_get_single_val(g_pg, SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, 515 g_fmri, g_fmri_sz, 0) == 0) 516 instp->restarter = safe_strdup(g_fmri); 517 else 518 instp->restarter = SCF_SERVICE_STARTD; 519 520 if (strcmp(instp->state, SCF_STATE_STRING_OFFLINE) == 0 && 521 load_dependencies(instp, inst) != 0) 522 return; 523 524 uu_list_node_init(instp, &instp->node, insts); 525 i = uu_list_append(svcp->instances, instp); 526 assert(i == 0); 527 } 528 529 static void 530 load_services(void) 531 { 532 scf_iter_t *siter, *iiter; 533 int r; 534 char *svcname, *instname; 535 536 if ((siter = scf_iter_create(h)) == NULL || 537 (iiter = scf_iter_create(h)) == NULL) 538 scfdie(); 539 540 svcname = safe_malloc(max_scf_name_length + 1); 541 instname = safe_malloc(max_scf_name_length + 1); 542 543 if (scf_iter_scope_services(siter, g_local_scope) != 0) 544 scfdie(); 545 546 for (;;) { 547 r = scf_iter_next_service(siter, g_svc); 548 if (r == 0) 549 break; 550 if (r != 1) 551 scfdie(); 552 553 if (scf_service_get_name(g_svc, svcname, 554 max_scf_name_length + 1) < 0) { 555 if (scf_error() != SCF_ERROR_DELETED) 556 scfdie(); 557 continue; 558 } 559 560 if (scf_iter_service_instances(iiter, g_svc) != 0) { 561 if (scf_error() != SCF_ERROR_DELETED) 562 scfdie(); 563 continue; 564 } 565 566 for (;;) { 567 r = scf_iter_next_instance(iiter, g_inst); 568 if (r == 0) 569 break; 570 if (r != 1) { 571 if (scf_error() != SCF_ERROR_DELETED) 572 scfdie(); 573 break; 574 } 575 576 if (scf_instance_get_name(g_inst, instname, 577 max_scf_name_length + 1) < 0) { 578 if (scf_error() != SCF_ERROR_DELETED) 579 scfdie(); 580 continue; 581 } 582 583 add_instance(svcname, instname, g_inst); 584 } 585 } 586 587 free(svcname); 588 free(instname); 589 scf_iter_destroy(siter); 590 scf_iter_destroy(iiter); 591 } 592 593 /* 594 * Dependency analysis routines. 595 */ 596 597 static void 598 add_svcptr(uu_list_t *lst, inst_t *svcp) 599 { 600 struct svcptr *spp; 601 uu_list_index_t idx; 602 int r; 603 604 spp = safe_malloc(sizeof (*spp)); 605 spp->svcp = svcp; 606 spp->next_hop = NULL; 607 608 if (uu_list_find(lst, spp, NULL, &idx) != NULL) { 609 free(spp); 610 return; 611 } 612 613 uu_list_node_init(spp, &spp->node, svcptrs); 614 r = uu_list_append(lst, spp); 615 assert(r == 0); 616 } 617 618 static int determine_causes(inst_t *, void *); 619 620 /* 621 * Determine the causes of src and add them to the causes list of dst. 622 * Returns ELOOP if src is active, and 0 otherwise. 623 */ 624 static int 625 add_causes(inst_t *dst, inst_t *src) 626 { 627 struct svcptr *spp, *copy; 628 uu_list_index_t idx; 629 630 if (determine_causes(src, (void *)1) != UU_WALK_NEXT) { 631 /* Dependency cycle. */ 632 (void) fprintf(stderr, " svc:/%s:%s\n", dst->svcname, 633 dst->instname); 634 return (ELOOP); 635 } 636 637 add_svcptr(src->impact_dependents, dst); 638 639 for (spp = uu_list_first(src->causes); 640 spp != NULL; 641 spp = uu_list_next(src->causes, spp)) { 642 if (uu_list_find(dst->causes, spp, NULL, &idx) != NULL) 643 continue; 644 645 copy = safe_malloc(sizeof (*copy)); 646 copy->svcp = spp->svcp; 647 copy->next_hop = src; 648 uu_list_node_init(copy, ©->node, svcptrs); 649 uu_list_insert(dst->causes, copy, idx); 650 651 add_svcptr(g_causes, spp->svcp); 652 } 653 654 return (0); 655 } 656 657 static int 658 inst_running(inst_t *ip) 659 { 660 return (strcmp(ip->state, SCF_STATE_STRING_ONLINE) == 0 || 661 strcmp(ip->state, SCF_STATE_STRING_DEGRADED) == 0); 662 } 663 664 static int 665 inst_running_or_maint(inst_t *ip) 666 { 667 return (inst_running(ip) || 668 strcmp(ip->state, SCF_STATE_STRING_MAINT) == 0); 669 } 670 671 static svc_t * 672 get_svc(const char *sn) 673 { 674 uint32_t h; 675 svc_t *svcp; 676 677 h = hash_name(sn) & SVC_HASH_MASK; 678 679 for (svcp = services[h]; svcp != NULL; svcp = svcp->next) { 680 if (strcmp(svcp->svcname, sn) == 0) 681 break; 682 } 683 684 return (svcp); 685 } 686 687 /* ARGSUSED */ 688 static inst_t * 689 get_inst(svc_t *svcp, const char *in) 690 { 691 inst_t *instp; 692 693 for (instp = uu_list_first(svcp->instances); 694 instp != NULL; 695 instp = uu_list_next(svcp->instances, instp)) { 696 if (strcmp(instp->instname, in) == 0) 697 return (instp); 698 } 699 700 return (NULL); 701 } 702 703 static int 704 get_fmri(const char *fmri, svc_t **spp, inst_t **ipp) 705 { 706 const char *sn, *in; 707 svc_t *sp; 708 inst_t *ip; 709 710 if (strlcpy(g_fmri, fmri, g_fmri_sz) >= g_fmri_sz) 711 return (EINVAL); 712 713 if (scf_parse_svc_fmri(g_fmri, NULL, &sn, &in, NULL, NULL) != 0) 714 return (EINVAL); 715 716 if (sn == NULL) 717 return (EINVAL); 718 719 sp = get_svc(sn); 720 if (sp == NULL) 721 return (ENOENT); 722 723 if (in != NULL) { 724 ip = get_inst(sp, in); 725 if (ip == NULL) 726 return (ENOENT); 727 } 728 729 if (spp != NULL) 730 *spp = sp; 731 if (ipp != NULL) 732 *ipp = ((in == NULL) ? NULL : ip); 733 734 return (0); 735 } 736 737 static int 738 process_reqall(inst_t *svcp, struct dependency_group *dg) 739 { 740 uu_list_walk_t *walk; 741 struct dependency *d; 742 int r, svcrunning; 743 svc_t *sp; 744 inst_t *ip; 745 746 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST); 747 if (walk == NULL) 748 uu_die(emsg_nomem); 749 750 while ((d = uu_list_walk_next(walk)) != NULL) { 751 r = get_fmri(d->fmri, &sp, &ip); 752 switch (r) { 753 case EINVAL: 754 /* LINTED */ 755 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 756 svcp->instname, d->fmri); 757 continue; 758 759 case ENOENT: 760 uu_list_remove(dg->entities, d); 761 r = uu_list_append(svcp->baddeps, d); 762 assert(r == 0); 763 continue; 764 765 case 0: 766 break; 767 768 default: 769 bad_error("get_fmri", r); 770 } 771 772 if (ip != NULL) { 773 if (inst_running(ip)) 774 continue; 775 r = add_causes(svcp, ip); 776 if (r != 0) { 777 assert(r == ELOOP); 778 return (r); 779 } 780 continue; 781 } 782 783 svcrunning = 0; 784 785 for (ip = uu_list_first(sp->instances); 786 ip != NULL; 787 ip = uu_list_next(sp->instances, ip)) { 788 if (inst_running(ip)) 789 svcrunning = 1; 790 } 791 792 if (!svcrunning) { 793 for (ip = uu_list_first(sp->instances); 794 ip != NULL; 795 ip = uu_list_next(sp->instances, ip)) { 796 r = add_causes(svcp, ip); 797 if (r != 0) { 798 assert(r == ELOOP); 799 uu_list_walk_end(walk); 800 return (r); 801 } 802 } 803 } 804 } 805 806 uu_list_walk_end(walk); 807 return (0); 808 } 809 810 static int 811 process_reqany(inst_t *svcp, struct dependency_group *dg) 812 { 813 svc_t *sp; 814 inst_t *ip; 815 struct dependency *d; 816 int r; 817 uu_list_walk_t *walk; 818 819 for (d = uu_list_first(dg->entities); 820 d != NULL; 821 d = uu_list_next(dg->entities, d)) { 822 r = get_fmri(d->fmri, &sp, &ip); 823 switch (r) { 824 case 0: 825 break; 826 827 case EINVAL: 828 /* LINTED */ 829 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 830 svcp->instname, d->fmri); 831 continue; 832 833 case ENOENT: 834 continue; 835 836 default: 837 bad_error("eval_svc_dep", r); 838 } 839 840 if (ip != NULL) { 841 if (inst_running(ip)) 842 return (0); 843 continue; 844 } 845 846 for (ip = uu_list_first(sp->instances); 847 ip != NULL; 848 ip = uu_list_next(sp->instances, ip)) { 849 if (inst_running(ip)) 850 return (0); 851 } 852 } 853 854 /* 855 * The dependency group is not satisfied. Add all unsatisfied members 856 * to the cause list. 857 */ 858 859 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST); 860 if (walk == NULL) 861 uu_die(emsg_nomem); 862 863 while ((d = uu_list_walk_next(walk)) != NULL) { 864 r = get_fmri(d->fmri, &sp, &ip); 865 switch (r) { 866 case 0: 867 break; 868 869 case ENOENT: 870 uu_list_remove(dg->entities, d); 871 r = uu_list_append(svcp->baddeps, d); 872 assert(r == 0); 873 continue; 874 875 case EINVAL: 876 /* Should have caught above. */ 877 default: 878 bad_error("eval_svc_dep", r); 879 } 880 881 if (ip != NULL) { 882 if (inst_running(ip)) 883 continue; 884 r = add_causes(svcp, ip); 885 if (r != 0) { 886 assert(r == ELOOP); 887 return (r); 888 } 889 continue; 890 } 891 892 for (ip = uu_list_first(sp->instances); 893 ip != NULL; 894 ip = uu_list_next(sp->instances, ip)) { 895 if (inst_running(ip)) 896 continue; 897 r = add_causes(svcp, ip); 898 if (r != 0) { 899 assert(r == ELOOP); 900 return (r); 901 } 902 } 903 } 904 905 return (0); 906 } 907 908 static int 909 process_optall(inst_t *svcp, struct dependency_group *dg) 910 { 911 uu_list_walk_t *walk; 912 struct dependency *d; 913 int r; 914 inst_t *ip; 915 svc_t *sp; 916 917 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST); 918 if (walk == NULL) 919 uu_die(emsg_nomem); 920 921 while ((d = uu_list_walk_next(walk)) != NULL) { 922 r = get_fmri(d->fmri, &sp, &ip); 923 924 switch (r) { 925 case 0: 926 break; 927 928 case EINVAL: 929 /* LINTED */ 930 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 931 svcp->instname, d->fmri); 932 continue; 933 934 case ENOENT: 935 continue; 936 937 default: 938 bad_error("get_fmri", r); 939 } 940 941 if (ip != NULL) { 942 if (ip->enabled && !inst_running_or_maint(ip)) { 943 r = add_causes(svcp, ip); 944 if (r != 0) { 945 assert(r == ELOOP); 946 uu_list_walk_end(walk); 947 return (r); 948 } 949 } 950 continue; 951 } 952 953 for (ip = uu_list_first(sp->instances); 954 ip != NULL; 955 ip = uu_list_next(sp->instances, ip)) { 956 if (ip->enabled && !inst_running_or_maint(ip)) { 957 r = add_causes(svcp, ip); 958 if (r != 0) { 959 assert(r == ELOOP); 960 uu_list_walk_end(walk); 961 return (r); 962 } 963 } 964 } 965 } 966 967 uu_list_walk_end(walk); 968 return (0); 969 } 970 971 static int 972 process_excall(inst_t *svcp, struct dependency_group *dg) 973 { 974 struct dependency *d; 975 int r; 976 svc_t *sp; 977 inst_t *ip; 978 979 for (d = uu_list_first(dg->entities); 980 d != NULL; 981 d = uu_list_next(dg->entities, d)) { 982 r = get_fmri(d->fmri, &sp, &ip); 983 984 switch (r) { 985 case 0: 986 break; 987 988 case EINVAL: 989 /* LINTED */ 990 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 991 svcp->instname, d->fmri); 992 continue; 993 994 case ENOENT: 995 continue; 996 997 default: 998 bad_error("eval_svc_dep", r); 999 } 1000 1001 if (ip != NULL) { 1002 if (inst_running(ip)) { 1003 r = add_causes(svcp, ip); 1004 if (r != 0) { 1005 assert(r == ELOOP); 1006 return (r); 1007 } 1008 } 1009 continue; 1010 } 1011 1012 for (ip = uu_list_first(sp->instances); 1013 ip != NULL; 1014 ip = uu_list_next(sp->instances, ip)) { 1015 if (inst_running(ip)) { 1016 r = add_causes(svcp, ip); 1017 if (r != 0) { 1018 assert(r == ELOOP); 1019 return (r); 1020 } 1021 } 1022 } 1023 } 1024 1025 return (0); 1026 } 1027 1028 static int 1029 process_svc_dg(inst_t *svcp, struct dependency_group *dg) 1030 { 1031 switch (dg->grouping) { 1032 case DGG_REQALL: 1033 return (process_reqall(svcp, dg)); 1034 1035 case DGG_REQANY: 1036 return (process_reqany(svcp, dg)); 1037 1038 case DGG_OPTALL: 1039 return (process_optall(svcp, dg)); 1040 1041 case DGG_EXCALL: 1042 return (process_excall(svcp, dg)); 1043 1044 default: 1045 #ifndef NDEBUG 1046 (void) fprintf(stderr, 1047 "%s:%d: Unknown dependency grouping %d.\n", __FILE__, 1048 __LINE__, dg->grouping); 1049 #endif 1050 abort(); 1051 /* NOTREACHED */ 1052 } 1053 } 1054 1055 /* 1056 * Returns 1057 * EINVAL - fmri is not a valid FMRI 1058 * 0 - the file indicated by fmri is missing 1059 * 1 - the file indicated by fmri is present 1060 */ 1061 static int 1062 eval_file_dep(const char *fmri) 1063 { 1064 const char *path; 1065 struct stat st; 1066 1067 if (strncmp(fmri, "file:", sizeof ("file:") - 1) != 0) 1068 return (EINVAL); 1069 1070 path = fmri + (sizeof ("file:") - 1); 1071 1072 if (path[0] != '/') 1073 return (EINVAL); 1074 1075 if (path[1] == '/') { 1076 path += 2; 1077 if (strncmp(path, "localhost/", sizeof ("localhost/") - 1) == 0) 1078 path += sizeof ("localhost") - 1; 1079 else if (path[0] != '/') 1080 return (EINVAL); 1081 } 1082 1083 return (stat(path, &st) == 0 ? 1 : 0); 1084 } 1085 1086 static void 1087 process_file_dg(inst_t *svcp, struct dependency_group *dg) 1088 { 1089 uu_list_walk_t *walk; 1090 struct dependency *d, **deps; 1091 int r, i = 0, any_satisfied = 0; 1092 1093 if (dg->grouping == DGG_REQANY) { 1094 deps = calloc(uu_list_numnodes(dg->entities), sizeof (*deps)); 1095 if (deps == NULL) 1096 uu_die(emsg_nomem); 1097 } 1098 1099 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST); 1100 if (walk == NULL) 1101 uu_die(emsg_nomem); 1102 1103 while ((d = uu_list_walk_next(walk)) != NULL) { 1104 r = eval_file_dep(d->fmri); 1105 if (r == EINVAL) { 1106 /* LINTED */ 1107 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname, 1108 svcp->instname, d->fmri); 1109 continue; 1110 } 1111 1112 assert(r == 0 || r == 1); 1113 1114 switch (dg->grouping) { 1115 case DGG_REQALL: 1116 case DGG_OPTALL: 1117 if (r == 0) { 1118 uu_list_remove(dg->entities, d); 1119 r = uu_list_append(svcp->baddeps, d); 1120 assert(r == 0); 1121 } 1122 break; 1123 1124 case DGG_REQANY: 1125 if (r == 1) 1126 any_satisfied = 1; 1127 else 1128 deps[i++] = d; 1129 break; 1130 1131 case DGG_EXCALL: 1132 if (r == 1) { 1133 uu_list_remove(dg->entities, d); 1134 r = uu_list_append(svcp->baddeps, d); 1135 assert(r == 0); 1136 } 1137 break; 1138 1139 default: 1140 #ifndef NDEBUG 1141 (void) fprintf(stderr, "%s:%d: Unknown grouping %d.\n", 1142 __FILE__, __LINE__, dg->grouping); 1143 #endif 1144 abort(); 1145 } 1146 } 1147 1148 uu_list_walk_end(walk); 1149 1150 if (dg->grouping != DGG_REQANY) 1151 return; 1152 1153 if (!any_satisfied) { 1154 for (; i >= 0; --i) { 1155 uu_list_remove(dg->entities, deps[i]); 1156 r = uu_list_append(svcp->baddeps, deps[i]); 1157 assert(r == 0); 1158 } 1159 } 1160 1161 free(deps); 1162 } 1163 1164 /* 1165 * Populate the causes list of svcp. This function should not return with 1166 * causes empty. 1167 */ 1168 static int 1169 determine_causes(inst_t *svcp, void *canfailp) 1170 { 1171 struct dependency_group *dg; 1172 int r; 1173 1174 if (svcp->active) { 1175 (void) fprintf(stderr, gettext("Dependency cycle detected:\n" 1176 " svc:/%s:%s\n"), svcp->svcname, svcp->instname); 1177 return ((int)canfailp != 0 ? UU_WALK_ERROR : UU_WALK_NEXT); 1178 } 1179 1180 if (svcp->causes != NULL) 1181 return (UU_WALK_NEXT); 1182 1183 svcp->causes = uu_list_create(svcptrs, svcp, UU_LIST_DEBUG); 1184 svcp->baddeps = uu_list_create(deps, svcp, UU_LIST_DEBUG); 1185 if (svcp->causes == NULL || svcp->baddeps == NULL) 1186 uu_die(emsg_nomem); 1187 1188 if (inst_running(svcp) || 1189 strcmp(svcp->state, SCF_STATE_STRING_UNINIT) == 0) { 1190 /* 1191 * If we're running, add a self-pointer in case we're 1192 * excluding another service. 1193 */ 1194 add_svcptr(svcp->causes, svcp); 1195 return (UU_WALK_NEXT); 1196 } 1197 1198 if (strcmp(svcp->state, SCF_STATE_STRING_MAINT) == 0) { 1199 add_svcptr(svcp->causes, svcp); 1200 add_svcptr(g_causes, svcp); 1201 return (UU_WALK_NEXT); 1202 } 1203 1204 if (strcmp(svcp->state, SCF_STATE_STRING_DISABLED) == 0) { 1205 add_svcptr(svcp->causes, svcp); 1206 if (svcp->enabled) 1207 add_svcptr(g_causes, svcp); 1208 1209 return (UU_WALK_NEXT); 1210 } 1211 1212 if (strcmp(svcp->state, SCF_STATE_STRING_OFFLINE) != 0) { 1213 (void) fprintf(stderr, 1214 gettext("svc:/%s:%s has invalid state \"%s\".\n"), 1215 svcp->svcname, svcp->instname, svcp->state); 1216 add_svcptr(svcp->causes, svcp); 1217 add_svcptr(g_causes, svcp); 1218 return (UU_WALK_NEXT); 1219 } 1220 1221 if (strcmp(svcp->next_state, SCF_STATE_STRING_NONE) != 0) { 1222 add_svcptr(svcp->causes, svcp); 1223 add_svcptr(g_causes, svcp); 1224 return (UU_WALK_NEXT); 1225 } 1226 1227 svcp->active = 1; 1228 1229 /* 1230 * Dependency analysis can add elements to our baddeps list (absent 1231 * dependency, unsatisfied file dependency), or to our cause list 1232 * (unsatisfied dependency). 1233 */ 1234 for (dg = uu_list_first(svcp->dependencies); 1235 dg != NULL; 1236 dg = uu_list_next(svcp->dependencies, dg)) { 1237 if (strcmp(dg->type, "path") == 0) { 1238 process_file_dg(svcp, dg); 1239 } else if (strcmp(dg->type, "service") == 0) { 1240 int r; 1241 1242 r = process_svc_dg(svcp, dg); 1243 if (r != 0) { 1244 assert(r == ELOOP); 1245 svcp->active = 0; 1246 return ((int)canfailp != 0 ? 1247 UU_WALK_ERROR : UU_WALK_NEXT); 1248 } 1249 } else { 1250 (void) fprintf(stderr, gettext("svc:/%s:%s has " 1251 "dependency group with invalid type \"%s\".\n"), 1252 svcp->svcname, svcp->instname, dg->type); 1253 } 1254 } 1255 1256 if (uu_list_numnodes(svcp->causes) == 0) { 1257 if (uu_list_numnodes(svcp->baddeps) > 0) { 1258 add_svcptr(g_causes, svcp); 1259 add_svcptr(svcp->causes, svcp); 1260 } else { 1261 inst_t *restarter; 1262 1263 r = get_fmri(svcp->restarter, NULL, &restarter); 1264 if (r == 0 && !inst_running(restarter)) { 1265 r = add_causes(svcp, restarter); 1266 if (r != 0) { 1267 assert(r == ELOOP); 1268 svcp->active = 0; 1269 return ((int)canfailp != 0 ? 1270 UU_WALK_ERROR : UU_WALK_NEXT); 1271 } 1272 } else { 1273 svcp->restarter_bad = r; 1274 add_svcptr(svcp->causes, svcp); 1275 add_svcptr(g_causes, svcp); 1276 } 1277 } 1278 } 1279 1280 assert(uu_list_numnodes(svcp->causes) > 0); 1281 1282 svcp->active = 0; 1283 return (UU_WALK_NEXT); 1284 } 1285 1286 static void 1287 determine_all_causes(void) 1288 { 1289 svc_t *svcp; 1290 int i; 1291 1292 for (i = 0; i < SVC_HASH_NBUCKETS; ++i) { 1293 for (svcp = services[i]; svcp != NULL; svcp = svcp->next) 1294 (void) uu_list_walk(svcp->instances, 1295 (uu_walk_fn_t *)determine_causes, 0, 0); 1296 } 1297 } 1298 1299 /* 1300 * Returns 1301 * 0 - success 1302 * ELOOP - dependency cycle detected 1303 */ 1304 static int 1305 determine_impact(inst_t *ip) 1306 { 1307 struct svcptr *idsp, *spp, *copy; 1308 uu_list_index_t idx; 1309 1310 if (ip->active) { 1311 (void) fprintf(stderr, gettext("Dependency cycle detected:\n" 1312 " svc:/%s:%s\n"), ip->svcname, ip->instname); 1313 return (ELOOP); 1314 } 1315 1316 if (ip->impact != NULL) 1317 return (0); 1318 1319 ip->impact = uu_list_create(svcptrs, ip, UU_LIST_DEBUG); 1320 if (ip->impact == NULL) 1321 uu_die(emsg_nomem); 1322 ip->active = 1; 1323 1324 for (idsp = uu_list_first(ip->impact_dependents); 1325 idsp != NULL; 1326 idsp = uu_list_next(ip->impact_dependents, idsp)) { 1327 if (determine_impact(idsp->svcp) != 0) { 1328 (void) fprintf(stderr, " svc:/%s:%s\n", 1329 ip->svcname, ip->instname); 1330 return (ELOOP); 1331 } 1332 1333 add_svcptr(ip->impact, idsp->svcp); 1334 1335 for (spp = uu_list_first(idsp->svcp->impact); 1336 spp != NULL; 1337 spp = uu_list_next(idsp->svcp->impact, spp)) { 1338 if (uu_list_find(ip->impact, spp, NULL, &idx) != NULL) 1339 continue; 1340 1341 copy = safe_malloc(sizeof (*copy)); 1342 copy->svcp = spp->svcp; 1343 copy->next_hop = NULL; 1344 uu_list_node_init(copy, ©->node, svcptrs); 1345 uu_list_insert(ip->impact, copy, idx); 1346 } 1347 } 1348 1349 ip->active = 0; 1350 return (0); 1351 } 1352 1353 /* 1354 * Printing routines. 1355 */ 1356 1357 static void 1358 check_msgbase(void) 1359 { 1360 if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL, g_inst, 1361 NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0) { 1362 if (scf_error() != SCF_ERROR_NOT_FOUND) 1363 scfdie(); 1364 1365 return; 1366 } 1367 1368 if (scf_instance_get_pg_composed(g_inst, NULL, "msg", g_pg) != 0) { 1369 switch (scf_error()) { 1370 case SCF_ERROR_NOT_FOUND: 1371 case SCF_ERROR_DELETED: 1372 return; 1373 1374 default: 1375 scfdie(); 1376 } 1377 } 1378 1379 if (scf_pg_get_property(g_pg, "base", g_prop) != 0) { 1380 switch (scf_error()) { 1381 case SCF_ERROR_NOT_FOUND: 1382 case SCF_ERROR_DELETED: 1383 return; 1384 1385 default: 1386 scfdie(); 1387 } 1388 } 1389 1390 if (scf_property_get_value(g_prop, g_val) != 0) { 1391 switch (scf_error()) { 1392 case SCF_ERROR_NOT_FOUND: 1393 case SCF_ERROR_CONSTRAINT_VIOLATED: 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