8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / svc / svcs / explain.c
blob533437cbee75d3170a2e8a14e7a0263c6de1b077
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2015, Joyent, Inc. All rights reserved.
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).
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.
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()).
53 * Because we always need at least impact counts, we always run
54 * determine_causes() on all services.
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().
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.
68 #include "svcs.h"
70 #include <sys/stat.h>
71 #include <sys/wait.h>
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>
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"
109 #define DEFAULT_MAN_PATH "/usr/share/man"
111 #define AUX_STATE_INVALID "invalid_aux_state"
113 #define uu_list_append(lst, e) uu_list_insert_before(lst, NULL, e)
115 #define bad_error(func, err) \
116 uu_panic("%s:%d: %s() failed with unknown error %d.\n", \
117 __FILE__, __LINE__, func, err);
119 typedef struct {
120 const char *svcname;
121 const char *instname;
123 /* restarter pg properties */
124 char state[MAX_SCF_STATE_STRING_SZ];
125 char next_state[MAX_SCF_STATE_STRING_SZ];
126 struct timeval stime;
127 const char *aux_state;
128 const char *aux_fmri;
129 int64_t start_method_waitstatus;
131 uint8_t enabled;
132 int temporary;
133 const char *restarter;
134 uu_list_t *dependencies; /* list of dependency_group's */
136 int active; /* In use? (cycle detection) */
137 int restarter_bad;
138 const char *summary;
139 uu_list_t *baddeps; /* list of dependency's */
140 uu_list_t *causes; /* list of svcptrs */
141 uu_list_t *impact_dependents; /* list of svcptrs */
142 uu_list_t *impact; /* list of svcptrs */
144 uu_list_node_t node;
145 } inst_t;
147 typedef struct service {
148 const char *svcname;
149 uu_list_t *instances;
150 struct service *next;
151 } svc_t;
153 struct svcptr {
154 inst_t *svcp;
155 inst_t *next_hop;
156 uu_list_node_t node;
159 struct dependency_group {
160 enum { DGG_REQALL, DGG_REQANY, DGG_OPTALL, DGG_EXCALL } grouping;
161 const char *type;
162 uu_list_t *entities; /* List of struct dependency's */
163 uu_list_node_t node;
166 struct dependency {
167 const char *fmri;
168 uu_list_node_t node;
171 /* Hash table of service names -> svc_t's */
172 #define SVC_HASH_NBUCKETS 256
173 #define SVC_HASH_MASK (SVC_HASH_NBUCKETS - 1)
175 static svc_t **services;
177 static uu_list_pool_t *insts, *svcptrs, *depgroups, *deps;
178 static uu_list_t *g_causes; /* list of svcptrs */
180 static scf_scope_t *g_local_scope;
181 static scf_service_t *g_svc;
182 static scf_instance_t *g_inst;
183 static scf_snapshot_t *g_snap;
184 static scf_propertygroup_t *g_pg;
185 static scf_property_t *g_prop;
186 static scf_value_t *g_val;
187 static scf_iter_t *g_iter, *g_viter;
188 static char *g_fmri, *g_value;
189 static size_t g_fmri_sz, g_value_sz;
190 static const char *g_msgbase = "http://illumos.org/msg/";
192 static char *emsg_nomem;
193 static char *emsg_invalid_dep;
195 extern scf_handle_t *h;
196 extern char *g_zonename;
198 /* ARGSUSED */
199 static int
200 svcptr_compare(struct svcptr *a, struct svcptr *b, void *data)
202 return (b->svcp - a->svcp);
205 static uint32_t
206 hash_name(const char *name)
208 uint32_t h = 0, g;
209 const char *p;
211 for (p = name; *p != '\0'; ++p) {
212 h = (h << 4) + *p;
213 if ((g = (h & 0xf0000000)) != 0) {
214 h ^= (g >> 24);
215 h ^= g;
219 return (h);
222 static void
223 x_init(void)
225 emsg_nomem = gettext("Out of memory.\n");
226 emsg_invalid_dep =
227 gettext("svc:/%s:%s has invalid dependency \"%s\".\n");
229 services = calloc(SVC_HASH_NBUCKETS, sizeof (*services));
230 if (services == NULL)
231 uu_die(emsg_nomem);
233 insts = uu_list_pool_create("insts", sizeof (inst_t),
234 offsetof(inst_t, node), NULL, UU_LIST_POOL_DEBUG);
235 svcptrs = uu_list_pool_create("svcptrs", sizeof (struct svcptr),
236 offsetof(struct svcptr, node), (uu_compare_fn_t *)svcptr_compare,
237 UU_LIST_POOL_DEBUG);
238 depgroups = uu_list_pool_create("depgroups",
239 sizeof (struct dependency_group),
240 offsetof(struct dependency_group, node), NULL, UU_LIST_POOL_DEBUG);
241 deps = uu_list_pool_create("deps", sizeof (struct dependency),
242 offsetof(struct dependency, node), NULL, UU_LIST_POOL_DEBUG);
243 g_causes = uu_list_create(svcptrs, NULL, UU_LIST_DEBUG);
244 if (insts == NULL || svcptrs == NULL || depgroups == NULL ||
245 deps == NULL || g_causes == NULL)
246 uu_die(emsg_nomem);
248 if ((g_local_scope = scf_scope_create(h)) == NULL ||
249 (g_svc = scf_service_create(h)) == NULL ||
250 (g_inst = scf_instance_create(h)) == NULL ||
251 (g_snap = scf_snapshot_create(h)) == NULL ||
252 (g_pg = scf_pg_create(h)) == NULL ||
253 (g_prop = scf_property_create(h)) == NULL ||
254 (g_val = scf_value_create(h)) == NULL ||
255 (g_iter = scf_iter_create(h)) == NULL ||
256 (g_viter = scf_iter_create(h)) == NULL)
257 scfdie();
259 if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, g_local_scope) != 0)
260 scfdie();
262 g_fmri_sz = max_scf_fmri_length + 1;
263 g_fmri = safe_malloc(g_fmri_sz);
265 g_value_sz = max_scf_value_length + 1;
266 g_value = safe_malloc(g_value_sz);
270 * Repository loading routines.
274 * Returns
275 * 0 - success
276 * ECANCELED - inst was deleted
277 * EINVAL - inst is invalid
279 static int
280 load_dependencies(inst_t *svcp, scf_instance_t *inst)
282 scf_snapshot_t *snap;
283 struct dependency_group *dg;
284 struct dependency *d;
285 int r;
287 assert(svcp->dependencies == NULL);
288 svcp->dependencies = uu_list_create(depgroups, svcp, UU_LIST_DEBUG);
289 if (svcp->dependencies == NULL)
290 uu_die(emsg_nomem);
292 if (scf_instance_get_snapshot(inst, "running", g_snap) == 0) {
293 snap = g_snap;
294 } else {
295 if (scf_error() != SCF_ERROR_NOT_FOUND)
296 scfdie();
298 snap = NULL;
301 if (scf_iter_instance_pgs_typed_composed(g_iter, inst, snap,
302 SCF_GROUP_DEPENDENCY) != 0) {
303 if (scf_error() != SCF_ERROR_DELETED)
304 scfdie();
305 return (ECANCELED);
308 for (;;) {
309 r = scf_iter_next_pg(g_iter, g_pg);
310 if (r == 0)
311 break;
312 if (r != 1) {
313 if (scf_error() != SCF_ERROR_DELETED)
314 scfdie();
315 return (ECANCELED);
318 dg = safe_malloc(sizeof (*dg));
319 (void) memset(dg, 0, sizeof (*dg));
320 dg->entities = uu_list_create(deps, dg, UU_LIST_DEBUG);
321 if (dg->entities == NULL)
322 uu_die(emsg_nomem);
324 if (pg_get_single_val(g_pg, SCF_PROPERTY_GROUPING,
325 SCF_TYPE_ASTRING, g_value, g_value_sz, 0) != 0)
326 return (EINVAL);
328 if (strcmp(g_value, "require_all") == 0)
329 dg->grouping = DGG_REQALL;
330 else if (strcmp(g_value, "require_any") == 0)
331 dg->grouping = DGG_REQANY;
332 else if (strcmp(g_value, "optional_all") == 0)
333 dg->grouping = DGG_OPTALL;
334 else if (strcmp(g_value, "exclude_all") == 0)
335 dg->grouping = DGG_EXCALL;
336 else {
337 (void) fprintf(stderr, gettext("svc:/%s:%s has "
338 "dependency with unknown type \"%s\".\n"),
339 svcp->svcname, svcp->instname, g_value);
340 return (EINVAL);
343 if (pg_get_single_val(g_pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
344 g_value, g_value_sz, 0) != 0)
345 return (EINVAL);
346 dg->type = safe_strdup(g_value);
348 if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) !=
349 0) {
350 switch (scf_error()) {
351 case SCF_ERROR_NOT_FOUND:
352 (void) fprintf(stderr, gettext("svc:/%s:%s has "
353 "dependency without an entities "
354 "property.\n"), svcp->svcname,
355 svcp->instname);
356 return (EINVAL);
358 case SCF_ERROR_DELETED:
359 return (ECANCELED);
361 default:
362 scfdie();
366 if (scf_iter_property_values(g_viter, g_prop) != 0) {
367 if (scf_error() != SCF_ERROR_DELETED)
368 scfdie();
369 return (ECANCELED);
372 for (;;) {
373 r = scf_iter_next_value(g_viter, g_val);
374 if (r == 0)
375 break;
376 if (r != 1) {
377 if (scf_error() != SCF_ERROR_DELETED)
378 scfdie();
379 return (ECANCELED);
382 d = safe_malloc(sizeof (*d));
383 d->fmri = safe_malloc(max_scf_fmri_length + 1);
385 if (scf_value_get_astring(g_val, (char *)d->fmri,
386 max_scf_fmri_length + 1) < 0)
387 scfdie();
389 uu_list_node_init(d, &d->node, deps);
390 (void) uu_list_append(dg->entities, d);
393 uu_list_node_init(dg, &dg->node, depgroups);
394 r = uu_list_append(svcp->dependencies, dg);
395 assert(r == 0);
398 return (0);
401 static void
402 add_instance(const char *svcname, const char *instname, scf_instance_t *inst)
404 inst_t *instp;
405 svc_t *svcp;
406 int have_enabled = 0;
407 uint8_t i;
408 uint32_t h;
409 int r;
411 h = hash_name(svcname) & SVC_HASH_MASK;
412 for (svcp = services[h]; svcp != NULL; svcp = svcp->next) {
413 if (strcmp(svcp->svcname, svcname) == 0)
414 break;
417 if (svcp == NULL) {
418 svcp = safe_malloc(sizeof (*svcp));
419 svcp->svcname = safe_strdup(svcname);
420 svcp->instances = uu_list_create(insts, svcp, UU_LIST_DEBUG);
421 if (svcp->instances == NULL)
422 uu_die(emsg_nomem);
423 svcp->next = services[h];
424 services[h] = svcp;
427 instp = safe_malloc(sizeof (*instp));
428 (void) memset(instp, 0, sizeof (*instp));
429 instp->svcname = svcp->svcname;
430 instp->instname = safe_strdup(instname);
431 instp->impact_dependents =
432 uu_list_create(svcptrs, instp, UU_LIST_DEBUG);
433 if (instp->impact_dependents == NULL)
434 uu_die(emsg_nomem);
436 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) != 0) {
437 switch (scf_error()) {
438 case SCF_ERROR_DELETED:
439 return;
441 case SCF_ERROR_NOT_FOUND:
442 (void) fprintf(stderr, gettext("svc:/%s:%s has no "
443 "\"%s\" property group; ignoring.\n"),
444 instp->svcname, instp->instname, SCF_PG_RESTARTER);
445 return;
447 default:
448 scfdie();
452 if (pg_get_single_val(g_pg, SCF_PROPERTY_STATE, SCF_TYPE_ASTRING,
453 (void *)instp->state, sizeof (instp->state), 0) != 0)
454 return;
456 if (pg_get_single_val(g_pg, SCF_PROPERTY_NEXT_STATE, SCF_TYPE_ASTRING,
457 (void *)instp->next_state, sizeof (instp->next_state), 0) != 0)
458 return;
460 if (pg_get_single_val(g_pg, SCF_PROPERTY_STATE_TIMESTAMP,
461 SCF_TYPE_TIME, &instp->stime, 0, 0) != 0)
462 return;
464 /* restarter may not set aux_state, allow to continue in that case */
465 if (pg_get_single_val(g_pg, SCF_PROPERTY_AUX_STATE, SCF_TYPE_ASTRING,
466 g_fmri, g_fmri_sz, 0) == 0)
467 instp->aux_state = safe_strdup(g_fmri);
468 else
469 instp->aux_state = safe_strdup(AUX_STATE_INVALID);
471 (void) pg_get_single_val(g_pg, SCF_PROPERTY_START_METHOD_WAITSTATUS,
472 SCF_TYPE_INTEGER, &instp->start_method_waitstatus, 0, 0);
474 /* Get the optional auxiliary_fmri */
475 if (pg_get_single_val(g_pg, SCF_PROPERTY_AUX_FMRI, SCF_TYPE_ASTRING,
476 g_fmri, g_fmri_sz, 0) == 0)
477 instp->aux_fmri = safe_strdup(g_fmri);
479 if (scf_instance_get_pg(inst, SCF_PG_GENERAL_OVR, g_pg) == 0) {
480 if (pg_get_single_val(g_pg, SCF_PROPERTY_ENABLED,
481 SCF_TYPE_BOOLEAN, &instp->enabled, 0, 0) == 0)
482 have_enabled = 1;
483 } else {
484 switch (scf_error()) {
485 case SCF_ERROR_NOT_FOUND:
486 break;
488 case SCF_ERROR_DELETED:
489 return;
491 default:
492 scfdie();
496 if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL, g_pg) !=
497 0) {
498 switch (scf_error()) {
499 case SCF_ERROR_DELETED:
500 case SCF_ERROR_NOT_FOUND:
501 return;
503 default:
504 scfdie();
508 if (pg_get_single_val(g_pg, SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN,
509 &i, 0, 0) != 0)
510 return;
511 if (!have_enabled) {
512 instp->enabled = i;
513 instp->temporary = 0;
514 } else {
515 instp->temporary = (instp->enabled != i);
518 if (pg_get_single_val(g_pg, SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING,
519 g_fmri, g_fmri_sz, 0) == 0)
520 instp->restarter = safe_strdup(g_fmri);
521 else
522 instp->restarter = SCF_SERVICE_STARTD;
524 if (strcmp(instp->state, SCF_STATE_STRING_OFFLINE) == 0 &&
525 load_dependencies(instp, inst) != 0)
526 return;
528 uu_list_node_init(instp, &instp->node, insts);
529 r = uu_list_append(svcp->instances, instp);
530 assert(r == 0);
533 static void
534 load_services(void)
536 scf_iter_t *siter, *iiter;
537 int r;
538 char *svcname, *instname;
540 if ((siter = scf_iter_create(h)) == NULL ||
541 (iiter = scf_iter_create(h)) == NULL)
542 scfdie();
544 svcname = safe_malloc(max_scf_name_length + 1);
545 instname = safe_malloc(max_scf_name_length + 1);
547 if (scf_iter_scope_services(siter, g_local_scope) != 0)
548 scfdie();
550 for (;;) {
551 r = scf_iter_next_service(siter, g_svc);
552 if (r == 0)
553 break;
554 if (r != 1)
555 scfdie();
557 if (scf_service_get_name(g_svc, svcname,
558 max_scf_name_length + 1) < 0) {
559 if (scf_error() != SCF_ERROR_DELETED)
560 scfdie();
561 continue;
564 if (scf_iter_service_instances(iiter, g_svc) != 0) {
565 if (scf_error() != SCF_ERROR_DELETED)
566 scfdie();
567 continue;
570 for (;;) {
571 r = scf_iter_next_instance(iiter, g_inst);
572 if (r == 0)
573 break;
574 if (r != 1) {
575 if (scf_error() != SCF_ERROR_DELETED)
576 scfdie();
577 break;
580 if (scf_instance_get_name(g_inst, instname,
581 max_scf_name_length + 1) < 0) {
582 if (scf_error() != SCF_ERROR_DELETED)
583 scfdie();
584 continue;
587 add_instance(svcname, instname, g_inst);
591 free(svcname);
592 free(instname);
593 scf_iter_destroy(siter);
594 scf_iter_destroy(iiter);
598 * Dependency analysis routines.
601 static void
602 add_svcptr(uu_list_t *lst, inst_t *svcp)
604 struct svcptr *spp;
605 uu_list_index_t idx;
606 int r;
608 spp = safe_malloc(sizeof (*spp));
609 spp->svcp = svcp;
610 spp->next_hop = NULL;
612 if (uu_list_find(lst, spp, NULL, &idx) != NULL) {
613 free(spp);
614 return;
617 uu_list_node_init(spp, &spp->node, svcptrs);
618 r = uu_list_append(lst, spp);
619 assert(r == 0);
622 static int determine_causes(inst_t *, void *);
625 * Determine the causes of src and add them to the causes list of dst.
626 * Returns ELOOP if src is active, and 0 otherwise.
628 static int
629 add_causes(inst_t *dst, inst_t *src)
631 struct svcptr *spp, *copy;
632 uu_list_index_t idx;
634 if (determine_causes(src, (void *)1) != UU_WALK_NEXT) {
635 /* Dependency cycle. */
636 (void) fprintf(stderr, " svc:/%s:%s\n", dst->svcname,
637 dst->instname);
638 return (ELOOP);
641 add_svcptr(src->impact_dependents, dst);
643 for (spp = uu_list_first(src->causes);
644 spp != NULL;
645 spp = uu_list_next(src->causes, spp)) {
646 if (uu_list_find(dst->causes, spp, NULL, &idx) != NULL)
647 continue;
649 copy = safe_malloc(sizeof (*copy));
650 copy->svcp = spp->svcp;
651 copy->next_hop = src;
652 uu_list_node_init(copy, &copy->node, svcptrs);
653 uu_list_insert(dst->causes, copy, idx);
655 add_svcptr(g_causes, spp->svcp);
658 return (0);
661 static int
662 inst_running(inst_t *ip)
664 return (strcmp(ip->state, SCF_STATE_STRING_ONLINE) == 0 ||
665 strcmp(ip->state, SCF_STATE_STRING_DEGRADED) == 0);
668 static int
669 inst_running_or_maint(inst_t *ip)
671 return (inst_running(ip) ||
672 strcmp(ip->state, SCF_STATE_STRING_MAINT) == 0);
675 static svc_t *
676 get_svc(const char *sn)
678 uint32_t h;
679 svc_t *svcp;
681 h = hash_name(sn) & SVC_HASH_MASK;
683 for (svcp = services[h]; svcp != NULL; svcp = svcp->next) {
684 if (strcmp(svcp->svcname, sn) == 0)
685 break;
688 return (svcp);
691 /* ARGSUSED */
692 static inst_t *
693 get_inst(svc_t *svcp, const char *in)
695 inst_t *instp;
697 for (instp = uu_list_first(svcp->instances);
698 instp != NULL;
699 instp = uu_list_next(svcp->instances, instp)) {
700 if (strcmp(instp->instname, in) == 0)
701 return (instp);
704 return (NULL);
707 static int
708 get_fmri(const char *fmri, svc_t **spp, inst_t **ipp)
710 const char *sn, *in;
711 svc_t *sp;
712 inst_t *ip;
714 if (strlcpy(g_fmri, fmri, g_fmri_sz) >= g_fmri_sz)
715 return (EINVAL);
717 if (scf_parse_svc_fmri(g_fmri, NULL, &sn, &in, NULL, NULL) != 0)
718 return (EINVAL);
720 if (sn == NULL)
721 return (EINVAL);
723 sp = get_svc(sn);
724 if (sp == NULL)
725 return (ENOENT);
727 if (in != NULL) {
728 ip = get_inst(sp, in);
729 if (ip == NULL)
730 return (ENOENT);
733 if (spp != NULL)
734 *spp = sp;
735 if (ipp != NULL)
736 *ipp = ((in == NULL) ? NULL : ip);
738 return (0);
741 static int
742 process_reqall(inst_t *svcp, struct dependency_group *dg)
744 uu_list_walk_t *walk;
745 struct dependency *d;
746 int r, svcrunning;
747 svc_t *sp;
748 inst_t *ip;
750 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST);
751 if (walk == NULL)
752 uu_die(emsg_nomem);
754 while ((d = uu_list_walk_next(walk)) != NULL) {
755 r = get_fmri(d->fmri, &sp, &ip);
756 switch (r) {
757 case EINVAL:
758 /* LINTED */
759 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname,
760 svcp->instname, d->fmri);
761 continue;
763 case ENOENT:
764 uu_list_remove(dg->entities, d);
765 r = uu_list_append(svcp->baddeps, d);
766 assert(r == 0);
767 continue;
769 case 0:
770 break;
772 default:
773 bad_error("get_fmri", r);
776 if (ip != NULL) {
777 if (inst_running(ip))
778 continue;
779 r = add_causes(svcp, ip);
780 if (r != 0) {
781 assert(r == ELOOP);
782 return (r);
784 continue;
787 svcrunning = 0;
789 for (ip = uu_list_first(sp->instances);
790 ip != NULL;
791 ip = uu_list_next(sp->instances, ip)) {
792 if (inst_running(ip))
793 svcrunning = 1;
796 if (!svcrunning) {
797 for (ip = uu_list_first(sp->instances);
798 ip != NULL;
799 ip = uu_list_next(sp->instances, ip)) {
800 r = add_causes(svcp, ip);
801 if (r != 0) {
802 assert(r == ELOOP);
803 uu_list_walk_end(walk);
804 return (r);
810 uu_list_walk_end(walk);
811 return (0);
814 static int
815 process_reqany(inst_t *svcp, struct dependency_group *dg)
817 svc_t *sp;
818 inst_t *ip;
819 struct dependency *d;
820 int r;
821 uu_list_walk_t *walk;
823 for (d = uu_list_first(dg->entities);
824 d != NULL;
825 d = uu_list_next(dg->entities, d)) {
826 r = get_fmri(d->fmri, &sp, &ip);
827 switch (r) {
828 case 0:
829 break;
831 case EINVAL:
832 /* LINTED */
833 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname,
834 svcp->instname, d->fmri);
835 continue;
837 case ENOENT:
838 continue;
840 default:
841 bad_error("eval_svc_dep", r);
844 if (ip != NULL) {
845 if (inst_running(ip))
846 return (0);
847 continue;
850 for (ip = uu_list_first(sp->instances);
851 ip != NULL;
852 ip = uu_list_next(sp->instances, ip)) {
853 if (inst_running(ip))
854 return (0);
859 * The dependency group is not satisfied. Add all unsatisfied members
860 * to the cause list.
863 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST);
864 if (walk == NULL)
865 uu_die(emsg_nomem);
867 while ((d = uu_list_walk_next(walk)) != NULL) {
868 r = get_fmri(d->fmri, &sp, &ip);
869 switch (r) {
870 case 0:
871 break;
873 case ENOENT:
874 uu_list_remove(dg->entities, d);
875 r = uu_list_append(svcp->baddeps, d);
876 assert(r == 0);
877 continue;
879 case EINVAL:
880 /* Should have caught above. */
881 default:
882 bad_error("eval_svc_dep", r);
885 if (ip != NULL) {
886 if (inst_running(ip))
887 continue;
888 r = add_causes(svcp, ip);
889 if (r != 0) {
890 assert(r == ELOOP);
891 return (r);
893 continue;
896 for (ip = uu_list_first(sp->instances);
897 ip != NULL;
898 ip = uu_list_next(sp->instances, ip)) {
899 if (inst_running(ip))
900 continue;
901 r = add_causes(svcp, ip);
902 if (r != 0) {
903 assert(r == ELOOP);
904 return (r);
909 return (0);
912 static int
913 process_optall(inst_t *svcp, struct dependency_group *dg)
915 uu_list_walk_t *walk;
916 struct dependency *d;
917 int r;
918 inst_t *ip;
919 svc_t *sp;
921 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST);
922 if (walk == NULL)
923 uu_die(emsg_nomem);
925 while ((d = uu_list_walk_next(walk)) != NULL) {
926 r = get_fmri(d->fmri, &sp, &ip);
928 switch (r) {
929 case 0:
930 break;
932 case EINVAL:
933 /* LINTED */
934 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname,
935 svcp->instname, d->fmri);
936 continue;
938 case ENOENT:
939 continue;
941 default:
942 bad_error("get_fmri", r);
945 if (ip != NULL) {
946 if ((ip->enabled != 0) && !inst_running_or_maint(ip)) {
947 r = add_causes(svcp, ip);
948 if (r != 0) {
949 assert(r == ELOOP);
950 uu_list_walk_end(walk);
951 return (r);
954 continue;
957 for (ip = uu_list_first(sp->instances);
958 ip != NULL;
959 ip = uu_list_next(sp->instances, ip)) {
960 if ((ip->enabled != 0) && !inst_running_or_maint(ip)) {
961 r = add_causes(svcp, ip);
962 if (r != 0) {
963 assert(r == ELOOP);
964 uu_list_walk_end(walk);
965 return (r);
971 uu_list_walk_end(walk);
972 return (0);
975 static int
976 process_excall(inst_t *svcp, struct dependency_group *dg)
978 struct dependency *d;
979 int r;
980 svc_t *sp;
981 inst_t *ip;
983 for (d = uu_list_first(dg->entities);
984 d != NULL;
985 d = uu_list_next(dg->entities, d)) {
986 r = get_fmri(d->fmri, &sp, &ip);
988 switch (r) {
989 case 0:
990 break;
992 case EINVAL:
993 /* LINTED */
994 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname,
995 svcp->instname, d->fmri);
996 continue;
998 case ENOENT:
999 continue;
1001 default:
1002 bad_error("eval_svc_dep", r);
1005 if (ip != NULL) {
1006 if (inst_running(ip)) {
1007 r = add_causes(svcp, ip);
1008 if (r != 0) {
1009 assert(r == ELOOP);
1010 return (r);
1013 continue;
1016 for (ip = uu_list_first(sp->instances);
1017 ip != NULL;
1018 ip = uu_list_next(sp->instances, ip)) {
1019 if (inst_running(ip)) {
1020 r = add_causes(svcp, ip);
1021 if (r != 0) {
1022 assert(r == ELOOP);
1023 return (r);
1029 return (0);
1032 static int
1033 process_svc_dg(inst_t *svcp, struct dependency_group *dg)
1035 switch (dg->grouping) {
1036 case DGG_REQALL:
1037 return (process_reqall(svcp, dg));
1039 case DGG_REQANY:
1040 return (process_reqany(svcp, dg));
1042 case DGG_OPTALL:
1043 return (process_optall(svcp, dg));
1045 case DGG_EXCALL:
1046 return (process_excall(svcp, dg));
1048 default:
1049 #ifndef NDEBUG
1050 (void) fprintf(stderr,
1051 "%s:%d: Unknown dependency grouping %d.\n", __FILE__,
1052 __LINE__, dg->grouping);
1053 #endif
1054 abort();
1055 /* NOTREACHED */
1060 * Returns
1061 * EINVAL - fmri is not a valid FMRI
1062 * 0 - the file indicated by fmri is missing
1063 * 1 - the file indicated by fmri is present
1065 static int
1066 eval_file_dep(const char *fmri)
1068 const char *path;
1069 struct stat st;
1071 if (strncmp(fmri, "file:", sizeof ("file:") - 1) != 0)
1072 return (EINVAL);
1074 path = fmri + (sizeof ("file:") - 1);
1076 if (path[0] != '/')
1077 return (EINVAL);
1079 if (path[1] == '/') {
1080 path += 2;
1081 if (strncmp(path, "localhost/", sizeof ("localhost/") - 1) == 0)
1082 path += sizeof ("localhost") - 1;
1083 else if (path[0] != '/')
1084 return (EINVAL);
1087 return (stat(path, &st) == 0 ? 1 : 0);
1090 static void
1091 process_file_dg(inst_t *svcp, struct dependency_group *dg)
1093 uu_list_walk_t *walk;
1094 struct dependency *d, **deps;
1095 int r, i = 0, any_satisfied = 0;
1097 if (dg->grouping == DGG_REQANY) {
1098 deps = calloc(uu_list_numnodes(dg->entities), sizeof (*deps));
1099 if (deps == NULL)
1100 uu_die(emsg_nomem);
1103 walk = uu_list_walk_start(dg->entities, UU_WALK_ROBUST);
1104 if (walk == NULL)
1105 uu_die(emsg_nomem);
1107 while ((d = uu_list_walk_next(walk)) != NULL) {
1108 r = eval_file_dep(d->fmri);
1109 if (r == EINVAL) {
1110 /* LINTED */
1111 (void) fprintf(stderr, emsg_invalid_dep, svcp->svcname,
1112 svcp->instname, d->fmri);
1113 continue;
1116 assert(r == 0 || r == 1);
1118 switch (dg->grouping) {
1119 case DGG_REQALL:
1120 case DGG_OPTALL:
1121 if (r == 0) {
1122 uu_list_remove(dg->entities, d);
1123 r = uu_list_append(svcp->baddeps, d);
1124 assert(r == 0);
1126 break;
1128 case DGG_REQANY:
1129 if (r == 1)
1130 any_satisfied = 1;
1131 else
1132 deps[i++] = d;
1133 break;
1135 case DGG_EXCALL:
1136 if (r == 1) {
1137 uu_list_remove(dg->entities, d);
1138 r = uu_list_append(svcp->baddeps, d);
1139 assert(r == 0);
1141 break;
1143 default:
1144 #ifndef NDEBUG
1145 (void) fprintf(stderr, "%s:%d: Unknown grouping %d.\n",
1146 __FILE__, __LINE__, dg->grouping);
1147 #endif
1148 abort();
1152 uu_list_walk_end(walk);
1154 if (dg->grouping != DGG_REQANY)
1155 return;
1157 if (!any_satisfied) {
1158 while (--i >= 0) {
1159 uu_list_remove(dg->entities, deps[i]);
1160 r = uu_list_append(svcp->baddeps, deps[i]);
1161 assert(r == 0);
1165 free(deps);
1169 * Populate the causes list of svcp. This function should not return with
1170 * causes empty.
1172 static int
1173 determine_causes(inst_t *svcp, void *canfailp)
1175 struct dependency_group *dg;
1176 int r;
1178 if (svcp->active) {
1179 (void) fprintf(stderr, gettext("Dependency cycle detected:\n"
1180 " svc:/%s:%s\n"), svcp->svcname, svcp->instname);
1181 return ((int)canfailp != 0 ? UU_WALK_ERROR : UU_WALK_NEXT);
1184 if (svcp->causes != NULL)
1185 return (UU_WALK_NEXT);
1187 svcp->causes = uu_list_create(svcptrs, svcp, UU_LIST_DEBUG);
1188 svcp->baddeps = uu_list_create(deps, svcp, UU_LIST_DEBUG);
1189 if (svcp->causes == NULL || svcp->baddeps == NULL)
1190 uu_die(emsg_nomem);
1192 if (inst_running(svcp) ||
1193 strcmp(svcp->state, SCF_STATE_STRING_UNINIT) == 0) {
1195 * If we're running, add a self-pointer in case we're
1196 * excluding another service.
1198 add_svcptr(svcp->causes, svcp);
1199 return (UU_WALK_NEXT);
1202 if (strcmp(svcp->state, SCF_STATE_STRING_MAINT) == 0) {
1203 add_svcptr(svcp->causes, svcp);
1204 add_svcptr(g_causes, svcp);
1205 return (UU_WALK_NEXT);
1208 if (strcmp(svcp->state, SCF_STATE_STRING_DISABLED) == 0) {
1209 add_svcptr(svcp->causes, svcp);
1210 if (svcp->enabled != 0)
1211 add_svcptr(g_causes, svcp);
1213 return (UU_WALK_NEXT);
1216 if (strcmp(svcp->state, SCF_STATE_STRING_OFFLINE) != 0) {
1217 (void) fprintf(stderr,
1218 gettext("svc:/%s:%s has invalid state \"%s\".\n"),
1219 svcp->svcname, svcp->instname, svcp->state);
1220 add_svcptr(svcp->causes, svcp);
1221 add_svcptr(g_causes, svcp);
1222 return (UU_WALK_NEXT);
1225 if (strcmp(svcp->next_state, SCF_STATE_STRING_NONE) != 0) {
1226 add_svcptr(svcp->causes, svcp);
1227 add_svcptr(g_causes, svcp);
1228 return (UU_WALK_NEXT);
1231 svcp->active = 1;
1234 * Dependency analysis can add elements to our baddeps list (absent
1235 * dependency, unsatisfied file dependency), or to our cause list
1236 * (unsatisfied dependency).
1238 for (dg = uu_list_first(svcp->dependencies);
1239 dg != NULL;
1240 dg = uu_list_next(svcp->dependencies, dg)) {
1241 if (strcmp(dg->type, "path") == 0) {
1242 process_file_dg(svcp, dg);
1243 } else if (strcmp(dg->type, "service") == 0) {
1244 int r;
1246 r = process_svc_dg(svcp, dg);
1247 if (r != 0) {
1248 assert(r == ELOOP);
1249 svcp->active = 0;
1250 return ((int)canfailp != 0 ?
1251 UU_WALK_ERROR : UU_WALK_NEXT);
1253 } else {
1254 (void) fprintf(stderr, gettext("svc:/%s:%s has "
1255 "dependency group with invalid type \"%s\".\n"),
1256 svcp->svcname, svcp->instname, dg->type);
1260 if (uu_list_numnodes(svcp->causes) == 0) {
1261 if (uu_list_numnodes(svcp->baddeps) > 0) {
1262 add_svcptr(g_causes, svcp);
1263 add_svcptr(svcp->causes, svcp);
1264 } else {
1265 inst_t *restarter;
1267 r = get_fmri(svcp->restarter, NULL, &restarter);
1268 if (r == 0 && !inst_running(restarter)) {
1269 r = add_causes(svcp, restarter);
1270 if (r != 0) {
1271 assert(r == ELOOP);
1272 svcp->active = 0;
1273 return ((int)canfailp != 0 ?
1274 UU_WALK_ERROR : UU_WALK_NEXT);
1276 } else {
1277 svcp->restarter_bad = r;
1278 add_svcptr(svcp->causes, svcp);
1279 add_svcptr(g_causes, svcp);
1284 assert(uu_list_numnodes(svcp->causes) > 0);
1286 svcp->active = 0;
1287 return (UU_WALK_NEXT);
1290 static void
1291 determine_all_causes(void)
1293 svc_t *svcp;
1294 int i;
1296 for (i = 0; i < SVC_HASH_NBUCKETS; ++i) {
1297 for (svcp = services[i]; svcp != NULL; svcp = svcp->next)
1298 (void) uu_list_walk(svcp->instances,
1299 (uu_walk_fn_t *)determine_causes, 0, 0);
1304 * Returns
1305 * 0 - success
1306 * ELOOP - dependency cycle detected
1308 static int
1309 determine_impact(inst_t *ip)
1311 struct svcptr *idsp, *spp, *copy;
1312 uu_list_index_t idx;
1314 if (ip->active) {
1315 (void) fprintf(stderr, gettext("Dependency cycle detected:\n"
1316 " svc:/%s:%s\n"), ip->svcname, ip->instname);
1317 return (ELOOP);
1320 if (ip->impact != NULL)
1321 return (0);
1323 ip->impact = uu_list_create(svcptrs, ip, UU_LIST_DEBUG);
1324 if (ip->impact == NULL)
1325 uu_die(emsg_nomem);
1326 ip->active = 1;
1328 for (idsp = uu_list_first(ip->impact_dependents);
1329 idsp != NULL;
1330 idsp = uu_list_next(ip->impact_dependents, idsp)) {
1331 if (determine_impact(idsp->svcp) != 0) {
1332 (void) fprintf(stderr, " svc:/%s:%s\n",
1333 ip->svcname, ip->instname);
1334 return (ELOOP);
1337 add_svcptr(ip->impact, idsp->svcp);
1339 for (spp = uu_list_first(idsp->svcp->impact);
1340 spp != NULL;
1341 spp = uu_list_next(idsp->svcp->impact, spp)) {
1342 if (uu_list_find(ip->impact, spp, NULL, &idx) != NULL)
1343 continue;
1345 copy = safe_malloc(sizeof (*copy));
1346 copy->svcp = spp->svcp;
1347 copy->next_hop = NULL;
1348 uu_list_node_init(copy, &copy->node, svcptrs);
1349 uu_list_insert(ip->impact, copy, idx);
1353 ip->active = 0;
1354 return (0);
1358 * Printing routines.
1361 static void
1362 check_msgbase(void)
1364 if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL, g_inst,
1365 NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0) {
1366 if (scf_error() != SCF_ERROR_NOT_FOUND)
1367 scfdie();
1369 return;
1372 if (scf_instance_get_pg_composed(g_inst, NULL, "msg", g_pg) != 0) {
1373 switch (scf_error()) {
1374 case SCF_ERROR_NOT_FOUND:
1375 case SCF_ERROR_DELETED:
1376 return;
1378 default:
1379 scfdie();
1383 if (scf_pg_get_property(g_pg, "base", g_prop) != 0) {
1384 switch (scf_error()) {
1385 case SCF_ERROR_NOT_FOUND:
1386 case SCF_ERROR_DELETED:
1387 return;
1389 default:
1390 scfdie();
1394 if (scf_property_get_value(g_prop, g_val) != 0) {
1395 switch (scf_error()) {
1396 case SCF_ERROR_NOT_FOUND:
1397 case SCF_ERROR_CONSTRAINT_VIOLATED:
1398 case SCF_ERROR_PERMISSION_DENIED:
1399 g_msgbase = NULL;
1400 return;
1402 case SCF_ERROR_DELETED:
1403 return;
1405 default:
1406 scfdie();
1410 if (scf_value_get_astring(g_val, g_value, g_value_sz) < 0) {
1411 if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
1412 scfdie();
1413 return;
1416 g_msgbase = safe_strdup(g_value);
1419 static void
1420 determine_summary(inst_t *ip)
1422 if (ip->summary != NULL)
1423 return;
1425 if (inst_running(ip)) {
1426 ip->summary = gettext("is running.");
1427 return;
1430 if (strcmp(ip->state, SCF_STATE_STRING_UNINIT) == 0) {
1431 ip->summary = gettext("is uninitialized.");
1432 } else if (strcmp(ip->state, SCF_STATE_STRING_DISABLED) == 0) {
1433 if (!ip->temporary)
1434 ip->summary = gettext("is disabled.");
1435 else
1436 ip->summary = gettext("is temporarily disabled.");
1437 } else if (strcmp(ip->state, SCF_STATE_STRING_OFFLINE) == 0) {
1438 if (uu_list_numnodes(ip->baddeps) != 0)
1439 ip->summary = gettext("has missing dependencies.");
1440 else if (strcmp(ip->next_state, SCF_STATE_STRING_ONLINE) == 0)
1441 ip->summary = gettext("is starting.");
1442 else
1443 ip->summary = gettext("is offline.");
1444 } else if (strcmp(ip->state, SCF_STATE_STRING_MAINT) == 0) {
1445 if (strcmp(ip->aux_state, "administrative_request") == 0) {
1446 ip->summary = gettext("was taken down for maintenace "
1447 "by an administrator.");
1448 } else if (strcmp(ip->aux_state, "dependency_cycle") == 0) {
1449 ip->summary = gettext("completed a dependency cycle.");
1450 } else if (strcmp(ip->aux_state, "fault_threshold_reached") ==
1451 0) {
1452 ip->summary = gettext("is not running because "
1453 "a method failed repeatedly.");
1454 } else if (strcmp(ip->aux_state, "invalid_dependency") == 0) {
1455 ip->summary = gettext("has an invalid dependency.");
1456 } else if (strcmp(ip->aux_state, "invalid_restarter") == 0) {
1457 ip->summary = gettext("has an invalid restarter.");
1458 } else if (strcmp(ip->aux_state, "method_failed") == 0) {
1459 ip->summary = gettext("is not running because "
1460 "a method failed.");
1461 } else if (strcmp(ip->aux_state, "none") == 0) {
1462 ip->summary =
1463 gettext("is not running for an unknown reason.");
1464 } else if (strcmp(ip->aux_state, "restarting_too_quickly") ==
1465 0) {
1466 ip->summary = gettext("was restarting too quickly.");
1467 } else {
1468 ip->summary = gettext("requires maintenance.");
1470 } else {
1471 ip->summary = gettext("is in an invalid state.");
1475 static void
1476 print_method_failure(const inst_t *ip, const char **dcp)
1478 char buf[50];
1479 int stat = ip->start_method_waitstatus;
1481 if (stat != 0) {
1482 if (WIFEXITED(stat)) {
1483 if (WEXITSTATUS(stat) == SMF_EXIT_ERR_CONFIG) {
1484 (void) strlcpy(buf, gettext(
1485 "exited with $SMF_EXIT_ERR_CONFIG"),
1486 sizeof (buf));
1487 } else if (WEXITSTATUS(stat) == SMF_EXIT_ERR_FATAL) {
1488 (void) strlcpy(buf, gettext(
1489 "exited with $SMF_EXIT_ERR_FATAL"),
1490 sizeof (buf));
1491 } else {
1492 (void) snprintf(buf, sizeof (buf),
1493 gettext("exited with status %d"),
1494 WEXITSTATUS(stat));
1496 } else if (WIFSIGNALED(stat)) {
1497 if (WCOREDUMP(stat)) {
1498 if (strsignal(WTERMSIG(stat)) != NULL)
1499 (void) snprintf(buf, sizeof (buf),
1500 gettext("dumped core on %s (%d)"),
1501 strsignal(WTERMSIG(stat)),
1502 WTERMSIG(stat));
1503 else
1504 (void) snprintf(buf, sizeof (buf),
1505 gettext("dumped core signal %d"),
1506 WTERMSIG(stat));
1507 } else {
1508 if (strsignal(WTERMSIG(stat)) != NULL) {
1509 (void) snprintf(buf, sizeof (buf),
1510 gettext("died on %s (%d)"),
1511 strsignal(WTERMSIG(stat)),
1512 WTERMSIG(stat));
1513 } else {
1514 (void) snprintf(buf, sizeof (buf),
1515 gettext("died on signal %d"),
1516 WTERMSIG(stat));
1519 } else {
1520 goto fail;
1523 if (strcmp(ip->aux_state, "fault_threshold_reached") != 0)
1524 (void) printf(gettext("Reason: Start method %s.\n"),
1525 buf);
1526 else
1527 (void) printf(gettext("Reason: "
1528 "Start method failed repeatedly, last %s.\n"), buf);
1529 *dcp = DC_STARTFAIL;
1530 } else {
1531 fail:
1532 if (strcmp(ip->aux_state, "fault_threshold_reached") == 0)
1533 (void) puts(gettext(
1534 "Reason: Method failed repeatedly."));
1535 else
1536 (void) puts(gettext("Reason: Method failed."));
1537 *dcp = DC_METHFAIL;
1541 static void
1542 print_dependency_reasons(const inst_t *svcp, int verbose)
1544 struct dependency *d;
1545 struct svcptr *spp;
1546 const char *dc;
1549 * If we couldn't determine why the service is offline, then baddeps
1550 * will be empty and causes will have a pointer to self.
1552 if (uu_list_numnodes(svcp->baddeps) == 0 &&
1553 uu_list_numnodes(svcp->causes) == 1) {
1554 spp = uu_list_first(svcp->causes);
1555 if (spp->svcp == svcp) {
1556 switch (svcp->restarter_bad) {
1557 case 0:
1558 (void) puts(gettext("Reason: Unknown."));
1559 dc = DC_UNKNOWN;
1560 break;
1562 case EINVAL:
1563 (void) printf(gettext("Reason: "
1564 "Restarter \"%s\" is invalid.\n"),
1565 svcp->restarter);
1566 dc = DC_RSTRINVALID;
1567 break;
1569 case ENOENT:
1570 (void) printf(gettext("Reason: "
1571 "Restarter \"%s\" does not exist.\n"),
1572 svcp->restarter);
1573 dc = DC_RSTRABSENT;
1574 break;
1576 default:
1577 #ifndef NDEBUG
1578 (void) fprintf(stderr, "%s:%d: Bad "
1579 "restarter_bad value %d. Aborting.\n",
1580 __FILE__, __LINE__, svcp->restarter_bad);
1581 #endif
1582 abort();
1585 if (g_msgbase)
1586 (void) printf(gettext(" See: %s%s\n"),
1587 g_msgbase, dc);
1588 return;
1592 for (d = uu_list_first(svcp->baddeps);
1593 d != NULL;
1594 d = uu_list_next(svcp->baddeps, d)) {
1595 (void) printf(gettext("Reason: Dependency %s is absent.\n"),
1596 d->fmri);
1597 if (g_msgbase)
1598 (void) printf(gettext(" See: %s%s\n"), g_msgbase,
1599 DC_DEPABSENT);
1602 for (spp = uu_list_first(svcp->causes);
1603 spp != NULL && spp->svcp != svcp;
1604 spp = uu_list_next(svcp->causes, spp)) {
1605 determine_summary(spp->svcp);
1607 if (inst_running(spp->svcp)) {
1608 (void) printf(gettext("Reason: "
1609 "Service svc:/%s:%s is running.\n"),
1610 spp->svcp->svcname, spp->svcp->instname);
1611 dc = DC_DEPRUNNING;
1612 } else {
1613 if (snprintf(NULL, 0,
1614 gettext("Reason: Service svc:/%s:%s %s"),
1615 spp->svcp->svcname, spp->svcp->instname,
1616 spp->svcp->summary) <= 80) {
1617 (void) printf(gettext(
1618 "Reason: Service svc:/%s:%s %s\n"),
1619 spp->svcp->svcname, spp->svcp->instname,
1620 spp->svcp->summary);
1621 } else {
1622 (void) printf(gettext(
1623 "Reason: Service svc:/%s:%s\n"
1624 " %s\n"), spp->svcp->svcname,
1625 spp->svcp->instname, spp->svcp->summary);
1628 dc = DC_DEPOTHER;
1631 if (g_msgbase != NULL)
1632 (void) printf(gettext(" See: %s%s\n"), g_msgbase, dc);
1634 if (verbose) {
1635 inst_t *pp;
1636 int indent;
1638 (void) printf(gettext(" Path: svc:/%s:%s\n"),
1639 svcp->svcname, svcp->instname);
1641 indent = 1;
1642 for (pp = spp->next_hop; ; ) {
1643 struct svcptr *tmp;
1645 (void) printf(gettext("%6s %*ssvc:/%s:%s\n"),
1646 "", indent++ * 2, "", pp->svcname,
1647 pp->instname);
1649 if (pp == spp->svcp)
1650 break;
1652 /* set pp to next_hop of cause with same svcp */
1653 tmp = uu_list_find(pp->causes, spp, NULL, NULL);
1654 pp = tmp->next_hop;
1660 static void
1661 print_logs(scf_instance_t *inst)
1663 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) != 0)
1664 return;
1666 if (pg_get_single_val(g_pg, SCF_PROPERTY_ALT_LOGFILE,
1667 SCF_TYPE_ASTRING, (void *)g_value, g_value_sz, 0) == 0)
1668 (void) printf(gettext(" See: %s\n"), g_value);
1670 if (pg_get_single_val(g_pg, SCF_PROPERTY_LOGFILE,
1671 SCF_TYPE_ASTRING, (void *)g_value, g_value_sz, 0) == 0)
1672 (void) printf(gettext(" See: %s\n"), g_value);
1675 static void
1676 print_aux_fmri_logs(const char *fmri)
1678 scf_instance_t *scf_inst = scf_instance_create(h);
1679 if (scf_inst == NULL)
1680 return;
1682 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, scf_inst,
1683 NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0)
1684 print_logs(scf_inst);
1686 scf_instance_destroy(scf_inst);
1689 static void
1690 print_reasons(const inst_t *svcp, int verbose)
1692 int r;
1693 const char *dc = NULL;
1695 if (strcmp(svcp->state, SCF_STATE_STRING_ONLINE) == 0)
1696 return;
1698 if (strcmp(svcp->state, SCF_STATE_STRING_UNINIT) == 0) {
1699 inst_t *rsp;
1701 r = get_fmri(svcp->restarter, NULL, &rsp);
1702 switch (r) {
1703 case 0:
1704 if (rsp != NULL)
1705 break;
1706 /* FALLTHROUGH */
1708 case EINVAL:
1709 (void) printf(gettext("Reason: "
1710 "Restarter \"%s\" is invalid.\n"), svcp->restarter);
1711 dc = DC_RSTRINVALID;
1712 goto diagcode;
1714 case ENOENT:
1715 (void) printf(gettext("Reason: "
1716 "Restarter \"%s\" does not exist.\n"),
1717 svcp->restarter);
1718 dc = DC_RSTRABSENT;
1719 goto diagcode;
1721 default:
1722 bad_error("get_fmri", r);
1725 if (inst_running(rsp)) {
1726 (void) printf(gettext("Reason: Restarter %s "
1727 "has not initialized service state.\n"),
1728 svcp->restarter);
1729 dc = DC_UNINIT;
1730 } else {
1731 (void) printf(gettext(
1732 "Reason: Restarter %s is not running.\n"),
1733 svcp->restarter);
1734 dc = DC_RSTRDEAD;
1737 } else if (strcmp(svcp->state, SCF_STATE_STRING_DISABLED) == 0) {
1738 if (!svcp->temporary) {
1739 (void) puts(gettext(
1740 "Reason: Disabled by an administrator."));
1741 dc = DC_DISABLED;
1742 } else {
1743 (void) puts(gettext("Reason: "
1744 "Temporarily disabled by an administrator."));
1745 dc = DC_TEMPDISABLED;
1748 } else if (strcmp(svcp->state, SCF_STATE_STRING_MAINT) == 0) {
1749 if (strcmp(svcp->aux_state, "administrative_request") == 0) {
1750 (void) puts(gettext("Reason: "
1751 "Maintenance requested by an administrator."));
1752 dc = DC_ADMINMAINT;
1753 } else if (strcmp(svcp->aux_state, "dependency_cycle") == 0) {
1754 (void) puts(gettext(
1755 "Reason: Completes a dependency cycle."));
1756 dc = DC_DEPCYCLE;
1757 } else if (strcmp(svcp->aux_state, "fault_threshold_reached") ==
1758 0) {
1759 print_method_failure(svcp, &dc);
1760 } else if (strcmp(svcp->aux_state, "service_request") == 0) {
1761 if (svcp->aux_fmri) {
1762 (void) printf(gettext("Reason: Maintenance "
1763 "requested by \"%s\"\n"), svcp->aux_fmri);
1764 print_aux_fmri_logs(svcp->aux_fmri);
1765 } else {
1766 (void) puts(gettext("Reason: Maintenance "
1767 "requested by another service."));
1769 dc = DC_SVCREQMAINT;
1770 } else if (strcmp(svcp->aux_state, "invalid_dependency") == 0) {
1771 (void) puts(gettext("Reason: Has invalid dependency."));
1772 dc = DC_INVALIDDEP;
1773 } else if (strcmp(svcp->aux_state, "invalid_restarter") == 0) {
1774 (void) printf(gettext("Reason: Restarter \"%s\" is "
1775 "invalid.\n"), svcp->restarter);
1776 dc = DC_RSTRINVALID;
1777 } else if (strcmp(svcp->aux_state, "method_failed") == 0) {
1778 print_method_failure(svcp, &dc);
1779 } else if (strcmp(svcp->aux_state, "restarting_too_quickly") ==
1780 0) {
1781 (void) puts(gettext("Reason: Restarting too quickly."));
1782 dc = DC_TOOQUICKLY;
1783 } else if (strcmp(svcp->aux_state, "none") == 0) {
1784 (void) printf(gettext(
1785 "Reason: Restarter %s gave no explanation.\n"),
1786 svcp->restarter);
1787 dc = DC_NONE;
1788 } else {
1789 (void) puts(gettext("Reason: Unknown."));
1790 dc = DC_UNKNOWN;
1793 } else if (strcmp(svcp->state, SCF_STATE_STRING_OFFLINE) == 0) {
1794 if (strcmp(svcp->next_state, SCF_STATE_STRING_ONLINE) == 0) {
1795 (void) puts(gettext(
1796 "Reason: Start method is running."));
1797 dc = DC_STARTING;
1798 } else if (strcmp(svcp->next_state, SCF_STATE_STRING_NONE) ==
1799 0) {
1800 print_dependency_reasons(svcp, verbose);
1801 /* Function prints diagcodes. */
1802 return;
1803 } else {
1804 (void) printf(gettext(
1805 "Reason: Transitioning to state %s.\n"),
1806 svcp->next_state);
1807 dc = DC_TRANSITION;
1810 } else if (strcmp(svcp->state, SCF_STATE_STRING_DEGRADED) == 0) {
1811 (void) puts(gettext("Reason: Degraded by an administrator."));
1812 dc = DC_ADMINDEGR;
1814 } else {
1815 (void) printf(gettext("Reason: Not in valid state (%s).\n"),
1816 svcp->state);
1817 dc = DC_INVALIDSTATE;
1820 diagcode:
1821 if (g_msgbase != NULL)
1822 (void) printf(gettext(" See: %s%s\n"), g_msgbase, dc);
1825 static void
1826 print_manpage(int verbose)
1828 static char *title = NULL;
1829 static char *section = NULL;
1831 if (title == NULL) {
1832 title = safe_malloc(g_value_sz);
1833 section = safe_malloc(g_value_sz);
1836 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_TITLE, SCF_TYPE_ASTRING,
1837 (void *)title, g_value_sz, 0) != 0)
1838 return;
1840 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_SECTION,
1841 SCF_TYPE_ASTRING, (void *)section, g_value_sz, 0) != 0)
1842 return;
1844 if (!verbose) {
1845 (void) printf(gettext(" See: %s(%s)\n"), title, section);
1846 return;
1849 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_MANPATH, SCF_TYPE_ASTRING,
1850 (void *)g_value, g_value_sz, 0) != 0)
1851 return;
1853 if (strcmp(g_value, ":default") == 0) {
1854 assert(sizeof (DEFAULT_MAN_PATH) < g_value_sz);
1855 (void) strcpy(g_value, DEFAULT_MAN_PATH);
1858 (void) printf(gettext(" See: man -M %s -s %s %s\n"), g_value,
1859 section, title);
1862 static void
1863 print_doclink()
1865 static char *uri = NULL;
1867 if (uri == NULL) {
1868 uri = safe_malloc(g_value_sz);
1871 if (pg_get_single_val(g_pg, SCF_PROPERTY_TM_URI, SCF_TYPE_ASTRING,
1872 (void *)uri, g_value_sz, 0) != 0)
1873 return;
1875 (void) printf(gettext(" See: %s\n"), uri);
1880 * Returns
1881 * 0 - success
1882 * 1 - inst was deleted
1884 static int
1885 print_docs(scf_instance_t *inst, int verbose)
1887 scf_snapshot_t *snap;
1888 int r;
1890 if (scf_instance_get_snapshot(inst, "running", g_snap) != 0) {
1891 switch (scf_error()) {
1892 case SCF_ERROR_NOT_FOUND:
1893 break;
1895 case SCF_ERROR_DELETED:
1896 return (1);
1898 default:
1899 scfdie();
1902 snap = NULL;
1903 } else {
1904 snap = g_snap;
1907 if (scf_iter_instance_pgs_typed_composed(g_iter, inst, snap,
1908 SCF_GROUP_TEMPLATE) != 0) {
1909 if (scf_error() != SCF_ERROR_DELETED)
1910 scfdie();
1912 return (1);
1915 for (;;) {
1916 r = scf_iter_next_pg(g_iter, g_pg);
1917 if (r == 0)
1918 break;
1919 if (r != 1) {
1920 if (scf_error() != SCF_ERROR_DELETED)
1921 scfdie();
1923 return (1);
1926 if (scf_pg_get_name(g_pg, g_fmri, g_fmri_sz) < 0) {
1927 if (scf_error() != SCF_ERROR_DELETED)
1928 scfdie();
1930 continue;
1933 if (strncmp(g_fmri, SCF_PG_TM_MAN_PREFIX,
1934 strlen(SCF_PG_TM_MAN_PREFIX)) == 0) {
1935 print_manpage(verbose);
1936 continue;
1939 if (strncmp(g_fmri, SCF_PG_TM_DOC_PREFIX,
1940 strlen(SCF_PG_TM_DOC_PREFIX)) == 0) {
1941 print_doclink();
1942 continue;
1945 return (0);
1948 static int first = 1;
1951 * Explain why the given service is in the state it's in.
1953 static void
1954 print_service(inst_t *svcp, int verbose)
1956 struct svcptr *spp;
1957 time_t stime;
1958 char *timebuf;
1959 size_t tbsz;
1960 struct tm *tmp;
1961 int deleted = 0;
1963 if (first)
1964 first = 0;
1965 else
1966 (void) putchar('\n');
1968 (void) printf(gettext("svc:/%s:%s"), svcp->svcname, svcp->instname);
1970 if (scf_scope_get_service(g_local_scope, svcp->svcname, g_svc) != 0) {
1971 if (scf_error() != SCF_ERROR_NOT_FOUND)
1972 scfdie();
1973 deleted = 1;
1974 } else if (scf_service_get_instance(g_svc, svcp->instname, g_inst) !=
1975 0) {
1976 if (scf_error() != SCF_ERROR_NOT_FOUND)
1977 scfdie();
1978 deleted = 1;
1981 if (!deleted) {
1982 if (inst_get_single_val(g_inst, SCF_PG_TM_COMMON_NAME, locale,
1983 SCF_TYPE_USTRING, g_value, g_value_sz, 0, 0, 1) == 0)
1984 /* EMPTY */;
1985 else if (inst_get_single_val(g_inst, SCF_PG_TM_COMMON_NAME, "C",
1986 SCF_TYPE_USTRING, g_value, g_value_sz, 0, 0, 1) != 0)
1987 (void) strcpy(g_value, "?");
1989 (void) printf(gettext(" (%s)\n"), g_value);
1990 } else {
1991 (void) putchar('\n');
1994 if (g_zonename != NULL)
1995 (void) printf(gettext(" Zone: %s\n"), g_zonename);
1997 stime = svcp->stime.tv_sec;
1998 tmp = localtime(&stime);
2000 for (tbsz = 50; ; tbsz *= 2) {
2001 timebuf = safe_malloc(tbsz);
2002 if (strftime(timebuf, tbsz, NULL, tmp) != 0)
2003 break;
2004 free(timebuf);
2007 (void) printf(gettext(" State: %s since %s\n"), svcp->state, timebuf);
2009 free(timebuf);
2011 /* Reasons */
2012 print_reasons(svcp, verbose);
2014 if (!deleted)
2015 deleted = print_docs(g_inst, verbose);
2016 if (!deleted)
2017 print_logs(g_inst);
2019 (void) determine_impact(svcp);
2021 switch (uu_list_numnodes(svcp->impact)) {
2022 case 0:
2023 if (inst_running(svcp))
2024 (void) puts(gettext("Impact: None."));
2025 else
2026 (void) puts(gettext(
2027 "Impact: This service is not running."));
2028 break;
2030 case 1:
2031 if (!verbose)
2032 (void) puts(gettext("Impact: 1 dependent service "
2033 "is not running. (Use -v for list.)"));
2034 else
2035 (void) puts(gettext(
2036 "Impact: 1 dependent service is not running:"));
2037 break;
2039 default:
2040 if (!verbose)
2041 (void) printf(gettext("Impact: %d dependent services "
2042 "are not running. (Use -v for list.)\n"),
2043 uu_list_numnodes(svcp->impact));
2044 else
2045 (void) printf(gettext(
2046 "Impact: %d dependent services are not running:\n"),
2047 uu_list_numnodes(svcp->impact));
2050 if (verbose) {
2051 for (spp = uu_list_first(svcp->impact);
2052 spp != NULL;
2053 spp = uu_list_next(svcp->impact, spp))
2054 (void) printf(gettext(" svc:/%s:%s\n"),
2055 spp->svcp->svcname, spp->svcp->instname);
2060 * Top level routine.
2063 static int
2064 impact_compar(const void *a, const void *b)
2066 int n, m;
2068 n = uu_list_numnodes((*(inst_t **)a)->impact);
2069 m = uu_list_numnodes((*(inst_t **)b)->impact);
2071 return (m - n);
2074 static int
2075 print_service_cb(void *verbose, scf_walkinfo_t *wip)
2077 int r;
2078 inst_t *ip;
2080 assert(wip->pg == NULL);
2082 r = get_fmri(wip->fmri, NULL, &ip);
2083 assert(r != EINVAL);
2084 if (r == ENOENT)
2085 return (0);
2087 assert(r == 0);
2088 assert(ip != NULL);
2090 print_service(ip, (int)verbose);
2092 return (0);
2095 void
2096 explain(int verbose, int argc, char **argv)
2099 * Initialize globals. If we have been called before (e.g., for a
2100 * different zone), this will clobber the previous globals -- keeping
2101 * with the proud svcs(1) tradition of not bothering to ever clean
2102 * anything up.
2104 x_init();
2106 /* Walk the graph and populate services with inst_t's */
2107 load_services();
2109 /* Populate causes for services. */
2110 determine_all_causes();
2112 if (argc > 0) {
2113 scf_error_t err;
2115 check_msgbase();
2117 /* Call print_service() for each operand. */
2119 err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
2120 print_service_cb, (void *)verbose, &exit_status, uu_warn);
2121 if (err != 0) {
2122 uu_warn(gettext(
2123 "failed to iterate over instances: %s\n"),
2124 scf_strerror(err));
2125 exit_status = UU_EXIT_FATAL;
2127 } else {
2128 struct svcptr *spp;
2129 int n, i;
2130 inst_t **ary;
2132 /* Sort g_causes. */
2134 n = uu_list_numnodes(g_causes);
2135 if (n == 0)
2136 return;
2138 check_msgbase();
2140 ary = calloc(n, sizeof (*ary));
2141 if (ary == NULL)
2142 uu_die(emsg_nomem);
2144 i = 0;
2145 for (spp = uu_list_first(g_causes);
2146 spp != NULL;
2147 spp = uu_list_next(g_causes, spp)) {
2148 (void) determine_impact(spp->svcp);
2149 ary[i++] = spp->svcp;
2152 qsort(ary, n, sizeof (*ary), impact_compar);
2154 /* Call print_service() for each service. */
2156 for (i = 0; i < n; ++i)
2157 print_service(ary[i], verbose);