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, &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, &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 }