/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }