various english fixes (Bruce Montague), OCaml docs links (Richard Jones),
describe the various bindings add Ruby link, Windows support page
(Richard Jones), authentication documentation updates (Daniel Berrange)
-
+
- Bug fixes: NUMA topology error handling (Beth Kon), NUMA topology
cells without CPU (Beth Kon), XML to/from XM bridge config (Daniel
Berrange), XM processing of vnc parameters (Daniel Berrange), Reset
parameter setting in XM config (Saori Fukuta), credential handling
fixes (Daniel Berrange), fix compatibility with Xen 3.2.0 (Daniel
Berrange)
-
+
- Improvements: /etc/libvirt/qemu.conf configuration for QEMU driver
(Daniel Berrange), NUMA cpu pinning in config files (DV and Saori Fukuta),
CDRom media change in KVM/QEMU (Daniel Berrange), tests for
--without-libvirtd config option (Richard Jones), Python bindings for
NUMA, add extra utility functions to buffer (Richard Jones),
separate qparams module for handling query parameters (Richard Jones)
-
+
- Code cleanups: remove virDomainRestart from API as it was never used
(Richard Jones), constify params for attach/detach APIs (Daniel Berrange),
gcc printf attribute checkings (Jim Meyering), refactoring of device
port (Richard Jones), disable the proxy if using PolicyKit, readline
availability detection, test libvirtd's config-processing code (Jim
Meyering), use a variable name as sizeof argument (Jim Meyering)
-
+
0.3.3: Sep 30 2007:
- New features: Avahi mDNS daemon export (Daniel Berrange),
- NUMA support (Beth Kan)
- - Documentation: cleanups (Toth Istvan), typos (Eduardo Pereira),
+ NUMA support (Beth Kan)
+ - Documentation: cleanups (Toth Istvan), typos (Eduardo Pereira),
- Bug fixes: memory corruption on large dumps (Masayuki Sunou), fix
virsh vncdisplay command exit (Masayuki Sunou), Fix network stats
TX/RX result (Richard Jones), warning on Xen 3.0.3 (Richard Jones),
speedup of domain queries on Xen (Daniel berrange), augment XML dumps
with interface devices names (Richard Jones), internal API to query
drivers for features (Richard Jones).
-
+
- Cleanups: Improve virNodeGetInfo implentation (Daniel berrange),
general UUID code cleanup (Daniel berrange), fix API generator
- file selection.
+ file selection.
0.3.1: Jul 24 2007:
- Secure Remote support (Richard Jones).
See the remote page
of the documentation
-
+
- Documentation: remote support (Richard Jones), description of
the URI connection strings (Richard Jones), update of virsh man
page, matrix of libvirt API/hypervisor support with version
0.2.3: Jun 8 2007:
- Documentation: documentation for upcoming remote access (Richard Jones),
virConnectNumOfDefinedDomains doc (Jan Michael), virsh help messages
- for dumpxml and net-dumpxml (Chris Wright),
+ for dumpxml and net-dumpxml (Chris Wright),
- Bug fixes: RelaxNG schemas regexp fix (Robin Green), RelaxNG arch bug
(Mark McLoughlin), large buffers bug fixes (Shigeki Sakamoto), error
on out of memory condition (Shigeki Sakamoto), virshStrdup fix, non-root
signal handler error cleanup (Richard Jones), iptables internal code
claenup (Mark McLoughlin), unified Xen driver (Richard Jones),
cleanup XPath libxml2 calls, IPTables rules tightening (Daniel
- Berrange),
+ Berrange),
- Improvements: more regression tests on XML (Daniel Berrange), Python
bindings now generate exception in error cases (Richard Jones),
Python bindings for vir*GetAutoStart (Daniel Berrange),
fix hypervisor call to work with Xen 3.0.5 (Daniel Berrange),
DomainGetOSType for inactive domains (Daniel Berrange), multiple boot
devices for HVM (Daniel Berrange),
-
+
0.2.1: Mar 16 2007:
- Support for localization of strings using gettext (Daniel Berrange)
- Support for new Xen-3.0.3 cdrom and disk configuration (Daniel Berrange)
- Support for setting VNC port when creating domains with new
- xend config files (Daniel Berrange)
+ xend config files (Daniel Berrange)
- Fix bug when running against xen-3.0.2 hypercalls (Jim Fehlig)
- Fix reconnection problem when talking directly to http xend
floppy and cdrom (Daniel Berrange), features block in XML to report/ask
PAE, ACPI, APIC for HVM domains (Daniel Berrange), fail saide-effect
operations when using read-only connection, large improvements to test
- driver (Daniel Berrange)
+ driver (Daniel Berrange)
- documentation: spelling (Daniel Berrange), test driver examples.
static int nconnections = 0;
static time_t lastRefresh = 0;
+char * xenXMAutoAssignMac(void);
+static int xenXMAttachDisk(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm,
+ xmlNodePtr node, xenXMConfCachePtr entry);
+static int xenXMAttachInterface(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm,
+ xmlNodePtr node, xenXMConfCachePtr entry);
+
#define XM_REFRESH_INTERVAL 10
#define XM_CONFIG_DIR "/etc/xen"
#define XEND_CONFIG_FILE "xend-config.sxp"
#define XEND_PCI_CONFIG_PREFIX "xend-pci-"
#define QEMU_IF_SCRIPT "qemu-ifup"
+#define XM_XML_ERROR "Invalid xml"
struct xenUnifiedDriver xenXMDriver = {
xenXMOpen, /* open */
xenXMDomainCreate, /* domainCreate */
xenXMDomainDefineXML, /* domainDefineXML */
xenXMDomainUndefine, /* domainUndefine */
- NULL, /* domainAttachDevice */
- NULL, /* domainDetachDevice */
+ xenXMDomainAttachDevice, /* domainAttachDevice */
+ xenXMDomainDetachDevice, /* domainDetachDevice */
NULL, /* domainGetAutostart */
NULL, /* domainSetAutostart */
NULL, /* domainGetSchedulerType */
return virHashSize(nameConfigMap);
}
+/**
+ * xenXMDomainAttachDevice:
+ * @domain: pointer to domain object
+ * @xml: pointer to XML description of device
+ *
+ * Create a virtual device attachment to backend.
+ * XML description is translated into config file.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+static int
+xenXMDomainAttachDevice(virDomainPtr domain, const char *xml) {
+ const char *filename = NULL;
+ xenXMConfCachePtr entry = NULL;
+ xmlDocPtr doc = NULL;
+ xmlNodePtr node = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlXPathObjectPtr obj = NULL;
+ char *domxml = NULL;
+ int ret = -1, hvm = 0;
+
+ if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) {
+ xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
+ __FUNCTION__);
+ goto cleanup;
+ }
+ if (domain->conn->flags & VIR_CONNECT_RO)
+ goto cleanup;
+ if (domain->id != -1)
+ goto cleanup;
+ if (!(filename = virHashLookup(nameConfigMap, domain->name)))
+ goto cleanup;
+ if (!(entry = virHashLookup(configCache, filename)))
+ goto cleanup;
+ if (!(entry->conf))
+ goto cleanup;
+
+ if (!(domxml = xenXMDomainDumpXML(domain, 0)))
+ goto cleanup;
+
+ doc = xmlReadDoc((const xmlChar *) domxml, "domain.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
+ if (!doc) {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR, "cannot read XML domain definition");
+ goto cleanup;
+ }
+ if (!(ctxt = xmlXPathNewContext(doc))) {
+ xenXMError(domain->conn, VIR_ERR_INTERNAL_ERROR, "cannot create XPath context");
+ goto cleanup;
+ }
+ obj = xmlXPathEval(BAD_CAST "string(/domain/os/type)", ctxt);
+ if ((obj != NULL) && (obj->type == XPATH_STRING) &&
+ (obj->stringval) && (STREQ((char *)obj->stringval, "hvm")))
+ hvm = 1;
+
+ if (ctxt)
+ xmlXPathFreeContext(ctxt);
+ ctxt = NULL;
+ if (doc)
+ xmlFreeDoc(doc);
+ doc = xmlReadDoc((const xmlChar *) xml, "device.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
+ if (!doc) {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR,
+ "cannot read XML domain definition");
+ goto cleanup;
+ }
+ if (!(ctxt = xmlXPathNewContext(doc))) {
+ xenXMError(domain->conn, VIR_ERR_INTERNAL_ERROR,
+ "cannot create XPath context");
+ goto cleanup;
+ }
+
+ if ((node = virXPathNode("/disk", ctxt))) {
+ if (xenXMAttachDisk(domain, ctxt, hvm, node, entry))
+ goto cleanup;
+ } else if ((node = virXPathNode("/interface", ctxt))) {
+ if (xenXMAttachInterface(domain, ctxt, hvm, node, entry))
+ goto cleanup;
+ } else {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR, "unknown device");
+ goto cleanup;
+ }
+
+ /* If this fails, should we try to undo our changes to the
+ * in-memory representation of the config file. I say not!
+ */
+ if (virConfWriteFile(entry->filename, entry->conf) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ if (domxml)
+ free(domxml);
+ if (obj)
+ xmlXPathFreeObject(obj);
+ if (ctxt)
+ xmlXPathFreeContext(ctxt);
+ if (doc)
+ xmlFreeDoc(doc);
+
+ return ret;
+}
+
+static int
+xenXMAttachDisk(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm,
+ xmlNodePtr node, xenXMConfCachePtr entry) {
+ virConfValuePtr list_item = NULL, list_val = NULL, prev = NULL;
+ xenUnifiedPrivatePtr priv = NULL;
+ xmlChar *type = NULL, *source = NULL, *target = NULL;
+ int ret = -1;
+ char *dev;
+
+ priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
+ xenXMParseXMLDisk(node, hvm, ((xenUnifiedPrivatePtr) domain->conn->privateData)->xendConfigVersion, &dev);
+ if (!dev)
+ goto cleanup;
+
+ if (!(type = xmlGetProp(node, BAD_CAST "type"))) {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
+ goto cleanup;
+ }
+ if (!(node = virXPathNode("/disk/source", ctxt))) {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
+ goto cleanup;
+ }
+ if (!strcmp((const char *) type, "block"))
+ source = xmlGetProp(node, BAD_CAST "dev");
+ else if (!strcmp((const char *) type, "file"))
+ source = xmlGetProp(node, BAD_CAST "file");
+ else {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
+ goto cleanup;
+ }
+ if (!(node = virXPathNode("/disk/target", ctxt))) {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
+ goto cleanup;
+ }
+ target = xmlGetProp(node, BAD_CAST "dev");
+
+ list_item = virConfGetValue(entry->conf, "disk");
+ if (list_item && list_item->type == VIR_CONF_LIST) {
+ prev = list_item;
+ list_val = list_item->list;
+ while (list_val) {
+ if ((list_val->type != VIR_CONF_STRING) || (!list_val->str))
+ goto skip;
+ char domdev[NAME_MAX];
+ char *head;
+ char *offset;
+ char *tmp;
+
+ head = list_val->str;
+
+ /* Extract the source */
+ if (!(offset = strchr(head, ',')) || offset[0] == '\0')
+ goto skip;
+ if ((offset - head) >= (PATH_MAX-1))
+ goto skip;
+ head = offset + 1;
+
+ /* Extract the dest */
+ if (!(offset = strchr(head, ',')) || offset[0] == '\0')
+ goto skip;
+ if ((offset - head) >= (PATH_MAX-1))
+ goto skip;
+ strncpy(domdev, head, (offset - head));
+ domdev[(offset-head)] = '\0';
+ head = offset + 1;
+
+ /* Remove legacy ioemu: junk */
+ if (!strncmp(domdev, "ioemu:", 6)) {
+ memmove(domdev, domdev+6, strlen(domdev)-5);
+ }
+
+ /* Check for a :cdrom/:disk postfix */
+ if ((tmp = strchr(domdev, ':')))
+ tmp[0] = '\0';
+
+ if (!(strcmp(domdev, (const char *) target)))
+ break;
+ skip:
+ prev = list_val;
+ list_val = list_val->next;
+ }
+ } else if (!list_item) {
+ if (!(list_item = calloc(1, sizeof(virConfValue))))
+ goto cleanup;
+ list_item->type = VIR_CONF_LIST;
+ if(virConfSetValue(entry->conf, "disk", list_item)) {
+ free(list_item);
+ goto cleanup;
+ }
+ list_val = NULL;
+ prev = list_item;
+ } else
+ goto cleanup;
+
+ if (!list_val) {
+ /* insert */
+ if (!(list_val = malloc(sizeof(virConfValue))))
+ goto cleanup;
+ list_val->type = VIR_CONF_STRING;
+ list_val->next = NULL;
+ list_val->str = dev;
+ if (prev->type == VIR_CONF_LIST)
+ prev->list = list_val;
+ else
+ prev->next = list_val;
+ } else {
+ /* configure */
+ if (list_val->str)
+ free(list_val->str);
+ list_val->str = dev;
+ }
+
+ ret = 0;
+ goto cleanup;
+
+ cleanup:
+ if (type)
+ free(type);
+ if (source)
+ free(source);
+ if (target)
+ free(target);
+
+ return (ret);
+}
+
+static int
+xenXMAttachInterface(virDomainPtr domain, xmlXPathContextPtr ctxt, int hvm,
+ xmlNodePtr node, xenXMConfCachePtr entry) {
+ virConfValuePtr list_item = NULL, list_val = NULL, prev = NULL;
+ xmlChar *type = NULL, *source = NULL, *mac = NULL;
+ int ret = -1, autoassign = 0;
+ char *dev;
+
+ xmlNodePtr node_cur = NULL, node_tmp = NULL;
+ xmlAttrPtr attr_node = NULL;
+ xmlNodePtr text_node = NULL;
+
+ if(!(type = xmlGetProp(node, BAD_CAST "type"))) {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
+ goto cleanup;
+ }
+
+ if (!(node = virXPathNode("/interface/source", ctxt))) {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR, XM_XML_ERROR);
+ goto cleanup;
+ }
+ source = xmlGetProp(node, BAD_CAST type);
+
+ if (!(node = virXPathNode("/interface/mac", ctxt))) {
+ if (!(mac = (xmlChar *)xenXMAutoAssignMac()))
+ goto cleanup;
+ autoassign = 1;
+ } else
+ mac = xmlGetProp(node, BAD_CAST "address");
+
+ list_item = virConfGetValue(entry->conf, "vif");
+ if (list_item && list_item->type == VIR_CONF_LIST) {
+ prev = list_item;
+ list_val = list_item->list;
+ while (list_val) {
+ if ((list_val->type != VIR_CONF_STRING) || (!list_val->str))
+ goto skip;
+ char dommac[18];
+ char *key;
+
+ dommac[0] = '\0';
+
+ key = list_val->str;
+ while (key) {
+ char *data;
+ char *nextkey = strchr(key, ',');
+
+ if (!(data = strchr(key, '=')) || (data[0] == '\0'))
+ goto skip;
+ data++;
+
+ if (!strncmp(key, "mac=", 4)) {
+ int len = nextkey ? (nextkey - data) : 17;
+ if (len > 17)
+ len = 17;
+ strncpy(dommac, data, len);
+ dommac[len] = '\0';
+ }
+
+ while (nextkey && (nextkey[0] == ',' ||
+ nextkey[0] == ' ' ||
+ nextkey[0] == '\t'))
+ nextkey++;
+ key = nextkey;
+ }
+
+ if (!(strcmp(dommac, (const char *) mac))) {
+ if (autoassign) {
+ if (mac)
+ free(mac);
+ mac = NULL;
+ if (!(mac = (xmlChar *)xenXMAutoAssignMac()))
+ goto cleanup;
+ /* initialize the list */
+ list_item = virConfGetValue(entry->conf, "vif");
+ prev = list_item;
+ list_val = list_item->list;
+ continue;
+ } else
+ break;
+ }
+ skip:
+ prev = list_val;
+ list_val = list_val->next;
+ }
+ } else if (!list_item) {
+ if (!(list_item = calloc(1, sizeof(virConfValue))))
+ goto cleanup;
+ list_item->type = VIR_CONF_LIST;
+ if(virConfSetValue(entry->conf, "vif", list_item)) {
+ free(list_item);
+ goto cleanup;
+ }
+ list_val = NULL;
+ prev = list_item;
+ } else
+ goto cleanup;
+
+ if ((node = virXPathNode("/interface", ctxt))) {
+ if (autoassign) {
+ node_cur = node->children;
+
+ while (node_cur->next)
+ node_cur = node_cur->next;
+
+ if (!(node_tmp = calloc(1, sizeof(xmlNode))))
+ goto node_cleanup;
+ node_tmp->type = XML_ELEMENT_NODE;
+ if (!(node_tmp->name = malloc(4)))
+ goto node_cleanup;
+ strcpy((char *)node_tmp->name, "mac");
+ node_tmp->children = NULL;
+
+ if (!(attr_node = calloc(1, sizeof(xmlAttr))))
+ goto node_cleanup;
+ attr_node->type = XML_ATTRIBUTE_NODE;
+ attr_node->ns = NULL;
+ if (!(attr_node->name = malloc(8)))
+ goto node_cleanup;
+ strcpy((char *) attr_node->name, "address");
+ node_tmp->properties = attr_node;
+
+ if (!(text_node = calloc(1, sizeof(xmlNode))))
+ goto node_cleanup;
+ text_node->type = XML_TEXT_NODE;
+ text_node->_private = NULL;
+ if (!(text_node->name = malloc(8)))
+ goto node_cleanup;
+ strcpy((char *) text_node->name, "text");
+ text_node->children = NULL;
+ text_node->parent = (xmlNodePtr)attr_node;
+ text_node->content = mac;
+ mac = NULL;
+ attr_node->children = text_node;
+ attr_node->last = text_node;
+ attr_node->parent = node_tmp;
+
+ node_cur->next = node_tmp;
+ }
+ if (!(dev = xenXMParseXMLVif(domain->conn, node, hvm)))
+ goto cleanup;
+ } else
+ goto cleanup;
+
+ if (!list_val) {
+ /* insert */
+ if (!(list_val = malloc(sizeof(virConfValue))))
+ goto cleanup;
+ list_val->type = VIR_CONF_STRING;
+ list_val->next = NULL;
+ list_val->str = dev;
+ if (prev->type == VIR_CONF_LIST)
+ prev->list = list_val;
+ else
+ prev->next = list_val;
+ } else {
+ /* configure */
+ if (list_val->str)
+ free(list_val->str);
+ list_val->str = dev;
+ }
+
+ ret = 0;
+ goto cleanup;
+
+ node_cleanup:
+ if (node_tmp)
+ xmlFree(node_tmp);
+ if (attr_node)
+ xmlFree(attr_node);
+ if (text_node)
+ xmlFree(text_node);
+ cleanup:
+ if (type)
+ free(type);
+ if (source)
+ free(source);
+ if (mac)
+ free(mac);
+
+ return (ret);
+}
+
+/**
+ * xenXMAutoAssignMac:
+ * @mac: pointer to Mac String
+ *
+ * a mac is assigned automatically.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+char *
+xenXMAutoAssignMac() {
+ char *buf;
+
+ if (!(buf = malloc(18)))
+ return 0;
+ srand((unsigned)time(NULL));
+ sprintf(buf, "00:16:3e:%02x:%02x:%02x"
+ ,1 + (int)(256*(rand()/(RAND_MAX+1.0)))
+ ,1 + (int)(256*(rand()/(RAND_MAX+1.0)))
+ ,1 + (int)(256*(rand()/(RAND_MAX+1.0))));
+ return buf;
+}
+
+/**
+ * xenXMDomainDetachDevice:
+ * @domain: pointer to domain object
+ * @xml: pointer to XML description of device
+ *
+ * Destroy a virtual device attachment to backend.
+ *
+ * Returns 0 in case of success, -1 in case of failure.
+ */
+static int
+xenXMDomainDetachDevice(virDomainPtr domain, const char *xml) {
+ const char *filename = NULL;
+ char device[8], *domdevice = NULL;
+ xenXMConfCachePtr entry = NULL;
+ virConfValuePtr prev = NULL, list_ptr = NULL, list_val = NULL;
+ xmlDocPtr doc = NULL;
+ xmlNodePtr node = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ xmlChar *key = NULL;
+ int ret = -1;
+
+ if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) {
+ xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
+ __FUNCTION__);
+ goto cleanup;
+ }
+ if (domain->conn->flags & VIR_CONNECT_RO)
+ goto cleanup;
+ if (domain->id != -1)
+ goto cleanup;
+ if (!(filename = virHashLookup(nameConfigMap, domain->name)))
+ goto cleanup;
+
+ doc = xmlReadDoc((const xmlChar *) xml, "device.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
+ if (!doc) {
+ xenXMError(domain->conn, VIR_ERR_XML_ERROR, "cannot read XML domain definition");
+ goto cleanup;
+ }
+ if (!(ctxt = xmlXPathNewContext(doc))) {
+ xenXMError(domain->conn, VIR_ERR_INTERNAL_ERROR, "cannot create XPath context");
+ goto cleanup;
+ }
+
+ if ((node = virXPathNode("/disk", ctxt))) {
+ strcpy(device, "disk");
+ if (!(node = virXPathNode("/disk/target", ctxt)))
+ goto cleanup;
+ key = xmlGetProp(node, BAD_CAST "dev");
+ } else if ((node = virXPathNode("/interface", ctxt))) {
+ strcpy(device, "vif");
+ if (!(node = virXPathNode("/interface/mac", ctxt)))
+ goto cleanup;
+ key = xmlGetProp(node, BAD_CAST "address");
+ } else
+ goto cleanup;
+ if (!key || (strlen((char *)key) == 0))
+ goto cleanup;
+
+ if (!(entry = virHashLookup(configCache, filename)))
+ goto cleanup;
+ if (!entry->conf)
+ goto cleanup;
+
+ list_ptr = virConfGetValue(entry->conf, device);
+ if (!list_ptr)
+ goto cleanup;
+ else if (list_ptr && list_ptr->type == VIR_CONF_LIST) {
+ list_val = list_ptr->list;
+ while (list_val) {
+ if (!(strcmp(device, "disk"))) {
+ char domdev[NAME_MAX];
+ char *head;
+ char *offset;
+ char *tmp;
+
+ if ((list_val->type != VIR_CONF_STRING) || (!list_val->str))
+ goto skip;
+ head = list_val->str;
+
+ /* Extract the source */
+ if (!(offset = strchr(head, ',')) || offset[0] == '\0')
+ goto skip;
+ if ((offset - head) >= (PATH_MAX-1))
+ goto skip;
+ head = offset + 1;
+
+ /* Extract the dest */
+ if (!(offset = strchr(head, ',')) || offset[0] == '\0')
+ goto skip;
+ if ((offset - head) >= (PATH_MAX-1))
+ goto skip;
+ strncpy(domdev, head, (offset - head));
+ domdev[(offset-head)] = '\0';
+ head = offset + 1;
+
+ /* Remove legacy ioemu: junk */
+ if (!strncmp(domdev, "ioemu:", 6)) {
+ memmove(domdev, domdev+6, strlen(domdev)-5);
+ }
+
+ /* Check for a :cdrom/:disk postfix */
+ if ((tmp = strchr(domdev, ':')))
+ tmp[0] = '\0';
+
+ if (!(strcmp(domdev, (const char *) key)))
+ break;
+ } else {
+ char dommac[18];
+ char *mac;
+
+ dommac[0] = '\0';
+
+ if ((list_val->type != VIR_CONF_STRING) || (!list_val->str))
+ goto skip;
+
+ mac = list_val->str;
+ while (mac) {
+ char *data;
+ char *nextmac = strchr(mac, ',');
+
+ if (!(data = strchr(mac, '=')) || (data[0] == '\0'))
+ goto skip;
+ data++;
+
+ if (!strncmp(mac, "mac=", 4)) {
+ int len = nextmac ? (nextmac - data) : 17;
+ if (len > 17)
+ len = 17;
+ strncpy(dommac, data, len);
+ dommac[len] = '\0';
+ }
+
+ while (nextmac && (nextmac[0] == ',' ||
+ nextmac[0] == ' ' ||
+ nextmac[0] == '\t'))
+ nextmac++;
+ mac = nextmac;
+ }
+
+ if (!(strcmp(dommac, (const char *) key)))
+ break;
+ }
+ skip:
+ prev = list_val;
+ list_val = list_val->next;
+ }
+ }
+
+ if (!list_val)
+ goto cleanup;
+ else {
+ if (!prev) {
+ virConfValuePtr value;
+ if (!(value = calloc(1, sizeof(virConfValue))))
+ goto cleanup;
+ value->type = VIR_CONF_LIST;
+ value->list = list_val->next;
+ list_val->next = NULL;
+ if (virConfSetValue(entry->conf, device, value)) {
+ free(value);
+ goto cleanup;
+ }
+ } else
+ prev->next = list_val->next;
+ }
+
+ /* If this fails, should we try to undo our changes to the
+ * in-memory representation of the config file. I say not!
+ */
+ if (virConfWriteFile(entry->filename, entry->conf) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ if (ctxt)
+ xmlXPathFreeContext(ctxt);
+ if (doc)
+ xmlFreeDoc(doc);
+ if (domdevice)
+ free(domdevice);
+ if (key)
+ free(key);
+ if (list_val)
+ free(list_val);
+
+ return (ret);
+}
+
#endif /* WITH_XEN */
/*
* Local variables: