From nobody Tue Sep 9 23:39:22 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1738158590284786.2283269542895; Wed, 29 Jan 2025 05:49:50 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 207C017B3; Wed, 29 Jan 2025 08:49:46 -0500 (EST) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 1F10115EE; Wed, 29 Jan 2025 08:43:02 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id 8C2FD1724; Wed, 29 Jan 2025 08:42:57 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 41BF21857 for ; Wed, 29 Jan 2025 08:42:34 -0500 (EST) Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-625-4Nw9KirvNEOLL1Ij3sNC2g-1; Wed, 29 Jan 2025 08:42:32 -0500 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D28EE19560AA for ; Wed, 29 Jan 2025 13:42:31 +0000 (UTC) Received: from localhost (unknown [10.39.208.35]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E98411956094; Wed, 29 Jan 2025 13:42:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1738158153; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0VR0qwG+QLz8dzJrv0gJ+6X9CDJ+ig6Bjdb6ET4qe9U=; b=eSqIr6Jx0rX2PSwAPdnw8KFxaCbvkhTb8ZdGv9MHjxgALdPOt3W283SSK8z2kOiW5i/a1v eCGBvHu+rTwdgRokIDywAnz6zjGuwMmWfBodCLUw6EExEZxAyBZDP+NHpjjJCSE+PbLOc8 vpLxwRQU5n8GFylckJUQxuZnwgLpyJg= X-MC-Unique: 4Nw9KirvNEOLL1Ij3sNC2g-1 X-Mimecast-MFC-AGG-ID: 4Nw9KirvNEOLL1Ij3sNC2g From: marcandre.lureau@redhat.com To: devel@lists.libvirt.org Subject: [PATCH 18/19] qemu: add RDP support Date: Wed, 29 Jan 2025 17:40:40 +0400 Message-ID: <20250129134042.1282472-19-marcandre.lureau@redhat.com> In-Reply-To: <20250129134042.1282472-1-marcandre.lureau@redhat.com> References: <20250129134042.1282472-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: -nAtY8Md2nMtQ4osTsS-sSjMyd7m0RDfAj3J6vk_tTM_1738158151 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: MKQHJ43S3MJ7FXM2NF4IDPPKRDR2Q5S3 X-Message-ID-Hash: MKQHJ43S3MJ7FXM2NF4IDPPKRDR2Q5S3 X-MailFrom: marcandre.lureau@redhat.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0 CC: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1738158597403019000 Content-Type: text/plain; charset="utf-8" From: Marc-Andr=C3=A9 Lureau 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=C3=A9 Lureau --- docs/formatdomain.rst | 25 ++++-- src/conf/domain_conf.h | 1 + src/qemu/qemu_extdevice.c | 46 +++++++++-- src/qemu/qemu_hotplug.c | 49 ++++++++++- src/qemu/qemu_hotplug.h | 1 + src/qemu/qemu_process.c | 167 ++++++++++++++++++++++++++++++++++---- 6 files changed, 257 insertions(+), 32 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 785b51bb4e..358feb2625 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -6410,7 +6410,7 @@ interaction with the admin. - + @@ -6573,14 +6573,21 @@ interaction with the admin. Starts a RDP server. The ``port`` attribute specifies the TCP port n= umber (with -1 as legacy syntax indicating that it should be auto-allocate= d). 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 t= he - ``port`` attribute. The ``multiUser`` attribute is a boolean deciding - whether multiple simultaneous connections to the VM are permitted. T= he - ``replaceUser`` attribute is a boolean deciding whether the existing - connection must be dropped and a new connection must be established = by the - VRDP server, when a new client connects in single connection mode. + auto-allocation of the TCP port to use. + + A ``dbus`` graphics is also required to enable the QEMU RDP support,= which + uses an external "qemu-rdp" helper process. The ``username`` and + ``passwd`` attributes set the credentials (when they are not set, th= e 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 chos= en + port will be reflected in the ``port`` attribute. The ``multiUser`` + attribute is a boolean deciding whether multiple simultaneous connec= tions + 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. =20 ``desktop`` This value is reserved for VirtualBox domains for the moment. It dis= plays diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index b08e2549b7..ff05920030 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2025,6 +2025,7 @@ struct _virDomainGraphicsDef { } sdl; struct { int port; + bool portReserved; bool autoport; bool replaceUser; bool multiUser; diff --git a/src/qemu/qemu_extdevice.c b/src/qemu/qemu_extdevice.c index 954cb323a4..e0c9cd1d91 100644 --- a/src/qemu/qemu_extdevice.c +++ b/src/qemu/qemu_extdevice.c @@ -236,14 +236,28 @@ qemuExtDevicesStart(virQEMUDriver *driver, for (i =3D 0; i < def->ngraphics; i++) { virDomainGraphicsDef *graphics =3D def->graphics[i]; =20 - if (graphics->type !=3D 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; + } } =20 for (i =3D 0; i < def->ndisks; i++) { @@ -309,6 +323,26 @@ qemuExtDevicesStop(virQEMUDriver *driver, qemuVirtioFSStop(driver, vm, fs); } =20 + for (i =3D 0; i < def->ngraphics; i++) { + virDomainGraphicsDef *graphics =3D 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 =3D 0; i < def->ndisks; i++) { virDomainDiskDef *disk =3D def->disks[i]; qemuNbdkitStopStorageSource(disk->src, vm, true); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 5a7e6c3b12..38d21642fe 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4410,6 +4410,7 @@ int qemuDomainChangeGraphicsPasswords(virDomainObj *vm, int type, virDomainGraphicsAuthDef *auth, + const char *defaultUsername, const char *defaultPasswd, int asyncJob) { @@ -4419,13 +4420,19 @@ qemuDomainChangeGraphicsPasswords(virDomainObj *vm, g_autofree char *validTo =3D NULL; const char *connected =3D NULL; const char *password; + const char *username; int ret =3D -1; =20 if (!auth->passwd && !defaultPasswd) return 0; =20 + username =3D auth->username ? auth->username : defaultUsername; password =3D auth->passwd ? auth->passwd : defaultPasswd; =20 + if (type =3D=3D VIR_DOMAIN_GRAPHICS_TYPE_RDP) { + return qemuRdpSetCredentials(vm, username, password, ""); + } + if (auth->connected) connected =3D virDomainGraphicsAuthConnectedTypeToString(auth->con= nected); =20 @@ -4555,6 +4562,7 @@ qemuDomainChangeGraphics(virQEMUDriver *driver, if (qemuDomainChangeGraphicsPasswords(vm, VIR_DOMAIN_GRAPHICS_TYPE= _VNC, &dev->data.vnc.auth, + NULL, cfg->vncPassword, VIR_ASYNC_JOB_NONE) < 0) return -1; @@ -4602,6 +4610,7 @@ qemuDomainChangeGraphics(virQEMUDriver *driver, if (qemuDomainChangeGraphicsPasswords(vm, VIR_DOMAIN_GRAPHICS_TYPE= _SPICE, &dev->data.spice.auth, + NULL, cfg->spicePassword, VIR_ASYNC_JOB_NONE) < 0) return -1; @@ -4617,8 +4626,46 @@ qemuDomainChangeGraphics(virQEMUDriver *driver, } break; =20 - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if ((olddev->data.rdp.autoport !=3D dev->data.rdp.autoport) || + (!dev->data.rdp.autoport && + (olddev->data.rdp.port !=3D 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 h= as + * 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 !=3D dev->data.rdp.auth.connec= ted || + 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 =3D g_steal_pointer(&dev->data.= rdp.auth.username); + VIR_FREE(olddev->data.rdp.auth.passwd); + olddev->data.rdp.auth.passwd =3D g_steal_pointer(&dev->data.rd= p.auth.passwd); + olddev->data.rdp.auth.validTo =3D dev->data.rdp.auth.validTo; + olddev->data.rdp.auth.expires =3D dev->data.rdp.auth.expires; + olddev->data.rdp.auth.connected =3D dev->data.rdp.auth.connect= ed; + } + break; + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 4fe7f4923e..8108d96de9 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -51,6 +51,7 @@ int qemuDomainFindGraphicsIndex(virDomainDef *def, int qemuDomainChangeGraphicsPasswords(virDomainObj *vm, int type, virDomainGraphicsAuthDef *auth, + const char *defaultUsername, const char *defaultPasswd, int asyncJob); =20 diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 34a755a49a..c0dd9c0b35 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -2949,18 +2949,29 @@ qemuProcessInitPasswords(virQEMUDriver *driver, =20 for (i =3D 0; i < vm->def->ngraphics; ++i) { virDomainGraphicsDef *graphics =3D vm->def->graphics[i]; - if (graphics->type =3D=3D VIR_DOMAIN_GRAPHICS_TYPE_VNC) { - ret =3D qemuDomainChangeGraphicsPasswords(vm, - VIR_DOMAIN_GRAPHICS_TY= PE_VNC, - &graphics->data.vnc.au= th, - cfg->vncPassword, - asyncJob); - } else if (graphics->type =3D=3D VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { - ret =3D qemuDomainChangeGraphicsPasswords(vm, - VIR_DOMAIN_GRAPHICS_TY= PE_SPICE, - &graphics->data.spice.= auth, - cfg->spicePassword, - asyncJob); + + switch (graphics->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + ret =3D qemuDomainChangeGraphicsPasswords( + vm, VIR_DOMAIN_GRAPHICS_TYPE_VNC, &graphics->data.vnc.auth= , NULL, + cfg->vncPassword, asyncJob); + break; + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + ret =3D qemuDomainChangeGraphicsPasswords( + vm, VIR_DOMAIN_GRAPHICS_TYPE_SPICE, &graphics->data.spice.= auth, + NULL, cfg->spicePassword, asyncJob); + break; + case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + ret =3D 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; } =20 if (ret < 0) @@ -4239,6 +4250,30 @@ qemuProcessSPICEAllocatePorts(virQEMUDriver *driver, return 0; } =20 +static int +qemuProcessRDPAllocatePorts(virQEMUDriver *driver, + virDomainGraphicsDef *graphics, + bool allocate) +{ + unsigned short port; + + if (!allocate) { + if (graphics->data.rdp.autoport) + graphics->data.rdp.port =3D 3389; + + return 0; + } + + if (graphics->data.rdp.autoport) { + if (virPortAllocatorAcquire(driver->rdpPorts, &port) < 0) + return -1; + graphics->data.rdp.port =3D port; + graphics->data.rdp.portReserved =3D true; + } + + return 0; +} + =20 static int qemuProcessVerifyHypervFeatures(virDomainDef *def, @@ -4943,8 +4978,16 @@ qemuProcessGraphicsReservePorts(virDomainGraphicsDef= *graphics, } break; =20 - 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 =3D true; + } + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: @@ -4983,8 +5026,12 @@ qemuProcessGraphicsAllocatePorts(virQEMUDriver *driv= er, return -1; break; =20 - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + if (qemuProcessRDPAllocatePorts(driver, graphics, allocate) < 0) + return -1; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: @@ -5151,8 +5198,11 @@ qemuProcessGraphicsSetupListen(virQEMUDriver *driver, listenAddr =3D cfg->spiceListen; break; =20 - case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_RDP: + listenAddr =3D cfg->rdpListen; + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: @@ -5435,6 +5485,23 @@ qemuProcessStartWarnShmem(virDomainObj *vm) } =20 =20 +static bool +virDomainDefHasDBus(const virDomainDef *def, bool p2p) +{ + size_t i =3D 0; + + for (i =3D 0; i < def->ngraphics; i++) { + virDomainGraphicsDef *graphics =3D def->graphics[i]; + + if (graphics->type =3D=3D VIR_DOMAIN_GRAPHICS_TYPE_DBUS) { + return graphics->data.dbus.p2p =3D=3D p2p; + } + } + + return false; +} + + static int qemuProcessStartValidateGraphics(virDomainObj *vm) { @@ -5453,8 +5520,30 @@ qemuProcessStartValidateGraphics(virDomainObj *vm) } break; =20 - 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 liste= ns 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 'replaceUse= r' attribute.")); + return -1; + } + if (!virDomainDefHasDBus(vm->def, false)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu-rdp support requires a D-Bus bus gr= aphics device.")); + return -1; + } + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: case VIR_DOMAIN_GRAPHICS_TYPE_DBUS: @@ -5939,6 +6028,42 @@ qemuProcessPrepareHostNetwork(virDomainObj *vm) return 0; } =20 +#include "qemu_rdp.h" + +static int +qemuPrepareGraphicsRdp(virQEMUDriver *driver, + virDomainGraphicsDef *gfx) +{ + g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); + g_autoptr(qemuRdp) rdp =3D NULL; + + if (!(rdp =3D qemuRdpNewForHelper(cfg->qemuRdpName))) + return -1; + + QEMU_DOMAIN_GRAPHICS_PRIVATE(gfx)->rdp =3D g_steal_pointer(&rdp); + + return 0; +} + + +static int +qemuProcessPrepareGraphics(virDomainObj *vm) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + size_t i; + + for (i =3D 0; i < vm->def->ngraphics; i++) { + virDomainGraphicsDef *gfx =3D vm->def->graphics[i]; + + if (gfx->type =3D=3D VIR_DOMAIN_GRAPHICS_TYPE_RDP && + qemuPrepareGraphicsRdp(priv->driver, gfx) < 0) + return -1; + + } + + return 0; +} + =20 struct qemuProcessSetupVcpuSchedCoreHelperData { pid_t vcpupid; @@ -7410,6 +7535,10 @@ qemuProcessPrepareHost(virQEMUDriver *driver, if (qemuProcessPrepareHostNetwork(vm) < 0) return -1; =20 + VIR_DEBUG("Preparing graphics"); + if (qemuProcessPrepareGraphics(vm) < 0) + return -1; + /* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); if (!cfg->relaxedACS) @@ -8982,6 +9111,12 @@ void qemuProcessStop(virQEMUDriver *driver, graphics->data.spice.tlsPortReserved =3D false; } } + if (graphics->type =3D=3D VIR_DOMAIN_GRAPHICS_TYPE_RDP) { + if (graphics->data.rdp.portReserved) { + virPortAllocatorRelease(graphics->data.rdp.port); + graphics->data.rdp.portReserved =3D false; + } + } } =20 for (i =3D 0; i < vm->ndeprecations; i++) --=20 2.47.0