svcs.c revision d5c6878f09d369f50900b01afe3c7a03d2398679
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 | -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-L  list the log file associated with the specified service(s)\n"
1900	"\t-o  list only the specified columns in the output\n"
1901	"\t-p  list process IDs and names associated with each service\n"
1902	"\t-R  list only those services with the specified restarter\n"
1903	"\t-s  sort output in ascending order by the specified column(s)\n"
1904	"\t-S  sort output in descending order by the specified column(s)\n"
1905	"\t-v  list verbose information appropriate to the type of output\n"
1906	"\t-x  explain the status of services that might require maintenance,\n"
1907	"\t    or explain the status of the specified service(s)\n"
1908	"\t-z  from global zone, show services in a specified zone\n"
1909	"\t-Z  from global zone, show services in all zones\n"
1910	"\n\t"
1911	"Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
1912	"\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
1913	"\n"
1914	"\t%1$s [opts] svc:/network/smtp:sendmail\n"
1915	"\t%1$s [opts] network/smtp:sendmail\n"
1916	"\t%1$s [opts] network/*mail\n"
1917	"\t%1$s [opts] network/smtp\n"
1918	"\t%1$s [opts] smtp:sendmail\n"
1919	"\t%1$s [opts] smtp\n"
1920	"\t%1$s [opts] sendmail\n"
1921	"\n\t"
1922	"Columns for output or sorting can be specified using these names:\n"
1923	"\n"), progname);
1924
1925	for (i = 0; i < ncolumns; i++) {
1926		(void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR "s  %s\n",
1927		    columns[i].name, description_of_column(i));
1928	}
1929}
1930
1931
1932/*
1933 * A getsubopt()-like function which returns an index into the columns table.
1934 * On success, *optionp is set to point to the next sub-option, or the
1935 * terminating null if there are none.
1936 */
1937static int
1938getcolumnopt(char **optionp)
1939{
1940	char *str = *optionp, *cp;
1941	int i;
1942
1943	assert(optionp != NULL);
1944	assert(*optionp != NULL);
1945
1946	cp = strchr(*optionp, ',');
1947	if (cp != NULL)
1948		*cp = '\0';
1949
1950	for (i = 0; i < ncolumns; ++i) {
1951		if (strcasecmp(str, columns[i].name) == 0) {
1952			if (cp != NULL)
1953				*optionp = cp + 1;
1954			else
1955				*optionp = strchr(*optionp, '\0');
1956
1957			return (i);
1958		}
1959	}
1960
1961	return (-1);
1962}
1963
1964static void
1965print_header()
1966{
1967	int i;
1968	char *line_buf, *cp;
1969
1970	line_buf = safe_malloc(line_sz);
1971	cp = line_buf;
1972	for (i = 0; i < opt_cnum; ++i) {
1973		const struct column * const colp = &columns[opt_columns[i]];
1974
1975		(void) snprintf(cp, colp->width + 1, "%-*s", colp->width,
1976		    colp->name);
1977		cp += colp->width;
1978		*cp++ = ' ';
1979	}
1980
1981	/* Trim the trailing whitespace */
1982	--cp;
1983	while (*cp == ' ')
1984		--cp;
1985	*(cp+1) = '\0';
1986	(void) puts(line_buf);
1987
1988	free(line_buf);
1989}
1990
1991
1992
1993/*
1994 * Long listing (-l) functions.
1995 */
1996
1997static int
1998pidcmp(const void *l, const void *r)
1999{
2000	pid_t lp = *(pid_t *)l, rp = *(pid_t *)r;
2001
2002	if (lp < rp)
2003		return (-1);
2004	if (lp > rp)
2005		return (1);
2006	return (0);
2007}
2008
2009/*
2010 * This is the strlen() of the longest label ("description"), plus intercolumn
2011 * space.
2012 */
2013#define	DETAILED_WIDTH	(11 + 2)
2014
2015/*
2016 * Callback routine to print header for contract id.
2017 * Called by ctids_by_restarter and print_detailed.
2018 */
2019static void
2020print_ctid_header()
2021{
2022	(void) printf("%-*s", DETAILED_WIDTH, "contract_id");
2023}
2024
2025/*
2026 * Callback routine to print a contract id.
2027 * Called by ctids_by_restarter and print_detailed.
2028 */
2029static void
2030print_ctid_detailed(uint64_t c)
2031{
2032	(void) printf("%lu ", (ctid_t)c);
2033}
2034
2035static void
2036detailed_list_processes(scf_walkinfo_t *wip)
2037{
2038	uint64_t c;
2039	pid_t *pids;
2040	uint_t i, n;
2041	psinfo_t psi;
2042
2043	if (get_restarter_count_prop(wip->inst, scf_property_contract, &c,
2044	    EMPTY_OK) != 0)
2045		return;
2046
2047	if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
2048		return;
2049
2050	qsort(pids, n, sizeof (*pids), pidcmp);
2051
2052	for (i = 0; i < n; ++i) {
2053		(void) printf("%-*s%lu", DETAILED_WIDTH, gettext("process"),
2054		    pids[i]);
2055
2056		if (get_psinfo(pids[i], &psi) == 0)
2057			(void) printf(" %.*s", PRARGSZ, psi.pr_psargs);
2058
2059		(void) putchar('\n');
2060	}
2061
2062	free(pids);
2063}
2064
2065/*
2066 * Determines the state of a dependency.  If the FMRI specifies a file, then we
2067 * fake up a state based on whether we can access the file.
2068 */
2069static void
2070get_fmri_state(char *fmri, char *state, size_t state_sz)
2071{
2072	char *lfmri;
2073	const char *svc_name, *inst_name, *pg_name, *path;
2074	scf_service_t *svc;
2075	scf_instance_t *inst;
2076	scf_iter_t *iter;
2077
2078	lfmri = safe_strdup(fmri);
2079
2080	/*
2081	 * Check for file:// dependencies
2082	 */
2083	if (scf_parse_file_fmri(lfmri, NULL, &path) == SCF_SUCCESS) {
2084		struct stat64 statbuf;
2085		const char *msg;
2086
2087		if (stat64(path, &statbuf) == 0)
2088			msg = "online";
2089		else if (errno == ENOENT)
2090			msg = "absent";
2091		else
2092			msg = "unknown";
2093
2094		(void) strlcpy(state, msg, state_sz);
2095		return;
2096	}
2097
2098	/*
2099	 * scf_parse_file_fmri() may have overwritten part of the string, so
2100	 * copy it back.
2101	 */
2102	(void) strcpy(lfmri, fmri);
2103
2104	if (scf_parse_svc_fmri(lfmri, NULL, &svc_name, &inst_name,
2105	    &pg_name, NULL) != SCF_SUCCESS) {
2106		free(lfmri);
2107		(void) strlcpy(state, "invalid", state_sz);
2108		return;
2109	}
2110
2111	free(lfmri);
2112
2113	if (svc_name == NULL || pg_name != NULL) {
2114		(void) strlcpy(state, "invalid", state_sz);
2115		return;
2116	}
2117
2118	if (inst_name != NULL) {
2119		/* instance: get state */
2120		inst = scf_instance_create(h);
2121		if (inst == NULL)
2122			scfdie();
2123
2124		if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
2125		    NULL, SCF_DECODE_FMRI_EXACT) == SCF_SUCCESS)
2126			get_restarter_string_prop(inst, scf_property_state,
2127			    state, state_sz);
2128		else {
2129			switch (scf_error()) {
2130			case SCF_ERROR_INVALID_ARGUMENT:
2131				(void) strlcpy(state, "invalid", state_sz);
2132				break;
2133			case SCF_ERROR_NOT_FOUND:
2134				(void) strlcpy(state, "absent", state_sz);
2135				break;
2136
2137			default:
2138				scfdie();
2139			}
2140		}
2141
2142		scf_instance_destroy(inst);
2143		return;
2144	}
2145
2146	/*
2147	 * service: If only one instance, use that state.  Otherwise, say
2148	 * "multiple".
2149	 */
2150	if ((svc = scf_service_create(h)) == NULL ||
2151	    (inst = scf_instance_create(h)) == NULL ||
2152	    (iter = scf_iter_create(h)) == NULL)
2153		scfdie();
2154
2155	if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
2156	    SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
2157		switch (scf_error()) {
2158		case SCF_ERROR_INVALID_ARGUMENT:
2159			(void) strlcpy(state, "invalid", state_sz);
2160			goto out;
2161		case SCF_ERROR_NOT_FOUND:
2162			(void) strlcpy(state, "absent", state_sz);
2163			goto out;
2164
2165		default:
2166			scfdie();
2167		}
2168	}
2169
2170	if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
2171		scfdie();
2172
2173	switch (scf_iter_next_instance(iter, inst)) {
2174	case 0:
2175		(void) strlcpy(state, "absent", state_sz);
2176		goto out;
2177
2178	case 1:
2179		break;
2180
2181	default:
2182		scfdie();
2183	}
2184
2185	/* Get the state in case this is the only instance. */
2186	get_restarter_string_prop(inst, scf_property_state, state, state_sz);
2187
2188	switch (scf_iter_next_instance(iter, inst)) {
2189	case 0:
2190		break;
2191
2192	case 1:
2193		/* Nope, multiple instances. */
2194		(void) strlcpy(state, "multiple", state_sz);
2195		goto out;
2196
2197	default:
2198		scfdie();
2199	}
2200
2201out:
2202	scf_iter_destroy(iter);
2203	scf_instance_destroy(inst);
2204	scf_service_destroy(svc);
2205}
2206
2207static void
2208print_application_properties(scf_walkinfo_t *wip, scf_snapshot_t *snap)
2209{
2210	scf_iter_t *pg_iter, *prop_iter, *val_iter;
2211	scf_propertygroup_t *pg;
2212	scf_property_t *prop;
2213	scf_value_t *val;
2214	scf_pg_tmpl_t *pt;
2215	scf_prop_tmpl_t *prt;
2216	char *pg_name_buf = safe_malloc(max_scf_name_length + 1);
2217	char *prop_name_buf = safe_malloc(max_scf_name_length + 1);
2218	char *snap_name = safe_malloc(max_scf_name_length + 1);
2219	char *val_buf = safe_malloc(max_scf_value_length + 1);
2220	char *desc, *cp;
2221	scf_type_t type;
2222	int i, j, k;
2223	uint8_t vis;
2224
2225	if ((pg_iter = scf_iter_create(h)) == NULL ||
2226	    (prop_iter = scf_iter_create(h)) == NULL ||
2227	    (val_iter = scf_iter_create(h)) == NULL ||
2228	    (val = scf_value_create(h)) == NULL ||
2229	    (prop = scf_property_create(h)) == NULL ||
2230	    (pt = scf_tmpl_pg_create(h)) == NULL ||
2231	    (prt = scf_tmpl_prop_create(h)) == NULL ||
2232	    (pg = scf_pg_create(h)) == NULL)
2233		scfdie();
2234
2235	if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap,
2236	    SCF_PG_APP_DEFAULT) == -1)
2237		scfdie();
2238
2239	/*
2240	 * Format for output:
2241	 *	pg (pgtype)
2242	 *	 description
2243	 *	pg/prop (proptype) = <value> <value>
2244	 *	 description
2245	 */
2246	while ((i = scf_iter_next_pg(pg_iter, pg)) == 1) {
2247		int tmpl = 0;
2248
2249		if (scf_pg_get_name(pg, pg_name_buf, max_scf_name_length) < 0)
2250			scfdie();
2251		if (scf_snapshot_get_name(snap, snap_name,
2252		    max_scf_name_length) < 0)
2253			scfdie();
2254
2255		if (scf_tmpl_get_by_pg_name(wip->fmri, snap_name, pg_name_buf,
2256		    SCF_PG_APP_DEFAULT, pt, 0) == 0)
2257			tmpl = 1;
2258		else
2259			tmpl = 0;
2260
2261		(void) printf("%s (%s)\n", pg_name_buf, SCF_PG_APP_DEFAULT);
2262
2263		if (tmpl == 1 && scf_tmpl_pg_description(pt, NULL, &desc) > 0) {
2264			(void) printf("  %s\n", desc);
2265			free(desc);
2266		}
2267
2268		if (scf_iter_pg_properties(prop_iter, pg) == -1)
2269			scfdie();
2270		while ((j = scf_iter_next_property(prop_iter, prop)) == 1) {
2271			if (scf_property_get_name(prop, prop_name_buf,
2272			    max_scf_name_length) < 0)
2273				scfdie();
2274			if (scf_property_type(prop, &type) == -1)
2275				scfdie();
2276
2277			if ((tmpl == 1) &&
2278			    (scf_tmpl_get_by_prop(pt, prop_name_buf, prt,
2279			    0) != 0))
2280				tmpl = 0;
2281
2282			if (tmpl == 1 &&
2283			    scf_tmpl_prop_visibility(prt, &vis) != -1 &&
2284			    vis == SCF_TMPL_VISIBILITY_HIDDEN)
2285				continue;
2286
2287			(void) printf("%s/%s (%s) = ", pg_name_buf,
2288			    prop_name_buf, scf_type_to_string(type));
2289
2290			if (scf_iter_property_values(val_iter, prop) == -1)
2291				scfdie();
2292
2293			while ((k = scf_iter_next_value(val_iter, val)) == 1) {
2294				if (scf_value_get_as_string(val, val_buf,
2295				    max_scf_value_length + 1) < 0)
2296					scfdie();
2297				if (strpbrk(val_buf, " \t\n\"()") != NULL) {
2298					(void) printf("\"");
2299					for (cp = val_buf; *cp != '\0'; ++cp) {
2300						if (*cp == '"' || *cp == '\\')
2301							(void) putc('\\',
2302							    stdout);
2303
2304						(void) putc(*cp, stdout);
2305					}
2306					(void) printf("\"");
2307				} else {
2308					(void) printf("%s ", val_buf);
2309				}
2310			}
2311
2312			(void) printf("\n");
2313
2314			if (k == -1)
2315				scfdie();
2316
2317			if (tmpl == 1 && scf_tmpl_prop_description(prt, NULL,
2318			    &desc) > 0) {
2319				(void) printf("  %s\n", desc);
2320				free(desc);
2321			}
2322		}
2323		if (j == -1)
2324			scfdie();
2325	}
2326	if (i == -1)
2327		scfdie();
2328
2329
2330	scf_iter_destroy(pg_iter);
2331	scf_iter_destroy(prop_iter);
2332	scf_iter_destroy(val_iter);
2333	scf_value_destroy(val);
2334	scf_property_destroy(prop);
2335	scf_tmpl_pg_destroy(pt);
2336	scf_tmpl_prop_destroy(prt);
2337	scf_pg_destroy(pg);
2338	free(pg_name_buf);
2339	free(prop_name_buf);
2340	free(snap_name);
2341	free(val_buf);
2342}
2343
2344static void
2345print_detailed_dependency(scf_propertygroup_t *pg)
2346{
2347	scf_property_t *eprop;
2348	scf_iter_t *iter;
2349	scf_type_t ty;
2350	char *val_buf;
2351	int i;
2352
2353	if ((eprop = scf_property_create(h)) == NULL ||
2354	    (iter = scf_iter_create(h)) == NULL)
2355		scfdie();
2356
2357	val_buf = safe_malloc(max_scf_value_length + 1);
2358
2359	if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, eprop) !=
2360	    SCF_SUCCESS ||
2361	    scf_property_type(eprop, &ty) != SCF_SUCCESS ||
2362	    ty != SCF_TYPE_FMRI)
2363		return;
2364
2365	(void) printf("%-*s", DETAILED_WIDTH, gettext("dependency"));
2366
2367	/* Print the grouping */
2368	if (pg_get_single_val(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
2369	    val_buf, max_scf_value_length + 1, 0) == 0)
2370		(void) fputs(val_buf, stdout);
2371	else
2372		(void) putchar('?');
2373
2374	(void) putchar('/');
2375
2376	if (pg_get_single_val(pg, SCF_PROPERTY_RESTART_ON, SCF_TYPE_ASTRING,
2377	    val_buf, max_scf_value_length + 1, 0) == 0)
2378		(void) fputs(val_buf, stdout);
2379	else
2380		(void) putchar('?');
2381
2382	/* Print the dependency entities. */
2383	if (scf_iter_property_values(iter, eprop) == -1)
2384		scfdie();
2385
2386	while ((i = scf_iter_next_value(iter, g_val)) == 1) {
2387		char state[MAX_SCF_STATE_STRING_SZ];
2388
2389		if (scf_value_get_astring(g_val, val_buf,
2390		    max_scf_value_length + 1) < 0)
2391			scfdie();
2392
2393		(void) putchar(' ');
2394		(void) fputs(val_buf, stdout);
2395
2396		/* Print the state. */
2397		state[0] = '-';
2398		state[1] = '\0';
2399
2400		get_fmri_state(val_buf, state, sizeof (state));
2401
2402		(void) printf(" (%s)", state);
2403	}
2404	if (i == -1)
2405		scfdie();
2406
2407	(void) putchar('\n');
2408
2409	free(val_buf);
2410	scf_iter_destroy(iter);
2411	scf_property_destroy(eprop);
2412}
2413
2414/* ARGSUSED */
2415static int
2416print_detailed(void *unused, scf_walkinfo_t *wip)
2417{
2418	scf_snapshot_t *snap;
2419	scf_propertygroup_t *rpg;
2420	scf_iter_t *pg_iter;
2421
2422	char *buf;
2423	char *timebuf;
2424	size_t tbsz;
2425	int ret;
2426	uint64_t c;
2427	int temp, perm;
2428	struct timeval tv;
2429	time_t stime;
2430	struct tm *tmp;
2431	int restarter_spec;
2432	int restarter_ret;
2433
2434	const char * const fmt = "%-*s%s\n";
2435
2436	assert(wip->pg == NULL);
2437
2438	rpg = scf_pg_create(h);
2439	if (rpg == NULL)
2440		scfdie();
2441
2442	if (first_paragraph)
2443		first_paragraph = 0;
2444	else
2445		(void) putchar('\n');
2446
2447	buf = safe_malloc(max_scf_fmri_length + 1);
2448
2449	if (scf_instance_to_fmri(wip->inst, buf, max_scf_fmri_length + 1) != -1)
2450		(void) printf(fmt, DETAILED_WIDTH, "fmri", buf);
2451
2452	if (common_name_buf == NULL)
2453		common_name_buf = safe_malloc(max_scf_value_length + 1);
2454
2455	if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
2456	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
2457	    == 0)
2458		(void) printf(fmt, DETAILED_WIDTH, gettext("name"),
2459		    common_name_buf);
2460	else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
2461	    SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
2462	    == 0)
2463		(void) printf(fmt, DETAILED_WIDTH, gettext("name"),
2464		    common_name_buf);
2465
2466	if (g_zonename != NULL)
2467		(void) printf(fmt, DETAILED_WIDTH, gettext("zone"), g_zonename);
2468
2469	/*
2470	 * Synthesize an 'enabled' property that hides the enabled_ovr
2471	 * implementation from the user.  If the service has been temporarily
2472	 * set to a state other than its permanent value, alert the user with
2473	 * a '(temporary)' message.
2474	 */
2475	perm = instance_enabled(wip->inst, B_FALSE);
2476	temp = instance_enabled(wip->inst, B_TRUE);
2477	if (temp != -1) {
2478		if (temp != perm)
2479			(void) printf(gettext("%-*s%s (temporary)\n"),
2480			    DETAILED_WIDTH, gettext("enabled"),
2481			    temp ? gettext("true") : gettext("false"));
2482		else
2483			(void) printf(fmt, DETAILED_WIDTH,
2484			    gettext("enabled"), temp ? gettext("true") :
2485			    gettext("false"));
2486	} else if (perm != -1) {
2487		(void) printf(fmt, DETAILED_WIDTH, gettext("enabled"),
2488		    perm ? gettext("true") : gettext("false"));
2489	}
2490
2491	/*
2492	 * Property values may be longer than max_scf_fmri_length, but these
2493	 * shouldn't be, so we'll just reuse buf.  The user can use svcprop if
2494	 * he suspects something fishy.
2495	 */
2496	if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) {
2497		if (scf_error() != SCF_ERROR_NOT_FOUND)
2498			scfdie();
2499
2500		scf_pg_destroy(rpg);
2501		rpg = NULL;
2502	}
2503
2504	if (rpg) {
2505		if (pg_get_single_val(rpg, scf_property_state, SCF_TYPE_ASTRING,
2506		    buf, max_scf_fmri_length + 1, 0) == 0)
2507			(void) printf(fmt, DETAILED_WIDTH, gettext("state"),
2508			    buf);
2509
2510		if (pg_get_single_val(rpg, scf_property_next_state,
2511		    SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2512			(void) printf(fmt, DETAILED_WIDTH,
2513			    gettext("next_state"), buf);
2514
2515		if (pg_get_single_val(rpg, SCF_PROPERTY_STATE_TIMESTAMP,
2516		    SCF_TYPE_TIME, &tv, NULL, 0) == 0) {
2517			stime = tv.tv_sec;
2518			tmp = localtime(&stime);
2519			for (tbsz = 50; ; tbsz *= 2) {
2520				timebuf = safe_malloc(tbsz);
2521				if (strftime(timebuf, tbsz, NULL, tmp) != 0)
2522					break;
2523				free(timebuf);
2524			}
2525			(void) printf(fmt, DETAILED_WIDTH,
2526			    gettext("state_time"),
2527			    timebuf);
2528			free(timebuf);
2529		}
2530
2531		if (pg_get_single_val(rpg, SCF_PROPERTY_ALT_LOGFILE,
2532		    SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2533			(void) printf(fmt, DETAILED_WIDTH,
2534			    gettext("alt_logfile"), buf);
2535
2536		if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE,
2537		    SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2538			(void) printf(fmt, DETAILED_WIDTH, gettext("logfile"),
2539			    buf);
2540	}
2541
2542	if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
2543	    SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, buf,
2544	    max_scf_fmri_length + 1, 0, 0, 1) == 0)
2545		(void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), buf);
2546	else
2547		(void) printf(fmt, DETAILED_WIDTH, gettext("restarter"),
2548		    SCF_SERVICE_STARTD);
2549
2550	free(buf);
2551
2552	/*
2553	 * Use the restarter specific routine to print the ctids, if available.
2554	 * If restarter specific action is available and it fails, then die.
2555	 */
2556	restarter_ret = ctids_by_restarter(wip, &c, 1, 0,
2557	    &restarter_spec, print_ctid_header, print_ctid_detailed);
2558	if (restarter_spec == 1) {
2559		if (restarter_ret != 0)
2560			uu_die(gettext("Unable to get restarter for %s"),
2561			    wip->fmri);
2562		goto restarter_common;
2563	}
2564
2565	if (rpg) {
2566		scf_iter_t *iter;
2567
2568		if ((iter = scf_iter_create(h)) == NULL)
2569			scfdie();
2570
2571		if (scf_pg_get_property(rpg, scf_property_contract, g_prop) ==
2572		    0) {
2573			if (scf_property_is_type(g_prop, SCF_TYPE_COUNT) == 0) {
2574
2575				/* Callback to print ctid header */
2576				print_ctid_header();
2577
2578				if (scf_iter_property_values(iter, g_prop) != 0)
2579					scfdie();
2580
2581				for (;;) {
2582					ret = scf_iter_next_value(iter, g_val);
2583					if (ret == -1)
2584						scfdie();
2585					if (ret == 0)
2586						break;
2587
2588					if (scf_value_get_count(g_val, &c) != 0)
2589						scfdie();
2590
2591					/* Callback to print contract id. */
2592					print_ctid_detailed(c);
2593				}
2594
2595				(void) putchar('\n');
2596			} else {
2597				if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
2598					scfdie();
2599			}
2600		} else {
2601			if (scf_error() != SCF_ERROR_NOT_FOUND)
2602				scfdie();
2603		}
2604
2605		scf_iter_destroy(iter);
2606	} else {
2607		if (scf_error() != SCF_ERROR_NOT_FOUND)
2608			scfdie();
2609	}
2610
2611restarter_common:
2612	scf_pg_destroy(rpg);
2613
2614	/* Dependencies. */
2615	if ((pg_iter = scf_iter_create(h)) == NULL)
2616		scfdie();
2617
2618	snap = get_running_snapshot(wip->inst);
2619
2620	if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap,
2621	    SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
2622		scfdie();
2623
2624	while ((ret = scf_iter_next_pg(pg_iter, g_pg)) == 1)
2625		print_detailed_dependency(g_pg);
2626	if (ret == -1)
2627		scfdie();
2628
2629	scf_iter_destroy(pg_iter);
2630
2631	if (opt_processes)
2632		detailed_list_processes(wip);
2633
2634	/* "application" type property groups */
2635	if (opt_verbose == 1)
2636		print_application_properties(wip, snap);
2637
2638	scf_snapshot_destroy(snap);
2639
2640	return (0);
2641}
2642
2643/* ARGSUSED */
2644static int
2645print_log(void *unused, scf_walkinfo_t *wip)
2646{
2647	scf_propertygroup_t *rpg;
2648	char buf[MAXPATHLEN];
2649
2650	if ((rpg = scf_pg_create(h)) == NULL)
2651		scfdie();
2652
2653	if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) {
2654		if (scf_error() != SCF_ERROR_NOT_FOUND)
2655			scfdie();
2656
2657		goto out;
2658	}
2659
2660	if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE,
2661	    SCF_TYPE_ASTRING, buf, sizeof (buf), 0) == 0) {
2662		(void) printf("%s\n", buf);
2663	}
2664
2665out:
2666	scf_pg_destroy(rpg);
2667
2668	return (0);
2669}
2670
2671int
2672qsort_str_compare(const void *p1, const void *p2)
2673{
2674	return (strcmp((const char *)p1, (const char *)p2));
2675}
2676
2677/*
2678 * get_notify_param_classes()
2679 * return the fma classes that don't have a tag in fma_tags[], otherwise NULL
2680 */
2681static char **
2682get_notify_param_classes()
2683{
2684	scf_handle_t		*h = _scf_handle_create_and_bind(SCF_VERSION);
2685	scf_instance_t		*inst = scf_instance_create(h);
2686	scf_snapshot_t		*snap = scf_snapshot_create(h);
2687	scf_snaplevel_t		*slvl = scf_snaplevel_create(h);
2688	scf_propertygroup_t	*pg = scf_pg_create(h);
2689	scf_iter_t		*iter = scf_iter_create(h);
2690	int size = 4;
2691	int n = 0;
2692	size_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2693	int err;
2694	char *pgname = safe_malloc(sz);
2695	char **buf = safe_malloc(size * sizeof (char *));
2696
2697	if (h == NULL || inst == NULL || snap == NULL || slvl == NULL ||
2698	    pg == NULL || iter == NULL) {
2699		uu_die(gettext("Failed object creation: %s\n"),
2700		    scf_strerror(scf_error()));
2701	}
2702
2703	if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL, NULL, inst,
2704	    NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
2705		uu_die(gettext("Failed to decode %s: %s\n"),
2706		    SCF_NOTIFY_PARAMS_INST, scf_strerror(scf_error()));
2707
2708	if (scf_instance_get_snapshot(inst, "running", snap) != 0)
2709		uu_die(gettext("Failed to get snapshot: %s\n"),
2710		    scf_strerror(scf_error()));
2711
2712	if (scf_snapshot_get_base_snaplevel(snap, slvl) != 0)
2713		uu_die(gettext("Failed to get base snaplevel: %s\n"),
2714		    scf_strerror(scf_error()));
2715
2716	if (scf_iter_snaplevel_pgs_typed(iter, slvl,
2717	    SCF_NOTIFY_PARAMS_PG_TYPE) != 0)
2718		uu_die(gettext("Failed to get iterator: %s\n"),
2719		    scf_strerror(scf_error()));
2720
2721	while ((err = scf_iter_next_pg(iter, pg)) == 1) {
2722		char *c;
2723
2724		if (scf_pg_get_name(pg, pgname, sz) == -1)
2725			uu_die(gettext("Failed to get pg name: %s\n"),
2726			    scf_strerror(scf_error()));
2727		if ((c = strrchr(pgname, ',')) != NULL)
2728			*c = '\0';
2729		if (has_fma_tag(pgname))
2730			continue;
2731		if (!is_fma_token(pgname))
2732			/*
2733			 * We don't emmit a warning here so that we don't
2734			 * pollute the output
2735			 */
2736			continue;
2737
2738		if (n + 1 >= size) {
2739			size *= 2;
2740			buf = realloc(buf, size * sizeof (char *));
2741			if (buf == NULL)
2742				uu_die(gettext("Out of memory.\n"));
2743		}
2744		buf[n] = safe_strdup(pgname);
2745		++n;
2746	}
2747	/*
2748	 * NULL terminate buf
2749	 */
2750	buf[n] = NULL;
2751	if (err == -1)
2752		uu_die(gettext("Failed to iterate pgs: %s\n"),
2753		    scf_strerror(scf_error()));
2754
2755	/* sort the classes */
2756	qsort((void *)buf, n, sizeof (char *), qsort_str_compare);
2757
2758	free(pgname);
2759	scf_iter_destroy(iter);
2760	scf_pg_destroy(pg);
2761	scf_snaplevel_destroy(slvl);
2762	scf_snapshot_destroy(snap);
2763	scf_instance_destroy(inst);
2764	scf_handle_destroy(h);
2765
2766	return (buf);
2767}
2768
2769/*
2770 * get_fma_notify_params()
2771 * populates an nvlist_t with notifycation parameters for a given FMA class
2772 * returns 0 if the nvlist is populated, 1 otherwise;
2773 */
2774int
2775get_fma_notify_params(nvlist_t *nvl, const char *class)
2776{
2777	if (_scf_get_fma_notify_params(class, nvl, 0) != 0) {
2778		/*
2779		 * if the preferences have just been deleted
2780		 * or does not exist, just skip.
2781		 */
2782		if (scf_error() != SCF_ERROR_NOT_FOUND &&
2783		    scf_error() != SCF_ERROR_DELETED)
2784			uu_warn(gettext(
2785			    "Failed get_fma_notify_params %s\n"),
2786			    scf_strerror(scf_error()));
2787
2788		return (1);
2789	}
2790
2791	return (0);
2792}
2793
2794/*
2795 * print_notify_fma()
2796 * outputs the notification paramets of FMA events.
2797 * It first outputs classes in fma_tags[], then outputs the other classes
2798 * sorted alphabetically
2799 */
2800static void
2801print_notify_fma(void)
2802{
2803	nvlist_t *nvl;
2804	char **tmp = NULL;
2805	char **classes, *p;
2806	const char *class;
2807	uint32_t i;
2808
2809	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
2810		uu_die(gettext("Out of memory.\n"));
2811
2812	for (i = 0; (class = get_fma_class(i)) != NULL; ++i) {
2813		if (get_fma_notify_params(nvl, class) == 0)
2814			listnotify_print(nvl, get_fma_tag(i));
2815	}
2816
2817	if ((classes = get_notify_param_classes()) == NULL)
2818		goto cleanup;
2819
2820	tmp = classes;
2821	for (p = *tmp; p; ++tmp, p = *tmp) {
2822		if (get_fma_notify_params(nvl, p) == 0)
2823			listnotify_print(nvl, re_tag(p));
2824
2825		free(p);
2826	}
2827
2828	free(classes);
2829
2830cleanup:
2831	nvlist_free(nvl);
2832}
2833
2834/*
2835 * print_notify_fmri()
2836 * prints notifycation parameters for an SMF instance.
2837 */
2838static void
2839print_notify_fmri(const char *fmri)
2840{
2841	nvlist_t *nvl;
2842
2843	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
2844		uu_die(gettext("Out of memory.\n"));
2845
2846	if (_scf_get_svc_notify_params(fmri, nvl, SCF_TRANSITION_ALL, 0, 0) !=
2847	    SCF_SUCCESS) {
2848		if (scf_error() != SCF_ERROR_NOT_FOUND &&
2849		    scf_error() != SCF_ERROR_DELETED)
2850			uu_warn(gettext(
2851			    "Failed _scf_get_svc_notify_params: %s\n"),
2852			    scf_strerror(scf_error()));
2853	} else {
2854		if (strcmp(SCF_INSTANCE_GLOBAL, fmri) == 0)
2855			safe_printf(
2856			    gettext("System wide notification parameters:\n"));
2857		safe_printf("%s:\n", fmri);
2858		listnotify_print(nvl, NULL);
2859	}
2860	nvlist_free(nvl);
2861}
2862
2863/*
2864 * print_notify_special()
2865 * prints notification parameters for FMA events and system wide SMF state
2866 * transitions parameters
2867 */
2868static void
2869print_notify_special()
2870{
2871	safe_printf("Notification parameters for FMA Events\n");
2872	print_notify_fma();
2873	print_notify_fmri(SCF_INSTANCE_GLOBAL);
2874}
2875
2876/*
2877 * print_notify()
2878 * callback function to print notification parameters for SMF state transition
2879 * instances. It skips global and notify-params instances as they should be
2880 * printed by print_notify_special()
2881 */
2882/* ARGSUSED */
2883static int
2884print_notify(void *unused, scf_walkinfo_t *wip)
2885{
2886	if (strcmp(SCF_INSTANCE_GLOBAL, wip->fmri) == 0 ||
2887	    strcmp(SCF_NOTIFY_PARAMS_INST, wip->fmri) == 0)
2888		return (0);
2889
2890	print_notify_fmri(wip->fmri);
2891
2892	return (0);
2893}
2894
2895/*
2896 * Append a one-lined description of each process in inst's contract(s) and
2897 * return the augmented string.
2898 */
2899static char *
2900add_processes(scf_walkinfo_t *wip, char *line, scf_propertygroup_t *lpg)
2901{
2902	pid_t *pids = NULL;
2903	uint_t i, n = 0;
2904
2905	if (lpg == NULL) {
2906		if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
2907			return (line);
2908	} else {
2909		/* Legacy services */
2910		scf_iter_t *iter;
2911
2912		if ((iter = scf_iter_create(h)) == NULL)
2913			scfdie();
2914
2915		(void) propvals_to_pids(lpg, scf_property_contract, &pids, &n,
2916		    g_prop, g_val, iter);
2917
2918		scf_iter_destroy(iter);
2919	}
2920
2921	if (n == 0)
2922		return (line);
2923
2924	qsort(pids, n, sizeof (*pids), pidcmp);
2925
2926	for (i = 0; i < n; ++i) {
2927		char *cp, stime[9];
2928		psinfo_t psi;
2929		struct tm *tm;
2930		int len = 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ;
2931
2932		if (get_psinfo(pids[i], &psi) != 0)
2933			continue;
2934
2935		line = realloc(line, strlen(line) + len);
2936		if (line == NULL)
2937			uu_die(gettext("Out of memory.\n"));
2938
2939		cp = strchr(line, '\0');
2940
2941		tm = localtime(&psi.pr_start.tv_sec);
2942
2943		/*
2944		 * Print time if started within the past 24 hours, print date
2945		 * if within the past 12 months, print year if started greater
2946		 * than 12 months ago.
2947		 */
2948		if (now - psi.pr_start.tv_sec < 24 * 60 * 60)
2949			(void) strftime(stime, sizeof (stime),
2950			    gettext(FORMAT_TIME), tm);
2951		else if (now - psi.pr_start.tv_sec < 12 * 30 * 24 * 60 * 60)
2952			(void) strftime(stime, sizeof (stime),
2953			    gettext(FORMAT_DATE), tm);
2954		else
2955			(void) strftime(stime, sizeof (stime),
2956			    gettext(FORMAT_YEAR), tm);
2957
2958		(void) snprintf(cp, len, "\n               %-8s   %6ld %.*s",
2959		    stime, pids[i], PRFNSZ, psi.pr_fname);
2960	}
2961
2962	free(pids);
2963
2964	return (line);
2965}
2966
2967/*ARGSUSED*/
2968static int
2969list_instance(void *unused, scf_walkinfo_t *wip)
2970{
2971	struct avl_string *lp;
2972	char *cp;
2973	int i;
2974	uu_avl_index_t idx;
2975
2976	/*
2977	 * If the user has specified a restarter, check for a match first
2978	 */
2979	if (restarters != NULL) {
2980		struct pfmri_list *rest;
2981		int match;
2982		char *restarter_fmri;
2983		const char *scope_name, *svc_name, *inst_name, *pg_name;
2984
2985		/* legacy services don't have restarters */
2986		if (wip->pg != NULL)
2987			return (0);
2988
2989		restarter_fmri = safe_malloc(max_scf_fmri_length + 1);
2990
2991		if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
2992		    SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, restarter_fmri,
2993		    max_scf_fmri_length + 1, 0, 0, 1) != 0)
2994			(void) strcpy(restarter_fmri, SCF_SERVICE_STARTD);
2995
2996		if (scf_parse_svc_fmri(restarter_fmri, &scope_name, &svc_name,
2997		    &inst_name, &pg_name, NULL) != SCF_SUCCESS) {
2998			free(restarter_fmri);
2999			return (0);
3000		}
3001
3002		match = 0;
3003		for (rest = restarters; rest != NULL; rest = rest->next) {
3004			if (strcmp(rest->scope, scope_name) == 0 &&
3005			    strcmp(rest->service, svc_name) == 0 &&
3006			    strcmp(rest->instance, inst_name) == 0)
3007				match = 1;
3008		}
3009
3010		free(restarter_fmri);
3011
3012		if (!match)
3013			return (0);
3014	}
3015
3016	if (wip->pg == NULL && ht_buckets != NULL && ht_add(wip->fmri)) {
3017		/* It was already there. */
3018		return (0);
3019	}
3020
3021	lp = safe_malloc(sizeof (*lp));
3022
3023	lp->str = NULL;
3024	for (i = 0; i < opt_cnum; ++i) {
3025		columns[opt_columns[i]].sprint(&lp->str, wip);
3026	}
3027	cp = lp->str + strlen(lp->str);
3028	cp--;
3029	while (*cp == ' ')
3030		cp--;
3031	*(cp+1) = '\0';
3032
3033	/* If we're supposed to list the processes, too, do that now. */
3034	if (opt_processes)
3035		lp->str = add_processes(wip, lp->str, wip->pg);
3036
3037	/* Create the sort key. */
3038	cp = lp->key = safe_malloc(sortkey_sz);
3039	for (i = 0; i < opt_snum; ++i) {
3040		int j = opt_sort[i] & 0xff;
3041
3042		assert(columns[j].get_sortkey != NULL);
3043		columns[j].get_sortkey(cp, opt_sort[i] & ~0xff, wip);
3044		cp += columns[j].sortkey_width;
3045	}
3046
3047	/* Insert into AVL tree. */
3048	uu_avl_node_init(lp, &lp->node, lines_pool);
3049	(void) uu_avl_find(lines, lp, NULL, &idx);
3050	uu_avl_insert(lines, lp, idx);
3051
3052	return (0);
3053}
3054
3055static int
3056list_if_enabled(void *unused, scf_walkinfo_t *wip)
3057{
3058	if (wip->pg != NULL ||
3059	    instance_enabled(wip->inst, B_FALSE) == 1 ||
3060	    instance_enabled(wip->inst, B_TRUE) == 1)
3061		return (list_instance(unused, wip));
3062
3063	return (0);
3064}
3065
3066/*
3067 * Service FMRI selection: Lookup and call list_instance() for the instances.
3068 * Instance FMRI selection: Lookup and call list_instance().
3069 *
3070 * Note: This is shoehorned into a walk_dependencies() callback prototype so
3071 * it can be used in list_dependencies.
3072 */
3073static int
3074list_svc_or_inst_fmri(void *complain, scf_walkinfo_t *wip)
3075{
3076	char *fmri;
3077	const char *svc_name, *inst_name, *pg_name, *save;
3078	scf_iter_t *iter;
3079	int ret;
3080
3081	fmri = safe_strdup(wip->fmri);
3082
3083	if (scf_parse_svc_fmri(fmri, NULL, &svc_name, &inst_name, &pg_name,
3084	    NULL) != SCF_SUCCESS) {
3085		if (complain)
3086			uu_warn(gettext("FMRI \"%s\" is invalid.\n"),
3087			    wip->fmri);
3088		exit_status = UU_EXIT_FATAL;
3089		free(fmri);
3090		return (0);
3091	}
3092
3093	/*
3094	 * Yes, this invalidates *_name, but we only care whether they're NULL
3095	 * or not.
3096	 */
3097	free(fmri);
3098
3099	if (svc_name == NULL || pg_name != NULL) {
3100		if (complain)
3101			uu_warn(gettext("FMRI \"%s\" does not designate a "
3102			    "service or instance.\n"), wip->fmri);
3103		return (0);
3104	}
3105
3106	if (inst_name != NULL) {
3107		/* instance */
3108		if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc,
3109		    wip->inst, NULL, NULL, 0) != SCF_SUCCESS) {
3110			if (scf_error() != SCF_ERROR_NOT_FOUND)
3111				scfdie();
3112
3113			if (complain)
3114				uu_warn(gettext(
3115				    "Instance \"%s\" does not exist.\n"),
3116				    wip->fmri);
3117			return (0);
3118		}
3119
3120		return (list_instance(NULL, wip));
3121	}
3122
3123	/* service: Walk the instances. */
3124	if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, NULL,
3125	    NULL, NULL, 0) != SCF_SUCCESS) {
3126		if (scf_error() != SCF_ERROR_NOT_FOUND)
3127			scfdie();
3128
3129		if (complain)
3130			uu_warn(gettext("Service \"%s\" does not exist.\n"),
3131			    wip->fmri);
3132
3133		exit_status = UU_EXIT_FATAL;
3134
3135		return (0);
3136	}
3137
3138	iter = scf_iter_create(h);
3139	if (iter == NULL)
3140		scfdie();
3141
3142	if (scf_iter_service_instances(iter, wip->svc) != SCF_SUCCESS)
3143		scfdie();
3144
3145	if ((fmri = malloc(max_scf_fmri_length + 1)) == NULL) {
3146		scf_iter_destroy(iter);
3147		exit_status = UU_EXIT_FATAL;
3148		return (0);
3149	}
3150
3151	save = wip->fmri;
3152	wip->fmri = fmri;
3153	while ((ret = scf_iter_next_instance(iter, wip->inst)) == 1) {
3154		if (scf_instance_to_fmri(wip->inst, fmri,
3155		    max_scf_fmri_length + 1) <= 0)
3156			scfdie();
3157		(void) list_instance(NULL, wip);
3158	}
3159	free(fmri);
3160	wip->fmri = save;
3161	if (ret == -1)
3162		scfdie();
3163
3164	exit_status = UU_EXIT_OK;
3165
3166	scf_iter_destroy(iter);
3167
3168	return (0);
3169}
3170
3171/*
3172 * Dependency selection: Straightforward since each instance lists the
3173 * services it depends on.
3174 */
3175
3176static void
3177walk_dependencies(scf_walkinfo_t *wip, scf_walk_callback callback, void *data)
3178{
3179	scf_snapshot_t *snap;
3180	scf_iter_t *iter, *viter;
3181	int ret, vret;
3182	char *dep;
3183
3184	assert(wip->inst != NULL);
3185
3186	if ((iter = scf_iter_create(h)) == NULL ||
3187	    (viter = scf_iter_create(h)) == NULL)
3188		scfdie();
3189
3190	snap = get_running_snapshot(wip->inst);
3191
3192	if (scf_iter_instance_pgs_typed_composed(iter, wip->inst, snap,
3193	    SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
3194		scfdie();
3195
3196	dep = safe_malloc(max_scf_value_length + 1);
3197
3198	while ((ret = scf_iter_next_pg(iter, g_pg)) == 1) {
3199		scf_type_t ty;
3200
3201		/* Ignore exclude_any dependencies. */
3202		if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) !=
3203		    SCF_SUCCESS) {
3204			if (scf_error() != SCF_ERROR_NOT_FOUND)
3205				scfdie();
3206
3207			continue;
3208		}
3209
3210		if (scf_property_type(g_prop, &ty) != SCF_SUCCESS)
3211			scfdie();
3212
3213		if (ty != SCF_TYPE_ASTRING)
3214			continue;
3215
3216		if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) {
3217			if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
3218				scfdie();
3219
3220			continue;
3221		}
3222
3223		if (scf_value_get_astring(g_val, dep,
3224		    max_scf_value_length + 1) < 0)
3225			scfdie();
3226
3227		if (strcmp(dep, SCF_DEP_EXCLUDE_ALL) == 0)
3228			continue;
3229
3230		if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) !=
3231		    SCF_SUCCESS) {
3232			if (scf_error() != SCF_ERROR_NOT_FOUND)
3233				scfdie();
3234
3235			continue;
3236		}
3237
3238		if (scf_iter_property_values(viter, g_prop) != SCF_SUCCESS)
3239			scfdie();
3240
3241		while ((vret = scf_iter_next_value(viter, g_val)) == 1) {
3242			if (scf_value_get_astring(g_val, dep,
3243			    max_scf_value_length + 1) < 0)
3244				scfdie();
3245
3246			wip->fmri = dep;
3247			if (callback(data, wip) != 0)
3248				goto out;
3249		}
3250		if (vret == -1)
3251			scfdie();
3252	}
3253	if (ret == -1)
3254		scfdie();
3255
3256out:
3257	scf_iter_destroy(viter);
3258	scf_iter_destroy(iter);
3259	scf_snapshot_destroy(snap);
3260}
3261
3262static int
3263list_dependencies(void *data, scf_walkinfo_t *wip)
3264{
3265	walk_dependencies(wip, list_svc_or_inst_fmri, data);
3266	return (0);
3267}
3268
3269
3270/*
3271 * Dependent selection: The "providing" service's or instance's FMRI is parsed
3272 * into the provider_* variables, the instances are walked, and any instance
3273 * which lists an FMRI which parses to these components is selected.  This is
3274 * inefficient in the face of multiple operands, but that should be uncommon.
3275 */
3276
3277static char *provider_scope;
3278static char *provider_svc;
3279static char *provider_inst;	/* NULL for services */
3280
3281/*ARGSUSED*/
3282static int
3283check_against_provider(void *arg, scf_walkinfo_t *wip)
3284{
3285	char *cfmri;
3286	const char *scope_name, *svc_name, *inst_name, *pg_name;
3287	int *matchp = arg;
3288
3289	cfmri = safe_strdup(wip->fmri);
3290
3291	if (scf_parse_svc_fmri(cfmri, &scope_name, &svc_name, &inst_name,
3292	    &pg_name, NULL) != SCF_SUCCESS) {
3293		free(cfmri);
3294		return (0);
3295	}
3296
3297	if (svc_name == NULL || pg_name != NULL) {
3298		free(cfmri);
3299		return (0);
3300	}
3301
3302	/*
3303	 * If the user has specified an instance, then also match dependencies
3304	 * on the service itself.
3305	 */
3306	*matchp = (strcmp(provider_scope, scope_name) == 0 &&
3307	    strcmp(provider_svc, svc_name) == 0 &&
3308	    (provider_inst == NULL ? (inst_name == NULL) :
3309	    (inst_name == NULL || strcmp(provider_inst, inst_name) == 0)));
3310
3311	free(cfmri);
3312
3313	/* Stop on matches. */
3314	return (*matchp);
3315}
3316
3317static int
3318list_if_dependent(void *unused, scf_walkinfo_t *wip)
3319{
3320	/* Only proceed if this instance depends on provider_*. */
3321	int match = 0;
3322
3323	(void) walk_dependencies(wip, check_against_provider, &match);
3324
3325	if (match)
3326		return (list_instance(unused, wip));
3327
3328	return (0);
3329}
3330
3331/*ARGSUSED*/
3332static int
3333list_dependents(void *unused, scf_walkinfo_t *wip)
3334{
3335	char *save;
3336	int ret;
3337
3338	if (scf_scope_get_name(wip->scope, provider_scope,
3339	    max_scf_fmri_length) <= 0 ||
3340	    scf_service_get_name(wip->svc, provider_svc,
3341	    max_scf_fmri_length) <= 0)
3342		scfdie();
3343
3344	save = provider_inst;
3345	if (wip->inst == NULL)
3346		provider_inst = NULL;
3347	else if (scf_instance_get_name(wip->inst, provider_inst,
3348	    max_scf_fmri_length) <= 0)
3349		scfdie();
3350
3351	ret = scf_walk_fmri(h, 0, NULL, 0, list_if_dependent, NULL, NULL,
3352	    uu_warn);
3353
3354	provider_inst = save;
3355
3356	return (ret);
3357}
3358
3359/*
3360 * main() & helpers
3361 */
3362
3363static void
3364add_sort_column(const char *col, int reverse)
3365{
3366	int i;
3367
3368	++opt_snum;
3369
3370	opt_sort = realloc(opt_sort, opt_snum * sizeof (*opt_sort));
3371	if (opt_sort == NULL)
3372		uu_die(gettext("Too many sort criteria: out of memory.\n"));
3373
3374	for (i = 0; i < ncolumns; ++i) {
3375		if (strcasecmp(col, columns[i].name) == 0)
3376			break;
3377	}
3378
3379	if (i < ncolumns)
3380		opt_sort[opt_snum - 1] = (reverse ? i | 0x100 : i);
3381	else
3382		uu_die(gettext("Unrecognized sort column \"%s\".\n"), col);
3383
3384	sortkey_sz += columns[i].sortkey_width;
3385}
3386
3387static void
3388add_restarter(const char *fmri)
3389{
3390	char *cfmri;
3391	const char *pg_name;
3392	struct pfmri_list *rest;
3393
3394	cfmri = safe_strdup(fmri);
3395	rest = safe_malloc(sizeof (*rest));
3396
3397	if (scf_parse_svc_fmri(cfmri, &rest->scope, &rest->service,
3398	    &rest->instance, &pg_name, NULL) != SCF_SUCCESS)
3399		uu_die(gettext("Restarter FMRI \"%s\" is invalid.\n"), fmri);
3400
3401	if (rest->instance == NULL || pg_name != NULL)
3402		uu_die(gettext("Restarter FMRI \"%s\" does not designate an "
3403		    "instance.\n"), fmri);
3404
3405	rest->next = restarters;
3406	restarters = rest;
3407	return;
3408
3409err:
3410	free(cfmri);
3411	free(rest);
3412}
3413
3414/* ARGSUSED */
3415static int
3416line_cmp(const void *l_arg, const void *r_arg, void *private)
3417{
3418	const struct avl_string *l = l_arg;
3419	const struct avl_string *r = r_arg;
3420
3421	return (memcmp(l->key, r->key, sortkey_sz));
3422}
3423
3424/* ARGSUSED */
3425static int
3426print_line(void *e, void *private)
3427{
3428	struct avl_string *lp = e;
3429
3430	(void) puts(lp->str);
3431
3432	return (UU_WALK_NEXT);
3433}
3434
3435/* ARGSUSED */
3436static void
3437errignore(const char *str, ...)
3438{}
3439
3440int
3441main(int argc, char **argv)
3442{
3443	char opt, opt_mode;
3444	int i, n;
3445	char *columns_str = NULL;
3446	char *cp;
3447	const char *progname;
3448	int err, missing = 1, ignored, *errarg;
3449	uint_t nzents = 0, zent = 0;
3450	zoneid_t *zids = NULL;
3451	char zonename[ZONENAME_MAX];
3452	void (*errfunc)(const char *, ...);
3453
3454	int show_all = 0;
3455	int show_header = 1;
3456	int show_zones = 0;
3457
3458	const char * const options = "aHpvno:R:s:S:dDlL?xZz:";
3459
3460	(void) setlocale(LC_ALL, "");
3461
3462	locale = setlocale(LC_MESSAGES, NULL);
3463	if (locale) {
3464		locale = safe_strdup(locale);
3465		_scf_sanitize_locale(locale);
3466	}
3467
3468	(void) textdomain(TEXT_DOMAIN);
3469	progname = uu_setpname(argv[0]);
3470
3471	exit_status = UU_EXIT_OK;
3472
3473	max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
3474	max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3475	max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
3476	max_scf_type_length = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH);
3477
3478	if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
3479	    max_scf_fmri_length == -1 || max_scf_type_length == -1)
3480		scfdie();
3481
3482	now = time(NULL);
3483	assert(now != -1);
3484
3485	/*
3486	 * opt_mode is the mode of operation.  0 for plain, 'd' for
3487	 * dependencies, 'D' for dependents, and 'l' for detailed (long).  We
3488	 * need to know now so we know which options are valid.
3489	 */
3490	opt_mode = 0;
3491	while ((opt = getopt(argc, argv, options)) != -1) {
3492		switch (opt) {
3493		case '?':
3494			if (optopt == '?') {
3495				print_help(progname);
3496				return (UU_EXIT_OK);
3497			} else {
3498				argserr(progname);
3499				/* NOTREACHED */
3500			}
3501
3502		case 'd':
3503		case 'D':
3504		case 'l':
3505		case 'L':
3506			if (opt_mode != 0)
3507				argserr(progname);
3508
3509			opt_mode = opt;
3510			break;
3511
3512		case 'n':
3513			if (opt_mode != 0)
3514				argserr(progname);
3515
3516			opt_mode = opt;
3517			break;
3518
3519		case 'x':
3520			if (opt_mode != 0)
3521				argserr(progname);
3522
3523			opt_mode = opt;
3524			break;
3525
3526		default:
3527			break;
3528		}
3529	}
3530
3531	sortkey_sz = 0;
3532
3533	optind = 1;	/* Reset getopt() */
3534	while ((opt = getopt(argc, argv, options)) != -1) {
3535		switch (opt) {
3536		case 'a':
3537			if (opt_mode != 0)
3538				argserr(progname);
3539			show_all = 1;
3540			break;
3541
3542		case 'H':
3543			if (opt_mode == 'l' || opt_mode == 'x')
3544				argserr(progname);
3545			show_header = 0;
3546			break;
3547
3548		case 'p':
3549			if (opt_mode == 'x')
3550				argserr(progname);
3551			opt_processes = 1;
3552			break;
3553
3554		case 'v':
3555			opt_verbose = 1;
3556			break;
3557
3558		case 'o':
3559			if (opt_mode == 'l' || opt_mode == 'x')
3560				argserr(progname);
3561			columns_str = optarg;
3562			break;
3563
3564		case 'R':
3565			if (opt_mode != 0 || opt_mode == 'x')
3566				argserr(progname);
3567
3568			add_restarter(optarg);
3569			break;
3570
3571		case 's':
3572		case 'S':
3573			if (opt_mode != 0)
3574				argserr(progname);
3575
3576			add_sort_column(optarg, optopt == 'S');
3577			break;
3578
3579		case 'd':
3580		case 'D':
3581		case 'l':
3582		case 'L':
3583		case 'n':
3584		case 'x':
3585			assert(opt_mode == optopt);
3586			break;
3587
3588		case 'z':
3589			if (getzoneid() != GLOBAL_ZONEID)
3590				uu_die(gettext("svcs -z may only be used from "
3591				    "the global zone\n"));
3592			if (show_zones)
3593				argserr(progname);
3594
3595			opt_zone = optarg;
3596			break;
3597
3598		case 'Z':
3599			if (getzoneid() != GLOBAL_ZONEID)
3600				uu_die(gettext("svcs -Z may only be used from "
3601				    "the global zone\n"));
3602			if (opt_zone != NULL)
3603				argserr(progname);
3604
3605			show_zones = 1;
3606			break;
3607
3608		case '?':
3609			argserr(progname);
3610			/* NOTREACHED */
3611
3612		default:
3613			assert(0);
3614			abort();
3615		}
3616	}
3617
3618	/*
3619	 * -a is only meaningful when given no arguments
3620	 */
3621	if (show_all && optind != argc)
3622		uu_warn(gettext("-a ignored when used with arguments.\n"));
3623
3624	while (show_zones) {
3625		uint_t found;
3626
3627		if (zone_list(NULL, &nzents) != 0)
3628			uu_die(gettext("could not get number of zones"));
3629
3630		if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL) {
3631			uu_die(gettext("could not allocate array for "
3632			    "%d zone IDs"), nzents);
3633		}
3634
3635		found = nzents;
3636
3637		if (zone_list(zids, &found) != 0)
3638			uu_die(gettext("could not get zone list"));
3639
3640		/*
3641		 * If the number of zones has not changed between our calls to
3642		 * zone_list(), we're done -- otherwise, we must free our array
3643		 * of zone IDs and take another lap.
3644		 */
3645		if (found == nzents)
3646			break;
3647
3648		free(zids);
3649	}
3650
3651	argc -= optind;
3652	argv += optind;
3653
3654again:
3655	h = scf_handle_create(SCF_VERSION);
3656	if (h == NULL)
3657		scfdie();
3658
3659	if (opt_zone != NULL || zids != NULL) {
3660		scf_value_t *zone;
3661
3662		assert(opt_zone == NULL || zids == NULL);
3663
3664		if (opt_zone == NULL) {
3665			if (getzonenamebyid(zids[zent++],
3666			    zonename, sizeof (zonename)) < 0) {
3667				uu_warn(gettext("could not get name for "
3668				    "zone %d; ignoring"), zids[zent - 1]);
3669				goto nextzone;
3670			}
3671
3672			g_zonename = zonename;
3673		} else {
3674			g_zonename = opt_zone;
3675		}
3676
3677		if ((zone = scf_value_create(h)) == NULL)
3678			scfdie();
3679
3680		if (scf_value_set_astring(zone, g_zonename) != SCF_SUCCESS)
3681			scfdie();
3682
3683		if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS)
3684			uu_die(gettext("invalid zone '%s'\n"), g_zonename);
3685
3686		scf_value_destroy(zone);
3687	}
3688
3689	if (scf_handle_bind(h) == -1) {
3690		if (g_zonename != NULL) {
3691			uu_warn(gettext("Could not bind to repository "
3692			    "server for zone %s: %s\n"), g_zonename,
3693			    scf_strerror(scf_error()));
3694
3695			if (!show_zones)
3696				return (UU_EXIT_FATAL);
3697
3698			goto nextzone;
3699		}
3700
3701		uu_die(gettext("Could not bind to repository server: %s.  "
3702		    "Exiting.\n"), scf_strerror(scf_error()));
3703	}
3704
3705	if ((g_pg = scf_pg_create(h)) == NULL ||
3706	    (g_prop = scf_property_create(h)) == NULL ||
3707	    (g_val = scf_value_create(h)) == NULL)
3708		scfdie();
3709
3710	if (show_zones) {
3711		/*
3712		 * It's hard to avoid editorializing here, but suffice it to
3713		 * say that scf_walk_fmri() takes an error handler, the
3714		 * interface to which has been regrettably misdesigned:  the
3715		 * handler itself takes exclusively a string -- even though
3716		 * scf_walk_fmri() has detailed, programmatic knowledge
3717		 * of the error condition at the time it calls its errfunc.
3718		 * That is, only the error message and not the error semantics
3719		 * are given to the handler.  This is poor interface at best,
3720		 * but it is particularly problematic when we are talking to
3721		 * multiple repository servers (as when we are iterating over
3722		 * all zones) as we do not want to treat failure to find a
3723		 * match in one zone as overall failure.  Ideally, we would
3724		 * simply ignore SCF_MSG_PATTERN_NOINSTANCE and correctly
3725		 * process the others, but alas, no such interface exists --
3726		 * and we must settle for instead ignoring all errfunc-called
3727		 * errors in the case that we are iterating over all zones...
3728		 */
3729		errfunc = errignore;
3730		errarg = missing ? &missing : &ignored;
3731		missing = 0;
3732	} else {
3733		errfunc = uu_warn;
3734		errarg = &exit_status;
3735	}
3736
3737	/*
3738	 * If we're in long mode, take care of it now before we deal with the
3739	 * sorting and the columns, since we won't use them anyway.
3740	 */
3741	if (opt_mode == 'l') {
3742		if (argc == 0)
3743			argserr(progname);
3744
3745		if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3746		    print_detailed, NULL, errarg, errfunc)) != 0) {
3747			uu_warn(gettext("failed to iterate over "
3748			    "instances: %s\n"), scf_strerror(err));
3749			exit_status = UU_EXIT_FATAL;
3750		}
3751
3752		goto nextzone;
3753	}
3754
3755	if (opt_mode == 'L') {
3756		if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3757		    print_log, NULL, &exit_status, uu_warn)) != 0) {
3758			uu_warn(gettext("failed to iterate over "
3759			    "instances: %s\n"), scf_strerror(err));
3760			exit_status = UU_EXIT_FATAL;
3761		}
3762
3763		goto nextzone;
3764	}
3765
3766	if (opt_mode == 'n') {
3767		print_notify_special();
3768		if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3769		    print_notify, NULL, errarg, errfunc)) != 0) {
3770			uu_warn(gettext("failed to iterate over "
3771			    "instances: %s\n"), scf_strerror(err));
3772			exit_status = UU_EXIT_FATAL;
3773		}
3774
3775		goto nextzone;
3776	}
3777
3778	if (opt_mode == 'x') {
3779		explain(opt_verbose, argc, argv);
3780		goto nextzone;
3781	}
3782
3783	if (columns_str == NULL) {
3784		if (opt_snum == 0) {
3785			if (show_zones)
3786				add_sort_column("zone", 0);
3787
3788			/* Default sort. */
3789			add_sort_column("state", 0);
3790			add_sort_column("stime", 0);
3791			add_sort_column("fmri", 0);
3792		}
3793
3794		if (!opt_verbose) {
3795			columns_str = safe_strdup(show_zones ?
3796			    "zone,state,stime,fmri" : "state,stime,fmri");
3797		} else {
3798			columns_str = safe_strdup(show_zones ?
3799			    "zone,state,nstate,stime,ctid,fmri" :
3800			    "state,nstate,stime,ctid,fmri");
3801		}
3802	}
3803
3804	if (opt_columns == NULL) {
3805		/* Decode columns_str into opt_columns. */
3806		line_sz = 0;
3807
3808		opt_cnum = 1;
3809		for (cp = columns_str; *cp != '\0'; ++cp)
3810			if (*cp == ',')
3811				++opt_cnum;
3812
3813		opt_columns = malloc(opt_cnum * sizeof (*opt_columns));
3814		if (opt_columns == NULL)
3815			uu_die(gettext("Too many columns.\n"));
3816
3817		for (n = 0; *columns_str != '\0'; ++n) {
3818			i = getcolumnopt(&columns_str);
3819			if (i == -1)
3820				uu_die(gettext("Unknown column \"%s\".\n"),
3821				    columns_str);
3822
3823			if (strcmp(columns[i].name, "N") == 0 ||
3824			    strcmp(columns[i].name, "SN") == 0 ||
3825			    strcmp(columns[i].name, "NSTA") == 0 ||
3826			    strcmp(columns[i].name, "NSTATE") == 0)
3827				opt_nstate_shown = 1;
3828
3829			opt_columns[n] = i;
3830			line_sz += columns[i].width + 1;
3831		}
3832
3833		if ((lines_pool = uu_avl_pool_create("lines_pool",
3834		    sizeof (struct avl_string), offsetof(struct avl_string,
3835		    node), line_cmp, UU_AVL_DEBUG)) == NULL ||
3836		    (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL)
3837			uu_die(gettext("Unexpected libuutil error: %s\n"),
3838			    uu_strerror(uu_error()));
3839	}
3840
3841	switch (opt_mode) {
3842	case 0:
3843		/*
3844		 * If we already have a hash table (e.g., because we are
3845		 * processing multiple zones), destroy it before creating
3846		 * a new one.
3847		 */
3848		if (ht_buckets != NULL)
3849			ht_free();
3850
3851		ht_init();
3852
3853		/* Always show all FMRIs when given arguments or restarters */
3854		if (argc != 0 || restarters != NULL)
3855			show_all =  1;
3856
3857		if ((err = scf_walk_fmri(h, argc, argv,
3858		    SCF_WALK_MULTIPLE | SCF_WALK_LEGACY,
3859		    show_all ? list_instance : list_if_enabled, NULL,
3860		    errarg, errfunc)) != 0) {
3861			uu_warn(gettext("failed to iterate over "
3862			    "instances: %s\n"), scf_strerror(err));
3863			exit_status = UU_EXIT_FATAL;
3864		}
3865		break;
3866
3867	case 'd':
3868		if (argc == 0)
3869			argserr(progname);
3870
3871		if ((err = scf_walk_fmri(h, argc, argv,
3872		    SCF_WALK_MULTIPLE, list_dependencies, NULL,
3873		    errarg, errfunc)) != 0) {
3874			uu_warn(gettext("failed to iterate over "
3875			    "instances: %s\n"), scf_strerror(err));
3876			exit_status = UU_EXIT_FATAL;
3877		}
3878		break;
3879
3880	case 'D':
3881		if (argc == 0)
3882			argserr(progname);
3883
3884		provider_scope = safe_malloc(max_scf_fmri_length);
3885		provider_svc = safe_malloc(max_scf_fmri_length);
3886		provider_inst = safe_malloc(max_scf_fmri_length);
3887
3888		if ((err = scf_walk_fmri(h, argc, argv,
3889		    SCF_WALK_MULTIPLE | SCF_WALK_SERVICE,
3890		    list_dependents, NULL, &exit_status, uu_warn)) != 0) {
3891			uu_warn(gettext("failed to iterate over "
3892			    "instances: %s\n"), scf_strerror(err));
3893			exit_status = UU_EXIT_FATAL;
3894		}
3895
3896		free(provider_scope);
3897		free(provider_svc);
3898		free(provider_inst);
3899		break;
3900
3901	case 'n':
3902		break;
3903
3904	default:
3905		assert(0);
3906		abort();
3907	}
3908
3909nextzone:
3910	if (show_zones && zent < nzents && exit_status == 0) {
3911		scf_handle_destroy(h);
3912		goto again;
3913	}
3914
3915	if (show_zones && exit_status == 0)
3916		exit_status = missing;
3917
3918	if (opt_columns == NULL)
3919		return (exit_status);
3920
3921	if (show_header)
3922		print_header();
3923
3924	(void) uu_avl_walk(lines, print_line, NULL, 0);
3925
3926	return (exit_status);
3927}
3928