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