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 kernel/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
, 0, 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
= reallocarray(*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 free(fmri_buf_canonified
);
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.
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
714 common_by_restarter(scf_instance_t
*inst
, const char *fmri
,
715 int *restarter_specp
)
720 /* Check for inetd specific restarter */
721 if (check_for_restarter(inst
, "svc:/network/inetd:default") != 0) {
722 *restarter_specp
= 0;
726 *restarter_specp
= 1;
728 /* Get the ctid filename associated with this instance */
729 r
= gen_filenms_from_fmri(fmri
, "ctid", genfmri_filename
, NULL
);
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
);
746 * The directory didn't exist, so no contracts.
747 * Return failure with no ctids.
752 uu_warn(gettext("%s:%d: gen_filenms_from_fmri() failed with "
753 "unknown error %d\n"), __FILE__
, __LINE__
, r
);
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.
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
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))
788 /* Check if callbacks are needed and were passed in */
790 if ((callback_header
== NULL
) || (callback_ctid
== NULL
))
794 /* Check for restarter specific action and generation of filename */
795 rest_ret
= common_by_restarter(wip
->inst
, wip
->fmri
, restarter_specp
);
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
) {
811 * Print all contract ids that are found.
812 * First callback to print ctid header.
816 /* fscanf may not set errno, so be sure to clear it first */
818 while ((fscanf_ret
= fscanf(fp
, "%llu", cp
)) == 1) {
819 /* Callback to print contract id */
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"),
828 (void) putchar('\n');
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
)
838 /* Success - found only 1 ctid */
850 * Get the process ids associated with an instance using a restarter
854 * 0 if success: restarter specific action found and used with no error
855 * -1 restarter specific action not found or if failure
858 pids_by_restarter(scf_instance_t
*inst
, const char *fmri
,
859 pid_t
**pids
, uint_t
*np
, int *restarter_specp
)
866 /* Check for restarter specific action and generation of filename */
867 rest_ret
= common_by_restarter(inst
, fmri
, restarter_specp
);
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
) {
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 */
884 while ((fscanf_ret
= fscanf(fp
, "%llu", &c
)) == 1) {
887 uu_die(gettext("ctid file for %s has corrupt data"),
890 ctid_to_pids(c
, pids
, np
);
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
);
903 instance_processes(scf_instance_t
*inst
, const char *fmri
,
904 pid_t
**pids
, uint_t
*np
)
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)
915 if ((iter
= scf_iter_create(h
)) == NULL
)
918 if (scf_instance_get_pg(inst
, SCF_PG_RESTARTER
, g_pg
) == 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
);
930 if (scf_error() != SCF_ERROR_NOT_FOUND
)
936 scf_iter_destroy(iter
);
942 get_psinfo(pid_t pid
, psinfo_t
*psip
)
947 (void) snprintf(path
, sizeof (path
), "/proc/%lu/psinfo", pid
);
949 fd
= open64(path
, O_RDONLY
);
953 if (read(fd
, psip
, sizeof (*psip
)) < 0)
954 uu_die(gettext("Could not read info for process %lu"), pid
);
964 * Column sprint and sortkey functions
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
*);
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
991 void (*get_sortkey
)(char *, int, scf_walkinfo_t
*);
995 reverse_bytes(char *buf
, size_t len
)
999 for (i
= 0; i
< len
; ++i
)
1004 #define CTID_COLUMN_WIDTH 6
1005 #define CTID_COLUMN_BUFSIZE 20 /* max ctid_t + space + \0 */
1008 sprint_ctid(char **buf
, scf_walkinfo_t
*wip
)
1012 size_t newsize
= (*buf
? strlen(*buf
) : 0) + CTID_COLUMN_BUFSIZE
;
1013 char *newbuf
= safe_malloc(newsize
);
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
);
1024 r
= ctids_by_restarter(wip
, &c
, 0, MULTI_OK
, &restarter_spec
,
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
);
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
);
1040 (void) snprintf(newbuf
, newsize
, "%s%*s ",
1041 *buf
? *buf
: "", CTID_COLUMN_WIDTH
, "-");
1046 #define CTID_SORTKEY_WIDTH (sizeof (uint64_t))
1049 sortkey_ctid(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
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
);
1063 r
= ctids_by_restarter(wip
, &c
, 0, MULTI_OK
, &restarter_spec
,
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
);
1074 * Use the id itself, but it must be big-endian for this to
1079 bcopy(&c
, buf
, CTID_SORTKEY_WIDTH
);
1081 bzero(buf
, CTID_SORTKEY_WIDTH
);
1085 reverse_bytes(buf
, CTID_SORTKEY_WIDTH
);
1089 #define DESC_COLUMN_WIDTH 100
1092 sprint_desc(char **buf
, scf_walkinfo_t
*wip
)
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,
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,
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
++)
1121 if (strlen(common_name_buf
) > DESC_COLUMN_WIDTH
)
1122 newsize
= (*buf
? strlen(*buf
) : 0) +
1123 strlen(common_name_buf
) + 1;
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
);
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) */
1143 state_to_char(const char *state
)
1145 if (strcmp(state
, SCF_STATE_STRING_UNINIT
) == 0)
1148 if (strcmp(state
, SCF_STATE_STRING_OFFLINE
) == 0)
1151 if (strcmp(state
, SCF_STATE_STRING_ONLINE
) == 0)
1154 if (strcmp(state
, SCF_STATE_STRING_MAINT
) == 0)
1157 if (strcmp(state
, SCF_STATE_STRING_DISABLED
) == 0)
1160 if (strcmp(state
, SCF_STATE_STRING_DEGRADED
) == 0)
1163 if (strcmp(state
, SCF_STATE_STRING_LEGACY
) == 0)
1169 /* Return true if inst is transitioning. */
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
) != '?');
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)
1197 else if (strcmp(state_name
, SCF_STATE_STRING_DEGRADED
) == 0)
1199 else if (strcmp(state_name
, SCF_STATE_STRING_OFFLINE
) == 0)
1201 else if (strcmp(state_name
, SCF_STATE_STRING_MAINT
) == 0)
1203 else if (strcmp(state_name
, SCF_STATE_STRING_DISABLED
) == 0)
1205 else if (strcmp(state_name
, SCF_STATE_STRING_UNINIT
) == 0)
1217 sprint_state(char **buf
, scf_walkinfo_t
*wip
)
1219 char state_name
[MAX_SCF_STATE_STRING_SZ
+ 1];
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
, "*");
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
);
1250 sortkey_state(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1252 sortkey_states(scf_property_state
, buf
, reverse
, wip
);
1256 sprint_nstate(char **buf
, scf_walkinfo_t
*wip
)
1258 char next_state_name
[MAX_SCF_STATE_STRING_SZ
];
1259 boolean_t blank
= 0;
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)
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
);
1288 sortkey_nstate(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1290 sortkey_states(scf_property_next_state
, buf
, reverse
, wip
);
1294 sprint_s(char **buf
, scf_walkinfo_t
*wip
)
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
))
1315 (void) snprintf(newbuf
, newsize
, "%s%-*s", *buf
? *buf
: "",
1322 sprint_n(char **buf
, scf_walkinfo_t
*wip
)
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)
1336 tmp
[0] = state_to_char(nstate_name
);
1340 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1347 sprint_sn(char **buf
, scf_walkinfo_t
*wip
)
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)
1365 tmp
[1] = state_to_char(nstate_name
);
1372 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1380 sortkey_sn(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1382 sortkey_state(buf
, reverse
, wip
);
1383 sortkey_nstate(buf
+ 1, reverse
, wip
);
1387 state_abbrev(const char *state
)
1389 if (strcmp(state
, SCF_STATE_STRING_UNINIT
) == 0)
1391 if (strcmp(state
, SCF_STATE_STRING_OFFLINE
) == 0)
1393 if (strcmp(state
, SCF_STATE_STRING_ONLINE
) == 0)
1395 if (strcmp(state
, SCF_STATE_STRING_MAINT
) == 0)
1397 if (strcmp(state
, SCF_STATE_STRING_DISABLED
) == 0)
1399 if (strcmp(state
, SCF_STATE_STRING_DEGRADED
) == 0)
1401 if (strcmp(state
, SCF_STATE_STRING_LEGACY
) == 0)
1408 sprint_sta(char **buf
, scf_walkinfo_t
*wip
)
1410 char state_name
[MAX_SCF_STATE_STRING_SZ
];
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
));
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
);
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
));
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
: "",
1448 (void) snprintf(newbuf
, newsize
, "%s%-4s ", *buf
? *buf
: "",
1449 state_abbrev(state_name
));
1455 #define FMRI_COLUMN_WIDTH 50
1457 sprint_fmri(char **buf
, scf_walkinfo_t
*wip
)
1459 char *fmri_buf
= safe_malloc(max_scf_fmri_length
+ 1);
1463 if (wip
->pg
== NULL
) {
1464 if (scf_instance_to_fmri(wip
->inst
, fmri_buf
,
1465 max_scf_fmri_length
+ 1) == -1)
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;
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
);
1490 sortkey_fmri(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1494 sprint_fmri(&tmp
, wip
);
1495 bcopy(tmp
, buf
, FMRI_COLUMN_WIDTH
);
1498 reverse_bytes(buf
, FMRI_COLUMN_WIDTH
);
1501 /* Component columns */
1502 #define COMPONENT_COLUMN_WIDTH 20
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)
1515 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1516 COMPONENT_COLUMN_WIDTH
, scope_buf
);
1523 sortkey_scope(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1527 sprint_scope(&tmp
, wip
);
1528 bcopy(tmp
, buf
, COMPONENT_COLUMN_WIDTH
);
1531 reverse_bytes(buf
, COMPONENT_COLUMN_WIDTH
);
1535 sprint_service(char **buf
, scf_walkinfo_t
*wip
)
1537 char *svc_buf
= safe_malloc(max_scf_name_length
+ 1);
1541 if (wip
->pg
== NULL
) {
1542 if (scf_service_get_name(wip
->svc
, svc_buf
,
1543 max_scf_name_length
+ 1) < 0)
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;
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
);
1566 sortkey_service(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1570 sprint_service(&tmp
, wip
);
1571 bcopy(tmp
, buf
, COMPONENT_COLUMN_WIDTH
);
1574 reverse_bytes(buf
, COMPONENT_COLUMN_WIDTH
);
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)
1594 (void) snprintf(newbuf
, newsize
, "%s%-*s ", *buf
? *buf
: "",
1595 COMPONENT_COLUMN_WIDTH
, tmp
);
1602 sortkey_instance(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1606 sprint_instance(&tmp
, wip
);
1607 bcopy(tmp
, buf
, COMPONENT_COLUMN_WIDTH
);
1610 reverse_bytes(buf
, COMPONENT_COLUMN_WIDTH
);
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.
1625 sprint_stime(char **buf
, scf_walkinfo_t
*wip
)
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);
1639 r
= pg_get_single_val(wip
->pg
, SCF_PROPERTY_STATE_TIMESTAMP
,
1640 SCF_TYPE_TIME
, &tv
, 0, 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, "-");
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
);
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
);
1677 #define STIME_SORTKEY_WIDTH (sizeof (uint64_t) + sizeof (uint32_t))
1681 sortkey_stime(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1686 if (wip
->pg
== NULL
)
1687 r
= get_restarter_time_prop(wip
->inst
,
1688 SCF_PROPERTY_STATE_TIMESTAMP
, &tv
, 0);
1690 r
= pg_get_single_val(wip
->pg
, SCF_PROPERTY_STATE_TIMESTAMP
,
1691 SCF_TYPE_TIME
, &tv
, 0, 0);
1697 /* Stick it straight into the buffer. */
1703 bcopy(&sec
, buf
, sizeof (sec
));
1704 bcopy(&us
, buf
+ sizeof (sec
), sizeof (us
));
1706 bzero(buf
, STIME_SORTKEY_WIDTH
);
1710 reverse_bytes(buf
, STIME_SORTKEY_WIDTH
);
1714 #define ZONE_COLUMN_WIDTH 16
1717 sprint_zone(char **buf
, scf_walkinfo_t
*wip
)
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"));
1731 if (strlen(zonename
) > ZONE_COLUMN_WIDTH
)
1732 newsize
= (*buf
? strlen(*buf
) : 0) + strlen(zonename
) + 2;
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
);
1745 sortkey_zone(char *buf
, int reverse
, scf_walkinfo_t
*wip
)
1749 sprint_zone(&tmp
, wip
);
1750 bcopy(tmp
, buf
, ZONE_COLUMN_WIDTH
);
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
,
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.
1796 description_of_column(int c
)
1798 const char *s
= NULL
;
1802 s
= gettext("contract ID for service (see contract(4))");
1805 s
= gettext("human-readable description of the service");
1808 s
= gettext("Fault Managed Resource Identifier for service");
1811 s
= gettext("portion of the FMRI indicating service instance");
1814 s
= gettext("abbreviation for next state (if in transition)");
1817 s
= gettext("abbreviation for next state (if in transition)");
1820 s
= gettext("name for next state (if in transition)");
1823 s
= gettext("abbreviation for current state");
1826 s
= gettext("name for scope associated with service");
1829 s
= gettext("abbreviation for current state and next state");
1832 s
= gettext("portion of the FMRI representing service name");
1835 s
= gettext("abbreviation for current state");
1838 s
= gettext("name for current state");
1841 s
= gettext("time of last state change");
1844 s
= gettext("name of zone");
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
);
1866 exit(UU_EXIT_USAGE
);
1869 #define argserr(progname) print_usage(progname, stderr, B_TRUE)
1872 print_help(const char *progname
)
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"
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"
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"
1908 "Columns for output or sorting can be specified using these names:\n"
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.
1924 getcolumnopt(char **optionp
)
1926 char *str
= *optionp
, *cp
;
1929 assert(optionp
!= NULL
);
1930 assert(*optionp
!= NULL
);
1932 cp
= strchr(*optionp
, ',');
1936 for (i
= 0; i
< ncolumns
; ++i
) {
1937 if (strcasecmp(str
, columns
[i
].name
) == 0) {
1941 *optionp
= strchr(*optionp
, '\0');
1954 char *line_buf
, *cp
;
1956 line_buf
= safe_malloc(line_sz
);
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
,
1967 /* Trim the trailing whitespace */
1972 (void) puts(line_buf
);
1980 * Long listing (-l) functions.
1984 pidcmp(const void *l
, const void *r
)
1986 pid_t lp
= *(pid_t
*)l
, rp
= *(pid_t
*)r
;
1996 * This is the strlen() of the longest label ("description"), plus intercolumn
1999 #define DETAILED_WIDTH (11 + 2)
2002 * Callback routine to print header for contract id.
2003 * Called by ctids_by_restarter and print_detailed.
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.
2016 print_ctid_detailed(uint64_t c
)
2018 (void) printf("%lu ", (ctid_t
)c
);
2022 detailed_list_processes(scf_walkinfo_t
*wip
)
2029 if (get_restarter_count_prop(wip
->inst
, scf_property_contract
, &c
,
2033 if (instance_processes(wip
->inst
, wip
->fmri
, &pids
, &n
) != 0)
2036 qsort(pids
, n
, sizeof (*pids
), pidcmp
);
2038 for (i
= 0; i
< n
; ++i
) {
2039 (void) printf("%-*s%lu", DETAILED_WIDTH
, gettext("process"),
2042 if (get_psinfo(pids
[i
], &psi
) == 0)
2043 (void) printf(" %.*s", PRARGSZ
, psi
.pr_psargs
);
2045 (void) putchar('\n');
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.
2056 get_fmri_state(char *fmri
, char *state
, size_t state_sz
)
2059 const char *svc_name
, *inst_name
, *pg_name
, *path
;
2061 scf_instance_t
*inst
;
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
;
2073 if (stat64(path
, &statbuf
) == 0)
2075 else if (errno
== ENOENT
)
2080 (void) strlcpy(state
, msg
, state_sz
);
2085 * scf_parse_file_fmri() may have overwritten part of the string, so
2088 (void) strcpy(lfmri
, fmri
);
2090 if (scf_parse_svc_fmri(lfmri
, NULL
, &svc_name
, &inst_name
,
2091 &pg_name
, NULL
) != SCF_SUCCESS
) {
2093 (void) strlcpy(state
, "invalid", state_sz
);
2099 if (svc_name
== NULL
|| pg_name
!= NULL
) {
2100 (void) strlcpy(state
, "invalid", state_sz
);
2104 if (inst_name
!= NULL
) {
2105 /* instance: get state */
2106 inst
= scf_instance_create(h
);
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
,
2115 switch (scf_error()) {
2116 case SCF_ERROR_INVALID_ARGUMENT
:
2117 (void) strlcpy(state
, "invalid", state_sz
);
2119 case SCF_ERROR_NOT_FOUND
:
2120 (void) strlcpy(state
, "absent", state_sz
);
2128 scf_instance_destroy(inst
);
2133 * service: If only one instance, use that state. Otherwise, say
2136 if ((svc
= scf_service_create(h
)) == NULL
||
2137 (inst
= scf_instance_create(h
)) == NULL
||
2138 (iter
= scf_iter_create(h
)) == NULL
)
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
);
2147 case SCF_ERROR_NOT_FOUND
:
2148 (void) strlcpy(state
, "absent", state_sz
);
2156 if (scf_iter_service_instances(iter
, svc
) != SCF_SUCCESS
)
2159 switch (scf_iter_next_instance(iter
, inst
)) {
2161 (void) strlcpy(state
, "absent", state_sz
);
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
)) {
2179 /* Nope, multiple instances. */
2180 (void) strlcpy(state
, "multiple", state_sz
);
2188 scf_iter_destroy(iter
);
2189 scf_instance_destroy(inst
);
2190 scf_service_destroy(svc
);
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
;
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);
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
)
2221 if (scf_iter_instance_pgs_typed_composed(pg_iter
, wip
->inst
, snap
,
2222 SCF_PG_APP_DEFAULT
) == -1)
2226 * Format for output:
2229 * pg/prop (proptype) = <value> <value>
2232 while ((i
= scf_iter_next_pg(pg_iter
, pg
)) == 1) {
2235 if (scf_pg_get_name(pg
, pg_name_buf
, max_scf_name_length
) < 0)
2237 if (scf_snapshot_get_name(snap
, snap_name
,
2238 max_scf_name_length
) < 0)
2241 if (scf_tmpl_get_by_pg_name(wip
->fmri
, snap_name
, pg_name_buf
,
2242 SCF_PG_APP_DEFAULT
, pt
, 0) == 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
);
2254 if (scf_iter_pg_properties(prop_iter
, pg
) == -1)
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)
2260 if (scf_property_type(prop
, &type
) == -1)
2264 (scf_tmpl_get_by_prop(pt
, prop_name_buf
, prt
,
2269 scf_tmpl_prop_visibility(prt
, &vis
) != -1 &&
2270 vis
== SCF_TMPL_VISIBILITY_HIDDEN
)
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)
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)
2283 if (strpbrk(val_buf
, " \t\n\"()") != NULL
) {
2284 (void) printf("\"");
2285 for (cp
= val_buf
; *cp
!= '\0'; ++cp
) {
2286 if (*cp
== '"' || *cp
== '\\')
2290 (void) putc(*cp
, stdout
);
2292 (void) printf("\"");
2294 (void) printf("%s ", val_buf
);
2298 (void) printf("\n");
2303 if (tmpl
== 1 && scf_tmpl_prop_description(prt
, NULL
,
2305 (void) printf(" %s\n", desc
);
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
);
2325 free(prop_name_buf
);
2331 print_detailed_dependency(scf_propertygroup_t
*pg
)
2333 scf_property_t
*eprop
;
2339 if ((eprop
= scf_property_create(h
)) == NULL
||
2340 (iter
= scf_iter_create(h
)) == NULL
)
2343 val_buf
= safe_malloc(max_scf_value_length
+ 1);
2345 if (scf_pg_get_property(pg
, SCF_PROPERTY_ENTITIES
, eprop
) !=
2347 scf_property_type(eprop
, &ty
) != SCF_SUCCESS
||
2348 ty
!= SCF_TYPE_FMRI
)
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
);
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
);
2366 (void) putchar('?');
2368 /* Print the dependency entities. */
2369 if (scf_iter_property_values(iter
, eprop
) == -1)
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)
2379 (void) putchar(' ');
2380 (void) fputs(val_buf
, stdout
);
2382 /* Print the state. */
2386 get_fmri_state(val_buf
, state
, sizeof (state
));
2388 (void) printf(" (%s)", state
);
2393 (void) putchar('\n');
2396 scf_iter_destroy(iter
);
2397 scf_property_destroy(eprop
);
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
;
2420 const char * const fmt
= "%-*s%s\n";
2422 assert(wip
->pg
== NULL
);
2424 rpg
= scf_pg_create(h
);
2428 if (first_paragraph
)
2429 first_paragraph
= 0;
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)
2444 (void) printf(fmt
, DETAILED_WIDTH
, gettext("name"),
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)
2449 (void) printf(fmt
, DETAILED_WIDTH
, gettext("name"),
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
);
2465 (void) printf(gettext("%-*s%s (temporary)\n"),
2466 DETAILED_WIDTH
, gettext("enabled"),
2467 temp
? gettext("true") : gettext("false"));
2469 (void) printf(fmt
, DETAILED_WIDTH
,
2470 gettext("enabled"), temp
? gettext("true") :
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
)
2486 scf_pg_destroy(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"),
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) {
2504 tmp
= localtime(&stime
);
2505 for (tbsz
= 50; ; tbsz
*= 2) {
2506 timebuf
= safe_malloc(tbsz
);
2507 if (strftime(timebuf
, tbsz
, NULL
, tmp
) != 0)
2511 (void) printf(fmt
, DETAILED_WIDTH
,
2512 gettext("state_time"),
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"),
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
);
2533 (void) printf(fmt
, DETAILED_WIDTH
, gettext("restarter"),
2534 SCF_SERVICE_STARTD
);
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"),
2548 goto restarter_common
;
2554 if ((iter
= scf_iter_create(h
)) == NULL
)
2557 if (scf_pg_get_property(rpg
, scf_property_contract
, g_prop
) ==
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)
2568 ret
= scf_iter_next_value(iter
, g_val
);
2574 if (scf_value_get_count(g_val
, &c
) != 0)
2577 /* Callback to print contract id. */
2578 print_ctid_detailed(c
);
2581 (void) putchar('\n');
2583 if (scf_error() != SCF_ERROR_TYPE_MISMATCH
)
2587 if (scf_error() != SCF_ERROR_NOT_FOUND
)
2591 scf_iter_destroy(iter
);
2593 if (scf_error() != SCF_ERROR_NOT_FOUND
)
2598 scf_pg_destroy(rpg
);
2601 if ((pg_iter
= scf_iter_create(h
)) == NULL
)
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
)
2610 while ((ret
= scf_iter_next_pg(pg_iter
, g_pg
)) == 1)
2611 print_detailed_dependency(g_pg
);
2615 scf_iter_destroy(pg_iter
);
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
);
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
)
2639 if (scf_instance_get_pg(wip
->inst
, SCF_PG_RESTARTER
, rpg
) != 0) {
2640 if (scf_error() != SCF_ERROR_NOT_FOUND
)
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
);
2652 scf_pg_destroy(rpg
);
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
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
);
2678 size_t sz
= scf_limit(SCF_LIMIT_MAX_NAME_LENGTH
) + 1;
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) {
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
)
2715 if (has_fma_tag(pgname
))
2717 if (!is_fma_token(pgname
))
2719 * We don't emmit a warning here so that we don't
2720 * pollute the output
2724 if (n
+ 1 >= size
) {
2726 buf
= reallocarray(buf
, size
, sizeof (char *));
2728 uu_die(gettext("Out of memory.\n"));
2730 buf
[n
] = safe_strdup(pgname
);
2734 * NULL terminate buf
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
);
2745 scf_iter_destroy(iter
);
2747 scf_snaplevel_destroy(slvl
);
2748 scf_snapshot_destroy(snap
);
2749 scf_instance_destroy(inst
);
2750 scf_handle_destroy(h
);
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
)
2771 "Failed get_fma_notify_params %s\n"),
2772 scf_strerror(scf_error()));
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
2787 print_notify_fma(void)
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
)
2807 for (p
= *tmp
; p
; ++tmp
, p
= *tmp
) {
2808 if (get_fma_notify_params(nvl
, p
) == 0)
2809 listnotify_print(nvl
, re_tag(p
));
2821 * print_notify_fmri()
2822 * prints notifycation parameters for an SMF instance.
2825 print_notify_fmri(const char *fmri
)
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) !=
2834 if (scf_error() != SCF_ERROR_NOT_FOUND
&&
2835 scf_error() != SCF_ERROR_DELETED
)
2837 "Failed _scf_get_svc_notify_params: %s\n"),
2838 scf_strerror(scf_error()));
2840 if (strcmp(SCF_INSTANCE_GLOBAL
, fmri
) == 0)
2842 gettext("System wide notification parameters:\n"));
2843 safe_printf("%s:\n", fmri
);
2844 listnotify_print(nvl
, NULL
);
2850 * print_notify_special()
2851 * prints notification parameters for FMA events and system wide SMF state
2852 * transitions parameters
2855 print_notify_special()
2857 safe_printf("Notification parameters for FMA Events\n");
2859 print_notify_fmri(SCF_INSTANCE_GLOBAL
);
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()
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)
2876 print_notify_fmri(wip
->fmri
);
2882 * Append a one-lined description of each process in inst's contract(s) and
2883 * return the augmented string.
2886 add_processes(scf_walkinfo_t
*wip
, char *line
, scf_propertygroup_t
*lpg
)
2892 if (instance_processes(wip
->inst
, wip
->fmri
, &pids
, &n
) != 0)
2895 /* Legacy services */
2898 if ((iter
= scf_iter_create(h
)) == NULL
)
2901 (void) propvals_to_pids(lpg
, scf_property_contract
, &pids
, &n
,
2902 g_prop
, g_val
, iter
);
2904 scf_iter_destroy(iter
);
2910 qsort(pids
, n
, sizeof (*pids
), pidcmp
);
2912 for (i
= 0; i
< n
; ++i
) {
2916 int len
= 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ
;
2918 if (get_psinfo(pids
[i
], &psi
) != 0)
2921 line
= realloc(line
, strlen(line
) + len
);
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
);
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
);
2955 list_instance(void *unused
, scf_walkinfo_t
*wip
)
2957 struct avl_string
*lp
;
2963 * If the user has specified a restarter, check for a match first
2965 if (restarters
!= NULL
) {
2966 struct pfmri_list
*rest
;
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
)
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
);
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)
2996 free(restarter_fmri
);
3002 if (wip
->pg
== NULL
&& ht_buckets
!= NULL
&& ht_add(wip
->fmri
)) {
3003 /* It was already there. */
3007 lp
= safe_malloc(sizeof (*lp
));
3010 for (i
= 0; i
< opt_cnum
; ++i
) {
3011 columns
[opt_columns
[i
]].sprint(&lp
->str
, wip
);
3013 cp
= lp
->str
+ strlen(lp
->str
);
3019 /* If we're supposed to list the processes, too, do that now. */
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
);
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
));
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.
3060 list_svc_or_inst_fmri(void *complain
, scf_walkinfo_t
*wip
)
3063 const char *svc_name
, *inst_name
, *pg_name
, *save
;
3067 fmri
= safe_strdup(wip
->fmri
);
3069 if (scf_parse_svc_fmri(fmri
, NULL
, &svc_name
, &inst_name
, &pg_name
,
3070 NULL
) != SCF_SUCCESS
) {
3072 uu_warn(gettext("FMRI \"%s\" is invalid.\n"),
3074 exit_status
= UU_EXIT_FATAL
;
3080 * Yes, this invalidates *_name, but we only care whether they're NULL
3085 if (svc_name
== NULL
|| pg_name
!= NULL
) {
3087 uu_warn(gettext("FMRI \"%s\" does not designate a "
3088 "service or instance.\n"), wip
->fmri
);
3092 if (inst_name
!= NULL
) {
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
)
3101 "Instance \"%s\" does not exist.\n"),
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
)
3116 uu_warn(gettext("Service \"%s\" does not exist.\n"),
3119 exit_status
= UU_EXIT_FATAL
;
3124 iter
= scf_iter_create(h
);
3128 if (scf_iter_service_instances(iter
, wip
->svc
) != SCF_SUCCESS
)
3131 if ((fmri
= malloc(max_scf_fmri_length
+ 1)) == NULL
) {
3132 scf_iter_destroy(iter
);
3133 exit_status
= UU_EXIT_FATAL
;
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)
3143 (void) list_instance(NULL
, wip
);
3150 exit_status
= UU_EXIT_OK
;
3152 scf_iter_destroy(iter
);
3158 * Dependency selection: Straightforward since each instance lists the
3159 * services it depends on.
3163 walk_dependencies(scf_walkinfo_t
*wip
, scf_walk_callback callback
, void *data
)
3165 scf_snapshot_t
*snap
;
3166 scf_iter_t
*iter
, *viter
;
3170 assert(wip
->inst
!= NULL
);
3172 if ((iter
= scf_iter_create(h
)) == NULL
||
3173 (viter
= scf_iter_create(h
)) == NULL
)
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
)
3182 dep
= safe_malloc(max_scf_value_length
+ 1);
3184 while ((ret
= scf_iter_next_pg(iter
, g_pg
)) == 1) {
3187 /* Ignore exclude_any dependencies. */
3188 if (scf_pg_get_property(g_pg
, SCF_PROPERTY_GROUPING
, g_prop
) !=
3190 if (scf_error() != SCF_ERROR_NOT_FOUND
)
3196 if (scf_property_type(g_prop
, &ty
) != SCF_SUCCESS
)
3199 if (ty
!= SCF_TYPE_ASTRING
)
3202 if (scf_property_get_value(g_prop
, g_val
) != SCF_SUCCESS
) {
3203 if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED
)
3209 if (scf_value_get_astring(g_val
, dep
,
3210 max_scf_value_length
+ 1) < 0)
3213 if (strcmp(dep
, SCF_DEP_EXCLUDE_ALL
) == 0)
3216 if (scf_pg_get_property(g_pg
, SCF_PROPERTY_ENTITIES
, g_prop
) !=
3218 if (scf_error() != SCF_ERROR_NOT_FOUND
)
3224 if (scf_iter_property_values(viter
, g_prop
) != SCF_SUCCESS
)
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)
3233 if (callback(data
, wip
) != 0)
3243 scf_iter_destroy(viter
);
3244 scf_iter_destroy(iter
);
3245 scf_snapshot_destroy(snap
);
3249 list_dependencies(void *data
, scf_walkinfo_t
*wip
)
3251 walk_dependencies(wip
, list_svc_or_inst_fmri
, data
);
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 */
3269 check_against_provider(void *arg
, scf_walkinfo_t
*wip
)
3272 const char *scope_name
, *svc_name
, *inst_name
, *pg_name
;
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
) {
3283 if (svc_name
== NULL
|| pg_name
!= NULL
) {
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)));
3299 /* Stop on matches. */
3304 list_if_dependent(void *unused
, scf_walkinfo_t
*wip
)
3306 /* Only proceed if this instance depends on provider_*. */
3309 (void) walk_dependencies(wip
, check_against_provider
, &match
);
3312 return (list_instance(unused
, wip
));
3319 list_dependents(void *unused
, scf_walkinfo_t
*wip
)
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)
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)
3337 ret
= scf_walk_fmri(h
, 0, NULL
, 0, list_if_dependent
, NULL
, NULL
,
3340 provider_inst
= save
;
3350 add_sort_column(const char *col
, int reverse
)
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)
3366 opt_sort
[opt_snum
- 1] = (reverse
? i
| 0x100 : i
);
3368 uu_die(gettext("Unrecognized sort column \"%s\".\n"), col
);
3370 sortkey_sz
+= columns
[i
].sortkey_width
;
3374 add_restarter(const char *fmri
)
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
;
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
));
3412 print_line(void *e
, void *private)
3414 struct avl_string
*lp
= e
;
3416 (void) puts(lp
->str
);
3418 return (UU_WALK_NEXT
);
3423 errignore(const char *str
, ...)
3427 main(int argc
, char **argv
)
3431 char *columns_str
= NULL
;
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 *, ...);
3441 int show_header
= 1;
3444 const char * const options
= "aHpvno:R:s:S:dDlL?xZz:";
3446 (void) setlocale(LC_ALL
, "");
3448 locale
= setlocale(LC_MESSAGES
, NULL
);
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)
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.
3477 while ((opt
= getopt(argc
, argv
, options
)) != -1) {
3480 if (optopt
== '?') {
3481 print_help(progname
);
3482 return (UU_EXIT_OK
);
3519 optind
= 1; /* Reset getopt() */
3520 while ((opt
= getopt(argc
, argv
, options
)) != -1) {
3529 if (opt_mode
== 'l' || opt_mode
== 'x')
3535 if (opt_mode
== 'x')
3545 if (opt_mode
== 'l' || opt_mode
== 'x')
3547 columns_str
= optarg
;
3551 if (opt_mode
!= 0 || opt_mode
== 'x')
3554 add_restarter(optarg
);
3562 add_sort_column(optarg
, optopt
== 'S');
3571 assert(opt_mode
== optopt
);
3575 if (getzoneid() != GLOBAL_ZONEID
)
3576 uu_die(gettext("svcs -z may only be used from "
3577 "the global zone\n"));
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
)
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
) {
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
);
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
)
3641 h
= scf_handle_create(SCF_VERSION
);
3645 if (opt_zone
!= NULL
|| zids
!= NULL
) {
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]);
3658 g_zonename
= zonename
;
3660 g_zonename
= opt_zone
;
3663 if ((zone
= scf_value_create(h
)) == NULL
)
3666 if (scf_value_set_astring(zone
, g_zonename
) != SCF_SUCCESS
)
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()));
3682 return (UU_EXIT_FATAL
);
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
)
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
;
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') {
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
;
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
;
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
;
3764 if (opt_mode
== 'x') {
3765 explain(opt_verbose
, argc
, argv
);
3769 if (columns_str
== NULL
) {
3770 if (opt_snum
== 0) {
3772 add_sort_column("zone", 0);
3775 add_sort_column("state", 0);
3776 add_sort_column("stime", 0);
3777 add_sort_column("fmri", 0);
3781 columns_str
= safe_strdup(show_zones
?
3782 "zone,state,stime,fmri" : "state,stime,fmri");
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. */
3795 for (cp
= columns_str
; *cp
!= '\0'; ++cp
)
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
);
3809 uu_die(gettext("Unknown column \"%s\".\n"),
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;
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()));
3833 * If we already have a hash table (e.g., because we are
3834 * processing multiple zones), destroy it before creating
3837 if (ht_buckets
!= NULL
)
3842 /* Always show all FMRIs when given arguments or restarters */
3843 if (argc
!= 0 || restarters
!= NULL
)
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
;
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
;
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
);
3887 free(provider_inst
);
3899 if (show_zones
&& zent
< nzents
&& exit_status
== 0) {
3900 scf_handle_destroy(h
);
3904 if (show_zones
&& exit_status
== 0)
3905 exit_status
= missing
;
3907 if (opt_columns
== NULL
)
3908 return (exit_status
);
3913 (void) uu_avl_walk(lines
, print_line
, NULL
, 0);
3915 return (exit_status
);