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