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