qemu/ui/dbus-chardev.c
<<
>>
Prefs
   1/*
   2 * QEMU DBus display
   3 *
   4 * Copyright (c) 2021 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#include "qemu/osdep.h"
  25#include "trace.h"
  26#include "qapi/error.h"
  27#include "qemu/config-file.h"
  28#include "qemu/option.h"
  29
  30#include <gio/gunixfdlist.h>
  31
  32#include "dbus.h"
  33
  34static char *
  35dbus_display_chardev_path(DBusChardev *chr)
  36{
  37    return g_strdup_printf(DBUS_DISPLAY1_ROOT "/Chardev_%s",
  38                           CHARDEV(chr)->label);
  39}
  40
  41static void
  42dbus_display_chardev_export(DBusDisplay *dpy, DBusChardev *chr)
  43{
  44    g_autoptr(GDBusObjectSkeleton) sk = NULL;
  45    g_autofree char *path = dbus_display_chardev_path(chr);
  46
  47    if (chr->exported) {
  48        return;
  49    }
  50
  51    sk = g_dbus_object_skeleton_new(path);
  52    g_dbus_object_skeleton_add_interface(
  53        sk, G_DBUS_INTERFACE_SKELETON(chr->iface));
  54    g_dbus_object_manager_server_export(dpy->server, sk);
  55    chr->exported = true;
  56}
  57
  58static void
  59dbus_display_chardev_unexport(DBusDisplay *dpy, DBusChardev *chr)
  60{
  61    g_autofree char *path = dbus_display_chardev_path(chr);
  62
  63    if (!chr->exported) {
  64        return;
  65    }
  66
  67    g_dbus_object_manager_server_unexport(dpy->server, path);
  68    chr->exported = false;
  69}
  70
  71static int
  72dbus_display_chardev_foreach(Object *obj, void *data)
  73{
  74    DBusDisplay *dpy = DBUS_DISPLAY(data);
  75
  76    if (!CHARDEV_IS_DBUS(obj)) {
  77        return 0;
  78    }
  79
  80    dbus_display_chardev_export(dpy, DBUS_CHARDEV(obj));
  81
  82    return 0;
  83}
  84
  85static void
  86dbus_display_on_notify(Notifier *notifier, void *data)
  87{
  88    DBusDisplay *dpy = container_of(notifier, DBusDisplay, notifier);
  89    DBusDisplayEvent *event = data;
  90
  91    switch (event->type) {
  92    case DBUS_DISPLAY_CHARDEV_OPEN:
  93        dbus_display_chardev_export(dpy, event->chardev);
  94        break;
  95    case DBUS_DISPLAY_CHARDEV_CLOSE:
  96        dbus_display_chardev_unexport(dpy, event->chardev);
  97        break;
  98    }
  99}
 100
 101void
 102dbus_chardev_init(DBusDisplay *dpy)
 103{
 104    dpy->notifier.notify = dbus_display_on_notify;
 105    dbus_display_notifier_add(&dpy->notifier);
 106
 107    object_child_foreach(container_get(object_get_root(), "/chardevs"),
 108                         dbus_display_chardev_foreach, dpy);
 109}
 110
 111static gboolean
 112dbus_chr_register(
 113    DBusChardev *dc,
 114    GDBusMethodInvocation *invocation,
 115    GUnixFDList *fd_list,
 116    GVariant *arg_stream,
 117    QemuDBusDisplay1Chardev *object)
 118{
 119    g_autoptr(GError) err = NULL;
 120    int fd;
 121
 122    fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_stream), &err);
 123    if (err) {
 124        g_dbus_method_invocation_return_error(
 125            invocation,
 126            DBUS_DISPLAY_ERROR,
 127            DBUS_DISPLAY_ERROR_FAILED,
 128            "Couldn't get peer FD: %s", err->message);
 129        return DBUS_METHOD_INVOCATION_HANDLED;
 130    }
 131
 132    if (qemu_chr_add_client(CHARDEV(dc), fd) < 0) {
 133        g_dbus_method_invocation_return_error(invocation,
 134                                              DBUS_DISPLAY_ERROR,
 135                                              DBUS_DISPLAY_ERROR_FAILED,
 136                                              "Couldn't register FD!");
 137        close(fd);
 138        return DBUS_METHOD_INVOCATION_HANDLED;
 139    }
 140
 141    g_object_set(dc->iface,
 142                 "owner", g_dbus_method_invocation_get_sender(invocation),
 143                 NULL);
 144
 145    qemu_dbus_display1_chardev_complete_register(object, invocation, NULL);
 146    return DBUS_METHOD_INVOCATION_HANDLED;
 147}
 148
 149static gboolean
 150dbus_chr_send_break(
 151    DBusChardev *dc,
 152    GDBusMethodInvocation *invocation,
 153    QemuDBusDisplay1Chardev *object)
 154{
 155    qemu_chr_be_event(CHARDEV(dc), CHR_EVENT_BREAK);
 156
 157    qemu_dbus_display1_chardev_complete_send_break(object, invocation);
 158    return DBUS_METHOD_INVOCATION_HANDLED;
 159}
 160
 161static void
 162dbus_chr_open(Chardev *chr, ChardevBackend *backend,
 163              bool *be_opened, Error **errp)
 164{
 165    ERRP_GUARD();
 166
 167    DBusChardev *dc = DBUS_CHARDEV(chr);
 168    DBusDisplayEvent event = {
 169        .type = DBUS_DISPLAY_CHARDEV_OPEN,
 170        .chardev = dc,
 171    };
 172    g_autoptr(ChardevBackend) be = NULL;
 173    g_autoptr(QemuOpts) opts = NULL;
 174
 175    dc->iface = qemu_dbus_display1_chardev_skeleton_new();
 176    g_object_set(dc->iface, "name", backend->u.dbus.data->name, NULL);
 177    g_object_connect(dc->iface,
 178                     "swapped-signal::handle-register",
 179                     dbus_chr_register, dc,
 180                     "swapped-signal::handle-send-break",
 181                     dbus_chr_send_break, dc,
 182                     NULL);
 183
 184    dbus_display_notify(&event);
 185
 186    be = g_new0(ChardevBackend, 1);
 187    opts = qemu_opts_create(qemu_find_opts("chardev"), NULL, 0, &error_abort);
 188    qemu_opt_set(opts, "server", "on", &error_abort);
 189    qemu_opt_set(opts, "wait", "off", &error_abort);
 190    CHARDEV_CLASS(object_class_by_name(TYPE_CHARDEV_SOCKET))->parse(
 191        opts, be, errp);
 192    if (*errp) {
 193        return;
 194    }
 195    CHARDEV_CLASS(object_class_by_name(TYPE_CHARDEV_SOCKET))->open(
 196        chr, be, be_opened, errp);
 197}
 198
 199static void
 200dbus_chr_set_fe_open(Chardev *chr, int fe_open)
 201{
 202    DBusChardev *dc = DBUS_CHARDEV(chr);
 203
 204    g_object_set(dc->iface, "feopened", fe_open, NULL);
 205}
 206
 207static void
 208dbus_chr_set_echo(Chardev *chr, bool echo)
 209{
 210    DBusChardev *dc = DBUS_CHARDEV(chr);
 211
 212    g_object_set(dc->iface, "echo", echo, NULL);
 213}
 214
 215static void
 216dbus_chr_be_event(Chardev *chr, QEMUChrEvent event)
 217{
 218    DBusChardev *dc = DBUS_CHARDEV(chr);
 219    DBusChardevClass *klass = DBUS_CHARDEV_GET_CLASS(chr);
 220
 221    switch (event) {
 222    case CHR_EVENT_CLOSED:
 223        if (dc->iface) {
 224            /* on finalize, iface is set to NULL */
 225            g_object_set(dc->iface, "owner", "", NULL);
 226        }
 227        break;
 228    default:
 229        break;
 230    };
 231
 232    klass->parent_chr_be_event(chr, event);
 233}
 234
 235static void
 236dbus_chr_parse(QemuOpts *opts, ChardevBackend *backend,
 237               Error **errp)
 238{
 239    const char *name = qemu_opt_get(opts, "name");
 240    ChardevDBus *dbus;
 241
 242    if (name == NULL) {
 243        error_setg(errp, "chardev: dbus: no name given");
 244        return;
 245    }
 246
 247    backend->type = CHARDEV_BACKEND_KIND_DBUS;
 248    dbus = backend->u.dbus.data = g_new0(ChardevDBus, 1);
 249    qemu_chr_parse_common(opts, qapi_ChardevDBus_base(dbus));
 250    dbus->name = g_strdup(name);
 251}
 252
 253static void
 254char_dbus_class_init(ObjectClass *oc, void *data)
 255{
 256    DBusChardevClass *klass = DBUS_CHARDEV_CLASS(oc);
 257    ChardevClass *cc = CHARDEV_CLASS(oc);
 258
 259    cc->parse = dbus_chr_parse;
 260    cc->open = dbus_chr_open;
 261    cc->chr_set_fe_open = dbus_chr_set_fe_open;
 262    cc->chr_set_echo = dbus_chr_set_echo;
 263    klass->parent_chr_be_event = cc->chr_be_event;
 264    cc->chr_be_event = dbus_chr_be_event;
 265}
 266
 267static void
 268char_dbus_finalize(Object *obj)
 269{
 270    DBusChardev *dc = DBUS_CHARDEV(obj);
 271    DBusDisplayEvent event = {
 272        .type = DBUS_DISPLAY_CHARDEV_CLOSE,
 273        .chardev = dc,
 274    };
 275
 276    dbus_display_notify(&event);
 277    g_clear_object(&dc->iface);
 278}
 279
 280static const TypeInfo char_dbus_type_info = {
 281    .name = TYPE_CHARDEV_DBUS,
 282    .parent = TYPE_CHARDEV_SOCKET,
 283    .class_size = sizeof(DBusChardevClass),
 284    .instance_size = sizeof(DBusChardev),
 285    .instance_finalize = char_dbus_finalize,
 286    .class_init = char_dbus_class_init,
 287};
 288module_obj(TYPE_CHARDEV_DBUS);
 289
 290static void
 291register_types(void)
 292{
 293    type_register_static(&char_dbus_type_info);
 294}
 295
 296type_init(register_types);
 297