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