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 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Copyright 2019 Joyent, Inc.
29 */
30
31/*
32 * svcprop - report service configuration properties
33 */
34
35#include <locale.h>
36#include <libintl.h>
37#include <libscf.h>
38#include <libscf_priv.h>
39#include <libuutil.h>
40#include <stddef.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <unistd.h>
44#include <strings.h>
45#include <assert.h>
46#include <zone.h>
47
48#ifndef TEXT_DOMAIN
49#define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
50#endif /* TEXT_DOMAIN */
51
52/*
53 * Error functions.  These can change if the quiet (-q) option is used.
54 */
55static void (*warn)(const char *, ...) = uu_warn;
56static __NORETURN void (*die)(const char *, ...) = uu_die;
57
58/*
59 * Entity encapsulation.  This allows me to treat services and instances
60 * similarly, and avoid duplicating process_ent().
61 */
62typedef struct {
63	char type;			/* !=0: service, 0: instance */
64	union {
65		scf_service_t *svc;
66		scf_instance_t *inst;
67	} u;
68} scf_entityp_t;
69
70#define	ENT_INSTANCE	0
71
72#define	SCF_ENTITY_SET_TO_SERVICE(ent, s)	{ ent.type = 1; ent.u.svc = s; }
73
74#define	SCF_ENTITY_SET_TO_INSTANCE(ent, i)	\
75	{ ent.type = ENT_INSTANCE; ent.u.inst = i; }
76
77#define	scf_entity_get_pg(ent, name, pg) \
78	(ent.type ? scf_service_get_pg(ent.u.svc, name, pg) : \
79	scf_instance_get_pg(ent.u.inst, name, pg))
80
81#define	scf_entity_to_fmri(ent, buf, buf_sz) \
82	(ent.type ? scf_service_to_fmri(ent.u.svc, buf, buf_sz) : \
83	scf_instance_to_fmri(ent.u.inst, buf, buf_sz))
84
85#define	SCF_ENTITY_TYPE_NAME(ent)	(ent.type ? "service" : "instance")
86
87/*
88 * Data structure for -p arguments.  Since they may be name or name/name, we
89 * just track the components.
90 */
91typedef struct svcprop_prop_node {
92	uu_list_node_t	spn_list_node;
93	const char	*spn_comp1;
94	const char	*spn_comp2;
95} svcprop_prop_node_t;
96
97static uu_list_pool_t	*prop_pool;
98static uu_list_t	*prop_list;
99
100static scf_handle_t *hndl;
101static ssize_t max_scf_name_length;
102static ssize_t max_scf_value_length;
103static ssize_t max_scf_fmri_length;
104
105/* Options */
106static int quiet = 0;			/* No output. Nothing found, exit(1) */
107static int types = 0;			/* Display types of properties. */
108static int verbose = 0;			/* Print not found errors to stderr. */
109static int fmris = 0;			/* Display full FMRIs for properties. */
110static int wait = 0;			/* Wait mode. */
111static char *snapshot = "running";	/* Snapshot to use. */
112static int Cflag = 0;			/* C option supplied */
113static int cflag = 0;			/* c option supplied */
114static int sflag = 0;			/* s option supplied */
115static int return_code;			/* main's return code */
116
117#define	PRINT_NOPROP_ERRORS	(!quiet || verbose)
118
119/*
120 * For unexpected libscf errors.  The ending newline is necessary to keep
121 * uu_die() from appending the errno error.
122 */
123static void
124scfdie(void)
125{
126	die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
127	    scf_strerror(scf_error()));
128}
129
130static void *
131safe_malloc(size_t sz)
132{
133	void *p;
134
135	p = malloc(sz);
136	if (p == NULL)
137		die(gettext("Could not allocate memory"));
138
139	return (p);
140}
141
142static void
143usage(void)
144{
145	(void) fprintf(stderr, gettext("Usage: %1$s [-fqtv] "
146	    "[-C | -c | -s snapshot] [-z zone] "
147	    "[-p [name/]name]... \n"
148	    "         {FMRI | pattern}...\n"
149	    "       %1$s -w [-fqtv] [-z zone] [-p [name/]name] "
150	    "{FMRI | pattern}\n"), uu_getpname());
151	exit(UU_EXIT_USAGE);
152}
153
154/*
155 * Return an allocated copy of str, with the Bourne shell's metacharacters
156 * escaped by '\'.
157 *
158 * What about unicode?
159 */
160static char *
161quote_for_shell(const char *str)
162{
163	const char *sp;
164	char *dst, *dp;
165	size_t dst_len;
166
167	const char * const metachars = ";&()|^<>\n \t\\\"\'`";
168
169	if (str[0] == '\0')
170		return (strdup("\"\""));
171
172	dst_len = 0;
173	for (sp = str; *sp != '\0'; ++sp) {
174		++dst_len;
175
176		if (strchr(metachars, *sp) != NULL)
177			++dst_len;
178	}
179
180	if (sp - str == dst_len)
181		return (strdup(str));
182
183	dst = safe_malloc(dst_len + 1);
184
185	for (dp = dst, sp = str; *sp != '\0'; ++dp, ++sp) {
186		if (strchr(metachars, *sp) != NULL)
187			*dp++ = '\\';
188
189		*dp = *sp;
190	}
191	*dp = '\0';
192
193	return (dst);
194}
195
196static void
197print_value(scf_value_t *val)
198{
199	char *buf, *qbuf;
200	ssize_t bufsz, r;
201
202	bufsz = scf_value_get_as_string(val, NULL, 0) + 1;
203	if (bufsz - 1 < 0)
204		scfdie();
205
206	buf = safe_malloc(bufsz);
207
208	r = scf_value_get_as_string(val, buf, bufsz);
209	assert(r + 1 == bufsz);
210
211	qbuf = quote_for_shell(buf);
212	(void) fputs(qbuf, stdout);
213
214	free(qbuf);
215	free(buf);
216}
217
218/*
219 * Display a property's values on a line.  If types is true, prepend
220 * identification (the FMRI if fmris is true, pg/prop otherwise) and the type
221 * of the property.
222 */
223static void
224display_prop(scf_propertygroup_t *pg, scf_property_t *prop)
225{
226	scf_value_t *val;
227	scf_iter_t *iter;
228	int ret, first, err;
229
230	const char * const permission_denied_emsg =
231	    gettext("Permission denied.\n");
232
233	if (types) {
234		scf_type_t ty;
235		char *buf;
236		size_t buf_sz;
237
238		if (fmris) {
239			buf_sz = max_scf_fmri_length + 1;
240			buf = safe_malloc(buf_sz);
241
242			if (scf_property_to_fmri(prop, buf, buf_sz) == -1)
243				scfdie();
244			(void) fputs(buf, stdout);
245
246			free(buf);
247		} else {
248			buf_sz = max_scf_name_length + 1;
249			buf = safe_malloc(buf_sz);
250
251			if (scf_pg_get_name(pg, buf, buf_sz) < 0)
252				scfdie();
253			(void) fputs(buf, stdout);
254			(void) putchar('/');
255
256			if (scf_property_get_name(prop, buf, buf_sz) < 0)
257				scfdie();
258			(void) fputs(buf, stdout);
259
260			free(buf);
261		}
262
263		(void) putchar(' ');
264
265		if (scf_property_type(prop, &ty) == -1)
266			scfdie();
267		(void) fputs(scf_type_to_string(ty), stdout);
268		(void) putchar(' ');
269	}
270
271	if ((iter = scf_iter_create(hndl)) == NULL ||
272	    (val = scf_value_create(hndl)) == NULL)
273		scfdie();
274
275	if (scf_iter_property_values(iter, prop) == -1)
276		scfdie();
277
278	first = 1;
279	while ((ret = scf_iter_next_value(iter, val)) == 1) {
280		if (first)
281			first = 0;
282		else
283			(void) putchar(' ');
284		print_value(val);
285	}
286	if (ret == -1) {
287		err = scf_error();
288		if (err == SCF_ERROR_PERMISSION_DENIED) {
289			if (uu_list_numnodes(prop_list) > 0)
290				die(permission_denied_emsg);
291		} else {
292			scfdie();
293		}
294	}
295
296	(void) putchar('\n');
297
298	scf_iter_destroy(iter);
299	(void) scf_value_destroy(val);
300}
301
302/*
303 * display_prop() all of the properties in the given property group.  Force
304 * types to true so identification will be displayed.
305 */
306static void
307display_pg(scf_propertygroup_t *pg)
308{
309	scf_property_t *prop;
310	scf_iter_t *iter;
311	int ret;
312
313	types = 1;	/* Always display types for whole propertygroups. */
314
315	if ((prop = scf_property_create(hndl)) == NULL ||
316	    (iter = scf_iter_create(hndl)) == NULL)
317		scfdie();
318
319	if (scf_iter_pg_properties(iter, pg) == -1)
320		scfdie();
321
322	while ((ret = scf_iter_next_property(iter, prop)) == 1)
323		display_prop(pg, prop);
324	if (ret == -1)
325		scfdie();
326
327	scf_iter_destroy(iter);
328	scf_property_destroy(prop);
329}
330
331/*
332 * Common code to execute when a nonexistant property is encountered.
333 */
334static void
335noprop_common_action()
336{
337	if (!PRINT_NOPROP_ERRORS)
338		/* We're not printing errors, so we can cut out early. */
339		exit(UU_EXIT_FATAL);
340
341	return_code = UU_EXIT_FATAL;
342}
343
344/*
345 * Iterate the properties of a service or an instance when no snapshot
346 * is specified.
347 */
348static int
349scf_iter_entity_pgs(scf_iter_t *iter, scf_entityp_t ent)
350{
351	int ret = 0;
352
353	if (ent.type) {
354		/*
355		 * If we are displaying properties for a service,
356		 * treat it as though it were a composed, current
357		 * lookup. (implicit cflag) However, if a snapshot
358		 * was specified, fail.
359		 */
360		if (sflag)
361			die(gettext("Only instances have "
362			    "snapshots.\n"));
363		ret = scf_iter_service_pgs(iter, ent.u.svc);
364	} else {
365		if (Cflag)
366			ret = scf_iter_instance_pgs(iter, ent.u.inst);
367		else
368			ret = scf_iter_instance_pgs_composed(iter, ent.u.inst,
369			    NULL);
370	}
371	return (ret);
372}
373
374/*
375 * Return a snapshot for the supplied instance and snapshot name.
376 */
377static scf_snapshot_t *
378get_snapshot(const scf_instance_t *inst, const char *snapshot)
379{
380	scf_snapshot_t *snap = scf_snapshot_create(hndl);
381
382	if (snap == NULL)
383		scfdie();
384
385	if (scf_instance_get_snapshot(inst, snapshot, snap) == -1) {
386		switch (scf_error()) {
387		case SCF_ERROR_INVALID_ARGUMENT:
388			die(gettext("Invalid snapshot name.\n"));
389			/* NOTREACHED */
390
391		case SCF_ERROR_NOT_FOUND:
392			if (sflag == 0) {
393				scf_snapshot_destroy(snap);
394				snap = NULL;
395			} else
396				die(gettext("No such snapshot.\n"));
397			break;
398
399		default:
400			scfdie();
401		}
402	}
403
404	return (snap);
405}
406
407/*
408 * Entity (service or instance): If there are -p options,
409 * display_{pg,prop}() the named property groups and/or properties.  Otherwise
410 * display_pg() all property groups.
411 */
412static void
413process_ent(scf_entityp_t ent)
414{
415	scf_snapshot_t *snap = NULL;
416	scf_propertygroup_t *pg;
417	scf_property_t *prop;
418	scf_iter_t *iter;
419	svcprop_prop_node_t *spn;
420	int ret, err;
421
422	if (uu_list_numnodes(prop_list) == 0) {
423		if (quiet)
424			return;
425
426		if ((pg = scf_pg_create(hndl)) == NULL ||
427		    (iter = scf_iter_create(hndl)) == NULL)
428			scfdie();
429
430		if (cflag || Cflag || ent.type != ENT_INSTANCE) {
431			if (scf_iter_entity_pgs(iter, ent) == -1)
432				scfdie();
433		} else {
434			if (snapshot != NULL)
435				snap = get_snapshot(ent.u.inst, snapshot);
436
437			if (scf_iter_instance_pgs_composed(iter, ent.u.inst,
438			    snap) == -1)
439				scfdie();
440			if (snap)
441				scf_snapshot_destroy(snap);
442		}
443
444		while ((ret = scf_iter_next_pg(iter, pg)) == 1)
445			display_pg(pg);
446		if (ret == -1)
447			scfdie();
448
449		/*
450		 * In normal usage, i.e. against the running snapshot,
451		 * we must iterate over the current non-persistent
452		 * pg's.
453		 */
454		if (sflag == 0 && snap != NULL) {
455			scf_iter_reset(iter);
456			if (scf_iter_instance_pgs_composed(iter, ent.u.inst,
457			    NULL) == -1)
458				scfdie();
459			while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
460				uint32_t flags;
461
462				if (scf_pg_get_flags(pg, &flags) == -1)
463					scfdie();
464				if (flags & SCF_PG_FLAG_NONPERSISTENT)
465					display_pg(pg);
466			}
467		}
468		if (ret == -1)
469			scfdie();
470
471		scf_iter_destroy(iter);
472		scf_pg_destroy(pg);
473
474		return;
475	}
476
477	if ((pg = scf_pg_create(hndl)) == NULL ||
478	    (prop = scf_property_create(hndl)) == NULL)
479		scfdie();
480
481	if (ent.type == ENT_INSTANCE && snapshot != NULL)
482		snap = get_snapshot(ent.u.inst, snapshot);
483
484	for (spn = uu_list_first(prop_list);
485	    spn != NULL;
486	    spn = uu_list_next(prop_list, spn)) {
487		if (ent.type == ENT_INSTANCE) {
488			if (Cflag)
489				ret = scf_instance_get_pg(ent.u.inst,
490				    spn->spn_comp1, pg);
491			else
492				ret = scf_instance_get_pg_composed(ent.u.inst,
493				    snap, spn->spn_comp1, pg);
494			err = scf_error();
495
496			/*
497			 * If we didn't find it in the specified snapshot, use
498			 * the current values if the pg is nonpersistent.
499			 */
500			if (ret == -1 && !Cflag &&snap != NULL && err ==
501			    SCF_ERROR_NOT_FOUND) {
502				ret = scf_instance_get_pg_composed(
503				    ent.u.inst, NULL, spn->spn_comp1,
504				    pg);
505
506				if (ret == 0) {
507					uint32_t flags;
508
509					if (scf_pg_get_flags(pg, &flags) == -1)
510						scfdie();
511					if ((flags & SCF_PG_FLAG_NONPERSISTENT)
512					    == 0) {
513						ret = -1;
514					}
515				}
516			}
517		} else {
518			/*
519			 * If we are displaying properties for a service,
520			 * treat it as though it were a composed, current
521			 * lookup. (implicit cflag) However, if a snapshot
522			 * was specified, fail.
523			 */
524			if (sflag)
525				die(gettext("Only instances have "
526				    "snapshots.\n"));
527			ret = scf_entity_get_pg(ent, spn->spn_comp1, pg);
528			err = scf_error();
529		}
530		if (ret == -1) {
531			if (err != SCF_ERROR_NOT_FOUND)
532				scfdie();
533
534			if (PRINT_NOPROP_ERRORS) {
535				char *buf;
536
537				buf = safe_malloc(max_scf_fmri_length + 1);
538				if (scf_entity_to_fmri(ent, buf,
539				    max_scf_fmri_length + 1) == -1)
540					scfdie();
541
542				uu_warn(gettext("Couldn't find property group "
543				    "`%s' for %s `%s'.\n"), spn->spn_comp1,
544				    SCF_ENTITY_TYPE_NAME(ent), buf);
545
546				free(buf);
547			}
548
549			noprop_common_action();
550
551			continue;
552		}
553
554		if (spn->spn_comp2 == NULL) {
555			if (!quiet)
556				display_pg(pg);
557			continue;
558		}
559
560		if (scf_pg_get_property(pg, spn->spn_comp2, prop) == -1) {
561			if (scf_error() != SCF_ERROR_NOT_FOUND)
562				scfdie();
563
564			if (PRINT_NOPROP_ERRORS) {
565				char *buf;
566
567				buf = safe_malloc(max_scf_fmri_length + 1);
568				if (scf_entity_to_fmri(ent, buf,
569				    max_scf_fmri_length + 1) == -1)
570					scfdie();
571
572				/* FMRI syntax knowledge */
573				uu_warn(gettext("Couldn't find property "
574				    "`%s/%s' for %s `%s'.\n"), spn->spn_comp1,
575				    spn->spn_comp2, SCF_ENTITY_TYPE_NAME(ent),
576				    buf);
577
578				free(buf);
579			}
580
581			noprop_common_action();
582
583			continue;
584		}
585
586		if (!quiet)
587			display_prop(pg, prop);
588	}
589
590	scf_property_destroy(prop);
591	scf_pg_destroy(pg);
592	if (snap)
593		scf_snapshot_destroy(snap);
594}
595
596/*
597 * Without -p options, just call display_pg().  Otherwise display_prop() the
598 * named properties of the property group.
599 */
600static void
601process_pg(scf_propertygroup_t *pg)
602{
603	scf_property_t *prop;
604	svcprop_prop_node_t *spn;
605
606	if (uu_list_first(prop_list) == NULL) {
607		if (quiet)
608			return;
609
610		display_pg(pg);
611		return;
612	}
613
614	prop = scf_property_create(hndl);
615	if (prop == NULL)
616		scfdie();
617
618	for (spn = uu_list_first(prop_list);
619	    spn != NULL;
620	    spn = uu_list_next(prop_list, spn)) {
621		if (spn->spn_comp2 != NULL) {
622			char *buf;
623
624			buf = safe_malloc(max_scf_fmri_length + 1);
625			if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) ==
626			    -1)
627				scfdie();
628
629			uu_xdie(UU_EXIT_USAGE, gettext("-p argument `%s/%s' "
630			    "has too many components for property "
631			    "group `%s'.\n"), spn->spn_comp1, spn->spn_comp2,
632			    buf);
633		}
634
635		if (scf_pg_get_property(pg, spn->spn_comp1, prop) == 0) {
636			if (!quiet)
637				display_prop(pg, prop);
638			continue;
639		}
640
641		if (scf_error() != SCF_ERROR_NOT_FOUND)
642			scfdie();
643
644		if (PRINT_NOPROP_ERRORS) {
645			char *buf;
646
647			buf = safe_malloc(max_scf_fmri_length + 1);
648			if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) ==
649			    -1)
650				scfdie();
651
652			uu_warn(gettext("Couldn't find property `%s' in "
653			    "property group `%s'.\n"), spn->spn_comp1, buf);
654
655			free(buf);
656		}
657
658		noprop_common_action();
659	}
660}
661
662/*
663 * If there are -p options, show the error.  Otherwise just call
664 * display_prop().
665 */
666static void
667process_prop(scf_propertygroup_t *pg, scf_property_t *prop)
668{
669	if (uu_list_first(prop_list) != NULL) {
670		uu_warn(gettext("The -p option cannot be used with property "
671		    "operands.\n"));
672		usage();
673	}
674
675	if (quiet)
676		return;
677
678	display_prop(pg, prop);
679}
680
681/* Decode an operand & dispatch. */
682/* ARGSUSED */
683static int
684process_fmri(void *unused, scf_walkinfo_t *wip)
685{
686	scf_entityp_t ent;
687
688	/* Multiple matches imply multiple entities. */
689	if (wip->count > 1)
690		types = fmris = 1;
691
692	if (wip->prop != NULL) {
693		process_prop(wip->pg, wip->prop);
694	} else if (wip->pg != NULL) {
695		process_pg(wip->pg);
696	} else if (wip->inst != NULL) {
697		SCF_ENTITY_SET_TO_INSTANCE(ent, wip->inst);
698		process_ent(ent);
699	} else {
700		/* scf_walk_fmri() won't let this happen */
701		assert(wip->svc != NULL);
702		SCF_ENTITY_SET_TO_SERVICE(ent, wip->svc);
703		process_ent(ent);
704	}
705
706	return (0);
707}
708
709static void
710add_prop(char *property)
711{
712	svcprop_prop_node_t *p, *last;
713	char *slash;
714
715	const char * const invalid_component_emsg =
716	    gettext("Invalid component name `%s'.\n");
717
718	/* FMRI syntax knowledge. */
719	slash = strchr(property, '/');
720	if (slash != NULL) {
721		if (strchr(slash + 1, '/') != NULL) {
722			uu_warn(gettext("-p argument `%s' has too many "
723			    "components.\n"), property);
724			usage();
725		}
726	}
727
728	if (slash != NULL)
729		*slash = '\0';
730
731	p = safe_malloc(sizeof (svcprop_prop_node_t));
732	uu_list_node_init(p, &p->spn_list_node, prop_pool);
733
734	p->spn_comp1 = property;
735	p->spn_comp2 = (slash == NULL) ? NULL : slash + 1;
736
737	if (uu_check_name(p->spn_comp1, UU_NAME_DOMAIN) == -1)
738		uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp1);
739	if (p->spn_comp2 != NULL &&
740	    uu_check_name(p->spn_comp2, UU_NAME_DOMAIN) == -1)
741		uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp2);
742
743	last = uu_list_last(prop_list);
744	if (last != NULL) {
745		if ((last->spn_comp2 == NULL) ^ (p->spn_comp2 == NULL)) {
746			/*
747			 * The -p options have mixed numbers of components.
748			 * If they both turn out to be valid, then the
749			 * single-component ones will specify property groups,
750			 * so we need to turn on types to keep the output of
751			 * display_prop() consistent with display_pg().
752			 */
753			types = 1;
754		}
755	}
756
757	(void) uu_list_insert_after(prop_list, NULL, p);
758}
759
760
761/*
762 * Wait for a property group or property change.
763 *
764 * Extract a pg and optionally a property name from fmri & prop_list.
765 * _scf_pg_wait() for the pg, and display_pg(pg) or display_prop(pg, prop)
766 * when it returns.
767 */
768/* ARGSUSED */
769static int
770do_wait(void *unused, scf_walkinfo_t *wip)
771{
772	scf_property_t *prop;
773	scf_propertygroup_t *lpg, *pg;
774	const char *propname;
775	svcprop_prop_node_t *p;
776
777	const char *emsg_not_found = gettext("Not found.\n");
778
779	if ((lpg = scf_pg_create(hndl)) == NULL ||
780	    (prop = scf_property_create(hndl)) == NULL)
781		scfdie();
782
783	if (wip->prop != NULL) {
784		if (uu_list_numnodes(prop_list) > 0)
785			uu_xdie(UU_EXIT_USAGE, gettext("-p cannot be used with "
786			    "property FMRIs.\n"));
787		pg = wip->pg;
788
789		assert(strrchr(wip->fmri, '/') != NULL);
790		propname = strrchr(wip->fmri, '/') + 1;
791
792	} else if (wip->pg != NULL) {
793		p = uu_list_first(prop_list);
794
795		if (p != NULL) {
796			if (p->spn_comp2 != NULL)
797				uu_xdie(UU_EXIT_USAGE, gettext("-p argument "
798				    "\"%s/%s\" has too many components for "
799				    "property group %s.\n"),
800				    p->spn_comp1, p->spn_comp2, wip->fmri);
801
802			propname = p->spn_comp1;
803
804			if (scf_pg_get_property(wip->pg, propname, prop) !=
805			    SCF_SUCCESS) {
806				switch (scf_error()) {
807				case SCF_ERROR_INVALID_ARGUMENT:
808					uu_xdie(UU_EXIT_USAGE,
809					    gettext("Invalid property name "
810					    "\"%s\".\n"), propname);
811
812					/* NOTREACHED */
813
814				case SCF_ERROR_NOT_FOUND:
815					die(emsg_not_found);
816
817				default:
818					scfdie();
819				}
820			}
821		} else {
822			propname = NULL;
823		}
824
825		pg = wip->pg;
826
827	} else if (wip->inst != NULL) {
828
829		p = uu_list_first(prop_list);
830		if (p == NULL)
831			uu_xdie(UU_EXIT_USAGE,
832			    gettext("Cannot wait for an instance.\n"));
833
834		if (scf_instance_get_pg(wip->inst, p->spn_comp1, lpg) !=
835		    SCF_SUCCESS) {
836			switch (scf_error()) {
837			case SCF_ERROR_INVALID_ARGUMENT:
838				uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
839				    "property group name \"%s\".\n"),
840				    p->spn_comp1);
841				/* NOTREACHED */
842
843			case SCF_ERROR_NOT_FOUND:
844				die(emsg_not_found);
845
846			default:
847				scfdie();
848			}
849		}
850
851		propname = p->spn_comp2;
852
853		if (propname != NULL) {
854			if (scf_pg_get_property(lpg, propname, prop) !=
855			    SCF_SUCCESS) {
856				switch (scf_error()) {
857				case SCF_ERROR_INVALID_ARGUMENT:
858					uu_xdie(UU_EXIT_USAGE,
859					    gettext("Invalid property name "
860					    "\"%s\".\n"), propname);
861					/* NOTREACHED */
862
863				case SCF_ERROR_NOT_FOUND:
864					die(emsg_not_found);
865
866				default:
867					scfdie();
868				}
869			}
870		}
871
872		pg = lpg;
873
874	} else if (wip->svc != NULL) {
875
876		p = uu_list_first(prop_list);
877		if (p == NULL)
878			uu_xdie(UU_EXIT_USAGE,
879			    gettext("Cannot wait for a service.\n"));
880
881		if (scf_service_get_pg(wip->svc, p->spn_comp1, lpg) !=
882		    SCF_SUCCESS) {
883			switch (scf_error()) {
884			case SCF_ERROR_INVALID_ARGUMENT:
885				uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
886				    "property group name \"%s\".\n"),
887				    p->spn_comp1);
888				/* NOTREACHED */
889
890			case SCF_ERROR_NOT_FOUND:
891				die(emsg_not_found);
892
893			default:
894				scfdie();
895			}
896		}
897
898		propname = p->spn_comp2;
899
900		if (propname != NULL) {
901			if (scf_pg_get_property(lpg, propname, prop) !=
902			    SCF_SUCCESS) {
903				switch (scf_error()) {
904				case SCF_ERROR_INVALID_ARGUMENT:
905					uu_xdie(UU_EXIT_USAGE,
906					    gettext("Invalid property name "
907					    "\"%s\".\n"), propname);
908
909					/* NOTREACHED */
910
911				case SCF_ERROR_NOT_FOUND:
912					die(emsg_not_found);
913
914				default:
915					scfdie();
916				}
917			}
918		}
919
920		pg = lpg;
921
922	} else {
923		uu_xdie(UU_EXIT_USAGE, gettext("FMRI must specify an entity, "
924		    "property group, or property.\n"));
925	}
926
927	for (;;) {
928		int ret;
929
930		ret = _scf_pg_wait(pg, -1);
931		if (ret != SCF_SUCCESS)
932			scfdie();
933
934		ret = scf_pg_update(pg);
935		if (ret < 0) {
936			if (scf_error() != SCF_ERROR_DELETED)
937				scfdie();
938
939			die(emsg_not_found);
940		}
941		if (ret == SCF_COMPLETE)
942			break;
943	}
944
945	if (propname != NULL) {
946		if (scf_pg_get_property(pg, propname, prop) == SCF_SUCCESS) {
947			if (!quiet)
948				display_prop(pg, prop);
949		} else {
950			if (scf_error() != SCF_ERROR_NOT_FOUND)
951				scfdie();
952
953			if (PRINT_NOPROP_ERRORS)
954				uu_warn(emsg_not_found);
955
956			return_code = UU_EXIT_FATAL;
957		}
958	} else {
959		if (!quiet)
960			display_pg(pg);
961	}
962
963	scf_property_destroy(prop);
964	scf_pg_destroy(lpg);
965
966	return (0);
967}
968
969/*
970 * These functions replace uu_warn() and uu_die() when the quiet (-q) option is
971 * used, and silently ignore any output.
972 */
973
974/*ARGSUSED*/
975static void
976quiet_warn(const char *fmt, ...)
977{
978	/* Do nothing */
979}
980
981/*ARGSUSED*/
982static __NORETURN void
983quiet_die(const char *fmt, ...)
984{
985	exit(UU_EXIT_FATAL);
986}
987
988int
989main(int argc, char *argv[])
990{
991	int c;
992	scf_walk_callback callback;
993	int flags;
994	int err;
995
996	(void) setlocale(LC_ALL, "");
997	(void) textdomain(TEXT_DOMAIN);
998
999	return_code = UU_EXIT_OK;
1000
1001	(void) uu_setpname(argv[0]);
1002
1003	prop_pool = uu_list_pool_create("properties",
1004	    sizeof (svcprop_prop_node_t),
1005	    offsetof(svcprop_prop_node_t, spn_list_node), NULL, 0);
1006	if (prop_pool == NULL)
1007		uu_die("%s\n", uu_strerror(uu_error()));
1008
1009	prop_list = uu_list_create(prop_pool, NULL, 0);
1010
1011	hndl = scf_handle_create(SCF_VERSION);
1012	if (hndl == NULL)
1013		scfdie();
1014
1015	while ((c = getopt(argc, argv, "Ccfp:qs:tvwz:")) != -1) {
1016		switch (c) {
1017		case 'C':
1018			if (cflag || sflag || wait)
1019				usage();	/* Not with -c, -s or -w */
1020			Cflag++;
1021			snapshot = NULL;
1022			break;
1023
1024		case 'c':
1025			if (Cflag || sflag || wait)
1026				usage();	/* Not with -C, -s or -w */
1027			cflag++;
1028			snapshot = NULL;
1029			break;
1030
1031		case 'f':
1032			types = 1;
1033			fmris = 1;
1034			break;
1035
1036		case 'p':
1037			add_prop(optarg);
1038			break;
1039
1040		case 'q':
1041			quiet = 1;
1042			warn = quiet_warn;
1043			die = quiet_die;
1044			break;
1045
1046		case 's':
1047			if (Cflag || cflag || wait)
1048				usage();	/* Not with -C, -c or -w */
1049			snapshot = optarg;
1050			sflag++;
1051			break;
1052
1053		case 't':
1054			types = 1;
1055			break;
1056
1057		case 'v':
1058			verbose = 1;
1059			break;
1060
1061		case 'w':
1062			if (Cflag || cflag || sflag)
1063				usage();	/* Not with -C, -c or -s */
1064			wait = 1;
1065			break;
1066
1067		case 'z': {
1068			scf_value_t *zone;
1069			scf_handle_t *h = hndl;
1070
1071			if (getzoneid() != GLOBAL_ZONEID)
1072				uu_die(gettext("svcprop -z may only be used "
1073				    "from the global zone\n"));
1074
1075			if ((zone = scf_value_create(h)) == NULL)
1076				scfdie();
1077
1078			if (scf_value_set_astring(zone, optarg) != SCF_SUCCESS)
1079				scfdie();
1080
1081			if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS)
1082				uu_die(gettext("invalid zone '%s'\n"), optarg);
1083
1084			scf_value_destroy(zone);
1085			break;
1086		}
1087
1088		case '?':
1089			switch (optopt) {
1090			case 'p':
1091				usage();
1092
1093			default:
1094				break;
1095			}
1096
1097			/* FALLTHROUGH */
1098
1099		default:
1100			usage();
1101		}
1102	}
1103
1104	if (optind == argc)
1105		usage();
1106
1107	max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1108	max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1109	max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
1110	if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
1111	    max_scf_fmri_length == -1)
1112		scfdie();
1113
1114	if (scf_handle_bind(hndl) == -1)
1115		die(gettext("Could not connect to configuration repository: "
1116		    "%s.\n"), scf_strerror(scf_error()));
1117
1118	flags = SCF_WALK_PROPERTY | SCF_WALK_SERVICE | SCF_WALK_EXPLICIT;
1119
1120	if (wait) {
1121		if (uu_list_numnodes(prop_list) > 1)
1122			usage();
1123
1124		if (argc - optind > 1)
1125			usage();
1126
1127		callback = do_wait;
1128
1129	} else {
1130		callback = process_fmri;
1131
1132		flags |= SCF_WALK_MULTIPLE;
1133	}
1134
1135	if ((err = scf_walk_fmri(hndl, argc - optind, argv + optind, flags,
1136	    callback, NULL, &return_code, warn)) != 0) {
1137		warn(gettext("failed to iterate over instances: %s\n"),
1138		    scf_strerror(err));
1139		return_code = UU_EXIT_FATAL;
1140	}
1141
1142	scf_handle_destroy(hndl);
1143
1144	return (return_code);
1145}
1146