diff options
Diffstat (limited to 'usr/src/lib/libpool/common/pool_xml.c')
-rw-r--r-- | usr/src/lib/libpool/common/pool_xml.c | 2787 |
1 files changed, 2787 insertions, 0 deletions
diff --git a/usr/src/lib/libpool/common/pool_xml.c b/usr/src/lib/libpool/common/pool_xml.c new file mode 100644 index 0000000..4f0b6df --- /dev/null +++ b/usr/src/lib/libpool/common/pool_xml.c @@ -0,0 +1,2787 @@ +/* + * 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. + */ + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <thread.h> +#include <time.h> +#include <unistd.h> + +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/utsname.h> + +#include <libxml/debugXML.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xmlerror.h> +#include <libxml/xpath.h> +#include <libxml/xmlmemory.h> + +#include <pool.h> +#include "pool_internal.h" +#include "pool_impl.h" +#include "pool_xml_impl.h" + +/* + * libpool XML Manipulation Routines + * + * pool_xml.c implements the XML manipulation routines used by the libpool + * XML datastore. The functions are grouped into the following logical areas + * - Result Sets + * The XPath API is used to search the XML document represented by a + * configuration. The results of XPath queries are represented through + * pool_result_set_t structures as part of the abstraction of the datastore + * representation. (see pool.c comment for more details) + * + * - Property Manipulation + * Validated XML (XML associated with a DTD) does not allow the introduction + * of attributes which are not recognised by the DTD. This is a limitation + * since we want to allow libpool to associate an arbitrary number of + * properties with an element. The property manipulation code overcomes this + * limitation by allowing property sub-elements to be created and manipulated + * through a single API so that they are indistinguishable from attributes + * to the libpool user. + * + * - XML Element/Attribute Manipulation + * These routines manipulate XML elements and attributes and are the routines + * which interact most directly with libxml. + * + * - File Processing/IO + * Since libpool must present its data in a consistent fashion, we have to + * implement file locking above libxml. These routines allow us to lock files + * during processing and maintain data integrity between processes. Note + * that locks are at the process scope and are advisory (see fcntl). + * + * - Utilities + * Sundry utility functions that aren't easily categorised. + */ + +#define MAX_PROP_SIZE 1024 /* Size of property buffer */ +/* + * The PAGE_READ_SIZE value is used to determine the size of the input buffer + * used to parse XML files. + */ +#define PAGE_READ_SIZE 8192 +#define ELEM_TYPE_COUNT 6 /* Count of Element types */ + +typedef struct dtype_tbl +{ + xmlChar *dt_name; + int dt_type; +} dtype_tbl_t; + +typedef struct elem_type_tbl +{ + xmlChar *ett_elem; + dtype_tbl_t (*ett_dtype)[]; +} elem_type_tbl_t; + +extern int xmlDoValidityCheckingDefaultValue; + +/* + * The _xml_lock is used to lock the state of libpool during + * xml initialisation operations. + */ +static mutex_t _xml_lock; + +const char *element_class_tags[] = { + "any", + "system", + "pool", + "res_comp", + "res_agg", + "comp", + NULL +}; + +static const char *data_type_tags[] = { + "uint", + "int", + "float", + "boolean", + "string" +}; + +const char *dtd_location = "file:///usr/share/lib/xml/dtd/rm_pool.dtd.1"; + +static elem_type_tbl_t elem_tbl[ELEM_TYPE_COUNT] = {0}; + +/* libpool initialisation indicator */ +static int _libpool_xml_initialised = PO_FALSE; + +/* + * Utility functions + */ +/* + * Those functions which are not static are shared with pool_kernel.c + * They provide the required XML support for exporting a kernel + * configuration as an XML document. + */ +void xml_init(void); +static int create_shadow(xmlNodePtr node); +static int pool_xml_free_doc(pool_conf_t *conf); +static int prop_sort(const void *a, const void *b); +static int dtd_exists(const char *path); +static void build_dtype_accelerator(void); +static dtype_tbl_t (*build_dtype_tbl(const xmlChar *rawdata))[]; +static int get_fast_dtype(xmlNodePtr node, xmlChar *name); +static int pool_assoc_default_resource_type(pool_t *, + pool_resource_elem_class_t); + +/* + * XML Data access and navigation APIs + */ +static int pool_build_xpath_buf(pool_xml_connection_t *, const pool_elem_t *, + pool_elem_class_t, pool_value_t **, char_buf_t *, int); +/* + * SHARED WITH pool_kernel.c for XML export support + */ +xmlNodePtr node_create(xmlNodePtr parent, const xmlChar *name); +static xmlNodePtr node_create_with_id(xmlNodePtr parent, const xmlChar *name); + +/* Configuration */ +static int pool_xml_close(pool_conf_t *); +static int pool_xml_validate(const pool_conf_t *, pool_valid_level_t); +static int pool_xml_commit(pool_conf_t *conf); +static int pool_xml_export(const pool_conf_t *conf, const char *location, + pool_export_format_t fmt); +static int pool_xml_rollback(pool_conf_t *conf); +static pool_result_set_t *pool_xml_exec_query(const pool_conf_t *conf, + const pool_elem_t *src, const char *src_attr, + pool_elem_class_t classes, pool_value_t **props); +static int pool_xml_remove(pool_conf_t *conf); +static int pool_xml_res_transfer(pool_resource_t *, pool_resource_t *, + uint64_t); +static int pool_xml_res_xtransfer(pool_resource_t *, pool_resource_t *, + pool_component_t **); + +/* Connections */ +static void pool_xml_connection_free(pool_xml_connection_t *prov); + +/* Result Sets */ +static pool_xml_result_set_t *pool_xml_result_set_alloc(const pool_conf_t *); +static void pool_xml_result_set_free(pool_xml_result_set_t *rs); +static pool_elem_t *pool_xml_rs_next(pool_result_set_t *set); +static pool_elem_t *pool_xml_rs_prev(pool_result_set_t *set); +static pool_elem_t *pool_xml_rs_first(pool_result_set_t *set); +static pool_elem_t *pool_xml_rs_last(pool_result_set_t *set); +static int pool_xml_rs_set_index(pool_result_set_t *set, int index); +static int pool_xml_rs_get_index(pool_result_set_t *set); +static int pool_xml_rs_count(pool_result_set_t *set); +static int pool_xml_rs_close(pool_result_set_t *set); + +/* Element (and sub-type) */ +static void pool_xml_elem_init(pool_conf_t *conf, pool_xml_elem_t *elem, + pool_elem_class_t, pool_resource_elem_class_t, pool_component_elem_class_t); +static int pool_xml_elem_wrap(xmlNodePtr node, pool_elem_class_t class, + pool_resource_elem_class_t, pool_component_elem_class_t); +static pool_elem_t *pool_xml_elem_create(pool_conf_t *, pool_elem_class_t, + pool_resource_elem_class_t, pool_component_elem_class_t); +static int pool_xml_elem_remove(pool_elem_t *pe); +static int pool_xml_set_container(pool_elem_t *, pool_elem_t *); +static pool_elem_t *pool_xml_get_container(const pool_elem_t *); + +/* + * Pool element specific + */ +static int pool_xml_pool_associate(pool_t *, const pool_resource_t *); +static int pool_xml_pool_dissociate(pool_t *, const pool_resource_t *); + +/* + * Resource elements specific + */ +static int pool_xml_resource_is_system(const pool_resource_t *); +static int pool_xml_resource_can_associate(const pool_resource_t *); + +/* Properties */ +static pool_value_class_t pool_xml_get_property(const pool_elem_t *, + const char *, pool_value_t *); +static int pool_xml_put_property(pool_elem_t *, const char *, + const pool_value_t *); +static int pool_xml_rm_property(pool_elem_t *, const char *); +static xmlNodePtr property_create(xmlNodePtr, const char *, + pool_value_class_t); + +/* Internal Attribute/Property manipulation */ +static int pool_is_xml_attr(xmlDocPtr, const char *, const char *); +static pool_value_class_t pool_xml_get_attr(xmlNodePtr node, xmlChar *name, + pool_value_t *value); +int pool_xml_set_attr(xmlNodePtr node, xmlChar *name, + const pool_value_t *value); +static pool_value_class_t pool_xml_get_prop(xmlNodePtr node, xmlChar *name, + pool_value_t *value); +int pool_xml_set_prop(xmlNodePtr node, xmlChar *name, + const pool_value_t *value); +static pool_value_t **pool_xml_get_properties(const pool_elem_t *, uint_t *); +/* XML Error handling */ +void pool_error_func(void *ctx, const char *msg, ...); + +/* XML File Input Processing */ +static int pool_xml_open_file(pool_conf_t *conf); +static int pool_xml_parse_document(pool_conf_t *); + +/* + * Initialise this module + */ +void +xml_init() +{ + (void) mutex_lock(&_xml_lock); + if (_libpool_xml_initialised == PO_TRUE) { + (void) mutex_unlock(&_xml_lock); + return; + } + xmlInitParser(); + + /* + * DTD validation, with line numbers. + */ + (void) xmlLineNumbersDefault(1); + xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS; + xmlDoValidityCheckingDefaultValue = 1; + /* Try to improve indentation and readability */ + (void) xmlKeepBlanksDefault(0); + /* Send all XML errors to our debug handler */ + xmlSetGenericErrorFunc(NULL, pool_error_func); + /* Load up DTD element a-dtype data to improve performance */ + build_dtype_accelerator(); + _libpool_xml_initialised = PO_TRUE; + (void) mutex_unlock(&_xml_lock); +} + +/* + * Get the next ID for this configuration + */ +static int +get_unique_id(xmlNodePtr node, char *id) +{ + pool_value_t val = POOL_VALUE_INITIALIZER; + uint64_t nid = 0; + if (node->doc->_private) { + if (pool_get_ns_property( + pool_conf_to_elem((pool_conf_t *)node->doc->_private), + "_next_id", &val) == POC_UINT) + (void) pool_value_get_uint64(&val, &nid); + } + if (snprintf(id, KEY_BUFFER_SIZE, "id_%llx", nid) > KEY_BUFFER_SIZE) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + pool_value_set_uint64(&val, ++nid); + return (pool_put_ns_property( + pool_conf_to_elem((pool_conf_t *)node->doc->_private), "_next_id", + &val)); +} + +/* Document building functions */ + +/* + * node_create() creates a child node of type name of the supplied parent in + * the supplied document. If the parent or document is NULL, create the node + * but do not associate it with a parent or document. + */ +xmlNodePtr +node_create(xmlNodePtr parent, const xmlChar *name) +{ + xmlNodePtr node; + + if (parent == NULL) + node = xmlNewNode(NULL, name); + else + node = xmlNewChild(parent, NULL, name, NULL); + return (node); +} + +/* + * node_create_with_id() creates a child node of type name of the supplied + * parent with the ref_id generated by get_unique_id(). Actual node creation + * is performed by node_create() and this function just sets the ref_id + * property to the value of the id. + */ +static xmlNodePtr +node_create_with_id(xmlNodePtr parent, const xmlChar *name) +{ + char id[KEY_BUFFER_SIZE]; /* Must be big enough for key below */ + xmlNodePtr node = node_create(parent, name); + if (node != NULL) { + if (get_unique_id(node, id) != PO_SUCCESS) { + xmlUnlinkNode(node); + xmlFreeNode(node); /* recurses all children */ + pool_seterror(POE_DATASTORE); + return (NULL); + } + if (xmlSetProp(node, BAD_CAST c_ref_id, BAD_CAST id) == NULL) { + xmlUnlinkNode(node); + xmlFreeNode(node); /* recurses all children */ + pool_seterror(POE_DATASTORE); + return (NULL); + } + } + return (node); +} + +/* Supporting Data Conversion Routines */ + +/* XML Parser Utility Functions */ + +/* + * Handler for XML Errors. Called by libxml at libxml Error. + */ +/*ARGSUSED*/ +void +pool_error_func(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + do_dprintf(msg, ap); + va_end(ap); +} + +/* + * Free the shadowed elements from within the supplied document and then + * free the document. This function should always be called when freeing + * a pool document to ensure that all "shadow" resources are reclaimed. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_free_doc(pool_conf_t *conf) +{ + /* Only do any of this if there is a document */ + if (((pool_xml_connection_t *)conf->pc_prov)->pxc_doc != NULL) { + pool_elem_t *pe; + pool_result_set_t *rs; + /* Delete all the "shadowed" children of the doc */ + rs = pool_exec_query(conf, NULL, NULL, PEC_QRY_ANY, NULL); + if (rs == NULL) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + for (pe = rs->prs_next(rs); pe != NULL; pe = rs->prs_next(rs)) { + /* + * Work out the element type and free the elem + */ + free(pe); + } + (void) pool_rs_close(rs); + xmlFreeDoc(((pool_xml_connection_t *)conf->pc_prov)->pxc_doc); + } + ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc = NULL; + return (PO_SUCCESS); +} + +/* + * Remove an element from the document. Note that only three types of elements + * can be removed, res, comp and pools. comp are moved around to the + * default res when a res is deleted. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_elem_remove(pool_elem_t *pe) +{ + pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe; + + /* + * You can only destroy three elements: pools, resources and + * components. + */ + switch (pe->pe_class) { + case PEC_POOL: + case PEC_RES_COMP: + case PEC_RES_AGG: + case PEC_COMP: + if (pxe->pxe_node) { + xmlUnlinkNode(pxe->pxe_node); + xmlFreeNode(pxe->pxe_node); /* recurses all children */ + } + free(pxe); + break; + default: + break; + } + return (PO_SUCCESS); +} + +/* + * Create a property element. + */ +static xmlNodePtr +property_create(xmlNodePtr parent, const char *name, pool_value_class_t type) +{ + + xmlNodePtr element; + pool_value_t val = POOL_VALUE_INITIALIZER; + + if ((element = node_create(parent, BAD_CAST "property")) == NULL) { + pool_seterror(POE_DATASTORE); + return (NULL); + } + if (pool_value_set_string(&val, name) != PO_SUCCESS) { + xmlFree(element); + return (NULL); + } + (void) pool_xml_set_attr(element, BAD_CAST c_name, &val); + if (pool_value_set_string(&val, data_type_tags[type]) != PO_SUCCESS) { + xmlFree(element); + return (NULL); + } + (void) pool_xml_set_attr(element, BAD_CAST c_type, &val); + return (element); +} + +/* + * External clients need to be able to put/get properties and this is the + * way to do it. + * This function is an interceptor, since it will *always* try to manipulate + * an attribute first. If the attribute doesn't exist, then it will treat + * the request as a property request. + */ +static pool_value_class_t +pool_xml_get_property(const pool_elem_t *pe, const char *name, + pool_value_t *val) +{ + pool_value_class_t type; + pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe; + + /* + * "type" is a special attribute which is not visible ever outside of + * libpool. Use the specific type accessor function. + */ + if (strcmp(name, c_type) == 0) { + return (pool_xml_get_attr(pxe->pxe_node, BAD_CAST name, + val)); + } + if (is_ns_property(pe, name) != NULL) { /* in ns */ + if ((type = pool_xml_get_attr(pxe->pxe_node, + BAD_CAST property_name_minus_ns(pe, name), val)) + == POC_INVAL) + return (pool_xml_get_prop(pxe->pxe_node, BAD_CAST name, + val)); + } else + return (pool_xml_get_prop(pxe->pxe_node, BAD_CAST name, val)); + + return (type); +} + +/* + * Put a property on an element. Check if the property is an attribute, + * if it is update that value. If not add a property element. + * + * There are three possible conditions here: + * - the name is a ns + * - the name is an attribute + * - the name isn't an attribute + * - the name is not a ns + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_put_property(pool_elem_t *pe, const char *name, + const pool_value_t *val) +{ + pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe; + + /* + * "type" is a special attribute which is not visible ever outside of + * libpool. Use the specific type accessor function. + */ + if (strcmp(name, c_type) == 0) { + return (pool_xml_set_attr(pxe->pxe_node, BAD_CAST name, + val)); + } + if (is_ns_property(pe, name) != NULL) { /* in ns */ + if (pool_xml_set_attr(pxe->pxe_node, + BAD_CAST property_name_minus_ns(pe, name), val) == PO_FAIL) + return (pool_xml_set_prop(pxe->pxe_node, BAD_CAST name, + val)); + } else + return (pool_xml_set_prop(pxe->pxe_node, BAD_CAST name, val)); + return (PO_SUCCESS); +} + +/* + * Remove a property from an element. Check if the property is an attribute, + * if it is fail. Otherwise remove the property subelement. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_rm_property(pool_elem_t *pe, const char *name) +{ + pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe; + xmlXPathContextPtr ctx; + xmlXPathObjectPtr path; + char buf[MAX_PROP_SIZE]; + int ret; + + if (xmlHasProp(pxe->pxe_node, BAD_CAST name) != NULL) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + + /* use xpath to find the node with the appropriate value for name */ + (void) snprintf(buf, sizeof (buf), "property[@name=\"%s\"]", name); + if ((ctx = xmlXPathNewContext(pxe->pxe_node->doc)) == NULL) { + pool_seterror(POE_PUTPROP); + return (PO_FAIL); + } + ctx->node = pxe->pxe_node; + path = xmlXPathEval(BAD_CAST buf, ctx); + + if (path && (path->type == XPATH_NODESET) && + (path->nodesetval->nodeNr == 1)) { + xmlUnlinkNode(path->nodesetval->nodeTab[0]); + xmlFreeNode(path->nodesetval->nodeTab[0]); + ret = PO_SUCCESS; + } else { + pool_seterror(POE_BADPARAM); + ret = PO_FAIL; + } + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + return (ret); +} + +/* + * Get the data type for an attribute name from the element node. The data + * type is returned and the value of the attribute updates the supplied value + * pointer. + */ +static pool_value_class_t +pool_xml_get_attr(xmlNodePtr node, xmlChar *name, pool_value_t *value) +{ + pool_value_class_t data_type; + xmlChar *data; + uint64_t uval; + int64_t ival; + + if (xmlHasProp(node, name) == NULL && pool_is_xml_attr(node->doc, + (const char *) node->name, (const char *) name) == PO_FALSE) { + pool_seterror(POE_BADPARAM); + return (POC_INVAL); + } + if (xmlHasProp(node, BAD_CAST c_a_dtype) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (POC_INVAL); + } + data = xmlGetProp(node, name); + data_type = get_fast_dtype(node, name); + if (data_type != POC_STRING && data == NULL) { + pool_seterror(POE_INVALID_CONF); + return (POC_INVAL); + } + switch (data_type) { + case POC_UINT: + errno = 0; + uval = strtoull((char *)data, NULL, 0); + if (errno != 0) { + data_type = POC_INVAL; + } + else + pool_value_set_uint64(value, uval); + break; + case POC_INT: + errno = 0; + ival = strtoll((char *)data, NULL, 0); + if (errno != 0) { + data_type = POC_INVAL; + } + else + pool_value_set_int64(value, ival); + break; + case POC_DOUBLE: + pool_value_set_double(value, atof((const char *)data)); + break; + case POC_BOOL: + if (strcmp((const char *)data, "true") == 0) + pool_value_set_bool(value, PO_TRUE); + else + pool_value_set_bool(value, PO_FALSE); + break; + case POC_STRING: + if (pool_value_set_string(value, data ? + (const char *)data : "") != PO_SUCCESS) { + xmlFree(data); + return (POC_INVAL); + } + break; + case POC_INVAL: + default: + break; + } + xmlFree(data); + return (data_type); +} + +/* + * Set the data type for an attribute name from the element node. The + * supplied value is used to update the designated name using the data + * type supplied. + */ +int +pool_xml_set_attr(xmlNodePtr node, xmlChar *name, const pool_value_t *value) +{ + xmlChar buf[MAX_PROP_SIZE] = {0}; + uint64_t ures; + int64_t ires; + uchar_t bres; + double dres; + const char *sres; + + pool_value_class_t data_type; + + if (xmlHasProp(node, name) == NULL && pool_is_xml_attr(node->doc, + (const char *) node->name, (const char *) name) == PO_FALSE) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + + if (xmlHasProp(node, BAD_CAST c_a_dtype) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + data_type = get_fast_dtype(node, name); + if (data_type != value->pv_class) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + switch (value->pv_class) { + case POC_UINT: + (void) pool_value_get_uint64(value, &ures); + (void) snprintf((char *)buf, sizeof (buf), "%llu", + (u_longlong_t)ures); + break; + case POC_INT: + (void) pool_value_get_int64(value, &ires); + (void) snprintf((char *)buf, sizeof (buf), "%lld", + (longlong_t)ires); + break; + case POC_DOUBLE: + (void) pool_value_get_double(value, &dres); + (void) snprintf((char *)buf, sizeof (buf), "%f", dres); + break; + case POC_BOOL: + (void) pool_value_get_bool(value, &bres); + if (bres == PO_FALSE) + (void) snprintf((char *)buf, sizeof (buf), + "false"); + else + (void) snprintf((char *)buf, sizeof (buf), + "true"); + break; + case POC_STRING: + (void) pool_value_get_string(value, &sres); + if (sres != NULL) + (void) snprintf((char *)buf, sizeof (buf), "%s", + sres); + break; + case POC_INVAL: + default: + break; + } + if (xmlSetProp(node, name, buf) == NULL) { + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + return (PO_SUCCESS); +} + +/* + * Get the data type for a property name from the element node. The data + * type is returned and the value of the property updates the supplied value + * pointer. The user is responsible for freeing the memory associated with + * a string. + */ +static pool_value_class_t +pool_xml_get_prop(xmlNodePtr node, xmlChar *name, pool_value_t *value) +{ + pool_value_class_t data_type; + xmlChar *data, *node_data; + xmlXPathContextPtr ctx; + xmlXPathObjectPtr path; + char buf[MAX_PROP_SIZE]; + int64_t uval; + int64_t ival; + + /* use xpath to find the node with the appropriate value for name */ + (void) snprintf(buf, sizeof (buf), "property[@name=\"%s\"]", name); + if ((ctx = xmlXPathNewContext(node->doc)) == NULL) { + pool_seterror(POE_BADPARAM); + return (POC_INVAL); + } + ctx->node = node; + path = xmlXPathEval(BAD_CAST buf, ctx); + + if (path && (path->type == XPATH_NODESET) && + (path->nodesetval->nodeNr == 1)) { + int i; + if (xmlHasProp(path->nodesetval->nodeTab[0], + BAD_CAST c_type) == NULL) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + pool_seterror(POE_INVALID_CONF); + return (POC_INVAL); + } + /* type is a string representation of the type */ + data = xmlGetProp(path->nodesetval->nodeTab[0], + BAD_CAST c_type); + node_data = xmlNodeGetContent(path->nodesetval->nodeTab[0]); + data_type = POC_INVAL; + for (i = 0; i < (sizeof (data_type_tags) / + sizeof (data_type_tags[0])); i++) { + if (strcmp((char *)data, data_type_tags[i]) == 0) { + data_type = i; + break; + } + } + switch (data_type) { + case POC_UINT: + errno = 0; + uval = strtoull((char *)node_data, NULL, 0); + if (errno != 0) + data_type = POC_INVAL; + else + pool_value_set_uint64(value, uval); + break; + case POC_INT: + errno = 0; + ival = strtoll((char *)node_data, NULL, 0); + if (errno != 0) + data_type = POC_INVAL; + else + pool_value_set_int64(value, ival); + break; + case POC_DOUBLE: + pool_value_set_double(value, + atof((const char *)node_data)); + break; + case POC_BOOL: + if (strcmp((const char *)node_data, "true") + == 0) + pool_value_set_bool(value, PO_TRUE); + else + pool_value_set_bool(value, PO_FALSE); + break; + case POC_STRING: + if (pool_value_set_string(value, + (const char *)node_data) != PO_SUCCESS) { + data_type = POC_INVAL; + break; + } + break; + case POC_INVAL: + default: + break; + } + xmlFree(data); + xmlFree(node_data); + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + return (data_type); + } else { /* No property exists, clean up and return */ + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + pool_seterror(POE_BADPARAM); + return (POC_INVAL); + } +} + +/* + * Set the data type for a property name from the element node. The + * supplied value is used to update the designated name using the data + * type supplied. + */ +int +pool_xml_set_prop(xmlNodePtr node, xmlChar *name, const pool_value_t *value) +{ +/* First check if we have a property with this name (and type???). */ + xmlXPathContextPtr ctx; + xmlXPathObjectPtr path; + xmlChar buf[MAX_PROP_SIZE]; + xmlNodePtr element; + uint64_t ures; + int64_t ires; + uchar_t bres; + double dres; + const char *sres; + + /* use xpath to find the node with the appropriate value for name */ + (void) snprintf((char *)buf, sizeof (buf), "property[@name=\"%s\"]", + name); + if ((ctx = xmlXPathNewContext(node->doc)) == NULL) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + ctx->node = node; + path = xmlXPathEval(buf, ctx); + if (path == NULL || path->type != XPATH_NODESET) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } else { + if (path->nodesetval->nodeNr == 0) + element = property_create + (node, (const char *)name, value->pv_class); + else if (path->nodesetval->nodeNr == 1) { + int i; + xmlChar *data; + + element = path->nodesetval->nodeTab[0]; + if (xmlHasProp(element, BAD_CAST c_type) == NULL) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + data = xmlGetProp(element, BAD_CAST c_type); + for (i = 0; i < (sizeof (data_type_tags) / + sizeof (data_type_tags[0])); i++) + if (strcmp((char *)data, data_type_tags[i]) + == 0) { + break; + } + xmlFree(data); + if (value->pv_class != i) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + } else { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + } + + switch (value->pv_class) { + case POC_UINT: + (void) pool_value_get_uint64(value, &ures); + (void) snprintf((char *)buf, sizeof (buf), "%llu", + (u_longlong_t)ures); + break; + case POC_INT: + (void) pool_value_get_int64(value, &ires); + (void) snprintf((char *)buf, sizeof (buf), "%lld", + (longlong_t)ires); + break; + case POC_DOUBLE: + (void) pool_value_get_double(value, &dres); + (void) snprintf((char *)buf, sizeof (buf), "%f", dres); + break; + case POC_BOOL: + (void) pool_value_get_bool(value, &bres); + if (bres == PO_FALSE) + (void) snprintf((char *)buf, sizeof (buf), + "false"); + else + (void) snprintf((char *)buf, sizeof (buf), + "true"); + break; + case POC_STRING: + (void) pool_value_get_string(value, &sres); + (void) snprintf((char *)buf, sizeof (buf), "%s", sres); + break; + case POC_INVAL: + default: + break; + } + xmlNodeSetContent(element, buf); + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + return (PO_SUCCESS); +} + +/* + * Return a NULL terminated array of pool_value_t which represents all + * of the properties stored for an element + * + * Return NULL on failure. It is the caller's responsibility to free + * the returned array of values. + */ +pool_value_t ** +pool_xml_get_properties(const pool_elem_t *pe, uint_t *nprops) +{ + pool_value_t **result; + pool_xml_elem_t *pxe = (pool_xml_elem_t *)pe; + int i, j; + pool_conf_t *conf = TO_CONF(pe); + xmlElementPtr elemDTD; + xmlAttributePtr attr; + xmlXPathContextPtr ctx; + xmlXPathObjectPtr path; + char_buf_t *cb = NULL; + + *nprops = 0; + + elemDTD = xmlGetDtdElementDesc(pxe->pxe_node->doc->extSubset, + pxe->pxe_node->name); + for (attr = elemDTD->attributes; attr != NULL; attr = attr->nexth) { + if (strcmp((const char *)attr->name, c_a_dtype) != 0 || + strcmp((const char *)attr->name, c_type) != 0) + (*nprops)++; + } + if ((ctx = xmlXPathNewContext( + ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc)) == NULL) { + pool_seterror(POE_BADPARAM); + return (NULL); + } + ctx->node = pxe->pxe_node; + path = xmlXPathEval(BAD_CAST "property", ctx); + + if (path != NULL && path->type == XPATH_NODESET && + path->nodesetval != NULL) + (*nprops) += path->nodesetval->nodeNr; + + if ((result = calloc(*nprops + 1, sizeof (pool_value_t *))) == NULL) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + pool_seterror(POE_SYSTEM); + return (NULL); + } + if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + free(result); + return (NULL); + } + /* + * Now store our attributes and properties in result + */ + for (i = 0, attr = elemDTD->attributes; attr != NULL; + attr = attr->nexth, i++) { + if (strcmp((const char *)attr->name, c_a_dtype) == 0 || + strcmp((const char *)attr->name, c_type) == 0) { + i--; + continue; + } + result[i] = pool_value_alloc(); + if (pool_xml_get_attr(pxe->pxe_node, + BAD_CAST attr->name, result[i]) == POC_INVAL) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + while (i-- >= 0) + pool_value_free(result[i]); + free(result); + free_char_buf(cb); + return (NULL); + } + if (strcmp((const char *)attr->name, c_type) != 0) { + if (set_char_buf(cb, "%s.%s", + pool_elem_class_string(pe), attr->name) != + PO_SUCCESS) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + while (i-- >= 0) + pool_value_free(result[i]); + free(result); + free_char_buf(cb); + return (NULL); + } + if (pool_value_set_name(result[i], cb->cb_buf) != + PO_SUCCESS) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + while (i-- >= 0) + pool_value_free(result[i]); + free(result); + free_char_buf(cb); + return (NULL); + } + } else { + if (pool_value_set_name(result[i], + (const char *)attr->name) != PO_SUCCESS) { + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + while (i-- >= 0) + pool_value_free(result[i]); + free(result); + free_char_buf(cb); + return (NULL); + } + } + } + free_char_buf(cb); + for (j = 0; j < path->nodesetval->nodeNr; j++, i++) { + xmlChar *name = xmlGetProp(path->nodesetval->nodeTab[j], + BAD_CAST c_name); + + result[i] = pool_value_alloc(); + + if (pool_xml_get_prop(pxe->pxe_node, name, result[i]) == + POC_INVAL) { + xmlFree(name); + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + while (i-- >= 0) + pool_value_free(result[i]); + free(result); + return (NULL); + } + if (pool_value_set_name(result[i], (const char *)name) != + PO_SUCCESS) { + xmlFree(name); + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + while (i-- >= 0) + pool_value_free(result[i]); + free(result); + return (NULL); + } + xmlFree(name); + } + xmlXPathFreeObject(path); + xmlXPathFreeContext(ctx); + return (result); +} + +/* + * Store a pointer to one of our data types in the _private member of each + * XML data node contained within the passed node. Note this function is + * recursive and so all sub-nodes are also shadowed. Only shadow the nodes + * which we are interested in, i.e. system, pool, res and comp + */ +static int +create_shadow(xmlNodePtr node) +{ + xmlNodePtr sib; + int ret = PO_SUCCESS; + /* Create a data structure of the appropriate type */ + + if (0 == (xmlStrcmp(node->name, + BAD_CAST element_class_tags[PEC_SYSTEM]))) { + ret = pool_xml_elem_wrap(node, PEC_SYSTEM, PREC_INVALID, + PCEC_INVALID); + } else if (0 == (xmlStrcmp(node->name, + BAD_CAST element_class_tags[PEC_POOL]))) { + ret = pool_xml_elem_wrap(node, PEC_POOL, PREC_INVALID, + PCEC_INVALID); + } else if (0 == (xmlStrcmp(node->name, + BAD_CAST element_class_tags[PEC_RES_COMP]))) { + xmlChar *data; + pool_resource_elem_class_t res_class; + data = xmlGetProp(node, BAD_CAST c_type); + + res_class = pool_resource_elem_class_from_string((char *)data); + xmlFree(data); + ret = pool_xml_elem_wrap(node, PEC_RES_COMP, res_class, + PCEC_INVALID); + } else if (0 == (xmlStrcmp(node->name, + BAD_CAST element_class_tags[PEC_RES_AGG]))) { + xmlChar *data; + pool_resource_elem_class_t res_class; + data = xmlGetProp(node, BAD_CAST c_type); + + res_class = pool_resource_elem_class_from_string((char *)data); + xmlFree(data); + ret = pool_xml_elem_wrap(node, PEC_RES_AGG, res_class, + PCEC_INVALID); + } else if (0 == (xmlStrcmp(node->name, + BAD_CAST element_class_tags[PEC_COMP]))) { + xmlChar *data; + pool_component_elem_class_t comp_class; + data = xmlGetProp(node, BAD_CAST c_type); + + comp_class = pool_component_elem_class_from_string( + (char *)data); + xmlFree(data); + ret = pool_xml_elem_wrap(node, PEC_COMP, PREC_INVALID, + comp_class); + } + /* Have to shadow all children and all siblings */ + for (sib = node->children; sib != NULL; sib = sib->next) { + if ((ret = create_shadow(sib)) != PO_SUCCESS) + break; + } + return (ret); +} + + +/* + * XML Data access and navigation APIs + */ + +/* + * Close the configuration. There are a few steps to closing a configuration: + * - Unlock the backing file (if there is one) + * - Close the file (if there is one) + * - Free the shadow memory }Done in pool_xml_free_doc + * - Free the document } + * - Free the data provider for this configuration + * - Free the configuration location specifier + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_close(pool_conf_t *conf) +{ + pool_xml_connection_t *pxc = (pool_xml_connection_t *)conf->pc_prov; + int ret = PO_SUCCESS; + + if (pxc->pxc_file != NULL) { + /* Close (and implicitly) unlock the file */ + if (fclose(pxc->pxc_file) != 0) { + pool_seterror(POE_SYSTEM); + ret = PO_FAIL; + } + pxc->pxc_file = NULL; + } + /* Close the xml specific parts */ + (void) pool_xml_free_doc(conf); + pool_xml_connection_free((pool_xml_connection_t *)conf->pc_prov); + return (ret); +} + +/* + * Remove the configuration from the backing store. In XML terms delete + * the file backing the configuration. You need a copy of the location + * since the pool_conf_close function, frees the location. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_remove(pool_conf_t *conf) +{ + if (pool_conf_location(conf) != NULL) { + /* First unlink the file, to prevent races on open */ + if (unlink(pool_conf_location(conf)) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* Now close the configuration */ + (void) pool_conf_close(conf); + return (PO_SUCCESS); + } + return (PO_FAIL); +} + +/* + * Validate the configuration. There are three levels of validation, loose, + * strict and runtime. In this, XML, implementation, loose is mapped to XML + * validation, strict implements additional application level validation + * checks, e.g. all pools must have unique names, runtime ensures that this + * configuration would instantiate on the current system. + * + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_validate(const pool_conf_t *conf, pool_valid_level_t level) +{ + pool_xml_connection_t *pxc = (pool_xml_connection_t *)conf->pc_prov; + xmlValidCtxtPtr cvp; + + if ((cvp = xmlNewValidCtxt()) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + cvp->error = pool_error_func; + cvp->warning = pool_error_func; + + if (xmlValidateDocument(cvp, pxc->pxc_doc) == 0) { + xmlFreeValidCtxt(cvp); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + xmlFreeValidCtxt(cvp); + + if (level >= POV_RUNTIME) { + /* + * Note: This is resource specific. + */ + return (((pool_validate_resource(conf, "pset", c_min_prop, 0) == + PO_SUCCESS) && + (pool_validate_resource(conf, "pset", c_max_prop, 0) == + PO_SUCCESS)) ? PO_SUCCESS : PO_FAIL); + } + return (PO_SUCCESS); +} + +/* + * Commit the configuration to the backing store. In XML terms this means + * write the changes to the backing file. Read the comments below for details + * on exactly how this operation is performed. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_commit(pool_conf_t *conf) +{ + pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov; + xmlOutputBufferPtr buf; + + /* + * Ensure that the configuration file has no contents + */ + if (fseek(prov->pxc_file, 0, SEEK_SET) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + + if (ftruncate(fileno(prov->pxc_file), 0) == -1) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* + * Create an XML output buffer and write out the contents of the + * configuration to the file. + */ + if ((buf = xmlOutputBufferCreateFile(prov->pxc_file, NULL)) == NULL) { + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + + if (xmlSaveFormatFileTo(buf, prov->pxc_doc, NULL, 1) == -1) { + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + + return (PO_SUCCESS); +} + +/* + * Export the configuration in the specified format to the specified location. + * The only format implemented now is the native format, which saves the + * active configuration to the supplied location. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_export(const pool_conf_t *conf, const char *location, + pool_export_format_t fmt) +{ + int ret; + + switch (fmt) { + case POX_NATIVE: + ret = xmlSaveFormatFile(location, + ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc, + 1); + if (ret == -1) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } else + return (PO_SUCCESS); + + default: + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } +} + +/* + * Discard the configuration and restore the configuration to the values + * specified in the configuration location. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_rollback(pool_conf_t *conf) +{ + pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov; + + /* Rollback the file pointer ready for the reparse */ + if (fseek(prov->pxc_file, 0, SEEK_SET) != 0) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* Reparse the document */ + /* In XML terms this means, discard and reparse the document */ + (void) pool_xml_free_doc(conf); + if (pool_xml_parse_document(conf) == PO_FAIL) + return (PO_FAIL); + return (PO_SUCCESS); +} + +/* + * Allocate a new pool_elem_t in the supplied configuration of the specified + * class. + * Returns element pointer/NULL + */ +static void +pool_xml_elem_init(pool_conf_t *conf, pool_xml_elem_t *elem, + pool_elem_class_t class, pool_resource_elem_class_t res_class, + pool_component_elem_class_t comp_class) +{ + pool_elem_t *pe = TO_ELEM(elem); + pe->pe_conf = conf; + pe->pe_class = class; + pe->pe_resource_class = res_class; + pe->pe_component_class = comp_class; + /* Set up the function pointers for element manipulation */ + pe->pe_get_prop = pool_xml_get_property; + pe->pe_put_prop = pool_xml_put_property; + pe->pe_rm_prop = pool_xml_rm_property; + pe->pe_get_props = pool_xml_get_properties; + pe->pe_remove = pool_xml_elem_remove; + pe->pe_get_container = pool_xml_get_container; + pe->pe_set_container = pool_xml_set_container; + /* + * Specific initialisation for different types of element + */ + if (class == PEC_POOL) { + pool_xml_pool_t *pp = (pool_xml_pool_t *)elem; + pp->pp_associate = pool_xml_pool_associate; + pp->pp_dissociate = pool_xml_pool_dissociate; + } + if (class == PEC_RES_COMP || class == PEC_RES_AGG) { + pool_xml_resource_t *pr = (pool_xml_resource_t *)elem; + pr->pr_is_system = pool_xml_resource_is_system; + pr->pr_can_associate = pool_xml_resource_can_associate; + } +} + +/* + * "Wrap" a suplied XML node with a pool_elem_t sub-type of the supplied + * class. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_elem_wrap(xmlNodePtr node, pool_elem_class_t class, + pool_resource_elem_class_t res_class, + pool_component_elem_class_t comp_class) +{ + pool_conf_t *conf = node->doc->_private; + pool_xml_elem_t *elem; + /* Need to do some messing about to support SubTypes */ + switch (class) { + case PEC_SYSTEM: + if ((elem = malloc(sizeof (pool_xml_system_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memset(elem, 0, sizeof (pool_xml_system_t)); + break; + case PEC_POOL: + if ((elem = malloc(sizeof (pool_xml_pool_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memset(elem, 0, sizeof (pool_xml_pool_t)); + break; + case PEC_RES_COMP: + case PEC_RES_AGG: + if ((elem = malloc(sizeof (pool_xml_resource_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memset(elem, 0, sizeof (pool_xml_resource_t)); + break; + case PEC_COMP: + if ((elem = malloc(sizeof (pool_xml_component_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memset(elem, 0, sizeof (pool_xml_component_t)); + break; + } + pool_xml_elem_init(conf, elem, class, res_class, comp_class); + node->_private = elem; + elem->pxe_node = node; + return (PO_SUCCESS); +} + +/* + * Associate a pool to the default resource for the supplied resource + * type. + */ +int +pool_assoc_default_resource_type(pool_t *pool, pool_resource_elem_class_t type) +{ + pool_value_t *props[] = { NULL, NULL, NULL }; + uint_t rl_size; + pool_resource_t **rsl; + pool_conf_t *conf = TO_ELEM(pool)->pe_conf; + char_buf_t *cb = NULL; + pool_value_t val0 = POOL_VALUE_INITIALIZER; + pool_value_t val1 = POOL_VALUE_INITIALIZER; + + props[0] = &val0; + props[1] = &val1; + + + if (pool_value_set_string(props[0], pool_resource_type_string(type)) != + PO_SUCCESS || + pool_value_set_name(props[0], c_type) != PO_SUCCESS) { + return (PO_FAIL); + } + + if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) { + return (PO_FAIL); + } + + if (set_char_buf(cb, "%s.default", + pool_resource_type_string(type)) != + PO_SUCCESS) { + free_char_buf(cb); + return (PO_FAIL); + } + if (pool_value_set_name(props[1], cb->cb_buf) != PO_SUCCESS) { + free_char_buf(cb); + return (PO_FAIL); + } + pool_value_set_bool(props[1], PO_TRUE); + free_char_buf(cb); + + if ((rsl = pool_query_resources(conf, &rl_size, props)) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + + /* + * One default resource set per type + */ + if (rl_size != 1) { + free(rsl); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + if (pool_associate(conf, pool, rsl[0]) < 0) { + free(rsl); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + free(rsl); + return (PO_SUCCESS); +} + +/* + * Create an XML node in the supplied configuration with a pool_elem_t + * sub-type of the supplied class. + * Returns pool_elem_t pointer/NULL + */ +static pool_elem_t * +pool_xml_elem_create(pool_conf_t *conf, pool_elem_class_t class, + pool_resource_elem_class_t res_class, + pool_component_elem_class_t comp_class) +{ + /* In XML terms, create an element of the appropriate class */ + pool_xml_elem_t *elem; + pool_elem_t *parent; + pool_system_t *parent_system; + + if (class == PEC_INVALID) { + pool_seterror(POE_BADPARAM); + return (NULL); + } + + /* Now create the XML component and add to it's parent */ + /* + * If we know the class of an element, we know it's parent. + * PEC_POOL, the parent must be the system node + * PEC_RES, treat as pool. + * PEC_COMP, we don't know the parent, leave this up to the + * create_comp function. + */ + /* Since we know the subtype we can create and populate the sub-type */ + switch (class) { + case PEC_POOL: + if ((parent_system = pool_conf_system(conf)) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (NULL); + } + if ((parent = pool_system_elem(parent_system)) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (NULL); + } + if ((elem = malloc(sizeof (pool_xml_system_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + (void) memset(elem, 0, sizeof (pool_xml_system_t)); + if ((elem->pxe_node = node_create_with_id( + ((pool_xml_elem_t *)parent)->pxe_node, + BAD_CAST element_class_tags[class])) == NULL) { + pool_seterror(POE_DATASTORE); + (void) pool_xml_elem_remove((pool_elem_t *)elem); + return (NULL); + } + break; + case PEC_RES_COMP: + case PEC_RES_AGG: + if ((parent_system = pool_conf_system(conf)) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (NULL); + } + if ((parent = pool_system_elem(parent_system)) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (NULL); + } + if ((elem = malloc(sizeof (pool_xml_resource_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + (void) memset(elem, 0, sizeof (pool_xml_resource_t)); + if ((elem->pxe_node = node_create_with_id + (((pool_xml_elem_t *)parent)->pxe_node, + BAD_CAST element_class_tags[class])) == NULL) { + pool_seterror(POE_DATASTORE); + (void) pool_xml_elem_remove((pool_elem_t *)elem); + return (NULL); + } + break; + case PEC_COMP: + if ((elem = malloc(sizeof (pool_xml_component_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + (void) memset(elem, 0, sizeof (pool_xml_component_t)); + if ((elem->pxe_node = node_create(NULL, + BAD_CAST element_class_tags[class])) == NULL) { + pool_seterror(POE_DATASTORE); + (void) pool_xml_elem_remove((pool_elem_t *)elem); + return (NULL); + } + break; + default: + pool_seterror(POE_BADPARAM); + return (NULL); + } + pool_xml_elem_init(conf, elem, class, res_class, comp_class); + elem->pxe_node->_private = elem; + if (class == PEC_RES_COMP || class == PEC_RES_AGG || + class == PEC_COMP) { + /* + * Put the type and an invalid sys_id on the node. + */ + if (xmlSetProp(elem->pxe_node, BAD_CAST c_sys_prop, + BAD_CAST POOL_SYSID_BAD_STRING) == NULL) { + pool_seterror(POE_DATASTORE); + (void) pool_xml_elem_remove((pool_elem_t *)elem); + return (NULL); + } + if (xmlSetProp(elem->pxe_node, BAD_CAST c_type, + BAD_CAST pool_elem_class_string( + (pool_elem_t *)elem)) == NULL) { + pool_seterror(POE_DATASTORE); + (void) pool_xml_elem_remove((pool_elem_t *)elem); + return (NULL); + } + } + if (class == PEC_POOL) { + /* + * Note: This is resource specific. + */ + if (pool_assoc_default_resource_type(pool_elem_pool( + (pool_elem_t *)elem), PREC_PSET) == PO_FAIL) { + (void) pool_xml_elem_remove((pool_elem_t *)elem); + return (NULL); + } + } + return ((pool_elem_t *)elem); +} + +/* + * Allocate a data provider for the supplied configuration and optionally + * discover resources. + * The data provider is the cross over point from the "abstract" configuration + * functions into the data representation specific manipulation routines. + * This function sets up all the required pointers to create an XML aware + * data provider. + * Returns PO_SUCCESS/PO_FAIL + */ +int +pool_xml_connection_alloc(pool_conf_t *conf, int oflags) +{ + pool_xml_connection_t *prov; + + xml_init(); + if ((prov = malloc(sizeof (pool_xml_connection_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + (void) memset(prov, 0, sizeof (pool_xml_connection_t)); + /* + * Initialise data members + */ + prov->pc_name = strdup("LIBXML 2.4.0"); + prov->pc_store_type = XML_DATA_STORE; + prov->pc_oflags = oflags; + /* + * Initialise function pointers + */ + prov->pc_close = pool_xml_close; + prov->pc_validate = pool_xml_validate; + prov->pc_commit = pool_xml_commit; + prov->pc_export = pool_xml_export; + prov->pc_rollback = pool_xml_rollback; + prov->pc_exec_query = pool_xml_exec_query; + prov->pc_elem_create = pool_xml_elem_create; + prov->pc_remove = pool_xml_remove; + prov->pc_res_xfer = pool_xml_res_transfer; + prov->pc_res_xxfer = pool_xml_res_xtransfer; + /* + * End of common initialisation + */ + /* + * Associate the provider to it's configuration + */ + conf->pc_prov = (pool_connection_t *)prov; + /* + * At this point the configuration provider has been initialized, + * mark the configuration as valid so that the various routines + * which rely on a valid configuration will work correctly. + */ + conf->pc_state = POF_VALID; + + if ((oflags & PO_CREAT) != 0) { + pool_conf_t *dyn; + + if ((dyn = pool_conf_alloc()) == NULL) + return (PO_FAIL); + + if (pool_conf_open(dyn, pool_dynamic_location(), + PO_RDONLY) != PO_SUCCESS) { + pool_conf_free(dyn); + return (PO_FAIL); + } + + if (pool_conf_export(dyn, conf->pc_location, + POX_NATIVE) != PO_SUCCESS) { + (void) pool_conf_close(dyn); + pool_conf_free(dyn); + return (PO_FAIL); + } + (void) pool_conf_close(dyn); + pool_conf_free(dyn); + } + + if (pool_xml_open_file(conf) == PO_FAIL) { + (void) pool_xml_close(conf); + return (PO_FAIL); + } + + return (PO_SUCCESS); +} + +/* + * Free the resources for an XML data provider. + */ +static void +pool_xml_connection_free(pool_xml_connection_t *prov) +{ + free((void *)prov->pc_name); + free(prov); +} + +/* + * Allocate a result set. The Result Set stores the result of an XPath + * query along with the parameters used to create the result set (for + * debugging purposes). + * Returns pool_xml_result_set_t pointer/NULL + */ +static pool_xml_result_set_t * +pool_xml_result_set_alloc(const pool_conf_t *conf) +{ + pool_xml_result_set_t *rs; + + if ((rs = malloc(sizeof (pool_xml_result_set_t))) == NULL) { + pool_seterror(POE_SYSTEM); + return (NULL); + } + (void) memset(rs, 0, sizeof (pool_xml_result_set_t)); + rs->prs_conf = conf; + rs->prs_index = -1; + rs->prs_active = PO_TRUE; + /* Fix up the result set accessor functions to the xml specfic ones */ + rs->prs_next = pool_xml_rs_next; + rs->prs_prev = pool_xml_rs_prev; + rs->prs_first = pool_xml_rs_first; + rs->prs_last = pool_xml_rs_last; + rs->prs_get_index = pool_xml_rs_get_index; + rs->prs_set_index = pool_xml_rs_set_index; + rs->prs_close = pool_xml_rs_close; + rs->prs_count = pool_xml_rs_count; + return (rs); +} + +/* + * Free a result set. Ensure that the resources are all released at this point. + */ +static void +pool_xml_result_set_free(pool_xml_result_set_t *rs) +{ + if (rs->pxr_path != NULL) + xmlXPathFreeObject(rs->pxr_path); + if (rs->pxr_ctx != NULL) + xmlXPathFreeContext(rs->pxr_ctx); + free(rs); +} + +/* + * Transfer size from one resource to another. + * Returns PO_SUCCESS/PO_FAIL + */ +/* ARGSUSED */ +int +pool_xml_res_transfer(pool_resource_t *src, pool_resource_t *tgt, uint64_t size) +{ + return (PO_SUCCESS); +} + +/* + * Transfer components rl from one resource to another. + * Returns PO_SUCCESS/PO_FAIL + */ +/* ARGSUSED */ +int +pool_xml_res_xtransfer(pool_resource_t *src, pool_resource_t *tgt, + pool_component_t **rl) { + int i; + + /* + * Walk the Result Set and move the resource components + */ + for (i = 0; rl[i] != NULL; i++) { + if (pool_set_container(TO_ELEM(tgt), TO_ELEM(rl[i])) == + PO_FAIL) { + return (PO_FAIL); + } + } + return (PO_SUCCESS); +} + +/* + * Return the next element in a result set. + * Returns pool_elem_t pointer/NULL + */ +static pool_elem_t * +pool_xml_rs_next(pool_result_set_t *set) +{ + pool_elem_t *next; + /* Since I know this is an XML result set */ + pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set; + + /* Update the context node */ + if (xset->prs_index == xset->pxr_path->nodesetval->nodeNr - 1) + return (NULL); + next = + xset->pxr_path->nodesetval->nodeTab[++xset->prs_index]->_private; + return (next); +} + +/* + * Return the previous element in a result set. + * Returns pool_elem_t pointer/NULL + */ +static pool_elem_t * +pool_xml_rs_prev(pool_result_set_t *set) +{ + pool_elem_t *prev; + /* Since I know this is an XML result set */ + pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set; + + /* Update the context node */ + if (xset->prs_index < 0) + return (NULL); + prev = + xset->pxr_path->nodesetval->nodeTab[xset->prs_index--]->_private; + return (prev); +} + +/* + * Sets the current index in a result set. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_rs_set_index(pool_result_set_t *set, int index) +{ + /* Since I know this is an XML result set */ + pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set; + + if (index < 0 || index >= xset->pxr_path->nodesetval->nodeNr) { + pool_seterror(POE_BADPARAM); + return (PO_FAIL); + } + xset->prs_index = index; + return (PO_SUCCESS); +} + +/* + * Return the current index in a result set. + * Returns current index + */ +static int +pool_xml_rs_get_index(pool_result_set_t *set) +{ + /* Since I know this is an XML result set */ + pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set; + + return (xset->prs_index); +} + +/* + * Return the first element in a result set. + * Returns pool_elem_t pointer/NULL + */ +static pool_elem_t * +pool_xml_rs_first(pool_result_set_t *set) +{ + /* Since I know this is an XML result set */ + pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set; + + /* Update the context node */ + return (xset->pxr_path->nodesetval->nodeTab[0]->_private); +} + +/* + * Return the last element in a result set. + * Returns pool_elem_t pointer/NULL + */ +static pool_elem_t * +pool_xml_rs_last(pool_result_set_t *set) +{ + /* Since I know this is an XML result set */ + pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set; + + /* Update the context node */ + return (xset->pxr_path->nodesetval-> + nodeTab[xset->pxr_path->nodesetval->nodeNr-1]->_private); +} + +/* + * Return the number of results in a result set. + * Returns result count + */ +static int +pool_xml_rs_count(pool_result_set_t *set) +{ + pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set; + + return (xset->pxr_path->nodesetval->nodeNr); +} + + +/* + * Close a result set. Remove this result set from the list of results and + * free the resources + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_rs_close(pool_result_set_t *set) +{ + pool_xml_result_set_t *xset = (pool_xml_result_set_t *)set; + + pool_xml_result_set_free(xset); + return (PO_SUCCESS); +} + +/* + * Set the container for a node. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_set_container(pool_elem_t *pp, pool_elem_t *pc) +{ + pool_xml_elem_t *pxp; + pool_xml_elem_t *pxc; + xmlNodePtr parent; + + pxp = (pool_xml_elem_t *)pp; + pxc = (pool_xml_elem_t *)pc; + parent = pxc->pxe_node->parent; + + xmlUnlinkNode(pxc->pxe_node); + if (xmlAddChild(pxp->pxe_node, pxc->pxe_node) == NULL) { + /* Try to move back */ + (void) xmlAddChild(parent, pxc->pxe_node); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + pc->pe_conf = pp->pe_conf; + return (PO_SUCCESS); +} +/* + * Get the container for a node. + * Returns Container/NULL + */ +static pool_elem_t * +pool_xml_get_container(const pool_elem_t *pc) +{ + pool_xml_elem_t *pxc = (pool_xml_elem_t *)pc; + + return ((pool_elem_t *)pxc->pxe_node->parent->_private); +} + +/* + * Note: This function is resource specific, needs extending for other + * resource types. + */ +int +pool_xml_resource_is_system(const pool_resource_t *pr) +{ + switch (pool_resource_elem_class(TO_ELEM(pr))) { + case PREC_PSET: + return (PSID_IS_SYSSET( + elem_get_sysid(TO_ELEM(pr)))); + default: + return (PO_FALSE); + } +} + +/* + * Note: This function is resource specific, needs extending for other + * resource types. + */ +int +pool_xml_resource_can_associate(const pool_resource_t *pr) +{ + switch (pool_resource_elem_class(TO_ELEM(pr))) { + case PREC_PSET: + return (PO_TRUE); + default: + return (PO_FALSE); + } +} + +/* + * Note: This function is resource specific. It must be extended to support + * multiple resource types. + */ +int +pool_xml_pool_associate(pool_t *pool, const pool_resource_t *pr) +{ + pool_value_t val = POOL_VALUE_INITIALIZER; + + if (pool_xml_get_property(TO_ELEM(pr), + "pset.ref_id", &val) != POC_STRING) + return (PO_FAIL); + if (pool_xml_put_property(TO_ELEM(pool), "pool.res", &val) != + PO_SUCCESS) + return (PO_FAIL); + return (PO_SUCCESS); +} + +/* + * pool_xml_pool_dissociate() simply finds the default resource for + * the type of resource being dissociated and then calls + * pool_xml_pool_associate() to associate to the default resource. + */ +int +pool_xml_pool_dissociate(pool_t *pool, const pool_resource_t *pr) +{ + const pool_resource_t *default_res; + + if ((default_res = get_default_resource(pr)) == NULL) + return (PO_FAIL); + if (default_res == pr) + return (PO_SUCCESS); + return (pool_xml_pool_associate(pool, default_res)); +} + +/* + * pool_xml_open_file() opens a file for a configuration. This establishes + * the locks required to ensure data integrity when manipulating a + * configuration. + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_xml_open_file(pool_conf_t *conf) +{ + struct flock lock; + struct stat s; + + pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov; + + /* + * Always close the pxc_file in case there was a previously failed open + */ + if (prov->pxc_file != NULL) { + (void) fclose(prov->pxc_file); + prov->pxc_file = NULL; + } + + /* + * Check that the DTD required for this operation is present. + * If it isn't fail + */ + if (dtd_exists(dtd_location) == PO_FALSE) { + pool_seterror(POE_DATASTORE); + return (PO_FAIL); + } + + if ((prov->pc_oflags & PO_RDWR) != 0) + prov->pxc_file = fopen(conf->pc_location, "r+F"); + else /* Assume opening PO_RDONLY */ + prov->pxc_file = fopen(conf->pc_location, "rF"); + + if (prov->pxc_file == NULL) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + + /* + * Setup the lock for the file + */ + lock.l_type = (prov->pc_oflags & PO_RDWR) ? F_WRLCK : F_RDLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + if (fcntl(fileno(prov->pxc_file), F_SETLKW, &lock) == -1) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* + * Check to see if the document was removed whilst waiting for + * the lock. If it was return an error. + */ + if (stat(conf->pc_location, &s) == -1) { + (void) fclose(prov->pxc_file); + prov->pxc_file = NULL; + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + /* Parse the document */ + if (pool_xml_parse_document(conf) != PO_SUCCESS) + return (PO_FAIL); + return (PO_SUCCESS); +} + +/* + * Try to work out if an element contains an attribute of the supplied name. + * Search the internal subset first and then the external subset. + * Return PO_TRUE if there is an attribute of that name declared for that + * element. + */ +int +pool_is_xml_attr(xmlDocPtr doc, const char *elem, const char *attr) +{ + xmlDtdPtr internal = xmlGetIntSubset(doc); + xmlDtdPtr external = doc->extSubset; + + if (xmlGetDtdAttrDesc(internal, BAD_CAST elem, BAD_CAST attr) == NULL) + if (xmlGetDtdAttrDesc(external, + BAD_CAST elem, BAD_CAST attr) == NULL) + return (PO_FALSE); + return (PO_TRUE); +} + +/* + * Execute the specified query using XPath. This complex function relies on + * a couple of helpers to build up an XPath query, pool_build_xpath_buf in + * particular. + * conf - the pool configuration being manipulated + * src - the root of the search, if NULL that means whole document + * src_attr - if supplied means an IDREF(S) search on this attribute + * classes - target classes + * props - target properties + * Returns pool_result_set_t pointer/NULL + */ +pool_result_set_t * +pool_xml_exec_query(const pool_conf_t *conf, const pool_elem_t *src, + const char *src_attr, pool_elem_class_t classes, pool_value_t **props) +{ + char *buf = NULL; + char_buf_t *cb = NULL; + pool_xml_result_set_t *rs; + pool_xml_elem_t *pxe = (pool_xml_elem_t *)src; + pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov; + + if ((cb = alloc_char_buf(CB_DEFAULT_LEN)) == NULL) + return (NULL); + + /* + * Prior to building up the complex XPath query, check to see if + * src_attr is an IDREF(S). If it is use the IDREF(S) information + * to generate the query rather than the other data + */ + if (src_attr != NULL) { + char *tok; + char *lasts; + char *or = ""; + xmlChar *id; + + /* + * Check the arguments for consistency + */ + if (pool_is_xml_attr(prov->pxc_doc, + element_class_tags[src->pe_class], src_attr) != PO_TRUE) { + free_char_buf(cb); + pool_seterror(POE_BADPARAM); + return (NULL); + } + + if ((id = xmlGetProp(pxe->pxe_node, BAD_CAST src_attr)) + == NULL) { + free_char_buf(cb); + pool_seterror(POE_DATASTORE); + return (NULL); + } + for (tok = strtok_r((char *)id, " ", &lasts); + tok != NULL; tok = strtok_r(NULL, " ", &lasts)) { + (void) append_char_buf(cb, "%s//*[@ref_id=\"%s\"]", + or, tok); + or = " | "; + if ((classes & PEC_QRY_SYSTEM) != 0) { + if (pool_build_xpath_buf(prov, src, PEC_SYSTEM, + props, cb, PO_TRUE) == PO_FAIL) { + free_char_buf(cb); + return (NULL); + } + } + if ((classes & PEC_QRY_POOL) != 0) { + if (pool_build_xpath_buf(prov, src, PEC_POOL, + props, cb, PO_TRUE) == PO_FAIL) { + free_char_buf(cb); + return (NULL); + } + } + if ((classes & PEC_QRY_RES_COMP) != 0) { + if (pool_build_xpath_buf(prov, src, + PEC_RES_COMP, props, cb, PO_TRUE) + == PO_FAIL) { + free_char_buf(cb); + return (NULL); + } + } else if ((classes & PEC_QRY_RES_AGG) != 0) { + if (pool_build_xpath_buf(prov, src, + PEC_RES_AGG, props, cb, PO_TRUE) + == PO_FAIL) { + free_char_buf(cb); + return (NULL); + } + } + } + xmlFree(id); + } else { + /* + * Build up an XPath query using the supplied parameters. + * The basic logic is to: + * - Identify which classes are the targets of the query + * - For each class work out if the props are attributes or not + * - Build up a piece of XPath for each class + * - Combine the results into one large XPath query. + * - Execute the query. + */ + if ((classes & PEC_QRY_SYSTEM) != 0) { + if (pool_build_xpath_buf(prov, src, PEC_SYSTEM, props, + cb, PO_FALSE) == PO_FAIL) { + free_char_buf(cb); + return (NULL); + } + } + if ((classes & PEC_QRY_POOL) != 0) { + if (pool_build_xpath_buf(prov, src, PEC_POOL, props, + cb, PO_FALSE) == PO_FAIL) { + free_char_buf(cb); + return (NULL); + } + } + if ((classes & PEC_QRY_RES_COMP) != 0) { + if (pool_build_xpath_buf(prov, src, PEC_RES_COMP, props, + cb, PO_FALSE) == PO_FAIL) { + free_char_buf(cb); + return (NULL); + } + } + if ((classes & PEC_QRY_RES_AGG) != 0) { + if (pool_build_xpath_buf(prov, src, PEC_RES_AGG, props, + cb, PO_FALSE) == PO_FAIL) { + free_char_buf(cb); + return (NULL); + } + } + if ((classes & PEC_QRY_COMP) != 0) { + if (pool_build_xpath_buf(prov, src, PEC_COMP, props, + cb, PO_FALSE) == PO_FAIL) { + free_char_buf(cb); + return (NULL); + } + } + } + buf = strdup(cb->cb_buf); + free_char_buf(cb); + /* + * Have a buffer at this point, that we can use + */ + if ((rs = pool_xml_result_set_alloc(conf)) == NULL) { + free(buf); + return (NULL); + } + /* + * Set up the XPath Query + */ + if ((rs->pxr_ctx = xmlXPathNewContext( + ((pool_xml_connection_t *)conf->pc_prov)->pxc_doc)) == NULL) { + free(buf); + (void) pool_xml_rs_close((pool_result_set_t *)rs); + pool_seterror(POE_DATASTORE); + return (NULL); + } + if (src == NULL) + rs->pxr_ctx->node = xmlDocGetRootElement + (((pool_xml_connection_t *)conf->pc_prov)->pxc_doc); + else + rs->pxr_ctx->node = pxe->pxe_node; + /* + * Select + */ + rs->pxr_path = xmlXPathEval(BAD_CAST buf, rs->pxr_ctx); + free(buf); + /* + * Generate the result set and wrap the results as pool_elem_t + */ + if (rs->pxr_path->nodesetval->nodeNr == 0) + pool_seterror(POE_INVALID_SEARCH); + return ((pool_result_set_t *)rs); +} + +/* + * Build an XPath query buffer. This is complex and a little fragile, but + * I'm trying to accomplish something complex with as little code as possible. + * I wait the implementation of XMLQuery with baited breath... + * Returns PO_SUCCESS/PO_FAIL + */ +static int +pool_build_xpath_buf(pool_xml_connection_t *prov, const pool_elem_t *src, + pool_elem_class_t class, pool_value_t *props[], char_buf_t *cb, int is_ref) +{ + int i; + const char *ATTR_FMTS[] = { + "[ @%s=\"%llu\" ]", /* POC_UINT */ + "[ @%s=\"%lld\" ]", /* POC_INT */ + "[ @%s=\"%f\" ]", /* POC_DOUBLE */ + "[ @%s=\"%s\" ]", /* POC_BOOL */ + "[ @%s=\"%s\" ]", /* POC_STRING */ + }; + const char *PROP_FMTS[] = { + "[ property[@name=\"%s\"][text()=\"%llu\"] ]", /* POC_UINT */ + "[ property[@name=\"%s\"][text()=\"%lld\"] ]", /* POC_INT */ + "[ property[@name=\"%s\"][text()=\"%f\"] ]", /* POC_DOUBLE */ + "[ property[@name=\"%s\"][text()=\"%s\"] ]", /* POC_BOOL */ + "[ property[@name=\"%s\"][text()=\"%s\"] ]" /* POC_STRING */ + }; + const char **fmts; + int nprop; + const char *last_prop_name = NULL; + char *type_prefix = NULL; + int has_type = PO_FALSE; + + if (is_ref == PO_FALSE) { + if (cb->cb_buf != NULL && strlen(cb->cb_buf) > 0) + (void) append_char_buf(cb, " |"); + if (src != NULL) + (void) append_char_buf(cb, " ./"); + else + (void) append_char_buf(cb, "//"); + (void) append_char_buf(cb, element_class_tags[class]); + } + if (props == NULL || props[0] == NULL) + return (PO_SUCCESS); + for (nprop = 0; props[nprop] != NULL; nprop++) + /* Count properties */; + /* + * Sort the attributes and properties by name. + */ + qsort(props, nprop, sizeof (pool_value_t *), prop_sort); + for (i = 0; i < nprop; i++) { + int is_attr = 0; + const char *prefix; + const char *prop_name; + uint64_t uval; + int64_t ival; + double dval; + uchar_t bval; + const char *sval; + pool_value_class_t pvc; + + prop_name = pool_value_get_name(props[i]); + if ((prefix = is_a_known_prefix(class, prop_name)) != NULL) { + const char *attr_name; + /* + * Possibly an attribute. Strip off the prefix. + */ + if (strcmp(prop_name, c_type) == 0) { + has_type = PO_TRUE; + attr_name = prop_name; + } else + attr_name = prop_name + strlen(prefix) + 1; + if (pool_is_xml_attr(prov->pxc_doc, + element_class_tags[class], attr_name)) { + is_attr = 1; + prop_name = attr_name; + if (class == PEC_RES_COMP || + class == PEC_RES_AGG || + class == PEC_COMP) { + if (type_prefix != NULL) + free(type_prefix); + type_prefix = strdup(prefix); + } + } + } + if (is_attr) { + fmts = ATTR_FMTS; + } else { + fmts = PROP_FMTS; + } + /* + * Add attributes/properties to the search buffer + */ + switch ((pvc = pool_value_get_type(props[i]))) { + case POC_UINT: + (void) pool_value_get_uint64(props[i], &uval); + if (append_char_buf(cb, fmts[pvc], prop_name, uval) + == PO_FAIL) { + free(type_prefix); + return (PO_FAIL); + } + break; + case POC_INT: + (void) pool_value_get_int64(props[i], &ival); + if (append_char_buf(cb, fmts[pvc], prop_name, ival) + == PO_FAIL) { + free(type_prefix); + return (PO_FAIL); + } + break; + case POC_DOUBLE: + (void) pool_value_get_double(props[i], &dval); + if (append_char_buf(cb, fmts[pvc], prop_name, dval) + == PO_FAIL) { + free(type_prefix); + return (PO_FAIL); + } + break; + case POC_BOOL: + (void) pool_value_get_bool(props[i], &bval); + if (append_char_buf(cb, fmts[pvc], prop_name, + bval ? "true" : "false") == PO_FAIL) { + free(type_prefix); + return (PO_FAIL); + } + break; + case POC_STRING: + (void) pool_value_get_string(props[i], &sval); + if (append_char_buf(cb, fmts[pvc], prop_name, sval) + == PO_FAIL) { + free(type_prefix); + return (PO_FAIL); + } + break; + default: + free(type_prefix); + pool_seterror(POE_INVALID_SEARCH); + return (PO_FAIL); + } + if (last_prop_name != NULL) { + const char *suffix1, *suffix2; + /* + * Extra fiddling for namespaces + */ + suffix1 = strrchr(prop_name, '.'); + suffix2 = strrchr(last_prop_name, '.'); + + if (suffix1 != NULL || suffix2 != NULL) { + if (suffix1 == NULL) + suffix1 = prop_name; + else + suffix1++; + if (suffix2 == NULL) + suffix2 = last_prop_name; + else + suffix2++; + } else { + suffix1 = prop_name; + suffix2 = last_prop_name; + } + if (strcmp(suffix1, suffix2) == 0) { + char *where = strrchr(cb->cb_buf, '['); + if (is_attr != PO_TRUE) { + /* repeat */ + while (*--where != '[') + ; + while (*--where != '[') + ; + } + *(where - 1) = 'o'; + *where = 'r'; + } + } + last_prop_name = prop_name; + } + if (has_type == PO_FALSE) { + if (type_prefix) { + if (append_char_buf(cb, ATTR_FMTS[POC_STRING], + c_type, type_prefix) == PO_FAIL) { + free(type_prefix); + return (PO_FAIL); + } + } + } + free(type_prefix); + return (PO_SUCCESS); +} + +/* + * Utility routine for use by quicksort. Assumes that the supplied data + * are pool values and compares the names of the two pool values. + * Returns an integer greater than, equal to, or less than 0. + */ +static int +prop_sort(const void *a, const void *b) +{ + pool_value_t **prop_a = (pool_value_t **)a; + pool_value_t **prop_b = (pool_value_t **)b; + const char *str_a; + const char *str_b; + const char *suffix1, *suffix2; + + str_a = pool_value_get_name(*prop_a); + str_b = pool_value_get_name(*prop_b); + /* + * Extra fiddling for namespaces + */ + suffix1 = strrchr(str_a, '.'); + suffix2 = strrchr(str_b, '.'); + + if (suffix1 != NULL || suffix2 != NULL) { + if (suffix1 == NULL) + suffix1 = str_a; + else + suffix1++; + if (suffix2 == NULL) + suffix2 = str_b; + else + suffix2++; + } else { + suffix1 = str_a; + suffix2 = str_b; + } + return (strcmp(suffix1, suffix2)); +} + +/* + * Order the elements by (ref_id) + */ + +/* + * Returns PO_TRUE/PO_FALSE to indicate whether the supplied path exists on the + * system. It is assumed that the supplied path is in URL format and represents + * a file and so file:// is stripped from the start of the search. + */ +static int +dtd_exists(const char *path) +{ + struct stat buf; + + if (strstr(path, "file://") != path) + return (PO_FALSE); + + if (path[7] == 0) + return (PO_FALSE); + + if (stat(&path[7], &buf) == 0) + return (PO_TRUE); + return (PO_FALSE); +} + +/* + * Build the dtype structures to accelerate data type lookup operations. The + * purpose is to avoid expensive XML manipulations on data which will not + * change over the life of a library invocation. It is designed to be invoked + * once from the library init function. + */ +static void +build_dtype_accelerator(void) +{ + xmlDtdPtr dtd; + const xmlChar *elem_list[ELEM_TYPE_COUNT] = { + BAD_CAST "res_comp", + BAD_CAST "res_agg", + BAD_CAST "comp", + BAD_CAST "pool", + BAD_CAST "property", + BAD_CAST "system" }; + int i; + + if (_libpool_xml_initialised == PO_TRUE) + return; + + /* Load up the d-type data for each element */ + /* + * Store data type information in nested lists + * Top level list contains attribute declaration pointers which + * can be used to match with supplied nodes. + * Second level list contains attribute type information for each + * element declaration + */ + /* + * Unfortunately, there's no easy way to get a list of all DTD + * element descriptions as there is no libxml API to do this (they + * are stored in a hash, which I guess is why). Explicitly seek + * for descriptions for elements that are known to hold an a-dtype + * attribute and build accelerators for those elements. + * If the DTD changes, the library may have to change as well now, + * since this code makes explicit assumptions about which elements + * contain a-dtype information. + */ + + if ((dtd = xmlParseDTD(BAD_CAST "-//Sun Microsystems Inc//DTD Resource" + " Management All//EN", BAD_CAST dtd_location)) == NULL) + return; + for (i = 0; i < ELEM_TYPE_COUNT; i++) { + xmlElementPtr elem; + xmlAttributePtr attr; + + if ((elem = xmlGetDtdElementDesc(dtd, elem_list[i])) == NULL) + return; + elem_tbl[i].ett_elem = xmlStrdup(elem->name); + /* Walk the list of attributes looking for a-dtype */ + for (attr = elem->attributes; attr != NULL; + attr = attr->nexth) { + if (strcmp((const char *)attr->name, c_a_dtype) == 0) { + /* + * Allocate a dtype_tbl_t + */ + elem_tbl[i].ett_dtype = + build_dtype_tbl(attr->defaultValue); + /* This could have returned NULL */ + } + } + } + xmlFreeDtd(dtd); +} + +/* + * build_dtype_tbl() parses the supplied data and returns an array (max size + * of 10, increase if required) of dtype_tbl_t structures holding data type + * information for an element. The supplied data is assumed to be in "a-dtype" + * format. The dtype_tbl_t array is NULL terminated, which is why space for + * 11 members is allocated. + */ +static dtype_tbl_t +(*build_dtype_tbl(const xmlChar *rawdata))[] +{ + char *tok; + char *lasts; + dtype_tbl_t (*tbl)[]; + int j = 0; + xmlChar *data; + const int max_attr = 11; /* Not more than 10 types per element */ + + /* + * Parse the supplied data, assumed to be in a-dtype format, and + * generate a lookup table which is indexed by the name and contains + * the data type + */ + if (rawdata == NULL) + return (NULL); + if ((data = xmlStrdup(rawdata)) == NULL) + return (NULL); + if ((tbl = calloc(max_attr, sizeof (dtype_tbl_t))) == NULL) { + xmlFree(data); + return (NULL); + } + for (tok = strtok_r((char *)data, " ", &lasts); tok != NULL; + tok = strtok_r(NULL, " ", &lasts)) { + int i; + (*tbl)[j].dt_name = xmlStrdup(BAD_CAST tok); + if ((tok = strtok_r(NULL, " ", &lasts)) == NULL) { + int k = j; + for (j = 0; j < k; j++) + free((*tbl)[j].dt_name); + pool_seterror(POE_DATASTORE); + xmlFree(data); + free(tbl); + return (NULL); + } + for (i = 0; i < (sizeof (data_type_tags) / + sizeof (data_type_tags[0])); i++) { + if (strcmp(tok, data_type_tags[i]) == 0) + (*tbl)[j++].dt_type = i; + } + if (j == max_attr) { /* too many attributes, bail out */ + for (j = 0; j < max_attr; j++) + free((*tbl)[j].dt_name); + free(tbl); + xmlFree(data); + return (NULL); + } + } + (*tbl)[j].dt_name = NULL; /* Terminate the table */ + xmlFree(data); + return (tbl); +} + +/* + * get_fast_dtype() finds the data type for a supplied attribute name on a + * supplied node. This is called get_fast_dtype() because it uses the cached + * data type information created at library initialisation. + */ +static int +get_fast_dtype(xmlNodePtr node, xmlChar *name) +{ + int i; + xmlElementPtr elem; + + if ((elem = xmlGetDtdElementDesc(node->doc->extSubset, node->name)) + == NULL) { + pool_seterror(POE_BADPARAM); + return (POC_INVAL); + } + + for (i = 0; i < ELEM_TYPE_COUNT; i++) { + if (xmlStrcmp(elem_tbl[i].ett_elem, elem->name) == 0) { + dtype_tbl_t (*tbl)[] = elem_tbl[i].ett_dtype; + int j = 0; + + if (tbl == NULL) + break; + for (j = 0; (*tbl)[j].dt_name != NULL; j++) + if (xmlStrcmp(name, (*tbl)[j].dt_name) == 0) + return ((*tbl)[j].dt_type); /* found */ + break; /* if we didn't find it in the elem, break */ + } + } + /* If we can't find it, say it's a string */ + return (POC_STRING); +} + +/* + * pool_xml_parse_document() parses the file associated with a supplied + * configuration to regenerate the runtime representation. The supplied + * configuration must reference an already opened file and this is used + * to generate the XML representation via the configuration provider's + * pxc_doc member. + * size must be >=4 in order for "content encoding detection" to work. + */ +static int +pool_xml_parse_document(pool_conf_t *conf) +{ + int res; + char chars[PAGE_READ_SIZE]; + struct stat f_stat; + xmlParserCtxtPtr ctxt; + size_t size; + pool_xml_connection_t *prov = (pool_xml_connection_t *)conf->pc_prov; + xmlNodePtr root; + pool_resource_t **rsl; + uint_t nelem; + int i; + + if (fstat(fileno(prov->pxc_file), &f_stat) == -1) { + pool_seterror(POE_SYSTEM); + return (PO_FAIL); + } + + if (f_stat.st_size == 0) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } else + size = f_stat.st_size < 4 ? 4 : PAGE_READ_SIZE; + + res = fread(chars, 1, size, prov->pxc_file); + + if (res >= 4) { + xmlValidCtxtPtr cvp; + + if ((ctxt = xmlCreatePushParserCtxt(NULL, NULL, + chars, res, conf->pc_location)) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + + while ((res = fread(chars, 1, size, prov->pxc_file)) > 0) { + if (xmlParseChunk(ctxt, chars, res, 0) != 0) { + xmlFreeParserCtxt(ctxt); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + } + if (xmlParseChunk(ctxt, chars, 0, 1) != 0) { + xmlFreeParserCtxt(ctxt); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + + if ((cvp = xmlNewValidCtxt()) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + cvp->error = pool_error_func; + cvp->warning = pool_error_func; + + if (xmlValidateDocument(cvp, ctxt->myDoc) == 0) { + xmlFreeValidCtxt(cvp); + xmlFreeParserCtxt(ctxt); + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + prov->pxc_doc = ctxt->myDoc; + xmlFreeValidCtxt(cvp); + xmlFreeParserCtxt(ctxt); + } + if (prov->pxc_doc == NULL) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + prov->pxc_doc->_private = conf; + + /* Get the root element */ + if ((root = xmlDocGetRootElement(prov->pxc_doc)) == NULL) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + /* + * Ensure that the parsed tree has been contained within + * our shadow tree. + */ + if (create_shadow(root) != PO_SUCCESS) { + pool_seterror(POE_INVALID_CONF); + return (PO_FAIL); + } + + if (pool_xml_validate(conf, POV_STRICT) != PO_SUCCESS) { + return (PO_FAIL); + } + /* + * For backwards compatibility with S9, make sure that all + * resources have a size and that it is correct. + */ + if ((rsl = pool_query_resources(conf, &nelem, NULL)) != NULL) { + pool_value_t val = POOL_VALUE_INITIALIZER; + for (i = 0; i < nelem; i++) { + if (pool_get_ns_property(TO_ELEM(rsl[i]), c_size_prop, + &val) != POC_UINT) { + pool_component_t **cs; + uint_t size; + if ((cs = pool_query_resource_components(conf, + rsl[i], &size, NULL)) != NULL) { + free(cs); + pool_value_set_uint64(&val, size); + } else + pool_value_set_uint64(&val, 0); + if (pool_put_any_ns_property(TO_ELEM(rsl[i]), + c_size_prop, &val) != PO_SUCCESS) { + free(rsl); + return (PO_FAIL); + } + } + } + free(rsl); + } + return (PO_SUCCESS); +} |