The check for a single display remains so no new functionality is added. --- Concerning both patches, I've tested running with all three simultaneously (sdl+spice+vnc) and with spice+vnc, using a single qxl device. src/qemu/qemu_command.c | 2425 ++++++++++++++++++++++++----------------------- src/qemu/qemu_process.c | 70 +- 2 files changed, 1259 insertions(+), 1236 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 1e96982..f9e4d4d 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4410,171 +4410,499 @@ error: return -1; } -/* - * Constructs a argv suitable for launching qemu with config defined - * for a given virtual machine. - * - * XXX 'conn' is only required to resolve network -> bridge name - * figure out how to remove this requirement some day - */ -virCommandPtr -qemuBuildCommandLine(virConnectPtr conn, - struct qemud_driver *driver, - virDomainDefPtr def, - virDomainChrSourceDefPtr monitor_chr, - bool monitor_json, - qemuCapsPtr caps, - const char *migrateFrom, - int migrateFd, - virDomainSnapshotObjPtr snapshot, - enum virNetDevVPortProfileOp vmop) +enum { + OK=0, + ERROR=1, + NO_MEMORY=2, +}; + +static int +qemuBuildGraphicsCommandLine(struct qemud_driver *driver, + virCommandPtr cmd, + virDomainDefPtr def, + qemuCapsPtr caps, + virDomainGraphicsDefPtr graphics) { - int i, j; - struct utsname ut; - int disableKQEMU = 0; - int enableKQEMU = 0; - int disableKVM = 0; - int enableKVM = 0; - const char *emulator; - char uuid[VIR_UUID_STRING_BUFLEN]; - char *cpu; - char *smp; - int last_good_net = -1; - bool hasHwVirt = false; - virCommandPtr cmd = NULL; - bool emitBootindex = false; - int usbcontroller = 0; - bool usblegacy = false; - uname_normalize(&ut); - int contOrder[] = { - /* We don't add an explicit IDE or FD controller because the - * provided PIIX4 device already includes one. It isn't possible to - * remove the PIIX4. */ - VIR_DOMAIN_CONTROLLER_TYPE_USB, - VIR_DOMAIN_CONTROLLER_TYPE_SCSI, - VIR_DOMAIN_CONTROLLER_TYPE_SATA, - VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL, - VIR_DOMAIN_CONTROLLER_TYPE_CCID, - }; + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + virBuffer opt = VIR_BUFFER_INITIALIZER; - VIR_DEBUG("conn=%p driver=%p def=%p mon=%p json=%d " - "caps=%p migrateFrom=%s migrateFD=%d " - "snapshot=%p vmop=%d", - conn, driver, def, monitor_chr, monitor_json, - caps, migrateFrom, migrateFd, snapshot, vmop); + if (!qemuCapsGet(caps, QEMU_CAPS_VNC)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vnc graphics are not supported with this QEMU")); + return ERROR; + } - virUUIDFormat(def->uuid, uuid); + if (graphics->data.vnc.socket || + driver->vncAutoUnixSocket) { - emulator = def->emulator; + if (!graphics->data.vnc.socket && + virAsprintf(&graphics->data.vnc.socket, + "%s/%s.vnc", driver->libDir, def->name) == -1) { + return NO_MEMORY; + } - /* - * do not use boot=on for drives when not using KVM since this - * is not supported at all in upstream QEmu. - */ - if (qemuCapsGet(caps, QEMU_CAPS_KVM) && - (def->virtType == VIR_DOMAIN_VIRT_QEMU)) - qemuCapsClear(caps, QEMU_CAPS_DRIVE_BOOT); + virBufferAsprintf(&opt, "unix:%s", + graphics->data.vnc.socket); - switch (def->virtType) { - case VIR_DOMAIN_VIRT_QEMU: - if (qemuCapsGet(caps, QEMU_CAPS_KQEMU)) - disableKQEMU = 1; - if (qemuCapsGet(caps, QEMU_CAPS_KVM)) - disableKVM = 1; - break; + } else if (qemuCapsGet(caps, QEMU_CAPS_VNC_COLON)) { + const char *listenNetwork; + const char *listenAddr = NULL; + char *netAddr = NULL; + bool escapeAddr; + int ret; - case VIR_DOMAIN_VIRT_KQEMU: - if (qemuCapsGet(caps, QEMU_CAPS_KVM)) - disableKVM = 1; + switch (virDomainGraphicsListenGetType(graphics, 0)) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + listenAddr = virDomainGraphicsListenGetAddress(graphics, 0); + break; - if (qemuCapsGet(caps, QEMU_CAPS_ENABLE_KQEMU)) { - enableKQEMU = 1; - } else if (!qemuCapsGet(caps, QEMU_CAPS_KQEMU)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary %s does not support kqemu"), - emulator); - goto error; - } - break; + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + listenNetwork = virDomainGraphicsListenGetNetwork(graphics, 0); + if (!listenNetwork) + break; + ret = networkGetNetworkAddress(listenNetwork, &netAddr); + if (ret <= -2) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("network-based listen not possible, " + "network driver not present")); + return 1; + } + if (ret < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("listen network '%s' had no usable address"), + listenNetwork); + return 1; + } + listenAddr = netAddr; + /* store the address we found in the <graphics> element so it will + * show up in status. */ + if (virDomainGraphicsListenSetAddress(graphics, 0, + listenAddr, -1, false) < 0) + return 1; + break; + } - case VIR_DOMAIN_VIRT_KVM: - if (qemuCapsGet(caps, QEMU_CAPS_KQEMU)) - disableKQEMU = 1; + if (!listenAddr) + listenAddr = driver->vncListen; - if (qemuCapsGet(caps, QEMU_CAPS_ENABLE_KVM)) { - enableKVM = 1; - } else if (!qemuCapsGet(caps, QEMU_CAPS_KVM)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary %s does not support kvm"), - emulator); - goto error; + escapeAddr = strchr(listenAddr, ':') != NULL; + if (escapeAddr) + virBufferAsprintf(&opt, "[%s]", listenAddr); + else + virBufferAdd(&opt, listenAddr, -1); + virBufferAsprintf(&opt, ":%d", + graphics->data.vnc.port - 5900); + + VIR_FREE(netAddr); + } else { + virBufferAsprintf(&opt, "%d", + graphics->data.vnc.port - 5900); } - break; - case VIR_DOMAIN_VIRT_XEN: - /* XXX better check for xenner */ - break; + if (qemuCapsGet(caps, QEMU_CAPS_VNC_COLON)) { + if (graphics->data.vnc.auth.passwd || + driver->vncPassword) + virBufferAddLit(&opt, ",password"); - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary %s does not support %s"), - emulator, virDomainVirtTypeToString(def->virtType)); - break; - } + if (driver->vncTLS) { + virBufferAddLit(&opt, ",tls"); + if (driver->vncTLSx509verify) { + virBufferAsprintf(&opt, ",x509verify=%s", + driver->vncTLSx509certdir); + } else { + virBufferAsprintf(&opt, ",x509=%s", + driver->vncTLSx509certdir); + } + } - cmd = virCommandNew(emulator); + if (driver->vncSASL) { + virBufferAddLit(&opt, ",sasl"); - virCommandAddEnvPassCommon(cmd); + if (driver->vncSASLdir) + virCommandAddEnvPair(cmd, "SASL_CONF_DIR", + driver->vncSASLdir); - if (qemuCapsGet(caps, QEMU_CAPS_NAME)) { - virCommandAddArg(cmd, "-name"); - if (driver->setProcessName && - qemuCapsGet(caps, QEMU_CAPS_NAME_PROCESS)) { - virCommandAddArgFormat(cmd, "%s,process=qemu:%s", - def->name, def->name); + /* TODO: Support ACLs later */ + } + } + + virCommandAddArg(cmd, "-vnc"); + virCommandAddArgBuffer(cmd, &opt); + if (graphics->data.vnc.keymap) { + virCommandAddArgList(cmd, "-k", graphics->data.vnc.keymap, + NULL); + } + + /* Unless user requested it, set the audio backend to none, to + * prevent it opening the host OS audio devices, since that causes + * security issues and might not work when using VNC. + */ + if (driver->vncAllowHostAudio) { + virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); } else { - virCommandAddArg(cmd, def->name); + virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none"); + } + } else if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { + if (qemuCapsGet(caps, QEMU_CAPS_0_10) && + !qemuCapsGet(caps, QEMU_CAPS_SDL)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("sdl not supported by '%s'"), + def->emulator); + return 1; } - } - virCommandAddArg(cmd, "-S"); /* freeze CPU */ - if (qemuBuildMachineArgStr(cmd, def, caps) < 0) - goto error; + if (graphics->data.sdl.xauth) + virCommandAddEnvPair(cmd, "XAUTHORITY", + graphics->data.sdl.xauth); + if (graphics->data.sdl.display) + virCommandAddEnvPair(cmd, "DISPLAY", + graphics->data.sdl.display); + if (graphics->data.sdl.fullscreen) + virCommandAddArg(cmd, "-full-screen"); - if (qemuBuildCpuArgStr(driver, def, emulator, caps, - &ut, &cpu, &hasHwVirt, !!migrateFrom) < 0) - goto error; + /* If using SDL for video, then we should just let it + * use QEMU's host audio drivers, possibly SDL too + * User can set these two before starting libvirtd + */ + virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); + virCommandAddEnvPass(cmd, "SDL_AUDIODRIVER"); - if (cpu) { - virCommandAddArgList(cmd, "-cpu", cpu, NULL); - VIR_FREE(cpu); + /* New QEMU has this flag to let us explicitly ask for + * SDL graphics. This is better than relying on the + * default, since the default changes :-( */ + if (qemuCapsGet(caps, QEMU_CAPS_SDL)) + virCommandAddArg(cmd, "-sdl"); - if (qemuCapsGet(caps, QEMU_CAPS_NESTING) && - hasHwVirt) - virCommandAddArg(cmd, "-enable-nesting"); - } + } else if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + virBuffer opt = VIR_BUFFER_INITIALIZER; + const char *listenNetwork; + const char *listenAddr = NULL; + char *netAddr = NULL; + int ret; + int defaultMode = graphics->data.spice.defaultMode; - if (disableKQEMU) - virCommandAddArg(cmd, "-no-kqemu"); - else if (enableKQEMU) - virCommandAddArgList(cmd, "-enable-kqemu", "-kernel-kqemu", NULL); - if (disableKVM) - virCommandAddArg(cmd, "-no-kvm"); - if (enableKVM) - virCommandAddArg(cmd, "-enable-kvm"); + if (!qemuCapsGet(caps, QEMU_CAPS_SPICE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("spice graphics are not supported with this QEMU")); + return 1; + } - if (def->os.loader) { - virCommandAddArg(cmd, "-bios"); - virCommandAddArg(cmd, def->os.loader); - } + virBufferAsprintf(&opt, "port=%u", graphics->data.spice.port); - /* Set '-m MB' based on maxmem, because the lower 'memory' limit - * is set post-startup using the balloon driver. If balloon driver - * is not supported, then they're out of luck anyway. Update the - * XML to reflect our rounding. - */ - virCommandAddArg(cmd, "-m"); - def->mem.max_balloon = VIR_DIV_UP(def->mem.max_balloon, 1024) * 1024; + if (graphics->data.spice.tlsPort > 0) { + if (!driver->spiceTLS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("spice TLS port set in XML configuration," + " but TLS is disabled in qemu.conf")); + return 1; + } + virBufferAsprintf(&opt, ",tls-port=%u", + graphics->data.spice.tlsPort); + } + + switch (virDomainGraphicsListenGetType(graphics, 0)) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + listenAddr = virDomainGraphicsListenGetAddress(graphics, 0); + break; + + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + listenNetwork = virDomainGraphicsListenGetNetwork(graphics, 0); + if (!listenNetwork) + break; + ret = networkGetNetworkAddress(listenNetwork, &netAddr); + if (ret <= -2) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("network-based listen not possible, " + "network driver not present")); + return 1; + } + if (ret < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("listen network '%s' had no usable address"), + listenNetwork); + return 1; + } + listenAddr = netAddr; + /* store the address we found in the <graphics> element so it will + * show up in status. */ + if (virDomainGraphicsListenSetAddress(graphics, 0, + listenAddr, -1, false) < 0) + return 1; + break; + } + + if (!listenAddr) + listenAddr = driver->spiceListen; + if (listenAddr) + virBufferAsprintf(&opt, ",addr=%s", listenAddr); + + VIR_FREE(netAddr); + + int mm = graphics->data.spice.mousemode; + if (mm) { + switch (mm) { + case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_SERVER: + virBufferAsprintf(&opt, ",agent-mouse=off"); + break; + case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_CLIENT: + virBufferAsprintf(&opt, ",agent-mouse=on"); + break; + default: + break; + } + } + + /* In the password case we set it via monitor command, to avoid + * making it visible on CLI, so there's no use of password=XXX + * in this bit of the code */ + if (!graphics->data.spice.auth.passwd && + !driver->spicePassword) + virBufferAddLit(&opt, ",disable-ticketing"); + + if (driver->spiceTLS) + virBufferAsprintf(&opt, ",x509-dir=%s", + driver->spiceTLSx509certdir); + + switch (defaultMode) { + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE: + virBufferAsprintf(&opt, ",tls-channel=default"); + break; + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE: + virBufferAsprintf(&opt, ",plaintext-channel=default"); + break; + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY: + /* nothing */ + break; + } + + for (int i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) { + int mode = graphics->data.spice.channels[i]; + switch (mode) { + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE: + if (!driver->spiceTLS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("spice secure channels set in XML configuration, but TLS is disabled in qemu.conf")); + return 1; + } + virBufferAsprintf(&opt, ",tls-channel=%s", + virDomainGraphicsSpiceChannelNameTypeToString(i)); + break; + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE: + virBufferAsprintf(&opt, ",plaintext-channel=%s", + virDomainGraphicsSpiceChannelNameTypeToString(i)); + break; + } + } + if (graphics->data.spice.image) + virBufferAsprintf(&opt, ",image-compression=%s", + virDomainGraphicsSpiceImageCompressionTypeToString(graphics->data.spice.image)); + if (graphics->data.spice.jpeg) + virBufferAsprintf(&opt, ",jpeg-wan-compression=%s", + virDomainGraphicsSpiceJpegCompressionTypeToString(graphics->data.spice.jpeg)); + if (graphics->data.spice.zlib) + virBufferAsprintf(&opt, ",zlib-glz-wan-compression=%s", + virDomainGraphicsSpiceZlibCompressionTypeToString(graphics->data.spice.zlib)); + if (graphics->data.spice.playback) + virBufferAsprintf(&opt, ",playback-compression=%s", + virDomainGraphicsSpicePlaybackCompressionTypeToString(graphics->data.spice.playback)); + if (graphics->data.spice.streaming) + virBufferAsprintf(&opt, ",streaming-video=%s", + virDomainGraphicsSpiceStreamingModeTypeToString(graphics->data.spice.streaming)); + if (graphics->data.spice.copypaste == VIR_DOMAIN_GRAPHICS_SPICE_CLIPBOARD_COPYPASTE_NO) + virBufferAddLit(&opt, ",disable-copy-paste"); + + if (qemuCapsGet(caps, QEMU_CAPS_SEAMLESS_MIGRATION)) { + /* If qemu supports seamless migration turn it + * unconditionally on. If migration destination + * doesn't support it, it fallbacks to previous + * migration algorithm silently. */ + virBufferAddLit(&opt, ",seamless-migration=on"); + } + + virCommandAddArg(cmd, "-spice"); + virCommandAddArgBuffer(cmd, &opt); + if (graphics->data.spice.keymap) + virCommandAddArgList(cmd, "-k", + graphics->data.spice.keymap, NULL); + /* SPICE includes native support for tunnelling audio, so we + * set the audio backend to point at SPICE's own driver + */ + virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=spice"); + + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported graphics type '%s'"), + virDomainGraphicsTypeToString(graphics->type)); + return 1; + } + return 0; +} + +/* + * Constructs a argv suitable for launching qemu with config defined + * for a given virtual machine. + * + * XXX 'conn' is only required to resolve network -> bridge name + * figure out how to remove this requirement some day + */ +virCommandPtr +qemuBuildCommandLine(virConnectPtr conn, + struct qemud_driver *driver, + virDomainDefPtr def, + virDomainChrSourceDefPtr monitor_chr, + bool monitor_json, + qemuCapsPtr caps, + const char *migrateFrom, + int migrateFd, + virDomainSnapshotObjPtr snapshot, + enum virNetDevVPortProfileOp vmop) +{ + int i, j; + struct utsname ut; + int disableKQEMU = 0; + int enableKQEMU = 0; + int disableKVM = 0; + int enableKVM = 0; + const char *emulator; + char uuid[VIR_UUID_STRING_BUFLEN]; + char *cpu; + char *smp; + int last_good_net = -1; + bool hasHwVirt = false; + virCommandPtr cmd = NULL; + bool emitBootindex = false; + int usbcontroller = 0; + bool usblegacy = false; + uname_normalize(&ut); + int contOrder[] = { + /* We don't add an explicit IDE or FD controller because the + * provided PIIX4 device already includes one. It isn't possible to + * remove the PIIX4. */ + VIR_DOMAIN_CONTROLLER_TYPE_USB, + VIR_DOMAIN_CONTROLLER_TYPE_SCSI, + VIR_DOMAIN_CONTROLLER_TYPE_SATA, + VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL, + VIR_DOMAIN_CONTROLLER_TYPE_CCID, + }; + + VIR_DEBUG("conn=%p driver=%p def=%p mon=%p json=%d " + "caps=%p migrateFrom=%s migrateFD=%d " + "snapshot=%p vmop=%d", + conn, driver, def, monitor_chr, monitor_json, + caps, migrateFrom, migrateFd, snapshot, vmop); + + virUUIDFormat(def->uuid, uuid); + + emulator = def->emulator; + + /* + * do not use boot=on for drives when not using KVM since this + * is not supported at all in upstream QEmu. + */ + if (qemuCapsGet(caps, QEMU_CAPS_KVM) && + (def->virtType == VIR_DOMAIN_VIRT_QEMU)) + qemuCapsClear(caps, QEMU_CAPS_DRIVE_BOOT); + + switch (def->virtType) { + case VIR_DOMAIN_VIRT_QEMU: + if (qemuCapsGet(caps, QEMU_CAPS_KQEMU)) + disableKQEMU = 1; + if (qemuCapsGet(caps, QEMU_CAPS_KVM)) + disableKVM = 1; + break; + + case VIR_DOMAIN_VIRT_KQEMU: + if (qemuCapsGet(caps, QEMU_CAPS_KVM)) + disableKVM = 1; + + if (qemuCapsGet(caps, QEMU_CAPS_ENABLE_KQEMU)) { + enableKQEMU = 1; + } else if (!qemuCapsGet(caps, QEMU_CAPS_KQEMU)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary %s does not support kqemu"), + emulator); + goto error; + } + break; + + case VIR_DOMAIN_VIRT_KVM: + if (qemuCapsGet(caps, QEMU_CAPS_KQEMU)) + disableKQEMU = 1; + + if (qemuCapsGet(caps, QEMU_CAPS_ENABLE_KVM)) { + enableKVM = 1; + } else if (!qemuCapsGet(caps, QEMU_CAPS_KVM)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary %s does not support kvm"), + emulator); + goto error; + } + break; + + case VIR_DOMAIN_VIRT_XEN: + /* XXX better check for xenner */ + break; + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary %s does not support %s"), + emulator, virDomainVirtTypeToString(def->virtType)); + break; + } + + cmd = virCommandNew(emulator); + + virCommandAddEnvPassCommon(cmd); + + if (qemuCapsGet(caps, QEMU_CAPS_NAME)) { + virCommandAddArg(cmd, "-name"); + if (driver->setProcessName && + qemuCapsGet(caps, QEMU_CAPS_NAME_PROCESS)) { + virCommandAddArgFormat(cmd, "%s,process=qemu:%s", + def->name, def->name); + } else { + virCommandAddArg(cmd, def->name); + } + } + virCommandAddArg(cmd, "-S"); /* freeze CPU */ + + if (qemuBuildMachineArgStr(cmd, def, caps) < 0) + goto error; + + if (qemuBuildCpuArgStr(driver, def, emulator, caps, + &ut, &cpu, &hasHwVirt, !!migrateFrom) < 0) + goto error; + + if (cpu) { + virCommandAddArgList(cmd, "-cpu", cpu, NULL); + VIR_FREE(cpu); + + if (qemuCapsGet(caps, QEMU_CAPS_NESTING) && + hasHwVirt) + virCommandAddArg(cmd, "-enable-nesting"); + } + + if (disableKQEMU) + virCommandAddArg(cmd, "-no-kqemu"); + else if (enableKQEMU) + virCommandAddArgList(cmd, "-enable-kqemu", "-kernel-kqemu", NULL); + if (disableKVM) + virCommandAddArg(cmd, "-no-kvm"); + if (enableKVM) + virCommandAddArg(cmd, "-enable-kvm"); + + if (def->os.loader) { + virCommandAddArg(cmd, "-bios"); + virCommandAddArg(cmd, def->os.loader); + } + + /* Set '-m MB' based on maxmem, because the lower 'memory' limit + * is set post-startup using the balloon driver. If balloon driver + * is not supported, then they're out of luck anyway. Update the + * XML to reflect our rounding. + */ + virCommandAddArg(cmd, "-m"); + def->mem.max_balloon = VIR_DIV_UP(def->mem.max_balloon, 1024) * 1024; virCommandAddArgFormat(cmd, "%llu", def->mem.max_balloon / 1024); if (def->mem.hugepage_backed) { if (!driver->hugetlbfs_mount) { @@ -4954,1231 +5282,924 @@ qemuBuildCommandLine(virConnectPtr conn, break; case VIR_DOMAIN_BOOT_NET: boot[i] = 'n'; - break; - default: - boot[i] = 'c'; - break; - } - } - boot[def->os.nBootDevs] = '\0'; - - virBufferAsprintf(&boot_buf, "%s", boot); - boot_nparams++; - } - - if (def->os.bootmenu) { - if (qemuCapsGet(caps, QEMU_CAPS_BOOT_MENU)) { - if (boot_nparams++) - virBufferAddChar(&boot_buf, ','); - - if (def->os.bootmenu == VIR_DOMAIN_BOOT_MENU_ENABLED) - virBufferAsprintf(&boot_buf, "menu=on"); - else - virBufferAsprintf(&boot_buf, "menu=off"); - } else { - /* We cannot emit an error when bootmenu is enabled but - * unsupported because of backward compatibility */ - VIR_WARN("bootmenu is enabled but not " - "supported by this QEMU binary"); - } - } - - if (def->os.bios.rt_set) { - if (!qemuCapsGet(caps, QEMU_CAPS_REBOOT_TIMEOUT)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("reboot timeout is not supported " - "by this QEMU binary")); - goto error; - } - - if (boot_nparams++) - virBufferAddChar(&boot_buf, ','); - - virBufferAsprintf(&boot_buf, - "reboot-timeout=%d", - def->os.bios.rt_delay); - } - - if (boot_nparams > 0) { - virCommandAddArg(cmd, "-boot"); - - if (boot_nparams < 2 || emitBootindex) { - virCommandAddArgBuffer(cmd, &boot_buf); - } else { - virCommandAddArgFormat(cmd, - "order=%s", - virBufferContentAndReset(&boot_buf)); - } - } - - if (def->os.kernel) - virCommandAddArgList(cmd, "-kernel", def->os.kernel, NULL); - if (def->os.initrd) - virCommandAddArgList(cmd, "-initrd", def->os.initrd, NULL); - if (def->os.cmdline) - virCommandAddArgList(cmd, "-append", def->os.cmdline, NULL); - } else { - virCommandAddArgList(cmd, "-bootloader", def->os.bootloader, NULL); - } - - for (i = 0 ; i < def->ndisks ; i++) { - virDomainDiskDefPtr disk = def->disks[i]; - - if (disk->driverName != NULL && - !STREQ(disk->driverName, "qemu")) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported driver name '%s' for disk '%s'"), - disk->driverName, disk->src); - goto error; - } - } - - if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - for (j = 0; j < ARRAY_CARDINALITY(contOrder); j++) { - for (i = 0; i < def->ncontrollers; i++) { - virDomainControllerDefPtr cont = def->controllers[i]; - - if (cont->type != contOrder[j]) - continue; - - /* Also, skip USB controllers with type none.*/ - if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && - cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) { - usbcontroller = -1; /* mark we don't want a controller */ - continue; - } - - /* Only recent QEMU implements a SATA (AHCI) controller */ - if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA) { - if (!qemuCapsGet(caps, QEMU_CAPS_ICH9_AHCI)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("SATA is not supported with this " - "QEMU binary")); - goto error; - } else { - char *devstr; - - virCommandAddArg(cmd, "-device"); - if (!(devstr = qemuBuildControllerDevStr(def, cont, - caps, NULL))) - goto error; - - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } - } else if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && - cont->model == -1 && - !qemuCapsGet(caps, QEMU_CAPS_PIIX3_USB_UHCI)) { - if (usblegacy) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Multiple legacy USB controllers are " - "not supported")); - goto error; - } - usblegacy = true; - } else { - virCommandAddArg(cmd, "-device"); - - char *devstr; - if (!(devstr = qemuBuildControllerDevStr(def, cont, caps, - &usbcontroller))) - goto error; - - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } - } - } - } - - if (usbcontroller == 0) - virCommandAddArg(cmd, "-usb"); - - for (i = 0 ; i < def->nhubs ; i++) { - virDomainHubDefPtr hub = def->hubs[i]; - char *optstr; - - virCommandAddArg(cmd, "-device"); - if (!(optstr = qemuBuildHubDevStr(hub, caps))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - } - - /* If QEMU supports -drive param instead of old -hda, -hdb, -cdrom .. */ - if (qemuCapsGet(caps, QEMU_CAPS_DRIVE)) { - int bootCD = 0, bootFloppy = 0, bootDisk = 0; - - if ((qemuCapsGet(caps, QEMU_CAPS_DRIVE_BOOT) || emitBootindex)) { - /* bootDevs will get translated into either bootindex=N or boot=on - * depending on what qemu supports */ - for (i = 0 ; i < def->os.nBootDevs ; i++) { - switch (def->os.bootDevs[i]) { - case VIR_DOMAIN_BOOT_CDROM: - bootCD = i + 1; - break; - case VIR_DOMAIN_BOOT_FLOPPY: - bootFloppy = i + 1; - break; - case VIR_DOMAIN_BOOT_DISK: - bootDisk = i + 1; - break; - } - } - } - - for (i = 0 ; i < def->ndisks ; i++) { - char *optstr; - int bootindex = 0; - virDomainDiskDefPtr disk = def->disks[i]; - int withDeviceArg = 0; - bool deviceFlagMasked = false; - - /* Unless we have -device, then USB disks need special - handling */ - if ((disk->bus == VIR_DOMAIN_DISK_BUS_USB) && - !qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - virCommandAddArg(cmd, "-usbdevice"); - virCommandAddArgFormat(cmd, "disk:%s", disk->src); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported usb disk type for '%s'"), - disk->src); - goto error; - } - continue; - } - - switch (disk->device) { - case VIR_DOMAIN_DISK_DEVICE_CDROM: - bootindex = bootCD; - bootCD = 0; - break; - case VIR_DOMAIN_DISK_DEVICE_FLOPPY: - bootindex = bootFloppy; - bootFloppy = 0; - break; - case VIR_DOMAIN_DISK_DEVICE_DISK: - case VIR_DOMAIN_DISK_DEVICE_LUN: - bootindex = bootDisk; - bootDisk = 0; - break; - } - - virCommandAddArg(cmd, "-drive"); - - /* Unfortunately it is not possible to use - -device for floppies, or Xen paravirt - devices. Fortunately, those don't need - static PCI addresses, so we don't really - care that we can't use -device */ - if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - if (disk->bus != VIR_DOMAIN_DISK_BUS_XEN) { - withDeviceArg = 1; - } else { - qemuCapsClear(caps, QEMU_CAPS_DEVICE); - deviceFlagMasked = true; + break; + default: + boot[i] = 'c'; + break; } } - optstr = qemuBuildDriveStr(conn, disk, - emitBootindex ? false : !!bootindex, - caps); - if (deviceFlagMasked) - qemuCapsSet(caps, QEMU_CAPS_DEVICE); - if (!optstr) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - - if (!emitBootindex) - bootindex = 0; - else if (disk->info.bootIndex) - bootindex = disk->info.bootIndex; + boot[def->os.nBootDevs] = '\0'; - if (withDeviceArg) { - if (disk->bus == VIR_DOMAIN_DISK_BUS_FDC) { - virCommandAddArg(cmd, "-global"); - virCommandAddArgFormat(cmd, "isa-fdc.drive%c=drive-%s", - disk->info.addr.drive.unit - ? 'B' : 'A', - disk->info.alias); + virBufferAsprintf(&boot_buf, "%s", boot); + boot_nparams++; + } - if (bootindex) { - virCommandAddArg(cmd, "-global"); - virCommandAddArgFormat(cmd, "isa-fdc.bootindex%c=%d", - disk->info.addr.drive.unit - ? 'B' : 'A', - bootindex); - } - } else { - virCommandAddArg(cmd, "-device"); + if (def->os.bootmenu) { + if (qemuCapsGet(caps, QEMU_CAPS_BOOT_MENU)) { + if (boot_nparams++) + virBufferAddChar(&boot_buf, ','); - if (!(optstr = qemuBuildDriveDevStr(def, disk, bootindex, - caps))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - } + if (def->os.bootmenu == VIR_DOMAIN_BOOT_MENU_ENABLED) + virBufferAsprintf(&boot_buf, "menu=on"); + else + virBufferAsprintf(&boot_buf, "menu=off"); + } else { + /* We cannot emit an error when bootmenu is enabled but + * unsupported because of backward compatibility */ + VIR_WARN("bootmenu is enabled but not " + "supported by this QEMU binary"); } } - } else { - for (i = 0 ; i < def->ndisks ; i++) { - char dev[NAME_MAX]; - char *file; - const char *fmt; - virDomainDiskDefPtr disk = def->disks[i]; - if ((disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK) && - (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) { + if (def->os.bios.rt_set) { + if (!qemuCapsGet(caps, QEMU_CAPS_REBOOT_TIMEOUT)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("tray status 'open' is invalid for " - "block type disk")); + _("reboot timeout is not supported " + "by this QEMU binary")); goto error; } - if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { - if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - virCommandAddArg(cmd, "-usbdevice"); - virCommandAddArgFormat(cmd, "disk:%s", disk->src); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported usb disk type for '%s'"), - disk->src); - goto error; - } - continue; - } + if (boot_nparams++) + virBufferAddChar(&boot_buf, ','); - if (STREQ(disk->dst, "hdc") && - disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { - if (disk->src) { - snprintf(dev, NAME_MAX, "-%s", "cdrom"); - } else { - continue; - } - } else { - if (STRPREFIX(disk->dst, "hd") || - STRPREFIX(disk->dst, "fd")) { - snprintf(dev, NAME_MAX, "-%s", disk->dst); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk type '%s'"), disk->dst); - goto error; - } - } + virBufferAsprintf(&boot_buf, + "reboot-timeout=%d", + def->os.bios.rt_delay); + } - if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { - /* QEMU only supports magic FAT format for now */ - if (disk->format > 0 && disk->format != VIR_STORAGE_FILE_FAT) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk driver type for '%s'"), - virStorageFileFormatTypeToString(disk->format)); - goto error; - } - if (!disk->readonly) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot create virtual FAT disks in read-write mode")); - goto error; - } - if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) - fmt = "fat:floppy:%s"; - else - fmt = "fat:%s"; + if (boot_nparams > 0) { + virCommandAddArg(cmd, "-boot"); - if (virAsprintf(&file, fmt, disk->src) < 0) { - goto no_memory; - } - } else if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { - switch (disk->protocol) { - case VIR_DOMAIN_DISK_PROTOCOL_NBD: - if (disk->nhosts != 1) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("NBD accepts only one host")); - goto error; - } - if (virAsprintf(&file, "nbd:%s:%s,", disk->hosts->name, - disk->hosts->port) < 0) { - goto no_memory; - } - break; - case VIR_DOMAIN_DISK_PROTOCOL_RBD: - { - virBuffer opt = VIR_BUFFER_INITIALIZER; - if (qemuBuildRBDString(conn, disk, &opt) < 0) - goto error; - if (virBufferError(&opt)) { - virReportOOMError(); - goto error; - } - file = virBufferContentAndReset(&opt); - } - break; - case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: - if (disk->nhosts == 0) { - if (virAsprintf(&file, "sheepdog:%s,", disk->src) < 0) { - goto no_memory; - } - } else { - /* only one host is supported now */ - if (virAsprintf(&file, "sheepdog:%s:%s:%s,", - disk->hosts->name, disk->hosts->port, - disk->src) < 0) { - goto no_memory; - } - } - break; - } + if (boot_nparams < 2 || emitBootindex) { + virCommandAddArgBuffer(cmd, &boot_buf); } else { - if (!(file = strdup(disk->src))) { - goto no_memory; - } + virCommandAddArgFormat(cmd, + "order=%s", + virBufferContentAndReset(&boot_buf)); } - - /* Don't start with source if the tray is open for - * CDROM and Floppy device. - */ - if (!((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY || - disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) && - disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) - virCommandAddArgList(cmd, dev, file, NULL); - VIR_FREE(file); } - } - - if (qemuCapsGet(caps, QEMU_CAPS_FSDEV)) { - for (i = 0 ; i < def->nfss ; i++) { - char *optstr; - virDomainFSDefPtr fs = def->fss[i]; - - virCommandAddArg(cmd, "-fsdev"); - if (!(optstr = qemuBuildFSStr(fs, caps))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - virCommandAddArg(cmd, "-device"); - if (!(optstr = qemuBuildFSDevStr(fs, caps))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - } + if (def->os.kernel) + virCommandAddArgList(cmd, "-kernel", def->os.kernel, NULL); + if (def->os.initrd) + virCommandAddArgList(cmd, "-initrd", def->os.initrd, NULL); + if (def->os.cmdline) + virCommandAddArgList(cmd, "-append", def->os.cmdline, NULL); } else { - if (def->nfss) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("filesystem passthrough not supported by this QEMU")); - goto error; - } + virCommandAddArgList(cmd, "-bootloader", def->os.bootloader, NULL); } - if (!def->nnets) { - /* If we have -device, then we set -nodefault already */ - if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) - virCommandAddArgList(cmd, "-net", "none", NULL); - } else { - int bootNet = 0; + for (i = 0 ; i < def->ndisks ; i++) { + virDomainDiskDefPtr disk = def->disks[i]; - if (emitBootindex) { - /* convert <boot dev='network'/> to bootindex since we didn't emit - * -boot n - */ - for (i = 0 ; i < def->os.nBootDevs ; i++) { - if (def->os.bootDevs[i] == VIR_DOMAIN_BOOT_NET) { - bootNet = i + 1; - break; - } - } + if (disk->driverName != NULL && + !STREQ(disk->driverName, "qemu")) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported driver name '%s' for disk '%s'"), + disk->driverName, disk->src); + goto error; } + } - for (i = 0 ; i < def->nnets ; i++) { - virDomainNetDefPtr net = def->nets[i]; - char *nic, *host; - char tapfd_name[50] = ""; - char vhostfd_name[50] = ""; - int vlan; - int bootindex = bootNet; - int actualType; - - bootNet = 0; - if (!bootindex) - bootindex = net->info.bootIndex; + if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + for (j = 0; j < ARRAY_CARDINALITY(contOrder); j++) { + for (i = 0; i < def->ncontrollers; i++) { + virDomainControllerDefPtr cont = def->controllers[i]; - /* VLANs are not used with -netdev, so don't record them */ - if (qemuCapsGet(caps, QEMU_CAPS_NETDEV) && - qemuCapsGet(caps, QEMU_CAPS_DEVICE)) - vlan = -1; - else - vlan = i; + if (cont->type != contOrder[j]) + continue; - /* If appropriate, grab a physical device from the configured - * network's pool of devices, or resolve bridge device name - * to the one defined in the network definition. - */ - if (networkAllocateActualDevice(net) < 0) - goto error; + /* Also, skip USB controllers with type none.*/ + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) { + usbcontroller = -1; /* mark we don't want a controller */ + continue; + } - actualType = virDomainNetGetActualType(net); - if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) { - if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { - virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net); - virDomainHostdevDefPtr found; - /* For a network with <forward mode='hostdev'>, there is a need to - * add the newly minted hostdev to the hostdevs array. - */ - if (qemuAssignDeviceHostdevAlias(def, hostdev, - (def->nhostdevs-1)) < 0) { + /* Only recent QEMU implements a SATA (AHCI) controller */ + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA) { + if (!qemuCapsGet(caps, QEMU_CAPS_ICH9_AHCI)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("SATA is not supported with this " + "QEMU binary")); goto error; - } + } else { + char *devstr; - if (virDomainHostdevFind(def, hostdev, &found) < 0) { - if (virDomainHostdevInsert(def, hostdev) < 0) { - virReportOOMError(); - goto error; - } - if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, - &hostdev, 1) < 0) { + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildControllerDevStr(def, cont, + caps, NULL))) goto error; - } + + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } - else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("PCI device %04x:%02x:%02x.%x " - "allocated from network %s is already " - "in use by domain %s"), - hostdev->source.subsys.u.pci.domain, - hostdev->source.subsys.u.pci.bus, - hostdev->source.subsys.u.pci.slot, - hostdev->source.subsys.u.pci.function, - net->data.network.name, - def->name); + } else if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + cont->model == -1 && + !qemuCapsGet(caps, QEMU_CAPS_PIIX3_USB_UHCI)) { + if (usblegacy) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Multiple legacy USB controllers are " + "not supported")); goto error; } + usblegacy = true; + } else { + virCommandAddArg(cmd, "-device"); + + char *devstr; + if (!(devstr = qemuBuildControllerDevStr(def, cont, caps, + &usbcontroller))) + goto error; + + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } - continue; } + } + } - if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || - actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) { - /* - * If type='bridge' then we attempt to allocate the tap fd here only if - * running under a privilged user or -netdev bridge option is not - * supported. - */ - if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || - driver->privileged || - (!qemuCapsGet(caps, QEMU_CAPS_NETDEV_BRIDGE))) { - int tapfd = qemuNetworkIfaceConnect(def, conn, driver, net, - caps); - if (tapfd < 0) - goto error; + if (usbcontroller == 0) + virCommandAddArg(cmd, "-usb"); - last_good_net = i; - virCommandTransferFD(cmd, tapfd); + for (i = 0 ; i < def->nhubs ; i++) { + virDomainHubDefPtr hub = def->hubs[i]; + char *optstr; - if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", - tapfd) >= sizeof(tapfd_name)) - goto no_memory; - } - } else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { - int tapfd = qemuPhysIfaceConnect(def, driver, net, - caps, vmop); - if (tapfd < 0) - goto error; + virCommandAddArg(cmd, "-device"); + if (!(optstr = qemuBuildHubDevStr(hub, caps))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + } - last_good_net = i; - virCommandTransferFD(cmd, tapfd); + /* If QEMU supports -drive param instead of old -hda, -hdb, -cdrom .. */ + if (qemuCapsGet(caps, QEMU_CAPS_DRIVE)) { + int bootCD = 0, bootFloppy = 0, bootDisk = 0; - if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", - tapfd) >= sizeof(tapfd_name)) - goto no_memory; + if ((qemuCapsGet(caps, QEMU_CAPS_DRIVE_BOOT) || emitBootindex)) { + /* bootDevs will get translated into either bootindex=N or boot=on + * depending on what qemu supports */ + for (i = 0 ; i < def->os.nBootDevs ; i++) { + switch (def->os.bootDevs[i]) { + case VIR_DOMAIN_BOOT_CDROM: + bootCD = i + 1; + break; + case VIR_DOMAIN_BOOT_FLOPPY: + bootFloppy = i + 1; + break; + case VIR_DOMAIN_BOOT_DISK: + bootDisk = i + 1; + break; + } } + } - if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || - actualType == VIR_DOMAIN_NET_TYPE_BRIDGE || - actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { - /* Attempt to use vhost-net mode for these types of - network device */ - int vhostfd; + for (i = 0 ; i < def->ndisks ; i++) { + char *optstr; + int bootindex = 0; + virDomainDiskDefPtr disk = def->disks[i]; + int withDeviceArg = 0; + bool deviceFlagMasked = false; - if (qemuOpenVhostNet(def, net, caps, &vhostfd) < 0) + /* Unless we have -device, then USB disks need special + handling */ + if ((disk->bus == VIR_DOMAIN_DISK_BUS_USB) && + !qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + virCommandAddArg(cmd, "-usbdevice"); + virCommandAddArgFormat(cmd, "disk:%s", disk->src); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported usb disk type for '%s'"), + disk->src); goto error; - if (vhostfd >= 0) { - virCommandTransferFD(cmd, vhostfd); - - if (snprintf(vhostfd_name, sizeof(vhostfd_name), "%d", - vhostfd) >= sizeof(vhostfd_name)) - goto no_memory; } + continue; } - /* Possible combinations: - * - * 1. Old way: -net nic,model=e1000,vlan=1 -net tap,vlan=1 - * 2. Semi-new: -device e1000,vlan=1 -net tap,vlan=1 - * 3. Best way: -netdev type=tap,id=netdev1 -device e1000,id=netdev1 - * - * NB, no support for -netdev without use of -device - */ - if (qemuCapsGet(caps, QEMU_CAPS_NETDEV) && - qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virCommandAddArg(cmd, "-netdev"); - if (!(host = qemuBuildHostNetStr(net, driver, caps, - ',', vlan, tapfd_name, - vhostfd_name))) - goto error; - virCommandAddArg(cmd, host); - VIR_FREE(host); + + switch (disk->device) { + case VIR_DOMAIN_DISK_DEVICE_CDROM: + bootindex = bootCD; + bootCD = 0; + break; + case VIR_DOMAIN_DISK_DEVICE_FLOPPY: + bootindex = bootFloppy; + bootFloppy = 0; + break; + case VIR_DOMAIN_DISK_DEVICE_DISK: + case VIR_DOMAIN_DISK_DEVICE_LUN: + bootindex = bootDisk; + bootDisk = 0; + break; } + + virCommandAddArg(cmd, "-drive"); + + /* Unfortunately it is not possible to use + -device for floppies, or Xen paravirt + devices. Fortunately, those don't need + static PCI addresses, so we don't really + care that we can't use -device */ if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virCommandAddArg(cmd, "-device"); - nic = qemuBuildNicDevStr(net, vlan, bootindex, caps); - if (!nic) - goto error; - virCommandAddArg(cmd, nic); - VIR_FREE(nic); - } else { - virCommandAddArg(cmd, "-net"); - if (!(nic = qemuBuildNicStr(net, "nic,", vlan))) - goto error; - virCommandAddArg(cmd, nic); - VIR_FREE(nic); - } - if (!(qemuCapsGet(caps, QEMU_CAPS_NETDEV) && - qemuCapsGet(caps, QEMU_CAPS_DEVICE))) { - virCommandAddArg(cmd, "-net"); - if (!(host = qemuBuildHostNetStr(net, driver, caps, - ',', vlan, tapfd_name, - vhostfd_name))) - goto error; - virCommandAddArg(cmd, host); - VIR_FREE(host); + if (disk->bus != VIR_DOMAIN_DISK_BUS_XEN) { + withDeviceArg = 1; + } else { + qemuCapsClear(caps, QEMU_CAPS_DEVICE); + deviceFlagMasked = true; + } } - } - } + optstr = qemuBuildDriveStr(conn, disk, + emitBootindex ? false : !!bootindex, + caps); + if (deviceFlagMasked) + qemuCapsSet(caps, QEMU_CAPS_DEVICE); + if (!optstr) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + + if (!emitBootindex) + bootindex = 0; + else if (disk->info.bootIndex) + bootindex = disk->info.bootIndex; - if (def->nsmartcards) { - /* -device usb-ccid was already emitted along with other - * controllers. For now, qemu handles only one smartcard. */ - virDomainSmartcardDefPtr smartcard = def->smartcards[0]; - char *devstr; - virBuffer opt = VIR_BUFFER_INITIALIZER; - const char *database; + if (withDeviceArg) { + if (disk->bus == VIR_DOMAIN_DISK_BUS_FDC) { + virCommandAddArg(cmd, "-global"); + virCommandAddArgFormat(cmd, "isa-fdc.drive%c=drive-%s", + disk->info.addr.drive.unit + ? 'B' : 'A', + disk->info.alias); - if (def->nsmartcards > 1 || - smartcard->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID || - smartcard->info.addr.ccid.controller != 0 || - smartcard->info.addr.ccid.slot != 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("this QEMU binary lacks multiple smartcard " - "support")); - virBufferFreeAndReset(&opt); - goto error; - } + if (bootindex) { + virCommandAddArg(cmd, "-global"); + virCommandAddArgFormat(cmd, "isa-fdc.bootindex%c=%d", + disk->info.addr.drive.unit + ? 'B' : 'A', + bootindex); + } + } else { + virCommandAddArg(cmd, "-device"); - switch (smartcard->type) { - case VIR_DOMAIN_SMARTCARD_TYPE_HOST: - if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) || - !qemuCapsGet(caps, QEMU_CAPS_CCID_EMULATED)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("this QEMU binary lacks smartcard host " - "mode support")); - goto error; + if (!(optstr = qemuBuildDriveDevStr(def, disk, bootindex, + caps))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + } } + } + } else { + for (i = 0 ; i < def->ndisks ; i++) { + char dev[NAME_MAX]; + char *file; + const char *fmt; + virDomainDiskDefPtr disk = def->disks[i]; - virBufferAddLit(&opt, "ccid-card-emulated,backend=nss-emulated"); - break; - - case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: - if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) || - !qemuCapsGet(caps, QEMU_CAPS_CCID_EMULATED)) { + if ((disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK) && + (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("this QEMU binary lacks smartcard host " - "mode support")); + _("tray status 'open' is invalid for " + "block type disk")); goto error; } - virBufferAddLit(&opt, "ccid-card-emulated,backend=certificates"); - for (j = 0; j < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; j++) { - if (strchr(smartcard->data.cert.file[j], ',')) { - virBufferFreeAndReset(&opt); - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("invalid certificate name: %s"), - smartcard->data.cert.file[j]); + if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { + if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + virCommandAddArg(cmd, "-usbdevice"); + virCommandAddArgFormat(cmd, "disk:%s", disk->src); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported usb disk type for '%s'"), + disk->src); goto error; } - virBufferAsprintf(&opt, ",cert%d=%s", j + 1, - smartcard->data.cert.file[j]); + continue; } - if (smartcard->data.cert.database) { - if (strchr(smartcard->data.cert.database, ',')) { - virBufferFreeAndReset(&opt); - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("invalid database name: %s"), - smartcard->data.cert.database); - goto error; + + if (STREQ(disk->dst, "hdc") && + disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + if (disk->src) { + snprintf(dev, NAME_MAX, "-%s", "cdrom"); + } else { + continue; } - database = smartcard->data.cert.database; } else { - database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE; + if (STRPREFIX(disk->dst, "hd") || + STRPREFIX(disk->dst, "fd")) { + snprintf(dev, NAME_MAX, "-%s", disk->dst); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk type '%s'"), disk->dst); + goto error; + } } - virBufferAsprintf(&opt, ",database=%s", database); - break; - case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: - if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) || - !qemuCapsGet(caps, QEMU_CAPS_CCID_PASSTHRU)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("this QEMU binary lacks smartcard " - "passthrough mode support")); - goto error; - } + if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { + /* QEMU only supports magic FAT format for now */ + if (disk->format > 0 && disk->format != VIR_STORAGE_FILE_FAT) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk driver type for '%s'"), + virStorageFileFormatTypeToString(disk->format)); + goto error; + } + if (!disk->readonly) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create virtual FAT disks in read-write mode")); + goto error; + } + if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) + fmt = "fat:floppy:%s"; + else + fmt = "fat:%s"; - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, - smartcard->info.alias, - caps))) { - virBufferFreeAndReset(&opt); - goto error; + if (virAsprintf(&file, fmt, disk->src) < 0) { + goto no_memory; + } + } else if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { + switch (disk->protocol) { + case VIR_DOMAIN_DISK_PROTOCOL_NBD: + if (disk->nhosts != 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("NBD accepts only one host")); + goto error; + } + if (virAsprintf(&file, "nbd:%s:%s,", disk->hosts->name, + disk->hosts->port) < 0) { + goto no_memory; + } + break; + case VIR_DOMAIN_DISK_PROTOCOL_RBD: + { + virBuffer opt = VIR_BUFFER_INITIALIZER; + if (qemuBuildRBDString(conn, disk, &opt) < 0) + goto error; + if (virBufferError(&opt)) { + virReportOOMError(); + goto error; + } + file = virBufferContentAndReset(&opt); + } + break; + case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: + if (disk->nhosts == 0) { + if (virAsprintf(&file, "sheepdog:%s,", disk->src) < 0) { + goto no_memory; + } + } else { + /* only one host is supported now */ + if (virAsprintf(&file, "sheepdog:%s:%s:%s,", + disk->hosts->name, disk->hosts->port, + disk->src) < 0) { + goto no_memory; + } + } + break; + } + } else { + if (!(file = strdup(disk->src))) { + goto no_memory; + } } - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - - virBufferAsprintf(&opt, "ccid-card-passthru,chardev=char%s", - smartcard->info.alias); - break; - default: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected smartcard type %d"), - smartcard->type); - virBufferFreeAndReset(&opt); - goto error; + /* Don't start with source if the tray is open for + * CDROM and Floppy device. + */ + if (!((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY || + disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) && + disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) + virCommandAddArgList(cmd, dev, file, NULL); + VIR_FREE(file); } - virCommandAddArg(cmd, "-device"); - virBufferAsprintf(&opt, ",id=%s,bus=ccid0.0", smartcard->info.alias); - virCommandAddArgBuffer(cmd, &opt); } - if (!def->nserials) { - /* If we have -device, then we set -nodefault already */ - if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) - virCommandAddArgList(cmd, "-serial", "none", NULL); - } else { - for (i = 0 ; i < def->nserials ; i++) { - virDomainChrDefPtr serial = def->serials[i]; - char *devstr; - - /* Use -chardev with -device if they are available */ - if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV) && - qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(&serial->source, - serial->info.alias, - caps))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); + if (qemuCapsGet(caps, QEMU_CAPS_FSDEV)) { + for (i = 0 ; i < def->nfss ; i++) { + char *optstr; + virDomainFSDefPtr fs = def->fss[i]; - virCommandAddArg(cmd, "-device"); - if (!(devstr = qemuBuildChrDeviceStr(serial, caps, - def->os.arch, - def->os.machine))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } else { - virCommandAddArg(cmd, "-serial"); - if (!(devstr = qemuBuildChrArgStr(&serial->source, NULL))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } + virCommandAddArg(cmd, "-fsdev"); + if (!(optstr = qemuBuildFSStr(fs, caps))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + + virCommandAddArg(cmd, "-device"); + if (!(optstr = qemuBuildFSDevStr(fs, caps))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + } + } else { + if (def->nfss) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("filesystem passthrough not supported by this QEMU")); + goto error; } } - if (!def->nparallels) { + if (!def->nnets) { /* If we have -device, then we set -nodefault already */ if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) - virCommandAddArgList(cmd, "-parallel", "none", NULL); + virCommandAddArgList(cmd, "-net", "none", NULL); } else { - for (i = 0 ; i < def->nparallels ; i++) { - virDomainChrDefPtr parallel = def->parallels[i]; - char *devstr; - - /* Use -chardev with -device if they are available */ - if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV) && - qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(¶llel->source, - parallel->info.alias, - caps))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); + int bootNet = 0; - virCommandAddArg(cmd, "-device"); - virCommandAddArgFormat(cmd, "isa-parallel,chardev=char%s,id=%s", - parallel->info.alias, - parallel->info.alias); - } else { - virCommandAddArg(cmd, "-parallel"); - if (!(devstr = qemuBuildChrArgStr(¶llel->source, NULL))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); + if (emitBootindex) { + /* convert <boot dev='network'/> to bootindex since we didn't emit + * -boot n + */ + for (i = 0 ; i < def->os.nBootDevs ; i++) { + if (def->os.bootDevs[i] == VIR_DOMAIN_BOOT_NET) { + bootNet = i + 1; + break; + } } } - } - - for (i = 0 ; i < def->nchannels ; i++) { - virDomainChrDefPtr channel = def->channels[i]; - char *devstr; - char *addr; - int port; - switch (channel->targetType) { - case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: - if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) || - !qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("guestfwd requires QEMU to support -chardev & -device")); - goto error; - } + for (i = 0 ; i < def->nnets ; i++) { + virDomainNetDefPtr net = def->nets[i]; + char *nic, *host; + char tapfd_name[50] = ""; + char vhostfd_name[50] = ""; + int vlan; + int bootindex = bootNet; + int actualType; - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(&channel->source, - channel->info.alias, - caps))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); + bootNet = 0; + if (!bootindex) + bootindex = net->info.bootIndex; - addr = virSocketAddrFormat(channel->target.addr); - if (!addr) - goto error; - port = virSocketAddrGetPort(channel->target.addr); + /* VLANs are not used with -netdev, so don't record them */ + if (qemuCapsGet(caps, QEMU_CAPS_NETDEV) && + qemuCapsGet(caps, QEMU_CAPS_DEVICE)) + vlan = -1; + else + vlan = i; - virCommandAddArg(cmd, "-netdev"); - virCommandAddArgFormat(cmd, - "user,guestfwd=tcp:%s:%i,chardev=char%s,id=user-%s", - addr, port, channel->info.alias, - channel->info.alias); - VIR_FREE(addr); - break; + /* If appropriate, grab a physical device from the configured + * network's pool of devices, or resolve bridge device name + * to the one defined in the network definition. + */ + if (networkAllocateActualDevice(net) < 0) + goto error; - case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: - if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("virtio channel requires QEMU to support -device")); - goto error; - } + actualType = virDomainNetGetActualType(net); + if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net); + virDomainHostdevDefPtr found; + /* For a network with <forward mode='hostdev'>, there is a need to + * add the newly minted hostdev to the hostdevs array. + */ + if (qemuAssignDeviceHostdevAlias(def, hostdev, + (def->nhostdevs-1)) < 0) { + goto error; + } - if (qemuCapsGet(caps, QEMU_CAPS_DEVICE_SPICEVMC) && - channel->source.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) { - /* spicevmc was originally introduced via a -device - * with a backend internal to qemu; although we prefer - * the newer -chardev interface. */ - ; - } else { - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(&channel->source, - channel->info.alias, - caps))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); + if (virDomainHostdevFind(def, hostdev, &found) < 0) { + if (virDomainHostdevInsert(def, hostdev) < 0) { + virReportOOMError(); + goto error; + } + if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, + &hostdev, 1) < 0) { + goto error; + } + } + else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("PCI device %04x:%02x:%02x.%x " + "allocated from network %s is already " + "in use by domain %s"), + hostdev->source.subsys.u.pci.domain, + hostdev->source.subsys.u.pci.bus, + hostdev->source.subsys.u.pci.slot, + hostdev->source.subsys.u.pci.function, + net->data.network.name, + def->name); + goto error; + } + } + continue; } - virCommandAddArg(cmd, "-device"); - if (!(devstr = qemuBuildVirtioSerialPortDevStr(channel, - caps))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - break; - } - } - - /* Explicit console devices */ - for (i = 0 ; i < def->nconsoles ; i++) { - virDomainChrDefPtr console = def->consoles[i]; - char *devstr; + if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || + actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) { + /* + * If type='bridge' then we attempt to allocate the tap fd here only if + * running under a privilged user or -netdev bridge option is not + * supported. + */ + if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || + driver->privileged || + (!qemuCapsGet(caps, QEMU_CAPS_NETDEV_BRIDGE))) { + int tapfd = qemuNetworkIfaceConnect(def, conn, driver, net, + caps); + if (tapfd < 0) + goto error; - switch (console->targetType) { - case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO: - if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("virtio channel requires QEMU to support -device")); - goto error; - } + last_good_net = i; + virCommandTransferFD(cmd, tapfd); - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(&console->source, - console->info.alias, - caps))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); + if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", + tapfd) >= sizeof(tapfd_name)) + goto no_memory; + } + } else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { + int tapfd = qemuPhysIfaceConnect(def, driver, net, + caps, vmop); + if (tapfd < 0) + goto error; - virCommandAddArg(cmd, "-device"); - if (!(devstr = qemuBuildVirtioSerialPortDevStr(console, - caps))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - break; + last_good_net = i; + virCommandTransferFD(cmd, tapfd); - case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL: - break; + if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", + tapfd) >= sizeof(tapfd_name)) + goto no_memory; + } - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported console target type %s"), - NULLSTR(virDomainChrConsoleTargetTypeToString(console->targetType))); - goto error; - } - } + if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || + actualType == VIR_DOMAIN_NET_TYPE_BRIDGE || + actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { + /* Attempt to use vhost-net mode for these types of + network device */ + int vhostfd; - for (i = 0 ; i < def->ninputs ; i++) { - virDomainInputDefPtr input = def->inputs[i]; + if (qemuOpenVhostNet(def, net, caps, &vhostfd) < 0) + goto error; + if (vhostfd >= 0) { + virCommandTransferFD(cmd, vhostfd); - if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) { + if (snprintf(vhostfd_name, sizeof(vhostfd_name), "%d", + vhostfd) >= sizeof(vhostfd_name)) + goto no_memory; + } + } + /* Possible combinations: + * + * 1. Old way: -net nic,model=e1000,vlan=1 -net tap,vlan=1 + * 2. Semi-new: -device e1000,vlan=1 -net tap,vlan=1 + * 3. Best way: -netdev type=tap,id=netdev1 -device e1000,id=netdev1 + * + * NB, no support for -netdev without use of -device + */ + if (qemuCapsGet(caps, QEMU_CAPS_NETDEV) && + qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + virCommandAddArg(cmd, "-netdev"); + if (!(host = qemuBuildHostNetStr(net, driver, caps, + ',', vlan, tapfd_name, + vhostfd_name))) + goto error; + virCommandAddArg(cmd, host); + VIR_FREE(host); + } if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - char *optstr; virCommandAddArg(cmd, "-device"); - if (!(optstr = qemuBuildUSBInputDevStr(input, caps))) + nic = qemuBuildNicDevStr(net, vlan, bootindex, caps); + if (!nic) goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); + virCommandAddArg(cmd, nic); + VIR_FREE(nic); } else { - virCommandAddArgList(cmd, "-usbdevice", - input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE - ? "mouse" : "tablet", NULL); + virCommandAddArg(cmd, "-net"); + if (!(nic = qemuBuildNicStr(net, "nic,", vlan))) + goto error; + virCommandAddArg(cmd, nic); + VIR_FREE(nic); + } + if (!(qemuCapsGet(caps, QEMU_CAPS_NETDEV) && + qemuCapsGet(caps, QEMU_CAPS_DEVICE))) { + virCommandAddArg(cmd, "-net"); + if (!(host = qemuBuildHostNetStr(net, driver, caps, + ',', vlan, tapfd_name, + vhostfd_name))) + goto error; + virCommandAddArg(cmd, host); + VIR_FREE(host); } } } - if (def->ngraphics > 1) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("only 1 graphics device is supported")); - goto error; - } - - if ((def->ngraphics == 1) && - def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + if (def->nsmartcards) { + /* -device usb-ccid was already emitted along with other + * controllers. For now, qemu handles only one smartcard. */ + virDomainSmartcardDefPtr smartcard = def->smartcards[0]; + char *devstr; virBuffer opt = VIR_BUFFER_INITIALIZER; + const char *database; - if (!qemuCapsGet(caps, QEMU_CAPS_VNC)) { + if (def->nsmartcards > 1 || + smartcard->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID || + smartcard->info.addr.ccid.controller != 0 || + smartcard->info.addr.ccid.slot != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("vnc graphics are not supported with this QEMU")); + _("this QEMU binary lacks multiple smartcard " + "support")); + virBufferFreeAndReset(&opt); goto error; } - if (def->graphics[0]->data.vnc.socket || - driver->vncAutoUnixSocket) { - - if (!def->graphics[0]->data.vnc.socket && - virAsprintf(&def->graphics[0]->data.vnc.socket, - "%s/%s.vnc", driver->libDir, def->name) == -1) { - goto no_memory; + switch (smartcard->type) { + case VIR_DOMAIN_SMARTCARD_TYPE_HOST: + if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) || + !qemuCapsGet(caps, QEMU_CAPS_CCID_EMULATED)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this QEMU binary lacks smartcard host " + "mode support")); + goto error; } - virBufferAsprintf(&opt, "unix:%s", - def->graphics[0]->data.vnc.socket); - - } else if (qemuCapsGet(caps, QEMU_CAPS_VNC_COLON)) { - const char *listenNetwork; - const char *listenAddr = NULL; - char *netAddr = NULL; - bool escapeAddr; - int ret; + virBufferAddLit(&opt, "ccid-card-emulated,backend=nss-emulated"); + break; - switch (virDomainGraphicsListenGetType(def->graphics[0], 0)) { - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: - listenAddr = virDomainGraphicsListenGetAddress(def->graphics[0], 0); - break; + case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: + if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) || + !qemuCapsGet(caps, QEMU_CAPS_CCID_EMULATED)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this QEMU binary lacks smartcard host " + "mode support")); + goto error; + } - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: - listenNetwork = virDomainGraphicsListenGetNetwork(def->graphics[0], 0); - if (!listenNetwork) - break; - ret = networkGetNetworkAddress(listenNetwork, &netAddr); - if (ret <= -2) { + virBufferAddLit(&opt, "ccid-card-emulated,backend=certificates"); + for (j = 0; j < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; j++) { + if (strchr(smartcard->data.cert.file[j], ',')) { + virBufferFreeAndReset(&opt); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("network-based listen not possible, " - "network driver not present")); + _("invalid certificate name: %s"), + smartcard->data.cert.file[j]); goto error; } - if (ret < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("listen network '%s' had no usable address"), - listenNetwork); + virBufferAsprintf(&opt, ",cert%d=%s", j + 1, + smartcard->data.cert.file[j]); + } + if (smartcard->data.cert.database) { + if (strchr(smartcard->data.cert.database, ',')) { + virBufferFreeAndReset(&opt); + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("invalid database name: %s"), + smartcard->data.cert.database); goto error; } - listenAddr = netAddr; - /* store the address we found in the <graphics> element so it will - * show up in status. */ - if (virDomainGraphicsListenSetAddress(def->graphics[0], 0, - listenAddr, -1, false) < 0) - goto error; - break; + database = smartcard->data.cert.database; + } else { + database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE; } + virBufferAsprintf(&opt, ",database=%s", database); + break; - if (!listenAddr) - listenAddr = driver->vncListen; - - escapeAddr = strchr(listenAddr, ':') != NULL; - if (escapeAddr) - virBufferAsprintf(&opt, "[%s]", listenAddr); - else - virBufferAdd(&opt, listenAddr, -1); - virBufferAsprintf(&opt, ":%d", - def->graphics[0]->data.vnc.port - 5900); - - VIR_FREE(netAddr); - } else { - virBufferAsprintf(&opt, "%d", - def->graphics[0]->data.vnc.port - 5900); - } - - if (qemuCapsGet(caps, QEMU_CAPS_VNC_COLON)) { - if (def->graphics[0]->data.vnc.auth.passwd || - driver->vncPassword) - virBufferAddLit(&opt, ",password"); - - if (driver->vncTLS) { - virBufferAddLit(&opt, ",tls"); - if (driver->vncTLSx509verify) { - virBufferAsprintf(&opt, ",x509verify=%s", - driver->vncTLSx509certdir); - } else { - virBufferAsprintf(&opt, ",x509=%s", - driver->vncTLSx509certdir); - } + case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: + if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) || + !qemuCapsGet(caps, QEMU_CAPS_CCID_PASSTHRU)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("this QEMU binary lacks smartcard " + "passthrough mode support")); + goto error; } - if (driver->vncSASL) { - virBufferAddLit(&opt, ",sasl"); - - if (driver->vncSASLdir) - virCommandAddEnvPair(cmd, "SASL_CONF_DIR", - driver->vncSASLdir); - - /* TODO: Support ACLs later */ + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, + smartcard->info.alias, + caps))) { + virBufferFreeAndReset(&opt); + goto error; } - } + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - virCommandAddArg(cmd, "-vnc"); - virCommandAddArgBuffer(cmd, &opt); - if (def->graphics[0]->data.vnc.keymap) { - virCommandAddArgList(cmd, "-k", def->graphics[0]->data.vnc.keymap, - NULL); - } + virBufferAsprintf(&opt, "ccid-card-passthru,chardev=char%s", + smartcard->info.alias); + break; - /* Unless user requested it, set the audio backend to none, to - * prevent it opening the host OS audio devices, since that causes - * security issues and might not work when using VNC. - */ - if (driver->vncAllowHostAudio) { - virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); - } else { - virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none"); - } - } else if ((def->ngraphics == 1) && - def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { - if (qemuCapsGet(caps, QEMU_CAPS_0_10) && - !qemuCapsGet(caps, QEMU_CAPS_SDL)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("sdl not supported by '%s'"), - def->emulator); + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected smartcard type %d"), + smartcard->type); + virBufferFreeAndReset(&opt); goto error; } + virCommandAddArg(cmd, "-device"); + virBufferAsprintf(&opt, ",id=%s,bus=ccid0.0", smartcard->info.alias); + virCommandAddArgBuffer(cmd, &opt); + } - if (def->graphics[0]->data.sdl.xauth) - virCommandAddEnvPair(cmd, "XAUTHORITY", - def->graphics[0]->data.sdl.xauth); - if (def->graphics[0]->data.sdl.display) - virCommandAddEnvPair(cmd, "DISPLAY", - def->graphics[0]->data.sdl.display); - if (def->graphics[0]->data.sdl.fullscreen) - virCommandAddArg(cmd, "-full-screen"); + if (!def->nserials) { + /* If we have -device, then we set -nodefault already */ + if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) + virCommandAddArgList(cmd, "-serial", "none", NULL); + } else { + for (i = 0 ; i < def->nserials ; i++) { + virDomainChrDefPtr serial = def->serials[i]; + char *devstr; - /* If using SDL for video, then we should just let it - * use QEMU's host audio drivers, possibly SDL too - * User can set these two before starting libvirtd - */ - virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); - virCommandAddEnvPass(cmd, "SDL_AUDIODRIVER"); + /* Use -chardev with -device if they are available */ + if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV) && + qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&serial->source, + serial->info.alias, + caps))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildChrDeviceStr(serial, caps, + def->os.arch, + def->os.machine))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } else { + virCommandAddArg(cmd, "-serial"); + if (!(devstr = qemuBuildChrArgStr(&serial->source, NULL))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } + } + } - /* New QEMU has this flag to let us explicitly ask for - * SDL graphics. This is better than relying on the - * default, since the default changes :-( */ - if (qemuCapsGet(caps, QEMU_CAPS_SDL)) - virCommandAddArg(cmd, "-sdl"); + if (!def->nparallels) { + /* If we have -device, then we set -nodefault already */ + if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) + virCommandAddArgList(cmd, "-parallel", "none", NULL); + } else { + for (i = 0 ; i < def->nparallels ; i++) { + virDomainChrDefPtr parallel = def->parallels[i]; + char *devstr; - } else if ((def->ngraphics == 1) && - def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { - virBuffer opt = VIR_BUFFER_INITIALIZER; - const char *listenNetwork; - const char *listenAddr = NULL; - char *netAddr = NULL; - int ret; - int defaultMode = def->graphics[0]->data.spice.defaultMode; + /* Use -chardev with -device if they are available */ + if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV) && + qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(¶llel->source, + parallel->info.alias, + caps))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - if (!qemuCapsGet(caps, QEMU_CAPS_SPICE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("spice graphics are not supported with this QEMU")); - goto error; + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "isa-parallel,chardev=char%s,id=%s", + parallel->info.alias, + parallel->info.alias); + } else { + virCommandAddArg(cmd, "-parallel"); + if (!(devstr = qemuBuildChrArgStr(¶llel->source, NULL))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } } + } - virBufferAsprintf(&opt, "port=%u", def->graphics[0]->data.spice.port); + for (i = 0 ; i < def->nchannels ; i++) { + virDomainChrDefPtr channel = def->channels[i]; + char *devstr; + char *addr; + int port; - if (def->graphics[0]->data.spice.tlsPort > 0) { - if (!driver->spiceTLS) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("spice TLS port set in XML configuration," - " but TLS is disabled in qemu.conf")); + switch (channel->targetType) { + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: + if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) || + !qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("guestfwd requires QEMU to support -chardev & -device")); goto error; } - virBufferAsprintf(&opt, ",tls-port=%u", - def->graphics[0]->data.spice.tlsPort); - } - switch (virDomainGraphicsListenGetType(def->graphics[0], 0)) { - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: - listenAddr = virDomainGraphicsListenGetAddress(def->graphics[0], 0); + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&channel->source, + channel->info.alias, + caps))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + addr = virSocketAddrFormat(channel->target.addr); + if (!addr) + goto error; + port = virSocketAddrGetPort(channel->target.addr); + + virCommandAddArg(cmd, "-netdev"); + virCommandAddArgFormat(cmd, + "user,guestfwd=tcp:%s:%i,chardev=char%s,id=user-%s", + addr, port, channel->info.alias, + channel->info.alias); + VIR_FREE(addr); break; - case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: - listenNetwork = virDomainGraphicsListenGetNetwork(def->graphics[0], 0); - if (!listenNetwork) - break; - ret = networkGetNetworkAddress(listenNetwork, &netAddr); - if (ret <= -2) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("network-based listen not possible, " - "network driver not present")); + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: + if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("virtio channel requires QEMU to support -device")); goto error; } - if (ret < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("listen network '%s' had no usable address"), - listenNetwork); - goto error; + + if (qemuCapsGet(caps, QEMU_CAPS_DEVICE_SPICEVMC) && + channel->source.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) { + /* spicevmc was originally introduced via a -device + * with a backend internal to qemu; although we prefer + * the newer -chardev interface. */ + ; + } else { + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&channel->source, + channel->info.alias, + caps))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } - listenAddr = netAddr; - /* store the address we found in the <graphics> element so it will - * show up in status. */ - if (virDomainGraphicsListenSetAddress(def->graphics[0], 0, - listenAddr, -1, false) < 0) - goto error; + + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildVirtioSerialPortDevStr(channel, + caps))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); break; } + } - if (!listenAddr) - listenAddr = driver->spiceListen; - if (listenAddr) - virBufferAsprintf(&opt, ",addr=%s", listenAddr); - - VIR_FREE(netAddr); + /* Explicit console devices */ + for (i = 0 ; i < def->nconsoles ; i++) { + virDomainChrDefPtr console = def->consoles[i]; + char *devstr; - int mm = def->graphics[0]->data.spice.mousemode; - if (mm) { - switch (mm) { - case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_SERVER: - virBufferAsprintf(&opt, ",agent-mouse=off"); - break; - case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_CLIENT: - virBufferAsprintf(&opt, ",agent-mouse=on"); - break; - default: - break; + switch (console->targetType) { + case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO: + if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("virtio channel requires QEMU to support -device")); + goto error; } - } - - /* In the password case we set it via monitor command, to avoid - * making it visible on CLI, so there's no use of password=XXX - * in this bit of the code */ - if (!def->graphics[0]->data.spice.auth.passwd && - !driver->spicePassword) - virBufferAddLit(&opt, ",disable-ticketing"); - if (driver->spiceTLS) - virBufferAsprintf(&opt, ",x509-dir=%s", - driver->spiceTLSx509certdir); + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&console->source, + console->info.alias, + caps))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - switch (defaultMode) { - case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE: - virBufferAsprintf(&opt, ",tls-channel=default"); - break; - case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE: - virBufferAsprintf(&opt, ",plaintext-channel=default"); + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildVirtioSerialPortDevStr(console, + caps))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); break; - case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY: - /* nothing */ + + case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL: break; + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported console target type %s"), + NULLSTR(virDomainChrConsoleTargetTypeToString(console->targetType))); + goto error; } + } - for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) { - int mode = def->graphics[0]->data.spice.channels[i]; - switch (mode) { - case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE: - if (!driver->spiceTLS) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("spice secure channels set in XML configuration, but TLS is disabled in qemu.conf")); + for (i = 0 ; i < def->ninputs ; i++) { + virDomainInputDefPtr input = def->inputs[i]; + + if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) { + if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + char *optstr; + virCommandAddArg(cmd, "-device"); + if (!(optstr = qemuBuildUSBInputDevStr(input, caps))) goto error; - } - virBufferAsprintf(&opt, ",tls-channel=%s", - virDomainGraphicsSpiceChannelNameTypeToString(i)); - break; - case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE: - virBufferAsprintf(&opt, ",plaintext-channel=%s", - virDomainGraphicsSpiceChannelNameTypeToString(i)); - break; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + } else { + virCommandAddArgList(cmd, "-usbdevice", + input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE + ? "mouse" : "tablet", NULL); } } - if (def->graphics[0]->data.spice.image) - virBufferAsprintf(&opt, ",image-compression=%s", - virDomainGraphicsSpiceImageCompressionTypeToString(def->graphics[0]->data.spice.image)); - if (def->graphics[0]->data.spice.jpeg) - virBufferAsprintf(&opt, ",jpeg-wan-compression=%s", - virDomainGraphicsSpiceJpegCompressionTypeToString(def->graphics[0]->data.spice.jpeg)); - if (def->graphics[0]->data.spice.zlib) - virBufferAsprintf(&opt, ",zlib-glz-wan-compression=%s", - virDomainGraphicsSpiceZlibCompressionTypeToString(def->graphics[0]->data.spice.zlib)); - if (def->graphics[0]->data.spice.playback) - virBufferAsprintf(&opt, ",playback-compression=%s", - virDomainGraphicsSpicePlaybackCompressionTypeToString(def->graphics[0]->data.spice.playback)); - if (def->graphics[0]->data.spice.streaming) - virBufferAsprintf(&opt, ",streaming-video=%s", - virDomainGraphicsSpiceStreamingModeTypeToString(def->graphics[0]->data.spice.streaming)); - if (def->graphics[0]->data.spice.copypaste == VIR_DOMAIN_GRAPHICS_SPICE_CLIPBOARD_COPYPASTE_NO) - virBufferAddLit(&opt, ",disable-copy-paste"); - - if (qemuCapsGet(caps, QEMU_CAPS_SEAMLESS_MIGRATION)) { - /* If qemu supports seamless migration turn it - * unconditionally on. If migration destination - * doesn't support it, it fallbacks to previous - * migration algorithm silently. */ - virBufferAddLit(&opt, ",seamless-migration=on"); - } - - virCommandAddArg(cmd, "-spice"); - virCommandAddArgBuffer(cmd, &opt); - if (def->graphics[0]->data.spice.keymap) - virCommandAddArgList(cmd, "-k", - def->graphics[0]->data.spice.keymap, NULL); - /* SPICE includes native support for tunnelling audio, so we - * set the audio backend to point at SPICE's own driver - */ - virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=spice"); + } - } else if ((def->ngraphics == 1)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported graphics type '%s'"), - virDomainGraphicsTypeToString(def->graphics[0]->type)); + if (def->ngraphics > 1) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("only 1 graphics device is supported")); goto error; } + for (i = 0 ; i < def->ngraphics ; ++i) { + switch (qemuBuildGraphicsCommandLine(driver, cmd, def, caps, + def->graphics[i])) { + case ERROR: + goto error; + case NO_MEMORY: + goto no_memory; + } + } if (def->nvideos > 0) { if (qemuCapsGet(caps, QEMU_CAPS_VGA)) { if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_XEN) { diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index d8cf4c3..2a77290 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2081,16 +2081,17 @@ qemuProcessInitPasswords(virConnectPtr conn, int ret = 0; qemuDomainObjPrivatePtr priv = vm->privateData; - if (vm->def->ngraphics == 1) { - if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + for (int i = 0 ; i < vm->def->ngraphics; ++i) { + virDomainGraphicsDefPtr graphics = vm->def->graphics[i]; + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { ret = qemuDomainChangeGraphicsPasswords(driver, vm, VIR_DOMAIN_GRAPHICS_TYPE_VNC, - &vm->def->graphics[0]->data.vnc.auth, + &graphics->data.vnc.auth, driver->vncPassword); - } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + } else if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { ret = qemuDomainChangeGraphicsPasswords(driver, vm, VIR_DOMAIN_GRAPHICS_TYPE_SPICE, - &vm->def->graphics[0]->data.spice.auth, + &graphics->data.spice.auth, driver->spicePassword); } } @@ -3484,21 +3485,22 @@ int qemuProcessStart(virConnectPtr conn, VIR_DEBUG("Ensuring no historical cgroup is lying around"); qemuRemoveCgroup(driver, vm, 1); - if (vm->def->ngraphics == 1) { - if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - !vm->def->graphics[0]->data.vnc.socket && - vm->def->graphics[0]->data.vnc.autoport) { + for (i = 0 ; i < vm->def->ngraphics; ++i) { + virDomainGraphicsDefPtr graphics = vm->def->graphics[i]; + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && + !graphics->data.vnc.socket && + graphics->data.vnc.autoport) { int port = qemuProcessNextFreePort(driver, driver->remotePortMin); if (port < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to find an unused port for VNC")); goto cleanup; } - vm->def->graphics[0]->data.vnc.port = port; - } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + graphics->data.vnc.port = port; + } else if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { int port = -1; - if (vm->def->graphics[0]->data.spice.autoport || - vm->def->graphics[0]->data.spice.port == -1) { + if (graphics->data.spice.autoport || + graphics->data.spice.port == -1) { port = qemuProcessNextFreePort(driver, driver->remotePortMin); if (port < 0) { @@ -3507,13 +3509,13 @@ int qemuProcessStart(virConnectPtr conn, goto cleanup; } - vm->def->graphics[0]->data.spice.port = port; + graphics->data.spice.port = port; } if (driver->spiceTLS && - (vm->def->graphics[0]->data.spice.autoport || - vm->def->graphics[0]->data.spice.tlsPort == -1)) { + (graphics->data.spice.autoport || + graphics->data.spice.tlsPort == -1)) { int tlsPort = qemuProcessNextFreePort(driver, - vm->def->graphics[0]->data.spice.port + 1); + graphics->data.spice.port + 1); if (tlsPort < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to find an unused port for SPICE TLS")); @@ -3521,20 +3523,19 @@ int qemuProcessStart(virConnectPtr conn, goto cleanup; } - vm->def->graphics[0]->data.spice.tlsPort = tlsPort; + graphics->data.spice.tlsPort = tlsPort; } } - if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC || - vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { - virDomainGraphicsDefPtr graphics = vm->def->graphics[0]; + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC || + graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { if (graphics->nListens == 0) { if (VIR_EXPAND_N(graphics->listens, graphics->nListens, 1) < 0) { virReportOOMError(); goto cleanup; } graphics->listens[0].type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS; - if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) graphics->listens[0].address = strdup(driver->vncListen); else graphics->listens[0].address = strdup(driver->spiceListen); @@ -4150,19 +4151,20 @@ retry: qemuProcessRemoveDomainStatus(driver, vm); - /* Remove VNC port from port reservation bitmap, but only if it was - reserved by the driver (autoport=yes) + /* Remove VNC and Spice ports from port reservation bitmap, but only if + they were reserved by the driver (autoport=yes) */ - if ((vm->def->ngraphics == 1) && - vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - vm->def->graphics[0]->data.vnc.autoport) { - qemuProcessReturnPort(driver, vm->def->graphics[0]->data.vnc.port); - } - if ((vm->def->ngraphics == 1) && - vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && - vm->def->graphics[0]->data.spice.autoport) { - qemuProcessReturnPort(driver, vm->def->graphics[0]->data.spice.port); - qemuProcessReturnPort(driver, vm->def->graphics[0]->data.spice.tlsPort); + for (i = 0 ; i < vm->def->ngraphics; ++i) { + virDomainGraphicsDefPtr graphics = vm->def->graphics[i]; + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && + graphics->data.vnc.autoport) { + qemuProcessReturnPort(driver, graphics->data.vnc.port); + } + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && + graphics->data.spice.autoport) { + qemuProcessReturnPort(driver, graphics->data.spice.port); + qemuProcessReturnPort(driver, graphics->data.spice.tlsPort); + } } vm->taint = 0; -- 1.8.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list