Originally, ejecting a cdrom from a qemu guest entailed
passing 'eject cdrom' to the monitor. But since qemu
added the -drive option, more than one cdrom can be
specified, so just using 'cdrom' isn't explicit enough.

The attached patch updates media change/eject to use
the current qemu syntax. The new generated commands
look something like "eject ide0-cd1", with the name
derived from device target and bus type.

While I was in there I added support for inserting/
ejecting media from scsi cdroms and floppy devices.

This is built around my previous two patches:
- Fix cd eject segfault
- Attempt to detect cdrom change failures

Thanks,
Cole

diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 05e7402..a6fd53b 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -2948,48 +2948,107 @@ static int qemudDomainUndefine(virDomainPtr dom) {
     return 0;
 }
 
-static int qemudDomainChangeCDROM(virDomainPtr dom,
-                                  virDomainObjPtr vm,
-                                  virDomainDiskDefPtr olddisk,
-                                  virDomainDiskDefPtr newdisk) {
+static int qemudDomainChangeEjectableMedia(virDomainPtr dom,
+                                           virDomainDeviceDefPtr dev)
+{
     struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(driver->domains, dom->uuid);
+    virDomainDiskDefPtr origdisk, newdisk;
     char *cmd, *reply, *safe_path;
+    char *devname = NULL;
+    int ret;
+
+    if (!vm) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "%s", _("no domain with matching uuid"));
+        return -1;
+    }
+
+    newdisk = dev->data.disk;
+    origdisk = vm->def->disks;
+    while (origdisk) {
+        if (origdisk->bus == newdisk->bus &&
+            STREQ(origdisk->dst, newdisk->dst))
+            break;
+        origdisk = origdisk->next;
+    }
+
+    if (!origdisk) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("No device with bus '%s' and target '%s'"),
+                         virDomainDiskBusTypeToString(newdisk->bus),
+                         newdisk->dst);
+        return -1;
+    }
+
+    int idx = virDiskNameToIndex(newdisk->dst);
+    switch (newdisk->bus) {
+        /* Assume that if we are here, device targets don't exceed hypervisor
+         * limits, and we are already an appropriate device type */
+        case VIR_DOMAIN_DISK_BUS_IDE:
+            /* Device name of the form 'ide{0-1}-cd{0-1}' */
+            ret = asprintf(&devname, "ide%d-cd%d", ((idx - (idx % 2)) / 2),
+                                                   (idx % 2));
+            break;
+        case VIR_DOMAIN_DISK_BUS_SCSI:
+            /* Device name of the form 'scsi{bus#}-cd{0-6}
+             * Each bus holds seven devs */
+            ret = asprintf(&devname, "scsi%d-cd%d", ((idx - (idx % 7)) / 7),
+                                                    (idx % 7));
+            break;
+        case VIR_DOMAIN_DISK_BUS_FDC:
+            /* Device name is 'floppy{0-1}' */
+            ret = asprintf(&devname, "floppy%d", idx);
+            break;
+
+        default:
+            qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+                             _("Cannot hotplug device with bus '%s'"),
+                             virDomainDiskBusTypeToString(newdisk->bus));
+            return -1;
+    }
+
+    if (!devname || ret == -1) {
+        qemudReportError(dom->conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return -1;
+    }
 
     if (newdisk->src) {
         safe_path = qemudEscapeMonitorArg(newdisk->src);
         if (!safe_path) {
             qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
                              "%s", _("out of memory"));
+            VIR_FREE(devname);
             return -1;
         }
-        if (asprintf (&cmd, "change %s \"%s\"",
-                      /* XXX qemu may support multiple CDROM in future */
-                      /* olddisk->dst */ "cdrom",
-                      safe_path) == -1) {
+        if (asprintf (&cmd, "change %s \"%s\"", devname, safe_path) == -1) {
             qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
                              "%s", _("out of memory"));
             VIR_FREE(safe_path);
+            VIR_FREE(devname);
             return -1;
         }
         VIR_FREE(safe_path);
 
-    } else if (asprintf(&cmd, "eject cdrom") == -1) {
+    } else if (asprintf(&cmd, "eject %s", devname) == -1) {
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
                          "%s", _("out of memory"));
+        VIR_FREE(devname);
         return -1;
     }
+    VIR_FREE(devname);
 
     if (qemudMonitorCommand(driver, vm, cmd, &reply) < 0) {
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
                          "%s", _("cannot change cdrom media"));
         VIR_FREE(cmd);
         return -1;
     }
 
     /* If the command failed qemu prints:
      * device not found, device is locked ...
      * No message is printed on success it seems */
-    DEBUG ("cdrom change reply: %s", reply);
+    DEBUG ("ejectable media change reply: %s", reply);
     if (strstr(reply, "\ndevice ")) {
         qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
                           _("changing cdrom media failed: %s"), reply);
@@ -3006,49 +3065,16 @@ static int qemudDomainChangeCDROM(virDomainPtr dom,
         VIR_FREE(cmd);
         return -1;
     }
-
     VIR_FREE(reply);
     VIR_FREE(cmd);
 
-    VIR_FREE(olddisk->src);
-    olddisk->src = newdisk->src;
-    newdisk->src = NULL;
-    olddisk->type = newdisk->type;
+    VIR_FREE(origdisk->src);
+    origdisk->src = newdisk->src;
+    newdisk->src = NULL;
+    origdisk->type = newdisk->type;
     return 0;
 }
 
-static int qemudDomainAttachCdromDevice(virDomainPtr dom,
-                                        virDomainDeviceDefPtr dev)
-{
-    struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
-    virDomainObjPtr vm = virDomainFindByUUID(driver->domains, dom->uuid);
-    virDomainDiskDefPtr disk;
-
-    if (!vm) {
-        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
-                         "%s", _("no domain with matching uuid"));
-        return -1;
-    }
-
-    disk = vm->def->disks;
-    while (disk) {
-        if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
-            STREQ(disk->dst, dev->data.disk->dst))
-            break;
-        disk = disk->next;
-    }
-
-    if (!disk) {
-        qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
-                         "%s", _("CDROM not attached, cannot change media"));
-        return -1;
-    }
-
-    if (qemudDomainChangeCDROM(dom, vm, disk, dev->data.disk) < 0) {
-        return -1;
-    }
-    return 0;
-}
 
 static int qemudDomainAttachUsbMassstorageDevice(virDomainPtr dom, virDomainDeviceDefPtr dev)
 {
@@ -3202,10 +3228,11 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
     }
 
     if (dev->type == VIR_DOMAIN_DEVICE_DISK &&
-        dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
-                ret = qemudDomainAttachCdromDevice(dom, dev);
+        (dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM ||
+         dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY)) {
+                ret = qemudDomainChangeEjectableMedia(dom, dev);
     } else if (dev->type == VIR_DOMAIN_DEVICE_DISK &&
-        dev->data.disk->device == VIR_DOMAIN_DEVICE_DISK &&
+        dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
         dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
                 ret = qemudDomainAttachUsbMassstorageDevice(dom, dev);
     } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
--
Libvir-list mailing list
Libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to