root/usr/src/cmd/svc/svccfg/svccfg_tmpl.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * This file provides the code that allows svccfg(8) to validate a
 * manifest against the template specifications.  svccfg uses the
 * validation facilities for the import and validate subcommands.
 *
 * There are three entry points -- tmpl_validate_bundle(),
 * tmpl_errors_print() and tmpl_errors_destroy().  svccfg calls
 * tmpl_validate_bundle() to validate a bundle.  tmpl_validate_bundle()
 * returns a pointer to a tmpl_errors_t.  This is a pointer to information
 * about any validation errors that were found.  If an error was detected,
 * svccfg calls tmpl_errors_print() to print the error information.  Once
 * the error information is printed, svccfg calls tmpl_errors_destroy() to
 * free the memory associated with the tmpl_errors_t.
 *
 * libscf's scf_tmpl.c performs similar checks to the ones described in
 * this paragraph.  Any changes to the algorithms in this file should also
 * be infcorporated into scf_tmpl.c.  The reason that there are two bodies
 * of code is that they work on different data structures.
 * tmpl_validate_bundle() validates each instance of each service in the
 * bundle.  The following checks are performed on each instance:
 *
 *      1.  Verify template consistency.
 *          A.  No conflicting definitions of "pg_pattern" are allowed
 *              within a single instance.
 *          B.  Templates at a narrow target (e.g. instance) which define
 *              property groups already templated at a broad target
 *              (e.g. delegate or all) are strongly discouraged.
 *          C.  Developers may not define a template which specifies a
 *              single prop_pattern name with differing types on the same
 *              target entity.
 *          D.  If a pg_pattern has a required attribute with a value of
 *              true, then its name and type attributes must be
 *              specified.
 *          E.  If a prop_pattern has a required attribute with a value
 *              of true, then its type attribute must be specified.
 *          F.  If a prop_pattern has an include_values element make sure
 *              that the appropriate constraints or values element has
 *              also been declared.
 *      2.  Validate that each property group in the instance is in
 *          conformance with the template specifications.
 *          A.  Verify that the types of the PG and the pg_pattern are
 *              compatible.
 *          B.  Verify properties of the PG against the prop_patterns in
 *              the template.
 *              o Verify property's type.
 *              o Verify cardinality.
 *              o Vefiy that property values satisfy the constraints
 *                imposed by the prop_pattern.
 *          C.  Verify that required properties are present.
 *      3.  Verify that all required property groups are present in the
 *          insance.
 *
 * tmpl_validate_bundle() is called after svccfg has processed the manifest
 * file.  The manifest is represented in memory by a set of entity_t,
 * pgroup_t, property_t and value_t structures.  These structures are
 * defined in svccfg.h.
 *
 * tmpl_validate_bundle() calls tmpl_validate_service() for each service in
 * the bundle, and tmpl_validate_service() then calls
 * tmpl_validate_instance() for each instance in the service.
 * tmpl_validate_instance() is the function that does the real work of
 * validation against the template specification.
 *
 * Subsystems:
 * ==========
 *
 * General Templates:
 * -----------------
 * In order to perform the validation specified by 1.B above, we need to
 * load the templates specifications for the global service and the
 * instance's restarter.  This information is loaded from the repository
 * and it is held in memory using the entity_t, pgroup_t, property_t and
 * value_t hierarchy of structures.  When a service is processed,
 * load_general_templates() is called to load the information for the
 * global service and restarter that is declared at the service level.  The
 * sc_service.sc_global and sc_service.sc_restarter members of the
 * service's entity_t point to the information for the global and restarter
 * services.
 *
 * The instance portion of a manifest can declare an instance specific
 * restarter.  If this is the case, load_instance_restarter() will load the
 * information for that restarter, and it is saved in the
 * sc_instance.sc_instance_restarter member of the entity_t that represents
 * the instance.
 *
 * Composed Properties:
 * -------------------
 * We need the ability to process the composed properties of an instance.
 * That is to say if an instance defines a property, it overrides any
 * definition in the service.  Otherwise, the service's definition is
 * inherited in the instance.
 *
 * In an entity_t, the sc_instance.sc_composed member points to a
 * uu_avl tree of composed property groups (composed_pg_t) for the
 * instance.  The composed_pg_t has two members, cpg_instance_pg and
 * cpg_service_pg, that point to the instance and service property group
 * definitions respectively.  Either of these may be NULL indicating that
 * only an instance or service definition exists in the manifest.
 *
 * In the case where both the instance and the service define a property
 * group, the properties must be composed.  This is done by
 * compose_props().  The compose_pg_t holds the composed properties in a
 * uu_avl_tree at cpf_compose_props.  This is a tree of property_t
 * structures.  If a property is defined in both the instance and service
 * property group, the tree will hold the instance definition.  If the
 * property is defined at only one level, the tree will hold the property_t
 * for that level.  Thus, the tree is truly a set of composed properties of
 * the property group.
 *
 * Property Group Iteration:
 * ------------------------
 * A number of functions must iterate through an instance's property groups
 * looking for the ones that define a pg_pattern or a prop_pattern.  To be
 * specific, the iteration starts with the composed view of the instance.
 * It then proceeds through the restarter information and finally moves on
 * to the global service.  The pg_iter_t structure holds the information
 * that is needed to implement this type of iteration.  pg_iter_create()
 * creates one of these iterators, and pg_iter_destroy() frees the memory
 * associated with the pg_iter_t.  next_pattern_pg(), is used to step
 * through the iteration.
 *
 * Error Reporting:
 * ---------------
 * While performing the templates validation checks, svccfg collects
 * information for all the errors that it finds.  Because of this you will
 * see many places in the code where a loop is not exited when an error is
 * encountered.  We note that fact that an error has occurred, but continue
 * in the loop to see if there are more validation errors.  The error code
 * of the last error that is encountered is returned.  This works, because
 * the callers of tmpl_validate_bundle() only look to see whether or not
 * the return code is TVS_SUCCESS.
 *
 * The information for reporting the errors is collected in a tmpl_errors_t
 * structure, and tmpl_validate_bundle() returns the address of this
 * structure.  The caller of tmpl_validate_bundle() can then call
 * tmpl_errors_print() to display the error information to the user.
 *
 * There are two categories of errors.  Some errors are seen when
 * processing the information in the manifest.  This type of error is only
 * seen by svccfg when it is importing or validating a manifest.  The other
 * type of error consists of template validation errors.  These errors can
 * be seen when processing a manifest or when performing a templates
 * validation of the information associated with an FMRI in the the
 * repository.  tmpl_errors_add_im() is used to capture error information
 * about the first type of error, and add_scf_error() is used to capture
 * error information about the second type of error.
 *
 * The distinction is important when printing the error information.  The
 * fuctions for printing the first type of error reside in this file, since
 * these errors will only be seen by the functions in this file.  The
 * functions for printing the template validation errors reside in libscf,
 * because these errors are of a more general nature.
 *
 * Thus, tmpl_errors_t has two lists -- one for each type of error.
 * te_list is a list of im_tmpl_error_t structures that represent the first
 * type of error.  te_scf is a list of tv_errors_t structures that hold
 * information about general template validation errors.
 * tmpl_errors_print() processes both lists to print information about all
 * errors.  In tmpl_errors_print() im_tmpl_error_print() is used to print
 * the errors that are specific to this file.  scf_tmpl_strerror() provides
 * the errors messages for general templates errors.
 *
 * As was mentioned in the previous paragraph, im_tmpl_error_print() is
 * responsible for printing the errors that are specific to this file.
 * Based on the error code, it dispatches to one of
 * im_perror_bad_conversion(), im_perror_bad_template(),
 * im_perror_invalid_type(), im_perror_missing_pg_type() or
 * im_perror_missing_type().  The rest of the im_perror_* functions provide
 * services to these error specific functions by printing common
 * information.
 *
 * im_perror_item() is the heart of this message printing subsystem.  It is
 * called directly or indirectly by all of the other im_perror_* functions.
 * im_perror_item() prints a single item of error information.  If svccfg
 * is running in interactive mode, im_perror_item() prints each item on a
 * single line, so that they are readable by a human.  In non-interactive
 * mode, all items are printed on a single line separated by semi-colons.
 */

#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <libintl.h>
#include <limits.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include "svccfg.h"

/*
 * Clear error_info_t structure.
 */
#define CLEAR_ERROR_INFO(ei)    ((void) memset((ei), 0, sizeof (error_info_t)))

/*
 * Retrieve the property group pointer from the composed_pg structure.
 */
#define CPG2PG(cpg)     (cpg->cpg_instance_pg ? cpg->cpg_instance_pg : \
                            cpg->cpg_service_pg)

/*
 * Convert a pointer to an empty string into a NULL pointer.
 */
#define EMPTY_TO_NULL(p) (((p) && (*(p) == 0)) ? NULL : (p))

/* uu_avl and uu_list debugging bits. */
#ifdef  NDEBUG
#define TMPL_DEBUG_AVL_POOL     UU_DEFAULT
#define TMPL_DEBUG_LIST         UU_DEFAULT
#define TMPL_DEBUG_LIST_POOL    UU_DEFAULT
#define TMPL_DEBUG_TREE         UU_DEFAULT
#else
#define TMPL_DEBUG_AVL_POOL     UU_AVL_POOL_DEBUG
#define TMPL_DEBUG_LIST         UU_LIST_DEBUG
#define TMPL_DEBUG_LIST_POOL    UU_LIST_POOL_DEBUG
#define TMPL_DEBUG_TREE         UU_AVL_DEBUG
#endif  /* NDEBUG */

/*
 * Structures and enums that are used in producing error messages:
 *
 * error_info_t is used to pass information about an error to
 * tmpl_errors_add_im() and add_scf_error().  tmpl_errors_add_im() collects
 * the error information and stores it in an im_tmpl_error_t.  The
 * im_tmpl_error_t is linked into the tmpl_errors_t, so that the captured
 * information can be used later when error messages are printed.
 *
 * tv_errors_t is used to keep track of error information for general
 * template errors that are known by libscf.  add_scf_error() captures the
 * error information for use in this structure.
 */
/*
 * enum to designate the type of data that is held in a err_info structure.
 */
typedef enum err_info_type {
        EIT_NONE,               /* No values in the structure */
        EIT_BAD_TEMPLATE,       /* Reason that template is bad */
        EIT_CARDINALITY,        /* Ranges for property cardinality */
        EIT_INCLUDE_VALUES,     /* include_values type attribute */
        EIT_MISSING_PG,         /* Name of missing pg */
        EIT_MISSING_PROP,       /* Name of missing property */
        EIT_PATTERN_CONFLICT,   /* Conflicting pattern definition */
        EIT_PROP_TYPE,          /* Value with invalid type */
        EIT_RANGE               /* Value that is out of range */
} err_info_type_t;

/*
 * Structure to hold information that will be used in generating error
 * messages.
 */
typedef struct error_info {
        err_info_type_t ei_type;        /* Type of information stored here */
        union {
                /* EIT_BAD_TEMPLATE */
                struct {
                        const char      *ei_reason;
                } ei_bad_template;
                /* EIT_CARDINALITY */
                struct {
                        uint64_t        ei_min;
                        uint64_t        ei_max;
                        uint64_t        ei_count; /* Number of prop values */
                } ei_cardinality;
                /* EIT_INCLUDE_VALUES */
                struct {
                        const char      *ei_type;
                } ei_inc_values;
                /* EIT_MISSING_PG */
                struct {
                        const char      *ei_pg_name;    /* Name of missing pg */
                        const char      *ei_pg_type;    /* Type of missing pg */
                } ei_missing_pg;
                /* EIT_MISSING_PROP */
                struct {
                        const char      *ei_prop_name; /* Name of prop */
                } ei_missing_prop;
                /* EIT_PATTERN_CONFLICT */
                struct {
                        pgroup_t        *ei_pattern; /* Conficting pattern */
                } ei_pattern_conflict;
                /* EIT_PROP_TYPE */
                struct {
                        scf_type_t      ei_actual;
                        scf_type_t      ei_specified;
                } ei_prop_type;
                /* EIT_RANGE */
                struct {
                        scf_type_t      ei_rtype;
                        int64_t         ei_ivalue;
                        uint64_t        ei_uvalue;
                } ei_range;
        } ei_u;
} error_info_t;

/*
 * Structure with information about a template violation.  This structure
 * is for use with in memory representations of the manifest and template.
 * See scf_tmpl_error_t for use with repository representations.  Some of
 * the pointers may be NULL for some types of errors.
 */
typedef struct im_tmpl_error {
        tmpl_validate_status_t ite_type; /* Type of error */
        entity_t        *ite_entity;    /* Instance or service */
        pgroup_t        *ite_pg;        /* Non-conforming prop. group */
        pgroup_t        *ite_pg_pattern; /* Violated pg_pattern */
        property_t      *ite_prop;      /* Non-conforming property */
        pgroup_t        *ite_prop_pattern; /* Violated prop_pattern */
        value_t         *ite_value;     /* Non-conforming value */
        error_info_t    ite_einfo;      /* Extra error information */
        uu_list_node_t  ite_node;       /* Node to link us in a list. */
} im_tmpl_error_t;

/*
 * This structure holds the data that will be used by scf_tmpl_strerror()
 * for printing template validation errors.
 */
typedef struct tv_errors {
        scf_tmpl_errors_t *tve_errors;  /* Errors for scf_tmpl_strerror() */
        uu_list_node_t  tve_node;       /* Linkage in a list. */
} tv_errors_t;

/*
 * Structure to collect template validation errors.
 */
struct tmpl_errors {
        uu_list_t       *te_list;       /* List of im_tmpl_error_t */
        im_tmpl_error_t *te_next;       /* Next error to present */
        uu_list_t       *te_scf;        /* Errors for scf_tmpl_strerror() */
        tv_errors_t     *te_cur_scf;    /* Current member of te_scf */
};

/* End of structures used in error processing. */

/*
 * Property group types that are of interest to us.  See pgroup_type().
 */
typedef enum pg_type {
        NORMAL_PG,
        PG_PATTERN_PG,
        PROP_PATTERN_PG
} pg_type_t;

/*
 * Structure to keep track of a set of ASTRING property values for a
 * property.  The consumer may wish to have the ASTRING property values
 * converted to a numeric form which is the reason for the av_v union.
 * This structure is returned by av_get_values() and is accessed by
 * av_get_integer(), av_get_string() and av_get_unsigned().
 */
typedef struct avalues {
        uint_t          av_count;       /* Number of values */
        scf_type_t      av_type;        /* Type of value representation */
        union {
                uint64_t        *av_unsigned;   /* Count & boolean values */
                int64_t         *av_integer;    /* Integer values */
                const char      **av_string;    /* String values */
        } av_v;                         /* Container for the values */
} avalues_t;

/*
 * composed_pg_t contains the information that is needed to compose a
 * property group.  See the section on Composed Properties in the block
 * comment at the beginning of this file.  The composed_pg structures are
 * linked into a uu_avl tree.  The tree is at sc_instance.sc_composed in
 * the entity_t.
 */
struct composed_pg {
        /*
         * Property group is uniquely identified by its name and type.
         * These two elements point to the name and type in a pgroup_t
         * (either service or instance), so they do not need to be
         * allocated or freed.
         */
        const char      *cpg_name;
        const char      *cpg_type;

        /* References to the actual property group definitions. */
        pgroup_t        *cpg_instance_pg;
        pgroup_t        *cpg_service_pg;

        /* Composed properties of the property group. */
        uu_avl_t        *cpg_composed_props;

        uu_avl_node_t   cpg_node;       /* Linkage for AVL tree */
};

/*
 * Prefixes for standard property names.  Used in
 * include_values_support().
 */
typedef struct prop_prefix {
        const char      *pp_prefix;
        size_t          pp_size;
} prop_prefix_t;

/*
 * Store a legal range for a property allowing for either signed or
 * unsigned ranges.  It is used to store a range from a template
 * constraint element of a prop_pattern.  The structure is returned by
 * get_ranges() and is used by value_in_range() to validate the values of a
 * property.
 */
typedef struct range {
        union {
                struct {
                        uint64_t        rng_min;
                        uint64_t        rng_max;
                } rng_unsigned;
                struct {
                        int64_t         rng_min;
                        int64_t         rng_max;
                } rng_signed;
        } rng_u;
} range_t;

/*
 * This enum defines the levels where templates can be defined.  See the
 * pg_iter structure below.
 */
typedef enum tmpl_level {
        TL_NOLEVEL = 0,         /* No level yet specified. */
        TL_INSTANCE,            /* Instance templates. */
        TL_COMPOSED,            /* Composed instance. */
        TL_SERVICE,             /* Service wide templates. */
        TL_RESTARTER,           /* Templates from restarter manifest. */
        TL_GLOBAL               /* SMF wide templates. */
} tmpl_level_t;

/*
 * pg_iter is a structure that allows us to iterate through property groups
 * in an instance followed by the property groups of the instance's
 * service, the instance's restarter and finally the global service.  See
 * the Property Group Iteration section of the block comment at the
 * beginning of this file.
 */
typedef struct pg_iter {
        entity_t        *pgi_entity;    /* Entity being searched */
        const char      *pgi_restrict;  /* Only return PGs of this type */
        tmpl_level_t    pgi_level;      /* Current level */
        entity_t        *pgi_service;   /* Service being processed. */
        union {
                pgroup_t        *pgi_pg;
                composed_pg_t   *pgi_cpg;
        } pgi_current;                  /* Current property group. */
} pg_iter_t;

/*
 * enum to distinguish between pg_patterns and prop_patterns.  It is used
 * in the ptrn_info_t structure.  See below.
 */
typedef enum ptrn_type {
        PG_PATTERN,
        PROP_PATTERN
} ptrn_type_t;

/*
 * Structure of information about a pg_pattern or a prop_pattern.  It is
 * used for template consistency checks.  gather_pattern() is used to
 * gather information for all the pg_patterns or prop_patterns in an
 * instance.  It allocates a ptrn_info_t for each of these and adds them to
 * an avl tree that is held by tmpl_consistency().
 */
typedef struct ptrn_info {
        ptrn_type_t     pi_ptrn_type;
        pgroup_t        *pi_ptrnpg;     /* pgroup_t defining the pattern. */
        const char      *pi_name;       /* Name attribute. */
        const char      *pi_type;       /* Type attribute. */
        const char      *pi_target;     /* Target attribute - only PG_PATTERN */
        const char      *pi_pgp_name;   /* Name of the pg pattern.  Only */
                                        /* used for PROP_PATTERN. */
        pgroup_t        *pi_enc_pgp;    /* PG of the pg_pattern that holds */
                                        /* the prop_pattern defined by this */
                                        /* structure.  Only used for */
                                        /* PROP_PATTERN. */
        uu_avl_node_t   pi_link;        /* Linkage into AVL tree */
} ptrn_info_t;

static const char *emesg_nomem;

/*
 * Pool for trees of composed property groups.
 */
static uu_avl_pool_t *composed_pg_pool;

/*
 * Pool for trees of composed properties.
 */
static uu_avl_pool_t *composed_prop_pool;

/*
 * Pool for lists of errors in the internal representation.
 */
static uu_list_pool_t *inmem_errors_pool;

/*
 * Pool for trees of pg_pattern info structures (ptrn_info_t).
 */
static uu_avl_pool_t *ptrn_info_pool;

/*
 * Pool for lists of template errors in the libscf representation.
 */
static uu_list_pool_t *tv_errors_pool;

/*
 * Property name prefixes for constraints and values.
 */
static const char *constraint_prefixes[] = {
        SCF_PROPERTY_TM_CONSTRAINT_NAME,
        SCF_PROPERTY_TM_CONSTRAINT_RANGE,
        NULL
};
static const char *value_prefixes[] = {
        SCF_PROPERTY_TM_VALUE_PREFIX,
        NULL
};

/*
 * Function to compare two composed_pg structures.
 */
/* ARGSUSED2 */
static int
composed_pg_compare(const void *left, const void *right, void *unused)
{
        composed_pg_t *l = (composed_pg_t *)left;
        composed_pg_t *r = (composed_pg_t *)right;
        int rc;

        if ((rc = strcmp(l->cpg_name, r->cpg_name)) == 0) {
                rc = strcmp(l->cpg_type, r->cpg_type);
        }
        return (rc);
}

/* ARGSUSED2 */
static int
composed_prop_compare(const void *left, const void *right, void *unused)
{
        property_t *l = (property_t *)left;
        property_t *r = (property_t *)right;

        return (strcmp(l->sc_property_name, r->sc_property_name));
}

static composed_pg_t *
composed_pg_create()
{
        composed_pg_t *cpg;

        cpg = safe_malloc(sizeof (*cpg));
        uu_avl_node_init(cpg, &cpg->cpg_node, composed_pg_pool);
        return (cpg);
}

static void
composed_pg_destroy(composed_pg_t *cpg)
{
        void *marker = NULL;
        pgroup_t *pg;

        if (cpg == NULL)
                return;
        /* Tear down composed property tree if we have one. */
        if ((cpg->cpg_composed_props != NULL)) {
                while (uu_avl_teardown(cpg->cpg_composed_props, &marker) !=
                    NULL) {
                        /*
                         * Nothing to do other than getting the property
                         * out of the list.  This cleans up the property's
                         * uu_avl_node.
                         */
                }
                uu_avl_destroy(cpg->cpg_composed_props);
        }

        /* Clean up any pgroup_t references to us. */
        if ((pg = cpg->cpg_instance_pg) != NULL) {
                assert((pg->sc_pgroup_composed == NULL) ||
                    (pg->sc_pgroup_composed == cpg));
                pg->sc_pgroup_composed = NULL;
        }

        uu_avl_node_fini(cpg, &cpg->cpg_node, composed_pg_pool);
        free(cpg);
}

/*
 * Walk the property group at pg, and add its properties to the AVL tree at
 * tree.
 */
static void
grow_props_tree(pgroup_t *pg, uu_avl_t *tree)
{
        uu_avl_index_t marker;
        property_t *prop;

        for (prop = uu_list_first(pg->sc_pgroup_props);
            prop != NULL;
            prop = uu_list_next(pg->sc_pgroup_props, prop)) {
                if (uu_avl_find(tree, prop, NULL, &marker) == NULL) {
                        /*
                         * If there was no match, insert the property into
                         * the tree.  If we do get a match, there is
                         * nothing to do.  That is because we rely on our
                         * caller to process the instance properties first,
                         * and the instance properties override the service
                         * properties.
                         */
                        uu_avl_insert(tree, prop, marker);
                }
        }
}

/*
 * The composed properties are stored in a uu_avl_tree.  First we populate
 * the tree with properties from the instance level property group.  Then,
 * we'll add the properties from the service level property group.
 */
static void
compose_props(composed_pg_t *cpg)
{
        uu_avl_t *tree;

        tree = uu_avl_create(composed_prop_pool, cpg, TMPL_DEBUG_TREE);
        if (tree == NULL) {
                uu_die(gettext("composed_pool tree creation failed: %s\n"),
                    uu_strerror(uu_error()));
        }
        cpg->cpg_composed_props = tree;

        /*
         * compose_props() is only called when there is both an instance
         * and a service definition of the property group.  This implies
         * that neither cpg->cpg_instance_pg nor cpg->cpg_service_pg can be
         * NULL.
         */
        /*
         * First add instance properties to the tree.
         */
        assert(cpg->cpg_instance_pg != NULL);
        grow_props_tree(cpg->cpg_instance_pg, tree);

        /*
         * Add service properties to the tree.
         */
        assert(cpg->cpg_service_pg != NULL);
        grow_props_tree(cpg->cpg_service_pg, tree);
}

/*
 * This function is a utility for build_composed_instance().
 */
static void
build_composed_property_groups(entity_t *inst, uu_avl_t *tree)
{
        composed_pg_t *cpg;
        uu_avl_index_t marker;
        composed_pg_t *match;
        pgroup_t *pg;
        entity_t *svc;

        /* First capture the instance property groups. */
        for (pg = uu_list_first(inst->sc_pgroups);
            pg != NULL;
            pg = uu_list_next(inst->sc_pgroups, pg)) {
                cpg = composed_pg_create();
                cpg->cpg_name = pg->sc_pgroup_name;
                cpg->cpg_type = pg->sc_pgroup_type;
                cpg->cpg_instance_pg = pg;
                match = uu_avl_find(tree, cpg, NULL, &marker);
                /* Since we do the instance first, there should be no match. */
                assert(match == NULL);
                uu_avl_insert(tree, cpg, marker);
                pg->sc_pgroup_composed = cpg;
        }

        /* Now capture the service property groups. */
        svc = inst->sc_parent;
        cpg = NULL;
        for (pg = uu_list_first(svc->sc_pgroups);
            pg != NULL;
            pg = uu_list_next(svc->sc_pgroups, pg)) {
                if (cpg == NULL)
                        cpg = composed_pg_create();
                cpg->cpg_name = pg->sc_pgroup_name;
                cpg->cpg_type = pg->sc_pgroup_type;
                cpg->cpg_service_pg = pg;
                match = uu_avl_find(tree, cpg, NULL, &marker);
                if (match == NULL) {
                        uu_avl_insert(tree, cpg, marker);
                        /* Get new composed_pg_t next at top of loop. */
                        cpg = NULL;
                } else {
                        /*
                         * Already have a composed_pg from instance
                         * processing.  Just add the pointer to the service
                         * pg and compose the properties.
                         */
                        match->cpg_service_pg = pg;
                        compose_props(match);
                }
        }
        if (cpg != NULL)
                composed_pg_destroy(cpg);
}

static void
build_composed_instance(entity_t *inst)
{
        uu_avl_t *tree;

        assert(inst->sc_etype == SVCCFG_INSTANCE_OBJECT);

        if (inst->sc_u.sc_instance.sc_composed == NULL) {
                tree = uu_avl_create(composed_pg_pool, inst, TMPL_DEBUG_TREE);
                if (tree == NULL) {
                        uu_die(gettext("composed_instance tree creation "
                            "failed: %s\n"), uu_strerror(uu_error()));
                }
                inst->sc_u.sc_instance.sc_composed = tree;
        }
        build_composed_property_groups(inst,
            inst->sc_u.sc_instance.sc_composed);
}

static void
demolish_composed_instance(entity_t *inst)
{
        composed_pg_t *cpg;
        void *marker = NULL;
        uu_avl_t *tree;

        tree = inst->sc_u.sc_instance.sc_composed;
        if (tree == NULL)
                return;

        marker = NULL;
        while ((cpg = uu_avl_teardown(tree, &marker)) != NULL) {
                composed_pg_destroy(cpg);
        }
        uu_avl_destroy(tree);

        inst->sc_u.sc_instance.sc_composed = NULL;
}
/*
 * Return the number of values in prop.
 */
static size_t
count_prop_values(property_t *prop)
{
        return (uu_list_numnodes(prop->sc_property_values));
}

static int
is_numeric_type(scf_type_t type)
{
        if (type == SCF_TYPE_BOOLEAN)
                return (1);
        if (type == SCF_TYPE_COUNT)
                return (1);
        if (type == SCF_TYPE_INTEGER)
                return (1);
        return (0);
}

static pg_type_t
pgroup_type(pgroup_t *pg)
{
        if (strcmp(pg->sc_pgroup_type, SCF_GROUP_TEMPLATE_PG_PATTERN) == 0)
                return (PG_PATTERN_PG);
        if (strcmp(pg->sc_pgroup_type, SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0)
                return (PROP_PATTERN_PG);
        return (NORMAL_PG);
}

/*
 * Search the property group at pg for a property named name.  If the
 * property group has a tree of composed properties, the tree will be
 * searched for the property.  Otherwise, the property group's linked list
 * will be searched.
 */
static property_t *
property_find(pgroup_t *pg, const char *name)
{
        composed_pg_t *cpg;
        property_t look;

        cpg = pg->sc_pgroup_composed;

        if ((cpg == NULL) || (cpg->cpg_composed_props == NULL)) {
                /* This is not a composed property group. */
                return (internal_property_find(pg, name));
        }

        /*
         * This is a composed property group, so look for the property in
         * the AVL tree.
         */
        look.sc_property_name = (char *)name;
        return (uu_avl_find(cpg->cpg_composed_props, &look, NULL, NULL));
}

/*
 * Functions for manipulating the avalues structure.
 */

/*
 * Free allocated memory referenced by the avalues structure.  Then, free
 * the structure itself.
 */
static void
av_destroy(avalues_t *av)
{
        if (av == NULL)
                return;
        switch (av->av_type) {
        case SCF_TYPE_BOOLEAN:
        case SCF_TYPE_COUNT:
                uu_free(av->av_v.av_unsigned);
                break;
        case SCF_TYPE_INTEGER:
                uu_free(av->av_v.av_integer);
                break;
        default:
                /*
                 * We don't need to free the strings that are referenced by
                 * av_string.  The strings are held in propery_t structures
                 * that will be freed at a later time.
                 */
                uu_free(av->av_v.av_string);
                break;
        }
        uu_free(av);
}
/*
 * Allocate and inialize an avalues structure.  count represents the
 * number of values the structure is expected to hold.  type specifies how
 * the consumer of the property values would like to see them represented.
 * See comments for the av_get_values() more details on how type is used.
 *
 * The returned structure must be freed by calling av_destroy().
 *
 * NULL is returned if memory allocation fails.
 */
static avalues_t *
av_create(size_t count, scf_type_t type)
{
        uint_t alloc_failed = 0;
        avalues_t *av;

        av = uu_zalloc(sizeof (*av));
        if (av == NULL)
                return (NULL);
        av->av_count = count;
        av->av_type = type;
        switch (type) {
        case SCF_TYPE_BOOLEAN:
        case SCF_TYPE_COUNT:
                av->av_v.av_unsigned = uu_zalloc(count * sizeof (uint64_t));
                if (av->av_v.av_unsigned == NULL)
                        alloc_failed = 1;
                break;
        case SCF_TYPE_INTEGER:
                av->av_v.av_integer = uu_zalloc(count * sizeof (int64_t));
                if (av->av_v.av_integer == NULL)
                        alloc_failed = 1;
                break;
        default:
                av->av_v.av_string = uu_zalloc(count * sizeof (char *));
                if (av->av_v.av_string == NULL)
                        alloc_failed = 1;
        }
        if (alloc_failed) {
                av_destroy(av);
                return (NULL);
        }
        return (av);
}

/*
 * Return the ith integer value in av.
 */
static int64_t
av_get_integer(avalues_t *av, uint_t i)
{
        assert(av->av_type == SCF_TYPE_INTEGER);
        assert(i < av->av_count);
        return (*(av->av_v.av_integer + i));
}

/*
 * Return the ith string value in av.
 */
static const char *
av_get_string(avalues_t *av, uint_t i)
{
        assert(is_numeric_type(av->av_type) == 0);
        assert(i < av->av_count);
        return (*(av->av_v.av_string + i));
}

/*
 * Return the ith unsigned value in av.
 */
static uint64_t
av_get_unsigned(avalues_t *av, uint_t i)
{
        assert((av->av_type == SCF_TYPE_BOOLEAN) ||
            (av->av_type == SCF_TYPE_COUNT));
        assert(i < av->av_count);
        return (*(av->av_v.av_unsigned + i));
}

/*
 * Store the value in the ith slot of the av structure.  If av is being
 * used to store numeric values, the string at value will be converted to
 * the appropriate numeric form.
 */
static tmpl_validate_status_t
av_set_value(avalues_t *av, uint_t i, const char *value)
{
        char *endptr;
        int64_t n;
        uint64_t un;

        if (is_numeric_type(av->av_type)) {
                switch (av->av_type) {
                case SCF_TYPE_BOOLEAN:
                case SCF_TYPE_COUNT:
                        un = strtoull(value, &endptr, 0);
                        if ((endptr == value) || (*endptr != 0)) {
                                return (TVS_BAD_CONVERSION);
                        }
                        *(av->av_v.av_unsigned + i) = un;
                        break;
                case SCF_TYPE_INTEGER:
                        n = strtoll(value, &endptr, 0);
                        if ((endptr == value) || (*endptr != 0)) {
                                return (TVS_BAD_CONVERSION);
                        }
                        *(av->av_v.av_integer + i) = n;
                }
        } else {
                *(av->av_v.av_string + i) = value;
        }

        return (TVS_SUCCESS);
}

/*
 * Find the property whose name is prop_name in the property group at pg.
 * Read all the values of this property and return them in an avalues
 * structure placing the address of the structure in *values.  The caller
 * must free the structure by calling av_destroy().
 *
 * The type parameter is used to indicate the type of information that the
 * caller would like to consume.  If it is one of the numeric types, the
 * property value will be converted to the appropriate numeric type before
 * placing it in the avalues struct.  Decoding will be done before the
 * conversion if necessary.
 */
static tmpl_validate_status_t
av_get_values(pgroup_t *pg, const char *prop_name, scf_type_t type,
    avalues_t **values)
{
        avalues_t *av;
        uint_t i;
        property_t *prop;
        tmpl_validate_status_t rc;
        value_t *v;

        prop = property_find(pg, prop_name);
        if (prop == NULL) {
                return (TVS_NOMATCH);
        }
        assert(prop->sc_value_type == SCF_TYPE_ASTRING);
        av = av_create(count_prop_values(prop), type);
        if (av == NULL)
                uu_die(emesg_nomem);

        /* Collect the values. */
        for ((v = uu_list_first(prop->sc_property_values)), i = 0;
            v != NULL;
            (v = uu_list_next(prop->sc_property_values, v)), i++) {
                assert(i < av->av_count);
                assert(v->sc_type == SCF_TYPE_ASTRING);
                rc = av_set_value(av, i, v->sc_u.sc_string);
                if (rc != TVS_SUCCESS) {
                        av_destroy(av);
                        return (rc);
                }
        }
        *values = av;
        return (TVS_SUCCESS);
}

/*
 * Find the property in pg whose name is prop_name.  Return a pointer to
 * the first astring value in that property.
 *
 * NULL is returned if there is no property named prop_name or if it does
 * not have an astring value.
 */
static const char *
find_astring_value_in_pg(pgroup_t *pg, const char *prop_name)
{
        property_t *prop;
        value_t *v;

        prop = property_find(pg, prop_name);
        if (prop == NULL)
                return (NULL);
        if (prop->sc_value_type != SCF_TYPE_ASTRING)
                return (NULL);
        v = uu_list_first(prop->sc_property_values);
        if (v == NULL)
                return (NULL);
        assert(v->sc_type == SCF_TYPE_ASTRING);
        return (v->sc_u.sc_string);
}
/*
 * Find the first property value of type SCF_TYPE_COUNT in the property at
 * prop.  Return the value to count.
 */
static tmpl_validate_status_t
find_count_value(property_t *prop, uint64_t *count)
{
        value_t *value;

        assert(prop->sc_value_type == SCF_TYPE_COUNT);
        value = uu_list_first(prop->sc_property_values);
        if (value == NULL)
                return (TVS_NOMATCH);
        *count = value->sc_u.sc_count;
        return (TVS_SUCCESS);
}

/*
 * pattern is a property group representing a pg_pattern or a
 * prop_pattern.  This function returns the name specification from the
 * pg_pattern or prop_pattern.
 */
static const char *
find_name_specification(pgroup_t *pattern)
{
        return (find_astring_value_in_pg(pattern, SCF_PROPERTY_TM_NAME));
}

/*
 * pattern is a property group representing a pg_pattern or a prop_pattern.
 * This function returns the type specification from the pg_pattern or
 * prop_pattern.
 */
static const char *
find_type_specification(pgroup_t *pattern)
{
        return (find_astring_value_in_pg(pattern, SCF_PROPERTY_TM_TYPE));
}

/*
 * Find the FMRI of the restarter for the entity, e.  The restarter is the
 * value of the "restarter" property in the "general" property group.
 */
static const char *
find_restarter(entity_t *e)
{
        pgroup_t *pg;
        property_t *prop;
        value_t *v;

        pg = internal_pgroup_find(e, scf_pg_general, scf_group_framework);
        if (pg != NULL) {
                prop = property_find(pg, SCF_PROPERTY_RESTARTER);
                if ((prop != NULL) && (prop->sc_value_type == SCF_TYPE_FMRI)) {
                        v = uu_list_first(prop->sc_property_values);
                        if (v != NULL)
                                return (v->sc_u.sc_string);
                }
        }

        /*
         * Didn't find the restarter.
         */
        return (NULL);
}

/*
 * prop_pattern points to a prop_pattern.  This function finds the
 * cardinality specification in the prop_pattern and returns the minimum
 * and maximum values of the cardinality.
 *
 * Returns TVS_NOMATCH if either the cardinality minimum or maximum are
 * missing.
 */
static tmpl_validate_status_t
get_cardinality(pgroup_t *prop_pattern, uint64_t *min, uint64_t *max)
{
        property_t *prop;
        tmpl_validate_status_t rc;

        assert(strcmp(prop_pattern->sc_pgroup_type,
            SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0);

        prop = property_find(prop_pattern,
            SCF_PROPERTY_TM_CARDINALITY_MIN);
        if (prop == NULL)
                return (TVS_NOMATCH);
        rc = find_count_value(prop, min);
        if (rc != TVS_SUCCESS)
                return (rc);

        prop = property_find(prop_pattern,
            SCF_PROPERTY_TM_CARDINALITY_MAX);
        if (prop == NULL)
                return (TVS_NOMATCH);
        rc = find_count_value(prop, max);

        return (rc);
}

/*
 * Ranges are represented as ASTRING values in the property at range_prop.
 * The minimum and maximum of the range are separated by a comma.
 *
 * range_prop can contain multiple range values, so we return a pointer to
 * an allocated array of range_t in ranges.  This array must be freed by
 * the caller using free().  count receives the number of range_t
 * structures that are allocated.
 *
 * type tells us whether the range values should be treated as signed or
 * unsigned.  It must be SCF_TYPE_COUNT or SCF_TYPE_INTEGER.
 */
static tmpl_validate_status_t
get_ranges(property_t *range_prop, scf_type_t type, range_t **ranges,
    uint_t *count)
{
        char *endptr;
        char *endptr2;
        range_t *r;
        value_t *value;

        *count = uu_list_numnodes(range_prop->sc_property_values);
        assert(*count != 0);
        r = safe_malloc(*count * sizeof (*r));
        *ranges = r;
        for (value = uu_list_first(range_prop->sc_property_values);
            value != NULL;
            value = uu_list_next(range_prop->sc_property_values, value)) {
                assert(value->sc_type == SCF_TYPE_ASTRING);

                /* First get the minimum */
                errno = 0;
                if (type == SCF_TYPE_INTEGER) {
                        r->rng_u.rng_signed.rng_min =
                            strtoll(value->sc_u.sc_string, &endptr, 0);
                } else {
                        r->rng_u.rng_unsigned.rng_min =
                            strtoull(value->sc_u.sc_string, &endptr, 0);
                }
                if ((errno != 0) || (endptr == value->sc_u.sc_string))
                        goto badtemplate;
                if (*endptr != ',')
                        goto badtemplate;

                /* Now get the maximum */
                endptr++;
                if (type == SCF_TYPE_INTEGER) {
                        r->rng_u.rng_signed.rng_max =
                            strtoll(endptr, &endptr2, 0);
                } else {
                        r->rng_u.rng_unsigned.rng_max =
                            strtoull(endptr, &endptr2, 0);
                }
                if ((errno != 0) || (endptr2 == endptr) ||
                    (*endptr2 != 0))
                        goto badtemplate;
                r++;
        }

        return (TVS_SUCCESS);

badtemplate:
        free(*ranges);
        *ranges = NULL;
        return (TVS_BAD_TEMPLATE);
}

static tv_errors_t *
tv_errors_create(const char *fmri)
{
        tv_errors_t *ste;

        ste = safe_malloc(sizeof (*ste));
        uu_list_node_init(ste, &ste->tve_node, tv_errors_pool);
        ste->tve_errors = _scf_create_errors(fmri, 1);
        if (ste->tve_errors == NULL)
                uu_die(emesg_nomem);

        return (ste);
}

static void
destroy_scf_errors(tv_errors_t *ste)
{
        scf_tmpl_errors_destroy(ste->tve_errors);
        uu_list_node_fini(ste, &ste->tve_node, tv_errors_pool);
        free(ste);
}

/*
 * Given a property group and the name of a property within that property
 * group, generate the name of the property group that holds the
 * prop_pattern information for the property.  The address of the generated
 * name is returned to prop_pattern_pg_name.  The memory holding the
 * generated name must be freed using uu_free().
 */
static tmpl_validate_status_t
gen_prop_pattern_pg_name(pgroup_t *pg_pattern, const char *prop_name,
    char **prop_pattern_pg_name)
{
        ssize_t limit;
        char *name;
        size_t prefix_size;
        const char *unique;

        limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
        assert(limit > 0);

        /* Get the unique part of the pg_pattern's property group name. */
        prefix_size = strlen(SCF_PG_TM_PG_PAT_BASE);
        assert(strncmp(pg_pattern->sc_pgroup_name, SCF_PG_TM_PG_PAT_BASE,
            prefix_size) == 0);
        unique = pg_pattern->sc_pgroup_name + prefix_size;

        /* Construct the prop pattern property group name. */
        *prop_pattern_pg_name = NULL;
        name = uu_zalloc(limit);
        if (name == NULL)
                uu_die(emesg_nomem);
        if (snprintf(name, limit, "%s%s_%s", SCF_PG_TM_PROP_PATTERN_PREFIX,
            unique, prop_name) >= limit) {
                uu_free(name);
                return (TVS_BAD_TEMPLATE);
        }
        *prop_pattern_pg_name = name;
        return (TVS_SUCCESS);
}

/*
 * Error message printing functions:
 */

/*
 * Flags for use by im_perror_item.
 */
#define IPI_NOT_FIRST   0x1     /* Not first item to be displayed. */

/*
 * Print a single item of information about a validation failure.  This
 * function takes care of printing the appropriate decoration before the
 * first item and between subsequent items.
 *
 * Parameters:
 *      out             Stream to receive the output.
 *      desc            Text describing the items
 *      item            Address of the item to be displayed
 *      type            Type of the item
 *      flags           Used by im_perror_item to keep track of where it
 *                      is.  Caller should set flags to 0 before calling
 *                      this function with the first item.
 */
static void
im_perror_item(FILE *out, const char *desc, void *item, scf_type_t type,
    int *flags)
{
        const char *cp;
        const char *first_sep;
        int64_t ival;
        const char *subsequent_sep;
        uint64_t uval;

        /* Nothing to print if item is NULL. */
        if (item == NULL)
                return;

        assert(type != SCF_TYPE_INVALID);

        /* Establish separators for environment. */
        if (est->sc_cmd_flags & SC_CMD_IACTIVE) {
                /* Interactive mode - make messages readable */
                first_sep = ":\n\t";
                subsequent_sep = "\n\t";
        } else {
                /* Non-interactive - one line messages. */
                first_sep = ": ";
                subsequent_sep = "; ";
        }

        /* Print separator and description */
        if (*flags & IPI_NOT_FIRST) {
                (void) fprintf(out, subsequent_sep);
        } else {
                (void) fprintf(out, first_sep);
                *flags |= IPI_NOT_FIRST;
        }
        (void) fprintf(out, "%s=", desc);

        switch (type) {
        case SCF_TYPE_BOOLEAN:
                uval = *((uint64_t *)item);
                if (uval) {
                        (void) fprintf(out, "\"%s\"", gettext("true"));
                } else {
                        (void) fprintf(out, "\"%s\"", gettext("false"));
                }
                break;
        case SCF_TYPE_COUNT:
                uval = *((uint64_t *)item);
                (void) fprintf(out, "%" PRIu64, uval);
                break;
        case SCF_TYPE_INTEGER:
                ival = *((int64_t *)item);
                (void) fprintf(out, "%" PRIi64, ival);
                break;
        default:
                /*
                 * Treat everything else as a string, but escape any
                 * internal quotes.
                 */
                (void) fputc('\"', out);
                cp = (const char *)item;
                while (*cp != 0) {
                        if (*cp == '\"') {
                                (void) fprintf(out, "\\\"");
                        } else {
                                (void) fputc(*cp, out);
                        }
                        cp++;
                }
                (void) fputc('\"', out);
                break;
        }
}

/*
 * Print erroneous FMRI.
 */
static void
im_perror_fmri(FILE *out, im_tmpl_error_t *i, int *flags)
{
        if (i->ite_entity != NULL) {
                im_perror_item(out, "FMRI", (void *)i->ite_entity->sc_fmri,
                    SCF_TYPE_FMRI, flags);
        }
}

/*
 * Print erroneous property group name.
 */
static void
im_perror_pg_name(FILE *out, im_tmpl_error_t *i, int *flags)
{
        if (i->ite_pg != NULL) {
                im_perror_item(out, gettext("Property group"),
                    (void *)i->ite_pg->sc_pgroup_name, SCF_TYPE_ASTRING,
                    flags);
        }
}

/*
 * If srcflag is 1, print the template source of the pg_pattern or
 * prop_pattern at pattern.  Always print the name and type of the pattern.
 */
static void
im_perror_pattern_info(FILE *out, pgroup_t *pattern, int *flags, int srcflag)
{
        void *c;
        const char *name_string;
        const char *type_string;

        if (pattern == NULL)
                return;
        switch (pgroup_type(pattern)) {
        case PG_PATTERN_PG:
                name_string = gettext("pg_pattern name");
                type_string = gettext("pg_pattern type");
                break;
        case PROP_PATTERN_PG:
                name_string = gettext("prop_pattern name");
                type_string = gettext("prop_pattern type");
                break;
        default:
                assert(0);
                abort();
        }
        if (srcflag) {
                im_perror_item(out, gettext("Template source"),
                    (void *)pattern->sc_parent->sc_fmri, SCF_TYPE_FMRI, flags);
        }
        c = (void *)find_name_specification(pattern);
        im_perror_item(out, name_string,
            (c == NULL) ? "" : c, SCF_TYPE_ASTRING, flags);
        c = (void *)find_type_specification(pattern);
        im_perror_item(out, type_string,
            (c == NULL) ? "" : c, SCF_TYPE_ASTRING, flags);
}

/*
 * Print information about the template specifications that were violated,
 * so that the user can find the specification.
 */
static void
im_perror_template_info(FILE *out, im_tmpl_error_t *i, int *flags)
{
        pgroup_t *pg_pattern = i->ite_pg_pattern;
        pgroup_t *prop_pattern = i->ite_prop_pattern;
        int srcflag = 1;

        if (pg_pattern != NULL) {
                im_perror_pattern_info(out, pg_pattern, flags, srcflag);
                srcflag = 0;
        }
        if (prop_pattern != NULL) {
                im_perror_pattern_info(out, prop_pattern, flags, srcflag);
        }
}

/* Print error message for TVS_BAD_CONVERSION errors. */
static void
im_perror_bad_conversion(FILE *out, im_tmpl_error_t *i, const char *prefix)
{
        int flags = 0;

        (void) fprintf(out, gettext("%sUnable to convert property value"),
            prefix);
        im_perror_fmri(out, i, &flags);
        im_perror_pg_name(out, i, &flags);
        im_perror_item(out, gettext("Property"),
            (void *)i->ite_prop->sc_property_name, SCF_TYPE_ASTRING, &flags);
        im_perror_template_info(out, i, &flags);
        (void) fputc('\n', out);
}

/* Print error message for TVS_BAD_TEMPLATE errors. */
static void
im_perror_bad_template(FILE *out, im_tmpl_error_t *i, const char *prefix)
{
        int flags = 0;

        assert(i->ite_einfo.ei_type == EIT_BAD_TEMPLATE);
        (void) fprintf(out, gettext("%sInvalid template - %s"), prefix,
            i->ite_einfo.ei_u.ei_bad_template.ei_reason);
        im_perror_fmri(out, i, &flags);
        im_perror_template_info(out, i, &flags);
        (void) fputc('\n', out);
}

/*
 * Print error message for TVS_INVALID_TYPE_SPECIFICATION errors.  This
 * error occurs if a prop_pattern has an invalid type specification.  Thus,
 * it is an indication of an invalid template rather than a violation of a
 * template.
 */
static void
im_perror_invalid_type(FILE *out, im_tmpl_error_t *i, const char *prefix)
{
        int flags = 0;
        const char *prop_pattern_name;

        (void) fprintf(out, gettext("%sInvalid type in prop_pattern"), prefix);
        im_perror_pg_name(out, i, &flags);
        if (i->ite_prop_pattern != NULL) {
                prop_pattern_name =
                    find_name_specification(i->ite_prop_pattern);
                im_perror_item(out, gettext("prop_pattern name"),
                    (void *)prop_pattern_name, SCF_TYPE_ASTRING, &flags);
        }
        im_perror_template_info(out, i, &flags);
        (void) fputc('\n', out);
}

/*
 * Print error message for TVS_MISSING_PG_TYPE errors.  In this case the
 * template specifies a type, but the property group itself has no type.
 */
static void
im_perror_missing_pg_type(FILE *out, im_tmpl_error_t *i, const char *prefix)
{
        int flags = 0;
        const char *type_spec;

        (void) fprintf(out, gettext("%sProperty group has no type"), prefix);
        im_perror_fmri(out, i, &flags);
        im_perror_pg_name(out, i, &flags);
        if (i->ite_pg_pattern != NULL) {
                type_spec = find_type_specification(i->ite_pg_pattern);
                im_perror_item(out, gettext("Type specified in pg_pattern"),
                    (void *)type_spec, SCF_TYPE_ASTRING, &flags);
        }
        (void) fputc('\n', out);
}

/*
 * Print error message for TVS_MISSING_TYPE_SPECIFICATION errors.  A
 * property group has a "required" attribute of true, but it does not have
 * a type specification.
 */
static void
im_perror_missing_type(FILE *out, im_tmpl_error_t *i, const char *prefix)
{
        int flags = 0;
        const char *pg_pattern_name;

        (void) fprintf(out, gettext("%sPg_pattern with true required attribute "
            "is missing the type attribute"), prefix);
        im_perror_fmri(out, i, &flags);
        if (i->ite_pg_pattern != NULL) {
                pg_pattern_name = find_name_specification(i->ite_pg_pattern);
                im_perror_item(out, gettext("Pg_pattern name"),
                    (void *)pg_pattern_name, SCF_TYPE_ASTRING, &flags);
        }
        im_perror_template_info(out, i, &flags);
        (void) fputc('\n', out);
}

static void
im_tmpl_error_print(FILE *out, im_tmpl_error_t *ite, const char *prefix)
{
        switch (ite->ite_type) {
        case TVS_BAD_CONVERSION:
                im_perror_bad_conversion(out, ite, prefix);
                break;
        case TVS_BAD_TEMPLATE:
                im_perror_bad_template(out, ite, prefix);
                break;
        case TVS_INVALID_TYPE_SPECIFICATION:
                im_perror_invalid_type(out, ite, prefix);
                break;
        case TVS_MISSING_PG_TYPE:
                im_perror_missing_pg_type(out, ite, prefix);
                break;
        case TVS_MISSING_TYPE_SPECIFICATION:
                im_perror_missing_type(out, ite, prefix);
                break;
        case TVS_NOMATCH:
                /*
                 * TVS_NOMATCH should be handled where it occurs.  Thus,
                 * there are no error messages associated with it.
                 */
                assert(0);
                abort();
                break;
        case TVS_SUCCESS:
                break;
        default:
                assert(0);
                abort();
        }
}

static char *
int64_to_str(int64_t i)
{
        char *c;
        const char *fmt;
        int size;

        fmt = "%" PRIi64;
        size = snprintf(NULL, 0, fmt, i) + 1;
        c = safe_malloc(size);
        (void) snprintf(c, size, fmt, i);
        return (c);
}

static char *
uint64_to_str(uint64_t u)
{
        char *c;
        const char *fmt;
        int size;

        fmt = "%" PRIu64;
        size = snprintf(NULL, 0, fmt, u) + 1;
        c = safe_malloc(size);
        (void) snprintf(c, size, fmt, u);
        return (c);
}

/*
 * Convert the value to a string.  The returned value must be freed using
 * free(3C).
 */
static const char *
value_to_string(value_t *v)
{
        char *c;

        if (is_numeric_type(v->sc_type)) {
                switch (v->sc_type) {
                case SCF_TYPE_BOOLEAN:
                        if (v->sc_u.sc_count == 0) {
                                c = gettext("false");
                        } else {
                                c = gettext("true");
                        }
                        break;
                case SCF_TYPE_COUNT:
                        c = uint64_to_str(v->sc_u.sc_count);
                        return (c);
                case SCF_TYPE_INTEGER:
                        c = int64_to_str(v->sc_u.sc_integer);
                        return (c);
                }
        } else {
                c = v->sc_u.sc_string;
        }

        return (safe_strdup(c));
}

/*
 * Subscripts for common error data.
 */
#define ED_PG_NAME      0
#define ED_PROP_NAME    1
#define ED_TMPL_FMRI    2
#define ED_TMPL_PG_NAME 3
#define ED_TMPL_PG_TYPE 4
#define ED_TMPL_PROP_NAME       5
#define ED_TMPL_PROP_TYPE       6
#define ED_COUNT        7

/*
 * This function converts the error information specified by the function
 * parameters.  It converts it to form needed by _scf_tmpl_add_error().
 * _scf_tmpl_add_error() requires that the error information be in the form
 * of allocated strings that can be freed when it is done with them.  Thus,
 * the bulk of this function is devoted to producing those allocated
 * strings.
 *
 * Once the strings are ready, we call _scf_tmpl_add_error() to add an
 * new error structure to errs.
 */
static int
add_scf_error(tmpl_errors_t *errs, scf_tmpl_error_type_t ec,
    pgroup_t *pg_pattern, pgroup_t *pg, pgroup_t *prop_pattern,
    property_t *prop, value_t *val, error_info_t *einfo)
{
        const char *actual = NULL;
        char *c;
        pgroup_t *conflict;
        const char *ed[ED_COUNT];
        const char *ev1 = NULL;
        const char *ev2 = NULL;
        int i;
        scf_type_t prop_type;
        int rc;

        (void) memset(ed, 0, sizeof (ed));

        /* Set values that are common to most error types. */
        if (pg != NULL) {
                ed[ED_PG_NAME] = pg->sc_pgroup_name;
        }
        if (prop != NULL) {
                ed[ED_PROP_NAME] = prop->sc_property_name;
        }
        if (pg_pattern == NULL) {
                if (prop_pattern != NULL) {
                        ed[ED_TMPL_FMRI] = prop_pattern->sc_parent->sc_fmri;
                }
        } else {
                ed[ED_TMPL_FMRI] = pg_pattern->sc_parent->sc_fmri;
                ed[ED_TMPL_PG_NAME] = find_name_specification(pg_pattern);
                ed[ED_TMPL_PG_TYPE] = find_type_specification(pg_pattern);
        }
        if (prop_pattern != NULL) {
                ed[ED_TMPL_PROP_NAME] = find_name_specification(prop_pattern);
                ed[ED_TMPL_PROP_TYPE] = find_type_specification(prop_pattern);
        }

        /*
         * All of the strings that we've found must be strduped.  This is
         * so that scf_tmpl_errors_destroy() can free them.  We cannot use
         * the flag argument of _scf_create_errors() to indicate that the
         * strings should not be freed.  The flag argument is an all or
         * nothing thing.  In the code below we need to convert integers to
         * strings, and this requires memory allocation.  Since we have to
         * allocate memory for that data, we need to allocate it for every
         * thing.
         */
        for (i = 0; i < ED_COUNT; i++) {
                if (ed[i] == NULL)
                        continue;
                ed[i] = safe_strdup(ed[i]);
        }

        /* actual, ev1 and ev2 are error code specific. */
        switch (ec) {
        case SCF_TERR_CARDINALITY_VIOLATION:
                assert(einfo != NULL);
                assert(einfo->ei_type == EIT_CARDINALITY);
                ev1 = uint64_to_str(einfo->ei_u.ei_cardinality.ei_min);
                ev2 = uint64_to_str(einfo->ei_u.ei_cardinality.ei_max);
                actual = uint64_to_str(einfo->ei_u.ei_cardinality.ei_count);
                break;
        case SCF_TERR_WRONG_PG_TYPE:
                /* Specified type. */
                if (pg_pattern != NULL) {
                        ev1 = find_type_specification(pg_pattern);
                        if (ev1 != NULL) {
                                ev1 = safe_strdup(ev1);
                        }
                }
                /* Actual type. */
                if (pg != NULL) {
                        actual = pg->sc_pgroup_type;
                        if (actual != NULL) {
                                actual = safe_strdup(actual);
                        }
                }
                break;
        case SCF_TERR_WRONG_PROP_TYPE:
                assert(einfo->ei_type == EIT_PROP_TYPE);
                prop_type = einfo->ei_u.ei_prop_type.ei_specified;
                ev1 = safe_strdup(scf_type_to_string(prop_type));
                prop_type = einfo->ei_u.ei_prop_type.ei_actual;
                actual = safe_strdup(scf_type_to_string(prop_type));
                break;
        case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
                actual = value_to_string(val);
                break;
        case SCF_TERR_MISSING_PG:
                assert(einfo->ei_type == EIT_MISSING_PG);
                ev1 = safe_strdup(einfo->ei_u.ei_missing_pg.ei_pg_name);
                ev2 = safe_strdup(einfo->ei_u.ei_missing_pg.ei_pg_type);
                break;
        case SCF_TERR_MISSING_PROP:
                assert(einfo->ei_type == EIT_MISSING_PROP);
                ev1 = safe_strdup(einfo->ei_u.ei_missing_prop.ei_prop_name);
                break;
        case SCF_TERR_RANGE_VIOLATION:
                assert(einfo->ei_type == EIT_RANGE);
                if (einfo->ei_u.ei_range.ei_rtype == SCF_TYPE_COUNT) {
                        c = uint64_to_str(einfo->ei_u.ei_range.ei_uvalue);
                } else {
                        c = int64_to_str(einfo->ei_u.ei_range.ei_ivalue);
                }
                actual = c;
                break;
        case SCF_TERR_PG_PATTERN_CONFLICT:
        case SCF_TERR_PROP_PATTERN_CONFLICT:
        case SCF_TERR_GENERAL_REDEFINE:
                assert(einfo->ei_type == EIT_PATTERN_CONFLICT);
                conflict = einfo->ei_u.ei_pattern_conflict.ei_pattern;
                ev1 = safe_strdup(conflict->sc_parent->sc_fmri);
                ev2 = find_name_specification(conflict);
                if (ev2 != NULL)
                        ev2 = safe_strdup(ev2);
                actual = find_type_specification(conflict);
                if (actual != NULL)
                        actual = safe_strdup(actual);
                break;
        case SCF_TERR_INCLUDE_VALUES:
                assert(einfo->ei_type == EIT_INCLUDE_VALUES);
                ev1 = safe_strdup(einfo->ei_u.ei_inc_values.ei_type);
                break;
        case SCF_TERR_PG_PATTERN_INCOMPLETE:
        case SCF_TERR_PROP_PATTERN_INCOMPLETE:
                break;
        default:
                assert(0);
                abort();
        };

        rc = _scf_tmpl_add_error(errs->te_cur_scf->tve_errors, ec,
            ed[ED_PG_NAME], ed[ED_PROP_NAME], ev1, ev2, actual,
            ed[ED_TMPL_FMRI], ed[ED_TMPL_PG_NAME], ed[ED_TMPL_PG_TYPE],
            ed[ED_TMPL_PROP_NAME], ed[ED_TMPL_PROP_TYPE]);

        return (rc);
}

/*
 * Create and initialize a new im_tmpl_error structure and add it to the
 * list of errors in errs.  The rest of the parameters are used to
 * initialize the im_tmpl_error structure.
 */
static tmpl_validate_status_t
tmpl_errors_add_im(tmpl_errors_t *errs, tmpl_validate_status_t ec, entity_t *e,
    pgroup_t *pg_pattern, pgroup_t *pg, pgroup_t *prop_pattern,
    property_t *prop, value_t *val, error_info_t *einfo)
{
        im_tmpl_error_t *ite;
        int result;

        ite = uu_zalloc(sizeof (*ite));
        if (ite == NULL)
                uu_die(emesg_nomem);
        uu_list_node_init(ite, &ite->ite_node, inmem_errors_pool);
        ite->ite_type = ec;
        ite->ite_entity = e;
        ite->ite_pg = pg;
        ite->ite_pg_pattern = pg_pattern;
        ite->ite_prop = prop;
        ite->ite_prop_pattern = prop_pattern;
        ite->ite_value = val;
        if (einfo != NULL)
                ite->ite_einfo = *einfo;

        result = uu_list_insert_after(errs->te_list, NULL, ite);
        assert(result == 0);
        return (TVS_SUCCESS);
}

/*
 * pattern must point to a pg_pattern or a prop_pattern.  This function
 * finds the property named required and returns the property's value.  If
 * the property does not exist, false is return since it is the default.
 */
static int
is_required(pgroup_t *pattern)
{
        property_t *required;
        value_t *value;

        assert((strcmp(pattern->sc_pgroup_type,
            SCF_GROUP_TEMPLATE_PG_PATTERN) == 0) ||
            (strcmp(pattern->sc_pgroup_type,
            SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0));

        required = property_find(pattern, SCF_PROPERTY_TM_REQUIRED);

        /* Default if there is no required property is false. */
        if (required == NULL)
                return (0);

        /* Retrieve the value of the required property. */
        value = uu_list_first(required->sc_property_values);
        if (value == NULL)
                return (0);
        if (value->sc_type == SCF_TYPE_BOOLEAN)
                return (value->sc_u.sc_count == 0 ? 0 : 1);

        /* No boolean property values, so return false. */
        return (0);
}

/*
 * Load the service's restarter instance and the global instance from the
 * repository.  This will allow us to use their templates in validating the
 * service.
 *
 * There is no function to unload the general templates.  The memory that
 * is allocated by load_general_templates() will be freed automatically in
 * internal_service_free() which is called by internal_bundle_free().
 */
static void
load_general_templates(entity_t *svc)
{
        const char *restarter;
        int is_global = 0;
        int r;

        assert(svc->sc_etype == SVCCFG_SERVICE_OBJECT);

        /*
         * If e is the global service, we only need to load the restarter.
         */
        if ((strcmp(svc->sc_fmri, SCF_INSTANCE_GLOBAL) == 0) ||
            (strcmp(svc->sc_fmri, SCF_SERVICE_GLOBAL) == 0)) {
                is_global = 1;
        }

        /*
         * Load the templates for the service's restarter.
         */
        restarter = find_restarter(svc);
        if (restarter == NULL)
                restarter = SCF_SERVICE_STARTD;
        if ((r = load_instance(restarter, "restarter",
            &svc->sc_u.sc_service.sc_restarter)) != 0) {
                /*
                 * During initial manifest import, restarter may
                 * not be in the repository yet.  In this case we
                 * continue on without it.
                 */
                if (r == EINVAL)
                        warn(gettext("WARNING: restarter FMRI %s is invalid\n"),
                            restarter);

                if (r == ENOTSUP)
                        warn(gettext("WARNING: restarter FMRI %s is not valid; "
                            "instance fmri required.\n"), restarter);

                if (r == ENOMEM)
                        uu_die(emesg_nomem);

                svc->sc_u.sc_service.sc_restarter = NULL;
        }
        if (is_global == 0) {
                if ((r = load_instance(SCF_INSTANCE_GLOBAL, "global",
                    &svc->sc_u.sc_service.sc_global)) != 0) {
                        /*
                         * During initial manifest import, global may not be in
                         * the repository yet.
                         */
                        if (r == ENOMEM)
                                uu_die(emesg_nomem);
                        else
                                svc->sc_u.sc_service.sc_global = NULL;
                }
        }
}

/*
 * Load the instance specific restarter if one is declared.
 *
 * There is no corresponding unload_instance_restarter() function because
 * it is not needed.  The memory will be freed in internal_instance_free()
 * when internal_bundle_free() is called.
 */
static void
load_instance_restarter(entity_t *i)
{
        const char *restarter;
        int r;

        assert(i->sc_etype == SVCCFG_INSTANCE_OBJECT);

        restarter = find_restarter(i);
        if (restarter == NULL) {
                /* No instance specific restarter */
                return;
        }
        r = load_instance(restarter, "instance_restarter",
            &i->sc_u.sc_instance.sc_instance_restarter);
        if (r != 0) {
                /*
                 * During initial manifest import, the restarter may not be
                 * in the repository yet.  In this case we continue on
                 * without it.
                 */
                if (r == EINVAL)
                        warn(gettext("WARNING: restarter FMRI %s is invalid\n"),
                            restarter);

                if (r == ENOTSUP)
                        warn(gettext("WARNING: restarter FMRI %s is not valid; "
                            "instance fmri required.\n"), restarter);

                if (r == ENOMEM)
                        uu_die(emesg_nomem);
        }
}

/*
 * Find the next property after current in the property group at pg.  If
 * the property group contains a tree of composed properties, that tree is
 * walked.  Otherwise, we walk through the uu_list at sc_pgroup_props.
 */
static property_t *
next_property(pgroup_t *pg, property_t *current)
{
        composed_pg_t *cpg;
        property_t *prop;

        cpg = pg->sc_pgroup_composed;
        if ((cpg != NULL) && (cpg->cpg_composed_props != NULL)) {
                /* Walk through composed property list. */
                if (current) {
                        prop = uu_avl_next(cpg->cpg_composed_props, current);
                } else {
                        prop = uu_avl_first(cpg->cpg_composed_props);
                }
        } else {
                /* No composition available, so walk the list of properties */
                if (current) {
                        prop = uu_list_next(pg->sc_pgroup_props, current);
                } else {
                        prop = uu_list_first(pg->sc_pgroup_props);
                }
        }

        return (prop);
}

static ptrn_info_t *
ptrn_info_create(pgroup_t *pat)
{
        entity_t *e;
        ptrn_info_t *info;
        composed_pg_t *match;
        composed_pg_t cpg;

        info = safe_malloc(sizeof (*info));

        switch (pgroup_type(pat)) {
        case PG_PATTERN_PG:
                info->pi_ptrn_type = PG_PATTERN;
                break;
        case PROP_PATTERN_PG:
                info->pi_ptrn_type = PROP_PATTERN;
                break;
        default:
                assert(0);
                abort();
        }
        info->pi_ptrnpg = pat;
        info->pi_name = find_name_specification(pat);
        info->pi_name = EMPTY_TO_NULL(info->pi_name);
        info->pi_type = find_type_specification(pat);
        info->pi_type = EMPTY_TO_NULL(info->pi_type);
        if (info->pi_ptrn_type == PG_PATTERN) {
                info->pi_target = find_astring_value_in_pg(pat,
                    SCF_PROPERTY_TM_TARGET);
                if (info->pi_target == NULL)
                        info->pi_target = SCF_TM_TARGET_THIS;
        }
        if (info->pi_ptrn_type == PROP_PATTERN) {
                info->pi_pgp_name = find_astring_value_in_pg(pat,
                    SCF_PROPERTY_TM_PG_PATTERN);
                assert((info->pi_pgp_name != NULL) &&
                    (*(info->pi_pgp_name) != 0));

                /*
                 * Find the property group that defines the pg_pattern that
                 * holds this prop_pattern.
                 */
                e = pat->sc_parent;
                if (e->sc_etype == SVCCFG_INSTANCE_OBJECT) {
                        (void) memset(&cpg, 0, sizeof (cpg));
                        cpg.cpg_name = info->pi_pgp_name;
                        cpg.cpg_type = SCF_GROUP_TEMPLATE_PG_PATTERN;
                        match = uu_avl_find(e->sc_u.sc_instance.sc_composed,
                            &cpg, NULL, NULL);
                        assert(match != NULL);
                        info->pi_enc_pgp = CPG2PG(match);
                } else {
                        info->pi_enc_pgp = internal_pgroup_find(e,
                            info->pi_pgp_name, SCF_GROUP_TEMPLATE_PG_PATTERN);
                }
                assert(info->pi_enc_pgp != NULL);
        }
        uu_avl_node_init(info, &info->pi_link, ptrn_info_pool);
        return (info);
}

static void
ptrn_info_destroy(ptrn_info_t *info)
{
        if (info == NULL)
                return;
        uu_avl_node_fini(info, &info->pi_link, ptrn_info_pool);
        free(info);
}

/*
 * Walk through the property groups of the instance or service at e looking
 * for definitions of pg_patterns or prop_patterns as specified by type.
 * For each property group that matches type create a ptrn_info_t and add
 * it to the avl tree at tree.  If duplicates are found add an error entry
 * to errs.
 */
static tmpl_validate_status_t
gather_pattern(entity_t *e, ptrn_type_t type, uu_avl_t *tree,
    tmpl_errors_t *errs)
{
        error_info_t einfo;
        ptrn_info_t *info = NULL;
        uu_avl_index_t marker;
        ptrn_info_t *match;
        pgroup_t *pg;
        tmpl_validate_status_t rc = TVS_SUCCESS;
        const char *selector;

        switch (type) {
        case PG_PATTERN:
                selector = SCF_GROUP_TEMPLATE_PG_PATTERN;
                break;
        case PROP_PATTERN:
                selector = SCF_GROUP_TEMPLATE_PROP_PATTERN;
                break;
        default:
                assert(0);
                abort();
        }

        for (pg = uu_list_first(e->sc_pgroups);
            pg != NULL;
            pg = uu_list_next(e->sc_pgroups, pg)) {
                if (strcmp(pg->sc_pgroup_type, selector) != 0) {
                        continue;
                }
                if (info != NULL) {
                        /* Get rid of old structure. */
                        ptrn_info_destroy(info);
                }
                info = ptrn_info_create(pg);
                match = uu_avl_find(tree, info, NULL, &marker);
                if (match == NULL) {
                        /* No match.  Insert the info. */
                        uu_avl_insert(tree, info, marker);
                        info = NULL;
                        continue;
                }

                /* Got a match.  Determine if it is a conflict. */
                if ((info->pi_name == NULL) ||
                    (info->pi_type == NULL) ||
                    (match->pi_name == NULL) ||
                    (match->pi_type == NULL)) {
                        /* No conflicts if any wild cards. */
                        continue;
                }

                /*
                 * Name already matches, or we wouldn't have gotten
                 * here.  Make sure that the type also matches.
                 */
                if (strcmp(info->pi_type, match->pi_type) == 0) {
                        continue;
                }

                /*
                 * If we get to this point we have a conflict, and
                 * we need to generate the correct type of error.
                 */
                CLEAR_ERROR_INFO(&einfo);
                einfo.ei_type = EIT_PATTERN_CONFLICT;
                einfo.ei_u.ei_pattern_conflict.ei_pattern =
                    match->pi_ptrnpg;
                if (type == PG_PATTERN) {
                        rc = TVS_VALIDATION;
                        if (add_scf_error(errs, SCF_TERR_PG_PATTERN_CONFLICT,
                            info->pi_ptrnpg, NULL, NULL, NULL, NULL,
                            &einfo) != 0) {
                                /*
                                 * If we can no longer accumulate
                                 * errors, break out of the loop.
                                 */
                                break;
                        }
                } else {
                        /*
                         * Possible conflicting prop_pattern.  See if the
                         * prop_patterns are declared in the same
                         * pg_pattern.
                         */
                        if ((info->pi_pgp_name == NULL) ||
                            (match->pi_pgp_name == NULL)) {
                                continue;
                        }
                        if (strcmp(info->pi_pgp_name, match->pi_pgp_name) != 0)
                                continue;

                        /* It is a real conflict. */
                        rc = TVS_VALIDATION;
                        if (add_scf_error(errs, SCF_TERR_PROP_PATTERN_CONFLICT,
                            info->pi_enc_pgp, NULL, info->pi_ptrnpg, NULL, NULL,
                            &einfo) != 0) {
                                /*
                                 * If we can no longer accumulate
                                 * errors, break out of the loop.
                                 */
                                break;
                        }
                }
        }

        ptrn_info_destroy(info);
        return (rc);
}

/*
 * Free the pg_iter structure.
 */
static void
pg_iter_destroy(pg_iter_t *i)
{
        if (i == NULL)
                return;

        uu_free(i);
}

/*
 * Create a property group iterator for the instance at e.  This iterator
 * will walk through the composed property groups of the instance.  It will
 * then step through the property groups of the instance's restarter and
 * finally the global service.  If you wish to iterate over a specific type
 * of property group, set restriction to point the the desired type.
 * Otherwise set restriction to NULL.
 *
 * The returned interator must be freed by calling pg_iter_destroy().  NULL
 * is returned if we are unable to allocate the necessary memory.
 */
static pg_iter_t *
pg_iter_create(entity_t *e, const char *restriction)
{
        pg_iter_t *i;

        assert(e->sc_etype == SVCCFG_INSTANCE_OBJECT);

        i = uu_zalloc(sizeof (*i));
        if (i == NULL)
                return (NULL);

        i->pgi_entity = e;
        i->pgi_restrict = restriction;
        i->pgi_level = TL_COMPOSED;
        i->pgi_service = e->sc_parent;

        return (i);
}

/*
 * Return the next property group in the iteration.  NULL is returned if we
 * reach the end of the list.  The iterator will automatically proceed from
 * most specific to most general levels.
 */
static pgroup_t *
next_pattern_pg(pg_iter_t *i)
{
        composed_pg_t *cpg;
        entity_t *e;
        pgroup_t *pg;
        uu_avl_t *composed_tree;

        assert(i->pgi_level != TL_NOLEVEL);

        while (i->pgi_entity != NULL) {
                if (i->pgi_level == TL_COMPOSED) {
                        composed_tree =
                            i->pgi_entity->sc_u.sc_instance.sc_composed;
                        cpg = i->pgi_current.pgi_cpg;
                        if (cpg == NULL) {
                                cpg = uu_avl_first(composed_tree);
                        } else {
                                cpg = uu_avl_next(composed_tree, cpg);
                        }
                        if (cpg == NULL) {
                                pg = NULL;
                        } else {
                                pg = CPG2PG(cpg);
                                i->pgi_current.pgi_cpg = cpg;
                        }
                } else {
                        pg = i->pgi_current.pgi_pg;
                        if (pg == NULL) {
                                pg = uu_list_first(i->pgi_entity->sc_pgroups);
                        } else {
                                pg = uu_list_next(i->pgi_entity->sc_pgroups,
                                    pg);
                        }
                        i->pgi_current.pgi_pg = pg;
                }

                if (pg == NULL) {
                        /*
                         * End of the list.  Reset current and break out of
                         * the loop.
                         */
                        (void) memset(&i->pgi_current, 0,
                            sizeof (i->pgi_current));
                        break;
                }

                /*
                 * If this iteration is for a specific type, verify that
                 * this pg is of that type.
                 */
                if (i->pgi_restrict) {
                        if (strcmp(pg->sc_pgroup_type, i->pgi_restrict) != 0) {
                                continue;
                        }
                }

                return (pg);
        }

        /*
         * End of the list in the current level.  Move up to the next
         * level.
         */
        switch (i->pgi_level) {
        case TL_COMPOSED:
                /* Skip service if we've finished a composed instance. */
                e = i->pgi_entity;
                if (e->sc_u.sc_instance.sc_instance_restarter == NULL) {
                        /* Use service restarter */
                        i->pgi_entity =
                            i->pgi_service->sc_u.sc_service.sc_restarter;
                } else {
                        /* Use instance restarter */
                        i->pgi_entity =
                            e->sc_u.sc_instance.sc_instance_restarter;
                }
                i->pgi_level = TL_RESTARTER;
                break;
        case TL_RESTARTER:
                i->pgi_entity = i->pgi_service->sc_u.sc_service.sc_global;
                i->pgi_level = TL_GLOBAL;
                break;
        case TL_GLOBAL:
                i->pgi_level = TL_NOLEVEL;
                return (NULL);
        default:
                assert(0);
                abort();
        }

        /* Go process the next level. */
        return (next_pattern_pg(i));
}

/*
 * Compare two pattern info structures (ptrn_info_t).  If both structures
 * have names, the comparison is based on the name.  If only one has a
 * name, the structure with no name will be first.  If neither structure
 * has a name, the comparison is based on their types using similar wild
 * card logic.
 */
/* ARGSUSED2 */
static int
ptrn_info_compare(const  void *left, const void *right, void *unused)
{
        ptrn_info_t *l = (ptrn_info_t *)left;
        ptrn_info_t *r = (ptrn_info_t *)right;

        if ((l->pi_name != NULL) && (r->pi_name != NULL))
                return (strcmp(l->pi_name, r->pi_name));
        if ((l->pi_name == NULL) && (r->pi_name == NULL)) {
                /* No names, so we need to compare types. */
                if ((l->pi_type != NULL) && (r->pi_type != NULL))
                        return (strcmp(l->pi_type, r->pi_type));
                if ((l->pi_type == NULL) && (r->pi_type == NULL))
                        return (0);

                /* If we get here, exactly one of the types is NULL */
                if (l->pi_type == NULL)
                        return (-1);
                return (1);
        }

        /* If we get here, exactly one of the names is NULL */
        if (l->pi_name == NULL)
                return (-1);
        return (1);
}

/*
 * The target of a pg_pattern in combination with the level at which the
 * pg_pattern was defined determines whether or not it should be applied.
 * The following combinations should be ignored, and all others should be
 * applied.
 *
 *      Target          Level
 *      ------          -----
 *      this            TL_RESTARTER, TL_GLOBAL
 *                      "this" only applies if the pg_pattern was defined
 *                      at the instance or service level
 *      delegate        TL_INSTANCE, TL_SERVICE
 *                      Only restarters and the global service can
 *                      delegate.
 *      instance        TL_INSTANCE, TL_RESTARTER, TL_GLOBAL
 *                      Only the service level can specify the "instance"
 *                      target.
 *      all             TL_INSTANCE, TL_SERVICE, TL_RESTARTER
 *                      Only the global service can specify the "all"
 *                      target.
 *
 * Return Values:
 *      1       apply the pg_pattern
 *      0       ignore the pg_pattern
 */
static int
target_check(const char *target, tmpl_level_t level)
{
        if ((target == NULL) || (*target == 0)) {
                /* Default is this */
                target = SCF_TM_TARGET_THIS;
        }
        if (strcmp(target, SCF_TM_TARGET_THIS) == 0) {
                if ((level == TL_RESTARTER) ||
                    (level == TL_GLOBAL)) {
                        return (0);
                } else {
                        return (1);
                }
        }
        if (strcmp(target, SCF_TM_TARGET_DELEGATE) == 0) {
                if ((level == TL_INSTANCE) ||
                    (level == TL_SERVICE)) {
                        return (0);
                } else {
                        return (1);
                }
        }
        if (strcmp(target, SCF_TM_TARGET_INSTANCE) == 0) {
                /*
                 * Note that the test is inverted from the other cases.
                 * This is because there is only one instance where apply
                 * is the correct thing to do.
                 */
                if (level == TL_SERVICE) {
                        return (1);
                } else {
                        return (0);
                }
        }
        if (strcmp(target, SCF_TM_TARGET_ALL) == 0) {
                if ((level == TL_INSTANCE) ||
                    (level == TL_SERVICE) ||
                    (level == TL_RESTARTER)) {
                        return (0);
                }
        }
        return (1);
}

static int
pg_target_check(pgroup_t *pg_pattern, tmpl_level_t level)
{
        const char *target;

        target = find_astring_value_in_pg(pg_pattern, SCF_PROPERTY_TM_TARGET);
        if (level == TL_COMPOSED) {
                switch (pg_pattern->sc_parent->sc_etype) {
                case SVCCFG_INSTANCE_OBJECT:
                        level = TL_INSTANCE;
                        break;
                case SVCCFG_SERVICE_OBJECT:
                        level = TL_SERVICE;
                        break;
                default:
                        assert(0);
                        abort();
                }
        }
        return (target_check(target, level));
}

/*
 * Find the prop_pattern's type sepcification and convert it to the
 * appropriate scf_type.
 */
static tmpl_validate_status_t
prop_pattern_type(pgroup_t *pattern, scf_type_t *type)
{
        const char *type_spec;

        assert(strcmp(pattern->sc_pgroup_type,
            SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0);

        type_spec = find_type_specification(pattern);
        if ((type_spec == NULL) || (*type_spec == 0))
                return (TVS_MISSING_TYPE_SPECIFICATION);
        *type = scf_string_to_type(type_spec);
        return (TVS_SUCCESS);
}

/*
 * This function is analagous to scf_property_is_type(3SCF), but it works
 * on the in memory representation of the property.
 *
 * RETURNS:
 *      0               The property at prop does not have the specified
 *                      type.
 *      non-zero        The property at prop does have the specified type.
 */
static int
property_is_type(property_t *prop, scf_type_t type)
{
        return (scf_is_compatible_type(type, prop->sc_value_type) ==
            SCF_SUCCESS);
}

/*
 * This function generates a property group name for a template's
 * pg_pattern.  The name and type of the pg_pattern are used to construct
 * the name, but either or both may be null.  A pointer to the constructed
 * name is returned, and the referenced memory must be freed using
 * free(3c).  NULL is returned if we are unable to allocate enough memory.
 */
static char *
gen_pg_pattern_pg_name(const char *name, const char *type)
{
        char *pg_name;
        char *rv = NULL;
        ssize_t name_size;

        name_size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
        pg_name = safe_malloc(name_size);
        rv = pg_name;

        /*
         * There are four cases -- name and type are both null, name and
         * type are both non-null, only name is present or only type is
         * present.
         */
        if ((name == NULL) || (*name == 0)) {
                if ((type == NULL) || (*type == 0)) {
                        /*
                         * Name and type are both null, so the PG name
                         * contains only the prefix.
                         */
                        if (strlcpy(pg_name, SCF_PG_TM_PG_PATTERN_PREFIX,
                            name_size) >= name_size) {
                                rv = NULL;
                        }
                } else {
                        /*
                         * If we have a type and no name, the type becomes
                         * part of the pg_pattern property group name.
                         */
                        if (snprintf(pg_name, name_size, "%s%s",
                            SCF_PG_TM_PG_PATTERN_T_PREFIX, type) >=
                            name_size) {
                                rv = NULL;
                        }
                }
        } else {
                /*
                 * As long as the pg_pattern has a name, it becomes part of
                 * the name of the pg_pattern property group name.  We
                 * merely need to pick the appropriate prefix.
                 */
                const char *prefix;
                if ((type == NULL) || (*type == 0)) {
                        prefix = SCF_PG_TM_PG_PATTERN_N_PREFIX;
                } else {
                        prefix = SCF_PG_TM_PG_PATTERN_NT_PREFIX;
                }
                if (snprintf(pg_name, name_size, "%s%s", prefix, name) >=
                    name_size) {
                        rv = NULL;
                }
        }

        if (rv == NULL) {
                /* Name was too big. */
                free(pg_name);
        }
        return (rv);
}

/*
 * pinfo contains information about a prop_pattern.  An include_values
 * element with a type of type has been included in the prop_pattern
 * specification.  We need to determine if the prop_pattern also contains
 * constraints or values specifications as determined by type.  Thus, we
 * search the prop_pattern for properties whose names start with the
 * correct prefix.
 */
static tmpl_validate_status_t
include_values_support(ptrn_info_t *pinfo, const char *type,
    tmpl_errors_t *errs)
{
        error_info_t einfo;
        int i;
        const char **prefixes;
        const char *pfx;
        property_t *prop;
        pgroup_t *ptrn;

        if (strcmp(type, "constraints") == 0) {
                prefixes = constraint_prefixes;
        } else if (strcmp(type, "values") == 0) {
                prefixes = value_prefixes;
        } else {
                CLEAR_ERROR_INFO(&einfo);
                einfo.ei_type = EIT_BAD_TEMPLATE;
                einfo.ei_u.ei_bad_template.ei_reason = gettext("include_values "
                    "type must be \"constraints\" or \"values\"");
                (void) tmpl_errors_add_im(errs, TVS_BAD_TEMPLATE,
                    pinfo->pi_ptrnpg->sc_parent, pinfo->pi_enc_pgp,
                    NULL, pinfo->pi_ptrnpg, NULL, NULL, &einfo);
                return (TVS_BAD_TEMPLATE);
        }

        /*
         * Now see if the prop_pattern has a property whose name starts
         * with one of these prefixes.
         */
        ptrn = pinfo->pi_ptrnpg;
        for (prop = uu_list_first(ptrn->sc_pgroup_props);
            prop != NULL;
            prop = uu_list_next(ptrn->sc_pgroup_props, prop)) {
                for (pfx = prefixes[0], i = 0;
                    pfx != NULL;
                    ++i, pfx = prefixes[i]) {
                        if (strncmp(prop->sc_property_name, pfx,
                            strlen(pfx)) == 0) {
                                return (TVS_SUCCESS);
                        }
                }
        }

        /* No match found.  Generate error */
        CLEAR_ERROR_INFO(&einfo);
        einfo.ei_type = EIT_INCLUDE_VALUES;
        einfo.ei_u.ei_inc_values.ei_type = type;
        (void) add_scf_error(errs, SCF_TERR_INCLUDE_VALUES, pinfo->pi_enc_pgp,
            NULL, ptrn, NULL, NULL, &einfo);

        return (TVS_VALIDATION);
}

/*
 * Walk through the prop_patterns in tree, looking for any that have an
 * include_values, SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES, property.  For
 * the prop_patterns with the include values property, verify that the
 * prop_pattern has constraint or values declarations as specified by the
 * include_values property.
 */
static tmpl_validate_status_t
tmpl_include_values_check(uu_avl_t *tree, tmpl_errors_t *errs)
{
        ptrn_info_t *info;
        property_t *iv;
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc = TVS_SUCCESS;
        value_t *v;

        for (info = uu_avl_first(tree);
            info != NULL;
            info = uu_avl_next(tree, info)) {
                iv = internal_property_find(info->pi_ptrnpg,
                    SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES);
                if (iv == NULL)
                        continue;
                for (v = uu_list_first(iv->sc_property_values);
                    v != NULL;
                    v = uu_list_next(iv->sc_property_values, v)) {
                        assert(is_numeric_type(v->sc_type) == 0);
                        r = include_values_support(info, v->sc_u.sc_string,
                            errs);
                        if (r != TVS_SUCCESS)
                                rc = r;
                }
        }
        return (rc);
}

/*
 * Verify that there are no conflicting definitions of pg_pattern or
 * prop_pattern.  Two patterns are said to be in conflict if they have the
 * same name and differing types.  There is a caveat, however.  Empty
 * pattern names or types are considered to be wild cards.  There is no
 * conflict if a pattern has a wild card.
 */
static tmpl_validate_status_t
tmpl_pattern_conflict(entity_t *inst, uu_avl_t *tree, ptrn_type_t type,
    tmpl_errors_t *errs)
{
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc;

        /* First walk the instance. */
        rc = gather_pattern(inst, type, tree, errs);

        /* Now walk the service */
        r = gather_pattern(inst->sc_parent, type, tree, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        return (rc);
}

static tmpl_validate_status_t
tmpl_required_attr_present(uu_avl_t *tree, tmpl_errors_t *errs)
{
        ptrn_info_t *pinfo;
        tmpl_validate_status_t rc = TVS_SUCCESS;
        int reported_name;
        int rv;

        for (pinfo = uu_avl_first(tree);
            pinfo != NULL;
            pinfo = uu_avl_next(tree, pinfo)) {
                if (is_required(pinfo->pi_ptrnpg) == 0) {
                        /* Nothing to check if pattern is not required. */
                        continue;
                }

                /*
                 * For pg_pattern both name and type are optional unless
                 * the required attribute has a value of true.  For
                 * prop_patterns only the type is optional, but it must be
                 * provided if the required attribute has a value of true.
                 */
                reported_name = 0;
                if ((pinfo->pi_ptrn_type == PG_PATTERN) &&
                    (pinfo->pi_name == NULL)) {
                        rc = TVS_VALIDATION;
                        if (add_scf_error(errs, SCF_TERR_PG_PATTERN_INCOMPLETE,
                            pinfo->pi_ptrnpg,
                            NULL, NULL, NULL, NULL, NULL) != 0) {
                                /*
                                 * If we're unable to report errors, break
                                 * out of the loop.
                                 */
                                break;
                        }
                        /*
                         * Don't report the error twice if both name and
                         * type are missing.  One error message is
                         * adequate.
                         */
                        reported_name = 1;
                }
                if ((pinfo->pi_type == NULL) && (reported_name == 0)) {
                        rc = TVS_VALIDATION;
                        if (pinfo->pi_ptrn_type == PG_PATTERN) {
                                rv = add_scf_error(errs,
                                    SCF_TERR_PG_PATTERN_INCOMPLETE,
                                    pinfo->pi_ptrnpg,
                                    NULL, NULL, NULL, NULL, NULL);
                        } else {
                                rv = add_scf_error(errs,
                                    SCF_TERR_PROP_PATTERN_INCOMPLETE,
                                    pinfo->pi_enc_pgp, NULL, pinfo->pi_ptrnpg,
                                    NULL, NULL, NULL);
                        }
                        /* If we're unable to log errors, break out of loop. */
                        if (rv != 0)
                                break;
                }
        }
        return (rc);
}

/*
 * Look for pg_pattern definitions in general.  general is either the
 * restarter serivce for inst or it is the global service.  tree contains
 * the ptrn_info_t structures describing the pg_patterns for an instance.
 * For each general pg_pattern, see if the instance contains an overriding
 * definition in tree.  If it does generate an error entry.
 *
 * If a redefinition is found, TVS_WARN is returned.  This is because a
 * redefinition is not sufficient reason to inhibit the import operation.
 */
static tmpl_validate_status_t
tmpl_scan_general(entity_t *general, uu_avl_t *tree,
    tmpl_level_t level, tmpl_errors_t *errs)
{
        tmpl_level_t cur_level;
        error_info_t einfo;
        pgroup_t *pg;
        ptrn_info_t *ginfo = NULL;
        ptrn_info_t *match;
        tmpl_validate_status_t rc = TVS_SUCCESS;

        /*
         * General services may not be in repository yet.  It depends on
         * the order that manifests are imported.
         */
        if (general == NULL)
                return (TVS_SUCCESS);

        for (pg = uu_list_first(general->sc_pgroups);
            pg != NULL;
            pg = uu_list_next(general->sc_pgroups, pg)) {
                if (strcmp(pg->sc_pgroup_type,
                    SCF_GROUP_TEMPLATE_PG_PATTERN) != 0) {
                        /* Not a pg_pattern */
                        continue;
                }
                if (ginfo != NULL)
                        ptrn_info_destroy(ginfo);
                ginfo = ptrn_info_create(pg);
                match = uu_avl_find(tree, ginfo, NULL, NULL);
                if (match != NULL) {
                        /* See if global pg_pattern is targeted at us. */
                        if (target_check(ginfo->pi_target, level) == 0)
                                continue;

                        /*
                         * See if the match applies to us.  If we happen to
                         * be a restarter, the pg_pattern could have a
                         * target of delegate.  That wouldn't apply to this
                         * instance, it would only apply to our delegates.
                         * Cases such as this are not a redefinition.
                         */
                        if (match->pi_ptrnpg->sc_parent->sc_etype ==
                            SVCCFG_INSTANCE_OBJECT) {
                                cur_level = TL_INSTANCE;
                        } else {
                                cur_level = TL_SERVICE;
                        }
                        if (target_check(match->pi_target, cur_level) == 0)
                                continue;

                        /*
                         * Instance or service overrides a general
                         * definition.  We need to issue a warning message.
                         */
                        rc = TVS_WARN;
                        CLEAR_ERROR_INFO(&einfo);
                        einfo.ei_type = EIT_PATTERN_CONFLICT;
                        einfo.ei_u.ei_pattern_conflict.ei_pattern = pg;
                        if (add_scf_error(errs, SCF_TERR_GENERAL_REDEFINE,
                            match->pi_ptrnpg, NULL, NULL, NULL, NULL,
                            &einfo) != 0) {
                                /*
                                 * No need to continue the search if we
                                 * cannot record errors.
                                 */
                                break;
                        }
                }
        }

        if (ginfo != NULL)
                ptrn_info_destroy(ginfo);
        return (rc);
}

/*
 * tree contains the pg_pattern definitions for the instance at inst.  See
 * if these pg_patterns redefine any pg_patterns in the instance's
 * restarter or in the global service.  TVS_WARN is returned if a
 * redefinition is encountered.
 */
static tmpl_validate_status_t
tmpl_level_redefine(entity_t *inst, uu_avl_t *tree, tmpl_errors_t *errs)
{
        entity_t *restarter;
        entity_t *svc = inst->sc_parent;
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc;

        restarter = inst->sc_u.sc_instance.sc_instance_restarter;
        if (restarter == NULL) {
                /* No instance restarter.  Use the service restarter */
                restarter = svc->sc_u.sc_service.sc_restarter;
        }
        rc = tmpl_scan_general(restarter, tree, TL_RESTARTER, errs);
        r = tmpl_scan_general(svc->sc_u.sc_service.sc_global, tree,
            TL_GLOBAL, errs);
        if (r != TVS_SUCCESS)
                rc = r;
        return (rc);
}

/*
 * Perform the following consistency checks on the template specifications
 * themselves:
 *
 *      - No conflicting definitions of `pg_pattern` are allowed within a
 *        single instance.
 *
 *      - Templates at a narrow target (e.g. instance) which define
 *        property groups already templated at a broad target
 *        (e.g. restarter or all) are strongly discouraged.
 *
 *      - Developers may not define a template which specifies a single
 *        prop_pattern name with differing types on the same target
 *        entity.
 *
 *      - If a pg_pattern has a required attribute with a value of true,
 *        then its name and type attributes must be specified.
 *
 *      - If a prop_pattern has a required attribute with a value of true,
 *        then its type attribute must be specified.
 *
 *      - If a prop_pattern has an include values make sure that the
 *        appropriate constraints or values element has also been
 *        declared.
 */
static tmpl_validate_status_t
tmpl_consistency(entity_t *inst, tmpl_errors_t *errs)
{
        void *marker = NULL;
        ptrn_info_t *info;
        uu_avl_t *tree;
        tmpl_validate_status_t rc;
        tmpl_validate_status_t r;

        /* Allocate the tree. */
        tree = uu_avl_create(ptrn_info_pool, NULL, TMPL_DEBUG_TREE);
        if (tree == NULL) {
                uu_die(gettext("pg_info tree creation failed: %s\n"),
                    uu_strerror(uu_error()));
        }

        rc = tmpl_pattern_conflict(inst, tree, PG_PATTERN, errs);

        /*
         * The tree now contains the instance and service pg_patterns.
         * Check to see if they override any pg_pattern definitions in the
         * restarter and global services.
         */
        r = tmpl_level_redefine(inst, tree, errs);
        if (r != TVS_SUCCESS) {
                /*
                 * tmpl_level_redefine() can return a warning.  Don't
                 * override a serious error with a warning.
                 */
                if (r == TVS_WARN) {
                        if (rc == TVS_SUCCESS)
                                rc = r;
                } else {
                        rc = r;
                }
        }

        /*
         * If the pg_pattern has a required attribute with a value of true,
         * then it must also have name and type attributes.
         */
        r = tmpl_required_attr_present(tree, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        /* Empty the tree, so that we can reuse it for prop_patterns. */
        while ((info = uu_avl_teardown(tree, &marker)) != NULL) {
                ptrn_info_destroy(info);
        }

        r = tmpl_pattern_conflict(inst, tree, PROP_PATTERN, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        /*
         * If a prop_pattern has required attribute with a value of true,
         * then it must also have a type attribute.
         */
        r = tmpl_required_attr_present(tree, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        /*
         * Insure that include_values have the constraint for values
         * elements that are needed.
         */
        r = tmpl_include_values_check(tree, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        /* Tear down the tree. */
        marker = NULL;
        while ((info = uu_avl_teardown(tree, &marker)) != NULL) {
                ptrn_info_destroy(info);
        }
        uu_avl_destroy(tree);

        return (rc);
}

/*
 * Release memory associated with the tmpl_errors structure and then free
 * the structure itself.
 */
void
tmpl_errors_destroy(tmpl_errors_t *te)
{
        im_tmpl_error_t *ite;
        tv_errors_t *ste;
        void *marker = NULL;

        if (te == NULL)
                return;
        if (te->te_list) {
                while ((ite = uu_list_teardown(te->te_list, &marker)) != NULL) {
                        uu_list_node_fini(ite, &ite->ite_node,
                            inmem_errors_pool);
                        uu_free(ite);
                }
                uu_list_destroy(te->te_list);
        }
        if (te->te_scf) {
                marker = NULL;
                while ((ste = uu_list_teardown(te->te_scf, &marker)) != NULL) {
                        destroy_scf_errors(ste);
                }
                uu_list_destroy(te->te_scf);
        }
        uu_free(te);
}

/*
 * Allocate and initialize a tmpl_errors structure.  The address of the
 * structure is returned, unless we are unable to allocate enough memory.
 * In the case of memory allocation failures, NULL is returned.
 *
 * The allocated structure should be freed by calling
 * tmpl_errors_destroy().
 */
static tmpl_errors_t *
tmpl_errors_create()
{
        tmpl_errors_t *te;

        te = uu_zalloc(sizeof (*te));
        if (te == NULL)
                return (NULL);
        te->te_list = uu_list_create(inmem_errors_pool, NULL, TMPL_DEBUG_LIST);
        if (te->te_list == NULL) {
                uu_free(te);
                return (NULL);
        }
        te->te_scf = uu_list_create(tv_errors_pool, NULL, TMPL_DEBUG_LIST);
        if (te->te_scf == NULL) {
                tmpl_errors_destroy(te);
                return (NULL);
        }

        return (te);
}

void
tmpl_errors_print(FILE *out, tmpl_errors_t *errs, const char *prefix)
{
        scf_tmpl_error_t *cur;
        size_t buf_size = 4096;
        im_tmpl_error_t *ite;
        char *s = NULL;
        scf_tmpl_errors_t *scferrs;
        tv_errors_t *scft;
        int interactive = (est->sc_cmd_flags & SC_CMD_IACTIVE) ?
            SCF_TMPL_STRERROR_HUMAN : 0;

        for (ite = uu_list_first(errs->te_list);
            ite != NULL;
            ite = uu_list_next(errs->te_list, ite)) {
                im_tmpl_error_print(out, ite, prefix);
        }

        /* Now handle the errors that can be printed via libscf. */
        s = safe_malloc(buf_size);
        for (scft = uu_list_first(errs->te_scf);
            scft != NULL;
            scft = uu_list_next(errs->te_scf, scft)) {
                scferrs = scft->tve_errors;
                if (_scf_tmpl_error_set_prefix(scferrs, prefix) != 0)
                        uu_die(emesg_nomem);
                while ((cur = scf_tmpl_next_error(scferrs)) != NULL) {
                        (void) scf_tmpl_strerror(cur, s, buf_size, interactive);
                        (void) fputs(s, out);
                        (void) fputc('\n', out);
                }
        }

        free(s);
}

/*
 * This function finds the prop_pattern for the property, prop.  e is the
 * instance where the search for the prop_pattern will start.  pg_pattern
 * is the address of the pg_pattern that holds the prop_pattern.
 */
static tmpl_validate_status_t
tmpl_find_prop_pattern(entity_t *inst, pgroup_t *pg_pattern,
    property_t *prop, pgroup_t **prop_pattern)
{
        pgroup_t *candidate;
        pg_iter_t *iter = NULL;
        char *prop_pattern_name = NULL;
        tmpl_validate_status_t rc;

        /*
         * Get the name of the property group that holds the prop_pattern
         * definition.
         */
        rc = gen_prop_pattern_pg_name(pg_pattern,
            prop->sc_property_name, &prop_pattern_name);
        if (rc != TVS_SUCCESS)
                goto out;

        /* Find the property group. */
        iter = pg_iter_create(inst, SCF_GROUP_TEMPLATE_PROP_PATTERN);
        if (iter == NULL)
                goto out;
        while ((candidate = next_pattern_pg(iter)) != NULL) {
                const char *c;

                if (strcmp(prop_pattern_name, candidate->sc_pgroup_name) != 0)
                        continue;
                c = find_astring_value_in_pg(candidate,
                    SCF_PROPERTY_TM_PG_PATTERN);
                if (c == NULL)
                        continue;
                if (strcmp(pg_pattern->sc_pgroup_name, c) == 0)
                        break;
        }
        *prop_pattern = candidate;
        if (candidate == NULL)
                rc = TVS_NOMATCH;

out:
        pg_iter_destroy(iter);
        uu_free((void *)prop_pattern_name);
        return (rc);
}

/*
 * Indexes for pg_pattern property group names.  Indexes are arranged
 * from most specific to least specific.
 */
#define PGN_BOTH        0       /* both name and type */
#define PGN_NAME        1       /* name only */
#define PGN_TYPE        2       /* type only */
#define PGN_NEITHER     3       /* neither name nor type */
#define PGN_MAX         4       /* Size of array */

/*
 * Given an instance entity, e, and a propety group, pg, within the
 * instance; return the address of the pg_pattern for the property group.
 * The address of the pg_pattern is placed at pgp.  NULL indicates that no
 * pg_pattern was specified.
 */
static tmpl_validate_status_t
tmpl_find_pg_pattern(entity_t *e, pgroup_t *pg, pgroup_t **pgp)
{
        pgroup_t *cpg;          /* candidate property group */
        int i;
        pg_iter_t *iter = NULL;
        char *pg_names[PGN_MAX];
        pgroup_t *pg_patterns[PGN_MAX];
        tmpl_validate_status_t rv = TVS_SUCCESS;

        (void) memset(pg_patterns, 0, sizeof (pg_patterns));
        *pgp = NULL;

        /* Generate candidate names for pg_pattern property groups. */
        pg_names[PGN_BOTH] = gen_pg_pattern_pg_name(pg->sc_pgroup_name,
            pg->sc_pgroup_type);
        pg_names[PGN_NAME] = gen_pg_pattern_pg_name(pg->sc_pgroup_name,
            NULL);
        pg_names[PGN_TYPE] = gen_pg_pattern_pg_name(NULL,
            pg->sc_pgroup_type);
        pg_names[PGN_NEITHER] = gen_pg_pattern_pg_name(NULL, NULL);
        for (i = 0; i < PGN_MAX; i++) {
                if (pg_names[i] == NULL) {
                        rv = TVS_BAD_TEMPLATE;
                        goto errout;
                }
        }

        /* Search for property groups that match these names */
        iter = pg_iter_create(e, SCF_GROUP_TEMPLATE_PG_PATTERN);
        if (iter == NULL) {
                uu_die(emesg_nomem);
        }
        while ((cpg = next_pattern_pg(iter)) != NULL) {
                if (pg_target_check(cpg, iter->pgi_level) == 0)
                        continue;

                /* See if we have a name match. */
                for (i = 0; i < PGN_MAX; i++) {
                        if (strcmp(cpg->sc_pgroup_name, pg_names[i]) == 0) {
                                /*
                                 * If we already have a lower level
                                 * pg_pattern, keep it.
                                 */
                                if (pg_patterns[i] == NULL)
                                        pg_patterns[i] = cpg;
                                break;
                        }
                }
        }

        /* Find the most specific pg_pattern. */
        for (i = 0; i < PGN_MAX; i++) {
                if (pg_patterns[i] != NULL) {
                        *pgp = pg_patterns[i];
                        break;
                }
        }
errout:
        for (i = 0; i < PGN_MAX; i++) {
                free(pg_names[i]);
        }
        pg_iter_destroy(iter);
        return (rv);
}

/*
 * Initialize structures that are required for validation using
 * templates specifications.
 */
void
tmpl_init(void)
{
        emesg_nomem = gettext("Out of memory.\n");

        composed_pg_pool = uu_avl_pool_create("composed_pg",
            sizeof (composed_pg_t), offsetof(composed_pg_t, cpg_node),
            composed_pg_compare, TMPL_DEBUG_AVL_POOL);
        if (composed_pg_pool == NULL) {
                uu_die(gettext("composed_pg pool creation failed: %s\n"),
                    uu_strerror(uu_error()));
        }
        composed_prop_pool = uu_avl_pool_create("composed_prop",
            sizeof (property_t), offsetof(property_t, sc_composed_node),
            composed_prop_compare, TMPL_DEBUG_AVL_POOL);
        if (composed_prop_pool == NULL) {
                uu_die(gettext("composed_prop pool creation failed. %s\n"),
                    uu_strerror(uu_error()));
        }
        ptrn_info_pool = uu_avl_pool_create("ptrn_info", sizeof (ptrn_info_t),
            offsetof(ptrn_info_t, pi_link), ptrn_info_compare,
            TMPL_DEBUG_AVL_POOL);
        if (ptrn_info_pool == NULL) {
                uu_die(gettext("pg_pattern info pool creation failed: %s\n"),
                    uu_strerror(uu_error()));
        }
        inmem_errors_pool = uu_list_pool_create("errors-internal",
            sizeof (im_tmpl_error_t), offsetof(im_tmpl_error_t,
            ite_node), NULL, TMPL_DEBUG_LIST_POOL);
        if (inmem_errors_pool == NULL) {
                uu_die(gettext("inmem_errors_pool pool creation failed: "
                    "%s\n"), uu_strerror(uu_error()));
        }
        tv_errors_pool = uu_list_pool_create("scf-terrors",
            sizeof (tv_errors_t), offsetof(tv_errors_t, tve_node),
            NULL,  TMPL_DEBUG_LIST_POOL);
        if (tv_errors_pool == NULL) {
                uu_die(gettext("tv_errors_pool pool creation failed: %s\n"),
                    uu_strerror(uu_error()));
        }
}

/*
 * Clean up the composed property node in the property.
 */
void
tmpl_property_fini(property_t *p)
{
        uu_avl_node_fini(p, &p->sc_composed_node, composed_prop_pool);
}

/*
 * Initialize the composed property node in the property.
 */
void
tmpl_property_init(property_t *p)
{
        uu_avl_node_init(p, &p->sc_composed_node, composed_prop_pool);
}

/*
 * Use the cardinality specification in the prop_pattern to verify the
 * cardinality of the property at prop.  The cardinality of the property is
 * the number of values that it has.
 *
 * pg is the property group that holds prop, and pg_pattern is the
 * pg_pattern for the property group.  pg and pg_pattern are only used for
 * error reporting.
 */
static tmpl_validate_status_t
tmpl_validate_cardinality(pgroup_t *prop_pattern, property_t *prop,
    pgroup_t *pg, pgroup_t *pg_pattern, tmpl_errors_t *errs)
{
        size_t count;
        uint64_t max;
        uint64_t min;
        tmpl_validate_status_t rc;
        error_info_t einfo;

        assert(strcmp(prop_pattern->sc_pgroup_type,
            SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0);

        rc = get_cardinality(prop_pattern, &min, &max);
        switch (rc) {
        case TVS_NOMATCH:
                /* Nothing to check. */
                return (TVS_SUCCESS);
        case TVS_SUCCESS:
                /* Process the limits. */
                break;
        default:
                return (rc);
        }

        if ((min == 0) && (max == ULLONG_MAX)) {
                /* Any number of values is permitted.  No need to count. */
                return (TVS_SUCCESS);
        }

        count = count_prop_values(prop);
        if ((count < min) || (count > max)) {
                CLEAR_ERROR_INFO(&einfo);
                einfo.ei_type = EIT_CARDINALITY;
                einfo.ei_u.ei_cardinality.ei_min = min;
                einfo.ei_u.ei_cardinality.ei_max = max;
                einfo.ei_u.ei_cardinality.ei_count = count;
                (void) add_scf_error(errs, SCF_TERR_CARDINALITY_VIOLATION,
                    pg_pattern, pg, prop_pattern, prop, NULL, &einfo);
                return (TVS_VALIDATION);
        }

        return (TVS_SUCCESS);
}

/*
 * Iterate over pg_patterns in the entity, e.  If the pg_pattern's required
 * attribute is true, verify that the entity contains the corresponding
 * property group.
 */
static tmpl_validate_status_t
tmpl_required_pg_present(entity_t *e, tmpl_errors_t *errs)
{
        composed_pg_t cpg;
        composed_pg_t *match;
        error_info_t einfo;
        pg_iter_t *iter;
        pgroup_t *pg;
        const char *pg_name;
        const char *pg_type;
        tmpl_validate_status_t rc = TVS_SUCCESS;
        uu_avl_t *tree;

        assert(e->sc_etype == SVCCFG_INSTANCE_OBJECT);

        iter = pg_iter_create(e, SCF_GROUP_TEMPLATE_PG_PATTERN);
        if (iter == NULL)
                uu_die(emesg_nomem);

        CLEAR_ERROR_INFO(&einfo);
        einfo.ei_type = EIT_MISSING_PG;

        while ((pg = next_pattern_pg(iter)) != NULL) {
                if (is_required(pg) == 0) {
                        /* If pg is not required, there is nothing to check. */
                        continue;
                }
                pg_name = find_astring_value_in_pg(pg, SCF_PROPERTY_TM_NAME);
                pg_type = find_astring_value_in_pg(pg, SCF_PROPERTY_TM_TYPE);
                if (pg_target_check(pg, iter->pgi_level) == 0)
                        continue;
                einfo.ei_u.ei_missing_pg.ei_pg_name = pg_name;
                einfo.ei_u.ei_missing_pg.ei_pg_type = pg_type;
                tree = e->sc_u.sc_instance.sc_composed;
                (void) memset(&cpg, 0, sizeof (cpg));
                cpg.cpg_name = pg_name;
                cpg.cpg_type = pg_type;
                match = uu_avl_find(tree, &cpg, NULL, NULL);
                if (match == NULL) {
                        rc = TVS_VALIDATION;
                        if (add_scf_error(errs, SCF_TERR_MISSING_PG, pg,
                            NULL, NULL, NULL, NULL, &einfo) != 0) {
                                break;
                        }
                }
        }

        pg_iter_destroy(iter);
        return (rc);
}

/*
 * Verify that the property group, pg, contains property declarations for
 * all required properties.  Unfortunately, there is no direct way to find
 * the prop_patterns for a given property group.  Therefore, we need to
 * scan the entity at e looking for property groups with a type of
 * SCF_GROUP_TEMPLATE_PROP_PATTERN.  That is, we scan the entity looking
 * for all prop_patterns.  When we find a prop_pattern, we look at the
 * value of its pg_pattern property to see if it matches the name of the
 * pg_pattern.  If they match, this is a prop_pattern that is of interest
 * to us.
 *
 * When we find an interesting prop_pattern, we see if it's required
 * property is true.  If it is, we verify that the property group at pg
 * contains the specified property.
 */
static tmpl_validate_status_t
tmpl_required_props_present(entity_t *e, pgroup_t *pg, pgroup_t *pg_pattern,
    tmpl_errors_t *errs)
{
        error_info_t einfo;
        pg_iter_t *iter;
        const char *prop_name;
        const char *prop_pg_pattern_name;
        pgroup_t *prop_pattern;
        scf_tmpl_error_type_t ec;
        tmpl_validate_status_t rc = TVS_SUCCESS;

        /*
         * Scan the entity's property groups looking for ones with a type
         * of SCF_GROUP_TEMPLATE_PROP_PATTERN.
         */
        iter = pg_iter_create(e, SCF_GROUP_TEMPLATE_PROP_PATTERN);
        if (iter == NULL)
                uu_die(emesg_nomem);
        CLEAR_ERROR_INFO(&einfo);
        for (prop_pattern = next_pattern_pg(iter);
            prop_pattern != NULL;
            prop_pattern = next_pattern_pg(iter)) {
                /*
                 * Find the pg_pattern property in this prop_pattern.
                 * Verify that its value matches the name of the
                 * pg_pattern.
                 */
                prop_pg_pattern_name = find_astring_value_in_pg(prop_pattern,
                    SCF_PROPERTY_TM_PG_PATTERN);
                assert(prop_pg_pattern_name != NULL);
                if (strcmp(pg_pattern->sc_pgroup_name,
                    prop_pg_pattern_name) != 0) {
                        continue;
                }

                /* If the property is required, see if it is in the pg. */
                if (is_required(prop_pattern) == 0)
                        continue;
                prop_name = find_astring_value_in_pg(prop_pattern,
                    SCF_PROPERTY_TM_NAME);
                assert(prop_name != NULL);
                if (property_find(pg, prop_name) == NULL) {
                        ec = SCF_TERR_MISSING_PROP;
                        rc = TVS_VALIDATION;
                        einfo.ei_type = EIT_MISSING_PROP;
                        einfo.ei_u.ei_missing_prop.ei_prop_name = prop_name;
                        if (add_scf_error(errs, ec, pg_pattern, pg,
                            prop_pattern, NULL, NULL, &einfo) != 0) {
                                /*
                                 * If we can no longer accumulate errors,
                                 * break out of the loop.
                                 */
                                break;
                        }
                }
        }

        pg_iter_destroy(iter);
        return (rc);
}

/*
 * Check the value at v to see if it falls within any of the ranges at r.
 * count is the number of ranges at r, and type tells whether to treat the
 * value as signed or unsigned.
 *
 * Return 1 if the value falls within one of the ranges.  Otherwise return
 * 0.
 */
static int
value_in_range(value_t *v, scf_type_t type, range_t *r, size_t count)
{
        for (; count > 0; --count, r++) {
                if (type == SCF_TYPE_COUNT) {
                        if ((v->sc_u.sc_count >=
                            r->rng_u.rng_unsigned.rng_min) &&
                            (v->sc_u.sc_count <=
                            r->rng_u.rng_unsigned.rng_max))
                                return (1);
                } else {
                        if ((v->sc_u.sc_integer >=
                            r->rng_u.rng_signed.rng_min) &&
                            (v->sc_u.sc_integer <=
                            r->rng_u.rng_signed.rng_max))
                                return (1);
                }
        }
        return (0);
}

/*
 * If the template prop_pattern at pattern contains a constraint_range
 * property, use the specified range to validate all the numeric property
 * values of the property at prop.
 *
 * pg is the property group that holds prop, and pg_pattern is the
 * pg_pattern for the property group.  pg and pg_pattern are only used for
 * error reporting.
 */
static tmpl_validate_status_t
tmpl_validate_value_range(pgroup_t *pattern, property_t *prop, pgroup_t *pg,
    pgroup_t *pg_pattern, tmpl_errors_t *errs)
{
        uint_t count;
        error_info_t einfo;
        property_t *range_prop;
        range_t *ranges;
        tmpl_validate_status_t rc;
        scf_type_t type;
        value_t *v;

        /* Get the range constraints if they exist. */
        if ((range_prop = property_find(pattern,
            SCF_PROPERTY_TM_CONSTRAINT_RANGE)) == NULL) {
                /* No range to check. */
                return (TVS_SUCCESS);
        }
        type = prop->sc_value_type;
        if ((type != SCF_TYPE_COUNT) && (type != SCF_TYPE_INTEGER)) {
                rc = TVS_BAD_TEMPLATE;
                CLEAR_ERROR_INFO(&einfo);
                einfo.ei_type = EIT_BAD_TEMPLATE;
                einfo.ei_u.ei_bad_template.ei_reason =
                    gettext("Property does not have correct type for "
                    "a range specification");
                (void) tmpl_errors_add_im(errs, rc, pg_pattern->sc_parent,
                    pg_pattern, pg, pattern, prop, NULL, &einfo);
                return (rc);
        }
        if ((rc = get_ranges(range_prop, prop->sc_value_type, &ranges,
            &count)) != TVS_SUCCESS) {
                rc = TVS_BAD_TEMPLATE;
                CLEAR_ERROR_INFO(&einfo);
                einfo.ei_type = EIT_BAD_TEMPLATE;
                einfo.ei_u.ei_bad_template.ei_reason = gettext("Illegal range "
                    "value");
                (void) tmpl_errors_add_im(errs, rc, pg_pattern->sc_parent,
                    pg_pattern, pg, pattern, prop, NULL, &einfo);
                return (rc);
        }

        /* Set up error info before entering loop. */
        CLEAR_ERROR_INFO(&einfo);
        einfo.ei_type = EIT_RANGE;
        einfo.ei_u.ei_range.ei_rtype = type;

        /* Compare numeric values of the property to the range. */
        for (v = uu_list_first(prop->sc_property_values);
            v != NULL;
            v = uu_list_next(prop->sc_property_values, v)) {
                if (value_in_range(v, type, ranges, count) == 1)
                        continue;
                if (type == SCF_TYPE_COUNT) {
                        einfo.ei_u.ei_range.ei_uvalue = v->sc_u.sc_count;
                } else {
                        einfo.ei_u.ei_range.ei_ivalue = v->sc_u.sc_integer;
                }
                rc = TVS_VALIDATION;
                if (add_scf_error(errs, SCF_TERR_RANGE_VIOLATION, pg_pattern,
                    pg, pattern, prop, v, &einfo) != 0) {
                        return (rc);
                }
        }

        return (rc);
}

/*
 * If the prop_pattern has value constraints, verify that all the values
 * for the property at prop are legal values.
 *
 * pg is the property group that holds prop, and pg_pattern is the
 * pg_pattern for the property group.  pg and pg_pattern are only used for
 * error reporting.
 */
static tmpl_validate_status_t
tmpl_validate_values(pgroup_t *prop_pattern, property_t *prop, pgroup_t *pg,
    pgroup_t *pg_pattern, tmpl_errors_t *errs)
{
        int found;
        uint_t i;
        avalues_t *legal;
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc = TVS_SUCCESS;
        value_t *v;

        /* Get list of legal values. */
        r = av_get_values(prop_pattern, SCF_PROPERTY_TM_CONSTRAINT_NAME,
            prop->sc_value_type, &legal);
        switch (r) {
        case TVS_BAD_CONVERSION:
                (void) tmpl_errors_add_im(errs, r, pg->sc_parent, pg_pattern,
                    pg, prop_pattern, prop, NULL, NULL);
                return (r);
        case TVS_NOMATCH:
                /* No constraints in template. */
                return (TVS_SUCCESS);
        case TVS_SUCCESS:
                /* process the constraints. */
                break;
        default:
                assert(0);
                abort();
        }

        /* Check the property values against the legal values. */
        for (v = uu_list_first(prop->sc_property_values);
            v != NULL;
            v = uu_list_next(prop->sc_property_values, v)) {
                /* Check this property value against the legal values. */
                found = 0;
                for (i = 0; (i < legal->av_count) && (found == 0); i++) {
                        switch (v->sc_type) {
                        case SCF_TYPE_BOOLEAN:
                        case SCF_TYPE_COUNT:
                                if (av_get_unsigned(legal, i) ==
                                    v->sc_u.sc_count) {
                                        found = 1;
                                }
                                break;
                        case SCF_TYPE_INTEGER:
                                if (av_get_integer(legal, i) ==
                                    v->sc_u.sc_integer) {
                                        found = 1;
                                }
                                break;
                        default:
                                if (strcmp(av_get_string(legal, i),
                                    v->sc_u.sc_string) == 0) {
                                        found = 1;
                                }
                                break;
                        }
                }
                if (found == 0) {
                        rc = TVS_VALIDATION;
                        if (add_scf_error(errs,
                            SCF_TERR_VALUE_CONSTRAINT_VIOLATED, pg_pattern, pg,
                            prop_pattern, prop, v, NULL) != 0) {
                                /*
                                 * Exit loop if no longer able to report
                                 * errors.
                                 */
                                break;
                        }
                }
        }

        av_destroy(legal);
        return (rc);
}

/*
 * Verify the following items about the values of property, prop.
 *
 *      - The values all have the type specified by the prop_pattern at
 *        pattern.
 *      - Check numeric values against range constraints.
 *      - If the prop_pattern has one or more value constraints, validate
 *        the property's values against the constraints.
 *
 * pg is the property group that holds prop, and pg_pattern is the
 * pg_pattern for the property group.  pg and pg_pattern are only used for
 * error reporting.
 */
static tmpl_validate_status_t
tmpl_validate_value_constraints(pgroup_t *pattern, property_t *prop,
    pgroup_t *pg, pgroup_t *pg_pattern, tmpl_errors_t *errs)
{
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc;

        rc = tmpl_validate_value_range(pattern, prop, pg, pg_pattern, errs);
        r = tmpl_validate_values(pattern, prop, pg, pg_pattern, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        return (rc);
}

/*
 * Perform the following validations on the property, prop.
 *
 *      - Verify that the property's type agrees with the type specified in
 *        the prop_pattern template, tmpl.
 *      - Verify the cardinality.
 *      - Verify that the property values satisfy the constraints specified
 *        by the template.
 *
 * pg is the property group that holds prop, and pg_pattern is the
 * pg_pattern for the property group.  pg and pg_pattern are only used for
 * error reporting.
 */
static tmpl_validate_status_t
tmpl_validate_prop(property_t *prop, pgroup_t *tmpl, pgroup_t *pg,
    pgroup_t *pg_pattern, tmpl_errors_t *errs)
{
        scf_tmpl_error_type_t ec;
        error_info_t einfo;
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc = TVS_SUCCESS;
        int status;
        scf_type_t type;

        r = prop_pattern_type(tmpl, &type);
        switch (r) {
        case TVS_SUCCESS:
                if (type == SCF_TYPE_INVALID) {
                        rc = TVS_INVALID_TYPE_SPECIFICATION;
                        r = tmpl_errors_add_im(errs, rc, pg->sc_parent, NULL,
                            pg, tmpl, NULL, NULL, NULL);
                        if (r != TVS_SUCCESS) {
                                /*
                                 * Give up if we can no longer accumulate
                                 * errors.
                                 */
                                return (rc);
                        }
                } else {
                        if (property_is_type(prop, type) == 0) {
                                CLEAR_ERROR_INFO(&einfo);
                                rc = TVS_VALIDATION;
                                ec = SCF_TERR_WRONG_PROP_TYPE;
                                einfo.ei_type  = EIT_PROP_TYPE;
                                einfo.ei_u.ei_prop_type.ei_specified = type;
                                einfo.ei_u.ei_prop_type.ei_actual =
                                    prop->sc_value_type;
                                status = add_scf_error(errs, ec,
                                    pg_pattern, pg, tmpl, prop, NULL, &einfo);
                                if (status != 0) {
                                        /*
                                         * Give up if we can no longer
                                         * accumulate errors.
                                         */
                                        return (rc);
                                }
                        }
                }
                break;
        case TVS_MISSING_TYPE_SPECIFICATION:
                /*
                 * A null type specification means that we do not need to
                 * check the property's type.
                 */
                break;
        default:
                rc = r;
        }

        /* Validate the cardinality */
        r = tmpl_validate_cardinality(tmpl, prop, pg, pg_pattern, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        /* Validate that property values satisfy constraints. */
        r = tmpl_validate_value_constraints(tmpl, prop, pg, pg_pattern, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        return (rc);
}

/*
 * Validate the property group at pg by performing the following checks:
 *
 *      - Verify that the types of the pg and the pg_pattern are
 *        compatible.
 *      - Verify the properties in the pg.
 *      - Verify that required properties are present.
 */
static tmpl_validate_status_t
tmpl_validate_pg(entity_t *e, pgroup_t *pg, tmpl_errors_t *errs)
{
        error_info_t einfo;
        const char *pg_pattern_type;    /* Type declared by pg_pattern. */
        pgroup_t *pg_pattern;   /* Prop. group for pg_pattern */
        property_t *prop;
        pgroup_t *prop_pattern;
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc = TVS_SUCCESS;
        int stat;

        /*
         * See if there is a pg_pattern for this property group.  If it
         * exists, use it to validate the property group.  If there is no
         * pg_pattern, then there is no validation to do.
         */
        rc = tmpl_find_pg_pattern(e, pg, &pg_pattern);
        switch (rc) {
        case TVS_SUCCESS:
                break;
        case TVS_BAD_TEMPLATE:
                CLEAR_ERROR_INFO(&einfo);
                einfo.ei_type = EIT_BAD_TEMPLATE;
                einfo.ei_u.ei_bad_template.ei_reason = gettext("Property "
                    "group name too long");
                (void) tmpl_errors_add_im(errs, rc, e, NULL, pg, NULL, NULL,
                    NULL, &einfo);
                return (rc);
        default:
                assert(0);
                abort();
        }
        if (pg_pattern == NULL)
                return (TVS_SUCCESS);

        /*
         * If the pg_pattern declares a type, verify that the PG has the
         * correct type.
         */
        pg_pattern_type = find_type_specification(pg_pattern);
        if ((pg_pattern_type != NULL) &&
            (*pg_pattern_type != 0)) {
                if ((pg->sc_pgroup_type != NULL) &&
                    (*(pg->sc_pgroup_type) != 0)) {
                        if (strcmp(pg_pattern_type,
                            pg->sc_pgroup_type) != 0) {
                                rc = TVS_VALIDATION;
                                stat = add_scf_error(errs,
                                    SCF_TERR_WRONG_PG_TYPE, pg_pattern, pg,
                                    NULL, NULL, NULL, NULL);
                                if (stat != 0) {
                                        /*
                                         * If we can no longer accumulate
                                         * errors, return without trying to
                                         * do further validation.
                                         */
                                        return (rc);
                                }
                        }
                } else {
                        rc = TVS_MISSING_PG_TYPE;
                        r = tmpl_errors_add_im(errs, rc, e, pg_pattern, pg,
                            NULL, NULL, NULL, NULL);
                        if (r != TVS_SUCCESS) {
                                /*
                                 * If we can no longer accumulate errors,
                                 * return without trying to do further
                                 * validation.
                                 */
                                return (rc);
                        }
                }
        }

        /* Verify the properties in the property group. */
        prop = NULL;
        while ((prop = next_property(pg, prop)) != NULL) {
                r = tmpl_find_prop_pattern(e, pg_pattern, prop, &prop_pattern);
                switch (r) {
                case TVS_SUCCESS:
                        /* Found match.  Validate property. */
                        break;
                case TVS_NOMATCH:
                        /* No prop_patern.  Go on to next property. */
                        continue;
                case TVS_BAD_TEMPLATE:
                        CLEAR_ERROR_INFO(&einfo);
                        einfo.ei_type = EIT_BAD_TEMPLATE;
                        einfo.ei_u.ei_bad_template.ei_reason =
                            gettext("prop_pattern name too long");
                        (void) tmpl_errors_add_im(errs, r, e, NULL, pg, NULL,
                            NULL, NULL, &einfo);
                        continue;
                default:
                        assert(0);
                        abort();
                }
                r = tmpl_validate_prop(prop, prop_pattern, pg, pg_pattern,
                    errs);
                if (r != TVS_SUCCESS)
                        rc = r;
        }

        /*
         * Confirm required properties are present.
         */
        r = tmpl_required_props_present(e, pg, pg_pattern, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        return (rc);
}

/*
 * Validate that the property groups in the entity conform to the template
 * specifications.  Specifically, this means do the following:
 *
 *      - Loop through the property groups in the entity skipping the ones
 *        that are of type "template".
 *
 *      - For the PG search for the corresponding template_pg_pattern
 *        property group.  It is possible that one may not exist.
 *
 *      - Verify that the PG is in conformance with the pg_pattern
 *        specification if it exists.
 */
static tmpl_validate_status_t
tmpl_validate_entity_pgs(entity_t *e, tmpl_errors_t *errs)
{
        composed_pg_t *cpg;
        uu_avl_t *pgroups;
        pgroup_t *pg;
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc = TVS_SUCCESS;

        assert(e->sc_etype == SVCCFG_INSTANCE_OBJECT);

        pgroups = e->sc_u.sc_instance.sc_composed;
        for (cpg = uu_avl_first(pgroups);
            cpg != NULL;
            cpg = uu_avl_next(pgroups, cpg)) {
                if (strcmp(cpg->cpg_type, SCF_GROUP_TEMPLATE) == 0)
                        continue;
                pg = CPG2PG(cpg);
                if ((r = tmpl_validate_pg(e, pg, errs)) != TVS_SUCCESS)
                        rc = r;
        }

        return (rc);
}

/*
 * Validate the instance, e, by performing the following checks:
 *
 *      - Verify template consistency.
 *
 *      - Validate each property group in the entity is in conformance
 *        with the template specifications.
 *
 *      - Verify that all required property groups are present in the
 *        entity.
 */
static tmpl_validate_status_t
tmpl_validate_instance(entity_t *e, tmpl_errors_t *errs)
{
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc = TVS_SUCCESS;
        int status;
        tv_errors_t *ste;

        /* Prepare to collect errors for this instance. */
        ste = tv_errors_create(e->sc_fmri);
        status = uu_list_insert_after(errs->te_scf, errs->te_cur_scf, ste);
        assert(status == 0);
        errs->te_cur_scf = ste;

        /* Verify template consistency */
        rc = tmpl_consistency(e, errs);

        /* Validate the property groups in the entity. */
        r = tmpl_validate_entity_pgs(e, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        /* Verify that all required property groups are present. */
        r = tmpl_required_pg_present(e, errs);
        if (r != TVS_SUCCESS)
                rc = r;

        return (rc);
}

/*
 * First validate the instances of the service.
 */
static tmpl_validate_status_t
tmpl_validate_service(entity_t *svc, tmpl_errors_t *errs)
{
        entity_t *inst;
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc = TVS_SUCCESS;

        assert(svc->sc_etype == SVCCFG_SERVICE_OBJECT);

        load_general_templates(svc);

        /* Validate the service's instances. */
        for (inst = uu_list_first(svc->sc_u.sc_service.sc_service_instances);
            inst != NULL;
            inst = uu_list_next(svc->sc_u.sc_service.sc_service_instances,
            inst)) {
                load_instance_restarter(inst);
                build_composed_instance(inst);
                r = tmpl_validate_instance(inst, errs);
                if (r != TVS_SUCCESS)
                        rc = r;
                demolish_composed_instance(inst);
        }

        return (rc);
}

/*
 * Validate all services and instances in the bundle against their
 * templates.  If err_list is not NULL, a tmpl_errors structure will be
 * allocated and its address will be returned to err_list.  This structure
 * can be used to generate error messages.
 */
tmpl_validate_status_t
tmpl_validate_bundle(bundle_t *bndl, tmpl_errors_t **err_list)
{
        tmpl_errors_t *errs = NULL;
        entity_t *svc;
        tmpl_validate_status_t r;
        tmpl_validate_status_t rc = TVS_SUCCESS;

        if (err_list != NULL)
                *err_list = NULL;
        if (bndl->sc_bundle_type != SVCCFG_MANIFEST) {
                semerr(gettext("Bundle is not a manifest.  Unable to validate "
                    "against templates.\n"));
                return (TVS_FATAL);
        }

        errs = tmpl_errors_create();
        if (errs == NULL)
                uu_die(emesg_nomem);

        lscf_prep_hndl();               /* Initialize g_hndl */
        if (load_init() != 0)
                uu_die(emesg_nomem);

        /*
         * We will process all services in the bundle, unless we get a
         * fatal error.  That way we can report all errors on all services
         * on a single run of svccfg.
         */
        for (svc = uu_list_first(bndl->sc_bundle_services);
            svc != NULL;
            svc = uu_list_next(bndl->sc_bundle_services, svc)) {
                if (svc->sc_etype != SVCCFG_SERVICE_OBJECT) {
                        semerr(gettext("Manifest for %s contains an object "
                            "named \"%s\" that is not a service.\n"),
                            bndl->sc_bundle_name, svc->sc_name);
                        tmpl_errors_destroy(errs);
                        load_fini();
                        return (TVS_FATAL);
                }
                if ((r = tmpl_validate_service(svc, errs)) != TVS_SUCCESS)
                        rc = r;
                if (r == TVS_FATAL)
                        break;
        }

        if (err_list == NULL) {
                tmpl_errors_destroy(errs);
        } else {
                *err_list = errs;
        }

        load_fini();

        return (rc);
}