
# HG changeset patch # User Dan Smith <danms@us.ibm.com> # Date 1202847565 28800 # Node ID 7dcf0ca88f1873778f94bd8920c437f5cfa65e78 # Parent 520d0cf3451a118877bf623b8927ff4f46a8e899 [CU] Add EI CIMXML parser This handles strings, bools, integers, and arrays of those. It doesn't yet handle nested objects, as we will need for RASD.HostResource[]. I need to do some further investigation of what the XML for that will look like. I have tested this by taking an existing XML for DefineSystem() and changing the MemResourceAllocationSettingData field to this: <INSTANCE CLASSNAME="Xen_MemResourceAllocationSettingData"> <PROPERTY NAME="CreationClassName" TYPE="string"> <VALUE>Xen_MemResourceAllocationSettingData</VALUE> </PROPERTY> <PROPERTY NAME="InstanceID" TYPE="string"> <VALUE>test/mem</VALUE> </PROPERTY> <PROPERTY NAME="ResourceType" TYPE="uint16"> <VALUE>4</VALUE> </PROPERTY> <PROPERTY NAME="VirtualQuantity" TYPE="uint64"> <VALUE>256 </VALUE> </PROPERTY> <PROPERTY.ARRAY NAME="Foo" TYPE="string"> <VALUE.ARRAY> <VALUE>Bar</VALUE> <VALUE>Baz</VALUE> </VALUE.ARRAY> </PROPERTY.ARRAY> </INSTANCE> (The Foo[] parameter in there is just for testing :) Changes: - Fixes as noted by Kaitlin - XML namespace and parse flags, as noted by DV Signed-off-by: Dan Smith <danms@us.ibm.com> diff -r 520d0cf3451a -r 7dcf0ca88f18 Makefile.am --- a/Makefile.am Tue Feb 12 08:45:50 2008 -0800 +++ b/Makefile.am Tue Feb 12 12:19:25 2008 -0800 @@ -10,6 +10,8 @@ pkgconfig_DATA = libcmpiutil.pc libcmpiutilincdir = $(includedir)/libcmpiutil +noinst_HEADERS = eo_parser_xml.h + libcmpiutilinc_HEADERS = libcmpiutil.h \ std_invokemethod.h \ std_association.h \ @@ -18,7 +20,7 @@ libcmpiutilinc_HEADERS = libcmpiutil.h \ libcmpiutil_la_SOURCES = args_util.c instance_util.c std_invokemethod.c \ std_association.c inst_list.c std_indication.c \ - debug_util.c + debug_util.c eo_parser_xml.c libcmpiutil_la_CFLAGS = $(CFLAGS) $(CFLAGS_STRICT) libcmpiutil_la_LIBADD = libcmpiutil_la_DEPENDENCIES = diff -r 520d0cf3451a -r 7dcf0ca88f18 eo_parser_xml.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eo_parser_xml.c Tue Feb 12 12:19:25 2008 -0800 @@ -0,0 +1,515 @@ +/* + * Copyright IBM Corp. 2007 + * + * Authors: + * Dan Smith <danms@us.ibm.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include <stdint.h> +#include <inttypes.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> + +#include <cmpidt.h> +#include <cmpift.h> +#include <cmpimacs.h> + +#include "libcmpiutil.h" + +#include "eo_parser_xml.h" + +static char *get_attr(xmlNode *node, const char *name) +{ + return (char *)xmlGetProp(node, (xmlChar *)name); +} + +static char *get_content(xmlNode *node) +{ + return (char *)xmlNodeGetContent(node); +} + +static CMPIType parse_string_property(const CMPIBroker *broker, + const char *string, + const char *type, + CMPIValue *val) +{ + CMPIString *str; + CMPIStatus s; + + str = CMNewString(broker, string, &s); + if ((str == NULL) || (s.rc != CMPI_RC_OK)) + return CMPI_null; + + val->string = str; + return CMPI_string; +} + +static CMPIType parse_bool_property(const char *string, + const char *type, + CMPIValue *val) +{ + val->boolean = STREQC(string, "true"); + + return CMPI_boolean; +} + +static CMPIType parse_int_property(const char *string, + const char *type, + CMPIValue *val) +{ + int size; + bool sign; + CMPIType t; + int ret; + + if (sscanf(type, "uint%i", &size) == 1) + sign = false; + else if (sscanf(type, "int%i", &size) == 1) + sign = true; + else { + CU_DEBUG("Unknown integer type: `%s'", type); + return CMPI_null; + } + + if (sign) { + int64_t _val; + ret = sscanf(string, "%" SCNi64, &_val); + val->sint64 = _val; + } else { + uint64_t _val; + ret = sscanf(string, "%" SCNu64, &_val); + val->uint64 = _val; + } + + if (ret != 1) { + CU_DEBUG("Failed to scan value `%s'\n", string); + return CMPI_null; + } + + switch (size) { + case 8: t = sign ? CMPI_sint8 : CMPI_uint8; break; + case 16: t = sign ? CMPI_sint16 : CMPI_uint16; break; + case 32: t = sign ? CMPI_sint32 : CMPI_uint32; break; + default: + case 64: t = sign ? CMPI_sint64 : CMPI_uint64; break; + }; + + return t; +} + +static CMPIType get_property_value(const CMPIBroker *broker, + const char *tstr, + const char *content, + CMPIValue *value) +{ + CMPIType type = CMPI_null; + + if (STREQC(tstr, "string")) + type = parse_string_property(broker, content, tstr, value); + else if (STREQC(tstr, "boolean")) + type = parse_bool_property(content, tstr, value); + else if (strstr(tstr, "int")) + type = parse_int_property(content, tstr, value); + else { + CU_DEBUG("Unhandled type: %s\n", tstr); + goto out; + } + + if (type == CMPI_null) { + CU_DEBUG("Unable to parse type %s\n", tstr); + goto out; + } + + out: + return type; +} + +static CMPIType get_node_value(const CMPIBroker *broker, + xmlNode *node, + const char *tstr, + CMPIValue *val) +{ + CMPIType type = CMPI_null; + char *content = NULL; + + if (node->type != XML_ELEMENT_NODE) { + CU_DEBUG("Non-element node: %s", node->name); + goto out; + } + + if (!STREQC((char *)node->name, "value") && + (node->ns == NULL)) { + CU_DEBUG("Expected <VALUE> but got <%s>", node->name); + goto out; + } + + content = get_content(node); + CU_DEBUG("Node content: %s", content); + type = get_property_value(broker, tstr, content, val); + free(content); + out: + return type; +} + +static CMPIType parse_array(const CMPIBroker *broker, + const char *tstr, + xmlNode *root, + CMPIArray **array) +{ + xmlNode *value; + CMPIValue *list = NULL; + int size = 0; + int cur = 0; + CMPIStatus s; + CMPIType type = CMPI_null; + int i; + + for (value = root->children; value; value = value->next) { + + if (value->type != XML_ELEMENT_NODE) + continue; + + if (cur == size) { + CMPIValue *tmp; + + size *= 2; + tmp = realloc(list, sizeof(CMPIValue) * size); + if (tmp == NULL) { + CU_DEBUG("Failed to alloc %i", + sizeof(CMPIValue) * size); + goto out; + } + + list = tmp; + } + + type = get_node_value(broker, value, tstr, &list[cur]); + if (type == CMPI_null) { + CU_DEBUG("Got nothing from child"); + } else { + CU_DEBUG("Array value type %i", type); + cur++; + } + } + + if (cur == 0) + return CMPI_null; + + *array = CMNewArray(broker, cur, type, &s); + if ((*array == NULL) || (s.rc != CMPI_RC_OK)) { + CU_DEBUG("Failed to alloc CMPIArray of %i", cur); + goto out; + } + + for (i = 0; i < cur; i++) + CMSetArrayElementAt((*array), i, &list[i], type); + + out: + free(list); + + return type; +} + +static bool parse_array_property(const CMPIBroker *broker, + xmlNode *node, + CMPIInstance *inst) +{ + char *name = NULL; + char *tstr = NULL; + bool ret = false; + xmlNode *val_arr; + CMPIArray *array; + CMPIType type; + + name = get_attr(node, "NAME"); + if (name == NULL) { + CU_DEBUG("Unnamed property\n"); + goto out; + } + + tstr = get_attr(node, "TYPE"); + if (tstr == NULL) { + CU_DEBUG("No type\n"); + goto out; + } + + CU_DEBUG("Array property `%s' of type `%s'\n", name, tstr); + + for (val_arr = node->children; val_arr; val_arr = val_arr->next) { + if (val_arr->type != XML_ELEMENT_NODE) + continue; + + if (!STREQC((char *)val_arr->name, "value.array")) { + CU_DEBUG("Expected <value.array> but got <%s>", + val_arr->name); + val_arr = NULL; + goto out; + } + + break; + } + + if (val_arr != NULL) { + type = parse_array(broker, tstr, val_arr, &array); + if (type != CMPI_null) { + CU_DEBUG("Setting array property"); + CMSetProperty(inst, name, &array, (CMPI_ARRAY | type)); + } + } + + out: + free(name); + free(tstr); + + return ret; +} + +static bool parse_property(const CMPIBroker *broker, + xmlNode *node, + CMPIInstance *inst) +{ + char *name = NULL; + char *tstr = NULL; + CMPIValue value; + CMPIType type = CMPI_null; + xmlNode *ptr; + + name = get_attr(node, "NAME"); + if (name == NULL) { + CU_DEBUG("Unnamed property\n"); + goto out; + } + + tstr = get_attr(node, "TYPE"); + if (tstr == NULL) { + CU_DEBUG("No type\n"); + goto out; + } + + CU_DEBUG("Property %s: %s", name, tstr); + + for (ptr = node->children; ptr; ptr = ptr->next) { + type = get_node_value(broker, ptr, tstr, &value); + if (type != CMPI_null) { + CMSetProperty(inst, name, &value, type); + break; + } + } + out: + free(name); + free(tstr); + + return type != CMPI_null; +} + +static CMPIStatus parse_instance(const CMPIBroker *broker, + const char *ns, + xmlNode *root, + CMPIInstance **inst) +{ + char *class = NULL; + xmlNode *child; + CMPIStatus s; + CMPIObjectPath *op; + + if (root->type != XML_ELEMENT_NODE) { + CU_DEBUG("First node is not <INSTANCE>"); + cu_statusf(broker, &s, + CMPI_RC_ERR_FAILED, + "First node of object is not <INSTANCE"); + goto out; + } + + if (!STREQC((char *)root->name, "instance")) { + CU_DEBUG("Got node %s, expecting INSTANCE", root->name); + cu_statusf(broker, &s, + CMPI_RC_ERR_FAILED, + "First node of object is not <INSTANCE"); + goto out; + } + + class = get_attr(root, "CLASSNAME"); + if (class == NULL) { + CU_DEBUG("No classname in object"); + cu_statusf(broker, &s, + CMPI_RC_ERR_FAILED, + "Missing CLASSNAME attribute of INSTANCE"); + goto out; + } + + CU_DEBUG("Instance of %s", class); + + op = CMNewObjectPath(broker, ns, class, &s); + if ((op == NULL) || (s.rc != CMPI_RC_OK)) { + CU_DEBUG("Unable to create path for %s:%s", ns, class); + goto out; + } + + *inst = CMNewInstance(broker, op, &s); + if ((*inst == NULL) || (s.rc != CMPI_RC_OK)) { + CU_DEBUG("Unable to create inst for %s:%s", ns, class); + goto out; + } + + for (child = root->children; child; child = child->next) { + if ((child->type == XML_ELEMENT_NODE) && + (child->ns == NULL)) { + if (STREQC((char *)child->name, "property")) + parse_property(broker, child, *inst); + else if (STREQC((char *)child->name, "property.array")) + parse_array_property(broker, child, *inst); + else + CU_DEBUG("Unexpected node: %s\n", child->name); + } + } + + out: + free(class); + + return s; +} + +#if 0 + +/* This isn't currently used but I think I might need it for nested + * instances, depending on how they look. + */ + +static char *parse_esc(const char *start, char *result) +{ + char *delim; + char *escape = NULL; + int len = 1; + + delim = strchr(start, ';'); + if (delim == NULL) + goto out; + + escape = strndup(start, delim - start + 1); + if (escape == NULL) { + CU_DEBUG("Memory alloc failed (%i)", delim-start); + return NULL; + } + + CU_DEBUG("Escape is: %s", escape); + + if (STREQC(escape, "<")) + *result = '<'; + else if (STREQC(escape, ">")) + *result = '>'; + else if (STREQC(escape, """)) + *result = '\"'; + else if (STREQC(escape, "&")) + *result = '&'; + else if (STREQC(escape, "'")) + *result = '\''; + else + CU_DEBUG("Unhandled escape: `%s'", escape); + + len = strlen(escape); + + out: + free(escape); + + return (char *)start + len; +} + +static char *xml_decode(const char *input) +{ + const char *iptr = input; + char *optr; + char *res = NULL; + + res = malloc(strlen(input) + 1); + if (res == NULL) + return res; + + optr = res; + + while ((iptr != NULL) && (*iptr != '\0')) { + if (*iptr == '&') { + iptr = parse_esc(iptr, optr); + } else { + *optr = *iptr; + iptr++; + } + + optr++; + } + + return res; +} + +#endif + +int cu_parse_ei_xml(const CMPIBroker *broker, + const char *ns, + const char *xml, + CMPIInstance **instance) +{ + xmlDoc *doc = NULL; + xmlNode *root = NULL; + int ret = 1; + CMPIStatus s; + + doc = xmlReadMemory(xml, + strlen(xml), + NULL, + NULL, + (XML_PARSE_NOENT | + XML_PARSE_NOCDATA | + XML_PARSE_NONET)); + if (doc == NULL) { + CU_DEBUG("Error reading decoded XML from memory"); + goto out; + } + + root = xmlDocGetRootElement(doc); + if (root == NULL) { + CU_DEBUG("Error getting root XML node"); + goto out; + } + + s = parse_instance(broker, ns, root, instance); + if (s.rc != CMPI_RC_OK) { + *instance = NULL; + CU_DEBUG("CIMXML EI Parsing failed: %s", + CMGetCharPtr(s.msg)); + goto out; + } + + ret = 0; + + out: + xmlFreeDoc(doc); + + return ret; +} + +/* + * Local Variables: + * mode: C + * c-set-style: "K&R" + * tab-width: 8 + * c-basic-offset: 8 + * indent-tabs-mode: nil + * End: + */ diff -r 520d0cf3451a -r 7dcf0ca88f18 eo_parser_xml.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eo_parser_xml.h Tue Feb 12 12:19:25 2008 -0800 @@ -0,0 +1,40 @@ +/* + * Copyright IBM Corp. 2007 + * + * Authors: + * Dan Smith <danms@us.ibm.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __EO_PARSER_XML_H +#define __EO_PARSER_XML_H + +int cu_parse_ei_xml(const CMPIBroker *broker, + const char *ns, + const char *xml, + CMPIInstance **instance); + +#endif + +/* + * Local Variables: + * mode: C + * c-set-style: "K&R" + * tab-width: 8 + * c-basic-offset: 8 + * indent-tabs-mode: nil + * End: + */