[PATCH v3 00/20] 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! v3: thanks to Martin Kletzander review - drop "[PATCH v2 05/21] qemu: fall-through for unsupported graphics" patch - drop extra \n when reporting dbus-daemon log - check password != NULL or VIR_ERR_INTERNAL_ERROR - remove some needless #include/leftover - remove some needless g_auto pointer - rebased - add r-b tags 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 (20): 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: 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 | 8 +- 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 | 51 ++- src/qemu/qemu_hotplug.h | 1 + src/qemu/qemu_process.c | 169 ++++++- src/qemu/qemu_rdp.c | 424 ++++++++++++++++++ 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 +- .../qemu_10.0.0-q35.x86_64+amdsev.xml | 1 + .../domaincapsdata/qemu_10.0.0-q35.x86_64.xml | 1 + .../qemu_10.0.0-tcg.x86_64+amdsev.xml | 1 + .../domaincapsdata/qemu_10.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_10.0.0.s390x.xml | 1 + .../qemu_10.0.0.x86_64+amdsev.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 + .../qemu_9.2.0-hvf.aarch64+hvf.xml | 1 + .../qemu_9.2.0-q35.x86_64+amdsev.xml | 1 + .../domaincapsdata/qemu_9.2.0-q35.x86_64.xml | 1 + .../qemu_9.2.0-tcg.x86_64+amdsev.xml | 1 + .../domaincapsdata/qemu_9.2.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_9.2.0.s390x.xml | 1 + .../qemu_9.2.0.x86_64+amdsev.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 +- 92 files changed, 1234 insertions(+), 137 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> Reviewed-by: Martin Kletzander <mkletzan@redhat.com> --- meson.build | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 2d76a0846c..56823ca25b 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> Reviewed-by: Martin Kletzander <mkletzan@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> Reviewed-by: Martin Kletzander <mkletzan@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> Reviewed-by: Martin Kletzander <mkletzan@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> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Martin Kletzander <mkletzan@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 b376841388..1708f4f5ad 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -226,6 +226,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); @@ -327,6 +328,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 3e1b41af73..af1f59708f 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 f0e9681161..6f0ce5a2f3 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 20409a6535..6189b47d9b 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> Reviewed-by: Martin Kletzander <mkletzan@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 1708f4f5ad..95113b8eb6 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -104,6 +104,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" @@ -241,6 +242,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; @@ -266,6 +268,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; @@ -352,6 +355,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); @@ -376,6 +384,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->autoDumpPath); @@ -500,6 +509,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, @@ -714,6 +738,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) @@ -1184,6 +1211,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; @@ -1271,6 +1301,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, @@ -1356,6 +1394,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 af1f59708f..b40b373358 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 6189b47d9b..66c05d7b46 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> Reviewed-by: Martin Kletzander <mkletzan@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 dedcf76511..7e7b007b9d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1994,6 +1994,7 @@ virDomainGraphicsAuthDefClear(virDomainGraphicsAuthDef *def) if (!def) return; + VIR_FREE(def->username); VIR_FREE(def->passwd); /* Don't free def */ @@ -11319,6 +11320,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; @@ -11708,6 +11711,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; } @@ -26306,6 +26313,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); @@ -26580,6 +26591,8 @@ virDomainGraphicsDefFormatRDP(virBuffer *attrBuf, virDomainGraphicsListenDefFormatAddr(attrBuf, glisten, flags); + virDomainGraphicsAuthDefFormatAttr(attrBuf, &def->data.rdp.auth, flags); + virDomainGraphicsDefFormatListnes(childBuf, def, flags); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3a97fd866c..dd49cfd144 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 3276569325..7ef45a1731 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -4540,6 +4540,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> Reviewed-by: Martin Kletzander <mkletzan@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 7e7b007b9d..5748a89bd1 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -31771,15 +31771,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 dd49cfd144..32756e4dce 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -4631,4 +4631,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 e78abdad15..f4ec26eba3 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 af334aa96a..ef217ffc9e 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -2153,7 +2153,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)); @@ -4737,7 +4737,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> Reviewed-by: Martin Kletzander <mkletzan@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 ef217ffc9e..eae75d7bf5 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4491,8 +4491,6 @@ qemuValidateDomainDeviceDefGraphics(const virDomainGraphicsDef *graphics, virQEMUCaps *qemuCaps) { virDomainCapsDeviceGraphics graphicsCaps = { 0 }; - bool have_egl_headless = false; - size_t i; virQEMUCapsFillDomainDeviceGraphicsCaps(qemuCaps, &graphicsCaps); @@ -4503,18 +4501,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> Reviewed-by: Martin Kletzander <mkletzan@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 95113b8eb6..65016feb6a 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -65,6 +65,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 @@ -248,6 +251,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 b40b373358..bbbbd6bf67 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 6f0ce5a2f3..d72805f0c7 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> Reviewed-by: Martin Kletzander <mkletzan@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 ead2307ed7..84ff62cd6c 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8432,6 +8432,7 @@ qemuBuildGraphicsCommandLine(virQEMUDriverConfig *cfg, break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: return -1; case VIR_DOMAIN_GRAPHICS_TYPE_LAST: @@ -9981,6 +9982,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 @@ -10029,15 +10031,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> Reviewed-by: Martin Kletzander <mkletzan@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> Reviewed-by: Martin Kletzander <mkletzan@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 8e53a270a7..a70617f3c0 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 0173fbe3be..1eb670e6ba 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -9405,6 +9405,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> Reviewed-by: Martin Kletzander <mkletzan@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

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> Reviewed-by: Martin Kletzander <mkletzan@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 eae75d7bf5..89dee51526 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4484,6 +4484,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, @@ -4555,8 +4578,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> Reviewed-by: Martin Kletzander <mkletzan@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 | 413 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_rdp.h | 71 +++++++ 6 files changed, 489 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 29fac0034e..efc1ebddd5 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 a70617f3c0..e3b206763c 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..97eb772abb --- /dev/null +++ b/src/qemu/qemu_rdp.c @@ -0,0 +1,413 @@ +/* + * 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); + if (!virFileExists((certpath))) { + virReportError(VIR_ERR_OPERATION_FAILED, _("Missing certificate file '%1$s'"), certpath); + return -1; + } + if (!virFileExists((keypath))) { + virReportError(VIR_ERR_OPERATION_FAILED, _("Missing key file '%1$s'"), keypath); + return -1; + } + 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

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> Reviewed-by: Martin Kletzander <mkletzan@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 d13b4c8109..ca99e61896 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6926,12 +6926,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; @@ -6948,6 +6947,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 e836d107aa..733b84029d 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 * @@ -865,12 +867,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 65016feb6a..79634511db 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1623,10 +1623,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 96dc029af3..71e181d179 100644 --- a/tests/domaincapstest.c +++ b/tests/domaincapstest.c @@ -99,10 +99,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> Reviewed-by: Martin Kletzander <mkletzan@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 ++- .../qemu_10.0.0-q35.x86_64+amdsev.xml | 1 + tests/domaincapsdata/qemu_10.0.0-q35.x86_64.xml | 1 + .../qemu_10.0.0-tcg.x86_64+amdsev.xml | 1 + tests/domaincapsdata/qemu_10.0.0-tcg.x86_64.xml | 1 + tests/domaincapsdata/qemu_10.0.0.s390x.xml | 1 + .../domaincapsdata/qemu_10.0.0.x86_64+amdsev.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 + .../domaincapsdata/qemu_9.2.0-hvf.aarch64+hvf.xml | 1 + .../qemu_9.2.0-q35.x86_64+amdsev.xml | 1 + tests/domaincapsdata/qemu_9.2.0-q35.x86_64.xml | 1 + .../qemu_9.2.0-tcg.x86_64+amdsev.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+amdsev.xml | 1 + tests/domaincapsdata/qemu_9.2.0.x86_64.xml | 1 + tests/testutilsqemu.c | 6 ++++++ 59 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index ca99e61896..2c137bb663 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6475,7 +6475,8 @@ virQEMUCapsFillDomainDeviceDiskCaps(virQEMUCaps *qemuCaps, void -virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUCaps *qemuCaps, +virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUDriverConfig *cfg, + virQEMUCaps *qemuCaps, virDomainCapsDeviceGraphics *dev) { dev->supported = VIR_TRISTATE_BOOL_YES; @@ -6489,8 +6490,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); + } + } } @@ -6974,7 +6981,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 733b84029d..df63fe51ae 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -876,7 +876,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 97eb772abb..984795d599 100644 --- a/src/qemu/qemu_rdp.c +++ b/src/qemu/qemu_rdp.c @@ -411,3 +411,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 89dee51526..19af0f0209 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -4513,9 +4513,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+amdsev.xml b/tests/domaincapsdata/qemu_10.0.0-q35.x86_64+amdsev.xml index d000e4296e..b7fa8daa15 100644 --- a/tests/domaincapsdata/qemu_10.0.0-q35.x86_64+amdsev.xml +++ b/tests/domaincapsdata/qemu_10.0.0-q35.x86_64+amdsev.xml @@ -697,6 +697,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-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+amdsev.xml b/tests/domaincapsdata/qemu_10.0.0-tcg.x86_64+amdsev.xml index 7d7e77cace..d8a6fe8354 100644 --- a/tests/domaincapsdata/qemu_10.0.0-tcg.x86_64+amdsev.xml +++ b/tests/domaincapsdata/qemu_10.0.0-tcg.x86_64+amdsev.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-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+amdsev.xml b/tests/domaincapsdata/qemu_10.0.0.x86_64+amdsev.xml index fb06905b86..1f852adfae 100644 --- a/tests/domaincapsdata/qemu_10.0.0.x86_64+amdsev.xml +++ b/tests/domaincapsdata/qemu_10.0.0.x86_64+amdsev.xml @@ -697,6 +697,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.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 ce2e4f9f37..0fbbc8118c 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 @@ -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-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-hvf.aarch64+hvf.xml b/tests/domaincapsdata/qemu_9.2.0-hvf.aarch64+hvf.xml index 644a0ef44a..b8615a0986 100644 --- a/tests/domaincapsdata/qemu_9.2.0-hvf.aarch64+hvf.xml +++ b/tests/domaincapsdata/qemu_9.2.0-hvf.aarch64+hvf.xml @@ -118,6 +118,7 @@ <graphics supported='yes'> <enum name='type'> <value>vnc</value> + <value>rdp</value> <value>dbus</value> </enum> </graphics> diff --git a/tests/domaincapsdata/qemu_9.2.0-q35.x86_64+amdsev.xml b/tests/domaincapsdata/qemu_9.2.0-q35.x86_64+amdsev.xml index 11a8cd0b97..62c61b9de5 100644 --- a/tests/domaincapsdata/qemu_9.2.0-q35.x86_64+amdsev.xml +++ b/tests/domaincapsdata/qemu_9.2.0-q35.x86_64+amdsev.xml @@ -697,6 +697,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+amdsev.xml b/tests/domaincapsdata/qemu_9.2.0-tcg.x86_64+amdsev.xml index f7668e911e..265680b197 100644 --- a/tests/domaincapsdata/qemu_9.2.0-tcg.x86_64+amdsev.xml +++ b/tests/domaincapsdata/qemu_9.2.0-tcg.x86_64+amdsev.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-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+amdsev.xml b/tests/domaincapsdata/qemu_9.2.0.x86_64+amdsev.xml index d1143ad68f..ee82f2905b 100644 --- a/tests/domaincapsdata/qemu_9.2.0.x86_64+amdsev.xml +++ b/tests/domaincapsdata/qemu_9.2.0.x86_64+amdsev.xml @@ -697,6 +697,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.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 66c05d7b46..ecd761dff0 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

On Sun, Mar 16, 2025 at 12:18:39 +0400, marcandre.lureau--- via Devel wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Martin Kletzander <mkletzan@redhat.com> ---
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index ca99e61896..2c137bb663 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -6475,7 +6475,8 @@ virQEMUCapsFillDomainDeviceDiskCaps(virQEMUCaps *qemuCaps,
void -virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUCaps *qemuCaps, +virQEMUCapsFillDomainDeviceGraphicsCaps(virQEMUDriverConfig *cfg, + virQEMUCaps *qemuCaps, virDomainCapsDeviceGraphics *dev) { dev->supported = VIR_TRISTATE_BOOL_YES; @@ -6489,8 +6490,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);
This produces spurious errors when 'qemu-rdp' is not installed. Also execing 'qemu-rdp' while *filling* capabilities seems overkill: https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/MMVHK...
+ } + } }

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> Reviewed-by: Martin Kletzander <mkletzan@redhat.com> --- docs/formatdomain.rst | 25 +++--- src/conf/domain_conf.h | 1 + src/qemu/qemu_extdevice.c | 46 +++++++++-- src/qemu/qemu_hotplug.c | 51 +++++++++++- src/qemu/qemu_hotplug.h | 1 + src/qemu/qemu_process.c | 166 ++++++++++++++++++++++++++++++++++---- 6 files changed, 258 insertions(+), 32 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index c1876ad467..8ecad75f28 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -6552,7 +6552,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'/> @@ -6715,14 +6715,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 non-p2p ``dbus`` graphics is required to enable the QEMU RDP support (it + 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 32756e4dce..cbad1b7f7d 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 b6ef10edf9..fdd40cfe15 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,21 @@ 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) { + if (!password) + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing password")); + return qemuRdpSetCredentials(vm, username, password, ""); + } + if (auth->connected) connected = virDomainGraphicsAuthConnectedTypeToString(auth->connected); @@ -4568,6 +4577,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 +4625,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 +4641,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 1eb670e6ba..194176f58a 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, @@ -4967,8 +5002,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: @@ -5007,8 +5050,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: @@ -5175,8 +5222,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: @@ -5409,6 +5459,23 @@ qemuProcessMakeDir(virQEMUDriver *driver, } +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) { @@ -5427,8 +5494,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: @@ -5938,6 +6027,41 @@ qemuProcessPrepareHostNetwork(virDomainObj *vm) } +static int +qemuPrepareGraphicsRdp(virQEMUDriver *driver, + virDomainGraphicsDef *gfx) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + qemuRdp *rdp; + + if (!(rdp = qemuRdpNewForHelper(cfg->qemuRdpName))) + return -1; + + QEMU_DOMAIN_GRAPHICS_PRIVATE(gfx)->rdp = 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; pid_t dummypid; @@ -7409,6 +7533,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) @@ -8981,6 +9109,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

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Martin Kletzander <mkletzan@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 43ba9fed82..17efed1379 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -1753,6 +1753,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 Sun, Mar 16, 2025 at 12:18:21PM +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.
My r-b still stands, I believe this can go in, the issue I faced is clearly only on my side and I'll get to it later.
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).
The only other issue I have currently is that "cargo install qemu-rdp" breaks due to some ironrdp stuff on clean fedora 41 install, but again, that's orthogonal to this patch being in libvirt. Do you still have the commit rights or should I push this for you? I see that all your patches that are newer than roughly 10 years are merged by someone else =)

Hi On Tue, Mar 18, 2025 at 5:48 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Sun, Mar 16, 2025 at 12:18:21PM +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.
My r-b still stands, I believe this can go in, the issue I faced is clearly only on my side and I'll get to it later.
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).
The only other issue I have currently is that "cargo install qemu-rdp" breaks due to some ironrdp stuff on clean fedora 41 install, but again, that's orthogonal to this patch being in libvirt.
Fwiw, I am trying to package it for fc43. But I compile it daily on fc41 fine. Would be interested to know what fails. If you build from the git repos, it is likely that deps break every now and then though, there is no CI yet.
Do you still have the commit rights or should I push this for you? I see that all your patches that are newer than roughly 10 years are merged by someone else =)
Oh, I don't even remember having commit rights! Thus it's probably better if someone more familiar with the current process does it. I agree, it's ok to go too, we can solve additional/remaining issues on top and avoid some rebase pain that way. thanks!

On Tue, Mar 18, 2025 at 05:57:18PM +0400, Marc-André Lureau wrote:
Hi
On Tue, Mar 18, 2025 at 5:48 PM Martin Kletzander <mkletzan@redhat.com> wrote:
On Sun, Mar 16, 2025 at 12:18:21PM +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.
My r-b still stands, I believe this can go in, the issue I faced is clearly only on my side and I'll get to it later.
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).
The only other issue I have currently is that "cargo install qemu-rdp" breaks due to some ironrdp stuff on clean fedora 41 install, but again, that's orthogonal to this patch being in libvirt.
Fwiw, I am trying to package it for fc43. But I compile it daily on fc41 fine. Would be interested to know what fails. If you build from the git repos, it is likely that deps break every now and then though, there is no CI yet.
I'll let you know off-list.
Do you still have the commit rights or should I push this for you? I see that all your patches that are newer than roughly 10 years are merged by someone else =)
Oh, I don't even remember having commit rights! Thus it's probably better if someone more familiar with the current process does it. I agree, it's ok to go too, we can solve additional/remaining issues on top and avoid some rebase pain that way.
thanks!
done, you're welcome

On Sun, Mar 16, 2025 at 12:18:21 +0400, marcandre.lureau--- via Devel wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com> Marc-André Lureau (20): 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: 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
This most likely broke upstream CI. I noticed the following when debugging something else locally: $ valgrind --trace-children=yes --leak-check=full ./tests/qemuxmlconftest =1007397== 5 bytes in 1 blocks are definitely lost in loss record 10 of 849 ==1007397== at 0x4841866: malloc (vg_replace_malloc.c:446) ==1007397== by 0x591EC0F: xmlStrndup (in /usr/lib64/libxml2.so.2.12.9) ==1007397== by 0x495C4D4: virDomainGraphicsAuthDefParseXML (domain_conf.c:11530) ==1007397== by 0x4971A8F: virDomainGraphicsDefParseXMLRDP (domain_conf.c:11926) ==1007397== by 0x4971A8F: virDomainGraphicsDefParseXML (domain_conf.c:12229) ==1007397== by 0x49903C5: virDomainDefParseXML (domain_conf.c:19665) ==1007397== by 0x4991FD8: virDomainDefParseNode (domain_conf.c:20260) ==1007397== by 0x49920E0: virDomainDefParse (domain_conf.c:20230) ==1007397== by 0x11FB54: testQemuConfXMLCommon.constprop.0 (qemuxmlconftest.c:683) ==1007397== by 0x1210D5: testXMLParse (qemuxmlconftest.c:981) ==1007397== by 0x121819: virTestRun (testutils.c:143) ==1007397== by 0x121AA0: virTestRunLog (testutils.c:198) ==1007397== by 0x10B371: testRun (qemuxmlconftest.c:1022) ==1007397== ==1007397== 5 bytes in 1 blocks are definitely lost in loss record 11 of 849 ==1007397== at 0x4841866: malloc (vg_replace_malloc.c:446) ==1007397== by 0x591EC0F: xmlStrndup (in /usr/lib64/libxml2.so.2.12.9) ==1007397== by 0x495C4F7: virDomainGraphicsAuthDefParseXML (domain_conf.c:11535) ==1007397== by 0x4971A8F: virDomainGraphicsDefParseXMLRDP (domain_conf.c:11926) ==1007397== by 0x4971A8F: virDomainGraphicsDefParseXML (domain_conf.c:12229) ==1007397== by 0x49903C5: virDomainDefParseXML (domain_conf.c:19665) ==1007397== by 0x4991FD8: virDomainDefParseNode (domain_conf.c:20260) ==1007397== by 0x49920E0: virDomainDefParse (domain_conf.c:20230) ==1007397== by 0x11FB54: testQemuConfXMLCommon.constprop.0 (qemuxmlconftest.c:683) ==1007397== by 0x1210D5: testXMLParse (qemuxmlconftest.c:981) ==1007397== by 0x121819: virTestRun (testutils.c:143) ==1007397== by 0x121AA0: virTestRunLog (testutils.c:198) ==1007397== by 0x10B371: testRun (qemuxmlconftest.c:1022) ==1007397== ==1007397== 5 bytes in 1 blocks are definitely lost in loss record 12 of 849 ==1007397== at 0x4841866: malloc (vg_replace_malloc.c:446) ==1007397== by 0x591EC0F: xmlStrndup (in /usr/lib64/libxml2.so.2.12.9) ==1007397== by 0x495C4D4: virDomainGraphicsAuthDefParseXML (domain_conf.c:11530) ==1007397== by 0x4971A8F: virDomainGraphicsDefParseXMLRDP (domain_conf.c:11926) ==1007397== by 0x4971A8F: virDomainGraphicsDefParseXML (domain_conf.c:12229) ==1007397== by 0x49903C5: virDomainDefParseXML (domain_conf.c:19665) ==1007397== by 0x4991FD8: virDomainDefParseNode (domain_conf.c:20260) ==1007397== by 0x49920E0: virDomainDefParse (domain_conf.c:20230) ==1007397== by 0x11FFE7: testCompareOutXML2XML (qemuxmlconftest.c:782) ==1007397== by 0x121819: virTestRun (testutils.c:143) ==1007397== by 0x121AA0: virTestRunLog (testutils.c:198) ==1007397== by 0x10B39D: testRun (qemuxmlconftest.c:1024) ==1007397== by 0x111C1A: mymain (qemuxmlconftest.c:1756) ==1007397== ==1007397== 5 bytes in 1 blocks are definitely lost in loss record 13 of 849 ==1007397== at 0x4841866: malloc (vg_replace_malloc.c:446) ==1007397== by 0x591EC0F: xmlStrndup (in /usr/lib64/libxml2.so.2.12.9) ==1007397== by 0x495C4F7: virDomainGraphicsAuthDefParseXML (domain_conf.c:11535) ==1007397== by 0x4971A8F: virDomainGraphicsDefParseXMLRDP (domain_conf.c:11926) ==1007397== by 0x4971A8F: virDomainGraphicsDefParseXML (domain_conf.c:12229) ==1007397== by 0x49903C5: virDomainDefParseXML (domain_conf.c:19665) ==1007397== by 0x4991FD8: virDomainDefParseNode (domain_conf.c:20260) ==1007397== by 0x49920E0: virDomainDefParse (domain_conf.c:20230) ==1007397== by 0x11FFE7: testCompareOutXML2XML (qemuxmlconftest.c:782) ==1007397== by 0x121819: virTestRun (testutils.c:143) ==1007397== by 0x121AA0: virTestRunLog (testutils.c:198) ==1007397== by 0x10B39D: testRun (qemuxmlconftest.c:1024) ==1007397== by 0x111C1A: mymain (qemuxmlconftest.c:1756)

On a Tuesday in 2025, Peter Krempa via Devel wrote:
On Sun, Mar 16, 2025 at 12:18:21 +0400, marcandre.lureau--- via Devel wrote:
From: Marc-André Lureau <marcandre.lureau@redhat.com> Marc-André Lureau (20): 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: 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
This most likely broke upstream CI. I noticed the following when debugging something else locally:
$ valgrind --trace-children=yes --leak-check=full ./tests/qemuxmlconftest
Fixed now: commit 815c22a3a79a6dac7abefff050bb2d5157001985 conf: also free auth for rdp in virDomainGraphicsDefFree Jano
participants (5)
-
Ján Tomko
-
Marc-André Lureau
-
marcandre.lureau@redhat.com
-
Martin Kletzander
-
Peter Krempa