[PATCH 00/19] Add qemu RDP server support

From: Marc-André Lureau <marcandre.lureau@redhat.com> Hi, This patch series offers an out-of-process Remote Desktop Protocol (RDP) server solution utilizing QEMU's -display dbus interface, offering improved modularity and potential security benefits compared to built-in server. This initiative was spearheaded by Mihnea Buzatu during the QEMU Summer of Code 2023. The project's goal was to develop an out-of-process RDP server using the -display dbus interface, implemented in Rust. Given that the IronRDP crate lacked some server support at the time, investments in IronRDP were required. I finally released an initial v0.1 version of qemu-rdp on crates.io (https://crates.io/crates/qemu-rdp). That should allow more people to review and evaluate the state of this work. On unix systems, with cargo/rust toolchain installed, it should be as easy as running "cargo install qemu-rdp", apply this patch series for libvirt, set the "rdp_tls_x509_cert_dir" location for your TLS certificates, and configure a VM with both dbus & rdp graphics (run "virsh domdisplay DOMAIN" to get the display connection details). Thanks for the reviews & feedback! Marc-André Lureau (19): build-sys: drop -Winline build: fix -Werror=maybe-uninitialized qemu-slirp: drop unneeded check for OOM util: add conn != NULL precondition in virGDBusCallMethod() qemu: report an error for unsupported graphics qemu: add rdp state directory qemu: add qemu RDP configuration conf: parse optional RDP username & password conf: generalize virDomainDefHasSpiceGraphics qemu: use virDomainDefHasGraphics qemu: add RDP ports range allocator qemu: limit to one <graphics type='rdp'> qemu/dbus: keep a connection to the VM D-Bus qemu/dbus: log daemon stdout/err qemu: validate RDP configuration qemu: if -display dbus capability is supported, accept rdp qemu: add qemu-rdp helper unit qemu: add RDP support tests: add qemu <graphics type='rdp'/> test docs/formatdomain.rst | 25 +- meson.build | 1 - po/POTFILES | 1 + src/conf/domain_conf.c | 28 +- src/conf/domain_conf.h | 5 +- src/conf/schemas/domaincommon.rng | 10 + src/libvirt_private.syms | 2 +- src/qemu/libvirtd_qemu.aug | 7 + src/qemu/meson.build | 1 + src/qemu/qemu.conf.in | 31 ++ src/qemu/qemu_capabilities.c | 7 +- src/qemu/qemu_command.c | 11 +- src/qemu/qemu_conf.c | 47 ++ src/qemu/qemu_conf.h | 13 + src/qemu/qemu_dbus.c | 83 +++- src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 4 + src/qemu/qemu_driver.c | 20 + src/qemu/qemu_extdevice.c | 46 +- src/qemu/qemu_hotplug.c | 49 +- src/qemu/qemu_hotplug.h | 1 + src/qemu/qemu_process.c | 167 ++++++- src/qemu/qemu_rdp.c | 427 ++++++++++++++++++ src/qemu/qemu_rdp.h | 71 +++ src/qemu/qemu_slirp.c | 6 - src/qemu/qemu_validate.c | 45 +- src/qemu/test_libvirtd_qemu.aug.in | 5 + src/util/virgdbus.c | 4 + tests/domaincapsdata/qemu_10.0.0.s390x.xml | 1 + .../domaincapsdata/qemu_7.0.0-q35.x86_64.xml | 1 + .../domaincapsdata/qemu_7.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.0.0.x86_64.xml | 1 + .../domaincapsdata/qemu_7.1.0-q35.x86_64.xml | 1 + .../domaincapsdata/qemu_7.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0.x86_64.xml | 1 + .../qemu_7.2.0-hvf.x86_64+hvf.xml | 1 + .../domaincapsdata/qemu_7.2.0-q35.x86_64.xml | 1 + .../qemu_7.2.0-tcg.x86_64+hvf.xml | 1 + .../domaincapsdata/qemu_7.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0.ppc.xml | 1 + tests/domaincapsdata/qemu_7.2.0.x86_64.xml | 1 + .../domaincapsdata/qemu_8.0.0-q35.x86_64.xml | 1 + .../domaincapsdata/qemu_8.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0.x86_64.xml | 1 + .../domaincapsdata/qemu_8.1.0-q35.x86_64.xml | 1 + .../domaincapsdata/qemu_8.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.1.0.x86_64.xml | 1 + .../domaincapsdata/qemu_8.2.0-q35.x86_64.xml | 1 + .../qemu_8.2.0-tcg-virt.loongarch64.xml | 1 + .../domaincapsdata/qemu_8.2.0-tcg.x86_64.xml | 1 + .../qemu_8.2.0-virt.aarch64.xml | 1 + .../qemu_8.2.0-virt.loongarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.aarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.armv7l.xml | 1 + tests/domaincapsdata/qemu_8.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.2.0.x86_64.xml | 1 + .../domaincapsdata/qemu_9.0.0-q35.x86_64.xml | 1 + .../domaincapsdata/qemu_9.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0.sparc.xml | 1 + tests/domaincapsdata/qemu_9.0.0.x86_64.xml | 1 + .../domaincapsdata/qemu_9.1.0-q35.x86_64.xml | 1 + .../qemu_9.1.0-tcg-virt.riscv64.xml | 1 + .../domaincapsdata/qemu_9.1.0-tcg.x86_64.xml | 1 + .../qemu_9.1.0-virt.riscv64.xml | 1 + tests/domaincapsdata/qemu_9.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.1.0.x86_64.xml | 1 + .../domaincapsdata/qemu_9.2.0-q35.x86_64.xml | 1 + .../domaincapsdata/qemu_9.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.2.0.x86_64.xml | 1 + .../graphics-rdp.x86_64-latest.args | 35 ++ .../graphics-rdp.x86_64-latest.xml | 1 + tests/qemuxmlconfdata/graphics-rdp.xml | 43 ++ tests/qemuxmlconftest.c | 2 + tests/testutilsqemu.c | 4 + tools/nss/libvirt_nss_leases.c | 2 +- tools/nss/libvirt_nss_macs.c | 2 +- 78 files changed, 1172 insertions(+), 78 deletions(-) create mode 100644 src/qemu/qemu_rdp.c create mode 100644 src/qemu/qemu_rdp.h create mode 100644 tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.args create mode 120000 tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/graphics-rdp.xml -- 2.47.0

From: Marc-André Lureau <marcandre.lureau@redhat.com> The warning is triggered when compiling with various build options, such as -Doptimization=g. From gcc(1) man page about -Winline: seemingly insignificant changes in the source program can cause the warnings produced by -Winline to appear or disappear. Such flaky behaviour is best left to the user discretion. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/meson.build b/meson.build index c7e5947d10..15f5a0fe74 100644 --- a/meson.build +++ b/meson.build @@ -340,7 +340,6 @@ cc_flags += [ '-Wimplicit-int', '-Wincompatible-pointer-types', '-Winit-self', - '-Winline', '-Wint-conversion', '-Wint-in-bool-context', '-Wint-to-pointer-cast', -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:23PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
The warning is triggered when compiling with various build options, such as -Doptimization=g.
From gcc(1) man page about -Winline: seemingly insignificant changes in the source program can cause the warnings produced by -Winline to appear or disappear.
Such flaky behaviour is best left to the user discretion.
IME it is not nearly as unreliable as that man page suggests. We have used '-Winline' by default for 18 years now with no significant reports of trouble before now that I recall, and IMHO it is useful to flag cases where contributors add pointless 'inline' annotations. If 'g' is known to create trouble though we could add a check for get_option('optimization') == 'g' and add -Wno-inline to override our default falgs.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- meson.build | 1 - 1 file changed, 1 deletion(-)
diff --git a/meson.build b/meson.build index c7e5947d10..15f5a0fe74 100644 --- a/meson.build +++ b/meson.build @@ -340,7 +340,6 @@ cc_flags += [ '-Wimplicit-int', '-Wincompatible-pointer-types', '-Winit-self', - '-Winline', '-Wint-conversion', '-Wint-in-bool-context', '-Wint-to-pointer-cast', -- 2.47.0
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> When compiled with -Doptimization=g ../tools/nss/libvirt_nss_macs.c:155:8: error: ‘jerr’ may be used uninitialized [-Werror=maybe-uninitialized] 155 | if (jerr == json_tokener_continue) { | ^ Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- tools/nss/libvirt_nss_leases.c | 2 +- tools/nss/libvirt_nss_macs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/nss/libvirt_nss_leases.c b/tools/nss/libvirt_nss_leases.c index 25ea6b0ce2..4d68787fb2 100644 --- a/tools/nss/libvirt_nss_leases.c +++ b/tools/nss/libvirt_nss_leases.c @@ -260,7 +260,7 @@ findLeases(const char *file, int ret = -1; json_object *jobj = NULL; json_tokener *tok = NULL; - enum json_tokener_error jerr; + enum json_tokener_error jerr = json_tokener_error_parse_eof; int jsonflags = JSON_TOKENER_STRICT | JSON_TOKENER_VALIDATE_UTF8; char line[1024]; size_t nreadTotal = 0; diff --git a/tools/nss/libvirt_nss_macs.c b/tools/nss/libvirt_nss_macs.c index bac8c0e1bb..c3af9375bc 100644 --- a/tools/nss/libvirt_nss_macs.c +++ b/tools/nss/libvirt_nss_macs.c @@ -122,7 +122,7 @@ findMACs(const char *file, char line[1024]; json_object *jobj = NULL; json_tokener *tok = NULL; - enum json_tokener_error jerr; + enum json_tokener_error jerr = json_tokener_error_parse_eof; int jsonflags = JSON_TOKENER_STRICT | JSON_TOKENER_VALIDATE_UTF8; size_t nreadTotal = 0; int rv; -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:24PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
When compiled with -Doptimization=g
../tools/nss/libvirt_nss_macs.c:155:8: error: ‘jerr’ may be used uninitialized [-Werror=maybe-uninitialized] 155 | if (jerr == json_tokener_continue) { | ^
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- tools/nss/libvirt_nss_leases.c | 2 +- tools/nss/libvirt_nss_macs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> glib anti-pattern, since it aborts on OOM. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_slirp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/qemu/qemu_slirp.c b/src/qemu/qemu_slirp.c index 66d9d77c6c..eac7e4cc47 100644 --- a/src/qemu/qemu_slirp.c +++ b/src/qemu/qemu_slirp.c @@ -99,12 +99,6 @@ qemuSlirpNewForHelper(const char *helper) size_t i, nfeatures; slirp = qemuSlirpNew(); - if (!slirp) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Failed to allocate slirp for '%1$s'"), helper); - return NULL; - } - cmd = virCommandNewArgList(helper, "--print-capabilities", NULL); virCommandSetOutputBuffer(cmd, &output); if (virCommandRun(cmd, NULL) < 0) -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:25PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
glib anti-pattern, since it aborts on OOM.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_slirp.c | 6 ------ 1 file changed, 6 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Helps avoid/debug a potential SEGV if conn is NULL, since gio will not set the "gerror" in that case and we will crash later at: virReportError(VIR_ERR_DBUS_SERVICE, "%s", gerror->message); Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/util/virgdbus.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/virgdbus.c b/src/util/virgdbus.c index 71ca3cd43b..5e11a95106 100644 --- a/src/util/virgdbus.c +++ b/src/util/virgdbus.c @@ -213,6 +213,8 @@ virGDBusCallMethod(GDBusConnection *conn, g_autoptr(GVariant) ret = NULL; g_autoptr(GError) gerror = NULL; + g_return_val_if_fail(conn != NULL, -1); + if (error) memset(error, 0, sizeof(*error)); @@ -268,6 +270,8 @@ virGDBusCallMethodWithFD(GDBusConnection *conn, g_autoptr(GVariant) ret = NULL; g_autoptr(GError) gerror = NULL; + g_return_val_if_fail(conn != NULL, -1); + if (error) memset(error, 0, sizeof(*error)); -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:26PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Helps avoid/debug a potential SEGV if conn is NULL, since gio will not set the "gerror" in that case and we will crash later at: virReportError(VIR_ERR_DBUS_SERVICE, "%s", gerror->message);
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/util/virgdbus.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/src/util/virgdbus.c b/src/util/virgdbus.c index 71ca3cd43b..5e11a95106 100644 --- a/src/util/virgdbus.c +++ b/src/util/virgdbus.c @@ -213,6 +213,8 @@ virGDBusCallMethod(GDBusConnection *conn, g_autoptr(GVariant) ret = NULL; g_autoptr(GError) gerror = NULL;
+ g_return_val_if_fail(conn != NULL, -1); + if (error) memset(error, 0, sizeof(*error));
@@ -268,6 +270,8 @@ virGDBusCallMethodWithFD(GDBusConnection *conn, g_autoptr(GVariant) ret = NULL; g_autoptr(GError) gerror = NULL;
+ g_return_val_if_fail(conn != NULL, -1); +
We don't use g_return_val_if_fail helpers in libvirt because that introduces inconsistent error reporting behaviour in our APIs. ie all error return paths call virReportError, except for the ones which don't call virReportError. If we expect 'conn' to be non-NULL, then we should at least add ATTRIBUTE_NONNULL in the header though, so static analysis tools validate this. If a runtime check is really needed, then it would have to be a full check with virReportError, but honestly we almost never do that anywhere in libvirt except for a few places where it has been easy to make such a mistake With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Without an error message, it can be tedious to figure out failure to start. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 1f20189e89..f08dba7988 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8421,6 +8421,9 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("graphics type '%1$s' not supported"), + virDomainGraphicsTypeToString(graphics->type)); return -1; case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:27PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Without an error message, it can be tedious to figure out failure to start.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 1f20189e89..f08dba7988 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8421,6 +8421,9 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("graphics type '%1$s' not supported"), + virDomainGraphicsTypeToString(graphics->type)); return -1;
We don't report such errors when building the command line as they should have already been caught & reported by qemuValidateDomainDef, in this case we have qemuValidateDomainDeviceDefGraphics which reports any unsupported graphics types, based on the capabilities we probe. If that check is wrong/incomplete, we should fix the validate method
case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default:
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Hi On Wed, Feb 12, 2025 at 7:44 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Wed, Jan 29, 2025 at 05:40:27PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Without an error message, it can be tedious to figure out failure to start.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 1f20189e89..f08dba7988 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8421,6 +8421,9 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("graphics type '%1$s' not supported"), + virDomainGraphicsTypeToString(graphics->type)); return -1;
We don't report such errors when building the command line as they should have already been caught & reported by qemuValidateDomainDef, in this case we have qemuValidateDomainDeviceDefGraphics which reports any unsupported graphics types, based on the capabilities we probe. If that check is wrong/incomplete, we should fix the validate method
I suppose I added it to help me figure out what were the missing pieces. It's a bit tedious when you have a flow that returns an error without any readable trace. I am fine to drop it..
case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default:
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_conf.c | 2 ++ src/qemu/qemu_conf.h | 1 + src/qemu/qemu_driver.c | 12 ++++++++++++ tests/testutilsqemu.c | 2 ++ 4 files changed, 17 insertions(+) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 8b9fe4e381..3c0bde1284 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -225,6 +225,7 @@ virQEMUDriverConfig *virQEMUDriverConfigNew(bool privileged, cfg->configDir = g_strdup_printf("%s/qemu", cfg->configBaseDir); cfg->autostartDir = g_strdup_printf("%s/qemu/autostart", cfg->configBaseDir); cfg->slirpStateDir = g_strdup_printf("%s/slirp", cfg->stateDir); + cfg->rdpStateDir = g_strdup_printf("%s/rdp", cfg->stateDir); cfg->passtStateDir = g_strdup_printf("%s/passt", cfg->stateDir); cfg->dbusStateDir = g_strdup_printf("%s/dbus", cfg->stateDir); @@ -326,6 +327,7 @@ static void virQEMUDriverConfigDispose(void *obj) g_free(cfg->slirpStateDir); g_free(cfg->passtStateDir); g_free(cfg->dbusStateDir); + g_free(cfg->rdpStateDir); g_free(cfg->libDir); g_free(cfg->cacheDir); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 42cdb6f883..8ded2b597c 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -99,6 +99,7 @@ struct _virQEMUDriverConfig { char *slirpStateDir; char *passtStateDir; char *dbusStateDir; + char *rdpStateDir; /* These two directories are ones QEMU processes use (so must match * the QEMU user/group */ char *libDir; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a1fc61bae2..5a6e7fa062 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -671,6 +671,11 @@ qemuStateInitialize(bool privileged, cfg->dbusStateDir); goto error; } + if (g_mkdir_with_parents(cfg->rdpStateDir, 0777) < 0) { + virReportSystemError(errno, _("Failed to create rdp state dir %1$s"), + cfg->rdpStateDir); + goto error; + } qemu_driver->inhibitor = virInhibitorNew( VIR_INHIBITOR_WHAT_SHUTDOWN, @@ -816,6 +821,13 @@ qemuStateInitialize(bool privileged, (int)cfg->group); goto error; } + if (chown(cfg->rdpStateDir, cfg->user, cfg->group) < 0) { + virReportSystemError(errno, + _("unable to set ownership of '%1$s' to %2$d:%3$d"), + cfg->rdpStateDir, (int)cfg->user, + (int)cfg->group); + goto error; + } run_uid = cfg->user; run_gid = cfg->group; diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index abc425b9b7..6635e5e0cd 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -330,6 +330,8 @@ int qemuTestDriverInit(virQEMUDriver *driver) cfg->passtStateDir = g_strdup("/var/run/libvirt/qemu/passt"); VIR_FREE(cfg->dbusStateDir); cfg->dbusStateDir = g_strdup("/var/run/libvirt/qemu/dbus"); + VIR_FREE(cfg->rdpStateDir); + cfg->rdpStateDir = g_strdup("/var/run/libvirt/qemu/rdp"); if (!g_mkdtemp(statedir)) { fprintf(stderr, "Cannot create fake stateDir"); -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:28PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_conf.c | 2 ++ src/qemu/qemu_conf.h | 1 + src/qemu/qemu_driver.c | 12 ++++++++++++ tests/testutilsqemu.c | 2 ++ 4 files changed, 17 insertions(+)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/libvirtd_qemu.aug | 7 ++++++ src/qemu/qemu.conf.in | 31 ++++++++++++++++++++++++ src/qemu/qemu_conf.c | 39 ++++++++++++++++++++++++++++++ src/qemu/qemu_conf.h | 6 +++++ src/qemu/test_libvirtd_qemu.aug.in | 5 ++++ tests/testutilsqemu.c | 2 ++ 6 files changed, 90 insertions(+) diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index 1377fd89cc..9fcdec29d1 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -50,6 +50,11 @@ module Libvirtd_qemu = | bool_entry "spice_sasl" | str_entry "spice_sasl_dir" + let rdp_entry = str_entry "rdp_listen" + | str_entry "rdp_tls_x509_cert_dir" + | str_entry "rdp_username" + | str_entry "rdp_password" + let chardev_entry = bool_entry "chardev_tls" | str_entry "chardev_tls_x509_cert_dir" | bool_entry "chardev_tls_x509_verify" @@ -102,6 +107,7 @@ module Libvirtd_qemu = | str_entry "bridge_helper" | str_entry "pr_helper" | str_entry "slirp_helper" + | str_entry "qemu_rdp" | str_entry "dbus_daemon" | bool_entry "set_process_name" | int_entry "max_processes" @@ -155,6 +161,7 @@ module Libvirtd_qemu = let entry = default_tls_entry | vnc_entry | spice_entry + | rdp_entry | chardev_entry | migrate_entry | backup_entry diff --git a/src/qemu/qemu.conf.in b/src/qemu/qemu.conf.in index d853136f10..a623b12195 100644 --- a/src/qemu/qemu.conf.in +++ b/src/qemu/qemu.conf.in @@ -229,6 +229,31 @@ # #spice_sasl_dir = "/some/directory/sasl2" +# RDP is configured to listen on 127.0.0.1 by default. +# To make it listen on all public interfaces, uncomment +# this next option. +# +#rdp_listen = "0.0.0.0" + +# In order to override the default TLS certificate location for +# RDP certificates, supply a valid path to the certificate directory. +# If the path is not provided, then the default_tls_x509_cert_dir path +# will be used. +# +#rdp_tls_x509_cert_dir = "/etc/pki/libvirt-rdp" + +# The default RDP username. This parameter is only used if the +# per-domain XML config does not already provide a username. +# +#rdp_username = "user" + +# The default RDP password. This parameter is only used if the +# per-domain XML config does not already provide a password. +# By default, RDP server will not allow password-less connections. +# Obviously change this example here before you set this. +# +#rdp_password = "RDP12345" + # Enable use of TLS encryption on the chardev TCP transports. # # It is necessary to setup CA and issue a server certificate @@ -918,6 +943,12 @@ # Path to the SLIRP networking helper. #slirp_helper = "/usr/bin/slirp-helper" + +# Path to qemu-rdp +# If this is not an absolute path, the program will be searched for +# in $PATH. +#qemu_rdp = "qemu-rdp" + # Path to the dbus-daemon # If this is not an absolute path, the program will be searched for # in $PATH. diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 3c0bde1284..4c8bfb97a9 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -103,6 +103,7 @@ VIR_ONCE_GLOBAL_INIT(virQEMUConfig); #define QEMU_BRIDGE_HELPER "qemu-bridge-helper" #define QEMU_PR_HELPER "qemu-pr-helper" +#define QEMU_RDP "qemu-rdp" #define QEMU_DBUS_DAEMON "dbus-daemon" @@ -240,6 +241,7 @@ virQEMUDriverConfig *virQEMUDriverConfigNew(bool privileged, } cfg->vncListen = g_strdup(VIR_LOOPBACK_IPV4_ADDR); + cfg->rdpListen = g_strdup(VIR_LOOPBACK_IPV4_ADDR); cfg->spiceListen = g_strdup(VIR_LOOPBACK_IPV4_ADDR); cfg->remotePortMin = QEMU_REMOTE_PORT_MIN; @@ -265,6 +267,7 @@ virQEMUDriverConfig *virQEMUDriverConfigNew(bool privileged, cfg->prHelperName = g_strdup(QEMU_PR_HELPER); cfg->slirpHelperName = g_strdup(QEMU_SLIRP_HELPER); cfg->dbusDaemonName = g_strdup(QEMU_DBUS_DAEMON); + cfg->qemuRdpName = g_strdup(QEMU_RDP); cfg->securityDefaultConfined = true; cfg->securityRequireConfined = false; @@ -351,6 +354,11 @@ static void virQEMUDriverConfigDispose(void *obj) g_free(cfg->spicePassword); g_free(cfg->spiceSASLdir); + g_free(cfg->rdpTLSx509certdir); + g_free(cfg->rdpListen); + g_free(cfg->rdpUsername); + g_free(cfg->rdpPassword); + g_free(cfg->chardevTLSx509certdir); g_free(cfg->chardevTLSx509secretUUID); @@ -375,6 +383,7 @@ static void virQEMUDriverConfigDispose(void *obj) g_free(cfg->prHelperName); g_free(cfg->slirpHelperName); g_free(cfg->dbusDaemonName); + g_free(cfg->qemuRdpName); g_free(cfg->saveImageFormat); g_free(cfg->dumpImageFormat); @@ -502,6 +511,21 @@ virQEMUDriverConfigLoadSPICEEntry(virQEMUDriverConfig *cfg, return 0; } +static int +virQEMUDriverConfigLoadRDPEntry(virQEMUDriverConfig *cfg, + virConf *conf) +{ + if (virConfGetValueString(conf, "rdp_tls_x509_cert_dir", &cfg->rdpTLSx509certdir) < 0) + return -1; + if (virConfGetValueString(conf, "rdp_listen", &cfg->rdpListen) < 0) + return -1; + if (virConfGetValueString(conf, "rdp_username", &cfg->rdpUsername) < 0) + return -1; + if (virConfGetValueString(conf, "rdp_password", &cfg->rdpPassword) < 0) + return -1; + + return 0; +} static int virQEMUDriverConfigLoadSpecificTLSEntry(virQEMUDriverConfig *cfg, @@ -689,6 +713,9 @@ virQEMUDriverConfigLoadProcessEntry(virQEMUDriverConfig *cfg, if (virConfGetValueString(conf, "dbus_daemon", &cfg->dbusDaemonName) < 0) return -1; + if (virConfGetValueString(conf, "qemu_rdp", &cfg->qemuRdpName) < 0) + return -1; + if (virConfGetValueBool(conf, "set_process_name", &cfg->setProcessName) < 0) return -1; if (virConfGetValueUInt(conf, "max_processes", &cfg->maxProcesses) < 0) @@ -1159,6 +1186,9 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfig *cfg, if (virQEMUDriverConfigLoadSPICEEntry(cfg, conf) < 0) return -1; + if (virQEMUDriverConfigLoadRDPEntry(cfg, conf) < 0) + return -1; + if (virQEMUDriverConfigLoadSpecificTLSEntry(cfg, conf) < 0) return -1; @@ -1246,6 +1276,14 @@ virQEMUDriverConfigValidate(virQEMUDriverConfig *cfg) return -1; } + if (cfg->rdpTLSx509certdir && + !virFileExists(cfg->rdpTLSx509certdir)) { + virReportError(VIR_ERR_CONF_SYNTAX, + _("rdp_tls_x509_cert_dir directory '%1$s' does not exist"), + cfg->rdpTLSx509certdir); + return -1; + } + if (cfg->chardevTLSx509certdir && !virFileExists(cfg->chardevTLSx509certdir)) { virReportError(VIR_ERR_CONF_SYNTAX, @@ -1331,6 +1369,7 @@ virQEMUDriverConfigSetDefaults(virQEMUDriverConfig *cfg) SET_TLS_X509_CERT_DEFAULT(vnc); SET_TLS_X509_CERT_DEFAULT(spice); + SET_TLS_X509_CERT_DEFAULT(rdp); SET_TLS_X509_CERT_DEFAULT(chardev); SET_TLS_X509_CERT_DEFAULT(migrate); SET_TLS_X509_CERT_DEFAULT(backup); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 8ded2b597c..3ab1c19e57 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -136,6 +136,11 @@ struct _virQEMUDriverConfig { char *spicePassword; bool spiceAutoUnixSocket; + char *rdpTLSx509certdir; + char *rdpListen; + char *rdpUsername; + char *rdpPassword; + bool chardevTLS; char *chardevTLSx509certdir; bool chardevTLSx509verify; @@ -174,6 +179,7 @@ struct _virQEMUDriverConfig { char *prHelperName; char *slirpHelperName; char *dbusDaemonName; + char *qemuRdpName; bool macFilter; diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 69fdae215a..9760976a22 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -22,6 +22,10 @@ module Test_libvirtd_qemu = { "spice_password" = "XYZ12345" } { "spice_sasl" = "1" } { "spice_sasl_dir" = "/some/directory/sasl2" } +{ "rdp_listen" = "0.0.0.0" } +{ "rdp_tls_x509_cert_dir" = "/etc/pki/libvirt-rdp" } +{ "rdp_username" = "user" } +{ "rdp_password" = "RDP12345" } { "chardev_tls" = "1" } { "chardev_tls_x509_cert_dir" = "/etc/pki/libvirt-chardev" } { "chardev_tls_x509_verify" = "1" } @@ -110,6 +114,7 @@ module Test_libvirtd_qemu = { "memory_backing_dir" = "/var/lib/libvirt/qemu/ram" } { "pr_helper" = "qemu-pr-helper" } { "slirp_helper" = "/usr/bin/slirp-helper" } +{ "qemu_rdp" = "qemu-rdp" } { "dbus_daemon" = "dbus-daemon" } { "swtpm_user" = "tss" } { "swtpm_group" = "tss" } diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index 6635e5e0cd..c6bea72783 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -382,6 +382,8 @@ int qemuTestDriverInit(virQEMUDriver *driver) cfg->vncTLSx509certdir = g_strdup("/etc/pki/libvirt-vnc"); VIR_FREE(cfg->spiceTLSx509certdir); cfg->spiceTLSx509certdir = g_strdup("/etc/pki/libvirt-spice"); + VIR_FREE(cfg->rdpTLSx509certdir); + cfg->rdpTLSx509certdir = g_strdup("/etc/pki/libvirt-rdp"); VIR_FREE(cfg->chardevTLSx509certdir); cfg->chardevTLSx509certdir = g_strdup("/etc/pki/libvirt-chardev"); VIR_FREE(cfg->vxhsTLSx509certdir); -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:29PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/libvirtd_qemu.aug | 7 ++++++ src/qemu/qemu.conf.in | 31 ++++++++++++++++++++++++ src/qemu/qemu_conf.c | 39 ++++++++++++++++++++++++++++++ src/qemu/qemu_conf.h | 6 +++++ src/qemu/test_libvirtd_qemu.aug.in | 5 ++++ tests/testutilsqemu.c | 2 ++ 6 files changed, 90 insertions(+)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
diff --git a/src/qemu/qemu.conf.in b/src/qemu/qemu.conf.in index d853136f10..a623b12195 100644 --- a/src/qemu/qemu.conf.in +++ b/src/qemu/qemu.conf.in @@ -229,6 +229,31 @@ # #spice_sasl_dir = "/some/directory/sasl2"
+# RDP is configured to listen on 127.0.0.1 by default. +# To make it listen on all public interfaces, uncomment +# this next option. +# +#rdp_listen = "0.0.0.0" + +# In order to override the default TLS certificate location for +# RDP certificates, supply a valid path to the certificate directory. +# If the path is not provided, then the default_tls_x509_cert_dir path +# will be used. +# +#rdp_tls_x509_cert_dir = "/etc/pki/libvirt-rdp" + +# The default RDP username. This parameter is only used if the +# per-domain XML config does not already provide a username. +# +#rdp_username = "user" + +# The default RDP password. This parameter is only used if the +# per-domain XML config does not already provide a password. +# By default, RDP server will not allow password-less connections. +# Obviously change this example here before you set this. +# +#rdp_password = "RDP12345"
I wanted to complain about this. We store the VNC password here but then VNC password auth is a bad joke so we don't especially care about security. For RDP we should care more seriously. Then I see we did the same for SPICE too which is the same situation as RDP, so we have precedent to justify this for RDP. So, anyway, not something you need to fix, just me observing that we really ought to NOT store passwords in plain text in this file at all. We need encrypted secrets using systemd credentials to unseal them. Future work..... With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Like VNC, allow to set credentials for RDP. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/conf/domain_conf.c | 13 +++++++++++++ src/conf/domain_conf.h | 2 ++ src/conf/schemas/domaincommon.rng | 10 ++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 45c2cd09f1..35dbb13106 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1986,6 +1986,7 @@ virDomainGraphicsAuthDefClear(virDomainGraphicsAuthDef *def) if (!def) return; + VIR_FREE(def->username); VIR_FREE(def->passwd); /* Don't free def */ @@ -11282,6 +11283,8 @@ virDomainGraphicsAuthDefParseXML(xmlNodePtr node, if (!def->passwd) return 0; + def->username = virXMLPropString(node, "username"); + validTo = virXMLPropString(node, "passwdValidTo"); if (validTo) { g_autoptr(GDateTime) then = NULL; @@ -11671,6 +11674,10 @@ virDomainGraphicsDefParseXMLRDP(virDomainGraphicsDef *def, if (STREQ_NULLABLE(multiUser, "yes")) def->data.rdp.multiUser = true; + if (virDomainGraphicsAuthDefParseXML(node, &def->data.rdp.auth, + def->type) < 0) + return -1; + return 0; } @@ -26314,6 +26321,10 @@ virDomainGraphicsAuthDefFormatAttr(virBuffer *buf, if (!def->passwd) return; + if (def->username) + virBufferEscapeString(buf, " username='%s'", + def->username); + if (flags & VIR_DOMAIN_DEF_FORMAT_SECURE) virBufferEscapeString(buf, " passwd='%s'", def->passwd); @@ -26543,6 +26554,8 @@ virDomainGraphicsDefFormat(virBuffer *buf, virDomainGraphicsListenDefFormatAddr(buf, glisten, flags); + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.rdp.auth, flags); + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ba1a495764..e5c2c2157c 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1903,6 +1903,7 @@ typedef enum { } virDomainGraphicsAuthConnectedType; struct _virDomainGraphicsAuthDef { + char *username; char *passwd; bool expires; /* Whether there is an expiry time set */ time_t validTo; /* seconds since epoch */ @@ -2027,6 +2028,7 @@ struct _virDomainGraphicsDef { bool autoport; bool replaceUser; bool multiUser; + virDomainGraphicsAuthDef auth; } rdp; struct { char *display; diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 5848d3eaaf..b5328601ff 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -4516,6 +4516,16 @@ <ref name="addrIPorName"/> </attribute> </optional> + <optional> + <attribute name="username"> + <text/> + </attribute> + </optional> + <optional> + <attribute name="passwd"> + <text/> + </attribute> + </optional> <ref name="listenElements"/> </group> <group> -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:30PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Like VNC, allow to set credentials for RDP.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/conf/domain_conf.c | 13 +++++++++++++ src/conf/domain_conf.h | 2 ++ src/conf/schemas/domaincommon.rng | 10 ++++++++++ 3 files changed, 25 insertions(+)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ba1a495764..e5c2c2157c 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1903,6 +1903,7 @@ typedef enum { } virDomainGraphicsAuthConnectedType;
struct _virDomainGraphicsAuthDef { + char *username; char *passwd; bool expires; /* Whether there is an expiry time set */ time_t validTo; /* seconds since epoch */ @@ -2027,6 +2028,7 @@ struct _virDomainGraphicsDef { bool autoport; bool replaceUser; bool multiUser; + virDomainGraphicsAuthDef auth;
Similar observation as the previous patch that we ought not to be storing the password in plain text directly in the XML. Everywhere except <graphics> we reference out of band secrets for getting credentials. We should fix the graphics XML one day to allow that too. Not something you need to fix.
} rdp; struct { char *display;
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Generalize the function, broaden its potential usage. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/conf/domain_conf.c | 15 ++++++++++++--- src/conf/domain_conf.h | 2 +- src/libvirt_private.syms | 2 +- src/qemu/qemu_validate.c | 4 ++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 35dbb13106..78878a9685 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -31747,15 +31747,24 @@ virDomainObjGetMessages(virDomainObj *vm, return rv; } + +/** + * virDomainDefHasGraphics: + * @def: domain definition + * @type: a graphics type + * + * Returns true if domain has a graphics of given type. + */ bool -virDomainDefHasSpiceGraphics(const virDomainDef *def) +virDomainDefHasGraphics(const virDomainDef *def, virDomainGraphicsType type) { size_t i = 0; for (i = 0; i < def->ngraphics; i++) { - if (def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + virDomainGraphicsDef *graphics = def->graphics[i]; + + if (graphics->type == type) return true; - } } return false; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index e5c2c2157c..b08e2549b7 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -4596,4 +4596,4 @@ virDomainObjGetMessages(virDomainObj *vm, unsigned int flags); bool -virDomainDefHasSpiceGraphics(const virDomainDef *def); +virDomainDefHasGraphics(const virDomainDef *def, virDomainGraphicsType type); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2fe0a07944..987947975b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -337,6 +337,7 @@ virDomainDefGetVcpus; virDomainDefGetVcpusMax; virDomainDefGetVcpusTopology; virDomainDefHasDeviceAddress; +virDomainDefHasGraphics; virDomainDefHasManagedPR; virDomainDefHasMdevHostdev; virDomainDefHasMemballoon; @@ -345,7 +346,6 @@ virDomainDefHasNVMeDisk; virDomainDefHasOldStyleROUEFI; virDomainDefHasOldStyleUEFI; virDomainDefHasPCIHostdev; -virDomainDefHasSpiceGraphics; virDomainDefHasUSB; virDomainDefHasVcpusOffline; virDomainDefHasVDPANet; diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 07a505d106..54226b8c62 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -2097,7 +2097,7 @@ qemuValidateDomainChrSourceDef(const virDomainChrSourceDef *def, case VIR_DOMAIN_CHR_TYPE_SPICEVMC: case VIR_DOMAIN_CHR_TYPE_SPICEPORT: - if (!virDomainDefHasSpiceGraphics(vmdef)) { + if (!virDomainDefHasGraphics(vmdef, VIR_DOMAIN_GRAPHICS_TYPE_SPICE)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("chardev '%1$s' not supported without spice graphics"), virDomainChrTypeToString(def->type)); @@ -4656,7 +4656,7 @@ qemuValidateDomainDeviceDefAudio(virDomainAudioDef *audio, break; case VIR_DOMAIN_AUDIO_TYPE_SPICE: - if (!virDomainDefHasSpiceGraphics(def)) { + if (!virDomainDefHasGraphics(def, VIR_DOMAIN_GRAPHICS_TYPE_SPICE)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Spice audio is not supported without spice graphics")); return -1; -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:31PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Generalize the function, broaden its potential usage.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/conf/domain_conf.c | 15 ++++++++++++--- src/conf/domain_conf.h | 2 +- src/libvirt_private.syms | 2 +- src/qemu/qemu_validate.c | 4 ++-- 4 files changed, 16 insertions(+), 7 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_validate.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 54226b8c62..c5b66c4966 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4409,8 +4409,6 @@ qemuValidateDomainDeviceDefGraphics(const virDomainGraphicsDef *graphics, virQEMUCaps *qemuCaps) { virDomainCapsDeviceGraphics graphicsCaps = { 0 }; - bool have_egl_headless = false; - size_t i; virQEMUCapsFillDomainDeviceGraphicsCaps(qemuCaps, &graphicsCaps); @@ -4421,18 +4419,11 @@ qemuValidateDomainDeviceDefGraphics(const virDomainGraphicsDef *graphics, return -1; } - for (i = 0; i < def->ngraphics; i++) { - if (def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS) { - have_egl_headless = true; - break; - } - } - /* Only VNC and SPICE can be paired with egl-headless, the other types * either don't make sense to pair with egl-headless or aren't even * supported by QEMU. */ - if (have_egl_headless) { + if (virDomainDefHasGraphics(def, VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS)) { if (graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS && graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC && graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:32PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_validate.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> RDP server use port 3389 by default. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_conf.c | 6 ++++++ src/qemu/qemu_conf.h | 6 ++++++ src/qemu/qemu_driver.c | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 4c8bfb97a9..2a6827bd79 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -64,6 +64,9 @@ VIR_LOG_INIT("qemu.qemu_conf"); #define QEMU_REMOTE_PORT_MIN 5900 #define QEMU_REMOTE_PORT_MAX 65535 +#define QEMU_RDP_PORT_MIN 3389 +#define QEMU_RDP_PORT_MAX 65535 + #define QEMU_WEBSOCKET_PORT_MIN 5700 #define QEMU_WEBSOCKET_PORT_MAX 65535 @@ -247,6 +250,9 @@ virQEMUDriverConfig *virQEMUDriverConfigNew(bool privileged, cfg->remotePortMin = QEMU_REMOTE_PORT_MIN; cfg->remotePortMax = QEMU_REMOTE_PORT_MAX; + cfg->rdpPortMin = QEMU_RDP_PORT_MIN; + cfg->rdpPortMax = QEMU_RDP_PORT_MAX; + cfg->webSocketPortMin = QEMU_WEBSOCKET_PORT_MIN; cfg->webSocketPortMax = QEMU_WEBSOCKET_PORT_MAX; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 3ab1c19e57..532487c6f0 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -169,6 +169,9 @@ struct _virQEMUDriverConfig { unsigned int remotePortMin; unsigned int remotePortMax; + unsigned int rdpPortMin; + unsigned int rdpPortMax; + unsigned int webSocketPortMin; unsigned int webSocketPortMax; @@ -311,6 +314,9 @@ struct _virQEMUDriver { /* Immutable pointer, immutable object */ virPortAllocatorRange *webSocketPorts; + /* Immutable pointer, immutable object */ + virPortAllocatorRange *rdpPorts; + /* Immutable pointer, immutable object */ virPortAllocatorRange *migrationPorts; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5a6e7fa062..d105a21244 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -724,6 +724,13 @@ qemuStateInitialize(bool privileged, cfg->webSocketPortMax)) == NULL) goto error; + if ((qemu_driver->rdpPorts = + virPortAllocatorRangeNew(_("rdp"), + cfg->rdpPortMin, + cfg->rdpPortMax)) == NULL) + goto error; + + if ((qemu_driver->migrationPorts = virPortAllocatorRangeNew(_("migration"), cfg->migrationPortMin, @@ -1071,6 +1078,7 @@ qemuStateCleanup(void) virSysinfoDefFree(qemu_driver->hostsysinfo); virPortAllocatorRangeFree(qemu_driver->migrationPorts); virPortAllocatorRangeFree(qemu_driver->webSocketPorts); + virPortAllocatorRangeFree(qemu_driver->rdpPorts); virPortAllocatorRangeFree(qemu_driver->remotePorts); virObjectUnref(qemu_driver->hostdevMgr); virObjectUnref(qemu_driver->securityManager); -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:33PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
RDP server use port 3389 by default.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_conf.c | 6 ++++++ src/qemu/qemu_conf.h | 6 ++++++ src/qemu/qemu_driver.c | 8 ++++++++ 3 files changed, 20 insertions(+)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f08dba7988..dc142b366a 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8420,6 +8420,7 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("graphics type '%1$s' not supported"), @@ -9974,6 +9975,7 @@ qemuBuildCommandLineValidate(virQEMUDriver *driver, int spice = 0; int egl_headless = 0; int dbus = 0; + int rdp = 0; if (!driver->privileged) { /* If we have no cgroups then we can have no tunings that @@ -10022,15 +10024,17 @@ qemuBuildCommandLineValidate(virQEMUDriver *driver, ++dbus; break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + ++rdp; + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } } - if (sdl > 1 || vnc > 1 || spice > 1 || egl_headless > 1 || dbus > 1) { + if (sdl > 1 || vnc > 1 || spice > 1 || egl_headless > 1 || dbus > 1 || rdp > 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("only 1 graphics device of each type (sdl, vnc, spice, headless, dbus) is supported")); + _("only 1 graphics device of each type (sdl, vnc, spice, headless, dbus, rdp) is supported")); return -1; } -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:34PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f08dba7988..dc142b366a 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8420,6 +8420,7 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg,
break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("graphics type '%1$s' not supported"), @@ -9974,6 +9975,7 @@ qemuBuildCommandLineValidate(virQEMUDriver *driver, int spice = 0; int egl_headless = 0; int dbus = 0; + int rdp = 0;
if (!driver->privileged) { /* If we have no cgroups then we can have no tunings that @@ -10022,15 +10024,17 @@ qemuBuildCommandLineValidate(virQEMUDriver *driver, ++dbus; break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + ++rdp; + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } }
- if (sdl > 1 || vnc > 1 || spice > 1 || egl_headless > 1 || dbus > 1) { + if (sdl > 1 || vnc > 1 || spice > 1 || egl_headless > 1 || dbus > 1 || rdp > 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("only 1 graphics device of each type (sdl, vnc, spice, headless, dbus) is supported")); + _("only 1 graphics device of each type (sdl, vnc, spice, headless, dbus, rdp) is supported")); return -1; }
The 'rdp' type also uses the 'dbus' backend in QEMU, so checking dbus > 1 || rdp > 1, allows for two uses of the 'dbus' backend. Hypothetically we could also have future 'vnc' or 'spice' types which used the dbus QEMU backend for out of process impls. IOW, we need to be counting QEMU backends here, rather than libvirt types. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Hi On Wed, Jan 29, 2025 at 5:58 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Wed, Jan 29, 2025 at 05:40:34PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f08dba7988..dc142b366a 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8420,6 +8420,7 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg,
break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("graphics type '%1$s' not supported"), @@ -9974,6 +9975,7 @@ qemuBuildCommandLineValidate(virQEMUDriver *driver, int spice = 0; int egl_headless = 0; int dbus = 0; + int rdp = 0;
if (!driver->privileged) { /* If we have no cgroups then we can have no tunings that @@ -10022,15 +10024,17 @@ qemuBuildCommandLineValidate(virQEMUDriver *driver, ++dbus; break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + ++rdp; + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } }
- if (sdl > 1 || vnc > 1 || spice > 1 || egl_headless > 1 || dbus > 1) { + if (sdl > 1 || vnc > 1 || spice > 1 || egl_headless > 1 || dbus > 1 || rdp > 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("only 1 graphics device of each type (sdl, vnc, spice, headless, dbus) is supported")); + _("only 1 graphics device of each type (sdl, vnc, spice, headless, dbus, rdp) is supported")); return -1; }
The 'rdp' type also uses the 'dbus' backend in QEMU, so checking dbus > 1 || rdp > 1, allows for two uses of the 'dbus' backend.
Hypothetically we could also have future 'vnc' or 'spice' types which used the dbus QEMU backend for out of process impls.
I don't think qemu supports multiple instances of the same graphics backends, this is true for -display dbus. If we were to have vnc or spice server out of process (I doubt someone will do one for spice, but I started one for vnc, fwiw), they would use the same dbus graphics instance. This check is thus still valid, only one of each kind is supported atm. Or am I missing something?
IOW, we need to be counting QEMU backends here, rather than libvirt types.
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> The following changes are going to communicate with the qemu-rdp server through the VM D-Bus bus, keep a connection for that and further usage. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_dbus.c | 22 +++++++++++++++++++--- src/qemu/qemu_domain.h | 2 ++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index 06b655d870..4a019ae092 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c @@ -85,7 +85,7 @@ qemuDBusGetAddress(virQEMUDriver *driver, static int -qemuDBusWriteConfig(const char *filename, const char *path) +qemuDBusWriteConfig(const char *filename, const char *address) { g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; g_autofree char *config = NULL; @@ -96,7 +96,7 @@ qemuDBusWriteConfig(const char *filename, const char *path) virBufferAdjustIndent(&buf, 2); virBufferAddLit(&buf, "<type>org.libvirt.qemu</type>\n"); - virBufferAsprintf(&buf, "<listen>unix:path=%s</listen>\n", path); + virBufferAsprintf(&buf, "<listen>%s</listen>\n", address); virBufferAddLit(&buf, "<auth>EXTERNAL</auth>\n"); virBufferAddLit(&buf, "<policy context='default'>\n"); @@ -140,6 +140,8 @@ qemuDBusStop(virQEMUDriver *driver, } else { priv->dbusDaemonRunning = false; } + + g_clear_object(&priv->dbusConnection); } @@ -175,11 +177,13 @@ qemuDBusStart(virQEMUDriver *driver, g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivate *priv = vm->privateData; g_autoptr(virCommand) cmd = NULL; + g_autoptr(GError) gerr = NULL; g_autofree char *dbusDaemonPath = NULL; g_autofree char *shortName = NULL; g_autofree char *pidfile = NULL; g_autofree char *configfile = NULL; g_autofree char *sockpath = NULL; + g_autofree char *address = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 500 * 1000; /* ms */ VIR_AUTOCLOSE errfd = -1; @@ -205,8 +209,9 @@ qemuDBusStart(virQEMUDriver *driver, pidfile = qemuDBusCreatePidFilename(cfg, shortName); configfile = qemuDBusCreateConfPath(cfg, shortName); sockpath = qemuDBusCreateSocketPath(cfg, shortName); + address = g_strdup_printf("unix:path=%s", sockpath); - if (qemuDBusWriteConfig(configfile, sockpath) < 0) { + if (qemuDBusWriteConfig(configfile, address) < 0) { virReportSystemError(errno, _("Failed to write '%1$s'"), configfile); return -1; } @@ -264,6 +269,17 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, sockpath, false) < 0) goto cleanup; + priv->dbusConnection = + g_dbus_connection_new_for_address_sync(address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT| + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, NULL, &gerr); + if (!priv->dbusConnection) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Failed to connect to dbus-daemon: %1$s"), gerr->message); + goto cleanup; + } + priv->dbusDaemonRunning = true; ret = 0; cleanup: diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 04577f1297..03cf84695f 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -39,6 +39,7 @@ #include "qemu_fd.h" #include "virchrdev.h" #include "virobject.h" +#include "virgdbus.h" #include "virdomainmomentobjlist.h" #include "virenum.h" #include "vireventthread.h" @@ -240,6 +241,7 @@ struct _qemuDomainObjPrivate { /* running backup job */ virDomainBackupDef *backup; + GDBusConnection *dbusConnection; bool dbusDaemonRunning; /* list of Ids to migrate */ -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:35PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
The following changes are going to communicate with the qemu-rdp server through the VM D-Bus bus, keep a connection for that and further usage.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_dbus.c | 22 +++++++++++++++++++--- src/qemu/qemu_domain.h | 2 ++ 2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index 06b655d870..4a019ae092 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c
@@ -175,11 +177,13 @@ qemuDBusStart(virQEMUDriver *driver, g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivate *priv = vm->privateData; g_autoptr(virCommand) cmd = NULL; + g_autoptr(GError) gerr = NULL; g_autofree char *dbusDaemonPath = NULL; g_autofree char *shortName = NULL; g_autofree char *pidfile = NULL; g_autofree char *configfile = NULL; g_autofree char *sockpath = NULL; + g_autofree char *address = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 500 * 1000; /* ms */ VIR_AUTOCLOSE errfd = -1; @@ -205,8 +209,9 @@ qemuDBusStart(virQEMUDriver *driver, pidfile = qemuDBusCreatePidFilename(cfg, shortName); configfile = qemuDBusCreateConfPath(cfg, shortName); sockpath = qemuDBusCreateSocketPath(cfg, shortName); + address = g_strdup_printf("unix:path=%s", sockpath);
- if (qemuDBusWriteConfig(configfile, sockpath) < 0) { + if (qemuDBusWriteConfig(configfile, address) < 0) { virReportSystemError(errno, _("Failed to write '%1$s'"), configfile); return -1; } @@ -264,6 +269,17 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, sockpath, false) < 0) goto cleanup;
+ priv->dbusConnection = + g_dbus_connection_new_for_address_sync(address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT| + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, NULL, &gerr);
We have just started dbus-daemon above here. We've proved the PID is running but can you confirm this guarantees the listener socket is accepting connections... that we're just about to establish.
+ if (!priv->dbusConnection) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Failed to connect to dbus-daemon: %1$s"), gerr->message); + goto cleanup; + } + priv->dbusDaemonRunning = true; ret = 0; cleanup:
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Hi On Wed, Feb 12, 2025 at 8:16 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Wed, Jan 29, 2025 at 05:40:35PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
The following changes are going to communicate with the qemu-rdp server through the VM D-Bus bus, keep a connection for that and further usage.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_dbus.c | 22 +++++++++++++++++++--- src/qemu/qemu_domain.h | 2 ++ 2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index 06b655d870..4a019ae092 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c
@@ -175,11 +177,13 @@ qemuDBusStart(virQEMUDriver *driver, g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); qemuDomainObjPrivate *priv = vm->privateData; g_autoptr(virCommand) cmd = NULL; + g_autoptr(GError) gerr = NULL; g_autofree char *dbusDaemonPath = NULL; g_autofree char *shortName = NULL; g_autofree char *pidfile = NULL; g_autofree char *configfile = NULL; g_autofree char *sockpath = NULL; + g_autofree char *address = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 500 * 1000; /* ms */ VIR_AUTOCLOSE errfd = -1; @@ -205,8 +209,9 @@ qemuDBusStart(virQEMUDriver *driver, pidfile = qemuDBusCreatePidFilename(cfg, shortName); configfile = qemuDBusCreateConfPath(cfg, shortName); sockpath = qemuDBusCreateSocketPath(cfg, shortName); + address = g_strdup_printf("unix:path=%s", sockpath);
- if (qemuDBusWriteConfig(configfile, sockpath) < 0) { + if (qemuDBusWriteConfig(configfile, address) < 0) { virReportSystemError(errno, _("Failed to write '%1$s'"), configfile); return -1; } @@ -264,6 +269,17 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, sockpath, false) < 0) goto cleanup;
+ priv->dbusConnection = + g_dbus_connection_new_for_address_sync(address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT| + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, NULL, &gerr);
We have just started dbus-daemon above here. We've proved the PID is running but can you confirm this guarantees the listener socket is accepting connections... that we're just about to establish.
Just above, we wait & check for the existence of the socket path.
+ if (!priv->dbusConnection) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Failed to connect to dbus-daemon: %1$s"), gerr->message); + goto cleanup; + } + priv->dbusDaemonRunning = true; ret = 0; cleanup:
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Currently, if dbus-daemon writes on errfd, it will SIGPIPE. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_dbus.c | 61 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index 4a019ae092..0e22392e6f 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c @@ -18,12 +18,17 @@ #include <config.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + #include "qemu_dbus.h" #include "qemu_security.h" #include "virlog.h" #include "virtime.h" #include "virpidfile.h" +#include "virutil.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -67,6 +72,14 @@ qemuDBusCreateConfPath(virQEMUDriverConfig *cfg, } +static char * +qemuDBusCreateLogFilename(virQEMUDriverConfig *cfg, + const char *shortName) +{ + return qemuDBusCreateFilename(cfg->dbusStateDir, shortName, ".log"); +} + + char * qemuDBusGetAddress(virQEMUDriver *driver, virDomainObj *vm) @@ -183,10 +196,11 @@ qemuDBusStart(virQEMUDriver *driver, g_autofree char *pidfile = NULL; g_autofree char *configfile = NULL; g_autofree char *sockpath = NULL; + g_autofree char *logpath = NULL; g_autofree char *address = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 500 * 1000; /* ms */ - VIR_AUTOCLOSE errfd = -1; + VIR_AUTOCLOSE logfd = -1; pid_t cpid = -1; int ret = -1; @@ -219,10 +233,40 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, configfile, false) < 0) goto cleanup; + logpath = qemuDBusCreateLogFilename(cfg, shortName); + + if (cfg->stdioLogD) { + g_autoptr(virLogManager) logManager = virLogManagerNew(driver->privileged); + + if (!logManager) + goto cleanup; + + if ((logfd = virLogManagerDomainOpenLogFile(logManager, + "qemu", + vm->def->uuid, + vm->def->name, + logpath, + 0, + NULL, NULL)) < 0) + goto cleanup; + } else { + if ((logfd = open(logpath, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)) < 0) { + virReportSystemError(errno, _("failed to create logfile %1$s"), + logpath); + goto cleanup; + } + if (virSetCloseExec(logfd) < 0) { + virReportSystemError(errno, _("failed to set close-on-exec flag on %1$s"), + logpath); + goto cleanup; + } + } + cmd = virCommandNew(dbusDaemonPath); virCommandClearCaps(cmd); virCommandSetPidFile(cmd, pidfile); - virCommandSetErrorFD(cmd, &errfd); + virCommandSetOutputFD(cmd, &logfd); + virCommandSetErrorFD(cmd, &logfd); virCommandDaemonize(cmd); virCommandAddArgFormat(cmd, "--config-file=%s", configfile); @@ -239,22 +283,15 @@ qemuDBusStart(virQEMUDriver *driver, if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) goto cleanup; while (virTimeBackOffWait(&timebackoff)) { - char errbuf[1024] = { 0 }; - if (virFileExists(sockpath)) break; if (virProcessKill(cpid, 0) == 0) continue; - if (saferead(errfd, errbuf, sizeof(errbuf) - 1) < 0) { - virReportSystemError(errno, - _("dbus-daemon %1$s died unexpectedly"), - dbusDaemonPath); - } else { - virReportError(VIR_ERR_OPERATION_FAILED, - _("dbus-daemon died and reported: %1$s"), errbuf); - } + virReportSystemError(errno, + _("dbus-daemon %1$s died unexpectedly, check the log"), + dbusDaemonPath); goto cleanup; } -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:36PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Currently, if dbus-daemon writes on errfd, it will SIGPIPE.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_dbus.c | 61 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 12 deletions(-)
diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index 4a019ae092..0e22392e6f 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c @@ -18,12 +18,17 @@
#include <config.h>
+#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + #include "qemu_dbus.h" #include "qemu_security.h"
#include "virlog.h" #include "virtime.h" #include "virpidfile.h" +#include "virutil.h"
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -67,6 +72,14 @@ qemuDBusCreateConfPath(virQEMUDriverConfig *cfg, }
+static char * +qemuDBusCreateLogFilename(virQEMUDriverConfig *cfg, + const char *shortName) +{ + return qemuDBusCreateFilename(cfg->dbusStateDir, shortName, ".log"); +} + + char * qemuDBusGetAddress(virQEMUDriver *driver, virDomainObj *vm) @@ -183,10 +196,11 @@ qemuDBusStart(virQEMUDriver *driver, g_autofree char *pidfile = NULL; g_autofree char *configfile = NULL; g_autofree char *sockpath = NULL; + g_autofree char *logpath = NULL; g_autofree char *address = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 500 * 1000; /* ms */ - VIR_AUTOCLOSE errfd = -1; + VIR_AUTOCLOSE logfd = -1; pid_t cpid = -1; int ret = -1;
@@ -219,10 +233,40 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, configfile, false) < 0) goto cleanup;
+ logpath = qemuDBusCreateLogFilename(cfg, shortName); + + if (cfg->stdioLogD) { + g_autoptr(virLogManager) logManager = virLogManagerNew(driver->privileged); + + if (!logManager) + goto cleanup; + + if ((logfd = virLogManagerDomainOpenLogFile(logManager, + "qemu", + vm->def->uuid, + vm->def->name, + logpath, + 0, + NULL, NULL)) < 0) + goto cleanup;
Hmm, this is going to make the dbus logs be intermingled with the QEMU logs which feels undesirable to me. IIUC for swtpm we have a dedicated log file per VM, and probably ought to follow that pattern generally for any helper app we spawn off.
+ } else { + if ((logfd = open(logpath, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)) < 0) { + virReportSystemError(errno, _("failed to create logfile %1$s"), + logpath); + goto cleanup; + } + if (virSetCloseExec(logfd) < 0) { + virReportSystemError(errno, _("failed to set close-on-exec flag on %1$s"), + logpath); + goto cleanup; + } + } + cmd = virCommandNew(dbusDaemonPath); virCommandClearCaps(cmd); virCommandSetPidFile(cmd, pidfile); - virCommandSetErrorFD(cmd, &errfd); + virCommandSetOutputFD(cmd, &logfd); + virCommandSetErrorFD(cmd, &logfd); virCommandDaemonize(cmd); virCommandAddArgFormat(cmd, "--config-file=%s", configfile);
@@ -239,22 +283,15 @@ qemuDBusStart(virQEMUDriver *driver, if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) goto cleanup; while (virTimeBackOffWait(&timebackoff)) { - char errbuf[1024] = { 0 }; - if (virFileExists(sockpath)) break;
if (virProcessKill(cpid, 0) == 0) continue;
- if (saferead(errfd, errbuf, sizeof(errbuf) - 1) < 0) { - virReportSystemError(errno, - _("dbus-daemon %1$s died unexpectedly"), - dbusDaemonPath); - } else { - virReportError(VIR_ERR_OPERATION_FAILED, - _("dbus-daemon died and reported: %1$s"), errbuf); - } + virReportSystemError(errno, + _("dbus-daemon %1$s died unexpectedly, check the log"), + dbusDaemonPath);
goto cleanup; } -- 2.47.0
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On Wed, Feb 12, 2025 at 04:18:49PM +0000, Daniel P. Berrangé wrote:
On Wed, Jan 29, 2025 at 05:40:36PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Currently, if dbus-daemon writes on errfd, it will SIGPIPE.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_dbus.c | 61 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 12 deletions(-)
diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index 4a019ae092..0e22392e6f 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c @@ -18,12 +18,17 @@
#include <config.h>
+#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + #include "qemu_dbus.h" #include "qemu_security.h"
#include "virlog.h" #include "virtime.h" #include "virpidfile.h" +#include "virutil.h"
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -67,6 +72,14 @@ qemuDBusCreateConfPath(virQEMUDriverConfig *cfg, }
+static char * +qemuDBusCreateLogFilename(virQEMUDriverConfig *cfg, + const char *shortName) +{ + return qemuDBusCreateFilename(cfg->dbusStateDir, shortName, ".log"); +} + + char * qemuDBusGetAddress(virQEMUDriver *driver, virDomainObj *vm) @@ -183,10 +196,11 @@ qemuDBusStart(virQEMUDriver *driver, g_autofree char *pidfile = NULL; g_autofree char *configfile = NULL; g_autofree char *sockpath = NULL; + g_autofree char *logpath = NULL; g_autofree char *address = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 500 * 1000; /* ms */ - VIR_AUTOCLOSE errfd = -1; + VIR_AUTOCLOSE logfd = -1; pid_t cpid = -1; int ret = -1;
@@ -219,10 +233,40 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, configfile, false) < 0) goto cleanup;
+ logpath = qemuDBusCreateLogFilename(cfg, shortName); + + if (cfg->stdioLogD) { + g_autoptr(virLogManager) logManager = virLogManagerNew(driver->privileged); + + if (!logManager) + goto cleanup; + + if ((logfd = virLogManagerDomainOpenLogFile(logManager, + "qemu", + vm->def->uuid, + vm->def->name, + logpath, + 0, + NULL, NULL)) < 0) + goto cleanup;
Hmm, this is going to make the dbus logs be intermingled with the QEMU logs which feels undesirable to me.
IIUC for swtpm we have a dedicated log file per VM, and probably ought to follow that pattern generally for any helper app we spawn off.
Nevermind, I was mis-reading the code - 'logpath' will of course be different from the main qemu log.
@@ -239,22 +283,15 @@ qemuDBusStart(virQEMUDriver *driver, if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) goto cleanup; while (virTimeBackOffWait(&timebackoff)) { - char errbuf[1024] = { 0 }; - if (virFileExists(sockpath)) break;
if (virProcessKill(cpid, 0) == 0) continue;
- if (saferead(errfd, errbuf, sizeof(errbuf) - 1) < 0) { - virReportSystemError(errno, - _("dbus-daemon %1$s died unexpectedly"), - dbusDaemonPath); - } else { - virReportError(VIR_ERR_OPERATION_FAILED, - _("dbus-daemon died and reported: %1$s"), errbuf); - } + virReportSystemError(errno, + _("dbus-daemon %1$s died unexpectedly, check the log"), + dbusDaemonPath);
Here we could call g_file_get_contents(logpath) and include that in the error message still. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_validate.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index c5b66c4966..86fc5ac0fe 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4402,6 +4402,29 @@ qemuValidateDomainDeviceDefDBusGraphics(const virDomainGraphicsDef *graphics, } +static int +qemuValidateDomainDeviceDefRDPGraphics(const virDomainGraphicsDef *graphics) +{ + if (graphics->data.rdp.replaceUser) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("RDP doesn't support 'replaceUser'")); + return -1; + } + if (graphics->data.rdp.multiUser) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("RDP doesn't support 'multiUser'")); + return -1; + } + if (graphics->data.rdp.auth.expires) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("RDP password expiration isn't supported")); + return -1; + } + + return 0; +} + + static int qemuValidateDomainDeviceDefGraphics(const virDomainGraphicsDef *graphics, const virDomainDef *def, @@ -4473,8 +4496,13 @@ qemuValidateDomainDeviceDefGraphics(const virDomainGraphicsDef *graphics, break; - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if (qemuValidateDomainDeviceDefRDPGraphics(graphics) < 0) + return -1; + + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:37PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_validate.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> We may want to check qemu-rdp presence, instead of failing later? Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_capabilities.c | 7 +++++-- tests/domaincapsdata/qemu_10.0.0.s390x.xml | 1 + tests/domaincapsdata/qemu_7.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0-hvf.x86_64+hvf.xml | 1 + tests/domaincapsdata/qemu_7.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0-tcg.x86_64+hvf.xml | 1 + tests/domaincapsdata/qemu_7.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0.ppc.xml | 1 + tests/domaincapsdata/qemu_7.2.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-tcg-virt.loongarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-virt.aarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-virt.loongarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.aarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.armv7l.xml | 1 + tests/domaincapsdata/qemu_8.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.2.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0.sparc.xml | 1 + tests/domaincapsdata/qemu_9.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-tcg-virt.riscv64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-virt.riscv64.xml | 1 + tests/domaincapsdata/qemu_9.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.2.0.x86_64.xml | 1 + 44 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 490f5b28fd..43669aa27d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6497,8 +6497,11 @@ virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUCaps *qemuCaps, VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_SPICE); if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_EGL_HEADLESS)) VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS); - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY_DBUS)) - VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_DBUS); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY_DBUS)) { + VIR_DOMAIN_CAPS_ENUM_SET(dev->type, + VIR_DOMAIN_GRAPHICS_TYPE_DBUS, + VIR_DOMAIN_GRAPHICS_TYPE_RDP); + } } diff --git a/tests/domaincapsdata/qemu_10.0.0.s390x.xml b/tests/domaincapsdata/qemu_10.0.0.s390x.xml index 4af3c7ec53..d66240307e 100644 --- a/tests/domaincapsdata/qemu_10.0.0.s390x.xml +++ b/tests/domaincapsdata/qemu_10.0.0.s390x.xml @@ -253,6 +253,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>egl-headless</value> <value>dbus</value> </enum> diff --git a/tests/domaincapsdata/qemu_7.0.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_7.0.0-q35.x86_64.xml index 2c5129453e..8fa50785b7 100644 --- a/tests/domaincapsdata/qemu_7.0.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_7.0.0-q35.x86_64.xml @@ -1050,6 +1050,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.0.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_7.0.0-tcg.x86_64.xml index d4a567f5c6..2686989d64 100644 --- a/tests/domaincapsdata/qemu_7.0.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_7.0.0-tcg.x86_64.xml @@ -1849,6 +1849,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.0.0.x86_64.xml b/tests/domaincapsdata/qemu_7.0.0.x86_64.xml index 6fa08af994..6ab3774452 100644 --- a/tests/domaincapsdata/qemu_7.0.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_7.0.0.x86_64.xml @@ -1050,6 +1050,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.1.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_7.1.0-q35.x86_64.xml index 8bed31ad22..75565c4495 100644 --- a/tests/domaincapsdata/qemu_7.1.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_7.1.0-q35.x86_64.xml @@ -1017,6 +1017,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.1.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_7.1.0-tcg.x86_64.xml index cb7fd8811b..4e44dbaf65 100644 --- a/tests/domaincapsdata/qemu_7.1.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_7.1.0-tcg.x86_64.xml @@ -1797,6 +1797,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.1.0.x86_64.xml b/tests/domaincapsdata/qemu_7.1.0.x86_64.xml index a0f8b13f72..898b081df0 100644 --- a/tests/domaincapsdata/qemu_7.1.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_7.1.0.x86_64.xml @@ -1017,6 +1017,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.2.0-hvf.x86_64+hvf.xml b/tests/domaincapsdata/qemu_7.2.0-hvf.x86_64+hvf.xml index 75b2f1102d..ad89db56d8 100644 --- a/tests/domaincapsdata/qemu_7.2.0-hvf.x86_64+hvf.xml +++ b/tests/domaincapsdata/qemu_7.2.0-hvf.x86_64+hvf.xml @@ -74,6 +74,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.2.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_7.2.0-q35.x86_64.xml index f9d27024fc..cfbe7e5dd1 100644 --- a/tests/domaincapsdata/qemu_7.2.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_7.2.0-q35.x86_64.xml @@ -1022,6 +1022,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.2.0-tcg.x86_64+hvf.xml b/tests/domaincapsdata/qemu_7.2.0-tcg.x86_64+hvf.xml index c7728ac454..805a636ec6 100644 --- a/tests/domaincapsdata/qemu_7.2.0-tcg.x86_64+hvf.xml +++ b/tests/domaincapsdata/qemu_7.2.0-tcg.x86_64+hvf.xml @@ -1502,6 +1502,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.2.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_7.2.0-tcg.x86_64.xml index c7728ac454..805a636ec6 100644 --- a/tests/domaincapsdata/qemu_7.2.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_7.2.0-tcg.x86_64.xml @@ -1502,6 +1502,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.2.0.ppc.xml b/tests/domaincapsdata/qemu_7.2.0.ppc.xml index c6ef37b1af..21dbe730c5 100644 --- a/tests/domaincapsdata/qemu_7.2.0.ppc.xml +++ b/tests/domaincapsdata/qemu_7.2.0.ppc.xml @@ -65,6 +65,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_7.2.0.x86_64.xml b/tests/domaincapsdata/qemu_7.2.0.x86_64.xml index f0d9493353..2b402f59c8 100644 --- a/tests/domaincapsdata/qemu_7.2.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_7.2.0.x86_64.xml @@ -1022,6 +1022,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.0.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_8.0.0-q35.x86_64.xml index b8c376cb14..c1f7b96465 100644 --- a/tests/domaincapsdata/qemu_8.0.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_8.0.0-q35.x86_64.xml @@ -1104,6 +1104,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.0.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_8.0.0-tcg.x86_64.xml index d3c9830a1a..4d8d4c7702 100644 --- a/tests/domaincapsdata/qemu_8.0.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_8.0.0-tcg.x86_64.xml @@ -1597,6 +1597,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.0.0.x86_64.xml b/tests/domaincapsdata/qemu_8.0.0.x86_64.xml index e8df30ae07..7db506386c 100644 --- a/tests/domaincapsdata/qemu_8.0.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_8.0.0.x86_64.xml @@ -1104,6 +1104,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.1.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_8.1.0-q35.x86_64.xml index e80e175376..4c9b674c1e 100644 --- a/tests/domaincapsdata/qemu_8.1.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_8.1.0-q35.x86_64.xml @@ -1363,6 +1363,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.1.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_8.1.0-tcg.x86_64.xml index 62ffabb3e2..52f0d339bb 100644 --- a/tests/domaincapsdata/qemu_8.1.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_8.1.0-tcg.x86_64.xml @@ -1619,6 +1619,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.1.0.s390x.xml b/tests/domaincapsdata/qemu_8.1.0.s390x.xml index 2ca3b1d2ae..be8bb70245 100644 --- a/tests/domaincapsdata/qemu_8.1.0.s390x.xml +++ b/tests/domaincapsdata/qemu_8.1.0.s390x.xml @@ -342,6 +342,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>egl-headless</value> <value>dbus</value> </enum> diff --git a/tests/domaincapsdata/qemu_8.1.0.x86_64.xml b/tests/domaincapsdata/qemu_8.1.0.x86_64.xml index 4117d926cb..8389909b74 100644 --- a/tests/domaincapsdata/qemu_8.1.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_8.1.0.x86_64.xml @@ -1363,6 +1363,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.2.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_8.2.0-q35.x86_64.xml index dfa88bcf96..dcb46e06ea 100644 --- a/tests/domaincapsdata/qemu_8.2.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_8.2.0-q35.x86_64.xml @@ -1364,6 +1364,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.2.0-tcg-virt.loongarch64.xml b/tests/domaincapsdata/qemu_8.2.0-tcg-virt.loongarch64.xml index c9f01904cd..18979cf280 100644 --- a/tests/domaincapsdata/qemu_8.2.0-tcg-virt.loongarch64.xml +++ b/tests/domaincapsdata/qemu_8.2.0-tcg-virt.loongarch64.xml @@ -72,6 +72,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>dbus</value> </enum> diff --git a/tests/domaincapsdata/qemu_8.2.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_8.2.0-tcg.x86_64.xml index 327cad253e..fae61e8955 100644 --- a/tests/domaincapsdata/qemu_8.2.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_8.2.0-tcg.x86_64.xml @@ -1585,6 +1585,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.2.0-virt.aarch64.xml b/tests/domaincapsdata/qemu_8.2.0-virt.aarch64.xml index 224c619168..ce17865e24 100644 --- a/tests/domaincapsdata/qemu_8.2.0-virt.aarch64.xml +++ b/tests/domaincapsdata/qemu_8.2.0-virt.aarch64.xml @@ -120,6 +120,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>egl-headless</value> <value>dbus</value> </enum> diff --git a/tests/domaincapsdata/qemu_8.2.0-virt.loongarch64.xml b/tests/domaincapsdata/qemu_8.2.0-virt.loongarch64.xml index ed3637eaec..8f4ebbc107 100644 --- a/tests/domaincapsdata/qemu_8.2.0-virt.loongarch64.xml +++ b/tests/domaincapsdata/qemu_8.2.0-virt.loongarch64.xml @@ -76,6 +76,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>dbus</value> </enum> diff --git a/tests/domaincapsdata/qemu_8.2.0.aarch64.xml b/tests/domaincapsdata/qemu_8.2.0.aarch64.xml index 224c619168..ce17865e24 100644 --- a/tests/domaincapsdata/qemu_8.2.0.aarch64.xml +++ b/tests/domaincapsdata/qemu_8.2.0.aarch64.xml @@ -120,6 +120,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>egl-headless</value> <value>dbus</value> </enum> diff --git a/tests/domaincapsdata/qemu_8.2.0.armv7l.xml b/tests/domaincapsdata/qemu_8.2.0.armv7l.xml index 7182dca796..ee653c0c49 100644 --- a/tests/domaincapsdata/qemu_8.2.0.armv7l.xml +++ b/tests/domaincapsdata/qemu_8.2.0.armv7l.xml @@ -69,6 +69,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_8.2.0.s390x.xml b/tests/domaincapsdata/qemu_8.2.0.s390x.xml index 57ac07c153..78f91736d6 100644 --- a/tests/domaincapsdata/qemu_8.2.0.s390x.xml +++ b/tests/domaincapsdata/qemu_8.2.0.s390x.xml @@ -342,6 +342,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>egl-headless</value> <value>dbus</value> </enum> diff --git a/tests/domaincapsdata/qemu_8.2.0.x86_64.xml b/tests/domaincapsdata/qemu_8.2.0.x86_64.xml index f8dbb717f1..97b3795b5a 100644 --- a/tests/domaincapsdata/qemu_8.2.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_8.2.0.x86_64.xml @@ -1364,6 +1364,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.0.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_9.0.0-q35.x86_64.xml index c5a653f57b..d55bc239a9 100644 --- a/tests/domaincapsdata/qemu_9.0.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_9.0.0-q35.x86_64.xml @@ -1364,6 +1364,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.0.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_9.0.0-tcg.x86_64.xml index 30876c5fef..48300b14f5 100644 --- a/tests/domaincapsdata/qemu_9.0.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_9.0.0-tcg.x86_64.xml @@ -1514,6 +1514,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.0.0.sparc.xml b/tests/domaincapsdata/qemu_9.0.0.sparc.xml index e48cdd0ae5..c7862f5842 100644 --- a/tests/domaincapsdata/qemu_9.0.0.sparc.xml +++ b/tests/domaincapsdata/qemu_9.0.0.sparc.xml @@ -60,6 +60,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.0.0.x86_64.xml b/tests/domaincapsdata/qemu_9.0.0.x86_64.xml index 6c141e1cb9..52f01a7cfc 100644 --- a/tests/domaincapsdata/qemu_9.0.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_9.0.0.x86_64.xml @@ -1364,6 +1364,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.1.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_9.1.0-q35.x86_64.xml index 9445d999b5..5449244329 100644 --- a/tests/domaincapsdata/qemu_9.1.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_9.1.0-q35.x86_64.xml @@ -1500,6 +1500,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.1.0-tcg-virt.riscv64.xml b/tests/domaincapsdata/qemu_9.1.0-tcg-virt.riscv64.xml index b4327bf878..b236ecbac4 100644 --- a/tests/domaincapsdata/qemu_9.1.0-tcg-virt.riscv64.xml +++ b/tests/domaincapsdata/qemu_9.1.0-tcg-virt.riscv64.xml @@ -82,6 +82,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.1.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_9.1.0-tcg.x86_64.xml index 61d92550c1..927a5a6d36 100644 --- a/tests/domaincapsdata/qemu_9.1.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_9.1.0-tcg.x86_64.xml @@ -1619,6 +1619,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.1.0-virt.riscv64.xml b/tests/domaincapsdata/qemu_9.1.0-virt.riscv64.xml index 73e5ba1bc3..1399b980fd 100644 --- a/tests/domaincapsdata/qemu_9.1.0-virt.riscv64.xml +++ b/tests/domaincapsdata/qemu_9.1.0-virt.riscv64.xml @@ -71,6 +71,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.1.0.s390x.xml b/tests/domaincapsdata/qemu_9.1.0.s390x.xml index be46cfe6ba..b73e0d0688 100644 --- a/tests/domaincapsdata/qemu_9.1.0.s390x.xml +++ b/tests/domaincapsdata/qemu_9.1.0.s390x.xml @@ -205,6 +205,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>egl-headless</value> <value>dbus</value> </enum> diff --git a/tests/domaincapsdata/qemu_9.1.0.x86_64.xml b/tests/domaincapsdata/qemu_9.1.0.x86_64.xml index 5e87efe5e8..dc2521135c 100644 --- a/tests/domaincapsdata/qemu_9.1.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_9.1.0.x86_64.xml @@ -1500,6 +1500,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.2.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_9.2.0-q35.x86_64.xml index 98c522f0fc..49f5e64bfa 100644 --- a/tests/domaincapsdata/qemu_9.2.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_9.2.0-q35.x86_64.xml @@ -1558,6 +1558,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.2.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_9.2.0-tcg.x86_64.xml index 7ccdc11412..884228db72 100644 --- a/tests/domaincapsdata/qemu_9.2.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_9.2.0-tcg.x86_64.xml @@ -1666,6 +1666,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> diff --git a/tests/domaincapsdata/qemu_9.2.0.s390x.xml b/tests/domaincapsdata/qemu_9.2.0.s390x.xml index e13809bc63..605a3af5c7 100644 --- a/tests/domaincapsdata/qemu_9.2.0.s390x.xml +++ b/tests/domaincapsdata/qemu_9.2.0.s390x.xml @@ -205,6 +205,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>egl-headless</value> <value>dbus</value> </enum> diff --git a/tests/domaincapsdata/qemu_9.2.0.x86_64.xml b/tests/domaincapsdata/qemu_9.2.0.x86_64.xml index 0b5162781e..d587c1316a 100644 --- a/tests/domaincapsdata/qemu_9.2.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_9.2.0.x86_64.xml @@ -1558,6 +1558,7 @@ <enum name='type'> <value>sdl</value> <value>vnc</value> + <value>rdp</value> <value>spice</value> <value>egl-headless</value> <value>dbus</value> -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:38PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
We may want to check qemu-rdp presence, instead of failing later?
Yes, we should validate qemu-rdp, because the capabilities get advertized to client apps in domain capabilities, as well as being the thing that checks XML configs in qemuDomainValidate. So if qemu-rdp is missing, we must not claim the capabilty.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_capabilities.c | 7 +++++-- tests/domaincapsdata/qemu_10.0.0.s390x.xml | 1 + tests/domaincapsdata/qemu_7.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0-hvf.x86_64+hvf.xml | 1 + tests/domaincapsdata/qemu_7.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0-tcg.x86_64+hvf.xml | 1 + tests/domaincapsdata/qemu_7.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0.ppc.xml | 1 + tests/domaincapsdata/qemu_7.2.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-tcg-virt.loongarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-virt.aarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-virt.loongarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.aarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.armv7l.xml | 1 + tests/domaincapsdata/qemu_8.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.2.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0.sparc.xml | 1 + tests/domaincapsdata/qemu_9.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-tcg-virt.riscv64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-virt.riscv64.xml | 1 + tests/domaincapsdata/qemu_9.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.2.0.x86_64.xml | 1 + 44 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 490f5b28fd..43669aa27d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6497,8 +6497,11 @@ virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUCaps *qemuCaps, VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_SPICE); if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_EGL_HEADLESS)) VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS); - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY_DBUS)) - VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_DBUS); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY_DBUS)) { + VIR_DOMAIN_CAPS_ENUM_SET(dev->type, + VIR_DOMAIN_GRAPHICS_TYPE_DBUS, + VIR_DOMAIN_GRAPHICS_TYPE_RDP); + } }
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Hi On Wed, Feb 12, 2025 at 8:21 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Wed, Jan 29, 2025 at 05:40:38PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
We may want to check qemu-rdp presence, instead of failing later?
Yes, we should validate qemu-rdp, because the capabilities get advertized to client apps in domain capabilities, as well as being the thing that checks XML configs in qemuDomainValidate. So if qemu-rdp is missing, we must not claim the capabilty.
Afaik, libvirt advertises capability for features that require external process support: swtpm, passt, various helpers.. Do you think we should also correct those?
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_capabilities.c | 7 +++++-- tests/domaincapsdata/qemu_10.0.0.s390x.xml | 1 + tests/domaincapsdata/qemu_7.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0-hvf.x86_64+hvf.xml | 1 + tests/domaincapsdata/qemu_7.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0-tcg.x86_64+hvf.xml | 1 + tests/domaincapsdata/qemu_7.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0.ppc.xml | 1 + tests/domaincapsdata/qemu_7.2.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-tcg-virt.loongarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-virt.aarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-virt.loongarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.aarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.armv7l.xml | 1 + tests/domaincapsdata/qemu_8.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.2.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0.sparc.xml | 1 + tests/domaincapsdata/qemu_9.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-tcg-virt.riscv64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-virt.riscv64.xml | 1 + tests/domaincapsdata/qemu_9.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.2.0.x86_64.xml | 1 + 44 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 490f5b28fd..43669aa27d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6497,8 +6497,11 @@ virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUCaps *qemuCaps, VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_SPICE); if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_EGL_HEADLESS)) VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS); - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY_DBUS)) - VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_DBUS); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY_DBUS)) { + VIR_DOMAIN_CAPS_ENUM_SET(dev->type, + VIR_DOMAIN_GRAPHICS_TYPE_DBUS, + VIR_DOMAIN_GRAPHICS_TYPE_RDP); + } }
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Hi On Fri, Feb 14, 2025 at 4:03 PM Marc-André Lureau <marcandre.lureau@redhat.com> wrote:
Hi
On Wed, Feb 12, 2025 at 8:21 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Wed, Jan 29, 2025 at 05:40:38PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
We may want to check qemu-rdp presence, instead of failing later?
Yes, we should validate qemu-rdp, because the capabilities get advertized to client apps in domain capabilities, as well as being the thing that checks XML configs in qemuDomainValidate. So if qemu-rdp is missing, we must not claim the capabilty.
Afaik, libvirt advertises capability for features that require external process support: swtpm, passt, various helpers.. Do you think we should also correct those?
Actually, there is some check for swtpm, but not for passt. I'll add one for qemu-rdp.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_capabilities.c | 7 +++++-- tests/domaincapsdata/qemu_10.0.0.s390x.xml | 1 + tests/domaincapsdata/qemu_7.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0-hvf.x86_64+hvf.xml | 1 + tests/domaincapsdata/qemu_7.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0-tcg.x86_64+hvf.xml | 1 + tests/domaincapsdata/qemu_7.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_7.2.0.ppc.xml | 1 + tests/domaincapsdata/qemu_7.2.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-tcg-virt.loongarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-virt.aarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0-virt.loongarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.aarch64.xml | 1 + tests/domaincapsdata/qemu_8.2.0.armv7l.xml | 1 + tests/domaincapsdata/qemu_8.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_8.2.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.0.0.sparc.xml | 1 + tests/domaincapsdata/qemu_9.0.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-tcg-virt.riscv64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.1.0-virt.riscv64.xml | 1 + tests/domaincapsdata/qemu_9.1.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.1.0.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0.s390x.xml | 1 + tests/domaincapsdata/qemu_9.2.0.x86_64.xml | 1 + 44 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 490f5b28fd..43669aa27d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6497,8 +6497,11 @@ virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUCaps *qemuCaps, VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_SPICE); if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_EGL_HEADLESS)) VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS); - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY_DBUS)) - VIR_DOMAIN_CAPS_ENUM_SET(dev->type, VIR_DOMAIN_GRAPHICS_TYPE_DBUS); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY_DBUS)) { + VIR_DOMAIN_CAPS_ENUM_SET(dev->type, + VIR_DOMAIN_GRAPHICS_TYPE_DBUS, + VIR_DOMAIN_GRAPHICS_TYPE_RDP); + } }
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Helpers to start the qemu-rdp server and set it up. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- po/POTFILES | 1 + src/qemu/meson.build | 1 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 2 + src/qemu/qemu_rdp.c | 427 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_rdp.h | 71 +++++++ 6 files changed, 503 insertions(+) create mode 100644 src/qemu/qemu_rdp.c create mode 100644 src/qemu/qemu_rdp.h diff --git a/po/POTFILES b/po/POTFILES index d4b3de781b..0c83affb44 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -195,6 +195,7 @@ src/qemu/qemu_passt.c src/qemu/qemu_postparse.c src/qemu/qemu_process.c src/qemu/qemu_qapi.c +src/qemu/qemu_rdp.c src/qemu/qemu_saveimage.c src/qemu/qemu_slirp.c src/qemu/qemu_snapshot.c diff --git a/src/qemu/meson.build b/src/qemu/meson.build index 43a8ad7c3b..7a07d4f2c4 100644 --- a/src/qemu/meson.build +++ b/src/qemu/meson.build @@ -34,6 +34,7 @@ qemu_driver_sources = [ 'qemu_postparse.c', 'qemu_process.c', 'qemu_qapi.c', + 'qemu_rdp.c', 'qemu_saveimage.c', 'qemu_security.c', 'qemu_snapshot.c', diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index c7d7ac26ce..7ad31c5844 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1038,6 +1038,7 @@ qemuDomainGraphicsPrivateDispose(void *obj) g_free(priv->tlsAlias); g_clear_pointer(&priv->secinfo, qemuDomainSecretInfoFree); + g_clear_pointer(&priv->rdp, qemuRdpFree); } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 03cf84695f..d3ccbcd63c 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -35,6 +35,7 @@ #include "qemu_capabilities.h" #include "qemu_migration_params.h" #include "qemu_nbdkit.h" +#include "qemu_rdp.h" #include "qemu_slirp.h" #include "qemu_fd.h" #include "virchrdev.h" @@ -419,6 +420,7 @@ struct _qemuDomainGraphicsPrivate { char *tlsAlias; qemuDomainSecretInfo *secinfo; + qemuRdp *rdp; }; diff --git a/src/qemu/qemu_rdp.c b/src/qemu/qemu_rdp.c new file mode 100644 index 0000000000..b1b03ad803 --- /dev/null +++ b/src/qemu/qemu_rdp.c @@ -0,0 +1,427 @@ +/* + * qemu_rdp.c: QEMU Rdp support + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <gio/gio.h> + +#include "qemu_dbus.h" +#include "qemu_extdevice.h" +#include "qemu_security.h" +#include "qemu_rdp.h" +#include "virenum.h" +#include "virerror.h" +#include "virjson.h" +#include "virlog.h" +#include "virpidfile.h" +#include "virutil.h" +#include "virgdbus.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("qemu.rdp"); + +VIR_ENUM_IMPL(qemuRdpFeature, + QEMU_RDP_FEATURE_LAST, + "", + "dbus-address", + "remotefx" +); + +#define ORG_QEMUDISPLAY_RDP "org.QemuDisplay.RDP" +#define ORG_QEMUDISPLAY_RDP_PATH "/org/qemu_display/rdp" +#define ORG_QEMUDISPLAY_RDP_IFACE "org.QemuDisplay.RDP" + + +void +qemuRdpFree(qemuRdp *rdp) +{ + if (!rdp) + return; + + virBitmapFree(rdp->features); + g_free(rdp); +} + + +void +qemuRdpSetFeature(qemuRdp *rdp, + qemuRdpFeature feature) +{ + ignore_value(virBitmapSetBit(rdp->features, feature)); +} + + +bool +qemuRdpHasFeature(const qemuRdp *rdp, + qemuRdpFeature feature) +{ + return virBitmapIsBitSet(rdp->features, feature); +} + + +qemuRdp * +qemuRdpNew(void) +{ + g_autoptr(qemuRdp) rdp = g_new0(qemuRdp, 1); + + rdp->features = virBitmapNew(QEMU_RDP_FEATURE_LAST); + rdp->pid = -1; + + return g_steal_pointer(&rdp); +} + + +qemuRdp * +qemuRdpNewForHelper(const char *helper) +{ + g_autoptr(qemuRdp) rdp = NULL; + g_autoptr(virCommand) cmd = NULL; + g_autofree char *output = NULL; + g_autoptr(virJSONValue) doc = NULL; + virJSONValue *featuresJSON; + g_autofree char *helperPath = NULL; + size_t i, nfeatures; + + helperPath = virFindFileInPath(helper); + if (!helperPath) { + virReportSystemError(errno, + _("'%1$s' is not a suitable qemu-rdp helper name"), + helper); + return NULL; + } + + rdp = qemuRdpNew(); + cmd = virCommandNewArgList(helperPath, "--print-capabilities", NULL); + virCommandSetOutputBuffer(cmd, &output); + if (virCommandRun(cmd, NULL) < 0) + return NULL; + + if (!(doc = virJSONValueFromString(output)) || + !(featuresJSON = virJSONValueObjectGetArray(doc, "features"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to parse json capabilities '%1$s'"), + helper); + return NULL; + } + + nfeatures = virJSONValueArraySize(featuresJSON); + for (i = 0; i < nfeatures; i++) { + virJSONValue *item = virJSONValueArrayGet(featuresJSON, i); + const char *tmpStr = virJSONValueGetString(item); + int tmp; + + if ((tmp = qemuRdpFeatureTypeFromString(tmpStr)) <= 0) { + VIR_WARN("unknown qemu-rdp feature %s", tmpStr); + continue; + } + + qemuRdpSetFeature(rdp, tmp); + } + + return g_steal_pointer(&rdp); +} + + +static char * +qemuRdpCreatePidFilename(virDomainObj *vm) +{ + qemuDomainObjPrivate *priv = vm->privateData; + virQEMUDriver *driver = priv->driver; + g_autofree char *shortName = virDomainDefGetShortName(vm->def); + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + g_autofree char *name = NULL; + + name = g_strdup_printf("%s-rdp", shortName); + + return virPidFileBuildPath(cfg->rdpStateDir, name); +} + + +static char * +qemuRdpCreateLogFilename(virQEMUDriverConfig *cfg, + const virDomainDef *def) +{ + return virFileBuildPath(cfg->logDir, def->name, "-qemu-rdp.log"); +} + + +void +qemuRdpStop(virDomainObj *vm, virDomainGraphicsDef *gfx) +{ + qemuDomainObjPrivate *priv = vm->privateData; + qemuDomainGraphicsPrivate *gfxpriv = QEMU_DOMAIN_GRAPHICS_PRIVATE(gfx); + qemuRdp *rdp = gfxpriv->rdp; + g_autofree char *pidfile = qemuRdpCreatePidFilename(vm); + virErrorPtr orig_err; + + if (!rdp) + return; + + if (rdp->leaving_id) { + g_dbus_connection_signal_unsubscribe(priv->dbusConnection, rdp->leaving_id); + rdp->leaving_id = 0; + } + g_clear_handle_id(&rdp->name_watch, g_bus_unwatch_name); + + virErrorPreserveLast(&orig_err); + + if (virPidFileForceCleanupPath(pidfile) < 0) { + VIR_WARN("Unable to kill qemu-rdp process"); + } else { + rdp->pid = -1; + } + + virErrorRestore(&orig_err); +} + + +int +qemuRdpSetupCgroup(qemuRdp *rdp, + virCgroup *cgroup) +{ + return virCgroupAddProcess(cgroup, rdp->pid); +} + + +static void +on_leaving_signal(GDBusConnection *connection, + const gchar *sender_name G_GNUC_UNUSED, + const gchar *object_path G_GNUC_UNUSED, + const gchar *interface_name G_GNUC_UNUSED, + const gchar *signal_name G_GNUC_UNUSED, + GVariant *parameters, + gpointer user_data) +{ + qemuRdp *rdp = user_data; + const gchar *reason; + + g_variant_get(parameters, "(&s)", &reason); + VIR_DEBUG("%s.Leaving reason: '%s'", ORG_QEMUDISPLAY_RDP_IFACE, reason); + g_dbus_connection_signal_unsubscribe(connection, rdp->leaving_id); + rdp->leaving_id = 0; +} + + +static void +name_appeared_cb(GDBusConnection* connection, + const gchar* name G_GNUC_UNUSED, + const gchar* name_owner G_GNUC_UNUSED, + gpointer user_data G_GNUC_UNUSED) +{ + qemuRdp *rdp = user_data; + + VIR_DEBUG("'%s' appeared", name); + rdp->name_appeared = true; + + if (!rdp->leaving_id) { + rdp->leaving_id = g_dbus_connection_signal_subscribe( + connection, + ORG_QEMUDISPLAY_RDP, + ORG_QEMUDISPLAY_RDP_IFACE, + "Leaving", + ORG_QEMUDISPLAY_RDP_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_leaving_signal, + rdp, + NULL); + } +} + + +static void +name_vanished_cb(GDBusConnection* connection G_GNUC_UNUSED, + const gchar* name G_GNUC_UNUSED, + gpointer user_data G_GNUC_UNUSED) +{ + qemuRdp *rdp = user_data; + + if (rdp->name_appeared && rdp->leaving_id) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("'%1$s' vanished unexpectedly"), name); + } +} + + +int +qemuRdpStart(virDomainObj *vm, virDomainGraphicsDef *gfx) +{ + qemuDomainObjPrivate *priv = vm->privateData; + virQEMUDriver *driver = priv->driver; + qemuDomainGraphicsPrivate *gfxpriv = QEMU_DOMAIN_GRAPHICS_PRIVATE(gfx); + qemuRdp *rdp = gfxpriv->rdp; + virDomainGraphicsListenDef *glisten = NULL; + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + g_autoptr(virCommand) cmd = NULL; + g_autofree char *pidfile = NULL; + g_autofree char *logpath = NULL; + g_autofree char *certpath = NULL; + g_autofree char *keypath = NULL; + g_autofree char *dbus_addr = qemuDBusGetAddress(driver, vm); + g_auto(virBuffer) bind_addr = VIR_BUFFER_INITIALIZER; + pid_t pid = -1; + VIR_AUTOCLOSE logfd = -1; + + if (rdp->pid != -1) { + return 0; + } + + if (!(glisten = virDomainGraphicsGetListen(gfx, 0))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing listen element")); + return -1; + } + + switch (glisten->type) { + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: + if (glisten->address) { + bool escapeAddr = strchr(glisten->address, ':') != NULL; + if (escapeAddr) + virBufferAsprintf(&bind_addr, "[%s]", glisten->address); + else + virBufferAdd(&bind_addr, glisten->address, -1); + } + virBufferAsprintf(&bind_addr, ":%d", + gfx->data.rdp.port); + break; + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: + case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unsupported qemu-rdp listen type")); + return -1; + } + + if (!(pidfile = qemuRdpCreatePidFilename(vm))) + return -1; + + logpath = qemuRdpCreateLogFilename(cfg, vm->def); + + if (cfg->stdioLogD) { + g_autoptr(virLogManager) logManager = virLogManagerNew(driver->privileged); + + if (!logManager) + goto error; + + if ((logfd = virLogManagerDomainOpenLogFile(logManager, + "qemu", + vm->def->uuid, + vm->def->name, + logpath, + 0, + NULL, NULL)) < 0) + goto error; + } else { + if ((logfd = open(logpath, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)) < 0) { + virReportSystemError(errno, _("failed to create logfile %1$s"), + logpath); + goto error; + } + if (virSetCloseExec(logfd) < 0) { + virReportSystemError(errno, _("failed to set close-on-exec flag on %1$s"), + logpath); + goto error; + } + } + + cmd = virCommandNew(cfg->qemuRdpName); + virCommandClearCaps(cmd); + virCommandSetPidFile(cmd, pidfile); + virCommandSetOutputFD(cmd, &logfd); + virCommandSetErrorFD(cmd, &logfd); + virCommandDaemonize(cmd); + if (dbus_addr) { + if (!qemuRdpHasFeature(rdp, QEMU_RDP_FEATURE_DBUS_ADDRESS)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu-rdp doesn't support custom D-Bus address")); + } + virCommandAddArgPair(cmd, "--dbus-address", dbus_addr); + } + virCommandAddArg(cmd, "serve"); + + virCommandAddArg(cmd, "--bind-address"); + virCommandAddArgBuffer(cmd, &bind_addr); + + certpath = g_build_filename(cfg->rdpTLSx509certdir, "server-cert.pem", NULL); + keypath = g_build_filename(cfg->rdpTLSx509certdir, "server-key.pem", NULL); + virCommandAddArgPair(cmd, "--cert", certpath); + virCommandAddArgPair(cmd, "--key", keypath); + + if (qemuExtDeviceLogCommand(driver, vm, cmd, "qemu-rdp") < 0) + return -1; + + rdp->name_watch = g_bus_watch_name_on_connection(priv->dbusConnection, + ORG_QEMUDISPLAY_RDP, + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_cb, + name_vanished_cb, + rdp, + NULL); + + if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, false, NULL) < 0) + goto error; + + if (virPidFileReadPath(pidfile, &pid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to read qemu-rdp pidfile '%1$s'"), + pidfile); + goto error; + } + + if (virProcessKill(pid, 0) != 0) { + virReportSystemError(errno, "%s", + _("qemu-rdp died unexpectedly")); + goto error; + } + + rdp->pid = pid; + + return 0; + + error: + g_clear_handle_id(&rdp->name_watch, g_bus_unwatch_name); + qemuRdpStop(vm, gfx); + return -1; +} + + +int +qemuRdpSetCredentials(virDomainObj *vm, + const char *username, + const char *password, + const char *domain) +{ + qemuDomainObjPrivate *priv = vm->privateData; + g_autoptr(GVariant) args = NULL; + + args = g_variant_new("(sss)", username, password, domain); + + return virGDBusCallMethod(priv->dbusConnection, + NULL, + G_VARIANT_TYPE("()"), + NULL, + ORG_QEMUDISPLAY_RDP, + ORG_QEMUDISPLAY_RDP_PATH, + ORG_QEMUDISPLAY_RDP_IFACE, + "SetCredentials", + args); +} diff --git a/src/qemu/qemu_rdp.h b/src/qemu/qemu_rdp.h new file mode 100644 index 0000000000..6af90b06d2 --- /dev/null +++ b/src/qemu/qemu_rdp.h @@ -0,0 +1,71 @@ +/* + * qemu_rdp.h: QEMU RDP support + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "qemu_conf.h" +#include "virbitmap.h" +#include "virenum.h" + +typedef enum { + QEMU_RDP_FEATURE_NONE = 0, + + QEMU_RDP_FEATURE_DBUS_ADDRESS, + QEMU_RDP_FEATURE_REMOTEFX, + QEMU_RDP_FEATURE_LAST, +} qemuRdpFeature; + +VIR_ENUM_DECL(qemuRdpFeature); + +typedef struct _qemuRdp qemuRdp; +struct _qemuRdp { + int fd[2]; + virBitmap *features; + pid_t pid; + guint name_watch; + bool name_appeared; + guint leaving_id; +}; + +qemuRdp *qemuRdpNew(void); + +qemuRdp *qemuRdpNewForHelper(const char *helper); + +void qemuRdpFree(qemuRdp *rdp); + +void qemuRdpSetFeature(qemuRdp *rdp, + qemuRdpFeature feature); + +bool qemuRdpHasFeature(const qemuRdp *rdp, + qemuRdpFeature feature); + +int qemuRdpStart(virDomainObj *vm, + virDomainGraphicsDef *gfx); + +void qemuRdpStop(virDomainObj *vm, + virDomainGraphicsDef *gfx); + +int qemuRdpSetupCgroup(qemuRdp *rdp, + virCgroup *cgroup); + +int qemuRdpSetCredentials(virDomainObj *vm, + const char *username, + const char *password, + const char *domain); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuRdp, qemuRdpFree); -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:39PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Helpers to start the qemu-rdp server and set it up.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- po/POTFILES | 1 + src/qemu/meson.build | 1 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 2 + src/qemu/qemu_rdp.c | 427 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_rdp.h | 71 +++++++ 6 files changed, 503 insertions(+) create mode 100644 src/qemu/qemu_rdp.c create mode 100644 src/qemu/qemu_rdp.h
snip
+ logpath = qemuRdpCreateLogFilename(cfg, vm->def); + + if (cfg->stdioLogD) { + g_autoptr(virLogManager) logManager = virLogManagerNew(driver->privileged); + + if (!logManager) + goto error; + + if ((logfd = virLogManagerDomainOpenLogFile(logManager, + "qemu", + vm->def->uuid, + vm->def->name, + logpath, + 0, + NULL, NULL)) < 0) + goto error; + } else { + if ((logfd = open(logpath, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)) < 0) { + virReportSystemError(errno, _("failed to create logfile %1$s"), + logpath); + goto error; + } + if (virSetCloseExec(logfd) < 0) { + virReportSystemError(errno, _("failed to set close-on-exec flag on %1$s"), + logpath); + goto error; + } + }
These 30 lines are repeated in the dbus helper, and likewise the virtiofs helper. This ought to be spun out to a helper API we can call like: if (!(logfd = qemuHelperOpenLogFile(driver, vm, "rdp")) goto error
+ + cmd = virCommandNew(cfg->qemuRdpName); + virCommandClearCaps(cmd); + virCommandSetPidFile(cmd, pidfile); + virCommandSetOutputFD(cmd, &logfd); + virCommandSetErrorFD(cmd, &logfd); + virCommandDaemonize(cmd); + if (dbus_addr) {
If 'dbus_addr' is NULL, AFAICT an error has been reported and we should be propagating that as a fatal error scenario.
+ if (!qemuRdpHasFeature(rdp, QEMU_RDP_FEATURE_DBUS_ADDRESS)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu-rdp doesn't support custom D-Bus address"));
Missing 'return -1', but...
+ }
...this ought be checked when we're probing capabilities for RDP support probably.
+ virCommandAddArgPair(cmd, "--dbus-address", dbus_addr); + } + virCommandAddArg(cmd, "serve"); + + virCommandAddArg(cmd, "--bind-address"); + virCommandAddArgBuffer(cmd, &bind_addr); + + certpath = g_build_filename(cfg->rdpTLSx509certdir, "server-cert.pem", NULL); + keypath = g_build_filename(cfg->rdpTLSx509certdir, "server-key.pem", NULL); + virCommandAddArgPair(cmd, "--cert", certpath); + virCommandAddArgPair(cmd, "--key", keypath); + + if (qemuExtDeviceLogCommand(driver, vm, cmd, "qemu-rdp") < 0) + return -1; + + rdp->name_watch = g_bus_watch_name_on_connection(priv->dbusConnection, + ORG_QEMUDISPLAY_RDP, + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_cb, + name_vanished_cb, + rdp, + NULL); + + if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, false, NULL) < 0) + goto error; + + if (virPidFileReadPath(pidfile, &pid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to read qemu-rdp pidfile '%1$s'"), + pidfile); + goto error; + } + + if (virProcessKill(pid, 0) != 0) { + virReportSystemError(errno, "%s", + _("qemu-rdp died unexpectedly")); + goto error; + } + + rdp->pid = pid; + + return 0; + + error: + g_clear_handle_id(&rdp->name_watch, g_bus_unwatch_name); + qemuRdpStop(vm, gfx); + return -1; +} + + +int +qemuRdpSetCredentials(virDomainObj *vm, + const char *username, + const char *password, + const char *domain) +{ + qemuDomainObjPrivate *priv = vm->privateData; + g_autoptr(GVariant) args = NULL; + + args = g_variant_new("(sss)", username, password, domain); + + return virGDBusCallMethod(priv->dbusConnection, + NULL, + G_VARIANT_TYPE("()"), + NULL, + ORG_QEMUDISPLAY_RDP, + ORG_QEMUDISPLAY_RDP_PATH, + ORG_QEMUDISPLAY_RDP_IFACE, + "SetCredentials", + args); +} diff --git a/src/qemu/qemu_rdp.h b/src/qemu/qemu_rdp.h new file mode 100644 index 0000000000..6af90b06d2 --- /dev/null +++ b/src/qemu/qemu_rdp.h @@ -0,0 +1,71 @@ +/* + * qemu_rdp.h: QEMU RDP support + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "qemu_conf.h" +#include "virbitmap.h" +#include "virenum.h" + +typedef enum { + QEMU_RDP_FEATURE_NONE = 0, + + QEMU_RDP_FEATURE_DBUS_ADDRESS, + QEMU_RDP_FEATURE_REMOTEFX, + QEMU_RDP_FEATURE_LAST, +} qemuRdpFeature; + +VIR_ENUM_DECL(qemuRdpFeature); + +typedef struct _qemuRdp qemuRdp; +struct _qemuRdp { + int fd[2]; + virBitmap *features; + pid_t pid; + guint name_watch; + bool name_appeared; + guint leaving_id; +}; + +qemuRdp *qemuRdpNew(void); + +qemuRdp *qemuRdpNewForHelper(const char *helper); + +void qemuRdpFree(qemuRdp *rdp); + +void qemuRdpSetFeature(qemuRdp *rdp, + qemuRdpFeature feature); + +bool qemuRdpHasFeature(const qemuRdp *rdp, + qemuRdpFeature feature); + +int qemuRdpStart(virDomainObj *vm, + virDomainGraphicsDef *gfx); + +void qemuRdpStop(virDomainObj *vm, + virDomainGraphicsDef *gfx); + +int qemuRdpSetupCgroup(qemuRdp *rdp, + virCgroup *cgroup); + +int qemuRdpSetCredentials(virDomainObj *vm, + const char *username, + const char *password, + const char *domain); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuRdp, qemuRdpFree); -- 2.47.0
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Wire the external server RDP support with QEMU. Check the configuration, allocate a port, start the process and set the credentials. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 25 ++++-- src/conf/domain_conf.h | 1 + src/qemu/qemu_extdevice.c | 46 +++++++++-- src/qemu/qemu_hotplug.c | 49 ++++++++++- src/qemu/qemu_hotplug.h | 1 + src/qemu/qemu_process.c | 167 ++++++++++++++++++++++++++++++++++---- 6 files changed, 257 insertions(+), 32 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 785b51bb4e..358feb2625 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -6410,7 +6410,7 @@ interaction with the admin. <graphics type='vnc' port='5904' sharePolicy='allow-exclusive'> <listen type='address' address='1.2.3.4'/> </graphics> - <graphics type='rdp' autoport='yes' multiUser='yes' /> + <graphics type='rdp' autoport='yes' multiUser='yes'/> <graphics type='desktop' fullscreen='yes'/> <graphics type='spice'> <listen type='network' network='rednet'/> @@ -6573,14 +6573,21 @@ interaction with the admin. Starts a RDP server. The ``port`` attribute specifies the TCP port number (with -1 as legacy syntax indicating that it should be auto-allocated). The ``autoport`` attribute is the new preferred syntax for indicating - auto-allocation of the TCP port to use. In the VirtualBox driver, the - ``autoport`` will make the hypervisor pick available port from 3389-3689 - range when the VM is started. The chosen port will be reflected in the - ``port`` attribute. The ``multiUser`` attribute is a boolean deciding - whether multiple simultaneous connections to the VM are permitted. The - ``replaceUser`` attribute is a boolean deciding whether the existing - connection must be dropped and a new connection must be established by the - VRDP server, when a new client connects in single connection mode. + auto-allocation of the TCP port to use. + + A ``dbus`` graphics is also required to enable the QEMU RDP support, which + uses an external "qemu-rdp" helper process. The ``username`` and + ``passwd`` attributes set the credentials (when they are not set, the RDP + access may be disabled by the helper). :since:`Since 11.1.0` + + In the VirtualBox driver, the ``autoport`` will make the hypervisor pick + available port from 3389-3689 range when the VM is started. The chosen + port will be reflected in the ``port`` attribute. The ``multiUser`` + attribute is a boolean deciding whether multiple simultaneous connections + to the VM are permitted. The ``replaceUser`` attribute is a boolean + deciding whether the existing connection must be dropped and a new + connection must be established by the VRDP server, when a new client + connects in single connection mode. ``desktop`` This value is reserved for VirtualBox domains for the moment. It displays diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index b08e2549b7..ff05920030 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2025,6 +2025,7 @@ struct _virDomainGraphicsDef { } sdl; struct { int port; + bool portReserved; bool autoport; bool replaceUser; bool multiUser; diff --git a/src/qemu/qemu_extdevice.c b/src/qemu/qemu_extdevice.c index 954cb323a4..e0c9cd1d91 100644 --- a/src/qemu/qemu_extdevice.c +++ b/src/qemu/qemu_extdevice.c @@ -236,14 +236,28 @@ qemuExtDevicesStart(virQEMUDriver *driver, for (i = 0; i < def->ngraphics; i++) { virDomainGraphicsDef *graphics = def->graphics[i]; - if (graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_DBUS) + switch (graphics->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: { + if (graphics->data.dbus.p2p || graphics->data.dbus.fromConfig) + continue; + if (qemuDBusStart(driver, vm) < 0) + return -1; continue; - - if (graphics->data.dbus.p2p || graphics->data.dbus.fromConfig) + } + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: { + if (qemuRdpStart(vm, graphics) < 0) + return -1; continue; - - if (qemuDBusStart(driver, vm) < 0) - return -1; + } + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + default: + continue; + } } for (i = 0; i < def->ndisks; i++) { @@ -309,6 +323,26 @@ qemuExtDevicesStop(virQEMUDriver *driver, qemuVirtioFSStop(driver, vm, fs); } + for (i = 0; i < def->ngraphics; i++) { + virDomainGraphicsDef *graphics = def->graphics[i]; + + switch (graphics->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: { + qemuRdpStop(vm, graphics); + continue; + } + case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + default: + continue; + } + } + for (i = 0; i < def->ndisks; i++) { virDomainDiskDef *disk = def->disks[i]; qemuNbdkitStopStorageSource(disk->src, vm, true); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 5a7e6c3b12..38d21642fe 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4410,6 +4410,7 @@ int qemuDomainChangeGraphicsPasswords(virDomainObj *vm, int type, virDomainGraphicsAuthDef *auth, + const char *defaultUsername, const char *defaultPasswd, int asyncJob) { @@ -4419,13 +4420,19 @@ qemuDomainChangeGraphicsPasswords(virDomainObj *vm, g_autofree char *validTo = NULL; const char *connected = NULL; const char *password; + const char *username; int ret = -1; if (!auth->passwd && !defaultPasswd) return 0; + username = auth->username ? auth->username : defaultUsername; password = auth->passwd ? auth->passwd : defaultPasswd; + if (type == VIR_DOMAIN_GRAPHICS_TYPE_RDP) { + return qemuRdpSetCredentials(vm, username, password, ""); + } + if (auth->connected) connected = virDomainGraphicsAuthConnectedTypeToString(auth->connected); @@ -4555,6 +4562,7 @@ qemuDomainChangeGraphics(virQEMUDriver *driver, if (qemuDomainChangeGraphicsPasswords(vm, VIR_DOMAIN_GRAPHICS_TYPE_VNC, &dev->data.vnc.auth, + NULL, cfg->vncPassword, VIR_ASYNC_JOB_NONE) < 0) return -1; @@ -4602,6 +4610,7 @@ qemuDomainChangeGraphics(virQEMUDriver *driver, if (qemuDomainChangeGraphicsPasswords(vm, VIR_DOMAIN_GRAPHICS_TYPE_SPICE, &dev->data.spice.auth, + NULL, cfg->spicePassword, VIR_ASYNC_JOB_NONE) < 0) return -1; @@ -4617,8 +4626,46 @@ qemuDomainChangeGraphics(virQEMUDriver *driver, } break; - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if ((olddev->data.rdp.autoport != dev->data.rdp.autoport) || + (!dev->data.rdp.autoport && + (olddev->data.rdp.port != dev->data.rdp.port))) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cannot change port settings on rdp graphics")); + return -1; + } + + /* If a password lifetime was, or is set, or action if connected has + * changed, then we must always run, even if new password matches + * old password */ + if (olddev->data.rdp.auth.expires || + dev->data.rdp.auth.expires || + olddev->data.rdp.auth.connected != dev->data.rdp.auth.connected || + STRNEQ_NULLABLE(olddev->data.rdp.auth.username, + dev->data.rdp.auth.username) || + STRNEQ_NULLABLE(olddev->data.rdp.auth.passwd, + dev->data.rdp.auth.passwd)) { + VIR_DEBUG("Updating password on RDP server %p %p", + dev->data.rdp.auth.passwd, cfg->rdpPassword); + if (qemuDomainChangeGraphicsPasswords(vm, + VIR_DOMAIN_GRAPHICS_TYPE_RDP, + &dev->data.rdp.auth, + cfg->rdpUsername, + cfg->rdpPassword, + VIR_ASYNC_JOB_NONE) < 0) + return -1; + + /* Steal the new dev's char * reference */ + VIR_FREE(olddev->data.rdp.auth.username); + olddev->data.rdp.auth.username = g_steal_pointer(&dev->data.rdp.auth.username); + VIR_FREE(olddev->data.rdp.auth.passwd); + olddev->data.rdp.auth.passwd = g_steal_pointer(&dev->data.rdp.auth.passwd); + olddev->data.rdp.auth.validTo = dev->data.rdp.auth.validTo; + olddev->data.rdp.auth.expires = dev->data.rdp.auth.expires; + olddev->data.rdp.auth.connected = dev->data.rdp.auth.connected; + } + break; + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 4fe7f4923e..8108d96de9 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -51,6 +51,7 @@ int qemuDomainFindGraphicsIndex(virDomainDef *def, int qemuDomainChangeGraphicsPasswords(virDomainObj *vm, int type, virDomainGraphicsAuthDef *auth, + const char *defaultUsername, const char *defaultPasswd, int asyncJob); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 34a755a49a..c0dd9c0b35 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2949,18 +2949,29 @@ qemuProcessInitPasswords(virQEMUDriver *driver, for (i = 0; i < vm->def->ngraphics; ++i) { virDomainGraphicsDef *graphics = vm->def->graphics[i]; - if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { - ret = qemuDomainChangeGraphicsPasswords(vm, - VIR_DOMAIN_GRAPHICS_TYPE_VNC, - &graphics->data.vnc.auth, - cfg->vncPassword, - asyncJob); - } else if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { - ret = qemuDomainChangeGraphicsPasswords(vm, - VIR_DOMAIN_GRAPHICS_TYPE_SPICE, - &graphics->data.spice.auth, - cfg->spicePassword, - asyncJob); + + switch (graphics->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + ret = qemuDomainChangeGraphicsPasswords( + vm, VIR_DOMAIN_GRAPHICS_TYPE_VNC, &graphics->data.vnc.auth, NULL, + cfg->vncPassword, asyncJob); + break; + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + ret = qemuDomainChangeGraphicsPasswords( + vm, VIR_DOMAIN_GRAPHICS_TYPE_SPICE, &graphics->data.spice.auth, + NULL, cfg->spicePassword, asyncJob); + break; + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + ret = qemuDomainChangeGraphicsPasswords( + vm, VIR_DOMAIN_GRAPHICS_TYPE_RDP, &graphics->data.rdp.auth, + cfg->rdpUsername, cfg->rdpPassword, asyncJob); + break; + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: + case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: + case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: + case VIR_DOMAIN_GRAPHICS_TYPE_LAST: + break; } if (ret < 0) @@ -4239,6 +4250,30 @@ qemuProcessSPICEAllocatePorts(virQEMUDriver *driver, return 0; } +static int +qemuProcessRDPAllocatePorts(virQEMUDriver *driver, + virDomainGraphicsDef *graphics, + bool allocate) +{ + unsigned short port; + + if (!allocate) { + if (graphics->data.rdp.autoport) + graphics->data.rdp.port = 3389; + + return 0; + } + + if (graphics->data.rdp.autoport) { + if (virPortAllocatorAcquire(driver->rdpPorts, &port) < 0) + return -1; + graphics->data.rdp.port = port; + graphics->data.rdp.portReserved = true; + } + + return 0; +} + static int qemuProcessVerifyHypervFeatures(virDomainDef *def, @@ -4943,8 +4978,16 @@ qemuProcessGraphicsReservePorts(virDomainGraphicsDef *graphics, } break; - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if (!graphics->data.rdp.autoport || + reconnect) { + if (virPortAllocatorSetUsed(graphics->data.rdp.port) < 0) + return -1; + graphics->data.rdp.portReserved = true; + } + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: @@ -4983,8 +5026,12 @@ qemuProcessGraphicsAllocatePorts(virQEMUDriver *driver, return -1; break; - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if (qemuProcessRDPAllocatePorts(driver, graphics, allocate) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: @@ -5151,8 +5198,11 @@ qemuProcessGraphicsSetupListen(virQEMUDriver *driver, listenAddr = cfg->spiceListen; break; - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + listenAddr = cfg->rdpListen; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: @@ -5435,6 +5485,23 @@ qemuProcessStartWarnShmem(virDomainObj *vm) } +static bool +virDomainDefHasDBus(const virDomainDef *def, bool p2p) +{ + size_t i = 0; + + for (i = 0; i < def->ngraphics; i++) { + virDomainGraphicsDef *graphics = def->graphics[i]; + + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_DBUS) { + return graphics->data.dbus.p2p == p2p; + } + } + + return false; +} + + static int qemuProcessStartValidateGraphics(virDomainObj *vm) { @@ -5453,8 +5520,30 @@ qemuProcessStartValidateGraphics(virDomainObj *vm) } break; - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if (graphics->nListens > 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu-rdp does not support multiple listens for one graphics device.")); + return -1; + } + if (graphics->data.rdp.multiUser) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu-rdp doesn't support the 'multiUser' attribute.")); + return -1; + } + if (graphics->data.rdp.replaceUser) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu-rdp doesn't support the 'replaceUser' attribute.")); + return -1; + } + if (!virDomainDefHasDBus(vm->def, false)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu-rdp support requires a D-Bus bus graphics device.")); + return -1; + } + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: @@ -5939,6 +6028,42 @@ qemuProcessPrepareHostNetwork(virDomainObj *vm) return 0; } +#include "qemu_rdp.h" + +static int +qemuPrepareGraphicsRdp(virQEMUDriver *driver, + virDomainGraphicsDef *gfx) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + g_autoptr(qemuRdp) rdp = NULL; + + if (!(rdp = qemuRdpNewForHelper(cfg->qemuRdpName))) + return -1; + + QEMU_DOMAIN_GRAPHICS_PRIVATE(gfx)->rdp = g_steal_pointer(&rdp); + + return 0; +} + + +static int +qemuProcessPrepareGraphics(virDomainObj *vm) +{ + qemuDomainObjPrivate *priv = vm->privateData; + size_t i; + + for (i = 0; i < vm->def->ngraphics; i++) { + virDomainGraphicsDef *gfx = vm->def->graphics[i]; + + if (gfx->type == VIR_DOMAIN_GRAPHICS_TYPE_RDP && + qemuPrepareGraphicsRdp(priv->driver, gfx) < 0) + return -1; + + } + + return 0; +} + struct qemuProcessSetupVcpuSchedCoreHelperData { pid_t vcpupid; @@ -7410,6 +7535,10 @@ qemuProcessPrepareHost(virQEMUDriver *driver, if (qemuProcessPrepareHostNetwork(vm) < 0) return -1; + VIR_DEBUG("Preparing graphics"); + if (qemuProcessPrepareGraphics(vm) < 0) + return -1; + /* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); if (!cfg->relaxedACS) @@ -8982,6 +9111,12 @@ void qemuProcessStop(virQEMUDriver *driver, graphics->data.spice.tlsPortReserved = false; } } + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_RDP) { + if (graphics->data.rdp.portReserved) { + virPortAllocatorRelease(graphics->data.rdp.port); + graphics->data.rdp.portReserved = false; + } + } } for (i = 0; i < vm->ndeprecations; i++) -- 2.47.0

On Wed, Jan 29, 2025 at 05:40:40PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Wire the external server RDP support with QEMU.
Check the configuration, allocate a port, start the process and set the credentials.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 25 ++++-- src/conf/domain_conf.h | 1 + src/qemu/qemu_extdevice.c | 46 +++++++++-- src/qemu/qemu_hotplug.c | 49 ++++++++++- src/qemu/qemu_hotplug.h | 1 + src/qemu/qemu_process.c | 167 ++++++++++++++++++++++++++++++++++---- 6 files changed, 257 insertions(+), 32 deletions(-)
+ if (!virDomainDefHasDBus(vm->def, false)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu-rdp support requires a D-Bus bus graphics device.")); + return -1; + }
IMHO this is not the correct way to model it. Use of DBus is an internal impl detail of the RDP backend that is being configured. Users shouldn't have to know that - they should just be able to request 'RDP' alone for the <graphics> type. Consider if we wanted to provide a non-integrated VNC server for QEMU. We wouldn't do that by requiring use of a <graphics type=dbus> alongside the <graphics type=vnc>, as that config has the distinct meaning of requiring VNC and DBus in parallel. Where we have a choice of underlying implementations, we would typically have something like a <backend type="qemu|dbus"/> as a child element to select between them. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Hi On Wed, Jan 29, 2025 at 7:25 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Wed, Jan 29, 2025 at 05:40:40PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Wire the external server RDP support with QEMU.
Check the configuration, allocate a port, start the process and set the credentials.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 25 ++++-- src/conf/domain_conf.h | 1 + src/qemu/qemu_extdevice.c | 46 +++++++++-- src/qemu/qemu_hotplug.c | 49 ++++++++++- src/qemu/qemu_hotplug.h | 1 + src/qemu/qemu_process.c | 167 ++++++++++++++++++++++++++++++++++---- 6 files changed, 257 insertions(+), 32 deletions(-)
+ if (!virDomainDefHasDBus(vm->def, false)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu-rdp support requires a D-Bus bus graphics device.")); + return -1; + }
IMHO this is not the correct way to model it.
Use of DBus is an internal impl detail of the RDP backend that is being configured. Users shouldn't have to know that - they should just be able to request 'RDP' alone for the <graphics> type.
Consider if we wanted to provide a non-integrated VNC server for QEMU. We wouldn't do that by requiring use of a <graphics type=dbus> alongside the <graphics type=vnc>, as that config has the distinct meaning of requiring VNC and DBus in parallel.
Where we have a choice of underlying implementations, we would typically have something like a <backend type="qemu|dbus"/> as a child element to select between them.
I wish it would be an implementation detail. Unfortunately, the qemu devices needs to be configured to export themself on dbus too (chardev/usb/audio), this is also exposed on the domain XML. For the most simple case, ie display only, we could probably hide the dbus implementation detail. But in general, we probably better be explicit about it. Being explicit would also avoid surprises when checking the list of available displays (think domdisplay --all, or by inspecting the bus). I am not sure it's worth trying to hide it (for ex, should <redirdev bus='usb'> be exposed on dbus by default when dbus or rdp is enabled?). If people feel it's really best, I can give it a try, but I fear it's going to be more struggle and surprises without much benefit.

On Thu, Jan 30, 2025 at 04:42:24PM +0400, Marc-André Lureau wrote:
Hi
On Wed, Jan 29, 2025 at 7:25 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
On Wed, Jan 29, 2025 at 05:40:40PM +0400, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Wire the external server RDP support with QEMU.
Check the configuration, allocate a port, start the process and set the credentials.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- docs/formatdomain.rst | 25 ++++-- src/conf/domain_conf.h | 1 + src/qemu/qemu_extdevice.c | 46 +++++++++-- src/qemu/qemu_hotplug.c | 49 ++++++++++- src/qemu/qemu_hotplug.h | 1 + src/qemu/qemu_process.c | 167 ++++++++++++++++++++++++++++++++++---- 6 files changed, 257 insertions(+), 32 deletions(-)
+ if (!virDomainDefHasDBus(vm->def, false)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu-rdp support requires a D-Bus bus graphics device.")); + return -1; + }
IMHO this is not the correct way to model it.
Use of DBus is an internal impl detail of the RDP backend that is being configured. Users shouldn't have to know that - they should just be able to request 'RDP' alone for the <graphics> type.
Consider if we wanted to provide a non-integrated VNC server for QEMU. We wouldn't do that by requiring use of a <graphics type=dbus> alongside the <graphics type=vnc>, as that config has the distinct meaning of requiring VNC and DBus in parallel.
Where we have a choice of underlying implementations, we would typically have something like a <backend type="qemu|dbus"/> as a child element to select between them.
I wish it would be an implementation detail. Unfortunately, the qemu devices needs to be configured to export themself on dbus too (chardev/usb/audio), this is also exposed on the domain XML. For the most simple case, ie display only, we could probably hide the dbus implementation detail. But in general, we probably better be explicit about it. Being explicit would also avoid surprises when checking the list of available displays (think domdisplay --all, or by inspecting the bus). I am not sure it's worth trying to hide it (for ex, should <redirdev bus='usb'> be exposed on dbus by default when dbus or rdp is enabled?). If people feel it's really best, I can give it a try, but I fear it's going to be more struggle and surprises without much benefit.
Hmm, yeah, that's awkward really, so guess we can't do much better than having dual <graphics> tags :-( With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- .../graphics-rdp.x86_64-latest.args | 35 +++++++++++++++ .../graphics-rdp.x86_64-latest.xml | 1 + tests/qemuxmlconfdata/graphics-rdp.xml | 43 +++++++++++++++++++ tests/qemuxmlconftest.c | 2 + 4 files changed, 81 insertions(+) create mode 100644 tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.args create mode 120000 tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/graphics-rdp.xml diff --git a/tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.args b/tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.args new file mode 100644 index 0000000000..292f283493 --- /dev/null +++ b/tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.args @@ -0,0 +1,35 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=QEMUGuest1,debug-threads=on \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \ +-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \ +-accel tcg \ +-cpu qemu64 \ +-m size=219136k \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \ +-overcommit mem-lock=off \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-boot strict=on \ +-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \ +-audiodev '{"id":"audio1","driver":"dbus"}' \ +-display dbus,addr=unix:path=/var/run/libvirt/qemu/dbus/-1-QEMUGuest1-dbus.sock \ +-device '{"driver":"cirrus-vga","id":"video0","bus":"pci.0","addr":"0x2"}' \ +-chardev dbus,id=charredir0,name=org.qemu.usbredir \ +-device '{"driver":"usb-redir","chardev":"charredir0","id":"redir0","bus":"usb.0","port":"1"}' \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.xml b/tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.xml new file mode 120000 index 0000000000..7bb46ae7c4 --- /dev/null +++ b/tests/qemuxmlconfdata/graphics-rdp.x86_64-latest.xml @@ -0,0 +1 @@ +graphics-rdp.xml \ No newline at end of file diff --git a/tests/qemuxmlconfdata/graphics-rdp.xml b/tests/qemuxmlconfdata/graphics-rdp.xml new file mode 100644 index 0000000000..7d65320f41 --- /dev/null +++ b/tests/qemuxmlconfdata/graphics-rdp.xml @@ -0,0 +1,43 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='usb' index='0' model='piix3-uhci'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <graphics type='dbus'/> + <graphics type='rdp' port='0' autoport='yes' username='user' passwd='pass'> + <listen type='address'/> + </graphics> + <audio id='1' type='dbus'/> + <video> + <model type='cirrus' vram='16384' heads='1' primary='yes'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </video> + <redirdev bus='usb' type='dbus'> + <source channel='org.qemu.usbredir'/> + </redirdev> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index 34d38f2c4b..e063b8a4fc 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -1751,6 +1751,8 @@ mymain(void) DO_TEST_CAPS_LATEST("graphics-dbus-chardev"); DO_TEST_CAPS_LATEST("graphics-dbus-usbredir"); + DO_TEST_CAPS_LATEST("graphics-rdp"); + DO_TEST_CAPS_LATEST("input-usbmouse"); DO_TEST_CAPS_LATEST("input-usbtablet"); -- 2.47.0

On Wed, Jan 29, 2025 at 2:41 PM <marcandre.lureau@redhat.com> wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Hi,
This patch series offers an out-of-process Remote Desktop Protocol (RDP) server solution utilizing QEMU's -display dbus interface, offering improved modularity and potential security benefits compared to built-in server.
This initiative was spearheaded by Mihnea Buzatu during the QEMU Summer of Code 2023. The project's goal was to develop an out-of-process RDP server using the -display dbus interface, implemented in Rust. Given that the IronRDP crate lacked some server support at the time, investments in IronRDP were required.
I finally released an initial v0.1 version of qemu-rdp on crates.io (https://crates.io/crates/qemu-rdp). That should allow more people to review and evaluate the state of this work.
On unix systems, with cargo/rust toolchain installed, it should be as easy as running "cargo install qemu-rdp", apply this patch series for libvirt, set the "rdp_tls_x509_cert_dir" location for your TLS certificates, and configure a VM with both dbus & rdp graphics (run "virsh domdisplay DOMAIN" to get the display connection details).
Thanks for the reviews & feedback!
Heh, it's been nearly nine years since RDPMux, and it looks like the way that RDP was implemented this go around is more or less similar to the architecture done there, just this time with Rust instead of C++. Nice to know the fundamental design idea still works. :) I'm pleased to finally see this happen, though I still lament the sad state of RDP clients on Linux... -- 真実はいつも一つ!/ Always, there's only one truth!
participants (4)
-
Daniel P. Berrangé
-
Marc-André Lureau
-
marcandre.lureau@redhat.com
-
Neal Gompa