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, ©->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, ©->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