mirror of
https://github.com/libvirt/libvirt.git
synced 2026-10-02 12:20:06 -05:00
qemu: add RDP support
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>
This commit is contained in:
1 parent
f5e5a9bec9
commit
d80f1f8e63
6 files changed
+258
-32
No files matched your search
+16
-9
@@ -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
|
||||
|
||||
@@ -2025,6 +2025,7 @@ struct _virDomainGraphicsDef {
|
||||
} sdl;
|
||||
struct {
|
||||
int port;
|
||||
bool portReserved;
|
||||
bool autoport;
|
||||
bool replaceUser;
|
||||
bool multiUser;
|
||||
|
||||
@@ -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);
|
||||
|
||||
+50
-1
@@ -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:
|
||||
|
||||
@@ -57,6 +57,7 @@ int
|
||||
qemuDomainChangeGraphicsPasswords(virDomainObj *vm,
|
||||
int type,
|
||||
virDomainGraphicsAuthDef *auth,
|
||||
const char *defaultUsername,
|
||||
const char *defaultPasswd,
|
||||
int asyncJob);
|
||||
|
||||
|
||||
+150
-16
@@ -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++)
|
||||
|
||||
Reference in new issue
Block a user