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