xref: /illumos-gate/usr/src/cmd/svc/svcprop/svcprop.c (revision 3eae19d9cf3390cf5b75e10c9c1945fd36ad856a)
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  */
52 static void (*warn)(const char *, ...) = uu_warn;
53 static 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  */
59 typedef 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  */
88 typedef 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 
94 static uu_list_pool_t	*prop_pool;
95 static uu_list_t	*prop_list;
96 
97 static scf_handle_t *hndl;
98 static ssize_t max_scf_name_length;
99 static ssize_t max_scf_value_length;
100 static ssize_t max_scf_fmri_length;
101 
102 /* Options */
103 static int quiet = 0;			/* No output. Nothing found, exit(1) */
104 static int types = 0;			/* Display types of properties. */
105 static int verbose = 0;			/* Print not found errors to stderr. */
106 static int fmris = 0;			/* Display full FMRIs for properties. */
107 static int wait = 0;			/* Wait mode. */
108 static char *snapshot = "running";	/* Snapshot to use. */
109 static int Cflag = 0;			/* C option supplied */
110 static int cflag = 0;			/* c option supplied */
111 static int sflag = 0;			/* s option supplied */
112 static 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  */
120 static void
121 scfdie()
122 {
123 	die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
124 	    scf_strerror(scf_error()));
125 }
126 
127 static void *
128 safe_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 
139 static void
140 usage()
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  */
157 static char *
158 quote_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 
193 static void
194 print_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  */
220 static void
221 display_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  */
303 static void
304 display_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  */
331 static void
332 noprop_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  */
345 static int
346 scf_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  */
374 static scf_snapshot_t *
375 get_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  */
409 static void
410 process_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  */
597 static void
598 process_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  */
665 static void
666 process_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 */
682 static int
683 process_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 
708 static void
709 add_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 */
768 static int
769 do_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*/
979 static void
980 quiet_warn(const char *fmt, ...)
981 {
982 	/* Do nothing */
983 }
984 
985 /*ARGSUSED*/
986 static void
987 quiet_die(const char *fmt, ...)
988 {
989 	exit(UU_EXIT_FATAL);
990 }
991 
992 int
993 main(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