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]
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)
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.
52 #include "notify_params.h"
54 /* Get the byteorder macros to ease sorting. */
55 #include <sys/types.h>
56 #include <netinet/in.h>
59 #include <sys/contract.h>
67 #include <libcontract.h>
68 #include <libcontract_priv.h>
71 #include <libscf_priv.h>
73 #include <libnvpair.h>
81 #include <libzonecfg.h>
85 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
86 #endif /* TEXT_DOMAIN */
88 #define LEGACY_UNKNOWN "unknown"
90 /* Flags for pg_get_single_val() */
96 * An AVL-storable node for output lines and the keys to sort them by.
105 * For lists of parsed restarter FMRIs.
110 const char *instance
;
111 struct pfmri_list
*next
;
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. */
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
;
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
] = "";
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
;
166 * For unexpected libscf errors. The ending newline is necessary to keep
167 * uu_die() from appending the errno error.
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()));
180 uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"),
181 scf_strerror(scf_error()));
186 safe_malloc(size_t sz
)
192 uu_die(gettext("Out of memory"));
198 safe_strdup(const char *str
)
204 uu_die(gettext("Out of memory.\n"));
210 * FMRI hashtable. For uniquifing listings.
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
;
225 struct ht_elem
*elem
, *next
;
228 for (i
= 0; i
< ht_buckets_num
; i
++) {
229 for (elem
= ht_buckets
[i
]; elem
!= NULL
; elem
= next
) {
231 free((char *)elem
->fmri
);
244 assert(ht_buckets
== NULL
);
247 ht_buckets
= safe_malloc(sizeof (*ht_buckets
) * ht_buckets_num
);
248 bzero(ht_buckets
, sizeof (*ht_buckets
) * ht_buckets_num
);
253 ht_hash_fmri(const char *fmri
)
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 uts/common/os/modhash.c.
265 for (p
= k
; *p
!= '\0'; ++p
) {
267 if ((g
= (h
& 0xf0000000)) != 0) {
279 uint_t new_ht_buckets_num
;
280 struct ht_elem
**new_ht_buckets
;
283 new_ht_buckets_num
= ht_buckets_num
* 2;
284 assert(new_ht_buckets_num
> ht_buckets_num
);
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
) {
297 h
= ht_hash_fmri(elem
->fmri
);
300 new_ht_buckets
[h
& (new_ht_buckets_num
- 1)];
301 new_ht_buckets
[h
& (new_ht_buckets_num
- 1)] = elem
;
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,
316 ht_add(const char *fmri
)
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)
330 /* Grow when average chain length is over 3. */
331 if (ht_num
> 3 * ht_buckets_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
;
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
];
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
)
377 if (scf_property_is_type(g_prop
, ty
) != SCF_SUCCESS
) {
378 if (scf_error() == SCF_ERROR_TYPE_MISMATCH
)
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
)
390 case SCF_ERROR_CONSTRAINT_VIOLATED
:
391 if (flags
& MULTI_OK
) {
397 case SCF_ERROR_PERMISSION_DENIED
:
404 case SCF_TYPE_ASTRING
:
405 r
= scf_value_get_astring(g_val
, vp
, sz
) > 0 ? SCF_SUCCESS
: -1;
408 case SCF_TYPE_BOOLEAN
:
409 r
= scf_value_get_boolean(g_val
, (uint8_t *)vp
);
413 r
= scf_value_get_count(g_val
, (uint64_t *)vp
);
416 case SCF_TYPE_INTEGER
:
417 r
= scf_value_get_integer(g_val
, (int64_t *)vp
);
420 case SCF_TYPE_TIME
: {
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;
429 case SCF_TYPE_USTRING
:
430 r
= scf_value_get_ustring(g_val
, vp
, sz
) > 0 ? SCF_SUCCESS
: -1;
435 uu_warn("%s:%d: Unknown type %d.\n", __FILE__
, __LINE__
, ty
);
439 if (r
!= SCF_SUCCESS
)
442 ret
= multi
? E2BIG
: 0;
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)
451 uu_warn(gettext("Property \"%s\" is misconfigured.\n"), buf
);
456 if (ret
!= 0 || g_zonename
== NULL
||
457 (strcmp(propname
, SCF_PROPERTY_LOGFILE
) != 0 &&
458 strcmp(propname
, SCF_PROPERTY_ALT_LOGFILE
) != 0))
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.
467 (void) zone_get_rootpath(g_zonename
, root
, sizeof (root
));
468 (void) strlcat(root
, vp
, sizeof (root
));
469 (void) snprintf(vp
, sz
, "%s", root
);
474 static scf_snapshot_t
*
475 get_running_snapshot(scf_instance_t
*inst
)
477 scf_snapshot_t
*snap
;
479 snap
= scf_snapshot_create(h
);
483 if (scf_instance_get_snapshot(inst
, "running", snap
) == 0)
486 if (scf_error() != SCF_ERROR_NOT_FOUND
)
489 scf_snapshot_destroy(snap
);
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
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
;
509 snap
= get_running_snapshot(inst
);
510 if (composed
|| use_running
)
511 r
= scf_instance_get_pg_composed(inst
, snap
, pgname
, g_pg
);
513 r
= scf_instance_get_pg(inst
, pgname
, g_pg
);
515 scf_snapshot_destroy(snap
);
519 r
= pg_get_single_val(g_pg
, propname
, ty
, vp
, sz
, flags
);
525 instance_enabled(scf_instance_t
*inst
, boolean_t temp
)
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)
538 * Get a string property from the restarter property group of the given
539 * instance. Return an empty string on normal problems.
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)
551 get_restarter_time_prop(scf_instance_t
*inst
, const char *pname
,
552 struct timeval
*tvp
, int ok_if_empty
)
556 r
= inst_get_single_val(inst
, SCF_PG_RESTARTER
, pname
, SCF_TYPE_TIME
,
557 tvp
, NULL
, ok_if_empty
? EMPTY_OK
: 0, 0, 1);
559 return (r
== 0 ? 0 : -1);
563 get_restarter_count_prop(scf_instance_t
*inst
, const char *pname
, uint64_t *cp
,
566 return (inst_get_single_val(inst
, SCF_PG_RESTARTER
, pname
,
567 SCF_TYPE_COUNT
, cp
, 0, flags
, 0, 1));
576 * Return an array of pids associated with the given contract id.
577 * Returned pids are added to the end of the pidsp array.
580 ctid_to_pids(uint64_t c
, pid_t
**pidsp
, uint_t
*np
)
588 fd
= contract_open(c
, NULL
, "status", O_RDONLY
);
592 err
= ct_status_read(fd
, CTD_ALL
, &ctst
);
594 uu_warn(gettext("Could not read status of contract "
595 "%ld: %s.\n"), c
, strerror(err
));
602 r
= ct_pr_status_get_members(ctst
, &pids
, &m
);
606 ct_status_free(ctst
);
610 *pidsp
= realloc(*pidsp
, (*np
+ m
) * sizeof (*pidsp
));
612 uu_die(gettext("Out of memory"));
614 bcopy(pids
, *pidsp
+ *np
, m
* sizeof (*pids
));
617 ct_status_free(ctst
);
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
)
628 if (scf_pg_get_property(pg
, pname
, prop
) != 0) {
629 if (scf_error() != SCF_ERROR_NOT_FOUND
)
635 if (scf_property_type(prop
, &ty
) != 0)
638 if (ty
!= SCF_TYPE_COUNT
)
641 if (scf_iter_property_values(iter
, prop
) != 0)
645 r
= scf_iter_next_value(iter
, val
);
651 if (scf_value_get_count(val
, &c
) != 0)
654 ctid_to_pids(c
, pidsp
, np
);
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.
666 check_for_restarter(scf_instance_t
*inst
, const char *restarter
)
669 char *fmri_buf_canonified
= NULL
;
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)
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)
687 if (strcmp(fmri_buf
, restarter
) == 0)
692 if (fmri_buf_canonified
)
693 free(fmri_buf_canonified
);
698 * Common code that is used by ctids_by_restarter and pids_by_restarter.
699 * Checks for a common restarter and if one is available, it generates
700 * the appropriate filename using wip->fmri and stores that in the
701 * global genfmri_filename.
703 * Restarters currently supported are: svc:/network/inetd:default
704 * If a restarter specific action is available, then restarter_spec
705 * is set to 1. If a restarter specific action is not available, then
706 * restarter_spec is set to 0 and a -1 is returned.
709 * 0 if success: restarter specific action found and filename generated
710 * -1 if restarter specific action not found,
711 * if restarter specific action found but an error was encountered
712 * during the generation of the wip->fmri based filename
715 common_by_restarter(scf_instance_t
*inst
, const char *fmri
,
716 int *restarter_specp
)
721 /* Check for inetd specific restarter */
722 if (check_for_restarter(inst
, "svc:/network/inetd:default") != 0) {
723 *restarter_specp
= 0;
727 *restarter_specp
= 1;
729 /* Get the ctid filename associated with this instance */
730 r
= gen_filenms_from_fmri(fmri
, "ctid", genfmri_filename
, NULL
);
738 * Unable to get filename from fmri. Print warning
739 * and return failure with no ctids.
741 uu_warn(gettext("Unable to read contract ids for %s -- "
742 "FMRI is too long\n"), fmri
);
747 * The directory didn't exist, so no contracts.
748 * Return failure with no ctids.
753 uu_warn(gettext("%s:%d: gen_filenms_from_fmri() failed with "
754 "unknown error %d\n"), __FILE__
, __LINE__
, r
);
763 * Get or print a contract id using a restarter specific action.
765 * If the print_flag is not set, this routine gets the single contract
766 * id associated with this instance.
767 * If the print flag is set, then print each contract id found.
770 * 0 if success: restarter specific action found and used with no error
771 * -1 if restarter specific action not found
772 * -1 if restarter specific action found, but there was a failure
773 * -1 if print flag is not set and no contract id is found or multiple
774 * contract ids were found
775 * E2BIG if print flag is not set, MULTI_OK bit in flag is set and multiple
776 * contract ids were found
779 ctids_by_restarter(scf_walkinfo_t
*wip
, uint64_t *cp
, int print_flag
,
780 uint_t flags
, int *restarter_specp
, void (*callback_header
)(),
781 void (*callback_ctid
)(uint64_t))
789 /* Check if callbacks are needed and were passed in */
791 if ((callback_header
== NULL
) || (callback_ctid
== NULL
))
795 /* Check for restarter specific action and generation of filename */
796 rest_ret
= common_by_restarter(wip
->inst
, wip
->fmri
, restarter_specp
);
801 * If fopen fails, then ctid file hasn't been created yet.
802 * If print_flag is set, this is ok; otherwise fail.
804 if ((fp
= fopen(genfmri_filename
, "r")) == NULL
) {
812 * Print all contract ids that are found.
813 * First callback to print ctid header.
817 /* fscanf may not set errno, so be sure to clear it first */
819 while ((fscanf_ret
= fscanf(fp
, "%llu", cp
)) == 1) {
820 /* Callback to print contract id */
824 /* EOF is not a failure when no errno. */
825 if ((fscanf_ret
!= EOF
) || (errno
!= 0)) {
826 uu_die(gettext("Unable to read ctid file for %s"),
829 (void) putchar('\n');
832 /* Must find 1 ctid or fail */
833 if (fscanf(fp
, "%llu", cp
) == 1) {
834 /* If 2nd ctid found - fail */
835 if (fscanf(fp
, "%llu", &cp2
) == 1) {
836 if (flags
& MULTI_OK
)
839 /* Success - found only 1 ctid */
851 * Get the process ids associated with an instance using a restarter
855 * 0 if success: restarter specific action found and used with no error
856 * -1 restarter specific action not found or if failure
859 pids_by_restarter(scf_instance_t
*inst
, const char *fmri
,
860 pid_t
**pids
, uint_t
*np
, int *restarter_specp
)
867 /* Check for restarter specific action and generation of filename */
868 rest_ret
= common_by_restarter(inst
, fmri
, restarter_specp
);
873 * If fopen fails with ENOENT then the ctid file hasn't been
874 * created yet so return success.
875 * For all other errors - fail with uu_die.
877 if ((fp
= fopen(genfmri_filename
, "r")) == NULL
) {
880 uu_die(gettext("Unable to open ctid file for %s"), fmri
);
883 /* fscanf may not set errno, so be sure to clear it first */
885 while ((fscanf_ret
= fscanf(fp
, "%llu", &c
)) == 1) {
888 uu_die(gettext("ctid file for %s has corrupt data"),
891 ctid_to_pids(c
, pids
, np
);
894 /* EOF is not a failure when no errno. */
895 if ((fscanf_ret
!= EOF
) || (errno
!= 0)) {
896 uu_die(gettext("Unable to read ctid file for %s"), fmri
);
904 instance_processes(scf_instance_t
*inst
, const char *fmri
,
905 pid_t
**pids
, uint_t
*np
)
911 /* Use the restarter specific get pids routine, if available. */
912 ret
= pids_by_restarter(inst
, fmri
, pids
, np
, &restarter_spec
);
913 if (restarter_spec
== 1)
916 if ((iter
= scf_iter_create(h
)) == NULL
)
919 if (scf_instance_get_pg(inst
, SCF_PG_RESTARTER
, g_pg
) == 0) {
923 (void) propvals_to_pids(g_pg
, scf_property_contract
, pids
, np
,
924 g_prop
, g_val
, iter
);
926 (void) propvals_to_pids(g_pg
, SCF_PROPERTY_TRANSIENT_CONTRACT
,
927 pids
, np
, g_prop
, g_val
, iter
);
931 if (scf_error() != SCF_ERROR_NOT_FOUND
)
937 scf_iter_destroy(iter
);
943 get_psinfo(pid_t pid
, psinfo_t
*psip
)
948 (void) snprintf(path
, sizeof (path
), "/proc/%lu/psinfo", pid
);
950 fd
= open64(path
, O_RDONLY
);
954 if (read(fd
, psip
, sizeof (*psip
)) < 0)
955 uu_die(gettext("Could not read info for process %lu"), pid
);
965 * Column sprint and sortkey functions
973 * This function should write the value for the column into buf, and
974 * grow or allocate buf accordingly. It should always write at least
975 * width bytes, blanking unused bytes with spaces. If the field is
976 * greater than the column width we allow it to overlap other columns.
977 * In particular, it shouldn't write any null bytes. (Though an extra
978 * null byte past the end is currently tolerated.) If the property
979 * group is non-NULL, then we are dealing with a legacy service.
981 void (*sprint
)(char **, scf_walkinfo_t
*);
986 * This function should write sortkey_width bytes into buf which will
987 * cause memcmp() to sort it properly. (Unlike sprint() above,
988 * however, an extra null byte may overrun the buffer.) The second
989 * argument controls whether the results are sorted in forward or
992 void (*get_sortkey
)(char *, int, scf_walkinfo_t
*);
996 reverse_bytes(char *buf
, size_t len
)
1000 for (i
= 0; i
< len
; ++i
)
1005 #define CTID_COLUMN_WIDTH 6
1006 #define CTID_COLUMN_BUFSIZE 20 /* max ctid_t + space + \0 */
1009 sprint_ctid(char **buf
, scf_walkinfo_t
*wip
)
1013 size_t newsize
= (*buf
? strlen(*buf
) : 0) + CTID_COLUMN_BUFSIZE
;
1014 char *newbuf
= safe_malloc(newsize
);
1018 * Use the restarter specific get pids routine, if available.
1019 * Only check for non-legacy services (wip->pg == 0).
1021 if (wip
->pg
!= NULL
) {
1022 r
= pg_get_single_val(wip
->pg
, scf_property_contract
,
1023 SCF_TYPE_COUNT
, &c
, 0, EMPTY_OK
| MULTI_OK
);
1025 r
= ctids_by_restarter(wip
, &c
, 0, MULTI_OK
, &restarter_spec
,
1027 if (restarter_spec
== 0) {
1028 /* No restarter specific routine */
1029 r
= get_restarter_count_prop(wip
->inst
,
1030 scf_property_contract
, &c
, EMPTY_OK
| MULTI_OK
);
1035 (void) snprintf(newbuf
, newsize
, "%s%*lu ",
1036 *buf
? *buf
: "", CTID_COLUMN_WIDTH
, (ctid_t
)c
);
1037 else if (r
== E2BIG
)
1038 (void) snprintf(newbuf
, newsize
, "%s%*lu* ",
1039 *buf
? *buf
: "", CTID_COLUMN_WIDTH
- 1, (ctid_t
)c
);
1041 (void) snprintf(newbuf
, newsize
, "%s%*s ",
1042 *buf
? *buf
: "", CTID_COLUMN_WIDTH
, "-");
1048 #define CTID_SORTKEY_WIDTH (sizeof (uint64_t))
1051 sortkey_ctid(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1058 * Use the restarter specific get pids routine, if available.
1059 * Only check for non-legacy services (wip->pg == 0).
1061 if (wip
->pg
!= NULL
) {
1062 r
= pg_get_single_val(wip
->pg
, scf_property_contract
,
1063 SCF_TYPE_COUNT
, &c
, 0, EMPTY_OK
);
1065 r
= ctids_by_restarter(wip
, &c
, 0, MULTI_OK
, &restarter_spec
,
1067 if (restarter_spec
== 0) {
1068 /* No restarter specific routine */
1069 r
= get_restarter_count_prop(wip
->inst
,
1070 scf_property_contract
, &c
, EMPTY_OK
);
1076 * Use the id itself, but it must be big-endian for this to
1081 bcopy(&c
, buf
, CTID_SORTKEY_WIDTH
);
1083 bzero(buf
, CTID_SORTKEY_WIDTH
);
1087 reverse_bytes(buf
, CTID_SORTKEY_WIDTH
);
1091 #define DESC_COLUMN_WIDTH 100
1094 sprint_desc(char **buf
, scf_walkinfo_t
*wip
)
1100 if (common_name_buf
== NULL
)
1101 common_name_buf
= safe_malloc(max_scf_value_length
+ 1);
1103 bzero(common_name_buf
, max_scf_value_length
+ 1);
1105 if (wip
->pg
!= NULL
) {
1106 common_name_buf
[0] = '-';
1107 } else if (inst_get_single_val(wip
->inst
, SCF_PG_TM_COMMON_NAME
, locale
,
1108 SCF_TYPE_USTRING
, common_name_buf
, max_scf_value_length
, 0,
1110 inst_get_single_val(wip
->inst
, SCF_PG_TM_COMMON_NAME
, "C",
1111 SCF_TYPE_USTRING
, common_name_buf
, max_scf_value_length
, 0,
1113 common_name_buf
[0] = '-';
1117 * Collapse multi-line tm_common_name values into a single line.
1119 for (x
= common_name_buf
; *x
!= '\0'; x
++)
1123 if (strlen(common_name_buf
) > DESC_COLUMN_WIDTH
)
1124 newsize
= (*buf
? strlen(*buf
) : 0) +
1125 strlen(common_name_buf
) + 1;
1127 newsize
= (*buf
? strlen(*buf
) : 0) + DESC_COLUMN_WIDTH
+ 1;
1128 newbuf
= safe_malloc(newsize
);
1129 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1130 DESC_COLUMN_WIDTH
, common_name_buf
);
1138 sortkey_desc(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1140 bzero(buf
, DESC_COLUMN_WIDTH
);
1143 /* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */
1146 state_to_char(const char *state
)
1148 if (strcmp(state
, SCF_STATE_STRING_UNINIT
) == 0)
1151 if (strcmp(state
, SCF_STATE_STRING_OFFLINE
) == 0)
1154 if (strcmp(state
, SCF_STATE_STRING_ONLINE
) == 0)
1157 if (strcmp(state
, SCF_STATE_STRING_MAINT
) == 0)
1160 if (strcmp(state
, SCF_STATE_STRING_DISABLED
) == 0)
1163 if (strcmp(state
, SCF_STATE_STRING_DEGRADED
) == 0)
1166 if (strcmp(state
, SCF_STATE_STRING_LEGACY
) == 0)
1172 /* Return true if inst is transitioning. */
1174 transitioning(scf_instance_t
*inst
)
1176 char nstate_name
[MAX_SCF_STATE_STRING_SZ
];
1178 get_restarter_string_prop(inst
, scf_property_next_state
, nstate_name
,
1179 sizeof (nstate_name
));
1181 return (state_to_char(nstate_name
) != '?');
1186 sortkey_states(const char *pname
, char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1188 char state_name
[MAX_SCF_STATE_STRING_SZ
];
1191 * Lower numbers are printed first, so these are arranged from least
1192 * interesting ("legacy run") to most interesting (unknown).
1194 if (wip
->pg
== NULL
) {
1195 get_restarter_string_prop(wip
->inst
, pname
, state_name
,
1196 sizeof (state_name
));
1198 if (strcmp(state_name
, SCF_STATE_STRING_ONLINE
) == 0)
1200 else if (strcmp(state_name
, SCF_STATE_STRING_DEGRADED
) == 0)
1202 else if (strcmp(state_name
, SCF_STATE_STRING_OFFLINE
) == 0)
1204 else if (strcmp(state_name
, SCF_STATE_STRING_MAINT
) == 0)
1206 else if (strcmp(state_name
, SCF_STATE_STRING_DISABLED
) == 0)
1208 else if (strcmp(state_name
, SCF_STATE_STRING_UNINIT
) == 0)
1220 sprint_state(char **buf
, scf_walkinfo_t
*wip
)
1222 char state_name
[MAX_SCF_STATE_STRING_SZ
+ 1];
1226 if (wip
->pg
== NULL
) {
1227 get_restarter_string_prop(wip
->inst
, scf_property_state
,
1228 state_name
, sizeof (state_name
));
1230 /* Don't print blank fields, to ease parsing. */
1231 if (state_name
[0] == '\0') {
1232 state_name
[0] = '-';
1233 state_name
[1] = '\0';
1236 if (!opt_nstate_shown
&& transitioning(wip
->inst
)) {
1237 /* Append an asterisk if nstate is valid. */
1238 (void) strcat(state_name
, "*");
1241 (void) strcpy(state_name
, SCF_STATE_STRING_LEGACY
);
1243 newsize
= (*buf
? strlen(*buf
) : 0) + MAX_SCF_STATE_STRING_SZ
+ 2;
1244 newbuf
= safe_malloc(newsize
);
1245 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1246 MAX_SCF_STATE_STRING_SZ
+ 1, state_name
);
1254 sortkey_state(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1256 sortkey_states(scf_property_state
, buf
, reverse
, wip
);
1260 sprint_nstate(char **buf
, scf_walkinfo_t
*wip
)
1262 char next_state_name
[MAX_SCF_STATE_STRING_SZ
];
1263 boolean_t blank
= 0;
1267 if (wip
->pg
== NULL
) {
1268 get_restarter_string_prop(wip
->inst
, scf_property_next_state
,
1269 next_state_name
, sizeof (next_state_name
));
1271 /* Don't print blank fields, to ease parsing. */
1272 if (next_state_name
[0] == '\0' ||
1273 strcmp(next_state_name
, SCF_STATE_STRING_NONE
) == 0)
1279 next_state_name
[0] = '-';
1280 next_state_name
[1] = '\0';
1283 newsize
= (*buf
? strlen(*buf
) : 0) + MAX_SCF_STATE_STRING_SZ
+ 1;
1284 newbuf
= safe_malloc(newsize
);
1285 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1286 MAX_SCF_STATE_STRING_SZ
- 1, next_state_name
);
1293 sortkey_nstate(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1295 sortkey_states(scf_property_next_state
, buf
, reverse
, wip
);
1299 sprint_s(char **buf
, scf_walkinfo_t
*wip
)
1302 char state_name
[MAX_SCF_STATE_STRING_SZ
];
1303 size_t newsize
= (*buf
? strlen(*buf
) : 0) + 4;
1304 char *newbuf
= safe_malloc(newsize
);
1306 if (wip
->pg
== NULL
) {
1307 get_restarter_string_prop(wip
->inst
, scf_property_state
,
1308 state_name
, sizeof (state_name
));
1309 tmp
[0] = state_to_char(state_name
);
1311 if (!opt_nstate_shown
&& transitioning(wip
->inst
))
1320 (void) snprintf(newbuf
, newsize
, "%s%-*s", *buf
? *buf
: "",
1328 sprint_n(char **buf
, scf_walkinfo_t
*wip
)
1331 size_t newsize
= (*buf
? strlen(*buf
) : 0) + 3;
1332 char *newbuf
= safe_malloc(newsize
);
1333 char nstate_name
[MAX_SCF_STATE_STRING_SZ
];
1335 if (wip
->pg
== NULL
) {
1336 get_restarter_string_prop(wip
->inst
, scf_property_next_state
,
1337 nstate_name
, sizeof (nstate_name
));
1339 if (strcmp(nstate_name
, SCF_STATE_STRING_NONE
) == 0)
1342 tmp
[0] = state_to_char(nstate_name
);
1346 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1354 sprint_sn(char **buf
, scf_walkinfo_t
*wip
)
1357 size_t newsize
= (*buf
? strlen(*buf
) : 0) + 4;
1358 char *newbuf
= safe_malloc(newsize
);
1359 char nstate_name
[MAX_SCF_STATE_STRING_SZ
];
1360 char state_name
[MAX_SCF_STATE_STRING_SZ
];
1362 if (wip
->pg
== NULL
) {
1363 get_restarter_string_prop(wip
->inst
, scf_property_state
,
1364 state_name
, sizeof (state_name
));
1365 get_restarter_string_prop(wip
->inst
, scf_property_next_state
,
1366 nstate_name
, sizeof (nstate_name
));
1367 tmp
[0] = state_to_char(state_name
);
1369 if (strcmp(nstate_name
, SCF_STATE_STRING_NONE
) == 0)
1372 tmp
[1] = state_to_char(nstate_name
);
1379 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1388 sortkey_sn(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1390 sortkey_state(buf
, reverse
, wip
);
1391 sortkey_nstate(buf
+ 1, reverse
, wip
);
1395 state_abbrev(const char *state
)
1397 if (strcmp(state
, SCF_STATE_STRING_UNINIT
) == 0)
1399 if (strcmp(state
, SCF_STATE_STRING_OFFLINE
) == 0)
1401 if (strcmp(state
, SCF_STATE_STRING_ONLINE
) == 0)
1403 if (strcmp(state
, SCF_STATE_STRING_MAINT
) == 0)
1405 if (strcmp(state
, SCF_STATE_STRING_DISABLED
) == 0)
1407 if (strcmp(state
, SCF_STATE_STRING_DEGRADED
) == 0)
1409 if (strcmp(state
, SCF_STATE_STRING_LEGACY
) == 0)
1416 sprint_sta(char **buf
, scf_walkinfo_t
*wip
)
1418 char state_name
[MAX_SCF_STATE_STRING_SZ
];
1420 size_t newsize
= (*buf
? strlen(*buf
) : 0) + 6;
1421 char *newbuf
= safe_malloc(newsize
);
1423 if (wip
->pg
== NULL
)
1424 get_restarter_string_prop(wip
->inst
, scf_property_state
,
1425 state_name
, sizeof (state_name
));
1427 (void) strcpy(state_name
, SCF_STATE_STRING_LEGACY
);
1429 (void) strcpy(sta
, state_abbrev(state_name
));
1431 if (wip
->pg
== NULL
&& !opt_nstate_shown
&& transitioning(wip
->inst
))
1432 (void) strcat(sta
, "*");
1434 (void) snprintf(newbuf
, newsize
, "%s%-4s ", *buf
? *buf
: "", sta
);
1441 sprint_nsta(char **buf
, scf_walkinfo_t
*wip
)
1443 char state_name
[MAX_SCF_STATE_STRING_SZ
];
1444 size_t newsize
= (*buf
? strlen(*buf
) : 0) + 6;
1445 char *newbuf
= safe_malloc(newsize
);
1447 if (wip
->pg
== NULL
)
1448 get_restarter_string_prop(wip
->inst
, scf_property_next_state
,
1449 state_name
, sizeof (state_name
));
1451 (void) strcpy(state_name
, SCF_STATE_STRING_NONE
);
1453 if (strcmp(state_name
, SCF_STATE_STRING_NONE
) == 0)
1454 (void) snprintf(newbuf
, newsize
, "%s%-4s ", *buf
? *buf
: "",
1457 (void) snprintf(newbuf
, newsize
, "%s%-4s ", *buf
? *buf
: "",
1458 state_abbrev(state_name
));
1465 #define FMRI_COLUMN_WIDTH 50
1467 sprint_fmri(char **buf
, scf_walkinfo_t
*wip
)
1469 char *fmri_buf
= safe_malloc(max_scf_fmri_length
+ 1);
1473 if (wip
->pg
== NULL
) {
1474 if (scf_instance_to_fmri(wip
->inst
, fmri_buf
,
1475 max_scf_fmri_length
+ 1) == -1)
1478 (void) strcpy(fmri_buf
, SCF_FMRI_LEGACY_PREFIX
);
1479 if (pg_get_single_val(wip
->pg
, SCF_LEGACY_PROPERTY_NAME
,
1480 SCF_TYPE_ASTRING
, fmri_buf
+
1481 sizeof (SCF_FMRI_LEGACY_PREFIX
) - 1,
1482 max_scf_fmri_length
+ 1 -
1483 (sizeof (SCF_FMRI_LEGACY_PREFIX
) - 1), 0) != 0)
1484 (void) strcat(fmri_buf
, LEGACY_UNKNOWN
);
1487 if (strlen(fmri_buf
) > FMRI_COLUMN_WIDTH
)
1488 newsize
= (*buf
? strlen(*buf
) : 0) + strlen(fmri_buf
) + 2;
1490 newsize
= (*buf
? strlen(*buf
) : 0) + FMRI_COLUMN_WIDTH
+ 2;
1491 newbuf
= safe_malloc(newsize
);
1492 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1493 FMRI_COLUMN_WIDTH
, fmri_buf
);
1501 sortkey_fmri(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1505 sprint_fmri(&tmp
, wip
);
1506 bcopy(tmp
, buf
, FMRI_COLUMN_WIDTH
);
1509 reverse_bytes(buf
, FMRI_COLUMN_WIDTH
);
1512 /* Component columns */
1513 #define COMPONENT_COLUMN_WIDTH 20
1515 sprint_scope(char **buf
, scf_walkinfo_t
*wip
)
1517 char *scope_buf
= safe_malloc(max_scf_name_length
+ 1);
1518 size_t newsize
= (*buf
? strlen(*buf
) : 0) + COMPONENT_COLUMN_WIDTH
+ 2;
1519 char *newbuf
= safe_malloc(newsize
);
1521 assert(wip
->scope
!= NULL
);
1523 if (scf_scope_get_name(wip
->scope
, scope_buf
, max_scf_name_length
) < 0)
1526 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1527 COMPONENT_COLUMN_WIDTH
, scope_buf
);
1535 sortkey_scope(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1539 sprint_scope(&tmp
, wip
);
1540 bcopy(tmp
, buf
, COMPONENT_COLUMN_WIDTH
);
1543 reverse_bytes(buf
, COMPONENT_COLUMN_WIDTH
);
1547 sprint_service(char **buf
, scf_walkinfo_t
*wip
)
1549 char *svc_buf
= safe_malloc(max_scf_name_length
+ 1);
1553 if (wip
->pg
== NULL
) {
1554 if (scf_service_get_name(wip
->svc
, svc_buf
,
1555 max_scf_name_length
+ 1) < 0)
1558 if (pg_get_single_val(wip
->pg
, "name", SCF_TYPE_ASTRING
,
1559 svc_buf
, max_scf_name_length
+ 1, EMPTY_OK
) != 0)
1560 (void) strcpy(svc_buf
, LEGACY_UNKNOWN
);
1564 if (strlen(svc_buf
) > COMPONENT_COLUMN_WIDTH
)
1565 newsize
= (*buf
? strlen(*buf
) : 0) + strlen(svc_buf
) + 2;
1567 newsize
= (*buf
? strlen(*buf
) : 0) +
1568 COMPONENT_COLUMN_WIDTH
+ 2;
1569 newbuf
= safe_malloc(newsize
);
1570 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1571 COMPONENT_COLUMN_WIDTH
, svc_buf
);
1579 sortkey_service(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1583 sprint_service(&tmp
, wip
);
1584 bcopy(tmp
, buf
, COMPONENT_COLUMN_WIDTH
);
1587 reverse_bytes(buf
, COMPONENT_COLUMN_WIDTH
);
1592 sprint_instance(char **buf
, scf_walkinfo_t
*wip
)
1594 char *tmp
= safe_malloc(max_scf_name_length
+ 1);
1595 size_t newsize
= (*buf
? strlen(*buf
) : 0) + COMPONENT_COLUMN_WIDTH
+ 2;
1596 char *newbuf
= safe_malloc(newsize
);
1598 if (wip
->pg
== NULL
) {
1599 if (scf_instance_get_name(wip
->inst
, tmp
,
1600 max_scf_name_length
+ 1) < 0)
1607 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1608 COMPONENT_COLUMN_WIDTH
, tmp
);
1616 sortkey_instance(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1620 sprint_instance(&tmp
, wip
);
1621 bcopy(tmp
, buf
, COMPONENT_COLUMN_WIDTH
);
1624 reverse_bytes(buf
, COMPONENT_COLUMN_WIDTH
);
1628 #define STIME_COLUMN_WIDTH 8
1629 #define FORMAT_TIME "%k:%M:%S"
1630 #define FORMAT_DATE "%b_%d "
1631 #define FORMAT_YEAR "%Y "
1634 * sprint_stime() will allocate a new buffer and snprintf the services's
1635 * state timestamp. If the timestamp is unavailable for some reason
1636 * a '-' is given instead.
1639 sprint_stime(char **buf
, scf_walkinfo_t
*wip
)
1645 char st_buf
[STIME_COLUMN_WIDTH
+ 1];
1646 size_t newsize
= (*buf
? strlen(*buf
) : 0) + STIME_COLUMN_WIDTH
+ 2;
1647 char *newbuf
= safe_malloc(newsize
);
1649 if (wip
->pg
== NULL
) {
1650 r
= get_restarter_time_prop(wip
->inst
,
1651 SCF_PROPERTY_STATE_TIMESTAMP
, &tv
, 0);
1653 r
= pg_get_single_val(wip
->pg
, SCF_PROPERTY_STATE_TIMESTAMP
,
1654 SCF_TYPE_TIME
, &tv
, NULL
, 0);
1659 * There's something amiss with our service
1660 * so we'll print a '-' for STIME.
1662 (void) snprintf(newbuf
, newsize
, "%s%-*s", *buf
? *buf
: "",
1663 STIME_COLUMN_WIDTH
+ 1, "-");
1665 /* tv should be valid so we'll format it */
1666 then
= (time_t)tv
.tv_sec
;
1668 tm
= localtime(&then
);
1670 * Print time if started within the past 24 hours, print date
1671 * if within the past 12 months or, finally, print year if
1672 * started greater than 12 months ago.
1674 if (now
- then
< 24 * 60 * 60) {
1675 (void) strftime(st_buf
, sizeof (st_buf
),
1676 gettext(FORMAT_TIME
), tm
);
1677 } else if (now
- then
< 12 * 30 * 24 * 60 * 60) {
1678 (void) strftime(st_buf
, sizeof (st_buf
),
1679 gettext(FORMAT_DATE
), tm
);
1681 (void) strftime(st_buf
, sizeof (st_buf
),
1682 gettext(FORMAT_YEAR
), tm
);
1684 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1685 STIME_COLUMN_WIDTH
+ 1, st_buf
);
1692 #define STIME_SORTKEY_WIDTH (sizeof (uint64_t) + sizeof (uint32_t))
1696 sortkey_stime(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1701 if (wip
->pg
== NULL
)
1702 r
= get_restarter_time_prop(wip
->inst
,
1703 SCF_PROPERTY_STATE_TIMESTAMP
, &tv
, 0);
1705 r
= pg_get_single_val(wip
->pg
, SCF_PROPERTY_STATE_TIMESTAMP
,
1706 SCF_TYPE_TIME
, &tv
, NULL
, 0);
1712 /* Stick it straight into the buffer. */
1718 bcopy(&sec
, buf
, sizeof (sec
));
1719 bcopy(&us
, buf
+ sizeof (sec
), sizeof (us
));
1721 bzero(buf
, STIME_SORTKEY_WIDTH
);
1725 reverse_bytes(buf
, STIME_SORTKEY_WIDTH
);
1729 #define ZONE_COLUMN_WIDTH 16
1732 sprint_zone(char **buf
, scf_walkinfo_t
*wip
)
1735 char *newbuf
, *zonename
= g_zonename
, b
[ZONENAME_MAX
];
1737 if (zonename
== NULL
) {
1738 zoneid_t zoneid
= getzoneid();
1740 if (getzonenamebyid(zoneid
, b
, sizeof (b
)) < 0)
1741 uu_die(gettext("could not determine zone name"));
1746 if (strlen(zonename
) > ZONE_COLUMN_WIDTH
)
1747 newsize
= (*buf
? strlen(*buf
) : 0) + strlen(zonename
) + 2;
1749 newsize
= (*buf
? strlen(*buf
) : 0) + ZONE_COLUMN_WIDTH
+ 2;
1751 newbuf
= safe_malloc(newsize
);
1752 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1753 ZONE_COLUMN_WIDTH
, zonename
);
1761 sortkey_zone(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1765 sprint_zone(&tmp
, wip
);
1766 bcopy(tmp
, buf
, ZONE_COLUMN_WIDTH
);
1769 reverse_bytes(buf
, ZONE_COLUMN_WIDTH
);
1773 * Information about columns which can be displayed. If you add something,
1774 * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below.
1776 static const struct column columns
[] = {
1777 { "CTID", CTID_COLUMN_WIDTH
, sprint_ctid
,
1778 CTID_SORTKEY_WIDTH
, sortkey_ctid
},
1779 { "DESC", DESC_COLUMN_WIDTH
, sprint_desc
,
1780 DESC_COLUMN_WIDTH
, sortkey_desc
},
1781 { "FMRI", FMRI_COLUMN_WIDTH
, sprint_fmri
,
1782 FMRI_COLUMN_WIDTH
, sortkey_fmri
},
1783 { "INST", COMPONENT_COLUMN_WIDTH
, sprint_instance
,
1784 COMPONENT_COLUMN_WIDTH
, sortkey_instance
},
1785 { "N", 1, sprint_n
, 1, sortkey_nstate
},
1786 { "NSTA", 4, sprint_nsta
, 1, sortkey_nstate
},
1787 { "NSTATE", MAX_SCF_STATE_STRING_SZ
- 1, sprint_nstate
,
1788 1, sortkey_nstate
},
1789 { "S", 2, sprint_s
, 1, sortkey_state
},
1790 { "SCOPE", COMPONENT_COLUMN_WIDTH
, sprint_scope
,
1791 COMPONENT_COLUMN_WIDTH
, sortkey_scope
},
1792 { "SN", 2, sprint_sn
, 2, sortkey_sn
},
1793 { "SVC", COMPONENT_COLUMN_WIDTH
, sprint_service
,
1794 COMPONENT_COLUMN_WIDTH
, sortkey_service
},
1795 { "STA", 4, sprint_sta
, 1, sortkey_state
},
1796 { "STATE", MAX_SCF_STATE_STRING_SZ
- 1 + 1, sprint_state
,
1798 { "STIME", STIME_COLUMN_WIDTH
, sprint_stime
,
1799 STIME_SORTKEY_WIDTH
, sortkey_stime
},
1800 { "ZONE", ZONE_COLUMN_WIDTH
, sprint_zone
,
1801 ZONE_COLUMN_WIDTH
, sortkey_zone
},
1804 #define MAX_COLUMN_NAME_LENGTH_STR "6"
1806 static const int ncolumns
= sizeof (columns
) / sizeof (columns
[0]);
1809 * Necessary thanks to gettext() & xgettext.
1812 description_of_column(int c
)
1814 const char *s
= NULL
;
1818 s
= gettext("contract ID for service (see contract(4))");
1821 s
= gettext("human-readable description of the service");
1824 s
= gettext("Fault Managed Resource Identifier for service");
1827 s
= gettext("portion of the FMRI indicating service instance");
1830 s
= gettext("abbreviation for next state (if in transition)");
1833 s
= gettext("abbreviation for next state (if in transition)");
1836 s
= gettext("name for next state (if in transition)");
1839 s
= gettext("abbreviation for current state");
1842 s
= gettext("name for scope associated with service");
1845 s
= gettext("abbreviation for current state and next state");
1848 s
= gettext("portion of the FMRI representing service name");
1851 s
= gettext("abbreviation for current state");
1854 s
= gettext("name for current state");
1857 s
= gettext("time of last state change");
1860 s
= gettext("name of zone");
1870 print_usage(const char *progname
, FILE *f
, boolean_t do_exit
)
1872 (void) fprintf(f
, gettext(
1873 "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] "
1874 "[-sS col] [-Z | -z zone ]\n [<service> ...]\n"
1875 " %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] "
1876 "[-Z | -z zone ]\n [<service> ...]\n"
1877 " %1$s [-l | -L] [-Z | -z zone] <service> ...\n"
1878 " %1$s -x [-v] [-Z | -z zone] [<service> ...]\n"
1879 " %1$s -?\n"), progname
);
1882 exit(UU_EXIT_USAGE
);
1885 #define argserr(progname) print_usage(progname, stderr, B_TRUE)
1888 print_help(const char *progname
)
1892 print_usage(progname
, stdout
, B_FALSE
);
1894 (void) printf(gettext("\n"
1895 "\t-a list all service instances rather than "
1896 "only those that are enabled\n"
1897 "\t-d list dependencies of the specified service(s)\n"
1898 "\t-D list dependents of the specified service(s)\n"
1899 "\t-H omit header line from output\n"
1900 "\t-l list detailed information about the specified service(s)\n"
1901 "\t-L list the log file associated with the specified service(s)\n"
1902 "\t-o list only the specified columns in the output\n"
1903 "\t-p list process IDs and names associated with each service\n"
1904 "\t-R list only those services with the specified restarter\n"
1905 "\t-s sort output in ascending order by the specified column(s)\n"
1906 "\t-S sort output in descending order by the specified column(s)\n"
1907 "\t-v list verbose information appropriate to the type of output\n"
1908 "\t-x explain the status of services that might require maintenance,\n"
1909 "\t or explain the status of the specified service(s)\n"
1910 "\t-z from global zone, show services in a specified zone\n"
1911 "\t-Z from global zone, show services in all zones\n"
1913 "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
1914 "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
1916 "\t%1$s [opts] svc:/network/smtp:sendmail\n"
1917 "\t%1$s [opts] network/smtp:sendmail\n"
1918 "\t%1$s [opts] network/*mail\n"
1919 "\t%1$s [opts] network/smtp\n"
1920 "\t%1$s [opts] smtp:sendmail\n"
1921 "\t%1$s [opts] smtp\n"
1922 "\t%1$s [opts] sendmail\n"
1924 "Columns for output or sorting can be specified using these names:\n"
1927 for (i
= 0; i
< ncolumns
; i
++) {
1928 (void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR
"s %s\n",
1929 columns
[i
].name
, description_of_column(i
));
1935 * A getsubopt()-like function which returns an index into the columns table.
1936 * On success, *optionp is set to point to the next sub-option, or the
1937 * terminating null if there are none.
1940 getcolumnopt(char **optionp
)
1942 char *str
= *optionp
, *cp
;
1945 assert(optionp
!= NULL
);
1946 assert(*optionp
!= NULL
);
1948 cp
= strchr(*optionp
, ',');
1952 for (i
= 0; i
< ncolumns
; ++i
) {
1953 if (strcasecmp(str
, columns
[i
].name
) == 0) {
1957 *optionp
= strchr(*optionp
, '\0');
1970 char *line_buf
, *cp
;
1972 line_buf
= safe_malloc(line_sz
);
1974 for (i
= 0; i
< opt_cnum
; ++i
) {
1975 const struct column
* const colp
= &columns
[opt_columns
[i
]];
1977 (void) snprintf(cp
, colp
->width
+ 1, "%-*s", colp
->width
,
1983 /* Trim the trailing whitespace */
1988 (void) puts(line_buf
);
1996 * Long listing (-l) functions.
2000 pidcmp(const void *l
, const void *r
)
2002 pid_t lp
= *(pid_t
*)l
, rp
= *(pid_t
*)r
;
2012 * This is the strlen() of the longest label ("description"), plus intercolumn
2015 #define DETAILED_WIDTH (11 + 2)
2018 * Callback routine to print header for contract id.
2019 * Called by ctids_by_restarter and print_detailed.
2024 (void) printf("%-*s", DETAILED_WIDTH
, "contract_id");
2028 * Callback routine to print a contract id.
2029 * Called by ctids_by_restarter and print_detailed.
2032 print_ctid_detailed(uint64_t c
)
2034 (void) printf("%lu ", (ctid_t
)c
);
2038 detailed_list_processes(scf_walkinfo_t
*wip
)
2045 if (get_restarter_count_prop(wip
->inst
, scf_property_contract
, &c
,
2049 if (instance_processes(wip
->inst
, wip
->fmri
, &pids
, &n
) != 0)
2052 qsort(pids
, n
, sizeof (*pids
), pidcmp
);
2054 for (i
= 0; i
< n
; ++i
) {
2055 (void) printf("%-*s%lu", DETAILED_WIDTH
, gettext("process"),
2058 if (get_psinfo(pids
[i
], &psi
) == 0)
2059 (void) printf(" %.*s", PRARGSZ
, psi
.pr_psargs
);
2061 (void) putchar('\n');
2068 * Determines the state of a dependency. If the FMRI specifies a file, then we
2069 * fake up a state based on whether we can access the file.
2072 get_fmri_state(char *fmri
, char *state
, size_t state_sz
)
2075 const char *svc_name
, *inst_name
, *pg_name
, *path
;
2077 scf_instance_t
*inst
;
2080 lfmri
= safe_strdup(fmri
);
2083 * Check for file:// dependencies
2085 if (scf_parse_file_fmri(lfmri
, NULL
, &path
) == SCF_SUCCESS
) {
2086 struct stat64 statbuf
;
2089 if (stat64(path
, &statbuf
) == 0)
2091 else if (errno
== ENOENT
)
2096 (void) strlcpy(state
, msg
, state_sz
);
2101 * scf_parse_file_fmri() may have overwritten part of the string, so
2104 (void) strcpy(lfmri
, fmri
);
2106 if (scf_parse_svc_fmri(lfmri
, NULL
, &svc_name
, &inst_name
,
2107 &pg_name
, NULL
) != SCF_SUCCESS
) {
2109 (void) strlcpy(state
, "invalid", state_sz
);
2115 if (svc_name
== NULL
|| pg_name
!= NULL
) {
2116 (void) strlcpy(state
, "invalid", state_sz
);
2120 if (inst_name
!= NULL
) {
2121 /* instance: get state */
2122 inst
= scf_instance_create(h
);
2126 if (scf_handle_decode_fmri(h
, fmri
, NULL
, NULL
, inst
, NULL
,
2127 NULL
, SCF_DECODE_FMRI_EXACT
) == SCF_SUCCESS
)
2128 get_restarter_string_prop(inst
, scf_property_state
,
2131 switch (scf_error()) {
2132 case SCF_ERROR_INVALID_ARGUMENT
:
2133 (void) strlcpy(state
, "invalid", state_sz
);
2135 case SCF_ERROR_NOT_FOUND
:
2136 (void) strlcpy(state
, "absent", state_sz
);
2144 scf_instance_destroy(inst
);
2149 * service: If only one instance, use that state. Otherwise, say
2152 if ((svc
= scf_service_create(h
)) == NULL
||
2153 (inst
= scf_instance_create(h
)) == NULL
||
2154 (iter
= scf_iter_create(h
)) == NULL
)
2157 if (scf_handle_decode_fmri(h
, fmri
, NULL
, svc
, NULL
, NULL
, NULL
,
2158 SCF_DECODE_FMRI_EXACT
) != SCF_SUCCESS
) {
2159 switch (scf_error()) {
2160 case SCF_ERROR_INVALID_ARGUMENT
:
2161 (void) strlcpy(state
, "invalid", state_sz
);
2163 case SCF_ERROR_NOT_FOUND
:
2164 (void) strlcpy(state
, "absent", state_sz
);
2172 if (scf_iter_service_instances(iter
, svc
) != SCF_SUCCESS
)
2175 switch (scf_iter_next_instance(iter
, inst
)) {
2177 (void) strlcpy(state
, "absent", state_sz
);
2187 /* Get the state in case this is the only instance. */
2188 get_restarter_string_prop(inst
, scf_property_state
, state
, state_sz
);
2190 switch (scf_iter_next_instance(iter
, inst
)) {
2195 /* Nope, multiple instances. */
2196 (void) strlcpy(state
, "multiple", state_sz
);
2204 scf_iter_destroy(iter
);
2205 scf_instance_destroy(inst
);
2206 scf_service_destroy(svc
);
2210 print_application_properties(scf_walkinfo_t
*wip
, scf_snapshot_t
*snap
)
2212 scf_iter_t
*pg_iter
, *prop_iter
, *val_iter
;
2213 scf_propertygroup_t
*pg
;
2214 scf_property_t
*prop
;
2217 scf_prop_tmpl_t
*prt
;
2218 char *pg_name_buf
= safe_malloc(max_scf_name_length
+ 1);
2219 char *prop_name_buf
= safe_malloc(max_scf_name_length
+ 1);
2220 char *snap_name
= safe_malloc(max_scf_name_length
+ 1);
2221 char *val_buf
= safe_malloc(max_scf_value_length
+ 1);
2227 if ((pg_iter
= scf_iter_create(h
)) == NULL
||
2228 (prop_iter
= scf_iter_create(h
)) == NULL
||
2229 (val_iter
= scf_iter_create(h
)) == NULL
||
2230 (val
= scf_value_create(h
)) == NULL
||
2231 (prop
= scf_property_create(h
)) == NULL
||
2232 (pt
= scf_tmpl_pg_create(h
)) == NULL
||
2233 (prt
= scf_tmpl_prop_create(h
)) == NULL
||
2234 (pg
= scf_pg_create(h
)) == NULL
)
2237 if (scf_iter_instance_pgs_typed_composed(pg_iter
, wip
->inst
, snap
,
2238 SCF_PG_APP_DEFAULT
) == -1)
2242 * Format for output:
2245 * pg/prop (proptype) = <value> <value>
2248 while ((i
= scf_iter_next_pg(pg_iter
, pg
)) == 1) {
2251 if (scf_pg_get_name(pg
, pg_name_buf
, max_scf_name_length
) < 0)
2253 if (scf_snapshot_get_name(snap
, snap_name
,
2254 max_scf_name_length
) < 0)
2257 if (scf_tmpl_get_by_pg_name(wip
->fmri
, snap_name
, pg_name_buf
,
2258 SCF_PG_APP_DEFAULT
, pt
, 0) == 0)
2263 (void) printf("%s (%s)\n", pg_name_buf
, SCF_PG_APP_DEFAULT
);
2265 if (tmpl
== 1 && scf_tmpl_pg_description(pt
, NULL
, &desc
) > 0) {
2266 (void) printf(" %s\n", desc
);
2270 if (scf_iter_pg_properties(prop_iter
, pg
) == -1)
2272 while ((j
= scf_iter_next_property(prop_iter
, prop
)) == 1) {
2273 if (scf_property_get_name(prop
, prop_name_buf
,
2274 max_scf_name_length
) < 0)
2276 if (scf_property_type(prop
, &type
) == -1)
2280 (scf_tmpl_get_by_prop(pt
, prop_name_buf
, prt
,
2285 scf_tmpl_prop_visibility(prt
, &vis
) != -1 &&
2286 vis
== SCF_TMPL_VISIBILITY_HIDDEN
)
2289 (void) printf("%s/%s (%s) = ", pg_name_buf
,
2290 prop_name_buf
, scf_type_to_string(type
));
2292 if (scf_iter_property_values(val_iter
, prop
) == -1)
2295 while ((k
= scf_iter_next_value(val_iter
, val
)) == 1) {
2296 if (scf_value_get_as_string(val
, val_buf
,
2297 max_scf_value_length
+ 1) < 0)
2299 if (strpbrk(val_buf
, " \t\n\"()") != NULL
) {
2300 (void) printf("\"");
2301 for (cp
= val_buf
; *cp
!= '\0'; ++cp
) {
2302 if (*cp
== '"' || *cp
== '\\')
2306 (void) putc(*cp
, stdout
);
2308 (void) printf("\"");
2310 (void) printf("%s ", val_buf
);
2314 (void) printf("\n");
2319 if (tmpl
== 1 && scf_tmpl_prop_description(prt
, NULL
,
2321 (void) printf(" %s\n", desc
);
2332 scf_iter_destroy(pg_iter
);
2333 scf_iter_destroy(prop_iter
);
2334 scf_iter_destroy(val_iter
);
2335 scf_value_destroy(val
);
2336 scf_property_destroy(prop
);
2337 scf_tmpl_pg_destroy(pt
);
2338 scf_tmpl_prop_destroy(prt
);
2341 free(prop_name_buf
);
2347 print_detailed_dependency(scf_propertygroup_t
*pg
)
2349 scf_property_t
*eprop
;
2355 if ((eprop
= scf_property_create(h
)) == NULL
||
2356 (iter
= scf_iter_create(h
)) == NULL
)
2359 val_buf
= safe_malloc(max_scf_value_length
+ 1);
2361 if (scf_pg_get_property(pg
, SCF_PROPERTY_ENTITIES
, eprop
) !=
2363 scf_property_type(eprop
, &ty
) != SCF_SUCCESS
||
2364 ty
!= SCF_TYPE_FMRI
)
2367 (void) printf("%-*s", DETAILED_WIDTH
, gettext("dependency"));
2369 /* Print the grouping */
2370 if (pg_get_single_val(pg
, SCF_PROPERTY_GROUPING
, SCF_TYPE_ASTRING
,
2371 val_buf
, max_scf_value_length
+ 1, 0) == 0)
2372 (void) fputs(val_buf
, stdout
);
2374 (void) putchar('?');
2376 (void) putchar('/');
2378 if (pg_get_single_val(pg
, SCF_PROPERTY_RESTART_ON
, SCF_TYPE_ASTRING
,
2379 val_buf
, max_scf_value_length
+ 1, 0) == 0)
2380 (void) fputs(val_buf
, stdout
);
2382 (void) putchar('?');
2384 /* Print the dependency entities. */
2385 if (scf_iter_property_values(iter
, eprop
) == -1)
2388 while ((i
= scf_iter_next_value(iter
, g_val
)) == 1) {
2389 char state
[MAX_SCF_STATE_STRING_SZ
];
2391 if (scf_value_get_astring(g_val
, val_buf
,
2392 max_scf_value_length
+ 1) < 0)
2395 (void) putchar(' ');
2396 (void) fputs(val_buf
, stdout
);
2398 /* Print the state. */
2402 get_fmri_state(val_buf
, state
, sizeof (state
));
2404 (void) printf(" (%s)", state
);
2409 (void) putchar('\n');
2412 scf_iter_destroy(iter
);
2413 scf_property_destroy(eprop
);
2418 print_detailed(void *unused
, scf_walkinfo_t
*wip
)
2420 scf_snapshot_t
*snap
;
2421 scf_propertygroup_t
*rpg
;
2422 scf_iter_t
*pg_iter
;
2436 const char * const fmt
= "%-*s%s\n";
2438 assert(wip
->pg
== NULL
);
2440 rpg
= scf_pg_create(h
);
2444 if (first_paragraph
)
2445 first_paragraph
= 0;
2447 (void) putchar('\n');
2449 buf
= safe_malloc(max_scf_fmri_length
+ 1);
2451 if (scf_instance_to_fmri(wip
->inst
, buf
, max_scf_fmri_length
+ 1) != -1)
2452 (void) printf(fmt
, DETAILED_WIDTH
, "fmri", buf
);
2454 if (common_name_buf
== NULL
)
2455 common_name_buf
= safe_malloc(max_scf_value_length
+ 1);
2457 if (inst_get_single_val(wip
->inst
, SCF_PG_TM_COMMON_NAME
, locale
,
2458 SCF_TYPE_USTRING
, common_name_buf
, max_scf_value_length
, 0, 1, 1)
2460 (void) printf(fmt
, DETAILED_WIDTH
, gettext("name"),
2462 else if (inst_get_single_val(wip
->inst
, SCF_PG_TM_COMMON_NAME
, "C",
2463 SCF_TYPE_USTRING
, common_name_buf
, max_scf_value_length
, 0, 1, 1)
2465 (void) printf(fmt
, DETAILED_WIDTH
, gettext("name"),
2468 if (g_zonename
!= NULL
)
2469 (void) printf(fmt
, DETAILED_WIDTH
, gettext("zone"), g_zonename
);
2472 * Synthesize an 'enabled' property that hides the enabled_ovr
2473 * implementation from the user. If the service has been temporarily
2474 * set to a state other than its permanent value, alert the user with
2475 * a '(temporary)' message.
2477 perm
= instance_enabled(wip
->inst
, B_FALSE
);
2478 temp
= instance_enabled(wip
->inst
, B_TRUE
);
2481 (void) printf(gettext("%-*s%s (temporary)\n"),
2482 DETAILED_WIDTH
, gettext("enabled"),
2483 temp
? gettext("true") : gettext("false"));
2485 (void) printf(fmt
, DETAILED_WIDTH
,
2486 gettext("enabled"), temp
? gettext("true") :
2488 } else if (perm
!= -1) {
2489 (void) printf(fmt
, DETAILED_WIDTH
, gettext("enabled"),
2490 perm
? gettext("true") : gettext("false"));
2494 * Property values may be longer than max_scf_fmri_length, but these
2495 * shouldn't be, so we'll just reuse buf. The user can use svcprop if
2496 * they suspect something fishy.
2498 if (scf_instance_get_pg(wip
->inst
, SCF_PG_RESTARTER
, rpg
) != 0) {
2499 if (scf_error() != SCF_ERROR_NOT_FOUND
)
2502 scf_pg_destroy(rpg
);
2507 if (pg_get_single_val(rpg
, scf_property_state
, SCF_TYPE_ASTRING
,
2508 buf
, max_scf_fmri_length
+ 1, 0) == 0)
2509 (void) printf(fmt
, DETAILED_WIDTH
, gettext("state"),
2512 if (pg_get_single_val(rpg
, scf_property_next_state
,
2513 SCF_TYPE_ASTRING
, buf
, max_scf_fmri_length
+ 1, 0) == 0)
2514 (void) printf(fmt
, DETAILED_WIDTH
,
2515 gettext("next_state"), buf
);
2517 if (pg_get_single_val(rpg
, SCF_PROPERTY_STATE_TIMESTAMP
,
2518 SCF_TYPE_TIME
, &tv
, NULL
, 0) == 0) {
2520 tmp
= localtime(&stime
);
2521 for (tbsz
= 50; ; tbsz
*= 2) {
2522 timebuf
= safe_malloc(tbsz
);
2523 if (strftime(timebuf
, tbsz
, NULL
, tmp
) != 0)
2527 (void) printf(fmt
, DETAILED_WIDTH
,
2528 gettext("state_time"),
2533 if (pg_get_single_val(rpg
, SCF_PROPERTY_ALT_LOGFILE
,
2534 SCF_TYPE_ASTRING
, buf
, max_scf_fmri_length
+ 1, 0) == 0)
2535 (void) printf(fmt
, DETAILED_WIDTH
,
2536 gettext("alt_logfile"), buf
);
2538 if (pg_get_single_val(rpg
, SCF_PROPERTY_LOGFILE
,
2539 SCF_TYPE_ASTRING
, buf
, max_scf_fmri_length
+ 1, 0) == 0)
2540 (void) printf(fmt
, DETAILED_WIDTH
, gettext("logfile"),
2544 if (inst_get_single_val(wip
->inst
, SCF_PG_GENERAL
,
2545 SCF_PROPERTY_RESTARTER
, SCF_TYPE_ASTRING
, buf
,
2546 max_scf_fmri_length
+ 1, 0, 0, 1) == 0)
2547 (void) printf(fmt
, DETAILED_WIDTH
, gettext("restarter"), buf
);
2549 (void) printf(fmt
, DETAILED_WIDTH
, gettext("restarter"),
2550 SCF_SERVICE_STARTD
);
2555 * Use the restarter specific routine to print the ctids, if available.
2556 * If restarter specific action is available and it fails, then die.
2558 restarter_ret
= ctids_by_restarter(wip
, &c
, 1, 0,
2559 &restarter_spec
, print_ctid_header
, print_ctid_detailed
);
2560 if (restarter_spec
== 1) {
2561 if (restarter_ret
!= 0)
2562 uu_die(gettext("Unable to get restarter for %s"),
2564 goto restarter_common
;
2570 if ((iter
= scf_iter_create(h
)) == NULL
)
2573 if (scf_pg_get_property(rpg
, scf_property_contract
, g_prop
) ==
2575 if (scf_property_is_type(g_prop
, SCF_TYPE_COUNT
) == 0) {
2577 /* Callback to print ctid header */
2578 print_ctid_header();
2580 if (scf_iter_property_values(iter
, g_prop
) != 0)
2584 ret
= scf_iter_next_value(iter
, g_val
);
2590 if (scf_value_get_count(g_val
, &c
) != 0)
2593 /* Callback to print contract id. */
2594 print_ctid_detailed(c
);
2597 (void) putchar('\n');
2599 if (scf_error() != SCF_ERROR_TYPE_MISMATCH
)
2603 if (scf_error() != SCF_ERROR_NOT_FOUND
)
2607 scf_iter_destroy(iter
);
2609 if (scf_error() != SCF_ERROR_NOT_FOUND
)
2614 scf_pg_destroy(rpg
);
2617 if ((pg_iter
= scf_iter_create(h
)) == NULL
)
2620 snap
= get_running_snapshot(wip
->inst
);
2622 if (scf_iter_instance_pgs_typed_composed(pg_iter
, wip
->inst
, snap
,
2623 SCF_GROUP_DEPENDENCY
) != SCF_SUCCESS
)
2626 while ((ret
= scf_iter_next_pg(pg_iter
, g_pg
)) == 1)
2627 print_detailed_dependency(g_pg
);
2631 scf_iter_destroy(pg_iter
);
2634 detailed_list_processes(wip
);
2636 /* "application" type property groups */
2637 if (opt_verbose
== 1)
2638 print_application_properties(wip
, snap
);
2640 scf_snapshot_destroy(snap
);
2647 print_log(void *unused
, scf_walkinfo_t
*wip
)
2649 scf_propertygroup_t
*rpg
;
2650 char buf
[MAXPATHLEN
];
2652 if ((rpg
= scf_pg_create(h
)) == NULL
)
2655 if (scf_instance_get_pg(wip
->inst
, SCF_PG_RESTARTER
, rpg
) != 0) {
2656 if (scf_error() != SCF_ERROR_NOT_FOUND
)
2662 if (pg_get_single_val(rpg
, SCF_PROPERTY_LOGFILE
,
2663 SCF_TYPE_ASTRING
, buf
, sizeof (buf
), 0) == 0) {
2664 (void) printf("%s\n", buf
);
2668 scf_pg_destroy(rpg
);
2674 qsort_str_compare(const void *p1
, const void *p2
)
2676 return (strcmp((const char *)p1
, (const char *)p2
));
2680 * get_notify_param_classes()
2681 * return the fma classes that don't have a tag in fma_tags[], otherwise NULL
2684 get_notify_param_classes()
2686 scf_handle_t
*h
= _scf_handle_create_and_bind(SCF_VERSION
);
2687 scf_instance_t
*inst
= scf_instance_create(h
);
2688 scf_snapshot_t
*snap
= scf_snapshot_create(h
);
2689 scf_snaplevel_t
*slvl
= scf_snaplevel_create(h
);
2690 scf_propertygroup_t
*pg
= scf_pg_create(h
);
2691 scf_iter_t
*iter
= scf_iter_create(h
);
2694 size_t sz
= scf_limit(SCF_LIMIT_MAX_NAME_LENGTH
) + 1;
2696 char *pgname
= safe_malloc(sz
);
2697 char **buf
= safe_malloc(size
* sizeof (char *));
2699 if (h
== NULL
|| inst
== NULL
|| snap
== NULL
|| slvl
== NULL
||
2700 pg
== NULL
|| iter
== NULL
) {
2701 uu_die(gettext("Failed object creation: %s\n"),
2702 scf_strerror(scf_error()));
2705 if (scf_handle_decode_fmri(h
, SCF_NOTIFY_PARAMS_INST
, NULL
, NULL
, inst
,
2706 NULL
, NULL
, SCF_DECODE_FMRI_EXACT
) != 0)
2707 uu_die(gettext("Failed to decode %s: %s\n"),
2708 SCF_NOTIFY_PARAMS_INST
, scf_strerror(scf_error()));
2710 if (scf_instance_get_snapshot(inst
, "running", snap
) != 0)
2711 uu_die(gettext("Failed to get snapshot: %s\n"),
2712 scf_strerror(scf_error()));
2714 if (scf_snapshot_get_base_snaplevel(snap
, slvl
) != 0)
2715 uu_die(gettext("Failed to get base snaplevel: %s\n"),
2716 scf_strerror(scf_error()));
2718 if (scf_iter_snaplevel_pgs_typed(iter
, slvl
,
2719 SCF_NOTIFY_PARAMS_PG_TYPE
) != 0)
2720 uu_die(gettext("Failed to get iterator: %s\n"),
2721 scf_strerror(scf_error()));
2723 while ((err
= scf_iter_next_pg(iter
, pg
)) == 1) {
2726 if (scf_pg_get_name(pg
, pgname
, sz
) == -1)
2727 uu_die(gettext("Failed to get pg name: %s\n"),
2728 scf_strerror(scf_error()));
2729 if ((c
= strrchr(pgname
, ',')) != NULL
)
2731 if (has_fma_tag(pgname
))
2733 if (!is_fma_token(pgname
))
2735 * We don't emmit a warning here so that we don't
2736 * pollute the output
2740 if (n
+ 1 >= size
) {
2742 buf
= realloc(buf
, size
* sizeof (char *));
2744 uu_die(gettext("Out of memory.\n"));
2746 buf
[n
] = safe_strdup(pgname
);
2750 * NULL terminate buf
2754 uu_die(gettext("Failed to iterate pgs: %s\n"),
2755 scf_strerror(scf_error()));
2757 /* sort the classes */
2758 qsort((void *)buf
, n
, sizeof (char *), qsort_str_compare
);
2761 scf_iter_destroy(iter
);
2763 scf_snaplevel_destroy(slvl
);
2764 scf_snapshot_destroy(snap
);
2765 scf_instance_destroy(inst
);
2766 scf_handle_destroy(h
);
2772 * get_fma_notify_params()
2773 * populates an nvlist_t with notifycation parameters for a given FMA class
2774 * returns 0 if the nvlist is populated, 1 otherwise;
2777 get_fma_notify_params(nvlist_t
*nvl
, const char *class)
2779 if (_scf_get_fma_notify_params(class, nvl
, 0) != 0) {
2781 * if the preferences have just been deleted
2782 * or does not exist, just skip.
2784 if (scf_error() != SCF_ERROR_NOT_FOUND
&&
2785 scf_error() != SCF_ERROR_DELETED
)
2787 "Failed get_fma_notify_params %s\n"),
2788 scf_strerror(scf_error()));
2797 * print_notify_fma()
2798 * outputs the notification paramets of FMA events.
2799 * It first outputs classes in fma_tags[], then outputs the other classes
2800 * sorted alphabetically
2803 print_notify_fma(void)
2811 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0)
2812 uu_die(gettext("Out of memory.\n"));
2814 for (i
= 0; (class = get_fma_class(i
)) != NULL
; ++i
) {
2815 if (get_fma_notify_params(nvl
, class) == 0)
2816 listnotify_print(nvl
, get_fma_tag(i
));
2819 if ((classes
= get_notify_param_classes()) == NULL
)
2823 for (p
= *tmp
; p
; ++tmp
, p
= *tmp
) {
2824 if (get_fma_notify_params(nvl
, p
) == 0)
2825 listnotify_print(nvl
, re_tag(p
));
2837 * print_notify_fmri()
2838 * prints notifycation parameters for an SMF instance.
2841 print_notify_fmri(const char *fmri
)
2845 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, 0) != 0)
2846 uu_die(gettext("Out of memory.\n"));
2848 if (_scf_get_svc_notify_params(fmri
, nvl
, SCF_TRANSITION_ALL
, 0, 0) !=
2850 if (scf_error() != SCF_ERROR_NOT_FOUND
&&
2851 scf_error() != SCF_ERROR_DELETED
)
2853 "Failed _scf_get_svc_notify_params: %s\n"),
2854 scf_strerror(scf_error()));
2856 if (strcmp(SCF_INSTANCE_GLOBAL
, fmri
) == 0)
2858 gettext("System wide notification parameters:\n"));
2859 safe_printf("%s:\n", fmri
);
2860 listnotify_print(nvl
, NULL
);
2866 * print_notify_special()
2867 * prints notification parameters for FMA events and system wide SMF state
2868 * transitions parameters
2871 print_notify_special()
2873 safe_printf("Notification parameters for FMA Events\n");
2875 print_notify_fmri(SCF_INSTANCE_GLOBAL
);
2880 * callback function to print notification parameters for SMF state transition
2881 * instances. It skips global and notify-params instances as they should be
2882 * printed by print_notify_special()
2886 print_notify(void *unused
, scf_walkinfo_t
*wip
)
2888 if (strcmp(SCF_INSTANCE_GLOBAL
, wip
->fmri
) == 0 ||
2889 strcmp(SCF_NOTIFY_PARAMS_INST
, wip
->fmri
) == 0)
2892 print_notify_fmri(wip
->fmri
);
2898 * Append a one-lined description of each process in inst's contract(s) and
2899 * return the augmented string.
2902 add_processes(scf_walkinfo_t
*wip
, char *line
, scf_propertygroup_t
*lpg
)
2908 if (instance_processes(wip
->inst
, wip
->fmri
, &pids
, &n
) != 0)
2911 /* Legacy services */
2914 if ((iter
= scf_iter_create(h
)) == NULL
)
2917 (void) propvals_to_pids(lpg
, scf_property_contract
, &pids
, &n
,
2918 g_prop
, g_val
, iter
);
2920 scf_iter_destroy(iter
);
2926 qsort(pids
, n
, sizeof (*pids
), pidcmp
);
2928 for (i
= 0; i
< n
; ++i
) {
2932 int len
= 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ
;
2934 if (get_psinfo(pids
[i
], &psi
) != 0)
2937 line
= realloc(line
, strlen(line
) + len
);
2939 uu_die(gettext("Out of memory.\n"));
2941 cp
= strchr(line
, '\0');
2943 tm
= localtime(&psi
.pr_start
.tv_sec
);
2946 * Print time if started within the past 24 hours, print date
2947 * if within the past 12 months, print year if started greater
2948 * than 12 months ago.
2950 if (now
- psi
.pr_start
.tv_sec
< 24 * 60 * 60)
2951 (void) strftime(stime
, sizeof (stime
),
2952 gettext(FORMAT_TIME
), tm
);
2953 else if (now
- psi
.pr_start
.tv_sec
< 12 * 30 * 24 * 60 * 60)
2954 (void) strftime(stime
, sizeof (stime
),
2955 gettext(FORMAT_DATE
), tm
);
2957 (void) strftime(stime
, sizeof (stime
),
2958 gettext(FORMAT_YEAR
), tm
);
2960 (void) snprintf(cp
, len
, "\n %-8s %6ld %.*s",
2961 stime
, pids
[i
], PRFNSZ
, psi
.pr_fname
);
2971 list_instance(void *unused
, scf_walkinfo_t
*wip
)
2973 struct avl_string
*lp
;
2979 * If the user has specified a restarter, check for a match first
2981 if (restarters
!= NULL
) {
2982 struct pfmri_list
*rest
;
2984 char *restarter_fmri
;
2985 const char *scope_name
, *svc_name
, *inst_name
, *pg_name
;
2987 /* legacy services don't have restarters */
2988 if (wip
->pg
!= NULL
)
2991 restarter_fmri
= safe_malloc(max_scf_fmri_length
+ 1);
2993 if (inst_get_single_val(wip
->inst
, SCF_PG_GENERAL
,
2994 SCF_PROPERTY_RESTARTER
, SCF_TYPE_ASTRING
, restarter_fmri
,
2995 max_scf_fmri_length
+ 1, 0, 0, 1) != 0)
2996 (void) strcpy(restarter_fmri
, SCF_SERVICE_STARTD
);
2998 if (scf_parse_svc_fmri(restarter_fmri
, &scope_name
, &svc_name
,
2999 &inst_name
, &pg_name
, NULL
) != SCF_SUCCESS
) {
3000 free(restarter_fmri
);
3005 for (rest
= restarters
; rest
!= NULL
; rest
= rest
->next
) {
3006 if (strcmp(rest
->scope
, scope_name
) == 0 &&
3007 strcmp(rest
->service
, svc_name
) == 0 &&
3008 strcmp(rest
->instance
, inst_name
) == 0)
3012 free(restarter_fmri
);
3018 if (wip
->pg
== NULL
&& ht_buckets
!= NULL
&& ht_add(wip
->fmri
)) {
3019 /* It was already there. */
3023 lp
= safe_malloc(sizeof (*lp
));
3026 for (i
= 0; i
< opt_cnum
; ++i
) {
3027 columns
[opt_columns
[i
]].sprint(&lp
->str
, wip
);
3029 cp
= lp
->str
+ strlen(lp
->str
);
3035 /* If we're supposed to list the processes, too, do that now. */
3037 lp
->str
= add_processes(wip
, lp
->str
, wip
->pg
);
3039 /* Create the sort key. */
3040 cp
= lp
->key
= safe_malloc(sortkey_sz
);
3041 for (i
= 0; i
< opt_snum
; ++i
) {
3042 int j
= opt_sort
[i
] & 0xff;
3044 assert(columns
[j
].get_sortkey
!= NULL
);
3045 columns
[j
].get_sortkey(cp
, opt_sort
[i
] & ~0xff, wip
);
3046 cp
+= columns
[j
].sortkey_width
;
3049 /* Insert into AVL tree. */
3050 uu_avl_node_init(lp
, &lp
->node
, lines_pool
);
3051 (void) uu_avl_find(lines
, lp
, NULL
, &idx
);
3052 uu_avl_insert(lines
, lp
, idx
);
3058 list_if_enabled(void *unused
, scf_walkinfo_t
*wip
)
3060 if (wip
->pg
!= NULL
||
3061 instance_enabled(wip
->inst
, B_FALSE
) == 1 ||
3062 instance_enabled(wip
->inst
, B_TRUE
) == 1)
3063 return (list_instance(unused
, wip
));
3069 * Service FMRI selection: Lookup and call list_instance() for the instances.
3070 * Instance FMRI selection: Lookup and call list_instance().
3072 * Note: This is shoehorned into a walk_dependencies() callback prototype so
3073 * it can be used in list_dependencies.
3076 list_svc_or_inst_fmri(void *complain
, scf_walkinfo_t
*wip
)
3079 const char *svc_name
, *inst_name
, *pg_name
, *save
;
3083 fmri
= safe_strdup(wip
->fmri
);
3085 if (scf_parse_svc_fmri(fmri
, NULL
, &svc_name
, &inst_name
, &pg_name
,
3086 NULL
) != SCF_SUCCESS
) {
3088 uu_warn(gettext("FMRI \"%s\" is invalid.\n"),
3090 exit_status
= UU_EXIT_FATAL
;
3096 * Yes, this invalidates *_name, but we only care whether they're NULL
3101 if (svc_name
== NULL
|| pg_name
!= NULL
) {
3103 uu_warn(gettext("FMRI \"%s\" does not designate a "
3104 "service or instance.\n"), wip
->fmri
);
3108 if (inst_name
!= NULL
) {
3110 if (scf_handle_decode_fmri(h
, wip
->fmri
, wip
->scope
, wip
->svc
,
3111 wip
->inst
, NULL
, NULL
, 0) != SCF_SUCCESS
) {
3112 if (scf_error() != SCF_ERROR_NOT_FOUND
)
3117 "Instance \"%s\" does not exist.\n"),
3122 return (list_instance(NULL
, wip
));
3125 /* service: Walk the instances. */
3126 if (scf_handle_decode_fmri(h
, wip
->fmri
, wip
->scope
, wip
->svc
, NULL
,
3127 NULL
, NULL
, 0) != SCF_SUCCESS
) {
3128 if (scf_error() != SCF_ERROR_NOT_FOUND
)
3132 uu_warn(gettext("Service \"%s\" does not exist.\n"),
3135 exit_status
= UU_EXIT_FATAL
;
3140 iter
= scf_iter_create(h
);
3144 if (scf_iter_service_instances(iter
, wip
->svc
) != SCF_SUCCESS
)
3147 if ((fmri
= malloc(max_scf_fmri_length
+ 1)) == NULL
) {
3148 scf_iter_destroy(iter
);
3149 exit_status
= UU_EXIT_FATAL
;
3155 while ((ret
= scf_iter_next_instance(iter
, wip
->inst
)) == 1) {
3156 if (scf_instance_to_fmri(wip
->inst
, fmri
,
3157 max_scf_fmri_length
+ 1) <= 0)
3159 (void) list_instance(NULL
, wip
);
3166 exit_status
= UU_EXIT_OK
;
3168 scf_iter_destroy(iter
);
3174 * Dependency selection: Straightforward since each instance lists the
3175 * services it depends on.
3179 walk_dependencies(scf_walkinfo_t
*wip
, scf_walk_callback callback
, void *data
)
3181 scf_snapshot_t
*snap
;
3182 scf_iter_t
*iter
, *viter
;
3186 assert(wip
->inst
!= NULL
);
3188 if ((iter
= scf_iter_create(h
)) == NULL
||
3189 (viter
= scf_iter_create(h
)) == NULL
)
3192 snap
= get_running_snapshot(wip
->inst
);
3194 if (scf_iter_instance_pgs_typed_composed(iter
, wip
->inst
, snap
,
3195 SCF_GROUP_DEPENDENCY
) != SCF_SUCCESS
)
3198 dep
= safe_malloc(max_scf_value_length
+ 1);
3200 while ((ret
= scf_iter_next_pg(iter
, g_pg
)) == 1) {
3203 /* Ignore exclude_any dependencies. */
3204 if (scf_pg_get_property(g_pg
, SCF_PROPERTY_GROUPING
, g_prop
) !=
3206 if (scf_error() != SCF_ERROR_NOT_FOUND
)
3212 if (scf_property_type(g_prop
, &ty
) != SCF_SUCCESS
)
3215 if (ty
!= SCF_TYPE_ASTRING
)
3218 if (scf_property_get_value(g_prop
, g_val
) != SCF_SUCCESS
) {
3219 if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED
)
3225 if (scf_value_get_astring(g_val
, dep
,
3226 max_scf_value_length
+ 1) < 0)
3229 if (strcmp(dep
, SCF_DEP_EXCLUDE_ALL
) == 0)
3232 if (scf_pg_get_property(g_pg
, SCF_PROPERTY_ENTITIES
, g_prop
) !=
3234 if (scf_error() != SCF_ERROR_NOT_FOUND
)
3240 if (scf_iter_property_values(viter
, g_prop
) != SCF_SUCCESS
)
3243 while ((vret
= scf_iter_next_value(viter
, g_val
)) == 1) {
3244 if (scf_value_get_astring(g_val
, dep
,
3245 max_scf_value_length
+ 1) < 0)
3249 if (callback(data
, wip
) != 0)
3259 scf_iter_destroy(viter
);
3260 scf_iter_destroy(iter
);
3261 scf_snapshot_destroy(snap
);
3265 list_dependencies(void *data
, scf_walkinfo_t
*wip
)
3267 walk_dependencies(wip
, list_svc_or_inst_fmri
, data
);
3273 * Dependent selection: The "providing" service's or instance's FMRI is parsed
3274 * into the provider_* variables, the instances are walked, and any instance
3275 * which lists an FMRI which parses to these components is selected. This is
3276 * inefficient in the face of multiple operands, but that should be uncommon.
3279 static char *provider_scope
;
3280 static char *provider_svc
;
3281 static char *provider_inst
; /* NULL for services */
3285 check_against_provider(void *arg
, scf_walkinfo_t
*wip
)
3288 const char *scope_name
, *svc_name
, *inst_name
, *pg_name
;
3291 cfmri
= safe_strdup(wip
->fmri
);
3293 if (scf_parse_svc_fmri(cfmri
, &scope_name
, &svc_name
, &inst_name
,
3294 &pg_name
, NULL
) != SCF_SUCCESS
) {
3299 if (svc_name
== NULL
|| pg_name
!= NULL
) {
3305 * If the user has specified an instance, then also match dependencies
3306 * on the service itself.
3308 *matchp
= (strcmp(provider_scope
, scope_name
) == 0 &&
3309 strcmp(provider_svc
, svc_name
) == 0 &&
3310 (provider_inst
== NULL
? (inst_name
== NULL
) :
3311 (inst_name
== NULL
|| strcmp(provider_inst
, inst_name
) == 0)));
3315 /* Stop on matches. */
3320 list_if_dependent(void *unused
, scf_walkinfo_t
*wip
)
3322 /* Only proceed if this instance depends on provider_*. */
3325 (void) walk_dependencies(wip
, check_against_provider
, &match
);
3328 return (list_instance(unused
, wip
));
3335 list_dependents(void *unused
, scf_walkinfo_t
*wip
)
3340 if (scf_scope_get_name(wip
->scope
, provider_scope
,
3341 max_scf_fmri_length
) <= 0 ||
3342 scf_service_get_name(wip
->svc
, provider_svc
,
3343 max_scf_fmri_length
) <= 0)
3346 save
= provider_inst
;
3347 if (wip
->inst
== NULL
)
3348 provider_inst
= NULL
;
3349 else if (scf_instance_get_name(wip
->inst
, provider_inst
,
3350 max_scf_fmri_length
) <= 0)
3353 ret
= scf_walk_fmri(h
, 0, NULL
, 0, list_if_dependent
, NULL
, NULL
,
3356 provider_inst
= save
;
3366 add_sort_column(const char *col
, int reverse
)
3372 opt_sort
= realloc(opt_sort
, opt_snum
* sizeof (*opt_sort
));
3373 if (opt_sort
== NULL
)
3374 uu_die(gettext("Too many sort criteria: out of memory.\n"));
3376 for (i
= 0; i
< ncolumns
; ++i
) {
3377 if (strcasecmp(col
, columns
[i
].name
) == 0)
3382 opt_sort
[opt_snum
- 1] = (reverse
? i
| 0x100 : i
);
3384 uu_die(gettext("Unrecognized sort column \"%s\".\n"), col
);
3386 sortkey_sz
+= columns
[i
].sortkey_width
;
3390 add_restarter(const char *fmri
)
3393 const char *pg_name
;
3394 struct pfmri_list
*rest
;
3396 cfmri
= safe_strdup(fmri
);
3397 rest
= safe_malloc(sizeof (*rest
));
3399 if (scf_parse_svc_fmri(cfmri
, &rest
->scope
, &rest
->service
,
3400 &rest
->instance
, &pg_name
, NULL
) != SCF_SUCCESS
)
3401 uu_die(gettext("Restarter FMRI \"%s\" is invalid.\n"), fmri
);
3403 if (rest
->instance
== NULL
|| pg_name
!= NULL
)
3404 uu_die(gettext("Restarter FMRI \"%s\" does not designate an "
3405 "instance.\n"), fmri
);
3407 rest
->next
= restarters
;
3418 line_cmp(const void *l_arg
, const void *r_arg
, void *private)
3420 const struct avl_string
*l
= l_arg
;
3421 const struct avl_string
*r
= r_arg
;
3423 return (memcmp(l
->key
, r
->key
, sortkey_sz
));
3428 print_line(void *e
, void *private)
3430 struct avl_string
*lp
= e
;
3432 (void) puts(lp
->str
);
3434 return (UU_WALK_NEXT
);
3439 errignore(const char *str
, ...)
3443 main(int argc
, char **argv
)
3447 char *columns_str
= NULL
;
3449 const char *progname
;
3450 int err
, missing
= 1, ignored
, *errarg
;
3451 uint_t nzents
= 0, zent
= 0;
3452 zoneid_t
*zids
= NULL
;
3453 char zonename
[ZONENAME_MAX
];
3454 void (*errfunc
)(const char *, ...);
3457 int show_header
= 1;
3460 const char * const options
= "aHpvno:R:s:S:dDlL?xZz:";
3462 (void) setlocale(LC_ALL
, "");
3464 locale
= setlocale(LC_MESSAGES
, NULL
);
3466 locale
= safe_strdup(locale
);
3467 _scf_sanitize_locale(locale
);
3470 (void) textdomain(TEXT_DOMAIN
);
3471 progname
= uu_setpname(argv
[0]);
3473 exit_status
= UU_EXIT_OK
;
3475 max_scf_name_length
= scf_limit(SCF_LIMIT_MAX_NAME_LENGTH
);
3476 max_scf_value_length
= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
3477 max_scf_fmri_length
= scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH
);
3478 max_scf_type_length
= scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH
);
3480 if (max_scf_name_length
== -1 || max_scf_value_length
== -1 ||
3481 max_scf_fmri_length
== -1 || max_scf_type_length
== -1)
3488 * opt_mode is the mode of operation. 0 for plain, 'd' for
3489 * dependencies, 'D' for dependents, and 'l' for detailed (long). We
3490 * need to know now so we know which options are valid.
3493 while ((opt
= getopt(argc
, argv
, options
)) != -1) {
3496 if (optopt
== '?') {
3497 print_help(progname
);
3498 return (UU_EXIT_OK
);
3535 optind
= 1; /* Reset getopt() */
3536 while ((opt
= getopt(argc
, argv
, options
)) != -1) {
3545 if (opt_mode
== 'l' || opt_mode
== 'x')
3551 if (opt_mode
== 'x')
3561 if (opt_mode
== 'l' || opt_mode
== 'x')
3563 columns_str
= optarg
;
3567 if (opt_mode
!= 0 || opt_mode
== 'x')
3570 add_restarter(optarg
);
3578 add_sort_column(optarg
, optopt
== 'S');
3587 assert(opt_mode
== optopt
);
3591 if (getzoneid() != GLOBAL_ZONEID
)
3592 uu_die(gettext("svcs -z may only be used from "
3593 "the global zone\n"));
3601 if (getzoneid() != GLOBAL_ZONEID
)
3602 uu_die(gettext("svcs -Z may only be used from "
3603 "the global zone\n"));
3604 if (opt_zone
!= NULL
)
3621 * -a is only meaningful when given no arguments
3623 if (show_all
&& optind
!= argc
)
3624 uu_warn(gettext("-a ignored when used with arguments.\n"));
3626 while (show_zones
) {
3629 if (zone_list(NULL
, &nzents
) != 0)
3630 uu_die(gettext("could not get number of zones"));
3632 if ((zids
= malloc(nzents
* sizeof (zoneid_t
))) == NULL
) {
3633 uu_die(gettext("could not allocate array for "
3634 "%d zone IDs"), nzents
);
3639 if (zone_list(zids
, &found
) != 0)
3640 uu_die(gettext("could not get zone list"));
3643 * If the number of zones has not changed between our calls to
3644 * zone_list(), we're done -- otherwise, we must free our array
3645 * of zone IDs and take another lap.
3647 if (found
== nzents
)
3657 h
= scf_handle_create(SCF_VERSION
);
3661 if (opt_zone
!= NULL
|| zids
!= NULL
) {
3664 assert(opt_zone
== NULL
|| zids
== NULL
);
3666 if (opt_zone
== NULL
) {
3667 if (getzonenamebyid(zids
[zent
++],
3668 zonename
, sizeof (zonename
)) < 0) {
3669 uu_warn(gettext("could not get name for "
3670 "zone %d; ignoring"), zids
[zent
- 1]);
3674 g_zonename
= zonename
;
3676 g_zonename
= opt_zone
;
3679 if ((zone
= scf_value_create(h
)) == NULL
)
3682 if (scf_value_set_astring(zone
, g_zonename
) != SCF_SUCCESS
)
3685 if (scf_handle_decorate(h
, "zone", zone
) != SCF_SUCCESS
)
3686 uu_die(gettext("invalid zone '%s'\n"), g_zonename
);
3688 scf_value_destroy(zone
);
3691 if (scf_handle_bind(h
) == -1) {
3692 if (g_zonename
!= NULL
) {
3693 uu_warn(gettext("Could not bind to repository "
3694 "server for zone %s: %s\n"), g_zonename
,
3695 scf_strerror(scf_error()));
3698 return (UU_EXIT_FATAL
);
3703 uu_die(gettext("Could not bind to repository server: %s. "
3704 "Exiting.\n"), scf_strerror(scf_error()));
3707 if ((g_pg
= scf_pg_create(h
)) == NULL
||
3708 (g_prop
= scf_property_create(h
)) == NULL
||
3709 (g_val
= scf_value_create(h
)) == NULL
)
3714 * It's hard to avoid editorializing here, but suffice it to
3715 * say that scf_walk_fmri() takes an error handler, the
3716 * interface to which has been regrettably misdesigned: the
3717 * handler itself takes exclusively a string -- even though
3718 * scf_walk_fmri() has detailed, programmatic knowledge
3719 * of the error condition at the time it calls its errfunc.
3720 * That is, only the error message and not the error semantics
3721 * are given to the handler. This is poor interface at best,
3722 * but it is particularly problematic when we are talking to
3723 * multiple repository servers (as when we are iterating over
3724 * all zones) as we do not want to treat failure to find a
3725 * match in one zone as overall failure. Ideally, we would
3726 * simply ignore SCF_MSG_PATTERN_NOINSTANCE and correctly
3727 * process the others, but alas, no such interface exists --
3728 * and we must settle for instead ignoring all errfunc-called
3729 * errors in the case that we are iterating over all zones...
3731 errfunc
= errignore
;
3732 errarg
= missing
? &missing
: &ignored
;
3736 errarg
= &exit_status
;
3740 * If we're in long mode, take care of it now before we deal with the
3741 * sorting and the columns, since we won't use them anyway.
3743 if (opt_mode
== 'l') {
3747 if ((err
= scf_walk_fmri(h
, argc
, argv
, SCF_WALK_MULTIPLE
,
3748 print_detailed
, NULL
, errarg
, errfunc
)) != 0) {
3749 uu_warn(gettext("failed to iterate over "
3750 "instances: %s\n"), scf_strerror(err
));
3751 exit_status
= UU_EXIT_FATAL
;
3757 if (opt_mode
== 'L') {
3758 if ((err
= scf_walk_fmri(h
, argc
, argv
, SCF_WALK_MULTIPLE
,
3759 print_log
, NULL
, &exit_status
, uu_warn
)) != 0) {
3760 uu_warn(gettext("failed to iterate over "
3761 "instances: %s\n"), scf_strerror(err
));
3762 exit_status
= UU_EXIT_FATAL
;
3768 if (opt_mode
== 'n') {
3769 print_notify_special();
3770 if ((err
= scf_walk_fmri(h
, argc
, argv
, SCF_WALK_MULTIPLE
,
3771 print_notify
, NULL
, errarg
, errfunc
)) != 0) {
3772 uu_warn(gettext("failed to iterate over "
3773 "instances: %s\n"), scf_strerror(err
));
3774 exit_status
= UU_EXIT_FATAL
;
3780 if (opt_mode
== 'x') {
3781 explain(opt_verbose
, argc
, argv
);
3785 if (columns_str
== NULL
) {
3786 if (opt_snum
== 0) {
3788 add_sort_column("zone", 0);
3791 add_sort_column("state", 0);
3792 add_sort_column("stime", 0);
3793 add_sort_column("fmri", 0);
3797 columns_str
= safe_strdup(show_zones
?
3798 "zone,state,stime,fmri" : "state,stime,fmri");
3800 columns_str
= safe_strdup(show_zones
?
3801 "zone,state,nstate,stime,ctid,fmri" :
3802 "state,nstate,stime,ctid,fmri");
3806 if (opt_columns
== NULL
) {
3807 /* Decode columns_str into opt_columns. */
3811 for (cp
= columns_str
; *cp
!= '\0'; ++cp
)
3815 if (*columns_str
== '\0')
3816 uu_die(gettext("No columns specified.\n"));
3818 opt_columns
= malloc(opt_cnum
* sizeof (*opt_columns
));
3819 if (opt_columns
== NULL
)
3820 uu_die(gettext("Too many columns.\n"));
3822 for (n
= 0; *columns_str
!= '\0'; ++n
) {
3823 i
= getcolumnopt(&columns_str
);
3825 uu_die(gettext("Unknown column \"%s\".\n"),
3828 if (strcmp(columns
[i
].name
, "N") == 0 ||
3829 strcmp(columns
[i
].name
, "SN") == 0 ||
3830 strcmp(columns
[i
].name
, "NSTA") == 0 ||
3831 strcmp(columns
[i
].name
, "NSTATE") == 0)
3832 opt_nstate_shown
= 1;
3835 line_sz
+= columns
[i
].width
+ 1;
3838 if ((lines_pool
= uu_avl_pool_create("lines_pool",
3839 sizeof (struct avl_string
), offsetof(struct avl_string
,
3840 node
), line_cmp
, UU_AVL_DEBUG
)) == NULL
||
3841 (lines
= uu_avl_create(lines_pool
, NULL
, 0)) == NULL
)
3842 uu_die(gettext("Unexpected libuutil error: %s\n"),
3843 uu_strerror(uu_error()));
3849 * If we already have a hash table (e.g., because we are
3850 * processing multiple zones), destroy it before creating
3853 if (ht_buckets
!= NULL
)
3858 /* Always show all FMRIs when given arguments or restarters */
3859 if (argc
!= 0 || restarters
!= NULL
)
3862 if ((err
= scf_walk_fmri(h
, argc
, argv
,
3863 SCF_WALK_MULTIPLE
| SCF_WALK_LEGACY
,
3864 show_all
? list_instance
: list_if_enabled
, NULL
,
3865 errarg
, errfunc
)) != 0) {
3866 uu_warn(gettext("failed to iterate over "
3867 "instances: %s\n"), scf_strerror(err
));
3868 exit_status
= UU_EXIT_FATAL
;
3876 if ((err
= scf_walk_fmri(h
, argc
, argv
,
3877 SCF_WALK_MULTIPLE
, list_dependencies
, NULL
,
3878 errarg
, errfunc
)) != 0) {
3879 uu_warn(gettext("failed to iterate over "
3880 "instances: %s\n"), scf_strerror(err
));
3881 exit_status
= UU_EXIT_FATAL
;
3889 provider_scope
= safe_malloc(max_scf_fmri_length
);
3890 provider_svc
= safe_malloc(max_scf_fmri_length
);
3891 provider_inst
= safe_malloc(max_scf_fmri_length
);
3893 if ((err
= scf_walk_fmri(h
, argc
, argv
,
3894 SCF_WALK_MULTIPLE
| SCF_WALK_SERVICE
,
3895 list_dependents
, NULL
, &exit_status
, uu_warn
)) != 0) {
3896 uu_warn(gettext("failed to iterate over "
3897 "instances: %s\n"), scf_strerror(err
));
3898 exit_status
= UU_EXIT_FATAL
;
3901 free(provider_scope
);
3903 free(provider_inst
);
3915 if (show_zones
&& zent
< nzents
&& exit_status
== 0) {
3916 scf_handle_destroy(h
);
3920 if (show_zones
&& exit_status
== 0)
3921 exit_status
= missing
;
3923 if (opt_columns
== NULL
)
3924 return (exit_status
);
3929 (void) uu_avl_walk(lines
, print_line
, NULL
, 0);
3931 return (exit_status
);