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