qemu/ui/spice-app.c
<<
>>
Prefs
   1/*
   2 * QEMU external Spice client display driver
   3 *
   4 * Copyright (c) 2018 Marc-André Lureau <marcandre.lureau@redhat.com>
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu/osdep.h"
  26
  27#include <gio/gio.h>
  28
  29#include "ui/console.h"
  30#include "ui/spice-display.h"
  31#include "qemu/config-file.h"
  32#include "qemu/option.h"
  33#include "qemu/cutils.h"
  34#include "qemu/module.h"
  35#include "qapi/error.h"
  36#include "io/channel-command.h"
  37#include "chardev/spice.h"
  38#include "sysemu/sysemu.h"
  39#include "qom/object.h"
  40
  41static const char *tmp_dir;
  42static char *app_dir;
  43static char *sock_path;
  44
  45struct VCChardev {
  46    SpiceChardev parent;
  47};
  48
  49struct VCChardevClass {
  50    ChardevClass parent;
  51    void (*parent_open)(Chardev *chr, ChardevBackend *backend,
  52                        bool *be_opened, Error **errp);
  53};
  54
  55#define TYPE_CHARDEV_VC "chardev-vc"
  56OBJECT_DECLARE_TYPE(VCChardev, VCChardevClass, CHARDEV_VC)
  57
  58static ChardevBackend *
  59chr_spice_backend_new(void)
  60{
  61    ChardevBackend *be = g_new0(ChardevBackend, 1);
  62
  63    be->type = CHARDEV_BACKEND_KIND_SPICEPORT;
  64    be->u.spiceport.data = g_new0(ChardevSpicePort, 1);
  65
  66    return be;
  67}
  68
  69static void vc_chr_open(Chardev *chr,
  70                        ChardevBackend *backend,
  71                        bool *be_opened,
  72                        Error **errp)
  73{
  74    VCChardevClass *vc = CHARDEV_VC_GET_CLASS(chr);
  75    ChardevBackend *be;
  76    const char *fqdn = NULL;
  77
  78    if (strstart(chr->label, "serial", NULL)) {
  79        fqdn = "org.qemu.console.serial.0";
  80    } else if (strstart(chr->label, "parallel", NULL)) {
  81        fqdn = "org.qemu.console.parallel.0";
  82    } else if (strstart(chr->label, "compat_monitor", NULL)) {
  83        fqdn = "org.qemu.monitor.hmp.0";
  84    }
  85
  86    be = chr_spice_backend_new();
  87    be->u.spiceport.data->fqdn = fqdn ?
  88        g_strdup(fqdn) : g_strdup_printf("org.qemu.console.%s", chr->label);
  89    vc->parent_open(chr, be, be_opened, errp);
  90    qapi_free_ChardevBackend(be);
  91}
  92
  93static void vc_chr_set_echo(Chardev *chr, bool echo)
  94{
  95    /* TODO: set echo for frontends QMP and qtest */
  96}
  97
  98static void char_vc_class_init(ObjectClass *oc, void *data)
  99{
 100    VCChardevClass *vc = CHARDEV_VC_CLASS(oc);
 101    ChardevClass *cc = CHARDEV_CLASS(oc);
 102
 103    vc->parent_open = cc->open;
 104
 105    cc->parse = qemu_chr_parse_vc;
 106    cc->open = vc_chr_open;
 107    cc->chr_set_echo = vc_chr_set_echo;
 108}
 109
 110static const TypeInfo char_vc_type_info = {
 111    .name = TYPE_CHARDEV_VC,
 112    .parent = TYPE_CHARDEV_SPICEPORT,
 113    .instance_size = sizeof(VCChardev),
 114    .class_init = char_vc_class_init,
 115    .class_size = sizeof(VCChardevClass),
 116};
 117
 118static void spice_app_atexit(void)
 119{
 120    if (sock_path) {
 121        unlink(sock_path);
 122    }
 123    if (tmp_dir) {
 124        rmdir(tmp_dir);
 125    }
 126    g_free(sock_path);
 127    g_free(app_dir);
 128}
 129
 130static void spice_app_display_early_init(DisplayOptions *opts)
 131{
 132    QemuOpts *qopts;
 133    QemuOptsList *list;
 134    GError *err = NULL;
 135
 136    if (opts->has_full_screen) {
 137        error_report("spice-app full-screen isn't supported yet.");
 138        exit(1);
 139    }
 140    if (opts->has_window_close) {
 141        error_report("spice-app window-close isn't supported yet.");
 142        exit(1);
 143    }
 144
 145    atexit(spice_app_atexit);
 146
 147    if (qemu_name) {
 148        app_dir = g_build_filename(g_get_user_runtime_dir(),
 149                                   "qemu", qemu_name, NULL);
 150        if (g_mkdir_with_parents(app_dir, S_IRWXU) < -1) {
 151            error_report("Failed to create directory %s: %s",
 152                         app_dir, strerror(errno));
 153            exit(1);
 154        }
 155    } else {
 156        app_dir = g_dir_make_tmp(NULL, &err);
 157        tmp_dir = app_dir;
 158        if (err) {
 159            error_report("Failed to create temporary directory: %s",
 160                         err->message);
 161            exit(1);
 162        }
 163    }
 164    list = qemu_find_opts("spice");
 165    if (list == NULL) {
 166        error_report("spice-app missing spice support");
 167        exit(1);
 168    }
 169
 170    type_register(&char_vc_type_info);
 171
 172    sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL);
 173    qopts = qemu_opts_create(list, NULL, 0, &error_abort);
 174    qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort);
 175    qemu_opt_set(qopts, "unix", "on", &error_abort);
 176    qemu_opt_set(qopts, "addr", sock_path, &error_abort);
 177    qemu_opt_set(qopts, "image-compression", "off", &error_abort);
 178    qemu_opt_set(qopts, "streaming-video", "off", &error_abort);
 179#ifdef HAVE_SPICE_GL
 180    qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort);
 181    display_opengl = opts->has_gl;
 182#endif
 183}
 184
 185static void spice_app_display_init(DisplayState *ds, DisplayOptions *opts)
 186{
 187    ChardevBackend *be = chr_spice_backend_new();
 188    QemuOpts *qopts;
 189    GError *err = NULL;
 190    gchar *uri;
 191
 192    be->u.spiceport.data->fqdn = g_strdup("org.qemu.monitor.qmp.0");
 193    qemu_chardev_new("org.qemu.monitor.qmp", TYPE_CHARDEV_SPICEPORT,
 194                     be, NULL, &error_abort);
 195    qopts = qemu_opts_create(qemu_find_opts("mon"),
 196                             NULL, 0, &error_fatal);
 197    qemu_opt_set(qopts, "chardev", "org.qemu.monitor.qmp", &error_abort);
 198    qemu_opt_set(qopts, "mode", "control", &error_abort);
 199
 200    qapi_free_ChardevBackend(be);
 201    uri = g_strjoin("", "spice+unix://", app_dir, "/", "spice.sock", NULL);
 202    info_report("Launching display with URI: %s", uri);
 203    g_app_info_launch_default_for_uri(uri, NULL, &err);
 204    if (err) {
 205        error_report("Failed to launch %s URI: %s", uri, err->message);
 206        error_report("You need a capable Spice client, "
 207                     "such as virt-viewer 8.0");
 208        exit(1);
 209    }
 210    g_free(uri);
 211}
 212
 213static QemuDisplay qemu_display_spice_app = {
 214    .type       = DISPLAY_TYPE_SPICE_APP,
 215    .early_init = spice_app_display_early_init,
 216    .init       = spice_app_display_init,
 217};
 218
 219static void register_spice_app(void)
 220{
 221    qemu_display_register(&qemu_display_spice_app);
 222}
 223
 224type_init(register_spice_app);
 225
 226module_dep("ui-spice-core");
 227module_dep("chardev-spice");
 228