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