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