2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * scf_tmpl.c
2N/A *
2N/A * This file implements the bulk of the libscf templates interfaces.
2N/A * Templates describe metadata about a service or instance in general,
2N/A * and individual configuration properties on those services and instances.
2N/A * Human-consumable descriptions can be provided, along with definitions
2N/A * of valid configuration. See service_bundle.dtd.1 for XML definitions
2N/A * of templates, and the svccfg code for information on how those definitions
2N/A * are translated into the repository.
2N/A *
2N/A * The main data structures are scf_pg_tmpl and scf_prop_tmpl. These
2N/A * are allocated by the callers through scf_tmpl_[pg|prop]_create(), and
2N/A * destroyed with scf_tmpl_[pg|prop]_destroy(). They are populated by
2N/A * scf_tmpl_get_by_pg_name(), scf_tmpl_get_by_pg(), and
2N/A * scf_tmpl_get_by_prop(). They also store the iterator state for
2N/A * scf_tmpl_iter_pgs() and scf_tmpl_iter_props().
2N/A *
2N/A * These data structures are then consumed by other functions to
2N/A * gather information about the template (e.g. name, description,
2N/A * choices, constraints, etc.).
2N/A *
2N/A * scf_tmpl_validate_fmri() does instance validation against template
2N/A * data, and populates a set of template errors which can be explored using
2N/A * the scf_tmpl_next_error() and the scf_tmpl_error*() suite of functions.
2N/A *
2N/A * The main data structures for template errors are scf_tmpl_errors,
2N/A * defined in this file, and scf_tmpl_error, defined in libscf_priv.h.
2N/A * scf_tmpl_error is shared with svccfg to offer common printing
2N/A * of error messages between libscf and svccfg.
2N/A *
2N/A * General convenience functions are towards the top of this file,
2N/A * followed by pg and prop template discovery functions, followed
2N/A * by functions which gather information about the discovered
2N/A * template. Validation and error functions are at the end of this file.
2N/A */
2N/A
2N/A#include "lowlevel_impl.h"
2N/A#include "libscf_impl.h"
2N/A#include <assert.h>
2N/A#include <errno.h>
2N/A#include <libintl.h>
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <strings.h>
2N/A#include <locale.h>
2N/A#include <ctype.h>
2N/A#include <inttypes.h>
2N/A
2N/A#define SCF_TMPL_PG_COMMON_NAME_C "common_name_C"
2N/A
2N/A#define SCF__TMPL_ITER_NONE 0
2N/A#define SCF__TMPL_ITER_INST 1
2N/A#define SCF__TMPL_ITER_RESTARTER 2
2N/A#define SCF__TMPL_ITER_GLOBAL 3
2N/A
2N/A#define SCF_TMPL_PG_NT 0
2N/A#define SCF_TMPL_PG_N 1
2N/A#define SCF_TMPL_PG_T 2
2N/A#define SCF_TMPL_PG_WILD 3
2N/A
2N/Astruct scf_pg_tmpl {
2N/A int pt_populated;
2N/A scf_handle_t *pt_h;
2N/A scf_propertygroup_t *pt_pg;
2N/A scf_service_t *pt_orig_svc;
2N/A scf_service_t *pt_svc;
2N/A scf_instance_t *pt_orig_inst;
2N/A scf_instance_t *pt_inst;
2N/A scf_snapshot_t *pt_snap;
2N/A int pt_is_iter;
2N/A scf_iter_t *pt_iter;
2N/A int pt_iter_last;
2N/A};
2N/A
2N/A#define SCF_WALK_ERROR -1
2N/A#define SCF_WALK_NEXT 0
2N/A#define SCF_WALK_DONE 1
2N/A
2N/Astruct pg_tmpl_walk {
2N/A const char *pw_snapname;
2N/A const char *pw_pgname;
2N/A const char *pw_pgtype;
2N/A scf_instance_t *pw_inst;
2N/A scf_service_t *pw_svc;
2N/A scf_snapshot_t *pw_snap;
2N/A scf_propertygroup_t *pw_pg;
2N/A const char *pw_target;
2N/A char *pw_tmpl_pgname;
2N/A};
2N/A
2N/Atypedef struct pg_tmpl_walk pg_tmpl_walk_t;
2N/A
2N/Atypedef int walk_template_inst_func_t(scf_service_t *_svc,
2N/A scf_instance_t *_inst, pg_tmpl_walk_t *p);
2N/A
2N/Astruct scf_prop_tmpl {
2N/A int prt_populated;
2N/A scf_handle_t *prt_h;
2N/A scf_pg_tmpl_t *prt_t;
2N/A scf_propertygroup_t *prt_pg;
2N/A char *prt_pg_name;
2N/A scf_iter_t *prt_iter;
2N/A};
2N/A
2N/A/*
2N/A * Common server errors are usually passed back to the caller. This
2N/A * array defines them centrally so that they don't need to be enumerated
2N/A * in every libscf call.
2N/A */
2N/Astatic const scf_error_t errors_server[] = {
2N/A SCF_ERROR_BACKEND_ACCESS,
2N/A SCF_ERROR_CONNECTION_BROKEN,
2N/A SCF_ERROR_DELETED,
2N/A SCF_ERROR_HANDLE_DESTROYED,
2N/A SCF_ERROR_INTERNAL,
2N/A SCF_ERROR_NO_MEMORY,
2N/A SCF_ERROR_NO_RESOURCES,
2N/A SCF_ERROR_NOT_BOUND,
2N/A SCF_ERROR_PERMISSION_DENIED,
2N/A 0
2N/A };
2N/A
2N/A/*
2N/A * int ismember()
2N/A *
2N/A * Returns 1 if the supplied error is a member of the error array, 0
2N/A * if it is not.
2N/A */
2N/Aint
2N/Aismember(const scf_error_t error, const scf_error_t error_array[])
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; error_array[i] != 0; ++i) {
2N/A if (error == error_array[i])
2N/A return (1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * char *_scf_tmpl_get_fmri()
2N/A *
2N/A * Given a pg_tmpl, returns the FMRI of the service or instance that
2N/A * template describes. The allocated string must be freed with free().
2N/A *
2N/A * On failure, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
2N/A * _DELETED, or _NO_MEMORY.
2N/A */
2N/Astatic char *
2N/A_scf_tmpl_get_fmri(const scf_pg_tmpl_t *t)
2N/A{
2N/A ssize_t sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
2N/A int r;
2N/A char *buf = malloc(sz);
2N/A
2N/A assert(t->pt_svc != NULL || t->pt_inst != NULL);
2N/A assert(t->pt_svc == NULL || t->pt_inst == NULL);
2N/A
2N/A if (buf == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (buf);
2N/A }
2N/A
2N/A if (t->pt_inst != NULL)
2N/A r = scf_instance_to_fmri(t->pt_inst, buf, sz);
2N/A else
2N/A r = scf_service_to_fmri(t->pt_svc, buf, sz);
2N/A
2N/A if (r == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A free(buf);
2N/A buf = NULL;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * char *_scf_get_pg_type()
2N/A *
2N/A * Given a propertygroup, returns an allocated string containing the
2N/A * type. The string must be freed with free().
2N/A *
2N/A * On failure, returns NULL and sets scf_error() to: _CONNECTION_BROKEN,
2N/A * _DELETED, or _NO_MEMORY.
2N/A */
2N/Astatic char *
2N/A_scf_get_pg_type(scf_propertygroup_t *pg)
2N/A{
2N/A ssize_t sz = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
2N/A char *buf = malloc(sz);
2N/A
2N/A if (buf == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A } else if (scf_pg_get_type(pg, buf, sz) == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A free(buf);
2N/A buf = NULL;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * char *_scf_get_prop_name()
2N/A *
2N/A * Given a property, returns the name in an allocated string. The string must
2N/A * be freed with free().
2N/A *
2N/A * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
2N/A * _DELETED, or _NO_MEMORY.
2N/A */
2N/Astatic char *
2N/A_scf_get_prop_name(scf_property_t *prop)
2N/A{
2N/A ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A char *buf = malloc(sz);
2N/A
2N/A if (buf == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A } else if (scf_property_get_name(prop, buf, sz) == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A free(buf);
2N/A buf = NULL;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * char *_scf_get_prop_type()
2N/A *
2N/A * Given a property, returns the type in an allocated string. The string must
2N/A * be freed with free().
2N/A *
2N/A * On error, returns NULL and sets scf_error() to _CONNECTION_BROKEN,
2N/A * _DELETED, or _NO_MEMORY.
2N/A */
2N/Astatic char *
2N/A_scf_get_prop_type(scf_property_t *prop)
2N/A{
2N/A scf_type_t type;
2N/A char *ret;
2N/A
2N/A if (scf_property_type(prop, &type) == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (NULL);
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A ret = strdup(scf_type_to_string(type));
2N/A if (ret == NULL)
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * int _read_single_value_from_pg()
2N/A *
2N/A * Reads a single value from the pg and property name specified. On success,
2N/A * returns an allocated value that must be freed.
2N/A *
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_CONSTRAINT_VIOLATED
2N/A * Property has more than one value associated with it.
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * prop_name not a valid property name.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_NOT_SET
2N/A * Property group specified by pg is not set.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A */
2N/Astatic int
2N/A_read_single_value_from_pg(scf_propertygroup_t *pg, const char *prop_name,
2N/A scf_value_t **val)
2N/A{
2N/A scf_handle_t *h;
2N/A scf_property_t *prop;
2N/A int ret = 0;
2N/A
2N/A assert(val != NULL);
2N/A if ((h = scf_pg_handle(pg)) == NULL) {
2N/A assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
2N/A return (-1);
2N/A }
2N/A
2N/A prop = scf_property_create(h);
2N/A *val = scf_value_create(h);
2N/A
2N/A if (prop == NULL || *val == NULL) {
2N/A assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
2N/A goto read_single_value_from_pg_fail;
2N/A }
2N/A
2N/A if (scf_pg_get_property(pg, prop_name, prop) != 0) {
2N/A assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
2N/A goto read_single_value_from_pg_fail;
2N/A }
2N/A
2N/A if (scf_property_get_value(prop, *val) == -1) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
2N/A goto read_single_value_from_pg_fail;
2N/A }
2N/A
2N/A goto read_single_value_from_pg_done;
2N/A
2N/Aread_single_value_from_pg_fail:
2N/A scf_value_destroy(*val);
2N/A *val = NULL;
2N/A ret = -1;
2N/A
2N/Aread_single_value_from_pg_done:
2N/A scf_property_destroy(prop);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * char *_scf_read_single_astring_from_pg()
2N/A *
2N/A * Reads an astring from the pg and property name specified. On success,
2N/A * returns an allocated string. The string must be freed with free().
2N/A *
2N/A * Returns NULL on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_CONSTRAINT_VIOLATED
2N/A * Property has more than one value associated with it.
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * prop_name not a valid property name.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_NOT_SET
2N/A * The property group specified by pg is not set.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TYPE_MISMATCH
2N/A */
2N/Achar *
2N/A_scf_read_single_astring_from_pg(scf_propertygroup_t *pg, const char *prop_name)
2N/A{
2N/A scf_value_t *val;
2N/A char *ret = NULL;
2N/A ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
2N/A
2N/A assert(rsize != 0);
2N/A if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
2N/A return (NULL);
2N/A
2N/A ret = malloc(rsize);
2N/A if (ret == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (scf_value_get_astring(val, ret, rsize) < 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A free(ret);
2N/A ret = NULL;
2N/A }
2N/A
2N/Acleanup:
2N/A scf_value_destroy(val);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * char *_scf_read_tmpl_prop_type_as_string()
2N/A *
2N/A * Reads the property type and returns it as an allocated string. The string
2N/A * must be freed with free().
2N/A *
2N/A * Returns NULL on failure, sets scf_error() to _BACKEND_ACCESS,
2N/A * _CONNECTION_BROKEN, _DELETED, _HANDLE_DESTROYED, _INTERNAL, _NO_MEMORY,
2N/A * _NO_RESOURCES, _NOT_BOUND, _PERMISSION_DENIED, or _TEMPLATE_INVALID.
2N/A */
2N/Achar *
2N/A_scf_read_tmpl_prop_type_as_string(const scf_prop_tmpl_t *pt)
2N/A{
2N/A char *type;
2N/A
2N/A type = _scf_read_single_astring_from_pg(pt->prt_pg,
2N/A SCF_PROPERTY_TM_TYPE);
2N/A if (type == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (NULL);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (NULL);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (type);
2N/A}
2N/A
2N/A/*
2N/A * int _read_single_boolean_from_pg()
2N/A *
2N/A * Reads a boolean from the pg and property name specified.
2N/A *
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_CONSTRAINT_VIOLATED
2N/A * Property has more than one value associated with it.
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * prop_name is not a valid property name.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_NOT_SET
2N/A * The property group specified by pg is not set.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TYPE_MISMATCH
2N/A */
2N/Astatic int
2N/A_read_single_boolean_from_pg(scf_propertygroup_t *pg, const char *prop_name,
2N/A uint8_t *bool)
2N/A{
2N/A scf_value_t *val;
2N/A int ret = 0;
2N/A
2N/A if (_read_single_value_from_pg(pg, prop_name, &val) == -1)
2N/A return (-1);
2N/A
2N/A if (scf_value_get_boolean(val, bool) < 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A ret = -1;
2N/A }
2N/A
2N/A scf_value_destroy(val);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * static char ** _append_astrings_values()
2N/A *
2N/A * This function reads the values from the property prop_name in pg and
2N/A * appends to an existing scf_values_t *vals. vals may be empty, but
2N/A * must exist. The function skips over zero-length and duplicate values.
2N/A *
2N/A * Returns NULL on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * prop_name is not a valid property name.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * SCF_ERROR_NOT_SET
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TYPE_MISMATCH
2N/A */
2N/Astatic char **
2N/A_append_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
2N/A scf_values_t *vals)
2N/A{
2N/A scf_handle_t *h;
2N/A scf_property_t *prop;
2N/A scf_value_t *val;
2N/A scf_iter_t *iter;
2N/A ssize_t rsize = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
2N/A int err, count, cursz, i;
2N/A
2N/A assert(vals != NULL);
2N/A assert(vals->value_type == SCF_TYPE_ASTRING);
2N/A assert(vals->reserved == NULL);
2N/A count = vals->value_count;
2N/A if (count == 0) {
2N/A cursz = 8;
2N/A vals->values.v_astring = calloc(cursz, sizeof (char *));
2N/A if (vals->values.v_astring == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (NULL);
2N/A }
2N/A } else {
2N/A /*
2N/A * The array may be bigger, but it is irrelevant since
2N/A * we will always re-allocate a new one.
2N/A */
2N/A cursz = count;
2N/A }
2N/A
2N/A if ((h = scf_pg_handle(pg)) == NULL) {
2N/A assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
2N/A return (NULL);
2N/A }
2N/A
2N/A prop = scf_property_create(h);
2N/A val = scf_value_create(h);
2N/A iter = scf_iter_create(h);
2N/A
2N/A if (prop == NULL || val == NULL || iter == NULL) {
2N/A assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
2N/A goto append_single_astring_from_pg_fail;
2N/A }
2N/A
2N/A if (scf_pg_get_property(pg, prop_name, prop) != 0) {
2N/A assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
2N/A goto append_single_astring_from_pg_fail;
2N/A }
2N/A
2N/A if (scf_iter_property_values(iter, prop) != 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
2N/A goto append_single_astring_from_pg_fail;
2N/A }
2N/A
2N/A while ((err = scf_iter_next_value(iter, val)) == 1) {
2N/A int flag;
2N/A int r;
2N/A
2N/A if (count + 1 >= cursz) {
2N/A void *aux;
2N/A
2N/A cursz *= 2;
2N/A if ((aux = calloc(cursz, sizeof (char *))) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto append_single_astring_from_pg_fail;
2N/A }
2N/A (void) memcpy(aux, vals->values.v_astring,
2N/A count * sizeof (char *));
2N/A free(vals->values.v_astring);
2N/A vals->values.v_astring = aux;
2N/A }
2N/A
2N/A vals->values.v_astring[count] = malloc(rsize);
2N/A if (vals->values.v_astring[count] == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto append_single_astring_from_pg_fail;
2N/A }
2N/A
2N/A if ((r = scf_value_get_astring(val,
2N/A vals->values.v_astring[count], rsize)) <= 0) {
2N/A /* discard zero length strings */
2N/A if (r == 0) {
2N/A free(vals->values.v_astring[count]);
2N/A continue;
2N/A }
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A goto append_single_astring_from_pg_fail;
2N/A }
2N/A for (i = 0, flag = 0; i < count; ++i) {
2N/A /* find and discard duplicates */
2N/A if (strncmp(vals->values.v_astring[i],
2N/A vals->values.v_astring[count], rsize) == 0) {
2N/A free(vals->values.v_astring[count]);
2N/A flag = 1;
2N/A break;
2N/A }
2N/A }
2N/A if (flag == 1)
2N/A continue;
2N/A
2N/A count++;
2N/A }
2N/A
2N/A vals->value_count = count;
2N/A
2N/A if (err != 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
2N/A assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
2N/A goto append_single_astring_from_pg_fail;
2N/A } else {
2N/A vals->values_as_strings = vals->values.v_astring;
2N/A }
2N/A
2N/A goto append_single_astring_from_pg_done;
2N/A
2N/Aappend_single_astring_from_pg_fail:
2N/A for (i = 0; i <= count; ++i) {
2N/A if (vals->values.v_astring[i] != NULL)
2N/A free(vals->values.v_astring[i]);
2N/A vals->values.v_astring[i] = NULL;
2N/A }
2N/A free(vals->values.v_astring);
2N/A vals->values.v_astring = NULL;
2N/A vals->value_count = 0;
2N/A
2N/Aappend_single_astring_from_pg_done:
2N/A scf_iter_destroy(iter);
2N/A scf_property_destroy(prop);
2N/A scf_value_destroy(val);
2N/A return (vals->values.v_astring);
2N/A}
2N/A
2N/A/*
2N/A * Returns NULL on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * prop_name is not a valid property name.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * SCF_ERROR_NOT_SET
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TYPE_MISMATCH
2N/A */
2N/Astatic char **
2N/A_read_astrings_values(scf_propertygroup_t *pg, const char *prop_name,
2N/A scf_values_t *vals)
2N/A{
2N/A assert(vals != NULL);
2N/A vals->value_count = 0;
2N/A vals->value_type = SCF_TYPE_ASTRING;
2N/A vals->reserved = NULL;
2N/A return (_append_astrings_values(pg, prop_name, vals));
2N/A}
2N/A
2N/Avoid
2N/A_scf_sanitize_locale(char *locale)
2N/A{
2N/A for (; *locale != '\0'; locale++)
2N/A if (!isalnum(*locale) && *locale != '_')
2N/A *locale = '_';
2N/A}
2N/A
2N/A/*
2N/A * The returned string needs to be freed by the caller
2N/A * Returns NULL on failure. Sets scf_error() to:
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * Name isn't short enough to add the locale to.
2N/A */
2N/Astatic char *
2N/A_add_locale_to_name(const char *name, const char *locale)
2N/A{
2N/A char *lname = NULL;
2N/A ssize_t lsz;
2N/A char *loc;
2N/A
2N/A assert(locale != NULL);
2N/A loc = strdup(locale);
2N/A if (loc == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (NULL);
2N/A } else {
2N/A _scf_sanitize_locale(loc);
2N/A }
2N/A
2N/A lsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A lname = malloc(lsz);
2N/A if (lname == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A (void) strlcpy(lname, name, lsz);
2N/A if (strlcat(lname, loc, lsz) >= lsz) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A free(lname);
2N/A lname = NULL;
2N/A }
2N/Acleanup:
2N/A free(loc);
2N/A
2N/A return (lname);
2N/A}
2N/A
2N/A/*
2N/A * char *_tmpl_pg_name(pg, type, use_type)
2N/A *
2N/A * pg and type can both be NULL. Returns the name of the most specific
2N/A * template property group name based on the inputs.
2N/A * If use_type is set and pg is not NULL, a property group name for a
2N/A * property group template that has type defined is returned, even if no
2N/A * type is provided.
2N/A *
2N/A * Returns NULL on failure and sets scf_error() to:
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * can't combine the arguments and get a reasonable length name
2N/A * SCF_ERROR_NO_MEMORY
2N/A *
2N/A */
2N/Astatic char *
2N/A_tmpl_pg_name(const char *pg, const char *type, int use_type)
2N/A{
2N/A char *name;
2N/A ssize_t limit, size = 0;
2N/A
2N/A limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A name = malloc(limit);
2N/A if (name == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (pg == NULL && type == NULL) {
2N/A if (strlcpy(name, SCF_PG_TM_PG_PATTERN_PREFIX, limit) >=
2N/A limit) {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A return (name);
2N/A } else if (pg != NULL && type != NULL) {
2N/A size = snprintf(name, limit, "%s%s",
2N/A SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
2N/A } else if (pg != NULL && type == NULL && use_type == 1) {
2N/A size = snprintf(name, limit, "%s%s",
2N/A SCF_PG_TM_PG_PATTERN_NT_PREFIX, pg);
2N/A } else if (pg != NULL && type == NULL) {
2N/A size = snprintf(name, limit, "%s%s",
2N/A SCF_PG_TM_PG_PATTERN_N_PREFIX, pg);
2N/A } else if (type != NULL && pg == NULL) {
2N/A size = snprintf(name, limit, "%s%s",
2N/A SCF_PG_TM_PG_PATTERN_T_PREFIX, type);
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A
2N/A if (size >= limit) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A free(name);
2N/A return (NULL);
2N/A } else {
2N/A return (name);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * _scf_get_pg_name()
2N/A * Gets the name of the supplied property group. On success, returns an
2N/A * allocated string. The string must be freed by free().
2N/A *
2N/A * Returns NULL on failure and sets scf_error() to _CONNECTION_BROKEN,
2N/A * _DELETED, or _NO_MEMORY.
2N/A */
2N/Astatic char *
2N/A_scf_get_pg_name(scf_propertygroup_t *pg)
2N/A{
2N/A ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A char *buf = malloc(sz);
2N/A
2N/A if (buf == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A } else if (scf_pg_get_name(pg, buf, sz) == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A free(buf);
2N/A buf = NULL;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * char *_tmpl_prop_name()
2N/A *
2N/A * Returns the name of the property template prop (which is the name of
2N/A * the property template property group) in the property group
2N/A * template t. Returns NULL on failure and sets scf_error() to:
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * can't combine the arguments and get a reasonable length name
2N/A * SCF_ERROR_NO_MEMORY
2N/A */
2N/Astatic char *
2N/A_tmpl_prop_name(const char *prop, scf_pg_tmpl_t *t)
2N/A{
2N/A char *name = NULL, *pg_name = NULL;
2N/A size_t prefix_size;
2N/A ssize_t limit, size = 0;
2N/A
2N/A assert(prop != NULL);
2N/A assert(t->pt_pg != NULL);
2N/A
2N/A limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A name = malloc(limit);
2N/A if (name == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((pg_name = _scf_get_pg_name(t->pt_pg)) == NULL) {
2N/A free(name);
2N/A return (NULL);
2N/A }
2N/A
2N/A prefix_size = strlen(SCF_PG_TM_PG_PAT_BASE);
2N/A if (strncmp(pg_name, SCF_PG_TM_PG_PAT_BASE, prefix_size) != 0) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A free(name);
2N/A free(pg_name);
2N/A return (NULL);
2N/A }
2N/A
2N/A size = snprintf(name, limit, "%s%s_%s", SCF_PG_TM_PROP_PATTERN_PREFIX,
2N/A pg_name + prefix_size, prop);
2N/A
2N/A if (size >= limit) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A free(name);
2N/A free(pg_name);
2N/A return (NULL);
2N/A } else {
2N/A free(pg_name);
2N/A return (name);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * int _get_snapshot()
2N/A *
2N/A * Gets the specified snapshot. If "snapshot" isn't defined, use the
2N/A * running snapshot. If the snapshot isn't found, that may or may
2N/A * not be an error depending on the caller. Return 0 in that case,
2N/A * but leave scf_error() set to SCF_ERROR_NOT_FOUND. On all other
2N/A * errors, set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * The handle argument is NULL, or snaphot is not a valid snapshot name
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A */
2N/Astatic int
2N/A_get_snapshot(scf_instance_t *inst, const char *snapshot,
2N/A scf_snapshot_t **snap)
2N/A{
2N/A int err;
2N/A scf_handle_t *h;
2N/A
2N/A h = scf_instance_handle(inst);
2N/A if (h == NULL)
2N/A return (-1);
2N/A
2N/A if ((*snap = scf_snapshot_create(h)) == NULL) {
2N/A return (-1);
2N/A }
2N/A
2N/A /* Use running snapshot by default. */
2N/A if (snapshot == NULL)
2N/A err = scf_instance_get_snapshot(inst, "running", *snap);
2N/A else
2N/A err = scf_instance_get_snapshot(inst, snapshot, *snap);
2N/A
2N/A if (err != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A scf_snapshot_destroy(*snap);
2N/A *snap = NULL;
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A scf_snapshot_destroy(*snap);
2N/A *snap = NULL;
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_NOT_FOUND:
2N/A scf_snapshot_destroy(*snap);
2N/A *snap = NULL;
2N/A return (0);
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Explicitly set SCF_ERROR_NONE so that the SCF_ERROR_NOT_FOUND
2N/A * return above is explicitly guaranteed to be from
2N/A * scf_instance_get_snapshot().
2N/A */
2N/A (void) scf_set_error(SCF_ERROR_NONE);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Returns NULL on error, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_CONSTRAINT_VIOLATED
2N/A * The restarter's FMRI does not match an existing instance.
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * The restarter's FMRI is not a valid FMRI.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * restarter property is not SCF_TYPE_ASTRING or has more than one value
2N/A */
2N/Astatic scf_instance_t *
2N/A_get_restarter_inst(scf_handle_t *h, scf_service_t *svc,
2N/A scf_instance_t *inst, scf_snapshot_t *s)
2N/A{
2N/A char *restarter = NULL;
2N/A scf_instance_t *ri = NULL;
2N/A scf_propertygroup_t *pg = NULL;
2N/A int ret = 0;
2N/A
2N/A assert(svc != NULL || inst != NULL);
2N/A assert(svc == NULL || inst == NULL);
2N/A
2N/A if ((ri = scf_instance_create(h)) == NULL ||
2N/A (pg = scf_pg_create(h)) == NULL) {
2N/A assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
2N/A goto _get_restarter_inst_fail;
2N/A }
2N/A
2N/A if (inst != NULL)
2N/A ret = scf_instance_get_pg_composed(inst, s, SCF_PG_GENERAL,
2N/A pg);
2N/A else
2N/A ret = scf_service_get_pg(svc, SCF_PG_GENERAL, pg);
2N/A
2N/A if (ret != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto _get_restarter_inst_fail;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A /* Assume default restarter. */
2N/A break;
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A /*
2N/A * If the arguments to the above functions
2N/A * aren't derived from the same handle, there's
2N/A * something wrong with the internal implementation,
2N/A * not the public caller further up the chain.
2N/A */
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A } else {
2N/A restarter = _scf_read_single_astring_from_pg(pg,
2N/A SCF_PROPERTY_RESTARTER);
2N/A /* zero length string is NOT a valid restarter */
2N/A if (restarter != NULL && restarter[0] == '\0') {
2N/A free(restarter);
2N/A restarter = NULL;
2N/A } else if (restarter == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto _get_restarter_inst_fail;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A break;
2N/A
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(
2N/A SCF_ERROR_TEMPLATE_INVALID);
2N/A goto _get_restarter_inst_fail;
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (restarter == NULL) {
2N/A /* Use default restarter */
2N/A restarter = strdup(SCF_SERVICE_STARTD);
2N/A if (restarter == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto _get_restarter_inst_fail;
2N/A }
2N/A }
2N/A
2N/A if (scf_handle_decode_fmri(h, restarter, NULL, NULL, ri, NULL, NULL,
2N/A SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto _get_restarter_inst_fail;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A goto _get_restarter_inst_fail;
2N/A
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A free(restarter);
2N/A scf_pg_destroy(pg);
2N/A
2N/A return (ri);
2N/A
2N/A_get_restarter_inst_fail:
2N/A free(restarter);
2N/A scf_instance_destroy(ri);
2N/A scf_pg_destroy(pg);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Returns NULL on error, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_CONSTRAINT_VIOLATED
2N/A * Restarter property has more than one value associated with it,
2N/A * or FMRI does not meet restrictions in scf_handle_decode_fmri() flags.
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * The fmri argument in scf_handle_decode_fmri() is not a valid FMRI.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A */
2N/Astatic scf_instance_t *
2N/A_get_global_inst(scf_handle_t *h)
2N/A{
2N/A scf_instance_t *ri;
2N/A
2N/A if ((ri = scf_instance_create(h)) == NULL) {
2N/A assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
2N/A (void) scf_set_error(SCF_ERROR_NO_RESOURCES);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (scf_handle_decode_fmri(h, SCF_INSTANCE_GLOBAL, NULL, NULL, ri,
2N/A NULL, NULL,
2N/A SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A scf_instance_destroy(ri);
2N/A return (NULL);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A scf_instance_destroy(ri);
2N/A return (NULL);
2N/A
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (ri);
2N/A}
2N/A
2N/A/*
2N/A * Call the supplied function for each of the service or instance, the
2N/A * service's restarter, and the globally defined template instance.
2N/A * If the function returns SCF_WALK_ERROR, the walk is ended. If
2N/A * the function returns SCF_WALK_NEXT, the next entity is tried.
2N/A *
2N/A * The function is only expected to return SCF_WALK_DONE if it has
2N/A * found a property group match in the current entity, and has
2N/A * populated p->pw_pg with the matching property group.
2N/A *
2N/A * The caller of _walk_template_instances() MUST check if the passed parameters
2N/A * inst and svc match the fields pw_inst and pw_svc in the resulting
2N/A * pg_tmpl_walk_t and call the destructor for the unmatching objects. The walker
2N/A * may silently drop them if the template definition is in the restarter or in
2N/A * the global instance.
2N/A */
2N/Astatic void
2N/A_walk_template_instances(scf_service_t *svc, scf_instance_t *inst,
2N/A scf_snapshot_t *snap, walk_template_inst_func_t *func,
2N/A pg_tmpl_walk_t *p, int flag)
2N/A{
2N/A scf_instance_t *tmpl_inst = NULL;
2N/A scf_handle_t *h;
2N/A int ret;
2N/A char *tg = NULL;
2N/A
2N/A assert(svc != NULL || inst != NULL);
2N/A assert(svc == NULL || inst == NULL);
2N/A
2N/A if (inst != NULL)
2N/A h = scf_instance_handle(inst);
2N/A else
2N/A h = scf_service_handle(svc);
2N/A if (h == NULL)
2N/A goto done;
2N/A
2N/A /* First, use supplied service or instance */
2N/A p->pw_target = SCF_TM_TARGET_THIS;
2N/A ret = func(svc, inst, p);
2N/A switch (ret) {
2N/A case SCF_WALK_NEXT:
2N/A break;
2N/A case SCF_WALK_DONE:
2N/A /*
2N/A * Check that the template scoping matches and if not,
2N/A * continue.
2N/A */
2N/A assert(p->pw_pg != NULL);
2N/A tg = _scf_read_single_astring_from_pg(p->pw_pg,
2N/A SCF_PROPERTY_TM_TARGET);
2N/A if (tg == NULL || /* scf_error() was set */
2N/A (strcmp(tg, SCF_TM_TARGET_INSTANCE) != 0 &&
2N/A strcmp(tg, SCF_TM_TARGET_THIS) != 0 &&
2N/A (flag & SCF_PG_TMPL_FLAG_EXACT) !=
2N/A SCF_PG_TMPL_FLAG_EXACT)) {
2N/A scf_pg_destroy(p->pw_pg);
2N/A p->pw_pg = NULL;
2N/A if (tg != NULL) {
2N/A free(tg);
2N/A tg = NULL;
2N/A break;
2N/A }
2N/A }
2N/A /*FALLTHROUGH*/
2N/A case SCF_WALK_ERROR:
2N/A goto done;
2N/A /*NOTREACHED*/
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A
2N/A /* Next the restarter. */
2N/A p->pw_target = SCF_TM_TARGET_DELEGATE;
2N/A tmpl_inst = _get_restarter_inst(h, svc, inst, snap);
2N/A if (tmpl_inst != NULL) {
2N/A ret = func(NULL, tmpl_inst, p);
2N/A switch (ret) {
2N/A case SCF_WALK_NEXT:
2N/A break;
2N/A case SCF_WALK_DONE:
2N/A /*
2N/A * Check that the template scoping matches and if not,
2N/A * continue.
2N/A */
2N/A assert(p->pw_pg != NULL);
2N/A tg = _scf_read_single_astring_from_pg(p->pw_pg,
2N/A SCF_PROPERTY_TM_TARGET);
2N/A if (tg == NULL || /* scf_error() was set */
2N/A strcmp(tg, SCF_TM_TARGET_DELEGATE) != 0) {
2N/A scf_pg_destroy(p->pw_pg);
2N/A p->pw_pg = NULL;
2N/A if (tg != NULL) {
2N/A free(tg);
2N/A tg = NULL;
2N/A break;
2N/A }
2N/A }
2N/A /*FALLTHROUGH*/
2N/A case SCF_WALK_ERROR:
2N/A goto done;
2N/A /*NOTREACHED*/
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A p->pw_target = SCF_TM_TARGET_ALL;
2N/A scf_instance_destroy(tmpl_inst);
2N/A tmpl_inst = _get_global_inst(h);
2N/A if (tmpl_inst != NULL) {
2N/A ret = func(NULL, tmpl_inst, p);
2N/A switch (ret) {
2N/A case SCF_WALK_NEXT:
2N/A break;
2N/A case SCF_WALK_DONE:
2N/A /*
2N/A * Check that the template scoping matches and if not,
2N/A * continue.
2N/A */
2N/A assert(p->pw_pg != NULL);
2N/A tg = _scf_read_single_astring_from_pg(p->pw_pg,
2N/A SCF_PROPERTY_TM_TARGET);
2N/A if (tg == NULL || /* scf_error() was set */
2N/A strcmp(tg, SCF_TM_TARGET_ALL) != 0) {
2N/A scf_pg_destroy(p->pw_pg);
2N/A p->pw_pg = NULL;
2N/A if (tg != NULL) {
2N/A free(tg);
2N/A tg = NULL;
2N/A break;
2N/A }
2N/A }
2N/A /*FALLTHROUGH*/
2N/A case SCF_WALK_ERROR:
2N/A goto done;
2N/A /*NOTREACHED*/
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/Adone:
2N/A free(tg);
2N/A if (ret != SCF_WALK_DONE)
2N/A scf_instance_destroy(tmpl_inst);
2N/A p->pw_target = NULL;
2N/A}
2N/A
2N/A/*
2N/A * _get_pg() returns 0 on success and -1 on failure. Sets scf_error()
2N/A * on failure.
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_MISMATCH
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * name is not a valid property group.
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * SCF_ERROR_NOT_SET
2N/A */
2N/Astatic int
2N/A_get_pg(scf_service_t *svc, scf_instance_t *inst,
2N/A const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
2N/A{
2N/A int ret;
2N/A
2N/A assert(svc != NULL || inst != NULL);
2N/A assert(svc == NULL || inst == NULL);
2N/A assert(pg != NULL);
2N/A
2N/A if (inst != NULL)
2N/A ret = scf_instance_get_pg_composed(inst, snap, name, pg);
2N/A else
2N/A ret = scf_service_get_pg(svc, name, pg);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Returns SCF_WALK_NEXT for not found, SCF_WALK_ERROR for error,
2N/A * and SCF_WALK_DONE for found.
2N/A * On error, destroy pg and set it to NULL.
2N/A *
2N/A * Sets scf_error() if SCF_WALK_ERROR is returned to _BACKEND_ACCESS,
2N/A * _CONNECTION_BROKEN, _INTERNAL, _INVALID_ARGUMENT (name is not a
2N/A * valid property group), _NO_RESOURCES, or _NOT_BOUND.
2N/A */
2N/Astatic int
2N/A_lookup_pg(scf_service_t *svc, scf_instance_t *inst,
2N/A const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
2N/A{
2N/A int ret;
2N/A
2N/A ret = _get_pg(svc, inst, snap, name, pg);
2N/A
2N/A if (ret == 0) {
2N/A return (SCF_WALK_DONE);
2N/A } else {
2N/A switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A case SCF_ERROR_DELETED:
2N/A return (SCF_WALK_NEXT);
2N/A
2N/A case SCF_ERROR_BACKEND_ACCESS:
2N/A case SCF_ERROR_CONNECTION_BROKEN:
2N/A case SCF_ERROR_INTERNAL:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_BOUND:
2N/A case SCF_ERROR_NO_RESOURCES:
2N/A scf_pg_destroy(pg);
2N/A pg = NULL;
2N/A return (SCF_WALK_ERROR);
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A /*NOTREACHED*/
2N/A}
2N/A
2N/A/*
2N/A * If match, return 0. If no match, return 1. If error, return -1.
2N/A * On error set scf_error() to _BACKEND_ACCESS, _CONNECTION_BROKEN,
2N/A * _HANDLE_DESTROYED, _INTERNAL, _NO_MEMORY, _NO_RESOURCES, _NOT_BOUND,
2N/A * _NOT_SET (property group specified by pg is not set), _PERMISSION_DENIED,
2N/A * or _TEMPLATE_INVALID (target property is not SCF_TYPE_ASTRING or has
2N/A * more than one value).
2N/A */
2N/Astatic int
2N/Acheck_target_match(scf_propertygroup_t *pg, const char *target)
2N/A{
2N/A char *pg_target;
2N/A int ret = 0;
2N/A
2N/A pg_target = _scf_read_single_astring_from_pg(pg,
2N/A SCF_PROPERTY_TM_TARGET);
2N/A if (pg_target == NULL) {
2N/A switch (scf_error()) {
2N/A case SCF_ERROR_DELETED:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A return (1);
2N/A
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(
2N/A SCF_ERROR_TEMPLATE_INVALID);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_BACKEND_ACCESS:
2N/A case SCF_ERROR_CONNECTION_BROKEN:
2N/A case SCF_ERROR_HANDLE_DESTROYED:
2N/A case SCF_ERROR_INTERNAL:
2N/A case SCF_ERROR_NO_RESOURCES:
2N/A case SCF_ERROR_NOT_BOUND:
2N/A case SCF_ERROR_PERMISSION_DENIED:
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A /*NOTREACHED*/
2N/A }
2N/A
2N/A /* For a desired target of 'this', check for 'this' and 'instance'. */
2N/A if ((strcmp(target, SCF_TM_TARGET_INSTANCE) == 0 ||
2N/A strcmp(target, SCF_TM_TARGET_THIS) == 0) &&
2N/A (strcmp(pg_target, SCF_TM_TARGET_INSTANCE) == 0 ||
2N/A strcmp(pg_target, SCF_TM_TARGET_THIS) == 0)) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (strcmp(target, SCF_TM_TARGET_DELEGATE) == 0 &&
2N/A strcmp(pg_target, SCF_TM_TARGET_DELEGATE) == 0) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (strcmp(target, SCF_TM_TARGET_ALL) == 0 &&
2N/A strcmp(pg_target, SCF_TM_TARGET_ALL) == 0) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A ret = 1;
2N/Acleanup:
2N/A free(pg_target);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Check if a matching template property group exists for each of:
2N/A * name and type, name only, type only, and completely wildcarded
2N/A * template.
2N/A *
2N/A * Both pg_name and pg_type are optional.
2N/A *
2N/A * Returns NULL on failure, sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * can't combine the _tmpl_pg_name arguments and get a reasonable
2N/A * length name, or pg_name is not a valid property group.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * target property is not SCF_TYPE_ASTRING or has more than one value.
2N/A */
2N/Astatic scf_propertygroup_t *
2N/A_find_template_pg_match(scf_service_t *svc, scf_instance_t *inst,
2N/A const scf_snapshot_t *snap, const char *pg_name, const char *pg_type,
2N/A const char *target, char **tmpl_pg_name)
2N/A{
2N/A int ret, r;
2N/A scf_propertygroup_t *pg = NULL;
2N/A scf_handle_t *h;
2N/A scf_iter_t *iter;
2N/A char *name, *type;
2N/A
2N/A assert(inst != NULL || svc != NULL);
2N/A assert(inst == NULL || svc == NULL);
2N/A
2N/A if (inst != NULL)
2N/A h = scf_instance_handle(inst);
2N/A else
2N/A h = scf_service_handle(svc);
2N/A if (h == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((pg = scf_pg_create(h)) == NULL ||
2N/A (iter = scf_iter_create(h)) == NULL) {
2N/A assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
2N/A scf_pg_destroy(pg);
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * We're going to walk through the possible pg templates that
2N/A * could match the supplied name and type. We do this
2N/A * by explicit name lookups when possible to avoid having to
2N/A * keep track of a most-explicit-match during iteration.
2N/A */
2N/A
2N/A /* First look for a template with name and type set and matching. */
2N/A *tmpl_pg_name = _tmpl_pg_name(pg_name, pg_type, 1);
2N/A if (*tmpl_pg_name == NULL)
2N/A goto fail;
2N/A ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
2N/A if (ret != SCF_WALK_NEXT) {
2N/A if (pg != NULL) {
2N/A if ((r = check_target_match(pg, target)) == 0)
2N/A goto done;
2N/A else if (r == -1)
2N/A goto fail;
2N/A } else {
2N/A goto done;
2N/A }
2N/A }
2N/A free(*tmpl_pg_name);
2N/A
2N/A /*
2N/A * Need to search on a name-only match before searching on
2N/A * type matches.
2N/A */
2N/A
2N/A *tmpl_pg_name = _tmpl_pg_name(pg_name, NULL, 0);
2N/A if (*tmpl_pg_name == NULL)
2N/A goto fail;
2N/A ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
2N/A if (ret != SCF_WALK_NEXT) {
2N/A if (pg != NULL) {
2N/A if ((r = check_target_match(pg, target)) == 0)
2N/A goto done;
2N/A else if (r == -1)
2N/A goto fail;
2N/A } else {
2N/A goto done;
2N/A }
2N/A }
2N/A free(*tmpl_pg_name);
2N/A
2N/A /* Next, see if there's an "nt" template where the type matches. */
2N/A if (pg_type != NULL && pg_name == NULL) {
2N/A if (inst != NULL)
2N/A ret = scf_iter_instance_pgs_typed_composed(iter, inst,
2N/A snap, SCF_GROUP_TEMPLATE_PG_PATTERN);
2N/A else
2N/A ret = scf_iter_service_pgs_typed(iter, svc,
2N/A SCF_GROUP_TEMPLATE_PG_PATTERN);
2N/A
2N/A if (ret != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto fail;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
2N/A /* Make sure this is a name and type specified pg. */
2N/A name = _scf_read_single_astring_from_pg(pg,
2N/A SCF_PROPERTY_TM_NAME);
2N/A if (name == NULL)
2N/A continue;
2N/A type = _scf_read_single_astring_from_pg(pg,
2N/A SCF_PROPERTY_TM_TYPE);
2N/A if (type == NULL) {
2N/A free(name);
2N/A continue;
2N/A }
2N/A if (strcmp(pg_type, type) == 0 &&
2N/A check_target_match(pg, target) == 0) {
2N/A *tmpl_pg_name = name;
2N/A free(type);
2N/A goto done;
2N/A }
2N/A free(type);
2N/A free(name);
2N/A }
2N/A if (ret == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto fail;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A }
2N/A
2N/A *tmpl_pg_name = _tmpl_pg_name(NULL, pg_type, 0);
2N/A if (*tmpl_pg_name == NULL)
2N/A goto fail;
2N/A ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
2N/A if (ret != SCF_WALK_NEXT) {
2N/A if (pg != NULL) {
2N/A if ((r = check_target_match(pg, target)) == 0)
2N/A goto done;
2N/A else if (r == -1)
2N/A goto fail;
2N/A } else {
2N/A goto done;
2N/A }
2N/A }
2N/A free(*tmpl_pg_name);
2N/A
2N/A *tmpl_pg_name = _tmpl_pg_name(NULL, NULL, 0);
2N/A if (*tmpl_pg_name == NULL)
2N/A goto fail;
2N/A ret = _lookup_pg(svc, inst, snap, *tmpl_pg_name, pg);
2N/A if (ret != SCF_WALK_NEXT) {
2N/A if (pg != NULL) {
2N/A if ((r = check_target_match(pg, target)) == 0)
2N/A goto done;
2N/A else if (r == -1)
2N/A goto fail;
2N/A } else {
2N/A goto done;
2N/A }
2N/A }
2N/A
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/Afail:
2N/A scf_pg_destroy(pg);
2N/A if (*tmpl_pg_name != NULL)
2N/A free(*tmpl_pg_name);
2N/A *tmpl_pg_name = NULL;
2N/A pg = NULL;
2N/Adone:
2N/A if (ret == SCF_WALK_ERROR)
2N/A free(*tmpl_pg_name);
2N/A scf_iter_destroy(iter);
2N/A return (pg);
2N/A}
2N/A
2N/A/*
2N/A * Finds the pg match in either the supplied service or instance.
2N/A * Returns SCF_WALK_ERROR, SCF_WALK_NEXT, or SCF_WALK_DONE.
2N/A * If returning SCF_WALK_ERROR, sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * The snaphot is not a valid snapshot name,
2N/A * or can't create a reasonable property group template name.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * target property is not SCF_TYPE_ASTRING or has more than one value.
2N/A */
2N/Astatic int
2N/Afind_pg_match(scf_service_t *svc, scf_instance_t *inst, pg_tmpl_walk_t *p)
2N/A{
2N/A scf_snapshot_t *tmpl_snap = NULL;
2N/A scf_propertygroup_t *pg;
2N/A scf_handle_t *h;
2N/A char *tmpl_pg_name;
2N/A
2N/A assert(svc != NULL || inst != NULL);
2N/A assert(svc == NULL || inst == NULL);
2N/A
2N/A if (inst != NULL)
2N/A h = scf_instance_handle(inst);
2N/A else
2N/A h = scf_service_handle(svc);
2N/A if (h == NULL)
2N/A return (SCF_WALK_ERROR);
2N/A
2N/A if (p->pw_snapname != NULL) {
2N/A if (_get_snapshot(inst, p->pw_snapname, &tmpl_snap) == -1)
2N/A return (SCF_WALK_ERROR);
2N/A }
2N/A pg = _find_template_pg_match(svc, inst, tmpl_snap, p->pw_pgname,
2N/A p->pw_pgtype, p->pw_target, &tmpl_pg_name);
2N/A
2N/A if (pg != NULL) {
2N/A p->pw_snap = tmpl_snap;
2N/A p->pw_pg = pg;
2N/A p->pw_tmpl_pgname = tmpl_pg_name;
2N/A p->pw_inst = inst;
2N/A p->pw_svc = svc;
2N/A return (SCF_WALK_DONE);
2N/A }
2N/A
2N/A scf_snapshot_destroy(tmpl_snap);
2N/A return (SCF_WALK_NEXT);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success and -1 on failure.
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_HANDLE_MISMATCH
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * FMRI argument, snapshot name, pg_name, or pg is invalid.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * SCF_ERROR_NOT_SET
2N/A */
2N/Aint
2N/Ascf_tmpl_get_by_pg(scf_propertygroup_t *pg, scf_pg_tmpl_t *pg_tmpl, int flags)
2N/A{
2N/A char *fmribuf = NULL, *snapbuf = NULL, *pg_name = NULL, *pg_type = NULL;
2N/A int ret;
2N/A ssize_t fbufsz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
2N/A ssize_t nbufsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A ssize_t tbufsz = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
2N/A scf_instance_t *inst = NULL;
2N/A scf_snaplevel_t *snaplvl = NULL;
2N/A scf_service_t *svc = NULL;
2N/A scf_handle_t *h;
2N/A scf_snapshot_t *snap = NULL;
2N/A pg_tmpl_walk_t *p = NULL;
2N/A
2N/A assert(fbufsz != 0 && nbufsz != 0 && tbufsz != 0);
2N/A
2N/A scf_tmpl_pg_reset(pg_tmpl);
2N/A
2N/A if ((h = scf_pg_handle(pg)) == NULL)
2N/A return (-1);
2N/A
2N/A if ((inst = scf_instance_create(h)) == NULL ||
2N/A (svc = scf_service_create(h)) == NULL ||
2N/A (snaplvl = scf_snaplevel_create(h)) == NULL) {
2N/A goto fail;
2N/A }
2N/A
2N/A if ((fmribuf = malloc(fbufsz)) == NULL ||
2N/A (pg_name = malloc(nbufsz)) == NULL ||
2N/A (pg_type = malloc(tbufsz)) == NULL ||
2N/A (p = calloc(1, sizeof (pg_tmpl_walk_t))) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto fail;
2N/A }
2N/A
2N/A if (scf_pg_get_name(pg, pg_name, nbufsz) < 0) {
2N/A goto fail;
2N/A }
2N/A
2N/A if (scf_pg_get_type(pg, pg_type, tbufsz) < 0) {
2N/A goto fail;
2N/A }
2N/A p->pw_pgname = pg_name;
2N/A p->pw_pgtype = pg_type;
2N/A
2N/A ret = scf_pg_get_parent_snaplevel(pg, snaplvl);
2N/A if (ret == -1) {
2N/A switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A /* Parent type doesn't match. Keep looking. */
2N/A break;
2N/A
2N/A case SCF_ERROR_DELETED:
2N/A case SCF_ERROR_NOT_BOUND:
2N/A case SCF_ERROR_NOT_SET:
2N/A /* Pass these back to the caller. */
2N/A goto fail;
2N/A
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A
2N/A /*
2N/A * No snapshot. We'll use 'editing' by default since
2N/A * snap and snapbuf are NULL.
2N/A */
2N/A p->pw_snapname = NULL;
2N/A
2N/A } else {
2N/A if ((snap = scf_snapshot_create(h)) == NULL) {
2N/A goto fail;
2N/A }
2N/A
2N/A ret = scf_snaplevel_get_parent(snaplvl, snap);
2N/A if (ret == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto fail;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A /* Grab snapshot name while we're here. */
2N/A if ((snapbuf = malloc(nbufsz)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto fail;
2N/A }
2N/A if (scf_snapshot_get_name(snap, snapbuf, nbufsz) < 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto fail;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A p->pw_snapname = snapbuf;
2N/A
2N/A ret = scf_snapshot_get_parent(snap, inst);
2N/A if (ret == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto fail;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A _walk_template_instances(NULL, inst, snap,
2N/A (walk_template_inst_func_t *)find_pg_match, p, flags);
2N/A }
2N/A
2N/A /* No snapshot parent. Go looking for instance parent. */
2N/A if (snapbuf == NULL) {
2N/A /* First look for instance parent. */
2N/A ret = scf_pg_get_parent_instance(pg, inst);
2N/A if (ret == 0) {
2N/A _walk_template_instances(NULL, inst, snap,
2N/A (walk_template_inst_func_t *)find_pg_match,
2N/A p, flags);
2N/A /* OK, check for service parent */
2N/A } else if (ret == -1 &&
2N/A scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
2N/A ret = scf_pg_get_parent_service(pg, svc);
2N/A if (ret == 0) {
2N/A _walk_template_instances(svc, NULL, snap,
2N/A (walk_template_inst_func_t *)find_pg_match,
2N/A p, flags);
2N/A } else {
2N/A switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A (void) scf_set_error(
2N/A SCF_ERROR_NOT_FOUND);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_CONNECTION_BROKEN:
2N/A case SCF_ERROR_DELETED:
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A case SCF_ERROR_NOT_BOUND:
2N/A case SCF_ERROR_NOT_SET:
2N/A goto fail;
2N/A
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A } else {
2N/A goto fail;
2N/A }
2N/A }
2N/A
2N/A if (p->pw_pg != NULL) {
2N/A pg_tmpl->pt_h = h;
2N/A pg_tmpl->pt_pg = p->pw_pg;
2N/A pg_tmpl->pt_inst = p->pw_inst;
2N/A /* we may get a different instance back */
2N/A if (p->pw_inst != inst)
2N/A scf_instance_destroy(inst);
2N/A pg_tmpl->pt_snap = p->pw_snap;
2N/A pg_tmpl->pt_svc = p->pw_svc;
2N/A /* we may get a different service back */
2N/A if (p->pw_svc != svc)
2N/A scf_service_destroy(svc);
2N/A pg_tmpl->pt_populated = 1;
2N/A free(p->pw_tmpl_pgname);
2N/A ret = 0;
2N/A goto done;
2N/A }
2N/A
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A
2N/Afail:
2N/A ret = -1;
2N/A scf_instance_destroy(inst);
2N/A scf_service_destroy(svc);
2N/Adone:
2N/A scf_snapshot_destroy(snap);
2N/A free(snapbuf);
2N/A free(fmribuf);
2N/A free(pg_name);
2N/A free(pg_type);
2N/A free(p);
2N/A scf_snaplevel_destroy(snaplvl);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * int scf_tmpl_get_by_pg_name()
2N/A *
2N/A * Get a template by a combination of the name and type. Either name
2N/A * or type can be null, which indicates a wildcard. flags may be
2N/A * SCF_PG_TMPL_FLAG_CURRENT (use current properties rather than
2N/A * the defined or running snapshot), and SCF_PG_TMPL_FLAG_EXACT (match
2N/A * only templates defined by the FMRI in question, not by its restarter
2N/A * or globally). Returns 0 on success and -1 on error, and sets
2N/A * scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * The connection to the repository was lost.
2N/A * SCF_ERROR_DELETED
2N/A * The instance has been deleted.
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * FMRI isn't valid, pg_name is too long to look for a template, or
2N/A * snapshot specified isn't a valid name
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * The server does not have adequate resources to complete the request.
2N/A * SCF_ERROR_NOT_BOUND
2N/A * The handle is not currently bound.
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Object matching FMRI doesn't exist in the repository, or snapshot
2N/A * doesn't exist.
2N/A */
2N/Aint
2N/Ascf_tmpl_get_by_pg_name(const char *fmri, const char *snapshot,
2N/A const char *pg_name, const char *pg_type, scf_pg_tmpl_t *pg_tmpl, int flags)
2N/A{
2N/A scf_instance_t *inst = NULL;
2N/A scf_service_t *svc = NULL;
2N/A scf_snapshot_t *snap = NULL;
2N/A pg_tmpl_walk_t *p = NULL;
2N/A scf_handle_t *h;
2N/A int ret;
2N/A
2N/A assert(pg_tmpl != NULL);
2N/A h = pg_tmpl->pt_h;
2N/A assert(h != NULL);
2N/A
2N/A scf_tmpl_pg_reset(pg_tmpl);
2N/A
2N/A if ((inst = scf_instance_create(h)) == NULL ||
2N/A (svc = scf_service_create(h)) == NULL) {
2N/A goto fail;
2N/A }
2N/A
2N/A p = calloc(1, sizeof (pg_tmpl_walk_t));
2N/A if (p == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto fail;
2N/A }
2N/A
2N/A ret = scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
2N/A NULL, SCF_DECODE_FMRI_EXACT);
2N/A if (ret == 0) {
2N/A scf_service_destroy(svc);
2N/A svc = NULL;
2N/A } else if (ret != 0 &&
2N/A scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
2N/A ret = scf_handle_decode_fmri(h, fmri, NULL, svc,
2N/A NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT);
2N/A if (ret == 0) {
2N/A scf_instance_destroy(inst);
2N/A inst = NULL;
2N/A }
2N/A }
2N/A if (ret != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto fail;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A goto fail;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A goto fail;
2N/A
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A assert(svc == NULL || inst == NULL);
2N/A assert(svc != NULL || inst != NULL);
2N/A
2N/A /* If we have a service fmri, snapshot is ignored. */
2N/A if (inst != NULL) {
2N/A if (snapshot == NULL || strcmp(snapshot, "running") == 0 ||
2N/A (flags & SCF_PG_TMPL_FLAG_CURRENT) ==
2N/A SCF_PG_TMPL_FLAG_CURRENT) {
2N/A if (_get_snapshot(inst, NULL, &snap) == -1)
2N/A goto fail;
2N/A } else {
2N/A if (_get_snapshot(inst, snapshot, &snap) == -1) {
2N/A goto fail;
2N/A } else if (scf_error() == SCF_ERROR_NOT_FOUND) {
2N/A goto fail;
2N/A }
2N/A }
2N/A }
2N/A
2N/A p->pw_snapname = snapshot;
2N/A p->pw_pgname = pg_name;
2N/A p->pw_pgtype = pg_type;
2N/A
2N/A /*
2N/A * For each of instance, restarter, global
2N/A * - check for a tm_pg_pattern_nt_<name> matching type
2N/A * - check for a tm_pg_pattern_t_<type> matching type
2N/A * - check for any tm_pg_pattern_
2N/A * Currently plan to return the most specific match only.
2N/A */
2N/A _walk_template_instances(svc, inst, snap,
2N/A (walk_template_inst_func_t *)find_pg_match, p, flags);
2N/A
2N/A if (p->pw_pg != NULL) {
2N/A pg_tmpl->pt_h = h;
2N/A pg_tmpl->pt_pg = p->pw_pg;
2N/A pg_tmpl->pt_inst = p->pw_inst;
2N/A /* we may get a different instance back */
2N/A if (p->pw_inst != inst)
2N/A scf_instance_destroy(inst);
2N/A pg_tmpl->pt_snap = p->pw_snap;
2N/A pg_tmpl->pt_svc = p->pw_svc;
2N/A /* we may get a different service back */
2N/A if (p->pw_svc != svc)
2N/A scf_service_destroy(svc);
2N/A pg_tmpl->pt_populated = 1;
2N/A scf_snapshot_destroy(snap);
2N/A free(p->pw_tmpl_pgname);
2N/A free(p);
2N/A return (0);
2N/A }
2N/A
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/Afail:
2N/A free(p);
2N/A scf_instance_destroy(inst);
2N/A scf_service_destroy(svc);
2N/A scf_snapshot_destroy(snap);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Returns NULL on failure, sets scf_error() to _CONNECTION_BROKEN,
2N/A * _DELETED, _NO_RESOURCES, or _NOT_BOUND.
2N/A */
2N/Astatic scf_iter_t *
2N/A_get_svc_or_inst_iter(scf_handle_t *h, scf_pg_tmpl_t *t)
2N/A{
2N/A scf_iter_t *iter;
2N/A int ret;
2N/A
2N/A assert(t->pt_svc != NULL || t->pt_inst != NULL);
2N/A assert(t->pt_svc == NULL || t->pt_inst == NULL);
2N/A
2N/A if ((iter = scf_iter_create(h)) == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Iterate on property groups of type template_pg_pattern */
2N/A
2N/A if (t->pt_inst != NULL)
2N/A ret = scf_iter_instance_pgs_typed_composed(iter,
2N/A t->pt_inst, t->pt_snap,
2N/A SCF_GROUP_TEMPLATE_PG_PATTERN);
2N/A if (t->pt_svc != NULL)
2N/A ret = scf_iter_service_pgs_typed(iter, t->pt_svc,
2N/A SCF_GROUP_TEMPLATE_PG_PATTERN);
2N/A
2N/A if (ret != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A scf_iter_destroy(iter);
2N/A return (NULL);
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (iter);
2N/A}
2N/A
2N/A/*
2N/A * Returns NULL on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * Handle argument is NULL, or snaphot is not a valid snapshot name.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A */
2N/Astatic scf_iter_t *
2N/A_get_next_iterator(scf_handle_t *h, scf_pg_tmpl_t *t, const char *snapshot,
2N/A int exact)
2N/A{
2N/A scf_iter_t *iter = NULL;
2N/A ssize_t limit;
2N/A
2N/A limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A assert(limit != 0);
2N/A
2N/A /*
2N/A * Check what level we last iterated on: none, service,
2N/A * restarter, or global. Make sure that if one in the middle
2N/A * doesn't exist, we move on to the next entity.
2N/A *
2N/A * Before we drop any references to pt_inst or pt_svc we must
2N/A * destroy them so we don't leak them.
2N/A */
2N/A do {
2N/A switch (t->pt_iter_last) {
2N/A case SCF__TMPL_ITER_NONE:
2N/A t->pt_iter_last = SCF__TMPL_ITER_INST;
2N/A if (t->pt_inst != t->pt_orig_inst)
2N/A scf_instance_destroy(t->pt_inst);
2N/A t->pt_inst = t->pt_orig_inst;
2N/A if (t->pt_svc != t->pt_orig_svc)
2N/A scf_service_destroy(t->pt_svc);
2N/A t->pt_svc = t->pt_orig_svc;
2N/A break;
2N/A
2N/A case SCF__TMPL_ITER_INST:
2N/A /*
2N/A * Don't go any further than the specified instance
2N/A * if exact was set.
2N/A */
2N/A if (exact == 1) {
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A goto fail;
2N/A }
2N/A t->pt_iter_last = SCF__TMPL_ITER_RESTARTER;
2N/A if (t->pt_inst != t->pt_orig_inst)
2N/A scf_instance_destroy(t->pt_inst);
2N/A t->pt_inst = _get_restarter_inst(h, t->pt_orig_svc,
2N/A t->pt_orig_inst, t->pt_snap);
2N/A if (t->pt_svc != t->pt_orig_svc)
2N/A scf_service_destroy(t->pt_svc);
2N/A t->pt_svc = NULL;
2N/A break;
2N/A
2N/A case SCF__TMPL_ITER_RESTARTER:
2N/A t->pt_iter_last = SCF__TMPL_ITER_GLOBAL;
2N/A if (t->pt_inst != t->pt_orig_inst)
2N/A scf_instance_destroy(t->pt_inst);
2N/A t->pt_inst = _get_global_inst(h);
2N/A if (t->pt_svc != t->pt_orig_svc)
2N/A scf_service_destroy(t->pt_svc);
2N/A t->pt_svc = NULL;
2N/A break;
2N/A
2N/A case SCF__TMPL_ITER_GLOBAL:
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A return (NULL);
2N/A
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A } while (t->pt_inst == NULL && t->pt_svc == NULL);
2N/A
2N/A /* Set pt_snap to the snapshot for this instance */
2N/A if (t->pt_inst != NULL) {
2N/A scf_snapshot_destroy(t->pt_snap);
2N/A if (_get_snapshot(t->pt_inst, snapshot,
2N/A &t->pt_snap) == -1)
2N/A goto fail;
2N/A }
2N/A
2N/A
2N/A iter = _get_svc_or_inst_iter(h, t);
2N/Afail:
2N/A return (iter);
2N/A}
2N/A
2N/A/*
2N/A * scf_pg_tmpl_t *scf_tmpl_pg_create(scf_handle_t *)
2N/A *
2N/A * Returns NULL on failure, sets scf_error() to _INVALID_ARGUMENT
2N/A * or _NO_MEMORY.
2N/A */
2N/Ascf_pg_tmpl_t *
2N/Ascf_tmpl_pg_create(scf_handle_t *handle)
2N/A{
2N/A scf_pg_tmpl_t *pg_tmpl = NULL;
2N/A
2N/A if (handle == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A return (NULL);
2N/A }
2N/A pg_tmpl = calloc(1, sizeof (scf_pg_tmpl_t));
2N/A if (pg_tmpl == NULL)
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A else
2N/A pg_tmpl->pt_h = handle;
2N/A
2N/A return (pg_tmpl);
2N/A}
2N/A
2N/A/*
2N/A * Retrieves name or type of a template pg.
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * pname property is not SCF_TYPE_ASTRING or has more than one value.
2N/A */
2N/Astatic ssize_t
2N/A_scf_tmpl_prop_value(scf_propertygroup_t *pg, const char *pname, char **out)
2N/A{
2N/A assert(strcmp(pname, SCF_PROPERTY_TM_NAME) == 0 ||
2N/A strcmp(pname, SCF_PROPERTY_TM_TYPE) == 0);
2N/A
2N/A *out = _scf_read_single_astring_from_pg(pg, pname);
2N/A
2N/A if (*out != NULL && *out[0] == '\0') {
2N/A (void) scf_set_error(SCF_ERROR_NONE);
2N/A free(*out);
2N/A *out = strdup(SCF_TMPL_WILDCARD);
2N/A if (*out == NULL)
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A }
2N/A if (*out == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A
2N/A/*
2N/A * int scf_tmpl_iter_pgs()
2N/A *
2N/A * Iterates through the property group templates for the fmri given.
2N/A * When t is uninitialized or reset, sets t to the first property group
2N/A * template in fmri. On subsequent calls, sets t to the next property group
2N/A * template in frmi.
2N/A * Returns 1 on success, 0 when no property group templates are left to
2N/A * iterate, -1 on error.
2N/A * The flags argument may include SCF_PG_TMPL_FLAG_REQUIRED,
2N/A * SCF_PG_TMPL_FLAG_CURRENT, and/or SCF_PG_TMPL_FLAG_EXACT.
2N/A *
2N/A * Returns -1 on error and sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * The handle argument is NULL, fmri is invalid, or snapshot is invalid.
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A */
2N/Aint
2N/Ascf_tmpl_iter_pgs(scf_pg_tmpl_t *t, const char *fmri, const char *snapshot,
2N/A const char *type, int flags)
2N/A{
2N/A scf_handle_t *h;
2N/A scf_service_t *svc = NULL;
2N/A scf_instance_t *inst = NULL;
2N/A scf_propertygroup_t *pg = NULL;
2N/A scf_snapshot_t *snap = NULL;
2N/A scf_pg_tmpl_t *pg_tmpl = NULL;
2N/A int err;
2N/A int found = 0;
2N/A char *tmpl_type;
2N/A uint8_t required;
2N/A int ret;
2N/A
2N/A if (t == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A return (-1);
2N/A }
2N/A
2N/A h = t->pt_h;
2N/A
2N/A if (t->pt_populated == 0) {
2N/A if ((svc = scf_service_create(h)) == NULL ||
2N/A (inst = scf_instance_create(h)) == NULL ||
2N/A (pg = scf_pg_create(h)) == NULL) {
2N/A goto fail_non_populated;
2N/A }
2N/A
2N/A ret = scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
2N/A NULL, SCF_DECODE_FMRI_EXACT);
2N/A if (ret == 0) {
2N/A scf_service_destroy(svc);
2N/A svc = NULL;
2N/A } else if (ret != 0 &&
2N/A scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
2N/A ret = scf_handle_decode_fmri(h, fmri, NULL, svc,
2N/A NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT);
2N/A if (ret == 0) {
2N/A scf_instance_destroy(inst);
2N/A inst = NULL;
2N/A }
2N/A }
2N/A
2N/A if (ret != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto fail_non_populated;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A (void) scf_set_error(
2N/A SCF_ERROR_INVALID_ARGUMENT);
2N/A goto fail_non_populated;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A goto fail_non_populated;
2N/A
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A assert(svc == NULL || inst == NULL);
2N/A assert(svc != NULL || inst != NULL);
2N/A
2N/A if (inst != NULL) {
2N/A if (snapshot == NULL ||
2N/A strcmp(snapshot, "running") == 0 ||
2N/A (flags & SCF_PG_TMPL_FLAG_CURRENT) ==
2N/A SCF_PG_TMPL_FLAG_CURRENT) {
2N/A if (_get_snapshot(inst, NULL, &snap) == -1)
2N/A goto fail_non_populated;
2N/A } else {
2N/A (void) scf_set_error(SCF_ERROR_NONE);
2N/A if (_get_snapshot(inst, snapshot,
2N/A &snap) == -1) {
2N/A goto fail_non_populated;
2N/A } else if (scf_error() == SCF_ERROR_NOT_FOUND) {
2N/A goto fail_non_populated;
2N/A }
2N/A }
2N/A } else {
2N/A scf_snapshot_destroy(snap);
2N/A snap = NULL;
2N/A }
2N/A
2N/A pg_tmpl = t;
2N/A pg_tmpl->pt_orig_inst = inst;
2N/A pg_tmpl->pt_orig_svc = svc;
2N/A pg_tmpl->pt_snap = snap;
2N/A pg_tmpl->pt_is_iter = 1;
2N/A pg_tmpl->pt_iter_last = SCF__TMPL_ITER_NONE;
2N/A pg_tmpl->pt_pg = pg;
2N/A pg_tmpl->pt_populated = 1;
2N/A } else {
2N/A if (t->pt_is_iter != 1) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A return (-1);
2N/A }
2N/A pg_tmpl = t;
2N/A assert(pg_tmpl->pt_pg != NULL);
2N/A }
2N/A
2N/A if (pg_tmpl->pt_iter == NULL) {
2N/A pg_tmpl->pt_iter = _get_next_iterator(h, pg_tmpl, snapshot,
2N/A (flags & SCF_PG_TMPL_FLAG_EXACT) ? 1 : 0);
2N/A if (pg_tmpl->pt_iter == NULL) {
2N/A if (scf_error() == SCF_ERROR_NOT_FOUND)
2N/A return (0);
2N/A else
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A while (found == 0) {
2N/A while ((err = scf_iter_next_pg(pg_tmpl->pt_iter,
2N/A pg_tmpl->pt_pg)) != 1) {
2N/A if (err == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A } else if (err == 0) {
2N/A /* This iteration is done. Get the next one */
2N/A scf_iter_destroy(pg_tmpl->pt_iter);
2N/A pg_tmpl->pt_iter = _get_next_iterator(h,
2N/A pg_tmpl, snapshot,
2N/A (flags & SCF_PG_TMPL_FLAG_EXACT) ? 1 : 0);
2N/A if (pg_tmpl->pt_iter == NULL) {
2N/A if (scf_error() == SCF_ERROR_NOT_FOUND)
2N/A return (0);
2N/A else
2N/A return (-1);
2N/A }
2N/A continue;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Discard pgs which don't exist at the right scoping. This
2N/A * check also makes sure that if we're looking at, for
2N/A * example, svc:/system/svc/restarter:default, that we
2N/A * don't hand back the same property groups twice.
2N/A */
2N/A switch (t->pt_iter_last) {
2N/A case SCF__TMPL_ITER_INST:
2N/A ret = check_target_match(pg_tmpl->pt_pg,
2N/A SCF_TM_TARGET_THIS);
2N/A break;
2N/A case SCF__TMPL_ITER_RESTARTER:
2N/A ret = check_target_match(pg_tmpl->pt_pg,
2N/A SCF_TM_TARGET_DELEGATE);
2N/A break;
2N/A case SCF__TMPL_ITER_GLOBAL:
2N/A ret = check_target_match(pg_tmpl->pt_pg,
2N/A SCF_TM_TARGET_ALL);
2N/A break;
2N/A case SCF__TMPL_ITER_NONE:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A
2N/A if (ret != 0)
2N/A continue;
2N/A
2N/A /*
2N/A * If walking only required property groups, check if
2N/A * the retrieved group is required.
2N/A */
2N/A if (flags & SCF_PG_TMPL_FLAG_REQUIRED) {
2N/A if (scf_tmpl_pg_required(pg_tmpl, &required) == 0) {
2N/A if (required == 0)
2N/A continue;
2N/A } else {
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If type != NULL, check if type property matches. If no
2N/A * type property exists, consider it a match.
2N/A */
2N/A if (type != NULL) {
2N/A if (scf_tmpl_pg_type(pg_tmpl, &tmpl_type) != -1) {
2N/A if (strcmp(tmpl_type, SCF_TMPL_WILDCARD)
2N/A == 0 || strcmp(type, tmpl_type) == 0) {
2N/A free(tmpl_type);
2N/A break;
2N/A }
2N/A free(tmpl_type);
2N/A } else if (scf_error() == SCF_ERROR_NOT_FOUND ||
2N/A scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED ||
2N/A scf_error() == SCF_ERROR_TYPE_MISMATCH) {
2N/A break;
2N/A } else {
2N/A return (-1);
2N/A }
2N/A } else {
2N/A break;
2N/A }
2N/A }
2N/A
2N/A return (1);
2N/A
2N/Afail_non_populated:
2N/A scf_service_destroy(svc);
2N/A scf_instance_destroy(inst);
2N/A scf_pg_destroy(pg);
2N/A scf_snapshot_destroy(snap);
2N/A return (-1);
2N/A}
2N/A
2N/Avoid
2N/Ascf_tmpl_pg_destroy(scf_pg_tmpl_t *t)
2N/A{
2N/A if (t == NULL)
2N/A return;
2N/A
2N/A scf_pg_destroy(t->pt_pg);
2N/A scf_instance_destroy(t->pt_inst);
2N/A if (t->pt_inst != t->pt_orig_inst)
2N/A scf_instance_destroy(t->pt_orig_inst);
2N/A scf_snapshot_destroy(t->pt_snap);
2N/A scf_service_destroy(t->pt_orig_svc);
2N/A if (t->pt_svc != t->pt_orig_svc)
2N/A scf_service_destroy(t->pt_svc);
2N/A scf_iter_destroy(t->pt_iter);
2N/A free(t);
2N/A}
2N/A
2N/Avoid
2N/Ascf_tmpl_pg_reset(scf_pg_tmpl_t *t)
2N/A{
2N/A scf_pg_destroy(t->pt_pg);
2N/A t->pt_pg = NULL;
2N/A
2N/A scf_instance_destroy(t->pt_inst);
2N/A if (t->pt_inst != t->pt_orig_inst)
2N/A scf_instance_destroy(t->pt_orig_inst);
2N/A t->pt_inst = NULL;
2N/A t->pt_orig_inst = NULL;
2N/A
2N/A scf_snapshot_destroy(t->pt_snap);
2N/A t->pt_snap = NULL;
2N/A
2N/A scf_service_destroy(t->pt_orig_svc);
2N/A if (t->pt_svc != t->pt_orig_svc)
2N/A scf_service_destroy(t->pt_svc);
2N/A t->pt_orig_svc = NULL;
2N/A t->pt_svc = NULL;
2N/A
2N/A scf_iter_destroy(t->pt_iter);
2N/A t->pt_iter = NULL;
2N/A
2N/A t->pt_populated = 0;
2N/A t->pt_is_iter = 0;
2N/A t->pt_iter_last = 0;
2N/A
2N/A /* Do not reset t->pt_h. */
2N/A}
2N/A
2N/A/*
2N/A * int scf_tmpl_get_by_prop()
2N/A *
2N/A * Get the property template given a property group template and property
2N/A * name. No flags are currently defined for this function.
2N/A *
2N/A * Returns NULL on failure, and sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Template object matching property doesn't exist in the repository.
2N/A * SCF_ERROR_TYPE_MISMATCH
2N/A * Matching template object is the wrong type in the repository.
2N/A */
2N/Aint
2N/Ascf_tmpl_get_by_prop(scf_pg_tmpl_t *t, const char *prop,
2N/A scf_prop_tmpl_t *prop_tmpl, int flags)
2N/A{
2N/A char *tmpl_prop_name;
2N/A scf_propertygroup_t *pg = NULL;
2N/A char *pg_type;
2N/A int found = 0;
2N/A
2N/A if (flags != 0) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A return (-1);
2N/A }
2N/A
2N/A scf_tmpl_prop_reset(prop_tmpl);
2N/A if ((pg = scf_pg_create(scf_pg_handle(t->pt_pg))) == NULL)
2N/A return (-1);
2N/A
2N/A tmpl_prop_name = _tmpl_prop_name(prop, t);
2N/A if (tmpl_prop_name == NULL) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A return (-1);
2N/A }
2N/A
2N/A if (_get_pg(t->pt_svc, t->pt_inst, t->pt_snap,
2N/A tmpl_prop_name, pg) != 0) {
2N/A if (!ismember(scf_error(), errors_server)) {
2N/A switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A break;
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A } else {
2N/A /*
2N/A * We've only found a template property group if the type
2N/A * is correct.
2N/A */
2N/A if ((pg_type = _scf_get_pg_type(pg)) != NULL &&
2N/A strcmp(pg_type, SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0)
2N/A found++;
2N/A else
2N/A (void) scf_set_error(SCF_ERROR_TYPE_MISMATCH);
2N/A
2N/A
2N/A free(pg_type);
2N/A }
2N/A
2N/A if (found == 0) {
2N/A scf_pg_destroy(pg);
2N/A free(tmpl_prop_name);
2N/A return (-1);
2N/A }
2N/A
2N/A prop_tmpl->prt_h = scf_pg_handle(t->pt_pg);
2N/A prop_tmpl->prt_t = t;
2N/A prop_tmpl->prt_pg = pg;
2N/A prop_tmpl->prt_pg_name = tmpl_prop_name;
2N/A prop_tmpl->prt_populated = 1;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * scf_prop_tmpl_t *scf_tmpl_prop_create(scf_handle_t *);
2N/A *
2N/A * Returns NULL on failure, sets scf_error() to _INVALID_ARGUMENT, or
2N/A * _NO_MEMORY.
2N/A */
2N/Ascf_prop_tmpl_t *
2N/Ascf_tmpl_prop_create(scf_handle_t *handle)
2N/A{
2N/A scf_prop_tmpl_t *pt;
2N/A
2N/A if (handle == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A return (NULL);
2N/A }
2N/A pt = calloc(1, sizeof (scf_prop_tmpl_t));
2N/A if (pt == NULL)
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A else
2N/A pt->prt_h = handle;
2N/A
2N/A return (pt);
2N/A}
2N/A
2N/A/*
2N/A * int scf_tmpl_iter_props()
2N/A *
2N/A * Iterates over all property templates defined in the specified property
2N/A * group template. The iterator state is stored on the property template
2N/A * data structure, and the data structure should be allocated with
2N/A * scf_tmpl_prop_create(). To continue the iteration, the previously
2N/A * returned structure should be passed in as an argument to this function.
2N/A * flags can include SCF_PROP_TMPL_FLAG_REQUIRED. The function returns
2N/A * 1 when a result was found, and 0 when the iteration is complete.
2N/A *
2N/A * Returns -1 on failure, and sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * Template data is invalid. One of the property templates in this
2N/A * pg_tmpl is malformed.
2N/A */
2N/Aint
2N/Ascf_tmpl_iter_props(scf_pg_tmpl_t *t, scf_prop_tmpl_t *pt, int flags)
2N/A{
2N/A scf_prop_tmpl_t *prop_tmpl;
2N/A char *pg_pat;
2N/A char *pg_name = NULL;
2N/A int err;
2N/A int ret;
2N/A ssize_t size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A uint8_t required;
2N/A scf_handle_t *h;
2N/A scf_propertygroup_t *pg = NULL;
2N/A scf_iter_t *iter = NULL;
2N/A
2N/A assert(size != 0);
2N/A if (t == NULL || pt == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A return (-1);
2N/A }
2N/A
2N/A assert(t->pt_inst == NULL || t->pt_svc == NULL);
2N/A assert(t->pt_inst != NULL || t->pt_svc != NULL);
2N/A
2N/A if ((pg_name = malloc(size)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (-1);
2N/A }
2N/A
2N/A if (pt->prt_populated == 0) {
2N/A if ((h = scf_pg_handle(t->pt_pg)) == NULL)
2N/A goto fail_non_populated;
2N/A
2N/A if ((pg = scf_pg_create(h)) == NULL ||
2N/A (iter = scf_iter_create(h)) == NULL)
2N/A goto fail_non_populated;
2N/A
2N/A if (t->pt_inst != NULL)
2N/A err = scf_iter_instance_pgs_typed_composed(iter,
2N/A t->pt_inst, t->pt_snap,
2N/A SCF_GROUP_TEMPLATE_PROP_PATTERN);
2N/A else if (t->pt_svc != NULL)
2N/A err = scf_iter_service_pgs_typed(iter, t->pt_svc,
2N/A SCF_GROUP_TEMPLATE_PROP_PATTERN);
2N/A
2N/A if (err != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto fail_non_populated;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A goto fail_non_populated;
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A
2N/A }
2N/A prop_tmpl = pt;
2N/A prop_tmpl->prt_t = t;
2N/A prop_tmpl->prt_populated = 1;
2N/A prop_tmpl->prt_pg = pg;
2N/A prop_tmpl->prt_iter = iter;
2N/A } else {
2N/A prop_tmpl = pt;
2N/A }
2N/A
2N/A while ((err = scf_iter_next_pg(prop_tmpl->prt_iter,
2N/A prop_tmpl->prt_pg)) > 0) {
2N/A /*
2N/A * Check if the name matches the appropriate property
2N/A * group template name.
2N/A */
2N/A pg_pat = _scf_read_single_astring_from_pg(prop_tmpl->prt_pg,
2N/A SCF_PROPERTY_TM_PG_PATTERN);
2N/A if (pg_pat == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A uu_free(pg_name);
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A continue;
2N/A
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(
2N/A SCF_ERROR_TEMPLATE_INVALID);
2N/A free(pg_name);
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A if ((ret = scf_pg_get_name(t->pt_pg, pg_name, size)) <= 0) {
2N/A free(pg_pat);
2N/A if (ret == 0)
2N/A continue;
2N/A
2N/A if (ismember(scf_error(), errors_server)) {
2N/A free(pg_name);
2N/A return (-1);
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A if (strcmp(pg_pat, pg_name) != 0) {
2N/A free(pg_pat);
2N/A continue;
2N/A }
2N/A free(pg_pat);
2N/A
2N/A /*
2N/A * If walking only required properties, check if
2N/A * the retrieved property is required.
2N/A */
2N/A if (flags & SCF_PROP_TMPL_FLAG_REQUIRED) {
2N/A if (scf_tmpl_prop_required(prop_tmpl, &required) == 0) {
2N/A if (required == 0)
2N/A continue;
2N/A } else {
2N/A free(pg_name);
2N/A return (-1);
2N/A }
2N/A }
2N/A free(pg_name);
2N/A return (0);
2N/A }
2N/A
2N/A if (err == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A free(pg_name);
2N/A return (-1);
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A } else if (err == 0) {
2N/A scf_iter_destroy(prop_tmpl->prt_iter);
2N/A prop_tmpl->prt_iter = NULL;
2N/A prop_tmpl->prt_populated = 0;
2N/A }
2N/A free(pg_name);
2N/A
2N/A return (1);
2N/A
2N/Afail_non_populated:
2N/A free(pg_name);
2N/A scf_pg_destroy(pg);
2N/A scf_iter_destroy(iter);
2N/A return (-1);
2N/A}
2N/A
2N/Avoid
2N/Ascf_tmpl_prop_destroy(scf_prop_tmpl_t *t)
2N/A{
2N/A if (t == NULL)
2N/A return;
2N/A
2N/A scf_pg_destroy(t->prt_pg);
2N/A scf_iter_destroy(t->prt_iter);
2N/A free(t->prt_pg_name);
2N/A free(t);
2N/A}
2N/A
2N/Avoid
2N/Ascf_tmpl_prop_reset(scf_prop_tmpl_t *t)
2N/A{
2N/A scf_pg_destroy(t->prt_pg);
2N/A t->prt_pg = NULL;
2N/A
2N/A free(t->prt_pg_name);
2N/A t->prt_pg_name = NULL;
2N/A
2N/A scf_iter_destroy(t->prt_iter);
2N/A t->prt_iter = NULL;
2N/A
2N/A t->prt_populated = 0;
2N/A t->prt_h = NULL;
2N/A t->prt_t = NULL;
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * The name of the template property group (the pname property) has
2N/A * an improper repository format and is not type astring or has
2N/A * more than one value.
2N/A */
2N/Assize_t
2N/Ascf_tmpl_pg_name(const scf_pg_tmpl_t *t, char **out)
2N/A{
2N/A return (_scf_tmpl_prop_value(t->pt_pg, SCF_PROPERTY_TM_NAME, out));
2N/A}
2N/A
2N/A/*
2N/A * returns an allocated string that must be freed
2N/A *
2N/A * Returns NULL on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * name not a valid property name
2N/A * name and locale are too long to make a property name
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic char *
2N/A_read_localized_astring_from_pg(scf_propertygroup_t *pg, const char *name,
2N/A const char *locale, const char **elocale)
2N/A{
2N/A char *str = NULL;
2N/A char *lname_prop = NULL;
2N/A char *loc = NULL;
2N/A
2N/A /*
2N/A * Valid locales are always in the for of xx_YY.CODESET@ATTRIBUTE
2N/A * Only xx, the language part is required. The country code, YY as well
2N/A * as the CODESET and ATTRIBUTE are all optional but they MUST appear
2N/A * that order.
2N/A *
2N/A * If we use the locale from setlocale, we'll look first for it as
2N/A * provided by the setlocale(3C). If the the lookup fails, we'll trim
2N/A * from right to left in this order: '@', '.' and '_'. We'll pick the
2N/A * first match. If all of them fail, we'll try the "C" locale.
2N/A *
2N/A * For locales provided by the caller, we'll not do the trimming
2N/A * described above, but will fall back to "C" locale.
2N/A */
2N/A if (locale == NULL) {
2N/A char const *t;
2N/A
2N/A loc = strdup(locale = setlocale(LC_MESSAGES, NULL));
2N/A if (loc == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (NULL);
2N/A }
2N/A
2N/A for (t = "@._"; *t != '\0'; t++) {
2N/A str = _add_locale_to_name(name, loc);
2N/A if (str != NULL) {
2N/A lname_prop =
2N/A _scf_read_single_astring_from_pg(pg, str);
2N/A
2N/A free(str);
2N/A if (lname_prop != NULL)
2N/A break;
2N/A if (scf_error() != SCF_ERROR_NOT_FOUND &&
2N/A scf_error() != SCF_ERROR_DELETED)
2N/A goto out; /* lname_prop == NULL */
2N/A }
2N/A while (*t != '\0') {
2N/A char *p;
2N/A
2N/A if ((p = strrchr(loc, *t)) != NULL) {
2N/A *p = '\0';
2N/A /*
2N/A * try new locale.
2N/A */
2N/A break;
2N/A }
2N/A /*
2N/A * The trimming char is not on the locale,
2N/A * let's try the one next without a trip to the
2N/A * repository.
2N/A */
2N/A t++;
2N/A }
2N/A /*
2N/A * if the inner while loop reached the end of t, bail.
2N/A */
2N/A if (*t == '\0')
2N/A break;
2N/A }
2N/A }
2N/A if (lname_prop == NULL) {
2N/A if (scf_error() != SCF_ERROR_NOT_FOUND &&
2N/A scf_error() != SCF_ERROR_DELETED)
2N/A goto out; /* lname_prop == NULL */
2N/A str = _add_locale_to_name(name, "C");
2N/A if (str == NULL)
2N/A goto out; /* lname_prop == NULL */
2N/A lname_prop = _scf_read_single_astring_from_pg(pg, str);
2N/A free(str);
2N/A if (lname_prop != NULL && elocale != NULL)
2N/A *elocale = "C";
2N/A } else if (elocale != NULL) {
2N/A *elocale = locale;
2N/A }
2N/A
2N/A if (lname_prop == NULL) {
2N/A if (scf_error() == SCF_ERROR_TYPE_MISMATCH ||
2N/A scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A }
2N/Aout:
2N/A free(loc);
2N/A return (lname_prop);
2N/A}
2N/A
2N/Assize_t
2N/Ascf_tmpl_pg_common_name_loc(const scf_pg_tmpl_t *t, const char *locale,
2N/A char **out, const char **elocale)
2N/A{
2N/A assert(out != NULL);
2N/A if ((*out = _read_localized_astring_from_pg(t->pt_pg,
2N/A SCF_PROPERTY_TM_COMMON_NAME_PREFIX, locale, elocale)) == NULL)
2N/A return (-1);
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A/*
2N/A * returns an allocated string that must be freed
2N/A *
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * locale is too long to make a valid property name
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Assize_t
2N/Ascf_tmpl_pg_common_name(const scf_pg_tmpl_t *t, const char *locale, char **out)
2N/A{
2N/A return (scf_tmpl_pg_common_name_loc(t, locale, out, NULL));
2N/A}
2N/A
2N/Assize_t
2N/Ascf_tmpl_pg_description_loc(const scf_pg_tmpl_t *t, const char *locale,
2N/A char **out, const char **elocale)
2N/A{
2N/A assert(out != NULL);
2N/A if ((*out = _read_localized_astring_from_pg(t->pt_pg,
2N/A SCF_PROPERTY_TM_DESCRIPTION_PREFIX, locale, elocale)) == NULL)
2N/A return (-1);
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A
2N/A/*
2N/A * returns an allocated string that must be freed
2N/A *
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * locale is too long to make a valid property name
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Assize_t
2N/Ascf_tmpl_pg_description(const scf_pg_tmpl_t *t, const char *locale, char **out)
2N/A{
2N/A return (scf_tmpl_pg_description_loc(t, locale, out, NULL));
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * 'type' property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * 'type' property is not SCF_TYPE_ASTRING or has more than one value.
2N/A */
2N/Assize_t
2N/Ascf_tmpl_pg_type(const scf_pg_tmpl_t *t, char **out)
2N/A{
2N/A return (_scf_tmpl_prop_value(t->pt_pg, SCF_PROPERTY_TM_TYPE, out));
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * required property is not SCF_TYPE_BOOLEAN or has more than one value.
2N/A */
2N/Aint
2N/Ascf_tmpl_pg_required(const scf_pg_tmpl_t *t, uint8_t *out)
2N/A{
2N/A
2N/A if (_read_single_boolean_from_pg(t->pt_pg, SCF_PROPERTY_TM_REQUIRED,
2N/A out) == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_NOT_FOUND:
2N/A *out = 0;
2N/A return (0);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * target property is not SCF_TYPE_ASTRING or has more than one value.
2N/A */
2N/Assize_t
2N/Ascf_tmpl_pg_target(const scf_pg_tmpl_t *t, char **out)
2N/A{
2N/A *out = _scf_read_single_astring_from_pg(t->pt_pg,
2N/A SCF_PROPERTY_TM_TARGET);
2N/A
2N/A if (*out == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Assize_t
2N/Ascf_tmpl_prop_name(const scf_prop_tmpl_t *t, char **out)
2N/A{
2N/A *out = _scf_read_single_astring_from_pg(t->prt_pg,
2N/A SCF_PROPERTY_TM_NAME);
2N/A
2N/A if (*out != NULL && *out[0] == '\0') {
2N/A free(*out);
2N/A *out = NULL;
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A }
2N/A if (*out == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A case SCF_ERROR_TEMPLATE_INVALID:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * 'type' property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * 'type' property is not SCF_TYPE_ASTRING, has more than one value,
2N/A * is SCF_TYPE_INVALID, or is the empty string.
2N/A */
2N/Aint
2N/Ascf_tmpl_prop_type(const scf_prop_tmpl_t *t, scf_type_t *out)
2N/A{
2N/A char *type;
2N/A
2N/A type = _scf_read_single_astring_from_pg(t->prt_pg,
2N/A SCF_PROPERTY_TM_TYPE);
2N/A if (type != NULL && type[0] == '\0') {
2N/A free(type);
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A return (-1);
2N/A }
2N/A if (type == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_NOT_FOUND:
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A *out = scf_string_to_type(type);
2N/A free(type);
2N/A
2N/A if (*out == SCF_TYPE_INVALID) {
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * Property group which represents t was deleted.
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * required property is not SCF_TYPE_ASTRING has more than one value.
2N/A */
2N/Aint
2N/Ascf_tmpl_prop_required(const scf_prop_tmpl_t *t, uint8_t *out)
2N/A{
2N/A if (_read_single_boolean_from_pg(t->prt_pg, SCF_PROPERTY_TM_REQUIRED,
2N/A out) == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_NOT_FOUND:
2N/A *out = 0;
2N/A return (0);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Assize_t
2N/Ascf_tmpl_prop_common_name_loc(const scf_prop_tmpl_t *t, const char *locale,
2N/A char **out, const char **elocale)
2N/A{
2N/A assert(out != NULL);
2N/A if ((*out = _read_localized_astring_from_pg(t->prt_pg,
2N/A SCF_PROPERTY_TM_COMMON_NAME_PREFIX, locale, elocale)) == NULL)
2N/A return (-1);
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * locale is too long to make a property name
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * common_name property is not SCF_TYPE_ASTRING has more than one value.
2N/A */
2N/Assize_t
2N/Ascf_tmpl_prop_common_name(const scf_prop_tmpl_t *t, const char *locale,
2N/A char **out)
2N/A{
2N/A return (scf_tmpl_prop_common_name_loc(t, locale, out, NULL));
2N/A}
2N/A
2N/Assize_t
2N/Ascf_tmpl_prop_description_loc(const scf_prop_tmpl_t *t, const char *locale,
2N/A char **out, const char **elocale)
2N/A{
2N/A assert(out != NULL);
2N/A if ((*out = _read_localized_astring_from_pg(t->prt_pg,
2N/A SCF_PROPERTY_TM_DESCRIPTION_PREFIX, locale, elocale)) == NULL)
2N/A return (-1);
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * locale is too long to make a property name
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * description property is not SCF_TYPE_ASTRING has more than one value.
2N/A */
2N/Assize_t
2N/Ascf_tmpl_prop_description(const scf_prop_tmpl_t *t, const char *locale,
2N/A char **out)
2N/A{
2N/A return (scf_tmpl_prop_description_loc(t, locale, out, NULL));
2N/A}
2N/A
2N/Assize_t
2N/Ascf_tmpl_prop_units_loc(const scf_prop_tmpl_t *t, const char *locale,
2N/A char **out, const char **elocale)
2N/A{
2N/A assert(out != NULL);
2N/A if ((*out = _read_localized_astring_from_pg(t->prt_pg,
2N/A SCF_PROPERTY_TM_UNITS_PREFIX, locale, elocale)) == NULL)
2N/A return (-1);
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * locale is too long to make a property name
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * units property is not SCF_TYPE_ASTRING has more than one value.
2N/A */
2N/Assize_t
2N/Ascf_tmpl_prop_units(const scf_prop_tmpl_t *t, const char *locale, char **out)
2N/A{
2N/A return (scf_tmpl_prop_units_loc(t, locale, out, NULL));
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * visibility property is not SCF_TYPE_ASTRING has more than one value.
2N/A */
2N/Aint
2N/Ascf_tmpl_prop_visibility(const scf_prop_tmpl_t *t, uint8_t *out)
2N/A{
2N/A char *visibility;
2N/A
2N/A visibility = _scf_read_single_astring_from_pg(t->prt_pg,
2N/A SCF_PROPERTY_TM_VISIBILITY);
2N/A if (visibility == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A /* prop doesn't exist we take the default value */
2N/A case SCF_ERROR_NOT_FOUND:
2N/A *out = SCF_TMPL_VISIBILITY_READWRITE;
2N/A return (0);
2N/A
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A } else if (strcmp(visibility, SCF_TM_VISIBILITY_READWRITE) == 0) {
2N/A *out = SCF_TMPL_VISIBILITY_READWRITE;
2N/A } else if (strcmp(visibility, SCF_TM_VISIBILITY_HIDDEN) == 0) {
2N/A *out = SCF_TMPL_VISIBILITY_HIDDEN;
2N/A } else if (strcmp(visibility, SCF_TM_VISIBILITY_READONLY) == 0) {
2N/A *out = SCF_TMPL_VISIBILITY_READONLY;
2N/A } else {
2N/A free(visibility);
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A }
2N/A
2N/A free(visibility);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Return an allocated string containing the value that must be freed
2N/A * with free().
2N/A *
2N/A * On error set scf_error() _NO_MEMORY, or _NOT_SET (val has not been set
2N/A * to a value).
2N/A */
2N/Astatic char *
2N/A_scf_value_get_as_string(scf_value_t *val)
2N/A{
2N/A ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
2N/A char *buf = malloc(sz);
2N/A
2N/A if (buf == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A } else if (scf_value_get_as_string(val, buf, sz) == -1) {
2N/A free(buf);
2N/A buf = NULL;
2N/A }
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Aint
2N/Ascf_tmpl_prop_cardinality(const scf_prop_tmpl_t *t, uint64_t *min,
2N/A uint64_t *max)
2N/A{
2N/A scf_value_t *val_min = NULL;
2N/A scf_value_t *val_max = NULL;
2N/A int ret = 0;
2N/A
2N/A if (_read_single_value_from_pg(t->prt_pg,
2N/A SCF_PROPERTY_TM_CARDINALITY_MIN, &val_min) == 0) {
2N/A if (scf_value_get_count(val_min, min) < 0)
2N/A goto error;
2N/A } else {
2N/A if (scf_error() == SCF_ERROR_NOT_FOUND)
2N/A *min = 0;
2N/A else
2N/A goto error;
2N/A }
2N/A
2N/A if (_read_single_value_from_pg(t->prt_pg,
2N/A SCF_PROPERTY_TM_CARDINALITY_MAX, &val_max) == 0) {
2N/A if (scf_value_get_count(val_max, max) < 0)
2N/A goto error;
2N/A } else {
2N/A if (scf_error() == SCF_ERROR_NOT_FOUND)
2N/A *max = UINT64_MAX;
2N/A else
2N/A goto error;
2N/A }
2N/A goto cleanup;
2N/A
2N/Aerror:
2N/A if (ismember(scf_error(), errors_server)) {
2N/A ret = -1;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_NOT_FOUND:
2N/A case SCF_ERROR_TEMPLATE_INVALID:
2N/A ret = -1;
2N/A break;
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A
2N/Acleanup:
2N/A scf_value_destroy(val_min);
2N/A scf_value_destroy(val_max);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Aint
2N/Ascf_tmpl_prop_internal_seps(const scf_prop_tmpl_t *t, scf_values_t *vals)
2N/A{
2N/A if (_read_astrings_values(t->prt_pg,
2N/A SCF_PROPERTY_INTERNAL_SEPARATORS, vals) == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_NOT_FOUND:
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A } else if (vals->value_count == 0) {
2N/A /* property has no value */
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A scf_values_destroy(vals);
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Aint
2N/Ascf_tmpl_value_name_constraints(const scf_prop_tmpl_t *t,
2N/A scf_values_t *vals)
2N/A{
2N/A char **ret;
2N/A
2N/A ret = _read_astrings_values(t->prt_pg,
2N/A SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
2N/A
2N/A if (ret == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_NOT_FOUND:
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A } else if (vals->value_count == 0) {
2N/A /* property has no value */
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A scf_values_destroy(vals);
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Returns NULL on failure. Sets scf_error():
2N/A * Caller is responsible for freeing returned pointer after use.
2N/A * SCF_ERROR_CONSTRAINT_VIOLATED
2N/A * More tokens than the array size supplied.
2N/A * SCF_ERROR_NO_MEMORY
2N/A */
2N/Astatic void *
2N/A_separate_by_separator(char *string, const char *sep, char **array, int size)
2N/A{
2N/A char *str, *token;
2N/A char *lasts;
2N/A int n = 0;
2N/A
2N/A assert(array != NULL);
2N/A assert(string != NULL);
2N/A assert(sep != NULL);
2N/A assert(size > 0);
2N/A
2N/A str = strdup(string);
2N/A if (str == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((array[n] = strtok_r(str, sep, &lasts)) == NULL) {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A
2N/A n++;
2N/A while ((token = strtok_r(NULL, sep, &lasts)) != NULL) {
2N/A if (n >= size) {
2N/A goto error;
2N/A }
2N/A array[n] = token;
2N/A n++;
2N/A }
2N/A if (n < size) {
2N/A goto error;
2N/A }
2N/A
2N/A return (str);
2N/Aerror:
2N/A free(str);
2N/A (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * check if name is among values of CHOICES_INCLUDE_VALUES
2N/A * return 0 if name is present, 1 name is not present, -1 on failure
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_check_choices_include_values(scf_propertygroup_t *pg, const char *name)
2N/A{
2N/A int n = 0, r = 1;
2N/A char **ret;
2N/A scf_values_t vals;
2N/A
2N/A if ((ret = _read_astrings_values(pg,
2N/A SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES, &vals)) == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A return (1);
2N/A
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A for (n = 0; n < vals.value_count; ++n) {
2N/A if (strcmp(name, ret[n]) == 0) {
2N/A r = 0;
2N/A break;
2N/A }
2N/A }
2N/A scf_values_destroy(&vals);
2N/A return (r);
2N/A}
2N/A
2N/Avoid
2N/Ascf_count_ranges_destroy(scf_count_ranges_t *ranges)
2N/A{
2N/A if (ranges == NULL)
2N/A return;
2N/A
2N/A ranges->scr_num_ranges = 0;
2N/A free(ranges->scr_min);
2N/A free(ranges->scr_max);
2N/A ranges->scr_min = NULL;
2N/A ranges->scr_max = NULL;
2N/A}
2N/A
2N/Avoid
2N/Ascf_int_ranges_destroy(scf_int_ranges_t *ranges)
2N/A{
2N/A if (ranges == NULL)
2N/A return;
2N/A
2N/A ranges->sir_num_ranges = 0;
2N/A free(ranges->sir_min);
2N/A free(ranges->sir_max);
2N/A ranges->sir_min = NULL;
2N/A ranges->sir_max = NULL;
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_CONSTRAINT_VIOLATED
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_scf_tmpl_get_count_ranges(const scf_prop_tmpl_t *t, const char *prop,
2N/A scf_count_ranges_t *ranges)
2N/A{
2N/A scf_values_t vals;
2N/A int i = 0;
2N/A char **ret;
2N/A char *one_range[2];
2N/A char *endptr;
2N/A char *str = NULL;
2N/A uint64_t *min = NULL;
2N/A uint64_t *max = NULL;
2N/A
2N/A assert(ranges != NULL);
2N/A if ((ret = _read_astrings_values(t->prt_pg, prop, &vals)) == NULL)
2N/A goto error;
2N/A if (vals.value_count == 0) {
2N/A /* range values are empty */
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A goto cleanup;
2N/A }
2N/A
2N/A min = malloc(vals.value_count * sizeof (uint64_t));
2N/A max = malloc(vals.value_count * sizeof (uint64_t));
2N/A if (min == NULL || max == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A for (i = 0; i < vals.value_count; ++i) {
2N/A /* min and max should be separated by a "," */
2N/A if ((str = _separate_by_separator(ret[i], ",", one_range,
2N/A 2)) == NULL)
2N/A goto cleanup;
2N/A errno = 0;
2N/A min[i] = strtoull(one_range[0], &endptr, 10);
2N/A if (errno != 0 || endptr == one_range[0] || *endptr) {
2N/A (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
2N/A goto cleanup;
2N/A }
2N/A errno = 0;
2N/A max[i] = strtoull(one_range[1], &endptr, 10);
2N/A if (errno != 0 || endptr == one_range[1] || *endptr) {
2N/A (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
2N/A goto cleanup;
2N/A }
2N/A if (min[i] > max[i]) {
2N/A (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
2N/A goto cleanup;
2N/A }
2N/A free(str);
2N/A str = NULL;
2N/A }
2N/A ranges->scr_num_ranges = vals.value_count;
2N/A ranges->scr_min = min;
2N/A ranges->scr_max = max;
2N/A scf_values_destroy(&vals);
2N/A return (0);
2N/Acleanup:
2N/A free(str);
2N/A free(min);
2N/A free(max);
2N/A scf_values_destroy(&vals);
2N/Aerror:
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A /*NOTREACHED*/
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_CONSTRAINT_VIOLATED
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_scf_tmpl_get_int_ranges(const scf_prop_tmpl_t *t, const char *prop,
2N/A scf_int_ranges_t *ranges)
2N/A{
2N/A scf_values_t vals;
2N/A int n = 0;
2N/A char **ret;
2N/A char *one_range[2];
2N/A char *endptr;
2N/A char *str = NULL;
2N/A int64_t *min = NULL;
2N/A int64_t *max = NULL;
2N/A
2N/A assert(ranges != NULL);
2N/A if ((ret = _read_astrings_values(t->prt_pg, prop, &vals)) == NULL)
2N/A goto error;
2N/A if (vals.value_count == 0) {
2N/A /* range values are empty */
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A goto cleanup;
2N/A }
2N/A
2N/A min = malloc(vals.value_count * sizeof (int64_t));
2N/A max = malloc(vals.value_count * sizeof (int64_t));
2N/A if (min == NULL || max == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A while (n < vals.value_count) {
2N/A /* min and max should be separated by a "," */
2N/A if ((str = _separate_by_separator(ret[n], ",", one_range, 2))
2N/A == NULL)
2N/A goto cleanup;
2N/A errno = 0;
2N/A min[n] = strtoll(one_range[0], &endptr, 10);
2N/A if (errno != 0 || endptr == one_range[0] || *endptr) {
2N/A (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
2N/A goto cleanup;
2N/A }
2N/A errno = 0;
2N/A max[n] = strtoll(one_range[1], &endptr, 10);
2N/A if (errno != 0 || endptr == one_range[1] || *endptr) {
2N/A (void) scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
2N/A goto cleanup;
2N/A }
2N/A if (min[n] > max[n]) {
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A goto cleanup;
2N/A }
2N/A ++n;
2N/A free(str);
2N/A str = NULL;
2N/A }
2N/A ranges->sir_num_ranges = vals.value_count;
2N/A ranges->sir_min = min;
2N/A ranges->sir_max = max;
2N/A scf_values_destroy(&vals);
2N/A return (0);
2N/Acleanup:
2N/A free(str);
2N/A free(min);
2N/A free(max);
2N/A scf_values_destroy(&vals);
2N/Aerror:
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A case SCF_ERROR_TEMPLATE_INVALID:
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A /*NOTREACHED*/
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_CONSTRAINT_VIOLATED
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Aint
2N/Ascf_tmpl_value_count_range_constraints(const scf_prop_tmpl_t *t,
2N/A scf_count_ranges_t *ranges)
2N/A{
2N/A return (_scf_tmpl_get_count_ranges(t, SCF_PROPERTY_TM_CONSTRAINT_RANGE,
2N/A ranges));
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_value_int_range_constraints(const scf_prop_tmpl_t *t,
2N/A scf_int_ranges_t *ranges)
2N/A{
2N/A return (_scf_tmpl_get_int_ranges(t, SCF_PROPERTY_TM_CONSTRAINT_RANGE,
2N/A ranges));
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_value_count_range_choices(const scf_prop_tmpl_t *t,
2N/A scf_count_ranges_t *ranges)
2N/A{
2N/A return (_scf_tmpl_get_count_ranges(t, SCF_PROPERTY_TM_CHOICES_RANGE,
2N/A ranges));
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_value_int_range_choices(const scf_prop_tmpl_t *t,
2N/A scf_int_ranges_t *ranges)
2N/A{
2N/A return (_scf_tmpl_get_int_ranges(t, SCF_PROPERTY_TM_CHOICES_RANGE,
2N/A ranges));
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Aint
2N/Ascf_tmpl_value_name_choices(const scf_prop_tmpl_t *t, scf_values_t *vals)
2N/A{
2N/A int c_flag = 0; /* have not read any value yet */
2N/A int r;
2N/A char **ret;
2N/A
2N/A /* First, look for explicitly declared choices. */
2N/A if ((ret = _read_astrings_values(t->prt_pg,
2N/A SCF_PROPERTY_TM_CHOICES_NAME, vals)) != NULL) {
2N/A c_flag = 1;
2N/A } else if (scf_error() != SCF_ERROR_NOT_FOUND) {
2N/A goto error;
2N/A }
2N/A
2N/A /* Next, check for choices included by 'values'. */
2N/A if ((r = _check_choices_include_values(t->prt_pg, "values")) == 0) {
2N/A /* read values_name */
2N/A if (c_flag == 1)
2N/A /* append values */
2N/A ret = _append_astrings_values(t->prt_pg,
2N/A SCF_PROPERTY_TM_VALUES_NAME, vals);
2N/A else
2N/A /* read values */
2N/A ret = _read_astrings_values(t->prt_pg,
2N/A SCF_PROPERTY_TM_VALUES_NAME, vals);
2N/A if (ret != NULL) {
2N/A c_flag = 1;
2N/A } else if (scf_error() != SCF_ERROR_NOT_FOUND) {
2N/A goto error;
2N/A }
2N/A } else if (r == -1) {
2N/A goto error;
2N/A }
2N/A
2N/A /* Finally check for choices included by 'constraints'. */
2N/A if ((r = _check_choices_include_values(t->prt_pg, "constraints")) ==
2N/A 0) {
2N/A /* read constraint_name */
2N/A if (c_flag == 1)
2N/A /* append values */
2N/A ret = _append_astrings_values(t->prt_pg,
2N/A SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
2N/A else
2N/A /* read values */
2N/A ret = _read_astrings_values(t->prt_pg,
2N/A SCF_PROPERTY_TM_CONSTRAINT_NAME, vals);
2N/A if (ret != NULL) {
2N/A c_flag = 1;
2N/A } else if (scf_error() != SCF_ERROR_NOT_FOUND) {
2N/A goto error;
2N/A }
2N/A } else if (r == -1) {
2N/A goto error;
2N/A }
2N/A
2N/A if (c_flag == 0 || vals->value_count == 0) {
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A
2N/Aerror:
2N/A if (ismember(scf_error(), errors_server)) {
2N/A return (-1);
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A
2N/A case SCF_ERROR_NOT_SET:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A /*NOTREACHED*/
2N/A}
2N/A
2N/Avoid
2N/Ascf_values_destroy(scf_values_t *vals)
2N/A{
2N/A int i;
2N/A char **items = NULL;
2N/A char **str = NULL;
2N/A
2N/A if (vals == NULL)
2N/A return;
2N/A
2N/A str = vals->values_as_strings;
2N/A
2N/A /* free values */
2N/A switch (vals->value_type) {
2N/A case SCF_TYPE_BOOLEAN:
2N/A free(vals->values.v_boolean);
2N/A break;
2N/A case SCF_TYPE_COUNT:
2N/A free(vals->values.v_count);
2N/A break;
2N/A case SCF_TYPE_INTEGER:
2N/A free(vals->values.v_integer);
2N/A break;
2N/A case SCF_TYPE_ASTRING:
2N/A items = vals->values.v_astring;
2N/A str = NULL;
2N/A break;
2N/A case SCF_TYPE_USTRING:
2N/A items = vals->values.v_ustring;
2N/A str = NULL;
2N/A break;
2N/A case SCF_TYPE_OPAQUE:
2N/A items = vals->values.v_opaque;
2N/A str = NULL;
2N/A break;
2N/A case SCF_TYPE_TIME:
2N/A free(vals->values.v_time);
2N/A break;
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A for (i = 0; i < vals->value_count; ++i) {
2N/A if (items != NULL)
2N/A free(items[i]);
2N/A if (str != NULL)
2N/A free(str[i]);
2N/A }
2N/A vals->value_count = 0;
2N/A free(items);
2N/A free(str);
2N/A}
2N/A
2N/A/*
2N/A * char *_make_value_name()
2N/A *
2N/A * Construct the prefix for a value common name or value description property.
2N/A * It takes the form:
2N/A * value_<BASE32 name>_<common_name|description>_
2N/A * This is then combined with a localized suffix by the caller to look
2N/A * up the property in the repository:
2N/A * value_<BASE32 name>_<common_name|description>_<lang>
2N/A *
2N/A * Returns NULL on failure. Sets scf_error():
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * Name isn't short enough make a value name with.
2N/A * SCF_ERROR_NO_MEMORY
2N/A */
2N/Astatic char *
2N/A_make_value_name(char *desc_name, const char *value)
2N/A{
2N/A char *name = NULL;
2N/A char *encoded = NULL;
2N/A ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A
2N/A name = malloc(sz);
2N/A encoded = malloc(sz);
2N/A if (name == NULL || encoded == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A free(name);
2N/A free(encoded);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (scf_encode32(value, strlen(value), encoded, sz, NULL,
2N/A SCF_ENCODE32_PAD) != 0) {
2N/A /* Shouldn't happen. */
2N/A assert(0);
2N/A }
2N/A
2N/A (void) strlcpy(name, SCF_PROPERTY_TM_VALUE_PREFIX, sz);
2N/A
2N/A if (strlcat(name, encoded, sz) >= sz) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A free(name);
2N/A free(encoded);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (strlcat(name, "_", sz) >= sz) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A free(name);
2N/A free(encoded);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (strlcat(name, desc_name, sz) >= sz) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A free(name);
2N/A free(encoded);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (strlcat(name, "_", sz) >= sz) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A free(name);
2N/A free(encoded);
2N/A return (NULL);
2N/A }
2N/A
2N/A free(encoded);
2N/A return (name);
2N/A}
2N/A
2N/Assize_t
2N/Ascf_tmpl_value_common_name_loc(const scf_prop_tmpl_t *t, const char *locale,
2N/A const char *value, char **out, const char **elocale)
2N/A{
2N/A char *value_name = NULL;
2N/A
2N/A value_name = _make_value_name("common_name", value);
2N/A if (value_name == NULL)
2N/A return (-1);
2N/A
2N/A *out = _read_localized_astring_from_pg(t->prt_pg, value_name, locale,
2N/A elocale);
2N/A
2N/A free(value_name);
2N/A
2N/A if (*out == NULL)
2N/A return (-1);
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A
2N/A/*
2N/A * ssize_t scf_tmpl_value_common_name()
2N/A *
2N/A * Populates "out" with an allocated string containing the value's
2N/A * common name. Returns the size of the string on successful return.
2N/A * out must be freed with free() on successful return.
2N/A *
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * Property group was deleted.
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * name not a valid property name
2N/A * name and locale are too long to make a property name
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * property is not SCF_TYPE_ASTRING has more than one value.
2N/A */
2N/Assize_t
2N/Ascf_tmpl_value_common_name(const scf_prop_tmpl_t *t, const char *locale,
2N/A const char *value, char **out)
2N/A{
2N/A return (scf_tmpl_value_common_name_loc(t, locale, value, out, NULL));
2N/A}
2N/A
2N/Assize_t
2N/Ascf_tmpl_value_description_loc(const scf_prop_tmpl_t *t, const char *locale,
2N/A const char *value, char **out, const char **elocale)
2N/A{
2N/A char *value_name = NULL;
2N/A
2N/A value_name = _make_value_name("description", value);
2N/A if (value_name == NULL)
2N/A return (-1);
2N/A
2N/A
2N/A *out = _read_localized_astring_from_pg(t->prt_pg, value_name, locale,
2N/A elocale);
2N/A
2N/A free(value_name);
2N/A
2N/A if (*out == NULL)
2N/A return (-1);
2N/A
2N/A return (strlen(*out));
2N/A}
2N/A
2N/A/*
2N/A * ssize_t scf_tmpl_value_description()
2N/A *
2N/A * Populates "out" with an allocated string containing the value's
2N/A * description. Returns the size of the string on successful return.
2N/A * out must be freed with free() on successful return.
2N/A *
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * Property group was deleted.
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * name not a valid property name
2N/A * name and locale are too long to make a property name
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * Property doesn't exist or exists and has no value.
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A * property is not SCF_TYPE_ASTRING has more than one value.
2N/A */
2N/Assize_t
2N/Ascf_tmpl_value_description(const scf_prop_tmpl_t *t, const char *locale,
2N/A const char *value, char **out)
2N/A{
2N/A return (scf_tmpl_value_description_loc(t, locale, value, out, NULL));
2N/A}
2N/A
2N/A/*
2N/A * Templates error messages format, in human readable form.
2N/A * Each line is one error item:
2N/A *
2N/A * prefix error message
2N/A * FMRI="err->te_errs->tes_fmri"
2N/A * Property group="err->te_pg_name"
2N/A * Property name="err->te_prop_name"
2N/A * expected value 1="err->te_ev1"
2N/A * expected value 2="err->te_ev2"
2N/A * actual value="err->te_actual"
2N/A * Tempalte source="err->te_tmpl_fmri"
2N/A * pg_pattern name="err->tmpl_pg_name"
2N/A * pg_pattern type="err->tmpl_pg_type"
2N/A * prop_pattern name="err->tmpl_prop_name"
2N/A * prop_pattern type="err->tmpl_prop_type"
2N/A *
2N/A * To add a new error type, include scf_tmpl_error_type_t in libscf.h
2N/A * add one entry in em_desc[], and update the functions pointed by the
2N/A * _tmpl_error_access array with the new error code. Also, update the
2N/A * scf_tmpl_error_* functions to provide access to desired
2N/A * scf_tmpl_error_t fields.
2N/A *
2N/A * To add a new error item, add a new field to scf_tmpl_error_t, a new field
2N/A * in _scf_tmpl_error_desc or a new non-error-dependent string, add a new entry
2N/A * in _tmpl_error_access array and create the appropriate get_val, get_desc
2N/A * functions.
2N/A *
2N/A * Changes to both the validation logic and the error types and items must
2N/A * be coordinated with the code in svccfg to ensure both libscf and svccfg's
2N/A * manifest validation validate the same things.
2N/A */
2N/A
2N/A/*
2N/A * Container for all template errors on a validated object.
2N/A */
2N/Astruct scf_tmpl_errors {
2N/A int tes_index;
2N/A int tes_num_errs;
2N/A scf_tmpl_error_t **tes_errs;
2N/A int tes_errs_size;
2N/A const char *tes_fmri;
2N/A const char *tes_prefix;
2N/A int tes_flag; /* if set, scf_tmpl_error_destroy */
2N/A /* will free strings in tes_errs */
2N/A};
2N/A
2N/A/*
2N/A * Templates error-dependent labels
2N/A */
2N/Astruct _scf_tmpl_error_desc {
2N/A const char *em_msg;
2N/A const char *em_ev1;
2N/A const char *em_ev2;
2N/A const char *em_actual;
2N/A};
2N/A
2N/A/*
2N/A * This array MUST be kept in synch with the template error definition of
2N/A * scf_tmpl_error_type_t in libscf.h
2N/A */
2N/Astatic struct _scf_tmpl_error_desc em_desc[] = {
2N/A /* SCF_TERR_MISSING_PG */
2N/A { "Required property group missing", "Name of missing property group",
2N/A "Type of missing property group", NULL },
2N/A /* SCF_TERR_WRONG_PG_TYPE */
2N/A { "Property group has bad type", "Specified type", NULL,
2N/A "Actual type" },
2N/A /* SCF_TERR_MISSING_PROP */
2N/A { "Required property missing", "Name of missing property", NULL, NULL },
2N/A /* SCF_TERR_WRONG_PROP_TYPE */
2N/A { "Property has bad type", "Specified property type", NULL,
2N/A "Actual property type" },
2N/A /* SCF_TERR_CARDINALITY_VIOLATION */
2N/A { "Number of property values violates cardinality restriction",
2N/A "Cardinality minimum", "Cardinality maximum",
2N/A "Actual number of values" },
2N/A /* SCF_TERR_VALUE_CONSTRAINT_VIOLATED */
2N/A { "Property has illegal value", NULL, NULL, "Illegal value" },
2N/A /* SCF_TERR_RANGE_VIOLATION */
2N/A { "Property value is out of range", NULL, NULL, "Actual value" },
2N/A /* SCF_TERR_PG_REDEFINE */
2N/A { "Instance redefines pg_pattern", "Instance pg_pattern name",
2N/A "Instance pg_pattern type", NULL },
2N/A /* SCF_TERR_PROP_TYPE_MISMATCH */
2N/A { "Property type and value type mismatch", NULL, NULL, "Value type" },
2N/A /* SCF_TERR_VALUE_OUT_OF_RANGE */
2N/A { "Value is out of range", NULL, NULL, "Value" },
2N/A /* SCF_TERR_INVALID_VALUE */
2N/A { "Value is not valid", NULL, NULL, "Value" },
2N/A /* SCF_TERR_PG_PATTERN_CONFLICT */
2N/A { "Conflicting pg_pattern specifications", "Template source",
2N/A "pg_pattern name", "pg_pattern type" },
2N/A /* SCF_TERR_PROP_PATTERN_CONFLICT */
2N/A { "Conflicting prop_pattern specifications", "Template source",
2N/A "prop_pattern name", "prop_pattern type" },
2N/A /* SCF_TERR_GENERAL_REDEFINE */
2N/A { "Service or instance pg_pattern redefines a global or restarter "
2N/A "pg_pattern", "Template source", "pg_pattern name",
2N/A "pg_pattern type" },
2N/A /* SCF_TERR_INCLUDE_VALUES */
2N/A { "Missing constraints or values for include_values element",
2N/A "include_values type", NULL, NULL },
2N/A /* SCF_TERR_PG_PATTERN_INCOMPLETE */
2N/A { "Required pg_pattern is missing a name or type attribute",
2N/A NULL, NULL, NULL },
2N/A /* SCF_TERR_PROP_PATTERN_INCOMPLETE */
2N/A { "Required prop_pattern is missing a type attribute",
2N/A NULL, NULL, NULL }
2N/A};
2N/A
2N/A/*
2N/A * Templates non error-dependent labels
2N/A */
2N/Astatic const char *em_fmri = "FMRI";
2N/Astatic const char *em_pg_name = "Property group";
2N/Astatic const char *em_prop_name = "Property name";
2N/Astatic const char *em_tmpl_fmri = "Template source";
2N/Astatic const char *em_tmpl_pg_name = "pg_pattern name";
2N/Astatic const char *em_tmpl_pg_type = "pg_pattern type";
2N/Astatic const char *em_tmpl_prop_name = "prop_pattern name";
2N/Astatic const char *em_tmpl_prop_type = "prop_pattern type";
2N/A
2N/Astatic const char *
2N/A_get_fmri_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A return (dgettext(TEXT_DOMAIN, em_fmri));
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_pg_name_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A return (dgettext(TEXT_DOMAIN, em_pg_name));
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_prop_name_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A return (dgettext(TEXT_DOMAIN, em_prop_name));
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_ev1_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A return (dgettext(TEXT_DOMAIN, em_desc[err->te_type].em_ev1));
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_ev2_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A return (dgettext(TEXT_DOMAIN, em_desc[err->te_type].em_ev2));
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_actual_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A return (dgettext(TEXT_DOMAIN,
2N/A em_desc[err->te_type].em_actual));
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_fmri_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A return (dgettext(TEXT_DOMAIN, em_tmpl_fmri));
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_pg_name_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A return (dgettext(TEXT_DOMAIN, em_tmpl_pg_name));
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_pg_type_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A return (dgettext(TEXT_DOMAIN, em_tmpl_pg_type));
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_prop_name_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A return (dgettext(TEXT_DOMAIN, em_tmpl_prop_name));
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_prop_type_desc(scf_tmpl_error_t *err)
2N/A{
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PROP_PATTERN_CONFLICT:
2N/A case SCF_TERR_INCLUDE_VALUES:
2N/A return (dgettext(TEXT_DOMAIN, em_tmpl_prop_type));
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A case SCF_TERR_PG_PATTERN_CONFLICT:
2N/A case SCF_TERR_GENERAL_REDEFINE:
2N/A case SCF_TERR_PG_PATTERN_INCOMPLETE:
2N/A case SCF_TERR_PROP_PATTERN_INCOMPLETE:
2N/A default:
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_fmri_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL && err->te_errs != NULL &&
2N/A err->te_errs->tes_fmri != NULL);
2N/A return (err->te_errs->tes_fmri);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_pg_name_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_pg_name);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_prop_name_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_prop_name);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_ev1_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_ev1);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_ev2_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_ev2);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_actual_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_actual);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_fmri_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_tmpl_fmri);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_pg_name_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_tmpl_pg_name);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_pg_type_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_tmpl_pg_type);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_prop_name_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_tmpl_prop_name);
2N/A}
2N/A
2N/Astatic const char *
2N/A_get_tmpl_prop_type_val(scf_tmpl_error_t *err)
2N/A{
2N/A assert(err != NULL);
2N/A return (err->te_tmpl_prop_type);
2N/A}
2N/A
2N/A/*
2N/A * Templates error item retrival functions
2N/A */
2N/Atypedef const char *(*get_em)(scf_tmpl_error_t *);
2N/A
2N/A/*
2N/A * if new items (lines) are added to the templates error messages,
2N/A * new entries in this array (and new fuctions) will be required.
2N/A */
2N/Astatic struct _tmpl_error_access {
2N/A get_em get_desc;
2N/A get_em get_val;
2N/A} _tmpl_error_items[] = {
2N/A { (get_em)_get_fmri_desc, (get_em)_get_fmri_val },
2N/A { (get_em)_get_pg_name_desc, (get_em)_get_pg_name_val },
2N/A { (get_em)_get_prop_name_desc, (get_em)_get_prop_name_val },
2N/A { (get_em)_get_ev1_desc, (get_em)_get_ev1_val },
2N/A { (get_em)_get_ev2_desc, (get_em)_get_ev2_val },
2N/A { (get_em)_get_actual_desc, (get_em)_get_actual_val },
2N/A { (get_em)_get_tmpl_fmri_desc, (get_em)_get_tmpl_fmri_val },
2N/A { (get_em)_get_tmpl_pg_name_desc, (get_em)_get_tmpl_pg_name_val },
2N/A { (get_em)_get_tmpl_pg_type_desc, (get_em)_get_tmpl_pg_type_val },
2N/A { (get_em)_get_tmpl_prop_name_desc, (get_em)_get_tmpl_prop_name_val },
2N/A { (get_em)_get_tmpl_prop_type_desc, (get_em)_get_tmpl_prop_type_val },
2N/A { NULL }
2N/A};
2N/A
2N/A/*
2N/A * Allocate a new scf_tmpl_error_t and add it to the errs list provided.
2N/A * Returns NULL on failure. Sets scf_error():
2N/A * SCF_ERROR_NO_MEMORY
2N/A */
2N/Astatic scf_tmpl_error_t *
2N/A_create_error(scf_tmpl_errors_t *errs)
2N/A{
2N/A scf_tmpl_error_t *ret;
2N/A scf_tmpl_error_t **saved_errs;
2N/A
2N/A assert(errs != NULL);
2N/A ret = calloc(1, sizeof (scf_tmpl_error_t));
2N/A if (ret == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (NULL);
2N/A }
2N/A
2N/A ret->te_errs = errs;
2N/A
2N/A assert(errs->tes_num_errs <= errs->tes_errs_size);
2N/A if (errs->tes_num_errs == errs->tes_errs_size) {
2N/A /* Time to grow the pointer array. */
2N/A saved_errs = errs->tes_errs;
2N/A errs->tes_errs = calloc(2 * errs->tes_errs_size,
2N/A sizeof (scf_tmpl_error_t *));
2N/A if (errs->tes_errs == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A errs->tes_errs = saved_errs;
2N/A free(ret);
2N/A return (NULL);
2N/A }
2N/A (void) memcpy(errs->tes_errs, saved_errs, errs->tes_errs_size *
2N/A sizeof (scf_tmpl_error_t *));
2N/A errs->tes_errs_size = 2 * errs->tes_errs_size;
2N/A free(saved_errs);
2N/A }
2N/A
2N/A errs->tes_errs[errs->tes_num_errs] = ret;
2N/A errs->tes_num_errs++;
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A *
2N/A * If destroy_strings is set, scf_tmpl_errors_destroy will free the
2N/A * strings in scf_tmpl_error_t entries.
2N/A *
2N/A * Returns NULL on failure. Sets scf_error():
2N/A * SCF_ERROR_NO_MEMORY
2N/A */
2N/Ascf_tmpl_errors_t *
2N/A_scf_create_errors(const char *fmri, int destroy_strings)
2N/A{
2N/A scf_tmpl_errors_t *ret;
2N/A int errs_size = 20;
2N/A
2N/A assert(fmri != NULL);
2N/A
2N/A ret = calloc(1, sizeof (scf_tmpl_errors_t));
2N/A if (ret == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (NULL);
2N/A }
2N/A
2N/A ret->tes_index = 0;
2N/A ret->tes_num_errs = 0;
2N/A if ((ret->tes_fmri = strdup(fmri)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A free(ret);
2N/A return (NULL);
2N/A }
2N/A
2N/A ret->tes_prefix = strdup("");
2N/A if (ret->tes_prefix == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A free((char *)ret->tes_fmri);
2N/A free(ret);
2N/A return (NULL);
2N/A }
2N/A ret->tes_flag = destroy_strings;
2N/A
2N/A /* Make space for a few errors. */
2N/A ret->tes_errs = calloc(errs_size, sizeof (scf_tmpl_error_t *));
2N/A if (ret->tes_errs == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A free((char *)ret->tes_fmri);
2N/A free((char *)ret->tes_prefix);
2N/A free(ret);
2N/A return (NULL);
2N/A }
2N/A ret->tes_errs_size = errs_size;
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, if fails set scf_error() to:
2N/A *
2N/A * SCF_ERROR_NO_MEMORY
2N/A */
2N/Aint
2N/A_scf_tmpl_error_set_prefix(scf_tmpl_errors_t *errs, const char *prefix)
2N/A{
2N/A free((void *) errs->tes_prefix);
2N/A if (prefix == NULL)
2N/A errs->tes_prefix = strdup("");
2N/A else
2N/A errs->tes_prefix = strdup(prefix);
2N/A if (errs->tes_prefix == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A *
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_NO_MEMORY
2N/A */
2N/Aint
2N/A_scf_tmpl_add_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
2N/A const char *pg_name, const char *prop_name,
2N/A const char *ev1, const char *ev2, const char *actual,
2N/A const char *tmpl_fmri, const char *tmpl_pg_name, const char *tmpl_pg_type,
2N/A const char *tmpl_prop_name, const char *tmpl_prop_type)
2N/A{
2N/A scf_tmpl_error_t *err;
2N/A
2N/A assert(errs != NULL);
2N/A assert(tmpl_fmri != NULL);
2N/A
2N/A err = _create_error(errs);
2N/A if (err == NULL)
2N/A return (-1);
2N/A
2N/A err->te_type = type;
2N/A err->te_pg_name = pg_name;
2N/A err->te_prop_name = prop_name;
2N/A err->te_ev1 = ev1;
2N/A err->te_ev2 = ev2;
2N/A err->te_actual = actual;
2N/A err->te_tmpl_fmri = tmpl_fmri;
2N/A err->te_tmpl_pg_name = tmpl_pg_name;
2N/A err->te_tmpl_pg_type = tmpl_pg_type;
2N/A err->te_tmpl_prop_name = tmpl_prop_name;
2N/A err->te_tmpl_prop_type = tmpl_prop_type;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * returns an allocated string that must be freed with free()
2N/A * string contains converted 64-bit integer value
2N/A * flag set for signed values
2N/A * if fails return NULL and set scf_error() to:
2N/A * SCF_ERROR_NO_MEMORY
2N/A */
2N/Astatic char *
2N/A_val_to_string(uint64_t val, int flag)
2N/A{
2N/A ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
2N/A char *buf;
2N/A
2N/A buf = malloc(sz);
2N/A if (buf == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (flag == 0)
2N/A (void) snprintf(buf, sz, "%" PRIu64, val);
2N/A else
2N/A (void) snprintf(buf, sz, "%" PRIi64, (int64_t)val);
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, -1 on failure.
2N/A * set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_add_tmpl_missing_pg_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t)
2N/A{
2N/A char *ev1 = NULL;
2N/A char *ev2 = NULL;
2N/A char *t_fmri = NULL;
2N/A char *t_pg_name = NULL;
2N/A char *t_pg_type = NULL;
2N/A
2N/A if ((t_fmri = _scf_tmpl_get_fmri(t)) == NULL)
2N/A return (-1);
2N/A if (scf_tmpl_pg_name(t, &t_pg_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(t, &t_pg_type) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if ((ev1 = strdup(t_pg_name)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A if ((ev2 = strdup(t_pg_type)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A return (_scf_tmpl_add_error(errs, SCF_TERR_MISSING_PG, NULL, NULL, ev1,
2N/A ev2, NULL, t_fmri, t_pg_name, t_pg_type, NULL, NULL));
2N/Acleanup:
2N/A free(ev1);
2N/A free(ev2);
2N/A free(t_fmri);
2N/A free(t_pg_name);
2N/A free(t_pg_type);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, -1 on failure.
2N/A * set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_add_tmpl_wrong_pg_type_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t,
2N/A scf_propertygroup_t *pg)
2N/A{
2N/A char *pg_name = NULL;
2N/A char *ev1 = NULL;
2N/A char *actual = NULL;
2N/A char *t_fmri = NULL;
2N/A char *t_pg_name = NULL;
2N/A char *t_pg_type = NULL;
2N/A
2N/A if ((t_fmri = _scf_tmpl_get_fmri(t)) == NULL)
2N/A return (-1);
2N/A if ((pg_name = _scf_get_pg_name(pg)) == NULL)
2N/A goto cleanup;
2N/A if ((actual = _scf_get_pg_type(pg)) == NULL)
2N/A goto cleanup;
2N/A if (scf_tmpl_pg_name(t, &t_pg_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(t, &t_pg_type) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if ((ev1 = strdup(t_pg_type)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A return (_scf_tmpl_add_error(errs, SCF_TERR_WRONG_PG_TYPE, pg_name, NULL,
2N/A ev1, NULL, actual, t_fmri, t_pg_name, t_pg_type, NULL, NULL));
2N/Acleanup:
2N/A free(pg_name);
2N/A free(ev1);
2N/A free(actual);
2N/A free(t_fmri);
2N/A free(t_pg_name);
2N/A free(t_pg_type);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, -1 on failure.
2N/A * set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_add_tmpl_missing_prop_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t,
2N/A scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt)
2N/A{
2N/A char *pg_name = NULL;
2N/A char *ev1 = NULL;
2N/A char *t_fmri = NULL;
2N/A char *t_pg_name = NULL;
2N/A char *t_pg_type = NULL;
2N/A char *t_prop_name = NULL;
2N/A char *t_prop_type = NULL;
2N/A
2N/A if ((t_fmri = _scf_tmpl_get_fmri(t)) == NULL)
2N/A return (-1);
2N/A if ((pg_name = _scf_get_pg_name(pg)) == NULL)
2N/A goto cleanup;
2N/A if (scf_tmpl_pg_name(t, &t_pg_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(t, &t_pg_type) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
2N/A if (t_prop_type != NULL && t_prop_type[0] == '\0') {
2N/A free(t_prop_type);
2N/A t_prop_type = NULL;
2N/A } else if (t_prop_type == NULL) {
2N/A goto cleanup;
2N/A }
2N/A if (t_prop_type == NULL)
2N/A if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A if ((ev1 = strdup(t_prop_name)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A return (_scf_tmpl_add_error(errs, SCF_TERR_MISSING_PROP, pg_name, NULL,
2N/A ev1, NULL, NULL, t_fmri, t_pg_name, t_pg_type, t_prop_name,
2N/A t_prop_type));
2N/Acleanup:
2N/A free(pg_name);
2N/A free(ev1);
2N/A free(t_fmri);
2N/A free(t_pg_name);
2N/A free(t_pg_type);
2N/A free(t_prop_name);
2N/A free(t_prop_type);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, -1 on failure.
2N/A * set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_add_tmpl_wrong_prop_type_error(scf_tmpl_errors_t *errs,
2N/A scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop)
2N/A{
2N/A char *pg_name = NULL;
2N/A char *prop_name = NULL;
2N/A char *ev1 = NULL;
2N/A char *actual = NULL;
2N/A char *t_fmri = NULL;
2N/A char *t_pg_name = NULL;
2N/A char *t_pg_type = NULL;
2N/A char *t_prop_name = NULL;
2N/A char *t_prop_type = NULL;
2N/A
2N/A if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
2N/A return (-1);
2N/A if ((pg_name = _scf_get_pg_name(pg)) == NULL)
2N/A goto cleanup;
2N/A if ((prop_name = _scf_get_prop_name(prop)) == NULL)
2N/A goto cleanup;
2N/A if ((actual = _scf_get_prop_type(prop)) == NULL)
2N/A goto cleanup;
2N/A if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
2N/A if (t_prop_type != NULL && t_prop_type[0] == '\0') {
2N/A free(t_prop_type);
2N/A t_prop_type = NULL;
2N/A } else if (t_prop_type == NULL) {
2N/A goto cleanup;
2N/A }
2N/A if (t_prop_type == NULL)
2N/A if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A if ((ev1 = strdup(t_prop_type)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A return (_scf_tmpl_add_error(errs, SCF_TERR_WRONG_PROP_TYPE, pg_name,
2N/A prop_name, ev1, NULL, actual, t_fmri, t_pg_name, t_pg_type,
2N/A t_prop_name, t_prop_type));
2N/Acleanup:
2N/A free(pg_name);
2N/A free(prop_name);
2N/A free(ev1);
2N/A free(actual);
2N/A free(t_fmri);
2N/A free(t_pg_name);
2N/A free(t_pg_type);
2N/A free(t_prop_name);
2N/A free(t_prop_type);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, -1 on failure.
2N/A * set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_add_tmpl_count_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
2N/A scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop,
2N/A uint64_t count, uint64_t *min, uint64_t *max)
2N/A{
2N/A char *pg_name = NULL;
2N/A char *prop_name = NULL;
2N/A char *s_min = NULL;
2N/A char *s_max = NULL;
2N/A char *num = NULL;
2N/A char *t_fmri = NULL;
2N/A char *t_pg_name = NULL;
2N/A char *t_pg_type = NULL;
2N/A char *t_prop_name = NULL;
2N/A char *t_prop_type = NULL;
2N/A
2N/A if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
2N/A return (-1);
2N/A switch (type) {
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A if ((pg_name = _scf_get_pg_name(pg)) == NULL)
2N/A goto cleanup;
2N/A if ((prop_name = _scf_get_prop_name(prop)) == NULL)
2N/A goto cleanup;
2N/A break;
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A /* keep pg_name = NULL and prop_name = NULL */
2N/A break;
2N/A }
2N/A if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
2N/A if (t_prop_type != NULL && t_prop_type[0] == '\0') {
2N/A free(t_prop_type);
2N/A t_prop_type = NULL;
2N/A } else if (t_prop_type == NULL) {
2N/A goto cleanup;
2N/A }
2N/A if (t_prop_type == NULL)
2N/A if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A if (min == NULL) {
2N/A if ((s_min = strdup("")) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A if ((s_min = _val_to_string(*min, 0)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A }
2N/A if (max == NULL) {
2N/A if ((s_max = strdup("")) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A if ((s_max = _val_to_string(*max, 0)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A }
2N/A if ((num = _val_to_string(count, 0)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A return (_scf_tmpl_add_error(errs, type, pg_name, prop_name, s_min,
2N/A s_max, num, t_fmri, t_pg_name, t_pg_type, t_prop_name,
2N/A t_prop_type));
2N/Acleanup:
2N/A free(pg_name);
2N/A free(prop_name);
2N/A free(s_min);
2N/A free(s_max);
2N/A free(num);
2N/A free(t_fmri);
2N/A free(t_pg_name);
2N/A free(t_pg_type);
2N/A free(t_prop_name);
2N/A free(t_prop_type);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, -1 on failure.
2N/A * set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_add_tmpl_constraint_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
2N/A scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop,
2N/A scf_value_t *val)
2N/A{
2N/A scf_type_t val_type;
2N/A char *pg_name = NULL;
2N/A char *prop_name = NULL;
2N/A char *value = NULL;
2N/A char *t_fmri = NULL;
2N/A char *t_pg_name = NULL;
2N/A char *t_pg_type = NULL;
2N/A char *t_prop_name = NULL;
2N/A char *t_prop_type = NULL;
2N/A
2N/A if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
2N/A return (-1);
2N/A switch (type) {
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A if ((pg_name = _scf_get_pg_name(pg)) == NULL)
2N/A goto cleanup;
2N/A if ((prop_name = _scf_get_prop_name(prop)) == NULL)
2N/A goto cleanup;
2N/A /*FALLTHROUGH*/
2N/A case SCF_TERR_INVALID_VALUE:
2N/A /* keep pg_name = NULL and prop_name = NULL */
2N/A if ((value = _scf_value_get_as_string(val)) == NULL)
2N/A goto cleanup;
2N/A break;
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A /* keep pg_name = NULL and prop_name = NULL */
2N/A /* use value for value type */
2N/A val_type = scf_value_type(val);
2N/A if ((value = strdup(scf_type_to_string(val_type))) ==
2N/A NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A break;
2N/A }
2N/A if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
2N/A if (t_prop_type != NULL && t_prop_type[0] == '\0') {
2N/A free(t_prop_type);
2N/A t_prop_type = NULL;
2N/A } else if (t_prop_type == NULL) {
2N/A goto cleanup;
2N/A }
2N/A if (t_prop_type == NULL)
2N/A if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A return (_scf_tmpl_add_error(errs, type, pg_name, prop_name, NULL, NULL,
2N/A value, t_fmri, t_pg_name, t_pg_type, t_prop_name, t_prop_type));
2N/Acleanup:
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A free(pg_name);
2N/A free(prop_name);
2N/A free(value);
2N/A free(t_fmri);
2N/A free(t_pg_name);
2N/A free(t_pg_type);
2N/A free(t_prop_name);
2N/A free(t_prop_type);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, -1 on failure.
2N/A * set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_add_tmpl_int_error(scf_tmpl_errors_t *errs, scf_tmpl_error_type_t type,
2N/A scf_propertygroup_t *pg, const scf_prop_tmpl_t *pt, scf_property_t *prop,
2N/A int64_t val, int64_t *min, int64_t *max)
2N/A{
2N/A char *pg_name = NULL;
2N/A char *prop_name = NULL;
2N/A char *s_min = NULL;
2N/A char *s_max = NULL;
2N/A char *value = NULL;
2N/A char *t_fmri = NULL;
2N/A char *t_pg_name = NULL;
2N/A char *t_pg_type = NULL;
2N/A char *t_prop_name = NULL;
2N/A char *t_prop_type = NULL;
2N/A
2N/A if ((t_fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
2N/A return (-1);
2N/A
2N/A switch (type) {
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A if ((pg_name = _scf_get_pg_name(pg)) == NULL)
2N/A goto cleanup;
2N/A if ((prop_name = _scf_get_prop_name(prop)) == NULL)
2N/A goto cleanup;
2N/A break;
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A /* keep pg_name = NULL and prop_name = NULL */
2N/A break;
2N/A }
2N/A if (scf_tmpl_pg_name(pt->prt_t, &t_pg_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(pt->prt_t, &t_pg_type) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_prop_name(pt, &t_prop_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A t_prop_type = _scf_read_tmpl_prop_type_as_string(pt);
2N/A if (t_prop_type != NULL && t_prop_type[0] == '\0') {
2N/A free(t_prop_type);
2N/A t_prop_type = NULL;
2N/A } else if (t_prop_type == NULL) {
2N/A goto cleanup;
2N/A }
2N/A if (t_prop_type == NULL)
2N/A if ((t_prop_type = strdup(SCF_TMPL_WILDCARD)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A if (min == NULL) {
2N/A if ((s_min = strdup("")) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A if ((s_min = _val_to_string(*((uint64_t *)min), 1)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A }
2N/A if (max == NULL) {
2N/A if ((s_max = strdup("")) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A if ((s_max = _val_to_string(*((uint64_t *)max), 1)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A }
2N/A if ((value = _val_to_string((uint64_t)val, 1)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A return (_scf_tmpl_add_error(errs, type, pg_name, prop_name, s_min,
2N/A s_max, value, t_fmri, t_pg_name, t_pg_type, t_prop_name,
2N/A t_prop_type));
2N/Acleanup:
2N/A free(pg_name);
2N/A free(prop_name);
2N/A free(s_min);
2N/A free(s_max);
2N/A free(value);
2N/A free(t_fmri);
2N/A free(t_pg_name);
2N/A free(t_pg_type);
2N/A free(t_prop_name);
2N/A free(t_prop_type);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, -1 on failure.
2N/A * set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_add_tmpl_pg_redefine_error(scf_tmpl_errors_t *errs, scf_pg_tmpl_t *t,
2N/A scf_pg_tmpl_t *r)
2N/A{
2N/A char *ev1 = NULL;
2N/A char *ev2 = NULL;
2N/A char *t_fmri = NULL;
2N/A char *t_pg_name = NULL;
2N/A char *t_pg_type = NULL;
2N/A
2N/A if ((t_fmri = _scf_tmpl_get_fmri(r)) == NULL)
2N/A return (-1);
2N/A if (scf_tmpl_pg_name(r, &t_pg_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(r, &t_pg_type) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_name(t, &ev1) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(t, &ev2) == -1) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A return (_scf_tmpl_add_error(errs, SCF_TERR_PG_REDEFINE, NULL, NULL,
2N/A ev1, ev2, NULL, t_fmri, t_pg_name, t_pg_type, NULL, NULL));
2N/Acleanup:
2N/A free(ev1);
2N/A free(ev2);
2N/A free(t_fmri);
2N/A free(t_pg_name);
2N/A free(t_pg_type);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * return 0 if value is within count ranges constraint.
2N/A * return -1 otherwise
2N/A */
2N/Astatic int
2N/A_check_count_ranges(scf_count_ranges_t *cr, uint64_t v)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < cr->scr_num_ranges; ++i) {
2N/A if (v >= cr->scr_min[i] &&
2N/A v <= cr->scr_max[i]) {
2N/A /* value is within ranges constraint */
2N/A return (0);
2N/A }
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * return 0 if value is within count ranges constraint.
2N/A * return -1 otherwise
2N/A */
2N/Astatic int
2N/A_check_int_ranges(scf_int_ranges_t *ir, int64_t v)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < ir->sir_num_ranges; ++i) {
2N/A if (v >= ir->sir_min[i] &&
2N/A v <= ir->sir_max[i]) {
2N/A /* value is within integer ranges constraint */
2N/A return (0);
2N/A }
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * int _value_in_constraint()
2N/A *
2N/A * Checks whether the supplied value violates any of the constraints
2N/A * specified in the supplied property template. If it does, an appropriate
2N/A * error is appended to "errs". pg and prop, if supplied, are used to
2N/A * augment the information in the error. Returns 0 on success.
2N/A *
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_value_in_constraint(scf_propertygroup_t *pg, scf_property_t *prop,
2N/A const scf_prop_tmpl_t *pt, scf_value_t *value, scf_tmpl_errors_t *errs)
2N/A{
2N/A scf_type_t type, tmpl_type;
2N/A scf_values_t vals;
2N/A scf_tmpl_error_type_t terr_type;
2N/A uint64_t v_count;
2N/A int64_t v_int;
2N/A char *vstr;
2N/A ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
2N/A ssize_t ret = 0;
2N/A char **constraints;
2N/A int n = 0;
2N/A int r;
2N/A int err_flag = 0;
2N/A scf_count_ranges_t cr;
2N/A scf_int_ranges_t ir;
2N/A
2N/A type = scf_value_type(value);
2N/A if (type == SCF_TYPE_INVALID) {
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A return (-1);
2N/A }
2N/A
2N/A /* Check if template type matches value type. */
2N/A if (scf_tmpl_prop_type(pt, &tmpl_type) == -1) {
2N/A if (scf_error() != SCF_ERROR_NOT_FOUND)
2N/A /* type is not wildcarded */
2N/A return (-1);
2N/A } else if (tmpl_type != type) {
2N/A if (errs != NULL) {
2N/A if (pg == NULL && prop == NULL) {
2N/A if (_add_tmpl_constraint_error(errs,
2N/A SCF_TERR_PROP_TYPE_MISMATCH, NULL, pt,
2N/A NULL, value) == -1)
2N/A return (-1);
2N/A }
2N/A }
2N/A return (1);
2N/A }
2N/A
2N/A /* Numeric values should be checked against any range constraints. */
2N/A switch (type) {
2N/A case SCF_TYPE_COUNT:
2N/A r = scf_value_get_count(value, &v_count);
2N/A assert(r == 0);
2N/A
2N/A if (scf_tmpl_value_count_range_constraints(pt, &cr) != 0) {
2N/A if (scf_error() == SCF_ERROR_NOT_FOUND)
2N/A break;
2N/A if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
2N/A (void) scf_set_error(
2N/A SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A } else {
2N/A if (_check_count_ranges(&cr, v_count) == 0) {
2N/A /* value is within ranges constraint */
2N/A scf_count_ranges_destroy(&cr);
2N/A return (0);
2N/A }
2N/A scf_count_ranges_destroy(&cr);
2N/A }
2N/A
2N/A /*
2N/A * If we get here, we have a possible constraint
2N/A * violation.
2N/A */
2N/A err_flag |= 0x1; /* RANGE_VIOLATION, count */
2N/A break;
2N/A case SCF_TYPE_INTEGER:
2N/A if (scf_value_get_integer(value, &v_int) != 0)
2N/A assert(0);
2N/A if (scf_tmpl_value_int_range_constraints(pt, &ir) != 0) {
2N/A if (scf_error() == SCF_ERROR_NOT_FOUND)
2N/A break;
2N/A if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
2N/A (void) scf_set_error(
2N/A SCF_ERROR_TEMPLATE_INVALID);
2N/A return (-1);
2N/A } else {
2N/A if (_check_int_ranges(&ir, v_int) == 0) {
2N/A /* value is within ranges constraint */
2N/A scf_int_ranges_destroy(&ir);
2N/A return (0);
2N/A }
2N/A scf_int_ranges_destroy(&ir);
2N/A }
2N/A /*
2N/A * If we get here, we have a possible constraint
2N/A * violation.
2N/A */
2N/A err_flag |= 0x2; /* RANGE_VIOLATION, integer */
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A vstr = malloc(sz);
2N/A if (vstr == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * If a set of names is provided, confirm value has one of
2N/A * those names.
2N/A */
2N/A if (scf_tmpl_value_name_constraints(pt, &vals) != 0) {
2N/A free(vstr);
2N/A if (scf_error() != SCF_ERROR_NOT_FOUND) {
2N/A return (-1);
2N/A }
2N/A } else {
2N/A r = scf_value_get_as_string_typed(value, type, vstr, sz);
2N/A
2N/A /*
2N/A * All errors (INVALID_ARGUMENT, NOT_SET, TYPE_MISMATCH)
2N/A * should be impossible or already caught above.
2N/A */
2N/A assert(r > 0);
2N/A
2N/A constraints = vals.values.v_astring;
2N/A for (n = 0; constraints[n] != NULL; ++n) {
2N/A if (strcmp(constraints[n], vstr) == 0) {
2N/A /* value is within constraint */
2N/A scf_values_destroy(&vals);
2N/A free(vstr);
2N/A return (0);
2N/A }
2N/A }
2N/A /* if we get here, we have a constraint violation */
2N/A err_flag |= 0x4; /* CONSTRAINT_VIOLATED */
2N/A scf_values_destroy(&vals);
2N/A free(vstr);
2N/A }
2N/A if (err_flag != 0)
2N/A ret = 1;
2N/A /* register the errors found */
2N/A if (ret == 1 && errs != NULL) {
2N/A if ((err_flag & 0x1) == 0x1) {
2N/A /*
2N/A * Help make the error more human-friendly. If
2N/A * pg and prop are provided, we know we're
2N/A * validating repository data. If they're not,
2N/A * we're validating a potentially hypothetical
2N/A * value.
2N/A */
2N/A if (pg == NULL && prop == NULL)
2N/A terr_type = SCF_TERR_VALUE_OUT_OF_RANGE;
2N/A else
2N/A terr_type = SCF_TERR_RANGE_VIOLATION;
2N/A if (_add_tmpl_count_error(errs, terr_type, pg, pt,
2N/A prop, v_count, 0, 0) == -1)
2N/A ret = -1;
2N/A }
2N/A if ((err_flag & 0x2) == 0x2) {
2N/A if (pg == NULL && prop == NULL)
2N/A terr_type = SCF_TERR_VALUE_OUT_OF_RANGE;
2N/A else
2N/A terr_type = SCF_TERR_RANGE_VIOLATION;
2N/A if (_add_tmpl_int_error(errs, terr_type, pg, pt, prop,
2N/A v_int, 0, 0) == -1)
2N/A ret = -1;
2N/A }
2N/A if ((err_flag & 0x4) == 0x4) {
2N/A if (pg == NULL && prop == NULL)
2N/A terr_type = SCF_TERR_INVALID_VALUE;
2N/A else
2N/A terr_type = SCF_TERR_VALUE_CONSTRAINT_VIOLATED;
2N/A if (_add_tmpl_constraint_error(errs, terr_type, pg,
2N/A pt, prop, value) == -1)
2N/A ret = -1;
2N/A }
2N/A }
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Aint
2N/Ascf_tmpl_value_in_constraint(const scf_prop_tmpl_t *pt, scf_value_t *value,
2N/A scf_tmpl_errors_t **errs)
2N/A{
2N/A scf_tmpl_errors_t *e = NULL;
2N/A
2N/A if (errs != NULL) {
2N/A char *fmri;
2N/A
2N/A if ((fmri = _scf_tmpl_get_fmri(pt->prt_t)) == NULL)
2N/A return (-1);
2N/A *errs = _scf_create_errors(fmri, 1);
2N/A free(fmri);
2N/A if (*errs == NULL)
2N/A return (-1);
2N/A e = *errs;
2N/A }
2N/A
2N/A return (_value_in_constraint(NULL, NULL, pt, value, e));
2N/A}
2N/A
2N/Ascf_tmpl_error_t *
2N/Ascf_tmpl_next_error(scf_tmpl_errors_t *errs)
2N/A{
2N/A if (errs->tes_index < errs->tes_num_errs) {
2N/A assert(errs->tes_errs[errs->tes_index] != NULL);
2N/A return (errs->tes_errs[errs->tes_index++]);
2N/A } else {
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Ascf_tmpl_reset_errors(scf_tmpl_errors_t *errs)
2N/A{
2N/A errs->tes_index = 0;
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_strerror(scf_tmpl_error_t *err, char *s, size_t n, int flag)
2N/A{
2N/A const char *str;
2N/A int i;
2N/A int ret = -1;
2N/A int nsz = 0; /* err msg length */
2N/A int sz = n; /* available buffer size */
2N/A char *buf = s; /* where to append in buffer */
2N/A char *s0 = (flag == SCF_TMPL_STRERROR_HUMAN) ? ":\n\t" : ": ";
2N/A char *s1 = (flag == SCF_TMPL_STRERROR_HUMAN) ? "\n\t" : "; ";
2N/A char *sep = s0;
2N/A const char *val;
2N/A
2N/A /* prefix */
2N/A if (err->te_errs->tes_prefix != NULL) {
2N/A ret = snprintf(buf, sz, "%s", dgettext(TEXT_DOMAIN,
2N/A err->te_errs->tes_prefix));
2N/A nsz += ret;
2N/A sz = (sz - ret) > 0 ? sz - ret : 0;
2N/A buf = (sz > 0) ? s + nsz : NULL;
2N/A }
2N/A /* error message */
2N/A ret = snprintf(buf, sz, "%s", dgettext(TEXT_DOMAIN,
2N/A em_desc[err->te_type].em_msg));
2N/A nsz += ret;
2N/A sz = (sz - ret) > 0 ? sz - ret : 0;
2N/A buf = (sz > 0) ? s + nsz : NULL;
2N/A
2N/A for (i = 0; _tmpl_error_items[i].get_desc != NULL; ++i) {
2N/A if ((str = _tmpl_error_items[i].get_desc(err)) == NULL)
2N/A /* no item to print */
2N/A continue;
2N/A val = _tmpl_error_items[i].get_val(err);
2N/A ret = snprintf(buf, sz, "%s%s=\"%s\"", sep, str,
2N/A (val == NULL) ? "" : val);
2N/A nsz += ret;
2N/A sz = (sz - ret) > 0 ? sz - ret : 0;
2N/A buf = (sz > 0) ? s + nsz : NULL;
2N/A sep = s1;
2N/A }
2N/A return (nsz);
2N/A}
2N/A
2N/A/*
2N/A * return 0 on success, -1 on failure.
2N/A * set scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_validate_cardinality(scf_propertygroup_t *pg, scf_prop_tmpl_t *pt,
2N/A scf_property_t *prop, scf_tmpl_errors_t *errs)
2N/A{
2N/A uint64_t min, max;
2N/A scf_handle_t *h;
2N/A scf_iter_t *iter = NULL;
2N/A scf_value_t *val = NULL;
2N/A int count = 0;
2N/A int ret = -1;
2N/A int r;
2N/A
2N/A if (scf_tmpl_prop_cardinality(pt, &min, &max) != 0) {
2N/A if (scf_error() == SCF_ERROR_NOT_FOUND)
2N/A return (0);
2N/A else
2N/A return (-1);
2N/A }
2N/A
2N/A /* Any number of values permitted. Just return success. */
2N/A if (min == 0 && max == UINT64_MAX) {
2N/A return (0);
2N/A }
2N/A
2N/A h = scf_property_handle(prop);
2N/A if (h == NULL) {
2N/A assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
2N/A goto cleanup;
2N/A }
2N/A
2N/A iter = scf_iter_create(h);
2N/A val = scf_value_create(h);
2N/A if (iter == NULL || val == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A if (scf_iter_property_values(iter, prop) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A while ((r = scf_iter_next_value(iter, val)) == 1)
2N/A count++;
2N/A
2N/A if (r < 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A if (count < min || count > max)
2N/A if (_add_tmpl_count_error(errs, SCF_TERR_CARDINALITY_VIOLATION,
2N/A pg, pt, prop, (uint64_t)count, &min, &max) == -1)
2N/A goto cleanup;
2N/A
2N/A ret = 0;
2N/A
2N/Acleanup:
2N/A scf_iter_destroy(iter);
2N/A scf_value_destroy(val);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on error. Sets scf_error():
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_check_property(scf_prop_tmpl_t *pt, scf_propertygroup_t *pg,
2N/A scf_property_t *prop, scf_tmpl_errors_t *errs)
2N/A{
2N/A scf_type_t tmpl_type;
2N/A uint8_t required;
2N/A scf_handle_t *h;
2N/A scf_iter_t *iter = NULL;
2N/A scf_value_t *val = NULL;
2N/A int r;
2N/A int ret = -1;
2N/A
2N/A h = scf_pg_handle(pg);
2N/A if (h == NULL) {
2N/A assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
2N/A return (-1);
2N/A }
2N/A
2N/A iter = scf_iter_create(h);
2N/A val = scf_value_create(h);
2N/A if (iter == NULL || val == NULL) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A scf_iter_destroy(iter);
2N/A scf_value_destroy(val);
2N/A return (-1);
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A if (scf_tmpl_prop_required(pt, &required) != 0)
2N/A goto cleanup;
2N/A
2N/A /* Check type */
2N/A if (scf_tmpl_prop_type(pt, &tmpl_type) == -1) {
2N/A if (scf_error() != SCF_ERROR_NOT_FOUND) {
2N/A goto cleanup;
2N/A } else if (required) {
2N/A /* If required, type must be specified. */
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A goto cleanup;
2N/A }
2N/A } else if (scf_property_is_type(prop, tmpl_type) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A if (_add_tmpl_wrong_prop_type_error(errs, pg, pt,
2N/A prop) == -1)
2N/A goto cleanup;
2N/A break;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A /*
2N/A * tmpl_prop_type shouldn't have handed back
2N/A * an invalid property type.
2N/A */
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A
2N/A /* Cardinality */
2N/A if (_validate_cardinality(pg, pt, prop, errs) == -1)
2N/A goto cleanup;
2N/A
2N/A /* Value constraints */
2N/A /*
2N/A * Iterate through each value, and confirm it is defined as
2N/A * constrained.
2N/A */
2N/A if (scf_iter_property_values(iter, prop) != 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET &&
2N/A scf_error() != SCF_ERROR_HANDLE_MISMATCH);
2N/A goto cleanup;
2N/A }
2N/A
2N/A while ((r = scf_iter_next_value(iter, val)) == 1) {
2N/A if (_value_in_constraint(pg, prop, pt, val, errs) == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_TEMPLATE_INVALID:
2N/A goto cleanup;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (r < 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A ret = 0;
2N/A
2N/Acleanup:
2N/A scf_iter_destroy(iter);
2N/A scf_value_destroy(val);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_check_pg(scf_pg_tmpl_t *t, scf_propertygroup_t *pg, char *pg_name,
2N/A char *type, scf_tmpl_errors_t *errs)
2N/A{
2N/A scf_prop_tmpl_t *pt = NULL;
2N/A char *pg_type = NULL;
2N/A scf_iter_t *iter = NULL;
2N/A uint8_t pg_required;
2N/A scf_property_t *prop = NULL;
2N/A scf_handle_t *h;
2N/A int r;
2N/A char *prop_name = NULL;
2N/A ssize_t nsize = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A int ret = -1;
2N/A
2N/A assert(pg_name != NULL);
2N/A assert(t != NULL);
2N/A assert(pg != NULL);
2N/A assert(type != NULL);
2N/A assert(nsize != 0);
2N/A
2N/A if ((h = scf_pg_handle(pg)) == NULL) {
2N/A assert(scf_error() == SCF_ERROR_HANDLE_DESTROYED);
2N/A return (-1);
2N/A }
2N/A if ((pt = scf_tmpl_prop_create(h)) == NULL) {
2N/A assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
2N/A return (-1);
2N/A }
2N/A
2N/A if ((prop = scf_property_create(h)) == NULL) {
2N/A assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((iter = scf_iter_create(h)) == NULL) {
2N/A assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
2N/A goto cleanup;
2N/A }
2N/A if ((prop_name = malloc(nsize)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (scf_tmpl_pg_required(t, &pg_required) != 0)
2N/A goto cleanup;
2N/A
2N/A if (scf_tmpl_pg_type(t, &pg_type) == -1) {
2N/A goto cleanup;
2N/A } else if (pg_required != 0 &&
2N/A strcmp(SCF_TMPL_WILDCARD, pg_type) == 0) {
2N/A /* Type must be specified for required pgs. */
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (pg_type != NULL) {
2N/A if (strcmp(pg_type, type) != 0 &&
2N/A strcmp(pg_type, SCF_TMPL_WILDCARD) != 0) {
2N/A if (_add_tmpl_wrong_pg_type_error(errs, t, pg) == -1)
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A
2N/A /* Iterate through properties in the repository and check them. */
2N/A if (scf_iter_pg_properties(iter, pg) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A while ((r = scf_iter_next_property(iter, prop)) == 1) {
2N/A if (scf_property_get_name(prop, prop_name, nsize) == -1) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_get_by_prop(t, prop_name, pt, 0) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A /* No template. Continue. */
2N/A continue;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A if (_check_property(pt, pg, prop, errs) != 0)
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (r < 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A scf_tmpl_prop_reset(pt);
2N/A free(prop_name);
2N/A prop_name = NULL;
2N/A /*
2N/A * Confirm required properties are present.
2N/A */
2N/A while ((r = scf_tmpl_iter_props(t, pt,
2N/A SCF_PROP_TMPL_FLAG_REQUIRED)) == 0) {
2N/A scf_type_t prop_type;
2N/A
2N/A if (scf_tmpl_prop_name(pt, &prop_name) == -1)
2N/A goto cleanup;
2N/A
2N/A /* required properties cannot have type wildcarded */
2N/A if (scf_tmpl_prop_type(pt, &prop_type) == -1) {
2N/A if (scf_error() == SCF_ERROR_NOT_FOUND)
2N/A (void) scf_set_error(
2N/A SCF_ERROR_TEMPLATE_INVALID);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (scf_pg_get_property(pg, prop_name, prop) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A if (_add_tmpl_missing_prop_error(errs, t, pg,
2N/A pt) == -1)
2N/A goto cleanup;
2N/A break;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A (void) scf_set_error(
2N/A SCF_ERROR_TEMPLATE_INVALID);
2N/A goto cleanup;
2N/A
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A free(prop_name);
2N/A prop_name = NULL;
2N/A }
2N/A if (r < 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A break;
2N/A
2N/A case SCF_ERROR_TEMPLATE_INVALID:
2N/A goto cleanup;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A ret = 0;
2N/Acleanup:
2N/A scf_tmpl_prop_destroy(pt);
2N/A scf_iter_destroy(iter);
2N/A scf_property_destroy(prop);
2N/A free(prop_name);
2N/A free(pg_type);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Checks if instance fmri redefines any pgs defined in restarter or global
2N/A * Return -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Astatic int
2N/A_scf_tmpl_check_pg_redef(scf_handle_t *h, const char *fmri,
2N/A const char *snapname, scf_tmpl_errors_t *errs)
2N/A{
2N/A scf_pg_tmpl_t *t = NULL;
2N/A scf_pg_tmpl_t *r = NULL;
2N/A char *pg_name = NULL;
2N/A char *pg_name_r = NULL;
2N/A char *pg_type = NULL;
2N/A char *pg_type_r = NULL;
2N/A char *target = NULL;
2N/A int ret_val = -1;
2N/A int ret;
2N/A
2N/A t = scf_tmpl_pg_create(h);
2N/A r = scf_tmpl_pg_create(h);
2N/A if (t == NULL || r == NULL)
2N/A goto cleanup;
2N/A
2N/A while ((ret = scf_tmpl_iter_pgs(t, fmri, snapname, NULL,
2N/A SCF_PG_TMPL_FLAG_EXACT)) == 1) {
2N/A if (scf_tmpl_pg_name(t, &pg_name) == -1) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_tmpl_pg_type(t, &pg_type) == -1) {
2N/A goto cleanup;
2N/A }
2N/A /* look for a redefinition of a global/restarter pg_pattern */
2N/A while ((ret = scf_tmpl_iter_pgs(r, fmri, snapname, pg_type,
2N/A 0)) == 1) {
2N/A if (scf_tmpl_pg_name(r, &pg_name_r) == -1) {
2N/A goto cleanup;
2N/A } else if (strcmp(pg_name_r, SCF_TMPL_WILDCARD) != 0 &&
2N/A strcmp(pg_name, SCF_TMPL_WILDCARD) != 0 &&
2N/A strcmp(pg_name, pg_name_r) != 0) {
2N/A /* not a match */
2N/A free(pg_name_r);
2N/A pg_name_r = NULL;
2N/A continue;
2N/A }
2N/A if (scf_tmpl_pg_type(r, &pg_type_r) == -1) {
2N/A goto cleanup;
2N/A } else if (strcmp(pg_type_r, SCF_TMPL_WILDCARD) != 0 &&
2N/A strcmp(pg_type, SCF_TMPL_WILDCARD) != 0 &&
2N/A strcmp(pg_type, pg_type_r) != 0) {
2N/A /* not a match */
2N/A free(pg_name_r);
2N/A pg_name_r = NULL;
2N/A free(pg_type_r);
2N/A pg_type_r = NULL;
2N/A continue;
2N/A }
2N/A if (scf_tmpl_pg_target(r, &target) == -1) {
2N/A target = NULL;
2N/A goto cleanup;
2N/A }
2N/A if (strcmp(target, SCF_TM_TARGET_ALL) == 0 ||
2N/A strcmp(target, SCF_TM_TARGET_DELEGATE) == 0) {
2N/A /* found a pg_pattern redefinition */
2N/A if (_add_tmpl_pg_redefine_error(errs, t,
2N/A r) == -1)
2N/A goto cleanup;
2N/A free(pg_name_r);
2N/A pg_name_r = NULL;
2N/A free(pg_type_r);
2N/A pg_type_r = NULL;
2N/A free(target);
2N/A target = NULL;
2N/A break;
2N/A }
2N/A free(pg_name_r);
2N/A pg_name_r = NULL;
2N/A free(pg_type_r);
2N/A pg_type_r = NULL;
2N/A free(target);
2N/A target = NULL;
2N/A }
2N/A if (ret == -1)
2N/A goto cleanup;
2N/A scf_tmpl_pg_reset(r);
2N/A
2N/A free(pg_name);
2N/A free(pg_type);
2N/A pg_name = NULL;
2N/A pg_type = NULL;
2N/A }
2N/A if (ret == -1)
2N/A goto cleanup;
2N/A
2N/A ret_val = 0;
2N/A
2N/Acleanup:
2N/A scf_tmpl_pg_destroy(t);
2N/A scf_tmpl_pg_destroy(r);
2N/A free(pg_name);
2N/A free(pg_type);
2N/A free(pg_name_r);
2N/A free(pg_type_r);
2N/A free(target);
2N/A
2N/A if (ret_val == -1) {
2N/A if (!ismember(scf_error(), errors_server)) {
2N/A switch (scf_error()) {
2N/A case SCF_ERROR_TYPE_MISMATCH:
2N/A (void) scf_set_error(
2N/A SCF_ERROR_TEMPLATE_INVALID);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A case SCF_ERROR_TEMPLATE_INVALID:
2N/A break;
2N/A
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A }
2N/A return (ret_val);
2N/A}
2N/A
2N/A/*
2N/A * Returns -1 on failure, sets scf_error() to:
2N/A * SCF_ERROR_BACKEND_ACCESS
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_HANDLE_DESTROYED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A * SCF_ERROR_NOT_FOUND
2N/A * SCF_ERROR_PERMISSION_DENIED
2N/A * SCF_ERROR_TEMPLATE_INVALID
2N/A */
2N/Aint
2N/Ascf_tmpl_validate_fmri(scf_handle_t *h, const char *fmri, const char *snapshot,
2N/A scf_tmpl_errors_t **errs, int flags)
2N/A{
2N/A scf_pg_tmpl_t *t = NULL;
2N/A scf_iter_t *iter = NULL;
2N/A scf_propertygroup_t *pg = NULL;
2N/A scf_instance_t *inst = NULL;
2N/A scf_snapshot_t *snap = NULL;
2N/A char *type = NULL;
2N/A char *pg_name = NULL;
2N/A ssize_t rsize = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH) + 1;
2N/A ssize_t nsize = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A int ret = -1;
2N/A int r;
2N/A
2N/A assert(errs != NULL);
2N/A
2N/A if ((*errs = _scf_create_errors(fmri, 1)) == NULL)
2N/A return (-1);
2N/A
2N/A if ((pg = scf_pg_create(h)) == NULL ||
2N/A (iter = scf_iter_create(h)) == NULL ||
2N/A (inst = scf_instance_create(h)) == NULL ||
2N/A (t = scf_tmpl_pg_create(h)) == NULL) {
2N/A /*
2N/A * Sets SCF_ERROR_INVALID_ARGUMENT, SCF_ERROR_NO_MEMORY,
2N/A * SCF_ERROR_NO_RESOURCES, SCF_ERROR_INTERNAL or
2N/A * SCF_ERROR_HANDLE_DESTROYED.
2N/A */
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((type = malloc(rsize)) == NULL ||
2N/A (pg_name = malloc(nsize)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL, NULL,
2N/A SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A /*FALLTHROUGH*/
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_NOT_FOUND:
2N/A goto cleanup;
2N/A
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A if (flags & SCF_TMPL_VALIDATE_FLAG_CURRENT) {
2N/A snap = NULL;
2N/A } else {
2N/A (void) scf_set_error(SCF_ERROR_NONE);
2N/A if (_get_snapshot(inst, snapshot, &snap) == -1) {
2N/A goto cleanup;
2N/A } else if (scf_error() == SCF_ERROR_NOT_FOUND) {
2N/A goto cleanup;
2N/A }
2N/A }
2N/A if (_scf_tmpl_check_pg_redef(h, fmri, snapshot, *errs) != 0) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Check that property groups on this instance conform to the template.
2N/A */
2N/A if (scf_iter_instance_pgs_composed(iter, inst, snap) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A while ((r = scf_iter_next_pg(iter, pg)) == 1) {
2N/A if (scf_pg_get_name(pg, pg_name, nsize) == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A if (scf_pg_get_type(pg, type, rsize) == -1) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A if (scf_tmpl_get_by_pg_name(fmri, snapshot, pg_name, type, t,
2N/A 0) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A continue;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A goto cleanup;
2N/A
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A if (_check_pg(t, pg, pg_name, type, *errs) != 0)
2N/A goto cleanup;
2N/A }
2N/A if (r < 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else {
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A scf_tmpl_pg_reset(t);
2N/A
2N/A /*
2N/A * Confirm required property groups are present.
2N/A */
2N/A while ((r = scf_tmpl_iter_pgs(t, fmri, snapshot, NULL,
2N/A SCF_PG_TMPL_FLAG_REQUIRED)) == 1) {
2N/A free(pg_name);
2N/A free(type);
2N/A
2N/A if (scf_tmpl_pg_name(t, &pg_name) == -1)
2N/A goto cleanup;
2N/A if (scf_tmpl_pg_type(t, &type) == -1)
2N/A goto cleanup;
2N/A /*
2N/A * required property group templates should not have
2N/A * wildcarded name or type
2N/A */
2N/A if (strcmp(pg_name, SCF_TMPL_WILDCARD) == 0 ||
2N/A strcmp(type, SCF_TMPL_WILDCARD) == 0) {
2N/A (void) scf_set_error(SCF_ERROR_TEMPLATE_INVALID);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (_get_pg(NULL, inst, snap, pg_name, pg) != 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A if (_add_tmpl_missing_pg_error(*errs, t) == -1)
2N/A goto cleanup;
2N/A continue;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A case SCF_ERROR_HANDLE_MISMATCH:
2N/A case SCF_ERROR_NOT_SET:
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A }
2N/A if (r < 0) {
2N/A if (ismember(scf_error(), errors_server)) {
2N/A goto cleanup;
2N/A } else switch (scf_error()) {
2N/A case SCF_ERROR_NOT_FOUND:
2N/A break;
2N/A
2N/A case SCF_ERROR_INVALID_ARGUMENT:
2N/A goto cleanup;
2N/A
2N/A default:
2N/A assert(0);
2N/A abort();
2N/A }
2N/A }
2N/A
2N/A ret = 0;
2N/A if ((*errs)->tes_num_errs > 0)
2N/A ret = 1;
2N/Acleanup:
2N/A if (ret != 1) {
2N/A /* there are no errors to report */
2N/A scf_tmpl_errors_destroy(*errs);
2N/A *errs = NULL;
2N/A }
2N/A scf_tmpl_pg_destroy(t);
2N/A free(type);
2N/A free(pg_name);
2N/A
2N/A scf_iter_destroy(iter);
2N/A scf_pg_destroy(pg);
2N/A scf_instance_destroy(inst);
2N/A scf_snapshot_destroy(snap);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Avoid
2N/Ascf_tmpl_errors_destroy(scf_tmpl_errors_t *errs)
2N/A{
2N/A int i;
2N/A scf_tmpl_error_t *e;
2N/A
2N/A if (errs == NULL)
2N/A return;
2N/A
2N/A for (i = 0; i < errs->tes_num_errs; ++i) {
2N/A e = errs->tes_errs[i];
2N/A if (errs->tes_flag != 0) {
2N/A free((char *)e->te_pg_name);
2N/A free((char *)e->te_prop_name);
2N/A free((char *)e->te_ev1);
2N/A free((char *)e->te_ev2);
2N/A free((char *)e->te_actual);
2N/A free((char *)e->te_tmpl_fmri);
2N/A free((char *)e->te_tmpl_pg_name);
2N/A free((char *)e->te_tmpl_pg_type);
2N/A free((char *)e->te_tmpl_prop_name);
2N/A free((char *)e->te_tmpl_prop_type);
2N/A }
2N/A free(e);
2N/A }
2N/A free((char *)errs->tes_fmri);
2N/A free((char *)errs->tes_prefix);
2N/A free(errs->tes_errs);
2N/A free(errs);
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_error_source_fmri(const scf_tmpl_error_t *err, char **fmri)
2N/A{
2N/A assert(err != NULL);
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A *fmri = (char *)err->te_tmpl_fmri;
2N/A return (0);
2N/A /*NOTREACHED*/
2N/A default:
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_error_type(const scf_tmpl_error_t *err, scf_tmpl_error_type_t *type)
2N/A{
2N/A assert(err != NULL);
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A *type = err->te_type;
2N/A return (0);
2N/A /*NOTREACHED*/
2N/A default:
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_error_pg_tmpl(const scf_tmpl_error_t *err, char **name, char **type)
2N/A{
2N/A assert(err != NULL);
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A if (err->te_tmpl_pg_name != NULL &&
2N/A err->te_tmpl_pg_type != NULL) {
2N/A if (name != NULL)
2N/A *name = (char *)err->te_tmpl_pg_name;
2N/A if (type != NULL)
2N/A *type = (char *)err->te_tmpl_pg_type;
2N/A return (0);
2N/A }
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A break;
2N/A default:
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_error_pg(const scf_tmpl_error_t *err, char **name, char **type)
2N/A{
2N/A assert(err != NULL);
2N/A switch (err->te_type) {
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A if (err->te_pg_name != NULL &&
2N/A err->te_actual != NULL) {
2N/A if (name != NULL)
2N/A *name = (char *)err->te_pg_name;
2N/A if (type != NULL)
2N/A *type = (char *)err->te_actual;
2N/A return (0);
2N/A }
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A break;
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A if (err->te_pg_name != NULL &&
2N/A err->te_tmpl_pg_type != NULL) {
2N/A if (name != NULL)
2N/A *name = (char *)err->te_pg_name;
2N/A if (type != NULL)
2N/A *type = (char *)err->te_tmpl_pg_type;
2N/A return (0);
2N/A }
2N/A /*FALLTHROUGH*/
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A break;
2N/A case SCF_TERR_PG_REDEFINE:
2N/A if (err->te_ev1 != NULL && err->te_ev2 != NULL) {
2N/A if (name != NULL)
2N/A *name = (char *)err->te_ev1;
2N/A if (type != NULL)
2N/A *type = (char *)err->te_ev2;
2N/A return (0);
2N/A }
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A break;
2N/A default:
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_error_prop_tmpl(const scf_tmpl_error_t *err, char **name, char **type)
2N/A{
2N/A assert(err != NULL);
2N/A switch (err->te_type) {
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A if (err->te_tmpl_prop_name != NULL &&
2N/A err->te_tmpl_prop_type != NULL) {
2N/A if (name != NULL)
2N/A *name = (char *)err->te_tmpl_prop_name;
2N/A if (type != NULL)
2N/A *type = (char *)err->te_tmpl_prop_type;
2N/A return (0);
2N/A }
2N/A /*FALLTHROUGH*/
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A break;
2N/A default:
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_error_prop(const scf_tmpl_error_t *err, char **name, char **type)
2N/A{
2N/A assert(err != NULL);
2N/A switch (err->te_type) {
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A if (err->te_prop_name != NULL &&
2N/A err->te_tmpl_prop_type != NULL) {
2N/A if (name != NULL)
2N/A *name = (char *)err->te_prop_name;
2N/A if (type != NULL)
2N/A *type = (char *)err->te_tmpl_prop_type;
2N/A return (0);
2N/A }
2N/A /*FALLTHROUGH*/
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A break;
2N/A default:
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/Aint
2N/Ascf_tmpl_error_value(const scf_tmpl_error_t *err, char **val)
2N/A{
2N/A assert(err != NULL);
2N/A switch (err->te_type) {
2N/A case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
2N/A case SCF_TERR_RANGE_VIOLATION:
2N/A case SCF_TERR_VALUE_OUT_OF_RANGE:
2N/A case SCF_TERR_INVALID_VALUE:
2N/A if (err->te_actual != NULL) {
2N/A if (val != NULL)
2N/A *val = (char *)err->te_actual;
2N/A return (0);
2N/A }
2N/A /*FALLTHROUGH*/
2N/A case SCF_TERR_MISSING_PG:
2N/A case SCF_TERR_WRONG_PG_TYPE:
2N/A case SCF_TERR_MISSING_PROP:
2N/A case SCF_TERR_WRONG_PROP_TYPE:
2N/A case SCF_TERR_CARDINALITY_VIOLATION:
2N/A case SCF_TERR_PROP_TYPE_MISMATCH:
2N/A case SCF_TERR_PG_REDEFINE:
2N/A (void) scf_set_error(SCF_ERROR_NOT_FOUND);
2N/A break;
2N/A default:
2N/A (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/Aconst char *
2N/Ascf_tmpl_visibility_to_string(uint8_t vis)
2N/A{
2N/A if (vis == SCF_TMPL_VISIBILITY_READONLY)
2N/A return (SCF_TM_VISIBILITY_READONLY);
2N/A else if (vis == SCF_TMPL_VISIBILITY_HIDDEN)
2N/A return (SCF_TM_VISIBILITY_HIDDEN);
2N/A else if (vis == SCF_TMPL_VISIBILITY_READWRITE)
2N/A return (SCF_TM_VISIBILITY_READWRITE);
2N/A else
2N/A return ("unknown");
2N/A}
2N/A
2N/A/*
2N/A * The conflict printing code is located here so we can keep the
2N/A * number files which require processing for the message build to
2N/A * a minimum.
2N/A */
2N/A
2N/A/*
2N/A * Message format:
2N/A * Conflicting value: FMRI="svc:/system/postrun:default"; Name of
2N/A * conflicting property="foo/bar"; Value="X" from
2N/A * file="/etc/svc/profile/site/a.xml"; Value="Y" from
2N/A * file="/etc/svc/profile/site/b.xml"
2N/A *
2N/A * Can set error to:
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A */
2N/Aint
2N/Asmf_pg_print_conflicts(scf_propertygroup_t *pg, const char *fmri)
2N/A{
2N/A scf_handle_t *h = pg->rd_d.rd_handle;
2N/A scf_iter_t *iter, *dec_iter;
2N/A scf_property_t *prop;
2N/A scf_decoration_t *dec;
2N/A char bundle[MAXPATHLEN];
2N/A int namelen;
2N/A int ret = -1;
2N/A char *pg_name = NULL, *prop_name = NULL;
2N/A
2N/A iter = scf_iter_create(h);
2N/A dec_iter = scf_iter_create(h);
2N/A prop = scf_property_create(h);
2N/A dec = scf_decoration_create(h);
2N/A
2N/A if (iter == NULL || dec_iter == NULL || prop == NULL || dec == NULL)
2N/A goto pg_conflicts_cleanup;
2N/A
2N/A namelen = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2N/A assert(namelen > 0);
2N/A if (((pg_name = malloc(namelen)) == NULL) ||
2N/A ((prop_name = malloc(namelen)) == NULL)) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto pg_conflicts_cleanup;
2N/A }
2N/A
2N/A if (scf_pg_get_name(pg, pg_name, namelen) < 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A goto pg_conflicts_cleanup;
2N/A }
2N/A
2N/A if (scf_pg_in_conflict(pg)) {
2N/A (void) printf("%s: FMRI=\"%s\";",
2N/A dgettext(TEXT_DOMAIN, "Conflicting property group"), fmri);
2N/A
2N/A (void) printf(" %s=\"%s\";", dgettext(TEXT_DOMAIN,
2N/A "Name of conflicting property group"), pg_name);
2N/A
2N/A if (scf_iter_pg_decorations(dec_iter, pg,
2N/A SCF_ITER_CONFLICTS_ONLY) < 0) {
2N/A assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A (void) printf("\n");
2N/A goto pg_conflicts_cleanup;
2N/A }
2N/A
2N/A while (scf_iter_next_decoration(dec_iter, dec) > 0) {
2N/A if (scf_decoration_get_bundle(dec, bundle,
2N/A MAXPATHLEN) < 0) {
2N/A assert(scf_error() !=
2N/A SCF_ERROR_HANDLE_MISMATCH);
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A assert(scf_error() !=
2N/A SCF_ERROR_INVALID_ARGUMENT);
2N/A (void) printf("\n");
2N/A goto pg_conflicts_cleanup;
2N/A }
2N/A
2N/A (void) printf(" %s=\"%s\";",
2N/A dgettext(TEXT_DOMAIN, "from file"), bundle);
2N/A }
2N/A
2N/A (void) printf("\n");
2N/A }
2N/A
2N/A scf_iter_reset(dec_iter);
2N/A
2N/A if (scf_iter_pg_properties(iter, pg) < 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
2N/A goto pg_conflicts_cleanup;
2N/A }
2N/A
2N/A while (scf_iter_next_property(iter, prop) > 0) {
2N/A if (!scf_property_in_conflict(prop))
2N/A continue;
2N/A
2N/A if (scf_property_get_name(prop, prop_name, namelen) < 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A goto pg_conflicts_cleanup;
2N/A }
2N/A
2N/A (void) printf("%s: FMRI=\"%s\";",
2N/A dgettext(TEXT_DOMAIN, "Conflicting value"), fmri);
2N/A
2N/A (void) printf(" %s=\"%s/%s\";",
2N/A dgettext(TEXT_DOMAIN, "Name of conflicting property"),
2N/A pg_name, prop_name);
2N/A
2N/A if (scf_iter_property_decorations(dec_iter, prop,
2N/A SCF_ITER_CONFLICTS_ONLY) < 0) {
2N/A assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A (void) printf("\n");
2N/A goto pg_conflicts_cleanup;
2N/A }
2N/A
2N/A while (scf_iter_next_decoration(dec_iter, dec) > 0) {
2N/A if (scf_decoration_get_bundle(dec, bundle,
2N/A MAXPATHLEN) < 0) {
2N/A assert(scf_error() !=
2N/A SCF_ERROR_HANDLE_MISMATCH);
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A assert(scf_error() !=
2N/A SCF_ERROR_INVALID_ARGUMENT);
2N/A (void) printf("\n");
2N/A goto pg_conflicts_cleanup;
2N/A }
2N/A
2N/A (void) printf(" %s=\"%s\";",
2N/A dgettext(TEXT_DOMAIN, "from file"), bundle);
2N/A }
2N/A
2N/A (void) printf("\n");
2N/A }
2N/A
2N/A ret = 0;
2N/A
2N/Apg_conflicts_cleanup:
2N/A scf_iter_destroy(iter);
2N/A scf_iter_destroy(dec_iter);
2N/A scf_property_destroy(prop);
2N/A scf_decoration_destroy(dec);
2N/A if (pg_name != NULL)
2N/A free(pg_name);
2N/A if (prop_name != NULL)
2N/A free(prop_name);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Prints all service and instance conflicts for an instance.
2N/A * Can set error to:
2N/A * SCF_ERROR_CONNECTION_BROKEN
2N/A * SCF_ERROR_DELETED
2N/A * SCF_ERROR_INTERNAL
2N/A * SCF_ERROR_INVALID_ARGUMENT
2N/A * SCF_ERROR_NO_MEMORY
2N/A * SCF_ERROR_NO_RESOURCES
2N/A * SCF_ERROR_NOT_BOUND
2N/A */
2N/Aint
2N/Asmf_inst_print_conflicts(scf_instance_t *inst)
2N/A{
2N/A scf_handle_t *h = inst->rd_d.rd_handle;
2N/A scf_service_t *svc;
2N/A scf_iter_t *iter;
2N/A scf_propertygroup_t *pg;
2N/A size_t fmri_sz;
2N/A char *fmri_buf = NULL;
2N/A int ret = -1;
2N/A
2N/A svc = scf_service_create(h);
2N/A iter = scf_iter_create(h);
2N/A pg = scf_pg_create(h);
2N/A
2N/A if (svc == NULL || iter == NULL || pg == NULL)
2N/A goto inst_conflicts_cleanup;
2N/A
2N/A /* First look at instance's parent for conflicts. */
2N/A if (scf_instance_get_parent(inst, svc) < 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A goto inst_conflicts_cleanup;
2N/A }
2N/A
2N/A fmri_sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
2N/A assert(fmri_sz > 0);
2N/A if ((fmri_buf = malloc(fmri_sz)) == NULL) {
2N/A (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2N/A goto inst_conflicts_cleanup;
2N/A }
2N/A
2N/A if (scf_service_to_fmri(svc, fmri_buf, fmri_sz) == -1) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A goto inst_conflicts_cleanup;
2N/A }
2N/A
2N/A if (scf_iter_service_pgs(iter, svc) < 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A goto inst_conflicts_cleanup;
2N/A }
2N/A
2N/A while (scf_iter_next_pg(iter, pg) > 0) {
2N/A if (smf_pg_print_conflicts(pg, fmri_buf) == -1)
2N/A goto inst_conflicts_cleanup;
2N/A }
2N/A
2N/A /* Reset the iterator, and then look at the instance. */
2N/A scf_iter_reset(iter);
2N/A
2N/A if (scf_instance_to_fmri(inst, fmri_buf, fmri_sz) == -1) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A goto inst_conflicts_cleanup;
2N/A }
2N/A
2N/A if (scf_iter_instance_pgs(iter, inst) < 0) {
2N/A assert(scf_error() != SCF_ERROR_NOT_SET);
2N/A goto inst_conflicts_cleanup;
2N/A }
2N/A
2N/A while (scf_iter_next_pg(iter, pg) > 0) {
2N/A if (smf_pg_print_conflicts(pg, fmri_buf) == -1)
2N/A goto inst_conflicts_cleanup;
2N/A }
2N/A
2N/A ret = 0;
2N/A
2N/Ainst_conflicts_cleanup:
2N/A scf_service_destroy(svc);
2N/A scf_iter_destroy(iter);
2N/A scf_pg_destroy(pg);
2N/A if (fmri_buf != NULL)
2N/A free(fmri_buf);
2N/A return (ret);
2N/A}