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