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