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