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