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