Home | History | Annotate | Download | only in svcs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * svcs - display attributes of service instances
     29  *
     30  * We have two output formats and six instance selection mechanisms.  The
     31  * primary output format is a line of attributes (selected by -o), possibly
     32  * followed by process description lines (if -p is specified), for each
     33  * instance selected.  The columns available to display are described by the
     34  * struct column columns array.  The columns to actually display are kept in
     35  * the opt_columns array as indicies into the columns array.  The selection
     36  * mechanisms available for this format are service FMRIs (selects all child
     37  * instances), instance FMRIs, instance FMRI glob patterns, instances with
     38  * a certain restarter (-R), dependencies of instances (-d), and dependents of
     39  * instances (-D).  Since the lines must be sorted (per -sS), we'll just stick
     40  * each into a data structure and print them in order when we're done.  To
     41  * avoid listing the same instance twice (when -d and -D aren't given), we'll
     42  * use a hash table of FMRIs to record that we've listed (added to the tree)
     43  * an instance.
     44  *
     45  * The secondary output format (-l "long") is a paragraph of text for the
     46  * services or instances selected.  Not needing to be sorted, it's implemented
     47  * by just calling print_detailed() for each FMRI given.
     48  */
     49 
     50 #include "svcs.h"
     51 
     52 /* Get the byteorder macros to ease sorting. */
     53 #include <sys/types.h>
     54 #include <netinet/in.h>
     55 #include <inttypes.h>
     56 
     57 #include <sys/contract.h>
     58 #include <sys/ctfs.h>
     59 #include <sys/stat.h>
     60 
     61 #include <assert.h>
     62 #include <errno.h>
     63 #include <fcntl.h>
     64 #include <fnmatch.h>
     65 #include <libcontract.h>
     66 #include <libcontract_priv.h>
     67 #include <libintl.h>
     68 #include <libscf.h>
     69 #include <libscf_priv.h>
     70 #include <libuutil.h>
     71 #include <locale.h>
     72 #include <procfs.h>
     73 #include <stdarg.h>
     74 #include <stdio.h>
     75 #include <stdlib.h>
     76 #include <strings.h>
     77 #include <time.h>
     78 
     79 
     80 #ifndef TEXT_DOMAIN
     81 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
     82 #endif /* TEXT_DOMAIN */
     83 
     84 #define	LEGACY_UNKNOWN	"unknown"
     85 
     86 /* Flags for pg_get_single_val() */
     87 #define	EMPTY_OK	0x01
     88 #define	MULTI_OK	0x02
     89 
     90 
     91 /*
     92  * An AVL-storable node for output lines and the keys to sort them by.
     93  */
     94 struct avl_string {
     95 	uu_avl_node_t	node;
     96 	char		*key;
     97 	char		*str;
     98 };
     99 
    100 /*
    101  * For lists of parsed restarter FMRIs.
    102  */
    103 struct pfmri_list {
    104 	const char		*scope;
    105 	const char		*service;
    106 	const char		*instance;
    107 	struct pfmri_list	*next;
    108 };
    109 
    110 
    111 /*
    112  * Globals
    113  */
    114 scf_handle_t *h;
    115 static scf_propertygroup_t *g_pg;
    116 static scf_property_t *g_prop;
    117 static scf_value_t *g_val;
    118 
    119 static size_t line_sz;			/* Bytes in the header line. */
    120 static size_t sortkey_sz;		/* Bytes in sort keys. */
    121 static uu_avl_pool_t *lines_pool;
    122 static uu_avl_t *lines;			/* Output lines. */
    123 int exit_status;
    124 ssize_t max_scf_name_length;
    125 ssize_t max_scf_value_length;
    126 ssize_t max_scf_fmri_length;
    127 static ssize_t max_scf_type_length;
    128 static time_t now;
    129 static struct pfmri_list *restarters = NULL;
    130 static int first_paragraph = 1;		/* For -l mode. */
    131 static char *common_name_buf;		/* Sized for maximal length value. */
    132 char *locale;				/* Current locale. */
    133 
    134 /*
    135  * Pathname storage for path generated from the fmri.
    136  * Used for reading the ctid and (start) pid files for an inetd service.
    137  */
    138 static char genfmri_filename[MAXPATHLEN] = "";
    139 
    140 /* Options */
    141 static int *opt_columns = NULL;		/* Indices into columns to display. */
    142 static int opt_cnum = 0;
    143 static int opt_processes = 0;		/* Print processes? */
    144 static int *opt_sort = NULL;		/* Indices into columns to sort. */
    145 static int opt_snum = 0;
    146 static int opt_nstate_shown = 0;	/* Will nstate be shown? */
    147 static int opt_verbose = 0;
    148 
    149 /* Minimize string constants. */
    150 static const char * const scf_property_state = SCF_PROPERTY_STATE;
    151 static const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE;
    152 static const char * const scf_property_contract = SCF_PROPERTY_CONTRACT;
    153 
    154 
    155 /*
    156  * Utility functions
    157  */
    158 
    159 /*
    160  * For unexpected libscf errors.  The ending newline is necessary to keep
    161  * uu_die() from appending the errno error.
    162  */
    163 #ifndef NDEBUG
    164 void
    165 do_scfdie(const char *file, int line)
    166 {
    167 	uu_die(gettext("%s:%d: Unexpected libscf error: %s.  Exiting.\n"),
    168 	    file, line, scf_strerror(scf_error()));
    169 }
    170 #else
    171 void
    172 scfdie(void)
    173 {
    174 	uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
    175 	    scf_strerror(scf_error()));
    176 }
    177 #endif
    178 
    179 void *
    180 safe_malloc(size_t sz)
    181 {
    182 	void *ptr;
    183 
    184 	ptr = malloc(sz);
    185 	if (ptr == NULL)
    186 		uu_die(gettext("Out of memory"));
    187 
    188 	return (ptr);
    189 }
    190 
    191 char *
    192 safe_strdup(const char *str)
    193 {
    194 	char *cp;
    195 
    196 	cp = strdup(str);
    197 	if (cp == NULL)
    198 		uu_die(gettext("Out of memory.\n"));
    199 
    200 	return (cp);
    201 }
    202 
    203 /*
    204  * FMRI hashtable.  For uniquifing listings.
    205  */
    206 
    207 struct ht_elem {
    208 	const char	*fmri;
    209 	struct ht_elem	*next;
    210 };
    211 
    212 static struct ht_elem	**ht_buckets;
    213 static uint_t		ht_buckets_num;
    214 static uint_t		ht_num;
    215 
    216 static void
    217 ht_init()
    218 {
    219 	ht_buckets_num = 8;
    220 	ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num);
    221 	bzero(ht_buckets, sizeof (*ht_buckets) * ht_buckets_num);
    222 	ht_num = 0;
    223 }
    224 
    225 static uint_t
    226 ht_hash_fmri(const char *fmri)
    227 {
    228 	uint_t h = 0, g;
    229 	const char *p, *k;
    230 
    231 	/* All FMRIs begin with svc:/, so skip that part. */
    232 	assert(strncmp(fmri, "svc:/", sizeof ("svc:/") - 1) == 0);
    233 	k = fmri + sizeof ("svc:/") - 1;
    234 
    235 	/*
    236 	 * Generic hash function from uts/common/os/modhash.c.
    237 	 */
    238 	for (p = k; *p != '\0'; ++p) {
    239 		h = (h << 4) + *p;
    240 		if ((g = (h & 0xf0000000)) != 0) {
    241 			h ^= (g >> 24);
    242 			h ^= g;
    243 		}
    244 	}
    245 
    246 	return (h);
    247 }
    248 
    249 static void
    250 ht_grow()
    251 {
    252 	uint_t new_ht_buckets_num;
    253 	struct ht_elem **new_ht_buckets;
    254 	int i;
    255 
    256 	new_ht_buckets_num = ht_buckets_num * 2;
    257 	assert(new_ht_buckets_num > ht_buckets_num);
    258 	new_ht_buckets =
    259 	    safe_malloc(sizeof (*new_ht_buckets) * new_ht_buckets_num);
    260 	bzero(new_ht_buckets, sizeof (*new_ht_buckets) * new_ht_buckets_num);
    261 
    262 	for (i = 0; i < ht_buckets_num; ++i) {
    263 		struct ht_elem *elem, *next;
    264 
    265 		for (elem = ht_buckets[i]; elem != NULL; elem = next) {
    266 			uint_t h;
    267 
    268 			next = elem->next;
    269 
    270 			h = ht_hash_fmri(elem->fmri);
    271 
    272 			elem->next =
    273 			    new_ht_buckets[h & (new_ht_buckets_num - 1)];
    274 			new_ht_buckets[h & (new_ht_buckets_num - 1)] = elem;
    275 		}
    276 	}
    277 
    278 	free(ht_buckets);
    279 
    280 	ht_buckets = new_ht_buckets;
    281 	ht_buckets_num = new_ht_buckets_num;
    282 }
    283 
    284 /*
    285  * Add an FMRI to the hash table.  Returns 1 if it was already there,
    286  * 0 otherwise.
    287  */
    288 static int
    289 ht_add(const char *fmri)
    290 {
    291 	uint_t h;
    292 	struct ht_elem *elem;
    293 
    294 	h = ht_hash_fmri(fmri);
    295 
    296 	elem = ht_buckets[h & (ht_buckets_num - 1)];
    297 
    298 	for (; elem != NULL; elem = elem->next) {
    299 		if (strcmp(elem->fmri, fmri) == 0)
    300 			return (1);
    301 	}
    302 
    303 	/* Grow when average chain length is over 3. */
    304 	if (ht_num > 3 * ht_buckets_num)
    305 		ht_grow();
    306 
    307 	++ht_num;
    308 
    309 	elem = safe_malloc(sizeof (*elem));
    310 	elem->fmri = strdup(fmri);
    311 	elem->next = ht_buckets[h & (ht_buckets_num - 1)];
    312 	ht_buckets[h & (ht_buckets_num - 1)] = elem;
    313 
    314 	return (0);
    315 }
    316 
    317 
    318 
    319 /*
    320  * Convenience libscf wrapper functions.
    321  */
    322 
    323 /*
    324  * Get the single value of the named property in the given property group,
    325  * which must have type ty, and put it in *vp.  If ty is SCF_TYPE_ASTRING, vp
    326  * is taken to be a char **, and sz is the size of the buffer.  sz is unused
    327  * otherwise.  Return 0 on success, -1 if the property doesn't exist, has the
    328  * wrong type, or doesn't have a single value.  If flags has EMPTY_OK, don't
    329  * complain if the property has no values (but return nonzero).  If flags has
    330  * MULTI_OK and the property has multiple values, succeed with E2BIG.
    331  */
    332 int
    333 pg_get_single_val(scf_propertygroup_t *pg, const char *propname, scf_type_t ty,
    334     void *vp, size_t sz, uint_t flags)
    335 {
    336 	char *buf;
    337 	size_t buf_sz;
    338 	int ret = -1, r;
    339 	boolean_t multi = B_FALSE;
    340 
    341 	assert((flags & ~(EMPTY_OK | MULTI_OK)) == 0);
    342 
    343 	if (scf_pg_get_property(pg, propname, g_prop) == -1) {
    344 		if (scf_error() != SCF_ERROR_NOT_FOUND)
    345 			scfdie();
    346 
    347 		goto out;
    348 	}
    349 
    350 	if (scf_property_is_type(g_prop, ty) != SCF_SUCCESS) {
    351 		if (scf_error() == SCF_ERROR_TYPE_MISMATCH)
    352 			goto misconfigured;
    353 		scfdie();
    354 	}
    355 
    356 	if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) {
    357 		switch (scf_error()) {
    358 		case SCF_ERROR_NOT_FOUND:
    359 			if (flags & EMPTY_OK)
    360 				goto out;
    361 			goto misconfigured;
    362 
    363 		case SCF_ERROR_CONSTRAINT_VIOLATED:
    364 			if (flags & MULTI_OK) {
    365 				multi = B_TRUE;
    366 				break;
    367 			}
    368 			goto misconfigured;
    369 
    370 		case SCF_ERROR_PERMISSION_DENIED:
    371 		default:
    372 			scfdie();
    373 		}
    374 	}
    375 
    376 	switch (ty) {
    377 	case SCF_TYPE_ASTRING:
    378 		r = scf_value_get_astring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1;
    379 		break;
    380 
    381 	case SCF_TYPE_BOOLEAN:
    382 		r = scf_value_get_boolean(g_val, (uint8_t *)vp);
    383 		break;
    384 
    385 	case SCF_TYPE_COUNT:
    386 		r = scf_value_get_count(g_val, (uint64_t *)vp);
    387 		break;
    388 
    389 	case SCF_TYPE_INTEGER:
    390 		r = scf_value_get_integer(g_val, (int64_t *)vp);
    391 		break;
    392 
    393 	case SCF_TYPE_TIME: {
    394 		int64_t sec;
    395 		int32_t ns;
    396 		r = scf_value_get_time(g_val, &sec, &ns);
    397 		((struct timeval *)vp)->tv_sec = sec;
    398 		((struct timeval *)vp)->tv_usec = ns / 1000;
    399 		break;
    400 	}
    401 
    402 	case SCF_TYPE_USTRING:
    403 		r = scf_value_get_ustring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1;
    404 		break;
    405 
    406 	default:
    407 #ifndef NDEBUG
    408 		uu_warn("%s:%d: Unknown type %d.\n", __FILE__, __LINE__, ty);
    409 #endif
    410 		abort();
    411 	}
    412 	if (r != SCF_SUCCESS)
    413 		scfdie();
    414 
    415 	ret = multi ? E2BIG : 0;
    416 	goto out;
    417 
    418 misconfigured:
    419 	buf_sz = max_scf_fmri_length + 1;
    420 	buf = safe_malloc(buf_sz);
    421 	if (scf_property_to_fmri(g_prop, buf, buf_sz) == -1)
    422 		scfdie();
    423 
    424 	uu_warn(gettext("Property \"%s\" is misconfigured.\n"), buf);
    425 
    426 	free(buf);
    427 
    428 out:
    429 	return (ret);
    430 }
    431 
    432 static scf_snapshot_t *
    433 get_running_snapshot(scf_instance_t *inst)
    434 {
    435 	scf_snapshot_t *snap;
    436 
    437 	snap = scf_snapshot_create(h);
    438 	if (snap == NULL)
    439 		scfdie();
    440 
    441 	if (scf_instance_get_snapshot(inst, "running", snap) == 0)
    442 		return (snap);
    443 
    444 	if (scf_error() != SCF_ERROR_NOT_FOUND)
    445 		scfdie();
    446 
    447 	scf_snapshot_destroy(snap);
    448 	return (NULL);
    449 }
    450 
    451 /*
    452  * As pg_get_single_val(), except look the property group up in an
    453  * instance.  If "use_running" is set, and the running snapshot exists,
    454  * do a composed lookup there.  Otherwise, do an (optionally composed)
    455  * lookup on the current values.  Note that lookups using snapshots are
    456  * always composed.
    457  */
    458 int
    459 inst_get_single_val(scf_instance_t *inst, const char *pgname,
    460     const char *propname, scf_type_t ty, void *vp, size_t sz, uint_t flags,
    461     int use_running, int composed)
    462 {
    463 	scf_snapshot_t *snap = NULL;
    464 	int r;
    465 
    466 	if (use_running)
    467 		snap = get_running_snapshot(inst);
    468 	if (composed || use_running)
    469 		r = scf_instance_get_pg_composed(inst, snap, pgname, g_pg);
    470 	else
    471 		r = scf_instance_get_pg(inst, pgname, g_pg);
    472 	if (snap)
    473 		scf_snapshot_destroy(snap);
    474 	if (r == -1)
    475 		return (-1);
    476 
    477 	r = pg_get_single_val(g_pg, propname, ty, vp, sz, flags);
    478 
    479 	return (r);
    480 }
    481 
    482 static int
    483 instance_enabled(scf_instance_t *inst, boolean_t temp)
    484 {
    485 	uint8_t b;
    486 
    487 	if (inst_get_single_val(inst,
    488 	    temp ? SCF_PG_GENERAL_OVR : SCF_PG_GENERAL, SCF_PROPERTY_ENABLED,
    489 	    SCF_TYPE_BOOLEAN, &b, 0, 0, 0, 0) != 0)
    490 		return (-1);
    491 
    492 	return (b ? 1 : 0);
    493 }
    494 
    495 /*
    496  * Get a string property from the restarter property group of the given
    497  * instance.  Return an empty string on normal problems.
    498  */
    499 static void
    500 get_restarter_string_prop(scf_instance_t *inst, const char *pname,
    501     char *buf, size_t buf_sz)
    502 {
    503 	if (inst_get_single_val(inst, SCF_PG_RESTARTER, pname,
    504 	    SCF_TYPE_ASTRING, buf, buf_sz, 0, 0, 1) != 0)
    505 		*buf = '\0';
    506 }
    507 
    508 static int
    509 get_restarter_time_prop(scf_instance_t *inst, const char *pname,
    510     struct timeval *tvp, int ok_if_empty)
    511 {
    512 	int r;
    513 
    514 	r = inst_get_single_val(inst, SCF_PG_RESTARTER, pname, SCF_TYPE_TIME,
    515 	    tvp, NULL, ok_if_empty ? EMPTY_OK : 0, 0, 1);
    516 
    517 	return (r == 0 ? 0 : -1);
    518 }
    519 
    520 static int
    521 get_restarter_count_prop(scf_instance_t *inst, const char *pname, uint64_t *cp,
    522     uint_t flags)
    523 {
    524 	return (inst_get_single_val(inst, SCF_PG_RESTARTER, pname,
    525 	    SCF_TYPE_COUNT, cp, 0, flags, 0, 1));
    526 }
    527 
    528 
    529 /*
    530  * Generic functions
    531  */
    532 
    533 /*
    534  * Return an array of pids associated with the given contract id.
    535  * Returned pids are added to the end of the pidsp array.
    536  */
    537 static void
    538 ctid_to_pids(uint64_t c, pid_t **pidsp, uint_t *np)
    539 {
    540 	ct_stathdl_t ctst;
    541 	uint_t m;
    542 	int fd;
    543 	int r, err;
    544 	pid_t *pids;
    545 
    546 	fd = contract_open(c, NULL, "status", O_RDONLY);
    547 	if (fd < 0)
    548 		return;
    549 
    550 	err = ct_status_read(fd, CTD_ALL, &ctst);
    551 	if (err != 0) {
    552 		uu_warn(gettext("Could not read status of contract "
    553 		    "%ld: %s.\n"), c, strerror(err));
    554 		(void) close(fd);
    555 		return;
    556 	}
    557 
    558 	(void) close(fd);
    559 
    560 	r = ct_pr_status_get_members(ctst, &pids, &m);
    561 	assert(r == 0);
    562 
    563 	if (m == 0) {
    564 		ct_status_free(ctst);
    565 		return;
    566 	}
    567 
    568 	*pidsp = realloc(*pidsp, (*np + m) * sizeof (*pidsp));
    569 	if (*pidsp == NULL)
    570 		uu_die(gettext("Out of memory"));
    571 
    572 	bcopy(pids, *pidsp + *np, m * sizeof (*pids));
    573 	*np += m;
    574 
    575 	ct_status_free(ctst);
    576 }
    577 
    578 static int
    579 propvals_to_pids(scf_propertygroup_t *pg, const char *pname, pid_t **pidsp,
    580     uint_t *np, scf_property_t *prop, scf_value_t *val, scf_iter_t *iter)
    581 {
    582 	scf_type_t ty;
    583 	uint64_t c;
    584 	int r;
    585 
    586 	if (scf_pg_get_property(pg, pname, prop) != 0) {
    587 		if (scf_error() != SCF_ERROR_NOT_FOUND)
    588 			scfdie();
    589 
    590 		return (ENOENT);
    591 	}
    592 
    593 	if (scf_property_type(prop, &ty) != 0)
    594 		scfdie();
    595 
    596 	if (ty != SCF_TYPE_COUNT)
    597 		return (EINVAL);
    598 
    599 	if (scf_iter_property_values(iter, prop) != 0)
    600 		scfdie();
    601 
    602 	for (;;) {
    603 		r = scf_iter_next_value(iter, val);
    604 		if (r == -1)
    605 			scfdie();
    606 		if (r == 0)
    607 			break;
    608 
    609 		if (scf_value_get_count(val, &c) != 0)
    610 			scfdie();
    611 
    612 		ctid_to_pids(c, pidsp, np);
    613 	}
    614 
    615 	return (0);
    616 }
    617 
    618 /*
    619  * Check if instance has general/restarter property that matches
    620  * given string.  Restarter string must be in canonified form.
    621  * Returns 0 for success; -1 otherwise.
    622  */
    623 static int
    624 check_for_restarter(scf_instance_t *inst, const char *restarter)
    625 {
    626 	char	*fmri_buf;
    627 	char	*fmri_buf_canonified = NULL;
    628 	int	ret = -1;
    629 
    630 	if (inst == NULL)
    631 		return (-1);
    632 
    633 	/* Get restarter */
    634 	fmri_buf = safe_malloc(max_scf_fmri_length + 1);
    635 	if (inst_get_single_val(inst, SCF_PG_GENERAL,
    636 	    SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, fmri_buf,
    637 	    max_scf_fmri_length + 1, 0, 0, 1) != 0)
    638 		goto out;
    639 
    640 	fmri_buf_canonified = safe_malloc(max_scf_fmri_length + 1);
    641 	if (scf_canonify_fmri(fmri_buf, fmri_buf_canonified,
    642 	    (max_scf_fmri_length + 1)) < 0)
    643 		goto out;
    644 
    645 	if (strcmp(fmri_buf, restarter) == 0)
    646 		ret = 0;
    647 
    648 out:
    649 	free(fmri_buf);
    650 	if (fmri_buf_canonified)
    651 		free(fmri_buf_canonified);
    652 	return (ret);
    653 }
    654 
    655 /*
    656  * Common code that is used by ctids_by_restarter and pids_by_restarter.
    657  * Checks for a common restarter and if one is available, it generates
    658  * the appropriate filename using wip->fmri and stores that in the
    659  * global genfmri_filename.
    660  *
    661  * Restarters currently supported are: svc:/network/inetd:default
    662  * If a restarter specific action is available, then restarter_spec
    663  * is set to 1.  If a restarter specific action is not available, then
    664  * restarter_spec is set to 0 and a -1 is returned.
    665  *
    666  * Returns:
    667  * 0 if success: restarter specific action found and filename generated
    668  * -1 if restarter specific action not found,
    669  *    if restarter specific action found but an error was encountered
    670  *    during the generation of the wip->fmri based filename
    671  */
    672 static int
    673 common_by_restarter(scf_instance_t *inst, const char *fmri,
    674     int *restarter_specp)
    675 {
    676 	int		ret = -1;
    677 	int		r;
    678 
    679 	/* Check for inetd specific restarter */
    680 	if (check_for_restarter(inst, "svc:/network/inetd:default") != 0) {
    681 		*restarter_specp = 0;
    682 		return (ret);
    683 	}
    684 
    685 	*restarter_specp = 1;
    686 
    687 	/* Get the ctid filename associated with this instance */
    688 	r = gen_filenms_from_fmri(fmri, "ctid", genfmri_filename, NULL);
    689 
    690 	switch (r) {
    691 	case 0:
    692 		break;
    693 
    694 	case -1:
    695 		/*
    696 		 * Unable to get filename from fmri.  Print warning
    697 		 * and return failure with no ctids.
    698 		 */
    699 		uu_warn(gettext("Unable to read contract ids for %s -- "
    700 		    "FMRI is too long\n"), fmri);
    701 		return (ret);
    702 
    703 	case -2:
    704 		/*
    705 		 * The directory didn't exist, so no contracts.
    706 		 * Return failure with no ctids.
    707 		 */
    708 		return (ret);
    709 
    710 	default:
    711 		uu_warn(gettext("%s:%d: gen_filenms_from_fmri() failed with "
    712 		    "unknown error %d\n"), __FILE__, __LINE__, r);
    713 		abort();
    714 	}
    715 
    716 	return (0);
    717 
    718 }
    719 
    720 /*
    721  * Get or print a contract id using a restarter specific action.
    722  *
    723  * If the print_flag is not set, this routine gets the single contract
    724  * id associated with this instance.
    725  * If the print flag is set, then print each contract id found.
    726  *
    727  * Returns:
    728  * 0 if success: restarter specific action found and used with no error
    729  * -1 if restarter specific action not found
    730  * -1 if restarter specific action found, but there was a failure
    731  * -1 if print flag is not set and no contract id is found or multiple
    732  *    contract ids were found
    733  * E2BIG if print flag is not set, MULTI_OK bit in flag is set and multiple
    734  *    contract ids were found
    735  */
    736 static int
    737 ctids_by_restarter(scf_walkinfo_t *wip, uint64_t *cp, int print_flag,
    738     uint_t flags, int *restarter_specp, void (*callback_header)(),
    739     void (*callback_ctid)(uint64_t))
    740 {
    741 	FILE		*fp;
    742 	int		ret = -1;
    743 	int		fscanf_ret;
    744 	uint64_t	cp2;
    745 	int		rest_ret;
    746 
    747 	/* Check if callbacks are needed and were passed in */
    748 	if (print_flag) {
    749 		if ((callback_header == NULL) || (callback_ctid == NULL))
    750 			return (ret);
    751 	}
    752 
    753 	/* Check for restarter specific action and generation of filename */
    754 	rest_ret = common_by_restarter(wip->inst, wip->fmri, restarter_specp);
    755 	if (rest_ret != 0)
    756 		return (rest_ret);
    757 
    758 	/*
    759 	 * If fopen fails, then ctid file hasn't been created yet.
    760 	 * If print_flag is set, this is ok; otherwise fail.
    761 	 */
    762 	if ((fp = fopen(genfmri_filename, "r")) == NULL) {
    763 		if (print_flag)
    764 			return (0);
    765 		goto out;
    766 	}
    767 
    768 	if (print_flag) {
    769 		/*
    770 		 * Print all contract ids that are found.
    771 		 * First callback to print ctid header.
    772 		 */
    773 		callback_header();
    774 
    775 		/* fscanf may not set errno, so be sure to clear it first */
    776 		errno = 0;
    777 		while ((fscanf_ret = fscanf(fp, "%llu", cp)) == 1) {
    778 			/* Callback to print contract id */
    779 			callback_ctid(*cp);
    780 			errno = 0;
    781 		}
    782 		/* EOF is not a failure when no errno. */
    783 		if ((fscanf_ret != EOF) || (errno != 0)) {
    784 			uu_die(gettext("Unable to read ctid file for %s"),
    785 			    wip->fmri);
    786 		}
    787 		(void) putchar('\n');
    788 		ret = 0;
    789 	} else {
    790 		/* Must find 1 ctid or fail */
    791 		if (fscanf(fp, "%llu", cp) == 1) {
    792 			/* If 2nd ctid found - fail */
    793 			if (fscanf(fp, "%llu", &cp2) == 1) {
    794 				if (flags & MULTI_OK)
    795 					ret = E2BIG;
    796 			} else {
    797 				/* Success - found only 1 ctid */
    798 				ret = 0;
    799 			}
    800 		}
    801 	}
    802 	(void) fclose(fp);
    803 
    804 out:
    805 	return (ret);
    806 }
    807 
    808 /*
    809  * Get the process ids associated with an instance using a restarter
    810  * specific action.
    811  *
    812  * Returns:
    813  *	0 if success: restarter specific action found and used with no error
    814  *	-1 restarter specific action not found or if failure
    815  */
    816 static int
    817 pids_by_restarter(scf_instance_t *inst, const char *fmri,
    818     pid_t **pids, uint_t *np, int *restarter_specp)
    819 {
    820 	uint64_t	c;
    821 	FILE		*fp;
    822 	int		fscanf_ret;
    823 	int		rest_ret;
    824 
    825 	/* Check for restarter specific action and generation of filename */
    826 	rest_ret = common_by_restarter(inst, fmri, restarter_specp);
    827 	if (rest_ret != 0)
    828 		return (rest_ret);
    829 
    830 	/*
    831 	 * If fopen fails with ENOENT then the ctid file hasn't been
    832 	 * created yet so return success.
    833 	 * For all other errors - fail with uu_die.
    834 	 */
    835 	if ((fp = fopen(genfmri_filename, "r")) == NULL) {
    836 		if (errno == ENOENT)
    837 			return (0);
    838 		uu_die(gettext("Unable to open ctid file for %s"), fmri);
    839 	}
    840 
    841 	/* fscanf may not set errno, so be sure to clear it first */
    842 	errno = 0;
    843 	while ((fscanf_ret = fscanf(fp, "%llu", &c)) == 1) {
    844 		if (c == 0) {
    845 			(void) fclose(fp);
    846 			uu_die(gettext("ctid file for %s has corrupt data"),
    847 			    fmri);
    848 		}
    849 		ctid_to_pids(c, pids, np);
    850 		errno = 0;
    851 	}
    852 	/* EOF is not a failure when no errno. */
    853 	if ((fscanf_ret != EOF) || (errno != 0)) {
    854 		uu_die(gettext("Unable to read ctid file for %s"), fmri);
    855 	}
    856 
    857 	(void) fclose(fp);
    858 	return (0);
    859 }
    860 
    861 static int
    862 instance_processes(scf_instance_t *inst, const char *fmri,
    863     pid_t **pids, uint_t *np)
    864 {
    865 	scf_iter_t *iter;
    866 	int ret;
    867 	int restarter_spec;
    868 
    869 	/* Use the restarter specific get pids routine, if available. */
    870 	ret = pids_by_restarter(inst, fmri, pids, np, &restarter_spec);
    871 	if (restarter_spec == 1)
    872 		return (ret);
    873 
    874 	if ((iter = scf_iter_create(h)) == NULL)
    875 		scfdie();
    876 
    877 	if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) == 0) {
    878 		*pids = NULL;
    879 		*np = 0;
    880 
    881 		(void) propvals_to_pids(g_pg, scf_property_contract, pids, np,
    882 		    g_prop, g_val, iter);
    883 
    884 		(void) propvals_to_pids(g_pg, SCF_PROPERTY_TRANSIENT_CONTRACT,
    885 		    pids, np, g_prop, g_val, iter);
    886 
    887 		ret = 0;
    888 	} else {
    889 		if (scf_error() != SCF_ERROR_NOT_FOUND)
    890 			scfdie();
    891 
    892 		ret = -1;
    893 	}
    894 
    895 	scf_iter_destroy(iter);
    896 
    897 	return (ret);
    898 }
    899 
    900 static int
    901 get_psinfo(pid_t pid, psinfo_t *psip)
    902 {
    903 	char path[100];
    904 	int fd;
    905 
    906 	(void) snprintf(path, sizeof (path), "/proc/%lu/psinfo", pid);
    907 
    908 	fd = open64(path, O_RDONLY);
    909 	if (fd < 0)
    910 		return (-1);
    911 
    912 	if (read(fd, psip, sizeof (*psip)) < 0)
    913 		uu_die(gettext("Could not read info for process %lu"), pid);
    914 
    915 	(void) close(fd);
    916 
    917 	return (0);
    918 }
    919 
    920 
    921 
    922 /*
    923  * Column sprint and sortkey functions
    924  */
    925 
    926 struct column {
    927 	const char *name;
    928 	int width;
    929 
    930 	/*
    931 	 * This function should write the value for the column into buf, and
    932 	 * grow or allocate buf accordingly.  It should always write at least
    933 	 * width bytes, blanking unused bytes with spaces.  If the field is
    934 	 * greater than the column width we allow it to overlap other columns.
    935 	 * In particular, it shouldn't write any null bytes.  (Though an extra
    936 	 * null byte past the end is currently tolerated.)  If the property
    937 	 * group is non-NULL, then we are dealing with a legacy service.
    938 	 */
    939 	void (*sprint)(char **, scf_walkinfo_t *);
    940 
    941 	int sortkey_width;
    942 
    943 	/*
    944 	 * This function should write sortkey_width bytes into buf which will
    945 	 * cause memcmp() to sort it properly.  (Unlike sprint() above,
    946 	 * however, an extra null byte may overrun the buffer.)  The second
    947 	 * argument controls whether the results are sorted in forward or
    948 	 * reverse order.
    949 	 */
    950 	void (*get_sortkey)(char *, int, scf_walkinfo_t *);
    951 };
    952 
    953 static void
    954 reverse_bytes(char *buf, size_t len)
    955 {
    956 	int i;
    957 
    958 	for (i = 0; i < len; ++i)
    959 		buf[i] = ~buf[i];
    960 }
    961 
    962 /* CTID */
    963 #define	CTID_COLUMN_WIDTH		6
    964 
    965 static void
    966 sprint_ctid(char **buf, scf_walkinfo_t *wip)
    967 {
    968 	int r;
    969 	uint64_t c;
    970 	size_t newsize = (*buf ? strlen(*buf) : 0) + CTID_COLUMN_WIDTH + 2;
    971 	char *newbuf = safe_malloc(newsize);
    972 	int restarter_spec;
    973 
    974 	/*
    975 	 * Use the restarter specific get pids routine, if available.
    976 	 * Only check for non-legacy services (wip->pg == 0).
    977 	 */
    978 	if (wip->pg != NULL) {
    979 		r = pg_get_single_val(wip->pg, scf_property_contract,
    980 		    SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK);
    981 	} else {
    982 		r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
    983 		    NULL, NULL);
    984 		if (restarter_spec == 0) {
    985 			/* No restarter specific routine */
    986 			r = get_restarter_count_prop(wip->inst,
    987 			    scf_property_contract, &c, EMPTY_OK | MULTI_OK);
    988 		}
    989 	}
    990 
    991 	if (r == 0)
    992 		(void) snprintf(newbuf, newsize, "%s%*lu ",
    993 		    *buf ? *buf : "", CTID_COLUMN_WIDTH, (ctid_t)c);
    994 	else if (r == E2BIG)
    995 		(void) snprintf(newbuf, newsize, "%s%*lu* ",
    996 		    *buf ? *buf : "", CTID_COLUMN_WIDTH - 1, (ctid_t)c);
    997 	else
    998 		(void) snprintf(newbuf, newsize, "%s%*s ",
    999 		    *buf ? *buf : "", CTID_COLUMN_WIDTH, "-");
   1000 	if (*buf)
   1001 		free(*buf);
   1002 	*buf = newbuf;
   1003 }
   1004 
   1005 #define	CTID_SORTKEY_WIDTH		(sizeof (uint64_t))
   1006 
   1007 static void
   1008 sortkey_ctid(char *buf, int reverse, scf_walkinfo_t *wip)
   1009 {
   1010 	int r;
   1011 	uint64_t c;
   1012 	int restarter_spec;
   1013 
   1014 	/*
   1015 	 * Use the restarter specific get pids routine, if available.
   1016 	 * Only check for non-legacy services (wip->pg == 0).
   1017 	 */
   1018 	if (wip->pg != NULL) {
   1019 		r = pg_get_single_val(wip->pg, scf_property_contract,
   1020 		    SCF_TYPE_COUNT, &c, 0, EMPTY_OK);
   1021 	} else {
   1022 		r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
   1023 		    NULL, NULL);
   1024 		if (restarter_spec == 0) {
   1025 			/* No restarter specific routine */
   1026 			r = get_restarter_count_prop(wip->inst,
   1027 			    scf_property_contract, &c, EMPTY_OK);
   1028 		}
   1029 	}
   1030 
   1031 	if (r == 0) {
   1032 		/*
   1033 		 * Use the id itself, but it must be big-endian for this to
   1034 		 * work.
   1035 		 */
   1036 		c = BE_64(c);
   1037 
   1038 		bcopy(&c, buf, CTID_SORTKEY_WIDTH);
   1039 	} else {
   1040 		bzero(buf, CTID_SORTKEY_WIDTH);
   1041 	}
   1042 
   1043 	if (reverse)
   1044 		reverse_bytes(buf, CTID_SORTKEY_WIDTH);
   1045 }
   1046 
   1047 /* DESC */
   1048 #define	DESC_COLUMN_WIDTH	100
   1049 
   1050 static void
   1051 sprint_desc(char **buf, scf_walkinfo_t *wip)
   1052 {
   1053 	char *x;
   1054 	size_t newsize;
   1055 	char *newbuf;
   1056 
   1057 	if (common_name_buf == NULL)
   1058 		common_name_buf = safe_malloc(max_scf_value_length + 1);
   1059 
   1060 	bzero(common_name_buf, max_scf_value_length + 1);
   1061 
   1062 	if (wip->pg != NULL) {
   1063 		common_name_buf[0] = '-';
   1064 	} else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
   1065 	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
   1066 	    1, 1) == -1 &&
   1067 	    inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
   1068 	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
   1069 	    1, 1) == -1) {
   1070 		common_name_buf[0] = '-';
   1071 	}
   1072 
   1073 	/*
   1074 	 * Collapse multi-line tm_common_name values into a single line.
   1075 	 */
   1076 	for (x = common_name_buf; *x != '\0'; x++)
   1077 		if (*x == '\n')
   1078 			*x = ' ';
   1079 
   1080 	if (strlen(common_name_buf) > DESC_COLUMN_WIDTH)
   1081 		newsize = (*buf ? strlen(*buf) : 0) +
   1082 		    strlen(common_name_buf) + 1;
   1083 	else
   1084 		newsize = (*buf ? strlen(*buf) : 0) + DESC_COLUMN_WIDTH + 1;
   1085 	newbuf = safe_malloc(newsize);
   1086 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1087 	    DESC_COLUMN_WIDTH, common_name_buf);
   1088 	if (*buf)
   1089 		free(*buf);
   1090 	*buf = newbuf;
   1091 }
   1092 
   1093 /* ARGSUSED */
   1094 static void
   1095 sortkey_desc(char *buf, int reverse, scf_walkinfo_t *wip)
   1096 {
   1097 	bzero(buf, DESC_COLUMN_WIDTH);
   1098 }
   1099 
   1100 /* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */
   1101 
   1102 static char
   1103 state_to_char(const char *state)
   1104 {
   1105 	if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
   1106 		return ('u');
   1107 
   1108 	if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
   1109 		return ('0');
   1110 
   1111 	if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
   1112 		return ('1');
   1113 
   1114 	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
   1115 		return ('m');
   1116 
   1117 	if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
   1118 		return ('d');
   1119 
   1120 	if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
   1121 		return ('D');
   1122 
   1123 	if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
   1124 		return ('L');
   1125 
   1126 	return ('?');
   1127 }
   1128 
   1129 /* Return true if inst is transitioning. */
   1130 static int
   1131 transitioning(scf_instance_t *inst)
   1132 {
   1133 	char nstate_name[MAX_SCF_STATE_STRING_SZ];
   1134 
   1135 	get_restarter_string_prop(inst, scf_property_next_state, nstate_name,
   1136 	    sizeof (nstate_name));
   1137 
   1138 	return (state_to_char(nstate_name) != '?');
   1139 }
   1140 
   1141 /* ARGSUSED */
   1142 static void
   1143 sortkey_states(const char *pname, char *buf, int reverse, scf_walkinfo_t *wip)
   1144 {
   1145 	char state_name[MAX_SCF_STATE_STRING_SZ];
   1146 
   1147 	/*
   1148 	 * Lower numbers are printed first, so these are arranged from least
   1149 	 * interesting ("legacy run") to most interesting (unknown).
   1150 	 */
   1151 	if (wip->pg == NULL) {
   1152 		get_restarter_string_prop(wip->inst, pname, state_name,
   1153 		    sizeof (state_name));
   1154 
   1155 		if (strcmp(state_name, SCF_STATE_STRING_ONLINE) == 0)
   1156 			*buf = 2;
   1157 		else if (strcmp(state_name, SCF_STATE_STRING_DEGRADED) == 0)
   1158 			*buf = 3;
   1159 		else if (strcmp(state_name, SCF_STATE_STRING_OFFLINE) == 0)
   1160 			*buf = 4;
   1161 		else if (strcmp(state_name, SCF_STATE_STRING_MAINT) == 0)
   1162 			*buf = 5;
   1163 		else if (strcmp(state_name, SCF_STATE_STRING_DISABLED) == 0)
   1164 			*buf = 1;
   1165 		else if (strcmp(state_name, SCF_STATE_STRING_UNINIT) == 0)
   1166 			*buf = 6;
   1167 		else
   1168 			*buf = 7;
   1169 	} else
   1170 		*buf = 0;
   1171 
   1172 	if (reverse)
   1173 		*buf = 255 - *buf;
   1174 }
   1175 
   1176 static void
   1177 sprint_state(char **buf, scf_walkinfo_t *wip)
   1178 {
   1179 	char state_name[MAX_SCF_STATE_STRING_SZ + 1];
   1180 	size_t newsize;
   1181 	char *newbuf;
   1182 
   1183 	if (wip->pg == NULL) {
   1184 		get_restarter_string_prop(wip->inst, scf_property_state,
   1185 		    state_name, sizeof (state_name));
   1186 
   1187 		/* Don't print blank fields, to ease parsing. */
   1188 		if (state_name[0] == '\0') {
   1189 			state_name[0] = '-';
   1190 			state_name[1] = '\0';
   1191 		}
   1192 
   1193 		if (!opt_nstate_shown && transitioning(wip->inst)) {
   1194 			/* Append an asterisk if nstate is valid. */
   1195 			(void) strcat(state_name, "*");
   1196 		}
   1197 	} else
   1198 		(void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
   1199 
   1200 	newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 2;
   1201 	newbuf = safe_malloc(newsize);
   1202 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1203 	    MAX_SCF_STATE_STRING_SZ + 1, state_name);
   1204 
   1205 	if (*buf)
   1206 		free(*buf);
   1207 	*buf = newbuf;
   1208 }
   1209 
   1210 static void
   1211 sortkey_state(char *buf, int reverse, scf_walkinfo_t *wip)
   1212 {
   1213 	sortkey_states(scf_property_state, buf, reverse, wip);
   1214 }
   1215 
   1216 static void
   1217 sprint_nstate(char **buf, scf_walkinfo_t *wip)
   1218 {
   1219 	char next_state_name[MAX_SCF_STATE_STRING_SZ];
   1220 	boolean_t blank = 0;
   1221 	size_t newsize;
   1222 	char *newbuf;
   1223 
   1224 	if (wip->pg == NULL) {
   1225 		get_restarter_string_prop(wip->inst, scf_property_next_state,
   1226 		    next_state_name, sizeof (next_state_name));
   1227 
   1228 		/* Don't print blank fields, to ease parsing. */
   1229 		if (next_state_name[0] == '\0' ||
   1230 		    strcmp(next_state_name, SCF_STATE_STRING_NONE) == 0)
   1231 			blank = 1;
   1232 	} else
   1233 		blank = 1;
   1234 
   1235 	if (blank) {
   1236 		next_state_name[0] = '-';
   1237 		next_state_name[1] = '\0';
   1238 	}
   1239 
   1240 	newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 1;
   1241 	newbuf = safe_malloc(newsize);
   1242 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1243 	    MAX_SCF_STATE_STRING_SZ - 1, next_state_name);
   1244 	if (*buf)
   1245 		free(*buf);
   1246 	*buf = newbuf;
   1247 }
   1248 
   1249 static void
   1250 sortkey_nstate(char *buf, int reverse, scf_walkinfo_t *wip)
   1251 {
   1252 	sortkey_states(scf_property_next_state, buf, reverse, wip);
   1253 }
   1254 
   1255 static void
   1256 sprint_s(char **buf, scf_walkinfo_t *wip)
   1257 {
   1258 	char tmp[3];
   1259 	char state_name[MAX_SCF_STATE_STRING_SZ];
   1260 	size_t newsize = (*buf ? strlen(*buf) : 0) + 4;
   1261 	char *newbuf = safe_malloc(newsize);
   1262 
   1263 	if (wip->pg == NULL) {
   1264 		get_restarter_string_prop(wip->inst, scf_property_state,
   1265 		    state_name, sizeof (state_name));
   1266 		tmp[0] = state_to_char(state_name);
   1267 
   1268 		if (!opt_nstate_shown && transitioning(wip->inst))
   1269 			tmp[1] = '*';
   1270 		else
   1271 			tmp[1] = ' ';
   1272 	} else {
   1273 		tmp[0] = 'L';
   1274 		tmp[1] = ' ';
   1275 	}
   1276 	tmp[2] = ' ';
   1277 	(void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "",
   1278 	    3, tmp);
   1279 	if (*buf)
   1280 		free(*buf);
   1281 	*buf = newbuf;
   1282 }
   1283 
   1284 static void
   1285 sprint_n(char **buf, scf_walkinfo_t *wip)
   1286 {
   1287 	char tmp[2];
   1288 	size_t newsize = (*buf ? strlen(*buf) : 0) + 3;
   1289 	char *newbuf = safe_malloc(newsize);
   1290 	char nstate_name[MAX_SCF_STATE_STRING_SZ];
   1291 
   1292 	if (wip->pg == NULL) {
   1293 		get_restarter_string_prop(wip->inst, scf_property_next_state,
   1294 		    nstate_name, sizeof (nstate_name));
   1295 
   1296 		if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
   1297 			tmp[0] = '-';
   1298 		else
   1299 			tmp[0] = state_to_char(nstate_name);
   1300 	} else
   1301 		tmp[0] = '-';
   1302 
   1303 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1304 	    2, tmp);
   1305 	if (*buf)
   1306 		free(*buf);
   1307 	*buf = newbuf;
   1308 }
   1309 
   1310 static void
   1311 sprint_sn(char **buf, scf_walkinfo_t *wip)
   1312 {
   1313 	char tmp[3];
   1314 	size_t newsize = (*buf ? strlen(*buf) : 0) + 4;
   1315 	char *newbuf = safe_malloc(newsize);
   1316 	char nstate_name[MAX_SCF_STATE_STRING_SZ];
   1317 	char state_name[MAX_SCF_STATE_STRING_SZ];
   1318 
   1319 	if (wip->pg == NULL) {
   1320 		get_restarter_string_prop(wip->inst, scf_property_state,
   1321 		    state_name, sizeof (state_name));
   1322 		get_restarter_string_prop(wip->inst, scf_property_next_state,
   1323 		    nstate_name, sizeof (nstate_name));
   1324 		tmp[0] = state_to_char(state_name);
   1325 
   1326 		if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
   1327 			tmp[1] = '-';
   1328 		else
   1329 			tmp[1] = state_to_char(nstate_name);
   1330 	} else {
   1331 		tmp[0] = 'L';
   1332 		tmp[1] = '-';
   1333 	}
   1334 
   1335 	tmp[2] = ' ';
   1336 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1337 	    3, tmp);
   1338 	if (*buf)
   1339 		free(*buf);
   1340 	*buf = newbuf;
   1341 }
   1342 
   1343 /* ARGSUSED */
   1344 static void
   1345 sortkey_sn(char *buf, int reverse, scf_walkinfo_t *wip)
   1346 {
   1347 	sortkey_state(buf, reverse, wip);
   1348 	sortkey_nstate(buf + 1, reverse, wip);
   1349 }
   1350 
   1351 static const char *
   1352 state_abbrev(const char *state)
   1353 {
   1354 	if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
   1355 		return ("UN");
   1356 	if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
   1357 		return ("OFF");
   1358 	if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
   1359 		return ("ON");
   1360 	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
   1361 		return ("MNT");
   1362 	if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
   1363 		return ("DIS");
   1364 	if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
   1365 		return ("DGD");
   1366 	if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
   1367 		return ("LRC");
   1368 
   1369 	return ("?");
   1370 }
   1371 
   1372 static void
   1373 sprint_sta(char **buf, scf_walkinfo_t *wip)
   1374 {
   1375 	char state_name[MAX_SCF_STATE_STRING_SZ];
   1376 	char sta[5];
   1377 	size_t newsize = (*buf ? strlen(*buf) : 0) + 6;
   1378 	char *newbuf = safe_malloc(newsize);
   1379 
   1380 	if (wip->pg == NULL)
   1381 		get_restarter_string_prop(wip->inst, scf_property_state,
   1382 		    state_name, sizeof (state_name));
   1383 	else
   1384 		(void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
   1385 
   1386 	(void) strcpy(sta, state_abbrev(state_name));
   1387 
   1388 	if (wip->pg == NULL && !opt_nstate_shown && transitioning(wip->inst))
   1389 		(void) strcat(sta, "*");
   1390 
   1391 	(void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", sta);
   1392 	if (*buf)
   1393 		free(*buf);
   1394 	*buf = newbuf;
   1395 }
   1396 
   1397 static void
   1398 sprint_nsta(char **buf, scf_walkinfo_t *wip)
   1399 {
   1400 	char state_name[MAX_SCF_STATE_STRING_SZ];
   1401 	size_t newsize = (*buf ? strlen(*buf) : 0) + 6;
   1402 	char *newbuf = safe_malloc(newsize);
   1403 
   1404 	if (wip->pg == NULL)
   1405 		get_restarter_string_prop(wip->inst, scf_property_next_state,
   1406 		    state_name, sizeof (state_name));
   1407 	else
   1408 		(void) strcpy(state_name, SCF_STATE_STRING_NONE);
   1409 
   1410 	if (strcmp(state_name, SCF_STATE_STRING_NONE) == 0)
   1411 		(void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "",
   1412 		    "-");
   1413 	else
   1414 		(void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "",
   1415 		    state_abbrev(state_name));
   1416 	if (*buf)
   1417 		free(*buf);
   1418 	*buf = newbuf;
   1419 }
   1420 
   1421 /* FMRI */
   1422 #define	FMRI_COLUMN_WIDTH	50
   1423 static void
   1424 sprint_fmri(char **buf, scf_walkinfo_t *wip)
   1425 {
   1426 	char *fmri_buf = safe_malloc(max_scf_fmri_length + 1);
   1427 	size_t newsize;
   1428 	char *newbuf;
   1429 
   1430 	if (wip->pg == NULL) {
   1431 		if (scf_instance_to_fmri(wip->inst, fmri_buf,
   1432 		    max_scf_fmri_length + 1) == -1)
   1433 			scfdie();
   1434 	} else {
   1435 		(void) strcpy(fmri_buf, SCF_FMRI_LEGACY_PREFIX);
   1436 		if (pg_get_single_val(wip->pg, SCF_LEGACY_PROPERTY_NAME,
   1437 		    SCF_TYPE_ASTRING, fmri_buf +
   1438 		    sizeof (SCF_FMRI_LEGACY_PREFIX) - 1,
   1439 		    max_scf_fmri_length + 1 -
   1440 		    (sizeof (SCF_FMRI_LEGACY_PREFIX) - 1), 0) != 0)
   1441 			(void) strcat(fmri_buf, LEGACY_UNKNOWN);
   1442 	}
   1443 
   1444 	if (strlen(fmri_buf) > FMRI_COLUMN_WIDTH)
   1445 		newsize = (*buf ? strlen(*buf) : 0) + strlen(fmri_buf) + 2;
   1446 	else
   1447 		newsize = (*buf ? strlen(*buf) : 0) + FMRI_COLUMN_WIDTH + 2;
   1448 	newbuf = safe_malloc(newsize);
   1449 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1450 	    FMRI_COLUMN_WIDTH, fmri_buf);
   1451 	free(fmri_buf);
   1452 	if (*buf)
   1453 		free(*buf);
   1454 	*buf = newbuf;
   1455 }
   1456 
   1457 static void
   1458 sortkey_fmri(char *buf, int reverse, scf_walkinfo_t *wip)
   1459 {
   1460 	char *tmp = NULL;
   1461 
   1462 	sprint_fmri(&tmp, wip);
   1463 	bcopy(tmp, buf, FMRI_COLUMN_WIDTH);
   1464 	free(tmp);
   1465 	if (reverse)
   1466 		reverse_bytes(buf, FMRI_COLUMN_WIDTH);
   1467 }
   1468 
   1469 /* Component columns */
   1470 #define	COMPONENT_COLUMN_WIDTH	20
   1471 static void
   1472 sprint_scope(char **buf, scf_walkinfo_t *wip)
   1473 {
   1474 	char *scope_buf = safe_malloc(max_scf_name_length + 1);
   1475 	size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2;
   1476 	char *newbuf = safe_malloc(newsize);
   1477 
   1478 	assert(wip->scope != NULL);
   1479 
   1480 	if (scf_scope_get_name(wip->scope, scope_buf, max_scf_name_length) < 0)
   1481 		scfdie();
   1482 
   1483 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1484 	    COMPONENT_COLUMN_WIDTH, scope_buf);
   1485 	if (*buf)
   1486 		free(*buf);
   1487 	*buf = newbuf;
   1488 	free(scope_buf);
   1489 }
   1490 
   1491 static void
   1492 sortkey_scope(char *buf, int reverse, scf_walkinfo_t *wip)
   1493 {
   1494 	char *tmp = NULL;
   1495 
   1496 	sprint_scope(&tmp, wip);
   1497 	bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
   1498 	free(tmp);
   1499 	if (reverse)
   1500 		reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
   1501 }
   1502 
   1503 static void
   1504 sprint_service(char **buf, scf_walkinfo_t *wip)
   1505 {
   1506 	char *svc_buf = safe_malloc(max_scf_name_length + 1);
   1507 	char *newbuf;
   1508 	size_t newsize;
   1509 
   1510 	if (wip->pg == NULL) {
   1511 		if (scf_service_get_name(wip->svc, svc_buf,
   1512 		    max_scf_name_length + 1) < 0)
   1513 			scfdie();
   1514 	} else {
   1515 		if (pg_get_single_val(wip->pg, "name", SCF_TYPE_ASTRING,
   1516 		    svc_buf, max_scf_name_length + 1, EMPTY_OK) != 0)
   1517 			(void) strcpy(svc_buf, LEGACY_UNKNOWN);
   1518 	}
   1519 
   1520 
   1521 	if (strlen(svc_buf) > COMPONENT_COLUMN_WIDTH)
   1522 		newsize = (*buf ? strlen(*buf) : 0) + strlen(svc_buf) + 2;
   1523 	else
   1524 		newsize = (*buf ? strlen(*buf) : 0) +
   1525 		    COMPONENT_COLUMN_WIDTH + 2;
   1526 	newbuf = safe_malloc(newsize);
   1527 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1528 	    COMPONENT_COLUMN_WIDTH, svc_buf);
   1529 	free(svc_buf);
   1530 	if (*buf)
   1531 		free(*buf);
   1532 	*buf = newbuf;
   1533 }
   1534 
   1535 static void
   1536 sortkey_service(char *buf, int reverse, scf_walkinfo_t *wip)
   1537 {
   1538 	char *tmp = NULL;
   1539 
   1540 	sprint_service(&tmp, wip);
   1541 	bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
   1542 	free(tmp);
   1543 	if (reverse)
   1544 		reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
   1545 }
   1546 
   1547 /* INST */
   1548 static void
   1549 sprint_instance(char **buf, scf_walkinfo_t *wip)
   1550 {
   1551 	char *tmp = safe_malloc(max_scf_name_length + 1);
   1552 	size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2;
   1553 	char *newbuf = safe_malloc(newsize);
   1554 
   1555 	if (wip->pg == NULL) {
   1556 		if (scf_instance_get_name(wip->inst, tmp,
   1557 		    max_scf_name_length + 1) < 0)
   1558 			scfdie();
   1559 	} else {
   1560 		tmp[0] = '-';
   1561 		tmp[1] = '\0';
   1562 	}
   1563 
   1564 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1565 	    COMPONENT_COLUMN_WIDTH, tmp);
   1566 	if (*buf)
   1567 		free(*buf);
   1568 	*buf = newbuf;
   1569 	free(tmp);
   1570 }
   1571 
   1572 static void
   1573 sortkey_instance(char *buf, int reverse, scf_walkinfo_t *wip)
   1574 {
   1575 	char *tmp = NULL;
   1576 
   1577 	sprint_instance(&tmp, wip);
   1578 	bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
   1579 	free(tmp);
   1580 	if (reverse)
   1581 		reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
   1582 }
   1583 
   1584 /* STIME */
   1585 #define	STIME_COLUMN_WIDTH		8
   1586 #define	FORMAT_TIME			"%k:%M:%S"
   1587 #define	FORMAT_DATE			"%b_%d  "
   1588 #define	FORMAT_YEAR			"%Y    "
   1589 
   1590 /*
   1591  * sprint_stime() will allocate a new buffer and snprintf the services's
   1592  * state timestamp.  If the timestamp is unavailable for some reason
   1593  * a '-' is given instead.
   1594  */
   1595 static void
   1596 sprint_stime(char **buf, scf_walkinfo_t *wip)
   1597 {
   1598 	int r;
   1599 	struct timeval tv;
   1600 	time_t then;
   1601 	struct tm *tm;
   1602 	char st_buf[STIME_COLUMN_WIDTH + 1];
   1603 	size_t newsize = (*buf ? strlen(*buf) : 0) + STIME_COLUMN_WIDTH + 2;
   1604 	char *newbuf = safe_malloc(newsize);
   1605 
   1606 	if (wip->pg == NULL) {
   1607 		r = get_restarter_time_prop(wip->inst,
   1608 		    SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
   1609 	} else {
   1610 		r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
   1611 		    SCF_TYPE_TIME, &tv, NULL, 0);
   1612 	}
   1613 
   1614 	if (r != 0) {
   1615 		/*
   1616 		 * There's something amiss with our service
   1617 		 * so we'll print a '-' for STIME.
   1618 		 */
   1619 		(void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "",
   1620 		    STIME_COLUMN_WIDTH + 1, "-");
   1621 	} else {
   1622 		/* tv should be valid so we'll format it */
   1623 		then = (time_t)tv.tv_sec;
   1624 
   1625 		tm = localtime(&then);
   1626 		/*
   1627 		 * Print time if started within the past 24 hours, print date
   1628 		 * if within the past 12 months or, finally, print year if
   1629 		 * started greater than 12 months ago.
   1630 		 */
   1631 		if (now - then < 24 * 60 * 60) {
   1632 			(void) strftime(st_buf, sizeof (st_buf),
   1633 			    gettext(FORMAT_TIME), tm);
   1634 		} else if (now - then < 12 * 30 * 24 * 60 * 60) {
   1635 			(void) strftime(st_buf, sizeof (st_buf),
   1636 			    gettext(FORMAT_DATE), tm);
   1637 		} else {
   1638 			(void) strftime(st_buf, sizeof (st_buf),
   1639 			    gettext(FORMAT_YEAR), tm);
   1640 		}
   1641 		(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
   1642 		    STIME_COLUMN_WIDTH + 1, st_buf);
   1643 	}
   1644 	if (*buf)
   1645 		free(*buf);
   1646 	*buf = newbuf;
   1647 }
   1648 
   1649 #define	STIME_SORTKEY_WIDTH		(sizeof (uint64_t) + sizeof (uint32_t))
   1650 
   1651 /* ARGSUSED */
   1652 static void
   1653 sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip)
   1654 {
   1655 	struct timeval tv;
   1656 	int r;
   1657 
   1658 	if (wip->pg == NULL)
   1659 		r = get_restarter_time_prop(wip->inst,
   1660 		    SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
   1661 	else
   1662 		r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
   1663 		    SCF_TYPE_TIME, &tv, NULL, 0);
   1664 
   1665 	if (r == 0) {
   1666 		int64_t sec;
   1667 		int32_t us;
   1668 
   1669 		/* Stick it straight into the buffer. */
   1670 		sec = tv.tv_sec;
   1671 		us = tv.tv_usec;
   1672 
   1673 		sec = BE_64(sec);
   1674 		us = BE_32(us);
   1675 		bcopy(&sec, buf, sizeof (sec));
   1676 		bcopy(&us, buf + sizeof (sec), sizeof (us));
   1677 	} else {
   1678 		bzero(buf, STIME_SORTKEY_WIDTH);
   1679 	}
   1680 
   1681 	if (reverse)
   1682 		reverse_bytes(buf, STIME_SORTKEY_WIDTH);
   1683 }
   1684 
   1685 
   1686 /*
   1687  * Information about columns which can be displayed.  If you add something,
   1688  * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below.
   1689  */
   1690 static const struct column columns[] = {
   1691 	{ "CTID", CTID_COLUMN_WIDTH, sprint_ctid,
   1692 		CTID_SORTKEY_WIDTH, sortkey_ctid },
   1693 	{ "DESC", DESC_COLUMN_WIDTH, sprint_desc,
   1694 		DESC_COLUMN_WIDTH, sortkey_desc },
   1695 	{ "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri,
   1696 		FMRI_COLUMN_WIDTH, sortkey_fmri },
   1697 	{ "INST", COMPONENT_COLUMN_WIDTH, sprint_instance,
   1698 		COMPONENT_COLUMN_WIDTH, sortkey_instance },
   1699 	{ "N", 1,  sprint_n, 1, sortkey_nstate },
   1700 	{ "NSTA", 4, sprint_nsta, 1, sortkey_nstate },
   1701 	{ "NSTATE", MAX_SCF_STATE_STRING_SZ - 1, sprint_nstate,
   1702 		1, sortkey_nstate },
   1703 	{ "S", 2, sprint_s, 1, sortkey_state },
   1704 	{ "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope,
   1705 		COMPONENT_COLUMN_WIDTH, sortkey_scope },
   1706 	{ "SN", 2, sprint_sn, 2, sortkey_sn },
   1707 	{ "SVC", COMPONENT_COLUMN_WIDTH, sprint_service,
   1708 		COMPONENT_COLUMN_WIDTH, sortkey_service },
   1709 	{ "STA", 4, sprint_sta, 1, sortkey_state },
   1710 	{ "STATE", MAX_SCF_STATE_STRING_SZ - 1 + 1, sprint_state,
   1711 		1, sortkey_state },
   1712 	{ "STIME", STIME_COLUMN_WIDTH, sprint_stime,
   1713 		STIME_SORTKEY_WIDTH, sortkey_stime },
   1714 };
   1715 
   1716 #define	MAX_COLUMN_NAME_LENGTH_STR	"6"
   1717 
   1718 static const int ncolumns = sizeof (columns) / sizeof (columns[0]);
   1719 
   1720 /*
   1721  * Necessary thanks to gettext() & xgettext.
   1722  */
   1723 static const char *
   1724 description_of_column(int c)
   1725 {
   1726 	const char *s = NULL;
   1727 
   1728 	switch (c) {
   1729 	case 0:
   1730 		s = gettext("contract ID for service (see contract(4))");
   1731 		break;
   1732 	case 1:
   1733 		s = gettext("human-readable description of the service");
   1734 		break;
   1735 	case 2:
   1736 		s = gettext("Fault Managed Resource Identifier for service");
   1737 		break;
   1738 	case 3:
   1739 		s = gettext("portion of the FMRI indicating service instance");
   1740 		break;
   1741 	case 4:
   1742 		s = gettext("abbreviation for next state (if in transition)");
   1743 		break;
   1744 	case 5:
   1745 		s = gettext("abbreviation for next state (if in transition)");
   1746 		break;
   1747 	case 6:
   1748 		s = gettext("name for next state (if in transition)");
   1749 		break;
   1750 	case 7:
   1751 		s = gettext("abbreviation for current state");
   1752 		break;
   1753 	case 8:
   1754 		s = gettext("name for scope associated with service");
   1755 		break;
   1756 	case 9:
   1757 		s = gettext("abbreviation for current state and next state");
   1758 		break;
   1759 	case 10:
   1760 		s = gettext("portion of the FMRI representing service name");
   1761 		break;
   1762 	case 11:
   1763 		s = gettext("abbreviation for current state");
   1764 		break;
   1765 	case 12:
   1766 		s = gettext("name for current state");
   1767 		break;
   1768 	case 13:
   1769 		s = gettext("time of last state change");
   1770 		break;
   1771 	}
   1772 
   1773 	assert(s != NULL);
   1774 	return (s);
   1775 }
   1776 
   1777 
   1778 static void
   1779 print_usage(const char *progname, FILE *f, boolean_t do_exit)
   1780 {
   1781 	(void) fprintf(f, gettext(
   1782 	    "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] "
   1783 	    "[-sS col] [<service> ...]\n"
   1784 	    "       %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] "
   1785 	    "[<service> ...]\n"
   1786 	    "       %1$s -l <service> ...\n"
   1787 	    "       %1$s -x [-v] [<service> ...]\n"
   1788 	    "       %1$s -?\n"), progname);
   1789 
   1790 	if (do_exit)
   1791 		exit(UU_EXIT_USAGE);
   1792 }
   1793 
   1794 #define	argserr(progname)	print_usage(progname, stderr, B_TRUE)
   1795 
   1796 static void
   1797 print_help(const char *progname)
   1798 {
   1799 	int i;
   1800 
   1801 	print_usage(progname, stdout, B_FALSE);
   1802 
   1803 	(void) printf(gettext("\n"
   1804 	"\t-a  list all service instances rather than "
   1805 	"only those that are enabled\n"
   1806 	"\t-d  list dependencies of the specified service(s)\n"
   1807 	"\t-D  list dependents of the specified service(s)\n"
   1808 	"\t-H  omit header line from output\n"
   1809 	"\t-l  list detailed information about the specified service(s)\n"
   1810 	"\t-o  list only the specified columns in the output\n"
   1811 	"\t-p  list process IDs and names associated with each service\n"
   1812 	"\t-R  list only those services with the specified restarter\n"
   1813 	"\t-s  sort output in ascending order by the specified column(s)\n"
   1814 	"\t-S  sort output in descending order by the specified column(s)\n"
   1815 	"\t-v  list verbose information appropriate to the type of output\n"
   1816 	"\t-x  explain the status of services that might require maintenance,\n"
   1817 	"\t    or explain the status of the specified service(s)\n"
   1818 	"\n\t"
   1819 	"Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
   1820 	"\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
   1821 	"\n"
   1822 	"\t%1$s [opts] svc:/network/smtp:sendmail\n"
   1823 	"\t%1$s [opts] network/smtp:sendmail\n"
   1824 	"\t%1$s [opts] network/*mail\n"
   1825 	"\t%1$s [opts] network/smtp\n"
   1826 	"\t%1$s [opts] smtp:sendmail\n"
   1827 	"\t%1$s [opts] smtp\n"
   1828 	"\t%1$s [opts] sendmail\n"
   1829 	"\n\t"
   1830 	"Columns for output or sorting can be specified using these names:\n"
   1831 	"\n"), progname);
   1832 
   1833 	for (i = 0; i < ncolumns; i++) {
   1834 		(void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR "s  %s\n",
   1835 		    columns[i].name, description_of_column(i));
   1836 	}
   1837 }
   1838 
   1839 
   1840 /*
   1841  * A getsubopt()-like function which returns an index into the columns table.
   1842  * On success, *optionp is set to point to the next sub-option, or the
   1843  * terminating null if there are none.
   1844  */
   1845 static int
   1846 getcolumnopt(char **optionp)
   1847 {
   1848 	char *str = *optionp, *cp;
   1849 	int i;
   1850 
   1851 	assert(optionp != NULL);
   1852 	assert(*optionp != NULL);
   1853 
   1854 	cp = strchr(*optionp, ',');
   1855 	if (cp != NULL)
   1856 		*cp = '\0';
   1857 
   1858 	for (i = 0; i < ncolumns; ++i) {
   1859 		if (strcasecmp(str, columns[i].name) == 0) {
   1860 			if (cp != NULL)
   1861 				*optionp = cp + 1;
   1862 			else
   1863 				*optionp = strchr(*optionp, '\0');
   1864 
   1865 			return (i);
   1866 		}
   1867 	}
   1868 
   1869 	return (-1);
   1870 }
   1871 
   1872 static void
   1873 print_header()
   1874 {
   1875 	int i;
   1876 	char *line_buf, *cp;
   1877 
   1878 	line_buf = safe_malloc(line_sz);
   1879 	cp = line_buf;
   1880 	for (i = 0; i < opt_cnum; ++i) {
   1881 		const struct column * const colp = &columns[opt_columns[i]];
   1882 
   1883 		(void) snprintf(cp, colp->width + 1, "%-*s", colp->width,
   1884 		    colp->name);
   1885 		cp += colp->width;
   1886 		*cp++ = ' ';
   1887 	}
   1888 
   1889 	/* Trim the trailing whitespace */
   1890 	--cp;
   1891 	while (*cp == ' ')
   1892 		--cp;
   1893 	*(cp+1) = '\0';
   1894 	(void) puts(line_buf);
   1895 
   1896 	free(line_buf);
   1897 }
   1898 
   1899 
   1900 
   1901 /*
   1902  * Long listing (-l) functions.
   1903  */
   1904 
   1905 static int
   1906 pidcmp(const void *l, const void *r)
   1907 {
   1908 	pid_t lp = *(pid_t *)l, rp = *(pid_t *)r;
   1909 
   1910 	if (lp < rp)
   1911 		return (-1);
   1912 	if (lp > rp)
   1913 		return (1);
   1914 	return (0);
   1915 }
   1916 
   1917 /*
   1918  * This is the strlen() of the longest label ("description"), plus intercolumn
   1919  * space.
   1920  */
   1921 #define	DETAILED_WIDTH	(11 + 2)
   1922 
   1923 /*
   1924  * Callback routine to print header for contract id.
   1925  * Called by ctids_by_restarter and print_detailed.
   1926  */
   1927 static void
   1928 print_ctid_header()
   1929 {
   1930 	(void) printf("%-*s", DETAILED_WIDTH, "contract_id");
   1931 }
   1932 
   1933 /*
   1934  * Callback routine to print a contract id.
   1935  * Called by ctids_by_restarter and print_detailed.
   1936  */
   1937 static void
   1938 print_ctid_detailed(uint64_t c)
   1939 {
   1940 	(void) printf("%lu ", (ctid_t)c);
   1941 }
   1942 
   1943 static void
   1944 detailed_list_processes(scf_walkinfo_t *wip)
   1945 {
   1946 	uint64_t c;
   1947 	pid_t *pids;
   1948 	uint_t i, n;
   1949 	psinfo_t psi;
   1950 
   1951 	if (get_restarter_count_prop(wip->inst, scf_property_contract, &c,
   1952 	    EMPTY_OK) != 0)
   1953 		return;
   1954 
   1955 	if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
   1956 		return;
   1957 
   1958 	qsort(pids, n, sizeof (*pids), pidcmp);
   1959 
   1960 	for (i = 0; i < n; ++i) {
   1961 		(void) printf("%-*s%lu", DETAILED_WIDTH, gettext("process"),
   1962 		    pids[i]);
   1963 
   1964 		if (get_psinfo(pids[i], &psi) == 0)
   1965 			(void) printf(" %.*s", PRARGSZ, psi.pr_psargs);
   1966 
   1967 		(void) putchar('\n');
   1968 	}
   1969 
   1970 	free(pids);
   1971 }
   1972 
   1973 /*
   1974  * Determines the state of a dependency.  If the FMRI specifies a file, then we
   1975  * fake up a state based on whether we can access the file.
   1976  */
   1977 static void
   1978 get_fmri_state(char *fmri, char *state, size_t state_sz)
   1979 {
   1980 	char *lfmri;
   1981 	const char *svc_name, *inst_name, *pg_name, *path;
   1982 	scf_service_t *svc;
   1983 	scf_instance_t *inst;
   1984 	scf_iter_t *iter;
   1985 
   1986 	lfmri = safe_strdup(fmri);
   1987 
   1988 	/*
   1989 	 * Check for file:// dependencies
   1990 	 */
   1991 	if (scf_parse_file_fmri(lfmri, NULL, &path) == SCF_SUCCESS) {
   1992 		struct stat64 statbuf;
   1993 		const char *msg;
   1994 
   1995 		if (stat64(path, &statbuf) == 0)
   1996 			msg = "online";
   1997 		else if (errno == ENOENT)
   1998 			msg = "absent";
   1999 		else
   2000 			msg = "unknown";
   2001 
   2002 		(void) strlcpy(state, msg, state_sz);
   2003 		return;
   2004 	}
   2005 
   2006 	/*
   2007 	 * scf_parse_file_fmri() may have overwritten part of the string, so
   2008 	 * copy it back.
   2009 	 */
   2010 	(void) strcpy(lfmri, fmri);
   2011 
   2012 	if (scf_parse_svc_fmri(lfmri, NULL, &svc_name, &inst_name,
   2013 	    &pg_name, NULL) != SCF_SUCCESS) {
   2014 		free(lfmri);
   2015 		(void) strlcpy(state, "invalid", state_sz);
   2016 		return;
   2017 	}
   2018 
   2019 	free(lfmri);
   2020 
   2021 	if (svc_name == NULL || pg_name != NULL) {
   2022 		(void) strlcpy(state, "invalid", state_sz);
   2023 		return;
   2024 	}
   2025 
   2026 	if (inst_name != NULL) {
   2027 		/* instance: get state */
   2028 		inst = scf_instance_create(h);
   2029 		if (inst == NULL)
   2030 			scfdie();
   2031 
   2032 		if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
   2033 		    NULL, SCF_DECODE_FMRI_EXACT) == SCF_SUCCESS)
   2034 			get_restarter_string_prop(inst, scf_property_state,
   2035 			    state, state_sz);
   2036 		else {
   2037 			switch (scf_error()) {
   2038 			case SCF_ERROR_INVALID_ARGUMENT:
   2039 				(void) strlcpy(state, "invalid", state_sz);
   2040 				break;
   2041 			case SCF_ERROR_NOT_FOUND:
   2042 				(void) strlcpy(state, "absent", state_sz);
   2043 				break;
   2044 
   2045 			default:
   2046 				scfdie();
   2047 			}
   2048 		}
   2049 
   2050 		scf_instance_destroy(inst);
   2051 		return;
   2052 	}
   2053 
   2054 	/*
   2055 	 * service: If only one instance, use that state.  Otherwise, say
   2056 	 * "multiple".
   2057 	 */
   2058 	if ((svc = scf_service_create(h)) == NULL ||
   2059 	    (inst = scf_instance_create(h)) == NULL ||
   2060 	    (iter = scf_iter_create(h)) == NULL)
   2061 		scfdie();
   2062 
   2063 	if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
   2064 	    SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
   2065 		switch (scf_error()) {
   2066 		case SCF_ERROR_INVALID_ARGUMENT:
   2067 			(void) strlcpy(state, "invalid", state_sz);
   2068 			goto out;
   2069 		case SCF_ERROR_NOT_FOUND:
   2070 			(void) strlcpy(state, "absent", state_sz);
   2071 			goto out;
   2072 
   2073 		default:
   2074 			scfdie();
   2075 		}
   2076 	}
   2077 
   2078 	if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
   2079 		scfdie();
   2080 
   2081 	switch (scf_iter_next_instance(iter, inst)) {
   2082 	case 0:
   2083 		(void) strlcpy(state, "absent", state_sz);
   2084 		goto out;
   2085 
   2086 	case 1:
   2087 		break;
   2088 
   2089 	default:
   2090 		scfdie();
   2091 	}
   2092 
   2093 	/* Get the state in case this is the only instance. */
   2094 	get_restarter_string_prop(inst, scf_property_state, state, state_sz);
   2095 
   2096 	switch (scf_iter_next_instance(iter, inst)) {
   2097 	case 0:
   2098 		break;
   2099 
   2100 	case 1:
   2101 		/* Nope, multiple instances. */
   2102 		(void) strlcpy(state, "multiple", state_sz);
   2103 		goto out;
   2104 
   2105 	default:
   2106 		scfdie();
   2107 	}
   2108 
   2109 out:
   2110 	scf_iter_destroy(iter);
   2111 	scf_instance_destroy(inst);
   2112 	scf_service_destroy(svc);
   2113 }
   2114 
   2115 static void
   2116 print_application_properties(scf_walkinfo_t *wip, scf_snapshot_t *snap)
   2117 {
   2118 	scf_iter_t *pg_iter, *prop_iter, *val_iter;
   2119 	scf_propertygroup_t *pg;
   2120 	scf_property_t *prop;
   2121 	scf_value_t *val;
   2122 	scf_pg_tmpl_t *pt;
   2123 	scf_prop_tmpl_t *prt;
   2124 	char *pg_name_buf = safe_malloc(max_scf_name_length + 1);
   2125 	char *prop_name_buf = safe_malloc(max_scf_name_length + 1);
   2126 	char *snap_name = safe_malloc(max_scf_name_length + 1);
   2127 	char *val_buf = safe_malloc(max_scf_value_length + 1);
   2128 	char *desc, *cp;
   2129 	scf_type_t type;
   2130 	int i, j, k;
   2131 	uint8_t vis;
   2132 
   2133 	if ((pg_iter = scf_iter_create(h)) == NULL ||
   2134 	    (prop_iter = scf_iter_create(h)) == NULL ||
   2135 	    (val_iter = scf_iter_create(h)) == NULL ||
   2136 	    (val = scf_value_create(h)) == NULL ||
   2137 	    (prop = scf_property_create(h)) == NULL ||
   2138 	    (pt = scf_tmpl_pg_create(h)) == NULL ||
   2139 	    (prt = scf_tmpl_prop_create(h)) == NULL ||
   2140 	    (pg = scf_pg_create(h)) == NULL)
   2141 		scfdie();
   2142 
   2143 	if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap,
   2144 	    SCF_PG_APP_DEFAULT) == -1)
   2145 		scfdie();
   2146 
   2147 	/*
   2148 	 * Format for output:
   2149 	 *	pg (pgtype)
   2150 	 *	 description
   2151 	 *	pg/prop (proptype) = <value> <value>
   2152 	 *	 description
   2153 	 */
   2154 	while ((i = scf_iter_next_pg(pg_iter, pg)) == 1) {
   2155 		int tmpl = 0;
   2156 
   2157 		if (scf_pg_get_name(pg, pg_name_buf, max_scf_name_length) < 0)
   2158 			scfdie();
   2159 		if (scf_snapshot_get_name(snap, snap_name,
   2160 		    max_scf_name_length) < 0)
   2161 			scfdie();
   2162 
   2163 		if (scf_tmpl_get_by_pg_name(wip->fmri, snap_name, pg_name_buf,
   2164 		    SCF_PG_APP_DEFAULT, pt, 0) == 0)
   2165 			tmpl = 1;
   2166 		else
   2167 			tmpl = 0;
   2168 
   2169 		(void) printf("%s (%s)\n", pg_name_buf, SCF_PG_APP_DEFAULT);
   2170 
   2171 		if (tmpl == 1 && scf_tmpl_pg_description(pt, NULL, &desc) > 0) {
   2172 			(void) printf("  %s\n", desc);
   2173 			free(desc);
   2174 		}
   2175 
   2176 		if (scf_iter_pg_properties(prop_iter, pg) == -1)
   2177 			scfdie();
   2178 		while ((j = scf_iter_next_property(prop_iter, prop)) == 1) {
   2179 			if (scf_property_get_name(prop, prop_name_buf,
   2180 			    max_scf_name_length) < 0)
   2181 				scfdie();
   2182 			if (scf_property_type(prop, &type) == -1)
   2183 				scfdie();
   2184 
   2185 			if ((tmpl == 1) &&
   2186 			    (scf_tmpl_get_by_prop(pt, prop_name_buf, prt,
   2187 			    0) != 0))
   2188 				tmpl = 0;
   2189 
   2190 			if (tmpl == 1 &&
   2191 			    scf_tmpl_prop_visibility(prt, &vis) != -1 &&
   2192 			    vis == SCF_TMPL_VISIBILITY_HIDDEN)
   2193 				continue;
   2194 
   2195 			(void) printf("%s/%s (%s) = ", pg_name_buf,
   2196 			    prop_name_buf, scf_type_to_string(type));
   2197 
   2198 			if (scf_iter_property_values(val_iter, prop) == -1)
   2199 				scfdie();
   2200 
   2201 			while ((k = scf_iter_next_value(val_iter, val)) == 1) {
   2202 				if (scf_value_get_as_string(val, val_buf,
   2203 				    max_scf_value_length + 1) < 0)
   2204 					scfdie();
   2205 				if (strpbrk(val_buf, " \t\n\"()") != NULL) {
   2206 					(void) printf("\"");
   2207 					for (cp = val_buf; *cp != '\0'; ++cp) {
   2208 						if (*cp == '"' || *cp == '\\')
   2209 							(void) putc('\\',
   2210 							    stdout);
   2211 
   2212 						(void) putc(*cp, stdout);
   2213 					}
   2214 					(void) printf("\"");
   2215 				} else {
   2216 					(void) printf("%s ", val_buf);
   2217 				}
   2218 			}
   2219 
   2220 			(void) printf("\n");
   2221 
   2222 			if (k == -1)
   2223 				scfdie();
   2224 
   2225 			if (tmpl == 1 && scf_tmpl_prop_description(prt, NULL,
   2226 			    &desc) > 0) {
   2227 				(void) printf("  %s\n", desc);
   2228 				free(desc);
   2229 			}
   2230 		}
   2231 		if (j == -1)
   2232 			scfdie();
   2233 	}
   2234 	if (i == -1)
   2235 		scfdie();
   2236 
   2237 
   2238 	scf_iter_destroy(pg_iter);
   2239 	scf_iter_destroy(prop_iter);
   2240 	scf_iter_destroy(val_iter);
   2241 	scf_value_destroy(val);
   2242 	scf_property_destroy(prop);
   2243 	scf_tmpl_pg_destroy(pt);
   2244 	scf_tmpl_prop_destroy(prt);
   2245 	scf_pg_destroy(pg);
   2246 	free(pg_name_buf);
   2247 	free(prop_name_buf);
   2248 	free(snap_name);
   2249 	free(val_buf);
   2250 }
   2251 
   2252 static void
   2253 print_detailed_dependency(scf_propertygroup_t *pg)
   2254 {
   2255 	scf_property_t *eprop;
   2256 	scf_iter_t *iter;
   2257 	scf_type_t ty;
   2258 	char *val_buf;
   2259 	int i;
   2260 
   2261 	if ((eprop = scf_property_create(h)) == NULL ||
   2262 	    (iter = scf_iter_create(h)) == NULL)
   2263 		scfdie();
   2264 
   2265 	val_buf = safe_malloc(max_scf_value_length + 1);
   2266 
   2267 	if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, eprop) !=
   2268 	    SCF_SUCCESS ||
   2269 	    scf_property_type(eprop, &ty) != SCF_SUCCESS ||
   2270 	    ty != SCF_TYPE_FMRI)
   2271 		return;
   2272 
   2273 	(void) printf("%-*s", DETAILED_WIDTH, gettext("dependency"));
   2274 
   2275 	/* Print the grouping */
   2276 	if (pg_get_single_val(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
   2277 	    val_buf, max_scf_value_length + 1, 0) == 0)
   2278 		(void) fputs(val_buf, stdout);
   2279 	else
   2280 		(void) putchar('?');
   2281 
   2282 	(void) putchar('/');
   2283 
   2284 	if (pg_get_single_val(pg, SCF_PROPERTY_RESTART_ON, SCF_TYPE_ASTRING,
   2285 	    val_buf, max_scf_value_length + 1, 0) == 0)
   2286 		(void) fputs(val_buf, stdout);
   2287 	else
   2288 		(void) putchar('?');
   2289 
   2290 	/* Print the dependency entities. */
   2291 	if (scf_iter_property_values(iter, eprop) == -1)
   2292 		scfdie();
   2293 
   2294 	while ((i = scf_iter_next_value(iter, g_val)) == 1) {
   2295 		char state[MAX_SCF_STATE_STRING_SZ];
   2296 
   2297 		if (scf_value_get_astring(g_val, val_buf,
   2298 		    max_scf_value_length + 1) < 0)
   2299 			scfdie();
   2300 
   2301 		(void) putchar(' ');
   2302 		(void) fputs(val_buf, stdout);
   2303 
   2304 		/* Print the state. */
   2305 		state[0] = '-';
   2306 		state[1] = '\0';
   2307 
   2308 		get_fmri_state(val_buf, state, sizeof (state));
   2309 
   2310 		(void) printf(" (%s)", state);
   2311 	}
   2312 	if (i == -1)
   2313 		scfdie();
   2314 
   2315 	(void) putchar('\n');
   2316 
   2317 	free(val_buf);
   2318 	scf_iter_destroy(iter);
   2319 	scf_property_destroy(eprop);
   2320 }
   2321 
   2322 /* ARGSUSED */
   2323 static int
   2324 print_detailed(void *unused, scf_walkinfo_t *wip)
   2325 {
   2326 	scf_snapshot_t *snap;
   2327 	scf_propertygroup_t *rpg;
   2328 	scf_iter_t *pg_iter;
   2329 
   2330 	char *buf;
   2331 	char *timebuf;
   2332 	size_t tbsz;
   2333 	int ret;
   2334 	uint64_t c;
   2335 	int temp, perm;
   2336 	struct timeval tv;
   2337 	time_t stime;
   2338 	struct tm *tmp;
   2339 	int restarter_spec;
   2340 	int restarter_ret;
   2341 
   2342 	const char * const fmt = "%-*s%s\n";
   2343 
   2344 	assert(wip->pg == NULL);
   2345 
   2346 	rpg = scf_pg_create(h);
   2347 	if (rpg == NULL)
   2348 		scfdie();
   2349 
   2350 	if (first_paragraph)
   2351 		first_paragraph = 0;
   2352 	else
   2353 		(void) putchar('\n');
   2354 
   2355 	buf = safe_malloc(max_scf_fmri_length + 1);
   2356 
   2357 	if (scf_instance_to_fmri(wip->inst, buf, max_scf_fmri_length + 1) != -1)
   2358 		(void) printf(fmt, DETAILED_WIDTH, "fmri", buf);
   2359 
   2360 	if (common_name_buf == NULL)
   2361 		common_name_buf = safe_malloc(max_scf_value_length + 1);
   2362 
   2363 	if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
   2364 	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
   2365 	    == 0)
   2366 		(void) printf(fmt, DETAILED_WIDTH, gettext("name"),
   2367 		    common_name_buf);
   2368 	else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
   2369 	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
   2370 	    == 0)
   2371 		(void) printf(fmt, DETAILED_WIDTH, gettext("name"),
   2372 		    common_name_buf);
   2373 
   2374 	/*
   2375 	 * Synthesize an 'enabled' property that hides the enabled_ovr
   2376 	 * implementation from the user.  If the service has been temporarily
   2377 	 * set to a state other than its permanent value, alert the user with
   2378 	 * a '(temporary)' message.
   2379 	 */
   2380 	perm = instance_enabled(wip->inst, B_FALSE);
   2381 	temp = instance_enabled(wip->inst, B_TRUE);
   2382 	if (temp != -1) {
   2383 		if (temp != perm)
   2384 			(void) printf(gettext("%-*s%s (temporary)\n"),
   2385 			    DETAILED_WIDTH, gettext("enabled"),
   2386 			    temp ? gettext("true") : gettext("false"));
   2387 		else
   2388 			(void) printf(fmt, DETAILED_WIDTH,
   2389 			    gettext("enabled"), temp ? gettext("true") :
   2390 			    gettext("false"));
   2391 	} else if (perm != -1) {
   2392 		(void) printf(fmt, DETAILED_WIDTH, gettext("enabled"),
   2393 		    perm ? gettext("true") : gettext("false"));
   2394 	}
   2395 
   2396 	/*
   2397 	 * Property values may be longer than max_scf_fmri_length, but these
   2398 	 * shouldn't be, so we'll just reuse buf.  The user can use svcprop if
   2399 	 * he suspects something fishy.
   2400 	 */
   2401 	if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) {
   2402 		if (scf_error() != SCF_ERROR_NOT_FOUND)
   2403 			scfdie();
   2404 
   2405 		scf_pg_destroy(rpg);
   2406 		rpg = NULL;
   2407 	}
   2408 
   2409 	if (rpg) {
   2410 		if (pg_get_single_val(rpg, scf_property_state, SCF_TYPE_ASTRING,
   2411 		    buf, max_scf_fmri_length + 1, 0) == 0)
   2412 			(void) printf(fmt, DETAILED_WIDTH, gettext("state"),
   2413 			    buf);
   2414 
   2415 		if (pg_get_single_val(rpg, scf_property_next_state,
   2416 		    SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
   2417 			(void) printf(fmt, DETAILED_WIDTH,
   2418 			    gettext("next_state"), buf);
   2419 
   2420 		if (pg_get_single_val(rpg, SCF_PROPERTY_STATE_TIMESTAMP,
   2421 		    SCF_TYPE_TIME, &tv, NULL, 0) == 0) {
   2422 			stime = tv.tv_sec;
   2423 			tmp = localtime(&stime);
   2424 			for (tbsz = 50; ; tbsz *= 2) {
   2425 				timebuf = safe_malloc(tbsz);
   2426 				if (strftime(timebuf, tbsz, NULL, tmp) != 0)
   2427 					break;
   2428 				free(timebuf);
   2429 			}
   2430 			(void) printf(fmt, DETAILED_WIDTH,
   2431 			    gettext("state_time"),
   2432 			    timebuf);
   2433 			free(timebuf);
   2434 		}
   2435 
   2436 		if (pg_get_single_val(rpg, SCF_PROPERTY_ALT_LOGFILE,
   2437 		    SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
   2438 			(void) printf(fmt, DETAILED_WIDTH,
   2439 			    gettext("alt_logfile"), buf);
   2440 
   2441 		if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE,
   2442 		    SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
   2443 			(void) printf(fmt, DETAILED_WIDTH, gettext("logfile"),
   2444 			    buf);
   2445 	}
   2446 
   2447 	if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
   2448 	    SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, buf,
   2449 	    max_scf_fmri_length + 1, 0, 0, 1) == 0)
   2450 		(void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), buf);
   2451 	else
   2452 		(void) printf(fmt, DETAILED_WIDTH, gettext("restarter"),
   2453 		    SCF_SERVICE_STARTD);
   2454 
   2455 	free(buf);
   2456 
   2457 	/*
   2458 	 * Use the restarter specific routine to print the ctids, if available.
   2459 	 * If restarter specific action is available and it fails, then die.
   2460 	 */
   2461 	restarter_ret = ctids_by_restarter(wip, &c, 1, 0,
   2462 	    &restarter_spec, print_ctid_header, print_ctid_detailed);
   2463 	if (restarter_spec == 1) {
   2464 		if (restarter_ret != 0)
   2465 			uu_die(gettext("Unable to get restarter for %s"),
   2466 			    wip->fmri);
   2467 		goto restarter_common;
   2468 	}
   2469 
   2470 	if (rpg) {
   2471 		scf_iter_t *iter;
   2472 
   2473 		if ((iter = scf_iter_create(h)) == NULL)
   2474 			scfdie();
   2475 
   2476 		if (scf_pg_get_property(rpg, scf_property_contract, g_prop) ==
   2477 		    0) {
   2478 			if (scf_property_is_type(g_prop, SCF_TYPE_COUNT) == 0) {
   2479 
   2480 				/* Callback to print ctid header */
   2481 				print_ctid_header();
   2482 
   2483 				if (scf_iter_property_values(iter, g_prop) != 0)
   2484 					scfdie();
   2485 
   2486 				for (;;) {
   2487 					ret = scf_iter_next_value(iter, g_val);
   2488 					if (ret == -1)
   2489 						scfdie();
   2490 					if (ret == 0)
   2491 						break;
   2492 
   2493 					if (scf_value_get_count(g_val, &c) != 0)
   2494 						scfdie();
   2495 
   2496 					/* Callback to print contract id. */
   2497 					print_ctid_detailed(c);
   2498 				}
   2499 
   2500 				(void) putchar('\n');
   2501 			} else {
   2502 				if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
   2503 					scfdie();
   2504 			}
   2505 		} else {
   2506 			if (scf_error() != SCF_ERROR_NOT_FOUND)
   2507 				scfdie();
   2508 		}
   2509 
   2510 		scf_iter_destroy(iter);
   2511 	} else {
   2512 		if (scf_error() != SCF_ERROR_NOT_FOUND)
   2513 			scfdie();
   2514 	}
   2515 
   2516 restarter_common:
   2517 	scf_pg_destroy(rpg);
   2518 
   2519 	/* Dependencies. */
   2520 	if ((pg_iter = scf_iter_create(h)) == NULL)
   2521 		scfdie();
   2522 
   2523 	snap = get_running_snapshot(wip->inst);
   2524 
   2525 	if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap,
   2526 	    SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
   2527 		scfdie();
   2528 
   2529 	while ((ret = scf_iter_next_pg(pg_iter, g_pg)) == 1)
   2530 		print_detailed_dependency(g_pg);
   2531 	if (ret == -1)
   2532 		scfdie();
   2533 
   2534 	scf_iter_destroy(pg_iter);
   2535 
   2536 	if (opt_processes)
   2537 		detailed_list_processes(wip);
   2538 
   2539 	/* "application" type property groups */
   2540 	if (opt_verbose == 1)
   2541 		print_application_properties(wip, snap);
   2542 
   2543 	scf_snapshot_destroy(snap);
   2544 
   2545 	return (0);
   2546 }
   2547 
   2548 /*
   2549  * Append a one-lined description of each process in inst's contract(s) and
   2550  * return the augmented string.
   2551  */
   2552 static char *
   2553 add_processes(scf_walkinfo_t *wip, char *line, scf_propertygroup_t *lpg)
   2554 {
   2555 	pid_t *pids = NULL;
   2556 	uint_t i, n = 0;
   2557 
   2558 	if (lpg == NULL) {
   2559 		if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
   2560 			return (line);
   2561 	} else {
   2562 		/* Legacy services */
   2563 		scf_iter_t *iter;
   2564 
   2565 		if ((iter = scf_iter_create(h)) == NULL)
   2566 			scfdie();
   2567 
   2568 		(void) propvals_to_pids(lpg, scf_property_contract, &pids, &n,
   2569 		    g_prop, g_val, iter);
   2570 
   2571 		scf_iter_destroy(iter);
   2572 	}
   2573 
   2574 	if (n == 0)
   2575 		return (line);
   2576 
   2577 	qsort(pids, n, sizeof (*pids), pidcmp);
   2578 
   2579 	for (i = 0; i < n; ++i) {
   2580 		char *cp, stime[9];
   2581 		psinfo_t psi;
   2582 		struct tm *tm;
   2583 		int len = 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ;
   2584 
   2585 		if (get_psinfo(pids[i], &psi) != 0)
   2586 			continue;
   2587 
   2588 		line = realloc(line, strlen(line) + len);
   2589 		if (line == NULL)
   2590 			uu_die(gettext("Out of memory.\n"));
   2591 
   2592 		cp = strchr(line, '\0');
   2593 
   2594 		tm = localtime(&psi.pr_start.tv_sec);
   2595 
   2596 		/*
   2597 		 * Print time if started within the past 24 hours, print date
   2598 		 * if within the past 12 months, print year if started greater
   2599 		 * than 12 months ago.
   2600 		 */
   2601 		if (now - psi.pr_start.tv_sec < 24 * 60 * 60)
   2602 			(void) strftime(stime, sizeof (stime),
   2603 			    gettext(FORMAT_TIME), tm);
   2604 		else if (now - psi.pr_start.tv_sec < 12 * 30 * 24 * 60 * 60)
   2605 			(void) strftime(stime, sizeof (stime),
   2606 			    gettext(FORMAT_DATE), tm);
   2607 		else
   2608 			(void) strftime(stime, sizeof (stime),
   2609 			    gettext(FORMAT_YEAR), tm);
   2610 
   2611 		(void) snprintf(cp, len, "\n               %-8s   %6ld %.*s",
   2612 		    stime, pids[i], PRFNSZ, psi.pr_fname);
   2613 	}
   2614 
   2615 	free(pids);
   2616 
   2617 	return (line);
   2618 }
   2619 
   2620 /*ARGSUSED*/
   2621 static int
   2622 list_instance(void *unused, scf_walkinfo_t *wip)
   2623 {
   2624 	struct avl_string *lp;
   2625 	char *cp;
   2626 	int i;
   2627 	uu_avl_index_t idx;
   2628 
   2629 	/*
   2630 	 * If the user has specified a restarter, check for a match first
   2631 	 */
   2632 	if (restarters != NULL) {
   2633 		struct pfmri_list *rest;
   2634 		int match;
   2635 		char *restarter_fmri;
   2636 		const char *scope_name, *svc_name, *inst_name, *pg_name;
   2637 
   2638 		/* legacy services don't have restarters */
   2639 		if (wip->pg != NULL)
   2640 			return (0);
   2641 
   2642 		restarter_fmri = safe_malloc(max_scf_fmri_length + 1);
   2643 
   2644 		if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
   2645 		    SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, restarter_fmri,
   2646 		    max_scf_fmri_length + 1, 0, 0, 1) != 0)
   2647 			(void) strcpy(restarter_fmri, SCF_SERVICE_STARTD);
   2648 
   2649 		if (scf_parse_svc_fmri(restarter_fmri, &scope_name, &svc_name,
   2650 		    &inst_name, &pg_name, NULL) != SCF_SUCCESS) {
   2651 			free(restarter_fmri);
   2652 			return (0);
   2653 		}
   2654 
   2655 		match = 0;
   2656 		for (rest = restarters; rest != NULL; rest = rest->next) {
   2657 			if (strcmp(rest->scope, scope_name) == 0 &&
   2658 			    strcmp(rest->service, svc_name) == 0 &&
   2659 			    strcmp(rest->instance, inst_name) == 0)
   2660 				match = 1;
   2661 		}
   2662 
   2663 		free(restarter_fmri);
   2664 
   2665 		if (!match)
   2666 			return (0);
   2667 	}
   2668 
   2669 	if (wip->pg == NULL && ht_buckets != NULL && ht_add(wip->fmri)) {
   2670 		/* It was already there. */
   2671 		return (0);
   2672 	}
   2673 
   2674 	lp = safe_malloc(sizeof (*lp));
   2675 
   2676 	lp->str = NULL;
   2677 	for (i = 0; i < opt_cnum; ++i) {
   2678 		columns[opt_columns[i]].sprint(&lp->str, wip);
   2679 	}
   2680 	cp = lp->str + strlen(lp->str);
   2681 	cp--;
   2682 	while (*cp == ' ')
   2683 		cp--;
   2684 	*(cp+1) = '\0';
   2685 
   2686 	/* If we're supposed to list the processes, too, do that now. */
   2687 	if (opt_processes)
   2688 		lp->str = add_processes(wip, lp->str, wip->pg);
   2689 
   2690 	/* Create the sort key. */
   2691 	cp = lp->key = safe_malloc(sortkey_sz);
   2692 	for (i = 0; i < opt_snum; ++i) {
   2693 		int j = opt_sort[i] & 0xff;
   2694 
   2695 		assert(columns[j].get_sortkey != NULL);
   2696 		columns[j].get_sortkey(cp, opt_sort[i] & ~0xff, wip);
   2697 		cp += columns[j].sortkey_width;
   2698 	}
   2699 
   2700 	/* Insert into AVL tree. */
   2701 	uu_avl_node_init(lp, &lp->node, lines_pool);
   2702 	(void) uu_avl_find(lines, lp, NULL, &idx);
   2703 	uu_avl_insert(lines, lp, idx);
   2704 
   2705 	return (0);
   2706 }
   2707 
   2708 static int
   2709 list_if_enabled(void *unused, scf_walkinfo_t *wip)
   2710 {
   2711 	if (wip->pg != NULL ||
   2712 	    instance_enabled(wip->inst, B_FALSE) == 1 ||
   2713 	    instance_enabled(wip->inst, B_TRUE) == 1)
   2714 		return (list_instance(unused, wip));
   2715 
   2716 	return (0);
   2717 }
   2718 
   2719 /*
   2720  * Service FMRI selection: Lookup and call list_instance() for the instances.
   2721  * Instance FMRI selection: Lookup and call list_instance().
   2722  *
   2723  * Note: This is shoehorned into a walk_dependencies() callback prototype so
   2724  * it can be used in list_dependencies.
   2725  */
   2726 static int
   2727 list_svc_or_inst_fmri(void *complain, scf_walkinfo_t *wip)
   2728 {
   2729 	char *fmri;
   2730 	const char *svc_name, *inst_name, *pg_name, *save;
   2731 	scf_iter_t *iter;
   2732 	int ret;
   2733 
   2734 	fmri = safe_strdup(wip->fmri);
   2735 
   2736 	if (scf_parse_svc_fmri(fmri, NULL, &svc_name, &inst_name, &pg_name,
   2737 	    NULL) != SCF_SUCCESS) {
   2738 		if (complain)
   2739 			uu_warn(gettext("FMRI \"%s\" is invalid.\n"),
   2740 			    wip->fmri);
   2741 		exit_status = UU_EXIT_FATAL;
   2742 		free(fmri);
   2743 		return (0);
   2744 	}
   2745 
   2746 	/*
   2747 	 * Yes, this invalidates *_name, but we only care whether they're NULL
   2748 	 * or not.
   2749 	 */
   2750 	free(fmri);
   2751 
   2752 	if (svc_name == NULL || pg_name != NULL) {
   2753 		if (complain)
   2754 			uu_warn(gettext("FMRI \"%s\" does not designate a "
   2755 			    "service or instance.\n"), wip->fmri);
   2756 		return (0);
   2757 	}
   2758 
   2759 	if (inst_name != NULL) {
   2760 		/* instance */
   2761 		if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc,
   2762 		    wip->inst, NULL, NULL, 0) != SCF_SUCCESS) {
   2763 			if (scf_error() != SCF_ERROR_NOT_FOUND)
   2764 				scfdie();
   2765 
   2766 			if (complain)
   2767 				uu_warn(gettext(
   2768 				    "Instance \"%s\" does not exist.\n"),
   2769 				    wip->fmri);
   2770 			return (0);
   2771 		}
   2772 
   2773 		return (list_instance(NULL, wip));
   2774 	}
   2775 
   2776 	/* service: Walk the instances. */
   2777 	if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, NULL,
   2778 	    NULL, NULL, 0) != SCF_SUCCESS) {
   2779 		if (scf_error() != SCF_ERROR_NOT_FOUND)
   2780 			scfdie();
   2781 
   2782 		if (complain)
   2783 			uu_warn(gettext("Service \"%s\" does not exist.\n"),
   2784 			    wip->fmri);
   2785 
   2786 		exit_status = UU_EXIT_FATAL;
   2787 
   2788 		return (0);
   2789 	}
   2790 
   2791 	iter = scf_iter_create(h);
   2792 	if (iter == NULL)
   2793 		scfdie();
   2794 
   2795 	if (scf_iter_service_instances(iter, wip->svc) != SCF_SUCCESS)
   2796 		scfdie();
   2797 
   2798 	if ((fmri = malloc(max_scf_fmri_length + 1)) == NULL) {
   2799 		scf_iter_destroy(iter);
   2800 		exit_status = UU_EXIT_FATAL;
   2801 		return (0);
   2802 	}
   2803 
   2804 	save = wip->fmri;
   2805 	wip->fmri = fmri;
   2806 	while ((ret = scf_iter_next_instance(iter, wip->inst)) == 1) {
   2807 		if (scf_instance_to_fmri(wip->inst, fmri,
   2808 		    max_scf_fmri_length + 1) <= 0)
   2809 			scfdie();
   2810 		(void) list_instance(NULL, wip);
   2811 	}
   2812 	free(fmri);
   2813 	wip->fmri = save;
   2814 	if (ret == -1)
   2815 		scfdie();
   2816 
   2817 	exit_status = UU_EXIT_OK;
   2818 
   2819 	scf_iter_destroy(iter);
   2820 
   2821 	return (0);
   2822 }
   2823 
   2824 /*
   2825  * Dependency selection: Straightforward since each instance lists the
   2826  * services it depends on.
   2827  */
   2828 
   2829 static void
   2830 walk_dependencies(scf_walkinfo_t *wip, scf_walk_callback callback, void *data)
   2831 {
   2832 	scf_snapshot_t *snap;
   2833 	scf_iter_t *iter, *viter;
   2834 	int ret, vret;
   2835 	char *dep;
   2836 
   2837 	assert(wip->inst != NULL);
   2838 
   2839 	if ((iter = scf_iter_create(h)) == NULL ||
   2840 	    (viter = scf_iter_create(h)) == NULL)
   2841 		scfdie();
   2842 
   2843 	snap = get_running_snapshot(wip->inst);
   2844 
   2845 	if (scf_iter_instance_pgs_typed_composed(iter, wip->inst, snap,
   2846 	    SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
   2847 		scfdie();
   2848 
   2849 	dep = safe_malloc(max_scf_value_length + 1);
   2850 
   2851 	while ((ret = scf_iter_next_pg(iter, g_pg)) == 1) {
   2852 		scf_type_t ty;
   2853 
   2854 		/* Ignore exclude_any dependencies. */
   2855 		if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) !=
   2856 		    SCF_SUCCESS) {
   2857 			if (scf_error() != SCF_ERROR_NOT_FOUND)
   2858 				scfdie();
   2859 
   2860 			continue;
   2861 		}
   2862 
   2863 		if (scf_property_type(g_prop, &ty) != SCF_SUCCESS)
   2864 			scfdie();
   2865 
   2866 		if (ty != SCF_TYPE_ASTRING)
   2867 			continue;
   2868 
   2869 		if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) {
   2870 			if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
   2871 				scfdie();
   2872 
   2873 			continue;
   2874 		}
   2875 
   2876 		if (scf_value_get_astring(g_val, dep,
   2877 		    max_scf_value_length + 1) < 0)
   2878 			scfdie();
   2879 
   2880 		if (strcmp(dep, SCF_DEP_EXCLUDE_ALL) == 0)
   2881 			continue;
   2882 
   2883 		if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) !=
   2884 		    SCF_SUCCESS) {
   2885 			if (scf_error() != SCF_ERROR_NOT_FOUND)
   2886 				scfdie();
   2887 
   2888 			continue;
   2889 		}
   2890 
   2891 		if (scf_iter_property_values(viter, g_prop) != SCF_SUCCESS)
   2892 			scfdie();
   2893 
   2894 		while ((vret = scf_iter_next_value(viter, g_val)) == 1) {
   2895 			if (scf_value_get_astring(g_val, dep,
   2896 			    max_scf_value_length + 1) < 0)
   2897 				scfdie();
   2898 
   2899 			wip->fmri = dep;
   2900 			if (callback(data, wip) != 0)
   2901 				goto out;
   2902 		}
   2903 		if (vret == -1)
   2904 			scfdie();
   2905 	}
   2906 	if (ret == -1)
   2907 		scfdie();
   2908 
   2909 out:
   2910 	scf_iter_destroy(viter);
   2911 	scf_iter_destroy(iter);
   2912 	scf_snapshot_destroy(snap);
   2913 }
   2914 
   2915 static int
   2916 list_dependencies(void *data, scf_walkinfo_t *wip)
   2917 {
   2918 	walk_dependencies(wip, list_svc_or_inst_fmri, data);
   2919 	return (0);
   2920 }
   2921 
   2922 
   2923 /*
   2924  * Dependent selection: The "providing" service's or instance's FMRI is parsed
   2925  * into the provider_* variables, the instances are walked, and any instance
   2926  * which lists an FMRI which parses to these components is selected.  This is
   2927  * inefficient in the face of multiple operands, but that should be uncommon.
   2928  */
   2929 
   2930 static char *provider_scope;
   2931 static char *provider_svc;
   2932 static char *provider_inst;	/* NULL for services */
   2933 
   2934 /*ARGSUSED*/
   2935 static int
   2936 check_against_provider(void *arg, scf_walkinfo_t *wip)
   2937 {
   2938 	char *cfmri;
   2939 	const char *scope_name, *svc_name, *inst_name, *pg_name;
   2940 	int *matchp = arg;
   2941 
   2942 	cfmri = safe_strdup(wip->fmri);
   2943 
   2944 	if (scf_parse_svc_fmri(cfmri, &scope_name, &svc_name, &inst_name,
   2945 	    &pg_name, NULL) != SCF_SUCCESS) {
   2946 		free(cfmri);
   2947 		return (0);
   2948 	}
   2949 
   2950 	if (svc_name == NULL || pg_name != NULL) {
   2951 		free(cfmri);
   2952 		return (0);
   2953 	}
   2954 
   2955 	/*
   2956 	 * If the user has specified an instance, then also match dependencies
   2957 	 * on the service itself.
   2958 	 */
   2959 	*matchp = (strcmp(provider_scope, scope_name) == 0 &&
   2960 	    strcmp(provider_svc, svc_name) == 0 &&
   2961 	    (provider_inst == NULL ? (inst_name == NULL) :
   2962 	    (inst_name == NULL || strcmp(provider_inst, inst_name) == 0)));
   2963 
   2964 	free(cfmri);
   2965 
   2966 	/* Stop on matches. */
   2967 	return (*matchp);
   2968 }
   2969 
   2970 static int
   2971 list_if_dependent(void *unused, scf_walkinfo_t *wip)
   2972 {
   2973 	/* Only proceed if this instance depends on provider_*. */
   2974 	int match = 0;
   2975 
   2976 	(void) walk_dependencies(wip, check_against_provider, &match);
   2977 
   2978 	if (match)
   2979 		return (list_instance(unused, wip));
   2980 
   2981 	return (0);
   2982 }
   2983 
   2984 /*ARGSUSED*/
   2985 static int
   2986 list_dependents(void *unused, scf_walkinfo_t *wip)
   2987 {
   2988 	char *save;
   2989 	int ret;
   2990 
   2991 	if (scf_scope_get_name(wip->scope, provider_scope,
   2992 	    max_scf_fmri_length) <= 0 ||
   2993 	    scf_service_get_name(wip->svc, provider_svc,
   2994 	    max_scf_fmri_length) <= 0)
   2995 		scfdie();
   2996 
   2997 	save = provider_inst;
   2998 	if (wip->inst == NULL)
   2999 		provider_inst = NULL;
   3000 	else if (scf_instance_get_name(wip->inst, provider_inst,
   3001 	    max_scf_fmri_length) <= 0)
   3002 		scfdie();
   3003 
   3004 	ret = scf_walk_fmri(h, 0, NULL, 0, list_if_dependent, NULL, NULL,
   3005 	    uu_warn);
   3006 
   3007 	provider_inst = save;
   3008 
   3009 	return (ret);
   3010 }
   3011 
   3012 /*
   3013  * main() & helpers
   3014  */
   3015 
   3016 static void
   3017 add_sort_column(const char *col, int reverse)
   3018 {
   3019 	int i;
   3020 
   3021 	++opt_snum;
   3022 
   3023 	opt_sort = realloc(opt_sort, opt_snum * sizeof (*opt_sort));
   3024 	if (opt_sort == NULL)
   3025 		uu_die(gettext("Too many sort criteria: out of memory.\n"));
   3026 
   3027 	for (i = 0; i < ncolumns; ++i) {
   3028 		if (strcasecmp(col, columns[i].name) == 0)
   3029 			break;
   3030 	}
   3031 
   3032 	if (i < ncolumns)
   3033 		opt_sort[opt_snum - 1] = (reverse ? i | 0x100 : i);
   3034 	else
   3035 		uu_die(gettext("Unrecognized sort column \"%s\".\n"), col);
   3036 
   3037 	sortkey_sz += columns[i].sortkey_width;
   3038 }
   3039 
   3040 static void
   3041 add_restarter(const char *fmri)
   3042 {
   3043 	char *cfmri;
   3044 	const char *pg_name;
   3045 	struct pfmri_list *rest;
   3046 
   3047 	cfmri = safe_strdup(fmri);
   3048 	rest = safe_malloc(sizeof (*rest));
   3049 
   3050 	if (scf_parse_svc_fmri(cfmri, &rest->scope, &rest->service,
   3051 	    &rest->instance, &pg_name, NULL) != SCF_SUCCESS)
   3052 		uu_die(gettext("Restarter FMRI \"%s\" is invalid.\n"), fmri);
   3053 
   3054 	if (rest->instance == NULL || pg_name != NULL)
   3055 		uu_die(gettext("Restarter FMRI \"%s\" does not designate an "
   3056 		    "instance.\n"), fmri);
   3057 
   3058 	rest->next = restarters;
   3059 	restarters = rest;
   3060 	return;
   3061 
   3062 err:
   3063 	free(cfmri);
   3064 	free(rest);
   3065 }
   3066 
   3067 /* ARGSUSED */
   3068 static int
   3069 line_cmp(const void *l_arg, const void *r_arg, void *private)
   3070 {
   3071 	const struct avl_string *l = l_arg;
   3072 	const struct avl_string *r = r_arg;
   3073 
   3074 	return (memcmp(l->key, r->key, sortkey_sz));
   3075 }
   3076 
   3077 /* ARGSUSED */
   3078 static int
   3079 print_line(void *e, void *private)
   3080 {
   3081 	struct avl_string *lp = e;
   3082 
   3083 	(void) puts(lp->str);
   3084 
   3085 	return (UU_WALK_NEXT);
   3086 }
   3087 
   3088 int
   3089 main(int argc, char **argv)
   3090 {
   3091 	char opt, opt_mode;
   3092 	int i, n;
   3093 	char *columns_str = NULL;
   3094 	char *cp;
   3095 	const char *progname;
   3096 	int err;
   3097 
   3098 	int show_all = 0;
   3099 	int show_header = 1;
   3100 
   3101 	const char * const options = "aHpvo:R:s:S:dDl?x";
   3102 
   3103 	(void) setlocale(LC_ALL, "");
   3104 
   3105 	locale = setlocale(LC_MESSAGES, "");
   3106 	if (locale) {
   3107 		locale = safe_strdup(locale);
   3108 		_scf_sanitize_locale(locale);
   3109 	}
   3110 
   3111 	(void) textdomain(TEXT_DOMAIN);
   3112 	progname = uu_setpname(argv[0]);
   3113 
   3114 	exit_status = UU_EXIT_OK;
   3115 
   3116 	max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
   3117 	max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
   3118 	max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
   3119 	max_scf_type_length = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH);
   3120 
   3121 	if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
   3122 	    max_scf_fmri_length == -1 || max_scf_type_length == -1)
   3123 		scfdie();
   3124 
   3125 	now = time(NULL);
   3126 	assert(now != -1);
   3127 
   3128 	/*
   3129 	 * opt_mode is the mode of operation.  0 for plain, 'd' for
   3130 	 * dependencies, 'D' for dependents, and 'l' for detailed (long).  We
   3131 	 * need to know now so we know which options are valid.
   3132 	 */
   3133 	opt_mode = 0;
   3134 	while ((opt = getopt(argc, argv, options)) != -1) {
   3135 		switch (opt) {
   3136 		case '?':
   3137 			if (optopt == '?') {
   3138 				print_help(progname);
   3139 				return (UU_EXIT_OK);
   3140 			} else {
   3141 				argserr(progname);
   3142 				/* NOTREACHED */
   3143 			}
   3144 
   3145 		case 'd':
   3146 		case 'D':
   3147 		case 'l':
   3148 			if (opt_mode != 0)
   3149 				argserr(progname);
   3150 
   3151 			opt_mode = opt;
   3152 			break;
   3153 
   3154 		case 'x':
   3155 			if (opt_mode != 0)
   3156 				argserr(progname);
   3157 
   3158 			opt_mode = opt;
   3159 			break;
   3160 
   3161 		default:
   3162 			break;
   3163 		}
   3164 	}
   3165 
   3166 	sortkey_sz = 0;
   3167 
   3168 	optind = 1;	/* Reset getopt() */
   3169 	while ((opt = getopt(argc, argv, options)) != -1) {
   3170 		switch (opt) {
   3171 		case 'a':
   3172 			if (opt_mode != 0)
   3173 				argserr(progname);
   3174 			show_all = 1;
   3175 			break;
   3176 
   3177 		case 'H':
   3178 			if (opt_mode == 'l' || opt_mode == 'x')
   3179 				argserr(progname);
   3180 			show_header = 0;
   3181 			break;
   3182 
   3183 		case 'p':
   3184 			if (opt_mode == 'x')
   3185 				argserr(progname);
   3186 			opt_processes = 1;
   3187 			break;
   3188 
   3189 		case 'v':
   3190 			opt_verbose = 1;
   3191 			break;
   3192 
   3193 		case 'o':
   3194 			if (opt_mode == 'l' || opt_mode == 'x')
   3195 				argserr(progname);
   3196 			columns_str = optarg;
   3197 			break;
   3198 
   3199 		case 'R':
   3200 			if (opt_mode != 0 || opt_mode == 'x')
   3201 				argserr(progname);
   3202 
   3203 			add_restarter(optarg);
   3204 			break;
   3205 
   3206 		case 's':
   3207 		case 'S':
   3208 			if (opt_mode != 0)
   3209 				argserr(progname);
   3210 
   3211 			add_sort_column(optarg, optopt == 'S');
   3212 			break;
   3213 
   3214 		case 'd':
   3215 		case 'D':
   3216 		case 'l':
   3217 		case 'x':
   3218 			assert(opt_mode == optopt);
   3219 			break;
   3220 
   3221 		case '?':
   3222 			argserr(progname);
   3223 			/* NOTREACHED */
   3224 
   3225 		default:
   3226 			assert(0);
   3227 			abort();
   3228 		}
   3229 	}
   3230 
   3231 	/*
   3232 	 * -a is only meaningful when given no arguments
   3233 	 */
   3234 	if (show_all && optind != argc)
   3235 		uu_warn(gettext("-a ignored when used with arguments.\n"));
   3236 
   3237 	h = scf_handle_create(SCF_VERSION);
   3238 	if (h == NULL)
   3239 		scfdie();
   3240 
   3241 	if (scf_handle_bind(h) == -1)
   3242 		uu_die(gettext("Could not bind to repository server: %s.  "
   3243 		    "Exiting.\n"), scf_strerror(scf_error()));
   3244 
   3245 	if ((g_pg = scf_pg_create(h)) == NULL ||
   3246 	    (g_prop = scf_property_create(h)) == NULL ||
   3247 	    (g_val = scf_value_create(h)) == NULL)
   3248 		scfdie();
   3249 
   3250 	argc -= optind;
   3251 	argv += optind;
   3252 
   3253 	/*
   3254 	 * If we're in long mode, take care of it now before we deal with the
   3255 	 * sorting and the columns, since we won't use them anyway.
   3256 	 */
   3257 	if (opt_mode == 'l') {
   3258 		if (argc == 0)
   3259 			argserr(progname);
   3260 
   3261 		if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
   3262 		    print_detailed, NULL, &exit_status, uu_warn)) != 0) {
   3263 			uu_warn(gettext("failed to iterate over "
   3264 			    "instances: %s\n"), scf_strerror(err));
   3265 			exit_status = UU_EXIT_FATAL;
   3266 		}
   3267 
   3268 		return (exit_status);
   3269 	}
   3270 
   3271 	if (opt_mode == 'x') {
   3272 		explain(opt_verbose, argc, argv);
   3273 
   3274 		return (exit_status);
   3275 	}
   3276 
   3277 
   3278 	if (opt_snum == 0) {
   3279 		/* Default sort. */
   3280 		add_sort_column("state", 0);
   3281 		add_sort_column("stime", 0);
   3282 		add_sort_column("fmri", 0);
   3283 	}
   3284 
   3285 	if (columns_str == NULL) {
   3286 		if (!opt_verbose)
   3287 			columns_str = safe_strdup("state,stime,fmri");
   3288 		else
   3289 			columns_str =
   3290 			    safe_strdup("state,nstate,stime,ctid,fmri");
   3291 	}
   3292 
   3293 	/* Decode columns_str into opt_columns. */
   3294 	line_sz = 0;
   3295 
   3296 	opt_cnum = 1;
   3297 	for (cp = columns_str; *cp != '\0'; ++cp)
   3298 		if (*cp == ',')
   3299 			++opt_cnum;
   3300 
   3301 	opt_columns = malloc(opt_cnum * sizeof (*opt_columns));
   3302 	if (opt_columns == NULL)
   3303 		uu_die(gettext("Too many columns.\n"));
   3304 
   3305 	for (n = 0; *columns_str != '\0'; ++n) {
   3306 		i = getcolumnopt(&columns_str);
   3307 		if (i == -1)
   3308 			uu_die(gettext("Unknown column \"%s\".\n"),
   3309 			    columns_str);
   3310 
   3311 		if (strcmp(columns[i].name, "N") == 0 ||
   3312 		    strcmp(columns[i].name, "SN") == 0 ||
   3313 		    strcmp(columns[i].name, "NSTA") == 0 ||
   3314 		    strcmp(columns[i].name, "NSTATE") == 0)
   3315 			opt_nstate_shown = 1;
   3316 
   3317 		opt_columns[n] = i;
   3318 		line_sz += columns[i].width + 1;
   3319 	}
   3320 
   3321 
   3322 	if ((lines_pool = uu_avl_pool_create("lines_pool",
   3323 	    sizeof (struct avl_string), offsetof(struct avl_string, node),
   3324 	    line_cmp, UU_AVL_DEBUG)) == NULL ||
   3325 	    (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL)
   3326 		uu_die(gettext("Unexpected libuutil error: %s.  Exiting.\n"),
   3327 		    uu_strerror(uu_error()));
   3328 
   3329 	switch (opt_mode) {
   3330 	case 0:
   3331 		ht_init();
   3332 
   3333 		/* Always show all FMRIs when given arguments or restarters */
   3334 		if (argc != 0 || restarters != NULL)
   3335 			show_all =  1;
   3336 
   3337 		if ((err = scf_walk_fmri(h, argc, argv,
   3338 		    SCF_WALK_MULTIPLE | SCF_WALK_LEGACY,
   3339 		    show_all ? list_instance : list_if_enabled, NULL,
   3340 		    &exit_status, uu_warn)) != 0) {
   3341 			uu_warn(gettext("failed to iterate over "
   3342 			    "instances: %s\n"), scf_strerror(err));
   3343 			exit_status = UU_EXIT_FATAL;
   3344 		}
   3345 		break;
   3346 
   3347 	case 'd':
   3348 		if (argc == 0)
   3349 			argserr(progname);
   3350 
   3351 		if ((err = scf_walk_fmri(h, argc, argv,
   3352 		    SCF_WALK_MULTIPLE, list_dependencies, NULL,
   3353 		    &exit_status, uu_warn)) != 0) {
   3354 			uu_warn(gettext("failed to iterate over "
   3355 			    "instances: %s\n"), scf_strerror(err));
   3356 			exit_status = UU_EXIT_FATAL;
   3357 		}
   3358 		break;
   3359 
   3360 	case 'D':
   3361 		if (argc == 0)
   3362 			argserr(progname);
   3363 
   3364 		provider_scope = safe_malloc(max_scf_fmri_length);
   3365 		provider_svc = safe_malloc(max_scf_fmri_length);
   3366 		provider_inst = safe_malloc(max_scf_fmri_length);
   3367 
   3368 		if ((err = scf_walk_fmri(h, argc, argv,
   3369 		    SCF_WALK_MULTIPLE | SCF_WALK_SERVICE,
   3370 		    list_dependents, NULL, &exit_status, uu_warn)) != 0) {
   3371 			uu_warn(gettext("failed to iterate over "
   3372 			    "instances: %s\n"), scf_strerror(err));
   3373 			exit_status = UU_EXIT_FATAL;
   3374 		}
   3375 
   3376 		free(provider_scope);
   3377 		free(provider_svc);
   3378 		free(provider_inst);
   3379 		break;
   3380 
   3381 	default:
   3382 		assert(0);
   3383 		abort();
   3384 	}
   3385 
   3386 	if (show_header)
   3387 		print_header();
   3388 
   3389 	(void) uu_avl_walk(lines, print_line, NULL, 0);
   3390 
   3391 	return (exit_status);
   3392 }
   3393