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