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