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