From: Alon Levy Date: Sat, 10 Nov 2012 01:40:23 +0000 (+0100) Subject: qemu: refactor graphics code to not hardcode a single display X-Git-Tag: CVE-2012-3411~103 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=23e8b5d8;p=thirdparty%2Flibvirt.git qemu: refactor graphics code to not hardcode a single display The check for a single display remains so no new functionality is added. --- diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 22bb209665..432fce314f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4422,1779 +4422,1796 @@ 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) +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, - }; - - 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 (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + virBuffer opt = VIR_BUFFER_INITIALIZER; - 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); + if (!qemuCapsGet(caps, QEMU_CAPS_VNC)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vnc graphics are not supported with this QEMU")); goto error; } - break; - case VIR_DOMAIN_VIRT_KVM: - if (qemuCapsGet(caps, QEMU_CAPS_KQEMU)) - disableKQEMU = 1; + if (graphics->data.vnc.socket || + driver->vncAutoUnixSocket) { - 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; + if (!graphics->data.vnc.socket && + virAsprintf(&graphics->data.vnc.socket, + "%s/%s.vnc", driver->libDir, def->name) == -1) { + goto no_memory; + } - case VIR_DOMAIN_VIRT_XEN: - /* XXX better check for xenner */ - break; + virBufferAsprintf(&opt, "unix:%s", + graphics->data.vnc.socket); - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary %s does not support %s"), - emulator, virDomainVirtTypeToString(def->virtType)); - break; - } + } else if (qemuCapsGet(caps, QEMU_CAPS_VNC_COLON)) { + const char *listenNetwork; + const char *listenAddr = NULL; + char *netAddr = NULL; + bool escapeAddr; + int ret; - cmd = virCommandNew(emulator); + switch (virDomainGraphicsListenGetType(graphics, 0)) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + listenAddr = virDomainGraphicsListenGetAddress(graphics, 0); + break; - virCommandAddEnvPassCommon(cmd); + 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")); + goto error; + } + if (ret < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("listen network '%s' had no usable address"), + listenNetwork); + goto error; + } + listenAddr = netAddr; + /* store the address we found in the element so it will + * show up in status. */ + if (virDomainGraphicsListenSetAddress(graphics, 0, + listenAddr, -1, false) < 0) + goto error; + break; + } - 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); + if (!listenAddr) + listenAddr = driver->vncListen; + + 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 { - virCommandAddArg(cmd, def->name); + virBufferAsprintf(&opt, "%d", + graphics->data.vnc.port - 5900); } - } - 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 (qemuCapsGet(caps, QEMU_CAPS_VNC_COLON)) { + if (graphics->data.vnc.auth.passwd || + driver->vncPassword) + virBufferAddLit(&opt, ",password"); - if (cpu) { - virCommandAddArgList(cmd, "-cpu", cpu, NULL); - VIR_FREE(cpu); + if (driver->vncTLS) { + virBufferAddLit(&opt, ",tls"); + if (driver->vncTLSx509verify) { + virBufferAsprintf(&opt, ",x509verify=%s", + driver->vncTLSx509certdir); + } else { + virBufferAsprintf(&opt, ",x509=%s", + driver->vncTLSx509certdir); + } + } - if (qemuCapsGet(caps, QEMU_CAPS_NESTING) && - hasHwVirt) - virCommandAddArg(cmd, "-enable-nesting"); - } + if (driver->vncSASL) { + virBufferAddLit(&opt, ",sasl"); - 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 (driver->vncSASLdir) + virCommandAddEnvPair(cmd, "SASL_CONF_DIR", + driver->vncSASLdir); - if (def->os.loader) { - virCommandAddArg(cmd, "-bios"); - virCommandAddArg(cmd, def->os.loader); - } + /* TODO: Support ACLs later */ + } + } - /* 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) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("hugetlbfs filesystem is not mounted")); - goto error; + virCommandAddArg(cmd, "-vnc"); + virCommandAddArgBuffer(cmd, &opt); + if (graphics->data.vnc.keymap) { + virCommandAddArgList(cmd, "-k", graphics->data.vnc.keymap, + NULL); } - if (!driver->hugepage_path) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("hugepages are disabled by administrator config")); - goto error; + + /* 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"); } - if (!qemuCapsGet(caps, QEMU_CAPS_MEM_PATH)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("hugepage backing not supported by '%s'"), + } 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); goto error; } - virCommandAddArgList(cmd, "-mem-prealloc", "-mem-path", - driver->hugepage_path, NULL); - } - virCommandAddArg(cmd, "-smp"); - if (!(smp = qemuBuildSmpArgStr(def, caps))) - goto error; - virCommandAddArg(cmd, smp); - VIR_FREE(smp); + 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 (def->cpu && def->cpu->ncells) - if (qemuBuildNumaArgStr(def, cmd) < 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 (qemuCapsGet(caps, QEMU_CAPS_UUID)) - virCommandAddArgList(cmd, "-uuid", uuid, NULL); - if (def->virtType == VIR_DOMAIN_VIRT_XEN || - STREQ(def->os.type, "xen") || - STREQ(def->os.type, "linux")) { - if (qemuCapsGet(caps, QEMU_CAPS_DOMID)) { - virCommandAddArg(cmd, "-domid"); - virCommandAddArgFormat(cmd, "%d", def->id); - } else if (qemuCapsGet(caps, QEMU_CAPS_XEN_DOMID)) { - virCommandAddArg(cmd, "-xen-attach"); - virCommandAddArg(cmd, "-xen-domid"); - virCommandAddArgFormat(cmd, "%d", def->id); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("qemu emulator '%s' does not support xen"), - def->emulator); - goto error; - } - } + /* 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->os.smbios_mode != VIR_DOMAIN_SMBIOS_NONE) && - (def->os.smbios_mode != VIR_DOMAIN_SMBIOS_EMULATE)) { - virSysinfoDefPtr source = NULL; - bool skip_uuid = false; + } 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; + int port = graphics->data.spice.port; + int tlsPort = graphics->data.spice.tlsPort; - if (!qemuCapsGet(caps, QEMU_CAPS_SMBIOS_TYPE)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary %s does not support smbios settings"), - emulator); + if (!qemuCapsGet(caps, QEMU_CAPS_SPICE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("spice graphics are not supported with this QEMU")); goto error; } - /* should we really error out or just warn in those cases ? */ - if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_HOST) { - if (driver->hostsysinfo == NULL) { + if (port > 0 || tlsPort <= 0) + virBufferAsprintf(&opt, "port=%u", port); + + if (tlsPort > 0) { + if (!driver->spiceTLS) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Host SMBIOS information is not available")); + _("spice TLS port set in XML configuration," + " but TLS is disabled in qemu.conf")); goto error; } - source = driver->hostsysinfo; - /* Host and guest uuid must differ, by definition of UUID. */ - skip_uuid = true; - } else if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_SYSINFO) { - if (def->sysinfo == NULL) { + if (port > 0) + virBufferAddChar(&opt, ','); + virBufferAsprintf(&opt, "tls-port=%u", 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")); + goto error; + } + if (ret < 0) { virReportError(VIR_ERR_XML_ERROR, - _("Domain '%s' sysinfo are not available"), - def->name); + _("listen network '%s' had no usable address"), + listenNetwork); goto error; } - source = def->sysinfo; - /* domain_conf guaranteed that system_uuid matches guest uuid. */ + listenAddr = netAddr; + /* store the address we found in the element so it will + * show up in status. */ + if (virDomainGraphicsListenSetAddress(graphics, 0, + listenAddr, -1, false) < 0) + goto error; + break; } - if (source != NULL) { - char *smbioscmd; - smbioscmd = qemuBuildSmbiosBiosStr(source); - if (smbioscmd != NULL) { - virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); - VIR_FREE(smbioscmd); - } - smbioscmd = qemuBuildSmbiosSystemStr(source, skip_uuid); - if (smbioscmd != NULL) { - virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); - VIR_FREE(smbioscmd); + 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; } } - } - /* - * NB, -nographic *MUST* come before any serial, or monitor - * or parallel port flags due to QEMU craziness, where it - * decides to change the serial port & monitor to be on stdout - * if you ask for nographic. So we have to make sure we override - * these defaults ourselves... - */ - if (!def->graphics) - virCommandAddArg(cmd, "-nographic"); + /* 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 (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - /* Disable global config files and default devices */ - if (qemuCapsGet(caps, QEMU_CAPS_NO_USER_CONFIG)) - virCommandAddArg(cmd, "-no-user-config"); - else if (qemuCapsGet(caps, QEMU_CAPS_NODEFCONFIG)) - virCommandAddArg(cmd, "-nodefconfig"); - virCommandAddArg(cmd, "-nodefaults"); - } + if (driver->spiceTLS) + virBufferAsprintf(&opt, ",x509-dir=%s", + driver->spiceTLSx509certdir); - /* Serial graphics adapter */ - if (def->os.bios.useserial == VIR_DOMAIN_BIOS_USESERIAL_YES) { - if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("qemu does not support -device")); - goto error; + 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; } - if (!qemuCapsGet(caps, QEMU_CAPS_SGA)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("qemu does not support SGA")); - goto error; + + 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")); + 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; + } } - if (!def->nserials) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("need at least one serial port to use SGA")); - goto error; + 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"); } - virCommandAddArgList(cmd, "-device", "sga", NULL); - } - if (monitor_chr) { - char *chrdev; - /* Use -chardev if it's available */ - if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV)) { + 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"); - virCommandAddArg(cmd, "-chardev"); - if (!(chrdev = qemuBuildChrChardevStr(monitor_chr, "monitor", - caps))) - goto error; - virCommandAddArg(cmd, chrdev); - VIR_FREE(chrdev); + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported graphics type '%s'"), + virDomainGraphicsTypeToString(graphics->type)); + goto error; + } - virCommandAddArg(cmd, "-mon"); - virCommandAddArgFormat(cmd, - "chardev=charmonitor,id=monitor,mode=%s", - monitor_json ? "control" : "readline"); - } else { - const char *prefix = NULL; - if (monitor_json) - prefix = "control,"; + return 0; - virCommandAddArg(cmd, "-monitor"); - if (!(chrdev = qemuBuildChrArgStr(monitor_chr, prefix))) - goto error; - virCommandAddArg(cmd, chrdev); - VIR_FREE(chrdev); - } - } +no_memory: + virReportOOMError(); +error: + return -1; +} - if (qemuCapsGet(caps, QEMU_CAPS_RTC)) { - const char *rtcopt; - virCommandAddArg(cmd, "-rtc"); - if (!(rtcopt = qemuBuildClockArgStr(&def->clock))) - goto error; - virCommandAddArg(cmd, rtcopt); - VIR_FREE(rtcopt); - } else { - switch (def->clock.offset) { - case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: - case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: - virCommandAddArg(cmd, "-localtime"); - break; +/* + * 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, + }; - case VIR_DOMAIN_CLOCK_OFFSET_UTC: - /* Nothing, its the default */ - break; + 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); - default: + 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, - _("unsupported clock offset '%s'"), - virDomainClockOffsetTypeToString(def->clock.offset)); + _("the QEMU binary %s does not support kqemu"), + emulator); goto error; } - } - if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE && - def->clock.data.timezone) { - virCommandAddEnvPair(cmd, "TZ", def->clock.data.timezone); - } + break; - for (i = 0; i < def->clock.ntimers; i++) { - switch (def->clock.timers[i]->name) { - default: - case VIR_DOMAIN_TIMER_NAME_PLATFORM: - case VIR_DOMAIN_TIMER_NAME_TSC: + 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, - _("unsupported timer type (name) '%s'"), - virDomainTimerNameTypeToString(def->clock.timers[i]->name)); + _("the QEMU binary %s does not support kvm"), + emulator); goto error; + } + break; - case VIR_DOMAIN_TIMER_NAME_KVMCLOCK: - /* This is handled when building -cpu. */ - break; + case VIR_DOMAIN_VIRT_XEN: + /* XXX better check for xenner */ + break; - case VIR_DOMAIN_TIMER_NAME_RTC: - /* This has already been taken care of (in qemuBuildClockArgStr) - if QEMU_CAPS_RTC is set (mutually exclusive with - QEMUD_FLAG_RTC_TD_HACK) */ - if (qemuCapsGet(caps, QEMU_CAPS_RTC_TD_HACK)) { - switch (def->clock.timers[i]->tickpolicy) { - case -1: - case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: - /* the default - do nothing */ - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: - virCommandAddArg(cmd, "-rtc-td-hack"); - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: - case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported rtc tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); - goto error; - } - } else if (!qemuCapsGet(caps, QEMU_CAPS_RTC) - && (def->clock.timers[i]->tickpolicy - != VIR_DOMAIN_TIMER_TICKPOLICY_DELAY) - && (def->clock.timers[i]->tickpolicy != -1)) { - /* a non-default rtc policy was given, but there is no - way to implement it in this version of qemu */ - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported rtc tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); - goto error; - } - break; + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary %s does not support %s"), + emulator, virDomainVirtTypeToString(def->virtType)); + break; + } - case VIR_DOMAIN_TIMER_NAME_PIT: - switch (def->clock.timers[i]->tickpolicy) { - case -1: - case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: - /* delay is the default if we don't have kernel - (-no-kvm-pit), otherwise, the default is catchup. */ - if (qemuCapsGet(caps, QEMU_CAPS_NO_KVM_PIT)) - virCommandAddArg(cmd, "-no-kvm-pit-reinjection"); - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: - if (qemuCapsGet(caps, QEMU_CAPS_NO_KVM_PIT)) { - /* do nothing - this is default for kvm-pit */ - } else if (qemuCapsGet(caps, QEMU_CAPS_TDF)) { - /* -tdf switches to 'catchup' with userspace pit. */ - virCommandAddArg(cmd, "-tdf"); - } else { - /* can't catchup if we have neither pit mode */ - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported pit tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); - goto error; - } - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: - case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: - /* no way to support these modes for pit in qemu */ - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported pit tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); - goto error; - } - break; + cmd = virCommandNew(emulator); - case VIR_DOMAIN_TIMER_NAME_HPET: - /* the only meaningful attribute for hpet is "present". If - * present is -1, that means it wasn't specified, and - * should be left at the default for the - * hypervisor. "default" when -no-hpet exists is "yes", - * and when -no-hpet doesn't exist is "no". "confusing"? - * "yes"! */ + virCommandAddEnvPassCommon(cmd); - if (qemuCapsGet(caps, QEMU_CAPS_NO_HPET)) { - if (def->clock.timers[i]->present == 0) - virCommandAddArg(cmd, "-no-hpet"); - } else { - /* no hpet timer available. The only possible action - is to raise an error if present="yes" */ - if (def->clock.timers[i]->present == 1) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("pit timer is not supported")); - } - } - break; + 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 (qemuCapsGet(caps, QEMU_CAPS_NO_REBOOT) && - def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART) - virCommandAddArg(cmd, "-no-reboot"); + if (qemuBuildMachineArgStr(cmd, def, caps) < 0) + goto error; - /* If JSON monitor is enabled, we can receive an event - * when QEMU stops. If we use no-shutdown, then we can - * watch for this event and do a soft/warm reboot. - */ - if (monitor_json && qemuCapsGet(caps, QEMU_CAPS_NO_SHUTDOWN)) - virCommandAddArg(cmd, "-no-shutdown"); + if (qemuBuildCpuArgStr(driver, def, emulator, caps, + &ut, &cpu, &hasHwVirt, !!migrateFrom) < 0) + goto error; - if (qemuCapsGet(caps, QEMU_CAPS_NO_ACPI)) { - if (!(def->features & (1 << VIR_DOMAIN_FEATURE_ACPI))) - virCommandAddArg(cmd, "-no-acpi"); + if (cpu) { + virCommandAddArgList(cmd, "-cpu", cpu, NULL); + VIR_FREE(cpu); + + if (qemuCapsGet(caps, QEMU_CAPS_NESTING) && + hasHwVirt) + virCommandAddArg(cmd, "-enable-nesting"); } - if (def->pm.s3) { - if (!qemuCapsGet(caps, QEMU_CAPS_DISABLE_S3)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("setting ACPI S3 not supported")); + 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) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("hugetlbfs filesystem is not mounted")); + goto error; + } + if (!driver->hugepage_path) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("hugepages are disabled by administrator config")); + goto error; + } + if (!qemuCapsGet(caps, QEMU_CAPS_MEM_PATH)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("hugepage backing not supported by '%s'"), + def->emulator); + goto error; + } + virCommandAddArgList(cmd, "-mem-prealloc", "-mem-path", + driver->hugepage_path, NULL); + } + + virCommandAddArg(cmd, "-smp"); + if (!(smp = qemuBuildSmpArgStr(def, caps))) + goto error; + virCommandAddArg(cmd, smp); + VIR_FREE(smp); + + if (def->cpu && def->cpu->ncells) + if (qemuBuildNumaArgStr(def, cmd) < 0) + goto error; + + if (qemuCapsGet(caps, QEMU_CAPS_UUID)) + virCommandAddArgList(cmd, "-uuid", uuid, NULL); + if (def->virtType == VIR_DOMAIN_VIRT_XEN || + STREQ(def->os.type, "xen") || + STREQ(def->os.type, "linux")) { + if (qemuCapsGet(caps, QEMU_CAPS_DOMID)) { + virCommandAddArg(cmd, "-domid"); + virCommandAddArgFormat(cmd, "%d", def->id); + } else if (qemuCapsGet(caps, QEMU_CAPS_XEN_DOMID)) { + virCommandAddArg(cmd, "-xen-attach"); + virCommandAddArg(cmd, "-xen-domid"); + virCommandAddArgFormat(cmd, "%d", def->id); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("qemu emulator '%s' does not support xen"), + def->emulator); goto error; } - virCommandAddArg(cmd, "-global"); - virCommandAddArgFormat(cmd, "PIIX4_PM.disable_s3=%d", - def->pm.s3 == VIR_DOMAIN_PM_STATE_DISABLED); } - if (def->pm.s4) { - if (!qemuCapsGet(caps, QEMU_CAPS_DISABLE_S4)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("setting ACPI S4 not supported")); + if ((def->os.smbios_mode != VIR_DOMAIN_SMBIOS_NONE) && + (def->os.smbios_mode != VIR_DOMAIN_SMBIOS_EMULATE)) { + virSysinfoDefPtr source = NULL; + bool skip_uuid = false; + + if (!qemuCapsGet(caps, QEMU_CAPS_SMBIOS_TYPE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary %s does not support smbios settings"), + emulator); goto error; } - virCommandAddArg(cmd, "-global"); - virCommandAddArgFormat(cmd, "PIIX4_PM.disable_s4=%d", - def->pm.s4 == VIR_DOMAIN_PM_STATE_DISABLED); - } - if (!def->os.bootloader) { - int boot_nparams = 0; - virBuffer boot_buf = VIR_BUFFER_INITIALIZER; - /* - * We prefer using explicit bootindex=N parameters for predictable - * results even though domain XML doesn't use per device boot elements. - * However, we can't use bootindex if boot menu was requested. - */ - if (!def->os.nBootDevs) { - /* def->os.nBootDevs is guaranteed to be > 0 unless per-device boot - * configuration is used - */ - if (!qemuCapsGet(caps, QEMU_CAPS_BOOTINDEX)) { + /* should we really error out or just warn in those cases ? */ + if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_HOST) { + if (driver->hostsysinfo == NULL) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("hypervisor lacks deviceboot feature")); + _("Host SMBIOS information is not available")); goto error; } - emitBootindex = true; - } else if (qemuCapsGet(caps, QEMU_CAPS_BOOTINDEX) && - (def->os.bootmenu != VIR_DOMAIN_BOOT_MENU_ENABLED || - !qemuCapsGet(caps, QEMU_CAPS_BOOT_MENU))) { - emitBootindex = true; - } - - if (!emitBootindex) { - char boot[VIR_DOMAIN_BOOT_LAST+1]; - - for (i = 0 ; i < def->os.nBootDevs ; i++) { - switch (def->os.bootDevs[i]) { - case VIR_DOMAIN_BOOT_CDROM: - boot[i] = 'd'; - break; - case VIR_DOMAIN_BOOT_FLOPPY: - boot[i] = 'a'; - break; - case VIR_DOMAIN_BOOT_DISK: - boot[i] = 'c'; - break; - case VIR_DOMAIN_BOOT_NET: - boot[i] = 'n'; - break; - default: - boot[i] = 'c'; - break; - } + source = driver->hostsysinfo; + /* Host and guest uuid must differ, by definition of UUID. */ + skip_uuid = true; + } else if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_SYSINFO) { + if (def->sysinfo == NULL) { + virReportError(VIR_ERR_XML_ERROR, + _("Domain '%s' sysinfo are not available"), + def->name); + goto error; } - boot[def->os.nBootDevs] = '\0'; - - virBufferAsprintf(&boot_buf, "%s", boot); - boot_nparams++; + source = def->sysinfo; + /* domain_conf guaranteed that system_uuid matches guest uuid. */ } + if (source != NULL) { + char *smbioscmd; - 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"); + smbioscmd = qemuBuildSmbiosBiosStr(source); + if (smbioscmd != NULL) { + virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); + VIR_FREE(smbioscmd); } - } - - 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; + smbioscmd = qemuBuildSmbiosSystemStr(source, skip_uuid); + if (smbioscmd != NULL) { + virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); + VIR_FREE(smbioscmd); } - - 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)); - } - } + /* + * NB, -nographic *MUST* come before any serial, or monitor + * or parallel port flags due to QEMU craziness, where it + * decides to change the serial port & monitor to be on stdout + * if you ask for nographic. So we have to make sure we override + * these defaults ourselves... + */ + if (!def->graphics) + virCommandAddArg(cmd, "-nographic"); - 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); + if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + /* Disable global config files and default devices */ + if (qemuCapsGet(caps, QEMU_CAPS_NO_USER_CONFIG)) + virCommandAddArg(cmd, "-no-user-config"); + else if (qemuCapsGet(caps, QEMU_CAPS_NODEFCONFIG)) + virCommandAddArg(cmd, "-nodefconfig"); + virCommandAddArg(cmd, "-nodefaults"); } - 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); + /* Serial graphics adapter */ + if (def->os.bios.useserial == VIR_DOMAIN_BIOS_USESERIAL_YES) { + if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu does not support -device")); + goto error; + } + if (!qemuCapsGet(caps, QEMU_CAPS_SGA)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu does not support SGA")); + goto error; + } + if (!def->nserials) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("need at least one serial port to use SGA")); goto error; } + virCommandAddArgList(cmd, "-device", "sga", NULL); } - 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; + if (monitor_chr) { + char *chrdev; + /* Use -chardev if it's available */ + if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV)) { - 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"); + virCommandAddArg(cmd, "-chardev"); + if (!(chrdev = qemuBuildChrChardevStr(monitor_chr, "monitor", + caps))) + goto error; + virCommandAddArg(cmd, chrdev); + VIR_FREE(chrdev); - char *devstr; - if (!(devstr = qemuBuildControllerDevStr(def, cont, caps, - &usbcontroller))) - goto error; + virCommandAddArg(cmd, "-mon"); + virCommandAddArgFormat(cmd, + "chardev=charmonitor,id=monitor,mode=%s", + monitor_json ? "control" : "readline"); + } else { + const char *prefix = NULL; + if (monitor_json) + prefix = "control,"; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } - } + virCommandAddArg(cmd, "-monitor"); + if (!(chrdev = qemuBuildChrArgStr(monitor_chr, prefix))) + goto error; + virCommandAddArg(cmd, chrdev); + VIR_FREE(chrdev); } } - if (usbcontroller == 0) - virCommandAddArg(cmd, "-usb"); + if (qemuCapsGet(caps, QEMU_CAPS_RTC)) { + const char *rtcopt; + virCommandAddArg(cmd, "-rtc"); + if (!(rtcopt = qemuBuildClockArgStr(&def->clock))) + goto error; + virCommandAddArg(cmd, rtcopt); + VIR_FREE(rtcopt); + } else { + switch (def->clock.offset) { + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: + virCommandAddArg(cmd, "-localtime"); + break; - for (i = 0 ; i < def->nhubs ; i++) { - virDomainHubDefPtr hub = def->hubs[i]; - char *optstr; + case VIR_DOMAIN_CLOCK_OFFSET_UTC: + /* Nothing, its the default */ + break; - virCommandAddArg(cmd, "-device"); - if (!(optstr = qemuBuildHubDevStr(hub, caps))) + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock offset '%s'"), + virDomainClockOffsetTypeToString(def->clock.offset)); goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); + } + } + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE && + def->clock.data.timezone) { + virCommandAddEnvPair(cmd, "TZ", def->clock.data.timezone); } - /* If QEMU supports -drive param instead of old -hda, -hdb, -cdrom .. */ - if (qemuCapsGet(caps, QEMU_CAPS_DRIVE)) { - int bootCD = 0, bootFloppy = 0, bootDisk = 0; + for (i = 0; i < def->clock.ntimers; i++) { + switch (def->clock.timers[i]->name) { + default: + case VIR_DOMAIN_TIMER_NAME_PLATFORM: + case VIR_DOMAIN_TIMER_NAME_TSC: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported timer type (name) '%s'"), + virDomainTimerNameTypeToString(def->clock.timers[i]->name)); + goto error; - 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; + case VIR_DOMAIN_TIMER_NAME_KVMCLOCK: + /* This is handled when building -cpu. */ + break; + + case VIR_DOMAIN_TIMER_NAME_RTC: + /* This has already been taken care of (in qemuBuildClockArgStr) + if QEMU_CAPS_RTC is set (mutually exclusive with + QEMUD_FLAG_RTC_TD_HACK) */ + if (qemuCapsGet(caps, QEMU_CAPS_RTC_TD_HACK)) { + switch (def->clock.timers[i]->tickpolicy) { + case -1: + case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: + /* the default - do nothing */ break; - case VIR_DOMAIN_BOOT_DISK: - bootDisk = i + 1; + case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: + virCommandAddArg(cmd, "-rtc-td-hack"); break; + case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: + case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; } + } else if (!qemuCapsGet(caps, QEMU_CAPS_RTC) + && (def->clock.timers[i]->tickpolicy + != VIR_DOMAIN_TIMER_TICKPOLICY_DELAY) + && (def->clock.timers[i]->tickpolicy != -1)) { + /* a non-default rtc policy was given, but there is no + way to implement it in this version of qemu */ + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; } - } - - for (i = 0 ; i < def->ndisks ; i++) { - char *optstr; - int bootindex = 0; - virDomainDiskDefPtr disk = def->disks[i]; - int withDeviceArg = 0; - bool deviceFlagMasked = false; + break; - /* 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); + case VIR_DOMAIN_TIMER_NAME_PIT: + switch (def->clock.timers[i]->tickpolicy) { + case -1: + case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: + /* delay is the default if we don't have kernel + (-no-kvm-pit), otherwise, the default is catchup. */ + if (qemuCapsGet(caps, QEMU_CAPS_NO_KVM_PIT)) + virCommandAddArg(cmd, "-no-kvm-pit-reinjection"); + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: + if (qemuCapsGet(caps, QEMU_CAPS_NO_KVM_PIT)) { + /* do nothing - this is default for kvm-pit */ + } else if (qemuCapsGet(caps, QEMU_CAPS_TDF)) { + /* -tdf switches to 'catchup' with userspace pit. */ + virCommandAddArg(cmd, "-tdf"); } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported usb disk type for '%s'"), - disk->src); + /* can't catchup if we have neither pit mode */ + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported pit tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); 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; + case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: + case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: + /* no way to support these modes for pit in qemu */ + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported pit tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; } + break; - virCommandAddArg(cmd, "-drive"); + case VIR_DOMAIN_TIMER_NAME_HPET: + /* the only meaningful attribute for hpet is "present". If + * present is -1, that means it wasn't specified, and + * should be left at the default for the + * hypervisor. "default" when -no-hpet exists is "yes", + * and when -no-hpet doesn't exist is "no". "confusing"? + * "yes"! */ - /* 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; + if (qemuCapsGet(caps, QEMU_CAPS_NO_HPET)) { + if (def->clock.timers[i]->present == 0) + virCommandAddArg(cmd, "-no-hpet"); + } else { + /* no hpet timer available. The only possible action + is to raise an error if present="yes" */ + if (def->clock.timers[i]->present == 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("pit timer is not supported")); } } - 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); + break; + } + } - if (!emitBootindex) - bootindex = 0; - else if (disk->info.bootIndex) - bootindex = disk->info.bootIndex; + if (qemuCapsGet(caps, QEMU_CAPS_NO_REBOOT) && + def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART) + virCommandAddArg(cmd, "-no-reboot"); - 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 JSON monitor is enabled, we can receive an event + * when QEMU stops. If we use no-shutdown, then we can + * watch for this event and do a soft/warm reboot. + */ + if (monitor_json && qemuCapsGet(caps, QEMU_CAPS_NO_SHUTDOWN)) + virCommandAddArg(cmd, "-no-shutdown"); - 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 (qemuCapsGet(caps, QEMU_CAPS_NO_ACPI)) { + if (!(def->features & (1 << VIR_DOMAIN_FEATURE_ACPI))) + virCommandAddArg(cmd, "-no-acpi"); + } - if (!(optstr = qemuBuildDriveDevStr(def, disk, bootindex, - caps))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - } - } + if (def->pm.s3) { + if (!qemuCapsGet(caps, QEMU_CAPS_DISABLE_S3)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("setting ACPI S3 not supported")); + goto error; } - } else { - for (i = 0 ; i < def->ndisks ; i++) { - char dev[NAME_MAX]; - char *file; - const char *fmt; - virDomainDiskDefPtr disk = def->disks[i]; + virCommandAddArg(cmd, "-global"); + virCommandAddArgFormat(cmd, "PIIX4_PM.disable_s3=%d", + def->pm.s3 == VIR_DOMAIN_PM_STATE_DISABLED); + } + + if (def->pm.s4) { + if (!qemuCapsGet(caps, QEMU_CAPS_DISABLE_S4)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("setting ACPI S4 not supported")); + goto error; + } + virCommandAddArg(cmd, "-global"); + virCommandAddArgFormat(cmd, "PIIX4_PM.disable_s4=%d", + def->pm.s4 == VIR_DOMAIN_PM_STATE_DISABLED); + } - if ((disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK) && - (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) { + if (!def->os.bootloader) { + int boot_nparams = 0; + virBuffer boot_buf = VIR_BUFFER_INITIALIZER; + /* + * We prefer using explicit bootindex=N parameters for predictable + * results even though domain XML doesn't use per device boot elements. + * However, we can't use bootindex if boot menu was requested. + */ + if (!def->os.nBootDevs) { + /* def->os.nBootDevs is guaranteed to be > 0 unless per-device boot + * configuration is used + */ + if (!qemuCapsGet(caps, QEMU_CAPS_BOOTINDEX)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("tray status 'open' is invalid for " - "block type disk")); + _("hypervisor lacks deviceboot feature")); goto error; } + emitBootindex = true; + } else if (qemuCapsGet(caps, QEMU_CAPS_BOOTINDEX) && + (def->os.bootmenu != VIR_DOMAIN_BOOT_MENU_ENABLED || + !qemuCapsGet(caps, QEMU_CAPS_BOOT_MENU))) { + emitBootindex = true; + } - 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 (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; - } - } - - 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 (!emitBootindex) { + char boot[VIR_DOMAIN_BOOT_LAST+1]; - 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; - } + for (i = 0 ; i < def->os.nBootDevs ; i++) { + switch (def->os.bootDevs[i]) { + case VIR_DOMAIN_BOOT_CDROM: + boot[i] = 'd'; 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); - } + case VIR_DOMAIN_BOOT_FLOPPY: + boot[i] = 'a'; 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; - } - } + case VIR_DOMAIN_BOOT_DISK: + boot[i] = 'c'; + break; + case VIR_DOMAIN_BOOT_NET: + boot[i] = 'n'; + break; + default: + boot[i] = 'c'; break; - } - } else { - if (!(file = strdup(disk->src))) { - goto no_memory; } } + boot[def->os.nBootDevs] = '\0'; - /* 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); + virBufferAsprintf(&boot_buf, "%s", boot); + boot_nparams++; } - } - if (qemuCapsGet(caps, QEMU_CAPS_FSDEV)) { - for (i = 0 ; i < def->nfss ; i++) { - char *optstr; - virDomainFSDefPtr fs = def->fss[i]; + if (def->os.bootmenu) { + if (qemuCapsGet(caps, QEMU_CAPS_BOOT_MENU)) { + if (boot_nparams++) + virBufferAddChar(&boot_buf, ','); - virCommandAddArg(cmd, "-fsdev"); - if (!(optstr = qemuBuildFSStr(fs, 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"); + } + } - virCommandAddArg(cmd, "-device"); - if (!(optstr = qemuBuildFSDevStr(fs, caps))) + 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; - 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 (boot_nparams++) + virBufferAddChar(&boot_buf, ','); + + virBufferAsprintf(&boot_buf, + "reboot-timeout=%d", + def->os.bios.rt_delay); } - } - 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; + if (boot_nparams > 0) { + virCommandAddArg(cmd, "-boot"); - if (emitBootindex) { - /* convert 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 (boot_nparams < 2 || emitBootindex) { + virCommandAddArgBuffer(cmd, &boot_buf); + } else { + virCommandAddArgFormat(cmd, + "order=%s", + virBufferContentAndReset(&boot_buf)); } } - 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; + 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); + } - bootNet = 0; - if (!bootindex) - bootindex = net->info.bootIndex; + for (i = 0 ; i < def->ndisks ; i++) { + virDomainDiskDefPtr disk = def->disks[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 (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 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; + if (cont->type != contOrder[j]) + 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 , there is a need to - * add the newly minted hostdev to the hostdevs array. - */ - if (qemuAssignDeviceHostdevAlias(def, hostdev, - (def->nhostdevs-1)) < 0) { + /* 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; - 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; + if (qemuCapsGet(caps, QEMU_CAPS_FSDEV)) { + for (i = 0 ; i < def->nfss ; i++) { + char *optstr; + virDomainFSDefPtr fs = def->fss[i]; - /* 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, "-fsdev"); + if (!(optstr = qemuBuildFSStr(fs, caps))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); - 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, "-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 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 , 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 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; - int port = def->graphics[0]->data.spice.port; - int tlsPort = def->graphics[0]->data.spice.tlsPort; + /* 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); + } } + } - if (port > 0 || tlsPort <= 0) - virBufferAsprintf(&opt, "port=%u", port); + for (i = 0 ; i < def->nchannels ; i++) { + virDomainChrDefPtr channel = def->channels[i]; + char *devstr; + char *addr; + int port; - if (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; } - if (port > 0) - virBufferAddChar(&opt, ','); - virBufferAsprintf(&opt, "tls-port=%u", 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 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) { + if (qemuBuildGraphicsCommandLine(driver, cmd, def, caps, + def->graphics[i]) < 0) + goto error; + } 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 29b7ae1ca2..3d7a5a054f 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2079,16 +2079,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); } } @@ -3479,21 +3480,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) { @@ -3502,13 +3504,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")); @@ -3516,20 +3518,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); @@ -4145,19 +4146,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;