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