Home | History | Annotate | Download | only in svcprop
      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