11f6eb02Liane Praza/*
21f6eb02Liane Praza * CDDL HEADER START
31f6eb02Liane Praza *
41f6eb02Liane Praza * The contents of this file are subject to the terms of the
51f6eb02Liane Praza * Common Development and Distribution License (the "License").
61f6eb02Liane Praza * You may not use this file except in compliance with the License.
71f6eb02Liane Praza *
81f6eb02Liane Praza * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91f6eb02Liane Praza * or http://www.opensolaris.org/os/licensing.
101f6eb02Liane Praza * See the License for the specific language governing permissions
111f6eb02Liane Praza * and limitations under the License.
121f6eb02Liane Praza *
131f6eb02Liane Praza * When distributing Covered Code, include this CDDL HEADER in each
141f6eb02Liane Praza * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151f6eb02Liane Praza * If applicable, add the following below this CDDL HEADER, with the
161f6eb02Liane Praza * fields enclosed by brackets "[]" replaced with your own identifying
171f6eb02Liane Praza * information: Portions Copyright [yyyy] [name of copyright owner]
181f6eb02Liane Praza *
191f6eb02Liane Praza * CDDL HEADER END
201f6eb02Liane Praza */
211f6eb02Liane Praza/*
22f6e214cGavin Maltby * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23a58a7feMilan Jurik * Copyright 2012 Milan Jurik. All rights reserved.
241f6eb02Liane Praza */
251f6eb02Liane Praza
261f6eb02Liane Praza/*
271f6eb02Liane Praza * scf_tmpl.c
281f6eb02Liane Praza *
291f6eb02Liane Praza * This file implements the bulk of the libscf templates interfaces.
301f6eb02Liane Praza * Templates describe metadata about a service or instance in general,
311f6eb02Liane Praza * and individual configuration properties on those services and instances.
321f6eb02Liane Praza * Human-consumable descriptions can be provided, along with definitions
331f6eb02Liane Praza * of valid configuration.  See service_bundle.dtd.1 for XML definitions
341f6eb02Liane Praza * of templates, and the svccfg code for information on how those definitions
351f6eb02Liane Praza * are translated into the repository.
361f6eb02Liane Praza *
371f6eb02Liane Praza * The main data structures are scf_pg_tmpl and scf_prop_tmpl.  These
381f6eb02Liane Praza * are allocated by the callers through scf_tmpl_[pg|prop]_create(), and
391f6eb02Liane Praza * destroyed with scf_tmpl_[pg|prop]_destroy().  They are populated by
401f6eb02Liane Praza * scf_tmpl_get_by_pg_name(), scf_tmpl_get_by_pg(), and
411f6eb02Liane Praza * scf_tmpl_get_by_prop().  They also store the iterator state for
421f6eb02Liane Praza * scf_tmpl_iter_pgs() and scf_tmpl_iter_props().
431f6eb02Liane Praza *
441f6eb02Liane Praza * These data structures are then consumed by other functions to
451f6eb02Liane Praza * gather information about the template (e.g. name, description,
461f6eb02Liane Praza * choices, constraints, etc.).
471f6eb02Liane Praza *
481f6eb02Liane Praza * scf_tmpl_validate_fmri() does instance validation against template
491f6eb02Liane Praza * data, and populates a set of template errors which can be explored using
501f6eb02Liane Praza * the scf_tmpl_next_error() and the scf_tmpl_error*() suite of functions.
511f6eb02Liane Praza *
521f6eb02Liane Praza * The main data structures for template errors are scf_tmpl_errors,
531f6eb02Liane Praza * defined in this file, and scf_tmpl_error, defined in libscf_priv.h.
541f6eb02Liane Praza * scf_tmpl_error is shared with svccfg to offer common printing
551f6eb02Liane Praza * of error messages between libscf and svccfg.
561f6eb02Liane Praza *
571f6eb02Liane Praza * General convenience functions are towards the top of this file,
581f6eb02Liane Praza * followed by pg and prop template discovery functions, followed
591f6eb02Liane Praza * by functions which gather information about the discovered
601f6eb02Liane Praza * template.  Validation and error functions are at the end of this file.
611f6eb02Liane Praza */
621f6eb02Liane Praza
631f6eb02Liane Praza#include "lowlevel_impl.h"
641f6eb02Liane Praza#include "libscf_impl.h"
651f6eb02Liane Praza#include <assert.h>
661f6eb02Liane Praza#include <errno.h>
671f6eb02Liane Praza#include <libintl.h>
681f6eb02Liane Praza#include <stdlib.h>
691f6eb02Liane Praza#include <stdio.h>
701f6eb02Liane Praza#include <strings.h>
711f6eb02Liane Praza#include <locale.h>
721f6eb02Liane Praza#include <ctype.h>
731f6eb02Liane Praza#include <inttypes.h>
741f6eb02Liane Praza
751f6eb02Liane Praza#define	SCF_TMPL_PG_COMMON_NAME_C	"common_name_C"
761f6eb02Liane Praza
771f6eb02Liane Praza#define	SCF__TMPL_ITER_NONE		0
781f6eb02Liane Praza#define	SCF__TMPL_ITER_INST		1
791f6eb02Liane Praza#define	SCF__TMPL_ITER_RESTARTER	2
801f6eb02Liane Praza#define	SCF__TMPL_ITER_GLOBAL		3
811f6eb02Liane Praza
821f6eb02Liane Praza#define	SCF_TMPL_PG_NT		0
831f6eb02Liane Praza#define	SCF_TMPL_PG_N		1
841f6eb02Liane Praza#define	SCF_TMPL_PG_T		2
851f6eb02Liane Praza#define	SCF_TMPL_PG_WILD	3
861f6eb02Liane Praza
871f6eb02Liane Prazastruct scf_pg_tmpl {
881f6eb02Liane Praza	int pt_populated;
891f6eb02Liane Praza	scf_handle_t *pt_h;
901f6eb02Liane Praza	scf_propertygroup_t *pt_pg;
911f6eb02Liane Praza	scf_service_t *pt_orig_svc;
921f6eb02Liane Praza	scf_service_t *pt_svc;
931f6eb02Liane Praza	scf_instance_t *pt_orig_inst;
941f6eb02Liane Praza	scf_instance_t *pt_inst;
951f6eb02Liane Praza	scf_snapshot_t *pt_snap;
961f6eb02Liane Praza	int pt_is_iter;
971f6eb02Liane Praza	scf_iter_t *pt_iter;
981f6eb02Liane Praza	int pt_iter_last;
991f6eb02Liane Praza};
1001f6eb02Liane Praza
1011f6eb02Liane Praza#define	SCF_WALK_ERROR		-1
1021f6eb02Liane Praza#define	SCF_WALK_NEXT		0
1031f6eb02Liane Praza#define	SCF_WALK_DONE		1
1041f6eb02Liane Praza
1051f6eb02Liane Prazastruct pg_tmpl_walk {
1061f6eb02Liane Praza	const char *pw_snapname;
1071f6eb02Liane Praza	const char *pw_pgname;
1081f6eb02Liane Praza	const char *pw_pgtype;
1091f6eb02Liane Praza	scf_instance_t *pw_inst;
1101f6eb02Liane Praza	scf_service_t *pw_svc;
1111f6eb02Liane Praza	scf_snapshot_t *pw_snap;
1121f6eb02Liane Praza	scf_propertygroup_t *pw_pg;
1131f6eb02Liane Praza	const char *pw_target;
1141f6eb02Liane Praza	char *pw_tmpl_pgname;
1151f6eb02Liane Praza};
1161f6eb02Liane Praza
1171f6eb02Liane Prazatypedef struct pg_tmpl_walk pg_tmpl_walk_t;
1181f6eb02Liane Praza
1191f6eb02Liane Prazatypedef int walk_template_inst_func_t(scf_service_t *_svc,
1201f6eb02Liane Praza    scf_instance_t *_inst, pg_tmpl_walk_t *p);
1211f6eb02Liane Praza
1221f6eb02Liane Prazastruct scf_prop_tmpl {
1231f6eb02Liane Praza	int prt_populated;
1241f6eb02Liane Praza	scf_handle_t *prt_h;
1251f6eb02Liane Praza	scf_pg_tmpl_t *prt_t;
1261f6eb02Liane Praza	scf_propertygroup_t *prt_pg;
1271f6eb02Liane Praza	char *prt_pg_name;
1281f6eb02Liane Praza	scf_iter_t *prt_iter;
1291f6eb02Liane Praza};
1301f6eb02Liane Praza
1311f6eb02Liane Praza/*
1321f6eb02Liane Praza * Common server errors are usually passed back to the caller.  This
1331f6eb02Liane Praza * array defines them centrally so that they don't need to be enumerated
1341f6eb02Liane Praza * in every libscf call.
1351f6eb02Liane Praza */
1361f6eb02Liane Prazastatic const scf_error_t errors_server[] = {
1371f6eb02Liane Praza	SCF_ERROR_BACKEND_ACCESS,
1391f6eb02Liane Praza	SCF_ERROR_DELETED,
1411f6eb02Liane Praza	SCF_ERROR_INTERNAL,
1421f6eb02Liane Praza	SCF_ERROR_NO_MEMORY,
1431f6eb02Liane Praza	SCF_ERROR_NO_RESOURCES,
1441f6eb02Liane Praza	SCF_ERROR_NOT_BOUND,
1461f6eb02Liane Praza	0
1471f6eb02Liane Praza	};
1481f6eb02Liane Praza
1491f6eb02Liane Praza/*
1501f6eb02Liane Praza * int ismember()
1511f6eb02Liane Praza *
1521f6eb02Liane Praza * Returns 1 if the supplied error is a member of the error array, 0
1531f6eb02Liane Praza * if it is not.
1541f6eb02Liane Praza */
155f6e214cGavin Maltbyint
156f6e214cGavin Maltbyismember(const scf_error_t error, const scf_error_t error_array[])
1571f6eb02Liane Praza{
1581f6eb02Liane Praza	int i;
1591f6eb02Liane Praza
1601f6eb02Liane Praza	for (i = 0; error_array[i] != 0; ++i) {
1611f6eb02Liane Praza		if (error == error_array[i])
1621f6eb02Liane Praza			return (1);
1631f6eb02Liane Praza	}
1641f6eb02Liane Praza
1651f6eb02Liane Praza	return (0);
1661f6eb02Liane Praza}
1671f6eb02Liane Praza
1681f6eb02Liane Praza/*
1691f6eb02Liane Praza * char *_scf_tmpl_get_fmri()
1701f6eb02Liane Praza *
1711f6eb02Liane Praza * Given a pg_tmpl, returns the FMRI of the service or instance that
1721f6eb02Liane Praza * template describes.  The allocated string must be freed with free().
1731f6eb02Liane Praza *
1741f6eb02Liane Praza * On failure, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
1751f6eb02Liane Praza * _DELETED, or _NO_MEMORY.
1761f6eb02Liane Praza */
1771f6eb02Liane Prazastatic char *
1781f6eb02Liane Praza_scf_tmpl_get_fmri(const scf_pg_tmpl_t *t)
1791f6eb02Liane Praza{
1801f6eb02Liane Praza	ssize_t sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
1811f6eb02Liane Praza	int r;
1821f6eb02Liane Praza	char *buf = malloc(sz);
1831f6eb02Liane Praza
1841f6eb02Liane Praza	assert(t->pt_svc != NULL || t->pt_inst != NULL);
1851f6eb02Liane Praza	assert(t->pt_svc == NULL || t->pt_inst == NULL);
1861f6eb02Liane Praza
1871f6eb02Liane Praza	if (buf == NULL) {
1881f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
1891f6eb02Liane Praza		return (buf);
1901f6eb02Liane Praza	}
1911f6eb02Liane Praza
1921f6eb02Liane Praza	if (t->pt_inst != NULL)
1931f6eb02Liane Praza		r = scf_instance_to_fmri(t->pt_inst, buf, sz);
1941f6eb02Liane Praza	else
1951f6eb02Liane Praza		r = scf_service_to_fmri(t->pt_svc, buf, sz);
1961f6eb02Liane Praza
1971f6eb02Liane Praza	if (r == -1) {
1981f6eb02Liane Praza		if (ismember(scf_error(), errors_server)) {
1991f6eb02Liane Praza			free(buf);
2001f6eb02Liane Praza			buf = NULL;
2011f6eb02Liane Praza		} else {
2021f6eb02Liane Praza			assert(0);
2031f6eb02Liane Praza			abort();
2041f6eb02Liane Praza		}
2051f6eb02Liane Praza	}
2061f6eb02Liane Praza
2071f6eb02Liane Praza	return (buf);
2081f6eb02Liane Praza}
2091f6eb02Liane Praza
2101f6eb02Liane Praza/*
2111f6eb02Liane Praza * char *_scf_get_pg_type()
2121f6eb02Liane Praza *
2131f6eb02Liane Praza * Given a propertygroup, returns an allocated string containing the
2141f6eb02Liane Praza * type.  The string must be freed with free().
2151f6eb02Liane Praza *
2161f6eb02Liane Praza * On failure, returns NULL and sets scf_error() to: _CONNECTION_BROKEN,
2171f6eb02Liane Praza * _DELETED, or _NO_MEMORY.
2181f6eb02Liane Praza */
2191f6eb02Liane Prazastatic char *
2201f6eb02Liane Praza_scf_get_pg_type(scf_propertygroup_t *pg)
2211f6eb02Liane Praza{
2221f6eb02Liane Praza	ssize_t sz = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
2231f6eb02Liane Praza	char *buf = malloc(sz);
2241f6eb02Liane Praza
2251f6eb02Liane Praza	if (buf == NULL) {
2261f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2271f6eb02Liane Praza	} else if (scf_pg_get_type(pg, buf, sz) == -1) {
2281f6eb02Liane Praza		if (ismember(scf_error(), errors_server)) {
2291f6eb02Liane Praza			free(buf);
2301f6eb02Liane Praza			buf = NULL;
2311f6eb02Liane Praza		} else {
2321f6eb02Liane Praza			assert(0);
2331f6eb02Liane Praza			abort();
2341f6eb02Liane Praza		}
2351f6eb02Liane Praza	}
2361f6eb02Liane Praza
2371f6eb02Liane Praza	return (buf);
2381f6eb02Liane Praza}
2391f6eb02Liane Praza
2401f6eb02Liane Praza/*
2411f6eb02Liane Praza * char *_scf_get_prop_name()
2421f6eb02Liane Praza *
2431f6eb02Liane Praza * Given a property, returns the name in an allocated string.  The string must
2441f6eb02Liane Praza * be freed with free().
2451f6eb02Liane Praza *
2461f6eb02Liane Praza * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
2471f6eb02Liane Praza * _DELETED, or _NO_MEMORY.
2481f6eb02Liane Praza */
2491f6eb02Liane Prazastatic char *
2501f6eb02Liane Praza_scf_get_prop_name(scf_property_t *prop)
2511f6eb02Liane Praza{
2521f6eb02Liane Praza	ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2531f6eb02Liane Praza	char *buf = malloc(sz);
2541f6eb02Liane Praza
2551f6eb02Liane Praza	if (buf == NULL) {
2561f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2571f6eb02Liane Praza	} else if (scf_property_get_name(prop, buf, sz) == -1) {
2581f6eb02Liane Praza		if (ismember(scf_error(), errors_server)) {
2591f6eb02Liane Praza			free(buf);
2601f6eb02Liane Praza			buf = NULL;
2611f6eb02Liane Praza		} else {
2621f6eb02Liane Praza			assert(0);
2631f6eb02Liane Praza			abort();
2641f6eb02Liane Praza		}
2651f6eb02Liane Praza	}
2661f6eb02Liane Praza
2671f6eb02Liane Praza	return (buf);
2681f6eb02Liane Praza}
2691f6eb02Liane Praza
2701f6eb02Liane Praza/*
2711f6eb02Liane Praza * char *_scf_get_prop_type()
2721f6eb02Liane Praza *
2731f6eb02Liane Praza * Given a property, returns the type in an allocated string.  The string must
2741f6eb02Liane Praza * be freed with free().
2751f6eb02Liane Praza *
2761f6eb02Liane Praza * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
2771f6eb02Liane Praza * _DELETED, or _NO_MEMORY.
2781f6eb02Liane Praza */
2791f6eb02Liane Prazastatic char *
2801f6eb02Liane Praza_scf_get_prop_type(scf_property_t *prop)
2811f6eb02Liane Praza{
2821f6eb02Liane Praza	scf_type_t type;
2831f6eb02Liane Praza	char *ret;
2841f6eb02Liane Praza
2851f6eb02Liane Praza	if (scf_property_type(prop, &type) == -1) {
2861f6eb02Liane Praza		if (ismember(scf_error(), errors_server)) {
2871f6eb02Liane Praza			return (NULL);
2881f6eb02Liane Praza		} else {
2891f6eb02Liane Praza			assert(0);
2901f6eb02Liane Praza			abort();
2911f6eb02Liane Praza		}
2921f6eb02Liane Praza	}
2931f6eb02Liane Praza
2941f6eb02Liane Praza	ret = strdup(scf_type_to_string(type));
2951f6eb02Liane Praza	if (ret == NULL)
2961f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2971f6eb02Liane Praza
2981f6eb02Liane Praza	return (ret);
2991f6eb02Liane Praza}
3001f6eb02Liane Praza
3011f6eb02Liane Praza/*
3021f6eb02Liane Praza * int _read_single_value_from_pg()
3031f6eb02Liane Praza *
3041f6eb02Liane Praza * Reads a single value from the pg and property name specified.  On success,
3051f6eb02Liane Praza * returns an allocated value that must be freed.
3061f6eb02Liane Praza *
3071f6eb02Liane Praza * Returns -1 on failure, sets scf_error() to:
3081f6eb02Liane Praza *  SCF_ERROR_BACKEND_ACCESS
3091f6eb02Liane Praza *  SCF_ERROR_CONNECTION_BROKEN
3111f6eb02Liane Praza *    Property has more than one value associated with it.
3121f6eb02Liane Praza *  SCF_ERROR_DELETED
3131f6eb02Liane Praza *  SCF_ERROR_HANDLE_DESTROYED
3141f6eb02Liane Praza *  SCF_ERROR_INTERNAL
3151f6eb02Liane Praza *  SCF_ERROR_INVALID_ARGUMENT
3161f6eb02Liane Praza *    prop_name not a valid property name.
3171f6eb02Liane Praza *  SCF_ERROR_NO_MEMORY
3181f6eb02Liane Praza *  SCF_ERROR_NO_RESOURCES
3191f6eb02Liane Praza *  SCF_ERROR_NOT_BOUND
3201f6eb02Liane Praza *  SCF_ERROR_NOT_FOUND
3211f6eb02Liane Praza *    Property doesn't exist or exists and has no value.
3221f6eb02Liane Praza *  SCF_ERROR_NOT_SET
3231f6eb02Liane Praza *    Property group specified by pg is not set.
3241f6eb02Liane Praza *  SCF_ERROR_PERMISSION_DENIED
3251f6eb02Liane Praza */
3261f6eb02Liane Prazastatic int
3271f6eb02Liane Praza_read_single_value_from_pg(scf_propertygroup_t *pg, const char *prop_name,
3281f6eb02Liane Praza    scf_value_t **val)
3291f6eb02Liane Praza{
3301f6eb02Liane Praza	scf_handle_t *h;
3311f6eb02Liane Praza	scf_property_t *prop;
3321f6eb02Liane Praza	int ret = 0;
3331f6eb02Liane Praza
3341f6eb02Liane Praza	assert(val != NULL);
3351f6eb02Liane Praza	if ((h = scf_pg_handle(pg)) == NULL) {
3361f6eb02Liane Praza		assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
3371f6eb02Liane Praza		return (-1);
3381f6eb02Liane Praza	}
3391f6eb02Liane Praza
3401f6eb02Liane Praza	prop = scf_property_create(h);
3411f6eb02Liane Praza	*val = scf_value_create(h);
3421f6eb02Liane Praza
3431f6eb02Liane Praza	if (prop == NULL || *val == NULL) {
3441f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
3451f6eb02Liane Praza		goto read_single_value_from_pg_fail;
3461f6eb02Liane Praza	}
3471f6eb02Liane Praza
3481f6eb02Liane Praza	if (scf_pg_get_property(pg, prop_name, prop) != 0) {
3491f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
3501f6eb02Liane Praza		goto read_single_value_from_pg_fail;
3511f6eb02Liane Praza	}
3521f6eb02Liane Praza
3531f6eb02Liane Praza	if (scf_property_get_value(prop, *val) == -1) {
3541f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_NOT_SET);
3551f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
3561f6eb02Liane Praza		goto read_single_value_from_pg_fail;
3571f6eb02Liane Praza	}
3581f6eb02Liane Praza
3591f6eb02Liane Praza	goto read_single_value_from_pg_done;
3601f6eb02Liane Praza
3611f6eb02Liane Prazaread_single_value_from_pg_fail:
3621f6eb02Liane Praza	scf_value_destroy(*val);
3631f6eb02Liane Praza	*val = NULL;
3641f6eb02Liane Praza	ret = -1;
3651f6eb02Liane Praza
3661f6eb02Liane Prazaread_single_value_from_pg_done:
3671f6eb02Liane Praza	scf_property_destroy(prop);
3681f6eb02Liane Praza	return (ret);
3691f6eb02Liane Praza}
3701f6eb02Liane Praza
3711f6eb02Liane Praza/*
3721f6eb02Liane Praza * char *_scf_read_single_astring_from_pg()
3731f6eb02Liane Praza *
3741f6eb02Liane Praza * Reads an astring from the pg and property name specified.  On success,
3751f6eb02Liane Praza * returns an allocated string.  The string must be freed with free().
3761f6eb02Liane Praza *
3771f6eb02Liane Praza * Returns NULL on failure, sets scf_error() to:
3781f6eb02Liane Praza *   SCF_ERROR_BACKEND_ACCESS
3791f6eb02Liane Praza *   SCF_ERROR_CONNECTION_BROKEN
3811f6eb02Liane Praza *     Property has more than one value associated with it.
3821f6eb02Liane Praza *   SCF_ERROR_DELETED
3831f6eb02Liane Praza *   SCF_ERROR_HANDLE_DESTROYED
3841f6eb02Liane Praza *   SCF_ERROR_INTERNAL
3851f6eb02Liane Praza *   SCF_ERROR_INVALID_ARGUMENT
3861f6eb02Liane Praza *     prop_name not a valid property name.
3871f6eb02Liane Praza *   SCF_ERROR_NO_MEMORY
3881f6eb02Liane Praza *   SCF_ERROR_NO_RESOURCES
3891f6eb02Liane Praza *   SCF_ERROR_NOT_BOUND
3901f6eb02Liane Praza *   SCF_ERROR_NOT_FOUND
3911f6eb02Liane Praza *     Property doesn't exist or exists and has no value.
3921f6eb02Liane Praza *   SCF_ERROR_NOT_SET
3931f6eb02Liane Praza *     The property group specified by pg is not set.
3941f6eb02Liane Praza *   SCF_ERROR_PERMISSION_DENIED
3951f6eb02Liane Praza *   SCF_ERROR_TYPE_MISMATCH
3961f6eb02Liane Praza */
3971f6eb02Liane Prazachar *
3981f6eb02Liane Praza_scf_read_single_astring_from_pg(scf_propertygroup_t *pg, const char *prop_name)
3991f6eb02Liane Praza{
4001f6eb02Liane Praza	scf_value_t *val;
4011f6eb02Liane Praza	char *ret = NULL;
4021f6eb02Liane Praza	ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
4031f6eb02Liane Praza
4041f6eb02Liane Praza	assert(rsize != 0);
4051f6eb02Liane Praza	if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
4061f6eb02Liane Praza		return (NULL);
4071f6eb02Liane Praza
4081f6eb02Liane Praza	ret = malloc(rsize);
4091f6eb02Liane Praza	if (ret == NULL) {
4101f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4111f6eb02Liane Praza		goto cleanup;
4121f6eb02Liane Praza	}
4131f6eb02Liane Praza
4141f6eb02Liane Praza	if (scf_value_get_astring(val, ret, rsize) < 0) {
4151f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_NOT_SET);
4161f6eb02Liane Praza		free(ret);
4171f6eb02Liane Praza		ret = NULL;
4181f6eb02Liane Praza	}
4191f6eb02Liane Praza
4201f6eb02Liane Prazacleanup:
4211f6eb02Liane Praza	scf_value_destroy(val);
4221f6eb02Liane Praza	return (ret);
4231f6eb02Liane Praza}
4241f6eb02Liane Praza
4251f6eb02Liane Praza/*
4261f6eb02Liane Praza * char *_scf_read_tmpl_prop_type_as_string()
4271f6eb02Liane Praza *
4281f6eb02Liane Praza * Reads the property type and returns it as an allocated string.  The string
4291f6eb02Liane Praza * must be freed with free().
4301f6eb02Liane Praza *
4311f6eb02Liane Praza * Returns NULL on failure, sets scf_error() to _BACKEND_ACCESS,
4341f6eb02Liane Praza */
4351f6eb02Liane Prazachar *
4361f6eb02Liane Praza_scf_read_tmpl_prop_type_as_string(const scf_prop_tmpl_t *pt)
4371f6eb02Liane Praza{
4381f6eb02Liane Praza	char *type;
4391f6eb02Liane Praza
4401f6eb02Liane Praza	type = _scf_read_single_astring_from_pg(pt->prt_pg,
4411f6eb02Liane Praza	    SCF_PROPERTY_TM_TYPE);
4421f6eb02Liane Praza	if (type == NULL) {
4431f6eb02Liane Praza		if (ismember(scf_error(), errors_server)) {
4441f6eb02Liane Praza			return (NULL);
4451f6eb02Liane Praza		} else switch (scf_error()) {
4461f6eb02Liane Praza		case SCF_ERROR_CONSTRAINT_VIOLATED:
4471f6eb02Liane Praza		case SCF_ERROR_NOT_FOUND:
4481f6eb02Liane Praza		case SCF_ERROR_TYPE_MISMATCH:
4491f6eb02Liane Praza			(void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
4501f6eb02Liane Praza			return (NULL);
4511f6eb02Liane Praza
4521f6eb02Liane Praza		case SCF_ERROR_INVALID_ARGUMENT:
4531f6eb02Liane Praza		case SCF_ERROR_NOT_SET:
4541f6eb02Liane Praza		default:
4551f6eb02Liane Praza			assert(0);
4561f6eb02Liane Praza			abort();
4571f6eb02Liane Praza		}
4581f6eb02Liane Praza	}
4591f6eb02Liane Praza
4601f6eb02Liane Praza	return (type);
4611f6eb02Liane Praza}
4621f6eb02Liane Praza
4631f6eb02Liane Praza/*
4641f6eb02Liane Praza * int _read_single_boolean_from_pg()
4651f6eb02Liane Praza *
4661f6eb02Liane Praza * Reads a boolean from the pg and property name specified.
4671f6eb02Liane Praza *
4681f6eb02Liane Praza * Returns -1 on failure, sets scf_error() to:
4691f6eb02Liane Praza *   SCF_ERROR_BACKEND_ACCESS
4701f6eb02Liane Praza *   SCF_ERROR_CONNECTION_BROKEN
4721f6eb02Liane Praza *     Property has more than one value associated with it.
4731f6eb02Liane Praza *   SCF_ERROR_DELETED
4741f6eb02Liane Praza *   SCF_ERROR_HANDLE_DESTROYED
4751f6eb02Liane Praza *   SCF_ERROR_INTERNAL
4761f6eb02Liane Praza *   SCF_ERROR_INVALID_ARGUMENT
4771f6eb02Liane Praza *     prop_name is not a valid property name.
4781f6eb02Liane Praza *   SCF_ERROR_NO_MEMORY
4791f6eb02Liane Praza *   SCF_ERROR_NO_RESOURCES
4801f6eb02Liane Praza *   SCF_ERROR_NOT_BOUND
4811f6eb02Liane Praza *   SCF_ERROR_NOT_FOUND
4821f6eb02Liane Praza *     Property doesn't exist or exists and has no value.
4831f6eb02Liane Praza *   SCF_ERROR_NOT_SET
4841f6eb02Liane Praza *     The property group specified by pg is not set.
4851f6eb02Liane Praza *   SCF_ERROR_PERMISSION_DENIED
4861f6eb02Liane Praza *   SCF_ERROR_TYPE_MISMATCH
4871f6eb02Liane Praza */
4881f6eb02Liane Prazastatic int
4891f6eb02Liane Praza_read_single_boolean_from_pg(scf_propertygroup_t *pg, const char *prop_name,
4901f6eb02Liane Praza    uint8_t *bool)
4911f6eb02Liane Praza{
4921f6eb02Liane Praza	scf_value_t *val;
4931f6eb02Liane Praza	int ret = 0;
4941f6eb02Liane Praza
4951f6eb02Liane Praza	if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
4961f6eb02Liane Praza		return (-1);
4971f6eb02Liane Praza
4981f6eb02Liane Praza	if (scf_value_get_boolean(val, bool) < 0) {
4991f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_NOT_SET);
5001f6eb02Liane Praza		ret = -1;
5011f6eb02Liane Praza	}
5021f6eb02Liane Praza
5031f6eb02Liane Praza	scf_value_destroy(val);
5041f6eb02Liane Praza	return (ret);
5051f6eb02Liane Praza}
5061f6eb02Liane Praza
5071f6eb02Liane Praza/*
508f6e214cGavin Maltby * static char ** _append_astrings_values()
5091f6eb02Liane Praza *
5101f6eb02Liane Praza * This function reads the values from the property prop_name in pg and
5111f6eb02Liane Praza * appends to an existing scf_values_t *vals.  vals may be empty, but
5121f6eb02Liane Praza * must exist.  The function skips over zero-length and duplicate values.
5131f6eb02Liane Praza *
5141f6eb02Liane Praza * Returns NULL on failure, sets scf_error() to:
5151f6eb02Liane Praza *   SCF_ERROR_BACKEND_ACCESS
5161f6eb02Liane Praza *   SCF_ERROR_CONNECTION_BROKEN
5171f6eb02Liane Praza *   SCF_ERROR_DELETED
5181f6eb02Liane Praza *   SCF_ERROR_HANDLE_DESTROYED
5191f6eb02Liane Praza *   SCF_ERROR_INTERNAL
5201f6eb02Liane Praza *   SCF_ERROR_INVALID_ARGUMENT
5211f6eb02Liane Praza *     prop_name is not a valid property name.
5221f6eb02Liane Praza *   SCF_ERROR_NO_MEMORY
5231f6eb02Liane Praza *   SCF_ERROR_NO_RESOURCES
5241f6eb02Liane Praza *   SCF_ERROR_NOT_BOUND
5251f6eb02Liane Praza *   SCF_ERROR_NOT_FOUND
5261f6eb02Liane Praza *   SCF_ERROR_NOT_SET
5271f6eb02Liane Praza *   SCF_ERROR_PERMISSION_DENIED
5281f6eb02Liane Praza *   SCF_ERROR_TYPE_MISMATCH
5291f6eb02Liane Praza */
5301f6eb02Liane Prazastatic char **
5311f6eb02Liane Praza_append_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
5321f6eb02Liane Praza    scf_values_t *vals)
5331f6eb02Liane Praza{
5341f6eb02Liane Praza	scf_handle_t *h;
5351f6eb02Liane Praza	scf_property_t *prop;
5361f6eb02Liane Praza	scf_value_t *val;
5371f6eb02Liane Praza	scf_iter_t *iter;
5381f6eb02Liane Praza	ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
5391f6eb02Liane Praza	int err, count, cursz, i;
5401f6eb02Liane Praza
5411f6eb02Liane Praza	assert(vals != NULL);
5421f6eb02Liane Praza	assert(vals->value_type == SCF_TYPE_ASTRING);
5431f6eb02Liane Praza	assert(vals->reserved == NULL);
5441f6eb02Liane Praza	count = vals->value_count;
5451f6eb02Liane Praza	if (count == 0) {
5461f6eb02Liane Praza		cursz = 8;
5471f6eb02Liane Praza		vals->values.v_astring = calloc(cursz, sizeof (char *));
5481f6eb02Liane Praza		if (vals->values.v_astring == NULL) {
5491f6eb02Liane Praza			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5501f6eb02Liane Praza			return (NULL);
5511f6eb02Liane Praza		}
5521f6eb02Liane Praza	} else {
5531f6eb02Liane Praza		/*
5541f6eb02Liane Praza		 * The array may be bigger, but it is irrelevant since
5551f6eb02Liane Praza		 * we will always re-allocate a new one.
5561f6eb02Liane Praza		 */
5571f6eb02Liane Praza		cursz = count;
5581f6eb02Liane Praza	}
5591f6eb02Liane Praza
5601f6eb02Liane Praza	if ((h = scf_pg_handle(pg)) == NULL) {
5611f6eb02Liane Praza		assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
5621f6eb02Liane Praza		return (NULL);
5631f6eb02Liane Praza	}
5641f6eb02Liane Praza
5651f6eb02Liane Praza	prop = scf_property_create(h);
5661f6eb02Liane Praza	val = scf_value_create(h);
5671f6eb02Liane Praza	iter = scf_iter_create(h);
5681f6eb02Liane Praza
5691f6eb02Liane Praza	if (prop == NULL || val == NULL || iter == NULL) {
5701f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
5711f6eb02Liane Praza		goto append_single_astring_from_pg_fail;
5721f6eb02Liane Praza	}
5731f6eb02Liane Praza
5741f6eb02Liane Praza	if (scf_pg_get_property(pg, prop_name, prop) != 0) {
5751f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
5761f6eb02Liane Praza		goto append_single_astring_from_pg_fail;
5771f6eb02Liane Praza	}
5781f6eb02Liane Praza
5791f6eb02Liane Praza	if (scf_iter_property_values(iter, prop) != 0) {
5801f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_NOT_SET);
5811f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
5821f6eb02Liane Praza		goto append_single_astring_from_pg_fail;
5831f6eb02Liane Praza	}
5841f6eb02Liane Praza
5851f6eb02Liane Praza	while ((err = scf_iter_next_value(iter, val)) == 1) {
5861f6eb02Liane Praza		int flag;
5871f6eb02Liane Praza		int r;
5881f6eb02Liane Praza
5891f6eb02Liane Praza		if (count + 1 >= cursz) {
5901f6eb02Liane Praza			void *aux;
5911f6eb02Liane Praza
5921f6eb02Liane Praza			cursz *= 2;
5931f6eb02Liane Praza			if ((aux = calloc(cursz, sizeof (char *))) == NULL) {
5941f6eb02Liane Praza				(void) scf_set_error(SCF_ERROR_NO_MEMORY);
5951f6eb02Liane Praza				goto append_single_astring_from_pg_fail;
5961f6eb02Liane Praza			}
5971f6eb02Liane Praza			(void) memcpy(aux, vals->values.v_astring,
5981f6eb02Liane Praza			    count * sizeof (char *));
5991f6eb02Liane Praza			free(vals->values.v_astring);
6001f6eb02Liane Praza			vals->values.v_astring = aux;
6011f6eb02Liane Praza		}
6021f6eb02Liane Praza
6031f6eb02Liane Praza		vals->values.v_astring[count] = malloc(rsize);
6041f6eb02Liane Praza		if (vals->values.v_astring[count] == NULL) {
6051f6eb02Liane Praza			(void) scf_set_error(SCF_ERROR_NO_MEMORY);
6061f6eb02Liane Praza			goto append_single_astring_from_pg_fail;
6071f6eb02Liane Praza		}
6081f6eb02Liane Praza
6091f6eb02Liane Praza		if ((r = scf_value_get_astring(val,
6101f6eb02Liane Praza		    vals->values.v_astring[count], rsize)) <= 0) {
6111f6eb02Liane Praza			/* discard zero length strings */
6121f6eb02Liane Praza			if (r == 0) {
6131f6eb02Liane Praza				free(vals->values.v_astring[count]);
6141f6eb02Liane Praza				continue;
6151f6eb02Liane Praza			}
6161f6eb02Liane Praza			assert(scf_error() != SCF_ERROR_NOT_SET);
6171f6eb02Liane Praza			goto append_single_astring_from_pg_fail;
6181f6eb02Liane Praza		}
6191f6eb02Liane Praza		for (i = 0, flag = 0; i < count; ++i) {
6201f6eb02Liane Praza			/* find  and discard duplicates */
6211f6eb02Liane Praza			if (strncmp(vals->values.v_astring[i],
6221f6eb02Liane Praza			    vals->values.v_astring[count], rsize) == 0) {
6231f6eb02Liane Praza				free(vals->values.v_astring[count]);
6241f6eb02Liane Praza				flag = 1;
6251f6eb02Liane Praza				break;
6261f6eb02Liane Praza			}
6271f6eb02Liane Praza		}
6281f6eb02Liane Praza		if (flag == 1)
6291f6eb02Liane Praza			continue;
6301f6eb02Liane Praza
6311f6eb02Liane Praza		count++;
6321f6eb02Liane Praza	}
6331f6eb02Liane Praza
6341f6eb02Liane Praza	vals->value_count = count;
6351f6eb02Liane Praza
6361f6eb02Liane Praza	if (err != 0) {
6371f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_NOT_SET);
6381f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
6391f6eb02Liane Praza		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
6401f6eb02Liane Praza		goto append_single_astring_from_pg_fail;
6411f6eb02Liane Praza	} else {
6421f6eb02Liane Praza		vals->values_as_strings = vals->values.v_astring;
6431f6eb02Liane Praza	}
6441f6eb02Liane Praza
6451f6eb02Liane Praza	goto append_single_astring_from_pg_done;
6461f6eb02Liane Praza
6471f6eb02Liane Prazaappend_single_astring_from_pg_fail:
6481f6eb02Liane Praza	for (i = 0; i <= count; ++i) {
6491f6eb02Liane Praza		if (vals->values.v_astring[i] != NULL)
6501f6eb02Liane Praza			free(vals->values.v_astring[i]);
6511f6eb02Liane Praza		vals->values.v_astring[i] = NULL;
6521f6eb02Liane Praza	}
6531f6eb02Liane Praza	free(vals->values.v_astring);
6541f6eb02Liane Praza	vals->values.v_astring = NULL;
6551f6eb02Liane Praza	vals->value_count = 0;
6561f6eb02Liane Praza
6571f6eb02Liane Prazaappend_single_astring_from_pg_done:
6581f6eb02Liane Praza	scf_iter_destroy(iter);
6591f6eb02Liane Praza	scf_property_destroy(prop);
6601f6eb02Liane Praza	scf_value_destroy(val);
6611f6eb02Liane Praza	return (vals->values.v_astring);
6621f6eb02Liane Praza}
6631f6eb02Liane Praza
6641f6eb02Liane Praza/*
6651f6eb02Liane Praza * Returns NULL on failure, sets scf_error() to:
6661f6eb02Liane Praza *   SCF_ERROR_BACKEND_ACCESS
6671f6eb02Liane Praza *   SCF_ERROR_CONNECTION_BROKEN
6681f6eb02Liane Praza *   SCF_ERROR_DELETED
6691f6eb02Liane Praza *   SCF_ERROR_HANDLE_DESTROYED
6701f6eb02Liane Praza *   SCF_ERROR_INTERNAL
6711f6eb02Liane Praza *   SCF_ERROR_INVALID_ARGUMENT
6721f6eb02Liane Praza *     prop_name is not a valid property name.
6731f6eb02Liane Praza *   SCF_ERROR_NO_MEMORY
6741f6eb02Liane Praza *   SCF_ERROR_NO_RESOURCES
6751f6eb02Liane Praza *   SCF_ERROR_NOT_BOUND
6761f6eb02Liane Praza *   SCF_ERROR_NOT_FOUND
6771f6eb02Liane Praza *   SCF_ERROR_NOT_SET
6781f6eb02Liane Praza *   SCF_ERROR_PERMISSION_DENIED
6791f6eb02Liane Praza *   SCF_ERROR_TYPE_MISMATCH
6801f6eb02Liane Praza */
6811f6eb02Liane Prazastatic char **
6821f6eb02Liane Praza_read_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
6831f6eb02Liane Praza    scf_values_t *vals)
6841f6eb02Liane Praza{
6851f6eb02Liane Praza	assert(vals != NULL);
6861f6eb02Liane Praza	vals->value_count = 0;
6871f6eb02Liane Praza	vals->value_type = SCF_TYPE_ASTRING;
6881f6eb02Liane Praza	vals->reserved = NULL;
6891f6eb02Liane Praza	return (_append_astrings_values(pg, prop_name, vals));
6901f6eb02Liane Praza}
6911f6eb02Liane Praza
6921f6eb02Liane Prazavoid
6931f6eb02Liane Praza_scf_sanitize_locale(char *locale)
6941f6eb02Liane Praza{
6951f6eb02Liane Praza	for (; *locale != '\0'; locale++)
6961f6eb02Liane Praza		if (!isalnum(*locale) && *locale != '_')
6971f6eb02Liane Praza			*locale = '_';
6981f6eb02Liane Praza}
6991f6eb02Liane Praza
7001f6eb02Liane Praza/*
7011f6eb02Liane Praza * The returned string needs to be freed by the caller
7021f6eb02Liane Praza * Returns NULL on failure.  Sets scf_error() to:
7031f6eb02Liane Praza *   SCF_ERROR_NO_MEMORY
7041f6eb02Liane Praza *   SCF_ERROR_INVALID_ARGUMENT
7051f6eb02Liane Praza *     Name isn't short enough to add the locale to.
7061f6eb02Liane Praza */
7071f6eb02Liane Prazastatic char *
7081f6eb02Liane Praza_add_locale_to_name(const char *name, const char *locale)
7091f6eb02Liane Praza{
7101f6eb02Liane Praza	char *lname = NULL;
7111f6eb02Liane Praza	ssize_t lsz;
7121f6eb02Liane Praza	char *loc;
7131f6eb02Liane Praza
7141f6eb02Liane Praza	if (locale == NULL)
7151f6eb02Liane Praza		locale = setlocale(LC_MESSAGES, NULL);
7161f6eb02Liane Praza	loc = strdup(locale);
7171f6eb02Liane Praza	if (loc == NULL) {
7181f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
7191f6eb02Liane Praza		return (NULL);
7201f6eb02Liane Praza	} else {
7211f6eb02Liane Praza		_scf_sanitize_locale(loc);
7221f6eb02Liane Praza	}
7231f6eb02Liane Praza
7241f6eb02Liane Praza	lsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
7251f6eb02Liane Praza	lname = malloc(lsz);
7261f6eb02Liane Praza	if (lname == NULL) {
7271f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
7281f6eb02Liane Praza		goto cleanup;
7291f6eb02Liane Praza	}
7301f6eb02Liane Praza
7311f6eb02Liane Praza	(void) strlcpy(lname, name, lsz);
7321f6eb02Liane Praza	if (strlcat(lname, loc, lsz) >= lsz) {
7331f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
7341f6eb02Liane Praza		free(lname);
7351f6eb02Liane Praza		lname = NULL;
7361f6eb02Liane Praza	}
7371f6eb02Liane Prazacleanup:
7381f6eb02Liane Praza	free(loc);
7391f6eb02Liane Praza
7401f6eb02Liane Praza	return (lname);
7411f6eb02Liane Praza}
7421f6eb02Liane Praza
7431f6eb02Liane Praza/*
7441f6eb02Liane Praza * char *_tmpl_pg_name(pg, type, use_type)
7451f6eb02Liane Praza *
7461f6eb02Liane Praza * pg and type can both be NULL.  Returns the name of the most specific
7471f6eb02Liane Praza * template property group name based on the inputs.
7481f6eb02Liane Praza * If use_type is set and pg is not NULL, a property group name for a
7491f6eb02Liane Praza * property group template that has type defined is returned, even if no
7501f6eb02Liane Praza * type is provided.
7511f6eb02Liane Praza *
7521f6eb02Liane Praza * Returns NULL on failure and sets scf_error() to:
7531f6eb02Liane Praza *   SCF_ERROR_INVALID_ARGUMENT
7541f6eb02Liane Praza *     can't combine the arguments and get a reasonable length name
7551f6eb02Liane Praza *   SCF_ERROR_NO_MEMORY
7561f6eb02Liane Praza *
7571f6eb02Liane Praza */
7581f6eb02Liane Prazastatic char *
7591f6eb02Liane Praza_tmpl_pg_name(const char *pg, const char *type, int use_type)
7601f6eb02Liane Praza{
7611f6eb02Liane Praza	char *name;
7621f6eb02Liane Praza	ssize_t limit, size = 0;
7631f6eb02Liane Praza
7641f6eb02Liane Praza	limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
7651f6eb02Liane Praza	name = malloc(limit);
7661f6eb02Liane Praza	if (name == NULL) {
7671f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
7681f6eb02Liane Praza		return (NULL);
7691f6eb02Liane Praza	}
7701f6eb02Liane Praza
7711f6eb02Liane Praza	if (pg == NULL && type == NULL) {
7721f6eb02Liane Praza		if (strlcpy(name, SCF_PG_TM_PG_PATTERN_PREFIX, limit) >=
7731f6eb02Liane Praza		    limit) {
7741f6eb02Liane Praza			assert(0);
7751f6eb02Liane Praza			abort();
7761f6eb02Liane Praza		}
7771f6eb02Liane Praza		return (name);
7781f6eb02Liane Praza	} else if (pg != NULL && type != NULL) {
7791f6eb02Liane Praza		size = snprintf(name, limit, "%s%s",
7801f6eb02Liane Praza		    SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
7811f6eb02Liane Praza	} else if (pg != NULL && type == NULL && use_type == 1) {
7821f6eb02Liane Praza		size = snprintf(name, limit, "%s%s",
7831f6eb02Liane Praza		    SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
7841f6eb02Liane Praza	} else if (pg != NULL && type == NULL) {
7851f6eb02Liane Praza		size = snprintf(name, limit, "%s%s",
7861f6eb02Liane Praza		    SCF_PG_TM_PG_PATTERN_N_PREFIX, pg);
7871f6eb02Liane Praza	} else if (type != NULL && pg == NULL) {
7881f6eb02Liane Praza		size = snprintf(name, limit, "%s%s",
7891f6eb02Liane Praza		    SCF_PG_TM_PG_PATTERN_T_PREFIX, type);
7901f6eb02Liane Praza	} else {
7911f6eb02Liane Praza		assert(0);
7921f6eb02Liane Praza		abort();
7931f6eb02Liane Praza	}
7941f6eb02Liane Praza
7951f6eb02Liane Praza	if (size >= limit) {
7961f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
7971f6eb02Liane Praza		free(name);
7981f6eb02Liane Praza		return (NULL);
7991f6eb02Liane Praza	} else {
8001f6eb02Liane Praza		return (name);
8011f6eb02Liane Praza	}
8021f6eb02Liane Praza}
8031f6eb02Liane Praza
8041f6eb02Liane Praza/*
8051f6eb02Liane Praza * _scf_get_pg_name()
8061f6eb02Liane Praza * Gets the name of the supplied property group.  On success, returns an
8071f6eb02Liane Praza * allocated string.  The string must be freed by free().
8081f6eb02Liane Praza *
8091f6eb02Liane Praza * Returns NULL on failure and sets scf_error() to _CONNECTION_BROKEN,
8101f6eb02Liane Praza * _DELETED, or _NO_MEMORY.
8111f6eb02Liane Praza */
8121f6eb02Liane Prazastatic char *
8131f6eb02Liane Praza_scf_get_pg_name(scf_propertygroup_t *pg)
8141f6eb02Liane Praza{
8151f6eb02Liane Praza	ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
8161f6eb02Liane Praza	char *buf = malloc(sz);
8171f6eb02Liane Praza
8181f6eb02Liane Praza	if (buf == NULL) {
8191f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
8201f6eb02Liane Praza	} else if (scf_pg_get_name(pg, buf, sz) == -1) {
8211f6eb02Liane Praza		if (ismember(scf_error(), errors_server)) {
8221f6eb02Liane Praza			free(buf);
8231f6eb02Liane Praza			buf = NULL;
8241f6eb02Liane Praza		} else {
8251f6eb02Liane Praza			assert(0);
8261f6eb02Liane Praza			abort();
8271f6eb02Liane Praza		}
8281f6eb02Liane Praza	}
8291f6eb02Liane Praza
8301f6eb02Liane Praza	return (buf);
8311f6eb02Liane Praza}
8321f6eb02Liane Praza
8331f6eb02Liane Praza/*
8341f6eb02Liane Praza * char *_tmpl_prop_name()
8351f6eb02Liane Praza *
8361f6eb02Liane Praza * Returns the name of the property template prop (which is the name of
8371f6eb02Liane Praza * the property template property group) in the property group
8381f6eb02Liane Praza * template t. Returns NULL on failure and sets scf_error() to:
8391f6eb02Liane Praza *   SCF_ERROR_CONNECTION_BROKEN
8401f6eb02Liane Praza *   SCF_ERROR_DELETED
8411f6eb02Liane Praza *   SCF_ERROR_INVALID_ARGUMENT
8421f6eb02Liane Praza *     can't combine the arguments and get a reasonable length name
8431f6eb02Liane Praza *   SCF_ERROR_NO_MEMORY
8441f6eb02Liane Praza */
8451f6eb02Liane Prazastatic char *
8461f6eb02Liane Praza_tmpl_prop_name(const char *prop, scf_pg_tmpl_t *t)
8471f6eb02Liane Praza{
8481f6eb02Liane Praza	char *name = NULL, *pg_name = NULL;
8491f6eb02Liane Praza	size_t prefix_size;
8501f6eb02Liane Praza	ssize_t limit, size = 0;
8511f6eb02Liane Praza
8521f6eb02Liane Praza	assert(prop != NULL);
8531f6eb02Liane Praza	assert(t->pt_pg != NULL);
8541f6eb02Liane Praza
8551f6eb02Liane Praza	limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
8561f6eb02Liane Praza	name = malloc(limit);
8571f6eb02Liane Praza	if (name == NULL) {
8581f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
8591f6eb02Liane Praza		return (NULL);
8601f6eb02Liane Praza	}
8611f6eb02Liane Praza
8621f6eb02Liane Praza	if ((pg_name = _scf_get_pg_name(t->pt_pg)) == NULL) {
8631f6eb02Liane Praza		free(name);
8641f6eb02Liane Praza		return (NULL);
8651f6eb02Liane Praza	}
8661f6eb02Liane Praza
8671f6eb02Liane Praza	prefix_size = strlen(SCF_PG_TM_PG_PAT_BASE);
8681f6eb02Liane Praza	if (strncmp(pg_name, SCF_PG_TM_PG_PAT_BASE, prefix_size) != 0) {
8691f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
8701f6eb02Liane Praza		free(name);
8711f6eb02Liane Praza		free(pg_name);
8721f6eb02Liane Praza		return (NULL);
8731f6eb02Liane Praza	}
8741f6eb02Liane Praza
8751f6eb02Liane Praza	size = snprintf(name, limit, "%s%s_%s", SCF_PG_TM_PROP_PATTERN_PREFIX,
8761f6eb02Liane Praza	    pg_name + prefix_size, prop);
8771f6eb02Liane Praza
8781f6eb02Liane Praza	if (size >= limit) {
8791f6eb02Liane Praza		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
8801f6eb02Liane Praza		free(name);
8811f6eb02Liane Praza		free(pg_name);
8821f6eb02Liane Praza		return (NULL);
8831f6eb02Liane Praza	} else {
8841f6eb02Liane Praza		free(pg_name);
8851f6eb02Liane Praza		return (name);
8861f6eb02Liane Praza	}
8871f6eb02Liane Praza}
8881f6eb02Liane Praza
8891f6eb02Liane Praza/*
8901f6eb02Liane Praza *  int _get_snapshot()
8911f6eb02Liane Praza *
8921f6eb02Liane Praza *  Gets the specified snapshot.  If "snapshot" isn't defined, use the
8931f6eb02Liane Praza *  running snapshot.  If the snapshot isn't found, that may or may
8941f6eb02Liane Praza *  not be an error depending on the caller.  Return 0 in that case,
8951f6eb02Liane Praza *  but leave scf_error() set to SCF_ERROR_NOT_FOUND.  On all other
8961f6eb02Liane Praza *  errors, set scf_error() to:
8971f6eb02Liane Praza *   SCF_ERROR_BACKEND_ACCESS
8981f6eb02Liane Praza *   SCF_ERROR_CONNECTION_BROKEN
8991f6eb02Liane Praza *   SCF_ERROR_DELETED
9001f6eb02Liane Praza *   SCF_ERR_HANDLE_DESTROYED
9011f6eb02Liane Praza *   SCF_ERROR_INTERNAL
9021f6eb02Liane Praza *   SCF_ERROR_INVALID_ARGUMENT
9031f6eb02Liane Praza *     The handle argument is NULL, or snaphot is not a valid snapshot name
9041f6eb02Liane Praza *   SCF_ERROR_NO_MEMORY
9051f6eb02Liane Praza *   SCF_ERROR_NO_RESOURCES
9061f6eb02Liane Praza *   SCF_ERROR_NOT_BOUND
9071f6eb02Liane Praza *   SCF_ERROR_NOT_FOUND
9081f6eb02Liane Praza */
9091f6eb02Liane Prazastatic int
9101f6eb02Liane Praza_get_snapshot(scf_instance_t *inst, const char *snapshot,
9111f6eb02Liane Praza    scf_snapshot_t **snap)
9121f6eb02Liane Praza{
9131f6eb02Liane Praza	int err;
9141f6eb02Liane Praza	scf_handle_t *h;
9151f6eb02Liane Praza
9161f6eb02Liane Praza	h = scf_instance_handle(inst);
917a58a7feMilan Jurik	if (h == NULL) {
918a58a7feMilan Jurik		*snap = NULL;
9191f6eb02Liane Praza		return (-1);
920a58a7feMilan Jurik	}
9211f6eb02Liane Praza
9221f6eb02Liane Praza	if ((*snap = scf_snapshot_create(h)) == NULL) {
9231f6eb02Liane Praza		return (-1);
9241f6eb02Liane Praza	}
9251f6eb02Liane Praza
9261f6eb02Liane Praza	/* Use running snapshot by default. */
9271f6eb02Liane Praza	if (snapshot == NULL)
9281f6eb02Liane Praza		err = scf_instance_get_snapshot(inst, "running", *snap);
9291f6eb02Liane Praza	else
9301f6eb02Liane Praza		err = scf_instance_get_snapshot(inst, snapshot, *snap);
9311f6eb02Liane Praza
9321f6eb02Liane Praza	if (err != 0) {
9331f6eb02Liane Praza		if (ismember(scf_error(), errors_server)) {
9341f6eb02Liane Praza			scf_snapshot_destroy(*snap);
9351f6eb02Liane Praza			*snap = NULL;
9361f6eb02Liane Praza			return (-1);
9371f6eb02Liane Praza		} else switch (scf_error()) {
9381f6eb02Liane Praza		case SCF_ERROR_INVALID_ARGUMENT:
9391f6eb02Liane Praza			scf_snapshot_destroy(*snap);
9401f6eb02Liane Praza			*snap = NULL;