[PATCH v2 00/21] 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! v2: thanks to Daniel review - drop extra error report from "qemu: report an error for unsupported graphics" - replace g_return pre-conditions with ATTRIBUTE_NONNULL - improve "qemu/dbus: keep a connection to the VM D-Bus" to also reconnect - use domainLogContext for logging (for virtiofs as well) - check for qemu-rdp availabilty for setting 'rdp' capability - make dbus-addr qemu-rdp capability mandatory - rebased - add r-b tags Marc-André Lureau (21): build-sys: drop -Winline when optimization=g build: fix -Werror=maybe-uninitialized qemu-slirp: drop unneeded check for OOM util: annotate non-null arguments for virGDBusCallMethod() qemu: fall-through 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/virtiofs: use domainLogContext qemu/dbus: keep a connection to the VM D-Bus qemu/dbus: log daemon stdout/err, use domainLogContext qemu: validate RDP configuration qemu: add qemu-rdp helper unit qemu: pass virQEMUDriverConfig to capabilities qemu: add 'rdp' capability if qemu-rdp is available qemu: add RDP support tests: add qemu <graphics type='rdp'/> test docs/formatdomain.rst | 25 +- meson.build | 7 +- 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 | 24 +- src/qemu/qemu_capabilities.h | 12 +- src/qemu/qemu_command.c | 9 +- src/qemu/qemu_conf.c | 56 ++- src/qemu/qemu_conf.h | 13 + src/qemu/qemu_dbus.c | 69 ++- src/qemu/qemu_dbus.h | 3 + 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 | 170 ++++++- src/qemu/qemu_rdp.c | 416 ++++++++++++++++++ src/qemu/qemu_rdp.h | 73 +++ src/qemu/qemu_slirp.c | 6 - src/qemu/qemu_validate.c | 48 +- src/qemu/qemu_virtiofs.c | 53 +-- src/qemu/test_libvirtd_qemu.aug.in | 5 + src/util/virgdbus.h | 13 +- .../domaincapsdata/qemu_10.0.0-q35.x86_64.xml | 1 + .../domaincapsdata/qemu_10.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_10.0.0.s390x.xml | 1 + tests/domaincapsdata/qemu_10.0.0.x86_64.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 + tests/domaincapstest.c | 7 +- .../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 | 10 + tools/nss/libvirt_nss_leases.c | 2 +- tools/nss/libvirt_nss_macs.c | 2 +- 85 files changed, 1218 insertions(+), 138 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 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index c7e5947d10..5bfd37e44d 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', @@ -444,6 +443,12 @@ cc_flags += [ '-Wwrite-strings', ] +if get_option('optimization') != 'g' + # Seemingly insignificant changes in the source program can cause the warnings + # produced by -Winline to appear or disappear. + cc_flags += [ '-Winline' ] +endif + if cc.get_id() == 'clang' # Stop CLang from doing inter-procedural analysis of calls # between functions in the same compilation unit. Such an -- 2.47.0

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> Reviewed-by: Daniel P. Berrangé <berrange@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

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> Reviewed-by: Daniel P. Berrangé <berrange@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

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.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/util/virgdbus.h b/src/util/virgdbus.h index dfe6138112..65e7ba7be4 100644 --- a/src/util/virgdbus.h +++ b/src/util/virgdbus.h @@ -54,7 +54,11 @@ virGDBusCallMethod(GDBusConnection *conn, const char *objectPath, const char *ifaceName, const char *method, - GVariant *data); + GVariant *data) + ATTRIBUTE_NONNULL(1) + ATTRIBUTE_NONNULL(6) + ATTRIBUTE_NONNULL(7) + ATTRIBUTE_NONNULL(8); int virGDBusCallMethodWithFD(GDBusConnection *conn, @@ -67,7 +71,12 @@ virGDBusCallMethodWithFD(GDBusConnection *conn, const char *ifaceName, const char *method, GVariant *data, - GUnixFDList *dataFD); + GUnixFDList *dataFD) + ATTRIBUTE_NONNULL(1) + ATTRIBUTE_NONNULL(7) + ATTRIBUTE_NONNULL(8) + ATTRIBUTE_NONNULL(9); + int virGDBusIsServiceEnabled(const char *name); -- 2.47.0

From: Marc-André Lureau <marcandre.lureau@redhat.com> Without an error message, it can be tedious to figure out failure to start, just fall-through the generic range error. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 54130ac4f0..772e98fbb4 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8448,7 +8448,6 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: - return -1; case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: virReportEnumRangeError(virDomainGraphicsType, graphics->type); -- 2.47.0

On Tue, Feb 18, 2025 at 02:16:10PM +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, just fall-through the generic range error.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 54130ac4f0..772e98fbb4 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8448,7 +8448,6 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: - return -1;
You're right that we're missing an error message here, but
case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: virReportEnumRangeError(virDomainGraphicsType, graphics->type);
This reports an error that corresponds to a completely invalid value provided in the internal code as an error in the code. I would be fine with it if the validation function qemuValidateDomainDeviceDefGraphics() threw an error for anyone requesting such an unsupported configuration, but that does not. We should either add it there or just add a proper error message here. It is not possible to trigger now unless someone is adding a previously unsupported graphics type, but still, would be nice to lead by an example.
-- 2.47.0

Hi On Fri, Mar 7, 2025 at 3:58 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Tue, Feb 18, 2025 at 02:16:10PM +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, just fall-through the generic range error.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 54130ac4f0..772e98fbb4 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8448,7 +8448,6 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: - return -1;
You're right that we're missing an error message here, but
case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: virReportEnumRangeError(virDomainGraphicsType, graphics->type);
This reports an error that corresponds to a completely invalid value provided in the internal code as an error in the code. I would be fine with it if the validation function qemuValidateDomainDeviceDefGraphics() threw an error for anyone requesting such an unsupported configuration, but that does not. We should either add it there or just add a proper error message here. It is not possible to trigger now unless someone is adding a previously unsupported graphics type, but still, would be nice to lead by an example.
I'll drop this patch.

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@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 b73dda7e10..c6e75d572d 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 97214f72d0..33597bba08 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 1d0da1028f..091b20129e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -648,6 +648,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, @@ -793,6 +798,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 44011c2b36..ad0c43ccdf 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

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@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 642093c40b..0288c2895b 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" @@ -103,6 +108,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" @@ -156,6 +162,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 31172303dc..9354e960f1 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 @@ -923,6 +948,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 c6e75d572d..746eaf0347 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, @@ -691,6 +715,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) @@ -1161,6 +1188,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; @@ -1248,6 +1278,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, @@ -1333,6 +1371,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 33597bba08..f161c4297a 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 c2a1d7d829..8ef26a9da1 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" } @@ -111,6 +115,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 ad0c43ccdf..8974882bda 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

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> Reviewed-by: Daniel P. Berrangé <berrange@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 49555efc56..81634134a7 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 */ @@ -11286,6 +11287,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; @@ -11675,6 +11678,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; } @@ -26315,6 +26322,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); @@ -26544,6 +26555,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 9da6586e66..b9c8031424 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 3328a63205..300ab21c57 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -4546,6 +4546,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

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> Reviewed-by: Daniel P. Berrangé <berrange@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 81634134a7..fde042a206 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -31779,15 +31779,24 @@ virDomainObjGetMessages(virDomainObj *vm, } + +/** + * 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 b9c8031424..eabb31d463 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -4601,4 +4601,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 30a9f806f0..2f20a124b3 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 3e3e368da3..3dc09ca5e0 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -2126,7 +2126,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)); @@ -4695,7 +4695,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

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@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 3dc09ca5e0..da3321849c 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4448,8 +4448,6 @@ qemuValidateDomainDeviceDefGraphics(const virDomainGraphicsDef *graphics, virQEMUCaps *qemuCaps) { virDomainCapsDeviceGraphics graphicsCaps = { 0 }; - bool have_egl_headless = false; - size_t i; virQEMUCapsFillDomainDeviceGraphicsCaps(qemuCaps, &graphicsCaps); @@ -4460,18 +4458,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

From: Marc-André Lureau <marcandre.lureau@redhat.com> RDP server uses port 3389 by default. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@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 746eaf0347..25cadf520d 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 f161c4297a..ccba88f7eb 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; @@ -312,6 +315,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 091b20129e..2ac76709cb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -701,6 +701,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, @@ -1050,6 +1057,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

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 772e98fbb4..4e29e841f9 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8447,6 +8447,7 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_LAST: default: @@ -9998,6 +9999,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 @@ -10046,15 +10048,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

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_virtiofs.c | 53 +++++++++------------------------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/src/qemu/qemu_virtiofs.c b/src/qemu/qemu_virtiofs.c index dd3e0dd9fe..aa282024a4 100644 --- a/src/qemu/qemu_virtiofs.c +++ b/src/qemu/qemu_virtiofs.c @@ -75,19 +75,6 @@ qemuVirtioFSCreateSocketFilename(virDomainObj *vm, } -static char * -qemuVirtioFSCreateLogFilename(virQEMUDriverConfig *cfg, - const virDomainDef *def, - const char *alias) -{ - g_autofree char *name = NULL; - - name = g_strdup_printf("%s-%s", def->name, alias); - - return virFileBuildPath(cfg->logDir, name, "-virtiofsd.log"); -} - - static int qemuVirtioFSOpenChardev(virQEMUDriver *driver, virDomainObj *vm, @@ -244,10 +231,11 @@ qemuVirtioFSStart(virQEMUDriver *driver, g_autoptr(virCommand) cmd = NULL; g_autofree char *socket_path = NULL; g_autofree char *pidfile = NULL; - g_autofree char *logpath = NULL; + g_autofree char *logname = NULL; pid_t pid = (pid_t) -1; VIR_AUTOCLOSE fd = -1; - VIR_AUTOCLOSE logfd = -1; + int logfd = -1; + g_autoptr(domainLogContext) logContext = NULL; int rc; if (!virFileIsExecutable(fs->binary)) { @@ -272,35 +260,18 @@ qemuVirtioFSStart(virQEMUDriver *driver, if ((fd = qemuVirtioFSOpenChardev(driver, vm, socket_path)) < 0) goto error; - logpath = qemuVirtioFSCreateLogFilename(cfg, vm->def, fs->info.alias); - - 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; - } + logname = g_strdup_printf("%s-%s-virtiofsd", vm->def->name, fs->info.alias); + if (!(logContext = domainLogContextNew(cfg->stdioLogD, cfg->logDir, + QEMU_DRIVER_NAME, + vm, driver->privileged, + logname))) { + virLastErrorPrefixMessage("%s", _("can't open log context")); + goto error; } + logfd = domainLogContextGetWriteFD(logContext); + if (!(cmd = qemuVirtioFSBuildCommandLine(cfg, fs, &fd))) goto error; -- 2.47.0

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 | 35 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_dbus.h | 3 +++ src/qemu/qemu_domain.h | 2 ++ src/qemu/qemu_process.c | 3 +++ 4 files changed, 43 insertions(+) diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index 06b655d870..c9e99ea27b 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c @@ -84,6 +84,36 @@ qemuDBusGetAddress(virQEMUDriver *driver, } +bool +qemuDBusConnect(virQEMUDriver *driver, + virDomainObj *vm) +{ + qemuDomainObjPrivate *priv = vm->privateData; + g_autoptr(GError) gerr = NULL; + g_autofree char *address = NULL; + + if (priv->dbusConnection) + return true; + + address = qemuDBusGetAddress(driver, vm); + if (!address) + return false; + + 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); + return false; + } + + return true; +} + + static int qemuDBusWriteConfig(const char *filename, const char *path) { @@ -140,6 +170,8 @@ qemuDBusStop(virQEMUDriver *driver, } else { priv->dbusDaemonRunning = false; } + + g_clear_object(&priv->dbusConnection); } @@ -264,6 +296,9 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, sockpath, false) < 0) goto cleanup; + if (!qemuDBusConnect(driver, vm)) + goto cleanup; + priv->dbusDaemonRunning = true; ret = 0; cleanup: diff --git a/src/qemu/qemu_dbus.h b/src/qemu/qemu_dbus.h index b27f38a591..2d97c6df2d 100644 --- a/src/qemu/qemu_dbus.h +++ b/src/qemu/qemu_dbus.h @@ -24,6 +24,9 @@ char *qemuDBusGetAddress(virQEMUDriver *driver, virDomainObj *vm); +bool qemuDBusConnect(virQEMUDriver *driver, + virDomainObj *vm); + int qemuDBusStart(virQEMUDriver *driver, virDomainObj *vm); 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 */ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 0d9b8bcb93..30d6560d52 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -9456,6 +9456,9 @@ qemuProcessReconnect(void *opaque) if (qemuDomainObjStartWorker(obj) < 0) goto error; + if (priv->dbusDaemonRunning && !qemuDBusConnect(driver, obj)) + goto error; + VIR_DEBUG("Reconnect monitor to def=%p name='%s'", obj, obj->def->name); tryMonReconn = true; -- 2.47.0

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 | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index c9e99ea27b..a9e2fb0fe2 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c @@ -24,6 +24,7 @@ #include "virlog.h" #include "virtime.h" #include "virpidfile.h" +#include "virutil.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -212,9 +213,12 @@ qemuDBusStart(virQEMUDriver *driver, g_autofree char *pidfile = NULL; g_autofree char *configfile = NULL; g_autofree char *sockpath = NULL; + g_autofree char *logpath = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 500 * 1000; /* ms */ - VIR_AUTOCLOSE errfd = -1; + int logfd = -1; + g_autoptr(domainLogContext) logContext = NULL; + pid_t cpid = -1; int ret = -1; @@ -246,10 +250,21 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, configfile, false) < 0) goto cleanup; + if (!(logContext = domainLogContextNew(cfg->stdioLogD, cfg->dbusStateDir, + QEMU_DRIVER_NAME, + vm, driver->privileged, + shortName))) { + virLastErrorPrefixMessage("%s", _("can't open log context")); + goto cleanup; + } + + logfd = domainLogContextGetWriteFD(logContext); + 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); @@ -266,7 +281,7 @@ qemuDBusStart(virQEMUDriver *driver, if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) goto cleanup; while (virTimeBackOffWait(&timebackoff)) { - char errbuf[1024] = { 0 }; + g_autofree char *msg = NULL; if (virFileExists(sockpath)) break; @@ -274,15 +289,12 @@ qemuDBusStart(virQEMUDriver *driver, 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); - } + if (domainLogContextReadFiltered(logContext, &msg, 1024) < 0) + VIR_WARN("Unable to read from dbus-daemon log"); + virReportError(VIR_ERR_OPERATION_FAILED, + _("dbus-daemon died and reported:\n%1$s"), + NULLSTR(msg)); goto cleanup; } -- 2.47.0

On Tue, Feb 18, 2025 at 02:16:20PM +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 | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-)
diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index c9e99ea27b..a9e2fb0fe2 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c @@ -24,6 +24,7 @@ #include "virlog.h" #include "virtime.h" #include "virpidfile.h" +#include "virutil.h"
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -212,9 +213,12 @@ qemuDBusStart(virQEMUDriver *driver, g_autofree char *pidfile = NULL; g_autofree char *configfile = NULL; g_autofree char *sockpath = NULL; + g_autofree char *logpath = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 500 * 1000; /* ms */ - VIR_AUTOCLOSE errfd = -1; + int logfd = -1; + g_autoptr(domainLogContext) logContext = NULL; + pid_t cpid = -1; int ret = -1;
@@ -246,10 +250,21 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, configfile, false) < 0) goto cleanup;
+ if (!(logContext = domainLogContextNew(cfg->stdioLogD, cfg->dbusStateDir, + QEMU_DRIVER_NAME, + vm, driver->privileged, + shortName))) { + virLastErrorPrefixMessage("%s", _("can't open log context")); + goto cleanup; + } + + logfd = domainLogContextGetWriteFD(logContext); + 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);
@@ -266,7 +281,7 @@ qemuDBusStart(virQEMUDriver *driver, if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) goto cleanup; while (virTimeBackOffWait(&timebackoff)) { - char errbuf[1024] = { 0 }; + g_autofree char *msg = NULL;
if (virFileExists(sockpath)) break; @@ -274,15 +289,12 @@ qemuDBusStart(virQEMUDriver *driver, 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); - } + if (domainLogContextReadFiltered(logContext, &msg, 1024) < 0) + VIR_WARN("Unable to read from dbus-daemon log");
+ virReportError(VIR_ERR_OPERATION_FAILED, + _("dbus-daemon died and reported:\n%1$s"), + NULLSTR(msg));
Are the errors usually multiline? If not, then I'd skip the newline in the translation string.
goto cleanup; }
-- 2.47.0

Hi On Fri, Mar 7, 2025 at 6:11 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Tue, Feb 18, 2025 at 02:16:20PM +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 | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-)
diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index c9e99ea27b..a9e2fb0fe2 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c @@ -24,6 +24,7 @@ #include "virlog.h" #include "virtime.h" #include "virpidfile.h" +#include "virutil.h"
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -212,9 +213,12 @@ qemuDBusStart(virQEMUDriver *driver, g_autofree char *pidfile = NULL; g_autofree char *configfile = NULL; g_autofree char *sockpath = NULL; + g_autofree char *logpath = NULL; virTimeBackOffVar timebackoff; const unsigned long long timeout = 500 * 1000; /* ms */ - VIR_AUTOCLOSE errfd = -1; + int logfd = -1; + g_autoptr(domainLogContext) logContext = NULL; + pid_t cpid = -1; int ret = -1;
@@ -246,10 +250,21 @@ qemuDBusStart(virQEMUDriver *driver, if (qemuSecurityDomainSetPathLabel(driver, vm, configfile, false) < 0) goto cleanup;
+ if (!(logContext = domainLogContextNew(cfg->stdioLogD, cfg->dbusStateDir, + QEMU_DRIVER_NAME, + vm, driver->privileged, + shortName))) { + virLastErrorPrefixMessage("%s", _("can't open log context")); + goto cleanup; + } + + logfd = domainLogContextGetWriteFD(logContext); + 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);
@@ -266,7 +281,7 @@ qemuDBusStart(virQEMUDriver *driver, if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) goto cleanup; while (virTimeBackOffWait(&timebackoff)) { - char errbuf[1024] = { 0 }; + g_autofree char *msg = NULL;
if (virFileExists(sockpath)) break; @@ -274,15 +289,12 @@ qemuDBusStart(virQEMUDriver *driver, 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); - } + if (domainLogContextReadFiltered(logContext, &msg, 1024) < 0) + VIR_WARN("Unable to read from dbus-daemon log");
+ virReportError(VIR_ERR_OPERATION_FAILED, + _("dbus-daemon died and reported:\n%1$s"), + NULLSTR(msg));
Are the errors usually multiline? If not, then I'd skip the newline in the translation string.
I don't remember. ok

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@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 da3321849c..051f93495c 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4441,6 +4441,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, @@ -4512,8 +4535,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

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 | 405 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_rdp.h | 71 ++++++++ 6 files changed, 481 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 cf05dca55a..bd740f11c0 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..e881b74ee3 --- /dev/null +++ b/src/qemu/qemu_rdp.c @@ -0,0 +1,405 @@ +/* + * 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 <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); +} + + +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 *shortName = virDomainDefGetShortName(vm->def); + g_autofree char *pidfile = NULL; + g_autofree char *logname = 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; + int logfd = -1; + g_autoptr(domainLogContext) logContext = NULL; + + if (rdp->pid != -1) { + return 0; + } + + if (!dbus_addr) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no D-Bus address")); + return -1; + } + + 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; + + logname = g_strdup_printf("%s-qemu-rdp", shortName); + if (!(logContext = domainLogContextNew(cfg->stdioLogD, cfg->logDir, + QEMU_DRIVER_NAME, + vm, driver->privileged, + logname))) { + virLastErrorPrefixMessage("%s", _("can't open log context")); + return -1; + } + + logfd = domainLogContextGetWriteFD(logContext); + + cmd = virCommandNew(cfg->qemuRdpName); + virCommandClearCaps(cmd); + virCommandSetPidFile(cmd, pidfile); + virCommandSetOutputFD(cmd, &logfd); + virCommandSetErrorFD(cmd, &logfd); + virCommandDaemonize(cmd); + 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); + + 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 (qemuExtDeviceLogCommand(driver, vm, cmd, "qemu-rdp") < 0) + return -1; + + 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) { + g_autofree char *msg = NULL; + + if (domainLogContextReadFiltered(logContext, &msg, 1024) < 0) + VIR_WARN("Unable to read from qemu-rdp log"); + + virReportError(VIR_ERR_OPERATION_FAILED, + _("qemu-rdp died and reported:\n%1$s"), + NULLSTR(msg)); + 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 Tue, Feb 18, 2025 at 02:16:22PM +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 | 405 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_rdp.h | 71 ++++++++ 6 files changed, 481 insertions(+) create mode 100644 src/qemu/qemu_rdp.c create mode 100644 src/qemu/qemu_rdp.h
diff --git a/src/qemu/qemu_rdp.c b/src/qemu/qemu_rdp.c new file mode 100644 index 0000000000..e881b74ee3 --- /dev/null +++ b/src/qemu/qemu_rdp.c @@ -0,0 +1,405 @@ +/* + * 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 <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'"),
It feels to me like this needs a "from" or "for" in order to disambiguate the meaning of the string, just: s/capabilities/capabilities for/ s/capabilities/capabilities from/ would be sufficient.

Hi On Fri, Mar 7, 2025 at 6:40 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Tue, Feb 18, 2025 at 02:16:22PM +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 | 405 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_rdp.h | 71 ++++++++ 6 files changed, 481 insertions(+) create mode 100644 src/qemu/qemu_rdp.c create mode 100644 src/qemu/qemu_rdp.h
diff --git a/src/qemu/qemu_rdp.c b/src/qemu/qemu_rdp.c new file mode 100644 index 0000000000..e881b74ee3 --- /dev/null +++ b/src/qemu/qemu_rdp.c @@ -0,0 +1,405 @@ +/* + * 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 <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'"),
It feels to me like this needs a "from" or "for" in order to disambiguate the meaning of the string, just:
s/capabilities/capabilities for/ s/capabilities/capabilities from/
would be sufficient.
The same error already exists in qemu_vhost_user.c and qemu_slirp.c. Should I touch that too?

On Thu, Mar 13, 2025 at 12:13:44AM +0400, Marc-André Lureau wrote:
Hi
On Fri, Mar 7, 2025 at 6:40 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Tue, Feb 18, 2025 at 02:16:22PM +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 | 405 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_rdp.h | 71 ++++++++ 6 files changed, 481 insertions(+) create mode 100644 src/qemu/qemu_rdp.c create mode 100644 src/qemu/qemu_rdp.h
diff --git a/src/qemu/qemu_rdp.c b/src/qemu/qemu_rdp.c new file mode 100644 index 0000000000..e881b74ee3 --- /dev/null +++ b/src/qemu/qemu_rdp.c @@ -0,0 +1,405 @@ +/* + * 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 <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'"),
It feels to me like this needs a "from" or "for" in order to disambiguate the meaning of the string, just:
s/capabilities/capabilities for/ s/capabilities/capabilities from/
would be sufficient.
The same error already exists in qemu_vhost_user.c and qemu_slirp.c. Should I touch that too?
Oh, I haven't noticed that. I'll leave that up to you, I'd prefer it with the preposition.

From: Marc-André Lureau <marcandre.lureau@redhat.com> This will help with the following patch, which also requires config access. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_capabilities.c | 9 +++++---- src/qemu/qemu_capabilities.h | 9 +++++---- src/qemu/qemu_conf.c | 9 +++++---- tests/domaincapstest.c | 7 +++---- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 23b466c36e..fed7f998d3 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6938,12 +6938,11 @@ virQEMUCapsFillDomainFeatureHypervCaps(virQEMUCaps *qemuCaps, int -virQEMUCapsFillDomainCaps(virQEMUCaps *qemuCaps, +virQEMUCapsFillDomainCaps(virQEMUDriverConfig *cfg, + virQEMUCaps *qemuCaps, virArch hostarch, virDomainCaps *domCaps, - bool privileged, - virFirmware **firmwares, - size_t nfirmwares) + bool privileged) { virDomainCapsOS *os = &domCaps->os; virDomainCapsDeviceDisk *disk = &domCaps->disk; @@ -6960,6 +6959,8 @@ virQEMUCapsFillDomainCaps(virQEMUCaps *qemuCaps, virDomainCapsLaunchSecurity *launchSecurity = &domCaps->launchSecurity; virDomainCapsDeviceNet *net = &domCaps->net; virDomainCapsDevicePanic *panic = &domCaps->panic; + virFirmware **firmwares = cfg->firmwares; + size_t nfirmwares = cfg->nfirmwares; virQEMUCapsFillDomainFeaturesFromQEMUCaps(qemuCaps, domCaps); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index ee71331a09..cca31172a6 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -28,6 +28,8 @@ #include "virfilecache.h" #include "virenum.h" +typedef struct _virQEMUDriverConfig virQEMUDriverConfig; + /* * Internal flags to keep track of qemu command line capabilities * @@ -864,12 +866,11 @@ void virQEMUCapsInitGuestFromBinary(virCaps *caps, virQEMUCaps *qemuCaps, virArch guestarch); -int virQEMUCapsFillDomainCaps(virQEMUCaps *qemuCaps, +int virQEMUCapsFillDomainCaps(virQEMUDriverConfig *cfg, + virQEMUCaps *qemuCaps, virArch hostarch, virDomainCaps *domCaps, - bool privileged, - virFirmware **firmwares, - size_t nfirmwares); + bool privileged); void virQEMUCapsFillDomainMemoryBackingCaps(virQEMUCaps *qemuCaps, virDomainCapsMemoryBacking *memoryBacking); diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 25cadf520d..f76e701898 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1600,10 +1600,11 @@ virQEMUDriverGetDomainCapabilities(virQEMUDriver *driver, if (!(domCaps = virDomainCapsNew(path, machine, arch, virttype))) return NULL; - if (virQEMUCapsFillDomainCaps(qemuCaps, driver->hostarch, - domCaps, driver->privileged, - cfg->firmwares, - cfg->nfirmwares) < 0) + if (virQEMUCapsFillDomainCaps(cfg, + qemuCaps, + driver->hostarch, + domCaps, + driver->privileged) < 0) return NULL; return g_steal_pointer(&domCaps); diff --git a/tests/domaincapstest.c b/tests/domaincapstest.c index e520c7d7bc..e4b03dfeab 100644 --- a/tests/domaincapstest.c +++ b/tests/domaincapstest.c @@ -98,10 +98,9 @@ fillQemuCaps(virDomainCaps *domCaps, if (!domCaps->machine) domCaps->machine = g_strdup(virQEMUCapsGetPreferredMachine(qemuCaps, virtType)); - if (virQEMUCapsFillDomainCaps(qemuCaps, domCaps->arch, domCaps, - false, - cfg->firmwares, - cfg->nfirmwares) < 0) + if (virQEMUCapsFillDomainCaps(cfg, + qemuCaps, domCaps->arch, domCaps, + false) < 0) return -1; /* The function above tries to query host's VFIO capabilities by calling -- 2.47.0

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_capabilities.c | 15 +++++++++++---- src/qemu/qemu_capabilities.h | 3 ++- src/qemu/qemu_rdp.c | 11 +++++++++++ src/qemu/qemu_rdp.h | 2 ++ src/qemu/qemu_validate.c | 3 ++- tests/domaincapsdata/qemu_10.0.0-q35.x86_64.xml | 1 + tests/domaincapsdata/qemu_10.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_10.0.0.s390x.xml | 1 + tests/domaincapsdata/qemu_10.0.0.x86_64.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 + .../domaincapsdata/qemu_7.2.0-hvf.x86_64+hvf.xml | 1 + tests/domaincapsdata/qemu_7.2.0-q35.x86_64.xml | 1 + .../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 + .../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 + .../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 + .../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 + tests/testutilsqemu.c | 6 ++++++ 52 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index fed7f998d3..6bedb5cd0d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6487,7 +6487,8 @@ virQEMUCapsFillDomainDeviceDiskCaps(virQEMUCaps *qemuCaps, void -virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUCaps *qemuCaps, +virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUDriverConfig *cfg, + virQEMUCaps *qemuCaps, virDomainCapsDeviceGraphics *dev) { dev->supported = VIR_TRISTATE_BOOL_YES; @@ -6501,8 +6502,14 @@ 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); + if (qemuRdpAvailable(cfg->qemuRdpName)) { + VIR_DOMAIN_CAPS_ENUM_SET(dev->type, + VIR_DOMAIN_GRAPHICS_TYPE_RDP); + } + } } @@ -6986,7 +6993,7 @@ virQEMUCapsFillDomainCaps(virQEMUDriverConfig *cfg, virQEMUCapsFillDomainCPUCaps(qemuCaps, hostarch, domCaps); virQEMUCapsFillDomainMemoryBackingCaps(qemuCaps, memoryBacking); virQEMUCapsFillDomainDeviceDiskCaps(qemuCaps, domCaps->machine, disk); - virQEMUCapsFillDomainDeviceGraphicsCaps(qemuCaps, graphics); + virQEMUCapsFillDomainDeviceGraphicsCaps(cfg, qemuCaps, graphics); virQEMUCapsFillDomainDeviceVideoCaps(qemuCaps, video); virQEMUCapsFillDomainDeviceHostdevCaps(qemuCaps, hostdev); virQEMUCapsFillDomainDeviceRNGCaps(qemuCaps, rng); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index cca31172a6..f0340dc646 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -875,7 +875,8 @@ int virQEMUCapsFillDomainCaps(virQEMUDriverConfig *cfg, void virQEMUCapsFillDomainMemoryBackingCaps(virQEMUCaps *qemuCaps, virDomainCapsMemoryBacking *memoryBacking); -void virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUCaps *qemuCaps, +void virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUDriverConfig *cfg, + virQEMUCaps *qemuCaps, virDomainCapsDeviceGraphics *dev); void virQEMUCapsFillDomainDeviceVideoCaps(virQEMUCaps *qemuCaps, diff --git a/src/qemu/qemu_rdp.c b/src/qemu/qemu_rdp.c index e881b74ee3..206035ac27 100644 --- a/src/qemu/qemu_rdp.c +++ b/src/qemu/qemu_rdp.c @@ -403,3 +403,14 @@ qemuRdpSetCredentials(virDomainObj *vm, "SetCredentials", args); } + + +bool +qemuRdpAvailable(const char *helper) +{ + g_autoptr(qemuRdp) rdp = NULL; + + rdp = qemuRdpNewForHelper(helper); + + return rdp && qemuRdpHasFeature(rdp, QEMU_RDP_FEATURE_DBUS_ADDRESS); +} diff --git a/src/qemu/qemu_rdp.h b/src/qemu/qemu_rdp.h index 6af90b06d2..2485a49de4 100644 --- a/src/qemu/qemu_rdp.h +++ b/src/qemu/qemu_rdp.h @@ -42,6 +42,8 @@ struct _qemuRdp { guint leaving_id; }; +bool qemuRdpAvailable(const char *helper); + qemuRdp *qemuRdpNew(void); qemuRdp *qemuRdpNewForHelper(const char *helper); diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 051f93495c..77ba05b96a 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4470,9 +4470,10 @@ qemuValidateDomainDeviceDefGraphics(const virDomainGraphicsDef *graphics, virQEMUDriver *driver, virQEMUCaps *qemuCaps) { + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); virDomainCapsDeviceGraphics graphicsCaps = { 0 }; - virQEMUCapsFillDomainDeviceGraphicsCaps(qemuCaps, &graphicsCaps); + virQEMUCapsFillDomainDeviceGraphicsCaps(cfg, qemuCaps, &graphicsCaps); if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(graphicsCaps.type, graphics->type)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, diff --git a/tests/domaincapsdata/qemu_10.0.0-q35.x86_64.xml b/tests/domaincapsdata/qemu_10.0.0-q35.x86_64.xml index 73cb50e7b6..e1ff01a7a8 100644 --- a/tests/domaincapsdata/qemu_10.0.0-q35.x86_64.xml +++ b/tests/domaincapsdata/qemu_10.0.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_10.0.0-tcg.x86_64.xml b/tests/domaincapsdata/qemu_10.0.0-tcg.x86_64.xml index 9a7d39c1f8..3ce5c0c412 100644 --- a/tests/domaincapsdata/qemu_10.0.0-tcg.x86_64.xml +++ b/tests/domaincapsdata/qemu_10.0.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_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_10.0.0.x86_64.xml b/tests/domaincapsdata/qemu_10.0.0.x86_64.xml index c06b9d1c51..66c4dfed9f 100644 --- a/tests/domaincapsdata/qemu_10.0.0.x86_64.xml +++ b/tests/domaincapsdata/qemu_10.0.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> 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> diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index 8974882bda..3d89f7ce72 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -52,6 +52,12 @@ bool virTPMHasSwtpm(void) } +/* Enough to tell capabilities code that qemu-rdp is usable */ +bool qemuRdpAvailable(const char *helper G_GNUC_UNUSED) +{ + return true; +} + bool virTPMSwtpmSetupCapsGet(virTPMSwtpmSetupFeature cap) -- 2.47.0

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 381bf84f67..1bb3efd79f 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -6502,7 +6502,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'/> @@ -6665,14 +6665,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 eabb31d463..69808d8489 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 2384bab7a6..78e72b7c10 100644 --- a/src/qemu/qemu_extdevice.c +++ b/src/qemu/qemu_extdevice.c @@ -238,14 +238,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++) { @@ -311,6 +325,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 28ca321c5c..5dae70cf7f 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4423,6 +4423,7 @@ int qemuDomainChangeGraphicsPasswords(virDomainObj *vm, int type, virDomainGraphicsAuthDef *auth, + const char *defaultUsername, const char *defaultPasswd, int asyncJob) { @@ -4432,13 +4433,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); @@ -4568,6 +4575,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; @@ -4615,6 +4623,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; @@ -4630,8 +4639,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 d51f649bac..7c858fc73e 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -57,6 +57,7 @@ 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 30d6560d52..53572f7315 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2969,18 +2969,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) @@ -4259,6 +4270,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, @@ -4964,8 +4999,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: @@ -5004,8 +5047,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: @@ -5172,8 +5219,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: @@ -5456,6 +5506,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) { @@ -5474,8 +5541,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: @@ -5988,6 +6077,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; @@ -7460,6 +7585,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) @@ -9032,6 +9161,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 Tue, Feb 18, 2025 at 02:16:25PM +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(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 28ca321c5c..5dae70cf7f 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4423,6 +4423,7 @@ int qemuDomainChangeGraphicsPasswords(virDomainObj *vm, int type, virDomainGraphicsAuthDef *auth, + const char *defaultUsername, const char *defaultPasswd, int asyncJob) { @@ -4432,13 +4433,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, ""); + } +
It's not immediately visible that defaultPasswd must not be NULL if the graphics type is RDP. The patch is semantically fine, but I worry about the future, would you mind adding a simple check here so that we do not end up calling g_variant_new() with NULL?
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 30d6560d52..53572f7315 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -5988,6 +6077,42 @@ qemuProcessPrepareHostNetwork(virDomainObj *vm) return 0; }
+#include "qemu_rdp.h" +
Any reason why you cannot put the include at the top with the other ones?
+static int +qemuPrepareGraphicsRdp(virQEMUDriver *driver, + virDomainGraphicsDef *gfx) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + g_autoptr(qemuRdp) rdp = NULL;
No need for autoptr variable here, there is no place where this function might fail with this variable set to non-NULL;
+ + if (!(rdp = qemuRdpNewForHelper(cfg->qemuRdpName))) + return -1; + + QEMU_DOMAIN_GRAPHICS_PRIVATE(gfx)->rdp = g_steal_pointer(&rdp); + + return 0; +}

Hi On Fri, Mar 7, 2025 at 7:03 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Tue, Feb 18, 2025 at 02:16:25PM +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(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 28ca321c5c..5dae70cf7f 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4423,6 +4423,7 @@ int qemuDomainChangeGraphicsPasswords(virDomainObj *vm, int type, virDomainGraphicsAuthDef *auth, + const char *defaultUsername, const char *defaultPasswd, int asyncJob) { @@ -4432,13 +4433,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, ""); + } +
It's not immediately visible that defaultPasswd must not be NULL if the graphics type is RDP. The patch is semantically fine, but I worry about the future, would you mind adding a simple check here so that we do not end up calling g_variant_new() with NULL?
assert(password != NULL) ?
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 30d6560d52..53572f7315 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -5988,6 +6077,42 @@ qemuProcessPrepareHostNetwork(virDomainObj *vm) return 0; }
+#include "qemu_rdp.h" +
Any reason why you cannot put the include at the top with the other ones?
oops, that's a left over, thanks!
+static int +qemuPrepareGraphicsRdp(virQEMUDriver *driver, + virDomainGraphicsDef *gfx) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + g_autoptr(qemuRdp) rdp = NULL;
No need for autoptr variable here, there is no place where this function might fail with this variable set to non-NULL;
yeah, I suppose the code evolved. It doesn't hurt though. ok
+ + if (!(rdp = qemuRdpNewForHelper(cfg->qemuRdpName))) + return -1; + + QEMU_DOMAIN_GRAPHICS_PRIVATE(gfx)->rdp = g_steal_pointer(&rdp); + + return 0; +}

On Thu, Mar 13, 2025 at 12:18:04AM +0400, Marc-André Lureau wrote:
Hi
On Fri, Mar 7, 2025 at 7:03 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Tue, Feb 18, 2025 at 02:16:25PM +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(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 28ca321c5c..5dae70cf7f 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4423,6 +4423,7 @@ int qemuDomainChangeGraphicsPasswords(virDomainObj *vm, int type, virDomainGraphicsAuthDef *auth, + const char *defaultUsername, const char *defaultPasswd, int asyncJob) { @@ -4432,13 +4433,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, ""); + } +
It's not immediately visible that defaultPasswd must not be NULL if the graphics type is RDP. The patch is semantically fine, but I worry about the future, would you mind adding a simple check here so that we do not end up calling g_variant_new() with NULL?
assert(password != NULL) ?
We prefer setting an VIR_ERR_INTERNAL_ERROR instead of aborting the whole daemon.
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 30d6560d52..53572f7315 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -5988,6 +6077,42 @@ qemuProcessPrepareHostNetwork(virDomainObj *vm) return 0; }
+#include "qemu_rdp.h" +
Any reason why you cannot put the include at the top with the other ones?
oops, that's a left over, thanks!
+static int +qemuPrepareGraphicsRdp(virQEMUDriver *driver, + virDomainGraphicsDef *gfx) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + g_autoptr(qemuRdp) rdp = NULL;
No need for autoptr variable here, there is no place where this function might fail with this variable set to non-NULL;
yeah, I suppose the code evolved. It doesn't hurt though. ok
It doesn't, true.
+ + if (!(rdp = qemuRdpNewForHelper(cfg->qemuRdpName))) + return -1; + + QEMU_DOMAIN_GRAPHICS_PRIVATE(gfx)->rdp = g_steal_pointer(&rdp); + + return 0; +}

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 c271170d25..03c839114c 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -1756,6 +1756,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 Tue, Feb 18, 2025 at 02:16:05PM +0400, 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!
v2: thanks to Daniel review - drop extra error report from "qemu: report an error for unsupported graphics" - replace g_return pre-conditions with ATTRIBUTE_NONNULL - improve "qemu/dbus: keep a connection to the VM D-Bus" to also reconnect - use domainLogContext for logging (for virtiofs as well) - check for qemu-rdp availabilty for setting 'rdp' capability - make dbus-addr qemu-rdp capability mandatory - rebased - add r-b tags
Marc-André Lureau (21): build-sys: drop -Winline when optimization=g build: fix -Werror=maybe-uninitialized qemu-slirp: drop unneeded check for OOM util: annotate non-null arguments for virGDBusCallMethod() qemu: fall-through 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/virtiofs: use domainLogContext qemu/dbus: keep a connection to the VM D-Bus qemu/dbus: log daemon stdout/err, use domainLogContext qemu: validate RDP configuration qemu: add qemu-rdp helper unit qemu: pass virQEMUDriverConfig to capabilities qemu: add 'rdp' capability if qemu-rdp is available qemu: add RDP support tests: add qemu <graphics type='rdp'/> test
Without testing it (yet, I want to try it next week), without deep knowledge of D-Bus details and ideally with the teeny tiny details adjusted: Reviewed-by: Martin Kletzander <mkletzan@redhat.com>

On Fri, Mar 07, 2025 at 04:22:46PM +0100, Martin Kletzander wrote:
On Tue, Feb 18, 2025 at 02:16:05PM +0400, 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!
v2: thanks to Daniel review - drop extra error report from "qemu: report an error for unsupported graphics" - replace g_return pre-conditions with ATTRIBUTE_NONNULL - improve "qemu/dbus: keep a connection to the VM D-Bus" to also reconnect - use domainLogContext for logging (for virtiofs as well) - check for qemu-rdp availabilty for setting 'rdp' capability - make dbus-addr qemu-rdp capability mandatory - rebased - add r-b tags
Marc-André Lureau (21): build-sys: drop -Winline when optimization=g build: fix -Werror=maybe-uninitialized qemu-slirp: drop unneeded check for OOM util: annotate non-null arguments for virGDBusCallMethod() qemu: fall-through 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/virtiofs: use domainLogContext qemu/dbus: keep a connection to the VM D-Bus qemu/dbus: log daemon stdout/err, use domainLogContext qemu: validate RDP configuration qemu: add qemu-rdp helper unit qemu: pass virQEMUDriverConfig to capabilities qemu: add 'rdp' capability if qemu-rdp is available qemu: add RDP support tests: add qemu <graphics type='rdp'/> test
Without testing it (yet, I want to try it next week), without deep knowledge of D-Bus details and ideally with the teeny tiny details adjusted:
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
So I tried testing it and I found a couple of issues. Those might be on my side, maybe caused by misunderstanding of it all. First of all <graphics type='dbus'/> fails with these patches for me even without <graphics type='rdp'/>. That might be the source of all other issues, but since I was not sure whether to use p2p='yes' or address='unix:path=...' I tried both. When address is specified qemu tries to connect to it, so that's not the right approach. When I specify p2p='yes' then the VM starts. If I add the RDP graphics I get a "confusing" error message: error: unsupported configuration: qemu-rdp support requires a D-Bus bus graphics device. which should probably say "qemu-rdp support requires a non-p2p dbus graphics device" instead since it is looking for a p2p='no' dbus graphics. If I delay the checking of qemu-rdp running I find out that it fails right away. It cannot connect to the unix socket, so I tried just running qemu VM with dbus graphics and qemu-rdp manually, but that failed on the missing certificate files. That could be checked before blindly passing it down to the helper, too (even though I was running it manually this time it reminded me of that possibility). Ideally some handshake could confirm it is actually running. If not a handshake, then maybe waiting whether it listens on where it is supposed to or some other indication of it being started while checking that the PID exists, so that it is not a dumb delay. Even running it manually I cannot verify it works, but that's probably a client-side issue? Server output when trying rdesktop: Waiting for org.qemu... Starting RDP server, args: ServerArgs { bind_address: 0.0.0.0:3389, cert: None, key: None, remotefx: Disable } Cert: "/home/nert/.config/qemu-rdp/server-cert.pem", Key: "/home/nert/.config/qemu-rdp/server-key.pem" 2025-03-11T13:21:00.350891Z ERROR ironrdp_server::server: Connection error error=[no credentials while doing credssp] general error 2025-03-11T13:21:00.352401Z ERROR ironrdp_server::server: Connection error error=accept_begin failed Caused by: [failed to negotiate security protocol] general error sdl-freerdp client output (no error on the server side reported): [14:24:35:046] [449399:0006db7b] [ERROR][com.freerdp.core.transport] - [transport_default_write]: BIO_should_retry returned a system error 32: Broken pipe rdesktop client output: Failed to initialize NLA, do you have correct Kerberos TGT initialized ? Core(error): rcp_recv(), connection closed by peer === Anyway, I'm clearly doing something wrong, but there are some issues in the code as well and from the greater picture it should not be this complicated to test it out, should it? I can test out other patches from you if you want, and I am open to hearing tips on what I'm doing wrong. Have a nice day, Martin

Hi On Tue, Mar 11, 2025 at 5:28 PM Martin Kletzander <mkletzan@redhat.com> wrote:
So I tried testing it and I found a couple of issues. Those might be on my side, maybe caused by misunderstanding of it all. First of all <graphics type='dbus'/> fails with these patches for me even without
How does it fail? (it works on my end)
<graphics type='rdp'/>. That might be the source of all other issues, but since I was not sure whether to use p2p='yes' or address='unix:path=...' I tried both. When address is specified qemu tries to connect to it, so that's not the right approach. When I specify p2p='yes' then the VM starts. If I add the RDP graphics I get a "confusing" error message:
error: unsupported configuration: qemu-rdp support requires a D-Bus bus graphics device.
which should probably say "qemu-rdp support requires a non-p2p dbus graphics device" instead since it is looking for a p2p='no' dbus graphics.
Not sure what is the best wording, but I understand it is confusing. It requires a "D-Bus bus" to work. (we could make it work with p2p too, eventually, but that might make multi-process handling more complicated in the future - say if vhost-user-gpu exports the dbus display interface etc)
If I delay the checking of qemu-rdp running I find out that it fails right away. It cannot connect to the unix socket, so I tried just running qemu VM with dbus graphics and qemu-rdp manually, but that failed on the missing certificate files. That could be checked before blindly passing it down to the helper, too (even though I was running it manually this time it reminded me of that possibility).
Are you saying that libvirt should check the presence of certificates before running qemu-rdp and provide an early error? qemu rdp log is explicit (Error: reading server cert `...`). However, I admit libvirt error report isn't great, as it fails with "The name org.QemuDisplay.RDP was not provided by any .service files". I will look into improving that.
Ideally some handshake could confirm it is actually running. If not a handshake, then maybe waiting whether it listens on where it is supposed to or some other indication of it being started while checking that the PID exists, so that it is not a dumb delay.
Even running it manually I cannot verify it works, but that's probably a client-side issue?
Server output when trying rdesktop: Waiting for org.qemu... Starting RDP server, args: ServerArgs { bind_address: 0.0.0.0:3389, cert: None, key: None, remotefx: Disable } Cert: "/home/nert/.config/qemu-rdp/server-cert.pem", Key: "/home/nert/.config/qemu-rdp/server-key.pem" 2025-03-11T13:21:00.350891Z ERROR ironrdp_server::server: Connection error error=[no credentials while doing credssp] general error 2025-03-11T13:21:00.352401Z ERROR ironrdp_server::server: Connection error error=accept_begin failed Caused by: [failed to negotiate security protocol] general error
sdl-freerdp client output (no error on the server side reported): [14:24:35:046] [449399:0006db7b] [ERROR][com.freerdp.core.transport] - [transport_default_write]: BIO_should_retry returned a system error 32: Broken pipe
rdesktop client output: Failed to initialize NLA, do you have correct Kerberos TGT initialized ? Core(error): rcp_recv(), connection closed by peer
If you run it manually, make sure you set the credentials before connecting the client, ex: busctl --user call org.QemuDisplay.RDP /org/qemu_display/rdp org.QemuDisplay.RDP SetCredentials sss user pass ''
===
Anyway, I'm clearly doing something wrong, but there are some issues in the code as well and from the greater picture it should not be this complicated to test it out, should it? I can test out other patches from you if you want, and I am open to hearing tips on what I'm doing wrong.
Thanks a lot for testing and the suggestions!

On Thu, Mar 13, 2025 at 11:59:41AM +0400, Marc-André Lureau wrote:
Hi
On Tue, Mar 11, 2025 at 5:28 PM Martin Kletzander <mkletzan@redhat.com> wrote:
So I tried testing it and I found a couple of issues. Those might be on my side, maybe caused by misunderstanding of it all. First of all <graphics type='dbus'/> fails with these patches for me even without
How does it fail? (it works on my end)
$ virsh dumpxml fedora39 --xpath //graphics <graphics type="dbus"/> <graphics type="spice" autoport="yes"> <listen type="address"/> <image compression="off"/> </graphics> $ virsh version Compiled against library: libvirt 11.1.0 Using library: libvirt 11.1.0 Using API: QEMU 11.1.0 Running hypervisor: QEMU 9.2.2 $ virsh start fedora39 Domain 'fedora39' started $ virsh destroy fedora39 Domain 'fedora39' destroyed $ echo 'Start daemon with patches' Start daemon with patches $ virsh version Compiled against library: libvirt 11.1.0 Using library: libvirt 11.1.0 Using API: QEMU 11.1.0 Running hypervisor: QEMU 9.2.2 $ # Same version, but this time with the patch series applied $ virsh dumpxml fedora39 --xpath //graphics <graphics type="dbus"/> <graphics type="spice" autoport="yes"> <listen type="address"/> <image compression="off"/> </graphics> $ virsh start fedora39 error: Failed to start domain 'fedora39' error: operation failed: Failed to connect to dbus-daemon: The connection is closed
<graphics type='rdp'/>. That might be the source of all other issues, but since I was not sure whether to use p2p='yes' or address='unix:path=...' I tried both. When address is specified qemu tries to connect to it, so that's not the right approach. When I specify p2p='yes' then the VM starts. If I add the RDP graphics I get a "confusing" error message:
error: unsupported configuration: qemu-rdp support requires a D-Bus bus graphics device.
which should probably say "qemu-rdp support requires a non-p2p dbus graphics device" instead since it is looking for a p2p='no' dbus graphics.
Not sure what is the best wording, but I understand it is confusing. It requires a "D-Bus bus" to work. (we could make it work with p2p too, eventually, but that might make multi-process handling more complicated in the future - say if vhost-user-gpu exports the dbus display interface etc)
No matter how, but mentioning that it must not be p2p would help.
If I delay the checking of qemu-rdp running I find out that it fails right away. It cannot connect to the unix socket, so I tried just running qemu VM with dbus graphics and qemu-rdp manually, but that failed on the missing certificate files. That could be checked before blindly passing it down to the helper, too (even though I was running it manually this time it reminded me of that possibility).
Are you saying that libvirt should check the presence of certificates before running qemu-rdp and provide an early error? qemu rdp log is explicit (Error: reading server cert `...`). However, I admit libvirt error report isn't great, as it fails with "The name org.QemuDisplay.RDP was not provided by any .service files". I will look into improving that.
Thanks. I haven't encountered the certificate issue, as I generated the certificated before I noticed that we're passing it blindly. But if the error gets spat out correctly, then that's fine, I guess.
Ideally some handshake could confirm it is actually running. If not a handshake, then maybe waiting whether it listens on where it is supposed to or some other indication of it being started while checking that the PID exists, so that it is not a dumb delay.
Even running it manually I cannot verify it works, but that's probably a client-side issue?
Server output when trying rdesktop: Waiting for org.qemu... Starting RDP server, args: ServerArgs { bind_address: 0.0.0.0:3389, cert: None, key: None, remotefx: Disable } Cert: "/home/nert/.config/qemu-rdp/server-cert.pem", Key: "/home/nert/.config/qemu-rdp/server-key.pem" 2025-03-11T13:21:00.350891Z ERROR ironrdp_server::server: Connection error error=[no credentials while doing credssp] general error 2025-03-11T13:21:00.352401Z ERROR ironrdp_server::server: Connection error error=accept_begin failed Caused by: [failed to negotiate security protocol] general error
sdl-freerdp client output (no error on the server side reported): [14:24:35:046] [449399:0006db7b] [ERROR][com.freerdp.core.transport] - [transport_default_write]: BIO_should_retry returned a system error 32: Broken pipe
rdesktop client output: Failed to initialize NLA, do you have correct Kerberos TGT initialized ? Core(error): rcp_recv(), connection closed by peer
If you run it manually, make sure you set the credentials before connecting the client, ex: busctl --user call org.QemuDisplay.RDP /org/qemu_display/rdp org.QemuDisplay.RDP SetCredentials sss user pass ''
That's not mentioned in https://crates.io/crates/qemu-rdp =D =D =D
===
Anyway, I'm clearly doing something wrong, but there are some issues in the code as well and from the greater picture it should not be this complicated to test it out, should it? I can test out other patches from you if you want, and I am open to hearing tips on what I'm doing wrong.
Thanks a lot for testing and the suggestions!
You're welcome, thanks for taking the feedback, I'll gladly test next versions.

Hi On Thu, Mar 13, 2025 at 1:07 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Thu, Mar 13, 2025 at 11:59:41AM +0400, Marc-André Lureau wrote:
Hi
On Tue, Mar 11, 2025 at 5:28 PM Martin Kletzander <mkletzan@redhat.com> wrote:
So I tried testing it and I found a couple of issues. Those might be on my side, maybe caused by misunderstanding of it all. First of all <graphics type='dbus'/> fails with these patches for me even without
How does it fail? (it works on my end)
$ virsh dumpxml fedora39 --xpath //graphics <graphics type="dbus"/> <graphics type="spice" autoport="yes"> <listen type="address"/> <image compression="off"/> </graphics>
$ virsh version Compiled against library: libvirt 11.1.0 Using library: libvirt 11.1.0 Using API: QEMU 11.1.0 Running hypervisor: QEMU 9.2.2
$ virsh start fedora39 Domain 'fedora39' started
$ virsh destroy fedora39 Domain 'fedora39' destroyed
$ echo 'Start daemon with patches' Start daemon with patches $ virsh version Compiled against library: libvirt 11.1.0 Using library: libvirt 11.1.0 Using API: QEMU 11.1.0 Running hypervisor: QEMU 9.2.2
$ # Same version, but this time with the patch series applied $ virsh dumpxml fedora39 --xpath //graphics <graphics type="dbus"/> <graphics type="spice" autoport="yes"> <listen type="address"/> <image compression="off"/> </graphics>
$ virsh start fedora39 error: Failed to start domain 'fedora39' error: operation failed: Failed to connect to dbus-daemon: The connection is closed
Hmm, I don't get this issue. libvirt fails to connect to the just launched bus (or got closed). This is weird, because we checked earlier the socket exists. Could you check dbus log? (for some reason it's not in cfg.logDir, but in cfg.dbusStateDir - I wonder if we can change that now) thanks

On Thu, Mar 13, 2025 at 02:08:47PM +0400, Marc-André Lureau wrote:
Hi
On Thu, Mar 13, 2025 at 1:07 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Thu, Mar 13, 2025 at 11:59:41AM +0400, Marc-André Lureau wrote:
Hi
On Tue, Mar 11, 2025 at 5:28 PM Martin Kletzander <mkletzan@redhat.com> wrote:
So I tried testing it and I found a couple of issues. Those might be on my side, maybe caused by misunderstanding of it all. First of all <graphics type='dbus'/> fails with these patches for me even without
How does it fail? (it works on my end)
$ virsh dumpxml fedora39 --xpath //graphics <graphics type="dbus"/> <graphics type="spice" autoport="yes"> <listen type="address"/> <image compression="off"/> </graphics>
$ virsh version Compiled against library: libvirt 11.1.0 Using library: libvirt 11.1.0 Using API: QEMU 11.1.0 Running hypervisor: QEMU 9.2.2
$ virsh start fedora39 Domain 'fedora39' started
$ virsh destroy fedora39 Domain 'fedora39' destroyed
$ echo 'Start daemon with patches' Start daemon with patches $ virsh version Compiled against library: libvirt 11.1.0 Using library: libvirt 11.1.0 Using API: QEMU 11.1.0 Running hypervisor: QEMU 9.2.2
$ # Same version, but this time with the patch series applied $ virsh dumpxml fedora39 --xpath //graphics <graphics type="dbus"/> <graphics type="spice" autoport="yes"> <listen type="address"/> <image compression="off"/> </graphics>
$ virsh start fedora39 error: Failed to start domain 'fedora39' error: operation failed: Failed to connect to dbus-daemon: The connection is closed
Hmm, I don't get this issue. libvirt fails to connect to the just launched bus (or got closed). This is weird, because we checked earlier the socket exists.
Could you check dbus log? (for some reason it's not in cfg.logDir, but in cfg.dbusStateDir - I wonder if we can change that now)
There is no log when it fails (but there is an empty file when it works) and the configurations differ only in the listen socket path.
thanks

Hi On Thu, Mar 13, 2025 at 4:58 PM Martin Kletzander <mkletzan@redhat.com> wrote:
$ virsh start fedora39 error: Failed to start domain 'fedora39' error: operation failed: Failed to connect to dbus-daemon: The connection is closed
Hmm, I don't get this issue. libvirt fails to connect to the just launched bus (or got closed). This is weird, because we checked earlier the socket exists.
Could you check dbus log? (for some reason it's not in cfg.logDir, but in cfg.dbusStateDir - I wonder if we can change that now)
There is no log when it fails (but there is an empty file when it works) and the configurations differ only in the listen socket path.
I have run this in a loop for a few hundreds time without issues.. I don't get what could happen. Do you have a chance to investigate or give me access to the host? thanks

On Fri, Mar 14, 2025 at 12:50:45PM +0400, Marc-André Lureau wrote:
Hi
On Thu, Mar 13, 2025 at 4:58 PM Martin Kletzander <mkletzan@redhat.com> wrote:
$ virsh start fedora39 error: Failed to start domain 'fedora39' error: operation failed: Failed to connect to dbus-daemon: The connection is closed
Hmm, I don't get this issue. libvirt fails to connect to the just launched bus (or got closed). This is weird, because we checked earlier the socket exists.
Could you check dbus log? (for some reason it's not in cfg.logDir, but in cfg.dbusStateDir - I wonder if we can change that now)
There is no log when it fails (but there is an empty file when it works) and the configurations differ only in the listen socket path.
I have run this in a loop for a few hundreds time without issues.. I don't get what could happen. Do you have a chance to investigate or give me access to the host?
thanks
Replied privately, will update the list with any findings.

On Thu, Mar 13, 2025 at 02:08:47PM +0400, Marc-André Lureau wrote:
Hi
On Thu, Mar 13, 2025 at 1:07 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Thu, Mar 13, 2025 at 11:59:41AM +0400, Marc-André Lureau wrote:
Hi
On Tue, Mar 11, 2025 at 5:28 PM Martin Kletzander <mkletzan@redhat.com> wrote:
So I tried testing it and I found a couple of issues. Those might be on my side, maybe caused by misunderstanding of it all. First of all <graphics type='dbus'/> fails with these patches for me even without
How does it fail? (it works on my end)
$ virsh dumpxml fedora39 --xpath //graphics <graphics type="dbus"/> <graphics type="spice" autoport="yes"> <listen type="address"/> <image compression="off"/> </graphics>
$ virsh version Compiled against library: libvirt 11.1.0 Using library: libvirt 11.1.0 Using API: QEMU 11.1.0 Running hypervisor: QEMU 9.2.2
$ virsh start fedora39 Domain 'fedora39' started
$ virsh destroy fedora39 Domain 'fedora39' destroyed
$ echo 'Start daemon with patches' Start daemon with patches $ virsh version Compiled against library: libvirt 11.1.0 Using library: libvirt 11.1.0 Using API: QEMU 11.1.0 Running hypervisor: QEMU 9.2.2
$ # Same version, but this time with the patch series applied $ virsh dumpxml fedora39 --xpath //graphics <graphics type="dbus"/> <graphics type="spice" autoport="yes"> <listen type="address"/> <image compression="off"/> </graphics>
$ virsh start fedora39 error: Failed to start domain 'fedora39' error: operation failed: Failed to connect to dbus-daemon: The connection is closed
Hmm, I don't get this issue. libvirt fails to connect to the just launched bus (or got closed). This is weird, because we checked earlier the socket exists.
Could you check dbus log? (for some reason it's not in cfg.logDir, but in cfg.dbusStateDir - I wonder if we can change that now)
I've bisected it to 28d4703a1f12711ab180e72db08a83bae59941df which is what I thought was the patch that changed the behaviour. I also noticed that we do not clean up the dbus-daemon config file when starting the domain fails. The diff in the configs is only the path. If I copy the config, run the dbus-daemon manually, change the socket path to /tmp/dbus-test.sock, then I can connect to it using: busctl --address=unix:path=/tmp/dbus-test.sock tree but if I try that for the libvirt-started dbus-daemon I cannot, and it does not matter whether that is with or without your patches. So there is something else going on, but the patch that is trying to keep the connection to the dbus-daemon exposes it. I guess something is wrong on my side and there is no reason for it to block these patches. Even when I added DBUS_VERBOSE=1 to the libvirt-started dbus-daemon the logfile was empty, I guess I need to recompile dbus with that enabled. I'll let you know if I figure it out. Not today though. Have a nice day, Martin

On a Friday in 2025, Martin Kletzander wrote:
I've bisected it to 28d4703a1f12711ab180e72db08a83bae59941df which is what I thought was the patch that changed the behaviour.
What is that commit? Both my libvirt checkouts give me: fatal: bad object 28d4703a1f12711ab180e72db08a83bae59941df And even the web gives me a 404: https://gitlab.com/libvirt/libvirt/-/commit/28d4703a1f12711ab180e72db08a83ba... Jano
I also noticed that we do not clean up the dbus-daemon config file when starting the domain fails.
The diff in the configs is only the path.
If I copy the config, run the dbus-daemon manually, change the socket path to /tmp/dbus-test.sock, then I can connect to it using:
busctl --address=unix:path=/tmp/dbus-test.sock tree
but if I try that for the libvirt-started dbus-daemon I cannot, and it does not matter whether that is with or without your patches. So there is something else going on, but the patch that is trying to keep the connection to the dbus-daemon exposes it. I guess something is wrong on my side and there is no reason for it to block these patches.
Even when I added DBUS_VERBOSE=1 to the libvirt-started dbus-daemon the logfile was empty, I guess I need to recompile dbus with that enabled.
I'll let you know if I figure it out. Not today though.
Have a nice day, Martin

On Fri, Mar 14, 2025 at 04:20:25PM +0100, Ján Tomko wrote:
On a Friday in 2025, Martin Kletzander wrote:
I've bisected it to 28d4703a1f12711ab180e72db08a83bae59941df which is what I thought was the patch that changed the behaviour.
What is that commit?
Well, that's because you did not check my local branch with the e-mail patches applied, have you! Of course that's a huckup on my part, but that's the one which tries keeping the connection to the dbus-daemon for running machines. Sorry.
Both my libvirt checkouts give me: fatal: bad object 28d4703a1f12711ab180e72db08a83bae59941df
And even the web gives me a 404: https://gitlab.com/libvirt/libvirt/-/commit/28d4703a1f12711ab180e72db08a83ba...
Jano
I also noticed that we do not clean up the dbus-daemon config file when starting the domain fails.
The diff in the configs is only the path.
If I copy the config, run the dbus-daemon manually, change the socket path to /tmp/dbus-test.sock, then I can connect to it using:
busctl --address=unix:path=/tmp/dbus-test.sock tree
but if I try that for the libvirt-started dbus-daemon I cannot, and it does not matter whether that is with or without your patches. So there is something else going on, but the patch that is trying to keep the connection to the dbus-daemon exposes it. I guess something is wrong on my side and there is no reason for it to block these patches.
Even when I added DBUS_VERBOSE=1 to the libvirt-started dbus-daemon the logfile was empty, I guess I need to recompile dbus with that enabled.
I'll let you know if I figure it out. Not today though.
Have a nice day, Martin
participants (4)
-
Ján Tomko
-
Marc-André Lureau
-
marcandre.lureau@redhat.com
-
Martin Kletzander