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