xref: /illumos-gate/usr/src/cmd/svc/svcs/svcs.c (revision 7b209c2cc5ea45251aba06dcc6181d3f23da807a)
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 static void
1601 sprint_stime(char **buf, scf_walkinfo_t *wip)
1602 {
1603 	int r;
1604 	struct timeval tv;
1605 	time_t then;
1606 	struct tm *tm;
1607 	char st_buf[STIME_COLUMN_WIDTH + 1];
1608 	size_t newsize = (*buf ? strlen(*buf) : 0) + STIME_COLUMN_WIDTH + 2;
1609 	char *newbuf = safe_malloc(newsize);
1610 
1611 	if (wip->pg == NULL) {
1612 		r = get_restarter_time_prop(wip->inst,
1613 		    SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1614 	} else {
1615 		r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1616 		    SCF_TYPE_TIME, &tv, NULL, 0);
1617 	}
1618 
1619 	if (r != 0) {
1620 		(void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "",
1621 		    STIME_COLUMN_WIDTH + 1, "?");
1622 		return;
1623 	}
1624 
1625 	then = (time_t)tv.tv_sec;
1626 
1627 	tm = localtime(&then);
1628 	/*
1629 	 * Print time if started within the past 24 hours, print date
1630 	 * if within the past 12 months, print year if started greater than
1631 	 * 12 months ago.
1632 	 */
1633 	if (now - then < 24 * 60 * 60)
1634 		(void) strftime(st_buf, sizeof (st_buf), gettext(FORMAT_TIME),
1635 		    tm);
1636 	else if (now - then < 12 * 30 * 24 * 60 * 60)
1637 		(void) strftime(st_buf, sizeof (st_buf), gettext(FORMAT_DATE),
1638 		    tm);
1639 	else
1640 		(void) strftime(st_buf, sizeof (st_buf), gettext(FORMAT_YEAR),
1641 		    tm);
1642 
1643 	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1644 	    STIME_COLUMN_WIDTH + 1, st_buf);
1645 	if (*buf)
1646 		free(*buf);
1647 	*buf = newbuf;
1648 }
1649 
1650 #define	STIME_SORTKEY_WIDTH		(sizeof (uint64_t) + sizeof (uint32_t))
1651 
1652 /* ARGSUSED */
1653 static void
1654 sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip)
1655 {
1656 	struct timeval tv;
1657 	int r;
1658 
1659 	if (wip->pg == NULL)
1660 		r = get_restarter_time_prop(wip->inst,
1661 		    SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1662 	else
1663 		r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1664 		    SCF_TYPE_TIME, &tv, NULL, 0);
1665 
1666 	if (r == 0) {
1667 		int64_t sec;
1668 		int32_t us;
1669 
1670 		/* Stick it straight into the buffer. */
1671 		sec = tv.tv_sec;
1672 		us = tv.tv_usec;
1673 
1674 		sec = BE_64(sec);
1675 		us = BE_32(us);
1676 		bcopy(&sec, buf, sizeof (sec));
1677 		bcopy(&us, buf + sizeof (sec), sizeof (us));
1678 	} else {
1679 		bzero(buf, STIME_SORTKEY_WIDTH);
1680 	}
1681 
1682 	if (reverse)
1683 		reverse_bytes(buf, STIME_SORTKEY_WIDTH);
1684 }
1685 
1686 
1687 /*
1688  * Information about columns which can be displayed.  If you add something,
1689  * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below.
1690  */
1691 static const struct column columns[] = {
1692 	{ "CTID", CTID_COLUMN_WIDTH, sprint_ctid,
1693 		CTID_SORTKEY_WIDTH, sortkey_ctid },
1694 	{ "DESC", DESC_COLUMN_WIDTH, sprint_desc,
1695 		DESC_COLUMN_WIDTH, sortkey_desc },
1696 	{ "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri,
1697 		FMRI_COLUMN_WIDTH, sortkey_fmri },
1698 	{ "INST", COMPONENT_COLUMN_WIDTH, sprint_instance,
1699 		COMPONENT_COLUMN_WIDTH, sortkey_instance },
1700 	{ "N", 1,  sprint_n, 1, sortkey_nstate },
1701 	{ "NSTA", 4, sprint_nsta, 1, sortkey_nstate },
1702 	{ "NSTATE", MAX_SCF_STATE_STRING_SZ - 1, sprint_nstate,
1703 		1, sortkey_nstate },
1704 	{ "S", 2, sprint_s, 1, sortkey_state },
1705 	{ "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope,
1706 		COMPONENT_COLUMN_WIDTH, sortkey_scope },
1707 	{ "SN", 2, sprint_sn, 2, sortkey_sn },
1708 	{ "SVC", COMPONENT_COLUMN_WIDTH, sprint_service,
1709 		COMPONENT_COLUMN_WIDTH, sortkey_service },
1710 	{ "STA", 4, sprint_sta, 1, sortkey_state },
1711 	{ "STATE", MAX_SCF_STATE_STRING_SZ - 1 + 1, sprint_state,
1712 		1, sortkey_state },
1713 	{ "STIME", STIME_COLUMN_WIDTH, sprint_stime,
1714 		STIME_SORTKEY_WIDTH, sortkey_stime },
1715 };
1716 
1717 #define	MAX_COLUMN_NAME_LENGTH_STR	"6"
1718 
1719 static const int ncolumns = sizeof (columns) / sizeof (columns[0]);
1720 
1721 /*
1722  * Necessary thanks to gettext() & xgettext.
1723  */
1724 static const char *
1725 description_of_column(int c)
1726 {
1727 	const char *s = NULL;
1728 
1729 	switch (c) {
1730 	case 0:
1731 		s = gettext("contract ID for service (see contract(4))");
1732 		break;
1733 	case 1:
1734 		s = gettext("human-readable description of the service");
1735 		break;
1736 	case 2:
1737 		s = gettext("Fault Managed Resource Identifier for service");
1738 		break;
1739 	case 3:
1740 		s = gettext("portion of the FMRI indicating service instance");
1741 		break;
1742 	case 4:
1743 		s = gettext("abbreviation for next state (if in transition)");
1744 		break;
1745 	case 5:
1746 		s = gettext("abbreviation for next state (if in transition)");
1747 		break;
1748 	case 6:
1749 		s = gettext("name for next state (if in transition)");
1750 		break;
1751 	case 7:
1752 		s = gettext("abbreviation for current state");
1753 		break;
1754 	case 8:
1755 		s = gettext("name for scope associated with service");
1756 		break;
1757 	case 9:
1758 		s = gettext("abbreviation for current state and next state");
1759 		break;
1760 	case 10:
1761 		s = gettext("portion of the FMRI representing service name");
1762 		break;
1763 	case 11:
1764 		s = gettext("abbreviation for current state");
1765 		break;
1766 	case 12:
1767 		s = gettext("name for current state");
1768 		break;
1769 	case 13:
1770 		s = gettext("time of last state change");
1771 		break;
1772 	}
1773 
1774 	assert(s != NULL);
1775 	return (s);
1776 }
1777 
1778 
1779 static void
1780 print_usage(const char *progname, FILE *f, boolean_t do_exit)
1781 {
1782 	(void) fprintf(f, gettext(
1783 	    "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] "
1784 	    "[-sS col] [<service> ...]\n"
1785 	    "       %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] "
1786 	    "[<service> ...]\n"
1787 	    "       %1$s -l <service> ...\n"
1788 	    "       %1$s -x [-v] [<service> ...]\n"
1789 	    "       %1$s -?\n"), progname);
1790 
1791 	if (do_exit)
1792 		exit(UU_EXIT_USAGE);
1793 }
1794 
1795 #define	argserr(progname)	print_usage(progname, stderr, B_TRUE)
1796 
1797 static void
1798 print_help(const char *progname)
1799 {
1800 	int i;
1801 
1802 	print_usage(progname, stdout, B_FALSE);
1803 
1804 	(void) printf(gettext("\n"
1805 	"\t-a  list all service instances rather than "
1806 	"only those that are enabled\n"
1807 	"\t-d  list dependencies of the specified service(s)\n"
1808 	"\t-D  list dependents of the specified service(s)\n"
1809 	"\t-H  omit header line from output\n"
1810 	"\t-l  list detailed information about the specified service(s)\n"
1811 	"\t-o  list only the specified columns in the output\n"
1812 	"\t-p  list process IDs and names associated with each service\n"
1813 	"\t-R  list only those services with the specified restarter\n"
1814 	"\t-s  sort output in ascending order by the specified column(s)\n"
1815 	"\t-S  sort output in descending order by the specified column(s)\n"
1816 	"\t-v  list verbose information appropriate to the type of output\n"
1817 	"\t-x  explain the status of services that might require maintenance,\n"
1818 	"\t    or explain the status of the specified service(s)\n"
1819 	"\n\t"
1820 	"Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
1821 	"\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
1822 	"\n"
1823 	"\t%1$s [opts] svc:/network/smtp:sendmail\n"
1824 	"\t%1$s [opts] network/smtp:sendmail\n"
1825 	"\t%1$s [opts] network/*mail\n"
1826 	"\t%1$s [opts] network/smtp\n"
1827 	"\t%1$s [opts] smtp:sendmail\n"
1828 	"\t%1$s [opts] smtp\n"
1829 	"\t%1$s [opts] sendmail\n"
1830 	"\n\t"
1831 	"Columns for output or sorting can be specified using these names:\n"
1832 	"\n"), progname);
1833 
1834 	for (i = 0; i < ncolumns; i++) {
1835 		(void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR "s  %s\n",
1836 		    columns[i].name, description_of_column(i));
1837 	}
1838 }
1839 
1840 
1841 /*
1842  * A getsubopt()-like function which returns an index into the columns table.
1843  * On success, *optionp is set to point to the next sub-option, or the
1844  * terminating null if there are none.
1845  */
1846 static int
1847 getcolumnopt(char **optionp)
1848 {
1849 	char *str = *optionp, *cp;
1850 	int i;
1851 
1852 	assert(optionp != NULL);
1853 	assert(*optionp != NULL);
1854 
1855 	cp = strchr(*optionp, ',');
1856 	if (cp != NULL)
1857 		*cp = '\0';
1858 
1859 	for (i = 0; i < ncolumns; ++i) {
1860 		if (strcasecmp(str, columns[i].name) == 0) {
1861 			if (cp != NULL)
1862 				*optionp = cp + 1;
1863 			else
1864 				*optionp = strchr(*optionp, '\0');
1865 
1866 			return (i);
1867 		}
1868 	}
1869 
1870 	return (-1);
1871 }
1872 
1873 static void
1874 print_header()
1875 {
1876 	int i;
1877 	char *line_buf, *cp;
1878 
1879 	line_buf = safe_malloc(line_sz);
1880 	cp = line_buf;
1881 	for (i = 0; i < opt_cnum; ++i) {
1882 		const struct column * const colp = &columns[opt_columns[i]];
1883 
1884 		(void) snprintf(cp, colp->width + 1, "%-*s", colp->width,
1885 		    colp->name);
1886 		cp += colp->width;
1887 		*cp++ = ' ';
1888 	}
1889 
1890 	/* Trim the trailing whitespace */
1891 	--cp;
1892 	while (*cp == ' ')
1893 		--cp;
1894 	*(cp+1) = '\0';
1895 	(void) puts(line_buf);
1896 
1897 	free(line_buf);
1898 }
1899 
1900 
1901 
1902 /*
1903  * Long listing (-l) functions.
1904  */
1905 
1906 static int
1907 pidcmp(const void *l, const void *r)
1908 {
1909 	pid_t lp = *(pid_t *)l, rp = *(pid_t *)r;
1910 
1911 	if (lp < rp)
1912 		return (-1);
1913 	if (lp > rp)
1914 		return (1);
1915 	return (0);
1916 }
1917 
1918 /*
1919  * This is the strlen() of the longest label ("description"), plus intercolumn
1920  * space.
1921  */
1922 #define	DETAILED_WIDTH	(11 + 2)
1923 
1924 /*
1925  * Callback routine to print header for contract id.
1926  * Called by ctids_by_restarter and print_detailed.
1927  */
1928 static void
1929 print_ctid_header()
1930 {
1931 	(void) printf("%-*s", DETAILED_WIDTH, "contract_id");
1932 }
1933 
1934 /*
1935  * Callback routine to print a contract id.
1936  * Called by ctids_by_restarter and print_detailed.
1937  */
1938 static void
1939 print_ctid_detailed(uint64_t c)
1940 {
1941 	(void) printf("%lu ", (ctid_t)c);
1942 }
1943 
1944 static void
1945 detailed_list_processes(scf_walkinfo_t *wip)
1946 {
1947 	uint64_t c;
1948 	pid_t *pids;
1949 	uint_t i, n;
1950 	psinfo_t psi;
1951 
1952 	if (get_restarter_count_prop(wip->inst, scf_property_contract, &c,
1953 	    EMPTY_OK) != 0)
1954 		return;
1955 
1956 	if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
1957 		return;
1958 
1959 	qsort(pids, n, sizeof (*pids), pidcmp);
1960 
1961 	for (i = 0; i < n; ++i) {
1962 		(void) printf("%-*s%lu", DETAILED_WIDTH, gettext("process"),
1963 		    pids[i]);
1964 
1965 		if (get_psinfo(pids[i], &psi) == 0)
1966 			(void) printf(" %.*s", PRARGSZ, psi.pr_psargs);
1967 
1968 		(void) putchar('\n');
1969 	}
1970 
1971 	free(pids);
1972 }
1973 
1974 /*
1975  * Determines the state of a dependency.  If the FMRI specifies a file, then we
1976  * fake up a state based on whether we can access the file.
1977  */
1978 static void
1979 get_fmri_state(char *fmri, char *state, size_t state_sz)
1980 {
1981 	char *lfmri;
1982 	const char *svc_name, *inst_name, *pg_name, *path;
1983 	scf_service_t *svc;
1984 	scf_instance_t *inst;
1985 	scf_iter_t *iter;
1986 
1987 	lfmri = safe_strdup(fmri);
1988 
1989 	/*
1990 	 * Check for file:// dependencies
1991 	 */
1992 	if (scf_parse_file_fmri(lfmri, NULL, &path) == SCF_SUCCESS) {
1993 		struct stat64 statbuf;
1994 		const char *msg;
1995 
1996 		if (stat64(path, &statbuf) == 0)
1997 			msg = "online";
1998 		else if (errno == ENOENT)
1999 			msg = "absent";
2000 		else
2001 			msg = "unknown";
2002 
2003 		(void) strlcpy(state, msg, state_sz);
2004 		return;
2005 	}
2006 
2007 	/*
2008 	 * scf_parse_file_fmri() may have overwritten part of the string, so
2009 	 * copy it back.
2010 	 */
2011 	(void) strcpy(lfmri, fmri);
2012 
2013 	if (scf_parse_svc_fmri(lfmri, NULL, &svc_name, &inst_name,
2014 	    &pg_name, NULL) != SCF_SUCCESS) {
2015 		free(lfmri);
2016 		(void) strlcpy(state, "invalid", state_sz);
2017 		return;
2018 	}
2019 
2020 	free(lfmri);
2021 
2022 	if (svc_name == NULL || pg_name != NULL) {
2023 		(void) strlcpy(state, "invalid", state_sz);
2024 		return;
2025 	}
2026 
2027 	if (inst_name != NULL) {
2028 		/* instance: get state */
2029 		inst = scf_instance_create(h);
2030 		if (inst == NULL)
2031 			scfdie();
2032 
2033 		if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
2034 		    NULL, SCF_DECODE_FMRI_EXACT) == SCF_SUCCESS)
2035 			get_restarter_string_prop(inst, scf_property_state,
2036 			    state, state_sz);
2037 		else {
2038 			switch (scf_error()) {
2039 			case SCF_ERROR_INVALID_ARGUMENT:
2040 				(void) strlcpy(state, "invalid", state_sz);
2041 				break;
2042 			case SCF_ERROR_NOT_FOUND:
2043 				(void) strlcpy(state, "absent", state_sz);
2044 				break;
2045 
2046 			default:
2047 				scfdie();
2048 			}
2049 		}
2050 
2051 		scf_instance_destroy(inst);
2052 		return;
2053 	}
2054 
2055 	/*
2056 	 * service: If only one instance, use that state.  Otherwise, say
2057 	 * "multiple".
2058 	 */
2059 	if ((svc = scf_service_create(h)) == NULL ||
2060 	    (inst = scf_instance_create(h)) == NULL ||
2061 	    (iter = scf_iter_create(h)) == NULL)
2062 		scfdie();
2063 
2064 	if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
2065 	    SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
2066 		switch (scf_error()) {
2067 		case SCF_ERROR_INVALID_ARGUMENT:
2068 			(void) strlcpy(state, "invalid", state_sz);
2069 			goto out;
2070 		case SCF_ERROR_NOT_FOUND:
2071 			(void) strlcpy(state, "absent", state_sz);
2072 			goto out;
2073 
2074 		default:
2075 			scfdie();
2076 		}
2077 	}
2078 
2079 	if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
2080 		scfdie();
2081 
2082 	switch (scf_iter_next_instance(iter, inst)) {
2083 	case 0:
2084 		(void) strlcpy(state, "absent", state_sz);
2085 		goto out;
2086 
2087 	case 1:
2088 		break;
2089 
2090 	default:
2091 		scfdie();
2092 	}
2093 
2094 	/* Get the state in case this is the only instance. */
2095 	get_restarter_string_prop(inst, scf_property_state, state, state_sz);
2096 
2097 	switch (scf_iter_next_instance(iter, inst)) {
2098 	case 0:
2099 		break;
2100 
2101 	case 1:
2102 		/* Nope, multiple instances. */
2103 		(void) strlcpy(state, "multiple", state_sz);
2104 		goto out;
2105 
2106 	default:
2107 		scfdie();
2108 	}
2109 
2110 out:
2111 	scf_iter_destroy(iter);
2112 	scf_instance_destroy(inst);
2113 	scf_service_destroy(svc);
2114 }
2115 
2116 static void
2117 print_detailed_dependency(scf_propertygroup_t *pg)
2118 {
2119 	scf_property_t *eprop;
2120 	scf_iter_t *iter;
2121 	scf_type_t ty;
2122 	char *val_buf;
2123 	int i;
2124 
2125 	if ((eprop = scf_property_create(h)) == NULL ||
2126 	    (iter = scf_iter_create(h)) == NULL)
2127 		scfdie();
2128 
2129 	val_buf = safe_malloc(max_scf_value_length + 1);
2130 
2131 	if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, eprop) !=
2132 	    SCF_SUCCESS ||
2133 	    scf_property_type(eprop, &ty) != SCF_SUCCESS ||
2134 	    ty != SCF_TYPE_FMRI)
2135 		return;
2136 
2137 	(void) printf("%-*s", DETAILED_WIDTH, gettext("dependency"));
2138 
2139 	/* Print the grouping */
2140 	if (pg_get_single_val(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
2141 	    val_buf, max_scf_value_length + 1, 0) == 0)
2142 		(void) fputs(val_buf, stdout);
2143 	else
2144 		(void) putchar('?');
2145 
2146 	(void) putchar('/');
2147 
2148 	if (pg_get_single_val(pg, SCF_PROPERTY_RESTART_ON, SCF_TYPE_ASTRING,
2149 	    val_buf, max_scf_value_length + 1, 0) == 0)
2150 		(void) fputs(val_buf, stdout);
2151 	else
2152 		(void) putchar('?');
2153 
2154 	/* Print the dependency entities. */
2155 	if (scf_iter_property_values(iter, eprop) == -1)
2156 		scfdie();
2157 
2158 	while ((i = scf_iter_next_value(iter, g_val)) == 1) {
2159 		char state[MAX_SCF_STATE_STRING_SZ];
2160 
2161 		if (scf_value_get_astring(g_val, val_buf,
2162 		    max_scf_value_length + 1) < 0)
2163 			scfdie();
2164 
2165 		(void) putchar(' ');
2166 		(void) fputs(val_buf, stdout);
2167 
2168 		/* Print the state. */
2169 		state[0] = '-';
2170 		state[1] = '\0';
2171 
2172 		get_fmri_state(val_buf, state, sizeof (state));
2173 
2174 		(void) printf(" (%s)", state);
2175 	}
2176 	if (i == -1)
2177 		scfdie();
2178 
2179 	(void) putchar('\n');
2180 
2181 	free(val_buf);
2182 	scf_iter_destroy(iter);
2183 	scf_property_destroy(eprop);
2184 }
2185 
2186 /* ARGSUSED */
2187 static int
2188 print_detailed(void *unused, scf_walkinfo_t *wip)
2189 {
2190 	scf_snapshot_t *snap;
2191 	scf_propertygroup_t *rpg;
2192 	scf_iter_t *pg_iter;
2193 
2194 	char *buf;
2195 	char *timebuf;
2196 	size_t tbsz;
2197 	int ret;
2198 	uint64_t c;
2199 	int temp, perm;
2200 	struct timeval tv;
2201 	time_t stime;
2202 	struct tm *tmp;
2203 	int restarter_spec;
2204 	int restarter_ret;
2205 
2206 	const char * const fmt = "%-*s%s\n";
2207 
2208 	assert(wip->pg == NULL);
2209 
2210 	rpg = scf_pg_create(h);
2211 	if (rpg == NULL)
2212 		scfdie();
2213 
2214 	if (first_paragraph)
2215 		first_paragraph = 0;
2216 	else
2217 		(void) putchar('\n');
2218 
2219 	buf = safe_malloc(max_scf_fmri_length + 1);
2220 
2221 	if (scf_instance_to_fmri(wip->inst, buf, max_scf_fmri_length + 1) != -1)
2222 		(void) printf(fmt, DETAILED_WIDTH, "fmri", buf);
2223 
2224 	if (common_name_buf == NULL)
2225 		common_name_buf = safe_malloc(max_scf_value_length + 1);
2226 
2227 	if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
2228 	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
2229 	    == 0)
2230 		(void) printf(fmt, DETAILED_WIDTH, gettext("name"),
2231 		    common_name_buf);
2232 	else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
2233 	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
2234 	    == 0)
2235 		(void) printf(fmt, DETAILED_WIDTH, gettext("name"),
2236 		    common_name_buf);
2237 
2238 	/*
2239 	 * Synthesize an 'enabled' property that hides the enabled_ovr
2240 	 * implementation from the user.  If the service has been temporarily
2241 	 * set to a state other than its permanent value, alert the user with
2242 	 * a '(temporary)' message.
2243 	 */
2244 	perm = instance_enabled(wip->inst, B_FALSE);
2245 	temp = instance_enabled(wip->inst, B_TRUE);
2246 	if (temp != -1) {
2247 		if (temp != perm)
2248 			(void) printf(gettext("%-*s%s (temporary)\n"),
2249 			    DETAILED_WIDTH, gettext("enabled"),
2250 			    temp ? gettext("true") : gettext("false"));
2251 		else
2252 			(void) printf(fmt, DETAILED_WIDTH,
2253 			    gettext("enabled"), temp ? gettext("true") :
2254 			    gettext("false"));
2255 	} else if (perm != -1) {
2256 		(void) printf(fmt, DETAILED_WIDTH, gettext("enabled"),
2257 		    perm ? gettext("true") : gettext("false"));
2258 	}
2259 
2260 	/*
2261 	 * Property values may be longer than max_scf_fmri_length, but these
2262 	 * shouldn't be, so we'll just reuse buf.  The user can use svcprop if
2263 	 * he suspects something fishy.
2264 	 */
2265 	if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) {
2266 		if (scf_error() != SCF_ERROR_NOT_FOUND)
2267 			scfdie();
2268 
2269 		scf_pg_destroy(rpg);
2270 		rpg = NULL;
2271 	}
2272 
2273 	if (rpg) {
2274 		if (pg_get_single_val(rpg, scf_property_state, SCF_TYPE_ASTRING,
2275 		    buf, max_scf_fmri_length + 1, 0) == 0)
2276 			(void) printf(fmt, DETAILED_WIDTH, gettext("state"),
2277 			    buf);
2278 
2279 		if (pg_get_single_val(rpg, scf_property_next_state,
2280 		    SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2281 			(void) printf(fmt, DETAILED_WIDTH,
2282 			    gettext("next_state"), buf);
2283 
2284 		if (pg_get_single_val(rpg, SCF_PROPERTY_STATE_TIMESTAMP,
2285 		    SCF_TYPE_TIME, &tv, NULL, 0) == 0) {
2286 			stime = tv.tv_sec;
2287 			tmp = localtime(&stime);
2288 			for (tbsz = 50; ; tbsz *= 2) {
2289 				timebuf = safe_malloc(tbsz);
2290 				if (strftime(timebuf, tbsz, NULL, tmp) != 0)
2291 					break;
2292 				free(timebuf);
2293 			}
2294 			(void) printf(fmt, DETAILED_WIDTH,
2295 			    gettext("state_time"),
2296 			    timebuf);
2297 			free(timebuf);
2298 		}
2299 
2300 		if (pg_get_single_val(rpg, SCF_PROPERTY_ALT_LOGFILE,
2301 		    SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2302 			(void) printf(fmt, DETAILED_WIDTH,
2303 			    gettext("alt_logfile"), buf);
2304 
2305 		if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE,
2306 		    SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2307 			(void) printf(fmt, DETAILED_WIDTH, gettext("logfile"),
2308 			    buf);
2309 	}
2310 
2311 	if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
2312 	    SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, buf,
2313 	    max_scf_fmri_length + 1, 0, 0, 1) == 0)
2314 		(void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), buf);
2315 	else
2316 		(void) printf(fmt, DETAILED_WIDTH, gettext("restarter"),
2317 		    SCF_SERVICE_STARTD);
2318 
2319 	free(buf);
2320 
2321 	/*
2322 	 * Use the restarter specific routine to print the ctids, if available.
2323 	 * If restarter specific action is available and it fails, then die.
2324 	 */
2325 	restarter_ret = ctids_by_restarter(wip, &c, 1, 0,
2326 	    &restarter_spec, print_ctid_header, print_ctid_detailed);
2327 	if (restarter_spec == 1) {
2328 		if (restarter_ret != 0)
2329 			uu_die(gettext("Unable to get restarter for %s"),
2330 			    wip->fmri);
2331 		goto restarter_common;
2332 	}
2333 
2334 	if (rpg) {
2335 		scf_iter_t *iter;
2336 
2337 		if ((iter = scf_iter_create(h)) == NULL)
2338 			scfdie();
2339 
2340 		if (scf_pg_get_property(rpg, scf_property_contract, g_prop) ==
2341 		    0) {
2342 			if (scf_property_is_type(g_prop, SCF_TYPE_COUNT) == 0) {
2343 
2344 				/* Callback to print ctid header */
2345 				print_ctid_header();
2346 
2347 				if (scf_iter_property_values(iter, g_prop) != 0)
2348 					scfdie();
2349 
2350 				for (;;) {
2351 					ret = scf_iter_next_value(iter, g_val);
2352 					if (ret == -1)
2353 						scfdie();
2354 					if (ret == 0)
2355 						break;
2356 
2357 					if (scf_value_get_count(g_val, &c) != 0)
2358 						scfdie();
2359 
2360 					/* Callback to print contract id. */
2361 					print_ctid_detailed(c);
2362 				}
2363 
2364 				(void) putchar('\n');
2365 			} else {
2366 				if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
2367 					scfdie();
2368 			}
2369 		} else {
2370 			if (scf_error() != SCF_ERROR_NOT_FOUND)
2371 				scfdie();
2372 		}
2373 
2374 		scf_iter_destroy(iter);
2375 	} else {
2376 		if (scf_error() != SCF_ERROR_NOT_FOUND)
2377 			scfdie();
2378 	}
2379 
2380 restarter_common:
2381 	scf_pg_destroy(rpg);
2382 
2383 	/* Dependencies. */
2384 	if ((pg_iter = scf_iter_create(h)) == NULL)
2385 		scfdie();
2386 
2387 	snap = get_running_snapshot(wip->inst);
2388 
2389 	if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap,
2390 	    SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
2391 		scfdie();
2392 
2393 	while ((ret = scf_iter_next_pg(pg_iter, g_pg)) == 1)
2394 		print_detailed_dependency(g_pg);
2395 	if (ret == -1)
2396 		scfdie();
2397 
2398 	scf_snapshot_destroy(snap);
2399 	scf_iter_destroy(pg_iter);
2400 
2401 	if (opt_processes)
2402 		detailed_list_processes(wip);
2403 
2404 	return (0);
2405 }
2406 
2407 /*
2408  * Append a one-lined description of each process in inst's contract(s) and
2409  * return the augmented string.
2410  */
2411 static char *
2412 add_processes(scf_walkinfo_t *wip, char *line, scf_propertygroup_t *lpg)
2413 {
2414 	pid_t *pids = NULL;
2415 	uint_t i, n = 0;
2416 
2417 	if (lpg == NULL) {
2418 		if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
2419 			return (line);
2420 	} else {
2421 		/* Legacy services */
2422 		scf_iter_t *iter;
2423 
2424 		if ((iter = scf_iter_create(h)) == NULL)
2425 			scfdie();
2426 
2427 		(void) propvals_to_pids(lpg, scf_property_contract, &pids, &n,
2428 		    g_prop, g_val, iter);
2429 
2430 		scf_iter_destroy(iter);
2431 	}
2432 
2433 	if (n == 0)
2434 		return (line);
2435 
2436 	qsort(pids, n, sizeof (*pids), pidcmp);
2437 
2438 	for (i = 0; i < n; ++i) {
2439 		char *cp, stime[9];
2440 		psinfo_t psi;
2441 		struct tm *tm;
2442 		int len = 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ;
2443 
2444 		if (get_psinfo(pids[i], &psi) != 0)
2445 			continue;
2446 
2447 		line = realloc(line, strlen(line) + len);
2448 		if (line == NULL)
2449 			uu_die(gettext("Out of memory.\n"));
2450 
2451 		cp = strchr(line, '\0');
2452 
2453 		tm = localtime(&psi.pr_start.tv_sec);
2454 
2455 		/*
2456 		 * Print time if started within the past 24 hours, print date
2457 		 * if within the past 12 months, print year if started greater
2458 		 * than 12 months ago.
2459 		 */
2460 		if (now - psi.pr_start.tv_sec < 24 * 60 * 60)
2461 			(void) strftime(stime, sizeof (stime),
2462 			    gettext(FORMAT_TIME), tm);
2463 		else if (now - psi.pr_start.tv_sec < 12 * 30 * 24 * 60 * 60)
2464 			(void) strftime(stime, sizeof (stime),
2465 			    gettext(FORMAT_DATE), tm);
2466 		else
2467 			(void) strftime(stime, sizeof (stime),
2468 			    gettext(FORMAT_YEAR), tm);
2469 
2470 		(void) snprintf(cp, len, "\n               %-8s   %6ld %.*s",
2471 		    stime, pids[i], PRFNSZ, psi.pr_fname);
2472 	}
2473 
2474 	free(pids);
2475 
2476 	return (line);
2477 }
2478 
2479 /*ARGSUSED*/
2480 static int
2481 list_instance(void *unused, scf_walkinfo_t *wip)
2482 {
2483 	struct avl_string *lp;
2484 	char *cp;
2485 	int i;
2486 	uu_avl_index_t idx;
2487 
2488 	/*
2489 	 * If the user has specified a restarter, check for a match first
2490 	 */
2491 	if (restarters != NULL) {
2492 		struct pfmri_list *rest;
2493 		int match;
2494 		char *restarter_fmri;
2495 		const char *scope_name, *svc_name, *inst_name, *pg_name;
2496 
2497 		/* legacy services don't have restarters */
2498 		if (wip->pg != NULL)
2499 			return (0);
2500 
2501 		restarter_fmri = safe_malloc(max_scf_fmri_length + 1);
2502 
2503 		if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
2504 		    SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, restarter_fmri,
2505 		    max_scf_fmri_length + 1, 0, 0, 1) != 0)
2506 			(void) strcpy(restarter_fmri, SCF_SERVICE_STARTD);
2507 
2508 		if (scf_parse_svc_fmri(restarter_fmri, &scope_name, &svc_name,
2509 		    &inst_name, &pg_name, NULL) != SCF_SUCCESS) {
2510 			free(restarter_fmri);
2511 			return (0);
2512 		}
2513 
2514 		match = 0;
2515 		for (rest = restarters; rest != NULL; rest = rest->next) {
2516 			if (strcmp(rest->scope, scope_name) == 0 &&
2517 			    strcmp(rest->service, svc_name) == 0 &&
2518 			    strcmp(rest->instance, inst_name) == 0)
2519 				match = 1;
2520 		}
2521 
2522 		free(restarter_fmri);
2523 
2524 		if (!match)
2525 			return (0);
2526 	}
2527 
2528 	if (wip->pg == NULL && ht_buckets != NULL && ht_add(wip->fmri)) {
2529 		/* It was already there. */
2530 		return (0);
2531 	}
2532 
2533 	lp = safe_malloc(sizeof (*lp));
2534 
2535 	lp->str = NULL;
2536 	for (i = 0; i < opt_cnum; ++i) {
2537 		columns[opt_columns[i]].sprint(&lp->str, wip);
2538 	}
2539 	cp = lp->str + strlen(lp->str);
2540 	cp--;
2541 	while (*cp == ' ')
2542 		cp--;
2543 	*(cp+1) = '\0';
2544 
2545 	/* If we're supposed to list the processes, too, do that now. */
2546 	if (opt_processes)
2547 		lp->str = add_processes(wip, lp->str, wip->pg);
2548 
2549 	/* Create the sort key. */
2550 	cp = lp->key = safe_malloc(sortkey_sz);
2551 	for (i = 0; i < opt_snum; ++i) {
2552 		int j = opt_sort[i] & 0xff;
2553 
2554 		assert(columns[j].get_sortkey != NULL);
2555 		columns[j].get_sortkey(cp, opt_sort[i] & ~0xff, wip);
2556 		cp += columns[j].sortkey_width;
2557 	}
2558 
2559 	/* Insert into AVL tree. */
2560 	uu_avl_node_init(lp, &lp->node, lines_pool);
2561 	(void) uu_avl_find(lines, lp, NULL, &idx);
2562 	uu_avl_insert(lines, lp, idx);
2563 
2564 	return (0);
2565 }
2566 
2567 static int
2568 list_if_enabled(void *unused, scf_walkinfo_t *wip)
2569 {
2570 	if (wip->pg != NULL ||
2571 	    instance_enabled(wip->inst, B_FALSE) == 1 ||
2572 	    instance_enabled(wip->inst, B_TRUE) == 1)
2573 		return (list_instance(unused, wip));
2574 
2575 	return (0);
2576 }
2577 
2578 /*
2579  * Service FMRI selection: Lookup and call list_instance() for the instances.
2580  * Instance FMRI selection: Lookup and call list_instance().
2581  *
2582  * Note: This is shoehorned into a walk_dependencies() callback prototype so
2583  * it can be used in list_dependencies.
2584  */
2585 static int
2586 list_svc_or_inst_fmri(void *complain, scf_walkinfo_t *wip)
2587 {
2588 	char *fmri;
2589 	const char *svc_name, *inst_name, *pg_name, *save;
2590 	scf_iter_t *iter;
2591 	int ret;
2592 
2593 	fmri = safe_strdup(wip->fmri);
2594 
2595 	if (scf_parse_svc_fmri(fmri, NULL, &svc_name, &inst_name, &pg_name,
2596 	    NULL) != SCF_SUCCESS) {
2597 		if (complain)
2598 			uu_warn(gettext("FMRI \"%s\" is invalid.\n"),
2599 			    wip->fmri);
2600 		exit_status = UU_EXIT_FATAL;
2601 		free(fmri);
2602 		return (0);
2603 	}
2604 
2605 	/*
2606 	 * Yes, this invalidates *_name, but we only care whether they're NULL
2607 	 * or not.
2608 	 */
2609 	free(fmri);
2610 
2611 	if (svc_name == NULL || pg_name != NULL) {
2612 		if (complain)
2613 			uu_warn(gettext("FMRI \"%s\" does not designate a "
2614 			    "service or instance.\n"), wip->fmri);
2615 		return (0);
2616 	}
2617 
2618 	if (inst_name != NULL) {
2619 		/* instance */
2620 		if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc,
2621 		    wip->inst, NULL, NULL, 0) != SCF_SUCCESS) {
2622 			if (scf_error() != SCF_ERROR_NOT_FOUND)
2623 				scfdie();
2624 
2625 			if (complain)
2626 				uu_warn(gettext(
2627 				    "Instance \"%s\" does not exist.\n"),
2628 				    wip->fmri);
2629 			return (0);
2630 		}
2631 
2632 		return (list_instance(NULL, wip));
2633 	}
2634 
2635 	/* service: Walk the instances. */
2636 	if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, NULL,
2637 	    NULL, NULL, 0) != SCF_SUCCESS) {
2638 		if (scf_error() != SCF_ERROR_NOT_FOUND)
2639 			scfdie();
2640 
2641 		if (complain)
2642 			uu_warn(gettext("Service \"%s\" does not exist.\n"),
2643 			    wip->fmri);
2644 
2645 		exit_status = UU_EXIT_FATAL;
2646 
2647 		return (0);
2648 	}
2649 
2650 	iter = scf_iter_create(h);
2651 	if (iter == NULL)
2652 		scfdie();
2653 
2654 	if (scf_iter_service_instances(iter, wip->svc) != SCF_SUCCESS)
2655 		scfdie();
2656 
2657 	if ((fmri = malloc(max_scf_fmri_length + 1)) == NULL) {
2658 		scf_iter_destroy(iter);
2659 		exit_status = UU_EXIT_FATAL;
2660 		return (0);
2661 	}
2662 
2663 	save = wip->fmri;
2664 	wip->fmri = fmri;
2665 	while ((ret = scf_iter_next_instance(iter, wip->inst)) == 1) {
2666 		if (scf_instance_to_fmri(wip->inst, fmri,
2667 		    max_scf_fmri_length + 1) <= 0)
2668 			scfdie();
2669 		(void) list_instance(NULL, wip);
2670 	}
2671 	free(fmri);
2672 	wip->fmri = save;
2673 	if (ret == -1)
2674 		scfdie();
2675 
2676 	exit_status = UU_EXIT_OK;
2677 
2678 	scf_iter_destroy(iter);
2679 
2680 	return (0);
2681 }
2682 
2683 /*
2684  * Dependency selection: Straightforward since each instance lists the
2685  * services it depends on.
2686  */
2687 
2688 static void
2689 walk_dependencies(scf_walkinfo_t *wip, scf_walk_callback callback, void *data)
2690 {
2691 	scf_snapshot_t *snap;
2692 	scf_iter_t *iter, *viter;
2693 	int ret, vret;
2694 	char *dep;
2695 
2696 	assert(wip->inst != NULL);
2697 
2698 	if ((iter = scf_iter_create(h)) == NULL ||
2699 	    (viter = scf_iter_create(h)) == NULL)
2700 		scfdie();
2701 
2702 	snap = get_running_snapshot(wip->inst);
2703 
2704 	if (scf_iter_instance_pgs_typed_composed(iter, wip->inst, snap,
2705 	    SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
2706 		scfdie();
2707 
2708 	dep = safe_malloc(max_scf_value_length + 1);
2709 
2710 	while ((ret = scf_iter_next_pg(iter, g_pg)) == 1) {
2711 		scf_type_t ty;
2712 
2713 		/* Ignore exclude_any dependencies. */
2714 		if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) !=
2715 		    SCF_SUCCESS) {
2716 			if (scf_error() != SCF_ERROR_NOT_FOUND)
2717 				scfdie();
2718 
2719 			continue;
2720 		}
2721 
2722 		if (scf_property_type(g_prop, &ty) != SCF_SUCCESS)
2723 			scfdie();
2724 
2725 		if (ty != SCF_TYPE_ASTRING)
2726 			continue;
2727 
2728 		if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) {
2729 			if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
2730 				scfdie();
2731 
2732 			continue;
2733 		}
2734 
2735 		if (scf_value_get_astring(g_val, dep,
2736 		    max_scf_value_length + 1) < 0)
2737 			scfdie();
2738 
2739 		if (strcmp(dep, SCF_DEP_EXCLUDE_ALL) == 0)
2740 			continue;
2741 
2742 		if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) !=
2743 		    SCF_SUCCESS) {
2744 			if (scf_error() != SCF_ERROR_NOT_FOUND)
2745 				scfdie();
2746 
2747 			continue;
2748 		}
2749 
2750 		if (scf_iter_property_values(viter, g_prop) != SCF_SUCCESS)
2751 			scfdie();
2752 
2753 		while ((vret = scf_iter_next_value(viter, g_val)) == 1) {
2754 			if (scf_value_get_astring(g_val, dep,
2755 			    max_scf_value_length + 1) < 0)
2756 				scfdie();
2757 
2758 			wip->fmri = dep;
2759 			if (callback(data, wip) != 0)
2760 				goto out;
2761 		}
2762 		if (vret == -1)
2763 			scfdie();
2764 	}
2765 	if (ret == -1)
2766 		scfdie();
2767 
2768 out:
2769 	scf_iter_destroy(viter);
2770 	scf_iter_destroy(iter);
2771 	scf_snapshot_destroy(snap);
2772 }
2773 
2774 static int
2775 list_dependencies(void *data, scf_walkinfo_t *wip)
2776 {
2777 	walk_dependencies(wip, list_svc_or_inst_fmri, data);
2778 	return (0);
2779 }
2780 
2781 
2782 /*
2783  * Dependent selection: The "providing" service's or instance's FMRI is parsed
2784  * into the provider_* variables, the instances are walked, and any instance
2785  * which lists an FMRI which parses to these components is selected.  This is
2786  * inefficient in the face of multiple operands, but that should be uncommon.
2787  */
2788 
2789 static char *provider_scope;
2790 static char *provider_svc;
2791 static char *provider_inst;	/* NULL for services */
2792 
2793 /*ARGSUSED*/
2794 static int
2795 check_against_provider(void *arg, scf_walkinfo_t *wip)
2796 {
2797 	char *cfmri;
2798 	const char *scope_name, *svc_name, *inst_name, *pg_name;
2799 	int *matchp = arg;
2800 
2801 	cfmri = safe_strdup(wip->fmri);
2802 
2803 	if (scf_parse_svc_fmri(cfmri, &scope_name, &svc_name, &inst_name,
2804 	    &pg_name, NULL) != SCF_SUCCESS) {
2805 		free(cfmri);
2806 		return (0);
2807 	}
2808 
2809 	if (svc_name == NULL || pg_name != NULL) {
2810 		free(cfmri);
2811 		return (0);
2812 	}
2813 
2814 	/*
2815 	 * If the user has specified an instance, then also match dependencies
2816 	 * on the service itself.
2817 	 */
2818 	*matchp = (strcmp(provider_scope, scope_name) == 0 &&
2819 	    strcmp(provider_svc, svc_name) == 0 &&
2820 	    (provider_inst == NULL ? (inst_name == NULL) :
2821 	    (inst_name == NULL || strcmp(provider_inst, inst_name) == 0)));
2822 
2823 	free(cfmri);
2824 
2825 	/* Stop on matches. */
2826 	return (*matchp);
2827 }
2828 
2829 static int
2830 list_if_dependent(void *unused, scf_walkinfo_t *wip)
2831 {
2832 	/* Only proceed if this instance depends on provider_*. */
2833 	int match = 0;
2834 
2835 	(void) walk_dependencies(wip, check_against_provider, &match);
2836 
2837 	if (match)
2838 		return (list_instance(unused, wip));
2839 
2840 	return (0);
2841 }
2842 
2843 /*ARGSUSED*/
2844 static int
2845 list_dependents(void *unused, scf_walkinfo_t *wip)
2846 {
2847 	char *save;
2848 	int ret;
2849 
2850 	if (scf_scope_get_name(wip->scope, provider_scope,
2851 	    max_scf_fmri_length) <= 0 ||
2852 	    scf_service_get_name(wip->svc, provider_svc,
2853 	    max_scf_fmri_length) <= 0)
2854 		scfdie();
2855 
2856 	save = provider_inst;
2857 	if (wip->inst == NULL)
2858 		provider_inst = NULL;
2859 	else if (scf_instance_get_name(wip->inst, provider_inst,
2860 	    max_scf_fmri_length) <= 0)
2861 		scfdie();
2862 
2863 	ret = scf_walk_fmri(h, 0, NULL, 0, list_if_dependent, NULL, NULL,
2864 	    uu_warn);
2865 
2866 	provider_inst = save;
2867 
2868 	return (ret);
2869 }
2870 
2871 /*
2872  * main() & helpers
2873  */
2874 
2875 static void
2876 add_sort_column(const char *col, int reverse)
2877 {
2878 	int i;
2879 
2880 	++opt_snum;
2881 
2882 	opt_sort = realloc(opt_sort, opt_snum * sizeof (*opt_sort));
2883 	if (opt_sort == NULL)
2884 		uu_die(gettext("Too many sort criteria: out of memory.\n"));
2885 
2886 	for (i = 0; i < ncolumns; ++i) {
2887 		if (strcasecmp(col, columns[i].name) == 0)
2888 			break;
2889 	}
2890 
2891 	if (i < ncolumns)
2892 		opt_sort[opt_snum - 1] = (reverse ? i | 0x100 : i);
2893 	else
2894 		uu_die(gettext("Unrecognized sort column \"%s\".\n"), col);
2895 
2896 	sortkey_sz += columns[i].sortkey_width;
2897 }
2898 
2899 static void
2900 add_restarter(const char *fmri)
2901 {
2902 	char *cfmri;
2903 	const char *pg_name;
2904 	struct pfmri_list *rest;
2905 
2906 	cfmri = safe_strdup(fmri);
2907 	rest = safe_malloc(sizeof (*rest));
2908 
2909 	if (scf_parse_svc_fmri(cfmri, &rest->scope, &rest->service,
2910 	    &rest->instance, &pg_name, NULL) != SCF_SUCCESS)
2911 		uu_die(gettext("Restarter FMRI \"%s\" is invalid.\n"), fmri);
2912 
2913 	if (rest->instance == NULL || pg_name != NULL)
2914 		uu_die(gettext("Restarter FMRI \"%s\" does not designate an "
2915 		    "instance.\n"), fmri);
2916 
2917 	rest->next = restarters;
2918 	restarters = rest;
2919 	return;
2920 
2921 err:
2922 	free(cfmri);
2923 	free(rest);
2924 }
2925 
2926 /* ARGSUSED */
2927 static int
2928 line_cmp(const void *l_arg, const void *r_arg, void *private)
2929 {
2930 	const struct avl_string *l = l_arg;
2931 	const struct avl_string *r = r_arg;
2932 
2933 	return (memcmp(l->key, r->key, sortkey_sz));
2934 }
2935 
2936 /* ARGSUSED */
2937 static int
2938 print_line(void *e, void *private)
2939 {
2940 	struct avl_string *lp = e;
2941 
2942 	(void) puts(lp->str);
2943 
2944 	return (UU_WALK_NEXT);
2945 }
2946 
2947 int
2948 main(int argc, char **argv)
2949 {
2950 	char opt, opt_mode;
2951 	int i, n;
2952 	char *columns_str = NULL;
2953 	char *cp;
2954 	const char *progname;
2955 	int err;
2956 
2957 	int show_all = 0;
2958 	int show_header = 1;
2959 
2960 	const char * const options = "aHpvo:R:s:S:dDl?x";
2961 
2962 	(void) setlocale(LC_ALL, "");
2963 
2964 	locale = setlocale(LC_MESSAGES, "");
2965 	if (locale) {
2966 		locale = safe_strdup(locale);
2967 		sanitize_locale(locale);
2968 	}
2969 
2970 	(void) textdomain(TEXT_DOMAIN);
2971 	progname = uu_setpname(argv[0]);
2972 
2973 	exit_status = UU_EXIT_OK;
2974 
2975 	max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
2976 	max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
2977 	max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2978 
2979 	if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
2980 	    max_scf_fmri_length == -1)
2981 		scfdie();
2982 
2983 	now = time(NULL);
2984 	assert(now != -1);
2985 
2986 	/*
2987 	 * opt_mode is the mode of operation.  0 for plain, 'd' for
2988 	 * dependencies, 'D' for dependents, and 'l' for detailed (long).  We
2989 	 * need to know now so we know which options are valid.
2990 	 */
2991 	opt_mode = 0;
2992 	while ((opt = getopt(argc, argv, options)) != -1) {
2993 		switch (opt) {
2994 		case '?':
2995 			if (optopt == '?') {
2996 				print_help(progname);
2997 				return (UU_EXIT_OK);
2998 			} else {
2999 				argserr(progname);
3000 				/* NOTREACHED */
3001 			}
3002 
3003 		case 'd':
3004 		case 'D':
3005 		case 'l':
3006 			if (opt_mode != 0)
3007 				argserr(progname);
3008 
3009 			opt_mode = opt;
3010 			break;
3011 
3012 		case 'x':
3013 			if (opt_mode != 0)
3014 				argserr(progname);
3015 
3016 			opt_mode = opt;
3017 			break;
3018 
3019 		default:
3020 			break;
3021 		}
3022 	}
3023 
3024 	sortkey_sz = 0;
3025 
3026 	optind = 1;	/* Reset getopt() */
3027 	while ((opt = getopt(argc, argv, options)) != -1) {
3028 		switch (opt) {
3029 		case 'a':
3030 			if (opt_mode != 0)
3031 				argserr(progname);
3032 			show_all = 1;
3033 			break;
3034 
3035 		case 'H':
3036 			if (opt_mode == 'l' || opt_mode == 'x')
3037 				argserr(progname);
3038 			show_header = 0;
3039 			break;
3040 
3041 		case 'p':
3042 			if (opt_mode == 'x')
3043 				argserr(progname);
3044 			opt_processes = 1;
3045 			break;
3046 
3047 		case 'v':
3048 			if (opt_mode == 'l')
3049 				argserr(progname);
3050 			opt_verbose = 1;
3051 			break;
3052 
3053 		case 'o':
3054 			if (opt_mode == 'l' || opt_mode == 'x')
3055 				argserr(progname);
3056 			columns_str = optarg;
3057 			break;
3058 
3059 		case 'R':
3060 			if (opt_mode != 0 || opt_mode == 'x')
3061 				argserr(progname);
3062 
3063 			add_restarter(optarg);
3064 			break;
3065 
3066 		case 's':
3067 		case 'S':
3068 			if (opt_mode != 0)
3069 				argserr(progname);
3070 
3071 			add_sort_column(optarg, optopt == 'S');
3072 			break;
3073 
3074 		case 'd':
3075 		case 'D':
3076 		case 'l':
3077 		case 'x':
3078 			assert(opt_mode == optopt);
3079 			break;
3080 
3081 		case '?':
3082 			argserr(progname);
3083 			/* NOTREACHED */
3084 
3085 		default:
3086 			assert(0);
3087 			abort();
3088 		}
3089 	}
3090 
3091 	/*
3092 	 * -a is only meaningful when given no arguments
3093 	 */
3094 	if (show_all && optind != argc)
3095 		uu_warn(gettext("-a ignored when used with arguments.\n"));
3096 
3097 	h = scf_handle_create(SCF_VERSION);
3098 	if (h == NULL)
3099 		scfdie();
3100 
3101 	if (scf_handle_bind(h) == -1)
3102 		uu_die(gettext("Could not bind to repository server: %s.  "
3103 		    "Exiting.\n"), scf_strerror(scf_error()));
3104 
3105 	if ((g_pg = scf_pg_create(h)) == NULL ||
3106 	    (g_prop = scf_property_create(h)) == NULL ||
3107 	    (g_val = scf_value_create(h)) == NULL)
3108 		scfdie();
3109 
3110 	argc -= optind;
3111 	argv += optind;
3112 
3113 	/*
3114 	 * If we're in long mode, take care of it now before we deal with the
3115 	 * sorting and the columns, since we won't use them anyway.
3116 	 */
3117 	if (opt_mode == 'l') {
3118 		if (argc == 0)
3119 			argserr(progname);
3120 
3121 		if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3122 		    print_detailed, NULL, &exit_status, uu_warn)) != 0) {
3123 			uu_warn(gettext("failed to iterate over "
3124 			    "instances: %s\n"), scf_strerror(err));
3125 			exit_status = UU_EXIT_FATAL;
3126 		}
3127 
3128 		return (exit_status);
3129 	}
3130 
3131 	if (opt_mode == 'x') {
3132 		explain(opt_verbose, argc, argv);
3133 
3134 		return (exit_status);
3135 	}
3136 
3137 
3138 	if (opt_snum == 0) {
3139 		/* Default sort. */
3140 		add_sort_column("state", 0);
3141 		add_sort_column("stime", 0);
3142 		add_sort_column("fmri", 0);
3143 	}
3144 
3145 	if (columns_str == NULL) {
3146 		if (!opt_verbose)
3147 			columns_str = safe_strdup("state,stime,fmri");
3148 		else
3149 			columns_str =
3150 			    safe_strdup("state,nstate,stime,ctid,fmri");
3151 	}
3152 
3153 	/* Decode columns_str into opt_columns. */
3154 	line_sz = 0;
3155 
3156 	opt_cnum = 1;
3157 	for (cp = columns_str; *cp != '\0'; ++cp)
3158 		if (*cp == ',')
3159 			++opt_cnum;
3160 
3161 	opt_columns = malloc(opt_cnum * sizeof (*opt_columns));
3162 	if (opt_columns == NULL)
3163 		uu_die(gettext("Too many columns.\n"));
3164 
3165 	for (n = 0; *columns_str != '\0'; ++n) {
3166 		i = getcolumnopt(&columns_str);
3167 		if (i == -1)
3168 			uu_die(gettext("Unknown column \"%s\".\n"),
3169 			    columns_str);
3170 
3171 		if (strcmp(columns[i].name, "N") == 0 ||
3172 		    strcmp(columns[i].name, "SN") == 0 ||
3173 		    strcmp(columns[i].name, "NSTA") == 0 ||
3174 		    strcmp(columns[i].name, "NSTATE") == 0)
3175 			opt_nstate_shown = 1;
3176 
3177 		opt_columns[n] = i;
3178 		line_sz += columns[i].width + 1;
3179 	}
3180 
3181 
3182 	if ((lines_pool = uu_avl_pool_create("lines_pool",
3183 	    sizeof (struct avl_string), offsetof(struct avl_string, node),
3184 	    line_cmp, UU_AVL_DEBUG)) == NULL ||
3185 	    (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL)
3186 		uu_die(gettext("Unexpected libuutil error: %s.  Exiting.\n"),
3187 		    uu_strerror(uu_error()));
3188 
3189 	switch (opt_mode) {
3190 	case 0:
3191 		ht_init();
3192 
3193 		/* Always show all FMRIs when given arguments or restarters */
3194 		if (argc != 0 || restarters != NULL)
3195 			show_all =  1;
3196 
3197 		if ((err = scf_walk_fmri(h, argc, argv,
3198 		    SCF_WALK_MULTIPLE | SCF_WALK_LEGACY,
3199 		    show_all ? list_instance : list_if_enabled, NULL,
3200 		    &exit_status, uu_warn)) != 0) {
3201 			uu_warn(gettext("failed to iterate over "
3202 			    "instances: %s\n"), scf_strerror(err));
3203 			exit_status = UU_EXIT_FATAL;
3204 		}
3205 		break;
3206 
3207 	case 'd':
3208 		if (argc == 0)
3209 			argserr(progname);
3210 
3211 		if ((err = scf_walk_fmri(h, argc, argv,
3212 		    SCF_WALK_MULTIPLE, list_dependencies, NULL,
3213 		    &exit_status, uu_warn)) != 0) {
3214 			uu_warn(gettext("failed to iterate over "
3215 			    "instances: %s\n"), scf_strerror(err));
3216 			exit_status = UU_EXIT_FATAL;
3217 		}
3218 		break;
3219 
3220 	case 'D':
3221 		if (argc == 0)
3222 			argserr(progname);
3223 
3224 		provider_scope = safe_malloc(max_scf_fmri_length);
3225 		provider_svc = safe_malloc(max_scf_fmri_length);
3226 		provider_inst = safe_malloc(max_scf_fmri_length);
3227 
3228 		if ((err = scf_walk_fmri(h, argc, argv,
3229 		    SCF_WALK_MULTIPLE | SCF_WALK_SERVICE,
3230 		    list_dependents, NULL, &exit_status, uu_warn)) != 0) {
3231 			uu_warn(gettext("failed to iterate over "
3232 			    "instances: %s\n"), scf_strerror(err));
3233 			exit_status = UU_EXIT_FATAL;
3234 		}
3235 
3236 		free(provider_scope);
3237 		free(provider_svc);
3238 		free(provider_inst);
3239 		break;
3240 
3241 	default:
3242 		assert(0);
3243 		abort();
3244 	}
3245 
3246 	if (show_header)
3247 		print_header();
3248 
3249 	(void) uu_avl_walk(lines, print_line, NULL, 0);
3250 
3251 	return (exit_status);
3252 }
3253