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