xref: /illumos-gate/usr/src/lib/libscf/common/scf_tmpl.c (revision 28b6fd27d5ff75fe6fdeb119a21575b0652a7e70)
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  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2012 Milan Jurik. All rights reserved.
24  */
25 
26 /*
27  * scf_tmpl.c
28  *
29  * This file implements the bulk of the libscf templates interfaces.
30  * Templates describe metadata about a service or instance in general,
31  * and individual configuration properties on those services and instances.
32  * Human-consumable descriptions can be provided, along with definitions
33  * of valid configuration.  See service_bundle.dtd.1 for XML definitions
34  * of templates, and the svccfg code for information on how those definitions
35  * are translated into the repository.
36  *
37  * The main data structures are scf_pg_tmpl and scf_prop_tmpl.  These
38  * are allocated by the callers through scf_tmpl_[pg|prop]_create(), and
39  * destroyed with scf_tmpl_[pg|prop]_destroy().  They are populated by
40  * scf_tmpl_get_by_pg_name(), scf_tmpl_get_by_pg(), and
41  * scf_tmpl_get_by_prop().  They also store the iterator state for
42  * scf_tmpl_iter_pgs() and scf_tmpl_iter_props().
43  *
44  * These data structures are then consumed by other functions to
45  * gather information about the template (e.g. name, description,
46  * choices, constraints, etc.).
47  *
48  * scf_tmpl_validate_fmri() does instance validation against template
49  * data, and populates a set of template errors which can be explored using
50  * the scf_tmpl_next_error() and the scf_tmpl_error*() suite of functions.
51  *
52  * The main data structures for template errors are scf_tmpl_errors,
53  * defined in this file, and scf_tmpl_error, defined in libscf_priv.h.
54  * scf_tmpl_error is shared with svccfg to offer common printing
55  * of error messages between libscf and svccfg.
56  *
57  * General convenience functions are towards the top of this file,
58  * followed by pg and prop template discovery functions, followed
59  * by functions which gather information about the discovered
60  * template.  Validation and error functions are at the end of this file.
61  */
62 
63 #include "lowlevel_impl.h"
64 #include "libscf_impl.h"
65 #include <assert.h>
66 #include <errno.h>
67 #include <libintl.h>
68 #include <stdlib.h>
69 #include <stdio.h>
70 #include <strings.h>
71 #include <locale.h>
72 #include <ctype.h>
73 #include <inttypes.h>
74 
75 #define	SCF_TMPL_PG_COMMON_NAME_C	"common_name_C"
76 
77 #define	SCF__TMPL_ITER_NONE		0
78 #define	SCF__TMPL_ITER_INST		1
79 #define	SCF__TMPL_ITER_RESTARTER	2
80 #define	SCF__TMPL_ITER_GLOBAL		3
81 
82 #define	SCF_TMPL_PG_NT		0
83 #define	SCF_TMPL_PG_N		1
84 #define	SCF_TMPL_PG_T		2
85 #define	SCF_TMPL_PG_WILD	3
86 
87 struct scf_pg_tmpl {
88 	int pt_populated;
89 	scf_handle_t *pt_h;
90 	scf_propertygroup_t *pt_pg;
91 	scf_service_t *pt_orig_svc;
92 	scf_service_t *pt_svc;
93 	scf_instance_t *pt_orig_inst;
94 	scf_instance_t *pt_inst;
95 	scf_snapshot_t *pt_snap;
96 	int pt_is_iter;
97 	scf_iter_t *pt_iter;
98 	int pt_iter_last;
99 };
100 
101 #define	SCF_WALK_ERROR		-1
102 #define	SCF_WALK_NEXT		0
103 #define	SCF_WALK_DONE		1
104 
105 struct pg_tmpl_walk {
106 	const char *pw_snapname;
107 	const char *pw_pgname;
108 	const char *pw_pgtype;
109 	scf_instance_t *pw_inst;
110 	scf_service_t *pw_svc;
111 	scf_snapshot_t *pw_snap;
112 	scf_propertygroup_t *pw_pg;
113 	const char *pw_target;
114 	char *pw_tmpl_pgname;
115 };
116 
117 typedef struct pg_tmpl_walk pg_tmpl_walk_t;
118 
119 typedef int walk_template_inst_func_t(scf_service_t *_svc,
120     scf_instance_t *_inst, pg_tmpl_walk_t *p);
121 
122 struct scf_prop_tmpl {
123 	int prt_populated;
124 	scf_handle_t *prt_h;
125 	scf_pg_tmpl_t *prt_t;
126 	scf_propertygroup_t *prt_pg;
127 	char *prt_pg_name;
128 	scf_iter_t *prt_iter;
129 };
130 
131 /*
132  * Common server errors are usually passed back to the caller.  This
133  * array defines them centrally so that they don't need to be enumerated
134  * in every libscf call.
135  */
136 static const scf_error_t errors_server[] = {
137 	SCF_ERROR_BACKEND_ACCESS,
138 	SCF_ERROR_CONNECTION_BROKEN,
139 	SCF_ERROR_DELETED,
140 	SCF_ERROR_HANDLE_DESTROYED,
141 	SCF_ERROR_INTERNAL,
142 	SCF_ERROR_NO_MEMORY,
143 	SCF_ERROR_NO_RESOURCES,
144 	SCF_ERROR_NOT_BOUND,
145 	SCF_ERROR_PERMISSION_DENIED,
146 	0
147 	};
148 
149 /*
150  * int ismember()
151  *
152  * Returns 1 if the supplied error is a member of the error array, 0
153  * if it is not.
154  */
155 int
156 ismember(const scf_error_t error, const scf_error_t error_array[])
157 {
158 	int i;
159 
160 	for (i = 0; error_array[i] != 0; ++i) {
161 		if (error == error_array[i])
162 			return (1);
163 	}
164 
165 	return (0);
166 }
167 
168 /*
169  * char *_scf_tmpl_get_fmri()
170  *
171  * Given a pg_tmpl, returns the FMRI of the service or instance that
172  * template describes.  The allocated string must be freed with free().
173  *
174  * On failure, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
175  * _DELETED, or _NO_MEMORY.
176  */
177 static char *
178 _scf_tmpl_get_fmri(const scf_pg_tmpl_t *t)
179 {
180 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
181 	int r;
182 	char *buf = malloc(sz);
183 
184 	assert(t->pt_svc != NULL || t->pt_inst != NULL);
185 	assert(t->pt_svc == NULL || t->pt_inst == NULL);
186 
187 	if (buf == NULL) {
188 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
189 		return (buf);
190 	}
191 
192 	if (t->pt_inst != NULL)
193 		r = scf_instance_to_fmri(t->pt_inst, buf, sz);
194 	else
195 		r = scf_service_to_fmri(t->pt_svc, buf, sz);
196 
197 	if (r == -1) {
198 		if (ismember(scf_error(), errors_server)) {
199 			free(buf);
200 			buf = NULL;
201 		} else {
202 			assert(0);
203 			abort();
204 		}
205 	}
206 
207 	return (buf);
208 }
209 
210 /*
211  * char *_scf_get_pg_type()
212  *
213  * Given a propertygroup, returns an allocated string containing the
214  * type.  The string must be freed with free().
215  *
216  * On failure, returns NULL and sets scf_error() to: _CONNECTION_BROKEN,
217  * _DELETED, or _NO_MEMORY.
218  */
219 static char *
220 _scf_get_pg_type(scf_propertygroup_t *pg)
221 {
222 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
223 	char *buf = malloc(sz);
224 
225 	if (buf == NULL) {
226 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
227 	} else if (scf_pg_get_type(pg, buf, sz) == -1) {
228 		if (ismember(scf_error(), errors_server)) {
229 			free(buf);
230 			buf = NULL;
231 		} else {
232 			assert(0);
233 			abort();
234 		}
235 	}
236 
237 	return (buf);
238 }
239 
240 /*
241  * char *_scf_get_prop_name()
242  *
243  * Given a property, returns the name in an allocated string.  The string must
244  * be freed with free().
245  *
246  * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
247  * _DELETED, or _NO_MEMORY.
248  */
249 static char *
250 _scf_get_prop_name(scf_property_t *prop)
251 {
252 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
253 	char *buf = malloc(sz);
254 
255 	if (buf == NULL) {
256 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
257 	} else if (scf_property_get_name(prop, buf, sz) == -1) {
258 		if (ismember(scf_error(), errors_server)) {
259 			free(buf);
260 			buf = NULL;
261 		} else {
262 			assert(0);
263 			abort();
264 		}
265 	}
266 
267 	return (buf);
268 }
269 
270 /*
271  * char *_scf_get_prop_type()
272  *
273  * Given a property, returns the type in an allocated string.  The string must
274  * be freed with free().
275  *
276  * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
277  * _DELETED, or _NO_MEMORY.
278  */
279 static char *
280 _scf_get_prop_type(scf_property_t *prop)
281 {
282 	scf_type_t type;
283 	char *ret;
284 
285 	if (scf_property_type(prop, &type) == -1) {
286 		if (ismember(scf_error(), errors_server)) {
287 			return (NULL);
288 		} else {
289 			assert(0);
290 			abort();
291 		}
292 	}
293 
294 	ret = strdup(scf_type_to_string(type));
295 	if (ret == NULL)
296 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
297 
298 	return (ret);
299 }
300 
301 /*
302  * int _read_single_value_from_pg()
303  *
304  * Reads a single value from the pg and property name specified.  On success,
305  * returns an allocated value that must be freed.
306  *
307  * Returns -1 on failure, sets scf_error() to:
308  *  SCF_ERROR_BACKEND_ACCESS
309  *  SCF_ERROR_CONNECTION_BROKEN
310  *  SCF_ERROR_CONSTRAINT_VIOLATED
311  *    Property has more than one value associated with it.
312  *  SCF_ERROR_DELETED
313  *  SCF_ERROR_HANDLE_DESTROYED
314  *  SCF_ERROR_INTERNAL
315  *  SCF_ERROR_INVALID_ARGUMENT
316  *    prop_name not a valid property name.
317  *  SCF_ERROR_NO_MEMORY
318  *  SCF_ERROR_NO_RESOURCES
319  *  SCF_ERROR_NOT_BOUND
320  *  SCF_ERROR_NOT_FOUND
321  *    Property doesn't exist or exists and has no value.
322  *  SCF_ERROR_NOT_SET
323  *    Property group specified by pg is not set.
324  *  SCF_ERROR_PERMISSION_DENIED
325  */
326 static int
327 _read_single_value_from_pg(scf_propertygroup_t *pg, const char *prop_name,
328     scf_value_t **val)
329 {
330 	scf_handle_t *h;
331 	scf_property_t *prop;
332 	int ret = 0;
333 
334 	assert(val != NULL);
335 	if ((h = scf_pg_handle(pg)) == NULL) {
336 		assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
337 		return (-1);
338 	}
339 
340 	prop = scf_property_create(h);
341 	*val = scf_value_create(h);
342 
343 	if (prop == NULL || *val == NULL) {
344 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
345 		goto read_single_value_from_pg_fail;
346 	}
347 
348 	if (scf_pg_get_property(pg, prop_name, prop) != 0) {
349 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
350 		goto read_single_value_from_pg_fail;
351 	}
352 
353 	if (scf_property_get_value(prop, *val) == -1) {
354 		assert(scf_error() != SCF_ERROR_NOT_SET);
355 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
356 		goto read_single_value_from_pg_fail;
357 	}
358 
359 	goto read_single_value_from_pg_done;
360 
361 read_single_value_from_pg_fail:
362 	scf_value_destroy(*val);
363 	*val = NULL;
364 	ret = -1;
365 
366 read_single_value_from_pg_done:
367 	scf_property_destroy(prop);
368 	return (ret);
369 }
370 
371 /*
372  * char *_scf_read_single_astring_from_pg()
373  *
374  * Reads an astring from the pg and property name specified.  On success,
375  * returns an allocated string.  The string must be freed with free().
376  *
377  * Returns NULL on failure, sets scf_error() to:
378  *   SCF_ERROR_BACKEND_ACCESS
379  *   SCF_ERROR_CONNECTION_BROKEN
380  *   SCF_ERROR_CONSTRAINT_VIOLATED
381  *     Property has more than one value associated with it.
382  *   SCF_ERROR_DELETED
383  *   SCF_ERROR_HANDLE_DESTROYED
384  *   SCF_ERROR_INTERNAL
385  *   SCF_ERROR_INVALID_ARGUMENT
386  *     prop_name not a valid property name.
387  *   SCF_ERROR_NO_MEMORY
388  *   SCF_ERROR_NO_RESOURCES
389  *   SCF_ERROR_NOT_BOUND
390  *   SCF_ERROR_NOT_FOUND
391  *     Property doesn't exist or exists and has no value.
392  *   SCF_ERROR_NOT_SET
393  *     The property group specified by pg is not set.
394  *   SCF_ERROR_PERMISSION_DENIED
395  *   SCF_ERROR_TYPE_MISMATCH
396  */
397 char *
398 _scf_read_single_astring_from_pg(scf_propertygroup_t *pg, const char *prop_name)
399 {
400 	scf_value_t *val;
401 	char *ret = NULL;
402 	ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
403 
404 	assert(rsize != 0);
405 	if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
406 		return (NULL);
407 
408 	ret = malloc(rsize);
409 	if (ret == NULL) {
410 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
411 		goto cleanup;
412 	}
413 
414 	if (scf_value_get_astring(val, ret, rsize) < 0) {
415 		assert(scf_error() != SCF_ERROR_NOT_SET);
416 		free(ret);
417 		ret = NULL;
418 	}
419 
420 cleanup:
421 	scf_value_destroy(val);
422 	return (ret);
423 }
424 
425 /*
426  * char *_scf_read_tmpl_prop_type_as_string()
427  *
428  * Reads the property type and returns it as an allocated string.  The string
429  * must be freed with free().
430  *
431  * Returns NULL on failure, sets scf_error() to _BACKEND_ACCESS,
432  * _CONNECTION_BROKEN, _DELETED, _HANDLE_DESTROYED, _INTERNAL, _NO_MEMORY,
433  * _NO_RESOURCES, _NOT_BOUND, _PERMISSION_DENIED, or _TEMPLATE_INVALID.
434  */
435 char *
436 _scf_read_tmpl_prop_type_as_string(const scf_prop_tmpl_t *pt)
437 {
438 	char *type;
439 
440 	type = _scf_read_single_astring_from_pg(pt->prt_pg,
441 	    SCF_PROPERTY_TM_TYPE);
442 	if (type == NULL) {
443 		if (ismember(scf_error(), errors_server)) {
444 			return (NULL);
445 		} else switch (scf_error()) {
446 		case SCF_ERROR_CONSTRAINT_VIOLATED:
447 		case SCF_ERROR_NOT_FOUND:
448 		case SCF_ERROR_TYPE_MISMATCH:
449 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
450 			return (NULL);
451 
452 		case SCF_ERROR_INVALID_ARGUMENT:
453 		case SCF_ERROR_NOT_SET:
454 		default:
455 			assert(0);
456 			abort();
457 		}
458 	}
459 
460 	return (type);
461 }
462 
463 /*
464  * int _read_single_boolean_from_pg()
465  *
466  * Reads a boolean from the pg and property name specified.
467  *
468  * Returns -1 on failure, sets scf_error() to:
469  *   SCF_ERROR_BACKEND_ACCESS
470  *   SCF_ERROR_CONNECTION_BROKEN
471  *   SCF_ERROR_CONSTRAINT_VIOLATED
472  *     Property has more than one value associated with it.
473  *   SCF_ERROR_DELETED
474  *   SCF_ERROR_HANDLE_DESTROYED
475  *   SCF_ERROR_INTERNAL
476  *   SCF_ERROR_INVALID_ARGUMENT
477  *     prop_name is not a valid property name.
478  *   SCF_ERROR_NO_MEMORY
479  *   SCF_ERROR_NO_RESOURCES
480  *   SCF_ERROR_NOT_BOUND
481  *   SCF_ERROR_NOT_FOUND
482  *     Property doesn't exist or exists and has no value.
483  *   SCF_ERROR_NOT_SET
484  *     The property group specified by pg is not set.
485  *   SCF_ERROR_PERMISSION_DENIED
486  *   SCF_ERROR_TYPE_MISMATCH
487  */
488 static int
489 _read_single_boolean_from_pg(scf_propertygroup_t *pg, const char *prop_name,
490     uint8_t *bool)
491 {
492 	scf_value_t *val;
493 	int ret = 0;
494 
495 	if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
496 		return (-1);
497 
498 	if (scf_value_get_boolean(val, bool) < 0) {
499 		assert(scf_error() != SCF_ERROR_NOT_SET);
500 		ret = -1;
501 	}
502 
503 	scf_value_destroy(val);
504 	return (ret);
505 }
506 
507 /*
508  * static char ** _append_astrings_values()
509  *
510  * This function reads the values from the property prop_name in pg and
511  * appends to an existing scf_values_t *vals.  vals may be empty, but
512  * must exist.  The function skips over zero-length and duplicate values.
513  *
514  * Returns NULL on failure, sets scf_error() to:
515  *   SCF_ERROR_BACKEND_ACCESS
516  *   SCF_ERROR_CONNECTION_BROKEN
517  *   SCF_ERROR_DELETED
518  *   SCF_ERROR_HANDLE_DESTROYED
519  *   SCF_ERROR_INTERNAL
520  *   SCF_ERROR_INVALID_ARGUMENT
521  *     prop_name is not a valid property name.
522  *   SCF_ERROR_NO_MEMORY
523  *   SCF_ERROR_NO_RESOURCES
524  *   SCF_ERROR_NOT_BOUND
525  *   SCF_ERROR_NOT_FOUND
526  *   SCF_ERROR_NOT_SET
527  *   SCF_ERROR_PERMISSION_DENIED
528  *   SCF_ERROR_TYPE_MISMATCH
529  */
530 static char **
531 _append_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
532     scf_values_t *vals)
533 {
534 	scf_handle_t *h;
535 	scf_property_t *prop;
536 	scf_value_t *val;
537 	scf_iter_t *iter;
538 	ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
539 	int err, count, cursz, i;
540 
541 	assert(vals != NULL);
542 	assert(vals->value_type == SCF_TYPE_ASTRING);
543 	assert(vals->reserved == NULL);
544 	count = vals->value_count;
545 	if (count == 0) {
546 		cursz = 8;
547 		vals->values.v_astring = calloc(cursz, sizeof (char *));
548 		if (vals->values.v_astring == NULL) {
549 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
550 			return (NULL);
551 		}
552 	} else {
553 		/*
554 		 * The array may be bigger, but it is irrelevant since
555 		 * we will always re-allocate a new one.
556 		 */
557 		cursz = count;
558 	}
559 
560 	if ((h = scf_pg_handle(pg)) == NULL) {
561 		assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
562 		return (NULL);
563 	}
564 
565 	prop = scf_property_create(h);
566 	val = scf_value_create(h);
567 	iter = scf_iter_create(h);
568 
569 	if (prop == NULL || val == NULL || iter == NULL) {
570 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
571 		goto append_single_astring_from_pg_fail;
572 	}
573 
574 	if (scf_pg_get_property(pg, prop_name, prop) != 0) {
575 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
576 		goto append_single_astring_from_pg_fail;
577 	}
578 
579 	if (scf_iter_property_values(iter, prop) != 0) {
580 		assert(scf_error() != SCF_ERROR_NOT_SET);
581 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
582 		goto append_single_astring_from_pg_fail;
583 	}
584 
585 	while ((err = scf_iter_next_value(iter, val)) == 1) {
586 		int flag;
587 		int r;
588 
589 		if (count + 1 >= cursz) {
590 			void *aux;
591 
592 			cursz *= 2;
593 			if ((aux = calloc(cursz, sizeof (char *))) == NULL) {
594 				(void) scf_set_error(SCF_ERROR_NO_MEMORY);
595 				goto append_single_astring_from_pg_fail;
596 			}
597 			(void) memcpy(aux, vals->values.v_astring,
598 			    count * sizeof (char *));
599 			free(vals->values.v_astring);
600 			vals->values.v_astring = aux;
601 		}
602 
603 		vals->values.v_astring[count] = malloc(rsize);
604 		if (vals->values.v_astring[count] == NULL) {
605 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
606 			goto append_single_astring_from_pg_fail;
607 		}
608 
609 		if ((r = scf_value_get_astring(val,
610 		    vals->values.v_astring[count], rsize)) <= 0) {
611 			/* discard zero length strings */
612 			if (r == 0) {
613 				free(vals->values.v_astring[count]);
614 				continue;
615 			}
616 			assert(scf_error() != SCF_ERROR_NOT_SET);
617 			goto append_single_astring_from_pg_fail;
618 		}
619 		for (i = 0, flag = 0; i < count; ++i) {
620 			/* find  and discard duplicates */
621 			if (strncmp(vals->values.v_astring[i],
622 			    vals->values.v_astring[count], rsize) == 0) {
623 				free(vals->values.v_astring[count]);
624 				flag = 1;
625 				break;
626 			}
627 		}
628 		if (flag == 1)
629 			continue;
630 
631 		count++;
632 	}
633 
634 	vals->value_count = count;
635 
636 	if (err != 0) {
637 		assert(scf_error() != SCF_ERROR_NOT_SET);
638 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
639 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
640 		goto append_single_astring_from_pg_fail;
641 	} else {
642 		vals->values_as_strings = vals->values.v_astring;
643 	}
644 
645 	goto append_single_astring_from_pg_done;
646 
647 append_single_astring_from_pg_fail:
648 	for (i = 0; i <= count; ++i) {
649 		if (vals->values.v_astring[i] != NULL)
650 			free(vals->values.v_astring[i]);
651 		vals->values.v_astring[i] = NULL;
652 	}
653 	free(vals->values.v_astring);
654 	vals->values.v_astring = NULL;
655 	vals->value_count = 0;
656 
657 append_single_astring_from_pg_done:
658 	scf_iter_destroy(iter);
659 	scf_property_destroy(prop);
660 	scf_value_destroy(val);
661 	return (vals->values.v_astring);
662 }
663 
664 /*
665  * Returns NULL on failure, sets scf_error() to:
666  *   SCF_ERROR_BACKEND_ACCESS
667  *   SCF_ERROR_CONNECTION_BROKEN
668  *   SCF_ERROR_DELETED
669  *   SCF_ERROR_HANDLE_DESTROYED
670  *   SCF_ERROR_INTERNAL
671  *   SCF_ERROR_INVALID_ARGUMENT
672  *     prop_name is not a valid property name.
673  *   SCF_ERROR_NO_MEMORY
674  *   SCF_ERROR_NO_RESOURCES
675  *   SCF_ERROR_NOT_BOUND
676  *   SCF_ERROR_NOT_FOUND
677  *   SCF_ERROR_NOT_SET
678  *   SCF_ERROR_PERMISSION_DENIED
679  *   SCF_ERROR_TYPE_MISMATCH
680  */
681 static char **
682 _read_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
683     scf_values_t *vals)
684 {
685 	assert(vals != NULL);
686 	vals->value_count = 0;
687 	vals->value_type = SCF_TYPE_ASTRING;
688 	vals->reserved = NULL;
689 	return (_append_astrings_values(pg, prop_name, vals));
690 }
691 
692 void
693 _scf_sanitize_locale(char *locale)
694 {
695 	for (; *locale != '\0'; locale++)
696 		if (!isalnum(*locale) && *locale != '_')
697 			*locale = '_';
698 }
699 
700 /*
701  * The returned string needs to be freed by the caller
702  * Returns NULL on failure.  Sets scf_error() to:
703  *   SCF_ERROR_NO_MEMORY
704  *   SCF_ERROR_INVALID_ARGUMENT
705  *     Name isn't short enough to add the locale to.
706  */
707 static char *
708 _add_locale_to_name(const char *name, const char *locale)
709 {
710 	char *lname = NULL;
711 	ssize_t lsz;
712 	char *loc;
713 
714 	if (locale == NULL)
715 		locale = setlocale(LC_MESSAGES, NULL);
716 	loc = strdup(locale);
717 	if (loc == NULL) {
718 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
719 		return (NULL);
720 	} else {
721 		_scf_sanitize_locale(loc);
722 	}
723 
724 	lsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
725 	lname = malloc(lsz);
726 	if (lname == NULL) {
727 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
728 		goto cleanup;
729 	}
730 
731 	(void) strlcpy(lname, name, lsz);
732 	if (strlcat(lname, loc, lsz) >= lsz) {
733 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
734 		free(lname);
735 		lname = NULL;
736 	}
737 cleanup:
738 	free(loc);
739 
740 	return (lname);
741 }
742 
743 /*
744  * char *_tmpl_pg_name(pg, type, use_type)
745  *
746  * pg and type can both be NULL.  Returns the name of the most specific
747  * template property group name based on the inputs.
748  * If use_type is set and pg is not NULL, a property group name for a
749  * property group template that has type defined is returned, even if no
750  * type is provided.
751  *
752  * Returns NULL on failure and sets scf_error() to:
753  *   SCF_ERROR_INVALID_ARGUMENT
754  *     can't combine the arguments and get a reasonable length name
755  *   SCF_ERROR_NO_MEMORY
756  *
757  */
758 static char *
759 _tmpl_pg_name(const char *pg, const char *type, int use_type)
760 {
761 	char *name;
762 	ssize_t limit, size = 0;
763 
764 	limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
765 	name = malloc(limit);
766 	if (name == NULL) {
767 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
768 		return (NULL);
769 	}
770 
771 	if (pg == NULL && type == NULL) {
772 		if (strlcpy(name, SCF_PG_TM_PG_PATTERN_PREFIX, limit) >=
773 		    limit) {
774 			assert(0);
775 			abort();
776 		}
777 		return (name);
778 	} else if (pg != NULL && type != NULL) {
779 		size = snprintf(name, limit, "%s%s",
780 		    SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
781 	} else if (pg != NULL && type == NULL && use_type == 1) {
782 		size = snprintf(name, limit, "%s%s",
783 		    SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
784 	} else if (pg != NULL && type == NULL) {
785 		size = snprintf(name, limit, "%s%s",
786 		    SCF_PG_TM_PG_PATTERN_N_PREFIX, pg);
787 	} else if (type != NULL && pg == NULL) {
788 		size = snprintf(name, limit, "%s%s",
789 		    SCF_PG_TM_PG_PATTERN_T_PREFIX, type);
790 	} else {
791 		assert(0);
792 		abort();
793 	}
794 
795 	if (size >= limit) {
796 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
797 		free(name);
798 		return (NULL);
799 	} else {
800 		return (name);
801 	}
802 }
803 
804 /*
805  * _scf_get_pg_name()
806  * Gets the name of the supplied property group.  On success, returns an
807  * allocated string.  The string must be freed by free().
808  *
809  * Returns NULL on failure and sets scf_error() to _CONNECTION_BROKEN,
810  * _DELETED, or _NO_MEMORY.
811  */
812 static char *
813 _scf_get_pg_name(scf_propertygroup_t *pg)
814 {
815 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
816 	char *buf = malloc(sz);
817 
818 	if (buf == NULL) {
819 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
820 	} else if (scf_pg_get_name(pg, buf, sz) == -1) {
821 		if (ismember(scf_error(), errors_server)) {
822 			free(buf);
823 			buf = NULL;
824 		} else {
825 			assert(0);
826 			abort();
827 		}
828 	}
829 
830 	return (buf);
831 }
832 
833 /*
834  * char *_tmpl_prop_name()
835  *
836  * Returns the name of the property template prop (which is the name of
837  * the property template property group) in the property group
838  * template t. Returns NULL on failure and sets scf_error() to:
839  *   SCF_ERROR_CONNECTION_BROKEN
840  *   SCF_ERROR_DELETED
841  *   SCF_ERROR_INVALID_ARGUMENT
842  *     can't combine the arguments and get a reasonable length name
843  *   SCF_ERROR_NO_MEMORY
844  */
845 static char *
846 _tmpl_prop_name(const char *prop, scf_pg_tmpl_t *t)
847 {
848 	char *name = NULL, *pg_name = NULL;
849 	size_t prefix_size;
850 	ssize_t limit, size = 0;
851 
852 	assert(prop != NULL);
853 	assert(t->pt_pg != NULL);
854 
855 	limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
856 	name = malloc(limit);
857 	if (name == NULL) {
858 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
859 		return (NULL);
860 	}
861 
862 	if ((pg_name = _scf_get_pg_name(t->pt_pg)) == NULL) {
863 		free(name);
864 		return (NULL);
865 	}
866 
867 	prefix_size = strlen(SCF_PG_TM_PG_PAT_BASE);
868 	if (strncmp(pg_name, SCF_PG_TM_PG_PAT_BASE, prefix_size) != 0) {
869 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
870 		free(name);
871 		free(pg_name);
872 		return (NULL);
873 	}
874 
875 	size = snprintf(name, limit, "%s%s_%s", SCF_PG_TM_PROP_PATTERN_PREFIX,
876 	    pg_name + prefix_size, prop);
877 
878 	if (size >= limit) {
879 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
880 		free(name);
881 		free(pg_name);
882 		return (NULL);
883 	} else {
884 		free(pg_name);
885 		return (name);
886 	}
887 }
888 
889 /*
890  *  int _get_snapshot()
891  *
892  *  Gets the specified snapshot.  If "snapshot" isn't defined, use the
893  *  running snapshot.  If the snapshot isn't found, that may or may
894  *  not be an error depending on the caller.  Return 0 in that case,
895  *  but leave scf_error() set to SCF_ERROR_NOT_FOUND.  On all other
896  *  errors, set scf_error() to:
897  *   SCF_ERROR_BACKEND_ACCESS
898  *   SCF_ERROR_CONNECTION_BROKEN
899  *   SCF_ERROR_DELETED
900  *   SCF_ERR_HANDLE_DESTROYED
901  *   SCF_ERROR_INTERNAL
902  *   SCF_ERROR_INVALID_ARGUMENT
903  *     The handle argument is NULL, or snaphot is not a valid snapshot name
904  *   SCF_ERROR_NO_MEMORY
905  *   SCF_ERROR_NO_RESOURCES
906  *   SCF_ERROR_NOT_BOUND
907  *   SCF_ERROR_NOT_FOUND
908  */
909 static int
910 _get_snapshot(scf_instance_t *inst, const char *snapshot,
911     scf_snapshot_t **snap)
912 {
913 	int err;
914 	scf_handle_t *h;
915 
916 	h = scf_instance_handle(inst);
917 	if (h == NULL) {
918 		*snap = NULL;
919 		return (-1);
920 	}
921 
922 	if ((*snap = scf_snapshot_create(h)) == NULL) {
923 		return (-1);
924 	}
925 
926 	/* Use running snapshot by default. */
927 	if (snapshot == NULL)
928 		err = scf_instance_get_snapshot(inst, "running", *snap);
929 	else
930 		err = scf_instance_get_snapshot(inst, snapshot, *snap);
931 
932 	if (err != 0) {
933 		if (ismember(scf_error(), errors_server)) {
934 			scf_snapshot_destroy(*snap);
935 			*snap = NULL;
936 			return (-1);
937 		} else switch (scf_error()) {
938 		case SCF_ERROR_INVALID_ARGUMENT:
939 			scf_snapshot_destroy(*snap);
940 			*snap = NULL;
941 			return (-1);
942 
943 		case SCF_ERROR_NOT_FOUND:
944 			scf_snapshot_destroy(*snap);
945 			*snap = NULL;
946 			return (0);
947 
948 		case SCF_ERROR_NOT_SET:
949 		case SCF_ERROR_HANDLE_MISMATCH:
950 		default:
951 			assert(0);
952 			abort();
953 		}
954 	}
955 
956 	/*
957 	 * Explicitly set SCF_ERROR_NONE so that the SCF_ERROR_NOT_FOUND
958 	 * return above is explicitly guaranteed to be from
959 	 * scf_instance_get_snapshot().
960 	 */
961 	(void) scf_set_error(SCF_ERROR_NONE);
962 	return (0);
963 }
964 
965 /*
966  * Returns NULL on error, sets scf_error() to:
967  *   SCF_ERROR_BACKEND_ACCESS
968  *   SCF_ERROR_CONNECTION_BROKEN
969  *   SCF_ERROR_CONSTRAINT_VIOLATED
970  *     The restarter's FMRI does not match an existing instance.
971  *   SCF_ERROR_DELETED
972  *   SCF_ERROR_HANDLE_DESTROYED
973  *   SCF_ERROR_INTERNAL
974  *   SCF_ERROR_INVALID_ARGUMENT
975  *     The restarter's FMRI is not a valid FMRI.
976  *   SCF_ERROR_NO_MEMORY
977  *   SCF_ERROR_NO_RESOURCES
978  *   SCF_ERROR_NOT_BOUND
979  *   SCF_ERROR_NOT_FOUND
980  *     Property doesn't exist or exists and has no value.
981  *   SCF_ERROR_TEMPLATE_INVALID
982  *     restarter property is not SCF_TYPE_ASTRING or has more than one value
983  */
984 static scf_instance_t *
985 _get_restarter_inst(scf_handle_t *h, scf_service_t *svc,
986     scf_instance_t *inst, scf_snapshot_t *s)
987 {
988 	char *restarter = NULL;
989 	scf_instance_t *ri = NULL;
990 	scf_propertygroup_t *pg = NULL;
991 	int ret = 0;
992 
993 	assert(svc != NULL || inst != NULL);
994 	assert(svc ==  NULL || inst == NULL);
995 
996 	if ((ri = scf_instance_create(h)) == NULL ||
997 	    (pg = scf_pg_create(h)) == NULL) {
998 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
999 		goto _get_restarter_inst_fail;
1000 	}
1001 
1002 	if (inst != NULL)
1003 		ret = scf_instance_get_pg_composed(inst, s, SCF_PG_GENERAL,
1004 		    pg);
1005 	else
1006 		ret = scf_service_get_pg(svc, SCF_PG_GENERAL, pg);
1007 
1008 	if (ret != 0) {
1009 		if (ismember(scf_error(), errors_server)) {
1010 			goto _get_restarter_inst_fail;
1011 		} else switch (scf_error()) {
1012 		case SCF_ERROR_NOT_FOUND:
1013 			/* Assume default restarter. */
1014 			break;
1015 
1016 		case SCF_ERROR_NOT_SET:
1017 		case SCF_ERROR_HANDLE_MISMATCH:
1018 			/*
1019 			 * If the arguments to the above functions
1020 			 * aren't derived from the same handle, there's
1021 			 * something wrong with the internal implementation,
1022 			 * not the public caller further up the chain.
1023 			 */
1024 		case SCF_ERROR_INVALID_ARGUMENT:
1025 		default:
1026 			assert(0);
1027 			abort();
1028 		}
1029 	} else {
1030 		restarter = _scf_read_single_astring_from_pg(pg,
1031 		    SCF_PROPERTY_RESTARTER);
1032 		/* zero length string is NOT a valid restarter */
1033 		if (restarter != NULL && restarter[0] == '\0') {
1034 			free(restarter);
1035 			restarter = NULL;
1036 		} else if (restarter == NULL) {
1037 			if (ismember(scf_error(), errors_server)) {
1038 				goto _get_restarter_inst_fail;
1039 			} else switch (scf_error()) {
1040 			case SCF_ERROR_NOT_FOUND:
1041 				break;
1042 
1043 			case SCF_ERROR_CONSTRAINT_VIOLATED:
1044 			case SCF_ERROR_TYPE_MISMATCH:
1045 				(void) scf_set_error(
1046 				    SCF_ERROR_TEMPLATE_INVALID);
1047 				goto _get_restarter_inst_fail;
1048 
1049 			case SCF_ERROR_NOT_SET:
1050 			case SCF_ERROR_INVALID_ARGUMENT:
1051 			default:
1052 				assert(0);
1053 				abort();
1054 			}
1055 		}
1056 	}
1057 
1058 	if (restarter == NULL) {
1059 		/* Use default restarter */
1060 		restarter = strdup(SCF_SERVICE_STARTD);
1061 		if (restarter == NULL) {
1062 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
1063 			goto _get_restarter_inst_fail;
1064 		}
1065 	}
1066 
1067 	if (scf_handle_decode_fmri(h, restarter, NULL, NULL, ri, NULL, NULL,
1068 	    SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1069 		if (ismember(scf_error(), errors_server)) {
1070 			goto _get_restarter_inst_fail;
1071 		} else switch (scf_error()) {
1072 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1073 		case SCF_ERROR_INVALID_ARGUMENT:
1074 		case SCF_ERROR_NOT_FOUND:
1075 			goto _get_restarter_inst_fail;
1076 
1077 		case SCF_ERROR_HANDLE_MISMATCH:
1078 		case SCF_ERROR_NOT_SET:
1079 		default:
1080 			assert(0);
1081 			abort();
1082 		}
1083 	}
1084 	free(restarter);
1085 	scf_pg_destroy(pg);
1086 
1087 	return (ri);
1088 
1089 _get_restarter_inst_fail:
1090 	free(restarter);
1091 	scf_instance_destroy(ri);
1092 	scf_pg_destroy(pg);
1093 	return (NULL);
1094 }
1095 
1096 /*
1097  * Returns NULL on error, sets scf_error() to:
1098  *   SCF_ERROR_BACKEND_ACCESS
1099  *   SCF_ERROR_CONNECTION_BROKEN
1100  *   SCF_ERROR_CONSTRAINT_VIOLATED
1101  *     Restarter property has more than one value associated with it,
1102  *     or FMRI does not meet restrictions in scf_handle_decode_fmri() flags.
1103  *   SCF_ERROR_DELETED
1104  *   SCF_ERROR_HANDLE_DESTROYED
1105  *   SCF_ERROR_INTERNAL
1106  *   SCF_ERROR_INVALID_ARGUMENT
1107  *     The fmri argument in scf_handle_decode_fmri() is not a valid FMRI.
1108  *   SCF_ERROR_NO_MEMORY
1109  *   SCF_ERROR_NO_RESOURCES
1110  *   SCF_ERROR_NOT_BOUND
1111  *   SCF_ERROR_NOT_FOUND
1112  *     Property doesn't exist or exists and has no value.
1113  */
1114 static scf_instance_t *
1115 _get_global_inst(scf_handle_t *h)
1116 {
1117 	scf_instance_t *ri;
1118 
1119 	if ((ri = scf_instance_create(h)) == NULL) {
1120 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
1121 		(void) scf_set_error(SCF_ERROR_NO_RESOURCES);
1122 		return (NULL);
1123 	}
1124 
1125 	if (scf_handle_decode_fmri(h, SCF_INSTANCE_GLOBAL, NULL, NULL, ri,
1126 	    NULL, NULL,
1127 	    SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1128 		if (ismember(scf_error(), errors_server)) {
1129 			scf_instance_destroy(ri);
1130 			return (NULL);
1131 		} else switch (scf_error()) {
1132 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1133 		case SCF_ERROR_INVALID_ARGUMENT:
1134 		case SCF_ERROR_NOT_FOUND:
1135 			scf_instance_destroy(ri);
1136 			return (NULL);
1137 
1138 		case SCF_ERROR_HANDLE_MISMATCH:
1139 		case SCF_ERROR_NOT_SET:
1140 		default:
1141 			assert(0);
1142 			abort();
1143 		}
1144 	}
1145 
1146 	return (ri);
1147 }
1148 
1149 /*
1150  * Call the supplied function for each of the service or instance, the
1151  * service's restarter, and the globally defined template instance.
1152  * If the function returns SCF_WALK_ERROR, the walk is ended.  If
1153  * the function returns SCF_WALK_NEXT, the next entity is tried.
1154  *
1155  * The function is only expected to return SCF_WALK_DONE if it has
1156  * found a property group match in the current entity, and has
1157  * populated p->pw_pg with the matching property group.
1158  *
1159  * The caller of _walk_template_instances() MUST check if the passed parameters
1160  * inst and svc match the fields pw_inst and pw_svc in the resulting
1161  * pg_tmpl_walk_t and call the destructor for the unmatching objects. The walker
1162  * may silently drop them if the template definition is in the restarter or in
1163  * the global instance.
1164  */
1165 static void
1166 _walk_template_instances(scf_service_t *svc, scf_instance_t *inst,
1167     scf_snapshot_t *snap, walk_template_inst_func_t *func,
1168     pg_tmpl_walk_t *p, int flag)
1169 {
1170 	scf_instance_t *tmpl_inst = NULL;
1171 	scf_handle_t *h;
1172 	int ret;
1173 	char *tg = NULL;
1174 
1175 	assert(svc != NULL || inst != NULL);
1176 	assert(svc == NULL || inst == NULL);
1177 
1178 	if (inst != NULL)
1179 		h = scf_instance_handle(inst);
1180 	else
1181 		h = scf_service_handle(svc);
1182 	if (h == NULL)
1183 		goto done;
1184 
1185 	/* First, use supplied service or instance */
1186 	p->pw_target = SCF_TM_TARGET_THIS;
1187 	ret = func(svc, inst, p);
1188 	switch (ret) {
1189 	case SCF_WALK_NEXT:
1190 		break;
1191 	case SCF_WALK_DONE:
1192 		/*
1193 		 * Check that the template scoping matches and if not,
1194 		 * continue.
1195 		 */
1196 		assert(p->pw_pg != NULL);
1197 		tg = _scf_read_single_astring_from_pg(p->pw_pg,
1198 		    SCF_PROPERTY_TM_TARGET);
1199 		if (tg == NULL || /* scf_error() was set */
1200 		    (strcmp(tg, SCF_TM_TARGET_INSTANCE) != 0 &&
1201 		    strcmp(tg, SCF_TM_TARGET_THIS) != 0 &&
1202 		    (flag & SCF_PG_TMPL_FLAG_EXACT) !=
1203 		    SCF_PG_TMPL_FLAG_EXACT)) {
1204 			scf_pg_destroy(p->pw_pg);
1205 			p->pw_pg = NULL;
1206 			if (tg != NULL) {
1207 				free(tg);
1208 				tg = NULL;
1209 				break;
1210 			}
1211 		}
1212 		/*FALLTHROUGH*/
1213 	case SCF_WALK_ERROR:
1214 		goto done;
1215 		/*NOTREACHED*/
1216 	default:
1217 		assert(0);
1218 		abort();
1219 	}
1220 
1221 	/* Next the restarter. */
1222 	p->pw_target = SCF_TM_TARGET_DELEGATE;
1223 	tmpl_inst = _get_restarter_inst(h, svc, inst, snap);
1224 	if (tmpl_inst != NULL) {
1225 		ret = func(NULL, tmpl_inst, p);
1226 		switch (ret) {
1227 		case SCF_WALK_NEXT:
1228 			break;
1229 		case SCF_WALK_DONE:
1230 			/*
1231 			 * Check that the template scoping matches and if not,
1232 			 * continue.
1233 			 */
1234 			assert(p->pw_pg != NULL);
1235 			tg = _scf_read_single_astring_from_pg(p->pw_pg,
1236 			    SCF_PROPERTY_TM_TARGET);
1237 			if (tg == NULL || /* scf_error() was set */
1238 			    strcmp(tg, SCF_TM_TARGET_DELEGATE) != 0) {
1239 				scf_pg_destroy(p->pw_pg);
1240 				p->pw_pg = NULL;
1241 				if (tg != NULL) {
1242 					free(tg);
1243 					tg = NULL;
1244 					break;
1245 				}
1246 			}
1247 			/*FALLTHROUGH*/
1248 		case SCF_WALK_ERROR:
1249 			goto done;
1250 			/*NOTREACHED*/
1251 		default:
1252 			assert(0);
1253 			abort();
1254 		}
1255 	}
1256 
1257 	p->pw_target = SCF_TM_TARGET_ALL;
1258 	scf_instance_destroy(tmpl_inst);
1259 	tmpl_inst = _get_global_inst(h);
1260 	if (tmpl_inst != NULL) {
1261 		ret = func(NULL, tmpl_inst, p);
1262 		switch (ret) {
1263 		case SCF_WALK_NEXT:
1264 			break;
1265 		case SCF_WALK_DONE:
1266 			/*
1267 			 * Check that the template scoping matches and if not,
1268 			 * continue.
1269 			 */
1270 			assert(p->pw_pg != NULL);
1271 			tg = _scf_read_single_astring_from_pg(p->pw_pg,
1272 			    SCF_PROPERTY_TM_TARGET);
1273 			if (tg == NULL || /* scf_error() was set */
1274 			    strcmp(tg, SCF_TM_TARGET_ALL) != 0) {
1275 				scf_pg_destroy(p->pw_pg);
1276 				p->pw_pg = NULL;
1277 				if (tg != NULL) {
1278 					free(tg);
1279 					tg = NULL;
1280 					break;
1281 				}
1282 			}
1283 			/*FALLTHROUGH*/
1284 		case SCF_WALK_ERROR:
1285 			goto done;
1286 			/*NOTREACHED*/
1287 		default:
1288 			assert(0);
1289 			abort();
1290 		}
1291 	}
1292 
1293 done:
1294 	free(tg);
1295 	if (ret != SCF_WALK_DONE)
1296 		scf_instance_destroy(tmpl_inst);
1297 	p->pw_target = NULL;
1298 }
1299 
1300 /*
1301  * _get_pg() returns 0 on success and -1 on failure.  Sets scf_error()
1302  * on failure.
1303  *   SCF_ERROR_BACKEND_ACCESS
1304  *   SCF_ERROR_CONNECTION_BROKEN
1305  *   SCF_ERROR_DELETED
1306  *   SCF_ERROR_HANDLE_MISMATCH
1307  *   SCF_ERROR_INTERNAL
1308  *   SCF_ERROR_INVALID_ARGUMENT
1309  *     name is not a valid property group.
1310  *   SCF_ERROR_NO_RESOURCES
1311  *   SCF_ERROR_NOT_BOUND
1312  *   SCF_ERROR_NOT_FOUND
1313  *   SCF_ERROR_NOT_SET
1314  */
1315 static int
1316 _get_pg(scf_service_t *svc, scf_instance_t *inst,
1317     const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
1318 {
1319 	int ret;
1320 
1321 	assert(svc != NULL || inst != NULL);
1322 	assert(svc == NULL || inst == NULL);
1323 	assert(pg != NULL);
1324 
1325 	if (inst != NULL)
1326 		ret = scf_instance_get_pg_composed(inst, snap, name, pg);
1327 	else
1328 		ret = scf_service_get_pg(svc, name, pg);
1329 
1330 	return (ret);
1331 }
1332 
1333 /*
1334  * Returns SCF_WALK_NEXT for not found, SCF_WALK_ERROR for error,
1335  * and SCF_WALK_DONE for found.
1336  * On error, destroy pg and set it to NULL.
1337  *
1338  * Sets scf_error() if SCF_WALK_ERROR is returned to _BACKEND_ACCESS,
1339  * _CONNECTION_BROKEN, _INTERNAL, _INVALID_ARGUMENT (name is not a
1340  *  valid property group), _NO_RESOURCES, or _NOT_BOUND.
1341  */
1342 static int
1343 _lookup_pg(scf_service_t *svc, scf_instance_t *inst,
1344     const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
1345 {
1346 	int ret;
1347 
1348 	ret = _get_pg(svc, inst, snap, name, pg);
1349 
1350 	if (ret == 0) {
1351 		return (SCF_WALK_DONE);
1352 	} else {
1353 		switch (scf_error()) {
1354 		case SCF_ERROR_NOT_FOUND:
1355 		case SCF_ERROR_DELETED:
1356 			return (SCF_WALK_NEXT);
1357 
1358 		case SCF_ERROR_BACKEND_ACCESS:
1359 		case SCF_ERROR_CONNECTION_BROKEN:
1360 		case SCF_ERROR_INTERNAL:
1361 		case SCF_ERROR_INVALID_ARGUMENT:
1362 		case SCF_ERROR_NOT_BOUND:
1363 		case SCF_ERROR_NO_RESOURCES:
1364 			scf_pg_destroy(pg);
1365 			pg = NULL;
1366 			return (SCF_WALK_ERROR);
1367 
1368 		case SCF_ERROR_NOT_SET:
1369 		case SCF_ERROR_HANDLE_MISMATCH:
1370 		default:
1371 			assert(0);
1372 			abort();
1373 		}
1374 	}
1375 
1376 	/*NOTREACHED*/
1377 }
1378 
1379 /*
1380  * If match, return 0.  If no match, return 1.  If error, return -1.
1381  * On error set scf_error() to _BACKEND_ACCESS, _CONNECTION_BROKEN,
1382  * _HANDLE_DESTROYED, _INTERNAL, _NO_MEMORY, _NO_RESOURCES, _NOT_BOUND,
1383  * _NOT_SET (property group specified by pg is not set), _PERMISSION_DENIED,
1384  * or _TEMPLATE_INVALID (target property is not SCF_TYPE_ASTRING or has
1385  * more than one value).
1386  */
1387 static int
1388 check_target_match(scf_propertygroup_t *pg, const char *target)
1389 {
1390 	char *pg_target;
1391 	int ret = 0;
1392 
1393 	pg_target = _scf_read_single_astring_from_pg(pg,
1394 	    SCF_PROPERTY_TM_TARGET);
1395 	if (pg_target == NULL) {
1396 		switch (scf_error()) {
1397 		case SCF_ERROR_DELETED:
1398 		case SCF_ERROR_NOT_FOUND:
1399 			return (1);
1400 
1401 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1402 		case SCF_ERROR_TYPE_MISMATCH:
1403 			(void) scf_set_error(
1404 			    SCF_ERROR_TEMPLATE_INVALID);
1405 			/*FALLTHROUGH*/
1406 
1407 		case SCF_ERROR_BACKEND_ACCESS:
1408 		case SCF_ERROR_CONNECTION_BROKEN:
1409 		case SCF_ERROR_HANDLE_DESTROYED:
1410 		case SCF_ERROR_INTERNAL:
1411 		case SCF_ERROR_NO_RESOURCES:
1412 		case SCF_ERROR_NOT_BOUND:
1413 		case SCF_ERROR_PERMISSION_DENIED:
1414 			return (-1);
1415 
1416 		case SCF_ERROR_NOT_SET:
1417 		case SCF_ERROR_INVALID_ARGUMENT:
1418 		default:
1419 			assert(0);
1420 			abort();
1421 		}
1422 		/*NOTREACHED*/
1423 	}
1424 
1425 	/* For a desired target of 'this', check for 'this' and 'instance'. */
1426 	if ((strcmp(target, SCF_TM_TARGET_INSTANCE) == 0 ||
1427 	    strcmp(target, SCF_TM_TARGET_THIS) == 0) &&
1428 	    (strcmp(pg_target, SCF_TM_TARGET_INSTANCE) == 0 ||
1429 	    strcmp(pg_target, SCF_TM_TARGET_THIS) == 0)) {
1430 		goto cleanup;
1431 	}
1432 
1433 	if (strcmp(target, SCF_TM_TARGET_DELEGATE) == 0 &&
1434 	    strcmp(pg_target, SCF_TM_TARGET_DELEGATE) == 0) {
1435 		goto cleanup;
1436 	}
1437 
1438 	if (strcmp(target, SCF_TM_TARGET_ALL) == 0 &&
1439 	    strcmp(pg_target, SCF_TM_TARGET_ALL) == 0) {
1440 		goto cleanup;
1441 	}
1442 
1443 	ret = 1;
1444 cleanup:
1445 	free(pg_target);
1446 	return (ret);
1447 }
1448 
1449 /*
1450  * Check if a matching template property group exists for each of:
1451  * name and type, name only, type only, and completely wildcarded
1452  * template.
1453  *
1454  * Both pg_name and pg_type are optional.
1455  *
1456  * Returns NULL on failure, sets scf_error():
1457  *   SCF_ERROR_BACKEND_ACCESS
1458  *   SCF_ERROR_CONNECTION_BROKEN
1459  *   SCF_ERROR_DELETED
1460  *   SCF_ERROR_HANDLE_DESTROYED
1461  *   SCF_ERROR_INTERNAL
1462  *   SCF_ERROR_INVALID_ARGUMENT
1463  *     can't combine the _tmpl_pg_name arguments and get a reasonable
1464  *     length name, or pg_name is not a valid property group.
1465  *   SCF_ERROR_NO_MEMORY
1466  *   SCF_ERROR_NO_RESOURCES
1467  *   SCF_ERROR_NOT_BOUND
1468  *   SCF_ERROR_NOT_FOUND
1469  *     Property doesn't exist or exists and has no value.
1470  *   SCF_ERROR_PERMISSION_DENIED
1471  *   SCF_ERROR_TEMPLATE_INVALID
1472  *     target property is not SCF_TYPE_ASTRING or has more than one value.
1473  */
1474 static scf_propertygroup_t *
1475 _find_template_pg_match(scf_service_t *svc, scf_instance_t *inst,
1476     const scf_snapshot_t *snap, const char *pg_name, const char *pg_type,
1477     const char *target, char **tmpl_pg_name)
1478 {
1479 	int ret, r;
1480 	scf_propertygroup_t *pg = NULL;
1481 	scf_handle_t *h;
1482 	scf_iter_t *iter;
1483 	char *name, *type;
1484 
1485 	assert(inst != NULL || svc != NULL);
1486 	assert(inst == NULL || svc == NULL);
1487 
1488 	if (inst != NULL)
1489 		h = scf_instance_handle(inst);
1490 	else
1491 		h = scf_service_handle(svc);
1492 	if (h == NULL) {
1493 		return (NULL);
1494 	}
1495 
1496 	if ((pg = scf_pg_create(h)) == NULL ||
1497 	    (iter = scf_iter_create(h)) == NULL) {
1498 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
1499 		scf_pg_destroy(pg);
1500 		return (NULL);
1501 	}
1502 
1503 	/*
1504 	 * We're going to walk through the possible pg templates that
1505 	 * could match the supplied name and type.  We do this
1506 	 * by explicit name lookups when possible to avoid having to
1507 	 * keep track of a most-explicit-match during iteration.
1508 	 */
1509 
1510 	/* First look for a template with name and type set and matching. */
1511 	*tmpl_pg_name = _tmpl_pg_name(pg_name, pg_type, 1);
1512 	if (*tmpl_pg_name == NULL)
1513 		goto fail;
1514 	ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1515 	if (ret != SCF_WALK_NEXT) {
1516 		if (pg != NULL) {
1517 			if ((r = check_target_match(pg, target)) == 0)
1518 				goto done;
1519 			else if (r == -1)
1520 				goto fail;
1521 		} else {
1522 			goto done;
1523 		}
1524 	}
1525 	free(*tmpl_pg_name);
1526 
1527 	/*
1528 	 * Need to search on a name-only match before searching on
1529 	 * type matches.
1530 	 */
1531 
1532 	*tmpl_pg_name = _tmpl_pg_name(pg_name, NULL, 0);
1533 	if (*tmpl_pg_name == NULL)
1534 		goto fail;
1535 	ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1536 	if (ret != SCF_WALK_NEXT) {
1537 		if (pg != NULL) {
1538 			if ((r = check_target_match(pg, target)) == 0)
1539 				goto done;
1540 			else if (r == -1)
1541 				goto fail;
1542 		} else {
1543 			goto done;
1544 		}
1545 	}
1546 	free(*tmpl_pg_name);
1547 
1548 	/* Next, see if there's an "nt" template where the type matches. */
1549 	if (pg_type != NULL && pg_name == NULL) {
1550 		if (inst != NULL)
1551 			ret = scf_iter_instance_pgs_typed_composed(iter, inst,
1552 			    snap, SCF_GROUP_TEMPLATE_PG_PATTERN);
1553 		else
1554 			ret = scf_iter_service_pgs_typed(iter, svc,
1555 			    SCF_GROUP_TEMPLATE_PG_PATTERN);
1556 
1557 		if (ret != 0) {
1558 			if (ismember(scf_error(), errors_server)) {
1559 				goto fail;
1560 			} else {
1561 				assert(0);
1562 				abort();
1563 			}
1564 		}
1565 
1566 		while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
1567 			/* Make sure this is a name and type specified pg. */
1568 			name = _scf_read_single_astring_from_pg(pg,
1569 			    SCF_PROPERTY_TM_NAME);
1570 			if (name == NULL)
1571 				continue;
1572 			type = _scf_read_single_astring_from_pg(pg,
1573 			    SCF_PROPERTY_TM_TYPE);
1574 			if (type == NULL) {
1575 				free(name);
1576 				continue;
1577 			}
1578 			if (strcmp(pg_type, type) == 0 &&
1579 			    check_target_match(pg, target) == 0) {
1580 				*tmpl_pg_name = name;
1581 				free(type);
1582 				goto done;
1583 			}
1584 			free(type);
1585 			free(name);
1586 		}
1587 		if (ret == -1) {
1588 			if (ismember(scf_error(), errors_server)) {
1589 				goto fail;
1590 			} else {
1591 				assert(0);
1592 				abort();
1593 			}
1594 		}
1595 	}
1596 
1597 	*tmpl_pg_name = _tmpl_pg_name(NULL, pg_type, 0);
1598 	if (*tmpl_pg_name == NULL)
1599 		goto fail;
1600 	ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1601 	if (ret != SCF_WALK_NEXT) {
1602 		if (pg != NULL) {
1603 			if ((r = check_target_match(pg, target)) == 0)
1604 				goto done;
1605 			else if (r == -1)
1606 				goto fail;
1607 		} else {
1608 			goto done;
1609 		}
1610 	}
1611 	free(*tmpl_pg_name);
1612 
1613 	*tmpl_pg_name = _tmpl_pg_name(NULL, NULL, 0);
1614 	if (*tmpl_pg_name == NULL)
1615 		goto fail;
1616 	ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1617 	if (ret != SCF_WALK_NEXT) {
1618 		if (pg != NULL) {
1619 			if ((r = check_target_match(pg, target)) == 0)
1620 				goto done;
1621 			else if (r == -1)
1622 				goto fail;
1623 		} else {
1624 			goto done;
1625 		}
1626 	}
1627 
1628 	(void) scf_set_error(SCF_ERROR_NOT_FOUND);
1629 fail:
1630 	scf_pg_destroy(pg);
1631 	if (*tmpl_pg_name != NULL)
1632 		free(*tmpl_pg_name);
1633 	*tmpl_pg_name = NULL;
1634 	pg = NULL;
1635 done:
1636 	if (ret == SCF_WALK_ERROR)
1637 		free(*tmpl_pg_name);
1638 	scf_iter_destroy(iter);
1639 	return (pg);
1640 }
1641 
1642 /*
1643  * Finds the pg match in either the supplied service or instance.
1644  * Returns SCF_WALK_ERROR, SCF_WALK_NEXT, or SCF_WALK_DONE.
1645  * If returning SCF_WALK_ERROR, sets scf_error():
1646  *   SCF_ERROR_BACKEND_ACCESS
1647  *   SCF_ERROR_CONNECTION_BROKEN
1648  *   SCF_ERROR_DELETED
1649  *   SCF_ERROR_HANDLE_DESTROYED
1650  *   SCF_ERROR_INTERNAL
1651  *   SCF_ERROR_INVALID_ARGUMENT
1652  *     The snaphot is not a valid snapshot name,
1653  *     or can't create a reasonable property group template name.
1654  *   SCF_ERROR_NO_MEMORY
1655  *   SCF_ERROR_NO_RESOURCES
1656  *   SCF_ERROR_NOT_BOUND
1657  *   SCF_ERROR_NOT_FOUND
1658  *     Property doesn't exist or exists and has no value.
1659  *   SCF_ERROR_PERMISSION_DENIED
1660  *   SCF_ERROR_TEMPLATE_INVALID
1661  *     target property is not SCF_TYPE_ASTRING or has more than one value.
1662  */
1663 static int
1664 find_pg_match(scf_service_t *svc, scf_instance_t *inst, pg_tmpl_walk_t *p)
1665 {
1666 	scf_snapshot_t *tmpl_snap = NULL;
1667 	scf_propertygroup_t *pg;
1668 	scf_handle_t *h;
1669 	char *tmpl_pg_name;
1670 
1671 	assert(svc != NULL || inst != NULL);
1672 	assert(svc == NULL || inst == NULL);
1673 
1674 	if (inst != NULL)
1675 		h = scf_instance_handle(inst);
1676 	else
1677 		h = scf_service_handle(svc);
1678 	if (h == NULL)
1679 		return (SCF_WALK_ERROR);
1680 
1681 	if (p->pw_snapname != NULL) {
1682 		if (_get_snapshot(inst, p->pw_snapname, &tmpl_snap) == -1)
1683 			return (SCF_WALK_ERROR);
1684 	}
1685 	pg = _find_template_pg_match(svc, inst, tmpl_snap, p->pw_pgname,
1686 	    p->pw_pgtype, p->pw_target, &tmpl_pg_name);
1687 
1688 	if (pg != NULL) {
1689 		p->pw_snap = tmpl_snap;
1690 		p->pw_pg = pg;
1691 		p->pw_tmpl_pgname = tmpl_pg_name;
1692 		p->pw_inst = inst;
1693 		p->pw_svc = svc;
1694 		return (SCF_WALK_DONE);
1695 	}
1696 
1697 	scf_snapshot_destroy(tmpl_snap);
1698 	return (SCF_WALK_NEXT);
1699 }
1700 
1701 /*
1702  * return 0 on success and -1 on failure.
1703  *   SCF_ERROR_CONNECTION_BROKEN
1704  *   SCF_ERROR_DELETED
1705  *   SCF_ERROR_HANDLE_DESTROYED
1706  *   SCF_ERROR_HANDLE_MISMATCH
1707  *   SCF_ERROR_INTERNAL
1708  *   SCF_ERROR_INVALID_ARGUMENT
1709  *     FMRI argument, snapshot name, pg_name, or pg is invalid.
1710  *   SCF_ERROR_NO_MEMORY
1711  *   SCF_ERROR_NO_RESOURCES
1712  *   SCF_ERROR_NOT_BOUND
1713  *   SCF_ERROR_NOT_FOUND
1714  *   SCF_ERROR_NOT_SET
1715  */
1716 int
1717 scf_tmpl_get_by_pg(scf_propertygroup_t *pg, scf_pg_tmpl_t *pg_tmpl, int flags)
1718 {
1719 	char *fmribuf = NULL, *snapbuf = NULL, *pg_name = NULL, *pg_type = NULL;
1720 	int ret;
1721 	ssize_t fbufsz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
1722 	ssize_t nbufsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
1723 	ssize_t tbufsz = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
1724 	scf_instance_t *inst = NULL;
1725 	scf_snaplevel_t *snaplvl = NULL;
1726 	scf_service_t *svc = NULL;
1727 	scf_handle_t *h;
1728 	scf_snapshot_t *snap = NULL;
1729 	pg_tmpl_walk_t *p = NULL;
1730 
1731 	assert(fbufsz != 0 && nbufsz != 0 && tbufsz != 0);
1732 
1733 	scf_tmpl_pg_reset(pg_tmpl);
1734 
1735 	if ((h = scf_pg_handle(pg)) == NULL)
1736 		return (-1);
1737 
1738 	if ((inst = scf_instance_create(h)) == NULL ||
1739 	    (svc = scf_service_create(h)) == NULL ||
1740 	    (snaplvl = scf_snaplevel_create(h)) == NULL) {
1741 		goto fail;
1742 	}
1743 
1744 	if ((fmribuf = malloc(fbufsz)) == NULL ||
1745 	    (pg_name = malloc(nbufsz)) == NULL ||
1746 	    (pg_type = malloc(tbufsz)) == NULL ||
1747 	    (p = calloc(1, sizeof (pg_tmpl_walk_t))) == NULL) {
1748 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
1749 		goto fail;
1750 	}
1751 
1752 	if (scf_pg_get_name(pg, pg_name, nbufsz) < 0) {
1753 		goto fail;
1754 	}
1755 
1756 	if (scf_pg_get_type(pg, pg_type, tbufsz) < 0) {
1757 		goto fail;
1758 	}
1759 	p->pw_pgname = pg_name;
1760 	p->pw_pgtype = pg_type;
1761 
1762 	ret = scf_pg_get_parent_snaplevel(pg, snaplvl);
1763 	if (ret == -1) {
1764 		switch (scf_error()) {
1765 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1766 			/* Parent type doesn't match.  Keep looking. */
1767 			break;
1768 
1769 		case SCF_ERROR_DELETED:
1770 		case SCF_ERROR_NOT_BOUND:
1771 		case SCF_ERROR_NOT_SET:
1772 			/* Pass these back to the caller. */
1773 			goto fail;
1774 
1775 		case SCF_ERROR_HANDLE_MISMATCH:
1776 		default:
1777 			assert(0);
1778 			abort();
1779 		}
1780 
1781 		/*
1782 		 * No snapshot.  We'll use 'editing' by default since
1783 		 * snap and snapbuf are NULL.
1784 		 */
1785 		p->pw_snapname = NULL;
1786 
1787 	} else {
1788 		if ((snap = scf_snapshot_create(h)) == NULL) {
1789 			goto fail;
1790 		}
1791 
1792 		ret = scf_snaplevel_get_parent(snaplvl, snap);
1793 		if (ret == -1) {
1794 			if (ismember(scf_error(), errors_server)) {
1795 				goto fail;
1796 			} else {
1797 				assert(0);
1798 				abort();
1799 			}
1800 		}
1801 
1802 		/* Grab snapshot name while we're here. */
1803 		if ((snapbuf = malloc(nbufsz)) == NULL) {
1804 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
1805 			goto fail;
1806 		}
1807 		if (scf_snapshot_get_name(snap, snapbuf, nbufsz) < 0) {
1808 			if (ismember(scf_error(), errors_server)) {
1809 				goto fail;
1810 			} else {
1811 				assert(0);
1812 				abort();
1813 			}
1814 		}
1815 		p->pw_snapname = snapbuf;
1816 
1817 		ret = scf_snapshot_get_parent(snap, inst);
1818 		if (ret == -1) {
1819 			if (ismember(scf_error(), errors_server)) {
1820 				goto fail;
1821 			} else {
1822 				assert(0);
1823 				abort();
1824 			}
1825 		}
1826 
1827 		_walk_template_instances(NULL, inst, snap,
1828 		    (walk_template_inst_func_t *)find_pg_match, p, flags);
1829 	}
1830 
1831 	/* No snapshot parent.  Go looking for instance parent. */
1832 	if (snapbuf == NULL) {
1833 		/* First look for instance parent. */
1834 		ret = scf_pg_get_parent_instance(pg, inst);
1835 		if (ret == 0) {
1836 			_walk_template_instances(NULL, inst, snap,
1837 			    (walk_template_inst_func_t *)find_pg_match,
1838 			    p, flags);
1839 		/* OK, check for service parent */
1840 		} else if (ret == -1 &&
1841 		    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
1842 			ret = scf_pg_get_parent_service(pg, svc);
1843 			if (ret == 0) {
1844 				_walk_template_instances(svc, NULL, snap,
1845 				    (walk_template_inst_func_t *)find_pg_match,
1846 				    p, flags);
1847 			} else {
1848 				switch (scf_error()) {
1849 				case SCF_ERROR_CONSTRAINT_VIOLATED:
1850 					(void) scf_set_error(
1851 					    SCF_ERROR_NOT_FOUND);
1852 					/*FALLTHROUGH*/
1853 
1854 				case SCF_ERROR_CONNECTION_BROKEN:
1855 				case SCF_ERROR_DELETED:
1856 				case SCF_ERROR_HANDLE_MISMATCH:
1857 				case SCF_ERROR_NOT_BOUND:
1858 				case SCF_ERROR_NOT_SET:
1859 					goto fail;
1860 
1861 				default:
1862 					assert(0);
1863 					abort();
1864 				}
1865 			}
1866 		} else {
1867 			goto fail;
1868 		}
1869 	}
1870 
1871 	if (p->pw_pg != NULL) {
1872 		pg_tmpl->pt_h = h;
1873 		pg_tmpl->pt_pg = p->pw_pg;
1874 		pg_tmpl->pt_inst = p->pw_inst;
1875 		/* we may get a different instance back */
1876 		if (p->pw_inst != inst)
1877 			scf_instance_destroy(inst);
1878 		pg_tmpl->pt_snap = p->pw_snap;
1879 		pg_tmpl->pt_svc = p->pw_svc;
1880 		/* we may get a different service back */
1881 		if (p->pw_svc != svc)
1882 			scf_service_destroy(svc);
1883 		pg_tmpl->pt_populated = 1;
1884 		free(p->pw_tmpl_pgname);
1885 		ret = 0;
1886 		goto done;
1887 	}
1888 
1889 	(void) scf_set_error(SCF_ERROR_NOT_FOUND);
1890 
1891 fail:
1892 	ret = -1;
1893 	scf_instance_destroy(inst);
1894 	scf_service_destroy(svc);
1895 done:
1896 	scf_snapshot_destroy(snap);
1897 	free(snapbuf);
1898 	free(fmribuf);
1899 	free(pg_name);
1900 	free(pg_type);
1901 	free(p);
1902 	scf_snaplevel_destroy(snaplvl);
1903 	return (ret);
1904 }
1905 
1906 /*
1907  * int scf_tmpl_get_by_pg_name()
1908  *
1909  * Get a template by a combination of the name and type.  Either name
1910  * or type can be null, which indicates a wildcard.  flags may be
1911  * SCF_PG_TMPL_FLAG_CURRENT (use current properties rather than
1912  * the defined or running snapshot), and SCF_PG_TMPL_FLAG_EXACT (match
1913  * only templates defined by the FMRI in question, not by its restarter
1914  * or globally).  Returns 0 on success and -1 on error, and sets
1915  * scf_error() to:
1916  *   SCF_ERROR_BACKEND_ACCESS
1917  *   SCF_ERROR_CONNECTION_BROKEN
1918  *     The connection to the repository was lost.
1919  *   SCF_ERROR_DELETED
1920  *     The instance has been deleted.
1921  *   SCF_ERROR_HANDLE_DESTROYED
1922  *   SCF_ERROR_INTERNAL
1923  *   SCF_ERROR_INVALID_ARGUMENT
1924  *     FMRI isn't valid, pg_name is too long to look for a template, or
1925  *     snapshot specified isn't a valid name
1926  *   SCF_ERROR_NO_MEMORY
1927  *   SCF_ERROR_NO_RESOURCES
1928  *     The server does not have adequate resources to complete the request.
1929  *   SCF_ERROR_NOT_BOUND
1930  *     The handle is not currently bound.
1931  *   SCF_ERROR_NOT_FOUND
1932  *     Object matching FMRI doesn't exist in the repository, or snapshot
1933  *     doesn't exist.
1934  */
1935 int
1936 scf_tmpl_get_by_pg_name(const char *fmri, const char *snapshot,
1937     const char *pg_name, const char *pg_type, scf_pg_tmpl_t *pg_tmpl, int flags)
1938 {
1939 	scf_instance_t *inst = NULL;
1940 	scf_service_t *svc = NULL;
1941 	scf_snapshot_t *snap = NULL;
1942 	pg_tmpl_walk_t *p = NULL;
1943 	scf_handle_t *h;
1944 	int ret;
1945 
1946 	assert(pg_tmpl != NULL);
1947 	h = pg_tmpl->pt_h;
1948 	assert(h != NULL);
1949 
1950 	scf_tmpl_pg_reset(pg_tmpl);
1951 
1952 	if ((inst = scf_instance_create(h)) == NULL ||
1953 	    (svc = scf_service_create(h)) == NULL) {
1954 		goto fail;
1955 	}
1956 
1957 	p = calloc(1, sizeof (pg_tmpl_walk_t));
1958 	if (p == NULL) {
1959 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
1960 		goto fail;
1961 	}
1962 
1963 	ret = scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
1964 	    NULL, SCF_DECODE_FMRI_EXACT);
1965 	if (ret == 0) {
1966 		scf_service_destroy(svc);
1967 		svc = NULL;
1968 	} else if (ret != 0 &&
1969 	    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
1970 		ret = scf_handle_decode_fmri(h, fmri, NULL, svc,
1971 		    NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT);
1972 		if (ret == 0) {
1973 			scf_instance_destroy(inst);
1974 			inst = NULL;
1975 		}
1976 	}
1977 	if (ret != 0) {
1978 		if (ismember(scf_error(), errors_server)) {
1979 			goto fail;
1980 		} else switch (scf_error()) {
1981 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1982 			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
1983 			goto fail;
1984 
1985 		case SCF_ERROR_INVALID_ARGUMENT:
1986 		case SCF_ERROR_NOT_FOUND:
1987 			goto fail;
1988 
1989 		case SCF_ERROR_HANDLE_MISMATCH:
1990 		case SCF_ERROR_NOT_SET:
1991 		default:
1992 			assert(0);
1993 			abort();
1994 		}
1995 	}
1996 
1997 	assert(svc == NULL || inst == NULL);
1998 	assert(svc != NULL || inst != NULL);
1999 
2000 	/* If we have a service fmri, snapshot is ignored. */
2001 	if (inst != NULL) {
2002 		if (snapshot == NULL || strcmp(snapshot, "running") == 0 ||
2003 		    (flags & SCF_PG_TMPL_FLAG_CURRENT) ==
2004 		    SCF_PG_TMPL_FLAG_CURRENT) {
2005 			if (_get_snapshot(inst, NULL, &snap) == -1)
2006 				goto fail;
2007 		} else {
2008 			if (_get_snapshot(inst, snapshot, &snap) == -1) {
2009 				goto fail;
2010 			} else if (scf_error() == SCF_ERROR_NOT_FOUND) {
2011 				goto fail;
2012 			}
2013 		}
2014 	}
2015 
2016 	p->pw_snapname = snapshot;
2017 	p->pw_pgname = pg_name;
2018 	p->pw_pgtype = pg_type;
2019 
2020 	/*
2021 	 * For each of instance, restarter, global
2022 	 *    - check for a tm_pg_pattern_nt_<name> matching type
2023 	 *    - check for a tm_pg_pattern_t_<type> matching type
2024 	 *    - check for any tm_pg_pattern_
2025 	 * Currently plan to return the most specific match only.
2026 	 */
2027 	_walk_template_instances(svc, inst, snap,
2028 	    (walk_template_inst_func_t *)find_pg_match, p, flags);
2029 
2030 	if (p->pw_pg != NULL) {
2031 		pg_tmpl->pt_h = h;
2032 		pg_tmpl->pt_pg = p->pw_pg;
2033 		pg_tmpl->pt_inst = p->pw_inst;
2034 		/* we may get a different instance back */
2035 		if (p->pw_inst != inst)
2036 			scf_instance_destroy(inst);
2037 		pg_tmpl->pt_snap = p->pw_snap;
2038 		pg_tmpl->pt_svc = p->pw_svc;
2039 		/* we may get a different service back */
2040 		if (p->pw_svc != svc)
2041 			scf_service_destroy(svc);
2042 		pg_tmpl->pt_populated = 1;
2043 		scf_snapshot_destroy(snap);
2044 		free(p->pw_tmpl_pgname);
2045 		free(p);
2046 		return (0);
2047 	}
2048 
2049 	(void) scf_set_error(SCF_ERROR_NOT_FOUND);
2050 fail:
2051 	free(p);
2052 	scf_instance_destroy(inst);
2053 	scf_service_destroy(svc);
2054 	scf_snapshot_destroy(snap);
2055 	return (-1);
2056 }
2057 
2058 /*
2059  * Returns NULL on failure, sets scf_error() to _CONNECTION_BROKEN,
2060  * _DELETED, _NO_RESOURCES, or _NOT_BOUND.
2061  */
2062 static scf_iter_t *
2063 _get_svc_or_inst_iter(scf_handle_t *h, scf_pg_tmpl_t *t)
2064 {
2065 	scf_iter_t *iter;
2066 	int ret;
2067 
2068 	assert(t->pt_svc != NULL || t->pt_inst != NULL);
2069 	assert(t->pt_svc == NULL || t->pt_inst == NULL);
2070 
2071 	if ((iter = scf_iter_create(h)) == NULL) {
2072 		return (NULL);
2073 	}
2074 
2075 	/* Iterate on property groups of type template_pg_pattern */
2076 
2077 	if (t->pt_inst != NULL)
2078 		ret = scf_iter_instance_pgs_typed_composed(iter,
2079 		    t->pt_inst, t->pt_snap,
2080 		    SCF_GROUP_TEMPLATE_PG_PATTERN);
2081 	if (t->pt_svc != NULL)
2082 		ret = scf_iter_service_pgs_typed(iter, t->pt_svc,
2083 		    SCF_GROUP_TEMPLATE_PG_PATTERN);
2084 
2085 	if (ret != 0) {
2086 		if (ismember(scf_error(), errors_server)) {
2087 			scf_iter_destroy(iter);
2088 			return (NULL);
2089 		} else {
2090 			assert(0);
2091 			abort();
2092 		}
2093 	}
2094 
2095 	return (iter);
2096 }
2097 
2098 /*
2099  * Returns NULL on failure, sets scf_error() to:
2100  *   SCF_ERROR_BACKEND_ACCESS
2101  *   SCF_ERROR_CONNECTION_BROKEN
2102  *   SCF_ERROR_DELETED
2103  *   SCF_HANDLE_DESTROYED
2104  *   SCF_ERROR_INTERNAL
2105  *   SCF_ERROR_INVALID_ARGUMENT
2106  *     Handle argument is NULL, or snaphot is not a valid snapshot name.
2107  *   SCF_ERROR_NO_MEMORY
2108  *   SCF_ERROR_NO_RESOURCES
2109  *   SCF_ERROR_NOT_BOUND
2110  *   SCF_ERROR_NOT_FOUND
2111  */
2112 static scf_iter_t *
2113 _get_next_iterator(scf_handle_t *h, scf_pg_tmpl_t *t, const char *snapshot,
2114     int exact)
2115 {
2116 	scf_iter_t  *iter = NULL;
2117 	ssize_t limit;
2118 
2119 	limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2120 	assert(limit != 0);
2121 
2122 	/*
2123 	 * Check what level we last iterated on: none, service,
2124 	 * restarter, or global.  Make sure that if one in the middle
2125 	 * doesn't exist, we move on to the next entity.
2126 	 *
2127 	 * Before we drop any references to pt_inst or pt_svc we must
2128 	 * destroy them so we don't leak them.
2129 	 */
2130 	do {
2131 		switch (t->pt_iter_last) {
2132 		case SCF__TMPL_ITER_NONE:
2133 			t->pt_iter_last = SCF__TMPL_ITER_INST;
2134 			if (t->pt_inst != t->pt_orig_inst)
2135 				scf_instance_destroy(t->pt_inst);
2136 			t->pt_inst = t->pt_orig_inst;
2137 			if (t->pt_svc != t->pt_orig_svc)
2138 				scf_service_destroy(t->pt_svc);
2139 			t->pt_svc = t->pt_orig_svc;
2140 			break;
2141 
2142 		case SCF__TMPL_ITER_INST:
2143 			/*
2144 			 * Don't go any further than the specified instance
2145 			 * if exact was set.
2146 			 */
2147 			if (exact == 1) {
2148 				(void) scf_set_error(SCF_ERROR_NOT_FOUND);
2149 				goto fail;
2150 			}
2151 			t->pt_iter_last = SCF__TMPL_ITER_RESTARTER;
2152 			if (t->pt_inst != t->pt_orig_inst)
2153 				scf_instance_destroy(t->pt_inst);
2154 			t->pt_inst = _get_restarter_inst(h, t->pt_orig_svc,
2155 			    t->pt_orig_inst, t->pt_snap);
2156 			if (t->pt_svc != t->pt_orig_svc)
2157 				scf_service_destroy(t->pt_svc);
2158 			t->pt_svc = NULL;
2159 			break;
2160 
2161 		case SCF__TMPL_ITER_RESTARTER:
2162 			t->pt_iter_last = SCF__TMPL_ITER_GLOBAL;
2163 			if (t->pt_inst != t->pt_orig_inst)
2164 				scf_instance_destroy(t->pt_inst);
2165 			t->pt_inst = _get_global_inst(h);
2166 			if (t->pt_svc != t->pt_orig_svc)
2167 				scf_service_destroy(t->pt_svc);
2168 			t->pt_svc = NULL;
2169 			break;
2170 
2171 		case SCF__TMPL_ITER_GLOBAL:
2172 			(void) scf_set_error(SCF_ERROR_NOT_FOUND);
2173 			return (NULL);
2174 
2175 		default:
2176 			assert(0);
2177 			abort();
2178 		}
2179 	} while (t->pt_inst == NULL && t->pt_svc == NULL);
2180 
2181 	/* Set pt_snap to the snapshot for this instance */
2182 	if (t->pt_inst != NULL) {
2183 		scf_snapshot_destroy(t->pt_snap);
2184 		if (_get_snapshot(t->pt_inst, snapshot,
2185 		    &t->pt_snap) == -1)
2186 			goto fail;
2187 	}
2188 
2189 	iter = _get_svc_or_inst_iter(h, t);
2190 fail:
2191 	return (iter);
2192 }
2193 
2194 /*
2195  * scf_pg_tmpl_t *scf_tmpl_pg_create(scf_handle_t *)
2196  *
2197  * Returns NULL on failure, sets scf_error() to _INVALID_ARGUMENT
2198  * or _NO_MEMORY.
2199  */
2200 scf_pg_tmpl_t *
2201 scf_tmpl_pg_create(scf_handle_t *handle)
2202 {
2203 	scf_pg_tmpl_t *pg_tmpl = NULL;
2204 
2205 	if (handle == NULL) {
2206 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2207 		return (NULL);
2208 	}
2209 	pg_tmpl = calloc(1, sizeof (scf_pg_tmpl_t));
2210 	if (pg_tmpl == NULL)
2211 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2212 	else
2213 		pg_tmpl->pt_h = handle;
2214 
2215 	return (pg_tmpl);
2216 }
2217 
2218 /*
2219  * Retrieves name or type of a template pg.
2220  * Returns -1 on failure.  Sets scf_error():
2221  *   SCF_ERROR_BACKEND_ACCESS
2222  *   SCF_ERROR_CONNECTION_BROKEN
2223  *   SCF_ERROR_DELETED
2224  *   SCF_ERROR_HANDLE_DESTROYED
2225  *   SCF_ERROR_INTERNAL
2226  *   SCF_ERROR_NO_MEMORY
2227  *   SCF_ERROR_NO_RESOURCES
2228  *   SCF_ERROR_NOT_BOUND
2229  *   SCF_ERROR_PERMISSION_DENIED
2230  *   SCF_ERROR_TEMPLATE_INVALID
2231  *     pname property is not SCF_TYPE_ASTRING or has more than one value.
2232  */
2233 static ssize_t
2234 _scf_tmpl_prop_value(scf_propertygroup_t *pg, const char *pname, char **out)
2235 {
2236 	assert(strcmp(pname, SCF_PROPERTY_TM_NAME) == 0 ||
2237 	    strcmp(pname, SCF_PROPERTY_TM_TYPE) == 0);
2238 
2239 	*out = _scf_read_single_astring_from_pg(pg, pname);
2240 
2241 	if (*out != NULL && *out[0] == '\0') {
2242 		(void) scf_set_error(SCF_ERROR_NONE);
2243 		free(*out);
2244 		*out = strdup(SCF_TMPL_WILDCARD);
2245 		if (*out == NULL)
2246 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2247 	}
2248 	if (*out == NULL) {
2249 		if (ismember(scf_error(), errors_server)) {
2250 			return (-1);
2251 		} else switch (scf_error()) {
2252 		case SCF_ERROR_CONSTRAINT_VIOLATED:
2253 		case SCF_ERROR_NOT_FOUND:
2254 		case SCF_ERROR_TYPE_MISMATCH:
2255 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2256 			return (-1);
2257 
2258 		case SCF_ERROR_INVALID_ARGUMENT:
2259 		case SCF_ERROR_NOT_SET:
2260 		default:
2261 			assert(0);
2262 			abort();
2263 		}
2264 	}
2265 
2266 	return (strlen(*out));
2267 }
2268 
2269 /*
2270  * int scf_tmpl_iter_pgs()
2271  *
2272  * Iterates through the property group templates for the fmri given.
2273  * When t is uninitialized or reset, sets t to the first property group
2274  * template in fmri. On subsequent calls, sets t to the next property group
2275  * template in frmi.
2276  * Returns 1 on success, 0 when no property group templates are left to
2277  * iterate, -1 on error.
2278  * The flags argument may include SCF_PG_TMPL_FLAG_REQUIRED,
2279  * SCF_PG_TMPL_FLAG_CURRENT,  and/or SCF_PG_TMPL_FLAG_EXACT.
2280  *
2281  * Returns -1 on error and sets scf_error() to:
2282  *   SCF_ERROR_BACKEND_ACCESS
2283  *   SCF_ERROR_CONNECTION_BROKEN
2284  *   SCF_ERROR_DELETED
2285  *   SCF_ERROR_HANDLE_DESTROYED
2286  *   SCF_ERROR_INTERNAL
2287  *   SCF_ERROR_INVALID_ARGUMENT
2288  *      The handle argument is NULL, fmri is invalid, or snapshot is invalid.
2289  *   SCF_ERROR_NO_MEMORY
2290  *   SCF_ERROR_NO_RESOURCES
2291  *   SCF_ERROR_NOT_BOUND
2292  *   SCF_ERROR_NOT_FOUND
2293  *   SCF_ERROR_PERMISSION_DENIED
2294  */
2295 int
2296 scf_tmpl_iter_pgs(scf_pg_tmpl_t *t, const char *fmri, const char *snapshot,
2297     const char *type, int flags)
2298 {
2299 	scf_handle_t *h;
2300 	scf_service_t *svc = NULL;
2301 	scf_instance_t *inst = NULL;
2302 	scf_propertygroup_t *pg = NULL;
2303 	scf_snapshot_t *snap = NULL;
2304 	scf_pg_tmpl_t *pg_tmpl = NULL;
2305 	int err;
2306 	int found = 0;
2307 	char *tmpl_type;
2308 	uint8_t required;
2309 	int ret;
2310 
2311 	if (t == NULL) {
2312 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2313 		return (-1);
2314 	}
2315 
2316 	h = t->pt_h;
2317 
2318 	if (t->pt_populated == 0) {
2319 		if ((svc = scf_service_create(h)) == NULL ||
2320 		    (inst = scf_instance_create(h)) == NULL ||
2321 		    (pg = scf_pg_create(h)) == NULL) {
2322 			goto fail_non_populated;
2323 		}
2324 
2325 		ret = scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
2326 		    NULL, SCF_DECODE_FMRI_EXACT);
2327 		if (ret == 0) {
2328 			scf_service_destroy(svc);
2329 			svc = NULL;
2330 		} else if (ret != 0 &&
2331 		    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
2332 			ret = scf_handle_decode_fmri(h, fmri, NULL, svc,
2333 			    NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT);
2334 			if (ret == 0) {
2335 				scf_instance_destroy(inst);
2336 				inst = NULL;
2337 			}
2338 		}
2339 
2340 		if (ret != 0) {
2341 			if (ismember(scf_error(), errors_server)) {
2342 				goto fail_non_populated;
2343 			} else switch (scf_error()) {
2344 			case SCF_ERROR_CONSTRAINT_VIOLATED:
2345 				(void) scf_set_error(
2346 				    SCF_ERROR_INVALID_ARGUMENT);
2347 				goto fail_non_populated;
2348 
2349 			case SCF_ERROR_INVALID_ARGUMENT:
2350 			case SCF_ERROR_NOT_FOUND:
2351 				goto fail_non_populated;
2352 
2353 			case SCF_ERROR_HANDLE_MISMATCH:
2354 			case SCF_ERROR_NOT_SET:
2355 			default:
2356 				assert(0);
2357 				abort();
2358 			}
2359 		}
2360 
2361 		assert(svc == NULL || inst == NULL);
2362 		assert(svc != NULL || inst != NULL);
2363 
2364 		if (inst != NULL) {
2365 			if (snapshot == NULL ||
2366 			    strcmp(snapshot, "running") == 0 ||
2367 			    (flags & SCF_PG_TMPL_FLAG_CURRENT) ==
2368 			    SCF_PG_TMPL_FLAG_CURRENT) {
2369 				if (_get_snapshot(inst, NULL, &snap) == -1)
2370 					goto fail_non_populated;
2371 			} else {
2372 				(void) scf_set_error(SCF_ERROR_NONE);
2373 				if (_get_snapshot(inst, snapshot,
2374 				    &snap) == -1) {
2375 					goto fail_non_populated;
2376 				} else if (scf_error() == SCF_ERROR_NOT_FOUND) {
2377 					goto fail_non_populated;
2378 				}
2379 			}
2380 		} else {
2381 			scf_snapshot_destroy(snap);
2382 			snap = NULL;
2383 		}
2384 
2385 		pg_tmpl = t;
2386 		pg_tmpl->pt_orig_inst = inst;
2387 		pg_tmpl->pt_orig_svc = svc;
2388 		pg_tmpl->pt_snap = snap;
2389 		pg_tmpl->pt_is_iter = 1;
2390 		pg_tmpl->pt_iter_last = SCF__TMPL_ITER_NONE;
2391 		pg_tmpl->pt_pg = pg;
2392 		pg_tmpl->pt_populated = 1;
2393 	} else {
2394 		if (t->pt_is_iter != 1) {
2395 			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2396 			return (-1);
2397 		}
2398 		pg_tmpl = t;
2399 		assert(pg_tmpl->pt_pg != NULL);
2400 	}
2401 
2402 	if (pg_tmpl->pt_iter == NULL) {
2403 		pg_tmpl->pt_iter = _get_next_iterator(h, pg_tmpl, snapshot,
2404 		    (flags & SCF_PG_TMPL_FLAG_EXACT) ? 1 : 0);
2405 		if (pg_tmpl->pt_iter == NULL) {
2406 			if (scf_error() == SCF_ERROR_NOT_FOUND)
2407 				return (0);
2408 			else
2409 				return (-1);
2410 		}
2411 	}
2412 
2413 	while (found == 0) {
2414 		while ((err = scf_iter_next_pg(pg_tmpl->pt_iter,
2415 		    pg_tmpl->pt_pg)) != 1) {
2416 			if (err == -1) {
2417 				if (ismember(scf_error(), errors_server)) {
2418 					return (-1);
2419 				} else switch (scf_error()) {
2420 				case SCF_ERROR_HANDLE_MISMATCH:
2421 					return (-1);
2422 
2423 				case SCF_ERROR_NOT_SET:
2424 				case SCF_ERROR_INVALID_ARGUMENT:
2425 				default:
2426 					assert(0);
2427 					abort();
2428 				}
2429 			} else if (err == 0)  {
2430 				/* This iteration is done.  Get the next one */
2431 				scf_iter_destroy(pg_tmpl->pt_iter);
2432 				pg_tmpl->pt_iter = _get_next_iterator(h,
2433 				    pg_tmpl, snapshot,
2434 				    (flags & SCF_PG_TMPL_FLAG_EXACT) ? 1 : 0);
2435 				if (pg_tmpl->pt_iter == NULL) {
2436 					if (scf_error() == SCF_ERROR_NOT_FOUND)
2437 						return (0);
2438 					else
2439 						return (-1);
2440 				}
2441 				continue;
2442 			} else {
2443 				assert(0);
2444 				abort();
2445 			}
2446 		}
2447 
2448 		/*
2449 		 * Discard pgs which don't exist at the right scoping.  This
2450 		 * check also makes sure that if we're looking at, for
2451 		 * example, svc:/system/svc/restarter:default, that we
2452 		 * don't hand back the same property groups twice.
2453 		 */
2454 		switch (t->pt_iter_last) {
2455 		case SCF__TMPL_ITER_INST:
2456 			ret = check_target_match(pg_tmpl->pt_pg,
2457 			    SCF_TM_TARGET_THIS);
2458 			break;
2459 		case SCF__TMPL_ITER_RESTARTER:
2460 			ret = check_target_match(pg_tmpl->pt_pg,
2461 			    SCF_TM_TARGET_DELEGATE);
2462 			break;
2463 		case SCF__TMPL_ITER_GLOBAL:
2464 			ret = check_target_match(pg_tmpl->pt_pg,
2465 			    SCF_TM_TARGET_ALL);
2466 			break;
2467 		case SCF__TMPL_ITER_NONE:
2468 		default:
2469 			assert(0);
2470 			abort();
2471 		}
2472 
2473 		if (ret != 0)
2474 			continue;
2475 
2476 		/*
2477 		 * If walking only required property groups, check if
2478 		 * the retrieved group is required.
2479 		 */
2480 		if (flags & SCF_PG_TMPL_FLAG_REQUIRED) {
2481 			if (scf_tmpl_pg_required(pg_tmpl, &required) == 0) {
2482 				if (required == 0)
2483 					continue;
2484 			} else {
2485 				return (-1);
2486 			}
2487 		}
2488 
2489 		/*
2490 		 * If type != NULL, check if type property matches.  If no
2491 		 * type property exists, consider it a match.
2492 		 */
2493 		if (type != NULL) {
2494 			if (scf_tmpl_pg_type(pg_tmpl, &tmpl_type) != -1) {
2495 				if (strcmp(tmpl_type, SCF_TMPL_WILDCARD)
2496 				    == 0 || strcmp(type, tmpl_type) == 0) {
2497 					free(tmpl_type);
2498 					break;
2499 				}
2500 				free(tmpl_type);
2501 			} else if (scf_error() == SCF_ERROR_NOT_FOUND ||
2502 			    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED ||
2503 			    scf_error() == SCF_ERROR_TYPE_MISMATCH) {
2504 				break;
2505 			} else {
2506 				return (-1);
2507 			}
2508 		} else {
2509 			break;
2510 		}
2511 	}
2512 
2513 	return (1);
2514 
2515 fail_non_populated:
2516 	scf_service_destroy(svc);
2517 	scf_instance_destroy(inst);
2518 	scf_pg_destroy(pg);
2519 	scf_snapshot_destroy(snap);
2520 	return (-1);
2521 }
2522 
2523 void
2524 scf_tmpl_pg_destroy(scf_pg_tmpl_t *t)
2525 {
2526 	if (t == NULL)
2527 		return;
2528 
2529 	scf_pg_destroy(t->pt_pg);
2530 	scf_instance_destroy(t->pt_inst);
2531 	if (t->pt_inst != t->pt_orig_inst)
2532 		scf_instance_destroy(t->pt_orig_inst);
2533 	scf_snapshot_destroy(t->pt_snap);
2534 	scf_service_destroy(t->pt_orig_svc);
2535 	if (t->pt_svc != t->pt_orig_svc)
2536 		scf_service_destroy(t->pt_svc);
2537 	scf_iter_destroy(t->pt_iter);
2538 	free(t);
2539 }
2540 
2541 void
2542 scf_tmpl_pg_reset(scf_pg_tmpl_t *t)
2543 {
2544 	scf_pg_destroy(t->pt_pg);
2545 	t->pt_pg = NULL;
2546 
2547 	scf_instance_destroy(t->pt_inst);
2548 	if (t->pt_inst != t->pt_orig_inst)
2549 		scf_instance_destroy(t->pt_orig_inst);
2550 	t->pt_inst = NULL;
2551 	t->pt_orig_inst = NULL;
2552 
2553 	scf_snapshot_destroy(t->pt_snap);
2554 	t->pt_snap = NULL;
2555 
2556 	scf_service_destroy(t->pt_orig_svc);
2557 	if (t->pt_svc != t->pt_orig_svc)
2558 		scf_service_destroy(t->pt_svc);
2559 	t->pt_orig_svc = NULL;
2560 	t->pt_svc = NULL;
2561 
2562 	scf_iter_destroy(t->pt_iter);
2563 	t->pt_iter = NULL;
2564 
2565 	t->pt_populated = 0;
2566 	t->pt_is_iter = 0;
2567 	t->pt_iter_last = 0;
2568 
2569 	/* Do not reset t->pt_h. */
2570 }
2571 
2572 /*
2573  * int scf_tmpl_get_by_prop()
2574  *
2575  * Get the property template given a property group template and property
2576  * name.  No flags are currently defined for this function.
2577  *
2578  * Returns NULL on failure, and sets scf_error():
2579  *   SCF_ERROR_BACKEND_ACCESS
2580  *   SCF_ERROR_CONNECTION_BROKEN
2581  *   SCF_ERROR_DELETED
2582  *   SCF_ERROR_HANDLE_DESTROYED
2583  *   SCF_ERROR_INTERNAL
2584  *   SCF_ERROR_INVALID_ARGUMENT
2585  *   SCF_ERROR_NO_MEMORY
2586  *   SCF_ERROR_NO_RESOURCES
2587  *   SCF_ERROR_NOT_BOUND
2588  *   SCF_ERROR_NOT_FOUND
2589  *     Template object matching property doesn't exist in the repository.
2590  *   SCF_ERROR_TYPE_MISMATCH
2591  *     Matching template object is the wrong type in the repository.
2592  */
2593 int
2594 scf_tmpl_get_by_prop(scf_pg_tmpl_t *t, const char *prop,
2595     scf_prop_tmpl_t *prop_tmpl, int flags)
2596 {
2597 	char *tmpl_prop_name;
2598 	scf_propertygroup_t *pg = NULL;
2599 	char *pg_type;
2600 	int found = 0;
2601 
2602 	if (flags != 0) {
2603 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2604 		return (-1);
2605 	}
2606 
2607 	scf_tmpl_prop_reset(prop_tmpl);
2608 	if ((pg = scf_pg_create(scf_pg_handle(t->pt_pg))) == NULL)
2609 		return (-1);
2610 
2611 	tmpl_prop_name = _tmpl_prop_name(prop, t);
2612 	if (tmpl_prop_name == NULL) {
2613 		assert(scf_error() != SCF_ERROR_NOT_SET);
2614 		return (-1);
2615 	}
2616 
2617 	if (_get_pg(t->pt_svc, t->pt_inst, t->pt_snap,
2618 	    tmpl_prop_name, pg) != 0) {
2619 		if (!ismember(scf_error(), errors_server)) {
2620 			switch (scf_error()) {
2621 			case SCF_ERROR_NOT_FOUND:
2622 			case SCF_ERROR_INVALID_ARGUMENT:
2623 				break;
2624 
2625 			case SCF_ERROR_NOT_SET:
2626 			case SCF_ERROR_HANDLE_MISMATCH:
2627 			default:
2628 				assert(0);
2629 				abort();
2630 			}
2631 		}
2632 	} else {
2633 		/*
2634 		 * We've only found a template property group if the type
2635 		 * is correct.
2636 		 */
2637 		if ((pg_type = _scf_get_pg_type(pg)) != NULL &&
2638 		    strcmp(pg_type, SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0)
2639 			found++;
2640 		else
2641 			(void) scf_set_error(SCF_ERROR_TYPE_MISMATCH);
2642 
2643 
2644 		free(pg_type);
2645 	}
2646 
2647 	if (found == 0) {
2648 		scf_pg_destroy(pg);
2649 		free(tmpl_prop_name);
2650 		return (-1);
2651 	}
2652 
2653 	prop_tmpl->prt_h = scf_pg_handle(t->pt_pg);
2654 	prop_tmpl->prt_t = t;
2655 	prop_tmpl->prt_pg = pg;
2656 	prop_tmpl->prt_pg_name = tmpl_prop_name;
2657 	prop_tmpl->prt_populated = 1;
2658 
2659 	return (0);
2660 }
2661 
2662 /*
2663  * scf_prop_tmpl_t *scf_tmpl_prop_create(scf_handle_t *);
2664  *
2665  * Returns NULL on failure, sets scf_error() to _INVALID_ARGUMENT, or
2666  * _NO_MEMORY.
2667  */
2668 scf_prop_tmpl_t *
2669 scf_tmpl_prop_create(scf_handle_t *handle)
2670 {
2671 	scf_prop_tmpl_t *pt;
2672 
2673 	if (handle == NULL) {
2674 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2675 		return (NULL);
2676 	}
2677 	pt = calloc(1, sizeof (scf_prop_tmpl_t));
2678 	if (pt == NULL)
2679 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2680 	else
2681 		pt->prt_h = handle;
2682 
2683 	return (pt);
2684 }
2685 
2686 /*
2687  * int scf_tmpl_iter_props()
2688  *
2689  * Iterates over all property templates defined in the specified property
2690  * group template.  The iterator state is stored on the property template
2691  * data structure, and the data structure should be allocated with
2692  * scf_tmpl_prop_create().  To continue the iteration, the previously
2693  * returned structure should be passed in as an argument to this function.
2694  * flags can include SCF_PROP_TMPL_FLAG_REQUIRED.  The function returns
2695  * 1 when a result was found, and 0 when the iteration is complete.
2696  *
2697  * Returns -1 on failure, and sets scf_error():
2698  *   SCF_ERROR_BACKEND_ACCESS
2699  *   SCF_ERROR_CONNECTION_BROKEN
2700  *   SCF_ERROR_DELETED
2701  *   SCF_ERROR_HANDLE_DESTROYED
2702  *   SCF_ERROR_INTERNAL
2703  *   SCF_ERROR_INVALID_ARGUMENT
2704  *   SCF_ERROR_NO_MEMORY
2705  *   SCF_ERROR_NO_RESOURCES
2706  *   SCF_ERROR_NOT_BOUND
2707  *   SCF_ERROR_PERMISSION_DENIED
2708  *   SCF_ERROR_TEMPLATE_INVALID
2709  *     Template data is invalid.  One of the property templates in this
2710  *     pg_tmpl is malformed.
2711  */
2712 int
2713 scf_tmpl_iter_props(scf_pg_tmpl_t *t, scf_prop_tmpl_t *pt, int flags)
2714 {
2715 	scf_prop_tmpl_t *prop_tmpl;
2716 	char *pg_pat;
2717 	char *pg_name = NULL;
2718 	int err;
2719 	int ret;
2720 	ssize_t size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2721 	uint8_t required;
2722 	scf_handle_t *h;
2723 	scf_propertygroup_t *pg = NULL;
2724 	scf_iter_t *iter = NULL;
2725 
2726 	assert(size != 0);
2727 	if (t == NULL || pt == NULL) {
2728 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2729 		return (-1);
2730 	}
2731 
2732 	assert(t->pt_inst == NULL || t->pt_svc == NULL);
2733 	assert(t->pt_inst != NULL || t->pt_svc != NULL);
2734 
2735 	if ((pg_name = malloc(size)) == NULL) {
2736 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2737 		return (-1);
2738 	}
2739 
2740 	if (pt->prt_populated == 0) {
2741 		if ((h = scf_pg_handle(t->pt_pg)) == NULL)
2742 			goto fail_non_populated;
2743 
2744 		if ((pg = scf_pg_create(h)) == NULL ||
2745 		    (iter = scf_iter_create(h)) == NULL)
2746 			goto fail_non_populated;
2747 
2748 		if (t->pt_inst != NULL)
2749 			err = scf_iter_instance_pgs_typed_composed(iter,
2750 			    t->pt_inst, t->pt_snap,
2751 			    SCF_GROUP_TEMPLATE_PROP_PATTERN);
2752 		else if (t->pt_svc != NULL)
2753 			err = scf_iter_service_pgs_typed(iter, t->pt_svc,
2754 			    SCF_GROUP_TEMPLATE_PROP_PATTERN);
2755 
2756 		if (err != 0) {
2757 			if (ismember(scf_error(), errors_server)) {
2758 				goto fail_non_populated;
2759 			} else switch (scf_error()) {
2760 			case SCF_ERROR_INVALID_ARGUMENT:
2761 				goto fail_non_populated;
2762 
2763 			case SCF_ERROR_NOT_SET:
2764 			case SCF_ERROR_HANDLE_MISMATCH:
2765 			default:
2766 				assert(0);
2767 				abort();
2768 			}
2769 
2770 		}
2771 		prop_tmpl = pt;
2772 		prop_tmpl->prt_t = t;
2773 		prop_tmpl->prt_populated = 1;
2774 		prop_tmpl->prt_pg = pg;
2775 		prop_tmpl->prt_iter = iter;
2776 	} else {
2777 		prop_tmpl = pt;
2778 	}
2779 
2780 	while ((err = scf_iter_next_pg(prop_tmpl->prt_iter,
2781 	    prop_tmpl->prt_pg)) > 0) {
2782 		/*
2783 		 * Check if the name matches the appropriate property
2784 		 * group template name.
2785 		 */
2786 		pg_pat = _scf_read_single_astring_from_pg(prop_tmpl->prt_pg,
2787 		    SCF_PROPERTY_TM_PG_PATTERN);
2788 		if (pg_pat == NULL) {
2789 			if (ismember(scf_error(), errors_server)) {
2790 				uu_free(pg_name);
2791 				return (-1);
2792 			} else switch (scf_error()) {
2793 			case SCF_ERROR_NOT_FOUND:
2794 				continue;
2795 
2796 			case SCF_ERROR_CONSTRAINT_VIOLATED:
2797 			case SCF_ERROR_TYPE_MISMATCH:
2798 				(void) scf_set_error(
2799 				    SCF_ERROR_TEMPLATE_INVALID);
2800 				free(pg_name);
2801 				return (-1);
2802 
2803 			case SCF_ERROR_INVALID_ARGUMENT:
2804 			case SCF_ERROR_NOT_SET:
2805 			default:
2806 				assert(0);
2807 				abort();
2808 			}
2809 		}
2810 		if ((ret = scf_pg_get_name(t->pt_pg, pg_name, size)) <= 0) {
2811 			free(pg_pat);
2812 			if (ret == 0)
2813 				continue;
2814 
2815 			if (ismember(scf_error(), errors_server)) {
2816 				free(pg_name);
2817 				return (-1);
2818 			} else {
2819 				assert(0);
2820 				abort();
2821 			}
2822 		}
2823 		if (strcmp(pg_pat, pg_name) != 0) {
2824 			free(pg_pat);
2825 			continue;
2826 		}
2827 		free(pg_pat);
2828 
2829 		/*
2830 		 * If walking only required properties, check if
2831 		 * the retrieved property is required.
2832 		 */
2833 		if (flags & SCF_PROP_TMPL_FLAG_REQUIRED) {
2834 			if (scf_tmpl_prop_required(prop_tmpl, &required) == 0) {
2835 				if (required == 0)
2836 					continue;
2837 			} else {
2838 				free(pg_name);
2839 				return (-1);
2840 			}
2841 		}
2842 		free(pg_name);
2843 		return (0);
2844 	}
2845 
2846 	if (err == -1) {
2847 		if (ismember(scf_error(), errors_server)) {
2848 			free(pg_name);
2849 			return (-1);
2850 		} else {
2851 			assert(0);
2852 			abort();
2853 		}
2854 	} else if (err == 0)  {
2855 		scf_iter_destroy(prop_tmpl->prt_iter);
2856 		prop_tmpl->prt_iter = NULL;
2857 		prop_tmpl->prt_populated = 0;
2858 	}
2859 	free(pg_name);
2860 
2861 	return (1);
2862 
2863 fail_non_populated:
2864 	free(pg_name);
2865 	scf_pg_destroy(pg);
2866 	scf_iter_destroy(iter);
2867 	return (-1);
2868 }
2869 
2870 void
2871 scf_tmpl_prop_destroy(scf_prop_tmpl_t *t)
2872 {
2873 	if (t == NULL)
2874 		return;
2875 
2876 	scf_pg_destroy(t->prt_pg);
2877 	free(t->prt_pg_name);
2878 	free(t->prt_iter);
2879 	free(t);
2880 }
2881 
2882 void
2883 scf_tmpl_prop_reset(scf_prop_tmpl_t *t)
2884 {
2885 	scf_pg_destroy(t->prt_pg);
2886 	t->prt_pg = NULL;
2887 
2888 	free(t->prt_pg_name);
2889 	t->prt_pg_name = NULL;
2890 
2891 	free(t->prt_iter);
2892 	t->prt_iter = NULL;
2893 
2894 	t->prt_populated = 0;
2895 	t->prt_h = NULL;
2896 	t->prt_t = NULL;
2897 }
2898 
2899 /*
2900  * Returns -1 on failure.  Sets scf_error():
2901  *   SCF_ERROR_BACKEND_ACCESS
2902  *   SCF_ERROR_CONNECTION_BROKEN
2903  *   SCF_ERROR_DELETED
2904  *   SCF_ERROR_HANDLE_DESTROYED
2905  *   SCF_ERROR_INTERNAL
2906  *   SCF_ERROR_NO_MEMORY
2907  *   SCF_ERROR_NO_RESOURCES
2908  *   SCF_ERROR_NOT_BOUND
2909  *   SCF_ERROR_PERMISSION_DENIED
2910  *   SCF_ERROR_TEMPLATE_INVALID
2911  *     The name of the template property group (the pname property) has
2912  *     an improper repository format and is not type astring or has
2913  *     more than one value.
2914  */
2915 ssize_t
2916 scf_tmpl_pg_name(const scf_pg_tmpl_t *t, char **out)
2917 {
2918 	return (_scf_tmpl_prop_value(t->pt_pg, SCF_PROPERTY_TM_NAME, out));
2919 }
2920 
2921 /*
2922  * returns an allocated string that must be freed
2923  *
2924  * Returns NULL on failure, sets scf_error() to:
2925  *   SCF_ERROR_BACKEND_ACCESS
2926  *   SCF_ERROR_CONNECTION_BROKEN
2927  *   SCF_ERROR_DELETED
2928  *   SCF_ERROR_HANDLE_DESTROYED
2929  *   SCF_ERROR_INTERNAL
2930  *   SCF_ERROR_INVALID_ARGUMENT
2931  *     name not a valid property name
2932  *     name and locale are too long to make a property name
2933  *   SCF_ERROR_NO_MEMORY
2934  *   SCF_ERROR_NO_RESOURCES
2935  *   SCF_ERROR_NOT_BOUND
2936  *   SCF_ERROR_NOT_FOUND
2937  *     Property doesn't exist or exists and has no value.
2938  *   SCF_ERROR_PERMISSION_DENIED
2939  *   SCF_ERROR_TEMPLATE_INVALID
2940  */
2941 static char *
2942 _read_localized_astring_from_pg(scf_propertygroup_t *pg, const char *name,
2943     const char *locale)
2944 {
2945 	char *str;
2946 	char *lname_prop;
2947 
2948 	str = _add_locale_to_name(name, locale);
2949 	if (str == NULL)
2950 		return (NULL);
2951 	lname_prop = _scf_read_single_astring_from_pg(pg, str);
2952 	if (lname_prop == NULL) {
2953 		free(str);
2954 		if (scf_error() != SCF_ERROR_NOT_FOUND)
2955 			return (NULL);
2956 		str = _add_locale_to_name(name, "C");
2957 		if (str == NULL)
2958 			return (NULL);
2959 		lname_prop = _scf_read_single_astring_from_pg(pg, str);
2960 	}
2961 	free(str);
2962 	if (lname_prop == NULL) {
2963 		if (scf_error() == SCF_ERROR_TYPE_MISMATCH ||
2964 		    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
2965 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2966 	}
2967 	return (lname_prop);
2968 }
2969 
2970 /*
2971  * returns an allocated string that must be freed
2972  *
2973  * Returns -1 on failure, sets scf_error() to:
2974  *   SCF_ERROR_BACKEND_ACCESS
2975  *   SCF_ERROR_CONNECTION_BROKEN
2976  *   SCF_ERROR_DELETED
2977  *   SCF_ERROR_HANDLE_DESTROYED
2978  *   SCF_ERROR_INTERNAL
2979  *   SCF_ERROR_INVALID_ARGUMENT
2980  *     locale is too long to make a valid property name
2981  *   SCF_ERROR_NO_MEMORY
2982  *   SCF_ERROR_NO_RESOURCES
2983  *   SCF_ERROR_NOT_BOUND
2984  *   SCF_ERROR_NOT_FOUND
2985  *     Property doesn't exist or exists and has no value.
2986  *   SCF_ERROR_PERMISSION_DENIED
2987  *   SCF_ERROR_TEMPLATE_INVALID
2988  */
2989 ssize_t
2990 scf_tmpl_pg_common_name(const scf_pg_tmpl_t *t, const char *locale, char **out)
2991 {
2992 	assert(out != NULL);
2993 	if ((*out = _read_localized_astring_from_pg(t->pt_pg,
2994 	    SCF_PROPERTY_TM_COMMON_NAME_PREFIX, locale)) == NULL)
2995 		return (-1);
2996 
2997 	return (strlen(*out));
2998 }
2999 
3000 /*
3001  * returns an allocated string that must be freed
3002  *
3003  * Returns -1 on failure, sets scf_error() to:
3004  *   SCF_ERROR_BACKEND_ACCESS
3005  *   SCF_ERROR_CONNECTION_BROKEN
3006  *   SCF_ERROR_DELETED
3007  *   SCF_ERROR_HANDLE_DESTROYED
3008  *   SCF_ERROR_INTERNAL
3009  *   SCF_ERROR_INVALID_ARGUMENT
3010  *     locale is too long to make a valid property name
3011  *   SCF_ERROR_NO_MEMORY
3012  *   SCF_ERROR_NO_RESOURCES
3013  *   SCF_ERROR_NOT_BOUND
3014  *   SCF_ERROR_NOT_FOUND
3015  *     Property doesn't exist or exists and has no value.
3016  *   SCF_ERROR_PERMISSION_DENIED
3017  *   SCF_ERROR_TEMPLATE_INVALID
3018  */
3019 ssize_t
3020 scf_tmpl_pg_description(const scf_pg_tmpl_t *t, const char *locale, char **out)
3021 {
3022 	assert(out != NULL);
3023 	if ((*out = _read_localized_astring_from_pg(t->pt_pg,
3024 	    SCF_PROPERTY_TM_DESCRIPTION_PREFIX, locale)) == NULL)
3025 		return (-1);
3026 
3027 	return (strlen(*out));
3028 }
3029 
3030 /*
3031  * Returns -1 on failure.  Sets scf_error():
3032  *   SCF_ERROR_BACKEND_ACCESS
3033  *   SCF_ERROR_CONNECTION_BROKEN
3034  *   SCF_ERROR_DELETED
3035  *   SCF_ERROR_HANDLE_DESTROYED
3036  *   SCF_ERROR_INTERNAL
3037  *   SCF_ERROR_NO_MEMORY
3038  *   SCF_ERROR_NO_RESOURCES
3039  *   SCF_ERROR_NOT_BOUND
3040  *   SCF_ERROR_NOT_FOUND
3041  *     'type' property doesn't exist or exists and has no value.
3042  *   SCF_ERROR_PERMISSION_DENIED
3043  *   SCF_ERROR_TEMPLATE_INVALID
3044  *     'type' property is not SCF_TYPE_ASTRING or has more than one value.
3045  */
3046 ssize_t
3047 scf_tmpl_pg_type(const scf_pg_tmpl_t *t, char **out)
3048 {
3049 	return (_scf_tmpl_prop_value(t->pt_pg, SCF_PROPERTY_TM_TYPE, out));
3050 }
3051 
3052 /*
3053  * Returns -1 on failure, sets scf_error() to:
3054  *   SCF_ERROR_BACKEND_ACCESS
3055  *   SCF_ERROR_CONNECTION_BROKEN
3056  *   SCF_ERROR_DELETED
3057  *   SCF_ERROR_HANDLE_DESTROYED
3058  *   SCF_ERROR_INTERNAL
3059  *   SCF_ERROR_NO_MEMORY
3060  *   SCF_ERROR_NO_RESOURCES
3061  *   SCF_ERROR_NOT_BOUND
3062  *   SCF_ERROR_PERMISSION_DENIED
3063  *   SCF_ERROR_TEMPLATE_INVALID
3064  *     required property is not SCF_TYPE_BOOLEAN or has more than one value.
3065  */
3066 int
3067 scf_tmpl_pg_required(const scf_pg_tmpl_t *t, uint8_t *out)
3068 {
3069 
3070 	if (_read_single_boolean_from_pg(t->pt_pg, SCF_PROPERTY_TM_REQUIRED,
3071 	    out) == -1) {
3072 		if (ismember(scf_error(), errors_server)) {
3073 			return (-1);
3074 		} else switch (scf_error()) {
3075 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3076 		case SCF_ERROR_TYPE_MISMATCH:
3077 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3078 			return (-1);
3079 
3080 		case SCF_ERROR_NOT_FOUND:
3081 			*out = 0;
3082 			return (0);
3083 
3084 		case SCF_ERROR_INVALID_ARGUMENT:
3085 		default:
3086 			assert(0);
3087 			abort();
3088 		}
3089 	}
3090 
3091 	return (0);
3092 }
3093 
3094 /*
3095  * Returns -1 on failure.  Sets scf_error():
3096  *   SCF_ERROR_BACKEND_ACCESS
3097  *   SCF_ERROR_CONNECTION_BROKEN
3098  *   SCF_ERROR_DELETED
3099  *   SCF_ERROR_HANDLE_DESTROYED
3100  *   SCF_ERROR_INTERNAL
3101  *   SCF_ERROR_NO_MEMORY
3102  *   SCF_ERROR_NO_RESOURCES
3103  *   SCF_ERROR_NOT_BOUND
3104  *   SCF_ERROR_PERMISSION_DENIED
3105  *   SCF_ERROR_TEMPLATE_INVALID
3106  *     target property is not SCF_TYPE_ASTRING or has more than one value.
3107  */
3108 ssize_t
3109 scf_tmpl_pg_target(const scf_pg_tmpl_t *t, char **out)
3110 {
3111 	*out = _scf_read_single_astring_from_pg(t->pt_pg,
3112 	    SCF_PROPERTY_TM_TARGET);
3113 
3114 	if (*out == NULL) {
3115 		if (ismember(scf_error(), errors_server)) {
3116 			return (-1);
3117 		} else switch (scf_error()) {
3118 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3119 		case SCF_ERROR_NOT_FOUND:
3120 		case SCF_ERROR_TYPE_MISMATCH:
3121 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3122 			return (-1);
3123 
3124 		case SCF_ERROR_INVALID_ARGUMENT:
3125 		case SCF_ERROR_NOT_SET:
3126 		default:
3127 			assert(0);
3128 			abort();
3129 		}
3130 	}
3131 
3132 	return (strlen(*out));
3133 }
3134 
3135 /*
3136  * Returns -1 on failure.  Sets scf_error():
3137  *   SCF_ERROR_BACKEND_ACCESS
3138  *   SCF_ERROR_CONNECTION_BROKEN
3139  *   SCF_ERROR_DELETED
3140  *   SCF_ERROR_HANDLE_DESTROYED
3141  *   SCF_ERROR_INTERNAL
3142  *   SCF_ERROR_NO_MEMORY
3143  *   SCF_ERROR_NO_RESOURCES
3144  *   SCF_ERROR_NOT_BOUND
3145  *   SCF_ERROR_PERMISSION_DENIED
3146  *   SCF_ERROR_TEMPLATE_INVALID
3147  */
3148 ssize_t
3149 scf_tmpl_prop_name(const scf_prop_tmpl_t *t, char **out)
3150 {
3151 	*out = _scf_read_single_astring_from_pg(t->prt_pg,
3152 	    SCF_PROPERTY_TM_NAME);
3153 
3154 	if (*out != NULL && *out[0] == '\0') {
3155 		free(*out);
3156 		*out = NULL;
3157 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3158 	}
3159 	if (*out == NULL) {
3160 		if (ismember(scf_error(), errors_server)) {
3161 			return (-1);
3162 		} else switch (scf_error()) {
3163 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3164 		case SCF_ERROR_NOT_FOUND:
3165 		case SCF_ERROR_TEMPLATE_INVALID:
3166 		case SCF_ERROR_TYPE_MISMATCH:
3167 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3168 			return (-1);
3169 
3170 		case SCF_ERROR_INVALID_ARGUMENT:
3171 		case SCF_ERROR_NOT_SET:
3172 		default:
3173 			assert(0);
3174 			abort();
3175 		}
3176 	}
3177 
3178 	return (strlen(*out));
3179 }
3180 
3181 /*
3182  * Returns -1 on failure.  Sets scf_error():
3183  *   SCF_ERROR_BACKEND_ACCESS
3184  *   SCF_ERROR_CONNECTION_BROKEN
3185  *   SCF_ERROR_DELETED
3186  *   SCF_ERROR_HANDLE_DESTROYED
3187  *   SCF_ERROR_INTERNAL
3188  *   SCF_ERROR_NO_MEMORY
3189  *   SCF_ERROR_NO_RESOURCES
3190  *   SCF_ERROR_NOT_BOUND
3191  *   SCF_ERROR_NOT_FOUND
3192  *     'type' property doesn't exist or exists and has no value.
3193  *   SCF_ERROR_PERMISSION_DENIED
3194  *   SCF_ERROR_TEMPLATE_INVALID
3195  *     'type' property is not SCF_TYPE_ASTRING, has more than one value,
3196  *     is SCF_TYPE_INVALID, or is the empty string.
3197  */
3198 int
3199 scf_tmpl_prop_type(const scf_prop_tmpl_t *t, scf_type_t *out)
3200 {
3201 	char *type;
3202 
3203 	type = _scf_read_single_astring_from_pg(t->prt_pg,
3204 	    SCF_PROPERTY_TM_TYPE);
3205 	if (type != NULL && type[0] == '\0') {
3206 		free(type);
3207 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3208 		return (-1);
3209 	}
3210 	if (type == NULL) {
3211 		if (ismember(scf_error(), errors_server)) {
3212 			return (-1);
3213 		} else switch (scf_error()) {
3214 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3215 		case SCF_ERROR_TYPE_MISMATCH:
3216 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3217 			/*FALLTHROUGH*/
3218 
3219 		case SCF_ERROR_NOT_FOUND:
3220 			return (-1);
3221 
3222 		case SCF_ERROR_INVALID_ARGUMENT:
3223 		case SCF_ERROR_NOT_SET:
3224 		default:
3225 			assert(0);
3226 			abort();
3227 		}
3228 	}
3229 
3230 	*out = scf_string_to_type(type);
3231 	free(type);
3232 
3233 	if (*out == SCF_TYPE_INVALID) {
3234 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3235 		return (-1);
3236 	}
3237 
3238 	return (0);
3239 }
3240 
3241 /*
3242  * Returns -1 on failure, sets scf_error() to:
3243  *   SCF_ERROR_BACKEND_ACCESS
3244  *   SCF_ERROR_CONNECTION_BROKEN
3245  *   SCF_ERROR_DELETED
3246  *    Property group which represents t was deleted.
3247  *   SCF_ERROR_HANDLE_DESTROYED
3248  *   SCF_ERROR_INTERNAL
3249  *   SCF_ERROR_NO_MEMORY
3250  *   SCF_ERROR_NO_RESOURCES
3251  *   SCF_ERROR_NOT_BOUND
3252  *   SCF_ERROR_PERMISSION_DENIED
3253  *   SCF_ERROR_TEMPLATE_INVALID
3254  *     required property is not SCF_TYPE_ASTRING has more than one value.
3255  */
3256 int
3257 scf_tmpl_prop_required(const scf_prop_tmpl_t *t, uint8_t *out)
3258 {
3259 	if (_read_single_boolean_from_pg(t->prt_pg, SCF_PROPERTY_TM_REQUIRED,
3260 	    out) == -1) {
3261 		if (ismember(scf_error(), errors_server)) {
3262 			return (-1);
3263 		} else switch (scf_error()) {
3264 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3265 		case SCF_ERROR_TYPE_MISMATCH:
3266 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3267 			return (-1);
3268 
3269 		case SCF_ERROR_NOT_FOUND:
3270 			*out = 0;
3271 			return (0);
3272 
3273 		case SCF_ERROR_INVALID_ARGUMENT:
3274 		case SCF_ERROR_NOT_SET:
3275 		default:
3276 			assert(0);
3277 			abort();
3278 		}
3279 	}
3280 
3281 	return (0);
3282 }
3283 
3284 /*
3285  * Returns -1 on failure.  Sets scf_error():
3286  *   SCF_ERROR_BACKEND_ACCESS
3287  *   SCF_ERROR_CONNECTION_BROKEN
3288  *   SCF_ERROR_DELETED
3289  *   SCF_ERROR_HANDLE_DESTROYED
3290  *   SCF_ERROR_INTERNAL
3291  *   SCF_ERROR_NO_MEMORY
3292  *   SCF_ERROR_NO_RESOURCES
3293  *   SCF_ERROR_NOT_BOUND
3294  *   SCF_ERROR_NOT_FOUND
3295  *     Property doesn't exist or exists and has no value.
3296  *   SCF_ERROR_INVALID_ARGUMENT
3297  *     locale is too long to make a property name
3298  *   SCF_ERROR_PERMISSION_DENIED
3299  *   SCF_ERROR_TEMPLATE_INVALID
3300  *     common_name property is not SCF_TYPE_ASTRING has more than one value.
3301  */
3302 ssize_t
3303 scf_tmpl_prop_common_name(const scf_prop_tmpl_t *t, const char *locale,
3304     char **out)
3305 {
3306 	assert(out != NULL);
3307 	if ((*out = _read_localized_astring_from_pg(t->prt_pg,
3308 	    SCF_PROPERTY_TM_COMMON_NAME_PREFIX, locale)) == NULL)
3309 		return (-1);
3310 
3311 	return (strlen(*out));
3312 }
3313 
3314 /*
3315  * Returns -1 on failure.  Sets scf_error():
3316  *   SCF_ERROR_BACKEND_ACCESS
3317  *   SCF_ERROR_CONNECTION_BROKEN
3318  *   SCF_ERROR_DELETED
3319  *   SCF_ERROR_HANDLE_DESTROYED
3320  *   SCF_ERROR_INTERNAL
3321  *   SCF_ERROR_NO_MEMORY
3322  *   SCF_ERROR_NO_RESOURCES
3323  *   SCF_ERROR_NOT_BOUND
3324  *   SCF_ERROR_NOT_FOUND
3325  *     Property doesn't exist or exists and has no value.
3326  *   SCF_ERROR_INVALID_ARGUMENT
3327  *     locale is too long to make a property name
3328  *   SCF_ERROR_PERMISSION_DENIED
3329  *   SCF_ERROR_TEMPLATE_INVALID
3330  *     description property is not SCF_TYPE_ASTRING has more than one value.
3331  */
3332 ssize_t
3333 scf_tmpl_prop_description(const scf_prop_tmpl_t *t, const char *locale,
3334     char **out)
3335 {
3336 	assert(out != NULL);
3337 	if ((*out = _read_localized_astring_from_pg(t->prt_pg,
3338 	    SCF_PROPERTY_TM_DESCRIPTION_PREFIX, locale)) == NULL)
3339 		return (-1);
3340 
3341 	return (strlen(*out));
3342 }
3343 
3344 /*
3345  * Returns -1 on failure.  Sets scf_error():
3346  *   SCF_ERROR_BACKEND_ACCESS
3347  *   SCF_ERROR_CONNECTION_BROKEN
3348  *   SCF_ERROR_DELETED
3349  *   SCF_ERROR_HANDLE_DESTROYED
3350  *   SCF_ERROR_INTERNAL
3351  *   SCF_ERROR_NO_MEMORY
3352  *   SCF_ERROR_NO_RESOURCES
3353  *   SCF_ERROR_NOT_BOUND
3354  *   SCF_ERROR_NOT_FOUND
3355  *     Property doesn't exist or exists and has no value.
3356  *   SCF_ERROR_INVALID_ARGUMENT
3357  *     locale is too long to make a property name
3358  *   SCF_ERROR_PERMISSION_DENIED
3359  *   SCF_ERROR_TEMPLATE_INVALID
3360  *     units property is not SCF_TYPE_ASTRING has more than one value.
3361  */
3362 ssize_t
3363 scf_tmpl_prop_units(const scf_prop_tmpl_t *t, const char *locale, char **out)
3364 {
3365 	assert(out != NULL);
3366 	if ((*out = _read_localized_astring_from_pg(t->prt_pg,
3367 	    SCF_PROPERTY_TM_UNITS_PREFIX, locale)) == NULL)
3368 		return (-1);
3369 
3370 	return (strlen(*out));
3371 }
3372 
3373 /*
3374  * Returns -1 on failure.  Sets scf_error():
3375  *   SCF_ERROR_BACKEND_ACCESS
3376  *   SCF_ERROR_CONNECTION_BROKEN
3377  *   SCF_ERROR_DELETED
3378  *   SCF_ERROR_HANDLE_DESTROYED
3379  *   SCF_ERROR_INTERNAL
3380  *   SCF_ERROR_NO_MEMORY
3381  *   SCF_ERROR_NO_RESOURCES
3382  *   SCF_ERROR_NOT_BOUND
3383  *   SCF_ERROR_PERMISSION_DENIED
3384  *   SCF_ERROR_TEMPLATE_INVALID
3385  *     visibility property is not SCF_TYPE_ASTRING has more than one value.
3386  */
3387 int
3388 scf_tmpl_prop_visibility(const scf_prop_tmpl_t *t, uint8_t *out)
3389 {
3390 	char *visibility;
3391 
3392 	visibility = _scf_read_single_astring_from_pg(t->prt_pg,
3393 	    SCF_PROPERTY_TM_VISIBILITY);
3394 	if (visibility == NULL) {
3395 		if (ismember(scf_error(), errors_server)) {
3396 			return (-1);
3397 		} else switch (scf_error()) {
3398 		/* prop doesn't exist we take the default value */
3399 		case SCF_ERROR_NOT_FOUND:
3400 			*out = SCF_TMPL_VISIBILITY_READWRITE;
3401 			return (0);
3402 
3403 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3404 		case SCF_ERROR_TYPE_MISMATCH:
3405 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3406 			return (-1);
3407 
3408 		case SCF_ERROR_INVALID_ARGUMENT:
3409 		case SCF_ERROR_NOT_SET:
3410 		default:
3411 			assert(0);
3412 			abort();
3413 		}
3414 	} else if (strcmp(visibility, SCF_TM_VISIBILITY_READWRITE) == 0) {
3415 		*out = SCF_TMPL_VISIBILITY_READWRITE;
3416 	} else if (strcmp(visibility, SCF_TM_VISIBILITY_HIDDEN) == 0) {
3417 		*out = SCF_TMPL_VISIBILITY_HIDDEN;
3418 	} else if (strcmp(visibility, SCF_TM_VISIBILITY_READONLY) == 0) {
3419 		*out = SCF_TMPL_VISIBILITY_READONLY;
3420 	} else {
3421 		free(visibility);
3422 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3423 		return (-1);
3424 	}
3425 
3426 	free(visibility);
3427 	return (0);
3428 }
3429 
3430 /*
3431  * Return an allocated string containing the value that must be freed
3432  * with free().
3433  *
3434  * On error set scf_error() _NO_MEMORY, or _NOT_SET (val has not been set
3435  * to a value).
3436  */
3437 static char *
3438 _scf_value_get_as_string(scf_value_t *val)
3439 {
3440 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
3441 	char *buf = malloc(sz);
3442 
3443 	if (buf == NULL) {
3444 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3445 	} else if (scf_value_get_as_string(val, buf, sz) == -1) {
3446 		free(buf);
3447 		buf = NULL;
3448 	}
3449 
3450 	return (buf);
3451 }
3452 
3453 /*
3454  * Returns -1 on failure, sets scf_error() to:
3455  *   SCF_ERROR_BACKEND_ACCESS
3456  *   SCF_ERROR_CONNECTION_BROKEN
3457  *   SCF_ERROR_DELETED
3458  *   SCF_ERROR_HANDLE_DESTROYED
3459  *   SCF_ERROR_INTERNAL
3460  *   SCF_ERROR_NO_MEMORY
3461  *   SCF_ERROR_NO_RESOURCES
3462  *   SCF_ERROR_NOT_BOUND
3463  *   SCF_ERROR_NOT_FOUND
3464  *   SCF_ERROR_PERMISSION_DENIED
3465  *   SCF_ERROR_TEMPLATE_INVALID
3466  */
3467 int
3468 scf_tmpl_prop_cardinality(const scf_prop_tmpl_t *t, uint64_t *min,
3469     uint64_t *max)
3470 {
3471 	scf_value_t *val_min = NULL;
3472 	scf_value_t *val_max = NULL;
3473 	int ret = 0;
3474 
3475 	if (_read_single_value_from_pg(t->prt_pg,
3476 	    SCF_PROPERTY_TM_CARDINALITY_MIN, &val_min) == 0) {
3477 		if (scf_value_get_count(val_min, min) < 0)
3478 			goto error;
3479 	} else {
3480 		if (scf_error() == SCF_ERROR_NOT_FOUND)
3481 			*min = 0;
3482 		else
3483 			goto error;
3484 	}
3485 
3486 	if (_read_single_value_from_pg(t->prt_pg,
3487 	    SCF_PROPERTY_TM_CARDINALITY_MAX, &val_max) == 0) {
3488 		if (scf_value_get_count(val_max, max) < 0)
3489 			goto error;
3490 	} else {
3491 		if (scf_error() == SCF_ERROR_NOT_FOUND)
3492 			*max = UINT64_MAX;
3493 		else
3494 			goto error;
3495 	}
3496 	goto cleanup;
3497 
3498 error:
3499 	if (ismember(scf_error(), errors_server)) {
3500 		ret = -1;
3501 	} else switch (scf_error()) {
3502 	case SCF_ERROR_TYPE_MISMATCH:
3503 	case SCF_ERROR_CONSTRAINT_VIOLATED:
3504 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3505 		/*FALLTHROUGH*/
3506 
3507 	case SCF_ERROR_NOT_FOUND:
3508 	case SCF_ERROR_TEMPLATE_INVALID:
3509 		ret = -1;
3510 		break;
3511 
3512 	case SCF_ERROR_NOT_SET:
3513 	case SCF_ERROR_INVALID_ARGUMENT:
3514 	default:
3515 		assert(0);
3516 		abort();
3517 	}
3518 
3519 cleanup:
3520 	scf_value_destroy(val_min);
3521 	scf_value_destroy(val_max);
3522 
3523 	return (ret);
3524 }
3525 
3526 /*
3527  * Returns -1 on failure.  Sets scf_error():
3528  *   SCF_ERROR_BACKEND_ACCESS
3529  *   SCF_ERROR_CONNECTION_BROKEN
3530  *   SCF_ERROR_DELETED
3531  *   SCF_ERROR_HANDLE_DESTROYED
3532  *   SCF_ERROR_INTERNAL
3533  *   SCF_ERROR_NO_MEMORY
3534  *   SCF_ERROR_NO_RESOURCES
3535  *   SCF_ERROR_NOT_BOUND
3536  *   SCF_ERROR_NOT_FOUND
3537  *     Property doesn't exist or exists and has no value.
3538  *   SCF_ERROR_PERMISSION_DENIED
3539  *   SCF_ERROR_TEMPLATE_INVALID
3540  */
3541 int
3542 scf_tmpl_prop_internal_seps(const scf_prop_tmpl_t *t, scf_values_t *vals)
3543 {
3544 	if (_read_astrings_values(t->prt_pg,
3545 	    SCF_PROPERTY_INTERNAL_SEPARATORS, vals) == NULL) {
3546 		if (ismember(scf_error(), errors_server)) {
3547 			return (-1);
3548 		} else switch (scf_error()) {
3549 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3550 		case SCF_ERROR_TYPE_MISMATCH:
3551 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3552 			/*FALLTHROUGH*/
3553 
3554 		case SCF_ERROR_NOT_FOUND:
3555 			return (-1);
3556 
3557 		case SCF_ERROR_INVALID_ARGUMENT:
3558 		case SCF_ERROR_NOT_SET:
3559 		default:
3560 			assert(0);
3561 			abort();
3562 		}
3563 	} else if (vals->value_count == 0) {
3564 		/* property has no value */
3565 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3566 		scf_values_destroy(vals);
3567 		return (-1);
3568 	}
3569 
3570 	return (0);
3571 }
3572 
3573 /*
3574  * Returns -1 on failure.  Sets scf_error():
3575  *   SCF_ERROR_BACKEND_ACCESS
3576  *   SCF_ERROR_CONNECTION_BROKEN
3577  *   SCF_ERROR_DELETED
3578  *   SCF_ERROR_HANDLE_DESTROYED
3579  *   SCF_ERROR_INTERNAL
3580  *   SCF_ERROR_NO_MEMORY
3581  *   SCF_ERROR_NO_RESOURCES
3582  *   SCF_ERROR_NOT_BOUND
3583  *   SCF_ERROR_NOT_FOUND
3584  *     Property doesn't exist or exists and has no value.
3585  *   SCF_ERROR_PERMISSION_DENIED
3586  *   SCF_ERROR_TEMPLATE_INVALID
3587  */
3588 int
3589 scf_tmpl_value_name_constraints(const scf_prop_tmpl_t *t,
3590     scf_values_t *vals)
3591 {
3592 	char **ret;
3593 
3594 	ret = _read_astrings_values(t->prt_pg,
3595 	    SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
3596 
3597 	if (ret == NULL) {
3598 		if (ismember(scf_error(), errors_server)) {
3599 			return (-1);
3600 		} else switch (scf_error()) {
3601 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3602 		case SCF_ERROR_TYPE_MISMATCH:
3603 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3604 			/*FALLTHROUGH*/
3605 
3606 		case SCF_ERROR_NOT_FOUND:
3607 			return (-1);
3608 
3609 		case SCF_ERROR_INVALID_ARGUMENT:
3610 		case SCF_ERROR_NOT_SET:
3611 		default:
3612 			assert(0);
3613 			abort();
3614 		}
3615 	} else if (vals->value_count == 0) {
3616 		/* property has no value */
3617 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3618 		scf_values_destroy(vals);
3619 		return (-1);
3620 	}
3621 
3622 	return (0);
3623 }
3624 
3625 /*
3626  * Returns NULL on failure.  Sets scf_error():
3627  * Caller is responsible for freeing returned pointer after use.
3628  *   SCF_ERROR_CONSTRAINT_VIOLATED
3629  *    More tokens than the array size supplied.
3630  *   SCF_ERROR_NO_MEMORY
3631  */
3632 static void *
3633 _separate_by_separator(char *string, const char *sep, char **array, int size)
3634 {
3635 	char *str, *token;
3636 	char *lasts;
3637 	int n = 0;
3638 
3639 	assert(array != NULL);
3640 	assert(string != NULL);
3641 	assert(sep != NULL);
3642 	assert(size > 0);
3643 
3644 	str = strdup(string);
3645 	if (str == NULL) {
3646 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3647 		return (NULL);
3648 	}
3649 
3650 	if ((array[n] = strtok_r(str, sep, &lasts)) == NULL) {
3651 		assert(0);
3652 		abort();
3653 	}
3654 
3655 	n++;
3656 	while ((token = strtok_r(NULL, sep, &lasts)) != NULL) {
3657 		if (n >= size) {
3658 			goto error;
3659 		}
3660 		array[n] = token;
3661 		n++;
3662 	}
3663 	if (n < size) {
3664 		goto error;
3665 	}
3666 
3667 	return (str);
3668 error:
3669 	free(str);
3670 	(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3671 	return (NULL);
3672 }
3673 
3674 /*
3675  * check if name is among values of CHOICES_INCLUDE_VALUES
3676  * return 0 if name is present, 1 name is not present, -1 on failure
3677  *   SCF_ERROR_BACKEND_ACCESS
3678  *   SCF_ERROR_CONNECTION_BROKEN
3679  *   SCF_ERROR_DELETED
3680  *   SCF_ERROR_HANDLE_DESTROYED
3681  *   SCF_ERROR_INTERNAL
3682  *   SCF_ERROR_NO_MEMORY
3683  *   SCF_ERROR_NO_RESOURCES
3684  *   SCF_ERROR_NOT_BOUND
3685  *   SCF_ERROR_PERMISSION_DENIED
3686  *   SCF_ERROR_TEMPLATE_INVALID
3687  */
3688 static int
3689 _check_choices_include_values(scf_propertygroup_t *pg, const char *name)
3690 {
3691 	int n = 0, r = 1;
3692 	char **ret;
3693 	scf_values_t vals;
3694 
3695 	if ((ret = _read_astrings_values(pg,
3696 	    SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES, &vals)) == NULL) {
3697 		if (ismember(scf_error(), errors_server)) {
3698 			return (-1);
3699 		} else switch (scf_error()) {
3700 		case SCF_ERROR_NOT_FOUND:
3701 			return (1);
3702 
3703 		case SCF_ERROR_TYPE_MISMATCH:
3704 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3705 			return (-1);
3706 
3707 		case SCF_ERROR_INVALID_ARGUMENT:
3708 		case SCF_ERROR_NOT_SET:
3709 		default:
3710 			assert(0);
3711 			abort();
3712 		}
3713 	}
3714 
3715 	for (n = 0; n < vals.value_count; ++n) {
3716 		if (strcmp(name, ret[n]) == 0) {
3717 			r = 0;
3718 			break;
3719 		}
3720 	}
3721 	scf_values_destroy(&vals);
3722 	return (r);
3723 }
3724 
3725 void
3726 scf_count_ranges_destroy(scf_count_ranges_t *ranges)
3727 {
3728 	if (ranges == NULL)
3729 		return;
3730 
3731 	ranges->scr_num_ranges = 0;
3732 	free(ranges->scr_min);
3733 	free(ranges->scr_max);
3734 	ranges->scr_min = NULL;
3735 	ranges->scr_max = NULL;
3736 }
3737 
3738 void
3739 scf_int_ranges_destroy(scf_int_ranges_t *ranges)
3740 {
3741 	if (ranges == NULL)
3742 		return;
3743 
3744 	ranges->sir_num_ranges = 0;
3745 	free(ranges->sir_min);
3746 	free(ranges->sir_max);
3747 	ranges->sir_min = NULL;
3748 	ranges->sir_max = NULL;
3749 }
3750 
3751 /*
3752  * Returns -1 on failure.  Sets scf_error():
3753  *   SCF_ERROR_BACKEND_ACCESS
3754  *   SCF_ERROR_CONNECTION_BROKEN
3755  *   SCF_ERROR_CONSTRAINT_VIOLATED
3756  *   SCF_ERROR_DELETED
3757  *   SCF_ERROR_HANDLE_DESTROYED
3758  *   SCF_ERROR_INTERNAL
3759  *   SCF_ERROR_NO_MEMORY
3760  *   SCF_ERROR_NO_RESOURCES
3761  *   SCF_ERROR_NOT_BOUND
3762  *   SCF_ERROR_NOT_FOUND
3763  *     Property doesn't exist or exists and has no value.
3764  *   SCF_ERROR_PERMISSION_DENIED
3765  *   SCF_ERROR_TEMPLATE_INVALID
3766  */
3767 static int
3768 _scf_tmpl_get_count_ranges(const scf_prop_tmpl_t *t, const char *prop,
3769     scf_count_ranges_t *ranges)
3770 {
3771 	scf_values_t vals;
3772 	int i = 0;
3773 	char **ret;
3774 	char *one_range[2];
3775 	char *endptr;
3776 	char *str = NULL;
3777 	uint64_t *min = NULL;
3778 	uint64_t *max = NULL;
3779 
3780 	assert(ranges != NULL);
3781 	if ((ret = _read_astrings_values(t->prt_pg, prop, &vals)) == NULL)
3782 		goto error;
3783 	if (vals.value_count == 0) {
3784 		/* range values are empty */
3785 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3786 		goto cleanup;
3787 	}
3788 
3789 	min = malloc(vals.value_count * sizeof (uint64_t));
3790 	max = malloc(vals.value_count * sizeof (uint64_t));
3791 	if (min == NULL || max == NULL) {
3792 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3793 		goto cleanup;
3794 	}
3795 	for (i = 0; i < vals.value_count; ++i) {
3796 		/* min and max should be separated by a "," */
3797 		if ((str = _separate_by_separator(ret[i], ",", one_range,
3798 		    2)) == NULL)
3799 			goto cleanup;
3800 		errno = 0;
3801 		min[i] = strtoull(one_range[0], &endptr, 10);
3802 		if (errno != 0 || endptr == one_range[0] || *endptr) {
3803 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3804 			goto cleanup;
3805 		}
3806 		errno = 0;
3807 		max[i] = strtoull(one_range[1], &endptr, 10);
3808 		if (errno != 0 || endptr == one_range[1] || *endptr) {
3809 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3810 			goto cleanup;
3811 		}
3812 		if (min[i] > max[i]) {
3813 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3814 			goto cleanup;
3815 		}
3816 		free(str);
3817 		str = NULL;
3818 	}
3819 	ranges->scr_num_ranges = vals.value_count;
3820 	ranges->scr_min = min;
3821 	ranges->scr_max = max;
3822 	scf_values_destroy(&vals);
3823 	return (0);
3824 cleanup:
3825 	free(str);
3826 	free(min);
3827 	free(max);
3828 	scf_values_destroy(&vals);
3829 error:
3830 	if (ismember(scf_error(), errors_server)) {
3831 		return (-1);
3832 	} else switch (scf_error()) {
3833 	case SCF_ERROR_TYPE_MISMATCH:
3834 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3835 		/*FALLTHROUGH*/
3836 
3837 	case SCF_ERROR_CONSTRAINT_VIOLATED:
3838 	case SCF_ERROR_NOT_FOUND:
3839 		return (-1);
3840 
3841 	case SCF_ERROR_INVALID_ARGUMENT:
3842 	case SCF_ERROR_NOT_SET:
3843 	default:
3844 		assert(0);
3845 		abort();
3846 	}
3847 	/*NOTREACHED*/
3848 }
3849 
3850 /*
3851  * Returns -1 on failure.  Sets scf_error():
3852  *   SCF_ERROR_BACKEND_ACCESS
3853  *   SCF_ERROR_CONNECTION_BROKEN
3854  *   SCF_ERROR_CONSTRAINT_VIOLATED
3855  *   SCF_ERROR_DELETED
3856  *   SCF_ERROR_HANDLE_DESTROYED
3857  *   SCF_ERROR_INTERNAL
3858  *   SCF_ERROR_NO_MEMORY
3859  *   SCF_ERROR_NO_RESOURCES
3860  *   SCF_ERROR_NOT_BOUND
3861  *   SCF_ERROR_NOT_FOUND
3862  *     Property doesn't exist or exists and has no value.
3863  *   SCF_ERROR_PERMISSION_DENIED
3864  *   SCF_ERROR_TEMPLATE_INVALID
3865  */
3866 static int
3867 _scf_tmpl_get_int_ranges(const scf_prop_tmpl_t *t, const char *prop,
3868     scf_int_ranges_t *ranges)
3869 {
3870 	scf_values_t vals;
3871 	int n = 0;
3872 	char **ret;
3873 	char *one_range[2];
3874 	char *endptr;
3875 	char *str = NULL;
3876 	int64_t *min = NULL;
3877 	int64_t *max = NULL;
3878 
3879 	assert(ranges != NULL);
3880 	if ((ret = _read_astrings_values(t->prt_pg, prop, &vals)) == NULL)
3881 		goto error;
3882 	if (vals.value_count == 0) {
3883 		/* range values are empty */
3884 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3885 		goto cleanup;
3886 	}
3887 
3888 	min = malloc(vals.value_count * sizeof (int64_t));
3889 	max = malloc(vals.value_count * sizeof (int64_t));
3890 	if (min == NULL || max == NULL) {
3891 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3892 		goto cleanup;
3893 	}
3894 	while (n < vals.value_count) {
3895 		/* min and max should be separated by a "," */
3896 		if ((str = _separate_by_separator(ret[n], ",", one_range, 2))
3897 		    == NULL)
3898 			goto cleanup;
3899 		errno = 0;
3900 		min[n] = strtoll(one_range[0], &endptr, 10);
3901 		if (errno != 0 || endptr == one_range[0] || *endptr) {
3902 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3903 			goto cleanup;
3904 		}
3905 		errno = 0;
3906 		max[n] = strtoll(one_range[1], &endptr, 10);
3907 		if (errno != 0 || endptr == one_range[1] || *endptr) {
3908 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3909 			goto cleanup;
3910 		}
3911 		if (min[n] > max[n]) {
3912 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3913 			goto cleanup;
3914 		}
3915 		++n;
3916 		free(str);
3917 		str = NULL;
3918 	}
3919 	ranges->sir_num_ranges = vals.value_count;
3920 	ranges->sir_min = min;
3921 	ranges->sir_max = max;
3922 	scf_values_destroy(&vals);
3923 	return (0);
3924 cleanup:
3925 	free(str);
3926 	free(min);
3927 	free(max);
3928 	scf_values_destroy(&vals);
3929 error:
3930 	if (ismember(scf_error(), errors_server)) {
3931 		return (-1);
3932 	} else switch (scf_error()) {
3933 	case SCF_ERROR_TYPE_MISMATCH:
3934 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3935 		/*FALLTHROUGH*/
3936 
3937 	case SCF_ERROR_CONSTRAINT_VIOLATED:
3938 	case SCF_ERROR_NOT_FOUND:
3939 	case SCF_ERROR_TEMPLATE_INVALID:
3940 		return (-1);
3941 
3942 	case SCF_ERROR_INVALID_ARGUMENT:
3943 	case SCF_ERROR_NOT_SET:
3944 	default:
3945 		assert(0);
3946 		abort();
3947 	}
3948 	/*NOTREACHED*/
3949 }
3950 
3951 /*
3952  * Returns -1 on failure.  Sets scf_error():
3953  *   SCF_ERROR_BACKEND_ACCESS
3954  *   SCF_ERROR_CONNECTION_BROKEN
3955  *   SCF_ERROR_CONSTRAINT_VIOLATED
3956  *   SCF_ERROR_DELETED
3957  *   SCF_ERROR_HANDLE_DESTROYED
3958  *   SCF_ERROR_INTERNAL
3959  *   SCF_ERROR_NO_MEMORY
3960  *   SCF_ERROR_NO_RESOURCES
3961  *   SCF_ERROR_NOT_BOUND
3962  *   SCF_ERROR_NOT_FOUND
3963  *     Property doesn't exist or exists and has no value.
3964  *   SCF_ERROR_PERMISSION_DENIED
3965  *   SCF_ERROR_TEMPLATE_INVALID
3966  */
3967 int
3968 scf_tmpl_value_count_range_constraints(const scf_prop_tmpl_t *t,
3969     scf_count_ranges_t *ranges)
3970 {
3971 	return (_scf_tmpl_get_count_ranges(t, SCF_PROPERTY_TM_CONSTRAINT_RANGE,
3972 	    ranges));
3973 }
3974 
3975 int
3976 scf_tmpl_value_int_range_constraints(const scf_prop_tmpl_t *t,
3977     scf_int_ranges_t *ranges)
3978 {
3979 	return (_scf_tmpl_get_int_ranges(t, SCF_PROPERTY_TM_CONSTRAINT_RANGE,
3980 	    ranges));
3981 }
3982 
3983 int
3984 scf_tmpl_value_count_range_choices(const scf_prop_tmpl_t *t,
3985     scf_count_ranges_t *ranges)
3986 {
3987 	return (_scf_tmpl_get_count_ranges(t, SCF_PROPERTY_TM_CHOICES_RANGE,
3988 	    ranges));
3989 }
3990 
3991 int
3992 scf_tmpl_value_int_range_choices(const scf_prop_tmpl_t *t,
3993     scf_int_ranges_t *ranges)
3994 {
3995 	return (_scf_tmpl_get_int_ranges(t, SCF_PROPERTY_TM_CHOICES_RANGE,
3996 	    ranges));
3997 }
3998 
3999 /*
4000  * Returns -1 on failure.  Sets scf_error():
4001  *   SCF_ERROR_BACKEND_ACCESS
4002  *   SCF_ERROR_CONNECTION_BROKEN
4003  *   SCF_ERROR_DELETED
4004  *   SCF_ERROR_HANDLE_DESTROYED
4005  *   SCF_ERROR_INTERNAL
4006  *   SCF_ERROR_NO_MEMORY
4007  *   SCF_ERROR_NO_RESOURCES
4008  *   SCF_ERROR_NOT_BOUND
4009  *   SCF_ERROR_NOT_FOUND
4010  *     Property doesn't exist or exists and has no value.
4011  *   SCF_ERROR_PERMISSION_DENIED
4012  *   SCF_ERROR_TEMPLATE_INVALID
4013  */
4014 int
4015 scf_tmpl_value_name_choices(const scf_prop_tmpl_t *t, scf_values_t *vals)
4016 {
4017 	int c_flag = 0; /* have not read any value yet */
4018 	int r;
4019 	char **ret;
4020 
4021 	/* First, look for explicitly declared choices. */
4022 	if ((ret = _read_astrings_values(t->prt_pg,
4023 	    SCF_PROPERTY_TM_CHOICES_NAME, vals)) != NULL) {
4024 		c_flag = 1;
4025 	} else if (scf_error() != SCF_ERROR_NOT_FOUND) {
4026 		goto error;
4027 	}
4028 
4029 	/* Next, check for choices included by 'values'. */
4030 	if ((r = _check_choices_include_values(t->prt_pg, "values")) == 0) {
4031 		/* read values_name */
4032 		if (c_flag == 1)
4033 			/* append values */
4034 			ret = _append_astrings_values(t->prt_pg,
4035 			    SCF_PROPERTY_TM_VALUES_NAME, vals);
4036 		else
4037 			/* read values */
4038 			ret = _read_astrings_values(t->prt_pg,
4039 			    SCF_PROPERTY_TM_VALUES_NAME, vals);
4040 		if (ret != NULL) {
4041 			c_flag = 1;
4042 		} else if (scf_error() != SCF_ERROR_NOT_FOUND) {
4043 			goto error;
4044 		}
4045 	} else if (r == -1) {
4046 		goto error;
4047 	}
4048 
4049 	/* Finally check for choices included by 'constraints'. */
4050 	if ((r = _check_choices_include_values(t->prt_pg, "constraints")) ==
4051 	    0) {
4052 		/* read constraint_name */
4053 		if (c_flag == 1)
4054 			/* append values */
4055 			ret = _append_astrings_values(t->prt_pg,
4056 			    SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
4057 		else
4058 			/* read values */
4059 			ret = _read_astrings_values(t->prt_pg,
4060 			    SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
4061 		if (ret != NULL) {
4062 			c_flag = 1;
4063 		} else if (scf_error() != SCF_ERROR_NOT_FOUND) {
4064 			goto error;
4065 		}
4066 	} else if (r == -1) {
4067 		goto error;
4068 	}
4069 
4070 	if (c_flag == 0 || vals->value_count == 0) {
4071 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
4072 		return (-1);
4073 	}
4074 
4075 	return (0);
4076 
4077 error:
4078 	if (ismember(scf_error(), errors_server)) {
4079 		return (-1);
4080 	} else switch (scf_error()) {
4081 	case SCF_ERROR_TYPE_MISMATCH:
4082 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
4083 		return (-1);
4084 
4085 	case SCF_ERROR_NOT_SET:
4086 	case SCF_ERROR_INVALID_ARGUMENT:
4087 	default:
4088 		assert(0);
4089 		abort();
4090 	}
4091 	/*NOTREACHED*/
4092 }
4093 
4094 void
4095 scf_values_destroy(scf_values_t *vals)
4096 {
4097 	int i;
4098 	char **items = NULL;
4099 	char **str = NULL;
4100 
4101 	if (vals == NULL)
4102 		return;
4103 
4104 	str = vals->values_as_strings;
4105 
4106 	/* free values */
4107 	switch (vals->value_type) {
4108 	case SCF_TYPE_BOOLEAN:
4109 		free(vals->values.v_boolean);
4110 		break;
4111 	case SCF_TYPE_COUNT:
4112 		free(vals->values.v_count);
4113 		break;
4114 	case SCF_TYPE_INTEGER:
4115 		free(vals->values.v_integer);
4116 		break;
4117 	case SCF_TYPE_ASTRING:
4118 		items = vals->values.v_astring;
4119 		str = NULL;
4120 		break;
4121 	case SCF_TYPE_USTRING:
4122 		items = vals->values.v_ustring;
4123 		str = NULL;
4124 		break;
4125 	case SCF_TYPE_OPAQUE:
4126 		items = vals->values.v_opaque;
4127 		str = NULL;
4128 		break;
4129 	case SCF_TYPE_TIME:
4130 		free(vals->values.v_time);
4131 		break;
4132 	default:
4133 		assert(0);
4134 		abort();
4135 	}
4136 	for (i = 0; i < vals->value_count; ++i) {
4137 		if (items != NULL)
4138 			free(items[i]);
4139 		if (str != NULL)
4140 			free(str[i]);
4141 	}
4142 	vals->value_count = 0;
4143 	free(items);
4144 	free(str);
4145 }
4146 
4147 /*
4148  * char *_make_value_name()
4149  *
4150  * Construct the prefix for a value common name or value description property.
4151  * It takes the form:
4152  *   value_<BASE32 name>_<common_name|description>_
4153  * This is then combined with a localized suffix by the caller to look
4154  * up the property in the repository:
4155  *   value_<BASE32 name>_<common_name|description>_<lang>
4156  *
4157  * Returns NULL on failure.  Sets scf_error():
4158  *   SCF_ERROR_INVALID_ARGUMENT
4159  *     Name isn't short enough make a value name with.
4160  *   SCF_ERROR_NO_MEMORY
4161  */
4162 static char *
4163 _make_value_name(char *desc_name, const char *value)
4164 {
4165 	char *name = NULL;
4166 	char *encoded = NULL;
4167 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
4168 
4169 	name = malloc(sz);
4170 	encoded = malloc(sz);
4171 	if (name == NULL || encoded == NULL) {
4172 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4173 		free(name);
4174 		free(encoded);
4175 		return (NULL);
4176 	}
4177 
4178 	if (scf_encode32(value, strlen(value), encoded, sz, NULL,
4179 	    SCF_ENCODE32_PAD) != 0) {
4180 		/* Shouldn't happen. */
4181 		assert(0);
4182 	}
4183 
4184 	(void) strlcpy(name, SCF_PROPERTY_TM_VALUE_PREFIX, sz);
4185 
4186 	if (strlcat(name, encoded, sz) >= sz) {
4187 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4188 		free(name);
4189 		free(encoded);
4190 		return (NULL);
4191 	}
4192 
4193 	if (strlcat(name, "_", sz) >= sz) {
4194 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4195 		free(name);
4196 		free(encoded);
4197 		return (NULL);
4198 	}
4199 
4200 	if (strlcat(name, desc_name, sz) >= sz) {
4201 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4202 		free(name);
4203 		free(encoded);
4204 		return (NULL);
4205 	}
4206 
4207 	if (strlcat(name, "_", sz) >= sz) {
4208 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4209 		free(name);
4210 		free(encoded);
4211 		return (NULL);
4212 	}
4213 
4214 	free(encoded);
4215 	return (name);
4216 }
4217 
4218 /*
4219  * ssize_t scf_tmpl_value_common_name()
4220  *
4221  * Populates "out" with an allocated string containing the value's
4222  * common name.  Returns the size of the string on successful return.
4223  * out must be freed with free() on successful return.
4224  *
4225  * Returns -1 on failure, sets scf_error() to:
4226  *   SCF_ERROR_BACKEND_ACCESS
4227  *   SCF_ERROR_CONNECTION_BROKEN
4228  *   SCF_ERROR_DELETED
4229  *     Property group was deleted.
4230  *   SCF_ERROR_HANDLE_DESTROYED
4231  *   SCF_ERROR_INTERNAL
4232  *   SCF_ERROR_INVALID_ARGUMENT
4233  *     name not a valid property name
4234  *     name and locale are too long to make a property name
4235  *   SCF_ERROR_NO_MEMORY
4236  *   SCF_ERROR_NO_RESOURCES
4237  *   SCF_ERROR_NOT_BOUND
4238  *   SCF_ERROR_NOT_FOUND
4239  *     Property doesn't exist or exists and has no value.
4240  *   SCF_ERROR_PERMISSION_DENIED
4241  *   SCF_ERROR_TEMPLATE_INVALID
4242  *     property is not SCF_TYPE_ASTRING has more than one value.
4243  */
4244 ssize_t
4245 scf_tmpl_value_common_name(const scf_prop_tmpl_t *t, const char *locale,
4246     const char *value, char **out)
4247 {
4248 	char *value_name = NULL;
4249 
4250 	value_name = _make_value_name("common_name", value);
4251 	if (value_name == NULL)
4252 		return (-1);
4253 
4254 	*out = _read_localized_astring_from_pg(t->prt_pg, value_name, locale);
4255 
4256 	free(value_name);
4257 
4258 	if (*out == NULL)
4259 		return (-1);
4260 
4261 	return (strlen(*out));
4262 }
4263 
4264 /*
4265  * ssize_t scf_tmpl_value_description()
4266  *
4267  * Populates "out" with an allocated string containing the value's
4268  * description.  Returns the size of the string on successful return.
4269  * out must be freed with free() on successful return.
4270  *
4271  * Returns -1 on failure, sets scf_error() to:
4272  *   SCF_ERROR_BACKEND_ACCESS
4273  *   SCF_ERROR_CONNECTION_BROKEN
4274  *   SCF_ERROR_DELETED
4275  *     Property group was deleted.
4276  *   SCF_ERROR_HANDLE_DESTROYED
4277  *   SCF_ERROR_INTERNAL
4278  *   SCF_ERROR_INVALID_ARGUMENT
4279  *     name not a valid property name
4280  *     name and locale are too long to make a property name
4281  *   SCF_ERROR_NO_MEMORY
4282  *   SCF_ERROR_NO_RESOURCES
4283  *   SCF_ERROR_NOT_BOUND
4284  *   SCF_ERROR_NOT_FOUND
4285  *     Property doesn't exist or exists and has no value.
4286  *   SCF_ERROR_PERMISSION_DENIED
4287  *   SCF_ERROR_TEMPLATE_INVALID
4288  *     property is not SCF_TYPE_ASTRING has more than one value.
4289  */
4290 ssize_t
4291 scf_tmpl_value_description(const scf_prop_tmpl_t *t, const char *locale,
4292     const char *value, char **out)
4293 {
4294 	char *value_name = NULL;
4295 
4296 	value_name = _make_value_name("description", value);
4297 	if (value_name == NULL)
4298 		return (-1);
4299 
4300 
4301 	*out = _read_localized_astring_from_pg(t->prt_pg, value_name, locale);
4302 
4303 	free(value_name);
4304 
4305 	if (*out == NULL)
4306 		return (-1);
4307 
4308 	return (strlen(*out));
4309 }
4310 
4311 /*
4312  * Templates error messages format, in human readable form.
4313  * Each line is one error item:
4314  *
4315  * prefix error message
4316  * 	FMRI="err->te_errs->tes_fmri"
4317  * 	Property group="err->te_pg_name"
4318  * 	Property name="err->te_prop_name"
4319  * 	expected value 1="err->te_ev1"
4320  * 	expected value 2="err->te_ev2"
4321  * 	actual value="err->te_actual"
4322  * 	Tempalte source="err->te_tmpl_fmri"
4323  * 	pg_pattern name="err->tmpl_pg_name"
4324  * 	pg_pattern type="err->tmpl_pg_type"
4325  * 	prop_pattern name="err->tmpl_prop_name"
4326  * 	prop_pattern type="err->tmpl_prop_type"
4327  *
4328  * To add a new error type, include scf_tmpl_error_type_t in libscf.h
4329  * add one entry in em_desc[], and update the functions pointed by the
4330  * _tmpl_error_access array with the new error code. Also, update the
4331  * scf_tmpl_error_* functions to provide access to desired
4332  * scf_tmpl_error_t fields.
4333  *
4334  * To add a new error item, add a new field to scf_tmpl_error_t, a new field
4335  * in _scf_tmpl_error_desc or a new non-error-dependent string, add a new entry
4336  * in _tmpl_error_access array and create the appropriate get_val, get_desc
4337  * functions.
4338  *
4339  * Changes to both the validation logic and the error types and items must
4340  * be coordinated with the code in svccfg to ensure both libscf and svccfg's
4341  * manifest validation validate the same things.
4342  */
4343 
4344 /*
4345  * Container for all template errors on a validated object.
4346  */
4347 struct scf_tmpl_errors {
4348 	int			tes_index;
4349 	int			tes_num_errs;
4350 	scf_tmpl_error_t	**tes_errs;
4351 	int			tes_errs_size;
4352 	const char		*tes_fmri;
4353 	const char		*tes_prefix;
4354 	int			tes_flag; /* if set, scf_tmpl_error_destroy */
4355 					    /* will free strings in tes_errs  */
4356 };
4357 
4358 /*
4359  * Templates error-dependent labels
4360  */
4361 struct _scf_tmpl_error_desc {
4362 	const char *em_msg;
4363 	const char *em_ev1;
4364 	const char *em_ev2;
4365 	const char *em_actual;
4366 };
4367 
4368 /*
4369  * This array MUST be kept in synch with the template error definition of
4370  * scf_tmpl_error_type_t in libscf.h
4371  */
4372 static struct _scf_tmpl_error_desc em_desc[] = {
4373 	/* SCF_TERR_MISSING_PG */
4374 	{ "Required property group missing", "Name of missing property group",
4375 	    "Type of missing property group", NULL },
4376 	/* SCF_TERR_WRONG_PG_TYPE */
4377 	{ "Property group has bad type", "Specified type", NULL,
4378 	    "Actual type" },
4379 	/* SCF_TERR_MISSING_PROP */
4380 	{ "Required property missing", "Name of missing property", NULL, NULL },
4381 	/* SCF_TERR_WRONG_PROP_TYPE */
4382 	{ "Property has bad type", "Specified property type", NULL,
4383 	    "Actual property type" },
4384 	/* SCF_TERR_CARDINALITY_VIOLATION */
4385 	{ "Number of property values violates cardinality restriction",
4386 	    "Cardinality minimum", "Cardinality maximum",
4387 	    "Actual number of values" },
4388 	/* SCF_TERR_VALUE_CONSTRAINT_VIOLATED */
4389 	{ "Property has illegal value", NULL, NULL, "Illegal value" },
4390 	/* SCF_TERR_RANGE_VIOLATION */
4391 	{ "Property value is out of range", NULL, NULL, "Actual value" },
4392 	/* SCF_TERR_PG_REDEFINE */
4393 	{ "Instance redefines pg_pattern", "Instance pg_pattern name",
4394 	    "Instance pg_pattern type", NULL },
4395 	/* SCF_TERR_PROP_TYPE_MISMATCH */
4396 	{ "Property type and value type mismatch", NULL, NULL, "Value type" },
4397 	/* SCF_TERR_VALUE_OUT_OF_RANGE */
4398 	{ "Value is out of range", NULL, NULL, "Value" },
4399 	/* SCF_TERR_INVALID_VALUE */
4400 	{ "Value is not valid", NULL, NULL, "Value" },
4401 	/* SCF_TERR_PG_PATTERN_CONFLICT */
4402 	{ "Conflicting pg_pattern specifications", "Template source",
4403 	    "pg_pattern name", "pg_pattern type" },
4404 	/* SCF_TERR_PROP_PATTERN_CONFLICT */
4405 	{ "Conflicting prop_pattern specifications", "Template source",
4406 	    "prop_pattern name", "prop_pattern type" },
4407 	/* SCF_TERR_GENERAL_REDEFINE */
4408 	{ "Service or instance pg_pattern redefines a global or restarter "
4409 	    "pg_pattern", "Template source", "pg_pattern name",
4410 	    "pg_pattern type" },
4411 	/* SCF_TERR_INCLUDE_VALUES */
4412 	{ "Missing constraints or values for include_values element",
4413 	    "include_values type", NULL, NULL },
4414 	/* SCF_TERR_PG_PATTERN_INCOMPLETE */
4415 	{ "Required pg_pattern is missing a name or type attribute",
4416 	    NULL, NULL, NULL },
4417 	/* SCF_TERR_PROP_PATTERN_INCOMPLETE */
4418 	{ "Required prop_pattern is missing a type attribute",
4419 	    NULL, NULL, NULL }
4420 };
4421 
4422 /*
4423  * Templates non error-dependent labels
4424  */
4425 static const char *em_fmri = "FMRI";
4426 static const char *em_pg_name = "Property group";
4427 static const char *em_prop_name = "Property name";
4428 static const char *em_tmpl_fmri = "Template source";
4429 static const char *em_tmpl_pg_name = "pg_pattern name";
4430 static const char *em_tmpl_pg_type = "pg_pattern type";
4431 static const char *em_tmpl_prop_name = "prop_pattern name";
4432 static const char *em_tmpl_prop_type = "prop_pattern type";
4433 
4434 static const char *
4435 _get_fmri_desc(scf_tmpl_error_t *err)
4436 {
4437 	switch (err->te_type) {
4438 	case SCF_TERR_MISSING_PG:
4439 	case SCF_TERR_WRONG_PG_TYPE:
4440 	case SCF_TERR_MISSING_PROP:
4441 	case SCF_TERR_WRONG_PROP_TYPE:
4442 	case SCF_TERR_CARDINALITY_VIOLATION:
4443 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4444 	case SCF_TERR_RANGE_VIOLATION:
4445 	case SCF_TERR_PG_REDEFINE:
4446 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4447 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4448 	case SCF_TERR_INCLUDE_VALUES:
4449 		return (dgettext(TEXT_DOMAIN, em_fmri));
4450 	case SCF_TERR_PROP_TYPE_MISMATCH:
4451 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4452 	case SCF_TERR_INVALID_VALUE:
4453 	case SCF_TERR_PG_PATTERN_CONFLICT:
4454 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4455 	case SCF_TERR_GENERAL_REDEFINE:
4456 	default:
4457 		return (NULL);
4458 	}
4459 }
4460 
4461 static const char *
4462 _get_pg_name_desc(scf_tmpl_error_t *err)
4463 {
4464 	switch (err->te_type) {
4465 	case SCF_TERR_WRONG_PG_TYPE:
4466 	case SCF_TERR_MISSING_PROP:
4467 	case SCF_TERR_WRONG_PROP_TYPE:
4468 	case SCF_TERR_CARDINALITY_VIOLATION:
4469 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4470 	case SCF_TERR_RANGE_VIOLATION:
4471 		return (dgettext(TEXT_DOMAIN, em_pg_name));
4472 	case SCF_TERR_MISSING_PG:
4473 	case SCF_TERR_PG_REDEFINE:
4474 	case SCF_TERR_PROP_TYPE_MISMATCH:
4475 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4476 	case SCF_TERR_INVALID_VALUE:
4477 	case SCF_TERR_PG_PATTERN_CONFLICT:
4478 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4479 	case SCF_TERR_GENERAL_REDEFINE:
4480 	case SCF_TERR_INCLUDE_VALUES:
4481 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4482 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4483 	default:
4484 		return (NULL);
4485 	}
4486 }
4487 
4488 static const char *
4489 _get_prop_name_desc(scf_tmpl_error_t *err)
4490 {
4491 	switch (err->te_type) {
4492 	case SCF_TERR_WRONG_PROP_TYPE:
4493 	case SCF_TERR_CARDINALITY_VIOLATION:
4494 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4495 	case SCF_TERR_RANGE_VIOLATION:
4496 		return (dgettext(TEXT_DOMAIN, em_prop_name));
4497 	case SCF_TERR_MISSING_PG:
4498 	case SCF_TERR_WRONG_PG_TYPE:
4499 	case SCF_TERR_MISSING_PROP:
4500 	case SCF_TERR_PG_REDEFINE:
4501 	case SCF_TERR_PROP_TYPE_MISMATCH:
4502 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4503 	case SCF_TERR_INVALID_VALUE:
4504 	case SCF_TERR_PG_PATTERN_CONFLICT:
4505 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4506 	case SCF_TERR_GENERAL_REDEFINE:
4507 	case SCF_TERR_INCLUDE_VALUES:
4508 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4509 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4510 	default:
4511 		return (NULL);
4512 	}
4513 }
4514 
4515 static const char *
4516 _get_ev1_desc(scf_tmpl_error_t *err)
4517 {
4518 	switch (err->te_type) {
4519 	case SCF_TERR_MISSING_PG:
4520 	case SCF_TERR_WRONG_PG_TYPE:
4521 	case SCF_TERR_MISSING_PROP:
4522 	case SCF_TERR_WRONG_PROP_TYPE:
4523 	case SCF_TERR_CARDINALITY_VIOLATION:
4524 	case SCF_TERR_RANGE_VIOLATION:
4525 	case SCF_TERR_PG_REDEFINE:
4526 	case SCF_TERR_PG_PATTERN_CONFLICT:
4527 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4528 	case SCF_TERR_GENERAL_REDEFINE:
4529 	case SCF_TERR_INCLUDE_VALUES:
4530 		return (dgettext(TEXT_DOMAIN, em_desc[err->te_type].em_ev1));
4531 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4532 	case SCF_TERR_PROP_TYPE_MISMATCH:
4533 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4534 	case SCF_TERR_INVALID_VALUE:
4535 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4536 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4537 	default:
4538 		return (NULL);
4539 	}
4540 }
4541 
4542 static const char *
4543 _get_ev2_desc(scf_tmpl_error_t *err)
4544 {
4545 	switch (err->te_type) {
4546 	case SCF_TERR_MISSING_PG:
4547 	case SCF_TERR_CARDINALITY_VIOLATION:
4548 	case SCF_TERR_RANGE_VIOLATION:
4549 	case SCF_TERR_PG_REDEFINE:
4550 	case SCF_TERR_PG_PATTERN_CONFLICT:
4551 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4552 	case SCF_TERR_GENERAL_REDEFINE:
4553 		return (dgettext(TEXT_DOMAIN, em_desc[err->te_type].em_ev2));
4554 	case SCF_TERR_WRONG_PG_TYPE:
4555 	case SCF_TERR_MISSING_PROP:
4556 	case SCF_TERR_WRONG_PROP_TYPE:
4557 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4558 	case SCF_TERR_PROP_TYPE_MISMATCH:
4559 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4560 	case SCF_TERR_INVALID_VALUE:
4561 	case SCF_TERR_INCLUDE_VALUES:
4562 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4563 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4564 	default:
4565 		return (NULL);
4566 	}
4567 }
4568 
4569 static const char *
4570 _get_actual_desc(scf_tmpl_error_t *err)
4571 {
4572 	switch (err->te_type) {
4573 	case SCF_TERR_MISSING_PG:
4574 	case SCF_TERR_WRONG_PG_TYPE:
4575 	case SCF_TERR_WRONG_PROP_TYPE:
4576 	case SCF_TERR_CARDINALITY_VIOLATION:
4577 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4578 	case SCF_TERR_RANGE_VIOLATION:
4579 	case SCF_TERR_PROP_TYPE_MISMATCH:
4580 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4581 	case SCF_TERR_INVALID_VALUE:
4582 	case SCF_TERR_PG_PATTERN_CONFLICT:
4583 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4584 	case SCF_TERR_GENERAL_REDEFINE:
4585 	case SCF_TERR_INCLUDE_VALUES:
4586 		return (dgettext(TEXT_DOMAIN,
4587 		    em_desc[err->te_type].em_actual));
4588 	case SCF_TERR_MISSING_PROP:
4589 	case SCF_TERR_PG_REDEFINE:
4590 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4591 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4592 	default:
4593 		return (NULL);
4594 	}
4595 }
4596 
4597 static const char *
4598 _get_tmpl_fmri_desc(scf_tmpl_error_t *err)
4599 {
4600 	switch (err->te_type) {
4601 	case SCF_TERR_MISSING_PG:
4602 	case SCF_TERR_WRONG_PG_TYPE:
4603 	case SCF_TERR_MISSING_PROP:
4604 	case SCF_TERR_WRONG_PROP_TYPE:
4605 	case SCF_TERR_CARDINALITY_VIOLATION:
4606 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4607 	case SCF_TERR_RANGE_VIOLATION:
4608 	case SCF_TERR_PG_REDEFINE:
4609 	case SCF_TERR_PROP_TYPE_MISMATCH:
4610 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4611 	case SCF_TERR_INVALID_VALUE:
4612 	case SCF_TERR_PG_PATTERN_CONFLICT:
4613 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4614 	case SCF_TERR_GENERAL_REDEFINE:
4615 	case SCF_TERR_INCLUDE_VALUES:
4616 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4617 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4618 		return (dgettext(TEXT_DOMAIN, em_tmpl_fmri));
4619 	default:
4620 		return (NULL);
4621 	}
4622 }
4623 
4624 static const char *
4625 _get_tmpl_pg_name_desc(scf_tmpl_error_t *err)
4626 {
4627 	switch (err->te_type) {
4628 	case SCF_TERR_MISSING_PG:
4629 	case SCF_TERR_WRONG_PG_TYPE:
4630 	case SCF_TERR_MISSING_PROP:
4631 	case SCF_TERR_WRONG_PROP_TYPE:
4632 	case SCF_TERR_CARDINALITY_VIOLATION:
4633 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4634 	case SCF_TERR_RANGE_VIOLATION:
4635 	case SCF_TERR_PG_REDEFINE:
4636 	case SCF_TERR_PROP_TYPE_MISMATCH:
4637 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4638 	case SCF_TERR_INVALID_VALUE:
4639 	case SCF_TERR_PG_PATTERN_CONFLICT:
4640 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4641 	case SCF_TERR_GENERAL_REDEFINE:
4642 	case SCF_TERR_INCLUDE_VALUES:
4643 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4644 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4645 		return (dgettext(TEXT_DOMAIN, em_tmpl_pg_name));
4646 	default:
4647 		return (NULL);
4648 	}
4649 }
4650 
4651 static const char *
4652 _get_tmpl_pg_type_desc(scf_tmpl_error_t *err)
4653 {
4654 	switch (err->te_type) {
4655 	case SCF_TERR_MISSING_PG:
4656 	case SCF_TERR_WRONG_PG_TYPE:
4657 	case SCF_TERR_MISSING_PROP:
4658 	case SCF_TERR_WRONG_PROP_TYPE:
4659 	case SCF_TERR_CARDINALITY_VIOLATION:
4660 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4661 	case SCF_TERR_RANGE_VIOLATION:
4662 	case SCF_TERR_PG_REDEFINE:
4663 	case SCF_TERR_PROP_TYPE_MISMATCH:
4664 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4665 	case SCF_TERR_INVALID_VALUE:
4666 	case SCF_TERR_PG_PATTERN_CONFLICT:
4667 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4668 	case SCF_TERR_GENERAL_REDEFINE:
4669 	case SCF_TERR_INCLUDE_VALUES:
4670 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4671 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4672 		return (dgettext(TEXT_DOMAIN, em_tmpl_pg_type));
4673 	default:
4674 		return (NULL);
4675 	}
4676 }
4677 
4678 static const char *
4679 _get_tmpl_prop_name_desc(scf_tmpl_error_t *err)
4680 {
4681 	switch (err->te_type) {
4682 	case SCF_TERR_MISSING_PROP:
4683 	case SCF_TERR_WRONG_PROP_TYPE:
4684 	case SCF_TERR_CARDINALITY_VIOLATION:
4685 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4686 	case SCF_TERR_RANGE_VIOLATION:
4687 	case SCF_TERR_PROP_TYPE_MISMATCH:
4688 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4689 	case SCF_TERR_INVALID_VALUE:
4690 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4691 	case SCF_TERR_INCLUDE_VALUES:
4692 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4693 		return (dgettext(TEXT_DOMAIN, em_tmpl_prop_name));
4694 	case SCF_TERR_MISSING_PG:
4695 	case SCF_TERR_WRONG_PG_TYPE:
4696 	case SCF_TERR_PG_REDEFINE:
4697 	case SCF_TERR_PG_PATTERN_CONFLICT:
4698 	case SCF_TERR_GENERAL_REDEFINE:
4699 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4700 	default:
4701 		return (NULL);
4702 	}
4703 }
4704 
4705 static const char *
4706 _get_tmpl_prop_type_desc(scf_tmpl_error_t *err)
4707 {
4708 	switch (err->te_type) {
4709 	case SCF_TERR_MISSING_PROP:
4710 	case SCF_TERR_WRONG_PROP_TYPE:
4711 	case SCF_TERR_CARDINALITY_VIOLATION:
4712 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
4713 	case SCF_TERR_RANGE_VIOLATION:
4714 	case SCF_TERR_PROP_TYPE_MISMATCH:
4715 	case SCF_TERR_VALUE_OUT_OF_RANGE:
4716 	case SCF_TERR_INVALID_VALUE:
4717 	case SCF_TERR_PROP_PATTERN_CONFLICT:
4718 	case SCF_TERR_INCLUDE_VALUES:
4719 		return (dgettext(TEXT_DOMAIN, em_tmpl_prop_type));
4720 	case SCF_TERR_MISSING_PG:
4721 	case SCF_TERR_WRONG_PG_TYPE:
4722 	case SCF_TERR_PG_REDEFINE:
4723 	case SCF_TERR_PG_PATTERN_CONFLICT:
4724 	case SCF_TERR_GENERAL_REDEFINE:
4725 	case SCF_TERR_PG_PATTERN_INCOMPLETE:
4726 	case SCF_TERR_PROP_PATTERN_INCOMPLETE:
4727 	default:
4728 		return (NULL);
4729 	}
4730 }
4731 
4732 static const char *
4733 _get_fmri_val(scf_tmpl_error_t *err)
4734 {
4735 	assert(err != NULL && err->te_errs != NULL &&
4736 	    err->te_errs->tes_fmri != NULL);
4737 	return (err->te_errs->tes_fmri);
4738 }
4739 
4740 static const char *
4741 _get_pg_name_val(scf_tmpl_error_t *err)
4742 {
4743 	assert(err != NULL);
4744 	return (err->te_pg_name);
4745 }
4746 
4747 static const char *
4748 _get_prop_name_val(scf_tmpl_error_t *err)
4749 {
4750 	assert(err != NULL);
4751 	return (err->te_prop_name);
4752 }
4753 
4754 static const char *
4755 _get_ev1_val(scf_tmpl_error_t *err)
4756 {
4757 	assert(err != NULL);
4758 	return (err->te_ev1);
4759 }
4760 
4761 static const char *
4762 _get_ev2_val(scf_tmpl_error_t *err)
4763 {
4764 	assert(err != NULL);
4765 	return (err->te_ev2);
4766 }
4767 
4768 static const char *
4769 _get_actual_val(scf_tmpl_error_t *err)
4770 {
4771 	assert(err != NULL);
4772 	return (err->te_actual);
4773 }
4774 
4775 static const char *
4776 _get_tmpl_fmri_val(scf_tmpl_error_t *err)
4777 {
4778 	assert(err != NULL);
4779 	return (err->te_tmpl_fmri);
4780 }
4781 
4782 static const char *
4783 _get_tmpl_pg_name_val(scf_tmpl_error_t *err)
4784 {
4785 	assert(err != NULL);
4786 	return (err->te_tmpl_pg_name);
4787 }
4788 
4789 static const char *
4790 _get_tmpl_pg_type_val(scf_tmpl_error_t *err)
4791 {
4792 	assert(err != NULL);
4793 	return (err->te_tmpl_pg_type);
4794 }
4795 
4796 static const char *
4797 _get_tmpl_prop_name_val(scf_tmpl_error_t *err)
4798 {
4799 	assert(err != NULL);
4800 	return (err->te_tmpl_prop_name);
4801 }
4802 
4803 static const char *
4804 _get_tmpl_prop_type_val(scf_tmpl_error_t *err)
4805 {
4806 	assert(err != NULL);
4807 	return (err->te_tmpl_prop_type);
4808 }
4809 
4810 /*
4811  * Templates error item retrival functions
4812  */
4813 typedef const char *(*get_em)(scf_tmpl_error_t *);
4814 
4815 /*
4816  * if new items (lines) are added to the templates error messages,
4817  * new entries in this array (and new fuctions) will be required.
4818  */
4819 static struct _tmpl_error_access {
4820 	get_em get_desc;
4821 	get_em get_val;
4822 } _tmpl_error_items[] = {
4823 	{ (get_em)_get_fmri_desc, (get_em)_get_fmri_val },
4824 	{ (get_em)_get_pg_name_desc, (get_em)_get_pg_name_val },
4825 	{ (get_em)_get_prop_name_desc, (get_em)_get_prop_name_val },
4826 	{ (get_em)_get_ev1_desc, (get_em)_get_ev1_val },
4827 	{ (get_em)_get_ev2_desc, (get_em)_get_ev2_val },
4828 	{ (get_em)_get_actual_desc, (get_em)_get_actual_val },
4829 	{ (get_em)_get_tmpl_fmri_desc, (get_em)_get_tmpl_fmri_val },
4830 	{ (get_em)_get_tmpl_pg_name_desc, (get_em)_get_tmpl_pg_name_val },
4831 	{ (get_em)_get_tmpl_pg_type_desc, (get_em)_get_tmpl_pg_type_val },
4832 	{ (get_em)_get_tmpl_prop_name_desc, (get_em)_get_tmpl_prop_name_val },
4833 	{ (get_em)_get_tmpl_prop_type_desc, (get_em)_get_tmpl_prop_type_val },
4834 	{ NULL }
4835 };
4836 
4837 /*
4838  * Allocate a new scf_tmpl_error_t and add it to the errs list provided.
4839  * Returns NULL on failure.  Sets scf_error():
4840  *   SCF_ERROR_NO_MEMORY
4841  */
4842 static scf_tmpl_error_t *
4843 _create_error(scf_tmpl_errors_t *errs)
4844 {
4845 	scf_tmpl_error_t *ret;
4846 	scf_tmpl_error_t **saved_errs;
4847 
4848 	assert(errs != NULL);
4849 	ret = calloc(1, sizeof (scf_tmpl_error_t));
4850 	if (ret == NULL) {
4851 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4852 		return (NULL);
4853 	}
4854 
4855 	ret->te_errs = errs;
4856 
4857 	assert(errs->tes_num_errs <= errs->tes_errs_size);
4858 	if (errs->tes_num_errs == errs->tes_errs_size) {
4859 		/* Time to grow the pointer array. */
4860 		saved_errs = errs->tes_errs;
4861 		errs->tes_errs = calloc(2 * errs->tes_errs_size,
4862 		    sizeof (scf_tmpl_error_t *));
4863 		if (errs->tes_errs == NULL) {
4864 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4865 			errs->tes_errs = saved_errs;
4866 			free(ret);
4867 			return (NULL);
4868 		}
4869 		(void) memcpy(errs->tes_errs, saved_errs, errs->tes_errs_size *
4870 		    sizeof (scf_tmpl_error_t *));
4871 		errs->tes_errs_size = 2 * errs->tes_errs_size;
4872 		free(saved_errs);
4873 	}
4874 
4875 	errs->tes_errs[errs->tes_num_errs] = ret;
4876 	errs->tes_num_errs++;
4877 
4878 	return (ret);
4879 }
4880 
4881 /*
4882  *
4883  * If destroy_strings is set, scf_tmpl_errors_destroy will free the
4884  * strings in scf_tmpl_error_t entries.
4885  *
4886  * Returns NULL on failure.  Sets scf_error():
4887  *    SCF_ERROR_NO_MEMORY
4888  */
4889 scf_tmpl_errors_t *
4890 _scf_create_errors(const char *fmri, int destroy_strings)
4891 {
4892 	scf_tmpl_errors_t *ret;
4893 	int errs_size = 20;
4894 
4895 	assert(fmri != NULL);
4896 
4897 	ret = calloc(1, sizeof (scf_tmpl_errors_t));
4898 	if (ret == NULL) {
4899 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4900 		return (NULL);
4901 	}
4902 
4903 	ret->tes_index = 0;
4904 	ret->tes_num_errs = 0;
4905 	if ((ret->tes_fmri = strdup(fmri)) == NULL) {
4906 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4907 		free(ret);
4908 		return (NULL);
4909 	}
4910 
4911 	ret->tes_prefix = strdup("");
4912 	if (ret->tes_prefix == NULL) {
4913 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4914 		free((char *)ret->tes_fmri);
4915 		free(ret);
4916 		return (NULL);
4917 	}
4918 	ret->tes_flag = destroy_strings;
4919 
4920 	/* Make space for a few errors. */
4921 	ret->tes_errs = calloc(errs_size, sizeof (scf_tmpl_error_t *));
4922 	if (ret->tes_errs == NULL) {
4923 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4924 		free((char *)ret->tes_fmri);
4925 		free((char *)ret->tes_prefix);
4926 		free(ret);
4927 		return (NULL);
4928 	}
4929 	ret->tes_errs_size = errs_size;
4930 
4931 	return (ret);
4932 }
4933 
4934 /*
4935  * return 0 on success, if fails set scf_error() to:
4936  *
4937  *    SCF_ERROR_NO_MEMORY
4938  */
4939 int
4940 _scf_tmpl_error_set_prefix(scf_tmpl_errors_t *errs, const char *prefix)
4941 {
4942 	free((void *) errs->tes_prefix);
4943 	if (prefix == NULL)
4944 		errs->tes_prefix = strdup("");
4945 	else
4946 		errs->tes_prefix = strdup(prefix);
4947 	if (errs->tes_prefix == NULL) {
4948 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4949 		return (-1);
4950 	}
4951 	return (0);
4952 }
4953 
4954 /*
4955  *
4956  * Returns -1 on failure.  Sets scf_error():
4957  *   SCF_ERROR_NO_MEMORY
4958  */
4959 int
4960 _scf_tmpl_add_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
4961     const char *pg_name, const char *prop_name,
4962     const char *ev1, const char *ev2, const char *actual,
4963     const char *tmpl_fmri, const char *tmpl_pg_name, const char *tmpl_pg_type,
4964     const char *tmpl_prop_name, const char *tmpl_prop_type)
4965 {
4966 	scf_tmpl_error_t *err;
4967 
4968 	assert(errs != NULL);
4969 	assert(tmpl_fmri != NULL);
4970 
4971 	err = _create_error(errs);
4972 	if (err == NULL)
4973 		return (-1);
4974 
4975 	err->te_type = type;
4976 	err->te_pg_name = pg_name;
4977 	err->te_prop_name = prop_name;
4978 	err->te_ev1 = ev1;
4979 	err->te_ev2 = ev2;
4980 	err->te_actual = actual;
4981 	err->te_tmpl_fmri = tmpl_fmri;
4982 	err->te_tmpl_pg_name = tmpl_pg_name;
4983 	err->te_tmpl_pg_type = tmpl_pg_type;
4984 	err->te_tmpl_prop_name = tmpl_prop_name;
4985 	err->te_tmpl_prop_type = tmpl_prop_type;
4986 
4987 	return (0);
4988 }
4989 
4990 /*
4991  * returns an allocated string that must be freed with free()
4992  * string contains converted 64-bit integer value
4993  * flag set for signed values
4994  * if fails return NULL and set scf_error() to:
4995  *   SCF_ERROR_NO_MEMORY
4996  */
4997 static char *
4998 _val_to_string(uint64_t val, int flag)
4999 {
5000 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
5001 	char *buf;
5002 
5003 	buf = malloc(sz);
5004 	if (buf == NULL) {
5005 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5006 		return (NULL);
5007 	}
5008 
5009 	if (flag == 0)
5010 		(void) snprintf(buf, sz, "%" PRIu64, val);
5011 	else
5012 		(void) snprintf(buf, sz, "%" PRIi64, (int64_t)val);
5013 
5014 	return (buf);
5015 }
5016 
5017 /*
5018  * return 0 on success, -1 on failure.
5019  * set scf_error() to:
5020  *   SCF_ERROR_BACKEND_ACCESS
5021  *   SCF_ERROR_CONNECTION_BROKEN
5022  *   SCF_ERROR_DELETED
5023  *   SCF_ERROR_HANDLE_DESTROYED
5024  *   SCF_ERROR_INTERNAL
5025  *   SCF_ERROR_NO_MEMORY
5026  *   SCF_ERROR_NO_RESOURCES
5027  *   SCF_ERROR_NOT_BOUND
5028  *   SCF_ERROR_PERMISSION_DENIED
5029  *   SCF_ERROR_TEMPLATE_INVALID
5030  */
5031 static int
5032 _add_tmpl_missing_pg_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t)
5033 {
5034 	char *ev1 = NULL;
5035 	char *ev2 = NULL;
5036 	char *t_fmri = NULL;
5037 	char *t_pg_name = NULL;
5038 	char *t_pg_type = NULL;
5039 
5040 	if ((t_fmri = _scf_tmpl_get_fmri(t)) == NULL)
5041 		return (-1);
5042 	if (scf_tmpl_pg_name(t, &t_pg_name) == -1) {
5043 		goto cleanup;
5044 	}
5045 	if (scf_tmpl_pg_type(t, &t_pg_type) == -1) {
5046 		goto cleanup;
5047 	}
5048 	if ((ev1 = strdup(t_pg_name)) == NULL) {
5049 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5050 		goto cleanup;
5051 	}
5052 	if ((ev2 = strdup(t_pg_type)) == NULL) {
5053 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5054 		goto cleanup;
5055 	}
5056 
5057 	return (_scf_tmpl_add_error(errs, SCF_TERR_MISSING_PG, NULL, NULL, ev1,
5058 	    ev2, NULL, t_fmri, t_pg_name, t_pg_type, NULL, NULL));
5059 cleanup:
5060 	free(ev1);
5061 	free(ev2);
5062 	free(t_fmri);
5063 	free(t_pg_name);
5064 	free(t_pg_type);
5065 	return (-1);
5066 }
5067 
5068 /*
5069  * return 0 on success, -1 on failure.
5070  * set scf_error() to:
5071  *   SCF_ERROR_BACKEND_ACCESS
5072  *   SCF_ERROR_CONNECTION_BROKEN
5073  *   SCF_ERROR_DELETED
5074  *   SCF_ERROR_HANDLE_DESTROYED
5075  *   SCF_ERROR_INTERNAL
5076  *   SCF_ERROR_NO_MEMORY
5077  *   SCF_ERROR_NO_RESOURCES
5078  *   SCF_ERROR_NOT_BOUND
5079  *   SCF_ERROR_PERMISSION_DENIED
5080  *   SCF_ERROR_TEMPLATE_INVALID
5081  */
5082 static int
5083 _add_tmpl_wrong_pg_type_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t,
5084     scf_propertygroup_t *pg)
5085 {
5086 	char *pg_name = NULL;
5087 	char *ev1 = NULL;
5088 	char *actual = NULL;
5089 	char *t_fmri = NULL;
5090 	char *t_pg_name = NULL;
5091 	char *t_pg_type = NULL;
5092 
5093 	if ((t_fmri = _scf_tmpl_get_fmri(t)) == NULL)
5094 		return (-1);
5095 	if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5096 		goto cleanup;
5097 	if ((actual = _scf_get_pg_type(pg)) == NULL)
5098 		goto cleanup;
5099 	if (scf_tmpl_pg_name(t, &t_pg_name) == -1) {
5100 		goto cleanup;
5101 	}
5102 	if (scf_tmpl_pg_type(t, &t_pg_type) == -1) {
5103 		goto cleanup;
5104 	}
5105 	if ((ev1 = strdup(t_pg_type)) == NULL) {
5106 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5107 		goto cleanup;
5108 	}
5109 
5110 	return (_scf_tmpl_add_error(errs, SCF_TERR_WRONG_PG_TYPE, pg_name, NULL,
5111 	    ev1, NULL, actual, t_fmri, t_pg_name, t_pg_type, NULL, NULL));
5112 cleanup:
5113 	free(pg_name);
5114 	free(ev1);
5115 	free(actual);
5116 	free(t_fmri);
5117 	free(t_pg_name);
5118 	free(t_pg_type);
5119 	return (-1);
5120 }
5121 
5122 /*
5123  * return 0 on success, -1 on failure.
5124  * set scf_error() to:
5125  *   SCF_ERROR_BACKEND_ACCESS
5126  *   SCF_ERROR_CONNECTION_BROKEN
5127  *   SCF_ERROR_DELETED
5128  *   SCF_ERROR_HANDLE_DESTROYED
5129  *   SCF_ERROR_INTERNAL
5130  *   SCF_ERROR_NO_MEMORY
5131  *   SCF_ERROR_NO_RESOURCES
5132  *   SCF_ERROR_NOT_BOUND
5133  *   SCF_ERROR_PERMISSION_DENIED
5134  *   SCF_ERROR_TEMPLATE_INVALID
5135  */
5136 static int
5137 _add_tmpl_missing_prop_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t,
5138     scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt)
5139 {
5140 	char *pg_name = NULL;
5141 	char *ev1 = NULL;
5142 	char *t_fmri = NULL;
5143 	char *t_pg_name = NULL;
5144 	char *t_pg_type = NULL;
5145 	char *t_prop_name = NULL;
5146 	char *t_prop_type = NULL;
5147 
5148 	if ((t_fmri = _scf_tmpl_get_fmri(t)) == NULL)
5149 		return (-1);
5150 	if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5151 		goto cleanup;
5152 	if (scf_tmpl_pg_name(t, &t_pg_name) == -1) {
5153 		goto cleanup;
5154 	}
5155 	if (scf_tmpl_pg_type(t, &t_pg_type) == -1) {
5156 		goto cleanup;
5157 	}
5158 	if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5159 		goto cleanup;
5160 	}
5161 	t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5162 	if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5163 		free(t_prop_type);
5164 		t_prop_type = NULL;
5165 	} else if (t_prop_type == NULL) {
5166 		goto cleanup;
5167 	}
5168 	if (t_prop_type == NULL)
5169 		if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5170 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5171 			goto cleanup;
5172 		}
5173 	if ((ev1 = strdup(t_prop_name)) == NULL) {
5174 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5175 		goto cleanup;
5176 	}
5177 
5178 	return (_scf_tmpl_add_error(errs, SCF_TERR_MISSING_PROP, pg_name, NULL,
5179 	    ev1, NULL, NULL, t_fmri, t_pg_name, t_pg_type, t_prop_name,
5180 	    t_prop_type));
5181 cleanup:
5182 	free(pg_name);
5183 	free(ev1);
5184 	free(t_fmri);
5185 	free(t_pg_name);
5186 	free(t_pg_type);
5187 	free(t_prop_name);
5188 	free(t_prop_type);
5189 	return (-1);
5190 }
5191 
5192 /*
5193  * return 0 on success, -1 on failure.
5194  * set scf_error() to:
5195  *   SCF_ERROR_BACKEND_ACCESS
5196  *   SCF_ERROR_CONNECTION_BROKEN
5197  *   SCF_ERROR_DELETED
5198  *   SCF_ERROR_HANDLE_DESTROYED
5199  *   SCF_ERROR_INTERNAL
5200  *   SCF_ERROR_NO_MEMORY
5201  *   SCF_ERROR_NO_RESOURCES
5202  *   SCF_ERROR_NOT_BOUND
5203  *   SCF_ERROR_PERMISSION_DENIED
5204  *   SCF_ERROR_TEMPLATE_INVALID
5205  */
5206 static int
5207 _add_tmpl_wrong_prop_type_error(scf_tmpl_errors_t *errs,
5208     scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop)
5209 {
5210 	char *pg_name = NULL;
5211 	char *prop_name = NULL;
5212 	char *ev1 = NULL;
5213 	char *actual = NULL;
5214 	char *t_fmri = NULL;
5215 	char *t_pg_name = NULL;
5216 	char *t_pg_type = NULL;
5217 	char *t_prop_name = NULL;
5218 	char *t_prop_type = NULL;
5219 
5220 	if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5221 		return (-1);
5222 	if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5223 		goto cleanup;
5224 	if ((prop_name = _scf_get_prop_name(prop)) == NULL)
5225 		goto cleanup;
5226 	if ((actual = _scf_get_prop_type(prop)) == NULL)
5227 		goto cleanup;
5228 	if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
5229 		goto cleanup;
5230 	}
5231 	if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
5232 		goto cleanup;
5233 	}
5234 	if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5235 		goto cleanup;
5236 	}
5237 	t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5238 	if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5239 		free(t_prop_type);
5240 		t_prop_type = NULL;
5241 	} else if (t_prop_type == NULL) {
5242 		goto cleanup;
5243 	}
5244 	if (t_prop_type == NULL)
5245 		if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5246 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5247 			goto cleanup;
5248 		}
5249 	if ((ev1 = strdup(t_prop_type)) == NULL) {
5250 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5251 		goto cleanup;
5252 	}
5253 
5254 	return (_scf_tmpl_add_error(errs, SCF_TERR_WRONG_PROP_TYPE, pg_name,
5255 	    prop_name, ev1, NULL, actual, t_fmri, t_pg_name, t_pg_type,
5256 	    t_prop_name, t_prop_type));
5257 cleanup:
5258 	free(pg_name);
5259 	free(prop_name);
5260 	free(ev1);
5261 	free(actual);
5262 	free(t_fmri);
5263 	free(t_pg_name);
5264 	free(t_pg_type);
5265 	free(t_prop_name);
5266 	free(t_prop_type);
5267 	return (-1);
5268 }
5269 
5270 /*
5271  * return 0 on success, -1 on failure.
5272  * set scf_error() to:
5273  *   SCF_ERROR_BACKEND_ACCESS
5274  *   SCF_ERROR_CONNECTION_BROKEN
5275  *   SCF_ERROR_DELETED
5276  *   SCF_ERROR_HANDLE_DESTROYED
5277  *   SCF_ERROR_INTERNAL
5278  *   SCF_ERROR_NO_MEMORY
5279  *   SCF_ERROR_NO_RESOURCES
5280  *   SCF_ERROR_NOT_BOUND
5281  *   SCF_ERROR_PERMISSION_DENIED
5282  *   SCF_ERROR_TEMPLATE_INVALID
5283  */
5284 static int
5285 _add_tmpl_count_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
5286     scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop,
5287     uint64_t count, uint64_t *min, uint64_t *max)
5288 {
5289 	char *pg_name = NULL;
5290 	char *prop_name = NULL;
5291 	char *s_min = NULL;
5292 	char *s_max = NULL;
5293 	char *num = NULL;
5294 	char *t_fmri = NULL;
5295 	char *t_pg_name = NULL;
5296 	char *t_pg_type = NULL;
5297 	char *t_prop_name = NULL;
5298 	char *t_prop_type = NULL;
5299 
5300 	if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5301 		return (-1);
5302 	switch (type) {
5303 	case SCF_TERR_RANGE_VIOLATION:
5304 	case SCF_TERR_CARDINALITY_VIOLATION:
5305 		if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5306 			goto cleanup;
5307 		if ((prop_name = _scf_get_prop_name(prop)) == NULL)
5308 			goto cleanup;
5309 		break;
5310 	case SCF_TERR_VALUE_OUT_OF_RANGE:
5311 		/* keep pg_name = NULL and prop_name = NULL */
5312 		break;
5313 	}
5314 	if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
5315 		goto cleanup;
5316 	}
5317 	if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
5318 		goto cleanup;
5319 	}
5320 	if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5321 		goto cleanup;
5322 	}
5323 	t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5324 	if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5325 		free(t_prop_type);
5326 		t_prop_type = NULL;
5327 	} else if (t_prop_type == NULL) {
5328 		goto cleanup;
5329 	}
5330 	if (t_prop_type == NULL)
5331 		if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5332 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5333 			goto cleanup;
5334 		}
5335 	if (min == NULL) {
5336 		if ((s_min = strdup("")) == NULL) {
5337 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5338 			goto cleanup;
5339 		}
5340 	} else {
5341 		if ((s_min = _val_to_string(*min, 0)) == NULL) {
5342 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5343 			goto cleanup;
5344 		}
5345 	}
5346 	if (max == NULL) {
5347 		if ((s_max = strdup("")) == NULL) {
5348 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5349 			goto cleanup;
5350 		}
5351 	} else {
5352 		if ((s_max = _val_to_string(*max, 0)) == NULL) {
5353 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5354 			goto cleanup;
5355 		}
5356 	}
5357 	if ((num = _val_to_string(count, 0)) == NULL) {
5358 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5359 		goto cleanup;
5360 	}
5361 
5362 	return (_scf_tmpl_add_error(errs, type, pg_name, prop_name, s_min,
5363 	    s_max, num, t_fmri, t_pg_name, t_pg_type, t_prop_name,
5364 	    t_prop_type));
5365 cleanup:
5366 	free(pg_name);
5367 	free(prop_name);
5368 	free(s_min);
5369 	free(s_max);
5370 	free(num);
5371 	free(t_fmri);
5372 	free(t_pg_name);
5373 	free(t_pg_type);
5374 	free(t_prop_name);
5375 	free(t_prop_type);
5376 	return (-1);
5377 }
5378 
5379 /*
5380  * return 0 on success, -1 on failure.
5381  * set scf_error() to:
5382  *   SCF_ERROR_BACKEND_ACCESS
5383  *   SCF_ERROR_CONNECTION_BROKEN
5384  *   SCF_ERROR_DELETED
5385  *   SCF_ERROR_HANDLE_DESTROYED
5386  *   SCF_ERROR_INTERNAL
5387  *   SCF_ERROR_NO_MEMORY
5388  *   SCF_ERROR_NO_RESOURCES
5389  *   SCF_ERROR_NOT_BOUND
5390  *   SCF_ERROR_PERMISSION_DENIED
5391  *   SCF_ERROR_TEMPLATE_INVALID
5392  */
5393 static int
5394 _add_tmpl_constraint_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
5395     scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop,
5396     scf_value_t *val)
5397 {
5398 	scf_type_t val_type;
5399 	char *pg_name = NULL;
5400 	char *prop_name = NULL;
5401 	char *value = NULL;
5402 	char *t_fmri = NULL;
5403 	char *t_pg_name = NULL;
5404 	char *t_pg_type = NULL;
5405 	char *t_prop_name = NULL;
5406 	char *t_prop_type = NULL;
5407 
5408 	if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5409 		return (-1);
5410 	switch (type) {
5411 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
5412 		if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5413 			goto cleanup;
5414 		if ((prop_name = _scf_get_prop_name(prop)) == NULL)
5415 			goto cleanup;
5416 		/*FALLTHROUGH*/
5417 	case SCF_TERR_INVALID_VALUE:
5418 		/* keep pg_name = NULL and prop_name = NULL */
5419 		if ((value = _scf_value_get_as_string(val)) == NULL)
5420 			goto cleanup;
5421 		break;
5422 	case SCF_TERR_PROP_TYPE_MISMATCH:
5423 		/* keep pg_name = NULL and prop_name = NULL */
5424 		/* use value for value type */
5425 		val_type = scf_value_type(val);
5426 		if ((value = strdup(scf_type_to_string(val_type))) ==
5427 		    NULL) {
5428 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5429 			goto cleanup;
5430 		}
5431 		break;
5432 	}
5433 	if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
5434 		goto cleanup;
5435 	}
5436 	if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
5437 		goto cleanup;
5438 	}
5439 	if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5440 		goto cleanup;
5441 	}
5442 	t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5443 	if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5444 		free(t_prop_type);
5445 		t_prop_type = NULL;
5446 	} else if (t_prop_type == NULL) {
5447 		goto cleanup;
5448 	}
5449 	if (t_prop_type == NULL)
5450 		if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5451 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5452 			goto cleanup;
5453 		}
5454 
5455 	return (_scf_tmpl_add_error(errs, type, pg_name, prop_name, NULL, NULL,
5456 	    value, t_fmri, t_pg_name, t_pg_type, t_prop_name, t_prop_type));
5457 cleanup:
5458 	assert(scf_error() != SCF_ERROR_NOT_SET);
5459 	free(pg_name);
5460 	free(prop_name);
5461 	free(value);
5462 	free(t_fmri);
5463 	free(t_pg_name);
5464 	free(t_pg_type);
5465 	free(t_prop_name);
5466 	free(t_prop_type);
5467 	return (-1);
5468 }
5469 
5470 /*
5471  * return 0 on success, -1 on failure.
5472  * set scf_error() to:
5473  *   SCF_ERROR_BACKEND_ACCESS
5474  *   SCF_ERROR_CONNECTION_BROKEN
5475  *   SCF_ERROR_DELETED
5476  *   SCF_ERROR_HANDLE_DESTROYED
5477  *   SCF_ERROR_INTERNAL
5478  *   SCF_ERROR_NO_MEMORY
5479  *   SCF_ERROR_NO_RESOURCES
5480  *   SCF_ERROR_NOT_BOUND
5481  *   SCF_ERROR_PERMISSION_DENIED
5482  *   SCF_ERROR_TEMPLATE_INVALID
5483  */
5484 static int
5485 _add_tmpl_int_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
5486     scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop,
5487     int64_t val, int64_t *min, int64_t *max)
5488 {
5489 	char *pg_name = NULL;
5490 	char *prop_name = NULL;
5491 	char *s_min = NULL;
5492 	char *s_max = NULL;
5493 	char *value = NULL;
5494 	char *t_fmri = NULL;
5495 	char *t_pg_name = NULL;
5496 	char *t_pg_type = NULL;
5497 	char *t_prop_name = NULL;
5498 	char *t_prop_type = NULL;
5499 
5500 	if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5501 		return (-1);
5502 
5503 	switch (type) {
5504 	case SCF_TERR_RANGE_VIOLATION:
5505 		if ((pg_name = _scf_get_pg_name(pg)) == NULL)
5506 			goto cleanup;
5507 		if ((prop_name = _scf_get_prop_name(prop)) == NULL)
5508 			goto cleanup;
5509 		break;
5510 	case SCF_TERR_VALUE_OUT_OF_RANGE:
5511 		/* keep pg_name = NULL and prop_name = NULL */
5512 		break;
5513 	}
5514 	if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
5515 		goto cleanup;
5516 	}
5517 	if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
5518 		goto cleanup;
5519 	}
5520 	if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
5521 		goto cleanup;
5522 	}
5523 	t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
5524 	if (t_prop_type != NULL && t_prop_type[0] == '\0') {
5525 		free(t_prop_type);
5526 		t_prop_type = NULL;
5527 	} else if (t_prop_type == NULL) {
5528 		goto cleanup;
5529 	}
5530 	if (t_prop_type == NULL)
5531 		if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
5532 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5533 			goto cleanup;
5534 		}
5535 	if (min == NULL) {
5536 		if ((s_min = strdup("")) == NULL) {
5537 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5538 			goto cleanup;
5539 		}
5540 	} else {
5541 		if ((s_min = _val_to_string(*((uint64_t *)min), 1)) == NULL) {
5542 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5543 			goto cleanup;
5544 		}
5545 	}
5546 	if (max == NULL) {
5547 		if ((s_max = strdup("")) == NULL) {
5548 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5549 			goto cleanup;
5550 		}
5551 	} else {
5552 		if ((s_max = _val_to_string(*((uint64_t *)max), 1)) == NULL) {
5553 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5554 			goto cleanup;
5555 		}
5556 	}
5557 	if ((value = _val_to_string((uint64_t)val, 1)) == NULL) {
5558 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5559 		goto cleanup;
5560 	}
5561 
5562 	return (_scf_tmpl_add_error(errs, type, pg_name, prop_name, s_min,
5563 	    s_max, value, t_fmri, t_pg_name, t_pg_type, t_prop_name,
5564 	    t_prop_type));
5565 cleanup:
5566 	free(pg_name);
5567 	free(prop_name);
5568 	free(s_min);
5569 	free(s_max);
5570 	free(value);
5571 	free(t_fmri);
5572 	free(t_pg_name);
5573 	free(t_pg_type);
5574 	free(t_prop_name);
5575 	free(t_prop_type);
5576 	return (-1);
5577 }
5578 
5579 /*
5580  * return 0 on success, -1 on failure.
5581  * set scf_error() to:
5582  *   SCF_ERROR_BACKEND_ACCESS
5583  *   SCF_ERROR_CONNECTION_BROKEN
5584  *   SCF_ERROR_DELETED
5585  *   SCF_ERROR_HANDLE_DESTROYED
5586  *   SCF_ERROR_INTERNAL
5587  *   SCF_ERROR_NO_MEMORY
5588  *   SCF_ERROR_NO_RESOURCES
5589  *   SCF_ERROR_NOT_BOUND
5590  *   SCF_ERROR_PERMISSION_DENIED
5591  *   SCF_ERROR_TEMPLATE_INVALID
5592  */
5593 static int
5594 _add_tmpl_pg_redefine_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t,
5595     scf_pg_tmpl_t *r)
5596 {
5597 	char *ev1 = NULL;
5598 	char *ev2 = NULL;
5599 	char *t_fmri = NULL;
5600 	char *t_pg_name = NULL;
5601 	char *t_pg_type = NULL;
5602 
5603 	if ((t_fmri = _scf_tmpl_get_fmri(r)) == NULL)
5604 		return (-1);
5605 	if (scf_tmpl_pg_name(r, &t_pg_name) == -1) {
5606 		goto cleanup;
5607 	}
5608 	if (scf_tmpl_pg_type(r, &t_pg_type) == -1) {
5609 		goto cleanup;
5610 	}
5611 	if (scf_tmpl_pg_name(t, &ev1) == -1) {
5612 		goto cleanup;
5613 	}
5614 	if (scf_tmpl_pg_type(t, &ev2) == -1) {
5615 		goto cleanup;
5616 	}
5617 
5618 	return (_scf_tmpl_add_error(errs, SCF_TERR_PG_REDEFINE, NULL, NULL,
5619 	    ev1, ev2, NULL, t_fmri, t_pg_name, t_pg_type, NULL, NULL));
5620 cleanup:
5621 	free(ev1);
5622 	free(ev2);
5623 	free(t_fmri);
5624 	free(t_pg_name);
5625 	free(t_pg_type);
5626 	return (-1);
5627 }
5628 
5629 /*
5630  * return 0 if value is within count ranges constraint.
5631  * return -1 otherwise
5632  */
5633 static int
5634 _check_count_ranges(scf_count_ranges_t *cr, uint64_t v)
5635 {
5636 	int i;
5637 
5638 	for (i = 0; i < cr->scr_num_ranges; ++i) {
5639 		if (v >= cr->scr_min[i] &&
5640 		    v <= cr->scr_max[i]) {
5641 			/* value is within ranges constraint */
5642 			return (0);
5643 		}
5644 	}
5645 	return (-1);
5646 }
5647 
5648 /*
5649  * return 0 if value is within count ranges constraint.
5650  * return -1 otherwise
5651  */
5652 static int
5653 _check_int_ranges(scf_int_ranges_t *ir, int64_t v)
5654 {
5655 	int i;
5656 
5657 	for (i = 0; i < ir->sir_num_ranges; ++i) {
5658 		if (v >= ir->sir_min[i] &&
5659 		    v <= ir->sir_max[i]) {
5660 			/* value is within integer ranges constraint */
5661 			return (0);
5662 		}
5663 	}
5664 	return (-1);
5665 }
5666 
5667 /*
5668  * int _value_in_constraint()
5669  *
5670  * Checks whether the supplied value violates any of the constraints
5671  * specified in the supplied property template.  If it does, an appropriate
5672  * error is appended to "errs".  pg and prop, if supplied, are used to
5673  * augment the information in the error.  Returns 0 on success.
5674  *
5675  * Returns -1 on failure.  Sets scf_error():
5676  *   SCF_ERROR_BACKEND_ACCESS
5677  *   SCF_ERROR_CONNECTION_BROKEN
5678  *   SCF_ERROR_DELETED
5679  *   SCF_ERROR_HANDLE_DESTROYED
5680  *   SCF_ERROR_INTERNAL
5681  *   SCF_ERROR_INVALID_ARGUMENT
5682  *   SCF_ERROR_NO_MEMORY
5683  *   SCF_ERROR_NO_RESOURCES
5684  *   SCF_ERROR_NOT_BOUND
5685  *   SCF_ERROR_PERMISSION_DENIED
5686  *   SCF_ERROR_TEMPLATE_INVALID
5687  */
5688 static int
5689 _value_in_constraint(scf_propertygroup_t *pg, scf_property_t *prop,
5690     const scf_prop_tmpl_t *pt, scf_value_t *value, scf_tmpl_errors_t *errs)
5691 {
5692 	scf_type_t type, tmpl_type;
5693 	scf_values_t vals;
5694 	scf_tmpl_error_type_t terr_type;
5695 	uint64_t v_count;
5696 	int64_t v_int;
5697 	char *vstr;
5698 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
5699 	ssize_t ret = 0;
5700 	char **constraints;
5701 	int n = 0;
5702 	int r;
5703 	int err_flag = 0;
5704 	scf_count_ranges_t cr;
5705 	scf_int_ranges_t ir;
5706 
5707 	type = scf_value_type(value);
5708 	if (type == SCF_TYPE_INVALID) {
5709 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
5710 		return (-1);
5711 	}
5712 
5713 	/* Check if template type matches value type. */
5714 	if (scf_tmpl_prop_type(pt, &tmpl_type) == -1) {
5715 		if (scf_error() != SCF_ERROR_NOT_FOUND)
5716 			/* type is not wildcarded */
5717 			return (-1);
5718 	} else if (tmpl_type != type) {
5719 		if (errs != NULL) {
5720 			if (pg == NULL && prop == NULL) {
5721 				if (_add_tmpl_constraint_error(errs,
5722 				    SCF_TERR_PROP_TYPE_MISMATCH, NULL, pt,
5723 				    NULL, value) == -1)
5724 					return (-1);
5725 			}
5726 		}
5727 		return (1);
5728 	}
5729 
5730 	/* Numeric values should be checked against any range constraints. */
5731 	switch (type) {
5732 	case SCF_TYPE_COUNT:
5733 		r = scf_value_get_count(value, &v_count);
5734 		assert(r == 0);
5735 
5736 		if (scf_tmpl_value_count_range_constraints(pt, &cr) != 0) {
5737 			if (scf_error() == SCF_ERROR_NOT_FOUND)
5738 				break;
5739 			if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
5740 				(void) scf_set_error(
5741 				    SCF_ERROR_TEMPLATE_INVALID);
5742 			return (-1);
5743 		} else {
5744 			if (_check_count_ranges(&cr, v_count) == 0) {
5745 				/* value is within ranges constraint */
5746 				scf_count_ranges_destroy(&cr);
5747 				return (0);
5748 			}
5749 			scf_count_ranges_destroy(&cr);
5750 		}
5751 
5752 		/*
5753 		 * If we get here, we have a possible constraint
5754 		 * violation.
5755 		 */
5756 		err_flag |= 0x1; /* RANGE_VIOLATION, count */
5757 		break;
5758 	case SCF_TYPE_INTEGER:
5759 		if (scf_value_get_integer(value, &v_int) != 0)
5760 			assert(0);
5761 		if (scf_tmpl_value_int_range_constraints(pt, &ir) != 0) {
5762 			if (scf_error() == SCF_ERROR_NOT_FOUND)
5763 				break;
5764 			if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
5765 				(void) scf_set_error(
5766 				    SCF_ERROR_TEMPLATE_INVALID);
5767 			return (-1);
5768 		} else {
5769 			if (_check_int_ranges(&ir, v_int) == 0) {
5770 				/* value is within ranges constraint */
5771 				scf_int_ranges_destroy(&ir);
5772 				return (0);
5773 			}
5774 			scf_int_ranges_destroy(&ir);
5775 		}
5776 		/*
5777 		 * If we get here, we have a possible constraint
5778 		 * violation.
5779 		 */
5780 		err_flag |= 0x2; /* RANGE_VIOLATION, integer */
5781 		break;
5782 	default:
5783 		break;
5784 	}
5785 
5786 	vstr = malloc(sz);
5787 	if (vstr == NULL) {
5788 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5789 		return (-1);
5790 	}
5791 
5792 	/*
5793 	 * If a set of names is provided, confirm value has one of
5794 	 * those names.
5795 	 */
5796 	if (scf_tmpl_value_name_constraints(pt, &vals) != 0) {
5797 		free(vstr);
5798 		if (scf_error() != SCF_ERROR_NOT_FOUND) {
5799 			return (-1);
5800 		}
5801 	} else {
5802 		r = scf_value_get_as_string_typed(value, type, vstr, sz);
5803 
5804 		/*
5805 		 * All errors (INVALID_ARGUMENT, NOT_SET, TYPE_MISMATCH)
5806 		 * should be impossible or already caught above.
5807 		 */
5808 		assert(r > 0);
5809 
5810 		constraints = vals.values.v_astring;
5811 		for (n = 0; constraints[n] != NULL; ++n) {
5812 			if (strcmp(constraints[n], vstr) == 0) {
5813 				/* value is within constraint */
5814 				scf_values_destroy(&vals);
5815 				free(vstr);
5816 				return (0);
5817 			}
5818 		}
5819 		/* if we get here, we have a constraint violation */
5820 		err_flag |= 0x4; /* CONSTRAINT_VIOLATED */
5821 		scf_values_destroy(&vals);
5822 		free(vstr);
5823 	}
5824 	if (err_flag != 0)
5825 		ret = 1;
5826 	/* register the errors found */
5827 	if (ret == 1 && errs != NULL) {
5828 		if ((err_flag & 0x1) == 0x1) {
5829 			/*
5830 			 * Help make the error more human-friendly.  If
5831 			 * pg and prop are provided, we know we're
5832 			 * validating repository data.  If they're not,
5833 			 * we're validating a potentially hypothetical
5834 			 * value.
5835 			 */
5836 			if (pg == NULL && prop == NULL)
5837 				terr_type = SCF_TERR_VALUE_OUT_OF_RANGE;
5838 			else
5839 				terr_type = SCF_TERR_RANGE_VIOLATION;
5840 			if (_add_tmpl_count_error(errs, terr_type, pg, pt,
5841 			    prop, v_count, 0, 0) == -1)
5842 				ret = -1;
5843 		}
5844 		if ((err_flag & 0x2) == 0x2) {
5845 			if (pg == NULL && prop == NULL)
5846 				terr_type = SCF_TERR_VALUE_OUT_OF_RANGE;
5847 			else
5848 				terr_type = SCF_TERR_RANGE_VIOLATION;
5849 			if (_add_tmpl_int_error(errs, terr_type, pg, pt, prop,
5850 			    v_int, 0, 0) == -1)
5851 				ret = -1;
5852 		}
5853 		if ((err_flag & 0x4) == 0x4) {
5854 			if (pg == NULL && prop == NULL)
5855 				terr_type = SCF_TERR_INVALID_VALUE;
5856 			else
5857 				terr_type = SCF_TERR_VALUE_CONSTRAINT_VIOLATED;
5858 			if (_add_tmpl_constraint_error(errs, terr_type, pg,
5859 			    pt, prop, value) == -1)
5860 				ret = -1;
5861 		}
5862 	}
5863 	return (ret);
5864 }
5865 
5866 /*
5867  * Returns -1 on failure.  Sets scf_error():
5868  *   SCF_ERROR_BACKEND_ACCESS
5869  *   SCF_ERROR_CONNECTION_BROKEN
5870  *   SCF_ERROR_DELETED
5871  *   SCF_ERROR_HANDLE_DESTROYED
5872  *   SCF_ERROR_INTERNAL
5873  *   SCF_ERROR_INVALID_ARGUMENT
5874  *   SCF_ERROR_NO_MEMORY
5875  *   SCF_ERROR_NO_RESOURCES
5876  *   SCF_ERROR_NOT_BOUND
5877  *   SCF_ERROR_PERMISSION_DENIED
5878  *   SCF_ERROR_TEMPLATE_INVALID
5879  */
5880 int
5881 scf_tmpl_value_in_constraint(const scf_prop_tmpl_t *pt, scf_value_t *value,
5882     scf_tmpl_errors_t **errs)
5883 {
5884 	scf_tmpl_errors_t *e = NULL;
5885 
5886 	if (errs != NULL) {
5887 		char *fmri;
5888 
5889 		if ((fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
5890 			return (-1);
5891 		*errs = _scf_create_errors(fmri, 1);
5892 		free(fmri);
5893 		if (*errs == NULL)
5894 			return (-1);
5895 		e = *errs;
5896 	}
5897 
5898 	return (_value_in_constraint(NULL, NULL, pt, value, e));
5899 }
5900 
5901 scf_tmpl_error_t *
5902 scf_tmpl_next_error(scf_tmpl_errors_t *errs)
5903 {
5904 	if (errs->tes_index < errs->tes_num_errs) {
5905 		assert(errs->tes_errs[errs->tes_index] != NULL);
5906 		return (errs->tes_errs[errs->tes_index++]);
5907 	} else {
5908 		return (NULL);
5909 	}
5910 }
5911 
5912 void
5913 scf_tmpl_reset_errors(scf_tmpl_errors_t *errs)
5914 {
5915 	errs->tes_index = 0;
5916 }
5917 
5918 int
5919 scf_tmpl_strerror(scf_tmpl_error_t *err,  char *s, size_t n, int flag)
5920 {
5921 	const char *str;
5922 	int i;
5923 	int ret = -1;
5924 	int nsz = 0;	/* err msg length */
5925 	int sz = n;	/* available buffer size */
5926 	char *buf = s;	/* where to append in buffer */
5927 	char *s0 = (flag == SCF_TMPL_STRERROR_HUMAN) ? ":\n\t" : ": ";
5928 	char *s1 = (flag == SCF_TMPL_STRERROR_HUMAN) ? "\n\t" : "; ";
5929 	char *sep = s0;
5930 	const char *val;
5931 
5932 	/* prefix */
5933 	if (err->te_errs->tes_prefix != NULL) {
5934 		ret = snprintf(buf, sz, "%s", dgettext(TEXT_DOMAIN,
5935 		    err->te_errs->tes_prefix));
5936 		nsz += ret;
5937 		sz = (sz - ret) > 0 ? sz - ret : 0;
5938 		buf = (sz > 0) ? s + nsz : NULL;
5939 	}
5940 	/* error message */
5941 	ret = snprintf(buf, sz, "%s", dgettext(TEXT_DOMAIN,
5942 	    em_desc[err->te_type].em_msg));
5943 	nsz += ret;
5944 	sz = (sz - ret) > 0 ? sz - ret : 0;
5945 	buf = (sz > 0) ? s + nsz : NULL;
5946 
5947 	for (i = 0; _tmpl_error_items[i].get_desc != NULL; ++i) {
5948 		if ((str = _tmpl_error_items[i].get_desc(err)) == NULL)
5949 			/* no item to print */
5950 			continue;
5951 		val = _tmpl_error_items[i].get_val(err);
5952 		ret = snprintf(buf, sz, "%s%s=\"%s\"", sep, str,
5953 		    (val == NULL) ? "" : val);
5954 		nsz += ret;
5955 		sz = (sz - ret) > 0 ? sz - ret : 0;
5956 		buf = (sz > 0) ? s + nsz : NULL;
5957 		sep = s1;
5958 	}
5959 	return (nsz);
5960 }
5961 
5962 /*
5963  * return 0 on success, -1 on failure.
5964  * set scf_error() to:
5965  *   SCF_ERROR_BACKEND_ACCESS
5966  *   SCF_ERROR_CONNECTION_BROKEN
5967  *   SCF_ERROR_DELETED
5968  *   SCF_ERROR_HANDLE_DESTROYED
5969  *   SCF_ERROR_INTERNAL
5970  *   SCF_ERROR_NO_MEMORY
5971  *   SCF_ERROR_NO_RESOURCES
5972  *   SCF_ERROR_NOT_BOUND
5973  *   SCF_ERROR_PERMISSION_DENIED
5974  *   SCF_ERROR_TEMPLATE_INVALID
5975  */
5976 static int
5977 _validate_cardinality(scf_propertygroup_t *pg, scf_prop_tmpl_t *pt,
5978     scf_property_t *prop, scf_tmpl_errors_t *errs)
5979 {
5980 	uint64_t min, max;
5981 	scf_handle_t *h;
5982 	scf_iter_t *iter = NULL;
5983 	scf_value_t *val = NULL;
5984 	int count = 0;
5985 	int ret = -1;
5986 	int r;
5987 
5988 	if (scf_tmpl_prop_cardinality(pt, &min, &max) != 0) {
5989 		if (scf_error() == SCF_ERROR_NOT_FOUND)
5990 			return (0);
5991 		else
5992 			return (-1);
5993 	}
5994 
5995 	/* Any number of values permitted.  Just return success. */
5996 	if (min == 0 && max == UINT64_MAX) {
5997 		return (0);
5998 	}
5999 
6000 	h = scf_property_handle(prop);
6001 	if (h == NULL) {
6002 		assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
6003 		goto cleanup;
6004 	}
6005 
6006 	iter = scf_iter_create(h);
6007 	val = scf_value_create(h);
6008 	if (iter == NULL || val == NULL) {
6009 		if (ismember(scf_error(), errors_server)) {
6010 			goto cleanup;
6011 		} else {
6012 			assert(0);
6013 			abort();
6014 		}
6015 	}
6016 
6017 	if (scf_iter_property_values(iter, prop) != 0) {
6018 		if (ismember(scf_error(), errors_server)) {
6019 			goto cleanup;
6020 		} else {
6021 			assert(0);
6022 			abort();
6023 		}
6024 	}
6025 
6026 	while ((r = scf_iter_next_value(iter, val)) == 1)
6027 		count++;
6028 
6029 	if (r < 0) {
6030 		if (ismember(scf_error(), errors_server)) {
6031 			goto cleanup;
6032 		} else {
6033 			assert(0);
6034 			abort();
6035 		}
6036 	}
6037 
6038 	if (count < min || count > max)
6039 		if (_add_tmpl_count_error(errs, SCF_TERR_CARDINALITY_VIOLATION,
6040 		    pg, pt, prop, (uint64_t)count, &min, &max) == -1)
6041 			goto cleanup;
6042 
6043 	ret = 0;
6044 
6045 cleanup:
6046 	scf_iter_destroy(iter);
6047 	scf_value_destroy(val);
6048 	return (ret);
6049 }
6050 
6051 /*
6052  * Returns -1 on error.  Sets scf_error():
6053  *   SCF_ERROR_BACKEND_ACCESS
6054  *   SCF_ERROR_CONNECTION_BROKEN
6055  *   SCF_ERROR_DELETED
6056  *   SCF_ERROR_HANDLE_DESTROYED
6057  *   SCF_ERROR_INTERNAL
6058  *   SCF_ERROR_NO_MEMORY
6059  *   SCF_ERROR_NO_RESOURCES
6060  *   SCF_ERROR_NOT_BOUND
6061  *   SCF_ERROR_PERMISSION_DENIED
6062  *   SCF_ERROR_TEMPLATE_INVALID
6063  */
6064 static int
6065 _check_property(scf_prop_tmpl_t *pt, scf_propertygroup_t *pg,
6066     scf_property_t *prop, scf_tmpl_errors_t *errs)
6067 {
6068 	scf_type_t tmpl_type;
6069 	uint8_t required;
6070 	scf_handle_t *h;
6071 	scf_iter_t *iter = NULL;
6072 	scf_value_t *val = NULL;
6073 	int r;
6074 	int ret = -1;
6075 
6076 	h = scf_pg_handle(pg);
6077 	if (h == NULL) {
6078 		assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
6079 		return (-1);
6080 	}
6081 
6082 	iter = scf_iter_create(h);
6083 	val = scf_value_create(h);
6084 	if (iter == NULL || val == NULL) {
6085 		if (ismember(scf_error(), errors_server)) {
6086 			scf_iter_destroy(iter);
6087 			scf_value_destroy(val);
6088 			return (-1);
6089 		} else {
6090 			assert(0);
6091 			abort();
6092 		}
6093 	}
6094 
6095 	if (scf_tmpl_prop_required(pt, &required) != 0)
6096 		goto cleanup;
6097 
6098 	/* Check type */
6099 	if (scf_tmpl_prop_type(pt, &tmpl_type) == -1) {
6100 		if (scf_error() != SCF_ERROR_NOT_FOUND) {
6101 			goto cleanup;
6102 		} else if (required) {
6103 			/* If required, type must be specified. */
6104 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
6105 			goto cleanup;
6106 		}
6107 	} else if (scf_property_is_type(prop, tmpl_type) != 0) {
6108 		if (ismember(scf_error(), errors_server)) {
6109 			goto cleanup;
6110 		} else switch (scf_error()) {
6111 		case SCF_ERROR_TYPE_MISMATCH:
6112 			if (_add_tmpl_wrong_prop_type_error(errs, pg, pt,
6113 			    prop) == -1)
6114 				goto cleanup;
6115 			break;
6116 
6117 		case SCF_ERROR_INVALID_ARGUMENT:
6118 			/*
6119 			 * tmpl_prop_type shouldn't have handed back
6120 			 * an invalid property type.
6121 			 */
6122 		case SCF_ERROR_NOT_SET:
6123 		default:
6124 			assert(0);
6125 			abort();
6126 		}
6127 	}
6128 
6129 
6130 	/* Cardinality */
6131 	if (_validate_cardinality(pg, pt, prop, errs) == -1)
6132 		goto cleanup;
6133 
6134 	/* Value constraints */
6135 	/*
6136 	 * Iterate through each value, and confirm it is defined as
6137 	 * constrained.
6138 	 */
6139 	if (scf_iter_property_values(iter, prop) != 0) {
6140 		assert(scf_error() != SCF_ERROR_NOT_SET &&
6141 		    scf_error() != SCF_ERROR_HANDLE_MISMATCH);
6142 		goto cleanup;
6143 	}
6144 
6145 	while ((r = scf_iter_next_value(iter, val)) == 1) {
6146 		if (_value_in_constraint(pg, prop, pt, val, errs) == -1) {
6147 			if (ismember(scf_error(), errors_server)) {
6148 				goto cleanup;
6149 			} else switch (scf_error()) {
6150 			case SCF_ERROR_TEMPLATE_INVALID:
6151 				goto cleanup;
6152 
6153 			case SCF_ERROR_INVALID_ARGUMENT:
6154 			default:
6155 				assert(0);
6156 				abort();
6157 			}
6158 		}
6159 	}
6160 
6161 	if (r < 0) {
6162 		if (ismember(scf_error(), errors_server)) {
6163 			goto cleanup;
6164 		} else {
6165 			assert(0);
6166 			abort();
6167 		}
6168 	}
6169 
6170 	ret = 0;
6171 
6172 cleanup:
6173 	scf_iter_destroy(iter);
6174 	scf_value_destroy(val);
6175 	return (ret);
6176 }
6177 
6178 /*
6179  * Returns -1 on failure, sets scf_error() to:
6180  *   SCF_ERROR_BACKEND_ACCESS
6181  *   SCF_ERROR_CONNECTION_BROKEN
6182  *   SCF_ERROR_DELETED
6183  *   SCF_ERROR_HANDLE_DESTROYED
6184  *   SCF_ERROR_INTERNAL
6185  *   SCF_ERROR_NO_MEMORY
6186  *   SCF_ERROR_NO_RESOURCES
6187  *   SCF_ERROR_NOT_BOUND
6188  *   SCF_ERROR_PERMISSION_DENIED
6189  *   SCF_ERROR_TEMPLATE_INVALID
6190  */
6191 static int
6192 _check_pg(scf_pg_tmpl_t *t, scf_propertygroup_t *pg, char *pg_name,
6193     char *type, scf_tmpl_errors_t *errs)
6194 {
6195 	scf_prop_tmpl_t *pt = NULL;
6196 	char *pg_type = NULL;
6197 	scf_iter_t *iter = NULL;
6198 	uint8_t pg_required;
6199 	scf_property_t *prop = NULL;
6200 	scf_handle_t *h;
6201 	int r;
6202 	char *prop_name = NULL;
6203 	ssize_t nsize = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
6204 	int ret = -1;
6205 
6206 	assert(pg_name != NULL);
6207 	assert(t != NULL);
6208 	assert(pg != NULL);
6209 	assert(type != NULL);
6210 	assert(nsize != 0);
6211 
6212 	if ((h = scf_pg_handle(pg)) == NULL) {
6213 		assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
6214 		return (-1);
6215 	}
6216 	if ((pt = scf_tmpl_prop_create(h)) == NULL) {
6217 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
6218 		return (-1);
6219 	}
6220 
6221 	if ((prop = scf_property_create(h)) == NULL) {
6222 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
6223 		goto cleanup;
6224 	}
6225 
6226 	if ((iter = scf_iter_create(h)) == NULL) {
6227 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
6228 		goto cleanup;
6229 	}
6230 	if ((prop_name = malloc(nsize)) == NULL) {
6231 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
6232 		goto cleanup;
6233 	}
6234 
6235 	if (scf_tmpl_pg_required(t, &pg_required) != 0)
6236 		goto cleanup;
6237 
6238 	if (scf_tmpl_pg_type(t, &pg_type) == -1) {
6239 		goto cleanup;
6240 	} else if (pg_required != 0 &&
6241 	    strcmp(SCF_TMPL_WILDCARD, pg_type) == 0) {
6242 		/* Type must be specified for required pgs. */
6243 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
6244 		goto cleanup;
6245 	}
6246 
6247 	if (pg_type != NULL) {
6248 		if (strcmp(pg_type, type) != 0 &&
6249 		    strcmp(pg_type, SCF_TMPL_WILDCARD) != 0) {
6250 			if (_add_tmpl_wrong_pg_type_error(errs, t, pg) == -1)
6251 				goto cleanup;
6252 		}
6253 	}
6254 
6255 
6256 	/* Iterate through properties in the repository and check them. */
6257 	if (scf_iter_pg_properties(iter, pg) != 0) {
6258 		if (ismember(scf_error(), errors_server)) {
6259 			goto cleanup;
6260 		} else {
6261 			assert(0);
6262 			abort();
6263 		}
6264 	}
6265 
6266 	while ((r = scf_iter_next_property(iter, prop)) == 1) {
6267 		if (scf_property_get_name(prop, prop_name, nsize) == -1) {
6268 			assert(scf_error() != SCF_ERROR_NOT_SET);
6269 			goto cleanup;
6270 		}
6271 		if (scf_tmpl_get_by_prop(t, prop_name, pt, 0) != 0) {
6272 			if (ismember(scf_error(), errors_server)) {
6273 				goto cleanup;
6274 			} else switch (scf_error()) {
6275 			case SCF_ERROR_NOT_FOUND:
6276 				/* No template.  Continue. */
6277 				continue;
6278 
6279 			case SCF_ERROR_INVALID_ARGUMENT:
6280 			default:
6281 				assert(0);
6282 				abort();
6283 			}
6284 		}
6285 
6286 		if (_check_property(pt, pg, prop, errs) != 0)
6287 			goto cleanup;
6288 	}
6289 
6290 	if (r < 0) {
6291 		if (ismember(scf_error(), errors_server)) {
6292 			goto cleanup;
6293 		} else {
6294 			assert(0);
6295 			abort();
6296 		}
6297 	}
6298 
6299 	scf_tmpl_prop_reset(pt);
6300 	free(prop_name);
6301 	prop_name = NULL;
6302 	/*
6303 	 * Confirm required properties are present.
6304 	 */
6305 	while ((r = scf_tmpl_iter_props(t, pt,
6306 	    SCF_PROP_TMPL_FLAG_REQUIRED)) == 0) {
6307 		scf_type_t prop_type;
6308 
6309 		if (scf_tmpl_prop_name(pt, &prop_name) == -1)
6310 			goto cleanup;
6311 
6312 		/* required properties cannot have type wildcarded */
6313 		if (scf_tmpl_prop_type(pt, &prop_type) == -1) {
6314 			if (scf_error() == SCF_ERROR_NOT_FOUND)
6315 				(void) scf_set_error(
6316 				    SCF_ERROR_TEMPLATE_INVALID);
6317 			goto cleanup;
6318 		}
6319 
6320 		if (scf_pg_get_property(pg, prop_name, prop) != 0) {
6321 			if (ismember(scf_error(), errors_server)) {
6322 				goto cleanup;
6323 			} else switch (scf_error()) {
6324 			case SCF_ERROR_NOT_FOUND:
6325 				if (_add_tmpl_missing_prop_error(errs, t, pg,
6326 				    pt) == -1)
6327 					goto cleanup;
6328 				break;
6329 
6330 			case SCF_ERROR_INVALID_ARGUMENT:
6331 				(void) scf_set_error(
6332 				    SCF_ERROR_TEMPLATE_INVALID);
6333 				goto cleanup;
6334 
6335 			case SCF_ERROR_HANDLE_MISMATCH:
6336 			case SCF_ERROR_NOT_SET:
6337 			default:
6338 				assert(0);
6339 				abort();
6340 			}
6341 		}
6342 		free(prop_name);
6343 		prop_name = NULL;
6344 	}
6345 	if (r < 0) {
6346 		if (ismember(scf_error(), errors_server)) {
6347 			goto cleanup;
6348 		} else switch (scf_error()) {
6349 		case SCF_ERROR_NOT_FOUND:
6350 			break;
6351 
6352 		case SCF_ERROR_TEMPLATE_INVALID:
6353 			goto cleanup;
6354 
6355 		case SCF_ERROR_INVALID_ARGUMENT:
6356 		default:
6357 			assert(0);
6358 			abort();
6359 		}
6360 	}
6361 
6362 	ret = 0;
6363 cleanup:
6364 	scf_tmpl_prop_destroy(pt);
6365 	scf_iter_destroy(iter);
6366 	scf_property_destroy(prop);
6367 	free(prop_name);
6368 	free(pg_type);
6369 	return (ret);
6370 }
6371 
6372 /*
6373  * Checks if instance fmri redefines any pgs defined in restarter or global
6374  * Return -1 on failure, sets scf_error() to:
6375  *   SCF_ERROR_BACKEND_ACCESS
6376  *   SCF_ERROR_CONNECTION_BROKEN
6377  *   SCF_ERROR_DELETED
6378  *   SCF_ERROR_HANDLE_DESTROYED
6379  *   SCF_ERROR_INTERNAL
6380  *   SCF_ERROR_INVALID_ARGUMENT
6381  *   SCF_ERROR_NO_MEMORY
6382  *   SCF_ERROR_NO_RESOURCES
6383  *   SCF_ERROR_NOT_BOUND
6384  *   SCF_ERROR_NOT_FOUND
6385  *   SCF_ERROR_PERMISSION_DENIED
6386  *   SCF_ERROR_TEMPLATE_INVALID
6387  */
6388 static int
6389 _scf_tmpl_check_pg_redef(scf_handle_t *h, const char *fmri,
6390     const char *snapname, scf_tmpl_errors_t *errs)
6391 {
6392 	scf_pg_tmpl_t *t = NULL;
6393 	scf_pg_tmpl_t *r = NULL;
6394 	char *pg_name = NULL;
6395 	char *pg_name_r = NULL;
6396 	char *pg_type = NULL;
6397 	char *pg_type_r = NULL;
6398 	char *target = NULL;
6399 	int ret_val = -1;
6400 	int ret;
6401 
6402 	t = scf_tmpl_pg_create(h);
6403 	r = scf_tmpl_pg_create(h);
6404 	if (t == NULL || r == NULL)
6405 		goto cleanup;
6406 
6407 	while ((ret = scf_tmpl_iter_pgs(t, fmri, snapname, NULL,
6408 	    SCF_PG_TMPL_FLAG_EXACT)) == 1) {
6409 		if (scf_tmpl_pg_name(t, &pg_name) == -1) {
6410 			goto cleanup;
6411 		}
6412 		if (scf_tmpl_pg_type(t, &pg_type) == -1) {
6413 			goto cleanup;
6414 		}
6415 		/* look for a redefinition of a global/restarter pg_pattern */
6416 		while ((ret = scf_tmpl_iter_pgs(r, fmri, snapname, pg_type,
6417 		    0)) == 1) {
6418 			if (scf_tmpl_pg_name(r, &pg_name_r) == -1) {
6419 				goto cleanup;
6420 			} else if (strcmp(pg_name_r, SCF_TMPL_WILDCARD) != 0 &&
6421 			    strcmp(pg_name, SCF_TMPL_WILDCARD) != 0 &&
6422 			    strcmp(pg_name, pg_name_r) != 0) {
6423 				/* not a match */
6424 				free(pg_name_r);
6425 				pg_name_r = NULL;
6426 				continue;
6427 			}
6428 			if (scf_tmpl_pg_type(r, &pg_type_r) == -1) {
6429 				goto cleanup;
6430 			} else if (strcmp(pg_type_r, SCF_TMPL_WILDCARD) != 0 &&
6431 			    strcmp(pg_type, SCF_TMPL_WILDCARD) != 0 &&
6432 			    strcmp(pg_type, pg_type_r) != 0) {
6433 				/* not a match */
6434 				free(pg_name_r);
6435 				pg_name_r = NULL;
6436 				free(pg_type_r);
6437 				pg_type_r = NULL;
6438 				continue;
6439 			}
6440 			if (scf_tmpl_pg_target(r, &target) == -1) {
6441 				target = NULL;
6442 				goto cleanup;
6443 			}
6444 			if (strcmp(target, SCF_TM_TARGET_ALL) == 0 ||
6445 			    strcmp(target, SCF_TM_TARGET_DELEGATE) == 0) {
6446 				/* found a pg_pattern redefinition */
6447 				if (_add_tmpl_pg_redefine_error(errs, t,
6448 				    r) == -1)
6449 					goto cleanup;
6450 				free(pg_name_r);
6451 				pg_name_r = NULL;
6452 				free(pg_type_r);
6453 				pg_type_r = NULL;
6454 				free(target);
6455 				target = NULL;
6456 				break;
6457 			}
6458 			free(pg_name_r);
6459 			pg_name_r = NULL;
6460 			free(pg_type_r);
6461 			pg_type_r = NULL;
6462 			free(target);
6463 			target = NULL;
6464 		}
6465 		if (ret == -1)
6466 			goto cleanup;
6467 		scf_tmpl_pg_reset(r);
6468 
6469 		free(pg_name);
6470 		free(pg_type);
6471 		pg_name = NULL;
6472 		pg_type = NULL;
6473 	}
6474 	if (ret == -1)
6475 		goto cleanup;
6476 
6477 	ret_val = 0;
6478 
6479 cleanup:
6480 	scf_tmpl_pg_destroy(t);
6481 	scf_tmpl_pg_destroy(r);
6482 	free(pg_name);
6483 	free(pg_type);
6484 	free(pg_name_r);
6485 	free(pg_type_r);
6486 	free(target);
6487 
6488 	if (ret_val == -1) {
6489 		if (!ismember(scf_error(), errors_server)) {
6490 			switch (scf_error()) {
6491 			case SCF_ERROR_TYPE_MISMATCH:
6492 				(void) scf_set_error(
6493 				    SCF_ERROR_TEMPLATE_INVALID);
6494 				/*FALLTHROUGH*/
6495 
6496 			case SCF_ERROR_CONSTRAINT_VIOLATED:
6497 			case SCF_ERROR_INVALID_ARGUMENT:
6498 			case SCF_ERROR_NOT_FOUND:
6499 			case SCF_ERROR_TEMPLATE_INVALID:
6500 				break;
6501 
6502 			case SCF_ERROR_HANDLE_MISMATCH:
6503 			case SCF_ERROR_NOT_SET:
6504 			default:
6505 				assert(0);
6506 				abort();
6507 			}
6508 		}
6509 	}
6510 	return (ret_val);
6511 }
6512 
6513 /*
6514  * Returns -1 on failure, sets scf_error() to:
6515  *   SCF_ERROR_BACKEND_ACCESS
6516  *   SCF_ERROR_CONNECTION_BROKEN
6517  *   SCF_ERROR_DELETED
6518  *   SCF_ERROR_HANDLE_DESTROYED
6519  *   SCF_ERROR_INTERNAL
6520  *   SCF_ERROR_INVALID_ARGUMENT
6521  *   SCF_ERROR_NO_MEMORY
6522  *   SCF_ERROR_NO_RESOURCES
6523  *   SCF_ERROR_NOT_BOUND
6524  *   SCF_ERROR_NOT_FOUND
6525  *   SCF_ERROR_PERMISSION_DENIED
6526  *   SCF_ERROR_TEMPLATE_INVALID
6527  */
6528 int
6529 scf_tmpl_validate_fmri(scf_handle_t *h, const char *fmri, const char *snapshot,
6530     scf_tmpl_errors_t **errs, int flags)
6531 {
6532 	scf_pg_tmpl_t *t = NULL;
6533 	scf_iter_t *iter = NULL;
6534 	scf_propertygroup_t *pg = NULL;
6535 	scf_instance_t *inst = NULL;
6536 	scf_snapshot_t *snap = NULL;
6537 	char *type = NULL;
6538 	char *pg_name = NULL;
6539 	ssize_t rsize = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
6540 	ssize_t nsize = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
6541 	int ret = -1;
6542 	int r;
6543 
6544 	assert(errs != NULL);
6545 
6546 	if ((*errs = _scf_create_errors(fmri, 1)) == NULL)
6547 		return (-1);
6548 
6549 	if ((pg = scf_pg_create(h)) == NULL ||
6550 	    (iter = scf_iter_create(h)) == NULL ||
6551 	    (inst = scf_instance_create(h)) == NULL ||
6552 	    (t = scf_tmpl_pg_create(h)) == NULL) {
6553 		/*
6554 		 * Sets SCF_ERROR_INVALID_ARGUMENT, SCF_ERROR_NO_MEMORY,
6555 		 * SCF_ERROR_NO_RESOURCES, SCF_ERROR_INTERNAL or
6556 		 * SCF_ERROR_HANDLE_DESTROYED.
6557 		 */
6558 		goto cleanup;
6559 	}
6560 
6561 	if ((type = malloc(rsize)) == NULL ||
6562 	    (pg_name = malloc(nsize)) == NULL) {
6563 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
6564 		goto cleanup;
6565 	}
6566 
6567 	if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL, NULL,
6568 	    SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
6569 		if (ismember(scf_error(), errors_server)) {
6570 			goto cleanup;
6571 		} else switch (scf_error()) {
6572 		case SCF_ERROR_CONSTRAINT_VIOLATED:
6573 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
6574 			/*FALLTHROUGH*/
6575 
6576 		case SCF_ERROR_INVALID_ARGUMENT:
6577 		case SCF_ERROR_NOT_FOUND:
6578 			goto cleanup;
6579 
6580 		case SCF_ERROR_HANDLE_MISMATCH:
6581 		case SCF_ERROR_NOT_SET:
6582 		default:
6583 			assert(0);
6584 			abort();
6585 		}
6586 	}
6587 
6588 	if (snapshot == NULL || strcmp(snapshot, "running") == 0 ||
6589 	    (flags & SCF_TMPL_VALIDATE_FLAG_CURRENT)) {
6590 		if (_get_snapshot(inst, NULL, &snap) == -1)
6591 			goto cleanup;
6592 	} else {
6593 		(void) scf_set_error(SCF_ERROR_NONE);
6594 		if (_get_snapshot(inst, snapshot, &snap) == -1) {
6595 			goto cleanup;
6596 		} else if (scf_error() == SCF_ERROR_NOT_FOUND) {
6597 			goto cleanup;
6598 		}
6599 	}
6600 	if (_scf_tmpl_check_pg_redef(h, fmri, snapshot, *errs) != 0) {
6601 		goto cleanup;
6602 	}
6603 
6604 	/*
6605 	 * Check that property groups on this instance conform to the template.
6606 	 */
6607 	if (scf_iter_instance_pgs_composed(iter, inst, snap) != 0) {
6608 		if (ismember(scf_error(), errors_server)) {
6609 			goto cleanup;
6610 		} else {
6611 			assert(0);
6612 			abort();
6613 		}
6614 	}
6615 
6616 	while ((r = scf_iter_next_pg(iter, pg)) == 1) {
6617 		if (scf_pg_get_name(pg, pg_name, nsize) == -1) {
6618 			if (ismember(scf_error(), errors_server)) {
6619 				goto cleanup;
6620 			} else {
6621 				assert(0);
6622 				abort();
6623 			}
6624 		}
6625 
6626 		if (scf_pg_get_type(pg, type, rsize) == -1) {
6627 			if (ismember(scf_error(), errors_server)) {
6628 				goto cleanup;
6629 			} else {
6630 				assert(0);
6631 				abort();
6632 			}
6633 		}
6634 
6635 		if (scf_tmpl_get_by_pg_name(fmri, snapshot, pg_name, type, t,
6636 		    0) != 0) {
6637 			if (ismember(scf_error(), errors_server)) {
6638 				goto cleanup;
6639 			} else switch (scf_error()) {
6640 			case SCF_ERROR_NOT_FOUND:
6641 				continue;
6642 
6643 			case SCF_ERROR_INVALID_ARGUMENT:
6644 				goto cleanup;
6645 
6646 			default:
6647 				assert(0);
6648 				abort();
6649 			}
6650 		}
6651 
6652 		if (_check_pg(t, pg, pg_name, type, *errs) != 0)
6653 			goto cleanup;
6654 	}
6655 	if (r < 0) {
6656 		if (ismember(scf_error(), errors_server)) {
6657 			goto cleanup;
6658 		} else {
6659 			assert(0);
6660 			abort();
6661 		}
6662 	}
6663 
6664 	scf_tmpl_pg_reset(t);
6665 
6666 	/*
6667 	 * Confirm required property groups are present.
6668 	 */
6669 	while ((r = scf_tmpl_iter_pgs(t, fmri, snapshot, NULL,
6670 	    SCF_PG_TMPL_FLAG_REQUIRED)) == 1) {
6671 		free(pg_name);
6672 		free(type);
6673 
6674 		if (scf_tmpl_pg_name(t, &pg_name) == -1)
6675 			goto cleanup;
6676 		if (scf_tmpl_pg_type(t, &type) == -1)
6677 			goto cleanup;
6678 		/*
6679 		 * required property group templates should not have
6680 		 * wildcarded name or type
6681 		 */
6682 		if (strcmp(pg_name, SCF_TMPL_WILDCARD) == 0 ||
6683 		    strcmp(type, SCF_TMPL_WILDCARD) == 0) {
6684 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
6685 			goto cleanup;
6686 		}
6687 
6688 		if (_get_pg(NULL, inst, snap, pg_name, pg) != 0) {
6689 			if (ismember(scf_error(), errors_server)) {
6690 				goto cleanup;
6691 			} else switch (scf_error()) {
6692 			case SCF_ERROR_NOT_FOUND:
6693 				if (_add_tmpl_missing_pg_error(*errs, t) == -1)
6694 					goto cleanup;
6695 				continue;
6696 
6697 			case SCF_ERROR_INVALID_ARGUMENT:
6698 			case SCF_ERROR_HANDLE_MISMATCH:
6699 			case SCF_ERROR_NOT_SET:
6700 			default:
6701 				assert(0);
6702 				abort();
6703 			}
6704 		}
6705 	}
6706 	if (r < 0) {
6707 		if (ismember(scf_error(), errors_server)) {
6708 			goto cleanup;
6709 		} else switch (scf_error()) {
6710 		case SCF_ERROR_NOT_FOUND:
6711 			break;
6712 
6713 		case SCF_ERROR_INVALID_ARGUMENT:
6714 			goto cleanup;
6715 
6716 		default:
6717 			assert(0);
6718 			abort();
6719 		}
6720 	}
6721 
6722 	ret = 0;
6723 	if ((*errs)->tes_num_errs > 0)
6724 		ret = 1;
6725 cleanup:
6726 	if (ret != 1) {
6727 		/* there are no errors to report */
6728 		scf_tmpl_errors_destroy(*errs);
6729 		*errs = NULL;
6730 	}
6731 	scf_tmpl_pg_destroy(t);
6732 	free(type);
6733 	free(pg_name);
6734 
6735 	scf_iter_destroy(iter);
6736 	scf_pg_destroy(pg);
6737 	scf_instance_destroy(inst);
6738 	scf_snapshot_destroy(snap);
6739 
6740 	return (ret);
6741 }
6742 
6743 void
6744 scf_tmpl_errors_destroy(scf_tmpl_errors_t *errs)
6745 {
6746 	int i;
6747 	scf_tmpl_error_t *e;
6748 
6749 	if (errs == NULL)
6750 		return;
6751 
6752 	for (i = 0; i < errs->tes_num_errs; ++i) {
6753 		e = errs->tes_errs[i];
6754 		if (errs->tes_flag != 0) {
6755 			free((char *)e->te_pg_name);
6756 			free((char *)e->te_prop_name);
6757 			free((char *)e->te_ev1);
6758 			free((char *)e->te_ev2);
6759 			free((char *)e->te_actual);
6760 			free((char *)e->te_tmpl_fmri);
6761 			free((char *)e->te_tmpl_pg_name);
6762 			free((char *)e->te_tmpl_pg_type);
6763 			free((char *)e->te_tmpl_prop_name);
6764 			free((char *)e->te_tmpl_prop_type);
6765 		}
6766 		free(e);
6767 	}
6768 	free((char *)errs->tes_fmri);
6769 	free((char *)errs->tes_prefix);
6770 	free(errs->tes_errs);
6771 	free(errs);
6772 }
6773 
6774 int
6775 scf_tmpl_error_source_fmri(const scf_tmpl_error_t *err, char **fmri)
6776 {
6777 	assert(err != NULL);
6778 	switch (err->te_type) {
6779 	case SCF_TERR_MISSING_PG:
6780 	case SCF_TERR_WRONG_PG_TYPE:
6781 	case SCF_TERR_MISSING_PROP:
6782 	case SCF_TERR_WRONG_PROP_TYPE:
6783 	case SCF_TERR_CARDINALITY_VIOLATION:
6784 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6785 	case SCF_TERR_RANGE_VIOLATION:
6786 	case SCF_TERR_PROP_TYPE_MISMATCH:
6787 	case SCF_TERR_VALUE_OUT_OF_RANGE:
6788 	case SCF_TERR_INVALID_VALUE:
6789 	case SCF_TERR_PG_REDEFINE:
6790 		*fmri = (char *)err->te_tmpl_fmri;
6791 		return (0);
6792 		/*NOTREACHED*/
6793 	default:
6794 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6795 	}
6796 	return (-1);
6797 }
6798 
6799 int
6800 scf_tmpl_error_type(const scf_tmpl_error_t *err, scf_tmpl_error_type_t *type)
6801 {
6802 	assert(err != NULL);
6803 	switch (err->te_type) {
6804 	case SCF_TERR_MISSING_PG:
6805 	case SCF_TERR_WRONG_PG_TYPE:
6806 	case SCF_TERR_MISSING_PROP:
6807 	case SCF_TERR_WRONG_PROP_TYPE:
6808 	case SCF_TERR_CARDINALITY_VIOLATION:
6809 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6810 	case SCF_TERR_RANGE_VIOLATION:
6811 	case SCF_TERR_PROP_TYPE_MISMATCH:
6812 	case SCF_TERR_VALUE_OUT_OF_RANGE:
6813 	case SCF_TERR_INVALID_VALUE:
6814 	case SCF_TERR_PG_REDEFINE:
6815 		*type = err->te_type;
6816 		return (0);
6817 		/*NOTREACHED*/
6818 	default:
6819 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6820 	}
6821 	return (-1);
6822 }
6823 
6824 int
6825 scf_tmpl_error_pg_tmpl(const scf_tmpl_error_t *err, char **name, char **type)
6826 {
6827 	assert(err != NULL);
6828 	switch (err->te_type) {
6829 	case SCF_TERR_MISSING_PG:
6830 	case SCF_TERR_WRONG_PG_TYPE:
6831 	case SCF_TERR_MISSING_PROP:
6832 	case SCF_TERR_WRONG_PROP_TYPE:
6833 	case SCF_TERR_CARDINALITY_VIOLATION:
6834 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6835 	case SCF_TERR_RANGE_VIOLATION:
6836 	case SCF_TERR_PROP_TYPE_MISMATCH:
6837 	case SCF_TERR_VALUE_OUT_OF_RANGE:
6838 	case SCF_TERR_INVALID_VALUE:
6839 	case SCF_TERR_PG_REDEFINE:
6840 		if (err->te_tmpl_pg_name != NULL &&
6841 		    err->te_tmpl_pg_type != NULL) {
6842 			if (name != NULL)
6843 				*name = (char *)err->te_tmpl_pg_name;
6844 			if (type != NULL)
6845 				*type = (char *)err->te_tmpl_pg_type;
6846 			return (0);
6847 		}
6848 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
6849 		break;
6850 	default:
6851 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6852 	}
6853 	return (-1);
6854 }
6855 
6856 int
6857 scf_tmpl_error_pg(const scf_tmpl_error_t *err, char **name, char **type)
6858 {
6859 	assert(err != NULL);
6860 	switch (err->te_type) {
6861 	case SCF_TERR_WRONG_PG_TYPE:
6862 		if (err->te_pg_name != NULL &&
6863 		    err->te_actual != NULL) {
6864 			if (name != NULL)
6865 				*name = (char *)err->te_pg_name;
6866 			if (type != NULL)
6867 				*type = (char *)err->te_actual;
6868 			return (0);
6869 		}
6870 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
6871 		break;
6872 	case SCF_TERR_WRONG_PROP_TYPE:
6873 	case SCF_TERR_CARDINALITY_VIOLATION:
6874 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6875 	case SCF_TERR_RANGE_VIOLATION:
6876 		if (err->te_pg_name != NULL &&
6877 		    err->te_tmpl_pg_type != NULL) {
6878 			if (name != NULL)
6879 				*name = (char *)err->te_pg_name;
6880 			if (type != NULL)
6881 				*type = (char *)err->te_tmpl_pg_type;
6882 			return (0);
6883 		}
6884 		/*FALLTHROUGH*/
6885 	case SCF_TERR_MISSING_PROP:
6886 	case SCF_TERR_MISSING_PG:
6887 	case SCF_TERR_PROP_TYPE_MISMATCH:
6888 	case SCF_TERR_VALUE_OUT_OF_RANGE:
6889 	case SCF_TERR_INVALID_VALUE:
6890 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
6891 		break;
6892 	case SCF_TERR_PG_REDEFINE:
6893 		if (err->te_ev1 != NULL && err->te_ev2 != NULL) {
6894 			if (name != NULL)
6895 				*name = (char *)err->te_ev1;
6896 			if (type != NULL)
6897 				*type = (char *)err->te_ev2;
6898 			return (0);
6899 		}
6900 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
6901 		break;
6902 	default:
6903 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6904 	}
6905 	return (-1);
6906 }
6907 
6908 int
6909 scf_tmpl_error_prop_tmpl(const scf_tmpl_error_t *err, char **name, char **type)
6910 {
6911 	assert(err != NULL);
6912 	switch (err->te_type) {
6913 	case SCF_TERR_MISSING_PROP:
6914 	case SCF_TERR_WRONG_PROP_TYPE:
6915 	case SCF_TERR_CARDINALITY_VIOLATION:
6916 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6917 	case SCF_TERR_RANGE_VIOLATION:
6918 	case SCF_TERR_PROP_TYPE_MISMATCH:
6919 	case SCF_TERR_VALUE_OUT_OF_RANGE:
6920 	case SCF_TERR_INVALID_VALUE:
6921 		if (err->te_tmpl_prop_name != NULL &&
6922 		    err->te_tmpl_prop_type != NULL) {
6923 			if (name != NULL)
6924 				*name = (char *)err->te_tmpl_prop_name;
6925 			if (type != NULL)
6926 				*type = (char *)err->te_tmpl_prop_type;
6927 			return (0);
6928 		}
6929 		/*FALLTHROUGH*/
6930 	case SCF_TERR_MISSING_PG:
6931 	case SCF_TERR_WRONG_PG_TYPE:
6932 	case SCF_TERR_PG_REDEFINE:
6933 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
6934 		break;
6935 	default:
6936 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6937 	}
6938 	return (-1);
6939 }
6940 
6941 int
6942 scf_tmpl_error_prop(const scf_tmpl_error_t *err, char **name, char **type)
6943 {
6944 	assert(err != NULL);
6945 	switch (err->te_type) {
6946 	case SCF_TERR_WRONG_PROP_TYPE:
6947 	case SCF_TERR_CARDINALITY_VIOLATION:
6948 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6949 	case SCF_TERR_RANGE_VIOLATION:
6950 		if (err->te_prop_name != NULL &&
6951 		    err->te_tmpl_prop_type != NULL) {
6952 			if (name != NULL)
6953 				*name = (char *)err->te_prop_name;
6954 			if (type != NULL)
6955 				*type = (char *)err->te_tmpl_prop_type;
6956 			return (0);
6957 		}
6958 		/*FALLTHROUGH*/
6959 	case SCF_TERR_MISSING_PG:
6960 	case SCF_TERR_WRONG_PG_TYPE:
6961 	case SCF_TERR_MISSING_PROP:
6962 	case SCF_TERR_PROP_TYPE_MISMATCH:
6963 	case SCF_TERR_VALUE_OUT_OF_RANGE:
6964 	case SCF_TERR_INVALID_VALUE:
6965 	case SCF_TERR_PG_REDEFINE:
6966 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
6967 		break;
6968 	default:
6969 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
6970 	}
6971 	return (-1);
6972 }
6973 
6974 int
6975 scf_tmpl_error_value(const scf_tmpl_error_t *err, char **val)
6976 {
6977 	assert(err != NULL);
6978 	switch (err->te_type) {
6979 	case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
6980 	case SCF_TERR_RANGE_VIOLATION:
6981 	case SCF_TERR_VALUE_OUT_OF_RANGE:
6982 	case SCF_TERR_INVALID_VALUE:
6983 		if (err->te_actual != NULL) {
6984 			if (val != NULL)
6985 				*val = (char *)err->te_actual;
6986 			return (0);
6987 		}
6988 		/*FALLTHROUGH*/
6989 	case SCF_TERR_MISSING_PG:
6990 	case SCF_TERR_WRONG_PG_TYPE:
6991 	case SCF_TERR_MISSING_PROP:
6992 	case SCF_TERR_WRONG_PROP_TYPE:
6993 	case SCF_TERR_CARDINALITY_VIOLATION:
6994 	case SCF_TERR_PROP_TYPE_MISMATCH:
6995 	case SCF_TERR_PG_REDEFINE:
6996 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
6997 		break;
6998 	default:
6999 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
7000 	}
7001 	return (-1);
7002 }
7003 
7004 const char *
7005 scf_tmpl_visibility_to_string(uint8_t vis)
7006 {
7007 	if (vis == SCF_TMPL_VISIBILITY_READONLY)
7008 		return (SCF_TM_VISIBILITY_READONLY);
7009 	else if (vis == SCF_TMPL_VISIBILITY_HIDDEN)
7010 		return (SCF_TM_VISIBILITY_HIDDEN);
7011 	else if (vis == SCF_TMPL_VISIBILITY_READWRITE)
7012 		return (SCF_TM_VISIBILITY_READWRITE);
7013 	else
7014 		return ("unknown");
7015 }
7016