pool_xml.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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.
*/
xmlLineNumbersDefault(1);
xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
xmlDoValidityCheckingDefaultValue = 1;
/* Try to improve indentation and readability */
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) {
xmlAddChild(parent, pxc->pxe_node); /* Try to move back */
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+");
else /* Assume opening PO_RDONLY */
prov->pxc_file = fopen(conf->pc_location, "r");
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);
pool_seterror(POE_INVALID_SEARCH);
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);
pool_seterror(POE_INVALID_SEARCH);
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);
pool_seterror(POE_INVALID_SEARCH);
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);
pool_seterror(POE_INVALID_SEARCH);
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);
pool_seterror(POE_INVALID_SEARCH);
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);
pool_seterror(POE_INVALID_SEARCH);
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);
pool_seterror(POE_INVALID_SEARCH);
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);
pool_seterror(POE_INVALID_SEARCH);
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);
pool_seterror(POE_INVALID_SEARCH);
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
*/
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);
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);
}