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 <graphics> 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 <graphics> 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 <boot dev='network'/> to bootindex since we didn't emit
- * -boot n
- */
- for (i = 0 ; i < def->os.nBootDevs ; i++) {
- if (def->os.bootDevs[i] == VIR_DOMAIN_BOOT_NET) {
- bootNet = i + 1;
- break;
- }
+ if (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 <forward mode='hostdev'>, there is a need to
- * add the newly minted hostdev to the hostdevs array.
- */
- if (qemuAssignDeviceHostdevAlias(def, hostdev,
- (def->nhostdevs-1)) < 0) {
+ /* 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 <boot dev='network'/> to bootindex since we didn't emit
+ * -boot n
+ */
+ for (i = 0 ; i < def->os.nBootDevs ; i++) {
+ if (def->os.bootDevs[i] == VIR_DOMAIN_BOOT_NET) {
+ bootNet = i + 1;
+ break;
+ }
}
}
- }
-
- for (i = 0 ; i < def->nchannels ; i++) {
- virDomainChrDefPtr channel = def->channels[i];
- char *devstr;
- char *addr;
- int port;
- switch (channel->targetType) {
- case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD:
- if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) ||
- !qemuCapsGet(caps, QEMU_CAPS_DEVICE)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- "%s", _("guestfwd requires QEMU to support -chardev & -device"));
- goto error;
- }
+ for (i = 0 ; i < def->nnets ; i++) {
+ virDomainNetDefPtr net = def->nets[i];
+ char *nic, *host;
+ char tapfd_name[50] = "";
+ char vhostfd_name[50] = "";
+ int vlan;
+ int bootindex = bootNet;
+ int actualType;
- virCommandAddArg(cmd, "-chardev");
- if (!(devstr = qemuBuildChrChardevStr(&channel->source,
- channel->info.alias,
- caps)))
- goto error;
- virCommandAddArg(cmd, devstr);
- VIR_FREE(devstr);
+ bootNet = 0;
+ if (!bootindex)
+ bootindex = net->info.bootIndex;
- addr = virSocketAddrFormat(channel->target.addr);
- if (!addr)
- goto error;
- port = virSocketAddrGetPort(channel->target.addr);
+ /* VLANs are not used with -netdev, so don't record them */
+ if (qemuCapsGet(caps, QEMU_CAPS_NETDEV) &&
+ qemuCapsGet(caps, QEMU_CAPS_DEVICE))
+ vlan = -1;
+ else
+ vlan = i;
- virCommandAddArg(cmd, "-netdev");
- virCommandAddArgFormat(cmd,
- "user,guestfwd=tcp:%s:%i,chardev=char%s,id=user-%s",
- addr, port, channel->info.alias,
- channel->info.alias);
- VIR_FREE(addr);
- break;
+ /* If appropriate, grab a physical device from the configured
+ * network's pool of devices, or resolve bridge device name
+ * to the one defined in the network definition.
+ */
+ if (networkAllocateActualDevice(net) < 0)
+ goto error;
- case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO:
- if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("virtio channel requires QEMU to support -device"));
- goto error;
- }
+ actualType = virDomainNetGetActualType(net);
+ if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+ if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
+ virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net);
+ virDomainHostdevDefPtr found;
+ /* For a network with <forward mode='hostdev'>, there is a need to
+ * add the newly minted hostdev to the hostdevs array.
+ */
+ if (qemuAssignDeviceHostdevAlias(def, hostdev,
+ (def->nhostdevs-1)) < 0) {
+ goto error;
+ }
- if (qemuCapsGet(caps, QEMU_CAPS_DEVICE_SPICEVMC) &&
- channel->source.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) {
- /* spicevmc was originally introduced via a -device
- * with a backend internal to qemu; although we prefer
- * the newer -chardev interface. */
- ;
- } else {
- virCommandAddArg(cmd, "-chardev");
- if (!(devstr = qemuBuildChrChardevStr(&channel->source,
- channel->info.alias,
- caps)))
- goto error;
- virCommandAddArg(cmd, devstr);
- VIR_FREE(devstr);
+ if (virDomainHostdevFind(def, hostdev, &found) < 0) {
+ if (virDomainHostdevInsert(def, hostdev) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+ if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid,
+ &hostdev, 1) < 0) {
+ goto error;
+ }
+ }
+ else {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("PCI device %04x:%02x:%02x.%x "
+ "allocated from network %s is already "
+ "in use by domain %s"),
+ hostdev->source.subsys.u.pci.domain,
+ hostdev->source.subsys.u.pci.bus,
+ hostdev->source.subsys.u.pci.slot,
+ hostdev->source.subsys.u.pci.function,
+ net->data.network.name,
+ def->name);
+ goto error;
+ }
+ }
+ continue;
}
- virCommandAddArg(cmd, "-device");
- if (!(devstr = qemuBuildVirtioSerialPortDevStr(channel,
- caps)))
- goto error;
- virCommandAddArg(cmd, devstr);
- VIR_FREE(devstr);
- break;
- }
- }
-
- /* Explicit console devices */
- for (i = 0 ; i < def->nconsoles ; i++) {
- virDomainChrDefPtr console = def->consoles[i];
- char *devstr;
+ if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
+ actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
+ /*
+ * If type='bridge' then we attempt to allocate the tap fd here only if
+ * running under a privilged user or -netdev bridge option is not
+ * supported.
+ */
+ if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
+ driver->privileged ||
+ (!qemuCapsGet(caps, QEMU_CAPS_NETDEV_BRIDGE))) {
+ int tapfd = qemuNetworkIfaceConnect(def, conn, driver, net,
+ caps);
+ if (tapfd < 0)
+ goto error;
- switch (console->targetType) {
- case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO:
- if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("virtio channel requires QEMU to support -device"));
- goto error;
- }
+ last_good_net = i;
+ virCommandTransferFD(cmd, tapfd);
- virCommandAddArg(cmd, "-chardev");
- if (!(devstr = qemuBuildChrChardevStr(&console->source,
- console->info.alias,
- caps)))
- goto error;
- virCommandAddArg(cmd, devstr);
- VIR_FREE(devstr);
+ if (snprintf(tapfd_name, sizeof(tapfd_name), "%d",
+ tapfd) >= sizeof(tapfd_name))
+ goto no_memory;
+ }
+ } else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) {
+ int tapfd = qemuPhysIfaceConnect(def, driver, net,
+ caps, vmop);
+ if (tapfd < 0)
+ goto error;
- virCommandAddArg(cmd, "-device");
- if (!(devstr = qemuBuildVirtioSerialPortDevStr(console,
- caps)))
- goto error;
- virCommandAddArg(cmd, devstr);
- VIR_FREE(devstr);
- break;
+ last_good_net = i;
+ virCommandTransferFD(cmd, tapfd);
- case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL:
- break;
+ if (snprintf(tapfd_name, sizeof(tapfd_name), "%d",
+ tapfd) >= sizeof(tapfd_name))
+ goto no_memory;
+ }
- default:
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("unsupported console target type %s"),
- NULLSTR(virDomainChrConsoleTargetTypeToString(console->targetType)));
- goto error;
- }
- }
+ if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
+ actualType == VIR_DOMAIN_NET_TYPE_BRIDGE ||
+ actualType == VIR_DOMAIN_NET_TYPE_DIRECT) {
+ /* Attempt to use vhost-net mode for these types of
+ network device */
+ int vhostfd;
- for (i = 0 ; i < def->ninputs ; i++) {
- virDomainInputDefPtr input = def->inputs[i];
+ if (qemuOpenVhostNet(def, net, caps, &vhostfd) < 0)
+ goto error;
+ if (vhostfd >= 0) {
+ virCommandTransferFD(cmd, vhostfd);
- if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) {
+ if (snprintf(vhostfd_name, sizeof(vhostfd_name), "%d",
+ vhostfd) >= sizeof(vhostfd_name))
+ goto no_memory;
+ }
+ }
+ /* Possible combinations:
+ *
+ * 1. Old way: -net nic,model=e1000,vlan=1 -net tap,vlan=1
+ * 2. Semi-new: -device e1000,vlan=1 -net tap,vlan=1
+ * 3. Best way: -netdev type=tap,id=netdev1 -device e1000,id=netdev1
+ *
+ * NB, no support for -netdev without use of -device
+ */
+ if (qemuCapsGet(caps, QEMU_CAPS_NETDEV) &&
+ qemuCapsGet(caps, QEMU_CAPS_DEVICE)) {
+ virCommandAddArg(cmd, "-netdev");
+ if (!(host = qemuBuildHostNetStr(net, driver, caps,
+ ',', vlan, tapfd_name,
+ vhostfd_name)))
+ goto error;
+ virCommandAddArg(cmd, host);
+ VIR_FREE(host);
+ }
if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) {
- char *optstr;
virCommandAddArg(cmd, "-device");
- if (!(optstr = qemuBuildUSBInputDevStr(input, caps)))
+ nic = qemuBuildNicDevStr(net, vlan, bootindex, caps);
+ if (!nic)
goto error;
- virCommandAddArg(cmd, optstr);
- VIR_FREE(optstr);
+ virCommandAddArg(cmd, nic);
+ VIR_FREE(nic);
} else {
- virCommandAddArgList(cmd, "-usbdevice",
- input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE
- ? "mouse" : "tablet", NULL);
+ virCommandAddArg(cmd, "-net");
+ if (!(nic = qemuBuildNicStr(net, "nic,", vlan)))
+ goto error;
+ virCommandAddArg(cmd, nic);
+ VIR_FREE(nic);
+ }
+ if (!(qemuCapsGet(caps, QEMU_CAPS_NETDEV) &&
+ qemuCapsGet(caps, QEMU_CAPS_DEVICE))) {
+ virCommandAddArg(cmd, "-net");
+ if (!(host = qemuBuildHostNetStr(net, driver, caps,
+ ',', vlan, tapfd_name,
+ vhostfd_name)))
+ goto error;
+ virCommandAddArg(cmd, host);
+ VIR_FREE(host);
}
}
}
- if (def->ngraphics > 1) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("only 1 graphics device is supported"));
- goto error;
- }
-
- if ((def->ngraphics == 1) &&
- def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
+ if (def->nsmartcards) {
+ /* -device usb-ccid was already emitted along with other
+ * controllers. For now, qemu handles only one smartcard. */
+ virDomainSmartcardDefPtr smartcard = def->smartcards[0];
+ char *devstr;
virBuffer opt = VIR_BUFFER_INITIALIZER;
+ const char *database;
- if (!qemuCapsGet(caps, QEMU_CAPS_VNC)) {
+ if (def->nsmartcards > 1 ||
+ smartcard->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID ||
+ smartcard->info.addr.ccid.controller != 0 ||
+ smartcard->info.addr.ccid.slot != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("vnc graphics are not supported with this QEMU"));
+ _("this QEMU binary lacks multiple smartcard "
+ "support"));
+ virBufferFreeAndReset(&opt);
goto error;
}
- if (def->graphics[0]->data.vnc.socket ||
- driver->vncAutoUnixSocket) {
-
- if (!def->graphics[0]->data.vnc.socket &&
- virAsprintf(&def->graphics[0]->data.vnc.socket,
- "%s/%s.vnc", driver->libDir, def->name) == -1) {
- goto no_memory;
+ switch (smartcard->type) {
+ case VIR_DOMAIN_SMARTCARD_TYPE_HOST:
+ if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) ||
+ !qemuCapsGet(caps, QEMU_CAPS_CCID_EMULATED)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("this QEMU binary lacks smartcard host "
+ "mode support"));
+ goto error;
}
- virBufferAsprintf(&opt, "unix:%s",
- def->graphics[0]->data.vnc.socket);
-
- } else if (qemuCapsGet(caps, QEMU_CAPS_VNC_COLON)) {
- const char *listenNetwork;
- const char *listenAddr = NULL;
- char *netAddr = NULL;
- bool escapeAddr;
- int ret;
+ virBufferAddLit(&opt, "ccid-card-emulated,backend=nss-emulated");
+ break;
- switch (virDomainGraphicsListenGetType(def->graphics[0], 0)) {
- case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
- listenAddr = virDomainGraphicsListenGetAddress(def->graphics[0], 0);
- break;
+ case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES:
+ if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) ||
+ !qemuCapsGet(caps, QEMU_CAPS_CCID_EMULATED)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("this QEMU binary lacks smartcard host "
+ "mode support"));
+ goto error;
+ }
- case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
- listenNetwork = virDomainGraphicsListenGetNetwork(def->graphics[0], 0);
- if (!listenNetwork)
- break;
- ret = networkGetNetworkAddress(listenNetwork, &netAddr);
- if (ret <= -2) {
+ virBufferAddLit(&opt, "ccid-card-emulated,backend=certificates");
+ for (j = 0; j < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; j++) {
+ if (strchr(smartcard->data.cert.file[j], ',')) {
+ virBufferFreeAndReset(&opt);
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- "%s", _("network-based listen not possible, "
- "network driver not present"));
+ _("invalid certificate name: %s"),
+ smartcard->data.cert.file[j]);
goto error;
}
- if (ret < 0) {
- virReportError(VIR_ERR_XML_ERROR,
- _("listen network '%s' had no usable address"),
- listenNetwork);
+ virBufferAsprintf(&opt, ",cert%d=%s", j + 1,
+ smartcard->data.cert.file[j]);
+ }
+ if (smartcard->data.cert.database) {
+ if (strchr(smartcard->data.cert.database, ',')) {
+ virBufferFreeAndReset(&opt);
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("invalid database name: %s"),
+ smartcard->data.cert.database);
goto error;
}
- listenAddr = netAddr;
- /* store the address we found in the <graphics> element so it will
- * show up in status. */
- if (virDomainGraphicsListenSetAddress(def->graphics[0], 0,
- listenAddr, -1, false) < 0)
- goto error;
- break;
+ database = smartcard->data.cert.database;
+ } else {
+ database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE;
}
+ virBufferAsprintf(&opt, ",database=%s", database);
+ break;
- if (!listenAddr)
- listenAddr = driver->vncListen;
-
- escapeAddr = strchr(listenAddr, ':') != NULL;
- if (escapeAddr)
- virBufferAsprintf(&opt, "[%s]", listenAddr);
- else
- virBufferAdd(&opt, listenAddr, -1);
- virBufferAsprintf(&opt, ":%d",
- def->graphics[0]->data.vnc.port - 5900);
-
- VIR_FREE(netAddr);
- } else {
- virBufferAsprintf(&opt, "%d",
- def->graphics[0]->data.vnc.port - 5900);
- }
-
- if (qemuCapsGet(caps, QEMU_CAPS_VNC_COLON)) {
- if (def->graphics[0]->data.vnc.auth.passwd ||
- driver->vncPassword)
- virBufferAddLit(&opt, ",password");
-
- if (driver->vncTLS) {
- virBufferAddLit(&opt, ",tls");
- if (driver->vncTLSx509verify) {
- virBufferAsprintf(&opt, ",x509verify=%s",
- driver->vncTLSx509certdir);
- } else {
- virBufferAsprintf(&opt, ",x509=%s",
- driver->vncTLSx509certdir);
- }
+ case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
+ if (!qemuCapsGet(caps, QEMU_CAPS_CHARDEV) ||
+ !qemuCapsGet(caps, QEMU_CAPS_CCID_PASSTHRU)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("this QEMU binary lacks smartcard "
+ "passthrough mode support"));
+ goto error;
}
- if (driver->vncSASL) {
- virBufferAddLit(&opt, ",sasl");
-
- if (driver->vncSASLdir)
- virCommandAddEnvPair(cmd, "SASL_CONF_DIR",
- driver->vncSASLdir);
-
- /* TODO: Support ACLs later */
+ virCommandAddArg(cmd, "-chardev");
+ if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru,
+ smartcard->info.alias,
+ caps))) {
+ virBufferFreeAndReset(&opt);
+ goto error;
}
- }
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
- virCommandAddArg(cmd, "-vnc");
- virCommandAddArgBuffer(cmd, &opt);
- if (def->graphics[0]->data.vnc.keymap) {
- virCommandAddArgList(cmd, "-k", def->graphics[0]->data.vnc.keymap,
- NULL);
- }
+ virBufferAsprintf(&opt, "ccid-card-passthru,chardev=char%s",
+ smartcard->info.alias);
+ break;
- /* Unless user requested it, set the audio backend to none, to
- * prevent it opening the host OS audio devices, since that causes
- * security issues and might not work when using VNC.
- */
- if (driver->vncAllowHostAudio) {
- virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV");
- } else {
- virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none");
- }
- } else if ((def->ngraphics == 1) &&
- def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) {
- if (qemuCapsGet(caps, QEMU_CAPS_0_10) &&
- !qemuCapsGet(caps, QEMU_CAPS_SDL)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("sdl not supported by '%s'"),
- def->emulator);
+ default:
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unexpected smartcard type %d"),
+ smartcard->type);
+ virBufferFreeAndReset(&opt);
goto error;
}
+ virCommandAddArg(cmd, "-device");
+ virBufferAsprintf(&opt, ",id=%s,bus=ccid0.0", smartcard->info.alias);
+ virCommandAddArgBuffer(cmd, &opt);
+ }
- if (def->graphics[0]->data.sdl.xauth)
- virCommandAddEnvPair(cmd, "XAUTHORITY",
- def->graphics[0]->data.sdl.xauth);
- if (def->graphics[0]->data.sdl.display)
- virCommandAddEnvPair(cmd, "DISPLAY",
- def->graphics[0]->data.sdl.display);
- if (def->graphics[0]->data.sdl.fullscreen)
- virCommandAddArg(cmd, "-full-screen");
+ if (!def->nserials) {
+ /* If we have -device, then we set -nodefault already */
+ if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE))
+ virCommandAddArgList(cmd, "-serial", "none", NULL);
+ } else {
+ for (i = 0 ; i < def->nserials ; i++) {
+ virDomainChrDefPtr serial = def->serials[i];
+ char *devstr;
- /* If using SDL for video, then we should just let it
- * use QEMU's host audio drivers, possibly SDL too
- * User can set these two before starting libvirtd
- */
- virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV");
- virCommandAddEnvPass(cmd, "SDL_AUDIODRIVER");
+ /* Use -chardev with -device if they are available */
+ if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV) &&
+ qemuCapsGet(caps, QEMU_CAPS_DEVICE)) {
+ virCommandAddArg(cmd, "-chardev");
+ if (!(devstr = qemuBuildChrChardevStr(&serial->source,
+ serial->info.alias,
+ caps)))
+ goto error;
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
+
+ virCommandAddArg(cmd, "-device");
+ if (!(devstr = qemuBuildChrDeviceStr(serial, caps,
+ def->os.arch,
+ def->os.machine)))
+ goto error;
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
+ } else {
+ virCommandAddArg(cmd, "-serial");
+ if (!(devstr = qemuBuildChrArgStr(&serial->source, NULL)))
+ goto error;
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
+ }
+ }
+ }
- /* New QEMU has this flag to let us explicitly ask for
- * SDL graphics. This is better than relying on the
- * default, since the default changes :-( */
- if (qemuCapsGet(caps, QEMU_CAPS_SDL))
- virCommandAddArg(cmd, "-sdl");
+ if (!def->nparallels) {
+ /* If we have -device, then we set -nodefault already */
+ if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE))
+ virCommandAddArgList(cmd, "-parallel", "none", NULL);
+ } else {
+ for (i = 0 ; i < def->nparallels ; i++) {
+ virDomainChrDefPtr parallel = def->parallels[i];
+ char *devstr;
- } else if ((def->ngraphics == 1) &&
- def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
- virBuffer opt = VIR_BUFFER_INITIALIZER;
- const char *listenNetwork;
- const char *listenAddr = NULL;
- char *netAddr = NULL;
- int ret;
- int defaultMode = def->graphics[0]->data.spice.defaultMode;
- 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 <graphics> element so it will
- * show up in status. */
- if (virDomainGraphicsListenSetAddress(def->graphics[0], 0,
- listenAddr, -1, false) < 0)
- goto error;
+
+ virCommandAddArg(cmd, "-device");
+ if (!(devstr = qemuBuildVirtioSerialPortDevStr(channel,
+ caps)))
+ goto error;
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
break;
}
+ }
- if (!listenAddr)
- listenAddr = driver->spiceListen;
- if (listenAddr)
- virBufferAsprintf(&opt, ",addr=%s", listenAddr);
-
- VIR_FREE(netAddr);
+ /* Explicit console devices */
+ for (i = 0 ; i < def->nconsoles ; i++) {
+ virDomainChrDefPtr console = def->consoles[i];
+ char *devstr;
- int mm = def->graphics[0]->data.spice.mousemode;
- if (mm) {
- switch (mm) {
- case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_SERVER:
- virBufferAsprintf(&opt, ",agent-mouse=off");
- break;
- case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_CLIENT:
- virBufferAsprintf(&opt, ",agent-mouse=on");
- break;
- default:
- break;
+ switch (console->targetType) {
+ case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO:
+ if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("virtio channel requires QEMU to support -device"));
+ goto error;
}
- }
- /* In the password case we set it via monitor command, to avoid
- * making it visible on CLI, so there's no use of password=XXX
- * in this bit of the code */
- if (!def->graphics[0]->data.spice.auth.passwd &&
- !driver->spicePassword)
- virBufferAddLit(&opt, ",disable-ticketing");
-
- if (driver->spiceTLS)
- virBufferAsprintf(&opt, ",x509-dir=%s",
- driver->spiceTLSx509certdir);
+ virCommandAddArg(cmd, "-chardev");
+ if (!(devstr = qemuBuildChrChardevStr(&console->source,
+ console->info.alias,
+ caps)))
+ goto error;
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
- switch (defaultMode) {
- case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE:
- virBufferAsprintf(&opt, ",tls-channel=default");
- break;
- case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE:
- virBufferAsprintf(&opt, ",plaintext-channel=default");
+ virCommandAddArg(cmd, "-device");
+ if (!(devstr = qemuBuildVirtioSerialPortDevStr(console,
+ caps)))
+ goto error;
+ virCommandAddArg(cmd, devstr);
+ VIR_FREE(devstr);
break;
- case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY:
- /* nothing */
+
+ case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL:
break;
+
+ default:
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("unsupported console target type %s"),
+ NULLSTR(virDomainChrConsoleTargetTypeToString(console->targetType)));
+ goto error;
}
+ }
- for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) {
- int mode = def->graphics[0]->data.spice.channels[i];
- switch (mode) {
- case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE:
- if (!driver->spiceTLS) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("spice secure channels set in XML configuration, but TLS is disabled in qemu.conf"));
+ for (i = 0 ; i < def->ninputs ; i++) {
+ virDomainInputDefPtr input = def->inputs[i];
+
+ if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) {
+ if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) {
+ char *optstr;
+ virCommandAddArg(cmd, "-device");
+ if (!(optstr = qemuBuildUSBInputDevStr(input, caps)))
goto error;
- }
- virBufferAsprintf(&opt, ",tls-channel=%s",
- virDomainGraphicsSpiceChannelNameTypeToString(i));
- break;
- case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE:
- virBufferAsprintf(&opt, ",plaintext-channel=%s",
- virDomainGraphicsSpiceChannelNameTypeToString(i));
- break;
+ virCommandAddArg(cmd, optstr);
+ VIR_FREE(optstr);
+ } else {
+ virCommandAddArgList(cmd, "-usbdevice",
+ input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE
+ ? "mouse" : "tablet", NULL);
}
}
- if (def->graphics[0]->data.spice.image)
- virBufferAsprintf(&opt, ",image-compression=%s",
- virDomainGraphicsSpiceImageCompressionTypeToString(def->graphics[0]->data.spice.image));
- if (def->graphics[0]->data.spice.jpeg)
- virBufferAsprintf(&opt, ",jpeg-wan-compression=%s",
- virDomainGraphicsSpiceJpegCompressionTypeToString(def->graphics[0]->data.spice.jpeg));
- if (def->graphics[0]->data.spice.zlib)
- virBufferAsprintf(&opt, ",zlib-glz-wan-compression=%s",
- virDomainGraphicsSpiceZlibCompressionTypeToString(def->graphics[0]->data.spice.zlib));
- if (def->graphics[0]->data.spice.playback)
- virBufferAsprintf(&opt, ",playback-compression=%s",
- virDomainGraphicsSpicePlaybackCompressionTypeToString(def->graphics[0]->data.spice.playback));
- if (def->graphics[0]->data.spice.streaming)
- virBufferAsprintf(&opt, ",streaming-video=%s",
- virDomainGraphicsSpiceStreamingModeTypeToString(def->graphics[0]->data.spice.streaming));
- if (def->graphics[0]->data.spice.copypaste == VIR_DOMAIN_GRAPHICS_SPICE_CLIPBOARD_COPYPASTE_NO)
- virBufferAddLit(&opt, ",disable-copy-paste");
-
- if (qemuCapsGet(caps, QEMU_CAPS_SEAMLESS_MIGRATION)) {
- /* If qemu supports seamless migration turn it
- * unconditionally on. If migration destination
- * doesn't support it, it fallbacks to previous
- * migration algorithm silently. */
- virBufferAddLit(&opt, ",seamless-migration=on");
- }
-
- virCommandAddArg(cmd, "-spice");
- virCommandAddArgBuffer(cmd, &opt);
- if (def->graphics[0]->data.spice.keymap)
- virCommandAddArgList(cmd, "-k",
- def->graphics[0]->data.spice.keymap, NULL);
- /* SPICE includes native support for tunnelling audio, so we
- * set the audio backend to point at SPICE's own driver
- */
- virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=spice");
+ }
- } else if ((def->ngraphics == 1)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("unsupported graphics type '%s'"),
- virDomainGraphicsTypeToString(def->graphics[0]->type));
+ if (def->ngraphics > 1) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("only 1 graphics device is supported"));
goto error;
}
+ for (i = 0 ; i < def->ngraphics ; ++i) {
+ 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) {