[PATCH 17/19] qemu: add qemu-rdp helper unit

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Marc-André Lureau <marcandre.lureau@xxxxxxxxxx>

Helpers to start the qemu-rdp server and set it up.

Signed-off-by: Marc-André Lureau <marcandre.lureau@xxxxxxxxxx>
---
 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 = [
   '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)
 
     g_free(priv->tlsAlias);
     g_clear_pointer(&priv->secinfo, qemuDomainSecretInfoFree);
+    g_clear_pointer(&priv->rdp, qemuRdpFree);
 }
 
 
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 03cf84695f..d3ccbcd63c 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -35,6 +35,7 @@
 #include "qemu_capabilities.h"
 #include "qemu_migration_params.h"
 #include "qemu_nbdkit.h"
+#include "qemu_rdp.h"
 #include "qemu_slirp.h"
 #include "qemu_fd.h"
 #include "virchrdev.h"
@@ -419,6 +420,7 @@ struct _qemuDomainGraphicsPrivate {
 
     char *tlsAlias;
     qemuDomainSecretInfo *secinfo;
+    qemuRdp *rdp;
 };
 
 
diff --git a/src/qemu/qemu_rdp.c b/src/qemu/qemu_rdp.c
new file mode 100644
index 0000000000..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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.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);
+}
+
+
+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 = 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 *pidfile = NULL;
+    g_autofree char *logpath = 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;
+    VIR_AUTOCLOSE logfd = -1;
+
+    if (rdp->pid != -1) {
+        return 0;
+    }
+
+    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;
+
+    logpath = qemuRdpCreateLogFilename(cfg, vm->def);
+
+    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;
+        }
+    }
+
+    cmd = 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 address"));
+        }
+        virCommandAddArgPair(cmd, "--dbus-address", dbus_addr);
+    }
+    virCommandAddArg(cmd, "serve");
+
+    virCommandAddArg(cmd, "--bind-address");
+    virCommandAddArgBuffer(cmd, &bind_addr);
+
+    certpath = g_build_filename(cfg->rdpTLSx509certdir, "server-cert.pem", NULL);
+    keypath = g_build_filename(cfg->rdpTLSx509certdir, "server-key.pem", NULL);
+    virCommandAddArgPair(cmd, "--cert", certpath);
+    virCommandAddArgPair(cmd, "--key", keypath);
+
+    if (qemuExtDeviceLogCommand(driver, vm, cmd, "qemu-rdp") < 0)
+        return -1;
+
+    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 (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) {
+        virReportSystemError(errno, "%s",
+                             _("qemu-rdp died unexpectedly"));
+        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




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux