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