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