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