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