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