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 1738158545026896.5991147769527; Wed, 29 Jan 2025 05:49:05 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 78E71177B; Wed, 29 Jan 2025 08:49:04 -0500 (EST) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 1F6091764; Wed, 29 Jan 2025 08:42:52 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id 81A79150E; Wed, 29 Jan 2025 08:42:48 -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 3A8081725 for ; Wed, 29 Jan 2025 08:42:28 -0500 (EST) Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-557-00NjjR-dOVGjB7dmTKGDmg-1; Wed, 29 Jan 2025 08:42:26 -0500 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id AD8EF18009D9 for ; Wed, 29 Jan 2025 13:42:25 +0000 (UTC) Received: from localhost (unknown [10.39.208.35]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2851D19560A3; Wed, 29 Jan 2025 13:42:23 +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=1738158147; 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=w8sKHb79iVH5gQSh9byCF8KZaAVIQxQnttj7Lo/823E=; b=U8wt82OhQiVesm2ZWWcBxxEIQJFWt9gA9FAO60UmS2ecpHXLl6oOJVbrxztadrJH8ilRXj N5HPenKR8haKpX+ZkHLknR/fguv19sWDQZXoziaeY64weM3HU9MPOBZkQt3Qg6mK3d0R82 p3NUQx0oksT0ZLZl1C91gSqq37UdZts= X-MC-Unique: 00NjjR-dOVGjB7dmTKGDmg-1 X-Mimecast-MFC-AGG-ID: 00NjjR-dOVGjB7dmTKGDmg From: marcandre.lureau@redhat.com To: devel@lists.libvirt.org Subject: [PATCH 17/19] qemu: add qemu-rdp helper unit Date: Wed, 29 Jan 2025 17:40:39 +0400 Message-ID: <20250129134042.1282472-18-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.17 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: nXLPwKxsK-_1wVhrfSXI71DkKHALceM4RW8a40Rl58M_1738158145 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: II6EZBHJOSIY62NN5YUGBC2ES5T22AJH X-Message-ID-Hash: II6EZBHJOSIY62NN5YUGBC2ES5T22AJH 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: 1738158546254019000 Content-Type: text/plain; charset="utf-8" From: Marc-Andr=C3=A9 Lureau Helpers to start the qemu-rdp server and set it up. Signed-off-by: Marc-Andr=C3=A9 Lureau --- po/POTFILES | 1 + src/qemu/meson.build | 1 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain.h | 2 + src/qemu/qemu_rdp.c | 427 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_rdp.h | 71 +++++++ 6 files changed, 503 insertions(+) create mode 100644 src/qemu/qemu_rdp.c create mode 100644 src/qemu/qemu_rdp.h diff --git a/po/POTFILES b/po/POTFILES index d4b3de781b..0c83affb44 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -195,6 +195,7 @@ src/qemu/qemu_passt.c src/qemu/qemu_postparse.c src/qemu/qemu_process.c src/qemu/qemu_qapi.c +src/qemu/qemu_rdp.c src/qemu/qemu_saveimage.c src/qemu/qemu_slirp.c src/qemu/qemu_snapshot.c diff --git a/src/qemu/meson.build b/src/qemu/meson.build index 43a8ad7c3b..7a07d4f2c4 100644 --- a/src/qemu/meson.build +++ b/src/qemu/meson.build @@ -34,6 +34,7 @@ qemu_driver_sources =3D [ 'qemu_postparse.c', 'qemu_process.c', 'qemu_qapi.c', + 'qemu_rdp.c', 'qemu_saveimage.c', 'qemu_security.c', 'qemu_snapshot.c', diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index c7d7ac26ce..7ad31c5844 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1038,6 +1038,7 @@ qemuDomainGraphicsPrivateDispose(void *obj) =20 g_free(priv->tlsAlias); g_clear_pointer(&priv->secinfo, qemuDomainSecretInfoFree); + g_clear_pointer(&priv->rdp, qemuRdpFree); } =20 =20 diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 03cf84695f..d3ccbcd63c 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -35,6 +35,7 @@ #include "qemu_capabilities.h" #include "qemu_migration_params.h" #include "qemu_nbdkit.h" +#include "qemu_rdp.h" #include "qemu_slirp.h" #include "qemu_fd.h" #include "virchrdev.h" @@ -419,6 +420,7 @@ struct _qemuDomainGraphicsPrivate { =20 char *tlsAlias; qemuDomainSecretInfo *secinfo; + qemuRdp *rdp; }; =20 =20 diff --git a/src/qemu/qemu_rdp.c b/src/qemu/qemu_rdp.c new file mode 100644 index 0000000000..b1b03ad803 --- /dev/null +++ b/src/qemu/qemu_rdp.c @@ -0,0 +1,427 @@ +/* + * qemu_rdp.c: QEMU Rdp support + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include +#include +#include +#include + +#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 =3D g_new0(qemuRdp, 1); + + rdp->features =3D virBitmapNew(QEMU_RDP_FEATURE_LAST); + rdp->pid =3D -1; + + return g_steal_pointer(&rdp); +} + + +qemuRdp * +qemuRdpNewForHelper(const char *helper) +{ + g_autoptr(qemuRdp) rdp =3D NULL; + g_autoptr(virCommand) cmd =3D NULL; + g_autofree char *output =3D NULL; + g_autoptr(virJSONValue) doc =3D NULL; + virJSONValue *featuresJSON; + g_autofree char *helperPath =3D NULL; + size_t i, nfeatures; + + helperPath =3D virFindFileInPath(helper); + if (!helperPath) { + virReportSystemError(errno, + _("'%1$s' is not a suitable qemu-rdp helper n= ame"), + helper); + return NULL; + } + + rdp =3D qemuRdpNew(); + cmd =3D virCommandNewArgList(helperPath, "--print-capabilities", NULL); + virCommandSetOutputBuffer(cmd, &output); + if (virCommandRun(cmd, NULL) < 0) + return NULL; + + if (!(doc =3D virJSONValueFromString(output)) || + !(featuresJSON =3D virJSONValueObjectGetArray(doc, "features"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to parse json capabilities '%1$s'"), + helper); + return NULL; + } + + nfeatures =3D virJSONValueArraySize(featuresJSON); + for (i =3D 0; i < nfeatures; i++) { + virJSONValue *item =3D virJSONValueArrayGet(featuresJSON, i); + const char *tmpStr =3D virJSONValueGetString(item); + int tmp; + + if ((tmp =3D qemuRdpFeatureTypeFromString(tmpStr)) <=3D 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 =3D vm->privateData; + virQEMUDriver *driver =3D priv->driver; + g_autofree char *shortName =3D virDomainDefGetShortName(vm->def); + g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); + g_autofree char *name =3D NULL; + + name =3D g_strdup_printf("%s-rdp", shortName); + + return virPidFileBuildPath(cfg->rdpStateDir, name); +} + + +static char * +qemuRdpCreateLogFilename(virQEMUDriverConfig *cfg, + const virDomainDef *def) +{ + return virFileBuildPath(cfg->logDir, def->name, "-qemu-rdp.log"); +} + + +void +qemuRdpStop(virDomainObj *vm, virDomainGraphicsDef *gfx) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + qemuDomainGraphicsPrivate *gfxpriv =3D QEMU_DOMAIN_GRAPHICS_PRIVATE(gf= x); + qemuRdp *rdp =3D gfxpriv->rdp; + g_autofree char *pidfile =3D qemuRdpCreatePidFilename(vm); + virErrorPtr orig_err; + + if (!rdp) + return; + + if (rdp->leaving_id) { + g_dbus_connection_signal_unsubscribe(priv->dbusConnection, rdp->le= aving_id); + rdp->leaving_id =3D 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 =3D -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 =3D 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 =3D 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 =3D user_data; + + VIR_DEBUG("'%s' appeared", name); + rdp->name_appeared =3D true; + + if (!rdp->leaving_id) { + rdp->leaving_id =3D 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 =3D user_data; + + if (rdp->name_appeared && rdp->leaving_id) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("'%1$s' vanished unexpect= edly"), name); + } +} + + +int +qemuRdpStart(virDomainObj *vm, virDomainGraphicsDef *gfx) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + virQEMUDriver *driver =3D priv->driver; + qemuDomainGraphicsPrivate *gfxpriv =3D QEMU_DOMAIN_GRAPHICS_PRIVATE(gf= x); + qemuRdp *rdp =3D gfxpriv->rdp; + virDomainGraphicsListenDef *glisten =3D NULL; + g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); + g_autoptr(virCommand) cmd =3D NULL; + g_autofree char *pidfile =3D NULL; + g_autofree char *logpath =3D NULL; + g_autofree char *certpath =3D NULL; + g_autofree char *keypath =3D NULL; + g_autofree char *dbus_addr =3D qemuDBusGetAddress(driver, vm); + g_auto(virBuffer) bind_addr =3D VIR_BUFFER_INITIALIZER; + pid_t pid =3D -1; + VIR_AUTOCLOSE logfd =3D -1; + + if (rdp->pid !=3D -1) { + return 0; + } + + if (!(glisten =3D 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 =3D strchr(glisten->address, ':') !=3D 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 =3D qemuRdpCreatePidFilename(vm))) + return -1; + + logpath =3D qemuRdpCreateLogFilename(cfg, vm->def); + + if (cfg->stdioLogD) { + g_autoptr(virLogManager) logManager =3D virLogManagerNew(driver->p= rivileged); + + if (!logManager) + goto error; + + if ((logfd =3D virLogManagerDomainOpenLogFile(logManager, + "qemu", + vm->def->uuid, + vm->def->name, + logpath, + 0, + NULL, NULL)) < 0) + goto error; + } else { + if ((logfd =3D open(logpath, O_WRONLY | O_CREAT | O_APPEND, S_IRUS= R | 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 fla= g on %1$s"), + logpath); + goto error; + } + } + + cmd =3D virCommandNew(cfg->qemuRdpName); + virCommandClearCaps(cmd); + virCommandSetPidFile(cmd, pidfile); + virCommandSetOutputFD(cmd, &logfd); + virCommandSetErrorFD(cmd, &logfd); + virCommandDaemonize(cmd); + if (dbus_addr) { + if (!qemuRdpHasFeature(rdp, QEMU_RDP_FEATURE_DBUS_ADDRESS)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu-rdp doesn't support custom D-Bus addres= s")); + } + virCommandAddArgPair(cmd, "--dbus-address", dbus_addr); + } + virCommandAddArg(cmd, "serve"); + + virCommandAddArg(cmd, "--bind-address"); + virCommandAddArgBuffer(cmd, &bind_addr); + + certpath =3D g_build_filename(cfg->rdpTLSx509certdir, "server-cert.pem= ", NULL); + keypath =3D g_build_filename(cfg->rdpTLSx509certdir, "server-key.pem",= NULL); + virCommandAddArgPair(cmd, "--cert", certpath); + virCommandAddArgPair(cmd, "--key", keypath); + + if (qemuExtDeviceLogCommand(driver, vm, cmd, "qemu-rdp") < 0) + return -1; + + rdp->name_watch =3D g_bus_watch_name_on_connection(priv->dbusConnectio= n, + ORG_QEMUDISPLAY_RDP, + G_BUS_NAME_WATCHER_FL= AGS_NONE, + name_appeared_cb, + name_vanished_cb, + rdp, + NULL); + + if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, false, NULL) < 0) + goto error; + + if (virPidFileReadPath(pidfile, &pid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to read qemu-rdp pidfile '%1$s'"), + pidfile); + goto error; + } + + if (virProcessKill(pid, 0) !=3D 0) { + virReportSystemError(errno, "%s", + _("qemu-rdp died unexpectedly")); + goto error; + } + + rdp->pid =3D 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 =3D vm->privateData; + g_autoptr(GVariant) args =3D NULL; + + args =3D 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 + * . + */ + +#pragma once + +#include "qemu_conf.h" +#include "virbitmap.h" +#include "virenum.h" + +typedef enum { + QEMU_RDP_FEATURE_NONE =3D 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); --=20 2.47.0