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