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