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