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