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