
On 11/14/2017 08:45 AM, Pavel Hrdina wrote:
So far we were configuring the sound output based on what graphic device was configured in domain XML. This had a several disadvantages, it's not transparent, in case of multiple graphic devices it was overwritten by the last one and there was no simple way how to configure this per domain.
The new <output> element for <sound> devices allows you to configure which output will be used for each domain, however QEMU has a limitation that all sound devices will always use the same output because it is configured by environment variable QEMU_AUDIO_DRV per domain.
For backward compatibility we need to preserve the defaults if no output is specified:
- for vnc graphic it's by default NONE unless "vnc_allow_host_audio" was enabled, in that case we use DEFAULT which means it will pass the environment variable visible by libvirtd
- for sdl graphic by default we pass the environment variable
- for spice graphic we configure the SPICE output
- if no graphic is configured we use by default NONE unless "nogfx_allow_host_audio" was enabled, in that case we pass the environment variable
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1375433
Signed-off-by: Pavel Hrdina <phrdina@redhat.com> --- docs/formatdomain.html.in | 4 ++- src/qemu/qemu_command.c | 84 +++++++++++++++++++++-------------------------- src/qemu/qemu_domain.c | 54 ++++++++++++++++++++++++++++++ src/qemu/qemu_process.c | 41 +++++++++++++++++++++++ 4 files changed, 135 insertions(+), 48 deletions(-)
Is there any way to make less things happen in one patch? Maybe even the PostParse, Prepare, and Validate together? I need to look at this one with fresh eyes in the morning - it's confusing at best especially since I don't find the functions self documenting. I'm having trouble with the settings between PostParse and Prepare as well as setting a default output which essentially changes an optional parameter into a required one, doesn't it? I'm sure there's a harder and even more confusing way to do things ;-). John
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 3b7c367fc7..ae0d8b86be 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -7064,7 +7064,9 @@ qemu-kvm -net nic,model=? /dev/null the audio output is connected within host. There is mandatory <code>type</code> attribute where valid values are 'none' to disable the audio output, 'spice', 'pa', 'sdl', 'alsa', 'oss'. - This might not be supported by all hypervisors. + This might not be supported by all hypervisors. QEMU driver + has a limitation that all sound devices have to use the same + output. </p>
<h4><a id="elementsWatchdog">Watchdog device</a></h4> diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c5c7bd7e54..5cbd1d0d46 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4442,67 +4442,57 @@ qemuBuildSoundCodecStr(virDomainSoundDefPtr sound, }
-static void +static int qemuBuildSoundAudioEnv(virCommandPtr cmd, - const virDomainDef *def, - virQEMUDriverConfigPtr cfg) + const virDomainDef *def) { + char *envStr = NULL; + if (def->nsounds == 0) { virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none"); - return; + return 0; }
- if (def->ngraphics == 0) { - if (cfg->nogfxAllowHostAudio) - virCommandAddEnvPassBlockSUID(cmd, "QEMU_AUDIO_DRV", NULL); - else - virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none"); - } else { - switch (def->graphics[def->ngraphics - 1]->type) { - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: - /* 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 - */ - virCommandAddEnvPassBlockSUID(cmd, "QEMU_AUDIO_DRV", NULL); + /* QEMU doesn't allow setting different audio output per sound device + * so it will always be the same for all devices. */
This is a case where the SoundPostParse should either be in its own patch or in the previous patch... Of course reading this "out of order" made me wonder how the the [0]th element was filled in...
+ switch (def->sounds[0]->output) { + case VIR_DOMAIN_SOUND_OUTPUT_TYPE_DEFAULT: + /* The default output is used only as backward compatible way to + * pass-through environment variables configured before starting + * libvirtd. */ + virCommandAddEnvPassBlockSUID(cmd, "QEMU_AUDIO_DRV", NULL); + if (def->ngraphics > 0 && + def->graphics[def->ngraphics - 1]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { virCommandAddEnvPassBlockSUID(cmd, "SDL_AUDIODRIVER", NULL); + } + break;
- break; - - case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - /* 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 (cfg->vncAllowHostAudio) - virCommandAddEnvPassBlockSUID(cmd, "QEMU_AUDIO_DRV", NULL); - else - virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none"); - - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: - /* 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"); - - break; - - case VIR_DOMAIN_GRAPHICS_TYPE_RDP: - case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: - case VIR_DOMAIN_GRAPHICS_TYPE_LAST: - break; + case VIR_DOMAIN_SOUND_OUTPUT_TYPE_NONE: + case VIR_DOMAIN_SOUND_OUTPUT_TYPE_SPICE: + case VIR_DOMAIN_SOUND_OUTPUT_TYPE_PA: + case VIR_DOMAIN_SOUND_OUTPUT_TYPE_SDL: + case VIR_DOMAIN_SOUND_OUTPUT_TYPE_ALSA: + case VIR_DOMAIN_SOUND_OUTPUT_TYPE_OSS: + if (virAsprintf(&envStr, "QEMU_AUDIO_DRV=%s", + virDomainSoundOutputTypeToString(def->sounds[0]->output)) < 0) { + return -1; } + virCommandAddEnvString(cmd, envStr); + VIR_FREE(envStr); + break; + + case VIR_DOMAIN_SOUND_OUTPUT_TYPE_LAST: + break; } + + return 0; }
static int qemuBuildSoundCommandLine(virCommandPtr cmd, const virDomainDef *def, - virQEMUCapsPtr qemuCaps, - virQEMUDriverConfigPtr cfg) + virQEMUCapsPtr qemuCaps) { size_t i, j;
@@ -4556,7 +4546,7 @@ qemuBuildSoundCommandLine(virCommandPtr cmd, } }
- qemuBuildSoundAudioEnv(cmd, def, cfg); + qemuBuildSoundAudioEnv(cmd, def);
return 0; } @@ -10118,7 +10108,7 @@ qemuBuildCommandLine(virQEMUDriverPtr driver, if (qemuBuildVideoCommandLine(cmd, def, qemuCaps) < 0) goto error;
- if (qemuBuildSoundCommandLine(cmd, def, qemuCaps, cfg) < 0) + if (qemuBuildSoundCommandLine(cmd, def, qemuCaps) < 0) goto error;
if (qemuBuildWatchdogCommandLine(cmd, def, qemuCaps) < 0) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index a36e157529..3b8fa2d79c 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -3148,6 +3148,31 @@ qemuDomainDefVerifyFeatures(const virDomainDef *def) }
+static void +qemuDomainDefSoundPostParse(virDomainDefPtr def) +{ + size_t i; + virDomainSoundOutputType output = VIR_DOMAIN_SOUND_OUTPUT_TYPE_DEFAULT; + + for (i = 0; i < def->nsounds; i++) { + if (output != def->sounds[i]->output) { + output = def->sounds[i]->output; + break; + } + } + + /* For convenience we will copy the first configured sound output to all + * sound devices that doesn't have any output configured because QEMU
s/doesn't/don't/
+ * will use only one output for all sound devices. */ + if (output != VIR_DOMAIN_SOUND_OUTPUT_TYPE_DEFAULT) { + for (i = 0; i < def->nsounds; i++) { + if (def->sounds[i]->output == VIR_DOMAIN_SOUND_OUTPUT_TYPE_DEFAULT) + def->sounds[i]->output = output; + } + } +} + + static int qemuDomainDefPostParseBasic(virDomainDefPtr def, virCapsPtr caps, @@ -3221,6 +3246,8 @@ qemuDomainDefPostParse(virDomainDefPtr def, if (qemuDomainDefCPUPostParse(def) < 0) goto cleanup;
+ qemuDomainDefSoundPostParse(def); + ret = 0; cleanup: virObjectUnref(cfg); @@ -3301,6 +3328,30 @@ qemuDomainDefValidateVideo(const virDomainDef *def) }
+static int +qemuDomainDefValidateSound(const virDomainDef *def) +{ + size_t i; + virDomainSoundOutputType output = VIR_DOMAIN_SOUND_OUTPUT_TYPE_DEFAULT; + + for (i = 0; i < def->nsounds; i++) { + if (output == VIR_DOMAIN_SOUND_OUTPUT_TYPE_DEFAULT) { + output = def->sounds[i]->output; + continue; + } + + if (output != def->sounds[i]->output) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("all sound devices must be configured to use " + "the same output")); + return -1; + } + } + + return 0; +} + + #define QEMU_MAX_VCPUS_WITHOUT_EIM 255
@@ -3403,6 +3454,9 @@ qemuDomainDefValidate(const virDomainDef *def, if (qemuDomainDefValidateVideo(def) < 0) goto cleanup;
+ if (qemuDomainDefValidateSound(def) < 0) + goto cleanup; + ret = 0;
cleanup: diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 6d242b1b51..2957c4a074 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -5393,6 +5393,45 @@ qemuProcessPrepareAllowReboot(virDomainObjPtr vm) }
+static void +qemuProcessPrepareSound(virDomainDefPtr def, + virQEMUDriverConfigPtr cfg) +{ + virDomainSoundOutputType output = VIR_DOMAIN_SOUND_OUTPUT_TYPE_DEFAULT; + size_t i; + + if (def->nsounds == 0) + return; + + if (def->ngraphics == 0) { + if (!cfg->nogfxAllowHostAudio) + output = VIR_DOMAIN_SOUND_OUTPUT_TYPE_NONE; + } else { + switch (def->graphics[def->ngraphics - 1]->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + output = VIR_DOMAIN_SOUND_OUTPUT_TYPE_SPICE; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + if (!cfg->vncAllowHostAudio) + output = VIR_DOMAIN_SOUND_OUTPUT_TYPE_NONE; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + break; + } + } + + for (i = 0; i < def->nsounds; i++) { + if (def->sounds[i]->output == VIR_DOMAIN_SOUND_OUTPUT_TYPE_DEFAULT) + def->sounds[i]->output = output; + } +} + + /** * qemuProcessPrepareDomain: * @conn: connection object (for looking up storage volumes) @@ -5513,6 +5552,8 @@ qemuProcessPrepareDomain(virConnectPtr conn, goto cleanup; }
+ qemuProcessPrepareSound(vm->def, cfg); + ret = 0; cleanup: virObjectUnref(caps);