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