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