xref: /illumos-gate/usr/src/cmd/svc/svcprop/svcprop.c (revision 44bf619d)
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  */
55 static void (*warn)(const char *, ...) = uu_warn;
56 static __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  */
62 typedef 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  */
91 typedef 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 
97 static uu_list_pool_t	*prop_pool;
98 static uu_list_t	*prop_list;
99 
100 static scf_handle_t *hndl;
101 static ssize_t max_scf_name_length;
102 static ssize_t max_scf_value_length;
103 static ssize_t max_scf_fmri_length;
104 
105 /* Options */
106 static int quiet = 0;			/* No output. Nothing found, exit(1) */
107 static int types = 0;			/* Display types of properties. */
108 static int verbose = 0;			/* Print not found errors to stderr. */
109 static int fmris = 0;			/* Display full FMRIs for properties. */
110 static int wait = 0;			/* Wait mode. */
111 static char *snapshot = "running";	/* Snapshot to use. */
112 static int Cflag = 0;			/* C option supplied */
113 static int cflag = 0;			/* c option supplied */
114 static int sflag = 0;			/* s option supplied */
115 static 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  */
123 static void
scfdie(void)124 scfdie(void)
125 {
126 	die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
127 	    scf_strerror(scf_error()));
128 }
129 
130 static void *
safe_malloc(size_t sz)131 safe_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 
142 static void
usage(void)143 usage(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  */
160 static char *
quote_for_shell(const char * str)161 quote_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 
196 static void
print_value(scf_value_t * val)197 print_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  */
223 static void
display_prop(scf_propertygroup_t * pg,scf_property_t * prop)224 display_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  */
306 static void
display_pg(scf_propertygroup_t * pg)307 display_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  */
334 static void
noprop_common_action()335 noprop_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  */
348 static int
scf_iter_entity_pgs(scf_iter_t * iter,scf_entityp_t ent)349 scf_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  */
377 static scf_snapshot_t *
get_snapshot(const scf_instance_t * inst,const char * snapshot)378 get_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  */
412 static void
process_ent(scf_entityp_t ent)413 process_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  */
600 static void
process_pg(scf_propertygroup_t * pg)601 process_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  */
666 static void
process_prop(scf_propertygroup_t * pg,scf_property_t * prop)667 process_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 */
683 static int
process_fmri(void * unused,scf_walkinfo_t * wip)684 process_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 
709 static void
add_prop(char * property)710 add_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 */
769 static int
do_wait(void * unused,scf_walkinfo_t * wip)770 do_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*/
975 static void
quiet_warn(const char * fmt,...)976 quiet_warn(const char *fmt, ...)
977 {
978 	/* Do nothing */
979 }
980 
981 /*ARGSUSED*/
982 static __NORETURN void
quiet_die(const char * fmt,...)983 quiet_die(const char *fmt, ...)
984 {
985 	exit(UU_EXIT_FATAL);
986 }
987 
988 int
main(int argc,char * argv[])989 main(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