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