1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2012 Milan Jurik. All rights reserved.
24  */
25 
26 /*
27  * scf_tmpl.c
28  *
29  * This file implements the bulk of the libscf templates interfaces.
30  * Templates describe metadata about a service or instance in general,
31  * and individual configuration properties on those services and instances.
32  * Human-consumable descriptions can be provided, along with definitions
33  * of valid configuration.  See service_bundle.dtd.1 for XML definitions
34  * of templates, and the svccfg code for information on how those definitions
35  * are translated into the repository.
36  *
37  * The main data structures are scf_pg_tmpl and scf_prop_tmpl.  These
38  * are allocated by the callers through scf_tmpl_[pg|prop]_create(), and
39  * destroyed with scf_tmpl_[pg|prop]_destroy().  They are populated by
40  * scf_tmpl_get_by_pg_name(), scf_tmpl_get_by_pg(), and
41  * scf_tmpl_get_by_prop().  They also store the iterator state for
42  * scf_tmpl_iter_pgs() and scf_tmpl_iter_props().
43  *
44  * These data structures are then consumed by other functions to
45  * gather information about the template (e.g. name, description,
46  * choices, constraints, etc.).
47  *
48  * scf_tmpl_validate_fmri() does instance validation against template
49  * data, and populates a set of template errors which can be explored using
50  * the scf_tmpl_next_error() and the scf_tmpl_error*() suite of functions.
51  *
52  * The main data structures for template errors are scf_tmpl_errors,
53  * defined in this file, and scf_tmpl_error, defined in libscf_priv.h.
54  * scf_tmpl_error is shared with svccfg to offer common printing
55  * of error messages between libscf and svccfg.
56  *
57  * General convenience functions are towards the top of this file,
58  * followed by pg and prop template discovery functions, followed
59  * by functions which gather information about the discovered
60  * template.  Validation and error functions are at the end of this file.
61  */
62 
63 #include "lowlevel_impl.h"
64 #include "libscf_impl.h"
65 #include <assert.h>
66 #include <errno.h>
67 #include <libintl.h>
68 #include <stdlib.h>
69 #include <stdio.h>
70 #include <strings.h>
71 #include <locale.h>
72 #include <ctype.h>
73 #include <inttypes.h>
74 
75 #define	SCF_TMPL_PG_COMMON_NAME_C	"common_name_C"
76 
77 #define	SCF__TMPL_ITER_NONE		0
78 #define	SCF__TMPL_ITER_INST		1
79 #define	SCF__TMPL_ITER_RESTARTER	2
80 #define	SCF__TMPL_ITER_GLOBAL		3
81 
82 #define	SCF_TMPL_PG_NT		0
83 #define	SCF_TMPL_PG_N		1
84 #define	SCF_TMPL_PG_T		2
85 #define	SCF_TMPL_PG_WILD	3
86 
87 struct scf_pg_tmpl {
88 	int pt_populated;
89 	scf_handle_t *pt_h;
90 	scf_propertygroup_t *pt_pg;
91 	scf_service_t *pt_orig_svc;
92 	scf_service_t *pt_svc;
93 	scf_instance_t *pt_orig_inst;
94 	scf_instance_t *pt_inst;
95 	scf_snapshot_t *pt_snap;
96 	int pt_is_iter;
97 	scf_iter_t *pt_iter;
98 	int pt_iter_last;
99 };
100 
101 #define	SCF_WALK_ERROR		-1
102 #define	SCF_WALK_NEXT		0
103 #define	SCF_WALK_DONE		1
104 
105 struct pg_tmpl_walk {
106 	const char *pw_snapname;
107 	const char *pw_pgname;
108 	const char *pw_pgtype;
109 	scf_instance_t *pw_inst;
110 	scf_service_t *pw_svc;
111 	scf_snapshot_t *pw_snap;
112 	scf_propertygroup_t *pw_pg;
113 	const char *pw_target;
114 	char *pw_tmpl_pgname;
115 };
116 
117 typedef struct pg_tmpl_walk pg_tmpl_walk_t;
118 
119 typedef int walk_template_inst_func_t(scf_service_t *_svc,
120     scf_instance_t *_inst, pg_tmpl_walk_t *p);
121 
122 struct scf_prop_tmpl {
123 	int prt_populated;
124 	scf_handle_t *prt_h;
125 	scf_pg_tmpl_t *prt_t;
126 	scf_propertygroup_t *prt_pg;
127 	char *prt_pg_name;
128 	scf_iter_t *prt_iter;
129 };
130 
131 /*
132  * Common server errors are usually passed back to the caller.  This
133  * array defines them centrally so that they don't need to be enumerated
134  * in every libscf call.
135  */
136 static const scf_error_t errors_server[] = {
137 	SCF_ERROR_BACKEND_ACCESS,
138 	SCF_ERROR_CONNECTION_BROKEN,
139 	SCF_ERROR_DELETED,
140 	SCF_ERROR_HANDLE_DESTROYED,
141 	SCF_ERROR_INTERNAL,
142 	SCF_ERROR_NO_MEMORY,
143 	SCF_ERROR_NO_RESOURCES,
144 	SCF_ERROR_NOT_BOUND,
145 	SCF_ERROR_PERMISSION_DENIED,
146 	0
147 	};
148 
149 /*
150  * int ismember()
151  *
152  * Returns 1 if the supplied error is a member of the error array, 0
153  * if it is not.
154  */
155 int
ismember(const scf_error_t error,const scf_error_t error_array[])156 ismember(const scf_error_t error, const scf_error_t error_array[])
157 {
158 	int i;
159 
160 	for (i = 0; error_array[i] != 0; ++i) {
161 		if (error == error_array[i])
162 			return (1);
163 	}
164 
165 	return (0);
166 }
167 
168 /*
169  * char *_scf_tmpl_get_fmri()
170  *
171  * Given a pg_tmpl, returns the FMRI of the service or instance that
172  * template describes.  The allocated string must be freed with free().
173  *
174  * On failure, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
175  * _DELETED, or _NO_MEMORY.
176  */
177 static char *
_scf_tmpl_get_fmri(const scf_pg_tmpl_t * t)178 _scf_tmpl_get_fmri(const scf_pg_tmpl_t *t)
179 {
180 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
181 	int r;
182 	char *buf = malloc(sz);
183 
184 	assert(t->pt_svc != NULL || t->pt_inst != NULL);
185 	assert(t->pt_svc == NULL || t->pt_inst == NULL);
186 
187 	if (buf == NULL) {
188 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
189 		return (buf);
190 	}
191 
192 	if (t->pt_inst != NULL)
193 		r = scf_instance_to_fmri(t->pt_inst, buf, sz);
194 	else
195 		r = scf_service_to_fmri(t->pt_svc, buf, sz);
196 
197 	if (r == -1) {
198 		if (ismember(scf_error(), errors_server)) {
199 			free(buf);
200 			buf = NULL;
201 		} else {
202 			assert(0);
203 			abort();
204 		}
205 	}
206 
207 	return (buf);
208 }
209 
210 /*
211  * char *_scf_get_pg_type()
212  *
213  * Given a propertygroup, returns an allocated string containing the
214  * type.  The string must be freed with free().
215  *
216  * On failure, returns NULL and sets scf_error() to: _CONNECTION_BROKEN,
217  * _DELETED, or _NO_MEMORY.
218  */
219 static char *
_scf_get_pg_type(scf_propertygroup_t * pg)220 _scf_get_pg_type(scf_propertygroup_t *pg)
221 {
222 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
223 	char *buf = malloc(sz);
224 
225 	if (buf == NULL) {
226 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
227 	} else if (scf_pg_get_type(pg, buf, sz) == -1) {
228 		if (ismember(scf_error(), errors_server)) {
229 			free(buf);
230 			buf = NULL;
231 		} else {
232 			assert(0);
233 			abort();
234 		}
235 	}
236 
237 	return (buf);
238 }
239 
240 /*
241  * char *_scf_get_prop_name()
242  *
243  * Given a property, returns the name in an allocated string.  The string must
244  * be freed with free().
245  *
246  * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
247  * _DELETED, or _NO_MEMORY.
248  */
249 static char *
_scf_get_prop_name(scf_property_t * prop)250 _scf_get_prop_name(scf_property_t *prop)
251 {
252 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
253 	char *buf = malloc(sz);
254 
255 	if (buf == NULL) {
256 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
257 	} else if (scf_property_get_name(prop, buf, sz) == -1) {
258 		if (ismember(scf_error(), errors_server)) {
259 			free(buf);
260 			buf = NULL;
261 		} else {
262 			assert(0);
263 			abort();
264 		}
265 	}
266 
267 	return (buf);
268 }
269 
270 /*
271  * char *_scf_get_prop_type()
272  *
273  * Given a property, returns the type in an allocated string.  The string must
274  * be freed with free().
275  *
276  * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
277  * _DELETED, or _NO_MEMORY.
278  */
279 static char *
_scf_get_prop_type(scf_property_t * prop)280 _scf_get_prop_type(scf_property_t *prop)
281 {
282 	scf_type_t type;
283 	char *ret;
284 
285 	if (scf_property_type(prop, &type) == -1) {
286 		if (ismember(scf_error(), errors_server)) {
287 			return (NULL);
288 		} else {
289 			assert(0);
290 			abort();
291 		}
292 	}
293 
294 	ret = strdup(scf_type_to_string(type));
295 	if (ret == NULL)
296 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
297 
298 	return (ret);
299 }
300 
301 /*
302  * int _read_single_value_from_pg()
303  *
304  * Reads a single value from the pg and property name specified.  On success,
305  * returns an allocated value that must be freed.
306  *
307  * Returns -1 on failure, sets scf_error() to:
308  *  SCF_ERROR_BACKEND_ACCESS
309  *  SCF_ERROR_CONNECTION_BROKEN
310  *  SCF_ERROR_CONSTRAINT_VIOLATED
311  *    Property has more than one value associated with it.
312  *  SCF_ERROR_DELETED
313  *  SCF_ERROR_HANDLE_DESTROYED
314  *  SCF_ERROR_INTERNAL
315  *  SCF_ERROR_INVALID_ARGUMENT
316  *    prop_name not a valid property name.
317  *  SCF_ERROR_NO_MEMORY
318  *  SCF_ERROR_NO_RESOURCES
319  *  SCF_ERROR_NOT_BOUND
320  *  SCF_ERROR_NOT_FOUND
321  *    Property doesn't exist or exists and has no value.
322  *  SCF_ERROR_NOT_SET
323  *    Property group specified by pg is not set.
324  *  SCF_ERROR_PERMISSION_DENIED
325  */
326 static int
_read_single_value_from_pg(scf_propertygroup_t * pg,const char * prop_name,scf_value_t ** val)327 _read_single_value_from_pg(scf_propertygroup_t *pg, const char *prop_name,
328     scf_value_t **val)
329 {
330 	scf_handle_t *h;
331 	scf_property_t *prop;
332 	int ret = 0;
333 
334 	assert(val != NULL);
335 	if ((h = scf_pg_handle(pg)) == NULL) {
336 		assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
337 		return (-1);
338 	}
339 
340 	prop = scf_property_create(h);
341 	*val = scf_value_create(h);
342 
343 	if (prop == NULL || *val == NULL) {
344 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
345 		goto read_single_value_from_pg_fail;
346 	}
347 
348 	if (scf_pg_get_property(pg, prop_name, prop) != 0) {
349 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
350 		goto read_single_value_from_pg_fail;
351 	}
352 
353 	if (scf_property_get_value(prop, *val) == -1) {
354 		assert(scf_error() != SCF_ERROR_NOT_SET);
355 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
356 		goto read_single_value_from_pg_fail;
357 	}
358 
359 	goto read_single_value_from_pg_done;
360 
361 read_single_value_from_pg_fail:
362 	scf_value_destroy(*val);
363 	*val = NULL;
364 	ret = -1;
365 
366 read_single_value_from_pg_done:
367 	scf_property_destroy(prop);
368 	return (ret);
369 }
370 
371 /*
372  * char *_scf_read_single_astring_from_pg()
373  *
374  * Reads an astring from the pg and property name specified.  On success,
375  * returns an allocated string.  The string must be freed with free().
376  *
377  * Returns NULL on failure, sets scf_error() to:
378  *   SCF_ERROR_BACKEND_ACCESS
379  *   SCF_ERROR_CONNECTION_BROKEN
380  *   SCF_ERROR_CONSTRAINT_VIOLATED
381  *     Property has more than one value associated with it.
382  *   SCF_ERROR_DELETED
383  *   SCF_ERROR_HANDLE_DESTROYED
384  *   SCF_ERROR_INTERNAL
385  *   SCF_ERROR_INVALID_ARGUMENT
386  *     prop_name not a valid property name.
387  *   SCF_ERROR_NO_MEMORY
388  *   SCF_ERROR_NO_RESOURCES
389  *   SCF_ERROR_NOT_BOUND
390  *   SCF_ERROR_NOT_FOUND
391  *     Property doesn't exist or exists and has no value.
392  *   SCF_ERROR_NOT_SET
393  *     The property group specified by pg is not set.
394  *   SCF_ERROR_PERMISSION_DENIED
395  *   SCF_ERROR_TYPE_MISMATCH
396  */
397 char *
_scf_read_single_astring_from_pg(scf_propertygroup_t * pg,const char * prop_name)398 _scf_read_single_astring_from_pg(scf_propertygroup_t *pg, const char *prop_name)
399 {
400 	scf_value_t *val;
401 	char *ret = NULL;
402 	ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
403 
404 	assert(rsize != 0);
405 	if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
406 		return (NULL);
407 
408 	ret = malloc(rsize);
409 	if (ret == NULL) {
410 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
411 		goto cleanup;
412 	}
413 
414 	if (scf_value_get_astring(val, ret, rsize) < 0) {
415 		assert(scf_error() != SCF_ERROR_NOT_SET);
416 		free(ret);
417 		ret = NULL;
418 	}
419 
420 cleanup:
421 	scf_value_destroy(val);
422 	return (ret);
423 }
424 
425 /*
426  * char *_scf_read_tmpl_prop_type_as_string()
427  *
428  * Reads the property type and returns it as an allocated string.  The string
429  * must be freed with free().
430  *
431  * Returns NULL on failure, sets scf_error() to _BACKEND_ACCESS,
432  * _CONNECTION_BROKEN, _DELETED, _HANDLE_DESTROYED, _INTERNAL, _NO_MEMORY,
433  * _NO_RESOURCES, _NOT_BOUND, _PERMISSION_DENIED, or _TEMPLATE_INVALID.
434  */
435 char *
_scf_read_tmpl_prop_type_as_string(const scf_prop_tmpl_t * pt)436 _scf_read_tmpl_prop_type_as_string(const scf_prop_tmpl_t *pt)
437 {
438 	char *type;
439 
440 	type = _scf_read_single_astring_from_pg(pt->prt_pg,
441 	    SCF_PROPERTY_TM_TYPE);
442 	if (type == NULL) {
443 		if (ismember(scf_error(), errors_server)) {
444 			return (NULL);
445 		} else switch (scf_error()) {
446 		case SCF_ERROR_CONSTRAINT_VIOLATED:
447 		case SCF_ERROR_NOT_FOUND:
448 		case SCF_ERROR_TYPE_MISMATCH:
449 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
450 			return (NULL);
451 
452 		case SCF_ERROR_INVALID_ARGUMENT:
453 		case SCF_ERROR_NOT_SET:
454 		default:
455 			assert(0);
456 			abort();
457 		}
458 	}
459 
460 	return (type);
461 }
462 
463 /*
464  * int _read_single_boolean_from_pg()
465  *
466  * Reads a boolean from the pg and property name specified.
467  *
468  * Returns -1 on failure, sets scf_error() to:
469  *   SCF_ERROR_BACKEND_ACCESS
470  *   SCF_ERROR_CONNECTION_BROKEN
471  *   SCF_ERROR_CONSTRAINT_VIOLATED
472  *     Property has more than one value associated with it.
473  *   SCF_ERROR_DELETED
474  *   SCF_ERROR_HANDLE_DESTROYED
475  *   SCF_ERROR_INTERNAL
476  *   SCF_ERROR_INVALID_ARGUMENT
477  *     prop_name is not a valid property name.
478  *   SCF_ERROR_NO_MEMORY
479  *   SCF_ERROR_NO_RESOURCES
480  *   SCF_ERROR_NOT_BOUND
481  *   SCF_ERROR_NOT_FOUND
482  *     Property doesn't exist or exists and has no value.
483  *   SCF_ERROR_NOT_SET
484  *     The property group specified by pg is not set.
485  *   SCF_ERROR_PERMISSION_DENIED
486  *   SCF_ERROR_TYPE_MISMATCH
487  */
488 static int
_read_single_boolean_from_pg(scf_propertygroup_t * pg,const char * prop_name,uint8_t * bool)489 _read_single_boolean_from_pg(scf_propertygroup_t *pg, const char *prop_name,
490     uint8_t *bool)
491 {
492 	scf_value_t *val;
493 	int ret = 0;
494 
495 	if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
496 		return (-1);
497 
498 	if (scf_value_get_boolean(val, bool) < 0) {
499 		assert(scf_error() != SCF_ERROR_NOT_SET);
500 		ret = -1;
501 	}
502 
503 	scf_value_destroy(val);
504 	return (ret);
505 }
506 
507 /*
508  * static char ** _append_astrings_values()
509  *
510  * This function reads the values from the property prop_name in pg and
511  * appends to an existing scf_values_t *vals.  vals may be empty, but
512  * must exist.  The function skips over zero-length and duplicate values.
513  *
514  * Returns NULL on failure, sets scf_error() to:
515  *   SCF_ERROR_BACKEND_ACCESS
516  *   SCF_ERROR_CONNECTION_BROKEN
517  *   SCF_ERROR_DELETED
518  *   SCF_ERROR_HANDLE_DESTROYED
519  *   SCF_ERROR_INTERNAL
520  *   SCF_ERROR_INVALID_ARGUMENT
521  *     prop_name is not a valid property name.
522  *   SCF_ERROR_NO_MEMORY
523  *   SCF_ERROR_NO_RESOURCES
524  *   SCF_ERROR_NOT_BOUND
525  *   SCF_ERROR_NOT_FOUND
526  *   SCF_ERROR_NOT_SET
527  *   SCF_ERROR_PERMISSION_DENIED
528  *   SCF_ERROR_TYPE_MISMATCH
529  */
530 static char **
_append_astrings_values(scf_propertygroup_t * pg,const char * prop_name,scf_values_t * vals)531 _append_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
532     scf_values_t *vals)
533 {
534 	scf_handle_t *h;
535 	scf_property_t *prop;
536 	scf_value_t *val;
537 	scf_iter_t *iter;
538 	ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
539 	int err, count, cursz, i;
540 
541 	assert(vals != NULL);
542 	assert(vals->value_type == SCF_TYPE_ASTRING);
543 	assert(vals->reserved == NULL);
544 	count = vals->value_count;
545 	if (count == 0) {
546 		cursz = 8;
547 		vals->values.v_astring = calloc(cursz, sizeof (char *));
548 		if (vals->values.v_astring == NULL) {
549 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
550 			return (NULL);
551 		}
552 	} else {
553 		/*
554 		 * The array may be bigger, but it is irrelevant since
555 		 * we will always re-allocate a new one.
556 		 */
557 		cursz = count;
558 	}
559 
560 	if ((h = scf_pg_handle(pg)) == NULL) {
561 		assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
562 		return (NULL);
563 	}
564 
565 	prop = scf_property_create(h);
566 	val = scf_value_create(h);
567 	iter = scf_iter_create(h);
568 
569 	if (prop == NULL || val == NULL || iter == NULL) {
570 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
571 		goto append_single_astring_from_pg_fail;
572 	}
573 
574 	if (scf_pg_get_property(pg, prop_name, prop) != 0) {
575 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
576 		goto append_single_astring_from_pg_fail;
577 	}
578 
579 	if (scf_iter_property_values(iter, prop) != 0) {
580 		assert(scf_error() != SCF_ERROR_NOT_SET);
581 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
582 		goto append_single_astring_from_pg_fail;
583 	}
584 
585 	while ((err = scf_iter_next_value(iter, val)) == 1) {
586 		int flag;
587 		int r;
588 
589 		if (count + 1 >= cursz) {
590 			void *aux;
591 
592 			cursz *= 2;
593 			if ((aux = calloc(cursz, sizeof (char *))) == NULL) {
594 				(void) scf_set_error(SCF_ERROR_NO_MEMORY);
595 				goto append_single_astring_from_pg_fail;
596 			}
597 			(void) memcpy(aux, vals->values.v_astring,
598 			    count * sizeof (char *));
599 			free(vals->values.v_astring);
600 			vals->values.v_astring = aux;
601 		}
602 
603 		vals->values.v_astring[count] = malloc(rsize);
604 		if (vals->values.v_astring[count] == NULL) {
605 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
606 			goto append_single_astring_from_pg_fail;
607 		}
608 
609 		if ((r = scf_value_get_astring(val,
610 		    vals->values.v_astring[count], rsize)) <= 0) {
611 			/* discard zero length strings */
612 			if (r == 0) {
613 				free(vals->values.v_astring[count]);
614 				continue;
615 			}
616 			assert(scf_error() != SCF_ERROR_NOT_SET);
617 			goto append_single_astring_from_pg_fail;
618 		}
619 		for (i = 0, flag = 0; i < count; ++i) {
620 			/* find  and discard duplicates */
621 			if (strncmp(vals->values.v_astring[i],
622 			    vals->values.v_astring[count], rsize) == 0) {
623 				free(vals->values.v_astring[count]);
624 				flag = 1;
625 				break;
626 			}
627 		}
628 		if (flag == 1)
629 			continue;
630 
631 		count++;
632 	}
633 
634 	vals->value_count = count;
635 
636 	if (err != 0) {
637 		assert(scf_error() != SCF_ERROR_NOT_SET);
638 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
639 		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
640 		goto append_single_astring_from_pg_fail;
641 	} else {
642 		vals->values_as_strings = vals->values.v_astring;
643 	}
644 
645 	goto append_single_astring_from_pg_done;
646 
647 append_single_astring_from_pg_fail:
648 	for (i = 0; i <= count; ++i) {
649 		if (vals->values.v_astring[i] != NULL)
650 			free(vals->values.v_astring[i]);
651 		vals->values.v_astring[i] = NULL;
652 	}
653 	free(vals->values.v_astring);
654 	vals->values.v_astring = NULL;
655 	vals->value_count = 0;
656 
657 append_single_astring_from_pg_done:
658 	scf_iter_destroy(iter);
659 	scf_property_destroy(prop);
660 	scf_value_destroy(val);
661 	return (vals->values.v_astring);
662 }
663 
664 /*
665  * Returns NULL on failure, sets scf_error() to:
666  *   SCF_ERROR_BACKEND_ACCESS
667  *   SCF_ERROR_CONNECTION_BROKEN
668  *   SCF_ERROR_DELETED
669  *   SCF_ERROR_HANDLE_DESTROYED
670  *   SCF_ERROR_INTERNAL
671  *   SCF_ERROR_INVALID_ARGUMENT
672  *     prop_name is not a valid property name.
673  *   SCF_ERROR_NO_MEMORY
674  *   SCF_ERROR_NO_RESOURCES
675  *   SCF_ERROR_NOT_BOUND
676  *   SCF_ERROR_NOT_FOUND
677  *   SCF_ERROR_NOT_SET
678  *   SCF_ERROR_PERMISSION_DENIED
679  *   SCF_ERROR_TYPE_MISMATCH
680  */
681 static char **
_read_astrings_values(scf_propertygroup_t * pg,const char * prop_name,scf_values_t * vals)682 _read_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
683     scf_values_t *vals)
684 {
685 	assert(vals != NULL);
686 	vals->value_count = 0;
687 	vals->value_type = SCF_TYPE_ASTRING;
688 	vals->reserved = NULL;
689 	return (_append_astrings_values(pg, prop_name, vals));
690 }
691 
692 void
_scf_sanitize_locale(char * locale)693 _scf_sanitize_locale(char *locale)
694 {
695 	for (; *locale != '\0'; locale++)
696 		if (!isalnum(*locale) && *locale != '_')
697 			*locale = '_';
698 }
699 
700 /*
701  * The returned string needs to be freed by the caller
702  * Returns NULL on failure.  Sets scf_error() to:
703  *   SCF_ERROR_NO_MEMORY
704  *   SCF_ERROR_INVALID_ARGUMENT
705  *     Name isn't short enough to add the locale to.
706  */
707 static char *
_add_locale_to_name(const char * name,const char * locale)708 _add_locale_to_name(const char *name, const char *locale)
709 {
710 	char *lname = NULL;
711 	ssize_t lsz;
712 	char *loc;
713 
714 	if (locale == NULL)
715 		locale = setlocale(LC_MESSAGES, NULL);
716 	loc = strdup(locale);
717 	if (loc == NULL) {
718 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
719 		return (NULL);
720 	} else {
721 		_scf_sanitize_locale(loc);
722 	}
723 
724 	lsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
725 	lname = malloc(lsz);
726 	if (lname == NULL) {
727 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
728 		goto cleanup;
729 	}
730 
731 	(void) strlcpy(lname, name, lsz);
732 	if (strlcat(lname, loc, lsz) >= lsz) {
733 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
734 		free(lname);
735 		lname = NULL;
736 	}
737 cleanup:
738 	free(loc);
739 
740 	return (lname);
741 }
742 
743 /*
744  * char *_tmpl_pg_name(pg, type, use_type)
745  *
746  * pg and type can both be NULL.  Returns the name of the most specific
747  * template property group name based on the inputs.
748  * If use_type is set and pg is not NULL, a property group name for a
749  * property group template that has type defined is returned, even if no
750  * type is provided.
751  *
752  * Returns NULL on failure and sets scf_error() to:
753  *   SCF_ERROR_INVALID_ARGUMENT
754  *     can't combine the arguments and get a reasonable length name
755  *   SCF_ERROR_NO_MEMORY
756  *
757  */
758 static char *
_tmpl_pg_name(const char * pg,const char * type,int use_type)759 _tmpl_pg_name(const char *pg, const char *type, int use_type)
760 {
761 	char *name;
762 	ssize_t limit, size = 0;
763 
764 	limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
765 	name = malloc(limit);
766 	if (name == NULL) {
767 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
768 		return (NULL);
769 	}
770 
771 	if (pg == NULL && type == NULL) {
772 		if (strlcpy(name, SCF_PG_TM_PG_PATTERN_PREFIX, limit) >=
773 		    limit) {
774 			assert(0);
775 			abort();
776 		}
777 		return (name);
778 	} else if (pg != NULL && type != NULL) {
779 		size = snprintf(name, limit, "%s%s",
780 		    SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
781 	} else if (pg != NULL && type == NULL && use_type == 1) {
782 		size = snprintf(name, limit, "%s%s",
783 		    SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
784 	} else if (pg != NULL && type == NULL) {
785 		size = snprintf(name, limit, "%s%s",
786 		    SCF_PG_TM_PG_PATTERN_N_PREFIX, pg);
787 	} else if (type != NULL && pg == NULL) {
788 		size = snprintf(name, limit, "%s%s",
789 		    SCF_PG_TM_PG_PATTERN_T_PREFIX, type);
790 	} else {
791 		assert(0);
792 		abort();
793 	}
794 
795 	if (size >= limit) {
796 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
797 		free(name);
798 		return (NULL);
799 	} else {
800 		return (name);
801 	}
802 }
803 
804 /*
805  * _scf_get_pg_name()
806  * Gets the name of the supplied property group.  On success, returns an
807  * allocated string.  The string must be freed by free().
808  *
809  * Returns NULL on failure and sets scf_error() to _CONNECTION_BROKEN,
810  * _DELETED, or _NO_MEMORY.
811  */
812 static char *
_scf_get_pg_name(scf_propertygroup_t * pg)813 _scf_get_pg_name(scf_propertygroup_t *pg)
814 {
815 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
816 	char *buf = malloc(sz);
817 
818 	if (buf == NULL) {
819 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
820 	} else if (scf_pg_get_name(pg, buf, sz) == -1) {
821 		if (ismember(scf_error(), errors_server)) {
822 			free(buf);
823 			buf = NULL;
824 		} else {
825 			assert(0);
826 			abort();
827 		}
828 	}
829 
830 	return (buf);
831 }
832 
833 /*
834  * char *_tmpl_prop_name()
835  *
836  * Returns the name of the property template prop (which is the name of
837  * the property template property group) in the property group
838  * template t. Returns NULL on failure and sets scf_error() to:
839  *   SCF_ERROR_CONNECTION_BROKEN
840  *   SCF_ERROR_DELETED
841  *   SCF_ERROR_INVALID_ARGUMENT
842  *     can't combine the arguments and get a reasonable length name
843  *   SCF_ERROR_NO_MEMORY
844  */
845 static char *
_tmpl_prop_name(const char * prop,scf_pg_tmpl_t * t)846 _tmpl_prop_name(const char *prop, scf_pg_tmpl_t *t)
847 {
848 	char *name = NULL, *pg_name = NULL;
849 	size_t prefix_size;
850 	ssize_t limit, size = 0;
851 
852 	assert(prop != NULL);
853 	assert(t->pt_pg != NULL);
854 
855 	limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
856 	name = malloc(limit);
857 	if (name == NULL) {
858 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
859 		return (NULL);
860 	}
861 
862 	if ((pg_name = _scf_get_pg_name(t->pt_pg)) == NULL) {
863 		free(name);
864 		return (NULL);
865 	}
866 
867 	prefix_size = strlen(SCF_PG_TM_PG_PAT_BASE);
868 	if (strncmp(pg_name, SCF_PG_TM_PG_PAT_BASE, prefix_size) != 0) {
869 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
870 		free(name);
871 		free(pg_name);
872 		return (NULL);
873 	}
874 
875 	size = snprintf(name, limit, "%s%s_%s", SCF_PG_TM_PROP_PATTERN_PREFIX,
876 	    pg_name + prefix_size, prop);
877 
878 	if (size >= limit) {
879 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
880 		free(name);
881 		free(pg_name);
882 		return (NULL);
883 	} else {
884 		free(pg_name);
885 		return (name);
886 	}
887 }
888 
889 /*
890  *  int _get_snapshot()
891  *
892  *  Gets the specified snapshot.  If "snapshot" isn't defined, use the
893  *  running snapshot.  If the snapshot isn't found, that may or may
894  *  not be an error depending on the caller.  Return 0 in that case,
895  *  but leave scf_error() set to SCF_ERROR_NOT_FOUND.  On all other
896  *  errors, set scf_error() to:
897  *   SCF_ERROR_BACKEND_ACCESS
898  *   SCF_ERROR_CONNECTION_BROKEN
899  *   SCF_ERROR_DELETED
900  *   SCF_ERR_HANDLE_DESTROYED
901  *   SCF_ERROR_INTERNAL
902  *   SCF_ERROR_INVALID_ARGUMENT
903  *     The handle argument is NULL, or snaphot is not a valid snapshot name
904  *   SCF_ERROR_NO_MEMORY
905  *   SCF_ERROR_NO_RESOURCES
906  *   SCF_ERROR_NOT_BOUND
907  *   SCF_ERROR_NOT_FOUND
908  */
909 static int
_get_snapshot(scf_instance_t * inst,const char * snapshot,scf_snapshot_t ** snap)910 _get_snapshot(scf_instance_t *inst, const char *snapshot,
911     scf_snapshot_t **snap)
912 {
913 	int err;
914 	scf_handle_t *h;
915 
916 	h = scf_instance_handle(inst);
917 	if (h == NULL) {
918 		*snap = NULL;
919 		return (-1);
920 	}
921 
922 	if ((*snap = scf_snapshot_create(h)) == NULL) {
923 		return (-1);
924 	}
925 
926 	/* Use running snapshot by default. */
927 	if (snapshot == NULL)
928 		err = scf_instance_get_snapshot(inst, "running", *snap);
929 	else
930 		err = scf_instance_get_snapshot(inst, snapshot, *snap);
931 
932 	if (err != 0) {
933 		if (ismember(scf_error(), errors_server)) {
934 			scf_snapshot_destroy(*snap);
935 			*snap = NULL;
936 			return (-1);
937 		} else switch (scf_error()) {
938 		case SCF_ERROR_INVALID_ARGUMENT:
939 			scf_snapshot_destroy(*snap);
940 			*snap = NULL;
941 			return (-1);
942 
943 		case SCF_ERROR_NOT_FOUND:
944 			scf_snapshot_destroy(*snap);
945 			*snap = NULL;
946 			return (0);
947 
948 		case SCF_ERROR_NOT_SET:
949 		case SCF_ERROR_HANDLE_MISMATCH:
950 		default:
951 			assert(0);
952 			abort();
953 		}
954 	}
955 
956 	/*
957 	 * Explicitly set SCF_ERROR_NONE so that the SCF_ERROR_NOT_FOUND
958 	 * return above is explicitly guaranteed to be from
959 	 * scf_instance_get_snapshot().
960 	 */
961 	(void) scf_set_error(SCF_ERROR_NONE);
962 	return (0);
963 }
964 
965 /*
966  * Returns NULL on error, sets scf_error() to:
967  *   SCF_ERROR_BACKEND_ACCESS
968  *   SCF_ERROR_CONNECTION_BROKEN
969  *   SCF_ERROR_CONSTRAINT_VIOLATED
970  *     The restarter's FMRI does not match an existing instance.
971  *   SCF_ERROR_DELETED
972  *   SCF_ERROR_HANDLE_DESTROYED
973  *   SCF_ERROR_INTERNAL
974  *   SCF_ERROR_INVALID_ARGUMENT
975  *     The restarter's FMRI is not a valid FMRI.
976  *   SCF_ERROR_NO_MEMORY
977  *   SCF_ERROR_NO_RESOURCES
978  *   SCF_ERROR_NOT_BOUND
979  *   SCF_ERROR_NOT_FOUND
980  *     Property doesn't exist or exists and has no value.
981  *   SCF_ERROR_TEMPLATE_INVALID
982  *     restarter property is not SCF_TYPE_ASTRING or has more than one value
983  */
984 static scf_instance_t *
_get_restarter_inst(scf_handle_t * h,scf_service_t * svc,scf_instance_t * inst,scf_snapshot_t * s)985 _get_restarter_inst(scf_handle_t *h, scf_service_t *svc,
986     scf_instance_t *inst, scf_snapshot_t *s)
987 {
988 	char *restarter = NULL;
989 	scf_instance_t *ri = NULL;
990 	scf_propertygroup_t *pg = NULL;
991 	int ret = 0;
992 
993 	assert(svc != NULL || inst != NULL);
994 	assert(svc ==  NULL || inst == NULL);
995 
996 	if ((ri = scf_instance_create(h)) == NULL ||
997 	    (pg = scf_pg_create(h)) == NULL) {
998 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
999 		goto _get_restarter_inst_fail;
1000 	}
1001 
1002 	if (inst != NULL)
1003 		ret = scf_instance_get_pg_composed(inst, s, SCF_PG_GENERAL,
1004 		    pg);
1005 	else
1006 		ret = scf_service_get_pg(svc, SCF_PG_GENERAL, pg);
1007 
1008 	if (ret != 0) {
1009 		if (ismember(scf_error(), errors_server)) {
1010 			goto _get_restarter_inst_fail;
1011 		} else switch (scf_error()) {
1012 		case SCF_ERROR_NOT_FOUND:
1013 			/* Assume default restarter. */
1014 			break;
1015 
1016 		case SCF_ERROR_NOT_SET:
1017 		case SCF_ERROR_HANDLE_MISMATCH:
1018 			/*
1019 			 * If the arguments to the above functions
1020 			 * aren't derived from the same handle, there's
1021 			 * something wrong with the internal implementation,
1022 			 * not the public caller further up the chain.
1023 			 */
1024 		case SCF_ERROR_INVALID_ARGUMENT:
1025 		default:
1026 			assert(0);
1027 			abort();
1028 		}
1029 	} else {
1030 		restarter = _scf_read_single_astring_from_pg(pg,
1031 		    SCF_PROPERTY_RESTARTER);
1032 		/* zero length string is NOT a valid restarter */
1033 		if (restarter != NULL && restarter[0] == '\0') {
1034 			free(restarter);
1035 			restarter = NULL;
1036 		} else if (restarter == NULL) {
1037 			if (ismember(scf_error(), errors_server)) {
1038 				goto _get_restarter_inst_fail;
1039 			} else switch (scf_error()) {
1040 			case SCF_ERROR_NOT_FOUND:
1041 				break;
1042 
1043 			case SCF_ERROR_CONSTRAINT_VIOLATED:
1044 			case SCF_ERROR_TYPE_MISMATCH:
1045 				(void) scf_set_error(
1046 				    SCF_ERROR_TEMPLATE_INVALID);
1047 				goto _get_restarter_inst_fail;
1048 
1049 			case SCF_ERROR_NOT_SET:
1050 			case SCF_ERROR_INVALID_ARGUMENT:
1051 			default:
1052 				assert(0);
1053 				abort();
1054 			}
1055 		}
1056 	}
1057 
1058 	if (restarter == NULL) {
1059 		/* Use default restarter */
1060 		restarter = strdup(SCF_SERVICE_STARTD);
1061 		if (restarter == NULL) {
1062 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
1063 			goto _get_restarter_inst_fail;
1064 		}
1065 	}
1066 
1067 	if (scf_handle_decode_fmri(h, restarter, NULL, NULL, ri, NULL, NULL,
1068 	    SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1069 		if (ismember(scf_error(), errors_server)) {
1070 			goto _get_restarter_inst_fail;
1071 		} else switch (scf_error()) {
1072 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1073 		case SCF_ERROR_INVALID_ARGUMENT:
1074 		case SCF_ERROR_NOT_FOUND:
1075 			goto _get_restarter_inst_fail;
1076 
1077 		case SCF_ERROR_HANDLE_MISMATCH:
1078 		case SCF_ERROR_NOT_SET:
1079 		default:
1080 			assert(0);
1081 			abort();
1082 		}
1083 	}
1084 	free(restarter);
1085 	scf_pg_destroy(pg);
1086 
1087 	return (ri);
1088 
1089 _get_restarter_inst_fail:
1090 	free(restarter);
1091 	scf_instance_destroy(ri);
1092 	scf_pg_destroy(pg);
1093 	return (NULL);
1094 }
1095 
1096 /*
1097  * Returns NULL on error, sets scf_error() to:
1098  *   SCF_ERROR_BACKEND_ACCESS
1099  *   SCF_ERROR_CONNECTION_BROKEN
1100  *   SCF_ERROR_CONSTRAINT_VIOLATED
1101  *     Restarter property has more than one value associated with it,
1102  *     or FMRI does not meet restrictions in scf_handle_decode_fmri() flags.
1103  *   SCF_ERROR_DELETED
1104  *   SCF_ERROR_HANDLE_DESTROYED
1105  *   SCF_ERROR_INTERNAL
1106  *   SCF_ERROR_INVALID_ARGUMENT
1107  *     The fmri argument in scf_handle_decode_fmri() is not a valid FMRI.
1108  *   SCF_ERROR_NO_MEMORY
1109  *   SCF_ERROR_NO_RESOURCES
1110  *   SCF_ERROR_NOT_BOUND
1111  *   SCF_ERROR_NOT_FOUND
1112  *     Property doesn't exist or exists and has no value.
1113  */
1114 static scf_instance_t *
_get_global_inst(scf_handle_t * h)1115 _get_global_inst(scf_handle_t *h)
1116 {
1117 	scf_instance_t *ri;
1118 
1119 	if ((ri = scf_instance_create(h)) == NULL) {
1120 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
1121 		(void) scf_set_error(SCF_ERROR_NO_RESOURCES);
1122 		return (NULL);
1123 	}
1124 
1125 	if (scf_handle_decode_fmri(h, SCF_INSTANCE_GLOBAL, NULL, NULL, ri,
1126 	    NULL, NULL,
1127 	    SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1128 		if (ismember(scf_error(), errors_server)) {
1129 			scf_instance_destroy(ri);
1130 			return (NULL);
1131 		} else switch (scf_error()) {
1132 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1133 		case SCF_ERROR_INVALID_ARGUMENT:
1134 		case SCF_ERROR_NOT_FOUND:
1135 			scf_instance_destroy(ri);
1136 			return (NULL);
1137 
1138 		case SCF_ERROR_HANDLE_MISMATCH:
1139 		case SCF_ERROR_NOT_SET:
1140 		default:
1141 			assert(0);
1142 			abort();
1143 		}
1144 	}
1145 
1146 	return (ri);
1147 }
1148 
1149 /*
1150  * Call the supplied function for each of the service or instance, the
1151  * service's restarter, and the globally defined template instance.
1152  * If the function returns SCF_WALK_ERROR, the walk is ended.  If
1153  * the function returns SCF_WALK_NEXT, the next entity is tried.
1154  *
1155  * The function is only expected to return SCF_WALK_DONE if it has
1156  * found a property group match in the current entity, and has
1157  * populated p->pw_pg with the matching property group.
1158  *
1159  * The caller of _walk_template_instances() MUST check if the passed parameters
1160  * inst and svc match the fields pw_inst and pw_svc in the resulting
1161  * pg_tmpl_walk_t and call the destructor for the unmatching objects. The walker
1162  * may silently drop them if the template definition is in the restarter or in
1163  * the global instance.
1164  */
1165 static void
_walk_template_instances(scf_service_t * svc,scf_instance_t * inst,scf_snapshot_t * snap,walk_template_inst_func_t * func,pg_tmpl_walk_t * p,int flag)1166 _walk_template_instances(scf_service_t *svc, scf_instance_t *inst,
1167     scf_snapshot_t *snap, walk_template_inst_func_t *func,
1168     pg_tmpl_walk_t *p, int flag)
1169 {
1170 	scf_instance_t *tmpl_inst = NULL;
1171 	scf_handle_t *h;
1172 	int ret;
1173 	char *tg = NULL;
1174 
1175 	assert(svc != NULL || inst != NULL);
1176 	assert(svc == NULL || inst == NULL);
1177 
1178 	if (inst != NULL)
1179 		h = scf_instance_handle(inst);
1180 	else
1181 		h = scf_service_handle(svc);
1182 	if (h == NULL)
1183 		goto done;
1184 
1185 	/* First, use supplied service or instance */
1186 	p->pw_target = SCF_TM_TARGET_THIS;
1187 	ret = func(svc, inst, p);
1188 	switch (ret) {
1189 	case SCF_WALK_NEXT:
1190 		break;
1191 	case SCF_WALK_DONE:
1192 		/*
1193 		 * Check that the template scoping matches and if not,
1194 		 * continue.
1195 		 */
1196 		assert(p->pw_pg != NULL);
1197 		tg = _scf_read_single_astring_from_pg(p->pw_pg,
1198 		    SCF_PROPERTY_TM_TARGET);
1199 		if (tg == NULL || /* scf_error() was set */
1200 		    (strcmp(tg, SCF_TM_TARGET_INSTANCE) != 0 &&
1201 		    strcmp(tg, SCF_TM_TARGET_THIS) != 0 &&
1202 		    (flag & SCF_PG_TMPL_FLAG_EXACT) !=
1203 		    SCF_PG_TMPL_FLAG_EXACT)) {
1204 			scf_pg_destroy(p->pw_pg);
1205 			p->pw_pg = NULL;
1206 			if (tg != NULL) {
1207 				free(tg);
1208 				tg = NULL;
1209 				break;
1210 			}
1211 		}
1212 		/*FALLTHROUGH*/
1213 	case SCF_WALK_ERROR:
1214 		goto done;
1215 		/*NOTREACHED*/
1216 	default:
1217 		assert(0);
1218 		abort();
1219 	}
1220 
1221 	/* Next the restarter. */
1222 	p->pw_target = SCF_TM_TARGET_DELEGATE;
1223 	tmpl_inst = _get_restarter_inst(h, svc, inst, snap);
1224 	if (tmpl_inst != NULL) {
1225 		ret = func(NULL, tmpl_inst, p);
1226 		switch (ret) {
1227 		case SCF_WALK_NEXT:
1228 			break;
1229 		case SCF_WALK_DONE:
1230 			/*
1231 			 * Check that the template scoping matches and if not,
1232 			 * continue.
1233 			 */
1234 			assert(p->pw_pg != NULL);
1235 			tg = _scf_read_single_astring_from_pg(p->pw_pg,
1236 			    SCF_PROPERTY_TM_TARGET);
1237 			if (tg == NULL || /* scf_error() was set */
1238 			    strcmp(tg, SCF_TM_TARGET_DELEGATE) != 0) {
1239 				scf_pg_destroy(p->pw_pg);
1240 				p->pw_pg = NULL;
1241 				if (tg != NULL) {
1242 					free(tg);
1243 					tg = NULL;
1244 					break;
1245 				}
1246 			}
1247 			/*FALLTHROUGH*/
1248 		case SCF_WALK_ERROR:
1249 			goto done;
1250 			/*NOTREACHED*/
1251 		default:
1252 			assert(0);
1253 			abort();
1254 		}
1255 	}
1256 
1257 	p->pw_target = SCF_TM_TARGET_ALL;
1258 	scf_instance_destroy(tmpl_inst);
1259 	tmpl_inst = _get_global_inst(h);
1260 	if (tmpl_inst != NULL) {
1261 		ret = func(NULL, tmpl_inst, p);
1262 		switch (ret) {
1263 		case SCF_WALK_NEXT:
1264 			break;
1265 		case SCF_WALK_DONE:
1266 			/*
1267 			 * Check that the template scoping matches and if not,
1268 			 * continue.
1269 			 */
1270 			assert(p->pw_pg != NULL);
1271 			tg = _scf_read_single_astring_from_pg(p->pw_pg,
1272 			    SCF_PROPERTY_TM_TARGET);
1273 			if (tg == NULL || /* scf_error() was set */
1274 			    strcmp(tg, SCF_TM_TARGET_ALL) != 0) {
1275 				scf_pg_destroy(p->pw_pg);
1276 				p->pw_pg = NULL;
1277 				if (tg != NULL) {
1278 					free(tg);
1279 					tg = NULL;
1280 					break;
1281 				}
1282 			}
1283 			/*FALLTHROUGH*/
1284 		case SCF_WALK_ERROR:
1285 			goto done;
1286 			/*NOTREACHED*/
1287 		default:
1288 			assert(0);
1289 			abort();
1290 		}
1291 	}
1292 
1293 done:
1294 	free(tg);
1295 	if (ret != SCF_WALK_DONE)
1296 		scf_instance_destroy(tmpl_inst);
1297 	p->pw_target = NULL;
1298 }
1299 
1300 /*
1301  * _get_pg() returns 0 on success and -1 on failure.  Sets scf_error()
1302  * on failure.
1303  *   SCF_ERROR_BACKEND_ACCESS
1304  *   SCF_ERROR_CONNECTION_BROKEN
1305  *   SCF_ERROR_DELETED
1306  *   SCF_ERROR_HANDLE_MISMATCH
1307  *   SCF_ERROR_INTERNAL
1308  *   SCF_ERROR_INVALID_ARGUMENT
1309  *     name is not a valid property group.
1310  *   SCF_ERROR_NO_RESOURCES
1311  *   SCF_ERROR_NOT_BOUND
1312  *   SCF_ERROR_NOT_FOUND
1313  *   SCF_ERROR_NOT_SET
1314  */
1315 static int
_get_pg(scf_service_t * svc,scf_instance_t * inst,const scf_snapshot_t * snap,const char * name,scf_propertygroup_t * pg)1316 _get_pg(scf_service_t *svc, scf_instance_t *inst,
1317     const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
1318 {
1319 	int ret;
1320 
1321 	assert(svc != NULL || inst != NULL);
1322 	assert(svc == NULL || inst == NULL);
1323 	assert(pg != NULL);
1324 
1325 	if (inst != NULL)
1326 		ret = scf_instance_get_pg_composed(inst, snap, name, pg);
1327 	else
1328 		ret = scf_service_get_pg(svc, name, pg);
1329 
1330 	return (ret);
1331 }
1332 
1333 /*
1334  * Returns SCF_WALK_NEXT for not found, SCF_WALK_ERROR for error,
1335  * and SCF_WALK_DONE for found.
1336  * On error, destroy pg and set it to NULL.
1337  *
1338  * Sets scf_error() if SCF_WALK_ERROR is returned to _BACKEND_ACCESS,
1339  * _CONNECTION_BROKEN, _INTERNAL, _INVALID_ARGUMENT (name is not a
1340  *  valid property group), _NO_RESOURCES, or _NOT_BOUND.
1341  */
1342 static int
_lookup_pg(scf_service_t * svc,scf_instance_t * inst,const scf_snapshot_t * snap,const char * name,scf_propertygroup_t * pg)1343 _lookup_pg(scf_service_t *svc, scf_instance_t *inst,
1344     const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
1345 {
1346 	int ret;
1347 
1348 	ret = _get_pg(svc, inst, snap, name, pg);
1349 
1350 	if (ret == 0) {
1351 		return (SCF_WALK_DONE);
1352 	} else {
1353 		switch (scf_error()) {
1354 		case SCF_ERROR_NOT_FOUND:
1355 		case SCF_ERROR_DELETED:
1356 			return (SCF_WALK_NEXT);
1357 
1358 		case SCF_ERROR_BACKEND_ACCESS:
1359 		case SCF_ERROR_CONNECTION_BROKEN:
1360 		case SCF_ERROR_INTERNAL:
1361 		case SCF_ERROR_INVALID_ARGUMENT:
1362 		case SCF_ERROR_NOT_BOUND:
1363 		case SCF_ERROR_NO_RESOURCES:
1364 			scf_pg_destroy(pg);
1365 			pg = NULL;
1366 			return (SCF_WALK_ERROR);
1367 
1368 		case SCF_ERROR_NOT_SET:
1369 		case SCF_ERROR_HANDLE_MISMATCH:
1370 		default:
1371 			assert(0);
1372 			abort();
1373 		}
1374 	}
1375 
1376 	/*NOTREACHED*/
1377 }
1378 
1379 /*
1380  * If match, return 0.  If no match, return 1.  If error, return -1.
1381  * On error set scf_error() to _BACKEND_ACCESS, _CONNECTION_BROKEN,
1382  * _HANDLE_DESTROYED, _INTERNAL, _NO_MEMORY, _NO_RESOURCES, _NOT_BOUND,
1383  * _NOT_SET (property group specified by pg is not set), _PERMISSION_DENIED,
1384  * or _TEMPLATE_INVALID (target property is not SCF_TYPE_ASTRING or has
1385  * more than one value).
1386  */
1387 static int
check_target_match(scf_propertygroup_t * pg,const char * target)1388 check_target_match(scf_propertygroup_t *pg, const char *target)
1389 {
1390 	char *pg_target;
1391 	int ret = 0;
1392 
1393 	pg_target = _scf_read_single_astring_from_pg(pg,
1394 	    SCF_PROPERTY_TM_TARGET);
1395 	if (pg_target == NULL) {
1396 		switch (scf_error()) {
1397 		case SCF_ERROR_DELETED:
1398 		case SCF_ERROR_NOT_FOUND:
1399 			return (1);
1400 
1401 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1402 		case SCF_ERROR_TYPE_MISMATCH:
1403 			(void) scf_set_error(
1404 			    SCF_ERROR_TEMPLATE_INVALID);
1405 			/*FALLTHROUGH*/
1406 
1407 		case SCF_ERROR_BACKEND_ACCESS:
1408 		case SCF_ERROR_CONNECTION_BROKEN:
1409 		case SCF_ERROR_HANDLE_DESTROYED:
1410 		case SCF_ERROR_INTERNAL:
1411 		case SCF_ERROR_NO_RESOURCES:
1412 		case SCF_ERROR_NOT_BOUND:
1413 		case SCF_ERROR_PERMISSION_DENIED:
1414 			return (-1);
1415 
1416 		case SCF_ERROR_NOT_SET:
1417 		case SCF_ERROR_INVALID_ARGUMENT:
1418 		default:
1419 			assert(0);
1420 			abort();
1421 		}
1422 		/*NOTREACHED*/
1423 	}
1424 
1425 	/* For a desired target of 'this', check for 'this' and 'instance'. */
1426 	if ((strcmp(target, SCF_TM_TARGET_INSTANCE) == 0 ||
1427 	    strcmp(target, SCF_TM_TARGET_THIS) == 0) &&
1428 	    (strcmp(pg_target, SCF_TM_TARGET_INSTANCE) == 0 ||
1429 	    strcmp(pg_target, SCF_TM_TARGET_THIS) == 0)) {
1430 		goto cleanup;
1431 	}
1432 
1433 	if (strcmp(target, SCF_TM_TARGET_DELEGATE) == 0 &&
1434 	    strcmp(pg_target, SCF_TM_TARGET_DELEGATE) == 0) {
1435 		goto cleanup;
1436 	}
1437 
1438 	if (strcmp(target, SCF_TM_TARGET_ALL) == 0 &&
1439 	    strcmp(pg_target, SCF_TM_TARGET_ALL) == 0) {
1440 		goto cleanup;
1441 	}
1442 
1443 	ret = 1;
1444 cleanup:
1445 	free(pg_target);
1446 	return (ret);
1447 }
1448 
1449 /*
1450  * Check if a matching template property group exists for each of:
1451  * name and type, name only, type only, and completely wildcarded
1452  * template.
1453  *
1454  * Both pg_name and pg_type are optional.
1455  *
1456  * Returns NULL on failure, sets scf_error():
1457  *   SCF_ERROR_BACKEND_ACCESS
1458  *   SCF_ERROR_CONNECTION_BROKEN
1459  *   SCF_ERROR_DELETED
1460  *   SCF_ERROR_HANDLE_DESTROYED
1461  *   SCF_ERROR_INTERNAL
1462  *   SCF_ERROR_INVALID_ARGUMENT
1463  *     can't combine the _tmpl_pg_name arguments and get a reasonable
1464  *     length name, or pg_name is not a valid property group.
1465  *   SCF_ERROR_NO_MEMORY
1466  *   SCF_ERROR_NO_RESOURCES
1467  *   SCF_ERROR_NOT_BOUND
1468  *   SCF_ERROR_NOT_FOUND
1469  *     Property doesn't exist or exists and has no value.
1470  *   SCF_ERROR_PERMISSION_DENIED
1471  *   SCF_ERROR_TEMPLATE_INVALID
1472  *     target property is not SCF_TYPE_ASTRING or has more than one value.
1473  */
1474 static scf_propertygroup_t *
_find_template_pg_match(scf_service_t * svc,scf_instance_t * inst,const scf_snapshot_t * snap,const char * pg_name,const char * pg_type,const char * target,char ** tmpl_pg_name)1475 _find_template_pg_match(scf_service_t *svc, scf_instance_t *inst,
1476     const scf_snapshot_t *snap, const char *pg_name, const char *pg_type,
1477     const char *target, char **tmpl_pg_name)
1478 {
1479 	int ret, r;
1480 	scf_propertygroup_t *pg = NULL;
1481 	scf_handle_t *h;
1482 	scf_iter_t *iter;
1483 	char *name, *type;
1484 
1485 	assert(inst != NULL || svc != NULL);
1486 	assert(inst == NULL || svc == NULL);
1487 
1488 	if (inst != NULL)
1489 		h = scf_instance_handle(inst);
1490 	else
1491 		h = scf_service_handle(svc);
1492 	if (h == NULL) {
1493 		return (NULL);
1494 	}
1495 
1496 	if ((pg = scf_pg_create(h)) == NULL ||
1497 	    (iter = scf_iter_create(h)) == NULL) {
1498 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
1499 		scf_pg_destroy(pg);
1500 		return (NULL);
1501 	}
1502 
1503 	/*
1504 	 * We're going to walk through the possible pg templates that
1505 	 * could match the supplied name and type.  We do this
1506 	 * by explicit name lookups when possible to avoid having to
1507 	 * keep track of a most-explicit-match during iteration.
1508 	 */
1509 
1510 	/* First look for a template with name and type set and matching. */
1511 	*tmpl_pg_name = _tmpl_pg_name(pg_name, pg_type, 1);
1512 	if (*tmpl_pg_name == NULL)
1513 		goto fail;
1514 	ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1515 	if (ret != SCF_WALK_NEXT) {
1516 		if (pg != NULL) {
1517 			if ((r = check_target_match(pg, target)) == 0)
1518 				goto done;
1519 			else if (r == -1)
1520 				goto fail;
1521 		} else {
1522 			goto done;
1523 		}
1524 	}
1525 	free(*tmpl_pg_name);
1526 
1527 	/*
1528 	 * Need to search on a name-only match before searching on
1529 	 * type matches.
1530 	 */
1531 
1532 	*tmpl_pg_name = _tmpl_pg_name(pg_name, NULL, 0);
1533 	if (*tmpl_pg_name == NULL)
1534 		goto fail;
1535 	ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1536 	if (ret != SCF_WALK_NEXT) {
1537 		if (pg != NULL) {
1538 			if ((r = check_target_match(pg, target)) == 0)
1539 				goto done;
1540 			else if (r == -1)
1541 				goto fail;
1542 		} else {
1543 			goto done;
1544 		}
1545 	}
1546 	free(*tmpl_pg_name);
1547 
1548 	/* Next, see if there's an "nt" template where the type matches. */
1549 	if (pg_type != NULL && pg_name == NULL) {
1550 		if (inst != NULL)
1551 			ret = scf_iter_instance_pgs_typed_composed(iter, inst,
1552 			    snap, SCF_GROUP_TEMPLATE_PG_PATTERN);
1553 		else
1554 			ret = scf_iter_service_pgs_typed(iter, svc,
1555 			    SCF_GROUP_TEMPLATE_PG_PATTERN);
1556 
1557 		if (ret != 0) {
1558 			if (ismember(scf_error(), errors_server)) {
1559 				goto fail;
1560 			} else {
1561 				assert(0);
1562 				abort();
1563 			}
1564 		}
1565 
1566 		while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
1567 			/* Make sure this is a name and type specified pg. */
1568 			name = _scf_read_single_astring_from_pg(pg,
1569 			    SCF_PROPERTY_TM_NAME);
1570 			if (name == NULL)
1571 				continue;
1572 			type = _scf_read_single_astring_from_pg(pg,
1573 			    SCF_PROPERTY_TM_TYPE);
1574 			if (type == NULL) {
1575 				free(name);
1576 				continue;
1577 			}
1578 			if (strcmp(pg_type, type) == 0 &&
1579 			    check_target_match(pg, target) == 0) {
1580 				*tmpl_pg_name = name;
1581 				free(type);
1582 				goto done;
1583 			}
1584 			free(type);
1585 			free(name);
1586 		}
1587 		if (ret == -1) {
1588 			if (ismember(scf_error(), errors_server)) {
1589 				goto fail;
1590 			} else {
1591 				assert(0);
1592 				abort();
1593 			}
1594 		}
1595 	}
1596 
1597 	*tmpl_pg_name = _tmpl_pg_name(NULL, pg_type, 0);
1598 	if (*tmpl_pg_name == NULL)
1599 		goto fail;
1600 	ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1601 	if (ret != SCF_WALK_NEXT) {
1602 		if (pg != NULL) {
1603 			if ((r = check_target_match(pg, target)) == 0)
1604 				goto done;
1605 			else if (r == -1)
1606 				goto fail;
1607 		} else {
1608 			goto done;
1609 		}
1610 	}
1611 	free(*tmpl_pg_name);
1612 
1613 	*tmpl_pg_name = _tmpl_pg_name(NULL, NULL, 0);
1614 	if (*tmpl_pg_name == NULL)
1615 		goto fail;
1616 	ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
1617 	if (ret != SCF_WALK_NEXT) {
1618 		if (pg != NULL) {
1619 			if ((r = check_target_match(pg, target)) == 0)
1620 				goto done;
1621 			else if (r == -1)
1622 				goto fail;
1623 		} else {
1624 			goto done;
1625 		}
1626 	}
1627 
1628 	(void) scf_set_error(SCF_ERROR_NOT_FOUND);
1629 fail:
1630 	scf_pg_destroy(pg);
1631 	if (*tmpl_pg_name != NULL)
1632 		free(*tmpl_pg_name);
1633 	*tmpl_pg_name = NULL;
1634 	pg = NULL;
1635 done:
1636 	if (ret == SCF_WALK_ERROR)
1637 		free(*tmpl_pg_name);
1638 	scf_iter_destroy(iter);
1639 	return (pg);
1640 }
1641 
1642 /*
1643  * Finds the pg match in either the supplied service or instance.
1644  * Returns SCF_WALK_ERROR, SCF_WALK_NEXT, or SCF_WALK_DONE.
1645  * If returning SCF_WALK_ERROR, sets scf_error():
1646  *   SCF_ERROR_BACKEND_ACCESS
1647  *   SCF_ERROR_CONNECTION_BROKEN
1648  *   SCF_ERROR_DELETED
1649  *   SCF_ERROR_HANDLE_DESTROYED
1650  *   SCF_ERROR_INTERNAL
1651  *   SCF_ERROR_INVALID_ARGUMENT
1652  *     The snaphot is not a valid snapshot name,
1653  *     or can't create a reasonable property group template name.
1654  *   SCF_ERROR_NO_MEMORY
1655  *   SCF_ERROR_NO_RESOURCES
1656  *   SCF_ERROR_NOT_BOUND
1657  *   SCF_ERROR_NOT_FOUND
1658  *     Property doesn't exist or exists and has no value.
1659  *   SCF_ERROR_PERMISSION_DENIED
1660  *   SCF_ERROR_TEMPLATE_INVALID
1661  *     target property is not SCF_TYPE_ASTRING or has more than one value.
1662  */
1663 static int
find_pg_match(scf_service_t * svc,scf_instance_t * inst,pg_tmpl_walk_t * p)1664 find_pg_match(scf_service_t *svc, scf_instance_t *inst, pg_tmpl_walk_t *p)
1665 {
1666 	scf_snapshot_t *tmpl_snap = NULL;
1667 	scf_propertygroup_t *pg;
1668 	scf_handle_t *h;
1669 	char *tmpl_pg_name;
1670 
1671 	assert(svc != NULL || inst != NULL);
1672 	assert(svc == NULL || inst == NULL);
1673 
1674 	if (inst != NULL)
1675 		h = scf_instance_handle(inst);
1676 	else
1677 		h = scf_service_handle(svc);
1678 	if (h == NULL)
1679 		return (SCF_WALK_ERROR);
1680 
1681 	if (p->pw_snapname != NULL) {
1682 		if (_get_snapshot(inst, p->pw_snapname, &tmpl_snap) == -1)
1683 			return (SCF_WALK_ERROR);
1684 	}
1685 	pg = _find_template_pg_match(svc, inst, tmpl_snap, p->pw_pgname,
1686 	    p->pw_pgtype, p->pw_target, &tmpl_pg_name);
1687 
1688 	if (pg != NULL) {
1689 		p->pw_snap = tmpl_snap;
1690 		p->pw_pg = pg;
1691 		p->pw_tmpl_pgname = tmpl_pg_name;
1692 		p->pw_inst = inst;
1693 		p->pw_svc = svc;
1694 		return (SCF_WALK_DONE);
1695 	}
1696 
1697 	scf_snapshot_destroy(tmpl_snap);
1698 	return (SCF_WALK_NEXT);
1699 }
1700 
1701 /*
1702  * return 0 on success and -1 on failure.
1703  *   SCF_ERROR_CONNECTION_BROKEN
1704  *   SCF_ERROR_DELETED
1705  *   SCF_ERROR_HANDLE_DESTROYED
1706  *   SCF_ERROR_HANDLE_MISMATCH
1707  *   SCF_ERROR_INTERNAL
1708  *   SCF_ERROR_INVALID_ARGUMENT
1709  *     FMRI argument, snapshot name, pg_name, or pg is invalid.
1710  *   SCF_ERROR_NO_MEMORY
1711  *   SCF_ERROR_NO_RESOURCES
1712  *   SCF_ERROR_NOT_BOUND
1713  *   SCF_ERROR_NOT_FOUND
1714  *   SCF_ERROR_NOT_SET
1715  */
1716 int
scf_tmpl_get_by_pg(scf_propertygroup_t * pg,scf_pg_tmpl_t * pg_tmpl,int flags)1717 scf_tmpl_get_by_pg(scf_propertygroup_t *pg, scf_pg_tmpl_t *pg_tmpl, int flags)
1718 {
1719 	char *fmribuf = NULL, *snapbuf = NULL, *pg_name = NULL, *pg_type = NULL;
1720 	int ret;
1721 	ssize_t fbufsz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
1722 	ssize_t nbufsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
1723 	ssize_t tbufsz = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
1724 	scf_instance_t *inst = NULL;
1725 	scf_snaplevel_t *snaplvl = NULL;
1726 	scf_service_t *svc = NULL;
1727 	scf_handle_t *h;
1728 	scf_snapshot_t *snap = NULL;
1729 	pg_tmpl_walk_t *p = NULL;
1730 
1731 	assert(fbufsz != 0 && nbufsz != 0 && tbufsz != 0);
1732 
1733 	scf_tmpl_pg_reset(pg_tmpl);
1734 
1735 	if ((h = scf_pg_handle(pg)) == NULL)
1736 		return (-1);
1737 
1738 	if ((inst = scf_instance_create(h)) == NULL ||
1739 	    (svc = scf_service_create(h)) == NULL ||
1740 	    (snaplvl = scf_snaplevel_create(h)) == NULL) {
1741 		goto fail;
1742 	}
1743 
1744 	if ((fmribuf = malloc(fbufsz)) == NULL ||
1745 	    (pg_name = malloc(nbufsz)) == NULL ||
1746 	    (pg_type = malloc(tbufsz)) == NULL ||
1747 	    (p = calloc(1, sizeof (pg_tmpl_walk_t))) == NULL) {
1748 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
1749 		goto fail;
1750 	}
1751 
1752 	if (scf_pg_get_name(pg, pg_name, nbufsz) < 0) {
1753 		goto fail;
1754 	}
1755 
1756 	if (scf_pg_get_type(pg, pg_type, tbufsz) < 0) {
1757 		goto fail;
1758 	}
1759 	p->pw_pgname = pg_name;
1760 	p->pw_pgtype = pg_type;
1761 
1762 	ret = scf_pg_get_parent_snaplevel(pg, snaplvl);
1763 	if (ret == -1) {
1764 		switch (scf_error()) {
1765 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1766 			/* Parent type doesn't match.  Keep looking. */
1767 			break;
1768 
1769 		case SCF_ERROR_DELETED:
1770 		case SCF_ERROR_NOT_BOUND:
1771 		case SCF_ERROR_NOT_SET:
1772 			/* Pass these back to the caller. */
1773 			goto fail;
1774 
1775 		case SCF_ERROR_HANDLE_MISMATCH:
1776 		default:
1777 			assert(0);
1778 			abort();
1779 		}
1780 
1781 		/*
1782 		 * No snapshot.  We'll use 'editing' by default since
1783 		 * snap and snapbuf are NULL.
1784 		 */
1785 		p->pw_snapname = NULL;
1786 
1787 	} else {
1788 		if ((snap = scf_snapshot_create(h)) == NULL) {
1789 			goto fail;
1790 		}
1791 
1792 		ret = scf_snaplevel_get_parent(snaplvl, snap);
1793 		if (ret == -1) {
1794 			if (ismember(scf_error(), errors_server)) {
1795 				goto fail;
1796 			} else {
1797 				assert(0);
1798 				abort();
1799 			}
1800 		}
1801 
1802 		/* Grab snapshot name while we're here. */
1803 		if ((snapbuf = malloc(nbufsz)) == NULL) {
1804 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
1805 			goto fail;
1806 		}
1807 		if (scf_snapshot_get_name(snap, snapbuf, nbufsz) < 0) {
1808 			if (ismember(scf_error(), errors_server)) {
1809 				goto fail;
1810 			} else {
1811 				assert(0);
1812 				abort();
1813 			}
1814 		}
1815 		p->pw_snapname = snapbuf;
1816 
1817 		ret = scf_snapshot_get_parent(snap, inst);
1818 		if (ret == -1) {
1819 			if (ismember(scf_error(), errors_server)) {
1820 				goto fail;
1821 			} else {
1822 				assert(0);
1823 				abort();
1824 			}
1825 		}
1826 
1827 		_walk_template_instances(NULL, inst, snap,
1828 		    (walk_template_inst_func_t *)find_pg_match, p, flags);
1829 	}
1830 
1831 	/* No snapshot parent.  Go looking for instance parent. */
1832 	if (snapbuf == NULL) {
1833 		/* First look for instance parent. */
1834 		ret = scf_pg_get_parent_instance(pg, inst);
1835 		if (ret == 0) {
1836 			_walk_template_instances(NULL, inst, snap,
1837 			    (walk_template_inst_func_t *)find_pg_match,
1838 			    p, flags);
1839 		/* OK, check for service parent */
1840 		} else if (ret == -1 &&
1841 		    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
1842 			ret = scf_pg_get_parent_service(pg, svc);
1843 			if (ret == 0) {
1844 				_walk_template_instances(svc, NULL, snap,
1845 				    (walk_template_inst_func_t *)find_pg_match,
1846 				    p, flags);
1847 			} else {
1848 				switch (scf_error()) {
1849 				case SCF_ERROR_CONSTRAINT_VIOLATED:
1850 					(void) scf_set_error(
1851 					    SCF_ERROR_NOT_FOUND);
1852 					/*FALLTHROUGH*/
1853 
1854 				case SCF_ERROR_CONNECTION_BROKEN:
1855 				case SCF_ERROR_DELETED:
1856 				case SCF_ERROR_HANDLE_MISMATCH:
1857 				case SCF_ERROR_NOT_BOUND:
1858 				case SCF_ERROR_NOT_SET:
1859 					goto fail;
1860 
1861 				default:
1862 					assert(0);
1863 					abort();
1864 				}
1865 			}
1866 		} else {
1867 			goto fail;
1868 		}
1869 	}
1870 
1871 	if (p->pw_pg != NULL) {
1872 		pg_tmpl->pt_h = h;
1873 		pg_tmpl->pt_pg = p->pw_pg;
1874 		pg_tmpl->pt_inst = p->pw_inst;
1875 		/* we may get a different instance back */
1876 		if (p->pw_inst != inst)
1877 			scf_instance_destroy(inst);
1878 		pg_tmpl->pt_snap = p->pw_snap;
1879 		pg_tmpl->pt_svc = p->pw_svc;
1880 		/* we may get a different service back */
1881 		if (p->pw_svc != svc)
1882 			scf_service_destroy(svc);
1883 		pg_tmpl->pt_populated = 1;
1884 		free(p->pw_tmpl_pgname);
1885 		ret = 0;
1886 		goto done;
1887 	}
1888 
1889 	(void) scf_set_error(SCF_ERROR_NOT_FOUND);
1890 
1891 fail:
1892 	ret = -1;
1893 	scf_instance_destroy(inst);
1894 	scf_service_destroy(svc);
1895 done:
1896 	scf_snapshot_destroy(snap);
1897 	free(snapbuf);
1898 	free(fmribuf);
1899 	free(pg_name);
1900 	free(pg_type);
1901 	free(p);
1902 	scf_snaplevel_destroy(snaplvl);
1903 	return (ret);
1904 }
1905 
1906 /*
1907  * int scf_tmpl_get_by_pg_name()
1908  *
1909  * Get a template by a combination of the name and type.  Either name
1910  * or type can be null, which indicates a wildcard.  flags may be
1911  * SCF_PG_TMPL_FLAG_CURRENT (use current properties rather than
1912  * the defined or running snapshot), and SCF_PG_TMPL_FLAG_EXACT (match
1913  * only templates defined by the FMRI in question, not by its restarter
1914  * or globally).  Returns 0 on success and -1 on error, and sets
1915  * scf_error() to:
1916  *   SCF_ERROR_BACKEND_ACCESS
1917  *   SCF_ERROR_CONNECTION_BROKEN
1918  *     The connection to the repository was lost.
1919  *   SCF_ERROR_DELETED
1920  *     The instance has been deleted.
1921  *   SCF_ERROR_HANDLE_DESTROYED
1922  *   SCF_ERROR_INTERNAL
1923  *   SCF_ERROR_INVALID_ARGUMENT
1924  *     FMRI isn't valid, pg_name is too long to look for a template, or
1925  *     snapshot specified isn't a valid name
1926  *   SCF_ERROR_NO_MEMORY
1927  *   SCF_ERROR_NO_RESOURCES
1928  *     The server does not have adequate resources to complete the request.
1929  *   SCF_ERROR_NOT_BOUND
1930  *     The handle is not currently bound.
1931  *   SCF_ERROR_NOT_FOUND
1932  *     Object matching FMRI doesn't exist in the repository, or snapshot
1933  *     doesn't exist.
1934  */
1935 int
scf_tmpl_get_by_pg_name(const char * fmri,const char * snapshot,const char * pg_name,const char * pg_type,scf_pg_tmpl_t * pg_tmpl,int flags)1936 scf_tmpl_get_by_pg_name(const char *fmri, const char *snapshot,
1937     const char *pg_name, const char *pg_type, scf_pg_tmpl_t *pg_tmpl, int flags)
1938 {
1939 	scf_instance_t *inst = NULL;
1940 	scf_service_t *svc = NULL;
1941 	scf_snapshot_t *snap = NULL;
1942 	pg_tmpl_walk_t *p = NULL;
1943 	scf_handle_t *h;
1944 	int ret;
1945 
1946 	assert(pg_tmpl != NULL);
1947 	h = pg_tmpl->pt_h;
1948 	assert(h != NULL);
1949 
1950 	scf_tmpl_pg_reset(pg_tmpl);
1951 
1952 	if ((inst = scf_instance_create(h)) == NULL ||
1953 	    (svc = scf_service_create(h)) == NULL) {
1954 		goto fail;
1955 	}
1956 
1957 	p = calloc(1, sizeof (pg_tmpl_walk_t));
1958 	if (p == NULL) {
1959 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
1960 		goto fail;
1961 	}
1962 
1963 	ret = scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
1964 	    NULL, SCF_DECODE_FMRI_EXACT);
1965 	if (ret == 0) {
1966 		scf_service_destroy(svc);
1967 		svc = NULL;
1968 	} else if (ret != 0 &&
1969 	    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
1970 		ret = scf_handle_decode_fmri(h, fmri, NULL, svc,
1971 		    NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT);
1972 		if (ret == 0) {
1973 			scf_instance_destroy(inst);
1974 			inst = NULL;
1975 		}
1976 	}
1977 	if (ret != 0) {
1978 		if (ismember(scf_error(), errors_server)) {
1979 			goto fail;
1980 		} else switch (scf_error()) {
1981 		case SCF_ERROR_CONSTRAINT_VIOLATED:
1982 			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
1983 			goto fail;
1984 
1985 		case SCF_ERROR_INVALID_ARGUMENT:
1986 		case SCF_ERROR_NOT_FOUND:
1987 			goto fail;
1988 
1989 		case SCF_ERROR_HANDLE_MISMATCH:
1990 		case SCF_ERROR_NOT_SET:
1991 		default:
1992 			assert(0);
1993 			abort();
1994 		}
1995 	}
1996 
1997 	assert(svc == NULL || inst == NULL);
1998 	assert(svc != NULL || inst != NULL);
1999 
2000 	/* If we have a service fmri, snapshot is ignored. */
2001 	if (inst != NULL) {
2002 		if (snapshot == NULL || strcmp(snapshot, "running") == 0 ||
2003 		    (flags & SCF_PG_TMPL_FLAG_CURRENT) ==
2004 		    SCF_PG_TMPL_FLAG_CURRENT) {
2005 			if (_get_snapshot(inst, NULL, &snap) == -1)
2006 				goto fail;
2007 		} else {
2008 			if (_get_snapshot(inst, snapshot, &snap) == -1) {
2009 				goto fail;
2010 			} else if (scf_error() == SCF_ERROR_NOT_FOUND) {
2011 				goto fail;
2012 			}
2013 		}
2014 	}
2015 
2016 	p->pw_snapname = snapshot;
2017 	p->pw_pgname = pg_name;
2018 	p->pw_pgtype = pg_type;
2019 
2020 	/*
2021 	 * For each of instance, restarter, global
2022 	 *    - check for a tm_pg_pattern_nt_<name> matching type
2023 	 *    - check for a tm_pg_pattern_t_<type> matching type
2024 	 *    - check for any tm_pg_pattern_
2025 	 * Currently plan to return the most specific match only.
2026 	 */
2027 	_walk_template_instances(svc, inst, snap,
2028 	    (walk_template_inst_func_t *)find_pg_match, p, flags);
2029 
2030 	if (p->pw_pg != NULL) {
2031 		pg_tmpl->pt_h = h;
2032 		pg_tmpl->pt_pg = p->pw_pg;
2033 		pg_tmpl->pt_inst = p->pw_inst;
2034 		/* we may get a different instance back */
2035 		if (p->pw_inst != inst)
2036 			scf_instance_destroy(inst);
2037 		pg_tmpl->pt_snap = p->pw_snap;
2038 		pg_tmpl->pt_svc = p->pw_svc;
2039 		/* we may get a different service back */
2040 		if (p->pw_svc != svc)
2041 			scf_service_destroy(svc);
2042 		pg_tmpl->pt_populated = 1;
2043 		scf_snapshot_destroy(snap);
2044 		free(p->pw_tmpl_pgname);
2045 		free(p);
2046 		return (0);
2047 	}
2048 
2049 	(void) scf_set_error(SCF_ERROR_NOT_FOUND);
2050 fail:
2051 	free(p);
2052 	scf_instance_destroy(inst);
2053 	scf_service_destroy(svc);
2054 	scf_snapshot_destroy(snap);
2055 	return (-1);
2056 }
2057 
2058 /*
2059  * Returns NULL on failure, sets scf_error() to _CONNECTION_BROKEN,
2060  * _DELETED, _NO_RESOURCES, or _NOT_BOUND.
2061  */
2062 static scf_iter_t *
_get_svc_or_inst_iter(scf_handle_t * h,scf_pg_tmpl_t * t)2063 _get_svc_or_inst_iter(scf_handle_t *h, scf_pg_tmpl_t *t)
2064 {
2065 	scf_iter_t *iter;
2066 	int ret;
2067 
2068 	assert(t->pt_svc != NULL || t->pt_inst != NULL);
2069 	assert(t->pt_svc == NULL || t->pt_inst == NULL);
2070 
2071 	if ((iter = scf_iter_create(h)) == NULL) {
2072 		return (NULL);
2073 	}
2074 
2075 	/* Iterate on property groups of type template_pg_pattern */
2076 
2077 	if (t->pt_inst != NULL)
2078 		ret = scf_iter_instance_pgs_typed_composed(iter,
2079 		    t->pt_inst, t->pt_snap,
2080 		    SCF_GROUP_TEMPLATE_PG_PATTERN);
2081 	if (t->pt_svc != NULL)
2082 		ret = scf_iter_service_pgs_typed(iter, t->pt_svc,
2083 		    SCF_GROUP_TEMPLATE_PG_PATTERN);
2084 
2085 	if (ret != 0) {
2086 		if (ismember(scf_error(), errors_server)) {
2087 			scf_iter_destroy(iter);
2088 			return (NULL);
2089 		} else {
2090 			assert(0);
2091 			abort();
2092 		}
2093 	}
2094 
2095 	return (iter);
2096 }
2097 
2098 /*
2099  * Returns NULL on failure, sets scf_error() to:
2100  *   SCF_ERROR_BACKEND_ACCESS
2101  *   SCF_ERROR_CONNECTION_BROKEN
2102  *   SCF_ERROR_DELETED
2103  *   SCF_HANDLE_DESTROYED
2104  *   SCF_ERROR_INTERNAL
2105  *   SCF_ERROR_INVALID_ARGUMENT
2106  *     Handle argument is NULL, or snaphot is not a valid snapshot name.
2107  *   SCF_ERROR_NO_MEMORY
2108  *   SCF_ERROR_NO_RESOURCES
2109  *   SCF_ERROR_NOT_BOUND
2110  *   SCF_ERROR_NOT_FOUND
2111  */
2112 static scf_iter_t *
_get_next_iterator(scf_handle_t * h,scf_pg_tmpl_t * t,const char * snapshot,int exact)2113 _get_next_iterator(scf_handle_t *h, scf_pg_tmpl_t *t, const char *snapshot,
2114     int exact)
2115 {
2116 	scf_iter_t  *iter = NULL;
2117 	ssize_t limit;
2118 
2119 	limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2120 	assert(limit != 0);
2121 
2122 	/*
2123 	 * Check what level we last iterated on: none, service,
2124 	 * restarter, or global.  Make sure that if one in the middle
2125 	 * doesn't exist, we move on to the next entity.
2126 	 *
2127 	 * Before we drop any references to pt_inst or pt_svc we must
2128 	 * destroy them so we don't leak them.
2129 	 */
2130 	do {
2131 		switch (t->pt_iter_last) {
2132 		case SCF__TMPL_ITER_NONE:
2133 			t->pt_iter_last = SCF__TMPL_ITER_INST;
2134 			if (t->pt_inst != t->pt_orig_inst)
2135 				scf_instance_destroy(t->pt_inst);
2136 			t->pt_inst = t->pt_orig_inst;
2137 			if (t->pt_svc != t->pt_orig_svc)
2138 				scf_service_destroy(t->pt_svc);
2139 			t->pt_svc = t->pt_orig_svc;
2140 			break;
2141 
2142 		case SCF__TMPL_ITER_INST:
2143 			/*
2144 			 * Don't go any further than the specified instance
2145 			 * if exact was set.
2146 			 */
2147 			if (exact == 1) {
2148 				(void) scf_set_error(SCF_ERROR_NOT_FOUND);
2149 				goto fail;
2150 			}
2151 			t->pt_iter_last = SCF__TMPL_ITER_RESTARTER;
2152 			if (t->pt_inst != t->pt_orig_inst)
2153 				scf_instance_destroy(t->pt_inst);
2154 			t->pt_inst = _get_restarter_inst(h, t->pt_orig_svc,
2155 			    t->pt_orig_inst, t->pt_snap);
2156 			if (t->pt_svc != t->pt_orig_svc)
2157 				scf_service_destroy(t->pt_svc);
2158 			t->pt_svc = NULL;
2159 			break;
2160 
2161 		case SCF__TMPL_ITER_RESTARTER:
2162 			t->pt_iter_last = SCF__TMPL_ITER_GLOBAL;
2163 			if (t->pt_inst != t->pt_orig_inst)
2164 				scf_instance_destroy(t->pt_inst);
2165 			t->pt_inst = _get_global_inst(h);
2166 			if (t->pt_svc != t->pt_orig_svc)
2167 				scf_service_destroy(t->pt_svc);
2168 			t->pt_svc = NULL;
2169 			break;
2170 
2171 		case SCF__TMPL_ITER_GLOBAL:
2172 			(void) scf_set_error(SCF_ERROR_NOT_FOUND);
2173 			return (NULL);
2174 
2175 		default:
2176 			assert(0);
2177 			abort();
2178 		}
2179 	} while (t->pt_inst == NULL && t->pt_svc == NULL);
2180 
2181 	/* Set pt_snap to the snapshot for this instance */
2182 	if (t->pt_inst != NULL) {
2183 		scf_snapshot_destroy(t->pt_snap);
2184 		if (_get_snapshot(t->pt_inst, snapshot,
2185 		    &t->pt_snap) == -1)
2186 			goto fail;
2187 	}
2188 
2189 	iter = _get_svc_or_inst_iter(h, t);
2190 fail:
2191 	return (iter);
2192 }
2193 
2194 /*
2195  * scf_pg_tmpl_t *scf_tmpl_pg_create(scf_handle_t *)
2196  *
2197  * Returns NULL on failure, sets scf_error() to _INVALID_ARGUMENT
2198  * or _NO_MEMORY.
2199  */
2200 scf_pg_tmpl_t *
scf_tmpl_pg_create(scf_handle_t * handle)2201 scf_tmpl_pg_create(scf_handle_t *handle)
2202 {
2203 	scf_pg_tmpl_t *pg_tmpl = NULL;
2204 
2205 	if (handle == NULL) {
2206 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2207 		return (NULL);
2208 	}
2209 	pg_tmpl = calloc(1, sizeof (scf_pg_tmpl_t));
2210 	if (pg_tmpl == NULL)
2211 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2212 	else
2213 		pg_tmpl->pt_h = handle;
2214 
2215 	return (pg_tmpl);
2216 }
2217 
2218 /*
2219  * Retrieves name or type of a template pg.
2220  * Returns -1 on failure.  Sets scf_error():
2221  *   SCF_ERROR_BACKEND_ACCESS
2222  *   SCF_ERROR_CONNECTION_BROKEN
2223  *   SCF_ERROR_DELETED
2224  *   SCF_ERROR_HANDLE_DESTROYED
2225  *   SCF_ERROR_INTERNAL
2226  *   SCF_ERROR_NO_MEMORY
2227  *   SCF_ERROR_NO_RESOURCES
2228  *   SCF_ERROR_NOT_BOUND
2229  *   SCF_ERROR_PERMISSION_DENIED
2230  *   SCF_ERROR_TEMPLATE_INVALID
2231  *     pname property is not SCF_TYPE_ASTRING or has more than one value.
2232  */
2233 static ssize_t
_scf_tmpl_prop_value(scf_propertygroup_t * pg,const char * pname,char ** out)2234 _scf_tmpl_prop_value(scf_propertygroup_t *pg, const char *pname, char **out)
2235 {
2236 	assert(strcmp(pname, SCF_PROPERTY_TM_NAME) == 0 ||
2237 	    strcmp(pname, SCF_PROPERTY_TM_TYPE) == 0);
2238 
2239 	*out = _scf_read_single_astring_from_pg(pg, pname);
2240 
2241 	if (*out != NULL && *out[0] == '\0') {
2242 		(void) scf_set_error(SCF_ERROR_NONE);
2243 		free(*out);
2244 		*out = strdup(SCF_TMPL_WILDCARD);
2245 		if (*out == NULL)
2246 			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2247 	}
2248 	if (*out == NULL) {
2249 		if (ismember(scf_error(), errors_server)) {
2250 			return (-1);
2251 		} else switch (scf_error()) {
2252 		case SCF_ERROR_CONSTRAINT_VIOLATED:
2253 		case SCF_ERROR_NOT_FOUND:
2254 		case SCF_ERROR_TYPE_MISMATCH:
2255 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2256 			return (-1);
2257 
2258 		case SCF_ERROR_INVALID_ARGUMENT:
2259 		case SCF_ERROR_NOT_SET:
2260 		default:
2261 			assert(0);
2262 			abort();
2263 		}
2264 	}
2265 
2266 	return (strlen(*out));
2267 }
2268 
2269 /*
2270  * int scf_tmpl_iter_pgs()
2271  *
2272  * Iterates through the property group templates for the fmri given.
2273  * When t is uninitialized or reset, sets t to the first property group
2274  * template in fmri. On subsequent calls, sets t to the next property group
2275  * template in frmi.
2276  * Returns 1 on success, 0 when no property group templates are left to
2277  * iterate, -1 on error.
2278  * The flags argument may include SCF_PG_TMPL_FLAG_REQUIRED,
2279  * SCF_PG_TMPL_FLAG_CURRENT,  and/or SCF_PG_TMPL_FLAG_EXACT.
2280  *
2281  * Returns -1 on error and sets scf_error() to:
2282  *   SCF_ERROR_BACKEND_ACCESS
2283  *   SCF_ERROR_CONNECTION_BROKEN
2284  *   SCF_ERROR_DELETED
2285  *   SCF_ERROR_HANDLE_DESTROYED
2286  *   SCF_ERROR_INTERNAL
2287  *   SCF_ERROR_INVALID_ARGUMENT
2288  *      The handle argument is NULL, fmri is invalid, or snapshot is invalid.
2289  *   SCF_ERROR_NO_MEMORY
2290  *   SCF_ERROR_NO_RESOURCES
2291  *   SCF_ERROR_NOT_BOUND
2292  *   SCF_ERROR_NOT_FOUND
2293  *   SCF_ERROR_PERMISSION_DENIED
2294  */
2295 int
scf_tmpl_iter_pgs(scf_pg_tmpl_t * t,const char * fmri,const char * snapshot,const char * type,int flags)2296 scf_tmpl_iter_pgs(scf_pg_tmpl_t *t, const char *fmri, const char *snapshot,
2297     const char *type, int flags)
2298 {
2299 	scf_handle_t *h;
2300 	scf_service_t *svc = NULL;
2301 	scf_instance_t *inst = NULL;
2302 	scf_propertygroup_t *pg = NULL;
2303 	scf_snapshot_t *snap = NULL;
2304 	scf_pg_tmpl_t *pg_tmpl = NULL;
2305 	int err;
2306 	int found = 0;
2307 	char *tmpl_type;
2308 	uint8_t required;
2309 	int ret;
2310 
2311 	if (t == NULL) {
2312 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2313 		return (-1);
2314 	}
2315 
2316 	h = t->pt_h;
2317 
2318 	if (t->pt_populated == 0) {
2319 		if ((svc = scf_service_create(h)) == NULL ||
2320 		    (inst = scf_instance_create(h)) == NULL ||
2321 		    (pg = scf_pg_create(h)) == NULL) {
2322 			goto fail_non_populated;
2323 		}
2324 
2325 		ret = scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
2326 		    NULL, SCF_DECODE_FMRI_EXACT);
2327 		if (ret == 0) {
2328 			scf_service_destroy(svc);
2329 			svc = NULL;
2330 		} else if (ret != 0 &&
2331 		    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
2332 			ret = scf_handle_decode_fmri(h, fmri, NULL, svc,
2333 			    NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT);
2334 			if (ret == 0) {
2335 				scf_instance_destroy(inst);
2336 				inst = NULL;
2337 			}
2338 		}
2339 
2340 		if (ret != 0) {
2341 			if (ismember(scf_error(), errors_server)) {
2342 				goto fail_non_populated;
2343 			} else switch (scf_error()) {
2344 			case SCF_ERROR_CONSTRAINT_VIOLATED:
2345 				(void) scf_set_error(
2346 				    SCF_ERROR_INVALID_ARGUMENT);
2347 				goto fail_non_populated;
2348 
2349 			case SCF_ERROR_INVALID_ARGUMENT:
2350 			case SCF_ERROR_NOT_FOUND:
2351 				goto fail_non_populated;
2352 
2353 			case SCF_ERROR_HANDLE_MISMATCH:
2354 			case SCF_ERROR_NOT_SET:
2355 			default:
2356 				assert(0);
2357 				abort();
2358 			}
2359 		}
2360 
2361 		assert(svc == NULL || inst == NULL);
2362 		assert(svc != NULL || inst != NULL);
2363 
2364 		if (inst != NULL) {
2365 			if (snapshot == NULL ||
2366 			    strcmp(snapshot, "running") == 0 ||
2367 			    (flags & SCF_PG_TMPL_FLAG_CURRENT) ==
2368 			    SCF_PG_TMPL_FLAG_CURRENT) {
2369 				if (_get_snapshot(inst, NULL, &snap) == -1)
2370 					goto fail_non_populated;
2371 			} else {
2372 				(void) scf_set_error(SCF_ERROR_NONE);
2373 				if (_get_snapshot(inst, snapshot,
2374 				    &snap) == -1) {
2375 					goto fail_non_populated;
2376 				} else if (scf_error() == SCF_ERROR_NOT_FOUND) {
2377 					goto fail_non_populated;
2378 				}
2379 			}
2380 		} else {
2381 			scf_snapshot_destroy(snap);
2382 			snap = NULL;
2383 		}
2384 
2385 		pg_tmpl = t;
2386 		pg_tmpl->pt_orig_inst = inst;
2387 		pg_tmpl->pt_orig_svc = svc;
2388 		pg_tmpl->pt_snap = snap;
2389 		pg_tmpl->pt_is_iter = 1;
2390 		pg_tmpl->pt_iter_last = SCF__TMPL_ITER_NONE;
2391 		pg_tmpl->pt_pg = pg;
2392 		pg_tmpl->pt_populated = 1;
2393 	} else {
2394 		if (t->pt_is_iter != 1) {
2395 			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2396 			return (-1);
2397 		}
2398 		pg_tmpl = t;
2399 		assert(pg_tmpl->pt_pg != NULL);
2400 	}
2401 
2402 	if (pg_tmpl->pt_iter == NULL) {
2403 		pg_tmpl->pt_iter = _get_next_iterator(h, pg_tmpl, snapshot,
2404 		    (flags & SCF_PG_TMPL_FLAG_EXACT) ? 1 : 0);
2405 		if (pg_tmpl->pt_iter == NULL) {
2406 			if (scf_error() == SCF_ERROR_NOT_FOUND)
2407 				return (0);
2408 			else
2409 				return (-1);
2410 		}
2411 	}
2412 
2413 	while (found == 0) {
2414 		while ((err = scf_iter_next_pg(pg_tmpl->pt_iter,
2415 		    pg_tmpl->pt_pg)) != 1) {
2416 			if (err == -1) {
2417 				if (ismember(scf_error(), errors_server)) {
2418 					return (-1);
2419 				} else switch (scf_error()) {
2420 				case SCF_ERROR_HANDLE_MISMATCH:
2421 					return (-1);
2422 
2423 				case SCF_ERROR_NOT_SET:
2424 				case SCF_ERROR_INVALID_ARGUMENT:
2425 				default:
2426 					assert(0);
2427 					abort();
2428 				}
2429 			} else if (err == 0)  {
2430 				/* This iteration is done.  Get the next one */
2431 				scf_iter_destroy(pg_tmpl->pt_iter);
2432 				pg_tmpl->pt_iter = _get_next_iterator(h,
2433 				    pg_tmpl, snapshot,
2434 				    (flags & SCF_PG_TMPL_FLAG_EXACT) ? 1 : 0);
2435 				if (pg_tmpl->pt_iter == NULL) {
2436 					if (scf_error() == SCF_ERROR_NOT_FOUND)
2437 						return (0);
2438 					else
2439 						return (-1);
2440 				}
2441 				continue;
2442 			} else {
2443 				assert(0);
2444 				abort();
2445 			}
2446 		}
2447 
2448 		/*
2449 		 * Discard pgs which don't exist at the right scoping.  This
2450 		 * check also makes sure that if we're looking at, for
2451 		 * example, svc:/system/svc/restarter:default, that we
2452 		 * don't hand back the same property groups twice.
2453 		 */
2454 		switch (t->pt_iter_last) {
2455 		case SCF__TMPL_ITER_INST:
2456 			ret = check_target_match(pg_tmpl->pt_pg,
2457 			    SCF_TM_TARGET_THIS);
2458 			break;
2459 		case SCF__TMPL_ITER_RESTARTER:
2460 			ret = check_target_match(pg_tmpl->pt_pg,
2461 			    SCF_TM_TARGET_DELEGATE);
2462 			break;
2463 		case SCF__TMPL_ITER_GLOBAL:
2464 			ret = check_target_match(pg_tmpl->pt_pg,
2465 			    SCF_TM_TARGET_ALL);
2466 			break;
2467 		case SCF__TMPL_ITER_NONE:
2468 		default:
2469 			assert(0);
2470 			abort();
2471 		}
2472 
2473 		if (ret != 0)
2474 			continue;
2475 
2476 		/*
2477 		 * If walking only required property groups, check if
2478 		 * the retrieved group is required.
2479 		 */
2480 		if (flags & SCF_PG_TMPL_FLAG_REQUIRED) {
2481 			if (scf_tmpl_pg_required(pg_tmpl, &required) == 0) {
2482 				if (required == 0)
2483 					continue;
2484 			} else {
2485 				return (-1);
2486 			}
2487 		}
2488 
2489 		/*
2490 		 * If type != NULL, check if type property matches.  If no
2491 		 * type property exists, consider it a match.
2492 		 */
2493 		if (type != NULL) {
2494 			if (scf_tmpl_pg_type(pg_tmpl, &tmpl_type) != -1) {
2495 				if (strcmp(tmpl_type, SCF_TMPL_WILDCARD)
2496 				    == 0 || strcmp(type, tmpl_type) == 0) {
2497 					free(tmpl_type);
2498 					break;
2499 				}
2500 				free(tmpl_type);
2501 			} else if (scf_error() == SCF_ERROR_NOT_FOUND ||
2502 			    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED ||
2503 			    scf_error() == SCF_ERROR_TYPE_MISMATCH) {
2504 				break;
2505 			} else {
2506 				return (-1);
2507 			}
2508 		} else {
2509 			break;
2510 		}
2511 	}
2512 
2513 	return (1);
2514 
2515 fail_non_populated:
2516 	scf_service_destroy(svc);
2517 	scf_instance_destroy(inst);
2518 	scf_pg_destroy(pg);
2519 	scf_snapshot_destroy(snap);
2520 	return (-1);
2521 }
2522 
2523 void
scf_tmpl_pg_destroy(scf_pg_tmpl_t * t)2524 scf_tmpl_pg_destroy(scf_pg_tmpl_t *t)
2525 {
2526 	if (t == NULL)
2527 		return;
2528 
2529 	scf_pg_destroy(t->pt_pg);
2530 	scf_instance_destroy(t->pt_inst);
2531 	if (t->pt_inst != t->pt_orig_inst)
2532 		scf_instance_destroy(t->pt_orig_inst);
2533 	scf_snapshot_destroy(t->pt_snap);
2534 	scf_service_destroy(t->pt_orig_svc);
2535 	if (t->pt_svc != t->pt_orig_svc)
2536 		scf_service_destroy(t->pt_svc);
2537 	scf_iter_destroy(t->pt_iter);
2538 	free(t);
2539 }
2540 
2541 void
scf_tmpl_pg_reset(scf_pg_tmpl_t * t)2542 scf_tmpl_pg_reset(scf_pg_tmpl_t *t)
2543 {
2544 	scf_pg_destroy(t->pt_pg);
2545 	t->pt_pg = NULL;
2546 
2547 	scf_instance_destroy(t->pt_inst);
2548 	if (t->pt_inst != t->pt_orig_inst)
2549 		scf_instance_destroy(t->pt_orig_inst);
2550 	t->pt_inst = NULL;
2551 	t->pt_orig_inst = NULL;
2552 
2553 	scf_snapshot_destroy(t->pt_snap);
2554 	t->pt_snap = NULL;
2555 
2556 	scf_service_destroy(t->pt_orig_svc);
2557 	if (t->pt_svc != t->pt_orig_svc)
2558 		scf_service_destroy(t->pt_svc);
2559 	t->pt_orig_svc = NULL;
2560 	t->pt_svc = NULL;
2561 
2562 	scf_iter_destroy(t->pt_iter);
2563 	t->pt_iter = NULL;
2564 
2565 	t->pt_populated = 0;
2566 	t->pt_is_iter = 0;
2567 	t->pt_iter_last = 0;
2568 
2569 	/* Do not reset t->pt_h. */
2570 }
2571 
2572 /*
2573  * int scf_tmpl_get_by_prop()
2574  *
2575  * Get the property template given a property group template and property
2576  * name.  No flags are currently defined for this function.
2577  *
2578  * Returns NULL on failure, and sets scf_error():
2579  *   SCF_ERROR_BACKEND_ACCESS
2580  *   SCF_ERROR_CONNECTION_BROKEN
2581  *   SCF_ERROR_DELETED
2582  *   SCF_ERROR_HANDLE_DESTROYED
2583  *   SCF_ERROR_INTERNAL
2584  *   SCF_ERROR_INVALID_ARGUMENT
2585  *   SCF_ERROR_NO_MEMORY
2586  *   SCF_ERROR_NO_RESOURCES
2587  *   SCF_ERROR_NOT_BOUND
2588  *   SCF_ERROR_NOT_FOUND
2589  *     Template object matching property doesn't exist in the repository.
2590  *   SCF_ERROR_TYPE_MISMATCH
2591  *     Matching template object is the wrong type in the repository.
2592  */
2593 int
scf_tmpl_get_by_prop(scf_pg_tmpl_t * t,const char * prop,scf_prop_tmpl_t * prop_tmpl,int flags)2594 scf_tmpl_get_by_prop(scf_pg_tmpl_t *t, const char *prop,
2595     scf_prop_tmpl_t *prop_tmpl, int flags)
2596 {
2597 	char *tmpl_prop_name;
2598 	scf_propertygroup_t *pg = NULL;
2599 	char *pg_type;
2600 	int found = 0;
2601 
2602 	if (flags != 0) {
2603 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2604 		return (-1);
2605 	}
2606 
2607 	scf_tmpl_prop_reset(prop_tmpl);
2608 	if ((pg = scf_pg_create(scf_pg_handle(t->pt_pg))) == NULL)
2609 		return (-1);
2610 
2611 	tmpl_prop_name = _tmpl_prop_name(prop, t);
2612 	if (tmpl_prop_name == NULL) {
2613 		assert(scf_error() != SCF_ERROR_NOT_SET);
2614 		return (-1);
2615 	}
2616 
2617 	if (_get_pg(t->pt_svc, t->pt_inst, t->pt_snap,
2618 	    tmpl_prop_name, pg) != 0) {
2619 		if (!ismember(scf_error(), errors_server)) {
2620 			switch (scf_error()) {
2621 			case SCF_ERROR_NOT_FOUND:
2622 			case SCF_ERROR_INVALID_ARGUMENT:
2623 				break;
2624 
2625 			case SCF_ERROR_NOT_SET:
2626 			case SCF_ERROR_HANDLE_MISMATCH:
2627 			default:
2628 				assert(0);
2629 				abort();
2630 			}
2631 		}
2632 	} else {
2633 		/*
2634 		 * We've only found a template property group if the type
2635 		 * is correct.
2636 		 */
2637 		if ((pg_type = _scf_get_pg_type(pg)) != NULL &&
2638 		    strcmp(pg_type, SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0)
2639 			found++;
2640 		else
2641 			(void) scf_set_error(SCF_ERROR_TYPE_MISMATCH);
2642 
2643 
2644 		free(pg_type);
2645 	}
2646 
2647 	if (found == 0) {
2648 		scf_pg_destroy(pg);
2649 		free(tmpl_prop_name);
2650 		return (-1);
2651 	}
2652 
2653 	prop_tmpl->prt_h = scf_pg_handle(t->pt_pg);
2654 	prop_tmpl->prt_t = t;
2655 	prop_tmpl->prt_pg = pg;
2656 	prop_tmpl->prt_pg_name = tmpl_prop_name;
2657 	prop_tmpl->prt_populated = 1;
2658 
2659 	return (0);
2660 }
2661 
2662 /*
2663  * scf_prop_tmpl_t *scf_tmpl_prop_create(scf_handle_t *);
2664  *
2665  * Returns NULL on failure, sets scf_error() to _INVALID_ARGUMENT, or
2666  * _NO_MEMORY.
2667  */
2668 scf_prop_tmpl_t *
scf_tmpl_prop_create(scf_handle_t * handle)2669 scf_tmpl_prop_create(scf_handle_t *handle)
2670 {
2671 	scf_prop_tmpl_t *pt;
2672 
2673 	if (handle == NULL) {
2674 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2675 		return (NULL);
2676 	}
2677 	pt = calloc(1, sizeof (scf_prop_tmpl_t));
2678 	if (pt == NULL)
2679 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2680 	else
2681 		pt->prt_h = handle;
2682 
2683 	return (pt);
2684 }
2685 
2686 /*
2687  * int scf_tmpl_iter_props()
2688  *
2689  * Iterates over all property templates defined in the specified property
2690  * group template.  The iterator state is stored on the property template
2691  * data structure, and the data structure should be allocated with
2692  * scf_tmpl_prop_create().  To continue the iteration, the previously
2693  * returned structure should be passed in as an argument to this function.
2694  * flags can include SCF_PROP_TMPL_FLAG_REQUIRED.  The function returns
2695  * 1 when a result was found, and 0 when the iteration is complete.
2696  *
2697  * Returns -1 on failure, and sets scf_error():
2698  *   SCF_ERROR_BACKEND_ACCESS
2699  *   SCF_ERROR_CONNECTION_BROKEN
2700  *   SCF_ERROR_DELETED
2701  *   SCF_ERROR_HANDLE_DESTROYED
2702  *   SCF_ERROR_INTERNAL
2703  *   SCF_ERROR_INVALID_ARGUMENT
2704  *   SCF_ERROR_NO_MEMORY
2705  *   SCF_ERROR_NO_RESOURCES
2706  *   SCF_ERROR_NOT_BOUND
2707  *   SCF_ERROR_PERMISSION_DENIED
2708  *   SCF_ERROR_TEMPLATE_INVALID
2709  *     Template data is invalid.  One of the property templates in this
2710  *     pg_tmpl is malformed.
2711  */
2712 int
scf_tmpl_iter_props(scf_pg_tmpl_t * t,scf_prop_tmpl_t * pt,int flags)2713 scf_tmpl_iter_props(scf_pg_tmpl_t *t, scf_prop_tmpl_t *pt, int flags)
2714 {
2715 	scf_prop_tmpl_t *prop_tmpl;
2716 	char *pg_pat;
2717 	char *pg_name = NULL;
2718 	int err;
2719 	int ret;
2720 	ssize_t size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2721 	uint8_t required;
2722 	scf_handle_t *h;
2723 	scf_propertygroup_t *pg = NULL;
2724 	scf_iter_t *iter = NULL;
2725 
2726 	assert(size != 0);
2727 	if (t == NULL || pt == NULL) {
2728 		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2729 		return (-1);
2730 	}
2731 
2732 	assert(t->pt_inst == NULL || t->pt_svc == NULL);
2733 	assert(t->pt_inst != NULL || t->pt_svc != NULL);
2734 
2735 	if ((pg_name = malloc(size)) == NULL) {
2736 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2737 		return (-1);
2738 	}
2739 
2740 	if (pt->prt_populated == 0) {
2741 		if ((h = scf_pg_handle(t->pt_pg)) == NULL)
2742 			goto fail_non_populated;
2743 
2744 		if ((pg = scf_pg_create(h)) == NULL ||
2745 		    (iter = scf_iter_create(h)) == NULL)
2746 			goto fail_non_populated;
2747 
2748 		if (t->pt_inst != NULL)
2749 			err = scf_iter_instance_pgs_typed_composed(iter,
2750 			    t->pt_inst, t->pt_snap,
2751 			    SCF_GROUP_TEMPLATE_PROP_PATTERN);
2752 		else if (t->pt_svc != NULL)
2753 			err = scf_iter_service_pgs_typed(iter, t->pt_svc,
2754 			    SCF_GROUP_TEMPLATE_PROP_PATTERN);
2755 
2756 		if (err != 0) {
2757 			if (ismember(scf_error(), errors_server)) {
2758 				goto fail_non_populated;
2759 			} else switch (scf_error()) {
2760 			case SCF_ERROR_INVALID_ARGUMENT:
2761 				goto fail_non_populated;
2762 
2763 			case SCF_ERROR_NOT_SET:
2764 			case SCF_ERROR_HANDLE_MISMATCH:
2765 			default:
2766 				assert(0);
2767 				abort();
2768 			}
2769 
2770 		}
2771 		prop_tmpl = pt;
2772 		prop_tmpl->prt_t = t;
2773 		prop_tmpl->prt_populated = 1;
2774 		prop_tmpl->prt_pg = pg;
2775 		prop_tmpl->prt_iter = iter;
2776 	} else {
2777 		prop_tmpl = pt;
2778 	}
2779 
2780 	while ((err = scf_iter_next_pg(prop_tmpl->prt_iter,
2781 	    prop_tmpl->prt_pg)) > 0) {
2782 		/*
2783 		 * Check if the name matches the appropriate property
2784 		 * group template name.
2785 		 */
2786 		pg_pat = _scf_read_single_astring_from_pg(prop_tmpl->prt_pg,
2787 		    SCF_PROPERTY_TM_PG_PATTERN);
2788 		if (pg_pat == NULL) {
2789 			if (ismember(scf_error(), errors_server)) {
2790 				uu_free(pg_name);
2791 				return (-1);
2792 			} else switch (scf_error()) {
2793 			case SCF_ERROR_NOT_FOUND:
2794 				continue;
2795 
2796 			case SCF_ERROR_CONSTRAINT_VIOLATED:
2797 			case SCF_ERROR_TYPE_MISMATCH:
2798 				(void) scf_set_error(
2799 				    SCF_ERROR_TEMPLATE_INVALID);
2800 				free(pg_name);
2801 				return (-1);
2802 
2803 			case SCF_ERROR_INVALID_ARGUMENT:
2804 			case SCF_ERROR_NOT_SET:
2805 			default:
2806 				assert(0);
2807 				abort();
2808 			}
2809 		}
2810 		if ((ret = scf_pg_get_name(t->pt_pg, pg_name, size)) <= 0) {
2811 			free(pg_pat);
2812 			if (ret == 0)
2813 				continue;
2814 
2815 			if (ismember(scf_error(), errors_server)) {
2816 				free(pg_name);
2817 				return (-1);
2818 			} else {
2819 				assert(0);
2820 				abort();
2821 			}
2822 		}
2823 		if (strcmp(pg_pat, pg_name) != 0) {
2824 			free(pg_pat);
2825 			continue;
2826 		}
2827 		free(pg_pat);
2828 
2829 		/*
2830 		 * If walking only required properties, check if
2831 		 * the retrieved property is required.
2832 		 */
2833 		if (flags & SCF_PROP_TMPL_FLAG_REQUIRED) {
2834 			if (scf_tmpl_prop_required(prop_tmpl, &required) == 0) {
2835 				if (required == 0)
2836 					continue;
2837 			} else {
2838 				free(pg_name);
2839 				return (-1);
2840 			}
2841 		}
2842 		free(pg_name);
2843 		return (0);
2844 	}
2845 
2846 	if (err == -1) {
2847 		if (ismember(scf_error(), errors_server)) {
2848 			free(pg_name);
2849 			return (-1);
2850 		} else {
2851 			assert(0);
2852 			abort();
2853 		}
2854 	} else if (err == 0)  {
2855 		scf_iter_destroy(prop_tmpl->prt_iter);
2856 		prop_tmpl->prt_iter = NULL;
2857 		prop_tmpl->prt_populated = 0;
2858 	}
2859 	free(pg_name);
2860 
2861 	return (1);
2862 
2863 fail_non_populated:
2864 	free(pg_name);
2865 	scf_pg_destroy(pg);
2866 	scf_iter_destroy(iter);
2867 	return (-1);
2868 }
2869 
2870 void
scf_tmpl_prop_destroy(scf_prop_tmpl_t * t)2871 scf_tmpl_prop_destroy(scf_prop_tmpl_t *t)
2872 {
2873 	if (t == NULL)
2874 		return;
2875 
2876 	scf_pg_destroy(t->prt_pg);
2877 	free(t->prt_pg_name);
2878 	free(t->prt_iter);
2879 	free(t);
2880 }
2881 
2882 void
scf_tmpl_prop_reset(scf_prop_tmpl_t * t)2883 scf_tmpl_prop_reset(scf_prop_tmpl_t *t)
2884 {
2885 	scf_pg_destroy(t->prt_pg);
2886 	t->prt_pg = NULL;
2887 
2888 	free(t->prt_pg_name);
2889 	t->prt_pg_name = NULL;
2890 
2891 	free(t->prt_iter);
2892 	t->prt_iter = NULL;
2893 
2894 	t->prt_populated = 0;
2895 	t->prt_h = NULL;
2896 	t->prt_t = NULL;
2897 }
2898 
2899 /*
2900  * Returns -1 on failure.  Sets scf_error():
2901  *   SCF_ERROR_BACKEND_ACCESS
2902  *   SCF_ERROR_CONNECTION_BROKEN
2903  *   SCF_ERROR_DELETED
2904  *   SCF_ERROR_HANDLE_DESTROYED
2905  *   SCF_ERROR_INTERNAL
2906  *   SCF_ERROR_NO_MEMORY
2907  *   SCF_ERROR_NO_RESOURCES
2908  *   SCF_ERROR_NOT_BOUND
2909  *   SCF_ERROR_PERMISSION_DENIED
2910  *   SCF_ERROR_TEMPLATE_INVALID
2911  *     The name of the template property group (the pname property) has
2912  *     an improper repository format and is not type astring or has
2913  *     more than one value.
2914  */
2915 ssize_t
scf_tmpl_pg_name(const scf_pg_tmpl_t * t,char ** out)2916 scf_tmpl_pg_name(const scf_pg_tmpl_t *t, char **out)
2917 {
2918 	return (_scf_tmpl_prop_value(t->pt_pg, SCF_PROPERTY_TM_NAME, out));
2919 }
2920 
2921 /*
2922  * returns an allocated string that must be freed
2923  *
2924  * Returns NULL on failure, sets scf_error() to:
2925  *   SCF_ERROR_BACKEND_ACCESS
2926  *   SCF_ERROR_CONNECTION_BROKEN
2927  *   SCF_ERROR_DELETED
2928  *   SCF_ERROR_HANDLE_DESTROYED
2929  *   SCF_ERROR_INTERNAL
2930  *   SCF_ERROR_INVALID_ARGUMENT
2931  *     name not a valid property name
2932  *     name and locale are too long to make a property name
2933  *   SCF_ERROR_NO_MEMORY
2934  *   SCF_ERROR_NO_RESOURCES
2935  *   SCF_ERROR_NOT_BOUND
2936  *   SCF_ERROR_NOT_FOUND
2937  *     Property doesn't exist or exists and has no value.
2938  *   SCF_ERROR_PERMISSION_DENIED
2939  *   SCF_ERROR_TEMPLATE_INVALID
2940  */
2941 static char *
_read_localized_astring_from_pg(scf_propertygroup_t * pg,const char * name,const char * locale)2942 _read_localized_astring_from_pg(scf_propertygroup_t *pg, const char *name,
2943     const char *locale)
2944 {
2945 	char *str;
2946 	char *lname_prop;
2947 
2948 	str = _add_locale_to_name(name, locale);
2949 	if (str == NULL)
2950 		return (NULL);
2951 	lname_prop = _scf_read_single_astring_from_pg(pg, str);
2952 	if (lname_prop == NULL) {
2953 		free(str);
2954 		if (scf_error() != SCF_ERROR_NOT_FOUND)
2955 			return (NULL);
2956 		str = _add_locale_to_name(name, "C");
2957 		if (str == NULL)
2958 			return (NULL);
2959 		lname_prop = _scf_read_single_astring_from_pg(pg, str);
2960 	}
2961 	free(str);
2962 	if (lname_prop == NULL) {
2963 		if (scf_error() == SCF_ERROR_TYPE_MISMATCH ||
2964 		    scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
2965 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2966 	}
2967 	return (lname_prop);
2968 }
2969 
2970 /*
2971  * returns an allocated string that must be freed
2972  *
2973  * Returns -1 on failure, sets scf_error() to:
2974  *   SCF_ERROR_BACKEND_ACCESS
2975  *   SCF_ERROR_CONNECTION_BROKEN
2976  *   SCF_ERROR_DELETED
2977  *   SCF_ERROR_HANDLE_DESTROYED
2978  *   SCF_ERROR_INTERNAL
2979  *   SCF_ERROR_INVALID_ARGUMENT
2980  *     locale is too long to make a valid property name
2981  *   SCF_ERROR_NO_MEMORY
2982  *   SCF_ERROR_NO_RESOURCES
2983  *   SCF_ERROR_NOT_BOUND
2984  *   SCF_ERROR_NOT_FOUND
2985  *     Property doesn't exist or exists and has no value.
2986  *   SCF_ERROR_PERMISSION_DENIED
2987  *   SCF_ERROR_TEMPLATE_INVALID
2988  */
2989 ssize_t
scf_tmpl_pg_common_name(const scf_pg_tmpl_t * t,const char * locale,char ** out)2990 scf_tmpl_pg_common_name(const scf_pg_tmpl_t *t, const char *locale, char **out)
2991 {
2992 	assert(out != NULL);
2993 	if ((*out = _read_localized_astring_from_pg(t->pt_pg,
2994 	    SCF_PROPERTY_TM_COMMON_NAME_PREFIX, locale)) == NULL)
2995 		return (-1);
2996 
2997 	return (strlen(*out));
2998 }
2999 
3000 /*
3001  * returns an allocated string that must be freed
3002  *
3003  * Returns -1 on failure, sets scf_error() to:
3004  *   SCF_ERROR_BACKEND_ACCESS
3005  *   SCF_ERROR_CONNECTION_BROKEN
3006  *   SCF_ERROR_DELETED
3007  *   SCF_ERROR_HANDLE_DESTROYED
3008  *   SCF_ERROR_INTERNAL
3009  *   SCF_ERROR_INVALID_ARGUMENT
3010  *     locale is too long to make a valid property name
3011  *   SCF_ERROR_NO_MEMORY
3012  *   SCF_ERROR_NO_RESOURCES
3013  *   SCF_ERROR_NOT_BOUND
3014  *   SCF_ERROR_NOT_FOUND
3015  *     Property doesn't exist or exists and has no value.
3016  *   SCF_ERROR_PERMISSION_DENIED
3017  *   SCF_ERROR_TEMPLATE_INVALID
3018  */
3019 ssize_t
scf_tmpl_pg_description(const scf_pg_tmpl_t * t,const char * locale,char ** out)3020 scf_tmpl_pg_description(const scf_pg_tmpl_t *t, const char *locale, char **out)
3021 {
3022 	assert(out != NULL);
3023 	if ((*out = _read_localized_astring_from_pg(t->pt_pg,
3024 	    SCF_PROPERTY_TM_DESCRIPTION_PREFIX, locale)) == NULL)
3025 		return (-1);
3026 
3027 	return (strlen(*out));
3028 }
3029 
3030 /*
3031  * Returns -1 on failure.  Sets scf_error():
3032  *   SCF_ERROR_BACKEND_ACCESS
3033  *   SCF_ERROR_CONNECTION_BROKEN
3034  *   SCF_ERROR_DELETED
3035  *   SCF_ERROR_HANDLE_DESTROYED
3036  *   SCF_ERROR_INTERNAL
3037  *   SCF_ERROR_NO_MEMORY
3038  *   SCF_ERROR_NO_RESOURCES
3039  *   SCF_ERROR_NOT_BOUND
3040  *   SCF_ERROR_NOT_FOUND
3041  *     'type' property doesn't exist or exists and has no value.
3042  *   SCF_ERROR_PERMISSION_DENIED
3043  *   SCF_ERROR_TEMPLATE_INVALID
3044  *     'type' property is not SCF_TYPE_ASTRING or has more than one value.
3045  */
3046 ssize_t
scf_tmpl_pg_type(const scf_pg_tmpl_t * t,char ** out)3047 scf_tmpl_pg_type(const scf_pg_tmpl_t *t, char **out)
3048 {
3049 	return (_scf_tmpl_prop_value(t->pt_pg, SCF_PROPERTY_TM_TYPE, out));
3050 }
3051 
3052 /*
3053  * Returns -1 on failure, sets scf_error() to:
3054  *   SCF_ERROR_BACKEND_ACCESS
3055  *   SCF_ERROR_CONNECTION_BROKEN
3056  *   SCF_ERROR_DELETED
3057  *   SCF_ERROR_HANDLE_DESTROYED
3058  *   SCF_ERROR_INTERNAL
3059  *   SCF_ERROR_NO_MEMORY
3060  *   SCF_ERROR_NO_RESOURCES
3061  *   SCF_ERROR_NOT_BOUND
3062  *   SCF_ERROR_PERMISSION_DENIED
3063  *   SCF_ERROR_TEMPLATE_INVALID
3064  *     required property is not SCF_TYPE_BOOLEAN or has more than one value.
3065  */
3066 int
scf_tmpl_pg_required(const scf_pg_tmpl_t * t,uint8_t * out)3067 scf_tmpl_pg_required(const scf_pg_tmpl_t *t, uint8_t *out)
3068 {
3069 
3070 	if (_read_single_boolean_from_pg(t->pt_pg, SCF_PROPERTY_TM_REQUIRED,
3071 	    out) == -1) {
3072 		if (ismember(scf_error(), errors_server)) {
3073 			return (-1);
3074 		} else switch (scf_error()) {
3075 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3076 		case SCF_ERROR_TYPE_MISMATCH:
3077 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3078 			return (-1);
3079 
3080 		case SCF_ERROR_NOT_FOUND:
3081 			*out = 0;
3082 			return (0);
3083 
3084 		case SCF_ERROR_INVALID_ARGUMENT:
3085 		default:
3086 			assert(0);
3087 			abort();
3088 		}
3089 	}
3090 
3091 	return (0);
3092 }
3093 
3094 /*
3095  * Returns -1 on failure.  Sets scf_error():
3096  *   SCF_ERROR_BACKEND_ACCESS
3097  *   SCF_ERROR_CONNECTION_BROKEN
3098  *   SCF_ERROR_DELETED
3099  *   SCF_ERROR_HANDLE_DESTROYED
3100  *   SCF_ERROR_INTERNAL
3101  *   SCF_ERROR_NO_MEMORY
3102  *   SCF_ERROR_NO_RESOURCES
3103  *   SCF_ERROR_NOT_BOUND
3104  *   SCF_ERROR_PERMISSION_DENIED
3105  *   SCF_ERROR_TEMPLATE_INVALID
3106  *     target property is not SCF_TYPE_ASTRING or has more than one value.
3107  */
3108 ssize_t
scf_tmpl_pg_target(const scf_pg_tmpl_t * t,char ** out)3109 scf_tmpl_pg_target(const scf_pg_tmpl_t *t, char **out)
3110 {
3111 	*out = _scf_read_single_astring_from_pg(t->pt_pg,
3112 	    SCF_PROPERTY_TM_TARGET);
3113 
3114 	if (*out == NULL) {
3115 		if (ismember(scf_error(), errors_server)) {
3116 			return (-1);
3117 		} else switch (scf_error()) {
3118 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3119 		case SCF_ERROR_NOT_FOUND:
3120 		case SCF_ERROR_TYPE_MISMATCH:
3121 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3122 			return (-1);
3123 
3124 		case SCF_ERROR_INVALID_ARGUMENT:
3125 		case SCF_ERROR_NOT_SET:
3126 		default:
3127 			assert(0);
3128 			abort();
3129 		}
3130 	}
3131 
3132 	return (strlen(*out));
3133 }
3134 
3135 /*
3136  * Returns -1 on failure.  Sets scf_error():
3137  *   SCF_ERROR_BACKEND_ACCESS
3138  *   SCF_ERROR_CONNECTION_BROKEN
3139  *   SCF_ERROR_DELETED
3140  *   SCF_ERROR_HANDLE_DESTROYED
3141  *   SCF_ERROR_INTERNAL
3142  *   SCF_ERROR_NO_MEMORY
3143  *   SCF_ERROR_NO_RESOURCES
3144  *   SCF_ERROR_NOT_BOUND
3145  *   SCF_ERROR_PERMISSION_DENIED
3146  *   SCF_ERROR_TEMPLATE_INVALID
3147  */
3148 ssize_t
scf_tmpl_prop_name(const scf_prop_tmpl_t * t,char ** out)3149 scf_tmpl_prop_name(const scf_prop_tmpl_t *t, char **out)
3150 {
3151 	*out = _scf_read_single_astring_from_pg(t->prt_pg,
3152 	    SCF_PROPERTY_TM_NAME);
3153 
3154 	if (*out != NULL && *out[0] == '\0') {
3155 		free(*out);
3156 		*out = NULL;
3157 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3158 	}
3159 	if (*out == NULL) {
3160 		if (ismember(scf_error(), errors_server)) {
3161 			return (-1);
3162 		} else switch (scf_error()) {
3163 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3164 		case SCF_ERROR_NOT_FOUND:
3165 		case SCF_ERROR_TEMPLATE_INVALID:
3166 		case SCF_ERROR_TYPE_MISMATCH:
3167 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3168 			return (-1);
3169 
3170 		case SCF_ERROR_INVALID_ARGUMENT:
3171 		case SCF_ERROR_NOT_SET:
3172 		default:
3173 			assert(0);
3174 			abort();
3175 		}
3176 	}
3177 
3178 	return (strlen(*out));
3179 }
3180 
3181 /*
3182  * Returns -1 on failure.  Sets scf_error():
3183  *   SCF_ERROR_BACKEND_ACCESS
3184  *   SCF_ERROR_CONNECTION_BROKEN
3185  *   SCF_ERROR_DELETED
3186  *   SCF_ERROR_HANDLE_DESTROYED
3187  *   SCF_ERROR_INTERNAL
3188  *   SCF_ERROR_NO_MEMORY
3189  *   SCF_ERROR_NO_RESOURCES
3190  *   SCF_ERROR_NOT_BOUND
3191  *   SCF_ERROR_NOT_FOUND
3192  *     'type' property doesn't exist or exists and has no value.
3193  *   SCF_ERROR_PERMISSION_DENIED
3194  *   SCF_ERROR_TEMPLATE_INVALID
3195  *     'type' property is not SCF_TYPE_ASTRING, has more than one value,
3196  *     is SCF_TYPE_INVALID, or is the empty string.
3197  */
3198 int
scf_tmpl_prop_type(const scf_prop_tmpl_t * t,scf_type_t * out)3199 scf_tmpl_prop_type(const scf_prop_tmpl_t *t, scf_type_t *out)
3200 {
3201 	char *type;
3202 
3203 	type = _scf_read_single_astring_from_pg(t->prt_pg,
3204 	    SCF_PROPERTY_TM_TYPE);
3205 	if (type != NULL && type[0] == '\0') {
3206 		free(type);
3207 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3208 		return (-1);
3209 	}
3210 	if (type == NULL) {
3211 		if (ismember(scf_error(), errors_server)) {
3212 			return (-1);
3213 		} else switch (scf_error()) {
3214 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3215 		case SCF_ERROR_TYPE_MISMATCH:
3216 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3217 			/*FALLTHROUGH*/
3218 
3219 		case SCF_ERROR_NOT_FOUND:
3220 			return (-1);
3221 
3222 		case SCF_ERROR_INVALID_ARGUMENT:
3223 		case SCF_ERROR_NOT_SET:
3224 		default:
3225 			assert(0);
3226 			abort();
3227 		}
3228 	}
3229 
3230 	*out = scf_string_to_type(type);
3231 	free(type);
3232 
3233 	if (*out == SCF_TYPE_INVALID) {
3234 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3235 		return (-1);
3236 	}
3237 
3238 	return (0);
3239 }
3240 
3241 /*
3242  * Returns -1 on failure, sets scf_error() to:
3243  *   SCF_ERROR_BACKEND_ACCESS
3244  *   SCF_ERROR_CONNECTION_BROKEN
3245  *   SCF_ERROR_DELETED
3246  *    Property group which represents t was deleted.
3247  *   SCF_ERROR_HANDLE_DESTROYED
3248  *   SCF_ERROR_INTERNAL
3249  *   SCF_ERROR_NO_MEMORY
3250  *   SCF_ERROR_NO_RESOURCES
3251  *   SCF_ERROR_NOT_BOUND
3252  *   SCF_ERROR_PERMISSION_DENIED
3253  *   SCF_ERROR_TEMPLATE_INVALID
3254  *     required property is not SCF_TYPE_ASTRING has more than one value.
3255  */
3256 int
scf_tmpl_prop_required(const scf_prop_tmpl_t * t,uint8_t * out)3257 scf_tmpl_prop_required(const scf_prop_tmpl_t *t, uint8_t *out)
3258 {
3259 	if (_read_single_boolean_from_pg(t->prt_pg, SCF_PROPERTY_TM_REQUIRED,
3260 	    out) == -1) {
3261 		if (ismember(scf_error(), errors_server)) {
3262 			return (-1);
3263 		} else switch (scf_error()) {
3264 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3265 		case SCF_ERROR_TYPE_MISMATCH:
3266 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3267 			return (-1);
3268 
3269 		case SCF_ERROR_NOT_FOUND:
3270 			*out = 0;
3271 			return (0);
3272 
3273 		case SCF_ERROR_INVALID_ARGUMENT:
3274 		case SCF_ERROR_NOT_SET:
3275 		default:
3276 			assert(0);
3277 			abort();
3278 		}
3279 	}
3280 
3281 	return (0);
3282 }
3283 
3284 /*
3285  * Returns -1 on failure.  Sets scf_error():
3286  *   SCF_ERROR_BACKEND_ACCESS
3287  *   SCF_ERROR_CONNECTION_BROKEN
3288  *   SCF_ERROR_DELETED
3289  *   SCF_ERROR_HANDLE_DESTROYED
3290  *   SCF_ERROR_INTERNAL
3291  *   SCF_ERROR_NO_MEMORY
3292  *   SCF_ERROR_NO_RESOURCES
3293  *   SCF_ERROR_NOT_BOUND
3294  *   SCF_ERROR_NOT_FOUND
3295  *     Property doesn't exist or exists and has no value.
3296  *   SCF_ERROR_INVALID_ARGUMENT
3297  *     locale is too long to make a property name
3298  *   SCF_ERROR_PERMISSION_DENIED
3299  *   SCF_ERROR_TEMPLATE_INVALID
3300  *     common_name property is not SCF_TYPE_ASTRING has more than one value.
3301  */
3302 ssize_t
scf_tmpl_prop_common_name(const scf_prop_tmpl_t * t,const char * locale,char ** out)3303 scf_tmpl_prop_common_name(const scf_prop_tmpl_t *t, const char *locale,
3304     char **out)
3305 {
3306 	assert(out != NULL);
3307 	if ((*out = _read_localized_astring_from_pg(t->prt_pg,
3308 	    SCF_PROPERTY_TM_COMMON_NAME_PREFIX, locale)) == NULL)
3309 		return (-1);
3310 
3311 	return (strlen(*out));
3312 }
3313 
3314 /*
3315  * Returns -1 on failure.  Sets scf_error():
3316  *   SCF_ERROR_BACKEND_ACCESS
3317  *   SCF_ERROR_CONNECTION_BROKEN
3318  *   SCF_ERROR_DELETED
3319  *   SCF_ERROR_HANDLE_DESTROYED
3320  *   SCF_ERROR_INTERNAL
3321  *   SCF_ERROR_NO_MEMORY
3322  *   SCF_ERROR_NO_RESOURCES
3323  *   SCF_ERROR_NOT_BOUND
3324  *   SCF_ERROR_NOT_FOUND
3325  *     Property doesn't exist or exists and has no value.
3326  *   SCF_ERROR_INVALID_ARGUMENT
3327  *     locale is too long to make a property name
3328  *   SCF_ERROR_PERMISSION_DENIED
3329  *   SCF_ERROR_TEMPLATE_INVALID
3330  *     description property is not SCF_TYPE_ASTRING has more than one value.
3331  */
3332 ssize_t
scf_tmpl_prop_description(const scf_prop_tmpl_t * t,const char * locale,char ** out)3333 scf_tmpl_prop_description(const scf_prop_tmpl_t *t, const char *locale,
3334     char **out)
3335 {
3336 	assert(out != NULL);
3337 	if ((*out = _read_localized_astring_from_pg(t->prt_pg,
3338 	    SCF_PROPERTY_TM_DESCRIPTION_PREFIX, locale)) == NULL)
3339 		return (-1);
3340 
3341 	return (strlen(*out));
3342 }
3343 
3344 /*
3345  * Returns -1 on failure.  Sets scf_error():
3346  *   SCF_ERROR_BACKEND_ACCESS
3347  *   SCF_ERROR_CONNECTION_BROKEN
3348  *   SCF_ERROR_DELETED
3349  *   SCF_ERROR_HANDLE_DESTROYED
3350  *   SCF_ERROR_INTERNAL
3351  *   SCF_ERROR_NO_MEMORY
3352  *   SCF_ERROR_NO_RESOURCES
3353  *   SCF_ERROR_NOT_BOUND
3354  *   SCF_ERROR_NOT_FOUND
3355  *     Property doesn't exist or exists and has no value.
3356  *   SCF_ERROR_INVALID_ARGUMENT
3357  *     locale is too long to make a property name
3358  *   SCF_ERROR_PERMISSION_DENIED
3359  *   SCF_ERROR_TEMPLATE_INVALID
3360  *     units property is not SCF_TYPE_ASTRING has more than one value.
3361  */
3362 ssize_t
scf_tmpl_prop_units(const scf_prop_tmpl_t * t,const char * locale,char ** out)3363 scf_tmpl_prop_units(const scf_prop_tmpl_t *t, const char *locale, char **out)
3364 {
3365 	assert(out != NULL);
3366 	if ((*out = _read_localized_astring_from_pg(t->prt_pg,
3367 	    SCF_PROPERTY_TM_UNITS_PREFIX, locale)) == NULL)
3368 		return (-1);
3369 
3370 	return (strlen(*out));
3371 }
3372 
3373 /*
3374  * Returns -1 on failure.  Sets scf_error():
3375  *   SCF_ERROR_BACKEND_ACCESS
3376  *   SCF_ERROR_CONNECTION_BROKEN
3377  *   SCF_ERROR_DELETED
3378  *   SCF_ERROR_HANDLE_DESTROYED
3379  *   SCF_ERROR_INTERNAL
3380  *   SCF_ERROR_NO_MEMORY
3381  *   SCF_ERROR_NO_RESOURCES
3382  *   SCF_ERROR_NOT_BOUND
3383  *   SCF_ERROR_PERMISSION_DENIED
3384  *   SCF_ERROR_TEMPLATE_INVALID
3385  *     visibility property is not SCF_TYPE_ASTRING has more than one value.
3386  */
3387 int
scf_tmpl_prop_visibility(const scf_prop_tmpl_t * t,uint8_t * out)3388 scf_tmpl_prop_visibility(const scf_prop_tmpl_t *t, uint8_t *out)
3389 {
3390 	char *visibility;
3391 
3392 	visibility = _scf_read_single_astring_from_pg(t->prt_pg,
3393 	    SCF_PROPERTY_TM_VISIBILITY);
3394 	if (visibility == NULL) {
3395 		if (ismember(scf_error(), errors_server)) {
3396 			return (-1);
3397 		} else switch (scf_error()) {
3398 		/* prop doesn't exist we take the default value */
3399 		case SCF_ERROR_NOT_FOUND:
3400 			*out = SCF_TMPL_VISIBILITY_READWRITE;
3401 			return (0);
3402 
3403 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3404 		case SCF_ERROR_TYPE_MISMATCH:
3405 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3406 			return (-1);
3407 
3408 		case SCF_ERROR_INVALID_ARGUMENT:
3409 		case SCF_ERROR_NOT_SET:
3410 		default:
3411 			assert(0);
3412 			abort();
3413 		}
3414 	} else if (strcmp(visibility, SCF_TM_VISIBILITY_READWRITE) == 0) {
3415 		*out = SCF_TMPL_VISIBILITY_READWRITE;
3416 	} else if (strcmp(visibility, SCF_TM_VISIBILITY_HIDDEN) == 0) {
3417 		*out = SCF_TMPL_VISIBILITY_HIDDEN;
3418 	} else if (strcmp(visibility, SCF_TM_VISIBILITY_READONLY) == 0) {
3419 		*out = SCF_TMPL_VISIBILITY_READONLY;
3420 	} else {
3421 		free(visibility);
3422 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3423 		return (-1);
3424 	}
3425 
3426 	free(visibility);
3427 	return (0);
3428 }
3429 
3430 /*
3431  * Return an allocated string containing the value that must be freed
3432  * with free().
3433  *
3434  * On error set scf_error() _NO_MEMORY, or _NOT_SET (val has not been set
3435  * to a value).
3436  */
3437 static char *
_scf_value_get_as_string(scf_value_t * val)3438 _scf_value_get_as_string(scf_value_t *val)
3439 {
3440 	ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
3441 	char *buf = malloc(sz);
3442 
3443 	if (buf == NULL) {
3444 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3445 	} else if (scf_value_get_as_string(val, buf, sz) == -1) {
3446 		free(buf);
3447 		buf = NULL;
3448 	}
3449 
3450 	return (buf);
3451 }
3452 
3453 /*
3454  * Returns -1 on failure, sets scf_error() to:
3455  *   SCF_ERROR_BACKEND_ACCESS
3456  *   SCF_ERROR_CONNECTION_BROKEN
3457  *   SCF_ERROR_DELETED
3458  *   SCF_ERROR_HANDLE_DESTROYED
3459  *   SCF_ERROR_INTERNAL
3460  *   SCF_ERROR_NO_MEMORY
3461  *   SCF_ERROR_NO_RESOURCES
3462  *   SCF_ERROR_NOT_BOUND
3463  *   SCF_ERROR_NOT_FOUND
3464  *   SCF_ERROR_PERMISSION_DENIED
3465  *   SCF_ERROR_TEMPLATE_INVALID
3466  */
3467 int
scf_tmpl_prop_cardinality(const scf_prop_tmpl_t * t,uint64_t * min,uint64_t * max)3468 scf_tmpl_prop_cardinality(const scf_prop_tmpl_t *t, uint64_t *min,
3469     uint64_t *max)
3470 {
3471 	scf_value_t *val_min = NULL;
3472 	scf_value_t *val_max = NULL;
3473 	int ret = 0;
3474 
3475 	if (_read_single_value_from_pg(t->prt_pg,
3476 	    SCF_PROPERTY_TM_CARDINALITY_MIN, &val_min) == 0) {
3477 		if (scf_value_get_count(val_min, min) < 0)
3478 			goto error;
3479 	} else {
3480 		if (scf_error() == SCF_ERROR_NOT_FOUND)
3481 			*min = 0;
3482 		else
3483 			goto error;
3484 	}
3485 
3486 	if (_read_single_value_from_pg(t->prt_pg,
3487 	    SCF_PROPERTY_TM_CARDINALITY_MAX, &val_max) == 0) {
3488 		if (scf_value_get_count(val_max, max) < 0)
3489 			goto error;
3490 	} else {
3491 		if (scf_error() == SCF_ERROR_NOT_FOUND)
3492 			*max = UINT64_MAX;
3493 		else
3494 			goto error;
3495 	}
3496 	goto cleanup;
3497 
3498 error:
3499 	if (ismember(scf_error(), errors_server)) {
3500 		ret = -1;
3501 	} else switch (scf_error()) {
3502 	case SCF_ERROR_TYPE_MISMATCH:
3503 	case SCF_ERROR_CONSTRAINT_VIOLATED:
3504 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3505 		/*FALLTHROUGH*/
3506 
3507 	case SCF_ERROR_NOT_FOUND:
3508 	case SCF_ERROR_TEMPLATE_INVALID:
3509 		ret = -1;
3510 		break;
3511 
3512 	case SCF_ERROR_NOT_SET:
3513 	case SCF_ERROR_INVALID_ARGUMENT:
3514 	default:
3515 		assert(0);
3516 		abort();
3517 	}
3518 
3519 cleanup:
3520 	scf_value_destroy(val_min);
3521 	scf_value_destroy(val_max);
3522 
3523 	return (ret);
3524 }
3525 
3526 /*
3527  * Returns -1 on failure.  Sets scf_error():
3528  *   SCF_ERROR_BACKEND_ACCESS
3529  *   SCF_ERROR_CONNECTION_BROKEN
3530  *   SCF_ERROR_DELETED
3531  *   SCF_ERROR_HANDLE_DESTROYED
3532  *   SCF_ERROR_INTERNAL
3533  *   SCF_ERROR_NO_MEMORY
3534  *   SCF_ERROR_NO_RESOURCES
3535  *   SCF_ERROR_NOT_BOUND
3536  *   SCF_ERROR_NOT_FOUND
3537  *     Property doesn't exist or exists and has no value.
3538  *   SCF_ERROR_PERMISSION_DENIED
3539  *   SCF_ERROR_TEMPLATE_INVALID
3540  */
3541 int
scf_tmpl_prop_internal_seps(const scf_prop_tmpl_t * t,scf_values_t * vals)3542 scf_tmpl_prop_internal_seps(const scf_prop_tmpl_t *t, scf_values_t *vals)
3543 {
3544 	if (_read_astrings_values(t->prt_pg,
3545 	    SCF_PROPERTY_INTERNAL_SEPARATORS, vals) == NULL) {
3546 		if (ismember(scf_error(), errors_server)) {
3547 			return (-1);
3548 		} else switch (scf_error()) {
3549 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3550 		case SCF_ERROR_TYPE_MISMATCH:
3551 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3552 			/*FALLTHROUGH*/
3553 
3554 		case SCF_ERROR_NOT_FOUND:
3555 			return (-1);
3556 
3557 		case SCF_ERROR_INVALID_ARGUMENT:
3558 		case SCF_ERROR_NOT_SET:
3559 		default:
3560 			assert(0);
3561 			abort();
3562 		}
3563 	} else if (vals->value_count == 0) {
3564 		/* property has no value */
3565 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3566 		scf_values_destroy(vals);
3567 		return (-1);
3568 	}
3569 
3570 	return (0);
3571 }
3572 
3573 /*
3574  * Returns -1 on failure.  Sets scf_error():
3575  *   SCF_ERROR_BACKEND_ACCESS
3576  *   SCF_ERROR_CONNECTION_BROKEN
3577  *   SCF_ERROR_DELETED
3578  *   SCF_ERROR_HANDLE_DESTROYED
3579  *   SCF_ERROR_INTERNAL
3580  *   SCF_ERROR_NO_MEMORY
3581  *   SCF_ERROR_NO_RESOURCES
3582  *   SCF_ERROR_NOT_BOUND
3583  *   SCF_ERROR_NOT_FOUND
3584  *     Property doesn't exist or exists and has no value.
3585  *   SCF_ERROR_PERMISSION_DENIED
3586  *   SCF_ERROR_TEMPLATE_INVALID
3587  */
3588 int
scf_tmpl_value_name_constraints(const scf_prop_tmpl_t * t,scf_values_t * vals)3589 scf_tmpl_value_name_constraints(const scf_prop_tmpl_t *t,
3590     scf_values_t *vals)
3591 {
3592 	char **ret;
3593 
3594 	ret = _read_astrings_values(t->prt_pg,
3595 	    SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
3596 
3597 	if (ret == NULL) {
3598 		if (ismember(scf_error(), errors_server)) {
3599 			return (-1);
3600 		} else switch (scf_error()) {
3601 		case SCF_ERROR_CONSTRAINT_VIOLATED:
3602 		case SCF_ERROR_TYPE_MISMATCH:
3603 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3604 			/*FALLTHROUGH*/
3605 
3606 		case SCF_ERROR_NOT_FOUND:
3607 			return (-1);
3608 
3609 		case SCF_ERROR_INVALID_ARGUMENT:
3610 		case SCF_ERROR_NOT_SET:
3611 		default:
3612 			assert(0);
3613 			abort();
3614 		}
3615 	} else if (vals->value_count == 0) {
3616 		/* property has no value */
3617 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3618 		scf_values_destroy(vals);
3619 		return (-1);
3620 	}
3621 
3622 	return (0);
3623 }
3624 
3625 /*
3626  * Returns NULL on failure.  Sets scf_error():
3627  * Caller is responsible for freeing returned pointer after use.
3628  *   SCF_ERROR_CONSTRAINT_VIOLATED
3629  *    More tokens than the array size supplied.
3630  *   SCF_ERROR_NO_MEMORY
3631  */
3632 static void *
_separate_by_separator(char * string,const char * sep,char ** array,int size)3633 _separate_by_separator(char *string, const char *sep, char **array, int size)
3634 {
3635 	char *str, *token;
3636 	char *lasts;
3637 	int n = 0;
3638 
3639 	assert(array != NULL);
3640 	assert(string != NULL);
3641 	assert(sep != NULL);
3642 	assert(size > 0);
3643 
3644 	str = strdup(string);
3645 	if (str == NULL) {
3646 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3647 		return (NULL);
3648 	}
3649 
3650 	if ((array[n] = strtok_r(str, sep, &lasts)) == NULL) {
3651 		assert(0);
3652 		abort();
3653 	}
3654 
3655 	n++;
3656 	while ((token = strtok_r(NULL, sep, &lasts)) != NULL) {
3657 		if (n >= size) {
3658 			goto error;
3659 		}
3660 		array[n] = token;
3661 		n++;
3662 	}
3663 	if (n < size) {
3664 		goto error;
3665 	}
3666 
3667 	return (str);
3668 error:
3669 	free(str);
3670 	(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3671 	return (NULL);
3672 }
3673 
3674 /*
3675  * check if name is among values of CHOICES_INCLUDE_VALUES
3676  * return 0 if name is present, 1 name is not present, -1 on failure
3677  *   SCF_ERROR_BACKEND_ACCESS
3678  *   SCF_ERROR_CONNECTION_BROKEN
3679  *   SCF_ERROR_DELETED
3680  *   SCF_ERROR_HANDLE_DESTROYED
3681  *   SCF_ERROR_INTERNAL
3682  *   SCF_ERROR_NO_MEMORY
3683  *   SCF_ERROR_NO_RESOURCES
3684  *   SCF_ERROR_NOT_BOUND
3685  *   SCF_ERROR_PERMISSION_DENIED
3686  *   SCF_ERROR_TEMPLATE_INVALID
3687  */
3688 static int
_check_choices_include_values(scf_propertygroup_t * pg,const char * name)3689 _check_choices_include_values(scf_propertygroup_t *pg, const char *name)
3690 {
3691 	int n = 0, r = 1;
3692 	char **ret;
3693 	scf_values_t vals;
3694 
3695 	if ((ret = _read_astrings_values(pg,
3696 	    SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES, &vals)) == NULL) {
3697 		if (ismember(scf_error(), errors_server)) {
3698 			return (-1);
3699 		} else switch (scf_error()) {
3700 		case SCF_ERROR_NOT_FOUND:
3701 			return (1);
3702 
3703 		case SCF_ERROR_TYPE_MISMATCH:
3704 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3705 			return (-1);
3706 
3707 		case SCF_ERROR_INVALID_ARGUMENT:
3708 		case SCF_ERROR_NOT_SET:
3709 		default:
3710 			assert(0);
3711 			abort();
3712 		}
3713 	}
3714 
3715 	for (n = 0; n < vals.value_count; ++n) {
3716 		if (strcmp(name, ret[n]) == 0) {
3717 			r = 0;
3718 			break;
3719 		}
3720 	}
3721 	scf_values_destroy(&vals);
3722 	return (r);
3723 }
3724 
3725 void
scf_count_ranges_destroy(scf_count_ranges_t * ranges)3726 scf_count_ranges_destroy(scf_count_ranges_t *ranges)
3727 {
3728 	if (ranges == NULL)
3729 		return;
3730 
3731 	ranges->scr_num_ranges = 0;
3732 	free(ranges->scr_min);
3733 	free(ranges->scr_max);
3734 	ranges->scr_min = NULL;
3735 	ranges->scr_max = NULL;
3736 }
3737 
3738 void
scf_int_ranges_destroy(scf_int_ranges_t * ranges)3739 scf_int_ranges_destroy(scf_int_ranges_t *ranges)
3740 {
3741 	if (ranges == NULL)
3742 		return;
3743 
3744 	ranges->sir_num_ranges = 0;
3745 	free(ranges->sir_min);
3746 	free(ranges->sir_max);
3747 	ranges->sir_min = NULL;
3748 	ranges->sir_max = NULL;
3749 }
3750 
3751 /*
3752  * Returns -1 on failure.  Sets scf_error():
3753  *   SCF_ERROR_BACKEND_ACCESS
3754  *   SCF_ERROR_CONNECTION_BROKEN
3755  *   SCF_ERROR_CONSTRAINT_VIOLATED
3756  *   SCF_ERROR_DELETED
3757  *   SCF_ERROR_HANDLE_DESTROYED
3758  *   SCF_ERROR_INTERNAL
3759  *   SCF_ERROR_NO_MEMORY
3760  *   SCF_ERROR_NO_RESOURCES
3761  *   SCF_ERROR_NOT_BOUND
3762  *   SCF_ERROR_NOT_FOUND
3763  *     Property doesn't exist or exists and has no value.
3764  *   SCF_ERROR_PERMISSION_DENIED
3765  *   SCF_ERROR_TEMPLATE_INVALID
3766  */
3767 static int
_scf_tmpl_get_count_ranges(const scf_prop_tmpl_t * t,const char * prop,scf_count_ranges_t * ranges)3768 _scf_tmpl_get_count_ranges(const scf_prop_tmpl_t *t, const char *prop,
3769     scf_count_ranges_t *ranges)
3770 {
3771 	scf_values_t vals;
3772 	int i = 0;
3773 	char **ret;
3774 	char *one_range[2];
3775 	char *endptr;
3776 	char *str = NULL;
3777 	uint64_t *min = NULL;
3778 	uint64_t *max = NULL;
3779 
3780 	assert(ranges != NULL);
3781 	if ((ret = _read_astrings_values(t->prt_pg, prop, &vals)) == NULL)
3782 		goto error;
3783 	if (vals.value_count == 0) {
3784 		/* range values are empty */
3785 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3786 		goto cleanup;
3787 	}
3788 
3789 	min = malloc(vals.value_count * sizeof (uint64_t));
3790 	max = malloc(vals.value_count * sizeof (uint64_t));
3791 	if (min == NULL || max == NULL) {
3792 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3793 		goto cleanup;
3794 	}
3795 	for (i = 0; i < vals.value_count; ++i) {
3796 		/* min and max should be separated by a "," */
3797 		if ((str = _separate_by_separator(ret[i], ",", one_range,
3798 		    2)) == NULL)
3799 			goto cleanup;
3800 		errno = 0;
3801 		min[i] = strtoull(one_range[0], &endptr, 10);
3802 		if (errno != 0 || endptr == one_range[0] || *endptr) {
3803 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3804 			goto cleanup;
3805 		}
3806 		errno = 0;
3807 		max[i] = strtoull(one_range[1], &endptr, 10);
3808 		if (errno != 0 || endptr == one_range[1] || *endptr) {
3809 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3810 			goto cleanup;
3811 		}
3812 		if (min[i] > max[i]) {
3813 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3814 			goto cleanup;
3815 		}
3816 		free(str);
3817 		str = NULL;
3818 	}
3819 	ranges->scr_num_ranges = vals.value_count;
3820 	ranges->scr_min = min;
3821 	ranges->scr_max = max;
3822 	scf_values_destroy(&vals);
3823 	return (0);
3824 cleanup:
3825 	free(str);
3826 	free(min);
3827 	free(max);
3828 	scf_values_destroy(&vals);
3829 error:
3830 	if (ismember(scf_error(), errors_server)) {
3831 		return (-1);
3832 	} else switch (scf_error()) {
3833 	case SCF_ERROR_TYPE_MISMATCH:
3834 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3835 		/*FALLTHROUGH*/
3836 
3837 	case SCF_ERROR_CONSTRAINT_VIOLATED:
3838 	case SCF_ERROR_NOT_FOUND:
3839 		return (-1);
3840 
3841 	case SCF_ERROR_INVALID_ARGUMENT:
3842 	case SCF_ERROR_NOT_SET:
3843 	default:
3844 		assert(0);
3845 		abort();
3846 	}
3847 	/*NOTREACHED*/
3848 }
3849 
3850 /*
3851  * Returns -1 on failure.  Sets scf_error():
3852  *   SCF_ERROR_BACKEND_ACCESS
3853  *   SCF_ERROR_CONNECTION_BROKEN
3854  *   SCF_ERROR_CONSTRAINT_VIOLATED
3855  *   SCF_ERROR_DELETED
3856  *   SCF_ERROR_HANDLE_DESTROYED
3857  *   SCF_ERROR_INTERNAL
3858  *   SCF_ERROR_NO_MEMORY
3859  *   SCF_ERROR_NO_RESOURCES
3860  *   SCF_ERROR_NOT_BOUND
3861  *   SCF_ERROR_NOT_FOUND
3862  *     Property doesn't exist or exists and has no value.
3863  *   SCF_ERROR_PERMISSION_DENIED
3864  *   SCF_ERROR_TEMPLATE_INVALID
3865  */
3866 static int
_scf_tmpl_get_int_ranges(const scf_prop_tmpl_t * t,const char * prop,scf_int_ranges_t * ranges)3867 _scf_tmpl_get_int_ranges(const scf_prop_tmpl_t *t, const char *prop,
3868     scf_int_ranges_t *ranges)
3869 {
3870 	scf_values_t vals;
3871 	int n = 0;
3872 	char **ret;
3873 	char *one_range[2];
3874 	char *endptr;
3875 	char *str = NULL;
3876 	int64_t *min = NULL;
3877 	int64_t *max = NULL;
3878 
3879 	assert(ranges != NULL);
3880 	if ((ret = _read_astrings_values(t->prt_pg, prop, &vals)) == NULL)
3881 		goto error;
3882 	if (vals.value_count == 0) {
3883 		/* range values are empty */
3884 		(void) scf_set_error(SCF_ERROR_NOT_FOUND);
3885 		goto cleanup;
3886 	}
3887 
3888 	min = malloc(vals.value_count * sizeof (int64_t));
3889 	max = malloc(vals.value_count * sizeof (int64_t));
3890 	if (min == NULL || max == NULL) {
3891 		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3892 		goto cleanup;
3893 	}
3894 	while (n < vals.value_count) {
3895 		/* min and max should be separated by a "," */
3896 		if ((str = _separate_by_separator(ret[n], ",", one_range, 2))
3897 		    == NULL)
3898 			goto cleanup;
3899 		errno = 0;
3900 		min[n] = strtoll(one_range[0], &endptr, 10);
3901 		if (errno != 0 || endptr == one_range[0] || *endptr) {
3902 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3903 			goto cleanup;
3904 		}
3905 		errno = 0;
3906 		max[n] = strtoll(one_range[1], &endptr, 10);
3907 		if (errno != 0 || endptr == one_range[1] || *endptr) {
3908 			(void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
3909 			goto cleanup;
3910 		}
3911 		if (min[n] > max[n]) {
3912 			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3913 			goto cleanup;
3914 		}
3915 		++n;
3916 		free(str);
3917 		str = NULL;
3918 	}
3919 	ranges->sir_num_ranges = vals.value_count;
3920 	ranges->sir_min = min;
3921 	ranges->sir_max = max;
3922 	scf_values_destroy(&vals);
3923 	return (0);
3924 cleanup:
3925 	free(str);
3926 	free(min);
3927 	free(max);
3928 	scf_values_destroy(&vals);
3929 error:
3930 	if (ismember(scf_error(), errors_server)) {
3931 		return (-1);
3932 	} else switch (scf_error()) {
3933 	case SCF_ERROR_TYPE_MISMATCH:
3934 		(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
3935 		/*FALLTHROUGH*/
3936 
3937 	case SCF_ERROR_CONSTRAINT_VIOLATED:
3938 	case SCF_ERROR_NOT_FOUND:
3939 	case SCF_ERROR_TEMPLATE_INVALID:
3940 		return (-1);
3941 
3942 	case SCF_ERROR_INVALID_ARGUMENT:
3943 	case SCF_ERROR_NOT_SET:
3944 	default:
3945 		assert(0);
3946 		abort();
3947 	}
3948 	/*NOTREACHED*/
3949 }
3950 
3951 /*
3952  * Returns -1 on failure.  Sets scf_error():
3953  *   SCF_ERROR_BACKEND_ACCESS
3954  *   SCF_ERROR_CONNECTION_BROKEN
3955  *   SCF_ERROR_CONSTRAINT_VIOLATED
3956  *   SCF_ERROR_DELETED
3957  *   SCF_ERROR_HANDLE_DESTROYED
3958  *   SCF_ERROR_INTERNAL
3959  *   SCF_ERROR_NO_MEMORY
3960  *   SCF_ERROR_NO_RESOURCES
3961  *   SCF_ERROR_NOT_BOUND
3962  *   SCF_ERROR_NOT_FOUND
3963  *     Property doesn't exist or exists and has no value.
3964  *   SCF_ERROR_PERMISSION_DENIED
3965  *   SCF_ERROR_TEMPLATE_INVALID
3966  */
3967 int
scf_tmpl_value_count_range_constraints(const scf_prop_tmpl_t * t,scf_count_ranges_t * ranges)3968 scf_tmpl_value_count_range_constraints(const scf_prop_tmpl_t *t,
3969     scf_count_ranges_t *ranges)
3970 {
3971 	return (_scf_tmpl_get_count_ranges(t, SCF_PROPERTY_TM_CONSTRAINT_RANGE,
3972 	    ranges));
3973 }
3974 
3975 int
scf_tmpl_value_int_range_constraints(const scf_prop_tmpl_t * t,scf_int_ranges_t * ranges)3976 scf_tmpl_value_int_range_constraints(const scf_prop_tmpl_t *t,
3977     scf_int_ranges_t *ranges)
3978 {
3979 	return (_scf_tmpl_get_int_ranges(t, SCF_PROPERTY_TM_CONSTRAINT_RANGE,
3980 	    ranges));
3981 }
3982 
3983 int
scf_tmpl_value_count_range_choices(const scf_prop_tmpl_t * t,scf_count_ranges_t * ranges)3984 scf_tmpl_value_count_range_choices(const scf_prop_tmpl_t *t,
3985     scf_count_ranges_t *ranges)
3986 {
3987 	return (_scf_tmpl_get_count_ranges(t, SCF_PROPERTY_TM_CHOICES_RANGE,
3988 	    ranges));
3989 }
3990 
3991 int
scf_tmpl_value_int_range_choices(const scf_prop_tmpl_t * t,scf_int_ranges_t * ranges)3992 scf_tmpl_value_int_range_choices(const scf_prop_tmpl_t *t,
3993     scf_int_ranges_t *ranges)
3994 {
3995 	return (_scf_tmpl_get_int_ranges(t, SCF_PROPERTY_TM_CHOICES_RANGE,
3996 	    ranges));
3997 }
3998 
3999 /*
4000  * Returns -1 on failure.  Sets scf_error():
4001&