xref: /illumos-gate/usr/src/cmd/svc/svcprop/svcprop.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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;
226 
227 	if (types) {
228 		scf_type_t ty;
229 		char *buf;
230 		size_t buf_sz;
231 
232 		if (fmris) {
233 			buf_sz = max_scf_fmri_length + 1;
234 			buf = safe_malloc(buf_sz);
235 
236 			if (scf_property_to_fmri(prop, buf, buf_sz) == -1)
237 				scfdie();
238 			(void) fputs(buf, stdout);
239 
240 			free(buf);
241 		} else {
242 			buf_sz = max_scf_name_length + 1;
243 			buf = safe_malloc(buf_sz);
244 
245 			if (scf_pg_get_name(pg, buf, buf_sz) < 0)
246 				scfdie();
247 			(void) fputs(buf, stdout);
248 			(void) putchar('/');
249 
250 			if (scf_property_get_name(prop, buf, buf_sz) < 0)
251 				scfdie();
252 			(void) fputs(buf, stdout);
253 
254 			free(buf);
255 		}
256 
257 		(void) putchar(' ');
258 
259 		if (scf_property_type(prop, &ty) == -1)
260 			scfdie();
261 		(void) fputs(scf_type_to_string(ty), stdout);
262 		(void) putchar(' ');
263 	}
264 
265 	if ((iter = scf_iter_create(hndl)) == NULL ||
266 	    (val = scf_value_create(hndl)) == NULL)
267 		scfdie();
268 
269 	if (scf_iter_property_values(iter, prop) == -1)
270 		scfdie();
271 
272 	first = 1;
273 	while ((ret = scf_iter_next_value(iter, val)) == 1) {
274 		if (first)
275 			first = 0;
276 		else
277 			(void) putchar(' ');
278 		print_value(val);
279 	}
280 	if (ret == -1)
281 		scfdie();
282 
283 	(void) putchar('\n');
284 
285 	scf_iter_destroy(iter);
286 	(void) scf_value_destroy(val);
287 }
288 
289 /*
290  * display_prop() all of the properties in the given property group.  Force
291  * types to true so identification will be displayed.
292  */
293 static void
294 display_pg(scf_propertygroup_t *pg)
295 {
296 	scf_property_t *prop;
297 	scf_iter_t *iter;
298 	int ret;
299 
300 	types = 1;	/* Always display types for whole propertygroups. */
301 
302 	if ((prop = scf_property_create(hndl)) == NULL ||
303 	    (iter = scf_iter_create(hndl)) == NULL)
304 		scfdie();
305 
306 	if (scf_iter_pg_properties(iter, pg) == -1)
307 		scfdie();
308 
309 	while ((ret = scf_iter_next_property(iter, prop)) == 1)
310 		display_prop(pg, prop);
311 	if (ret == -1)
312 		scfdie();
313 
314 	scf_iter_destroy(iter);
315 	scf_property_destroy(prop);
316 }
317 
318 /*
319  * Common code to execute when a nonexistant property is encountered.
320  */
321 static void
322 noprop_common_action()
323 {
324 	if (!PRINT_NOPROP_ERRORS)
325 		/* We're not printing errors, so we can cut out early. */
326 		exit(UU_EXIT_FATAL);
327 
328 	return_code = UU_EXIT_FATAL;
329 }
330 
331 /*
332  * Iterate the properties of a service or an instance when no snapshot
333  * is specified.
334  */
335 static int
336 scf_iter_entity_pgs(scf_iter_t *iter, scf_entityp_t ent)
337 {
338 	int ret = 0;
339 
340 	if (ent.type) {
341 		/*
342 		 * If we are displaying properties for a service,
343 		 * treat it as though it were a composed, current
344 		 * lookup. (implicit cflag) However, if a snapshot
345 		 * was specified, fail.
346 		 */
347 		if (sflag)
348 			die(gettext("Only instances have "
349 			    "snapshots.\n"));
350 		ret = scf_iter_service_pgs(iter, ent.u.svc);
351 	} else {
352 		if (Cflag)
353 			ret = scf_iter_instance_pgs(iter, ent.u.inst);
354 		else
355 			ret = scf_iter_instance_pgs_composed(iter, ent.u.inst,
356 			    NULL);
357 	}
358 	return (ret);
359 }
360 
361 /*
362  * Return a snapshot for the supplied instance and snapshot name.
363  */
364 static scf_snapshot_t *
365 get_snapshot(const scf_instance_t *inst, const char *snapshot)
366 {
367 	scf_snapshot_t *snap = scf_snapshot_create(hndl);
368 
369 	if (snap == NULL)
370 		scfdie();
371 
372 	if (scf_instance_get_snapshot(inst, snapshot, snap) == -1) {
373 		switch (scf_error()) {
374 		case SCF_ERROR_INVALID_ARGUMENT:
375 			die(gettext("Invalid snapshot name.\n"));
376 			/* NOTREACHED */
377 
378 		case SCF_ERROR_NOT_FOUND:
379 			if (sflag == 0) {
380 				scf_snapshot_destroy(snap);
381 				snap = NULL;
382 			} else
383 				die(gettext("No such snapshot.\n"));
384 			break;
385 
386 		default:
387 			scfdie();
388 		}
389 	}
390 
391 	return (snap);
392 }
393 
394 /*
395  * Entity (service or instance): If there are -p options,
396  * display_{pg,prop}() the named property groups and/or properties.  Otherwise
397  * display_pg() all property groups.
398  */
399 static void
400 process_ent(scf_entityp_t ent)
401 {
402 	scf_snapshot_t *snap = NULL;
403 	scf_propertygroup_t *pg;
404 	scf_property_t *prop;
405 	scf_iter_t *iter;
406 	svcprop_prop_node_t *spn;
407 	int ret, err;
408 
409 	if (uu_list_numnodes(prop_list) == 0) {
410 		if (quiet)
411 			return;
412 
413 		if ((pg = scf_pg_create(hndl)) == NULL ||
414 		    (iter = scf_iter_create(hndl)) == NULL)
415 			scfdie();
416 
417 		if (cflag || Cflag || ent.type != ENT_INSTANCE) {
418 			if (scf_iter_entity_pgs(iter, ent) == -1)
419 				scfdie();
420 		} else {
421 			if (snapshot != NULL)
422 				snap = get_snapshot(ent.u.inst, snapshot);
423 
424 			if (scf_iter_instance_pgs_composed(iter, ent.u.inst,
425 			    snap) == -1)
426 				scfdie();
427 			if (snap)
428 				scf_snapshot_destroy(snap);
429 		}
430 
431 		while ((ret = scf_iter_next_pg(iter, pg)) == 1)
432 			display_pg(pg);
433 		if (ret == -1)
434 			scfdie();
435 
436 		/*
437 		 * In normal usage, i.e. against the running snapshot,
438 		 * we must iterate over the current non-persistent
439 		 * pg's.
440 		 */
441 		if (sflag == 0 && snap != NULL) {
442 			scf_iter_reset(iter);
443 			if (scf_iter_instance_pgs_composed(iter, ent.u.inst,
444 			    NULL) == -1)
445 				scfdie();
446 			while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
447 				uint32_t flags;
448 
449 				if (scf_pg_get_flags(pg, &flags) == -1)
450 					scfdie();
451 				if (flags & SCF_PG_FLAG_NONPERSISTENT)
452 					display_pg(pg);
453 			}
454 		}
455 		if (ret == -1)
456 			scfdie();
457 
458 		scf_iter_destroy(iter);
459 		scf_pg_destroy(pg);
460 
461 		return;
462 	}
463 
464 	if ((pg = scf_pg_create(hndl)) == NULL ||
465 	    (prop = scf_property_create(hndl)) == NULL)
466 		scfdie();
467 
468 	if (ent.type == ENT_INSTANCE && snapshot != NULL)
469 		snap = get_snapshot(ent.u.inst, snapshot);
470 
471 	for (spn = uu_list_first(prop_list);
472 	    spn != NULL;
473 	    spn = uu_list_next(prop_list, spn)) {
474 		if (ent.type == ENT_INSTANCE) {
475 			if (Cflag)
476 				ret = scf_instance_get_pg(ent.u.inst,
477 				    spn->spn_comp1, pg);
478 			else
479 				ret = scf_instance_get_pg_composed(ent.u.inst,
480 				    snap, spn->spn_comp1, pg);
481 			err = scf_error();
482 
483 			/*
484 			 * If we didn't find it in the specified snapshot, use
485 			 * the current values if the pg is nonpersistent.
486 			 */
487 			if (ret == -1 && !Cflag &&snap != NULL && err ==
488 			    SCF_ERROR_NOT_FOUND) {
489 				ret = scf_instance_get_pg_composed(
490 				    ent.u.inst, NULL, spn->spn_comp1,
491 				    pg);
492 
493 				if (ret == 0) {
494 					uint32_t flags;
495 
496 					if (scf_pg_get_flags(pg, &flags) == -1)
497 						scfdie();
498 					if ((flags & SCF_PG_FLAG_NONPERSISTENT)
499 					    == 0) {
500 						ret = -1;
501 					}
502 				}
503 			}
504 		} else {
505 			/*
506 			 * If we are displaying properties for a service,
507 			 * treat it as though it were a composed, current
508 			 * lookup. (implicit cflag) However, if a snapshot
509 			 * was specified, fail.
510 			 */
511 			if (sflag)
512 				die(gettext("Only instances have "
513 				    "snapshots.\n"));
514 			ret = scf_entity_get_pg(ent, spn->spn_comp1, pg);
515 			err = scf_error();
516 		}
517 		if (ret == -1) {
518 			if (err != SCF_ERROR_NOT_FOUND)
519 				scfdie();
520 
521 			if (PRINT_NOPROP_ERRORS) {
522 				char *buf;
523 
524 				buf = safe_malloc(max_scf_fmri_length + 1);
525 				if (scf_entity_to_fmri(ent, buf,
526 				    max_scf_fmri_length + 1) == -1)
527 					scfdie();
528 
529 				uu_warn(gettext("Couldn't find property group "
530 				    "`%s' for %s `%s'.\n"), spn->spn_comp1,
531 				    SCF_ENTITY_TYPE_NAME(ent), buf);
532 
533 				free(buf);
534 			}
535 
536 			noprop_common_action();
537 
538 			continue;
539 		}
540 
541 		if (spn->spn_comp2 == NULL) {
542 			if (!quiet)
543 				display_pg(pg);
544 			continue;
545 		}
546 
547 		if (scf_pg_get_property(pg, spn->spn_comp2, prop) == -1) {
548 			if (scf_error() != SCF_ERROR_NOT_FOUND)
549 				scfdie();
550 
551 			if (PRINT_NOPROP_ERRORS) {
552 				char *buf;
553 
554 				buf = safe_malloc(max_scf_fmri_length + 1);
555 				if (scf_entity_to_fmri(ent, buf,
556 				    max_scf_fmri_length + 1) == -1)
557 					scfdie();
558 
559 				/* FMRI syntax knowledge */
560 				uu_warn(gettext("Couldn't find property "
561 				    "`%s/%s' for %s `%s'.\n"), spn->spn_comp1,
562 				    spn->spn_comp2, SCF_ENTITY_TYPE_NAME(ent),
563 				    buf);
564 
565 				free(buf);
566 			}
567 
568 			noprop_common_action();
569 
570 			continue;
571 		}
572 
573 		if (!quiet)
574 			display_prop(pg, prop);
575 	}
576 
577 	scf_property_destroy(prop);
578 	scf_pg_destroy(pg);
579 	if (snap)
580 		scf_snapshot_destroy(snap);
581 }
582 
583 /*
584  * Without -p options, just call display_pg().  Otherwise display_prop() the
585  * named properties of the property group.
586  */
587 static void
588 process_pg(scf_propertygroup_t *pg)
589 {
590 	scf_property_t *prop;
591 	svcprop_prop_node_t *spn;
592 
593 	if (uu_list_first(prop_list) == NULL) {
594 		if (quiet)
595 			return;
596 
597 		display_pg(pg);
598 		return;
599 	}
600 
601 	prop = scf_property_create(hndl);
602 	if (prop == NULL)
603 		scfdie();
604 
605 	for (spn = uu_list_first(prop_list);
606 	    spn != NULL;
607 	    spn = uu_list_next(prop_list, spn)) {
608 		if (spn->spn_comp2 != NULL) {
609 			char *buf;
610 
611 			buf = safe_malloc(max_scf_fmri_length + 1);
612 			if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) ==
613 			    -1)
614 				scfdie();
615 
616 			uu_xdie(UU_EXIT_USAGE, gettext("-p argument `%s/%s' "
617 			    "has too many components for property "
618 			    "group `%s'.\n"), spn->spn_comp1, spn->spn_comp2,
619 			    buf);
620 
621 			free(buf);
622 		}
623 
624 		if (scf_pg_get_property(pg, spn->spn_comp1, prop) == 0) {
625 			if (!quiet)
626 				display_prop(pg, prop);
627 			continue;
628 		}
629 
630 		if (scf_error() != SCF_ERROR_NOT_FOUND)
631 			scfdie();
632 
633 		if (PRINT_NOPROP_ERRORS) {
634 			char *buf;
635 
636 			buf = safe_malloc(max_scf_fmri_length + 1);
637 			if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) ==
638 			    -1)
639 				scfdie();
640 
641 			uu_warn(gettext("Couldn't find property `%s' in "
642 			    "property group `%s'.\n"), spn->spn_comp1, buf);
643 
644 			free(buf);
645 		}
646 
647 		noprop_common_action();
648 	}
649 }
650 
651 /*
652  * If there are -p options, show the error.  Otherwise just call
653  * display_prop().
654  */
655 static void
656 process_prop(scf_propertygroup_t *pg, scf_property_t *prop)
657 {
658 	if (uu_list_first(prop_list) != NULL) {
659 		uu_warn(gettext("The -p option cannot be used with property "
660 		    "operands.\n"));
661 		usage();
662 	}
663 
664 	if (quiet)
665 		return;
666 
667 	display_prop(pg, prop);
668 }
669 
670 /* Decode an operand & dispatch. */
671 /* ARGSUSED */
672 static int
673 process_fmri(void *unused, scf_walkinfo_t *wip)
674 {
675 	scf_entityp_t ent;
676 
677 	/* Multiple matches imply multiple entities. */
678 	if (wip->count > 1)
679 		types = fmris = 1;
680 
681 	if (wip->prop != NULL) {
682 		process_prop(wip->pg, wip->prop);
683 	} else if (wip->pg != NULL) {
684 		process_pg(wip->pg);
685 	} else if (wip->inst != NULL) {
686 		SCF_ENTITY_SET_TO_INSTANCE(ent, wip->inst);
687 		process_ent(ent);
688 	} else {
689 		/* scf_walk_fmri() won't let this happen */
690 		assert(wip->svc != NULL);
691 		SCF_ENTITY_SET_TO_SERVICE(ent, wip->svc);
692 		process_ent(ent);
693 	}
694 
695 	return (0);
696 }
697 
698 static void
699 add_prop(char *property)
700 {
701 	svcprop_prop_node_t *p, *last;
702 	char *slash;
703 
704 	const char * const invalid_component_emsg =
705 	    gettext("Invalid component name `%s'.\n");
706 
707 	/* FMRI syntax knowledge. */
708 	slash = strchr(property, '/');
709 	if (slash != NULL) {
710 		if (strchr(slash + 1, '/') != NULL) {
711 			uu_warn(gettext("-p argument `%s' has too many "
712 			    "components.\n"), property);
713 			usage();
714 		}
715 	}
716 
717 	if (slash != NULL)
718 		*slash = '\0';
719 
720 	p = safe_malloc(sizeof (svcprop_prop_node_t));
721 	uu_list_node_init(p, &p->spn_list_node, prop_pool);
722 
723 	p->spn_comp1 = property;
724 	p->spn_comp2 = (slash == NULL) ? NULL : slash + 1;
725 
726 	if (uu_check_name(p->spn_comp1, UU_NAME_DOMAIN) == -1)
727 		uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp1);
728 	if (p->spn_comp2 != NULL &&
729 	    uu_check_name(p->spn_comp2, UU_NAME_DOMAIN) == -1)
730 		uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp2);
731 
732 	last = uu_list_last(prop_list);
733 	if (last != NULL) {
734 		if ((last->spn_comp2 == NULL) ^ (p->spn_comp2 == NULL)) {
735 			/*
736 			 * The -p options have mixed numbers of components.
737 			 * If they both turn out to be valid, then the
738 			 * single-component ones will specify property groups,
739 			 * so we need to turn on types to keep the output of
740 			 * display_prop() consistent with display_pg().
741 			 */
742 			types = 1;
743 		}
744 	}
745 
746 	(void) uu_list_insert_after(prop_list, NULL, p);
747 }
748 
749 
750 /*
751  * Wait for a property group or property change.
752  *
753  * Extract a pg and optionally a property name from fmri & prop_list.
754  * _scf_pg_wait() for the pg, and display_pg(pg) or display_prop(pg, prop)
755  * when it returns.
756  */
757 /* ARGSUSED */
758 static int
759 do_wait(void *unused, scf_walkinfo_t *wip)
760 {
761 	scf_property_t *prop;
762 	scf_propertygroup_t *lpg, *pg;
763 	const char *propname;
764 	svcprop_prop_node_t *p;
765 
766 	const char *emsg_not_found = gettext("Not found.\n");
767 
768 	if ((lpg = scf_pg_create(hndl)) == NULL ||
769 	    (prop = scf_property_create(hndl)) == NULL)
770 		scfdie();
771 
772 	if (wip->prop != NULL) {
773 		if (uu_list_numnodes(prop_list) > 0)
774 			uu_xdie(UU_EXIT_USAGE, gettext("-p cannot be used with "
775 			    "property FMRIs.\n"));
776 		pg = wip->pg;
777 
778 		assert(strrchr(wip->fmri, '/') != NULL);
779 		propname = strrchr(wip->fmri, '/') + 1;
780 
781 	} else if (wip->pg != NULL) {
782 		p = uu_list_first(prop_list);
783 
784 		if (p != NULL) {
785 			if (p->spn_comp2 != NULL)
786 				uu_xdie(UU_EXIT_USAGE, gettext("-p argument "
787 				    "\"%s/%s\" has too many components for "
788 				    "property group %s.\n"),
789 				    p->spn_comp1, p->spn_comp2, wip->fmri);
790 
791 			propname = p->spn_comp1;
792 
793 			if (scf_pg_get_property(wip->pg, propname, prop) !=
794 			    SCF_SUCCESS) {
795 				switch (scf_error()) {
796 				case SCF_ERROR_INVALID_ARGUMENT:
797 					uu_xdie(UU_EXIT_USAGE,
798 					    gettext("Invalid property name "
799 					    "\"%s\".\n"), propname);
800 
801 					/* NOTREACHED */
802 
803 				case SCF_ERROR_NOT_FOUND:
804 					die(emsg_not_found);
805 
806 					/* NOTREACHED */
807 
808 				default:
809 					scfdie();
810 				}
811 			}
812 		} else {
813 			propname = NULL;
814 		}
815 
816 		pg = wip->pg;
817 
818 	} else if (wip->inst != NULL) {
819 
820 		p = uu_list_first(prop_list);
821 		if (p == NULL)
822 			uu_xdie(UU_EXIT_USAGE,
823 			    gettext("Cannot wait for an instance.\n"));
824 
825 		if (scf_instance_get_pg(wip->inst, p->spn_comp1, lpg) !=
826 		    SCF_SUCCESS) {
827 			switch (scf_error()) {
828 			case SCF_ERROR_INVALID_ARGUMENT:
829 				uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
830 				    "property group name \"%s\".\n"),
831 				    p->spn_comp1);
832 
833 			case SCF_ERROR_NOT_FOUND:
834 				die(emsg_not_found);
835 
836 				/* NOTREACHED */
837 
838 			default:
839 				scfdie();
840 			}
841 		}
842 
843 		propname = p->spn_comp2;
844 
845 		if (propname != NULL) {
846 			if (scf_pg_get_property(lpg, propname, prop) !=
847 			    SCF_SUCCESS) {
848 				switch (scf_error()) {
849 				case SCF_ERROR_INVALID_ARGUMENT:
850 					uu_xdie(UU_EXIT_USAGE,
851 					    gettext("Invalid property name "
852 					    "\"%s\".\n"), propname);
853 
854 				case SCF_ERROR_NOT_FOUND:
855 					die(emsg_not_found);
856 
857 					/* NOTREACHED */
858 
859 				default:
860 					scfdie();
861 				}
862 			}
863 		}
864 
865 		pg = lpg;
866 
867 	} else if (wip->svc != NULL) {
868 
869 		p = uu_list_first(prop_list);
870 		if (p == NULL)
871 			uu_xdie(UU_EXIT_USAGE,
872 			    gettext("Cannot wait for a service.\n"));
873 
874 		if (scf_service_get_pg(wip->svc, p->spn_comp1, lpg) !=
875 		    SCF_SUCCESS) {
876 			switch (scf_error()) {
877 			case SCF_ERROR_INVALID_ARGUMENT:
878 				uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
879 				    "property group name \"%s\".\n"),
880 				    p->spn_comp1);
881 
882 			case SCF_ERROR_NOT_FOUND:
883 				die(emsg_not_found);
884 
885 			default:
886 				scfdie();
887 			}
888 		}
889 
890 		propname = p->spn_comp2;
891 
892 		if (propname != NULL) {
893 			if (scf_pg_get_property(lpg, propname, prop) !=
894 			    SCF_SUCCESS) {
895 				switch (scf_error()) {
896 				case SCF_ERROR_INVALID_ARGUMENT:
897 					uu_xdie(UU_EXIT_USAGE,
898 					    gettext("Invalid property name "
899 					    "\"%s\".\n"), propname);
900 
901 					/* NOTREACHED */
902 
903 				case SCF_ERROR_NOT_FOUND:
904 					die(emsg_not_found);
905 
906 					/* NOTREACHED */
907 
908 				default:
909 					scfdie();
910 				}
911 			}
912 		}
913 
914 		pg = lpg;
915 
916 	} else {
917 		uu_xdie(UU_EXIT_USAGE, gettext("FMRI must specify an entity, "
918 		    "property group, or property.\n"));
919 	}
920 
921 	for (;;) {
922 		int ret;
923 
924 		ret = _scf_pg_wait(pg, -1);
925 		if (ret != SCF_SUCCESS)
926 			scfdie();
927 
928 		ret = scf_pg_update(pg);
929 		if (ret < 0) {
930 			if (scf_error() != SCF_ERROR_DELETED)
931 				scfdie();
932 
933 			die(emsg_not_found);
934 		}
935 		if (ret == SCF_COMPLETE)
936 			break;
937 	}
938 
939 	if (propname != NULL) {
940 		if (scf_pg_get_property(pg, propname, prop) == SCF_SUCCESS) {
941 			if (!quiet)
942 				display_prop(pg, prop);
943 		} else {
944 			if (scf_error() != SCF_ERROR_NOT_FOUND)
945 				scfdie();
946 
947 			if (PRINT_NOPROP_ERRORS)
948 				uu_warn(emsg_not_found);
949 
950 			return_code = UU_EXIT_FATAL;
951 		}
952 	} else {
953 		if (!quiet)
954 			display_pg(pg);
955 	}
956 
957 	scf_property_destroy(prop);
958 	scf_pg_destroy(lpg);
959 
960 	return (0);
961 }
962 
963 /*
964  * These functions replace uu_warn() and uu_die() when the quiet (-q) option is
965  * used, and silently ignore any output.
966  */
967 
968 /*ARGSUSED*/
969 static void
970 quiet_warn(const char *fmt, ...)
971 {
972 	/* Do nothing */
973 }
974 
975 /*ARGSUSED*/
976 static void
977 quiet_die(const char *fmt, ...)
978 {
979 	exit(UU_EXIT_FATAL);
980 }
981 
982 int
983 main(int argc, char *argv[])
984 {
985 	int c;
986 	scf_walk_callback callback;
987 	int flags;
988 	int err;
989 
990 	(void) setlocale(LC_ALL, "");
991 	(void) textdomain(TEXT_DOMAIN);
992 
993 	return_code = UU_EXIT_OK;
994 
995 	(void) uu_setpname(argv[0]);
996 
997 	prop_pool = uu_list_pool_create("properties",
998 	    sizeof (svcprop_prop_node_t),
999 	    offsetof(svcprop_prop_node_t, spn_list_node), NULL, 0);
1000 	if (prop_pool == NULL)
1001 		uu_die("%s\n", uu_strerror(uu_error()));
1002 
1003 	prop_list = uu_list_create(prop_pool, NULL, 0);
1004 
1005 	while ((c = getopt(argc, argv, "Ccfp:qs:tvw")) != -1) {
1006 		switch (c) {
1007 		case 'C':
1008 			if (cflag || sflag || wait)
1009 				usage();	/* Not with -c, -s or -w */
1010 			Cflag++;
1011 			snapshot = NULL;
1012 			break;
1013 
1014 		case 'c':
1015 			if (Cflag || sflag || wait)
1016 				usage();	/* Not with -C, -s or -w */
1017 			cflag++;
1018 			snapshot = NULL;
1019 			break;
1020 
1021 		case 'f':
1022 			types = 1;
1023 			fmris = 1;
1024 			break;
1025 
1026 		case 'p':
1027 			add_prop(optarg);
1028 			break;
1029 
1030 		case 'q':
1031 			quiet = 1;
1032 			warn = quiet_warn;
1033 			die = quiet_die;
1034 			break;
1035 
1036 		case 's':
1037 			if (Cflag || cflag || wait)
1038 				usage();	/* Not with -C, -c or -w */
1039 			snapshot = optarg;
1040 			sflag++;
1041 			break;
1042 
1043 		case 't':
1044 			types = 1;
1045 			break;
1046 
1047 		case 'v':
1048 			verbose = 1;
1049 			break;
1050 
1051 		case 'w':
1052 			if (Cflag || cflag || sflag)
1053 				usage();	/* Not with -C, -c or -s */
1054 			wait = 1;
1055 			break;
1056 
1057 		case '?':
1058 			switch (optopt) {
1059 			case 'p':
1060 				usage();
1061 
1062 			default:
1063 				break;
1064 			}
1065 
1066 			/* FALLTHROUGH */
1067 
1068 		default:
1069 			usage();
1070 		}
1071 	}
1072 
1073 	if (optind == argc)
1074 		usage();
1075 
1076 	max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1077 	max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1078 	max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
1079 	if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
1080 	    max_scf_fmri_length == -1)
1081 		scfdie();
1082 
1083 	hndl = scf_handle_create(SCF_VERSION);
1084 	if (hndl == NULL)
1085 		scfdie();
1086 	if (scf_handle_bind(hndl) == -1)
1087 		die(gettext("Could not connect to configuration repository: "
1088 		    "%s.\n"), scf_strerror(scf_error()));
1089 
1090 	flags = SCF_WALK_PROPERTY | SCF_WALK_SERVICE | SCF_WALK_EXPLICIT;
1091 
1092 	if (wait) {
1093 		if (uu_list_numnodes(prop_list) > 1)
1094 			usage();
1095 
1096 		if (argc - optind > 1)
1097 			usage();
1098 
1099 		callback = do_wait;
1100 
1101 	} else {
1102 		callback = process_fmri;
1103 
1104 		flags |= SCF_WALK_MULTIPLE;
1105 	}
1106 
1107 	if ((err = scf_walk_fmri(hndl, argc - optind, argv + optind, flags,
1108 	    callback, NULL, &return_code, warn)) != 0) {
1109 		warn(gettext("failed to iterate over instances: %s\n"),
1110 		    scf_strerror(err));
1111 		return_code = UU_EXIT_FATAL;
1112 	}
1113 
1114 	scf_handle_destroy(hndl);
1115 
1116 	return (return_code);
1117 }
1118