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