dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / svc / svcs / svcs.c
blob3b4e8b5ee5134eb0cc6293b2ad7f79cefc9137fd
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011, Joyent, Inc. All rights reserved.
25 * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
29 * svcs - display attributes of service instances
31 * We have two output formats and six instance selection mechanisms. The
32 * primary output format is a line of attributes (selected by -o), possibly
33 * followed by process description lines (if -p is specified), for each
34 * instance selected. The columns available to display are described by the
35 * struct column columns array. The columns to actually display are kept in
36 * the opt_columns array as indicies into the columns array. The selection
37 * mechanisms available for this format are service FMRIs (selects all child
38 * instances), instance FMRIs, instance FMRI glob patterns, instances with
39 * a certain restarter (-R), dependencies of instances (-d), and dependents of
40 * instances (-D). Since the lines must be sorted (per -sS), we'll just stick
41 * each into a data structure and print them in order when we're done. To
42 * avoid listing the same instance twice (when -d and -D aren't given), we'll
43 * use a hash table of FMRIs to record that we've listed (added to the tree)
44 * an instance.
46 * The secondary output format (-l "long") is a paragraph of text for the
47 * services or instances selected. Not needing to be sorted, it's implemented
48 * by just calling print_detailed() for each FMRI given.
51 #include "svcs.h"
52 #include "notify_params.h"
54 /* Get the byteorder macros to ease sorting. */
55 #include <sys/types.h>
56 #include <netinet/in.h>
57 #include <inttypes.h>
59 #include <sys/contract.h>
60 #include <sys/ctfs.h>
61 #include <sys/stat.h>
63 #include <assert.h>
64 #include <errno.h>
65 #include <fcntl.h>
66 #include <fnmatch.h>
67 #include <libcontract.h>
68 #include <libcontract_priv.h>
69 #include <libintl.h>
70 #include <libscf.h>
71 #include <libscf_priv.h>
72 #include <libuutil.h>
73 #include <libnvpair.h>
74 #include <locale.h>
75 #include <procfs.h>
76 #include <stdarg.h>
77 #include <stdio.h>
78 #include <stdlib.h>
79 #include <strings.h>
80 #include <time.h>
81 #include <libzonecfg.h>
82 #include <zone.h>
84 #ifndef TEXT_DOMAIN
85 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
86 #endif /* TEXT_DOMAIN */
88 #define LEGACY_UNKNOWN "unknown"
90 /* Flags for pg_get_single_val() */
91 #define EMPTY_OK 0x01
92 #define MULTI_OK 0x02
96 * An AVL-storable node for output lines and the keys to sort them by.
98 struct avl_string {
99 uu_avl_node_t node;
100 char *key;
101 char *str;
105 * For lists of parsed restarter FMRIs.
107 struct pfmri_list {
108 const char *scope;
109 const char *service;
110 const char *instance;
111 struct pfmri_list *next;
116 * Globals
118 scf_handle_t *h;
119 static scf_propertygroup_t *g_pg;
120 static scf_property_t *g_prop;
121 static scf_value_t *g_val;
123 static size_t line_sz; /* Bytes in the header line. */
124 static size_t sortkey_sz; /* Bytes in sort keys. */
125 static uu_avl_pool_t *lines_pool;
126 static uu_avl_t *lines; /* Output lines. */
127 int exit_status;
128 ssize_t max_scf_name_length;
129 ssize_t max_scf_value_length;
130 ssize_t max_scf_fmri_length;
131 static ssize_t max_scf_type_length;
132 static time_t now;
133 static struct pfmri_list *restarters = NULL;
134 static int first_paragraph = 1; /* For -l mode. */
135 static char *common_name_buf; /* Sized for maximal length value. */
136 char *locale; /* Current locale. */
137 char *g_zonename; /* zone being operated upon */
140 * Pathname storage for path generated from the fmri.
141 * Used for reading the ctid and (start) pid files for an inetd service.
143 static char genfmri_filename[MAXPATHLEN] = "";
145 /* Options */
146 static int *opt_columns = NULL; /* Indices into columns to display. */
147 static int opt_cnum = 0;
148 static int opt_processes = 0; /* Print processes? */
149 static int *opt_sort = NULL; /* Indices into columns to sort. */
150 static int opt_snum = 0;
151 static int opt_nstate_shown = 0; /* Will nstate be shown? */
152 static int opt_verbose = 0;
153 static char *opt_zone; /* zone selected, if any */
155 /* Minimize string constants. */
156 static const char * const scf_property_state = SCF_PROPERTY_STATE;
157 static const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE;
158 static const char * const scf_property_contract = SCF_PROPERTY_CONTRACT;
162 * Utility functions
166 * For unexpected libscf errors. The ending newline is necessary to keep
167 * uu_die() from appending the errno error.
169 #ifndef NDEBUG
170 void
171 do_scfdie(const char *file, int line)
173 uu_die(gettext("%s:%d: Unexpected libscf error: %s. Exiting.\n"),
174 file, line, scf_strerror(scf_error()));
176 #else
177 void
178 scfdie(void)
180 uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"),
181 scf_strerror(scf_error()));
183 #endif
185 void *
186 safe_malloc(size_t sz)
188 void *ptr;
190 ptr = malloc(sz);
191 if (ptr == NULL)
192 uu_die(gettext("Out of memory"));
194 return (ptr);
197 char *
198 safe_strdup(const char *str)
200 char *cp;
202 cp = strdup(str);
203 if (cp == NULL)
204 uu_die(gettext("Out of memory.\n"));
206 return (cp);
210 * FMRI hashtable. For uniquifing listings.
213 struct ht_elem {
214 const char *fmri;
215 struct ht_elem *next;
218 static struct ht_elem **ht_buckets = NULL;
219 static uint_t ht_buckets_num = 0;
220 static uint_t ht_num;
222 static void
223 ht_free(void)
225 struct ht_elem *elem, *next;
226 int i;
228 for (i = 0; i < ht_buckets_num; i++) {
229 for (elem = ht_buckets[i]; elem != NULL; elem = next) {
230 next = elem->next;
231 free((char *)elem->fmri);
232 free(elem);
236 free(ht_buckets);
237 ht_buckets_num = 0;
238 ht_buckets = NULL;
241 static void
242 ht_init(void)
244 assert(ht_buckets == NULL);
246 ht_buckets_num = 8;
247 ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num);
248 bzero(ht_buckets, sizeof (*ht_buckets) * ht_buckets_num);
249 ht_num = 0;
252 static uint_t
253 ht_hash_fmri(const char *fmri)
255 uint_t h = 0, g;
256 const char *p, *k;
258 /* All FMRIs begin with svc:/, so skip that part. */
259 assert(strncmp(fmri, "svc:/", sizeof ("svc:/") - 1) == 0);
260 k = fmri + sizeof ("svc:/") - 1;
263 * Generic hash function from kernel/os/modhash.c.
265 for (p = k; *p != '\0'; ++p) {
266 h = (h << 4) + *p;
267 if ((g = (h & 0xf0000000)) != 0) {
268 h ^= (g >> 24);
269 h ^= g;
273 return (h);
276 static void
277 ht_grow()
279 uint_t new_ht_buckets_num;
280 struct ht_elem **new_ht_buckets;
281 int i;
283 new_ht_buckets_num = ht_buckets_num * 2;
284 assert(new_ht_buckets_num > ht_buckets_num);
285 new_ht_buckets =
286 safe_malloc(sizeof (*new_ht_buckets) * new_ht_buckets_num);
287 bzero(new_ht_buckets, sizeof (*new_ht_buckets) * new_ht_buckets_num);
289 for (i = 0; i < ht_buckets_num; ++i) {
290 struct ht_elem *elem, *next;
292 for (elem = ht_buckets[i]; elem != NULL; elem = next) {
293 uint_t h;
295 next = elem->next;
297 h = ht_hash_fmri(elem->fmri);
299 elem->next =
300 new_ht_buckets[h & (new_ht_buckets_num - 1)];
301 new_ht_buckets[h & (new_ht_buckets_num - 1)] = elem;
305 free(ht_buckets);
307 ht_buckets = new_ht_buckets;
308 ht_buckets_num = new_ht_buckets_num;
312 * Add an FMRI to the hash table. Returns 1 if it was already there,
313 * 0 otherwise.
315 static int
316 ht_add(const char *fmri)
318 uint_t h;
319 struct ht_elem *elem;
321 h = ht_hash_fmri(fmri);
323 elem = ht_buckets[h & (ht_buckets_num - 1)];
325 for (; elem != NULL; elem = elem->next) {
326 if (strcmp(elem->fmri, fmri) == 0)
327 return (1);
330 /* Grow when average chain length is over 3. */
331 if (ht_num > 3 * ht_buckets_num)
332 ht_grow();
334 ++ht_num;
336 elem = safe_malloc(sizeof (*elem));
337 elem->fmri = strdup(fmri);
338 elem->next = ht_buckets[h & (ht_buckets_num - 1)];
339 ht_buckets[h & (ht_buckets_num - 1)] = elem;
341 return (0);
347 * Convenience libscf wrapper functions.
351 * Get the single value of the named property in the given property group,
352 * which must have type ty, and put it in *vp. If ty is SCF_TYPE_ASTRING, vp
353 * is taken to be a char **, and sz is the size of the buffer. sz is unused
354 * otherwise. Return 0 on success, -1 if the property doesn't exist, has the
355 * wrong type, or doesn't have a single value. If flags has EMPTY_OK, don't
356 * complain if the property has no values (but return nonzero). If flags has
357 * MULTI_OK and the property has multiple values, succeed with E2BIG.
360 pg_get_single_val(scf_propertygroup_t *pg, const char *propname, scf_type_t ty,
361 void *vp, size_t sz, uint_t flags)
363 char *buf, root[MAXPATHLEN];
364 size_t buf_sz;
365 int ret = -1, r;
366 boolean_t multi = B_FALSE;
368 assert((flags & ~(EMPTY_OK | MULTI_OK)) == 0);
370 if (scf_pg_get_property(pg, propname, g_prop) == -1) {
371 if (scf_error() != SCF_ERROR_NOT_FOUND)
372 scfdie();
374 goto out;
377 if (scf_property_is_type(g_prop, ty) != SCF_SUCCESS) {
378 if (scf_error() == SCF_ERROR_TYPE_MISMATCH)
379 goto misconfigured;
380 scfdie();
383 if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) {
384 switch (scf_error()) {
385 case SCF_ERROR_NOT_FOUND:
386 if (flags & EMPTY_OK)
387 goto out;
388 goto misconfigured;
390 case SCF_ERROR_CONSTRAINT_VIOLATED:
391 if (flags & MULTI_OK) {
392 multi = B_TRUE;
393 break;
395 goto misconfigured;
397 case SCF_ERROR_PERMISSION_DENIED:
398 default:
399 scfdie();
403 switch (ty) {
404 case SCF_TYPE_ASTRING:
405 r = scf_value_get_astring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1;
406 break;
408 case SCF_TYPE_BOOLEAN:
409 r = scf_value_get_boolean(g_val, (uint8_t *)vp);
410 break;
412 case SCF_TYPE_COUNT:
413 r = scf_value_get_count(g_val, (uint64_t *)vp);
414 break;
416 case SCF_TYPE_INTEGER:
417 r = scf_value_get_integer(g_val, (int64_t *)vp);
418 break;
420 case SCF_TYPE_TIME: {
421 int64_t sec;
422 int32_t ns;
423 r = scf_value_get_time(g_val, &sec, &ns);
424 ((struct timeval *)vp)->tv_sec = sec;
425 ((struct timeval *)vp)->tv_usec = ns / 1000;
426 break;
429 case SCF_TYPE_USTRING:
430 r = scf_value_get_ustring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1;
431 break;
433 default:
434 #ifndef NDEBUG
435 uu_warn("%s:%d: Unknown type %d.\n", __FILE__, __LINE__, ty);
436 #endif
437 abort();
439 if (r != SCF_SUCCESS)
440 scfdie();
442 ret = multi ? E2BIG : 0;
443 goto out;
445 misconfigured:
446 buf_sz = max_scf_fmri_length + 1;
447 buf = safe_malloc(buf_sz);
448 if (scf_property_to_fmri(g_prop, buf, buf_sz) == -1)
449 scfdie();
451 uu_warn(gettext("Property \"%s\" is misconfigured.\n"), buf);
453 free(buf);
455 out:
456 if (ret != 0 || g_zonename == NULL ||
457 (strcmp(propname, SCF_PROPERTY_LOGFILE) != 0 &&
458 strcmp(propname, SCF_PROPERTY_ALT_LOGFILE) != 0))
459 return (ret);
462 * If we're here, we have a log file and we have specified a zone.
463 * As a convenience, we're going to prepend the zone path to the
464 * name of the log file.
466 root[0] = '\0';
467 (void) zone_get_rootpath(g_zonename, root, sizeof (root));
468 (void) strlcat(root, vp, sizeof (root));
469 (void) snprintf(vp, sz, "%s", root);
471 return (ret);
474 static scf_snapshot_t *
475 get_running_snapshot(scf_instance_t *inst)
477 scf_snapshot_t *snap;
479 snap = scf_snapshot_create(h);
480 if (snap == NULL)
481 scfdie();
483 if (scf_instance_get_snapshot(inst, "running", snap) == 0)
484 return (snap);
486 if (scf_error() != SCF_ERROR_NOT_FOUND)
487 scfdie();
489 scf_snapshot_destroy(snap);
490 return (NULL);
494 * As pg_get_single_val(), except look the property group up in an
495 * instance. If "use_running" is set, and the running snapshot exists,
496 * do a composed lookup there. Otherwise, do an (optionally composed)
497 * lookup on the current values. Note that lookups using snapshots are
498 * always composed.
501 inst_get_single_val(scf_instance_t *inst, const char *pgname,
502 const char *propname, scf_type_t ty, void *vp, size_t sz, uint_t flags,
503 int use_running, int composed)
505 scf_snapshot_t *snap = NULL;
506 int r;
508 if (use_running)
509 snap = get_running_snapshot(inst);
510 if (composed || use_running)
511 r = scf_instance_get_pg_composed(inst, snap, pgname, g_pg);
512 else
513 r = scf_instance_get_pg(inst, pgname, g_pg);
514 if (snap)
515 scf_snapshot_destroy(snap);
516 if (r == -1)
517 return (-1);
519 r = pg_get_single_val(g_pg, propname, ty, vp, sz, flags);
521 return (r);
524 static int
525 instance_enabled(scf_instance_t *inst, boolean_t temp)
527 uint8_t b;
529 if (inst_get_single_val(inst,
530 temp ? SCF_PG_GENERAL_OVR : SCF_PG_GENERAL, SCF_PROPERTY_ENABLED,
531 SCF_TYPE_BOOLEAN, &b, 0, 0, 0, 0) != 0)
532 return (-1);
534 return (b ? 1 : 0);
538 * Get a string property from the restarter property group of the given
539 * instance. Return an empty string on normal problems.
541 static void
542 get_restarter_string_prop(scf_instance_t *inst, const char *pname,
543 char *buf, size_t buf_sz)
545 if (inst_get_single_val(inst, SCF_PG_RESTARTER, pname,
546 SCF_TYPE_ASTRING, buf, buf_sz, 0, 0, 1) != 0)
547 *buf = '\0';
550 static int
551 get_restarter_time_prop(scf_instance_t *inst, const char *pname,
552 struct timeval *tvp, int ok_if_empty)
554 int r;
556 r = inst_get_single_val(inst, SCF_PG_RESTARTER, pname, SCF_TYPE_TIME,
557 tvp, 0, ok_if_empty ? EMPTY_OK : 0, 0, 1);
559 return (r == 0 ? 0 : -1);
562 static int
563 get_restarter_count_prop(scf_instance_t *inst, const char *pname, uint64_t *cp,
564 uint_t flags)
566 return (inst_get_single_val(inst, SCF_PG_RESTARTER, pname,
567 SCF_TYPE_COUNT, cp, 0, flags, 0, 1));
572 * Generic functions
576 * Return an array of pids associated with the given contract id.
577 * Returned pids are added to the end of the pidsp array.
579 static void
580 ctid_to_pids(uint64_t c, pid_t **pidsp, uint_t *np)
582 ct_stathdl_t ctst;
583 uint_t m;
584 int fd;
585 int r, err;
586 pid_t *pids;
588 fd = contract_open(c, NULL, "status", O_RDONLY);
589 if (fd < 0)
590 return;
592 err = ct_status_read(fd, CTD_ALL, &ctst);
593 if (err != 0) {
594 uu_warn(gettext("Could not read status of contract "
595 "%ld: %s.\n"), c, strerror(err));
596 (void) close(fd);
597 return;
600 (void) close(fd);
602 r = ct_pr_status_get_members(ctst, &pids, &m);
603 assert(r == 0);
605 if (m == 0) {
606 ct_status_free(ctst);
607 return;
610 *pidsp = reallocarray(*pidsp, *np + m, sizeof (*pidsp));
611 if (*pidsp == NULL)
612 uu_die(gettext("Out of memory"));
614 bcopy(pids, *pidsp + *np, m * sizeof (*pids));
615 *np += m;
617 ct_status_free(ctst);
620 static int
621 propvals_to_pids(scf_propertygroup_t *pg, const char *pname, pid_t **pidsp,
622 uint_t *np, scf_property_t *prop, scf_value_t *val, scf_iter_t *iter)
624 scf_type_t ty;
625 uint64_t c;
626 int r;
628 if (scf_pg_get_property(pg, pname, prop) != 0) {
629 if (scf_error() != SCF_ERROR_NOT_FOUND)
630 scfdie();
632 return (ENOENT);
635 if (scf_property_type(prop, &ty) != 0)
636 scfdie();
638 if (ty != SCF_TYPE_COUNT)
639 return (EINVAL);
641 if (scf_iter_property_values(iter, prop) != 0)
642 scfdie();
644 for (;;) {
645 r = scf_iter_next_value(iter, val);
646 if (r == -1)
647 scfdie();
648 if (r == 0)
649 break;
651 if (scf_value_get_count(val, &c) != 0)
652 scfdie();
654 ctid_to_pids(c, pidsp, np);
657 return (0);
661 * Check if instance has general/restarter property that matches
662 * given string. Restarter string must be in canonified form.
663 * Returns 0 for success; -1 otherwise.
665 static int
666 check_for_restarter(scf_instance_t *inst, const char *restarter)
668 char *fmri_buf;
669 char *fmri_buf_canonified = NULL;
670 int ret = -1;
672 if (inst == NULL)
673 return (-1);
675 /* Get restarter */
676 fmri_buf = safe_malloc(max_scf_fmri_length + 1);
677 if (inst_get_single_val(inst, SCF_PG_GENERAL,
678 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, fmri_buf,
679 max_scf_fmri_length + 1, 0, 0, 1) != 0)
680 goto out;
682 fmri_buf_canonified = safe_malloc(max_scf_fmri_length + 1);
683 if (scf_canonify_fmri(fmri_buf, fmri_buf_canonified,
684 (max_scf_fmri_length + 1)) < 0)
685 goto out;
687 if (strcmp(fmri_buf, restarter) == 0)
688 ret = 0;
690 out:
691 free(fmri_buf);
692 free(fmri_buf_canonified);
693 return (ret);
697 * Common code that is used by ctids_by_restarter and pids_by_restarter.
698 * Checks for a common restarter and if one is available, it generates
699 * the appropriate filename using wip->fmri and stores that in the
700 * global genfmri_filename.
702 * Restarters currently supported are: svc:/network/inetd:default
703 * If a restarter specific action is available, then restarter_spec
704 * is set to 1. If a restarter specific action is not available, then
705 * restarter_spec is set to 0 and a -1 is returned.
707 * Returns:
708 * 0 if success: restarter specific action found and filename generated
709 * -1 if restarter specific action not found,
710 * if restarter specific action found but an error was encountered
711 * during the generation of the wip->fmri based filename
713 static int
714 common_by_restarter(scf_instance_t *inst, const char *fmri,
715 int *restarter_specp)
717 int ret = -1;
718 int r;
720 /* Check for inetd specific restarter */
721 if (check_for_restarter(inst, "svc:/network/inetd:default") != 0) {
722 *restarter_specp = 0;
723 return (ret);
726 *restarter_specp = 1;
728 /* Get the ctid filename associated with this instance */
729 r = gen_filenms_from_fmri(fmri, "ctid", genfmri_filename, NULL);
731 switch (r) {
732 case 0:
733 break;
735 case -1:
737 * Unable to get filename from fmri. Print warning
738 * and return failure with no ctids.
740 uu_warn(gettext("Unable to read contract ids for %s -- "
741 "FMRI is too long\n"), fmri);
742 return (ret);
744 case -2:
746 * The directory didn't exist, so no contracts.
747 * Return failure with no ctids.
749 return (ret);
751 default:
752 uu_warn(gettext("%s:%d: gen_filenms_from_fmri() failed with "
753 "unknown error %d\n"), __FILE__, __LINE__, r);
754 abort();
757 return (0);
762 * Get or print a contract id using a restarter specific action.
764 * If the print_flag is not set, this routine gets the single contract
765 * id associated with this instance.
766 * If the print flag is set, then print each contract id found.
768 * Returns:
769 * 0 if success: restarter specific action found and used with no error
770 * -1 if restarter specific action not found
771 * -1 if restarter specific action found, but there was a failure
772 * -1 if print flag is not set and no contract id is found or multiple
773 * contract ids were found
774 * E2BIG if print flag is not set, MULTI_OK bit in flag is set and multiple
775 * contract ids were found
777 static int
778 ctids_by_restarter(scf_walkinfo_t *wip, uint64_t *cp, int print_flag,
779 uint_t flags, int *restarter_specp, void (*callback_header)(),
780 void (*callback_ctid)(uint64_t))
782 FILE *fp;
783 int ret = -1;
784 int fscanf_ret;
785 uint64_t cp2;
786 int rest_ret;
788 /* Check if callbacks are needed and were passed in */
789 if (print_flag) {
790 if ((callback_header == NULL) || (callback_ctid == NULL))
791 return (ret);
794 /* Check for restarter specific action and generation of filename */
795 rest_ret = common_by_restarter(wip->inst, wip->fmri, restarter_specp);
796 if (rest_ret != 0)
797 return (rest_ret);
800 * If fopen fails, then ctid file hasn't been created yet.
801 * If print_flag is set, this is ok; otherwise fail.
803 if ((fp = fopen(genfmri_filename, "r")) == NULL) {
804 if (print_flag)
805 return (0);
806 goto out;
809 if (print_flag) {
811 * Print all contract ids that are found.
812 * First callback to print ctid header.
814 callback_header();
816 /* fscanf may not set errno, so be sure to clear it first */
817 errno = 0;
818 while ((fscanf_ret = fscanf(fp, "%llu", cp)) == 1) {
819 /* Callback to print contract id */
820 callback_ctid(*cp);
821 errno = 0;
823 /* EOF is not a failure when no errno. */
824 if ((fscanf_ret != EOF) || (errno != 0)) {
825 uu_die(gettext("Unable to read ctid file for %s"),
826 wip->fmri);
828 (void) putchar('\n');
829 ret = 0;
830 } else {
831 /* Must find 1 ctid or fail */
832 if (fscanf(fp, "%llu", cp) == 1) {
833 /* If 2nd ctid found - fail */
834 if (fscanf(fp, "%llu", &cp2) == 1) {
835 if (flags & MULTI_OK)
836 ret = E2BIG;
837 } else {
838 /* Success - found only 1 ctid */
839 ret = 0;
843 (void) fclose(fp);
845 out:
846 return (ret);
850 * Get the process ids associated with an instance using a restarter
851 * specific action.
853 * Returns:
854 * 0 if success: restarter specific action found and used with no error
855 * -1 restarter specific action not found or if failure
857 static int
858 pids_by_restarter(scf_instance_t *inst, const char *fmri,
859 pid_t **pids, uint_t *np, int *restarter_specp)
861 uint64_t c;
862 FILE *fp;
863 int fscanf_ret;
864 int rest_ret;
866 /* Check for restarter specific action and generation of filename */
867 rest_ret = common_by_restarter(inst, fmri, restarter_specp);
868 if (rest_ret != 0)
869 return (rest_ret);
872 * If fopen fails with ENOENT then the ctid file hasn't been
873 * created yet so return success.
874 * For all other errors - fail with uu_die.
876 if ((fp = fopen(genfmri_filename, "r")) == NULL) {
877 if (errno == ENOENT)
878 return (0);
879 uu_die(gettext("Unable to open ctid file for %s"), fmri);
882 /* fscanf may not set errno, so be sure to clear it first */
883 errno = 0;
884 while ((fscanf_ret = fscanf(fp, "%llu", &c)) == 1) {
885 if (c == 0) {
886 (void) fclose(fp);
887 uu_die(gettext("ctid file for %s has corrupt data"),
888 fmri);
890 ctid_to_pids(c, pids, np);
891 errno = 0;
893 /* EOF is not a failure when no errno. */
894 if ((fscanf_ret != EOF) || (errno != 0)) {
895 uu_die(gettext("Unable to read ctid file for %s"), fmri);
898 (void) fclose(fp);
899 return (0);
902 static int
903 instance_processes(scf_instance_t *inst, const char *fmri,
904 pid_t **pids, uint_t *np)
906 scf_iter_t *iter;
907 int ret;
908 int restarter_spec;
910 /* Use the restarter specific get pids routine, if available. */
911 ret = pids_by_restarter(inst, fmri, pids, np, &restarter_spec);
912 if (restarter_spec == 1)
913 return (ret);
915 if ((iter = scf_iter_create(h)) == NULL)
916 scfdie();
918 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) == 0) {
919 *pids = NULL;
920 *np = 0;
922 (void) propvals_to_pids(g_pg, scf_property_contract, pids, np,
923 g_prop, g_val, iter);
925 (void) propvals_to_pids(g_pg, SCF_PROPERTY_TRANSIENT_CONTRACT,
926 pids, np, g_prop, g_val, iter);
928 ret = 0;
929 } else {
930 if (scf_error() != SCF_ERROR_NOT_FOUND)
931 scfdie();
933 ret = -1;
936 scf_iter_destroy(iter);
938 return (ret);
941 static int
942 get_psinfo(pid_t pid, psinfo_t *psip)
944 char path[100];
945 int fd;
947 (void) snprintf(path, sizeof (path), "/proc/%lu/psinfo", pid);
949 fd = open64(path, O_RDONLY);
950 if (fd < 0)
951 return (-1);
953 if (read(fd, psip, sizeof (*psip)) < 0)
954 uu_die(gettext("Could not read info for process %lu"), pid);
956 (void) close(fd);
958 return (0);
964 * Column sprint and sortkey functions
967 struct column {
968 const char *name;
969 int width;
972 * This function should write the value for the column into buf, and
973 * grow or allocate buf accordingly. It should always write at least
974 * width bytes, blanking unused bytes with spaces. If the field is
975 * greater than the column width we allow it to overlap other columns.
976 * In particular, it shouldn't write any null bytes. (Though an extra
977 * null byte past the end is currently tolerated.) If the property
978 * group is non-NULL, then we are dealing with a legacy service.
980 void (*sprint)(char **, scf_walkinfo_t *);
982 int sortkey_width;
985 * This function should write sortkey_width bytes into buf which will
986 * cause memcmp() to sort it properly. (Unlike sprint() above,
987 * however, an extra null byte may overrun the buffer.) The second
988 * argument controls whether the results are sorted in forward or
989 * reverse order.
991 void (*get_sortkey)(char *, int, scf_walkinfo_t *);
994 static void
995 reverse_bytes(char *buf, size_t len)
997 int i;
999 for (i = 0; i < len; ++i)
1000 buf[i] = ~buf[i];
1003 /* CTID */
1004 #define CTID_COLUMN_WIDTH 6
1005 #define CTID_COLUMN_BUFSIZE 20 /* max ctid_t + space + \0 */
1007 static void
1008 sprint_ctid(char **buf, scf_walkinfo_t *wip)
1010 int r;
1011 uint64_t c;
1012 size_t newsize = (*buf ? strlen(*buf) : 0) + CTID_COLUMN_BUFSIZE;
1013 char *newbuf = safe_malloc(newsize);
1014 int restarter_spec;
1017 * Use the restarter specific get pids routine, if available.
1018 * Only check for non-legacy services (wip->pg == 0).
1020 if (wip->pg != NULL) {
1021 r = pg_get_single_val(wip->pg, scf_property_contract,
1022 SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK);
1023 } else {
1024 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
1025 NULL, NULL);
1026 if (restarter_spec == 0) {
1027 /* No restarter specific routine */
1028 r = get_restarter_count_prop(wip->inst,
1029 scf_property_contract, &c, EMPTY_OK | MULTI_OK);
1033 if (r == 0)
1034 (void) snprintf(newbuf, newsize, "%s%*lu ",
1035 *buf ? *buf : "", CTID_COLUMN_WIDTH, (ctid_t)c);
1036 else if (r == E2BIG)
1037 (void) snprintf(newbuf, newsize, "%s%*lu* ",
1038 *buf ? *buf : "", CTID_COLUMN_WIDTH - 1, (ctid_t)c);
1039 else
1040 (void) snprintf(newbuf, newsize, "%s%*s ",
1041 *buf ? *buf : "", CTID_COLUMN_WIDTH, "-");
1042 free(*buf);
1043 *buf = newbuf;
1046 #define CTID_SORTKEY_WIDTH (sizeof (uint64_t))
1048 static void
1049 sortkey_ctid(char *buf, int reverse, scf_walkinfo_t *wip)
1051 int r;
1052 uint64_t c;
1053 int restarter_spec;
1056 * Use the restarter specific get pids routine, if available.
1057 * Only check for non-legacy services (wip->pg == 0).
1059 if (wip->pg != NULL) {
1060 r = pg_get_single_val(wip->pg, scf_property_contract,
1061 SCF_TYPE_COUNT, &c, 0, EMPTY_OK);
1062 } else {
1063 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
1064 NULL, NULL);
1065 if (restarter_spec == 0) {
1066 /* No restarter specific routine */
1067 r = get_restarter_count_prop(wip->inst,
1068 scf_property_contract, &c, EMPTY_OK);
1072 if (r == 0) {
1074 * Use the id itself, but it must be big-endian for this to
1075 * work.
1077 c = BE_64(c);
1079 bcopy(&c, buf, CTID_SORTKEY_WIDTH);
1080 } else {
1081 bzero(buf, CTID_SORTKEY_WIDTH);
1084 if (reverse)
1085 reverse_bytes(buf, CTID_SORTKEY_WIDTH);
1088 /* DESC */
1089 #define DESC_COLUMN_WIDTH 100
1091 static void
1092 sprint_desc(char **buf, scf_walkinfo_t *wip)
1094 char *x;
1095 size_t newsize;
1096 char *newbuf;
1098 if (common_name_buf == NULL)
1099 common_name_buf = safe_malloc(max_scf_value_length + 1);
1101 bzero(common_name_buf, max_scf_value_length + 1);
1103 if (wip->pg != NULL) {
1104 common_name_buf[0] = '-';
1105 } else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
1106 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
1107 1, 1) == -1 &&
1108 inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
1109 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
1110 1, 1) == -1) {
1111 common_name_buf[0] = '-';
1115 * Collapse multi-line tm_common_name values into a single line.
1117 for (x = common_name_buf; *x != '\0'; x++)
1118 if (*x == '\n')
1119 *x = ' ';
1121 if (strlen(common_name_buf) > DESC_COLUMN_WIDTH)
1122 newsize = (*buf ? strlen(*buf) : 0) +
1123 strlen(common_name_buf) + 1;
1124 else
1125 newsize = (*buf ? strlen(*buf) : 0) + DESC_COLUMN_WIDTH + 1;
1126 newbuf = safe_malloc(newsize);
1127 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1128 DESC_COLUMN_WIDTH, common_name_buf);
1129 free(*buf);
1130 *buf = newbuf;
1133 /* ARGSUSED */
1134 static void
1135 sortkey_desc(char *buf, int reverse, scf_walkinfo_t *wip)
1137 bzero(buf, DESC_COLUMN_WIDTH);
1140 /* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */
1142 static char
1143 state_to_char(const char *state)
1145 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
1146 return ('u');
1148 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
1149 return ('0');
1151 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
1152 return ('1');
1154 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
1155 return ('m');
1157 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
1158 return ('d');
1160 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
1161 return ('D');
1163 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
1164 return ('L');
1166 return ('?');
1169 /* Return true if inst is transitioning. */
1170 static int
1171 transitioning(scf_instance_t *inst)
1173 char nstate_name[MAX_SCF_STATE_STRING_SZ];
1175 get_restarter_string_prop(inst, scf_property_next_state, nstate_name,
1176 sizeof (nstate_name));
1178 return (state_to_char(nstate_name) != '?');
1181 /* ARGSUSED */
1182 static void
1183 sortkey_states(const char *pname, char *buf, int reverse, scf_walkinfo_t *wip)
1185 char state_name[MAX_SCF_STATE_STRING_SZ];
1188 * Lower numbers are printed first, so these are arranged from least
1189 * interesting ("legacy run") to most interesting (unknown).
1191 if (wip->pg == NULL) {
1192 get_restarter_string_prop(wip->inst, pname, state_name,
1193 sizeof (state_name));
1195 if (strcmp(state_name, SCF_STATE_STRING_ONLINE) == 0)
1196 *buf = 2;
1197 else if (strcmp(state_name, SCF_STATE_STRING_DEGRADED) == 0)
1198 *buf = 3;
1199 else if (strcmp(state_name, SCF_STATE_STRING_OFFLINE) == 0)
1200 *buf = 4;
1201 else if (strcmp(state_name, SCF_STATE_STRING_MAINT) == 0)
1202 *buf = 5;
1203 else if (strcmp(state_name, SCF_STATE_STRING_DISABLED) == 0)
1204 *buf = 1;
1205 else if (strcmp(state_name, SCF_STATE_STRING_UNINIT) == 0)
1206 *buf = 6;
1207 else
1208 *buf = 7;
1209 } else
1210 *buf = 0;
1212 if (reverse)
1213 *buf = 255 - *buf;
1216 static void
1217 sprint_state(char **buf, scf_walkinfo_t *wip)
1219 char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1220 size_t newsize;
1221 char *newbuf;
1223 if (wip->pg == NULL) {
1224 get_restarter_string_prop(wip->inst, scf_property_state,
1225 state_name, sizeof (state_name));
1227 /* Don't print blank fields, to ease parsing. */
1228 if (state_name[0] == '\0') {
1229 state_name[0] = '-';
1230 state_name[1] = '\0';
1233 if (!opt_nstate_shown && transitioning(wip->inst)) {
1234 /* Append an asterisk if nstate is valid. */
1235 (void) strcat(state_name, "*");
1237 } else
1238 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
1240 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 2;
1241 newbuf = safe_malloc(newsize);
1242 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1243 MAX_SCF_STATE_STRING_SZ + 1, state_name);
1245 free(*buf);
1246 *buf = newbuf;
1249 static void
1250 sortkey_state(char *buf, int reverse, scf_walkinfo_t *wip)
1252 sortkey_states(scf_property_state, buf, reverse, wip);
1255 static void
1256 sprint_nstate(char **buf, scf_walkinfo_t *wip)
1258 char next_state_name[MAX_SCF_STATE_STRING_SZ];
1259 boolean_t blank = 0;
1260 size_t newsize;
1261 char *newbuf;
1263 if (wip->pg == NULL) {
1264 get_restarter_string_prop(wip->inst, scf_property_next_state,
1265 next_state_name, sizeof (next_state_name));
1267 /* Don't print blank fields, to ease parsing. */
1268 if (next_state_name[0] == '\0' ||
1269 strcmp(next_state_name, SCF_STATE_STRING_NONE) == 0)
1270 blank = 1;
1271 } else
1272 blank = 1;
1274 if (blank) {
1275 next_state_name[0] = '-';
1276 next_state_name[1] = '\0';
1279 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 1;
1280 newbuf = safe_malloc(newsize);
1281 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1282 MAX_SCF_STATE_STRING_SZ - 1, next_state_name);
1283 free(*buf);
1284 *buf = newbuf;
1287 static void
1288 sortkey_nstate(char *buf, int reverse, scf_walkinfo_t *wip)
1290 sortkey_states(scf_property_next_state, buf, reverse, wip);
1293 static void
1294 sprint_s(char **buf, scf_walkinfo_t *wip)
1296 char tmp[3];
1297 char state_name[MAX_SCF_STATE_STRING_SZ];
1298 size_t newsize = (*buf ? strlen(*buf) : 0) + 4;
1299 char *newbuf = safe_malloc(newsize);
1301 if (wip->pg == NULL) {
1302 get_restarter_string_prop(wip->inst, scf_property_state,
1303 state_name, sizeof (state_name));
1304 tmp[0] = state_to_char(state_name);
1306 if (!opt_nstate_shown && transitioning(wip->inst))
1307 tmp[1] = '*';
1308 else
1309 tmp[1] = ' ';
1310 } else {
1311 tmp[0] = 'L';
1312 tmp[1] = ' ';
1314 tmp[2] = ' ';
1315 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "",
1316 3, tmp);
1317 free(*buf);
1318 *buf = newbuf;
1321 static void
1322 sprint_n(char **buf, scf_walkinfo_t *wip)
1324 char tmp[2];
1325 size_t newsize = (*buf ? strlen(*buf) : 0) + 3;
1326 char *newbuf = safe_malloc(newsize);
1327 char nstate_name[MAX_SCF_STATE_STRING_SZ];
1329 if (wip->pg == NULL) {
1330 get_restarter_string_prop(wip->inst, scf_property_next_state,
1331 nstate_name, sizeof (nstate_name));
1333 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
1334 tmp[0] = '-';
1335 else
1336 tmp[0] = state_to_char(nstate_name);
1337 } else
1338 tmp[0] = '-';
1340 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1341 2, tmp);
1342 free(*buf);
1343 *buf = newbuf;
1346 static void
1347 sprint_sn(char **buf, scf_walkinfo_t *wip)
1349 char tmp[3];
1350 size_t newsize = (*buf ? strlen(*buf) : 0) + 4;
1351 char *newbuf = safe_malloc(newsize);
1352 char nstate_name[MAX_SCF_STATE_STRING_SZ];
1353 char state_name[MAX_SCF_STATE_STRING_SZ];
1355 if (wip->pg == NULL) {
1356 get_restarter_string_prop(wip->inst, scf_property_state,
1357 state_name, sizeof (state_name));
1358 get_restarter_string_prop(wip->inst, scf_property_next_state,
1359 nstate_name, sizeof (nstate_name));
1360 tmp[0] = state_to_char(state_name);
1362 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
1363 tmp[1] = '-';
1364 else
1365 tmp[1] = state_to_char(nstate_name);
1366 } else {
1367 tmp[0] = 'L';
1368 tmp[1] = '-';
1371 tmp[2] = ' ';
1372 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1373 3, tmp);
1374 free(*buf);
1375 *buf = newbuf;
1378 /* ARGSUSED */
1379 static void
1380 sortkey_sn(char *buf, int reverse, scf_walkinfo_t *wip)
1382 sortkey_state(buf, reverse, wip);
1383 sortkey_nstate(buf + 1, reverse, wip);
1386 static const char *
1387 state_abbrev(const char *state)
1389 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
1390 return ("UN");
1391 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
1392 return ("OFF");
1393 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
1394 return ("ON");
1395 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
1396 return ("MNT");
1397 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
1398 return ("DIS");
1399 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
1400 return ("DGD");
1401 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
1402 return ("LRC");
1404 return ("?");
1407 static void
1408 sprint_sta(char **buf, scf_walkinfo_t *wip)
1410 char state_name[MAX_SCF_STATE_STRING_SZ];
1411 char sta[5];
1412 size_t newsize = (*buf ? strlen(*buf) : 0) + 6;
1413 char *newbuf = safe_malloc(newsize);
1415 if (wip->pg == NULL)
1416 get_restarter_string_prop(wip->inst, scf_property_state,
1417 state_name, sizeof (state_name));
1418 else
1419 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
1421 (void) strcpy(sta, state_abbrev(state_name));
1423 if (wip->pg == NULL && !opt_nstate_shown && transitioning(wip->inst))
1424 (void) strcat(sta, "*");
1426 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", sta);
1427 free(*buf);
1428 *buf = newbuf;
1431 static void
1432 sprint_nsta(char **buf, scf_walkinfo_t *wip)
1434 char state_name[MAX_SCF_STATE_STRING_SZ];
1435 size_t newsize = (*buf ? strlen(*buf) : 0) + 6;
1436 char *newbuf = safe_malloc(newsize);
1438 if (wip->pg == NULL)
1439 get_restarter_string_prop(wip->inst, scf_property_next_state,
1440 state_name, sizeof (state_name));
1441 else
1442 (void) strcpy(state_name, SCF_STATE_STRING_NONE);
1444 if (strcmp(state_name, SCF_STATE_STRING_NONE) == 0)
1445 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "",
1446 "-");
1447 else
1448 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "",
1449 state_abbrev(state_name));
1450 free(*buf);
1451 *buf = newbuf;
1454 /* FMRI */
1455 #define FMRI_COLUMN_WIDTH 50
1456 static void
1457 sprint_fmri(char **buf, scf_walkinfo_t *wip)
1459 char *fmri_buf = safe_malloc(max_scf_fmri_length + 1);
1460 size_t newsize;
1461 char *newbuf;
1463 if (wip->pg == NULL) {
1464 if (scf_instance_to_fmri(wip->inst, fmri_buf,
1465 max_scf_fmri_length + 1) == -1)
1466 scfdie();
1467 } else {
1468 (void) strcpy(fmri_buf, SCF_FMRI_LEGACY_PREFIX);
1469 if (pg_get_single_val(wip->pg, SCF_LEGACY_PROPERTY_NAME,
1470 SCF_TYPE_ASTRING, fmri_buf +
1471 sizeof (SCF_FMRI_LEGACY_PREFIX) - 1,
1472 max_scf_fmri_length + 1 -
1473 (sizeof (SCF_FMRI_LEGACY_PREFIX) - 1), 0) != 0)
1474 (void) strcat(fmri_buf, LEGACY_UNKNOWN);
1477 if (strlen(fmri_buf) > FMRI_COLUMN_WIDTH)
1478 newsize = (*buf ? strlen(*buf) : 0) + strlen(fmri_buf) + 2;
1479 else
1480 newsize = (*buf ? strlen(*buf) : 0) + FMRI_COLUMN_WIDTH + 2;
1481 newbuf = safe_malloc(newsize);
1482 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1483 FMRI_COLUMN_WIDTH, fmri_buf);
1484 free(fmri_buf);
1485 free(*buf);
1486 *buf = newbuf;
1489 static void
1490 sortkey_fmri(char *buf, int reverse, scf_walkinfo_t *wip)
1492 char *tmp = NULL;
1494 sprint_fmri(&tmp, wip);
1495 bcopy(tmp, buf, FMRI_COLUMN_WIDTH);
1496 free(tmp);
1497 if (reverse)
1498 reverse_bytes(buf, FMRI_COLUMN_WIDTH);
1501 /* Component columns */
1502 #define COMPONENT_COLUMN_WIDTH 20
1503 static void
1504 sprint_scope(char **buf, scf_walkinfo_t *wip)
1506 char *scope_buf = safe_malloc(max_scf_name_length + 1);
1507 size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2;
1508 char *newbuf = safe_malloc(newsize);
1510 assert(wip->scope != NULL);
1512 if (scf_scope_get_name(wip->scope, scope_buf, max_scf_name_length) < 0)
1513 scfdie();
1515 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1516 COMPONENT_COLUMN_WIDTH, scope_buf);
1517 free(*buf);
1518 *buf = newbuf;
1519 free(scope_buf);
1522 static void
1523 sortkey_scope(char *buf, int reverse, scf_walkinfo_t *wip)
1525 char *tmp = NULL;
1527 sprint_scope(&tmp, wip);
1528 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1529 free(tmp);
1530 if (reverse)
1531 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1534 static void
1535 sprint_service(char **buf, scf_walkinfo_t *wip)
1537 char *svc_buf = safe_malloc(max_scf_name_length + 1);
1538 char *newbuf;
1539 size_t newsize;
1541 if (wip->pg == NULL) {
1542 if (scf_service_get_name(wip->svc, svc_buf,
1543 max_scf_name_length + 1) < 0)
1544 scfdie();
1545 } else {
1546 if (pg_get_single_val(wip->pg, "name", SCF_TYPE_ASTRING,
1547 svc_buf, max_scf_name_length + 1, EMPTY_OK) != 0)
1548 (void) strcpy(svc_buf, LEGACY_UNKNOWN);
1552 if (strlen(svc_buf) > COMPONENT_COLUMN_WIDTH)
1553 newsize = (*buf ? strlen(*buf) : 0) + strlen(svc_buf) + 2;
1554 else
1555 newsize = (*buf ? strlen(*buf) : 0) +
1556 COMPONENT_COLUMN_WIDTH + 2;
1557 newbuf = safe_malloc(newsize);
1558 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1559 COMPONENT_COLUMN_WIDTH, svc_buf);
1560 free(svc_buf);
1561 free(*buf);
1562 *buf = newbuf;
1565 static void
1566 sortkey_service(char *buf, int reverse, scf_walkinfo_t *wip)
1568 char *tmp = NULL;
1570 sprint_service(&tmp, wip);
1571 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1572 free(tmp);
1573 if (reverse)
1574 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1577 /* INST */
1578 static void
1579 sprint_instance(char **buf, scf_walkinfo_t *wip)
1581 char *tmp = safe_malloc(max_scf_name_length + 1);
1582 size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2;
1583 char *newbuf = safe_malloc(newsize);
1585 if (wip->pg == NULL) {
1586 if (scf_instance_get_name(wip->inst, tmp,
1587 max_scf_name_length + 1) < 0)
1588 scfdie();
1589 } else {
1590 tmp[0] = '-';
1591 tmp[1] = '\0';
1594 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1595 COMPONENT_COLUMN_WIDTH, tmp);
1596 free(*buf);
1597 *buf = newbuf;
1598 free(tmp);
1601 static void
1602 sortkey_instance(char *buf, int reverse, scf_walkinfo_t *wip)
1604 char *tmp = NULL;
1606 sprint_instance(&tmp, wip);
1607 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1608 free(tmp);
1609 if (reverse)
1610 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1613 /* STIME */
1614 #define STIME_COLUMN_WIDTH 8
1615 #define FORMAT_TIME "%k:%M:%S"
1616 #define FORMAT_DATE "%b_%d "
1617 #define FORMAT_YEAR "%Y "
1620 * sprint_stime() will allocate a new buffer and snprintf the services's
1621 * state timestamp. If the timestamp is unavailable for some reason
1622 * a '-' is given instead.
1624 static void
1625 sprint_stime(char **buf, scf_walkinfo_t *wip)
1627 int r;
1628 struct timeval tv;
1629 time_t then;
1630 struct tm *tm;
1631 char st_buf[STIME_COLUMN_WIDTH + 1];
1632 size_t newsize = (*buf ? strlen(*buf) : 0) + STIME_COLUMN_WIDTH + 2;
1633 char *newbuf = safe_malloc(newsize);
1635 if (wip->pg == NULL) {
1636 r = get_restarter_time_prop(wip->inst,
1637 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1638 } else {
1639 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1640 SCF_TYPE_TIME, &tv, 0, 0);
1643 if (r != 0) {
1645 * There's something amiss with our service
1646 * so we'll print a '-' for STIME.
1648 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "",
1649 STIME_COLUMN_WIDTH + 1, "-");
1650 } else {
1651 /* tv should be valid so we'll format it */
1652 then = (time_t)tv.tv_sec;
1654 tm = localtime(&then);
1656 * Print time if started within the past 24 hours, print date
1657 * if within the past 12 months or, finally, print year if
1658 * started greater than 12 months ago.
1660 if (now - then < 24 * 60 * 60) {
1661 (void) strftime(st_buf, sizeof (st_buf),
1662 gettext(FORMAT_TIME), tm);
1663 } else if (now - then < 12 * 30 * 24 * 60 * 60) {
1664 (void) strftime(st_buf, sizeof (st_buf),
1665 gettext(FORMAT_DATE), tm);
1666 } else {
1667 (void) strftime(st_buf, sizeof (st_buf),
1668 gettext(FORMAT_YEAR), tm);
1670 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1671 STIME_COLUMN_WIDTH + 1, st_buf);
1673 free(*buf);
1674 *buf = newbuf;
1677 #define STIME_SORTKEY_WIDTH (sizeof (uint64_t) + sizeof (uint32_t))
1679 /* ARGSUSED */
1680 static void
1681 sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip)
1683 struct timeval tv;
1684 int r;
1686 if (wip->pg == NULL)
1687 r = get_restarter_time_prop(wip->inst,
1688 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1689 else
1690 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1691 SCF_TYPE_TIME, &tv, 0, 0);
1693 if (r == 0) {
1694 int64_t sec;
1695 int32_t us;
1697 /* Stick it straight into the buffer. */
1698 sec = tv.tv_sec;
1699 us = tv.tv_usec;
1701 sec = BE_64(sec);
1702 us = BE_32(us);
1703 bcopy(&sec, buf, sizeof (sec));
1704 bcopy(&us, buf + sizeof (sec), sizeof (us));
1705 } else {
1706 bzero(buf, STIME_SORTKEY_WIDTH);
1709 if (reverse)
1710 reverse_bytes(buf, STIME_SORTKEY_WIDTH);
1713 /* ZONE */
1714 #define ZONE_COLUMN_WIDTH 16
1715 /*ARGSUSED*/
1716 static void
1717 sprint_zone(char **buf, scf_walkinfo_t *wip)
1719 size_t newsize;
1720 char *newbuf, *zonename = g_zonename, b[ZONENAME_MAX];
1722 if (zonename == NULL) {
1723 zoneid_t zoneid = getzoneid();
1725 if (getzonenamebyid(zoneid, b, sizeof (b)) < 0)
1726 uu_die(gettext("could not determine zone name"));
1728 zonename = b;
1731 if (strlen(zonename) > ZONE_COLUMN_WIDTH)
1732 newsize = (*buf ? strlen(*buf) : 0) + strlen(zonename) + 2;
1733 else
1734 newsize = (*buf ? strlen(*buf) : 0) + ZONE_COLUMN_WIDTH + 2;
1736 newbuf = safe_malloc(newsize);
1737 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1738 ZONE_COLUMN_WIDTH, zonename);
1740 free(*buf);
1741 *buf = newbuf;
1744 static void
1745 sortkey_zone(char *buf, int reverse, scf_walkinfo_t *wip)
1747 char *tmp = NULL;
1749 sprint_zone(&tmp, wip);
1750 bcopy(tmp, buf, ZONE_COLUMN_WIDTH);
1751 free(tmp);
1752 if (reverse)
1753 reverse_bytes(buf, ZONE_COLUMN_WIDTH);
1757 * Information about columns which can be displayed. If you add something,
1758 * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below.
1760 static const struct column columns[] = {
1761 { "CTID", CTID_COLUMN_WIDTH, sprint_ctid,
1762 CTID_SORTKEY_WIDTH, sortkey_ctid },
1763 { "DESC", DESC_COLUMN_WIDTH, sprint_desc,
1764 DESC_COLUMN_WIDTH, sortkey_desc },
1765 { "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri,
1766 FMRI_COLUMN_WIDTH, sortkey_fmri },
1767 { "INST", COMPONENT_COLUMN_WIDTH, sprint_instance,
1768 COMPONENT_COLUMN_WIDTH, sortkey_instance },
1769 { "N", 1, sprint_n, 1, sortkey_nstate },
1770 { "NSTA", 4, sprint_nsta, 1, sortkey_nstate },
1771 { "NSTATE", MAX_SCF_STATE_STRING_SZ - 1, sprint_nstate,
1772 1, sortkey_nstate },
1773 { "S", 2, sprint_s, 1, sortkey_state },
1774 { "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope,
1775 COMPONENT_COLUMN_WIDTH, sortkey_scope },
1776 { "SN", 2, sprint_sn, 2, sortkey_sn },
1777 { "SVC", COMPONENT_COLUMN_WIDTH, sprint_service,
1778 COMPONENT_COLUMN_WIDTH, sortkey_service },
1779 { "STA", 4, sprint_sta, 1, sortkey_state },
1780 { "STATE", MAX_SCF_STATE_STRING_SZ - 1 + 1, sprint_state,
1781 1, sortkey_state },
1782 { "STIME", STIME_COLUMN_WIDTH, sprint_stime,
1783 STIME_SORTKEY_WIDTH, sortkey_stime },
1784 { "ZONE", ZONE_COLUMN_WIDTH, sprint_zone,
1785 ZONE_COLUMN_WIDTH, sortkey_zone },
1788 #define MAX_COLUMN_NAME_LENGTH_STR "6"
1790 static const int ncolumns = sizeof (columns) / sizeof (columns[0]);
1793 * Necessary thanks to gettext() & xgettext.
1795 static const char *
1796 description_of_column(int c)
1798 const char *s = NULL;
1800 switch (c) {
1801 case 0:
1802 s = gettext("contract ID for service (see contract(4))");
1803 break;
1804 case 1:
1805 s = gettext("human-readable description of the service");
1806 break;
1807 case 2:
1808 s = gettext("Fault Managed Resource Identifier for service");
1809 break;
1810 case 3:
1811 s = gettext("portion of the FMRI indicating service instance");
1812 break;
1813 case 4:
1814 s = gettext("abbreviation for next state (if in transition)");
1815 break;
1816 case 5:
1817 s = gettext("abbreviation for next state (if in transition)");
1818 break;
1819 case 6:
1820 s = gettext("name for next state (if in transition)");
1821 break;
1822 case 7:
1823 s = gettext("abbreviation for current state");
1824 break;
1825 case 8:
1826 s = gettext("name for scope associated with service");
1827 break;
1828 case 9:
1829 s = gettext("abbreviation for current state and next state");
1830 break;
1831 case 10:
1832 s = gettext("portion of the FMRI representing service name");
1833 break;
1834 case 11:
1835 s = gettext("abbreviation for current state");
1836 break;
1837 case 12:
1838 s = gettext("name for current state");
1839 break;
1840 case 13:
1841 s = gettext("time of last state change");
1842 break;
1843 case 14:
1844 s = gettext("name of zone");
1845 break;
1848 assert(s != NULL);
1849 return (s);
1853 static void
1854 print_usage(const char *progname, FILE *f, boolean_t do_exit)
1856 (void) fprintf(f, gettext(
1857 "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] "
1858 "[-sS col] [-Z | -z zone ]\n [<service> ...]\n"
1859 " %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] "
1860 "[-Z | -z zone ]\n [<service> ...]\n"
1861 " %1$s [-l | -L] [-Z | -z zone] <service> ...\n"
1862 " %1$s -x [-v] [-Z | -z zone] [<service> ...]\n"
1863 " %1$s -?\n"), progname);
1865 if (do_exit)
1866 exit(UU_EXIT_USAGE);
1869 #define argserr(progname) print_usage(progname, stderr, B_TRUE)
1871 static void
1872 print_help(const char *progname)
1874 int i;
1876 print_usage(progname, stdout, B_FALSE);
1878 (void) printf(gettext("\n"
1879 "\t-a list all service instances rather than "
1880 "only those that are enabled\n"
1881 "\t-d list dependencies of the specified service(s)\n"
1882 "\t-D list dependents of the specified service(s)\n"
1883 "\t-H omit header line from output\n"
1884 "\t-l list detailed information about the specified service(s)\n"
1885 "\t-L list the log file associated with the specified service(s)\n"
1886 "\t-o list only the specified columns in the output\n"
1887 "\t-p list process IDs and names associated with each service\n"
1888 "\t-R list only those services with the specified restarter\n"
1889 "\t-s sort output in ascending order by the specified column(s)\n"
1890 "\t-S sort output in descending order by the specified column(s)\n"
1891 "\t-v list verbose information appropriate to the type of output\n"
1892 "\t-x explain the status of services that might require maintenance,\n"
1893 "\t or explain the status of the specified service(s)\n"
1894 "\t-z from global zone, show services in a specified zone\n"
1895 "\t-Z from global zone, show services in all zones\n"
1896 "\n\t"
1897 "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
1898 "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
1899 "\n"
1900 "\t%1$s [opts] svc:/network/smtp:sendmail\n"
1901 "\t%1$s [opts] network/smtp:sendmail\n"
1902 "\t%1$s [opts] network/*mail\n"
1903 "\t%1$s [opts] network/smtp\n"
1904 "\t%1$s [opts] smtp:sendmail\n"
1905 "\t%1$s [opts] smtp\n"
1906 "\t%1$s [opts] sendmail\n"
1907 "\n\t"
1908 "Columns for output or sorting can be specified using these names:\n"
1909 "\n"), progname);
1911 for (i = 0; i < ncolumns; i++) {
1912 (void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR "s %s\n",
1913 columns[i].name, description_of_column(i));
1919 * A getsubopt()-like function which returns an index into the columns table.
1920 * On success, *optionp is set to point to the next sub-option, or the
1921 * terminating null if there are none.
1923 static int
1924 getcolumnopt(char **optionp)
1926 char *str = *optionp, *cp;
1927 int i;
1929 assert(optionp != NULL);
1930 assert(*optionp != NULL);
1932 cp = strchr(*optionp, ',');
1933 if (cp != NULL)
1934 *cp = '\0';
1936 for (i = 0; i < ncolumns; ++i) {
1937 if (strcasecmp(str, columns[i].name) == 0) {
1938 if (cp != NULL)
1939 *optionp = cp + 1;
1940 else
1941 *optionp = strchr(*optionp, '\0');
1943 return (i);
1947 return (-1);
1950 static void
1951 print_header()
1953 int i;
1954 char *line_buf, *cp;
1956 line_buf = safe_malloc(line_sz);
1957 cp = line_buf;
1958 for (i = 0; i < opt_cnum; ++i) {
1959 const struct column * const colp = &columns[opt_columns[i]];
1961 (void) snprintf(cp, colp->width + 1, "%-*s", colp->width,
1962 colp->name);
1963 cp += colp->width;
1964 *cp++ = ' ';
1967 /* Trim the trailing whitespace */
1968 --cp;
1969 while (*cp == ' ')
1970 --cp;
1971 *(cp+1) = '\0';
1972 (void) puts(line_buf);
1974 free(line_buf);
1980 * Long listing (-l) functions.
1983 static int
1984 pidcmp(const void *l, const void *r)
1986 pid_t lp = *(pid_t *)l, rp = *(pid_t *)r;
1988 if (lp < rp)
1989 return (-1);
1990 if (lp > rp)
1991 return (1);
1992 return (0);
1996 * This is the strlen() of the longest label ("description"), plus intercolumn
1997 * space.
1999 #define DETAILED_WIDTH (11 + 2)
2002 * Callback routine to print header for contract id.
2003 * Called by ctids_by_restarter and print_detailed.
2005 static void
2006 print_ctid_header()
2008 (void) printf("%-*s", DETAILED_WIDTH, "contract_id");
2012 * Callback routine to print a contract id.
2013 * Called by ctids_by_restarter and print_detailed.
2015 static void
2016 print_ctid_detailed(uint64_t c)
2018 (void) printf("%lu ", (ctid_t)c);
2021 static void
2022 detailed_list_processes(scf_walkinfo_t *wip)
2024 uint64_t c;
2025 pid_t *pids;
2026 uint_t i, n;
2027 psinfo_t psi;
2029 if (get_restarter_count_prop(wip->inst, scf_property_contract, &c,
2030 EMPTY_OK) != 0)
2031 return;
2033 if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
2034 return;
2036 qsort(pids, n, sizeof (*pids), pidcmp);
2038 for (i = 0; i < n; ++i) {
2039 (void) printf("%-*s%lu", DETAILED_WIDTH, gettext("process"),
2040 pids[i]);
2042 if (get_psinfo(pids[i], &psi) == 0)
2043 (void) printf(" %.*s", PRARGSZ, psi.pr_psargs);
2045 (void) putchar('\n');
2048 free(pids);
2052 * Determines the state of a dependency. If the FMRI specifies a file, then we
2053 * fake up a state based on whether we can access the file.
2055 static void
2056 get_fmri_state(char *fmri, char *state, size_t state_sz)
2058 char *lfmri;
2059 const char *svc_name, *inst_name, *pg_name, *path;
2060 scf_service_t *svc;
2061 scf_instance_t *inst;
2062 scf_iter_t *iter;
2064 lfmri = safe_strdup(fmri);
2067 * Check for file:// dependencies
2069 if (scf_parse_file_fmri(lfmri, NULL, &path) == SCF_SUCCESS) {
2070 struct stat64 statbuf;
2071 const char *msg;
2073 if (stat64(path, &statbuf) == 0)
2074 msg = "online";
2075 else if (errno == ENOENT)
2076 msg = "absent";
2077 else
2078 msg = "unknown";
2080 (void) strlcpy(state, msg, state_sz);
2081 return;
2085 * scf_parse_file_fmri() may have overwritten part of the string, so
2086 * copy it back.
2088 (void) strcpy(lfmri, fmri);
2090 if (scf_parse_svc_fmri(lfmri, NULL, &svc_name, &inst_name,
2091 &pg_name, NULL) != SCF_SUCCESS) {
2092 free(lfmri);
2093 (void) strlcpy(state, "invalid", state_sz);
2094 return;
2097 free(lfmri);
2099 if (svc_name == NULL || pg_name != NULL) {
2100 (void) strlcpy(state, "invalid", state_sz);
2101 return;
2104 if (inst_name != NULL) {
2105 /* instance: get state */
2106 inst = scf_instance_create(h);
2107 if (inst == NULL)
2108 scfdie();
2110 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
2111 NULL, SCF_DECODE_FMRI_EXACT) == SCF_SUCCESS)
2112 get_restarter_string_prop(inst, scf_property_state,
2113 state, state_sz);
2114 else {
2115 switch (scf_error()) {
2116 case SCF_ERROR_INVALID_ARGUMENT:
2117 (void) strlcpy(state, "invalid", state_sz);
2118 break;
2119 case SCF_ERROR_NOT_FOUND:
2120 (void) strlcpy(state, "absent", state_sz);
2121 break;
2123 default:
2124 scfdie();
2128 scf_instance_destroy(inst);
2129 return;
2133 * service: If only one instance, use that state. Otherwise, say
2134 * "multiple".
2136 if ((svc = scf_service_create(h)) == NULL ||
2137 (inst = scf_instance_create(h)) == NULL ||
2138 (iter = scf_iter_create(h)) == NULL)
2139 scfdie();
2141 if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
2142 SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
2143 switch (scf_error()) {
2144 case SCF_ERROR_INVALID_ARGUMENT:
2145 (void) strlcpy(state, "invalid", state_sz);
2146 goto out;
2147 case SCF_ERROR_NOT_FOUND:
2148 (void) strlcpy(state, "absent", state_sz);
2149 goto out;
2151 default:
2152 scfdie();
2156 if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
2157 scfdie();
2159 switch (scf_iter_next_instance(iter, inst)) {
2160 case 0:
2161 (void) strlcpy(state, "absent", state_sz);
2162 goto out;
2164 case 1:
2165 break;
2167 default:
2168 scfdie();
2171 /* Get the state in case this is the only instance. */
2172 get_restarter_string_prop(inst, scf_property_state, state, state_sz);
2174 switch (scf_iter_next_instance(iter, inst)) {
2175 case 0:
2176 break;
2178 case 1:
2179 /* Nope, multiple instances. */
2180 (void) strlcpy(state, "multiple", state_sz);
2181 goto out;
2183 default:
2184 scfdie();
2187 out:
2188 scf_iter_destroy(iter);
2189 scf_instance_destroy(inst);
2190 scf_service_destroy(svc);
2193 static void
2194 print_application_properties(scf_walkinfo_t *wip, scf_snapshot_t *snap)
2196 scf_iter_t *pg_iter, *prop_iter, *val_iter;
2197 scf_propertygroup_t *pg;
2198 scf_property_t *prop;
2199 scf_value_t *val;
2200 scf_pg_tmpl_t *pt;
2201 scf_prop_tmpl_t *prt;
2202 char *pg_name_buf = safe_malloc(max_scf_name_length + 1);
2203 char *prop_name_buf = safe_malloc(max_scf_name_length + 1);
2204 char *snap_name = safe_malloc(max_scf_name_length + 1);
2205 char *val_buf = safe_malloc(max_scf_value_length + 1);
2206 char *desc, *cp;
2207 scf_type_t type;
2208 int i, j, k;
2209 uint8_t vis;
2211 if ((pg_iter = scf_iter_create(h)) == NULL ||
2212 (prop_iter = scf_iter_create(h)) == NULL ||
2213 (val_iter = scf_iter_create(h)) == NULL ||
2214 (val = scf_value_create(h)) == NULL ||
2215 (prop = scf_property_create(h)) == NULL ||
2216 (pt = scf_tmpl_pg_create(h)) == NULL ||
2217 (prt = scf_tmpl_prop_create(h)) == NULL ||
2218 (pg = scf_pg_create(h)) == NULL)
2219 scfdie();
2221 if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap,
2222 SCF_PG_APP_DEFAULT) == -1)
2223 scfdie();
2226 * Format for output:
2227 * pg (pgtype)
2228 * description
2229 * pg/prop (proptype) = <value> <value>
2230 * description
2232 while ((i = scf_iter_next_pg(pg_iter, pg)) == 1) {
2233 int tmpl = 0;
2235 if (scf_pg_get_name(pg, pg_name_buf, max_scf_name_length) < 0)
2236 scfdie();
2237 if (scf_snapshot_get_name(snap, snap_name,
2238 max_scf_name_length) < 0)
2239 scfdie();
2241 if (scf_tmpl_get_by_pg_name(wip->fmri, snap_name, pg_name_buf,
2242 SCF_PG_APP_DEFAULT, pt, 0) == 0)
2243 tmpl = 1;
2244 else
2245 tmpl = 0;
2247 (void) printf("%s (%s)\n", pg_name_buf, SCF_PG_APP_DEFAULT);
2249 if (tmpl == 1 && scf_tmpl_pg_description(pt, NULL, &desc) > 0) {
2250 (void) printf(" %s\n", desc);
2251 free(desc);
2254 if (scf_iter_pg_properties(prop_iter, pg) == -1)
2255 scfdie();
2256 while ((j = scf_iter_next_property(prop_iter, prop)) == 1) {
2257 if (scf_property_get_name(prop, prop_name_buf,
2258 max_scf_name_length) < 0)
2259 scfdie();
2260 if (scf_property_type(prop, &type) == -1)
2261 scfdie();
2263 if ((tmpl == 1) &&
2264 (scf_tmpl_get_by_prop(pt, prop_name_buf, prt,
2265 0) != 0))
2266 tmpl = 0;
2268 if (tmpl == 1 &&
2269 scf_tmpl_prop_visibility(prt, &vis) != -1 &&
2270 vis == SCF_TMPL_VISIBILITY_HIDDEN)
2271 continue;
2273 (void) printf("%s/%s (%s) = ", pg_name_buf,
2274 prop_name_buf, scf_type_to_string(type));
2276 if (scf_iter_property_values(val_iter, prop) == -1)
2277 scfdie();
2279 while ((k = scf_iter_next_value(val_iter, val)) == 1) {
2280 if (scf_value_get_as_string(val, val_buf,
2281 max_scf_value_length + 1) < 0)
2282 scfdie();
2283 if (strpbrk(val_buf, " \t\n\"()") != NULL) {
2284 (void) printf("\"");
2285 for (cp = val_buf; *cp != '\0'; ++cp) {
2286 if (*cp == '"' || *cp == '\\')
2287 (void) putc('\\',
2288 stdout);
2290 (void) putc(*cp, stdout);
2292 (void) printf("\"");
2293 } else {
2294 (void) printf("%s ", val_buf);
2298 (void) printf("\n");
2300 if (k == -1)
2301 scfdie();
2303 if (tmpl == 1 && scf_tmpl_prop_description(prt, NULL,
2304 &desc) > 0) {
2305 (void) printf(" %s\n", desc);
2306 free(desc);
2309 if (j == -1)
2310 scfdie();
2312 if (i == -1)
2313 scfdie();
2316 scf_iter_destroy(pg_iter);
2317 scf_iter_destroy(prop_iter);
2318 scf_iter_destroy(val_iter);
2319 scf_value_destroy(val);
2320 scf_property_destroy(prop);
2321 scf_tmpl_pg_destroy(pt);
2322 scf_tmpl_prop_destroy(prt);
2323 scf_pg_destroy(pg);
2324 free(pg_name_buf);
2325 free(prop_name_buf);
2326 free(snap_name);
2327 free(val_buf);
2330 static void
2331 print_detailed_dependency(scf_propertygroup_t *pg)
2333 scf_property_t *eprop;
2334 scf_iter_t *iter;
2335 scf_type_t ty;
2336 char *val_buf;
2337 int i;
2339 if ((eprop = scf_property_create(h)) == NULL ||
2340 (iter = scf_iter_create(h)) == NULL)
2341 scfdie();
2343 val_buf = safe_malloc(max_scf_value_length + 1);
2345 if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, eprop) !=
2346 SCF_SUCCESS ||
2347 scf_property_type(eprop, &ty) != SCF_SUCCESS ||
2348 ty != SCF_TYPE_FMRI)
2349 return;
2351 (void) printf("%-*s", DETAILED_WIDTH, gettext("dependency"));
2353 /* Print the grouping */
2354 if (pg_get_single_val(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
2355 val_buf, max_scf_value_length + 1, 0) == 0)
2356 (void) fputs(val_buf, stdout);
2357 else
2358 (void) putchar('?');
2360 (void) putchar('/');
2362 if (pg_get_single_val(pg, SCF_PROPERTY_RESTART_ON, SCF_TYPE_ASTRING,
2363 val_buf, max_scf_value_length + 1, 0) == 0)
2364 (void) fputs(val_buf, stdout);
2365 else
2366 (void) putchar('?');
2368 /* Print the dependency entities. */
2369 if (scf_iter_property_values(iter, eprop) == -1)
2370 scfdie();
2372 while ((i = scf_iter_next_value(iter, g_val)) == 1) {
2373 char state[MAX_SCF_STATE_STRING_SZ];
2375 if (scf_value_get_astring(g_val, val_buf,
2376 max_scf_value_length + 1) < 0)
2377 scfdie();
2379 (void) putchar(' ');
2380 (void) fputs(val_buf, stdout);
2382 /* Print the state. */
2383 state[0] = '-';
2384 state[1] = '\0';
2386 get_fmri_state(val_buf, state, sizeof (state));
2388 (void) printf(" (%s)", state);
2390 if (i == -1)
2391 scfdie();
2393 (void) putchar('\n');
2395 free(val_buf);
2396 scf_iter_destroy(iter);
2397 scf_property_destroy(eprop);
2400 /* ARGSUSED */
2401 static int
2402 print_detailed(void *unused, scf_walkinfo_t *wip)
2404 scf_snapshot_t *snap;
2405 scf_propertygroup_t *rpg;
2406 scf_iter_t *pg_iter;
2408 char *buf;
2409 char *timebuf;
2410 size_t tbsz;
2411 int ret;
2412 uint64_t c;
2413 int temp, perm;
2414 struct timeval tv;
2415 time_t stime;
2416 struct tm *tmp;
2417 int restarter_spec;
2418 int restarter_ret;
2420 const char * const fmt = "%-*s%s\n";
2422 assert(wip->pg == NULL);
2424 rpg = scf_pg_create(h);
2425 if (rpg == NULL)
2426 scfdie();
2428 if (first_paragraph)
2429 first_paragraph = 0;
2430 else
2431 (void) putchar('\n');
2433 buf = safe_malloc(max_scf_fmri_length + 1);
2435 if (scf_instance_to_fmri(wip->inst, buf, max_scf_fmri_length + 1) != -1)
2436 (void) printf(fmt, DETAILED_WIDTH, "fmri", buf);
2438 if (common_name_buf == NULL)
2439 common_name_buf = safe_malloc(max_scf_value_length + 1);
2441 if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
2442 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
2443 == 0)
2444 (void) printf(fmt, DETAILED_WIDTH, gettext("name"),
2445 common_name_buf);
2446 else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
2447 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
2448 == 0)
2449 (void) printf(fmt, DETAILED_WIDTH, gettext("name"),
2450 common_name_buf);
2452 if (g_zonename != NULL)
2453 (void) printf(fmt, DETAILED_WIDTH, gettext("zone"), g_zonename);
2456 * Synthesize an 'enabled' property that hides the enabled_ovr
2457 * implementation from the user. If the service has been temporarily
2458 * set to a state other than its permanent value, alert the user with
2459 * a '(temporary)' message.
2461 perm = instance_enabled(wip->inst, B_FALSE);
2462 temp = instance_enabled(wip->inst, B_TRUE);
2463 if (temp != -1) {
2464 if (temp != perm)
2465 (void) printf(gettext("%-*s%s (temporary)\n"),
2466 DETAILED_WIDTH, gettext("enabled"),
2467 temp ? gettext("true") : gettext("false"));
2468 else
2469 (void) printf(fmt, DETAILED_WIDTH,
2470 gettext("enabled"), temp ? gettext("true") :
2471 gettext("false"));
2472 } else if (perm != -1) {
2473 (void) printf(fmt, DETAILED_WIDTH, gettext("enabled"),
2474 perm ? gettext("true") : gettext("false"));
2478 * Property values may be longer than max_scf_fmri_length, but these
2479 * shouldn't be, so we'll just reuse buf. The user can use svcprop if
2480 * they suspect something fishy.
2482 if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) {
2483 if (scf_error() != SCF_ERROR_NOT_FOUND)
2484 scfdie();
2486 scf_pg_destroy(rpg);
2487 rpg = NULL;
2490 if (rpg) {
2491 if (pg_get_single_val(rpg, scf_property_state, SCF_TYPE_ASTRING,
2492 buf, max_scf_fmri_length + 1, 0) == 0)
2493 (void) printf(fmt, DETAILED_WIDTH, gettext("state"),
2494 buf);
2496 if (pg_get_single_val(rpg, scf_property_next_state,
2497 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2498 (void) printf(fmt, DETAILED_WIDTH,
2499 gettext("next_state"), buf);
2501 if (pg_get_single_val(rpg, SCF_PROPERTY_STATE_TIMESTAMP,
2502 SCF_TYPE_TIME, &tv, 0, 0) == 0) {
2503 stime = tv.tv_sec;
2504 tmp = localtime(&stime);
2505 for (tbsz = 50; ; tbsz *= 2) {
2506 timebuf = safe_malloc(tbsz);
2507 if (strftime(timebuf, tbsz, NULL, tmp) != 0)
2508 break;
2509 free(timebuf);
2511 (void) printf(fmt, DETAILED_WIDTH,
2512 gettext("state_time"),
2513 timebuf);
2514 free(timebuf);
2517 if (pg_get_single_val(rpg, SCF_PROPERTY_ALT_LOGFILE,
2518 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2519 (void) printf(fmt, DETAILED_WIDTH,
2520 gettext("alt_logfile"), buf);
2522 if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE,
2523 SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2524 (void) printf(fmt, DETAILED_WIDTH, gettext("logfile"),
2525 buf);
2528 if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
2529 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, buf,
2530 max_scf_fmri_length + 1, 0, 0, 1) == 0)
2531 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), buf);
2532 else
2533 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"),
2534 SCF_SERVICE_STARTD);
2536 free(buf);
2539 * Use the restarter specific routine to print the ctids, if available.
2540 * If restarter specific action is available and it fails, then die.
2542 restarter_ret = ctids_by_restarter(wip, &c, 1, 0,
2543 &restarter_spec, print_ctid_header, print_ctid_detailed);
2544 if (restarter_spec == 1) {
2545 if (restarter_ret != 0)
2546 uu_die(gettext("Unable to get restarter for %s"),
2547 wip->fmri);
2548 goto restarter_common;
2551 if (rpg) {
2552 scf_iter_t *iter;
2554 if ((iter = scf_iter_create(h)) == NULL)
2555 scfdie();
2557 if (scf_pg_get_property(rpg, scf_property_contract, g_prop) ==
2558 0) {
2559 if (scf_property_is_type(g_prop, SCF_TYPE_COUNT) == 0) {
2561 /* Callback to print ctid header */
2562 print_ctid_header();
2564 if (scf_iter_property_values(iter, g_prop) != 0)
2565 scfdie();
2567 for (;;) {
2568 ret = scf_iter_next_value(iter, g_val);
2569 if (ret == -1)
2570 scfdie();
2571 if (ret == 0)
2572 break;
2574 if (scf_value_get_count(g_val, &c) != 0)
2575 scfdie();
2577 /* Callback to print contract id. */
2578 print_ctid_detailed(c);
2581 (void) putchar('\n');
2582 } else {
2583 if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
2584 scfdie();
2586 } else {
2587 if (scf_error() != SCF_ERROR_NOT_FOUND)
2588 scfdie();
2591 scf_iter_destroy(iter);
2592 } else {
2593 if (scf_error() != SCF_ERROR_NOT_FOUND)
2594 scfdie();
2597 restarter_common:
2598 scf_pg_destroy(rpg);
2600 /* Dependencies. */
2601 if ((pg_iter = scf_iter_create(h)) == NULL)
2602 scfdie();
2604 snap = get_running_snapshot(wip->inst);
2606 if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap,
2607 SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
2608 scfdie();
2610 while ((ret = scf_iter_next_pg(pg_iter, g_pg)) == 1)
2611 print_detailed_dependency(g_pg);
2612 if (ret == -1)
2613 scfdie();
2615 scf_iter_destroy(pg_iter);
2617 if (opt_processes)
2618 detailed_list_processes(wip);
2620 /* "application" type property groups */
2621 if (opt_verbose == 1)
2622 print_application_properties(wip, snap);
2624 scf_snapshot_destroy(snap);
2626 return (0);
2629 /* ARGSUSED */
2630 static int
2631 print_log(void *unused, scf_walkinfo_t *wip)
2633 scf_propertygroup_t *rpg;
2634 char buf[MAXPATHLEN];
2636 if ((rpg = scf_pg_create(h)) == NULL)
2637 scfdie();
2639 if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) {
2640 if (scf_error() != SCF_ERROR_NOT_FOUND)
2641 scfdie();
2643 goto out;
2646 if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE,
2647 SCF_TYPE_ASTRING, buf, sizeof (buf), 0) == 0) {
2648 (void) printf("%s\n", buf);
2651 out:
2652 scf_pg_destroy(rpg);
2654 return (0);
2658 qsort_str_compare(const void *p1, const void *p2)
2660 return (strcmp((const char *)p1, (const char *)p2));
2664 * get_notify_param_classes()
2665 * return the fma classes that don't have a tag in fma_tags[], otherwise NULL
2667 static char **
2668 get_notify_param_classes()
2670 scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
2671 scf_instance_t *inst = scf_instance_create(h);
2672 scf_snapshot_t *snap = scf_snapshot_create(h);
2673 scf_snaplevel_t *slvl = scf_snaplevel_create(h);
2674 scf_propertygroup_t *pg = scf_pg_create(h);
2675 scf_iter_t *iter = scf_iter_create(h);
2676 int size = 4;
2677 int n = 0;
2678 size_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2679 int err;
2680 char *pgname = safe_malloc(sz);
2681 char **buf = safe_malloc(size * sizeof (char *));
2683 if (h == NULL || inst == NULL || snap == NULL || slvl == NULL ||
2684 pg == NULL || iter == NULL) {
2685 uu_die(gettext("Failed object creation: %s\n"),
2686 scf_strerror(scf_error()));
2689 if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL, NULL, inst,
2690 NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
2691 uu_die(gettext("Failed to decode %s: %s\n"),
2692 SCF_NOTIFY_PARAMS_INST, scf_strerror(scf_error()));
2694 if (scf_instance_get_snapshot(inst, "running", snap) != 0)
2695 uu_die(gettext("Failed to get snapshot: %s\n"),
2696 scf_strerror(scf_error()));
2698 if (scf_snapshot_get_base_snaplevel(snap, slvl) != 0)
2699 uu_die(gettext("Failed to get base snaplevel: %s\n"),
2700 scf_strerror(scf_error()));
2702 if (scf_iter_snaplevel_pgs_typed(iter, slvl,
2703 SCF_NOTIFY_PARAMS_PG_TYPE) != 0)
2704 uu_die(gettext("Failed to get iterator: %s\n"),
2705 scf_strerror(scf_error()));
2707 while ((err = scf_iter_next_pg(iter, pg)) == 1) {
2708 char *c;
2710 if (scf_pg_get_name(pg, pgname, sz) == -1)
2711 uu_die(gettext("Failed to get pg name: %s\n"),
2712 scf_strerror(scf_error()));
2713 if ((c = strrchr(pgname, ',')) != NULL)
2714 *c = '\0';
2715 if (has_fma_tag(pgname))
2716 continue;
2717 if (!is_fma_token(pgname))
2719 * We don't emmit a warning here so that we don't
2720 * pollute the output
2722 continue;
2724 if (n + 1 >= size) {
2725 size *= 2;
2726 buf = reallocarray(buf, size, sizeof (char *));
2727 if (buf == NULL)
2728 uu_die(gettext("Out of memory.\n"));
2730 buf[n] = safe_strdup(pgname);
2731 ++n;
2734 * NULL terminate buf
2736 buf[n] = NULL;
2737 if (err == -1)
2738 uu_die(gettext("Failed to iterate pgs: %s\n"),
2739 scf_strerror(scf_error()));
2741 /* sort the classes */
2742 qsort((void *)buf, n, sizeof (char *), qsort_str_compare);
2744 free(pgname);
2745 scf_iter_destroy(iter);
2746 scf_pg_destroy(pg);
2747 scf_snaplevel_destroy(slvl);
2748 scf_snapshot_destroy(snap);
2749 scf_instance_destroy(inst);
2750 scf_handle_destroy(h);
2752 return (buf);
2756 * get_fma_notify_params()
2757 * populates an nvlist_t with notifycation parameters for a given FMA class
2758 * returns 0 if the nvlist is populated, 1 otherwise;
2761 get_fma_notify_params(nvlist_t *nvl, const char *class)
2763 if (_scf_get_fma_notify_params(class, nvl, 0) != 0) {
2765 * if the preferences have just been deleted
2766 * or does not exist, just skip.
2768 if (scf_error() != SCF_ERROR_NOT_FOUND &&
2769 scf_error() != SCF_ERROR_DELETED)
2770 uu_warn(gettext(
2771 "Failed get_fma_notify_params %s\n"),
2772 scf_strerror(scf_error()));
2774 return (1);
2777 return (0);
2781 * print_notify_fma()
2782 * outputs the notification paramets of FMA events.
2783 * It first outputs classes in fma_tags[], then outputs the other classes
2784 * sorted alphabetically
2786 static void
2787 print_notify_fma(void)
2789 nvlist_t *nvl;
2790 char **tmp = NULL;
2791 char **classes, *p;
2792 const char *class;
2793 uint32_t i;
2795 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
2796 uu_die(gettext("Out of memory.\n"));
2798 for (i = 0; (class = get_fma_class(i)) != NULL; ++i) {
2799 if (get_fma_notify_params(nvl, class) == 0)
2800 listnotify_print(nvl, get_fma_tag(i));
2803 if ((classes = get_notify_param_classes()) == NULL)
2804 goto cleanup;
2806 tmp = classes;
2807 for (p = *tmp; p; ++tmp, p = *tmp) {
2808 if (get_fma_notify_params(nvl, p) == 0)
2809 listnotify_print(nvl, re_tag(p));
2811 free(p);
2814 free(classes);
2816 cleanup:
2817 nvlist_free(nvl);
2821 * print_notify_fmri()
2822 * prints notifycation parameters for an SMF instance.
2824 static void
2825 print_notify_fmri(const char *fmri)
2827 nvlist_t *nvl;
2829 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
2830 uu_die(gettext("Out of memory.\n"));
2832 if (_scf_get_svc_notify_params(fmri, nvl, SCF_TRANSITION_ALL, 0, 0) !=
2833 SCF_SUCCESS) {
2834 if (scf_error() != SCF_ERROR_NOT_FOUND &&
2835 scf_error() != SCF_ERROR_DELETED)
2836 uu_warn(gettext(
2837 "Failed _scf_get_svc_notify_params: %s\n"),
2838 scf_strerror(scf_error()));
2839 } else {
2840 if (strcmp(SCF_INSTANCE_GLOBAL, fmri) == 0)
2841 safe_printf(
2842 gettext("System wide notification parameters:\n"));
2843 safe_printf("%s:\n", fmri);
2844 listnotify_print(nvl, NULL);
2846 nvlist_free(nvl);
2850 * print_notify_special()
2851 * prints notification parameters for FMA events and system wide SMF state
2852 * transitions parameters
2854 static void
2855 print_notify_special()
2857 safe_printf("Notification parameters for FMA Events\n");
2858 print_notify_fma();
2859 print_notify_fmri(SCF_INSTANCE_GLOBAL);
2863 * print_notify()
2864 * callback function to print notification parameters for SMF state transition
2865 * instances. It skips global and notify-params instances as they should be
2866 * printed by print_notify_special()
2868 /* ARGSUSED */
2869 static int
2870 print_notify(void *unused, scf_walkinfo_t *wip)
2872 if (strcmp(SCF_INSTANCE_GLOBAL, wip->fmri) == 0 ||
2873 strcmp(SCF_NOTIFY_PARAMS_INST, wip->fmri) == 0)
2874 return (0);
2876 print_notify_fmri(wip->fmri);
2878 return (0);
2882 * Append a one-lined description of each process in inst's contract(s) and
2883 * return the augmented string.
2885 static char *
2886 add_processes(scf_walkinfo_t *wip, char *line, scf_propertygroup_t *lpg)
2888 pid_t *pids = NULL;
2889 uint_t i, n = 0;
2891 if (lpg == NULL) {
2892 if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
2893 return (line);
2894 } else {
2895 /* Legacy services */
2896 scf_iter_t *iter;
2898 if ((iter = scf_iter_create(h)) == NULL)
2899 scfdie();
2901 (void) propvals_to_pids(lpg, scf_property_contract, &pids, &n,
2902 g_prop, g_val, iter);
2904 scf_iter_destroy(iter);
2907 if (n == 0)
2908 return (line);
2910 qsort(pids, n, sizeof (*pids), pidcmp);
2912 for (i = 0; i < n; ++i) {
2913 char *cp, stime[9];
2914 psinfo_t psi;
2915 struct tm *tm;
2916 int len = 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ;
2918 if (get_psinfo(pids[i], &psi) != 0)
2919 continue;
2921 line = realloc(line, strlen(line) + len);
2922 if (line == NULL)
2923 uu_die(gettext("Out of memory.\n"));
2925 cp = strchr(line, '\0');
2927 tm = localtime(&psi.pr_start.tv_sec);
2930 * Print time if started within the past 24 hours, print date
2931 * if within the past 12 months, print year if started greater
2932 * than 12 months ago.
2934 if (now - psi.pr_start.tv_sec < 24 * 60 * 60)
2935 (void) strftime(stime, sizeof (stime),
2936 gettext(FORMAT_TIME), tm);
2937 else if (now - psi.pr_start.tv_sec < 12 * 30 * 24 * 60 * 60)
2938 (void) strftime(stime, sizeof (stime),
2939 gettext(FORMAT_DATE), tm);
2940 else
2941 (void) strftime(stime, sizeof (stime),
2942 gettext(FORMAT_YEAR), tm);
2944 (void) snprintf(cp, len, "\n %-8s %6ld %.*s",
2945 stime, pids[i], PRFNSZ, psi.pr_fname);
2948 free(pids);
2950 return (line);
2953 /*ARGSUSED*/
2954 static int
2955 list_instance(void *unused, scf_walkinfo_t *wip)
2957 struct avl_string *lp;
2958 char *cp;
2959 int i;
2960 uu_avl_index_t idx;
2963 * If the user has specified a restarter, check for a match first
2965 if (restarters != NULL) {
2966 struct pfmri_list *rest;
2967 int match;
2968 char *restarter_fmri;
2969 const char *scope_name, *svc_name, *inst_name, *pg_name;
2971 /* legacy services don't have restarters */
2972 if (wip->pg != NULL)
2973 return (0);
2975 restarter_fmri = safe_malloc(max_scf_fmri_length + 1);
2977 if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
2978 SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, restarter_fmri,
2979 max_scf_fmri_length + 1, 0, 0, 1) != 0)
2980 (void) strcpy(restarter_fmri, SCF_SERVICE_STARTD);
2982 if (scf_parse_svc_fmri(restarter_fmri, &scope_name, &svc_name,
2983 &inst_name, &pg_name, NULL) != SCF_SUCCESS) {
2984 free(restarter_fmri);
2985 return (0);
2988 match = 0;
2989 for (rest = restarters; rest != NULL; rest = rest->next) {
2990 if (strcmp(rest->scope, scope_name) == 0 &&
2991 strcmp(rest->service, svc_name) == 0 &&
2992 strcmp(rest->instance, inst_name) == 0)
2993 match = 1;
2996 free(restarter_fmri);
2998 if (!match)
2999 return (0);
3002 if (wip->pg == NULL && ht_buckets != NULL && ht_add(wip->fmri)) {
3003 /* It was already there. */
3004 return (0);
3007 lp = safe_malloc(sizeof (*lp));
3009 lp->str = NULL;
3010 for (i = 0; i < opt_cnum; ++i) {
3011 columns[opt_columns[i]].sprint(&lp->str, wip);
3013 cp = lp->str + strlen(lp->str);
3014 cp--;
3015 while (*cp == ' ')
3016 cp--;
3017 *(cp+1) = '\0';
3019 /* If we're supposed to list the processes, too, do that now. */
3020 if (opt_processes)
3021 lp->str = add_processes(wip, lp->str, wip->pg);
3023 /* Create the sort key. */
3024 cp = lp->key = safe_malloc(sortkey_sz);
3025 for (i = 0; i < opt_snum; ++i) {
3026 int j = opt_sort[i] & 0xff;
3028 assert(columns[j].get_sortkey != NULL);
3029 columns[j].get_sortkey(cp, opt_sort[i] & ~0xff, wip);
3030 cp += columns[j].sortkey_width;
3033 /* Insert into AVL tree. */
3034 uu_avl_node_init(lp, &lp->node, lines_pool);
3035 (void) uu_avl_find(lines, lp, NULL, &idx);
3036 uu_avl_insert(lines, lp, idx);
3038 return (0);
3041 static int
3042 list_if_enabled(void *unused, scf_walkinfo_t *wip)
3044 if (wip->pg != NULL ||
3045 instance_enabled(wip->inst, B_FALSE) == 1 ||
3046 instance_enabled(wip->inst, B_TRUE) == 1)
3047 return (list_instance(unused, wip));
3049 return (0);
3053 * Service FMRI selection: Lookup and call list_instance() for the instances.
3054 * Instance FMRI selection: Lookup and call list_instance().
3056 * Note: This is shoehorned into a walk_dependencies() callback prototype so
3057 * it can be used in list_dependencies.
3059 static int
3060 list_svc_or_inst_fmri(void *complain, scf_walkinfo_t *wip)
3062 char *fmri;
3063 const char *svc_name, *inst_name, *pg_name, *save;
3064 scf_iter_t *iter;
3065 int ret;
3067 fmri = safe_strdup(wip->fmri);
3069 if (scf_parse_svc_fmri(fmri, NULL, &svc_name, &inst_name, &pg_name,
3070 NULL) != SCF_SUCCESS) {
3071 if (complain)
3072 uu_warn(gettext("FMRI \"%s\" is invalid.\n"),
3073 wip->fmri);
3074 exit_status = UU_EXIT_FATAL;
3075 free(fmri);
3076 return (0);
3080 * Yes, this invalidates *_name, but we only care whether they're NULL
3081 * or not.
3083 free(fmri);
3085 if (svc_name == NULL || pg_name != NULL) {
3086 if (complain)
3087 uu_warn(gettext("FMRI \"%s\" does not designate a "
3088 "service or instance.\n"), wip->fmri);
3089 return (0);
3092 if (inst_name != NULL) {
3093 /* instance */
3094 if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc,
3095 wip->inst, NULL, NULL, 0) != SCF_SUCCESS) {
3096 if (scf_error() != SCF_ERROR_NOT_FOUND)
3097 scfdie();
3099 if (complain)
3100 uu_warn(gettext(
3101 "Instance \"%s\" does not exist.\n"),
3102 wip->fmri);
3103 return (0);
3106 return (list_instance(NULL, wip));
3109 /* service: Walk the instances. */
3110 if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, NULL,
3111 NULL, NULL, 0) != SCF_SUCCESS) {
3112 if (scf_error() != SCF_ERROR_NOT_FOUND)
3113 scfdie();
3115 if (complain)
3116 uu_warn(gettext("Service \"%s\" does not exist.\n"),
3117 wip->fmri);
3119 exit_status = UU_EXIT_FATAL;
3121 return (0);
3124 iter = scf_iter_create(h);
3125 if (iter == NULL)
3126 scfdie();
3128 if (scf_iter_service_instances(iter, wip->svc) != SCF_SUCCESS)
3129 scfdie();
3131 if ((fmri = malloc(max_scf_fmri_length + 1)) == NULL) {
3132 scf_iter_destroy(iter);
3133 exit_status = UU_EXIT_FATAL;
3134 return (0);
3137 save = wip->fmri;
3138 wip->fmri = fmri;
3139 while ((ret = scf_iter_next_instance(iter, wip->inst)) == 1) {
3140 if (scf_instance_to_fmri(wip->inst, fmri,
3141 max_scf_fmri_length + 1) <= 0)
3142 scfdie();
3143 (void) list_instance(NULL, wip);
3145 free(fmri);
3146 wip->fmri = save;
3147 if (ret == -1)
3148 scfdie();
3150 exit_status = UU_EXIT_OK;
3152 scf_iter_destroy(iter);
3154 return (0);
3158 * Dependency selection: Straightforward since each instance lists the
3159 * services it depends on.
3162 static void
3163 walk_dependencies(scf_walkinfo_t *wip, scf_walk_callback callback, void *data)
3165 scf_snapshot_t *snap;
3166 scf_iter_t *iter, *viter;
3167 int ret, vret;
3168 char *dep;
3170 assert(wip->inst != NULL);
3172 if ((iter = scf_iter_create(h)) == NULL ||
3173 (viter = scf_iter_create(h)) == NULL)
3174 scfdie();
3176 snap = get_running_snapshot(wip->inst);
3178 if (scf_iter_instance_pgs_typed_composed(iter, wip->inst, snap,
3179 SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
3180 scfdie();
3182 dep = safe_malloc(max_scf_value_length + 1);
3184 while ((ret = scf_iter_next_pg(iter, g_pg)) == 1) {
3185 scf_type_t ty;
3187 /* Ignore exclude_any dependencies. */
3188 if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) !=
3189 SCF_SUCCESS) {
3190 if (scf_error() != SCF_ERROR_NOT_FOUND)
3191 scfdie();
3193 continue;
3196 if (scf_property_type(g_prop, &ty) != SCF_SUCCESS)
3197 scfdie();
3199 if (ty != SCF_TYPE_ASTRING)
3200 continue;
3202 if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) {
3203 if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
3204 scfdie();
3206 continue;
3209 if (scf_value_get_astring(g_val, dep,
3210 max_scf_value_length + 1) < 0)
3211 scfdie();
3213 if (strcmp(dep, SCF_DEP_EXCLUDE_ALL) == 0)
3214 continue;
3216 if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) !=
3217 SCF_SUCCESS) {
3218 if (scf_error() != SCF_ERROR_NOT_FOUND)
3219 scfdie();
3221 continue;
3224 if (scf_iter_property_values(viter, g_prop) != SCF_SUCCESS)
3225 scfdie();
3227 while ((vret = scf_iter_next_value(viter, g_val)) == 1) {
3228 if (scf_value_get_astring(g_val, dep,
3229 max_scf_value_length + 1) < 0)
3230 scfdie();
3232 wip->fmri = dep;
3233 if (callback(data, wip) != 0)
3234 goto out;
3236 if (vret == -1)
3237 scfdie();
3239 if (ret == -1)
3240 scfdie();
3242 out:
3243 scf_iter_destroy(viter);
3244 scf_iter_destroy(iter);
3245 scf_snapshot_destroy(snap);
3248 static int
3249 list_dependencies(void *data, scf_walkinfo_t *wip)
3251 walk_dependencies(wip, list_svc_or_inst_fmri, data);
3252 return (0);
3257 * Dependent selection: The "providing" service's or instance's FMRI is parsed
3258 * into the provider_* variables, the instances are walked, and any instance
3259 * which lists an FMRI which parses to these components is selected. This is
3260 * inefficient in the face of multiple operands, but that should be uncommon.
3263 static char *provider_scope;
3264 static char *provider_svc;
3265 static char *provider_inst; /* NULL for services */
3267 /*ARGSUSED*/
3268 static int
3269 check_against_provider(void *arg, scf_walkinfo_t *wip)
3271 char *cfmri;
3272 const char *scope_name, *svc_name, *inst_name, *pg_name;
3273 int *matchp = arg;
3275 cfmri = safe_strdup(wip->fmri);
3277 if (scf_parse_svc_fmri(cfmri, &scope_name, &svc_name, &inst_name,
3278 &pg_name, NULL) != SCF_SUCCESS) {
3279 free(cfmri);
3280 return (0);
3283 if (svc_name == NULL || pg_name != NULL) {
3284 free(cfmri);
3285 return (0);
3289 * If the user has specified an instance, then also match dependencies
3290 * on the service itself.
3292 *matchp = (strcmp(provider_scope, scope_name) == 0 &&
3293 strcmp(provider_svc, svc_name) == 0 &&
3294 (provider_inst == NULL ? (inst_name == NULL) :
3295 (inst_name == NULL || strcmp(provider_inst, inst_name) == 0)));
3297 free(cfmri);
3299 /* Stop on matches. */
3300 return (*matchp);
3303 static int
3304 list_if_dependent(void *unused, scf_walkinfo_t *wip)
3306 /* Only proceed if this instance depends on provider_*. */
3307 int match = 0;
3309 (void) walk_dependencies(wip, check_against_provider, &match);
3311 if (match)
3312 return (list_instance(unused, wip));
3314 return (0);
3317 /*ARGSUSED*/
3318 static int
3319 list_dependents(void *unused, scf_walkinfo_t *wip)
3321 char *save;
3322 int ret;
3324 if (scf_scope_get_name(wip->scope, provider_scope,
3325 max_scf_fmri_length) <= 0 ||
3326 scf_service_get_name(wip->svc, provider_svc,
3327 max_scf_fmri_length) <= 0)
3328 scfdie();
3330 save = provider_inst;
3331 if (wip->inst == NULL)
3332 provider_inst = NULL;
3333 else if (scf_instance_get_name(wip->inst, provider_inst,
3334 max_scf_fmri_length) <= 0)
3335 scfdie();
3337 ret = scf_walk_fmri(h, 0, NULL, 0, list_if_dependent, NULL, NULL,
3338 uu_warn);
3340 provider_inst = save;
3342 return (ret);
3346 * main() & helpers
3349 static void
3350 add_sort_column(const char *col, int reverse)
3352 int i;
3354 ++opt_snum;
3356 opt_sort = reallocarray(opt_sort, opt_snum, sizeof (*opt_sort));
3357 if (opt_sort == NULL)
3358 uu_die(gettext("Too many sort criteria: out of memory.\n"));
3360 for (i = 0; i < ncolumns; ++i) {
3361 if (strcasecmp(col, columns[i].name) == 0)
3362 break;
3365 if (i < ncolumns)
3366 opt_sort[opt_snum - 1] = (reverse ? i | 0x100 : i);
3367 else
3368 uu_die(gettext("Unrecognized sort column \"%s\".\n"), col);
3370 sortkey_sz += columns[i].sortkey_width;
3373 static void
3374 add_restarter(const char *fmri)
3376 char *cfmri;
3377 const char *pg_name;
3378 struct pfmri_list *rest;
3380 cfmri = safe_strdup(fmri);
3381 rest = safe_malloc(sizeof (*rest));
3383 if (scf_parse_svc_fmri(cfmri, &rest->scope, &rest->service,
3384 &rest->instance, &pg_name, NULL) != SCF_SUCCESS)
3385 uu_die(gettext("Restarter FMRI \"%s\" is invalid.\n"), fmri);
3387 if (rest->instance == NULL || pg_name != NULL)
3388 uu_die(gettext("Restarter FMRI \"%s\" does not designate an "
3389 "instance.\n"), fmri);
3391 rest->next = restarters;
3392 restarters = rest;
3393 return;
3395 err:
3396 free(cfmri);
3397 free(rest);
3400 /* ARGSUSED */
3401 static int
3402 line_cmp(const void *l_arg, const void *r_arg, void *private)
3404 const struct avl_string *l = l_arg;
3405 const struct avl_string *r = r_arg;
3407 return (memcmp(l->key, r->key, sortkey_sz));
3410 /* ARGSUSED */
3411 static int
3412 print_line(void *e, void *private)
3414 struct avl_string *lp = e;
3416 (void) puts(lp->str);
3418 return (UU_WALK_NEXT);
3421 /* ARGSUSED */
3422 static void
3423 errignore(const char *str, ...)
3427 main(int argc, char **argv)
3429 char opt, opt_mode;
3430 int i, n;
3431 char *columns_str = NULL;
3432 char *cp;
3433 const char *progname;
3434 int err, missing = 1, ignored, *errarg;
3435 uint_t nzents = 0, zent = 0;
3436 zoneid_t *zids = NULL;
3437 char zonename[ZONENAME_MAX];
3438 void (*errfunc)(const char *, ...);
3440 int show_all = 0;
3441 int show_header = 1;
3442 int show_zones = 0;
3444 const char * const options = "aHpvno:R:s:S:dDlL?xZz:";
3446 (void) setlocale(LC_ALL, "");
3448 locale = setlocale(LC_MESSAGES, NULL);
3449 if (locale) {
3450 locale = safe_strdup(locale);
3451 _scf_sanitize_locale(locale);
3454 (void) textdomain(TEXT_DOMAIN);
3455 progname = uu_setpname(argv[0]);
3457 exit_status = UU_EXIT_OK;
3459 max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
3460 max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3461 max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
3462 max_scf_type_length = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH);
3464 if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
3465 max_scf_fmri_length == -1 || max_scf_type_length == -1)
3466 scfdie();
3468 now = time(NULL);
3469 assert(now != -1);
3472 * opt_mode is the mode of operation. 0 for plain, 'd' for
3473 * dependencies, 'D' for dependents, and 'l' for detailed (long). We
3474 * need to know now so we know which options are valid.
3476 opt_mode = 0;
3477 while ((opt = getopt(argc, argv, options)) != -1) {
3478 switch (opt) {
3479 case '?':
3480 if (optopt == '?') {
3481 print_help(progname);
3482 return (UU_EXIT_OK);
3483 } else {
3484 argserr(progname);
3485 /* NOTREACHED */
3488 case 'd':
3489 case 'D':
3490 case 'l':
3491 case 'L':
3492 if (opt_mode != 0)
3493 argserr(progname);
3495 opt_mode = opt;
3496 break;
3498 case 'n':
3499 if (opt_mode != 0)
3500 argserr(progname);
3502 opt_mode = opt;
3503 break;
3505 case 'x':
3506 if (opt_mode != 0)
3507 argserr(progname);
3509 opt_mode = opt;
3510 break;
3512 default:
3513 break;
3517 sortkey_sz = 0;
3519 optind = 1; /* Reset getopt() */
3520 while ((opt = getopt(argc, argv, options)) != -1) {
3521 switch (opt) {
3522 case 'a':
3523 if (opt_mode != 0)
3524 argserr(progname);
3525 show_all = 1;
3526 break;
3528 case 'H':
3529 if (opt_mode == 'l' || opt_mode == 'x')
3530 argserr(progname);
3531 show_header = 0;
3532 break;
3534 case 'p':
3535 if (opt_mode == 'x')
3536 argserr(progname);
3537 opt_processes = 1;
3538 break;
3540 case 'v':
3541 opt_verbose = 1;
3542 break;
3544 case 'o':
3545 if (opt_mode == 'l' || opt_mode == 'x')
3546 argserr(progname);
3547 columns_str = optarg;
3548 break;
3550 case 'R':
3551 if (opt_mode != 0 || opt_mode == 'x')
3552 argserr(progname);
3554 add_restarter(optarg);
3555 break;
3557 case 's':
3558 case 'S':
3559 if (opt_mode != 0)
3560 argserr(progname);
3562 add_sort_column(optarg, optopt == 'S');
3563 break;
3565 case 'd':
3566 case 'D':
3567 case 'l':
3568 case 'L':
3569 case 'n':
3570 case 'x':
3571 assert(opt_mode == optopt);
3572 break;
3574 case 'z':
3575 if (getzoneid() != GLOBAL_ZONEID)
3576 uu_die(gettext("svcs -z may only be used from "
3577 "the global zone\n"));
3578 if (show_zones)
3579 argserr(progname);
3581 opt_zone = optarg;
3582 break;
3584 case 'Z':
3585 if (getzoneid() != GLOBAL_ZONEID)
3586 uu_die(gettext("svcs -Z may only be used from "
3587 "the global zone\n"));
3588 if (opt_zone != NULL)
3589 argserr(progname);
3591 show_zones = 1;
3592 break;
3594 case '?':
3595 argserr(progname);
3596 /* NOTREACHED */
3598 default:
3599 assert(0);
3600 abort();
3605 * -a is only meaningful when given no arguments
3607 if (show_all && optind != argc)
3608 uu_warn(gettext("-a ignored when used with arguments.\n"));
3610 while (show_zones) {
3611 uint_t found;
3613 if (zone_list(NULL, &nzents) != 0)
3614 uu_die(gettext("could not get number of zones"));
3616 if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL) {
3617 uu_die(gettext("could not allocate array for "
3618 "%d zone IDs"), nzents);
3621 found = nzents;
3623 if (zone_list(zids, &found) != 0)
3624 uu_die(gettext("could not get zone list"));
3627 * If the number of zones has not changed between our calls to
3628 * zone_list(), we're done -- otherwise, we must free our array
3629 * of zone IDs and take another lap.
3631 if (found == nzents)
3632 break;
3634 free(zids);
3637 argc -= optind;
3638 argv += optind;
3640 again:
3641 h = scf_handle_create(SCF_VERSION);
3642 if (h == NULL)
3643 scfdie();
3645 if (opt_zone != NULL || zids != NULL) {
3646 scf_value_t *zone;
3648 assert(opt_zone == NULL || zids == NULL);
3650 if (opt_zone == NULL) {
3651 if (getzonenamebyid(zids[zent++],
3652 zonename, sizeof (zonename)) < 0) {
3653 uu_warn(gettext("could not get name for "
3654 "zone %d; ignoring"), zids[zent - 1]);
3655 goto nextzone;
3658 g_zonename = zonename;
3659 } else {
3660 g_zonename = opt_zone;
3663 if ((zone = scf_value_create(h)) == NULL)
3664 scfdie();
3666 if (scf_value_set_astring(zone, g_zonename) != SCF_SUCCESS)
3667 scfdie();
3669 if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS)
3670 uu_die(gettext("invalid zone '%s'\n"), g_zonename);
3672 scf_value_destroy(zone);
3675 if (scf_handle_bind(h) == -1) {
3676 if (g_zonename != NULL) {
3677 uu_warn(gettext("Could not bind to repository "
3678 "server for zone %s: %s\n"), g_zonename,
3679 scf_strerror(scf_error()));
3681 if (!show_zones)
3682 return (UU_EXIT_FATAL);
3684 goto nextzone;
3687 uu_die(gettext("Could not bind to repository server: %s. "
3688 "Exiting.\n"), scf_strerror(scf_error()));
3691 if ((g_pg = scf_pg_create(h)) == NULL ||
3692 (g_prop = scf_property_create(h)) == NULL ||
3693 (g_val = scf_value_create(h)) == NULL)
3694 scfdie();
3696 if (show_zones) {
3698 * It's hard to avoid editorializing here, but suffice it to
3699 * say that scf_walk_fmri() takes an error handler, the
3700 * interface to which has been regrettably misdesigned: the
3701 * handler itself takes exclusively a string -- even though
3702 * scf_walk_fmri() has detailed, programmatic knowledge
3703 * of the error condition at the time it calls its errfunc.
3704 * That is, only the error message and not the error semantics
3705 * are given to the handler. This is poor interface at best,
3706 * but it is particularly problematic when we are talking to
3707 * multiple repository servers (as when we are iterating over
3708 * all zones) as we do not want to treat failure to find a
3709 * match in one zone as overall failure. Ideally, we would
3710 * simply ignore SCF_MSG_PATTERN_NOINSTANCE and correctly
3711 * process the others, but alas, no such interface exists --
3712 * and we must settle for instead ignoring all errfunc-called
3713 * errors in the case that we are iterating over all zones...
3715 errfunc = errignore;
3716 errarg = missing ? &missing : &ignored;
3717 missing = 0;
3718 } else {
3719 errfunc = uu_warn;
3720 errarg = &exit_status;
3724 * If we're in long mode, take care of it now before we deal with the
3725 * sorting and the columns, since we won't use them anyway.
3727 if (opt_mode == 'l') {
3728 if (argc == 0)
3729 argserr(progname);
3731 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3732 print_detailed, NULL, errarg, errfunc)) != 0) {
3733 uu_warn(gettext("failed to iterate over "
3734 "instances: %s\n"), scf_strerror(err));
3735 exit_status = UU_EXIT_FATAL;
3738 goto nextzone;
3741 if (opt_mode == 'L') {
3742 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3743 print_log, NULL, &exit_status, uu_warn)) != 0) {
3744 uu_warn(gettext("failed to iterate over "
3745 "instances: %s\n"), scf_strerror(err));
3746 exit_status = UU_EXIT_FATAL;
3749 goto nextzone;
3752 if (opt_mode == 'n') {
3753 print_notify_special();
3754 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3755 print_notify, NULL, errarg, errfunc)) != 0) {
3756 uu_warn(gettext("failed to iterate over "
3757 "instances: %s\n"), scf_strerror(err));
3758 exit_status = UU_EXIT_FATAL;
3761 goto nextzone;
3764 if (opt_mode == 'x') {
3765 explain(opt_verbose, argc, argv);
3766 goto nextzone;
3769 if (columns_str == NULL) {
3770 if (opt_snum == 0) {
3771 if (show_zones)
3772 add_sort_column("zone", 0);
3774 /* Default sort. */
3775 add_sort_column("state", 0);
3776 add_sort_column("stime", 0);
3777 add_sort_column("fmri", 0);
3780 if (!opt_verbose) {
3781 columns_str = safe_strdup(show_zones ?
3782 "zone,state,stime,fmri" : "state,stime,fmri");
3783 } else {
3784 columns_str = safe_strdup(show_zones ?
3785 "zone,state,nstate,stime,ctid,fmri" :
3786 "state,nstate,stime,ctid,fmri");
3790 if (opt_columns == NULL) {
3791 /* Decode columns_str into opt_columns. */
3792 line_sz = 0;
3794 opt_cnum = 1;
3795 for (cp = columns_str; *cp != '\0'; ++cp)
3796 if (*cp == ',')
3797 ++opt_cnum;
3799 if (*columns_str == '\0')
3800 uu_die(gettext("No columns specified.\n"));
3802 opt_columns = malloc(opt_cnum * sizeof (*opt_columns));
3803 if (opt_columns == NULL)
3804 uu_die(gettext("Too many columns.\n"));
3806 for (n = 0; *columns_str != '\0'; ++n) {
3807 i = getcolumnopt(&columns_str);
3808 if (i == -1)
3809 uu_die(gettext("Unknown column \"%s\".\n"),
3810 columns_str);
3812 if (strcmp(columns[i].name, "N") == 0 ||
3813 strcmp(columns[i].name, "SN") == 0 ||
3814 strcmp(columns[i].name, "NSTA") == 0 ||
3815 strcmp(columns[i].name, "NSTATE") == 0)
3816 opt_nstate_shown = 1;
3818 opt_columns[n] = i;
3819 line_sz += columns[i].width + 1;
3822 if ((lines_pool = uu_avl_pool_create("lines_pool",
3823 sizeof (struct avl_string), offsetof(struct avl_string,
3824 node), line_cmp, UU_AVL_DEBUG)) == NULL ||
3825 (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL)
3826 uu_die(gettext("Unexpected libuutil error: %s\n"),
3827 uu_strerror(uu_error()));
3830 switch (opt_mode) {
3831 case 0:
3833 * If we already have a hash table (e.g., because we are
3834 * processing multiple zones), destroy it before creating
3835 * a new one.
3837 if (ht_buckets != NULL)
3838 ht_free();
3840 ht_init();
3842 /* Always show all FMRIs when given arguments or restarters */
3843 if (argc != 0 || restarters != NULL)
3844 show_all = 1;
3846 if ((err = scf_walk_fmri(h, argc, argv,
3847 SCF_WALK_MULTIPLE | SCF_WALK_LEGACY,
3848 show_all ? list_instance : list_if_enabled, NULL,
3849 errarg, errfunc)) != 0) {
3850 uu_warn(gettext("failed to iterate over "
3851 "instances: %s\n"), scf_strerror(err));
3852 exit_status = UU_EXIT_FATAL;
3854 break;
3856 case 'd':
3857 if (argc == 0)
3858 argserr(progname);
3860 if ((err = scf_walk_fmri(h, argc, argv,
3861 SCF_WALK_MULTIPLE, list_dependencies, NULL,
3862 errarg, errfunc)) != 0) {
3863 uu_warn(gettext("failed to iterate over "
3864 "instances: %s\n"), scf_strerror(err));
3865 exit_status = UU_EXIT_FATAL;
3867 break;
3869 case 'D':
3870 if (argc == 0)
3871 argserr(progname);
3873 provider_scope = safe_malloc(max_scf_fmri_length);
3874 provider_svc = safe_malloc(max_scf_fmri_length);
3875 provider_inst = safe_malloc(max_scf_fmri_length);
3877 if ((err = scf_walk_fmri(h, argc, argv,
3878 SCF_WALK_MULTIPLE | SCF_WALK_SERVICE,
3879 list_dependents, NULL, &exit_status, uu_warn)) != 0) {
3880 uu_warn(gettext("failed to iterate over "
3881 "instances: %s\n"), scf_strerror(err));
3882 exit_status = UU_EXIT_FATAL;
3885 free(provider_scope);
3886 free(provider_svc);
3887 free(provider_inst);
3888 break;
3890 case 'n':
3891 break;
3893 default:
3894 assert(0);
3895 abort();
3898 nextzone:
3899 if (show_zones && zent < nzents && exit_status == 0) {
3900 scf_handle_destroy(h);
3901 goto again;
3904 if (show_zones && exit_status == 0)
3905 exit_status = missing;
3907 if (opt_columns == NULL)
3908 return (exit_status);
3910 if (show_header)
3911 print_header();
3913 (void) uu_avl_walk(lines, print_line, NULL, 0);
3915 return (exit_status);