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