Add support for parsing node device XML descriptions.
This will be used by PCI passthrough related functions to
obtain the PCI device address for a given node device.
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
---
src/libvirt_private.syms | 1 +
src/node_device_conf.c | 687 ++++++++++++++++++++++++++++++++++++++++++++++
src/node_device_conf.h | 3 +-
3 files changed, 690 insertions(+), 1 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 9e9b3e5..fdda478 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -223,6 +223,7 @@ virNodeDeviceObjListFree;
virNodeDeviceDefFree;
virNodeDevCapsDefFree;
virNodeDeviceDefFormat;
+virNodeDeviceDefParseString;
virNodeDeviceObjLock;
virNodeDeviceObjUnlock;
virNodeDeviceAssignDef;
diff --git a/src/node_device_conf.c b/src/node_device_conf.c
index af2f9a3..6e04112 100644
--- a/src/node_device_conf.c
+++ b/src/node_device_conf.c
@@ -27,6 +27,7 @@
#include <errno.h>
#include "virterror_internal.h"
+#include "datatypes.h"
#include "memory.h"
#include "node_device_conf.h"
@@ -367,6 +368,692 @@ char *virNodeDeviceDefFormat(virConnectPtr conn,
return NULL;
}
+static int
+virNodeDevCapsDefParseULong(virConnectPtr conn,
+ const char *xpath,
+ xmlXPathContextPtr ctxt,
+ unsigned *value,
+ virNodeDeviceDefPtr def,
+ const char *missing_error_fmt,
+ const char *invalid_error_fmt)
+{
+ int ret;
+ unsigned long val;
+
+ ret = virXPathULong(conn, xpath, ctxt, &val);
+ if (ret < 0) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ ret == -1 ? missing_error_fmt : invalid_error_fmt,
+ def->name);
+ return -1;
+ }
+
+ *value = val;
+ return 0;
+}
+
+static int
+virNodeDevCapsDefParseULongLong(virConnectPtr conn,
+ const char *xpath,
+ xmlXPathContextPtr ctxt,
+ unsigned long long *value,
+ virNodeDeviceDefPtr def,
+ const char *missing_error_fmt,
+ const char *invalid_error_fmt)
+{
+ int ret;
+ unsigned long long val;
+
+ ret = virXPathULongLong(conn, xpath, ctxt, &val);
+ if (ret < 0) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ ret == -1 ? missing_error_fmt : invalid_error_fmt,
+ def->name);
+ return -1;
+ }
+
+ *value = val;
+ return 0;
+}
+
+static int
+virNodeDevCapStorageParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node,
+ union _virNodeDevCapData *data)
+{
+ xmlNodePtr orignode, *nodes = NULL;
+ int i, n, ret = -1;
+ unsigned long long val;
+
+ orignode = ctxt->node;
+ ctxt->node = node;
+
+ data->storage.block = virXPathString(conn, "string(./block[1])", ctxt);
+ if (!data->storage.block) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("no block device path supplied for
'%s'"),
+ def->name);
+ goto out;
+ }
+
+ data->storage.bus = virXPathString(conn, "string(./bus[1])",
ctxt);
+ data->storage.drive_type = virXPathString(conn,
"string(./drive_type[1])", ctxt);
+ data->storage.model = virXPathString(conn, "string(./model[1])",
ctxt);
+ data->storage.vendor = virXPathString(conn, "string(./vendor[1])",
ctxt);
+
+ if ((n = virXPathNodeSet(conn, "./capability", ctxt, &nodes)) < 0)
{
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("error parsing storage capabilities for
'%s'"),
+ def->name);
+ goto out;
+ }
+
+ for (i = 0 ; i < n ; i++) {
+ char *type = virXMLPropString(nodes[i], "type");
+
+ if (!type) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("missing storage capability type for
'%s'"),
+ def->name);
+ goto out;
+ }
+
+ if (STREQ(type, "hotpluggable"))
+ data->storage.flags |= VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE;
+ else if (STREQ(type, "removable")) {
+ xmlNodePtr orignode2;
+
+ data->storage.flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE;
+
+ orignode2 = ctxt->node;
+ ctxt->node = nodes[i];
+
+ if (virXPathBoolean(conn, "count(./media_available[. = '1'])
> 0", ctxt))
+ data->storage.flags |=
VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
+
+ val = 0;
+ if (virNodeDevCapsDefParseULongLong(conn,
"number(./media_size[1])", ctxt, &val, def,
+ _("no removable media size supplied
for '%s'"),
+ _("invalid removable media size
supplied for '%s'")) < 0) {
+ ctxt->node = orignode2;
+ VIR_FREE(type);
+ goto out;
+ }
+ data->storage.removable_media_size = val;
+
+ ctxt->node = orignode2;
+ } else {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("unknown storage capability type '%s'
for '%s'"),
+ type, def->name);
+ VIR_FREE(type);
+ goto out;
+ }
+
+ VIR_FREE(type);
+ }
+
+ if (!(data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE)) {
+ val = 0;
+ if (virNodeDevCapsDefParseULongLong(conn, "number(./size[1])", ctxt,
&val, def,
+ _("no size supplied for
'%s'"),
+ _("invalid size supplied for
'%s'")) < 0)
+ goto out;
+ data->storage.size = val;
+ }
+
+ ret = 0;
+out:
+ VIR_FREE(nodes);
+ ctxt->node = orignode;
+ return ret;
+}
+
+static int
+virNodeDevCapScsiParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node,
+ union _virNodeDevCapData *data)
+{
+ xmlNodePtr orignode;
+ int ret = -1;
+
+ orignode = ctxt->node;
+ ctxt->node = node;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./host[1])", ctxt,
+ &data->scsi.host, def,
+ _("no SCSI host ID supplied for
'%s'"),
+ _("invalid SCSI host ID supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./bus[1])", ctxt,
+ &data->scsi.bus, def,
+ _("no SCSI bus ID supplied for
'%s'"),
+ _("invalid SCSI bus ID supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./target[1])", ctxt,
+ &data->scsi.target, def,
+ _("no SCSI target ID supplied for
'%s'"),
+ _("invalid SCSI target ID supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./lun[1])", ctxt,
+ &data->scsi.lun, def,
+ _("no SCSI LUN ID supplied for
'%s'"),
+ _("invalid SCSI LUN ID supplied for
'%s'")) < 0)
+ goto out;
+
+ data->scsi.type = virXPathString(conn, "string(./type[1])", ctxt);
+
+ ret = 0;
+out:
+ ctxt->node = orignode;
+ return ret;
+}
+
+static int
+virNodeDevCapScsiHostParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node,
+ union _virNodeDevCapData *data)
+{
+ xmlNodePtr orignode;
+ int ret = -1;
+
+ orignode = ctxt->node;
+ ctxt->node = node;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./host[1])", ctxt,
+ &data->scsi_host.host, def,
+ _("no SCSI host ID supplied for
'%s'"),
+ _("invalid SCSI host ID supplied for
'%s'")) < 0)
+ goto out;
+
+ ret = 0;
+out:
+ ctxt->node = orignode;
+ return ret;
+}
+
+static int
+virNodeDevCapNetParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node,
+ union _virNodeDevCapData *data)
+{
+ xmlNodePtr orignode;
+ int ret = -1;
+ char *tmp;
+
+ orignode = ctxt->node;
+ ctxt->node = node;
+
+ data->net.ifname = virXPathString(conn, "string(./interface[1])",
ctxt);
+ if (!data->net.ifname) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("no network interface supplied for
'%s'"),
+ def->name);
+ goto out;
+ }
+
+ data->net.address = virXPathString(conn, "string(./address[1])", ctxt);
+
+ data->net.subtype = VIR_NODE_DEV_CAP_NET_LAST;
+
+ tmp = virXPathString(conn, "string(./capability/@type)", ctxt);
+ if (tmp) {
+ int val = virNodeDevNetCapTypeFromString(tmp);
+ VIR_FREE(tmp);
+ if (val < 0) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("invalid network type supplied for
'%s'"),
+ def->name);
+ goto out;
+ }
+ data->net.subtype = val;
+ }
+
+ ret = 0;
+out:
+ ctxt->node = orignode;
+ return ret;
+}
+
+static int
+virNodeDevCapUsbInterfaceParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node,
+ union _virNodeDevCapData *data)
+{
+ xmlNodePtr orignode;
+ int ret = -1;
+
+ orignode = ctxt->node;
+ ctxt->node = node;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./number[1])", ctxt,
+ &data->usb_if.number, def,
+ _("no USB interface number supplied for
'%s'"),
+ _("invalid USB interface number supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./class[1])", ctxt,
+ &data->usb_if._class, def,
+ _("no USB interface class supplied for
'%s'"),
+ _("invalid USB interface class supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./subclass[1])", ctxt,
+ &data->usb_if.subclass, def,
+ _("no USB interface subclass supplied for
'%s'"),
+ _("invalid USB interface subclass supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./protocol[1])", ctxt,
+ &data->usb_if.protocol, def,
+ _("no USB interface protocol supplied for
'%s'"),
+ _("invalid USB interface protocol supplied for
'%s'")) < 0)
+ goto out;
+
+ data->usb_if.description = virXPathString(conn,
"string(./description[1])", ctxt);
+
+ ret = 0;
+out:
+ ctxt->node = orignode;
+ return ret;
+}
+
+static int
+virNodeDevCapsDefParseHexId(virConnectPtr conn,
+ const char *xpath,
+ xmlXPathContextPtr ctxt,
+ unsigned *value,
+ virNodeDeviceDefPtr def,
+ const char *missing_error_fmt,
+ const char *invalid_error_fmt)
+{
+ int ret;
+ unsigned long val;
+
+ ret = virXPathULongHex(conn, xpath, ctxt, &val);
+ if (ret < 0) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ ret == -1 ? missing_error_fmt : invalid_error_fmt,
+ def->name);
+ return -1;
+ }
+
+ *value = val;
+ return 0;
+}
+
+static int
+virNodeDevCapUsbDevParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node,
+ union _virNodeDevCapData *data)
+{
+ xmlNodePtr orignode;
+ int ret = -1;
+
+ orignode = ctxt->node;
+ ctxt->node = node;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./bus[1])", ctxt,
+ &data->usb_dev.bus, def,
+ _("no USB bus number supplied for
'%s'"),
+ _("invalid USB bus number supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./device[1])", ctxt,
+ &data->usb_dev.device, def,
+ _("no USB device number supplied for
'%s'"),
+ _("invalid USB device number supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseHexId(conn, "string(./vendor[1]/@id)", ctxt,
+ &data->usb_dev.vendor, def,
+ _("no USB vendor ID supplied for
'%s'"),
+ _("invalid USB vendor ID supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseHexId(conn, "string(./product[1]/@id)", ctxt,
+ &data->usb_dev.product, def,
+ _("no USB product ID supplied for
'%s'"),
+ _("invalid USB product ID supplied for
'%s'")) < 0)
+ goto out;
+
+ data->usb_dev.vendor_name = virXPathString(conn, "string(./vendor[1])",
ctxt);
+ data->usb_dev.product_name = virXPathString(conn,
"string(./product[1])", ctxt);
+
+ ret = 0;
+out:
+ ctxt->node = orignode;
+ return ret;
+}
+
+static int
+virNodeDevCapPciDevParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node,
+ union _virNodeDevCapData *data)
+{
+ xmlNodePtr orignode;
+ int ret = -1;
+
+ orignode = ctxt->node;
+ ctxt->node = node;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./domain[1])", ctxt,
+ &data->pci_dev.domain, def,
+ _("no PCI domain ID supplied for
'%s'"),
+ _("invalid PCI domain ID supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./bus[1])", ctxt,
+ &data->pci_dev.bus, def,
+ _("no PCI bus ID supplied for
'%s'"),
+ _("invalid PCI bus ID supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./slot[1])", ctxt,
+ &data->pci_dev.slot, def,
+ _("no PCI slot ID supplied for
'%s'"),
+ _("invalid PCI slot ID supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseULong(conn, "number(./function[1])", ctxt,
+ &data->pci_dev.function, def,
+ _("no PCI function ID supplied for
'%s'"),
+ _("invalid PCI function ID supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseHexId(conn, "string(./vendor[1]/@id)", ctxt,
+ &data->pci_dev.vendor, def,
+ _("no PCI vendor ID supplied for
'%s'"),
+ _("invalid PCI vendor ID supplied for
'%s'")) < 0)
+ goto out;
+
+ if (virNodeDevCapsDefParseHexId(conn, "string(./product[1]/@id)", ctxt,
+ &data->pci_dev.product, def,
+ _("no PCI product ID supplied for
'%s'"),
+ _("invalid PCI product ID supplied for
'%s'")) < 0)
+ goto out;
+
+ data->pci_dev.vendor_name = virXPathString(conn, "string(./vendor[1])",
ctxt);
+ data->pci_dev.product_name = virXPathString(conn,
"string(./product[1])", ctxt);
+
+ ret = 0;
+out:
+ ctxt->node = orignode;
+ return ret;
+}
+
+static int
+virNodeDevCapSystemParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node,
+ union _virNodeDevCapData *data)
+{
+ xmlNodePtr orignode;
+ int ret = -1;
+ char *tmp;
+
+ orignode = ctxt->node;
+ ctxt->node = node;
+
+ data->system.product_name = virXPathString(conn, "string(./product[1])",
ctxt);
+
+ data->system.hardware.vendor_name = virXPathString(conn,
"string(./hardware/vendor[1])", ctxt);
+ data->system.hardware.version = virXPathString(conn,
"string(./hardware/version[1])", ctxt);
+ data->system.hardware.serial = virXPathString(conn,
"string(./hardware/serial[1])", ctxt);
+
+ tmp = virXPathString(conn, "string(./hardware/uuid[1])", ctxt);
+ if (!tmp) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("no system UUID supplied for '%s'"),
def->name);
+ goto out;
+ }
+
+ if (virUUIDParse(tmp, data->system.hardware.uuid) < 0) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("malformed uuid element for '%s'"),
def->name);
+ VIR_FREE(tmp);
+ goto out;
+ }
+ VIR_FREE(tmp);
+
+ data->system.firmware.vendor_name = virXPathString(conn,
"string(./firmware/vendor[1])", ctxt);
+ data->system.firmware.version = virXPathString(conn,
"string(./firmware/version[1])", ctxt);
+ data->system.firmware.release_date = virXPathString(conn,
"string(./firmware/release_date[1])", ctxt);
+
+ ret = 0;
+out:
+ ctxt->node = orignode;
+ return ret;
+}
+
+static virNodeDevCapsDefPtr
+virNodeDevCapsDefParseXML(virConnectPtr conn,
+ xmlXPathContextPtr ctxt,
+ virNodeDeviceDefPtr def,
+ xmlNodePtr node)
+{
+ virNodeDevCapsDefPtr caps;
+ char *tmp;
+ int val, ret;
+
+ if (VIR_ALLOC(caps) < 0) {
+ virReportOOMError(conn);
+ return NULL;
+ }
+
+ tmp = virXMLPropString(node, "type");
+ if (!tmp) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing capability
type"));
+ goto error;
+ }
+
+ if ((val = virNodeDevCapTypeFromString(tmp)) < 0) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("unknown capability type '%s'"),
tmp);
+ VIR_FREE(tmp);
+ goto error;
+ }
+ caps->type = val;
+ VIR_FREE(tmp);
+
+ switch (caps->type) {
+ case VIR_NODE_DEV_CAP_SYSTEM:
+ ret = virNodeDevCapSystemParseXML(conn, ctxt, def, node, &caps->data);
+ break;
+ case VIR_NODE_DEV_CAP_PCI_DEV:
+ ret = virNodeDevCapPciDevParseXML(conn, ctxt, def, node, &caps->data);
+ break;
+ case VIR_NODE_DEV_CAP_USB_DEV:
+ ret = virNodeDevCapUsbDevParseXML(conn, ctxt, def, node, &caps->data);
+ break;
+ case VIR_NODE_DEV_CAP_USB_INTERFACE:
+ ret = virNodeDevCapUsbInterfaceParseXML(conn, ctxt, def, node,
&caps->data);
+ break;
+ case VIR_NODE_DEV_CAP_NET:
+ ret = virNodeDevCapNetParseXML(conn, ctxt, def, node, &caps->data);
+ break;
+ case VIR_NODE_DEV_CAP_SCSI_HOST:
+ ret = virNodeDevCapScsiHostParseXML(conn, ctxt, def, node, &caps->data);
+ break;
+ case VIR_NODE_DEV_CAP_SCSI:
+ ret = virNodeDevCapScsiParseXML(conn, ctxt, def, node, &caps->data);
+ break;
+ case VIR_NODE_DEV_CAP_STORAGE:
+ ret = virNodeDevCapStorageParseXML(conn, ctxt, def, node, &caps->data);
+ break;
+ default:
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("unknown capability type '%d' for
'%s'"),
+ caps->type, def->name);
+ ret = -1;
+ break;
+ }
+
+ if (ret < 0)
+ goto error;
+ return caps;
+
+error:
+ virNodeDevCapsDefFree(caps);
+ return NULL;
+}
+
+static virNodeDeviceDefPtr
+virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt)
+{
+ virNodeDeviceDefPtr def;
+ virNodeDevCapsDefPtr *next_cap;
+ xmlNodePtr *nodes;
+ int n, i;
+
+ if (VIR_ALLOC(def) < 0) {
+ virReportOOMError(conn);
+ return NULL;
+ }
+
+ /* Extract device name */
+ def->name = virXPathString(conn, "string(./name[1])", ctxt);
+ if (!def->name) {
+ virNodeDeviceReportError(conn, VIR_ERR_NO_NAME, NULL);
+ goto error;
+ }
+
+ /* Extract device parent, if any */
+ def->parent = virXPathString(conn, "string(./parent[1])", ctxt);
+
+ /* Parse device capabilities */
+ nodes = NULL;
+ if ((n = virXPathNodeSet(conn, "./capability", ctxt, &nodes)) <= 0)
{
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("no device capabilities for '%s'"),
+ def->name);
+ goto error;
+ }
+
+ next_cap = &def->caps;
+ for (i = 0 ; i < n ; i++) {
+ *next_cap = virNodeDevCapsDefParseXML(conn, ctxt, def, nodes[i]);
+ if (!*next_cap) {
+ VIR_FREE(nodes);
+ goto error;
+ }
+
+ next_cap = &(*next_cap)->next;
+ }
+ VIR_FREE(nodes);
+
+ return def;
+
+ error:
+ virNodeDeviceDefFree(def);
+ return NULL;
+}
+
+static virNodeDeviceDefPtr
+virNodeDeviceDefParseNode(virConnectPtr conn, xmlDocPtr xml, xmlNodePtr root)
+{
+ xmlXPathContextPtr ctxt = NULL;
+ virNodeDeviceDefPtr def = NULL;
+
+ if (!xmlStrEqual(root->name, BAD_CAST "device")) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("incorrect root element"));
+ return NULL;
+ }
+
+ ctxt = xmlXPathNewContext(xml);
+ if (ctxt == NULL) {
+ virReportOOMError(conn);
+ goto cleanup;
+ }
+
+ ctxt->node = root;
+ def = virNodeDeviceDefParseXML(conn, ctxt);
+
+cleanup:
+ xmlXPathFreeContext(ctxt);
+ return def;
+}
+
+/* Called from SAX on parsing errors in the XML. */
+static void
+catchXMLError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
+{
+ xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
+
+ if (ctxt) {
+ virConnectPtr conn = ctxt->_private;
+
+ if (virGetLastError() == NULL &&
+ ctxt->lastError.level == XML_ERR_FATAL &&
+ ctxt->lastError.message != NULL) {
+ virNodeDeviceReportError(conn, VIR_ERR_XML_DETAIL,
+ _("at line %d: %s"),
+ ctxt->lastError.line,
+ ctxt->lastError.message);
+ }
+ }
+}
+
+virNodeDeviceDefPtr
+virNodeDeviceDefParseString(virConnectPtr conn, const char *str)
+{
+ xmlParserCtxtPtr pctxt;
+ xmlDocPtr xml = NULL;
+ xmlNodePtr root;
+ virNodeDeviceDefPtr def = NULL;
+
+ /* Set up a parser context so we can catch the details of XML errors. */
+ pctxt = xmlNewParserCtxt ();
+ if (!pctxt || !pctxt->sax)
+ goto cleanup;
+ pctxt->sax->error = catchXMLError;
+ pctxt->_private = conn;
+
+ if (conn) virResetError (&conn->err);
+ xml = xmlCtxtReadDoc(pctxt, BAD_CAST str, "device.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOWARNING);
+ if (!xml) {
+ if (conn && conn->err.code == VIR_ERR_NONE)
+ virNodeDeviceReportError(conn, VIR_ERR_XML_ERROR,
+ "%s", _("failed to parse xml
document"));
+ goto cleanup;
+ }
+
+ if ((root = xmlDocGetRootElement(xml)) == NULL) {
+ virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing root element"));
+ goto cleanup;
+ }
+
+ def = virNodeDeviceDefParseNode(conn, xml, root);
+
+cleanup:
+ xmlFreeParserCtxt(pctxt);
+ xmlFreeDoc(xml);
+ return def;
+}
+
void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
{
union _virNodeDevCapData *data = &caps->data;
diff --git a/src/node_device_conf.h b/src/node_device_conf.h
index 2024755..26e5558 100644
--- a/src/node_device_conf.h
+++ b/src/node_device_conf.h
@@ -184,7 +184,8 @@ void virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs,
char *virNodeDeviceDefFormat(virConnectPtr conn,
const virNodeDeviceDefPtr def);
-// TODO: virNodeDeviceDefParseString/File/Node for virNodeDeviceCreate
+virNodeDeviceDefPtr virNodeDeviceDefParseString(virConnectPtr conn,
+ const char *str);
void virNodeDeviceDefFree(virNodeDeviceDefPtr def);
--
1.6.0.6