mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-02 11:21:12 +00:00
cc7499dd03
daniel
1238 lines
43 KiB
C
1238 lines
43 KiB
C
/*
|
|
* node_device_conf.c: config handling for node devices
|
|
*
|
|
* Copyright (C) 2008 Virtual Iron Software, Inc.
|
|
* Copyright (C) 2008 David F. Lively
|
|
*
|
|
* 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
|
|
*
|
|
* Author: David F. Lively <dlively@virtualiron.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include "virterror_internal.h"
|
|
#include "datatypes.h"
|
|
#include "memory.h"
|
|
|
|
#include "node_device_conf.h"
|
|
#include "memory.h"
|
|
#include "xml.h"
|
|
#include "util.h"
|
|
#include "buf.h"
|
|
#include "uuid.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NODEDEV
|
|
|
|
VIR_ENUM_IMPL(virNodeDevCap, VIR_NODE_DEV_CAP_LAST,
|
|
"system",
|
|
"pci",
|
|
"usb_device",
|
|
"usb",
|
|
"net",
|
|
"scsi_host",
|
|
"scsi",
|
|
"storage")
|
|
|
|
VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST,
|
|
"80203",
|
|
"80211")
|
|
|
|
VIR_ENUM_IMPL(virNodeDevHBACap, VIR_NODE_DEV_CAP_HBA_LAST,
|
|
"fc_host",
|
|
"vport_ops")
|
|
|
|
|
|
static int
|
|
virNodeDevCapsDefParseString(virConnectPtr conn,
|
|
const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
char **string,
|
|
virNodeDeviceDefPtr def,
|
|
const char *missing_error_fmt)
|
|
{
|
|
char *s;
|
|
|
|
s = virXPathString(conn, xpath, ctxt);
|
|
if (s == NULL) {
|
|
virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
missing_error_fmt,
|
|
def->name);
|
|
return -1;
|
|
}
|
|
|
|
*string = s;
|
|
return 0;
|
|
}
|
|
|
|
|
|
virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs,
|
|
const char *name)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < devs->count; i++) {
|
|
virNodeDeviceObjLock(devs->objs[i]);
|
|
if (STREQ(devs->objs[i]->def->name, name))
|
|
return devs->objs[i];
|
|
virNodeDeviceObjUnlock(devs->objs[i]);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void virNodeDeviceDefFree(virNodeDeviceDefPtr def)
|
|
{
|
|
virNodeDevCapsDefPtr caps;
|
|
|
|
if (!def)
|
|
return;
|
|
|
|
VIR_FREE(def->name);
|
|
VIR_FREE(def->parent);
|
|
VIR_FREE(def->driver);
|
|
|
|
caps = def->caps;
|
|
while (caps) {
|
|
virNodeDevCapsDefPtr next = caps->next;
|
|
virNodeDevCapsDefFree(caps);
|
|
caps = next;
|
|
}
|
|
|
|
VIR_FREE(def);
|
|
}
|
|
|
|
void virNodeDeviceObjFree(virNodeDeviceObjPtr dev)
|
|
{
|
|
if (!dev)
|
|
return;
|
|
|
|
VIR_FREE(dev->devicePath);
|
|
virNodeDeviceDefFree(dev->def);
|
|
if (dev->privateFree)
|
|
(*dev->privateFree)(dev->privateData);
|
|
|
|
virMutexDestroy(&dev->lock);
|
|
|
|
VIR_FREE(dev);
|
|
}
|
|
|
|
void virNodeDeviceObjListFree(virNodeDeviceObjListPtr devs)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0 ; i < devs->count ; i++)
|
|
virNodeDeviceObjFree(devs->objs[i]);
|
|
VIR_FREE(devs->objs);
|
|
devs->count = 0;
|
|
}
|
|
|
|
virNodeDeviceObjPtr virNodeDeviceAssignDef(virConnectPtr conn,
|
|
virNodeDeviceObjListPtr devs,
|
|
const virNodeDeviceDefPtr def)
|
|
{
|
|
virNodeDeviceObjPtr device;
|
|
|
|
if ((device = virNodeDeviceFindByName(devs, def->name))) {
|
|
virNodeDeviceDefFree(device->def);
|
|
device->def = def;
|
|
return device;
|
|
}
|
|
|
|
if (VIR_ALLOC(device) < 0) {
|
|
virReportOOMError(conn);
|
|
return NULL;
|
|
}
|
|
|
|
if (virMutexInit(&device->lock) < 0) {
|
|
virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("cannot initialize mutex"));
|
|
VIR_FREE(device);
|
|
return NULL;
|
|
}
|
|
virNodeDeviceObjLock(device);
|
|
device->def = def;
|
|
|
|
if (VIR_REALLOC_N(devs->objs, devs->count+1) < 0) {
|
|
device->def = NULL;
|
|
virNodeDeviceObjUnlock(device);
|
|
virNodeDeviceObjFree(device);
|
|
virReportOOMError(conn);
|
|
return NULL;
|
|
}
|
|
devs->objs[devs->count++] = device;
|
|
|
|
return device;
|
|
|
|
}
|
|
|
|
void virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs,
|
|
const virNodeDeviceObjPtr dev)
|
|
{
|
|
unsigned int i;
|
|
|
|
virNodeDeviceObjUnlock(dev);
|
|
|
|
for (i = 0; i < devs->count; i++) {
|
|
virNodeDeviceObjLock(dev);
|
|
if (devs->objs[i] == dev) {
|
|
virNodeDeviceObjUnlock(dev);
|
|
virNodeDeviceObjFree(devs->objs[i]);
|
|
|
|
if (i < (devs->count - 1))
|
|
memmove(devs->objs + i, devs->objs + i + 1,
|
|
sizeof(*(devs->objs)) * (devs->count - (i + 1)));
|
|
|
|
if (VIR_REALLOC_N(devs->objs, devs->count - 1) < 0) {
|
|
; /* Failure to reduce memory allocation isn't fatal */
|
|
}
|
|
devs->count--;
|
|
|
|
break;
|
|
}
|
|
virNodeDeviceObjUnlock(dev);
|
|
}
|
|
}
|
|
|
|
char *virNodeDeviceDefFormat(virConnectPtr conn,
|
|
const virNodeDeviceDefPtr def)
|
|
{
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
virNodeDevCapsDefPtr caps = def->caps;
|
|
char *tmp;
|
|
|
|
virBufferAddLit(&buf, "<device>\n");
|
|
virBufferEscapeString(&buf, " <name>%s</name>\n", def->name);
|
|
|
|
if (def->parent)
|
|
virBufferEscapeString(&buf, " <parent>%s</parent>\n", def->parent);
|
|
if (def->driver) {
|
|
virBufferAddLit(&buf, " <driver>\n");
|
|
virBufferEscapeString(&buf, " <name>%s</name>\n", def->driver);
|
|
virBufferAddLit(&buf, " </driver>\n");
|
|
}
|
|
|
|
for (caps = def->caps; caps; caps = caps->next) {
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
union _virNodeDevCapData *data = &caps->data;
|
|
|
|
virBufferVSprintf(&buf, " <capability type='%s'>\n",
|
|
virNodeDevCapTypeToString(caps->type));
|
|
switch (caps->type) {
|
|
case VIR_NODE_DEV_CAP_SYSTEM:
|
|
if (data->system.product_name)
|
|
virBufferEscapeString(&buf, " <product>%s</product>\n",
|
|
data->system.product_name);
|
|
virBufferAddLit(&buf, " <hardware>\n");
|
|
if (data->system.hardware.vendor_name)
|
|
virBufferEscapeString(&buf, " <vendor>%s</vendor>\n",
|
|
data->system.hardware.vendor_name);
|
|
if (data->system.hardware.version)
|
|
virBufferEscapeString(&buf, " <version>%s</version>\n",
|
|
data->system.hardware.version);
|
|
if (data->system.hardware.serial)
|
|
virBufferEscapeString(&buf, " <serial>%s</serial>\n",
|
|
data->system.hardware.serial);
|
|
virUUIDFormat(data->system.hardware.uuid, uuidstr);
|
|
virBufferVSprintf(&buf, " <uuid>%s</uuid>\n", uuidstr);
|
|
virBufferAddLit(&buf, " </hardware>\n");
|
|
virBufferAddLit(&buf, " <firmware>\n");
|
|
if (data->system.firmware.vendor_name)
|
|
virBufferEscapeString(&buf, " <vendor>%s</vendor>\n",
|
|
data->system.firmware.vendor_name);
|
|
if (data->system.firmware.version)
|
|
virBufferEscapeString(&buf, " <version>%s</version>\n",
|
|
data->system.firmware.version);
|
|
if (data->system.firmware.release_date)
|
|
virBufferEscapeString(&buf,
|
|
" <release_date>%s</release_date>\n",
|
|
data->system.firmware.release_date);
|
|
virBufferAddLit(&buf, " </firmware>\n");
|
|
break;
|
|
case VIR_NODE_DEV_CAP_PCI_DEV:
|
|
virBufferVSprintf(&buf, " <domain>%d</domain>\n",
|
|
data->pci_dev.domain);
|
|
virBufferVSprintf(&buf, " <bus>%d</bus>\n", data->pci_dev.bus);
|
|
virBufferVSprintf(&buf, " <slot>%d</slot>\n",
|
|
data->pci_dev.slot);
|
|
virBufferVSprintf(&buf, " <function>%d</function>\n",
|
|
data->pci_dev.function);
|
|
virBufferVSprintf(&buf, " <product id='0x%04x'",
|
|
data->pci_dev.product);
|
|
if (data->pci_dev.product_name)
|
|
virBufferEscapeString(&buf, ">%s</product>\n",
|
|
data->pci_dev.product_name);
|
|
else
|
|
virBufferAddLit(&buf, " />\n");
|
|
virBufferVSprintf(&buf, " <vendor id='0x%04x'",
|
|
data->pci_dev.vendor);
|
|
if (data->pci_dev.vendor_name)
|
|
virBufferEscapeString(&buf, ">%s</vendor>\n",
|
|
data->pci_dev.vendor_name);
|
|
else
|
|
virBufferAddLit(&buf, " />\n");
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_DEV:
|
|
virBufferVSprintf(&buf, " <bus>%d</bus>\n", data->usb_dev.bus);
|
|
virBufferVSprintf(&buf, " <device>%d</device>\n",
|
|
data->usb_dev.device);
|
|
virBufferVSprintf(&buf, " <product id='0x%04x'",
|
|
data->usb_dev.product);
|
|
if (data->usb_dev.product_name)
|
|
virBufferEscapeString(&buf, ">%s</product>\n",
|
|
data->usb_dev.product_name);
|
|
else
|
|
virBufferAddLit(&buf, " />\n");
|
|
virBufferVSprintf(&buf, " <vendor id='0x%04x'",
|
|
data->usb_dev.vendor);
|
|
if (data->usb_dev.vendor_name)
|
|
virBufferEscapeString(&buf, ">%s</vendor>\n",
|
|
data->usb_dev.vendor_name);
|
|
else
|
|
virBufferAddLit(&buf, " />\n");
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_INTERFACE:
|
|
virBufferVSprintf(&buf, " <number>%d</number>\n",
|
|
data->usb_if.number);
|
|
virBufferVSprintf(&buf, " <class>%d</class>\n",
|
|
data->usb_if._class);
|
|
virBufferVSprintf(&buf, " <subclass>%d</subclass>\n",
|
|
data->usb_if.subclass);
|
|
virBufferVSprintf(&buf, " <protocol>%d</protocol>\n",
|
|
data->usb_if.protocol);
|
|
if (data->usb_if.description)
|
|
virBufferVSprintf(&buf, " <description>%s</description>\n",
|
|
data->usb_if.description);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_NET:
|
|
virBufferVSprintf(&buf, " <interface>%s</interface>\n",
|
|
data->net.ifname);
|
|
if (data->net.address)
|
|
virBufferVSprintf(&buf, " <address>%s</address>\n",
|
|
data->net.address);
|
|
if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) {
|
|
const char *subtyp =
|
|
virNodeDevNetCapTypeToString(data->net.subtype);
|
|
virBufferVSprintf(&buf, " <capability type='%s'/>\n", subtyp);
|
|
}
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI_HOST:
|
|
virBufferVSprintf(&buf, " <host>%d</host>\n",
|
|
data->scsi_host.host);
|
|
if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
|
|
virBufferAddLit(&buf, " <capability type='fc_host'>\n");
|
|
virBufferVSprintf(&buf,
|
|
" <wwnn>%s</wwnn>\n", data->scsi_host.wwnn);
|
|
virBufferVSprintf(&buf,
|
|
" <wwpn>%s</wwpn>\n", data->scsi_host.wwpn);
|
|
virBufferAddLit(&buf, " </capability>\n");
|
|
}
|
|
if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) {
|
|
virBufferAddLit(&buf, " <capability type='vport_ops' />\n");
|
|
}
|
|
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI:
|
|
virBufferVSprintf(&buf, " <host>%d</host>\n", data->scsi.host);
|
|
virBufferVSprintf(&buf, " <bus>%d</bus>\n", data->scsi.bus);
|
|
virBufferVSprintf(&buf, " <target>%d</target>\n",
|
|
data->scsi.target);
|
|
virBufferVSprintf(&buf, " <lun>%d</lun>\n", data->scsi.lun);
|
|
if (data->scsi.type)
|
|
virBufferVSprintf(&buf, " <type>%s</type>\n",
|
|
data->scsi.type);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_STORAGE:
|
|
virBufferVSprintf(&buf, " <block>%s</block>\n",
|
|
data->storage.block);
|
|
if (data->storage.bus)
|
|
virBufferVSprintf(&buf, " <bus>%s</bus>\n",
|
|
data->storage.bus);
|
|
if (data->storage.drive_type)
|
|
virBufferVSprintf(&buf, " <drive_type>%s</drive_type>\n",
|
|
data->storage.drive_type);
|
|
if (data->storage.model)
|
|
virBufferVSprintf(&buf, " <model>%s</model>\n",
|
|
data->storage.model);
|
|
if (data->storage.vendor)
|
|
virBufferVSprintf(&buf, " <vendor>%s</vendor>\n",
|
|
data->storage.vendor);
|
|
if (data->storage.serial)
|
|
virBufferVSprintf(&buf, " <serial>%s</serial>\n",
|
|
data->storage.serial);
|
|
if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE) {
|
|
int avl = data->storage.flags &
|
|
VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
|
|
virBufferAddLit(&buf, " <capability type='removable'>\n");
|
|
virBufferVSprintf(&buf,
|
|
" <media_available>%d"
|
|
"</media_available>\n", avl ? 1 : 0);
|
|
virBufferVSprintf(&buf, " <media_size>%llu</media_size>\n",
|
|
data->storage.removable_media_size);
|
|
virBufferAddLit(&buf, " </capability>\n");
|
|
} else {
|
|
virBufferVSprintf(&buf, " <size>%llu</size>\n",
|
|
data->storage.size);
|
|
}
|
|
if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE)
|
|
virBufferAddLit(&buf,
|
|
" <capability type='hotpluggable' />\n");
|
|
break;
|
|
case VIR_NODE_DEV_CAP_LAST:
|
|
/* ignore special LAST value */
|
|
break;
|
|
}
|
|
|
|
virBufferAddLit(&buf, " </capability>\n");
|
|
}
|
|
|
|
virBufferAddLit(&buf, "</device>\n");
|
|
|
|
if (virBufferError(&buf))
|
|
goto no_memory;
|
|
|
|
return virBufferContentAndReset(&buf);
|
|
|
|
no_memory:
|
|
virReportOOMError(conn);
|
|
tmp = virBufferContentAndReset(&buf);
|
|
VIR_FREE(tmp);
|
|
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);
|
|
data->storage.serial = virXPathString(conn, "string(./serial[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,
|
|
int create)
|
|
{
|
|
xmlNodePtr orignode, *nodes = NULL;
|
|
int ret = -1, n = 0, i;
|
|
char *type = NULL;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
if (create == EXISTING_DEVICE &&
|
|
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;
|
|
}
|
|
|
|
if ((n = virXPathNodeSet(conn, "./capability", ctxt, &nodes)) < 0) {
|
|
virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("error parsing SCSI host capabilities for '%s'"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0 ; i < n ; i++) {
|
|
type = virXMLPropString(nodes[i], "type");
|
|
|
|
if (!type) {
|
|
virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("missing SCSI host capability type for '%s'"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
|
|
if (STREQ(type, "vport_ops")) {
|
|
|
|
data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS;
|
|
|
|
} else if (STREQ(type, "fc_host")) {
|
|
|
|
xmlNodePtr orignode2;
|
|
|
|
data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST;
|
|
|
|
orignode2 = ctxt->node;
|
|
ctxt->node = nodes[i];
|
|
|
|
if (virNodeDevCapsDefParseString(conn, "string(./wwnn[1])",
|
|
ctxt,
|
|
&data->scsi_host.wwnn,
|
|
def,
|
|
_("no WWNN supplied for '%s'")) < 0) {
|
|
goto out;
|
|
}
|
|
|
|
if (virNodeDevCapsDefParseString(conn, "string(./wwpn[1])",
|
|
ctxt,
|
|
&data->scsi_host.wwpn,
|
|
def,
|
|
_("no WWPN supplied for '%s'")) < 0) {
|
|
goto out;
|
|
}
|
|
|
|
ctxt->node = orignode2;
|
|
|
|
} else {
|
|
virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
_("unknown SCSI host capability type '%s' for '%s'"),
|
|
type, def->name);
|
|
goto out;
|
|
}
|
|
|
|
VIR_FREE(type);
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
VIR_FREE(type);
|
|
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,
|
|
int create)
|
|
{
|
|
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, create);
|
|
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, int create)
|
|
{
|
|
virNodeDeviceDefPtr def;
|
|
virNodeDevCapsDefPtr *next_cap;
|
|
xmlNodePtr *nodes;
|
|
int n, i;
|
|
|
|
if (VIR_ALLOC(def) < 0) {
|
|
virReportOOMError(conn);
|
|
return NULL;
|
|
}
|
|
|
|
/* Extract device name */
|
|
if (create == EXISTING_DEVICE) {
|
|
def->name = virXPathString(conn, "string(./name[1])", ctxt);
|
|
} else {
|
|
def->name = strdup("new device");
|
|
}
|
|
|
|
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], create);
|
|
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, int create)
|
|
{
|
|
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, create);
|
|
|
|
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, int create)
|
|
{
|
|
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, create);
|
|
|
|
cleanup:
|
|
xmlFreeParserCtxt(pctxt);
|
|
xmlFreeDoc(xml);
|
|
return def;
|
|
}
|
|
|
|
void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
|
|
{
|
|
union _virNodeDevCapData *data = &caps->data;
|
|
|
|
switch (caps->type) {
|
|
case VIR_NODE_DEV_CAP_SYSTEM:
|
|
VIR_FREE(data->system.product_name);
|
|
VIR_FREE(data->system.hardware.vendor_name);
|
|
VIR_FREE(data->system.hardware.version);
|
|
VIR_FREE(data->system.hardware.serial);
|
|
VIR_FREE(data->system.firmware.vendor_name);
|
|
VIR_FREE(data->system.firmware.version);
|
|
VIR_FREE(data->system.firmware.release_date);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_PCI_DEV:
|
|
VIR_FREE(data->pci_dev.product_name);
|
|
VIR_FREE(data->pci_dev.vendor_name);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_DEV:
|
|
VIR_FREE(data->usb_dev.product_name);
|
|
VIR_FREE(data->usb_dev.vendor_name);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_INTERFACE:
|
|
VIR_FREE(data->usb_if.description);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_NET:
|
|
VIR_FREE(data->net.ifname);
|
|
VIR_FREE(data->net.address);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI_HOST:
|
|
VIR_FREE(data->scsi_host.wwnn);
|
|
VIR_FREE(data->scsi_host.wwpn);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI:
|
|
VIR_FREE(data->scsi.type);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_STORAGE:
|
|
VIR_FREE(data->storage.block);
|
|
VIR_FREE(data->storage.bus);
|
|
VIR_FREE(data->storage.drive_type);
|
|
VIR_FREE(data->storage.model);
|
|
VIR_FREE(data->storage.vendor);
|
|
VIR_FREE(data->storage.serial);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_LAST:
|
|
/* This case is here to shutup the compiler */
|
|
break;
|
|
}
|
|
|
|
VIR_FREE(caps);
|
|
}
|
|
|
|
|
|
void virNodeDeviceObjLock(virNodeDeviceObjPtr obj)
|
|
{
|
|
virMutexLock(&obj->lock);
|
|
}
|
|
|
|
void virNodeDeviceObjUnlock(virNodeDeviceObjPtr obj)
|
|
{
|
|
virMutexUnlock(&obj->lock);
|
|
}
|