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