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