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