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