svcs.c revision 81ff72c5f2cea16235aa4a42d83d07e20090498d
17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
594501b6skamm * Common Development and Distribution License (the "License").
694501b6skamm * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
203eae19dwesolows */
213eae19dwesolows
223eae19dwesolows/*
23f6e214cGavin Maltby * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
2481ff72cJason King * Copyright 2019 Joyent, Inc.
2548bbca8Daniel Hoffman * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
267c478bdstevel@tonic-gate */
277c478bdstevel@tonic-gate
287c478bdstevel@tonic-gate/*
297c478bdstevel@tonic-gate * svcs - display attributes of service instances
307c478bdstevel@tonic-gate *
317c478bdstevel@tonic-gate * We have two output formats and six instance selection mechanisms.  The
327c478bdstevel@tonic-gate * primary output format is a line of attributes (selected by -o), possibly
337c478bdstevel@tonic-gate * followed by process description lines (if -p is specified), for each
347c478bdstevel@tonic-gate * instance selected.  The columns available to display are described by the
357c478bdstevel@tonic-gate * struct column columns array.  The columns to actually display are kept in
367c478bdstevel@tonic-gate * the opt_columns array as indicies into the columns array.  The selection
377c478bdstevel@tonic-gate * mechanisms available for this format are service FMRIs (selects all child
387c478bdstevel@tonic-gate * instances), instance FMRIs, instance FMRI glob patterns, instances with
397c478bdstevel@tonic-gate * a certain restarter (-R), dependencies of instances (-d), and dependents of
407c478bdstevel@tonic-gate * instances (-D).  Since the lines must be sorted (per -sS), we'll just stick
417c478bdstevel@tonic-gate * each into a data structure and print them in order when we're done.  To
427c478bdstevel@tonic-gate * avoid listing the same instance twice (when -d and -D aren't given), we'll
437c478bdstevel@tonic-gate * use a hash table of FMRIs to record that we've listed (added to the tree)
447c478bdstevel@tonic-gate * an instance.
457c478bdstevel@tonic-gate *
467c478bdstevel@tonic-gate * The secondary output format (-l "long") is a paragraph of text for the
477c478bdstevel@tonic-gate * services or instances selected.  Not needing to be sorted, it's implemented
487c478bdstevel@tonic-gate * by just calling print_detailed() for each FMRI given.
497c478bdstevel@tonic-gate */
507c478bdstevel@tonic-gate
517c478bdstevel@tonic-gate#include "svcs.h"
52f6e214cGavin Maltby#include "notify_params.h"
537c478bdstevel@tonic-gate
547c478bdstevel@tonic-gate/* Get the byteorder macros to ease sorting. */
557c478bdstevel@tonic-gate#include <sys/types.h>
567c478bdstevel@tonic-gate#include <netinet/in.h>
577c478bdstevel@tonic-gate#include <inttypes.h>
587c478bdstevel@tonic-gate
597c478bdstevel@tonic-gate#include <sys/contract.h>
607c478bdstevel@tonic-gate#include <sys/ctfs.h>
617c478bdstevel@tonic-gate#include <sys/stat.h>
627c478bdstevel@tonic-gate
637c478bdstevel@tonic-gate#include <assert.h>
647c478bdstevel@tonic-gate#include <errno.h>
657c478bdstevel@tonic-gate#include <fcntl.h>
667c478bdstevel@tonic-gate#include <fnmatch.h>
677c478bdstevel@tonic-gate#include <libcontract.h>
687c478bdstevel@tonic-gate#include <libcontract_priv.h>
697c478bdstevel@tonic-gate#include <libintl.h>
707c478bdstevel@tonic-gate#include <libscf.h>
717c478bdstevel@tonic-gate#include <libscf_priv.h>
727c478bdstevel@tonic-gate#include <libuutil.h>
73f6e214cGavin Maltby#include <libnvpair.h>
7481ff72cJason King#include <libproc.h>
757c478bdstevel@tonic-gate#include <locale.h>
767c478bdstevel@tonic-gate#include <stdarg.h>
777c478bdstevel@tonic-gate#include <stdio.h>
787c478bdstevel@tonic-gate#include <stdlib.h>
797c478bdstevel@tonic-gate#include <strings.h>
807c478bdstevel@tonic-gate#include <time.h>
81048b027Bryan Cantrill#include <libzonecfg.h>
82048b027Bryan Cantrill#include <zone.h>
837c478bdstevel@tonic-gate
847c478bdstevel@tonic-gate#ifndef TEXT_DOMAIN
857c478bdstevel@tonic-gate#define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
867c478bdstevel@tonic-gate#endif /* TEXT_DOMAIN */
877c478bdstevel@tonic-gate
887c478bdstevel@tonic-gate#define	LEGACY_UNKNOWN	"unknown"
897c478bdstevel@tonic-gate
907c478bdstevel@tonic-gate/* Flags for pg_get_single_val() */
917c478bdstevel@tonic-gate#define	EMPTY_OK	0x01
927c478bdstevel@tonic-gate#define	MULTI_OK	0x02
937c478bdstevel@tonic-gate
9481ff72cJason King/*
9581ff72cJason King * Per proc(4) when pr_nlwp, pr_nzomb, and pr_lwp.pr_lwpid are all 0,
9681ff72cJason King * the process is a zombie.
9781ff72cJason King */
9881ff72cJason King#define	IS_ZOMBIE(_psip) \
9981ff72cJason King	((_psip)->pr_nlwp == 0 && (_psip)->pr_nzomb == 0 && \
10081ff72cJason King	(_psip)->pr_lwp.pr_lwpid == 0)
1017c478bdstevel@tonic-gate
1027c478bdstevel@tonic-gate/*
1037c478bdstevel@tonic-gate * An AVL-storable node for output lines and the keys to sort them by.
1047c478bdstevel@tonic-gate */
1057c478bdstevel@tonic-gatestruct avl_string {
1067c478bdstevel@tonic-gate	uu_avl_node_t	node;
1077c478bdstevel@tonic-gate	char		*key;
1087c478bdstevel@tonic-gate	char		*str;
1097c478bdstevel@tonic-gate};
1107c478bdstevel@tonic-gate
1117c478bdstevel@tonic-gate/*
1127c478bdstevel@tonic-gate * For lists of parsed restarter FMRIs.
1137c478bdstevel@tonic-gate */
1147c478bdstevel@tonic-gatestruct pfmri_list {
1157c478bdstevel@tonic-gate	const char		*scope;
1167c478bdstevel@tonic-gate	const char		*service;
1177c478bdstevel@tonic-gate	const char		*instance;
1187c478bdstevel@tonic-gate	struct pfmri_list	*next;
1197c478bdstevel@tonic-gate};
1207c478bdstevel@tonic-gate
1217c478bdstevel@tonic-gate
1227c478bdstevel@tonic-gate/*
1237c478bdstevel@tonic-gate * Globals
1247c478bdstevel@tonic-gate */
1257c478bdstevel@tonic-gatescf_handle_t *h;
1267c478bdstevel@tonic-gatestatic scf_propertygroup_t *g_pg;
1277c478bdstevel@tonic-gatestatic scf_property_t *g_prop;
1287c478bdstevel@tonic-gatestatic scf_value_t *g_val;
1297c478bdstevel@tonic-gate
1307c478bdstevel@tonic-gatestatic size_t line_sz;			/* Bytes in the header line. */
1317c478bdstevel@tonic-gatestatic size_t sortkey_sz;		/* Bytes in sort keys. */
1327c478bdstevel@tonic-gatestatic uu_avl_pool_t *lines_pool;
1337c478bdstevel@tonic-gatestatic uu_avl_t *lines;			/* Output lines. */
1347c478bdstevel@tonic-gateint exit_status;
1357c478bdstevel@tonic-gatessize_t max_scf_name_length;
1367c478bdstevel@tonic-gatessize_t max_scf_value_length;
1377c478bdstevel@tonic-gatessize_t max_scf_fmri_length;
1381f6eb02Liane Prazastatic ssize_t max_scf_type_length;
1397c478bdstevel@tonic-gatestatic time_t now;
1407c478bdstevel@tonic-gatestatic struct pfmri_list *restarters = NULL;
1417c478bdstevel@tonic-gatestatic int first_paragraph = 1;		/* For -l mode. */
1427c478bdstevel@tonic-gatestatic char *common_name_buf;		/* Sized for maximal length value. */
1437c478bdstevel@tonic-gatechar *locale;				/* Current locale. */
144048b027Bryan Cantrillchar *g_zonename;			/* zone being operated upon */
1457c478bdstevel@tonic-gate
14694501b6skamm/*
14794501b6skamm * Pathname storage for path generated from the fmri.
14894501b6skamm * Used for reading the ctid and (start) pid files for an inetd service.
14994501b6skamm */
15094501b6skammstatic char genfmri_filename[MAXPATHLEN] = "";
15194501b6skamm
1527c478bdstevel@tonic-gate/* Options */
1537c478bdstevel@tonic-gatestatic int *opt_columns = NULL;		/* Indices into columns to display. */
1547c478bdstevel@tonic-gatestatic int opt_cnum = 0;
1557c478bdstevel@tonic-gatestatic int opt_processes = 0;		/* Print processes? */
1567c478bdstevel@tonic-gatestatic int *opt_sort = NULL;		/* Indices into columns to sort. */
1577c478bdstevel@tonic-gatestatic int opt_snum = 0;
1587c478bdstevel@tonic-gatestatic int opt_nstate_shown = 0;	/* Will nstate be shown? */
1597c478bdstevel@tonic-gatestatic int opt_verbose = 0;
160048b027Bryan Cantrillstatic char *opt_zone;			/* zone selected, if any */
1617c478bdstevel@tonic-gate
1627c478bdstevel@tonic-gate/* Minimize string constants. */
1637c478bdstevel@tonic-gatestatic const char * const scf_property_state = SCF_PROPERTY_STATE;
1647c478bdstevel@tonic-gatestatic const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE;
1657c478bdstevel@tonic-gatestatic const char * const scf_property_contract = SCF_PROPERTY_CONTRACT;
1667c478bdstevel@tonic-gate
1677c478bdstevel@tonic-gate
1687c478bdstevel@tonic-gate/*
1697c478bdstevel@tonic-gate * Utility functions
1707c478bdstevel@tonic-gate */
1717c478bdstevel@tonic-gate
1727c478bdstevel@tonic-gate/*
1737c478bdstevel@tonic-gate * For unexpected libscf errors.  The ending newline is necessary to keep
1747c478bdstevel@tonic-gate * uu_die() from appending the errno error.
1757c478bdstevel@tonic-gate */
1767c478bdstevel@tonic-gate#ifndef NDEBUG
1777c478bdstevel@tonic-gatevoid
1787c478bdstevel@tonic-gatedo_scfdie(const char *file, int line)
1797c478bdstevel@tonic-gate{
1807c478bdstevel@tonic-gate	uu_die(gettext("%s:%d: Unexpected libscf error: %s.  Exiting.\n"),
1817c478bdstevel@tonic-gate	    file, line, scf_strerror(scf_error()));
1827c478bdstevel@tonic-gate}
1837c478bdstevel@tonic-gate#else
1847c478bdstevel@tonic-gatevoid
1857c478bdstevel@tonic-gatescfdie(void)
1867c478bdstevel@tonic-gate{
1877c478bdstevel@tonic-gate	uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
1887c478bdstevel@tonic-gate	    scf_strerror(scf_error()));
1897c478bdstevel@tonic-gate}
1907c478bdstevel@tonic-gate#endif
1917c478bdstevel@tonic-gate
1927c478bdstevel@tonic-gatevoid *
1937c478bdstevel@tonic-gatesafe_malloc(size_t sz)
1947c478bdstevel@tonic-gate{
1957c478bdstevel@tonic-gate	void *ptr;
1967c478bdstevel@tonic-gate
1977c478bdstevel@tonic-gate	ptr = malloc(sz);
1987c478bdstevel@tonic-gate	if (ptr == NULL)
1997c478bdstevel@tonic-gate		uu_die(gettext("Out of memory"));
2007c478bdstevel@tonic-gate
2017c478bdstevel@tonic-gate	return (ptr);
2027c478bdstevel@tonic-gate}
2037c478bdstevel@tonic-gate
2047c478bdstevel@tonic-gatechar *
2057c478bdstevel@tonic-gatesafe_strdup(const char *str)
2067c478bdstevel@tonic-gate{
2077c478bdstevel@tonic-gate	char *cp;
2087c478bdstevel@tonic-gate
2097c478bdstevel@tonic-gate	cp = strdup(str);
2107c478bdstevel@tonic-gate	if (cp == NULL)
2117c478bdstevel@tonic-gate		uu_die(gettext("Out of memory.\n"));
2127c478bdstevel@tonic-gate
2137c478bdstevel@tonic-gate	return (cp);
2147c478bdstevel@tonic-gate}
2157c478bdstevel@tonic-gate
2167c478bdstevel@tonic-gate/*
2177c478bdstevel@tonic-gate * FMRI hashtable.  For uniquifing listings.
2187c478bdstevel@tonic-gate */
2197c478bdstevel@tonic-gate
2207c478bdstevel@tonic-gatestruct ht_elem {
2217c478bdstevel@tonic-gate	const char	*fmri;
2227c478bdstevel@tonic-gate	struct ht_elem	*next;
2237c478bdstevel@tonic-gate};
2247c478bdstevel@tonic-gate
225048b027Bryan Cantrillstatic struct ht_elem	**ht_buckets = NULL;
226048b027Bryan Cantrillstatic uint_t		ht_buckets_num = 0;
2277c478bdstevel@tonic-gatestatic uint_t		ht_num;
2287c478bdstevel@tonic-gate
2297c478bdstevel@tonic-gatestatic void
230048b027Bryan Cantrillht_free(void)
231048b027Bryan Cantrill{
232048b027Bryan Cantrill	struct ht_elem *elem, *next;
233048b027Bryan Cantrill	int i;
234048b027Bryan Cantrill
235048b027Bryan Cantrill	for (i = 0; i < ht_buckets_num; i++) {
236048b027Bryan Cantrill		for (elem = ht_buckets[i]; elem != NULL; elem = next) {
237048b027Bryan Cantrill			next = elem->next;
238048b027Bryan Cantrill			free((char *)elem->fmri);
239048b027Bryan Cantrill			free(elem);
240048b027Bryan Cantrill		}
241048b027Bryan Cantrill	}
242048b027Bryan Cantrill
243048b027Bryan Cantrill	free(ht_buckets);
244048b027Bryan Cantrill	ht_buckets_num = 0;
245048b027Bryan Cantrill	ht_buckets = NULL;
246048b027Bryan Cantrill}
247048b027Bryan Cantrill
248048b027Bryan Cantrillstatic void
249048b027Bryan Cantrillht_init(void)
2507c478bdstevel@tonic-gate{
251048b027Bryan Cantrill	assert(ht_buckets == NULL);
252048b027Bryan Cantrill
2537c478bdstevel@tonic-gate	ht_buckets_num = 8;
2547c478bdstevel@tonic-gate	ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num);
2557c478bdstevel@tonic-gate	bzero(ht_buckets, sizeof (*ht_buckets) * ht_buckets_num);
2567c478bdstevel@tonic-gate	ht_num = 0;
2577c478bdstevel@tonic-gate}
2587c478bdstevel@tonic-gate
2597c478bdstevel@tonic-gatestatic uint_t
2607c478bdstevel@tonic-gateht_hash_fmri(const char *fmri)
2617c478bdstevel@tonic-gate{
2627c478bdstevel@tonic-gate	uint_t h = 0, g;
2637c478bdstevel@tonic-gate	const char *p, *k;
2647c478bdstevel@tonic-gate
2657c478bdstevel@tonic-gate	/* All FMRIs begin with svc:/, so skip that part. */
2667c478bdstevel@tonic-gate	assert(strncmp(fmri, "svc:/", sizeof ("svc:/") - 1) == 0);
2677c478bdstevel@tonic-gate	k = fmri + sizeof ("svc:/") - 1;
2687c478bdstevel@tonic-gate
2697c478bdstevel@tonic-gate	/*
2707c478bdstevel@tonic-gate	 * Generic hash function from uts/common/os/modhash.c.
2717c478bdstevel@tonic-gate	 */
2727c478bdstevel@tonic-gate	for (p = k; *p != '\0'; ++p) {
2737c478bdstevel@tonic-gate		h = (h << 4) + *p;
2747c478bdstevel@tonic-gate		if ((g = (h & 0xf0000000)) != 0) {
2757c478bdstevel@tonic-gate			h ^= (g >> 24);
2767c478bdstevel@tonic-gate			h ^= g;
2777c478bdstevel@tonic-gate		}
2787c478bdstevel@tonic-gate	}
2797c478bdstevel@tonic-gate
2807c478bdstevel@tonic-gate	return (h);
2817c478bdstevel@tonic-gate}
2827c478bdstevel@tonic-gate
2837c478bdstevel@tonic-gatestatic void
2847c478bdstevel@tonic-gateht_grow()
2857c478bdstevel@tonic-gate{
2867c478bdstevel@tonic-gate	uint_t new_ht_buckets_num;
2877c478bdstevel@tonic-gate	struct ht_elem **new_ht_buckets;
2887c478bdstevel@tonic-gate	int i;
2897c478bdstevel@tonic-gate
2907c478bdstevel@tonic-gate	new_ht_buckets_num = ht_buckets_num * 2;
2917c478bdstevel@tonic-gate	assert(new_ht_buckets_num > ht_buckets_num);
2927c478bdstevel@tonic-gate	new_ht_buckets =
2937c478bdstevel@tonic-gate	    safe_malloc(sizeof (*new_ht_buckets) * new_ht_buckets_num);
2947c478bdstevel@tonic-gate	bzero(new_ht_buckets, sizeof (*new_ht_buckets) * new_ht_buckets_num);
2957c478bdstevel@tonic-gate
2967c478bdstevel@tonic-gate	for (i = 0; i < ht_buckets_num; ++i) {
2977c478bdstevel@tonic-gate		struct ht_elem *elem, *next;
2987c478bdstevel@tonic-gate
2997c478bdstevel@tonic-gate		for (elem = ht_buckets[i]; elem != NULL; elem = next) {
3007c478bdstevel@tonic-gate			uint_t h;
3017c478bdstevel@tonic-gate
3027c478bdstevel@tonic-gate			next = elem->next;
3037c478bdstevel@tonic-gate
3047c478bdstevel@tonic-gate			h = ht_hash_fmri(elem->fmri);
3057c478bdstevel@tonic-gate
3067c478bdstevel@tonic-gate			elem->next =
3077c478bdstevel@tonic-gate			    new_ht_buckets[h & (new_ht_buckets_num - 1)];
3087c478bdstevel@tonic-gate			new_ht_buckets[h & (new_ht_buckets_num - 1)] = elem;
3097c478bdstevel@tonic-gate		}
3107c478bdstevel@tonic-gate	}
3117c478bdstevel@tonic-gate
3127c478bdstevel@tonic-gate	free(ht_buckets);
3137c478bdstevel@tonic-gate
3147c478bdstevel@tonic-gate	ht_buckets = new_ht_buckets;
3157c478bdstevel@tonic-gate	ht_buckets_num = new_ht_buckets_num;
3167c478bdstevel@tonic-gate}
3177c478bdstevel@tonic-gate
3187c478bdstevel@tonic-gate/*
3197c478bdstevel@tonic-gate * Add an FMRI to the hash table.  Returns 1 if it was already there,
3207c478bdstevel@tonic-gate * 0 otherwise.
3217c478bdstevel@tonic-gate */
3227c478bdstevel@tonic-gatestatic int
3237c478bdstevel@tonic-gateht_add(const char *fmri)
3247c478bdstevel@tonic-gate{
3257c478bdstevel@tonic-gate	uint_t h;
3267c478bdstevel@tonic-gate	struct ht_elem *elem;
3277c478bdstevel@tonic-gate
3287c478bdstevel@tonic-gate	h = ht_hash_fmri(fmri);
3297c478bdstevel@tonic-gate
3307c478bdstevel@tonic-gate	elem = ht_buckets[h & (ht_buckets_num - 1)];
3317c478bdstevel@tonic-gate
3327c478bdstevel@tonic-gate	for (; elem != NULL; elem = elem->next) {
3337c478bdstevel@tonic-gate		if (strcmp(elem->fmri, fmri) == 0)
3347c478bdstevel@tonic-gate			return (1);
3357c478bdstevel@tonic-gate	}
3367c478bdstevel@tonic-gate
3377c478bdstevel@tonic-gate	/* Grow when average chain length is over 3. */
3387c478bdstevel@tonic-gate	if (ht_num > 3 * ht_buckets_num)
3397c478bdstevel@tonic-gate		ht_grow();
3407c478bdstevel@tonic-gate
3417c478bdstevel@tonic-gate	++ht_num;
3427c478bdstevel@tonic-gate
3437c478bdstevel@tonic-gate	elem = safe_malloc(sizeof (*elem));
3447c478bdstevel@tonic-gate	elem->fmri = strdup(fmri);
3457c478bdstevel@tonic-gate	elem->next = ht_buckets[h & (ht_buckets_num - 1)];
3467c478bdstevel@tonic-gate	ht_buckets[h & (ht_buckets_num - 1)] = elem;
3477c478bdstevel@tonic-gate
3487c478bdstevel@tonic-gate	return (0);
3497c478bdstevel@tonic-gate}
3507c478bdstevel@tonic-gate
3517c478bdstevel@tonic-gate
3527c478bdstevel@tonic-gate
3537c478bdstevel@tonic-gate/*
3547c478bdstevel@tonic-gate * Convenience libscf wrapper functions.
3557c478bdstevel@tonic-gate */
3567c478bdstevel@tonic-gate
3577c478bdstevel@tonic-gate/*
3587c478bdstevel@tonic-gate * Get the single value of the named property in the given property group,
3597c478bdstevel@tonic-gate * which must have type ty, and put it in *vp.  If ty is SCF_TYPE_ASTRING, vp
3607c478bdstevel@tonic-gate * is taken to be a char **, and sz is the size of the buffer.  sz is unused
3617c478bdstevel@tonic-gate * otherwise.  Return 0 on success, -1 if the property doesn't exist, has the
3627c478bdstevel@tonic-gate * wrong type, or doesn't have a single value.  If flags has EMPTY_OK, don't
3637c478bdstevel@tonic-gate * complain if the property has no values (but return nonzero).  If flags has
3647c478bdstevel@tonic-gate * MULTI_OK and the property has multiple values, succeed with E2BIG.
3657c478bdstevel@tonic-gate */
3667c478bdstevel@tonic-gateint
3677c478bdstevel@tonic-gatepg_get_single_val(scf_propertygroup_t *pg, const char *propname, scf_type_t ty,
3687c478bdstevel@tonic-gate    void *vp, size_t sz, uint_t flags)
3697c478bdstevel@tonic-gate{
370048b027Bryan Cantrill	char *buf, root[MAXPATHLEN];
3717c478bdstevel@tonic-gate	size_t buf_sz;
3727c478bdstevel@tonic-gate	int ret = -1, r;
3737c478bdstevel@tonic-gate	boolean_t multi = B_FALSE;
3747c478bdstevel@tonic-gate
3757c478bdstevel@tonic-gate	assert((flags & ~(EMPTY_OK | MULTI_OK)) == 0);
3767c478bdstevel@tonic-gate
3777c478bdstevel@tonic-gate	if (scf_pg_get_property(pg, propname, g_prop) == -1) {
3787c478bdstevel@tonic-gate		if (scf_error() != SCF_ERROR_NOT_FOUND)
3797c478bdstevel@tonic-gate			scfdie();
3807c478bdstevel@tonic-gate
3817c478bdstevel@tonic-gate		goto out;
3827c478bdstevel@tonic-gate	}
3837c478bdstevel@tonic-gate
3847c478bdstevel@tonic-gate	if (scf_property_is_type(g_prop, ty) != SCF_SUCCESS) {
3857c478bdstevel@tonic-gate		if (scf_error() == SCF_ERROR_TYPE_MISMATCH)
3867c478bdstevel@tonic-gate			goto misconfigured;
3877c478bdstevel@tonic-gate		scfdie();
3887c478bdstevel@tonic-gate	}
3897c478bdstevel@tonic-gate
3907c478bdstevel@tonic-gate	if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) {
3917c478bdstevel@tonic-gate		switch (scf_error()) {
3927c478bdstevel@tonic-gate		case SCF_ERROR_NOT_FOUND:
3937c478bdstevel@tonic-gate			if (flags & EMPTY_OK)
3947c478bdstevel@tonic-gate				goto out;
3957c478bdstevel@tonic-gate			goto misconfigured;
3967c478bdstevel@tonic-gate
3977c478bdstevel@tonic-gate		case SCF_ERROR_CONSTRAINT_VIOLATED:
3987c478bdstevel@tonic-gate			if (flags & MULTI_OK) {
3997c478bdstevel@tonic-gate				multi = B_TRUE;
4007c478bdstevel@tonic-gate				break;
4017c478bdstevel@tonic-gate			}
4027c478bdstevel@tonic-gate			goto misconfigured;
4037c478bdstevel@tonic-gate
4043eae19dwesolows		case SCF_ERROR_PERMISSION_DENIED:
4057c478bdstevel@tonic-gate		default:
4067c478bdstevel@tonic-gate			scfdie();
4077c478bdstevel@tonic-gate		}
4087c478bdstevel@tonic-gate	}
4097c478bdstevel@tonic-gate
4107c478bdstevel@tonic-gate	switch (ty) {
4117c478bdstevel@tonic-gate	case SCF_TYPE_ASTRING:
4127c478bdstevel@tonic-gate		r = scf_value_get_astring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1;
4137c478bdstevel@tonic-gate		break;
4147c478bdstevel@tonic-gate
4157c478bdstevel@tonic-gate	case SCF_TYPE_BOOLEAN:
4167c478bdstevel@tonic-gate		r = scf_value_get_boolean(g_val, (uint8_t *)vp);
4177c478bdstevel@tonic-gate		break;
4187c478bdstevel@tonic-gate
4197c478bdstevel@tonic-gate	case SCF_TYPE_COUNT:
4207c478bdstevel@tonic-gate		r = scf_value_get_count(g_val, (uint64_t *)vp);
4217c478bdstevel@tonic-gate		break;
4227c478bdstevel@tonic-gate
4237c478bdstevel@tonic-gate	case SCF_TYPE_INTEGER:
4247c478bdstevel@tonic-gate		r = scf_value_get_integer(g_val, (int64_t *)vp);
4257c478bdstevel@tonic-gate		break;
4267c478bdstevel@tonic-gate
4277c478bdstevel@tonic-gate	case SCF_TYPE_TIME: {
4287c478bdstevel@tonic-gate		int64_t sec;
4297c478bdstevel@tonic-gate		int32_t ns;
4307c478bdstevel@tonic-gate		r = scf_value_get_time(g_val, &sec, &ns);
4317c478bdstevel@tonic-gate		((struct timeval *)vp)->tv_sec = sec;
4327c478bdstevel@tonic-gate		((struct timeval *)vp)->tv_usec = ns / 1000;
4337c478bdstevel@tonic-gate		break;
4347c478bdstevel@tonic-gate	}
4357c478bdstevel@tonic-gate
4367c478bdstevel@tonic-gate	case SCF_TYPE_USTRING:
4377c478bdstevel@tonic-gate		r = scf_value_get_ustring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1;
4387c478bdstevel@tonic-gate		break;
4397c478bdstevel@tonic-gate
4407c478bdstevel@tonic-gate	default:
4417c478bdstevel@tonic-gate#ifndef NDEBUG
4427c478bdstevel@tonic-gate		uu_warn("%s:%d: Unknown type %d.\n", __FILE__, __LINE__, ty);
4437c478bdstevel@tonic-gate#endif
4447c478bdstevel@tonic-gate		abort();
4457c478bdstevel@tonic-gate	}
4467c478bdstevel@tonic-gate	if (r != SCF_SUCCESS)
4477c478bdstevel@tonic-gate		scfdie();
4487c478bdstevel@tonic-gate
4497c478bdstevel@tonic-gate	ret = multi ? E2BIG : 0;
4507c478bdstevel@tonic-gate	goto out;
4517c478bdstevel@tonic-gate
4527c478bdstevel@tonic-gatemisconfigured:
4537c478bdstevel@tonic-gate	buf_sz = max_scf_fmri_length + 1;
4547c478bdstevel@tonic-gate	buf = safe_malloc(buf_sz);
4557c478bdstevel@tonic-gate	if (scf_property_to_fmri(g_prop, buf, buf_sz) == -1)
4567c478bdstevel@tonic-gate		scfdie();
4577c478bdstevel@tonic-gate
4587c478bdstevel@tonic-gate	uu_warn(gettext("Property \"%s\" is misconfigured.\n"), buf);
4597c478bdstevel@tonic-gate
4607c478bdstevel@tonic-gate	free(buf);
4617c478bdstevel@tonic-gate
4627c478bdstevel@tonic-gateout:
463048b027Bryan Cantrill	if (ret != 0 || g_zonename == NULL ||
464048b027Bryan Cantrill	    (strcmp(propname, SCF_PROPERTY_LOGFILE) != 0 &&
465048b027Bryan Cantrill	    strcmp(propname, SCF_PROPERTY_ALT_LOGFILE) != 0))
466048b027Bryan Cantrill		return (ret);
467048b027Bryan Cantrill
468048b027Bryan Cantrill	/*
469048b027Bryan Cantrill	 * If we're here, we have a log file and we have specified a zone.
470048b027Bryan Cantrill	 * As a convenience, we're going to prepend the zone path to the
471048b027Bryan Cantrill	 * name of the log file.
472048b027Bryan Cantrill	 */
473048b027Bryan Cantrill	root[0] = '\0';
474048b027Bryan Cantrill	(void) zone_get_rootpath(g_zonename, root, sizeof (root));
475048b027Bryan Cantrill	(void) strlcat(root, vp, sizeof (root));
476048b027Bryan Cantrill	(void) snprintf(vp, sz, "%s", root);
477048b027Bryan Cantrill
4787c478bdstevel@tonic-gate	return (ret);
4797c478bdstevel@tonic-gate}
4807c478bdstevel@tonic-gate
4817c478bdstevel@tonic-gatestatic scf_snapshot_t *
4827c478bdstevel@tonic-gateget_running_snapshot(scf_instance_t *inst)
4837c478bdstevel@tonic-gate{
4847c478bdstevel@tonic-gate	scf_snapshot_t *snap;
4857c478bdstevel@tonic-gate
4867c478bdstevel@tonic-gate	snap = scf_snapshot_create(h);
4877c478bdstevel@tonic-gate	if (snap == NULL)
4887c478bdstevel@tonic-gate		scfdie();
4897c478bdstevel@tonic-gate
4907c478bdstevel@tonic-gate	if (scf_instance_get_snapshot(inst, "running", snap) == 0)
4917c478bdstevel@tonic-gate		return (snap);
4927c478bdstevel@tonic-gate
4937c478bdstevel@tonic-gate	if (scf_error() != SCF_ERROR_NOT_FOUND)
4947c478bdstevel@tonic-gate		scfdie();
4957c478bdstevel@tonic-gate
4967c478bdstevel@tonic-gate	scf_snapshot_destroy(snap);
4977c478bdstevel@tonic-gate	return (NULL);
4987c478bdstevel@tonic-gate}
4997c478bdstevel@tonic-gate
5007c478bdstevel@tonic-gate/*
5017c478bdstevel@tonic-gate * As pg_get_single_val(), except look the property group up in an
5027c478bdstevel@tonic-gate * instance.  If "use_running" is set, and the running snapshot exists,
5037c478bdstevel@tonic-gate * do a composed lookup there.  Otherwise, do an (optionally composed)
5047c478bdstevel@tonic-gate * lookup on the current values.  Note that lookups using snapshots are
5057c478bdstevel@tonic-gate * always composed.
5067c478bdstevel@tonic-gate */
5077c478bdstevel@tonic-gateint
5087c478bdstevel@tonic-gateinst_get_single_val(scf_instance_t *inst, const char *pgname,
5097c478bdstevel@tonic-gate    const char *propname, scf_type_t ty, void *vp, size_t sz, uint_t flags,
5107c478bdstevel@tonic-gate    int use_running, int composed)
5117c478bdstevel@tonic-gate{
5127c478bdstevel@tonic-gate	scf_snapshot_t *snap = NULL;
5137c478bdstevel@tonic-gate	int r;
5147c478bdstevel@tonic-gate
5157c478bdstevel@tonic-gate	if (use_running)
5167c478bdstevel@tonic-gate		snap = get_running_snapshot(inst);
5177c478bdstevel@tonic-gate	if (composed || use_running)
5187c478bdstevel@tonic-gate		r = scf_instance_get_pg_composed(inst, snap, pgname, g_pg);
5197c478bdstevel@tonic-gate	else
5207c478bdstevel@tonic-gate		r = scf_instance_get_pg(inst, pgname, g_pg);
5217c478bdstevel@tonic-gate	if (snap)
5227c478bdstevel@tonic-gate		scf_snapshot_destroy(snap);
5237c478bdstevel@tonic-gate	if (r == -1)
5247c478bdstevel@tonic-gate		return (-1);
5257c478bdstevel@tonic-gate
5267c478bdstevel@tonic-gate	r = pg_get_single_val(g_pg, propname, ty, vp, sz, flags);
5277c478bdstevel@tonic-gate
5287c478bdstevel@tonic-gate	return (r);
5297c478bdstevel@tonic-gate}
5307c478bdstevel@tonic-gate
5317c478bdstevel@tonic-gatestatic int
5327c478bdstevel@tonic-gateinstance_enabled(scf_instance_t *inst, boolean_t temp)
5337c478bdstevel@tonic-gate{
5347c478bdstevel@tonic-gate	uint8_t b;
5357c478bdstevel@tonic-gate
5367c478bdstevel@tonic-gate	if (inst_get_single_val(inst,
5377c478bdstevel@tonic-gate	    temp ? SCF_PG_GENERAL_OVR : SCF_PG_GENERAL, SCF_PROPERTY_ENABLED,
5387c478bdstevel@tonic-gate	    SCF_TYPE_BOOLEAN, &b, 0, 0, 0, 0) != 0)
5397c478bdstevel@tonic-gate		return (-1);
5407c478bdstevel@tonic-gate
5417c478bdstevel@tonic-gate	return (b ? 1 : 0);
5427c478bdstevel@tonic-gate}
5437c478bdstevel@tonic-gate
5447c478bdstevel@tonic-gate/*
5457c478bdstevel@tonic-gate * Get a string property from the restarter property group of the given
5467c478bdstevel@tonic-gate * instance.  Return an empty string on normal problems.
5477c478bdstevel@tonic-gate */
5487c478bdstevel@tonic-gatestatic void
5497c478bdstevel@tonic-gateget_restarter_string_prop(scf_instance_t *inst, const char *pname,
5507c478bdstevel@tonic-gate    char *buf, size_t buf_sz)
5517c478bdstevel@tonic-gate{
5527c478bdstevel@tonic-gate	if (inst_get_single_val(inst, SCF_PG_RESTARTER, pname,
5537c478bdstevel@tonic-gate	    SCF_TYPE_ASTRING, buf, buf_sz, 0, 0, 1) != 0)
5547c478bdstevel@tonic-gate		*buf = '\0';
5557c478bdstevel@tonic-gate}
5567c478bdstevel@tonic-gate
5577c478bdstevel@tonic-gatestatic int
5587c478bdstevel@tonic-gateget_restarter_time_prop(scf_instance_t *inst, const char *pname,
5597c478bdstevel@tonic-gate    struct timeval *tvp, int ok_if_empty)
5607c478bdstevel@tonic-gate{
5617c478bdstevel@tonic-gate	int r;
5627c478bdstevel@tonic-gate
5637c478bdstevel@tonic-gate	r = inst_get_single_val(inst, SCF_PG_RESTARTER, pname, SCF_TYPE_TIME,
5645f8fc37Toomas Soome	    tvp, 0, ok_if_empty ? EMPTY_OK : 0, 0, 1);
5657c478bdstevel@tonic-gate
5667c478bdstevel@tonic-gate	return (r == 0 ? 0 : -1);
5677c478bdstevel@tonic-gate}
5687c478bdstevel@tonic-gate
5697c478bdstevel@tonic-gatestatic int
5707c478bdstevel@tonic-gateget_restarter_count_prop(scf_instance_t *inst, const char *pname, uint64_t *cp,
5717c478bdstevel@tonic-gate    uint_t flags)
5727c478bdstevel@tonic-gate{
5737c478bdstevel@tonic-gate	return (inst_get_single_val(inst, SCF_PG_RESTARTER, pname,
5747c478bdstevel@tonic-gate	    SCF_TYPE_COUNT, cp, 0, flags, 0, 1));
5757c478bdstevel@tonic-gate}
5767c478bdstevel@tonic-gate
5777c478bdstevel@tonic-gate
5787c478bdstevel@tonic-gate/*
5797c478bdstevel@tonic-gate * Generic functions
5807c478bdstevel@tonic-gate */
5817c478bdstevel@tonic-gate
58294501b6skamm/*
58394501b6skamm * Return an array of pids associated with the given contract id.
58494501b6skamm * Returned pids are added to the end of the pidsp array.
58594501b6skamm */
58694501b6skammstatic void
58794501b6skammctid_to_pids(uint64_t c, pid_t **pidsp, uint_t *np)
58894501b6skamm{
58994501b6skamm	ct_stathdl_t ctst;
59094501b6skamm	uint_t m;
59194501b6skamm	int fd;
59294501b6skamm	int r, err;
59394501b6skamm	pid_t *pids;
59494501b6skamm
59594501b6skamm	fd = contract_open(c, NULL, "status", O_RDONLY);
59694501b6skamm	if (fd < 0)
59794501b6skamm		return;
59894501b6skamm
59994501b6skamm	err = ct_status_read(fd, CTD_ALL, &ctst);
60094501b6skamm	if (err != 0) {
60194501b6skamm		uu_warn(gettext("Could not read status of contract "
60294501b6skamm		    "%ld: %s.\n"), c, strerror(err));
60394501b6skamm		(void) close(fd);
60494501b6skamm		return;
60594501b6skamm	}
60694501b6skamm
60794501b6skamm	(void) close(fd);
60894501b6skamm
60994501b6skamm	r = ct_pr_status_get_members(ctst, &pids, &m);
61094501b6skamm	assert(r == 0);
61194501b6skamm
61294501b6skamm	if (m == 0) {
61394501b6skamm		ct_status_free(ctst);
61494501b6skamm		return;
61594501b6skamm	}
61694501b6skamm
61794501b6skamm	*pidsp = realloc(*pidsp, (*np + m) * sizeof (*pidsp));
61894501b6skamm	if (*pidsp == NULL)
61994501b6skamm		uu_die(gettext("Out of memory"));
62094501b6skamm
62194501b6skamm	bcopy(pids, *pidsp + *np, m * sizeof (*pids));
62294501b6skamm	*np += m;
62394501b6skamm
62494501b6skamm	ct_status_free(ctst);
62594501b6skamm}
62694501b6skamm
6277c478bdstevel@tonic-gatestatic int
6287c478bdstevel@tonic-gatepropvals_to_pids(scf_propertygroup_t *pg, const char *pname, pid_t **pidsp,
6297c478bdstevel@tonic-gate    uint_t *np, scf_property_t *prop, scf_value_t *val, scf_iter_t *iter)
6307c478bdstevel@tonic-gate{
6317c478bdstevel@tonic-gate	scf_type_t ty;
6327c478bdstevel@tonic-gate	uint64_t c;
63394501b6skamm	int r;
6347c478bdstevel@tonic-gate
6357c478bdstevel@tonic-gate	if (scf_pg_get_property(pg, pname, prop) != 0) {
6367c478bdstevel@tonic-gate		if (scf_error() != SCF_ERROR_NOT_FOUND)
6377c478bdstevel@tonic-gate			scfdie();
6387c478bdstevel@tonic-gate
6397c478bdstevel@tonic-gate		return (ENOENT);
6407c478bdstevel@tonic-gate	}
6417c478bdstevel@tonic-gate
6427c478bdstevel@tonic-gate	if (scf_property_type(prop, &ty) != 0)
6437c478bdstevel@tonic-gate		scfdie();
6447c478bdstevel@tonic-gate
6457c478bdstevel@tonic-gate	if (ty != SCF_TYPE_COUNT)
6467c478bdstevel@tonic-gate		return (EINVAL);
6477c478bdstevel@tonic-gate
6487c478bdstevel@tonic-gate	if (scf_iter_property_values(iter, prop) != 0)
6497c478bdstevel@tonic-gate		scfdie();
6507c478bdstevel@tonic-gate
6517c478bdstevel@tonic-gate	for (;;) {
6527c478bdstevel@tonic-gate		r = scf_iter_next_value(iter, val);
6537c478bdstevel@tonic-gate		if (r == -1)
6547c478bdstevel@tonic-gate			scfdie();
6557c478bdstevel@tonic-gate		if (r == 0)
6567c478bdstevel@tonic-gate			break;
6577c478bdstevel@tonic-gate
6587c478bdstevel@tonic-gate		if (scf_value_get_count(val, &c) != 0)
6597c478bdstevel@tonic-gate			scfdie();
6607c478bdstevel@tonic-gate
66194501b6skamm		ctid_to_pids(c, pidsp, np);
66294501b6skamm	}
6637c478bdstevel@tonic-gate
66494501b6skamm	return (0);
66594501b6skamm}
6667c478bdstevel@tonic-gate
66794501b6skamm/*
66894501b6skamm * Check if instance has general/restarter property that matches
66994501b6skamm * given string.  Restarter string must be in canonified form.
67094501b6skamm * Returns 0 for success; -1 otherwise.
67194501b6skamm */
67294501b6skammstatic int
67394501b6skammcheck_for_restarter(scf_instance_t *inst, const char *restarter)
67494501b6skamm{
67594501b6skamm	char	*fmri_buf;
67694501b6skamm	char	*fmri_buf_canonified = NULL;
67794501b6skamm	int	ret = -1;
6787c478bdstevel@tonic-gate
67994501b6skamm	if (inst == NULL)
68094501b6skamm		return (-1);
6817c478bdstevel@tonic-gate
68294501b6skamm	/* Get restarter */
68394501b6skamm	fmri_buf = safe_malloc(max_scf_fmri_length + 1);
68494501b6skamm	if (inst_get_single_val(inst, SCF_PG_GENERAL,
68594501b6skamm	    SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, fmri_buf,
68694501b6skamm	    max_scf_fmri_length + 1, 0, 0, 1) != 0)
68794501b6skamm		goto out;
68894501b6skamm
68994501b6skamm	fmri_buf_canonified = safe_malloc(max_scf_fmri_length + 1);
69094501b6skamm	if (scf_canonify_fmri(fmri_buf, fmri_buf_canonified,
69194501b6skamm	    (max_scf_fmri_length + 1)) < 0)
69294501b6skamm		goto out;
69394501b6skamm
69494501b6skamm	if (strcmp(fmri_buf, restarter) == 0)
69594501b6skamm		ret = 0;
69694501b6skamm
69794501b6skammout:
69894501b6skamm	free(fmri_buf);
69994501b6skamm	if (fmri_buf_canonified)
70094501b6skamm		free(fmri_buf_canonified);
70194501b6skamm	return (ret);
70294501b6skamm}
70394501b6skamm
70494501b6skamm/*
70594501b6skamm * Common code that is used by ctids_by_restarter and pids_by_restarter.
70694501b6skamm * Checks for a common restarter and if one is available, it generates
70794501b6skamm * the appropriate filename using wip->fmri and stores that in the
70894501b6skamm * global genfmri_filename.
70994501b6skamm *
71094501b6skamm * Restarters currently supported are: svc:/network/inetd:default
71194501b6skamm * If a restarter specific action is available, then restarter_spec
71294501b6skamm * is set to 1.  If a restarter specific action is not available, then
71394501b6skamm * restarter_spec is set to 0 and a -1 is returned.
71494501b6skamm *
71594501b6skamm * Returns:
71694501b6skamm * 0 if success: restarter specific action found and filename generated
71794501b6skamm * -1 if restarter specific action not found,
71894501b6skamm *    if restarter specific action found but an error was encountered
71994501b6skamm *    during the generation of the wip->fmri based filename
72094501b6skamm */
72194501b6skammstatic int
72294501b6skammcommon_by_restarter(scf_instance_t *inst, const char *fmri,
72394501b6skamm    int *restarter_specp)
72494501b6skamm{
72594501b6skamm	int		ret = -1;
72694501b6skamm	int		r;
72794501b6skamm
72894501b6skamm	/* Check for inetd specific restarter */
72994501b6skamm	if (check_for_restarter(inst, "svc:/network/inetd:default") != 0) {
73094501b6skamm		*restarter_specp = 0;
73194501b6skamm		return (ret);
73294501b6skamm	}
73394501b6skamm
73494501b6skamm	*restarter_specp = 1;
73594501b6skamm
73694501b6skamm	/* Get the ctid filename associated with this instance */
73794501b6skamm	r = gen_filenms_from_fmri(fmri, "ctid", genfmri_filename, NULL);
73894501b6skamm
73994501b6skamm	switch (r) {
74094501b6skamm	case 0:
74194501b6skamm		break;
74294501b6skamm
74394501b6skamm	case -1:
74494501b6skamm		/*
74594501b6skamm		 * Unable to get filename from fmri.  Print warning
74694501b6skamm		 * and return failure with no ctids.
74794501b6skamm		 */
74894501b6skamm		uu_warn(gettext("Unable to read contract ids for %s -- "
74994501b6skamm		    "FMRI is too long\n"), fmri);
75094501b6skamm		return (ret);
75194501b6skamm
75294501b6skamm	case -2:
75394501b6skamm		/*
75494501b6skamm		 * The directory didn't exist, so no contracts.
75594501b6skamm		 * Return failure with no ctids.
75694501b6skamm		 */
75794501b6skamm		return (ret);
75894501b6skamm
75994501b6skamm	default:
76094501b6skamm		uu_warn(gettext("%s:%d: gen_filenms_from_fmri() failed with "
76194501b6skamm		    "unknown error %d\n"), __FILE__, __LINE__, r);
76294501b6skamm		abort();
76394501b6skamm	}
76494501b6skamm
76594501b6skamm	return (0);
76694501b6skamm
76794501b6skamm}
76894501b6skamm
76994501b6skamm/*
77094501b6skamm * Get or print a contract id using a restarter specific action.
77194501b6skamm *
77294501b6skamm * If the print_flag is not set, this routine gets the single contract
77394501b6skamm * id associated with this instance.
77494501b6skamm * If the print flag is set, then print each contract id found.
77594501b6skamm *
77694501b6skamm * Returns:
77794501b6skamm * 0 if success: restarter specific action found and used with no error
77894501b6skamm * -1 if restarter specific action not found
77994501b6skamm * -1 if restarter specific action found, but there was a failure
78094501b6skamm * -1 if print flag is not set and no contract id is found or multiple
78194501b6skamm *    contract ids were found
78294501b6skamm * E2BIG if print flag is not set, MULTI_OK bit in flag is set and multiple
78394501b6skamm *    contract ids were found
78494501b6skamm */
78594501b6skammstatic int
78694501b6skammctids_by_restarter(scf_walkinfo_t *wip, uint64_t *cp, int print_flag,
78794501b6skamm    uint_t flags, int *restarter_specp, void (*callback_header)(),
78894501b6skamm    void (*callback_ctid)(uint64_t))
78994501b6skamm{
79094501b6skamm	FILE		*fp;
79194501b6skamm	int		ret = -1;
79294501b6skamm	int		fscanf_ret;
79394501b6skamm	uint64_t	cp2;
79494501b6skamm	int		rest_ret;
79594501b6skamm
79694501b6skamm	/* Check if callbacks are needed and were passed in */
79794501b6skamm	if (print_flag) {
79894501b6skamm		if ((callback_header == NULL) || (callback_ctid == NULL))
79994501b6skamm			return (ret);
80094501b6skamm	}
80194501b6skamm
80294501b6skamm	/* Check for restarter specific action and generation of filename */
80394501b6skamm	rest_ret = common_by_restarter(wip->inst, wip->fmri, restarter_specp);
80494501b6skamm	if (rest_ret != 0)
80594501b6skamm		return (rest_ret);
80694501b6skamm
80794501b6skamm	/*
80894501b6skamm	 * If fopen fails, then ctid file hasn't been created yet.
80994501b6skamm	 * If print_flag is set, this is ok; otherwise fail.
81094501b6skamm	 */
81194501b6skamm	if ((fp = fopen(genfmri_filename, "r")) == NULL) {
81294501b6skamm		if (print_flag)
81394501b6skamm			return (0);
81494501b6skamm		goto out;
81594501b6skamm	}
81694501b6skamm
81794501b6skamm	if (print_flag) {
81894501b6skamm		/*
81994501b6skamm		 * Print all contract ids that are found.
82094501b6skamm		 * First callback to print ctid header.
82194501b6skamm		 */
82294501b6skamm		callback_header();
82394501b6skamm
82494501b6skamm		/* fscanf may not set errno, so be sure to clear it first */
82594501b6skamm		errno = 0;
82694501b6skamm		while ((fscanf_ret = fscanf(fp, "%llu", cp)) == 1) {
82794501b6skamm			/* Callback to print contract id */
82894501b6skamm			callback_ctid(*cp);
82994501b6skamm			errno = 0;
83094501b6skamm		}
83194501b6skamm		/* EOF is not a failure when no errno. */
83294501b6skamm		if ((fscanf_ret != EOF) || (errno != 0)) {
83394501b6skamm			uu_die(gettext("Unable to read ctid file for %s"),
83494501b6skamm			    wip->fmri);
83594501b6skamm		}
83694501b6skamm		(void) putchar('\n');
83794501b6skamm		ret = 0;
83894501b6skamm	} else {
83994501b6skamm		/* Must find 1 ctid or fail */
84094501b6skamm		if (fscanf(fp, "%llu", cp) == 1) {
84194501b6skamm			/* If 2nd ctid found - fail */
84294501b6skamm			if (fscanf(fp, "%llu", &cp2) == 1) {
84394501b6skamm				if (flags & MULTI_OK)
84494501b6skamm					ret = E2BIG;
84594501b6skamm			} else {
84694501b6skamm				/* Success - found only 1 ctid */
84794501b6skamm				ret = 0;
84894501b6skamm			}
8497c478bdstevel@tonic-gate		}
85094501b6skamm	}
85194501b6skamm	(void) fclose(fp);
8527c478bdstevel@tonic-gate
85394501b6skammout:
85494501b6skamm	return (ret);
85594501b6skamm}
85694501b6skamm
85794501b6skamm/*
85894501b6skamm * Get the process ids associated with an instance using a restarter
85994501b6skamm * specific action.
86094501b6skamm *
86194501b6skamm * Returns:
86294501b6skamm *	0 if success: restarter specific action found and used with no error
86394501b6skamm *	-1 restarter specific action not found or if failure
86494501b6skamm */
86594501b6skammstatic int
86694501b6skammpids_by_restarter(scf_instance_t *inst, const char *fmri,
86794501b6skamm    pid_t **pids, uint_t *np, int *restarter_specp)
86894501b6skamm{
86994501b6skamm	uint64_t	c;
87094501b6skamm	FILE		*fp;
87194501b6skamm	int		fscanf_ret;
87294501b6skamm	int		rest_ret;
8737c478bdstevel@tonic-gate
87494501b6skamm	/* Check for restarter specific action and generation of filename */
87594501b6skamm	rest_ret = common_by_restarter(inst, fmri, restarter_specp);
87694501b6skamm	if (rest_ret != 0)
87794501b6skamm		return (rest_ret);
8787c478bdstevel@tonic-gate
87994501b6skamm	/*
88094501b6skamm	 * If fopen fails with ENOENT then the ctid file hasn't been
88194501b6skamm	 * created yet so return success.
88294501b6skamm	 * For all other errors - fail with uu_die.
88394501b6skamm	 */
88494501b6skamm	if ((fp = fopen(genfmri_filename, "r")) == NULL) {
88594501b6skamm		if (errno == ENOENT)
88694501b6skamm			return (0);
88794501b6skamm		uu_die(gettext("Unable to open ctid file for %s"), fmri);
88894501b6skamm	}
88994501b6skamm
89094501b6skamm	/* fscanf may not set errno, so be sure to clear it first */
89194501b6skamm	errno = 0;
89294501b6skamm	while ((fscanf_ret = fscanf(fp, "%llu", &c)) == 1) {
89394501b6skamm		if (c == 0) {
89494501b6skamm			(void) fclose(fp);
89594501b6skamm			uu_die(gettext("ctid file for %s has corrupt data"),
89694501b6skamm			    fmri);
89794501b6skamm		}
89894501b6skamm		ctid_to_pids(c, pids, np);
89994501b6skamm		errno = 0;
90094501b6skamm	}
90194501b6skamm	/* EOF is not a failure when no errno. */
90294501b6skamm	if ((fscanf_ret != EOF) || (errno != 0)) {
90394501b6skamm		uu_die(gettext("Unable to read ctid file for %s"), fmri);
9047c478bdstevel@tonic-gate	}
9057c478bdstevel@tonic-gate
90694501b6skamm	(void) fclose(fp);
9077c478bdstevel@tonic-gate	return (0);
9087c478bdstevel@tonic-gate}
9097c478bdstevel@tonic-gate
9107c478bdstevel@tonic-gatestatic int
91194501b6skamminstance_processes(scf_instance_t *inst, const char *fmri,
91294501b6skamm    pid_t **pids, uint_t *np)
9137c478bdstevel@tonic-gate{
9147c478bdstevel@tonic-gate	scf_iter_t *iter;
9157c478bdstevel@tonic-gate	int ret;
91694501b6skamm	int restarter_spec;
91794501b6skamm
91894501b6skamm	/* Use the restarter specific get pids routine, if available. */
91994501b6skamm	ret = pids_by_restarter(inst, fmri, pids, np, &restarter_spec);
92094501b6skamm	if (restarter_spec == 1)
92194501b6skamm		return (ret);
9227c478bdstevel@tonic-gate
9237c478bdstevel@tonic-gate	if ((iter = scf_iter_create(h)) == NULL)
9247c478bdstevel@tonic-gate		scfdie();
9257c478bdstevel@tonic-gate
9267c478bdstevel@tonic-gate	if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) == 0) {
9277c478bdstevel@tonic-gate		*pids = NULL;
9287c478bdstevel@tonic-gate		*np = 0;
9297c478bdstevel@tonic-gate
9307c478bdstevel@tonic-gate		(void) propvals_to_pids(g_pg, scf_property_contract, pids, np,
9317c478bdstevel@tonic-gate		    g_prop, g_val, iter);
9327c478bdstevel@tonic-gate
9337c478bdstevel@tonic-gate		(void) propvals_to_pids(g_pg, SCF_PROPERTY_TRANSIENT_CONTRACT,
9347c478bdstevel@tonic-gate		    pids, np, g_prop, g_val, iter);
9357c478bdstevel@tonic-gate
9367c478bdstevel@tonic-gate		ret = 0;
9377c478bdstevel@tonic-gate	} else {
9387c478bdstevel@tonic-gate		if (scf_error() != SCF_ERROR_NOT_FOUND)
9397c478bdstevel@tonic-gate			scfdie();
9407c478bdstevel@tonic-gate
9417c478bdstevel@tonic-gate		ret = -1;
9427c478bdstevel@tonic-gate	}
9437c478bdstevel@tonic-gate
9447c478bdstevel@tonic-gate	scf_iter_destroy(iter);
9457c478bdstevel@tonic-gate
9467c478bdstevel@tonic-gate	return (ret);
9477c478bdstevel@tonic-gate}
9487c478bdstevel@tonic-gate
9497c478bdstevel@tonic-gate/*
9507c478bdstevel@tonic-gate * Column sprint and sortkey functions
9517c478bdstevel@tonic-gate */
9527c478bdstevel@tonic-gate
9537c478bdstevel@tonic-gatestruct column {
9547c478bdstevel@tonic-gate	const char *name;
9557c478bdstevel@tonic-gate	int width;
9567c478bdstevel@tonic-gate
9577c478bdstevel@tonic-gate	/*
9587c478bdstevel@tonic-gate	 * This function should write the value for the column into buf, and
9597c478bdstevel@tonic-gate	 * grow or allocate buf accordingly.  It should always write at least
9607c478bdstevel@tonic-gate	 * width bytes, blanking unused bytes with spaces.  If the field is
9617c478bdstevel@tonic-gate	 * greater than the column width we allow it to overlap other columns.
9627c478bdstevel@tonic-gate	 * In particular, it shouldn't write any null bytes.  (Though an extra
9637c478bdstevel@tonic-gate	 * null byte past the end is currently tolerated.)  If the property
9647c478bdstevel@tonic-gate	 * group is non-NULL, then we are dealing with a legacy service.
9657c478bdstevel@tonic-gate	 */
9667c478bdstevel@tonic-gate	void (*sprint)(char **, scf_walkinfo_t *);
9677c478bdstevel@tonic-gate
9687c478bdstevel@tonic-gate	int sortkey_width;
9697c478bdstevel@tonic-gate
9707c478bdstevel@tonic-gate	/*
9717c478bdstevel@tonic-gate	 * This function should write sortkey_width bytes into buf which will
9727c478bdstevel@tonic-gate	 * cause memcmp() to sort it properly.  (Unlike sprint() above,
9737c478bdstevel@tonic-gate	 * however, an extra null byte may overrun the buffer.)  The second
9747c478bdstevel@tonic-gate	 * argument controls whether the results are sorted in forward or
9757c478bdstevel@tonic-gate	 * reverse order.
9767c478bdstevel@tonic-gate	 */
9777c478bdstevel@tonic-gate	void (*get_sortkey)(char *, int, scf_walkinfo_t *);
9787c478bdstevel@tonic-gate};
9797c478bdstevel@tonic-gate
9807c478bdstevel@tonic-gatestatic void
9817c478bdstevel@tonic-gatereverse_bytes(char *buf, size_t len)
9827c478bdstevel@tonic-gate{
9837c478bdstevel@tonic-gate	int i;
9847c478bdstevel@tonic-gate
9857c478bdstevel@tonic-gate	for (i = 0; i < len; ++i)
9867c478bdstevel@tonic-gate		buf[i] = ~buf[i];
9877c478bdstevel@tonic-gate}
9887c478bdstevel@tonic-gate
9897c478bdstevel@tonic-gate/* CTID */
9907c478bdstevel@tonic-gate#define	CTID_COLUMN_WIDTH		6
99151f8254Eric Sproul#define	CTID_COLUMN_BUFSIZE		20	/* max ctid_t + space + \0 */
9927c478bdstevel@tonic-gate
9937c478bdstevel@tonic-gatestatic void
9947c478bdstevel@tonic-gatesprint_ctid(char **buf, scf_walkinfo_t *wip)
9957c478bdstevel@tonic-gate{
9967c478bdstevel@tonic-gate	int r;
9977c478bdstevel@tonic-gate	uint64_t c;
99851f8254Eric Sproul	size_t newsize = (*buf ? strlen(*buf) : 0) + CTID_COLUMN_BUFSIZE;
9997c478bdstevel@tonic-gate	char *newbuf = safe_malloc(newsize);
100094501b6skamm	int restarter_spec;
10017c478bdstevel@tonic-gate
100294501b6skamm	/*
100394501b6skamm	 * Use the restarter specific get pids routine, if available.
100494501b6skamm	 * Only check for non-legacy services (wip->pg == 0).
100594501b6skamm	 */
100694501b6skamm	if (wip->pg != NULL) {
10077c478bdstevel@tonic-gate		r = pg_get_single_val(wip->pg, scf_property_contract,
10087c478bdstevel@tonic-gate		    SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK);
100994501b6skamm	} else {
101094501b6skamm		r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
101194501b6skamm		    NULL, NULL);
101294501b6skamm		if (restarter_spec == 0) {
101394501b6skamm			/* No restarter specific routine */
101494501b6skamm			r = get_restarter_count_prop(wip->inst,
101594501b6skamm			    scf_property_contract, &c, EMPTY_OK | MULTI_OK);
101694501b6skamm		}
101794501b6skamm	}
10187c478bdstevel@tonic-gate
10197c478bdstevel@tonic-gate	if (r == 0)
10207c478bdstevel@tonic-gate		(void) snprintf(newbuf, newsize, "%s%*lu ",
10217c478bdstevel@tonic-gate		    *buf ? *buf : "", CTID_COLUMN_WIDTH, (ctid_t)c);
10227c478bdstevel@tonic-gate	else if (r == E2BIG)
10237c478bdstevel@tonic-gate		(void) snprintf(newbuf, newsize, "%s%*lu* ",
10247c478bdstevel@tonic-gate		    *buf ? *buf : "", CTID_COLUMN_WIDTH - 1, (ctid_t)c);
10257c478bdstevel@tonic-gate	else
10267c478bdstevel@tonic-gate		(void) snprintf(newbuf, newsize, "%s%*s ",
10277c478bdstevel@tonic-gate		    *buf ? *buf : "", CTID_COLUMN_WIDTH, "-");
10287c478bdstevel@tonic-gate	if (*buf)
10297c478bdstevel@tonic-gate		free(*buf);
10307c478bdstevel@tonic-gate	*buf = newbuf;
10317c478bdstevel@tonic-gate}
10327c478bdstevel@tonic-gate
10337c478bdstevel@tonic-gate#define	CTID_SORTKEY_WIDTH		(sizeof (uint64_t))
10347c478bdstevel@tonic-gate
10357c478bdstevel@tonic-gatestatic void
10367c478bdstevel@tonic-gatesortkey_ctid(char *buf, int reverse, scf_walkinfo_t *wip)
10377c478bdstevel@tonic-gate{
10387c478bdstevel@tonic-gate	int r;
10397c478bdstevel@tonic-gate	uint64_t c;
104094501b6skamm	int restarter_spec;
10417c478bdstevel@tonic-gate
104294501b6skamm	/*
104394501b6skamm	 * Use the restarter specific get pids routine, if available.
104494501b6skamm	 * Only check for non-legacy services (wip->pg == 0).
104594501b6skamm	 */
104694501b6skamm	if (wip->pg != NULL) {
10477c478bdstevel@tonic-gate		r = pg_get_single_val(wip->pg, scf_property_contract,
10487c478bdstevel@tonic-gate		    SCF_TYPE_COUNT, &c, 0, EMPTY_OK);
104994501b6skamm	} else {
105094501b6skamm		r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
105194501b6skamm		    NULL, NULL);
105294501b6skamm		if (restarter_spec == 0) {
105394501b6skamm			/* No restarter specific routine */
105494501b6skamm			r = get_restarter_count_prop(wip->inst,
105594501b6skamm			    scf_property_contract, &c, EMPTY_OK);
105694501b6skamm		}
105794501b6skamm	}
10587c478bdstevel@tonic-gate
10597c478bdstevel@tonic-gate	if (r == 0) {
10607c478bdstevel@tonic-gate		/*
10617c478bdstevel@tonic-gate		 * Use the id itself, but it must be big-endian for this to
10627c478bdstevel@tonic-gate		 * work.
10637c478bdstevel@tonic-gate		 */
10647c478bdstevel@tonic-gate		c = BE_64(c);
10657c478bdstevel@tonic-gate
10667c478bdstevel@tonic-gate		bcopy(&c, buf, CTID_SORTKEY_WIDTH);
10677c478bdstevel@tonic-gate	} else {
10687c478bdstevel@tonic-gate		bzero(buf, CTID_SORTKEY_WIDTH);
10697c478bdstevel@tonic-gate	}
10707c478bdstevel@tonic-gate
10717c478bdstevel@tonic-gate	if (reverse)
10727c478bdstevel@tonic-gate		reverse_bytes(buf, CTID_SORTKEY_WIDTH);
10737c478bdstevel@tonic-gate}
10747c478bdstevel@tonic-gate
10757c478bdstevel@tonic-gate/* DESC */
10767c478bdstevel@tonic-gate#define	DESC_COLUMN_WIDTH	100
10777c478bdstevel@tonic-gate
10787c478bdstevel@tonic-gatestatic void
10797c478bdstevel@tonic-gatesprint_desc(char **buf, scf_walkinfo_t *wip)
10807c478bdstevel@tonic-gate{
10817c478bdstevel@tonic-gate	char *x;
10827c478bdstevel@tonic-gate	size_t newsize;
10837c478bdstevel@tonic-gate	char *newbuf;
10847c478bdstevel@tonic-gate
10857c478bdstevel@tonic-gate	if (common_name_buf == NULL)
10867c478bdstevel@tonic-gate		common_name_buf = safe_malloc(max_scf_value_length + 1);
10877c478bdstevel@tonic-gate
10887c478bdstevel@tonic-gate	bzero(common_name_buf, max_scf_value_length + 1);
10897c478bdstevel@tonic-gate
10907c478bdstevel@tonic-gate	if (wip->pg != NULL) {
10917c478bdstevel@tonic-gate		common_name_buf[0] = '-';
10927c478bdstevel@tonic-gate	} else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
10937c478bdstevel@tonic-gate	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
10947c478bdstevel@tonic-gate	    1, 1) == -1 &&
10957c478bdstevel@tonic-gate	    inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
10967c478bdstevel@tonic-gate	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
10977c478bdstevel@tonic-gate	    1, 1) == -1) {
10987c478bdstevel@tonic-gate		common_name_buf[0] = '-';
10997c478bdstevel@tonic-gate	}
11007c478bdstevel@tonic-gate
11017c478bdstevel@tonic-gate	/*
11027c478bdstevel@tonic-gate	 * Collapse multi-line tm_common_name values into a single line.
11037c478bdstevel@tonic-gate	 */
11047c478bdstevel@tonic-gate	for (x = common_name_buf; *x != '\0'; x++)
11057c478bdstevel@tonic-gate		if (*x == '\n')
11067c478bdstevel@tonic-gate			*x = ' ';
11077c478bdstevel@tonic-gate
11087c478bdstevel@tonic-gate	if (strlen(common_name_buf) > DESC_COLUMN_WIDTH)
11097c478bdstevel@tonic-gate		newsize = (*buf ? strlen(*buf) : 0) +
11107c478bdstevel@tonic-gate		    strlen(common_name_buf) + 1;
11117c478bdstevel@tonic-gate	else
11127c478bdstevel@tonic-gate		newsize = (*buf ? strlen(*buf) : 0) + DESC_COLUMN_WIDTH + 1;
11137c478bdstevel@tonic-gate	newbuf = safe_malloc(newsize);
11147c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
11153eae19dwesolows	    DESC_COLUMN_WIDTH, common_name_buf);
11167c478bdstevel@tonic-gate	if (*buf)
11177c478bdstevel@tonic-gate		free(*buf);
11187c478bdstevel@tonic-gate	*buf = newbuf;
11197c478bdstevel@tonic-gate}
11207c478bdstevel@tonic-gate
11217c478bdstevel@tonic-gate/* ARGSUSED */
11227c478bdstevel@tonic-gatestatic void
11237c478bdstevel@tonic-gatesortkey_desc(char *buf, int reverse, scf_walkinfo_t *wip)
11247c478bdstevel@tonic-gate{
11257c478bdstevel@tonic-gate	bzero(buf, DESC_COLUMN_WIDTH);
11267c478bdstevel@tonic-gate}
11277c478bdstevel@tonic-gate
11287c478bdstevel@tonic-gate/* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */
11297c478bdstevel@tonic-gate
11307c478bdstevel@tonic-gatestatic char
11317c478bdstevel@tonic-gatestate_to_char(const char *state)
11327c478bdstevel@tonic-gate{
11337c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
11347c478bdstevel@tonic-gate		return ('u');
11357c478bdstevel@tonic-gate
11367c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
11377c478bdstevel@tonic-gate		return ('0');
11387c478bdstevel@tonic-gate
11397c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
11407c478bdstevel@tonic-gate		return ('1');
11417c478bdstevel@tonic-gate
11427c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
11437c478bdstevel@tonic-gate		return ('m');
11447c478bdstevel@tonic-gate
11457c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
11467c478bdstevel@tonic-gate		return ('d');
11477c478bdstevel@tonic-gate
11487c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
11497c478bdstevel@tonic-gate		return ('D');
11507c478bdstevel@tonic-gate
11517c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
11527c478bdstevel@tonic-gate		return ('L');
11537c478bdstevel@tonic-gate
11547c478bdstevel@tonic-gate	return ('?');
11557c478bdstevel@tonic-gate}
11567c478bdstevel@tonic-gate
11577c478bdstevel@tonic-gate/* Return true if inst is transitioning. */
11587c478bdstevel@tonic-gatestatic int
11597c478bdstevel@tonic-gatetransitioning(scf_instance_t *inst)
11607c478bdstevel@tonic-gate{
11617c478bdstevel@tonic-gate	char nstate_name[MAX_SCF_STATE_STRING_SZ];
11627c478bdstevel@tonic-gate
11637c478bdstevel@tonic-gate	get_restarter_string_prop(inst, scf_property_next_state, nstate_name,
11647c478bdstevel@tonic-gate	    sizeof (nstate_name));
11657c478bdstevel@tonic-gate
11667c478bdstevel@tonic-gate	return (state_to_char(nstate_name) != '?');
11677c478bdstevel@tonic-gate}
11687c478bdstevel@tonic-gate
11697c478bdstevel@tonic-gate/* ARGSUSED */
11707c478bdstevel@tonic-gatestatic void
11717c478bdstevel@tonic-gatesortkey_states(const char *pname, char *buf, int reverse, scf_walkinfo_t *wip)
11727c478bdstevel@tonic-gate{
11737c478bdstevel@tonic-gate	char state_name[MAX_SCF_STATE_STRING_SZ];
11747c478bdstevel@tonic-gate
11757c478bdstevel@tonic-gate	/*
11767c478bdstevel@tonic-gate	 * Lower numbers are printed first, so these are arranged from least
11777c478bdstevel@tonic-gate	 * interesting ("legacy run") to most interesting (unknown).
11787c478bdstevel@tonic-gate	 */
11797c478bdstevel@tonic-gate	if (wip->pg == NULL) {
11807c478bdstevel@tonic-gate		get_restarter_string_prop(wip->inst, pname, state_name,
11817c478bdstevel@tonic-gate		    sizeof (state_name));
11827c478bdstevel@tonic-gate
11837c478bdstevel@tonic-gate		if (strcmp(state_name, SCF_STATE_STRING_ONLINE) == 0)
11847c478bdstevel@tonic-gate			*buf = 2;
11857c478bdstevel@tonic-gate		else if (strcmp(state_name, SCF_STATE_STRING_DEGRADED) == 0)
11867c478bdstevel@tonic-gate			*buf = 3;
11877c478bdstevel@tonic-gate		else if (strcmp(state_name, SCF_STATE_STRING_OFFLINE) == 0)
11887c478bdstevel@tonic-gate			*buf = 4;
11897c478bdstevel@tonic-gate		else if (strcmp(state_name, SCF_STATE_STRING_MAINT) == 0)
11907c478bdstevel@tonic-gate			*buf = 5;
11917c478bdstevel@tonic-gate		else if (strcmp(state_name, SCF_STATE_STRING_DISABLED) == 0)
11927c478bdstevel@tonic-gate			*buf = 1;
11937c478bdstevel@tonic-gate		else if (strcmp(state_name, SCF_STATE_STRING_UNINIT) == 0)
11947c478bdstevel@tonic-gate			*buf = 6;
11957c478bdstevel@tonic-gate		else
11967c478bdstevel@tonic-gate			*buf = 7;
11977c478bdstevel@tonic-gate	} else
11987c478bdstevel@tonic-gate		*buf = 0;
11997c478bdstevel@tonic-gate
12007c478bdstevel@tonic-gate	if (reverse)
12017c478bdstevel@tonic-gate		*buf = 255 - *buf;
12027c478bdstevel@tonic-gate}
12037c478bdstevel@tonic-gate
12047c478bdstevel@tonic-gatestatic void
12057c478bdstevel@tonic-gatesprint_state(char **buf, scf_walkinfo_t *wip)
12067c478bdstevel@tonic-gate{
12077c478bdstevel@tonic-gate	char state_name[MAX_SCF_STATE_STRING_SZ + 1];
12087c478bdstevel@tonic-gate	size_t newsize;
12097c478bdstevel@tonic-gate	char *newbuf;
12107c478bdstevel@tonic-gate
12117c478bdstevel@tonic-gate	if (wip->pg == NULL) {
12127c478bdstevel@tonic-gate		get_restarter_string_prop(wip->inst, scf_property_state,
12137c478bdstevel@tonic-gate		    state_name, sizeof (state_name));
12147c478bdstevel@tonic-gate
12157c478bdstevel@tonic-gate		/* Don't print blank fields, to ease parsing. */
12167c478bdstevel@tonic-gate		if (state_name[0] == '\0') {
12177c478bdstevel@tonic-gate			state_name[0] = '-';
12187c478bdstevel@tonic-gate			state_name[1] = '\0';
12197c478bdstevel@tonic-gate		}
12207c478bdstevel@tonic-gate
12217c478bdstevel@tonic-gate		if (!opt_nstate_shown && transitioning(wip->inst)) {
12227c478bdstevel@tonic-gate			/* Append an asterisk if nstate is valid. */
12237c478bdstevel@tonic-gate			(void) strcat(state_name, "*");
12247c478bdstevel@tonic-gate		}
12257c478bdstevel@tonic-gate	} else
12267c478bdstevel@tonic-gate		(void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
12277c478bdstevel@tonic-gate
12287c478bdstevel@tonic-gate	newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 2;
12297c478bdstevel@tonic-gate	newbuf = safe_malloc(newsize);
12307c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
12317c478bdstevel@tonic-gate	    MAX_SCF_STATE_STRING_SZ + 1, state_name);
12327c478bdstevel@tonic-gate
12337c478bdstevel@tonic-gate	if (*buf)
12347c478bdstevel@tonic-gate		free(*buf);
12357c478bdstevel@tonic-gate	*buf = newbuf;
12367c478bdstevel@tonic-gate}
12377c478bdstevel@tonic-gate
12387c478bdstevel@tonic-gatestatic void
12397c478bdstevel@tonic-gatesortkey_state(char *buf, int reverse, scf_walkinfo_t *wip)
12407c478bdstevel@tonic-gate{
12417c478bdstevel@tonic-gate	sortkey_states(scf_property_state, buf, reverse, wip);
12427c478bdstevel@tonic-gate}
12437c478bdstevel@tonic-gate
12447c478bdstevel@tonic-gatestatic void
12457c478bdstevel@tonic-gatesprint_nstate(char **buf, scf_walkinfo_t *wip)
12467c478bdstevel@tonic-gate{
12477c478bdstevel@tonic-gate	char next_state_name[MAX_SCF_STATE_STRING_SZ];
12487c478bdstevel@tonic-gate	boolean_t blank = 0;
12497c478bdstevel@tonic-gate	size_t newsize;
12507c478bdstevel@tonic-gate	char *newbuf;
12517c478bdstevel@tonic-gate
12527c478bdstevel@tonic-gate	if (wip->pg == NULL) {
12537c478bdstevel@tonic-gate		get_restarter_string_prop(wip->inst, scf_property_next_state,
12547c478bdstevel@tonic-gate		    next_state_name, sizeof (next_state_name));
12557c478bdstevel@tonic-gate
12567c478bdstevel@tonic-gate		/* Don't print blank fields, to ease parsing. */
12577c478bdstevel@tonic-gate		if (next_state_name[0] == '\0' ||
12587c478bdstevel@tonic-gate		    strcmp(next_state_name, SCF_STATE_STRING_NONE) == 0)
12597c478bdstevel@tonic-gate			blank = 1;
12607c478bdstevel@tonic-gate	} else
12617c478bdstevel@tonic-gate		blank = 1;
12627c478bdstevel@tonic-gate
12637c478bdstevel@tonic-gate	if (blank) {
12647c478bdstevel@tonic-gate		next_state_name[0] = '-';
12657c478bdstevel@tonic-gate		next_state_name[1] = '\0';
12667c478bdstevel@tonic-gate	}
12677c478bdstevel@tonic-gate
12687c478bdstevel@tonic-gate	newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 1;
12697c478bdstevel@tonic-gate	newbuf = safe_malloc(newsize);
12707c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
12717c478bdstevel@tonic-gate	    MAX_SCF_STATE_STRING_SZ - 1, next_state_name);
12727c478bdstevel@tonic-gate	if (*buf)
12737c478bdstevel@tonic-gate		free(*buf);
12747c478bdstevel@tonic-gate	*buf = newbuf;
12757c478bdstevel@tonic-gate}
12767c478bdstevel@tonic-gate
12777c478bdstevel@tonic-gatestatic void
12787c478bdstevel@tonic-gatesortkey_nstate(char *buf, int reverse, scf_walkinfo_t *wip)
12797c478bdstevel@tonic-gate{
12807c478bdstevel@tonic-gate	sortkey_states(scf_property_next_state, buf, reverse, wip);
12817c478bdstevel@tonic-gate}
12827c478bdstevel@tonic-gate
12837c478bdstevel@tonic-gatestatic void
12847c478bdstevel@tonic-gatesprint_s(char **buf, scf_walkinfo_t *wip)
12857c478bdstevel@tonic-gate{
12867c478bdstevel@tonic-gate	char tmp[3];
12877c478bdstevel@tonic-gate	char state_name[MAX_SCF_STATE_STRING_SZ];
12887c478bdstevel@tonic-gate	size_t newsize = (*buf ? strlen(*buf) : 0) + 4;
12897c478bdstevel@tonic-gate	char *newbuf = safe_malloc(newsize);
12907c478bdstevel@tonic-gate
12917c478bdstevel@tonic-gate	if (wip->pg == NULL) {
12927c478bdstevel@tonic-gate		get_restarter_string_prop(wip->inst, scf_property_state,
12937c478bdstevel@tonic-gate		    state_name, sizeof (state_name));
12947c478bdstevel@tonic-gate		tmp[0] = state_to_char(state_name);
12957c478bdstevel@tonic-gate
12967c478bdstevel@tonic-gate		if (!opt_nstate_shown && transitioning(wip->inst))
12977c478bdstevel@tonic-gate			tmp[1] = '*';
12987c478bdstevel@tonic-gate		else
12997c478bdstevel@tonic-gate			tmp[1] = ' ';
13007c478bdstevel@tonic-gate	} else {
13017c478bdstevel@tonic-gate		tmp[0] = 'L';
13027c478bdstevel@tonic-gate		tmp[1] = ' ';
13037c478bdstevel@tonic-gate	}
13047c478bdstevel@tonic-gate	tmp[2] = ' ';
13057c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "",
13067c478bdstevel@tonic-gate	    3, tmp);
13077c478bdstevel@tonic-gate	if (*buf)
13087c478bdstevel@tonic-gate		free(*buf);
13097c478bdstevel@tonic-gate	*buf = newbuf;
13107c478bdstevel@tonic-gate}
13117c478bdstevel@tonic-gate
13127c478bdstevel@tonic-gatestatic void
13137c478bdstevel@tonic-gatesprint_n(char **buf, scf_walkinfo_t *wip)
13147c478bdstevel@tonic-gate{
13157c478bdstevel@tonic-gate	char tmp[2];
13167c478bdstevel@tonic-gate	size_t newsize = (*buf ? strlen(*buf) : 0) + 3;
13177c478bdstevel@tonic-gate	char *newbuf = safe_malloc(newsize);
13187c478bdstevel@tonic-gate	char nstate_name[MAX_SCF_STATE_STRING_SZ];
13197c478bdstevel@tonic-gate
13207c478bdstevel@tonic-gate	if (wip->pg == NULL) {
13217c478bdstevel@tonic-gate		get_restarter_string_prop(wip->inst, scf_property_next_state,
13227c478bdstevel@tonic-gate		    nstate_name, sizeof (nstate_name));
13237c478bdstevel@tonic-gate
13247c478bdstevel@tonic-gate		if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
13257c478bdstevel@tonic-gate			tmp[0] = '-';
13267c478bdstevel@tonic-gate		else
13277c478bdstevel@tonic-gate			tmp[0] = state_to_char(nstate_name);
13287c478bdstevel@tonic-gate	} else
13297c478bdstevel@tonic-gate		tmp[0] = '-';
13307c478bdstevel@tonic-gate
13317c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
13327c478bdstevel@tonic-gate	    2, tmp);
13337c478bdstevel@tonic-gate	if (*buf)
13347c478bdstevel@tonic-gate		free(*buf);
13357c478bdstevel@tonic-gate	*buf = newbuf;
13367c478bdstevel@tonic-gate}
13377c478bdstevel@tonic-gate
13387c478bdstevel@tonic-gatestatic void
13397c478bdstevel@tonic-gatesprint_sn(char **buf, scf_walkinfo_t *wip)
13407c478bdstevel@tonic-gate{
13417c478bdstevel@tonic-gate	char tmp[3];
13427c478bdstevel@tonic-gate	size_t newsize = (*buf ? strlen(*buf) : 0) + 4;
13437c478bdstevel@tonic-gate	char *newbuf = safe_malloc(newsize);
13447c478bdstevel@tonic-gate	char nstate_name[MAX_SCF_STATE_STRING_SZ];
13457c478bdstevel@tonic-gate	char state_name[MAX_SCF_STATE_STRING_SZ];
13467c478bdstevel@tonic-gate
13477c478bdstevel@tonic-gate	if (wip->pg == NULL) {
13487c478bdstevel@tonic-gate		get_restarter_string_prop(wip->inst, scf_property_state,
13497c478bdstevel@tonic-gate		    state_name, sizeof (state_name));
13507c478bdstevel@tonic-gate		get_restarter_string_prop(wip->inst, scf_property_next_state,
13517c478bdstevel@tonic-gate		    nstate_name, sizeof (nstate_name));
13527c478bdstevel@tonic-gate		tmp[0] = state_to_char(state_name);
13537c478bdstevel@tonic-gate
13547c478bdstevel@tonic-gate		if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
13557c478bdstevel@tonic-gate			tmp[1] = '-';
13567c478bdstevel@tonic-gate		else
13577c478bdstevel@tonic-gate			tmp[1] = state_to_char(nstate_name);
13587c478bdstevel@tonic-gate	} else {
13597c478bdstevel@tonic-gate		tmp[0] = 'L';
13607c478bdstevel@tonic-gate		tmp[1] = '-';
13617c478bdstevel@tonic-gate	}
13627c478bdstevel@tonic-gate
13637c478bdstevel@tonic-gate	tmp[2] = ' ';
13647c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
13657c478bdstevel@tonic-gate	    3, tmp);
13667c478bdstevel@tonic-gate	if (*buf)
13677c478bdstevel@tonic-gate		free(*buf);
13687c478bdstevel@tonic-gate	*buf = newbuf;
13697c478bdstevel@tonic-gate}
13707c478bdstevel@tonic-gate
13717c478bdstevel@tonic-gate/* ARGSUSED */
13727c478bdstevel@tonic-gatestatic void
13737c478bdstevel@tonic-gatesortkey_sn(char *buf, int reverse, scf_walkinfo_t *wip)
13747c478bdstevel@tonic-gate{
13757c478bdstevel@tonic-gate	sortkey_state(buf, reverse, wip);
13767c478bdstevel@tonic-gate	sortkey_nstate(buf + 1, reverse, wip);
13777c478bdstevel@tonic-gate}
13787c478bdstevel@tonic-gate
13797c478bdstevel@tonic-gatestatic const char *
13807c478bdstevel@tonic-gatestate_abbrev(const char *state)
13817c478bdstevel@tonic-gate{
13827c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
13837c478bdstevel@tonic-gate		return ("UN");
13847c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
13857c478bdstevel@tonic-gate		return ("OFF");
13867c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
13877c478bdstevel@tonic-gate		return ("ON");
13887c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
13897c478bdstevel@tonic-gate		return ("MNT");
13907c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
13917c478bdstevel@tonic-gate		return ("DIS");
13927c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
13937c478bdstevel@tonic-gate		return ("DGD");
13947c478bdstevel@tonic-gate	if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
13957c478bdstevel@tonic-gate		return ("LRC");
13967c478bdstevel@tonic-gate
13977c478bdstevel@tonic-gate	return ("?");
13987c478bdstevel@tonic-gate}
13997c478bdstevel@tonic-gate
14007c478bdstevel@tonic-gatestatic void
14017c478bdstevel@tonic-gatesprint_sta(char **buf, scf_walkinfo_t *wip)
14027c478bdstevel@tonic-gate{
14037c478bdstevel@tonic-gate	char state_name[MAX_SCF_STATE_STRING_SZ];
14047c478bdstevel@tonic-gate	char sta[5];
14057c478bdstevel@tonic-gate	size_t newsize = (*buf ? strlen(*buf) : 0) + 6;
14067c478bdstevel@tonic-gate	char *newbuf = safe_malloc(newsize);
14077c478bdstevel@tonic-gate
14087c478bdstevel@tonic-gate	if (wip->pg == NULL)
14097c478bdstevel@tonic-gate		get_restarter_string_prop(wip->inst, scf_property_state,
14107c478bdstevel@tonic-gate		    state_name, sizeof (state_name));
14117c478bdstevel@tonic-gate	else
14127c478bdstevel@tonic-gate		(void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
14137c478bdstevel@tonic-gate
14147c478bdstevel@tonic-gate	(void) strcpy(sta, state_abbrev(state_name));
14157c478bdstevel@tonic-gate
14167c478bdstevel@tonic-gate	if (wip->pg == NULL && !opt_nstate_shown && transitioning(wip->inst))
14177c478bdstevel@tonic-gate		(void) strcat(sta, "*");
14187c478bdstevel@tonic-gate
14197c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", sta);
14207c478bdstevel@tonic-gate	if (*buf)
14217c478bdstevel@tonic-gate		free(*buf);
14227c478bdstevel@tonic-gate	*buf = newbuf;
14237c478bdstevel@tonic-gate}
14247c478bdstevel@tonic-gate
14257c478bdstevel@tonic-gatestatic void
14267c478bdstevel@tonic-gatesprint_nsta(char **buf, scf_walkinfo_t *wip)
14277c478bdstevel@tonic-gate{
14287c478bdstevel@tonic-gate	char state_name[MAX_SCF_STATE_STRING_SZ];
14297c478bdstevel@tonic-gate	size_t newsize = (*buf ? strlen(*buf) : 0) + 6;
14307c478bdstevel@tonic-gate	char *newbuf = safe_malloc(newsize);
14317c478bdstevel@tonic-gate
14327c478bdstevel@tonic-gate	if (wip->pg == NULL)
14337c478bdstevel@tonic-gate		get_restarter_string_prop(wip->inst, scf_property_next_state,
14347c478bdstevel@tonic-gate		    state_name, sizeof (state_name));
14357c478bdstevel@tonic-gate	else
14367c478bdstevel@tonic-gate		(void) strcpy(state_name, SCF_STATE_STRING_NONE);
14377c478bdstevel@tonic-gate
14387c478bdstevel@tonic-gate	if (strcmp(state_name, SCF_STATE_STRING_NONE) == 0)
14397c478bdstevel@tonic-gate		(void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "",
14407c478bdstevel@tonic-gate		    "-");
14417c478bdstevel@tonic-gate	else
14427c478bdstevel@tonic-gate		(void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "",
14437c478bdstevel@tonic-gate		    state_abbrev(state_name));
14447c478bdstevel@tonic-gate	if (*buf)
14457c478bdstevel@tonic-gate		free(*buf);
14467c478bdstevel@tonic-gate	*buf = newbuf;
14477c478bdstevel@tonic-gate}
14487c478bdstevel@tonic-gate
14497c478bdstevel@tonic-gate/* FMRI */
14507c478bdstevel@tonic-gate#define	FMRI_COLUMN_WIDTH	50
14517c478bdstevel@tonic-gatestatic void
14527c478bdstevel@tonic-gatesprint_fmri(char **buf, scf_walkinfo_t *wip)
14537c478bdstevel@tonic-gate{
14547c478bdstevel@tonic-gate	char *fmri_buf = safe_malloc(max_scf_fmri_length + 1);
14557c478bdstevel@tonic-gate	size_t newsize;
14567c478bdstevel@tonic-gate	char *newbuf;
14577c478bdstevel@tonic-gate
14587c478bdstevel@tonic-gate	if (wip->pg == NULL) {
14597c478bdstevel@tonic-gate		if (scf_instance_to_fmri(wip->inst, fmri_buf,
14607c478bdstevel@tonic-gate		    max_scf_fmri_length + 1) == -1)
14617c478bdstevel@tonic-gate			scfdie();
14627c478bdstevel@tonic-gate	} else {
14637b209c2acruz		(void) strcpy(fmri_buf, SCF_FMRI_LEGACY_PREFIX);
14647c478bdstevel@tonic-gate		if (pg_get_single_val(wip->pg, SCF_LEGACY_PROPERTY_NAME,
14657b209c2acruz		    SCF_TYPE_ASTRING, fmri_buf +
14667b209c2acruz		    sizeof (SCF_FMRI_LEGACY_PREFIX) - 1,
14677b209c2acruz		    max_scf_fmri_length + 1 -
14687b209c2acruz		    (sizeof (SCF_FMRI_LEGACY_PREFIX) - 1), 0) != 0)
14697c478bdstevel@tonic-gate			(void) strcat(fmri_buf, LEGACY_UNKNOWN);
14707c478bdstevel@tonic-gate	}
14717c478bdstevel@tonic-gate
14727c478bdstevel@tonic-gate	if (strlen(fmri_buf) > FMRI_COLUMN_WIDTH)
14737c478bdstevel@tonic-gate		newsize = (*buf ? strlen(*buf) : 0) + strlen(fmri_buf) + 2;
14747c478bdstevel@tonic-gate	else
14757c478bdstevel@tonic-gate		newsize = (*buf ? strlen(*buf) : 0) + FMRI_COLUMN_WIDTH + 2;
14767c478bdstevel@tonic-gate	newbuf = safe_malloc(newsize);
14777c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
14787c478bdstevel@tonic-gate	    FMRI_COLUMN_WIDTH, fmri_buf);
14797c478bdstevel@tonic-gate	free(fmri_buf);
14807c478bdstevel@tonic-gate	if (*buf)
14817c478bdstevel@tonic-gate		free(*buf);
14827c478bdstevel@tonic-gate	*buf = newbuf;
14837c478bdstevel@tonic-gate}
14847c478bdstevel@tonic-gate
14857c478bdstevel@tonic-gatestatic void
14867c478bdstevel@tonic-gatesortkey_fmri(char *buf, int reverse, scf_walkinfo_t *wip)
14877c478bdstevel@tonic-gate{
14887c478bdstevel@tonic-gate	char *tmp = NULL;
14897c478bdstevel@tonic-gate
14907c478bdstevel@tonic-gate	sprint_fmri(&tmp, wip);
14917c478bdstevel@tonic-gate	bcopy(tmp, buf, FMRI_COLUMN_WIDTH);
14927c478bdstevel@tonic-gate	free(tmp);
14937c478bdstevel@tonic-gate	if (reverse)
14947c478bdstevel@tonic-gate		reverse_bytes(buf, FMRI_COLUMN_WIDTH);
14957c478bdstevel@tonic-gate}
14967c478bdstevel@tonic-gate
14977c478bdstevel@tonic-gate/* Component columns */
14987c478bdstevel@tonic-gate#define	COMPONENT_COLUMN_WIDTH	20
14997c478bdstevel@tonic-gatestatic void
15007c478bdstevel@tonic-gatesprint_scope(char **buf, scf_walkinfo_t *wip)
15017c478bdstevel@tonic-gate{
15027c478bdstevel@tonic-gate	char *scope_buf = safe_malloc(max_scf_name_length + 1);
15037c478bdstevel@tonic-gate	size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2;
15047c478bdstevel@tonic-gate	char *newbuf = safe_malloc(newsize);
15057c478bdstevel@tonic-gate
15067c478bdstevel@tonic-gate	assert(wip->scope != NULL);
15077c478bdstevel@tonic-gate
15087c478bdstevel@tonic-gate	if (scf_scope_get_name(wip->scope, scope_buf, max_scf_name_length) < 0)
15097c478bdstevel@tonic-gate		scfdie();
15107c478bdstevel@tonic-gate
15117c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
15127c478bdstevel@tonic-gate	    COMPONENT_COLUMN_WIDTH, scope_buf);
15137c478bdstevel@tonic-gate	if (*buf)
15147c478bdstevel@tonic-gate		free(*buf);
15157c478bdstevel@tonic-gate	*buf = newbuf;
15167c478bdstevel@tonic-gate	free(scope_buf);
15177c478bdstevel@tonic-gate}
15187c478bdstevel@tonic-gate
15197c478bdstevel@tonic-gatestatic void
15207c478bdstevel@tonic-gatesortkey_scope(char *buf, int reverse, scf_walkinfo_t *wip)
15217c478bdstevel@tonic-gate{
15227c478bdstevel@tonic-gate	char *tmp = NULL;
15237c478bdstevel@tonic-gate
15247c478bdstevel@tonic-gate	sprint_scope(&tmp, wip);
15257c478bdstevel@tonic-gate	bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
15267c478bdstevel@tonic-gate	free(tmp);
15277c478bdstevel@tonic-gate	if (reverse)
15287c478bdstevel@tonic-gate		reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
15297c478bdstevel@tonic-gate}
15307c478bdstevel@tonic-gate
15317c478bdstevel@tonic-gatestatic void
15327c478bdstevel@tonic-gatesprint_service(char **buf, scf_walkinfo_t *wip)
15337c478bdstevel@tonic-gate{
15347c478bdstevel@tonic-gate	char *svc_buf = safe_malloc(max_scf_name_length + 1);
15357c478bdstevel@tonic-gate	char *newbuf;
15367c478bdstevel@tonic-gate	size_t newsize;
15377c478bdstevel@tonic-gate
15387c478bdstevel@tonic-gate	if (wip->pg == NULL) {
15397c478bdstevel@tonic-gate		if (scf_service_get_name(wip->svc, svc_buf,
15407c478bdstevel@tonic-gate		    max_scf_name_length + 1) < 0)
15417c478bdstevel@tonic-gate			scfdie();
15427c478bdstevel@tonic-gate	} else {
15437c478bdstevel@tonic-gate		if (pg_get_single_val(wip->pg, "name", SCF_TYPE_ASTRING,
15447c478bdstevel@tonic-gate		    svc_buf, max_scf_name_length + 1, EMPTY_OK) != 0)
15457c478bdstevel@tonic-gate			(void) strcpy(svc_buf, LEGACY_UNKNOWN);
15467c478bdstevel@tonic-gate	}
15477c478bdstevel@tonic-gate
15487c478bdstevel@tonic-gate
15497c478bdstevel@tonic-gate	if (strlen(svc_buf) > COMPONENT_COLUMN_WIDTH)
15507c478bdstevel@tonic-gate		newsize = (*buf ? strlen(*buf) : 0) + strlen(svc_buf) + 2;
15517c478bdstevel@tonic-gate	else
15527c478bdstevel@tonic-gate		newsize = (*buf ? strlen(*buf) : 0) +
15537c478bdstevel@tonic-gate		    COMPONENT_COLUMN_WIDTH + 2;
15547c478bdstevel@tonic-gate	newbuf = safe_malloc(newsize);
15557c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
15567c478bdstevel@tonic-gate	    COMPONENT_COLUMN_WIDTH, svc_buf);
15577c478bdstevel@tonic-gate	free(svc_buf);
15587c478bdstevel@tonic-gate	if (*buf)
15597c478bdstevel@tonic-gate		free(*buf);
15607c478bdstevel@tonic-gate	*buf = newbuf;
15617c478bdstevel@tonic-gate}
15627c478bdstevel@tonic-gate
15637c478bdstevel@tonic-gatestatic void
15647c478bdstevel@tonic-gatesortkey_service(char *buf, int reverse, scf_walkinfo_t *wip)
15657c478bdstevel@tonic-gate{
15667c478bdstevel@tonic-gate	char *tmp = NULL;
15677c478bdstevel@tonic-gate
15687c478bdstevel@tonic-gate	sprint_service(&tmp, wip);
15697c478bdstevel@tonic-gate	bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
15707c478bdstevel@tonic-gate	free(tmp);
15717c478bdstevel@tonic-gate	if (reverse)
15727c478bdstevel@tonic-gate		reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
15737c478bdstevel@tonic-gate}
15747c478bdstevel@tonic-gate
15757c478bdstevel@tonic-gate/* INST */
15767c478bdstevel@tonic-gatestatic void
15777c478bdstevel@tonic-gatesprint_instance(char **buf, scf_walkinfo_t *wip)
15787c478bdstevel@tonic-gate{
15797c478bdstevel@tonic-gate	char *tmp = safe_malloc(max_scf_name_length + 1);
15807c478bdstevel@tonic-gate	size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2;
15817c478bdstevel@tonic-gate	char *newbuf = safe_malloc(newsize);
15827c478bdstevel@tonic-gate
15837c478bdstevel@tonic-gate	if (wip->pg == NULL) {
15847c478bdstevel@tonic-gate		if (scf_instance_get_name(wip->inst, tmp,
15857c478bdstevel@tonic-gate		    max_scf_name_length + 1) < 0)
15867c478bdstevel@tonic-gate			scfdie();
15877c478bdstevel@tonic-gate	} else {
15887c478bdstevel@tonic-gate		tmp[0] = '-';
15897c478bdstevel@tonic-gate		tmp[1] = '\0';
15907c478bdstevel@tonic-gate	}
15917c478bdstevel@tonic-gate
15927c478bdstevel@tonic-gate	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
15937c478bdstevel@tonic-gate	    COMPONENT_COLUMN_WIDTH, tmp);
15947c478bdstevel@tonic-gate	if (*buf)
15957c478bdstevel@tonic-gate		free(*buf);
15967c478bdstevel@tonic-gate	*buf = newbuf;
15977c478bdstevel@tonic-gate	free(tmp);
15987c478bdstevel@tonic-gate}
15997c478bdstevel@tonic-gate
16007c478bdstevel@tonic-gatestatic void
16017c478bdstevel@tonic-gatesortkey_instance(char *buf, int reverse, scf_walkinfo_t *wip)
16027c478bdstevel@tonic-gate{
16037c478bdstevel@tonic-gate	char *tmp = NULL;
16047c478bdstevel@tonic-gate
16057c478bdstevel@tonic-gate	sprint_instance(&tmp, wip);
16067c478bdstevel@tonic-gate	bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
16077c478bdstevel@tonic-gate	free(tmp);
16087c478bdstevel@tonic-gate	if (reverse)
16097c478bdstevel@tonic-gate		reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
16107c478bdstevel@tonic-gate}
16117c478bdstevel@tonic-gate
16127c478bdstevel@tonic-gate/* STIME */
16137c478bdstevel@tonic-gate#define	STIME_COLUMN_WIDTH		8
16147c478bdstevel@tonic-gate#define	FORMAT_TIME			"%k:%M:%S"
16157c478bdstevel@tonic-gate#define	FORMAT_DATE			"%b_%d  "
16167c478bdstevel@tonic-gate#define	FORMAT_YEAR			"%Y    "
16177c478bdstevel@tonic-gate
16186140201lianep/*
16196140201lianep * sprint_stime() will allocate a new buffer and snprintf the services's
16206140201lianep * state timestamp.  If the timestamp is unavailable for some reason
16216140201lianep * a '-' is given instead.
16226140201lianep */
16237c478bdstevel@tonic-gatestatic void
16247c478bdstevel@tonic-gatesprint_stime(char **buf, scf_walkinfo_t *wip)
16257c478bdstevel@tonic-gate{
16267c478bdstevel@tonic-gate	int r;
16277c478bdstevel@tonic-gate	struct timeval tv;
16287c478bdstevel@tonic-gate	time_t then;
16297c478bdstevel@tonic-gate	struct tm *tm;
16307c478bdstevel@tonic-gate	char st_buf[STIME_COLUMN_WIDTH + 1];
16317c478bdstevel@tonic-gate	size_t newsize = (*buf ? strlen(*buf) : 0) + STIME_COLUMN_WIDTH + 2;
16327c478bdstevel@tonic-gate	char *newbuf = safe_malloc(newsize);
16337c478bdstevel@tonic-gate
16347c478bdstevel@tonic-gate	if (wip->pg == NULL) {
16357c478bdstevel@tonic-gate		r = get_restarter_time_prop(wip->inst,
16367c478bdstevel@tonic-gate		    SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
16377c478bdstevel@tonic-gate	} else {
16387c478bdstevel@tonic-gate		r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
16395f8fc37Toomas Soome		    SCF_TYPE_TIME, &tv, 0, 0);
16407c478bdstevel@tonic-gate	}
16417c478bdstevel@tonic-gate
16427c478bdstevel@tonic-gate	if (r != 0) {
16436140201lianep		/*
16446140201lianep		 * There's something amiss with our service
16456140201lianep		 * so we'll print a '-' for STIME.
16466140201lianep		 */
16477c478bdstevel@tonic-gate		(void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "",
16486140201lianep		    STIME_COLUMN_WIDTH + 1, "-");
16496140201lianep	} else {
16506140201lianep		/* tv should be valid so we'll format it */
16516140201lianep		then = (time_t)tv.tv_sec;
16527c478bdstevel@tonic-gate
16536140201lianep		tm = localtime(&then);
16546140201lianep		/*
16556140201lianep		 * Print time if started within the past 24 hours, print date
16566140201lianep		 * if within the past 12 months or, finally, print year if
16576140201lianep		 * started greater than 12 months ago.
16586140201lianep		 */
16596140201lianep		if (now - then < 24 * 60 * 60) {
16606140201lianep			(void) strftime(st_buf, sizeof (st_buf),
16616140201lianep			    gettext(FORMAT_TIME), tm);
16626140201lianep		} else if (now - then < 12 * 30 * 24 * 60 * 60) {
16636140201lianep			(void) strftime(st_buf, sizeof (st_buf),
16646140201lianep			    gettext(FORMAT_DATE), tm);
16656140201lianep		} else {
16666140201lianep			(void) strftime(st_buf, sizeof (st_buf),
16676140201lianep			    gettext(FORMAT_YEAR), tm);
16686140201lianep		}
16696140201lianep		(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
16706140201lianep		    STIME_COLUMN_WIDTH + 1, st_buf);
16716140201lianep	}
16727c478bdstevel@tonic-gate	if (*buf)
16737c478bdstevel@tonic-gate		free(*buf);
16747c478bdstevel@tonic-gate	*buf = newbuf;
16757c478bdstevel@tonic-gate}
16767c478bdstevel@tonic-gate
16777c478bdstevel@tonic-gate#define	STIME_SORTKEY_WIDTH		(sizeof (uint64_t) + sizeof (uint32_t))
16787c478bdstevel@tonic-gate
16797c478bdstevel@tonic-gate/* ARGSUSED */
16807c478bdstevel@tonic-gatestatic void
16817c478bdstevel@tonic-gatesortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip)
16827c478bdstevel@tonic-gate{
16837c478bdstevel@tonic-gate	struct timeval tv;
16847c478bdstevel@tonic-gate	int r;
16857c478bdstevel@tonic-gate
16867c478bdstevel@tonic-gate	if (wip->pg == NULL)
16877c478bdstevel@tonic-gate		r = get_restarter_time_prop(wip->inst,
16887c478bdstevel@tonic-gate		    SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
16897c478bdstevel@tonic-gate	else
16907c478bdstevel@tonic-gate		r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
16915f8fc37Toomas Soome		    SCF_TYPE_TIME, &tv, 0, 0);
16927c478bdstevel@tonic-gate
16937c478bdstevel@tonic-gate	if (r == 0) {
16947c478bdstevel@tonic-gate		int64_t sec;
16957c478bdstevel@tonic-gate		int32_t us;
16967c478bdstevel@tonic-gate
16977c478bdstevel@tonic-gate		/* Stick it straight into the buffer. */
16987c478bdstevel@tonic-gate		sec = tv.tv_sec;
16997c478bdstevel@tonic-gate		us = tv.tv_usec;
17007c478bdstevel@tonic-gate
17017c478bdstevel@tonic-gate		sec = BE_64(sec);
17027c478bdstevel@tonic-gate		us = BE_32(us);
17037c478bdstevel@tonic-gate		bcopy(&sec, buf, sizeof (sec));
17047c478bdstevel@tonic-gate		bcopy(&us, buf + sizeof (sec), sizeof (us));
17057c478bdstevel@tonic-gate	} else {
17067c478bdstevel@tonic-gate		bzero(buf, STIME_SORTKEY_WIDTH);
17077c478bdstevel@tonic-gate	}
17087c478bdstevel@tonic-gate
17097c478bdstevel@tonic-gate	if (reverse)
17107c478bdstevel@tonic-gate		reverse_bytes(buf, STIME_SORTKEY_WIDTH);
17117c478bdstevel@tonic-gate}
17127c478bdstevel@tonic-gate
1713048b027Bryan Cantrill/* ZONE */
1714048b027Bryan Cantrill#define	ZONE_COLUMN_WIDTH	16
1715048b027Bryan Cantrill/*ARGSUSED*/
1716048b027Bryan Cantrillstatic void
1717048b027Bryan Cantrillsprint_zone(char **buf, scf_walkinfo_t *wip)
1718048b027Bryan Cantrill{
1719048b027Bryan Cantrill	size_t newsize;
1720048b027Bryan Cantrill	char *newbuf, *zonename = g_zonename, b[ZONENAME_MAX];
1721048b027Bryan Cantrill
1722048b027Bryan Cantrill	if (zonename == NULL) {
1723048b027Bryan Cantrill		zoneid_t zoneid = getzoneid();
1724048b027Bryan Cantrill
1725048b027Bryan Cantrill		if (getzonenamebyid(zoneid, b, sizeof (b)) < 0)
1726048b027Bryan Cantrill			uu_die(gettext("could not determine zone name"));
1727048b027Bryan Cantrill
1728048b027Bryan Cantrill		zonename = b;
1729048b027Bryan Cantrill	}
1730048b027Bryan Cantrill
1731048b027Bryan Cantrill	if (strlen(zonename) > ZONE_COLUMN_WIDTH)
1732048b027Bryan Cantrill		newsize = (*buf ? strlen(*buf) : 0) + strlen(zonename) + 2;
1733048b027Bryan Cantrill	else
1734048b027Bryan Cantrill		newsize = (*buf ? strlen(*buf) : 0) + ZONE_COLUMN_WIDTH + 2;
1735048b027Bryan Cantrill
1736048b027Bryan Cantrill	newbuf = safe_malloc(newsize);
1737048b027Bryan Cantrill	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1738048b027Bryan Cantrill	    ZONE_COLUMN_WIDTH, zonename);
1739048b027Bryan Cantrill
1740048b027Bryan Cantrill	if (*buf)
1741048b027Bryan Cantrill		free(*buf);
1742048b027Bryan Cantrill	*buf = newbuf;
1743048b027Bryan Cantrill}
1744048b027Bryan Cantrill
1745048b027Bryan Cantrillstatic void
1746048b027Bryan Cantrillsortkey_zone(char *buf, int reverse, scf_walkinfo_t *wip)
1747048b027Bryan Cantrill{
1748048b027Bryan Cantrill	char *tmp = NULL;
1749048b027Bryan Cantrill
1750048b027Bryan Cantrill	sprint_zone(&tmp, wip);
1751048b027Bryan Cantrill	bcopy(tmp, buf, ZONE_COLUMN_WIDTH);
1752048b027Bryan Cantrill	free(tmp);
1753048b027Bryan Cantrill	if (reverse)
1754048b027Bryan Cantrill		reverse_bytes(buf, ZONE_COLUMN_WIDTH);
1755048b027Bryan Cantrill}
17567c478bdstevel@tonic-gate
17577c478bdstevel@tonic-gate/*
17587c478bdstevel@tonic-gate * Information about columns which can be displayed.  If you add something,
17597c478bdstevel@tonic-gate * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below.
17607c478bdstevel@tonic-gate */
17617c478bdstevel@tonic-gatestatic const struct column columns[] = {
17627c478bdstevel@tonic-gate	{ "CTID", CTID_COLUMN_WIDTH, sprint_ctid,
17637c478bdstevel@tonic-gate		CTID_SORTKEY_WIDTH, sortkey_ctid },
17647c478bdstevel@tonic-gate	{ "DESC", DESC_COLUMN_WIDTH, sprint_desc,
17657c478bdstevel@tonic-gate		DESC_COLUMN_WIDTH, sortkey_desc },
17667c478bdstevel@tonic-gate	{ "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri,
17677c478bdstevel@tonic-gate		FMRI_COLUMN_WIDTH, sortkey_fmri },
17687c478bdstevel@tonic-gate	{ "INST", COMPONENT_COLUMN_WIDTH, sprint_instance,
17697c478bdstevel@tonic-gate		COMPONENT_COLUMN_WIDTH, sortkey_instance },
17707c478bdstevel@tonic-gate	{ "N", 1,  sprint_n, 1, sortkey_nstate },
17717c478bdstevel@tonic-gate	{ "NSTA", 4, sprint_nsta, 1, sortkey_nstate },
17727c478bdstevel@tonic-gate	{ "NSTATE", MAX_SCF_STATE_STRING_SZ - 1, sprint_nstate,
17737c478bdstevel@tonic-gate		1, sortkey_nstate },
17747c478bdstevel@tonic-gate	{ "S", 2, sprint_s, 1, sortkey_state },
17757c478bdstevel@tonic-gate	{ "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope,
17767c478bdstevel@tonic-gate		COMPONENT_COLUMN_WIDTH, sortkey_scope },
17777c478bdstevel@tonic-gate	{ "SN", 2, sprint_sn, 2, sortkey_sn },
17787c478bdstevel@tonic-gate	{ "SVC", COMPONENT_COLUMN_WIDTH, sprint_service,
17797c478bdstevel@tonic-gate		COMPONENT_COLUMN_WIDTH, sortkey_service },
17807c478bdstevel@tonic-gate	{ "STA", 4, sprint_sta, 1, sortkey_state },
17817c478bdstevel@tonic-gate	{ "STATE", MAX_SCF_STATE_STRING_SZ - 1 + 1, sprint_state,
17827c478bdstevel@tonic-gate		1, sortkey_state },
17837c478bdstevel@tonic-gate	{ "STIME", STIME_COLUMN_WIDTH, sprint_stime,
17847c478bdstevel@tonic-gate		STIME_SORTKEY_WIDTH, sortkey_stime },
1785048b027Bryan Cantrill	{ "ZONE", ZONE_COLUMN_WIDTH, sprint_zone,
1786048b027Bryan Cantrill		ZONE_COLUMN_WIDTH, sortkey_zone },
17877c478bdstevel@tonic-gate};
17887c478bdstevel@tonic-gate
17897c478bdstevel@tonic-gate#define	MAX_COLUMN_NAME_LENGTH_STR	"6"
17907c478bdstevel@tonic-gate
17917c478bdstevel@tonic-gatestatic const int ncolumns = sizeof (columns) / sizeof (columns[0]);
17927c478bdstevel@tonic-gate
17937c478bdstevel@tonic-gate/*
17947c478bdstevel@tonic-gate * Necessary thanks to gettext() & xgettext.
17957c478bdstevel@tonic-gate */
17967c478bdstevel@tonic-gatestatic const char *
17977c478bdstevel@tonic-gatedescription_of_column(int c)
17987c478bdstevel@tonic-gate{
17997c478bdstevel@tonic-gate	const char *s = NULL;
18007c478bdstevel@tonic-gate
18017c478bdstevel@tonic-gate	switch (c) {
18027c478bdstevel@tonic-gate	case 0:
18037c478bdstevel@tonic-gate		s = gettext("contract ID for service (see contract(4))");
18047c478bdstevel@tonic-gate		break;
18057c478bdstevel@tonic-gate	case 1:
18067c478bdstevel@tonic-gate		s = gettext("human-readable description of the service");
18077c478bdstevel@tonic-gate		break;
18087c478bdstevel@tonic-gate	case 2:
18097c478bdstevel@tonic-gate		s = gettext("Fault Managed Resource Identifier for service");
18107c478bdstevel@tonic-gate		break;
18117c478bdstevel@tonic-gate	case 3:
18127c478bdstevel@tonic-gate		s = gettext("portion of the FMRI indicating service instance");
18137c478bdstevel@tonic-gate		break;
18147c478bdstevel@tonic-gate	case 4:
18157c478bdstevel@tonic-gate		s = gettext("abbreviation for next state (if in transition)");
18167c478bdstevel@tonic-gate		break;
18177c478bdstevel@tonic-gate	case 5:
18187c478bdstevel@tonic-gate		s = gettext("abbreviation for next state (if in transition)");
18197c478bdstevel@tonic-gate		break;
18207c478bdstevel@tonic-gate	case 6:
18217c478bdstevel@tonic-gate		s = gettext("name for next state (if in transition)");
18227c478bdstevel@tonic-gate		break;
18237c478bdstevel@tonic-gate	case 7:
18247c478bdstevel@tonic-gate		s = gettext("abbreviation for current state");
18257c478bdstevel@tonic-gate		break;
18267c478bdstevel@tonic-gate	case 8:
18277c478bdstevel@tonic-gate		s = gettext("name for scope associated with service");
18287c478bdstevel@tonic-gate		break;
18297c478bdstevel@tonic-gate	case 9:
18307c478bdstevel@tonic-gate		s = gettext("abbreviation for current state and next state");
18317c478bdstevel@tonic-gate		break;
18327c478bdstevel@tonic-gate	case 10:
18337c478bdstevel@tonic-gate		s = gettext("portion of the FMRI representing service name");
18347c478bdstevel@tonic-gate		break;
18357c478bdstevel@tonic-gate	case 11:
18367c478bdstevel@tonic-gate		s = gettext("abbreviation for current state");
18377c478bdstevel@tonic-gate		break;
18387c478bdstevel@tonic-gate	case 12:
18397c478bdstevel@tonic-gate		s = gettext("name for current state");
18407c478bdstevel@tonic-gate		break;
18417c478bdstevel@tonic-gate	case 13:
18427c478bdstevel@tonic-gate		s = gettext("time of last state change");
18437c478bdstevel@tonic-gate		break;
1844048b027Bryan Cantrill	case 14:
1845048b027Bryan Cantrill		s = gettext("name of zone");
1846048b027Bryan Cantrill		break;
18477c478bdstevel@tonic-gate	}
18487c478bdstevel@tonic-gate
18497c478bdstevel@tonic-gate	assert(s != NULL);
18507c478bdstevel@tonic-gate	return (s);
18517c478bdstevel@tonic-gate}
18527c478bdstevel@tonic-gate
18537c478bdstevel@tonic-gate
18547c478bdstevel@tonic-gatestatic void
1855a5f0d1fToomas Soomeprint_usage(const char *progname, FILE *f)
18567c478bdstevel@tonic-gate{
18577c478bdstevel@tonic-gate	(void) fprintf(f, gettext(
18587c478bdstevel@tonic-gate	    "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] "
1859048b027Bryan Cantrill	    "[-sS col] [-Z | -z zone ]\n            [<service> ...]\n"
18607c478bdstevel@tonic-gate	    "       %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] "
1861048b027Bryan Cantrill	    "[-Z | -z zone ]\n            [<service> ...]\n"
1862d5c6878Bryan Cantrill	    "       %1$s [-l | -L] [-Z | -z zone] <service> ...\n"
1863048b027Bryan Cantrill	    "       %1$s -x [-v] [-Z | -z zone] [<service> ...]\n"
18647c478bdstevel@tonic-gate	    "       %1$s -?\n"), progname);
18657c478bdstevel@tonic-gate}
18667c478bdstevel@tonic-gate
1867a5f0d1fToomas Soomestatic __NORETURN void
1868a5f0d1fToomas Soomeargserr(const char *progname)
1869a5f0d1fToomas Soome{
1870a5f0d1fToomas Soome	print_usage(progname, stderr);
1871a5f0d1fToomas Soome	exit(UU_EXIT_USAGE);
1872a5f0d1fToomas Soome}
18737c478bdstevel@tonic-gate
18747c478bdstevel@tonic-gatestatic void
18757c478bdstevel@tonic-gateprint_help(const char *progname)
18767c478bdstevel@tonic-gate{
18777c478bdstevel@tonic-gate	int i;
18787c478bdstevel@tonic-gate
1879a5f0d1fToomas Soome	print_usage(progname, stdout);
18807c478bdstevel@tonic-gate
18817c478bdstevel@tonic-gate	(void) printf(gettext("\n"
18827c478bdstevel@tonic-gate	"\t-a  list all service instances rather than "
18837c478bdstevel@tonic-gate	"only those that are enabled\n"
18847c478bdstevel@tonic-gate	"\t-d  list dependencies of the specified service(s)\n"
18857c478bdstevel@tonic-gate	"\t-D  list dependents of the specified service(s)\n"
18867c478bdstevel@tonic-gate	"\t-H  omit header line from output\n"
18877c478bdstevel@tonic-gate	"\t-l  list detailed information about the specified service(s)\n"
1888d5c6878Bryan Cantrill	"\t-L  list the log file associated with the specified service(s)\n"
18897c478bdstevel@tonic-gate	"\t-o  list only the specified columns in the output\n"
18907c478bdstevel@tonic-gate	"\t-p  list process IDs and names associated with each service\n"
18917c478bdstevel@tonic-gate	"\t-R  list only those services with the specified restarter\n"
18927c478bdstevel@tonic-gate	"\t-s  sort output in ascending order by the specified column(s)\n"
18937c478bdstevel@tonic-gate	"\t-S  sort output in descending order by the specified column(s)\n"
18947c478bdstevel@tonic-gate	"\t-v  list verbose information appropriate to the type of output\n"
18957c478bdstevel@tonic-gate	"\t-x  explain the status of services that might require maintenance,\n"
18967c478bdstevel@tonic-gate	"\t    or explain the status of the specified service(s)\n"
1897048b027Bryan Cantrill	"\t-z  from global zone, show services in a specified zone\n"
1898048b027Bryan Cantrill	"\t-Z  from global zone, show services in all zones\n"
18997c478bdstevel@tonic-gate	"\n\t"
19007c478bdstevel@tonic-gate	"Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
19017c478bdstevel@tonic-gate	"\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
19027c478bdstevel@tonic-gate	"\n"
19037c478bdstevel@tonic-gate	"\t%1$s [opts] svc:/network/smtp:sendmail\n"
19047c478bdstevel@tonic-gate	"\t%1$s [opts] network/smtp:sendmail\n"
19057c478bdstevel@tonic-gate	"\t%1$s [opts] network/*mail\n"
19067c478bdstevel@tonic-gate	"\t%1$s [opts] network/smtp\n"
19077c478bdstevel@tonic-gate	"\t%1$s [opts] smtp:sendmail\n"
19087c478bdstevel@tonic-gate	"\t%1$s [opts] smtp\n"
19097c478bdstevel@tonic-gate	"\t%1$s [opts] sendmail\n"
19107c478bdstevel@tonic-gate	"\n\t"
19117c478bdstevel@tonic-gate	"Columns for output or sorting can be specified using these names:\n"
19127c478bdstevel@tonic-gate	"\n"), progname);
19137c478bdstevel@tonic-gate
19147c478bdstevel@tonic-gate	for (i = 0; i < ncolumns; i++) {
19157c478bdstevel@tonic-gate		(void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR "s  %s\n",
19167c478bdstevel@tonic-gate		    columns[i].name, description_of_column(i));
19177c478bdstevel@tonic-gate	}
19187c478bdstevel@tonic-gate}
19197c478bdstevel@tonic-gate
19207c478bdstevel@tonic-gate
19217c478bdstevel@tonic-gate/*
19227c478bdstevel@tonic-gate * A getsubopt()-like function which returns an index into the columns table.
19237c478bdstevel@tonic-gate * On success, *optionp is set to point to the next sub-option, or the
19247c478bdstevel@tonic-gate * terminating null if there are none.
19257c478bdstevel@tonic-gate */
19267c478bdstevel@tonic-gatestatic int
19277c478bdstevel@tonic-gategetcolumnopt(char **optionp)
19287c478bdstevel@tonic-gate{
19297c478bdstevel@tonic-gate	char *str = *optionp, *cp;
19307c478bdstevel@tonic-gate	int i;
19317c478bdstevel@tonic-gate
19327c478bdstevel@tonic-gate	assert(optionp != NULL);
19337c478bdstevel@tonic-gate	assert(*optionp != NULL);
19347c478bdstevel@tonic-gate
19357c478bdstevel@tonic-gate	cp = strchr(*optionp, ',');
19367c478bdstevel@tonic-gate	if (cp != NULL)
19377c478bdstevel@tonic-gate		*cp = '\0';
19387c478bdstevel@tonic-gate
19397c478bdstevel@tonic-gate	for (i = 0; i < ncolumns; ++i) {
19407c478bdstevel@tonic-gate		if (strcasecmp(str, columns[i].name) == 0) {
19417c478bdstevel@tonic-gate			if (cp != NULL)
19427c478bdstevel@tonic-gate				*optionp = cp + 1;
19437c478bdstevel@tonic-gate			else
19447c478bdstevel@tonic-gate				*optionp = strchr(*optionp, '\0');
19457c478bdstevel@tonic-gate
19467c478bdstevel@tonic-gate			return (i);
19477c478bdstevel@tonic-gate		}
19487c478bdstevel@tonic-gate	}
19497c478bdstevel@tonic-gate
19507c478bdstevel@tonic-gate	return (-1);
19517c478bdstevel@tonic-gate}
19527c478bdstevel@tonic-gate
19537c478bdstevel@tonic-gatestatic void
19547c478bdstevel@tonic-gateprint_header()
19557c478bdstevel@tonic-gate{
19567c478bdstevel@tonic-gate	int i;
19577c478bdstevel@tonic-gate	char *line_buf, *cp;
19587c478bdstevel@tonic-gate
19597c478bdstevel@tonic-gate	line_buf = safe_malloc(line_sz);
19607c478bdstevel@tonic-gate	cp = line_buf;
19617c478bdstevel@tonic-gate	for (i = 0; i < opt_cnum; ++i) {
19627c478bdstevel@tonic-gate		const struct column * const colp = &columns[opt_columns[i]];
19637c478bdstevel@tonic-gate
19647c478bdstevel@tonic-gate		(void) snprintf(cp, colp->width + 1, "%-*s", colp->width,
19657c478bdstevel@tonic-gate		    colp->name);
19667c478bdstevel@tonic-gate		cp += colp->width;
19677c478bdstevel@tonic-gate		*cp++ = ' ';
19687c478bdstevel@tonic-gate	}
19697c478bdstevel@tonic-gate
19707c478bdstevel@tonic-gate	/* Trim the trailing whitespace */
19717c478bdstevel@tonic-gate	--cp;
19727c478bdstevel@tonic-gate	while (*cp == ' ')
19737c478bdstevel@tonic-gate		--cp;
19747c478bdstevel@tonic-gate	*(cp+1) = '\0';
19757c478bdstevel@tonic-gate	(void) puts(line_buf);
19767c478bdstevel@tonic-gate
19777c478bdstevel@tonic-gate	free(line_buf);
19787c478bdstevel@tonic-gate}
19797c478bdstevel@tonic-gate
19807c478bdstevel@tonic-gate
19817c478bdstevel@tonic-gate
19827c478bdstevel@tonic-gate/*
19837c478bdstevel@tonic-gate * Long listing (-l) functions.
19847c478bdstevel@tonic-gate */
19857c478bdstevel@tonic-gate
19867c478bdstevel@tonic-gatestatic int
19877c478bdstevel@tonic-gatepidcmp(const void *l, const void *r)
19887c478bdstevel@tonic-gate{
19897c478bdstevel@tonic-gate	pid_t lp = *(pid_t *)l, rp = *(pid_t *)r;
19907c478bdstevel@tonic-gate
19917c478bdstevel@tonic-gate	if (lp < rp)
19927c478bdstevel@tonic-gate		return (-1);
19937c478bdstevel@tonic-gate	if (lp > rp)
19947c478bdstevel@tonic-gate		return (1);
19957c478bdstevel@tonic-gate	return (0);
19967c478bdstevel@tonic-gate}
19977c478bdstevel@tonic-gate
19987c478bdstevel@tonic-gate/*
19997c478bdstevel@tonic-gate * This is the strlen() of the longest label ("description"), plus intercolumn
20007c478bdstevel@tonic-gate * space.
20017c478bdstevel@tonic-gate */
20027c478bdstevel@tonic-gate#define	DETAILED_WIDTH	(11 + 2)
20037c478bdstevel@tonic-gate
200494501b6skamm/*
200594501b6skamm * Callback routine to print header for contract id.
200694501b6skamm * Called by ctids_by_restarter and print_detailed.
200794501b6skamm */
200894501b6skammstatic void
200994501b6skammprint_ctid_header()
201094501b6skamm{
201194501b6skamm	(void) printf("%-*s", DETAILED_WIDTH, "contract_id");
201294501b6skamm}
201394501b6skamm
201494501b6skamm/*
201594501b6skamm * Callback routine to print a contract id.
201694501b6skamm * Called by ctids_by_restarter and print_detailed.
201794501b6skamm */
201894501b6skammstatic void
201994501b6skammprint_ctid_detailed(uint64_t c)
202094501b6skamm{
202194501b6skamm	(void) printf("%lu ", (ctid_t)c);
202294501b6skamm}
202394501b6skamm
20247c478bdstevel@tonic-gatestatic void
202594501b6skammdetailed_list_processes(scf_walkinfo_t *wip)
20267c478bdstevel@tonic-gate{
20277c478bdstevel@tonic-gate	uint64_t c;
20287c478bdstevel@tonic-gate	pid_t *pids;
20297c478bdstevel@tonic-gate	uint_t i, n;
20307c478bdstevel@tonic-gate	psinfo_t psi;
20317c478bdstevel@tonic-gate
203294501b6skamm	if (get_restarter_count_prop(wip->inst, scf_property_contract, &c,
20337c478bdstevel@tonic-gate	    EMPTY_OK) != 0)
20347c478bdstevel@tonic-gate		return;
20357c478bdstevel@tonic-gate
203694501b6skamm	if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
20377c478bdstevel@tonic-gate		return;
20387c478bdstevel@tonic-gate
20397c478bdstevel@tonic-gate	qsort(pids, n, sizeof (*pids), pidcmp);
20407c478bdstevel@tonic-gate
20417c478bdstevel@tonic-gate	for (i = 0; i < n; ++i) {
20427c478bdstevel@tonic-gate		(void) printf("%-*s%lu", DETAILED_WIDTH, gettext("process"),
20437c478bdstevel@tonic-gate		    pids[i]);
20447c478bdstevel@tonic-gate
204581ff72cJason King		if (proc_get_psinfo(pids[i], &psi) == 0 && !IS_ZOMBIE(&psi))
20467c478bdstevel@tonic-gate			(void) printf(" %.*s", PRARGSZ, psi.pr_psargs);
20477c478bdstevel@tonic-gate
20487c478bdstevel@tonic-gate		(void) putchar('\n');
20497c478bdstevel@tonic-gate	}
20507c478bdstevel@tonic-gate
20517c478bdstevel@tonic-gate	free(pids);
20527c478bdstevel@tonic-gate}
20537c478bdstevel@tonic-gate
20547c478bdstevel@tonic-gate/*
20557c478bdstevel@tonic-gate * Determines the state of a dependency.  If the FMRI specifies a file, then we
20567c478bdstevel@tonic-gate * fake up a state based on whether we can access the file.
20577c478bdstevel@tonic-gate */
20587c478bdstevel@tonic-gatestatic void
20597c478bdstevel@tonic-gateget_fmri_state(char *fmri, char *state, size_t state_sz)
20607c478bdstevel@tonic-gate{
20617c478bdstevel@tonic-gate	char *lfmri;
20627c478bdstevel@tonic-gate	const char *svc_name, *inst_name, *pg_name, *path;
20637c478bdstevel@tonic-gate	scf_service_t *svc;
20647c478bdstevel@tonic-gate	scf_instance_t *inst;
20657c478bdstevel@tonic-gate	scf_iter_t *iter;
20667c478bdstevel@tonic-gate
20677c478bdstevel@tonic-gate	lfmri = safe_strdup(fmri);
20687c478bdstevel@tonic-gate
20697c478bdstevel@tonic-gate	/*
20707c478bdstevel@tonic-gate	 * Check for file:// dependencies
20717c478bdstevel@tonic-gate	 */
20727c478bdstevel@tonic-gate	if (scf_parse_file_fmri(lfmri, NULL, &path) == SCF_SUCCESS) {
20737c478bdstevel@tonic-gate		struct stat64 statbuf;
20747c478bdstevel@tonic-gate		const char *msg;
20757c478bdstevel@tonic-gate
20767c478bdstevel@tonic-gate		if (stat64(path, &statbuf) == 0)
20777c478bdstevel@tonic-gate			msg = "online";
20787c478bdstevel@tonic-gate		else if (errno == ENOENT)
20797c478bdstevel@tonic-gate			msg = "absent";
20807c478bdstevel@tonic-gate		else
20817c478bdstevel@tonic-gate			msg = "unknown";
20827c478bdstevel@tonic-gate
20837c478bdstevel@tonic-gate		(void) strlcpy(state, msg, state_s