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 2007 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 while (--i >= 0) { 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 case SCF_ERROR_PERMISSION_DENIED: 1395 g_msgbase = NULL; 1396 return; 1397 1398 case SCF_ERROR_DELETED: 1399 return; 1400 1401 default: 1402 scfdie(); 1403 } 1404 } 1405 1406 if (scf_value_get_astring(g_val, g_value, g_value_sz) < 0) { 1407 if (scf_error() != SCF_ERROR_TYPE_MISMATCH) 1408 scfdie(); 1409 return; 1410 } 1411 1412 g_msgbase = safe_strdup(g_value); 1413 } 1414 1415 static void 1416 determine_summary(inst_t *ip) 1417 { 1418 if (ip->summary != NULL) 1419 return; 1420 1421 if (inst_running(ip)) { 1422 ip->summary = gettext("is running."); 1423 return; 1424 } 1425 1426 if (strcmp(ip->state, SCF_STATE_STRING_UNINIT) == 0) { 1427 ip->summary = gettext("is uninitialized."); 1428 } else if (strcmp(ip->state, SCF_STATE_STRING_DISABLED) == 0) { 1429 if (!ip->temporary) 1430 ip->summary = gettext("is disabled."); 1431 else 1432 ip->summary = gettext("is temporarily disabled."); 1433 } else if (strcmp(ip->state, SCF_STATE_STRING_OFFLINE) == 0) { 1434 if (uu_list_numnodes(ip->baddeps) != 0) 1435 ip->summary = gettext("has missing dependencies."); 1436 else if (strcmp(ip->next_state, SCF_STATE_STRING_ONLINE) == 0) 1437 ip->summary = gettext("is starting."); 1438 else 1439 ip->summary = gettext("is offline."); 1440 } else if (strcmp(ip->state, SCF_STATE_STRING_MAINT) == 0) { 1441 if (strcmp(ip->aux_state, "administrative_request") == 0) { 1442 ip->summary = gettext("was taken down for maintenace " 1443 "by an administrator."); 1444 } else if (strcmp(ip->aux_state, "dependency_cycle") == 0) { 1445 ip->summary = gettext("completed a dependency cycle."); 1446 } else if (strcmp(ip->aux_state, "fault_threshold_reached") == 1447 0) { 1448 ip->summary = gettext("is not running because " 1449 "a method failed repeatedly."); 1450 } else if (strcmp(ip->aux_state, "invalid_dependency") == 0) { 1451 ip->summary = gettext("has an invalid dependency."); 1452 } else if (strcmp(ip->aux_state, "invalid_restarter") == 0) { 1453 ip->summary = gettext("has an invalid restarter."); 1454 } else if (strcmp(ip->aux_state, "method_failed") == 0) { 1455 ip->summary = gettext("is not running because " 1456 "a method failed."); 1457 } else if (strcmp(ip->aux_state, "none") == 0) { 1458 ip->summary = 1459 gettext("is not running for an unknown reason."); 1460 } else if (strcmp(ip->aux_state, "restarting_too_quickly") == 1461 0) { 1462 ip->summary = gettext("was restarting too quickly."); 1463 } else { 1464 ip->summary = gettext("requires maintenance."); 1465 } 1466 } else { 1467 ip->summary = gettext("is in an invalid state."); 1468 } 1469 } 1470 1471 static void 1472 print_method_failure(const inst_t *ip, const char **dcp) 1473 { 1474 char buf[50]; 1475 int stat = ip->start_method_waitstatus; 1476 1477 if (stat != 0) { 1478 if (WIFEXITED(stat)) { 1479 if (WEXITSTATUS(stat) == SMF_EXIT_ERR_CONFIG) { 1480 (void) strlcpy(buf, gettext( 1481 "exited with $SMF_EXIT_ERR_CONFIG"), 1482 sizeof (buf)); 1483 } else if (WEXITSTATUS(stat) == SMF_EXIT_ERR_FATAL) { 1484 (void) strlcpy(buf, gettext( 1485 "exited with $SMF_EXIT_ERR_FATAL"), 1486 sizeof (buf)); 1487 } else { 1488 (void) snprintf(buf, sizeof (buf), 1489 gettext("exited with status %d"), 1490 WEXITSTATUS(stat)); 1491 } 1492 } else if (WIFSIGNALED(stat)) { 1493 if (WCOREDUMP(stat)) { 1494 if (strsignal(WTERMSIG(stat)) != NULL) 1495 (void) snprintf(buf, sizeof (buf), 1496 gettext("dumped core on %s (%d)"), 1497 strsignal(WTERMSIG(stat)), 1498 WTERMSIG(stat)); 1499 else 1500 (void) snprintf(buf, sizeof (buf), 1501 gettext("dumped core signal %d"), 1502 WTERMSIG(stat)); 1503 } else { 1504 if (strsignal(WTERMSIG(stat)) != NULL) { 1505 (void) snprintf(buf, sizeof (buf), 1506 gettext("died on %s (%d)"), 1507 strsignal(WTERMSIG(stat)), 1508 WTERMSIG(stat)); 1509 } else { 1510 (void) snprintf(buf, sizeof (buf), 1511 gettext("died on signal %d"), 1512 WTERMSIG(stat)); 1513 } 1514 } 1515 } else { 1516 goto fail; 1517 } 1518 1519 if (strcmp(ip->aux_state, "fault_threshold_reached") != 0) 1520 (void) printf(gettext("Reason: Start method %s.\n"), 1521 buf); 1522 else 1523 (void) printf(gettext("Reason: " 1524 "Start method failed repeatedly, last %s.\n"), buf); 1525 *dcp = DC_STARTFAIL; 1526 } else { 1527 fail: 1528 if (strcmp(ip->aux_state, "fault_threshold_reached") == 0) 1529 (void) puts(gettext( 1530 "Reason: Method failed repeatedly.")); 1531 else 1532 (void) puts(gettext("Reason: Method failed.")); 1533 *dcp = DC_METHFAIL; 1534 } 1535 } 1536 1537 static void 1538 print_dependency_reasons(const inst_t *svcp, int verbose) 1539 { 1540 struct dependency *d; 1541 struct svcptr *spp; 1542 const char *dc; 1543 1544 /* 1545 * If we couldn't determine why the service is offline, then baddeps 1546 * will be empty and causes will have a pointer to self. 1547 */ 1548 if (uu_list_numnodes(svcp->baddeps) == 0 && 1549 uu_list_numnodes(svcp->causes) == 1) { 1550 spp = uu_list_first(svcp->causes); 1551 if (spp->svcp == svcp) { 1552 switch (svcp->restarter_bad) { 1553 case 0: 1554 (void) puts(gettext("Reason: Unknown.")); 1555 dc = DC_UNKNOWN; 1556 break; 1557 1558 case EINVAL: 1559 (void) printf(gettext("Reason: " 1560 "Restarter \"%s\" is invalid.\n"), 1561 svcp->restarter); 1562 dc = DC_RSTRINVALID; 1563 break; 1564 1565 case ENOENT: 1566 (void) printf(gettext("Reason: " 1567 "Restarter \"%s\" does not exist.\n"), 1568 svcp->restarter); 1569 dc = DC_RSTRABSENT; 1570 break; 1571 1572 default: 1573 #ifndef NDEBUG 1574 (void) fprintf(stderr, "%s:%d: Bad " 1575 "restarter_bad value %d. Aborting.\n", 1576 __FILE__, __LINE__, svcp->restarter_bad); 1577 #endif 1578 abort(); 1579 } 1580 1581 if (g_msgbase) 1582 (void) printf(gettext(" See: %s%s\n"), 1583 g_msgbase, dc); 1584 return; 1585 } 1586 } 1587 1588 for (d = uu_list_first(svcp->baddeps); 1589 d != NULL; 1590 d = uu_list_next(svcp->baddeps, d)) { 1591 (void) printf(gettext("Reason: Dependency %s is absent.\n"), 1592 d->fmri); 1593 if (g_msgbase) 1594 (void) printf(gettext(" See: %s%s\n"), g_msgbase, 1595 DC_DEPABSENT); 1596 } 1597 1598 for (spp = uu_list_first(svcp->causes); 1599 spp != NULL && spp->svcp != svcp; 1600 spp = uu_list_next(svcp->causes, spp)) { 1601 determine_summary(spp->svcp); 1602 1603 if (inst_running(spp->svcp)) { 1604 (void) printf(gettext("Reason: " 1605 "Service svc:/%s:%s is running.\n"), 1606 spp->svcp->svcname, spp->svcp->instname); 1607 dc = DC_DEPRUNNING; 1608 } else { 1609 if (snprintf(NULL, 0, 1610 gettext("Reason: Service svc:/%s:%s %s"), 1611 spp->svcp->svcname, spp->svcp->instname, 1612 spp->svcp->summary) <= 80) { 1613 (void) printf(gettext( 1614 "Reason: Service svc:/%s:%s %s\n"), 1615 spp->svcp->svcname, spp->svcp->instname, 1616 spp->svcp->summary); 1617 } else { 1618 (void) printf(gettext( 1619 "Reason: Service svc:/%s:%s\n" 1620 " %s\n"), spp->svcp->svcname, 1621 spp->svcp->instname, spp->svcp->summary); 1622 } 1623 1624 dc = DC_DEPOTHER; 1625 } 1626 1627 if (g_msgbase != NULL) 1628 (void) printf(gettext(" See: %s%s\n"), g_msgbase, dc); 1629 1630 if (verbose) { 1631 inst_t *pp; 1632 int indent; 1633 1634 (void) printf(gettext(" Path: svc:/%s:%s\n"), 1635 svcp->svcname, svcp->instname); 1636 1637 indent = 1; 1638 for (pp = spp->next_hop; ; ) { 1639 struct svcptr *tmp; 1640 1641 (void) printf(gettext("%6s %*ssvc:/%s:%s\n"), 1642 "", indent++ * 2, "", pp->svcname, 1643 pp->instname); 1644 1645 if (pp == spp->svcp) 1646 break; 1647 1648 /* set pp to next_hop of cause with same svcp */ 1649 tmp = uu_list_find(pp->causes, spp, NULL, NULL); 1650 pp = tmp->next_hop; 1651 } 1652 } 1653 } 1654 } 1655 1656 static void 1657 print_reasons(const inst_t *svcp, int verbose) 1658 { 1659 int r; 1660 const char *dc = NULL; 1661 1662 if (strcmp(svcp->state, SCF_STATE_STRING_ONLINE) == 0) 1663 return; 1664 1665 if (strcmp(svcp->state, SCF_STATE_STRING_UNINIT) == 0) { 1666 inst_t *rsp; 1667 1668 r = get_fmri(svcp->restarter, NULL, &rsp); 1669 switch (r) { 1670 case 0: 1671 if (rsp != NULL) 1672 break; 1673 /* FALLTHROUGH */ 1674 1675 case EINVAL: 1676 (void) printf(gettext("Reason: " 1677 "Restarter \"%s\" is invalid.\n"), svcp->restarter); 1678 dc = DC_RSTRINVALID; 1679 goto diagcode; 1680 1681 case ENOENT: 1682 (void) printf(gettext("Reason: " 1683 "Restarter \"%s\" does not exist.\n"), 1684 svcp->restarter); 1685 dc = DC_RSTRABSENT; 1686 goto diagcode; 1687 1688 default: 1689 bad_error("get_fmri", r); 1690 } 1691 1692 if (inst_running(rsp)) { 1693 (void) printf(gettext("Reason: Restarter %s " 1694 "has not initialized service state.\n"), 1695 svcp->restarter); 1696 dc = DC_UNINIT; 1697 } else { 1698 (void) printf(gettext( 1699 "Reason: Restarter %s is not running.\n"), 1700 svcp->restarter); 1701 dc = DC_RSTRDEAD; 1702 } 1703 1704 } else if (strcmp(svcp->state, SCF_STATE_STRING_DISABLED) == 0) { 1705 if (!svcp->temporary) { 1706 (void) puts(gettext( 1707 "Reason: Disabled by an administrator.")); 1708 dc = DC_DISABLED; 1709 } else { 1710 (void) puts(gettext("Reason: " 1711 "Temporarily disabled by an administrator.")); 1712 dc = DC_TEMPDISABLED; 1713 } 1714 1715 } else if (strcmp(svcp->state, SCF_STATE_STRING_MAINT) == 0) { 1716 if (strcmp(svcp->aux_state, "administrative_request") == 0) { 1717 (void) puts(gettext("Reason: " 1718 "Maintenance requested by an administrator.")); 1719 dc = DC_ADMINMAINT; 1720 } else if (strcmp(svcp->aux_state, "dependency_cycle") == 0) { 1721 (void) puts(gettext( 1722 "Reason: Completes a dependency cycle.")); 1723 dc = DC_DEPCYCLE; 1724 } else if (strcmp(svcp->aux_state, "fault_threshold_reached") == 1725 0) { 1726 print_method_failure(svcp, &dc); 1727 } else if (strcmp(svcp->aux_state, "invalid_dependency") == 0) { 1728 (void) puts(gettext("Reason: Has invalid dependency.")); 1729 dc = DC_INVALIDDEP; 1730 } else if (strcmp(svcp->aux_state, "invalid_restarter") == 0) { 1731 (void) printf(gettext("Reason: Restarter \"%s\" is " 1732 "invalid.\n"), svcp->restarter); 1733 dc = DC_RSTRINVALID; 1734 } else if (strcmp(svcp->aux_state, "method_failed") == 0) { 1735 print_method_failure(svcp, &dc); 1736 } else if (strcmp(svcp->aux_state, "restarting_too_quickly") == 1737 0) { 1738 (void) puts(gettext("Reason: Restarting too quickly.")); 1739 dc = DC_TOOQUICKLY; 1740 } else if (strcmp(svcp->aux_state, "none") == 0) { 1741 (void) printf(gettext( 1742 "Reason: Restarter %s gave no explanation.\n"), 1743 svcp->restarter); 1744 dc = DC_NONE; 1745 } else { 1746 (void) puts(gettext("Reason: Unknown.")); 1747 dc = DC_UNKNOWN; 1748 } 1749 1750 } else if (strcmp(svcp->state, SCF_STATE_STRING_OFFLINE) == 0) { 1751 if (strcmp(svcp->next_state, SCF_STATE_STRING_ONLINE) == 0) { 1752 (void) puts(gettext( 1753 "Reason: Start method is running.")); 1754 dc = DC_STARTING; 1755 } else if (strcmp(svcp->next_state, SCF_STATE_STRING_NONE) == 1756 0) { 1757 print_dependency_reasons(svcp, verbose); 1758 /* Function prints diagcodes. */ 1759 return; 1760 } else { 1761 (void) printf(gettext( 1762 "Reason: Transitioning to state %s.\n"), 1763 svcp->next_state); 1764 dc = DC_TRANSITION; 1765 } 1766 1767 } else if (strcmp(svcp->state, SCF_STATE_STRING_DEGRADED) == 0) { 1768 (void) puts(gettext("Reason: Degraded by an administrator.")); 1769 dc = DC_ADMINDEGR; 1770 1771 } else { 1772 (void) printf(gettext("Reason: Not in valid state (%s).\n"), 1773 svcp->state); 1774 dc = DC_INVALIDSTATE; 1775 } 1776 1777 diagcode: 1778 if (g_msgbase != NULL) 1779 (void) printf(gettext(" See: %s%s\n"), g_msgbase, dc); 1780 } 1781 1782 static void 1783 print_manpage(int verbose) 1784 { 1785 static char *title = NULL; 1786 static char *section = NULL; 1787 1788 if (title == NULL) { 1789 title = safe_malloc(g_value_sz); 1790 section = safe_malloc(g_value_sz); 1791 } 1792 1793 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_TITLE, SCF_TYPE_ASTRING, 1794 (void *)title, g_value_sz, 0) != 0) 1795 return; 1796 1797 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_SECTION, 1798 SCF_TYPE_ASTRING, (void *)section, g_value_sz, 0) != 0) 1799 return; 1800 1801 if (!verbose) { 1802 (void) printf(gettext(" See: %s(%s)\n"), title, section); 1803 return; 1804 } 1805 1806 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_MANPATH, SCF_TYPE_ASTRING, 1807 (void *)g_value, g_value_sz, 0) != 0) 1808 return; 1809 1810 if (strcmp(g_value, ":default") == 0) { 1811 assert(sizeof (DEFAULT_MAN_PATH) < g_value_sz); 1812 (void) strcpy(g_value, DEFAULT_MAN_PATH); 1813 } 1814 1815 (void) printf(gettext(" See: man -M %s -s %s %s\n"), g_value, 1816 section, title); 1817 } 1818 1819 static void 1820 print_doclink() 1821 { 1822 static char *uri = NULL; 1823 1824 if (uri == NULL) { 1825 uri = safe_malloc(g_value_sz); 1826 } 1827 1828 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_URI, SCF_TYPE_ASTRING, 1829 (void *)uri, g_value_sz, 0) != 0) 1830 return; 1831 1832 (void) printf(gettext(" See: %s\n"), uri); 1833 } 1834 1835 1836 /* 1837 * Returns 1838 * 0 - success 1839 * 1 - inst was deleted 1840 */ 1841 static int 1842 print_docs(scf_instance_t *inst, int verbose) 1843 { 1844 scf_snapshot_t *snap; 1845 int r; 1846 1847 if (scf_instance_get_snapshot(inst, "running", g_snap) != 0) { 1848 switch (scf_error()) { 1849 case SCF_ERROR_NOT_FOUND: 1850 break; 1851 1852 case SCF_ERROR_DELETED: 1853 return (1); 1854 1855 default: 1856 scfdie(); 1857 } 1858 1859 snap = NULL; 1860 } else { 1861 snap = g_snap; 1862 } 1863 1864 if (scf_iter_instance_pgs_typed_composed(g_iter, inst, snap, 1865 SCF_GROUP_TEMPLATE) != 0) { 1866 if (scf_error() != SCF_ERROR_DELETED) 1867 scfdie(); 1868 1869 return (1); 1870 } 1871 1872 for (;;) { 1873 r = scf_iter_next_pg(g_iter, g_pg); 1874 if (r == 0) 1875 break; 1876 if (r != 1) { 1877 if (scf_error() != SCF_ERROR_DELETED) 1878 scfdie(); 1879 1880 return (1); 1881 } 1882 1883 if (scf_pg_get_name(g_pg, g_fmri, g_fmri_sz) < 0) { 1884 if (scf_error() != SCF_ERROR_DELETED) 1885 scfdie(); 1886 1887 continue; 1888 } 1889 1890 if (strncmp(g_fmri, SCF_PG_TM_MAN_PREFIX, 1891 strlen(SCF_PG_TM_MAN_PREFIX)) == 0) { 1892 print_manpage(verbose); 1893 continue; 1894 } 1895 1896 if (strncmp(g_fmri, SCF_PG_TM_DOC_PREFIX, 1897 strlen(SCF_PG_TM_DOC_PREFIX)) == 0) { 1898 print_doclink(); 1899 continue; 1900 } 1901 } 1902 return (0); 1903 } 1904 1905 static void 1906 print_logs(scf_instance_t *inst) 1907 { 1908 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) != 0) 1909 return; 1910 1911 if (pg_get_single_val(g_pg, SCF_PROPERTY_ALT_LOGFILE, 1912 SCF_TYPE_ASTRING, (void *)g_value, g_value_sz, 0) == 0) 1913 (void) printf(gettext(" See: %s\n"), g_value); 1914 1915 if (pg_get_single_val(g_pg, SCF_PROPERTY_LOGFILE, 1916 SCF_TYPE_ASTRING, (void *)g_value, g_value_sz, 0) == 0) 1917 (void) printf(gettext(" See: %s\n"), g_value); 1918 } 1919 1920 static int first = 1; 1921 1922 /* 1923 * Explain why the given service is in the state it's in. 1924 */ 1925 static void 1926 print_service(inst_t *svcp, int verbose) 1927 { 1928 struct svcptr *spp; 1929 time_t stime; 1930 char *timebuf; 1931 size_t tbsz; 1932 struct tm *tmp; 1933 int deleted = 0; 1934 1935 if (first) 1936 first = 0; 1937 else 1938 (void) putchar('\n'); 1939 1940 (void) printf(gettext("svc:/%s:%s"), svcp->svcname, svcp->instname); 1941 1942 if (scf_scope_get_service(g_local_scope, svcp->svcname, g_svc) != 0) { 1943 if (scf_error() != SCF_ERROR_NOT_FOUND) 1944 scfdie(); 1945 deleted = 1; 1946 } else if (scf_service_get_instance(g_svc, svcp->instname, g_inst) != 1947 0) { 1948 if (scf_error() != SCF_ERROR_NOT_FOUND) 1949 scfdie(); 1950 deleted = 1; 1951 } 1952 1953 if (!deleted) { 1954 if (inst_get_single_val(g_inst, SCF_PG_TM_COMMON_NAME, locale, 1955 SCF_TYPE_USTRING, g_value, g_value_sz, 0, 0, 1) == 0) 1956 /* EMPTY */; 1957 else if (inst_get_single_val(g_inst, SCF_PG_TM_COMMON_NAME, "C", 1958 SCF_TYPE_USTRING, g_value, g_value_sz, 0, 0, 1) != 0) 1959 (void) strcpy(g_value, "?"); 1960 1961 (void) printf(gettext(" (%s)\n"), g_value); 1962 } else { 1963 (void) putchar('\n'); 1964 } 1965 1966 stime = svcp->stime.tv_sec; 1967 tmp = localtime(&stime); 1968 1969 for (tbsz = 50; ; tbsz *= 2) { 1970 timebuf = safe_malloc(tbsz); 1971 if (strftime(timebuf, tbsz, NULL, tmp) != 0) 1972 break; 1973 free(timebuf); 1974 } 1975 1976 (void) printf(gettext(" State: %s since %s\n"), svcp->state, timebuf); 1977 1978 free(timebuf); 1979 1980 /* Reasons */ 1981 print_reasons(svcp, verbose); 1982 1983 if (!deleted) 1984 deleted = print_docs(g_inst, verbose); 1985 if (!deleted) 1986 print_logs(g_inst); 1987 1988 (void) determine_impact(svcp); 1989 1990 switch (uu_list_numnodes(svcp->impact)) { 1991 case 0: 1992 if (inst_running(svcp)) 1993 (void) puts(gettext("Impact: None.")); 1994 else 1995 (void) puts(gettext( 1996 "Impact: This service is not running.")); 1997 break; 1998 1999 case 1: 2000 if (!verbose) 2001 (void) puts(gettext("Impact: 1 dependent service " 2002 "is not running. (Use -v for list.)")); 2003 else 2004 (void) puts(gettext( 2005 "Impact: 1 dependent service is not running:")); 2006 break; 2007 2008 default: 2009 if (!verbose) 2010 (void) printf(gettext("Impact: %d dependent services " 2011 "are not running. (Use -v for list.)\n"), 2012 uu_list_numnodes(svcp->impact)); 2013 else 2014 (void) printf(gettext( 2015 "Impact: %d dependent services are not running:\n"), 2016 uu_list_numnodes(svcp->impact)); 2017 } 2018 2019 if (verbose) { 2020 for (spp = uu_list_first(svcp->impact); 2021 spp != NULL; 2022 spp = uu_list_next(svcp->impact, spp)) 2023 (void) printf(gettext(" svc:/%s:%s\n"), 2024 spp->svcp->svcname, spp->svcp->instname); 2025 } 2026 } 2027 2028 /* 2029 * Top level routine. 2030 */ 2031 2032 static int 2033 impact_compar(const void *a, const void *b) 2034 { 2035 int n, m; 2036 2037 n = uu_list_numnodes((*(inst_t **)a)->impact); 2038 m = uu_list_numnodes((*(inst_t **)b)->impact); 2039 2040 return (m - n); 2041 } 2042 2043 static int 2044 print_service_cb(void *verbose, scf_walkinfo_t *wip) 2045 { 2046 int r; 2047 inst_t *ip; 2048 2049 assert(wip->pg == NULL); 2050 2051 r = get_fmri(wip->fmri, NULL, &ip); 2052 assert(r != EINVAL); 2053 if (r == ENOENT) 2054 return (0); 2055 2056 assert(r == 0); 2057 assert(ip != NULL); 2058 2059 print_service(ip, (int)verbose); 2060 2061 return (0); 2062 } 2063 2064 void 2065 explain(int verbose, int argc, char **argv) 2066 { 2067 /* Initialize globals. */ 2068 x_init(); 2069 2070 /* Walk the graph and populate services with inst_t's */ 2071 load_services(); 2072 2073 /* Populate causes for services. */ 2074 determine_all_causes(); 2075 2076 if (argc > 0) { 2077 scf_error_t err; 2078 2079 check_msgbase(); 2080 2081 /* Call print_service() for each operand. */ 2082 2083 err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE, 2084 print_service_cb, (void *)verbose, &exit_status, uu_warn); 2085 if (err != 0) { 2086 uu_warn(gettext( 2087 "failed to iterate over instances: %s\n"), 2088 scf_strerror(err)); 2089 exit_status = UU_EXIT_FATAL; 2090 } 2091 } else { 2092 struct svcptr *spp; 2093 int n, i; 2094 inst_t **ary; 2095 2096 /* Sort g_causes. */ 2097 2098 n = uu_list_numnodes(g_causes); 2099 if (n == 0) 2100 return; 2101 2102 check_msgbase(); 2103 2104 ary = calloc(n, sizeof (*ary)); 2105 if (ary == NULL) 2106 uu_die(emsg_nomem); 2107 2108 i = 0; 2109 for (spp = uu_list_first(g_causes); 2110 spp != NULL; 2111 spp = uu_list_next(g_causes, spp)) { 2112 (void) determine_impact(spp->svcp); 2113 ary[i++] = spp->svcp; 2114 } 2115 2116 qsort(ary, n, sizeof (*ary), impact_compar); 2117 2118 /* Call print_service() for each service. */ 2119 2120 for (i = 0; i < n; ++i) 2121 print_service(ary[i], verbose); 2122 } 2123 } 2124