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