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