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