]> xenbits.xensource.com Git - libvirt.git/commitdiff
add/change/delete a Disk/NIC of an inactive domains
authorDaniel Veillard <veillard@redhat.com>
Wed, 6 Feb 2008 17:57:10 +0000 (17:57 +0000)
committerDaniel Veillard <veillard@redhat.com>
Wed, 6 Feb 2008 17:57:10 +0000 (17:57 +0000)
* src/xm_internal.[ch]: applied patch from Shigeki Sakamoto to
  add/change/delete a Disk/NIC of an inactive domains
Daniel

ChangeLog
NEWS
src/xm_internal.c
src/xm_internal.h

index 74ebae576128c3a191bf6a215bc8ab9d916c269d..b761bf0172d0a08e6196b23a1d94bfc78f4b1a9f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Wed Feb  6 18:55:37 CET 2008 Daniel Veillard <veillard@redhat.com>
+
+       * src/xm_internal.[ch]: applied patch from Shigeki Sakamoto to 
+         add/change/delete a Disk/NIC of an inactive domains
+
 Wed Feb  6 17:22:34 CET 2008 Daniel Veillard <veillard@redhat.com>
 
        * src/qemu_conf.c: applied 2 patches from Guido Guenther to avoid 
diff --git a/NEWS b/NEWS
index fb4878f68fbfef0504a6e12e05b62a34fe1b5725..581acb6aeb60d09658817c7142ea74c70041fe9f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,7 +17,7 @@ Releases
       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
@@ -42,7 +42,7 @@ Releases
       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
@@ -51,7 +51,7 @@ Releases
       --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
@@ -72,13 +72,13 @@ Releases
       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),
@@ -124,10 +124,10 @@ Releases
       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:
@@ -161,7 +161,7 @@ Releases
    - 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
@@ -202,7 +202,7 @@ Releases
 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
@@ -262,7 +262,7 @@ Releases
       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),
@@ -270,7 +270,7 @@ Releases
       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:
@@ -375,7 +375,7 @@ Releases
    - 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
 
@@ -409,7 +409,7 @@ Releases
     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.
 
 
index ccd3d837427bab3f3bb7e9de6a778fbe11f0f11a..b3a9f3a285566a75aa9ea1bd155c9cffaf518c01 100644 (file)
@@ -72,6 +72,12 @@ static virHashTablePtr nameConfigMap = NULL;
 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"
@@ -79,6 +85,7 @@ static time_t lastRefresh = 0;
 #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 */
@@ -113,8 +120,8 @@ struct xenUnifiedDriver xenXMDriver = {
     xenXMDomainCreate, /* domainCreate */
     xenXMDomainDefineXML, /* domainDefineXML */
     xenXMDomainUndefine, /* domainUndefine */
-    NULL, /* domainAttachDevice */
-    NULL, /* domainDetachDevice */
+    xenXMDomainAttachDevice, /* domainAttachDevice */
+    xenXMDomainDetachDevice, /* domainDetachDevice */
     NULL, /* domainGetAutostart */
     NULL, /* domainSetAutostart */
     NULL, /* domainGetSchedulerType */
@@ -2515,6 +2522,635 @@ int xenXMNumOfDefinedDomains(virConnectPtr conn) {
     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:
index bdb5bb768e09ca510d733add03829986df87554d..b7b5599b9ec0233217282427c4c0343bd748c9f3 100644 (file)
@@ -61,6 +61,9 @@ int xenXMDomainUndefine(virDomainPtr domain);
 virConfPtr xenXMParseXMLToConfig(virConnectPtr conn, const char *xml);
 char *xenXMDomainFormatXML(virConnectPtr conn, virConfPtr conf);
 
+static int xenXMDomainAttachDevice(virDomainPtr domain, const char *xml);
+static int xenXMDomainDetachDevice(virDomainPtr domain, const char *xml);
+
 #ifdef __cplusplus
 }
 #endif