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