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