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