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