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