master
c 148 lines 4.11 KB
Raw
1 /*
2 * Standalone VNC server connecting to QEMU via D-Bus display interface.
3 *
4 * Copyright (C) 2026 Red Hat, Inc.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "qemu/osdep.h"
10
11 #include "qemu/sockets.h"
12 #include "qemu/error-report.h"
13 #include "qapi/util.h"
14 #include "qapi-types-char.h"
15 #include "ui/dbus-display1.h"
16 #include "trace.h"
17 #include "qemu-vnc.h"
18
19 typedef struct ChardevRegisterData {
20 QemuDBusDisplay1Chardev *proxy;
21 int local_fd;
22 char *name;
23 bool echo;
24 ChardevVCEncoding encoding;
25 } ChardevRegisterData;
26
27 static void
28 on_chardev_register_finished(GObject *source_object,
29 GAsyncResult *res,
30 gpointer user_data)
31 {
32 ChardevRegisterData *data = user_data;
33 g_autoptr(GError) err = NULL;
34 QemuTextConsole *tc;
35
36 if (!qemu_dbus_display1_chardev_call_register_finish(
37 data->proxy, NULL, res, &err)) {
38 error_report("Chardev Register failed for %s: %s",
39 data->name, err->message);
40 close(data->local_fd);
41 goto out;
42 }
43
44 tc = qemu_vnc_text_console_new(data->name, data->local_fd, data->echo,
45 data->encoding);
46 if (!tc) {
47 close(data->local_fd);
48 goto out;
49 }
50
51 trace_qemu_vnc_chardev_connected(data->name);
52
53 out:
54 g_object_unref(data->proxy);
55 g_free(data->name);
56 g_free(data);
57 }
58
59 /* Default chardevs to expose as VNC text consoles */
60 static const char * const default_names[] = {
61 "org.qemu.console.serial.0",
62 "org.qemu.monitor.hmp.0",
63 NULL,
64 };
65
66 /* Active chardev names list (points to CLI args or default_names) */
67 static const char * const *names;
68
69 static void
70 chardev_register(QemuDBusDisplay1Chardev *proxy,
71 ChardevVCEncoding encoding)
72 {
73 g_autoptr(GUnixFDList) fd_list = NULL;
74 ChardevRegisterData *data;
75 const char *name;
76 int pair[2];
77 int idx;
78
79 name = qemu_dbus_display1_chardev_get_name(proxy);
80 if (!name || !g_strv_contains(names, name)) {
81 return;
82 }
83
84 if (qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
85 error_report("chardev socketpair failed: %s", strerror(errno));
86 return;
87 }
88
89 fd_list = g_unix_fd_list_new();
90 idx = g_unix_fd_list_append(fd_list, pair[1], NULL);
91 close(pair[1]);
92
93 data = g_new0(ChardevRegisterData, 1);
94 data->proxy = g_object_ref(proxy);
95 data->local_fd = pair[0];
96 data->name = g_strdup(name);
97 data->echo = qemu_dbus_display1_chardev_get_echo(proxy);
98 data->encoding = encoding;
99
100 qemu_dbus_display1_chardev_call_register(
101 proxy, g_variant_new_handle(idx),
102 G_DBUS_CALL_FLAGS_NONE, -1,
103 fd_list, NULL,
104 on_chardev_register_finished, data);
105 }
106
107 void chardev_setup(const char * const *chardev_names,
108 GDBusObjectManager *manager)
109 {
110 GList *objects, *l;
111
112 names = chardev_names ? chardev_names : default_names;
113
114 objects = g_dbus_object_manager_get_objects(manager);
115 for (l = objects; l; l = l->next) {
116 GDBusObject *obj = l->data;
117 const char *path = g_dbus_object_get_object_path(obj);
118 g_autoptr(GDBusInterface) iface = NULL;
119 g_autoptr(GDBusInterface) enc_iface = NULL;
120 ChardevVCEncoding encoding = CHARDEV_VC_ENCODING_UTF8;
121
122 if (!g_str_has_prefix(path, DBUS_DISPLAY1_ROOT "/Chardev_")) {
123 continue;
124 }
125
126 iface = g_dbus_object_get_interface(
127 obj, "org.qemu.Display1.Chardev");
128 if (!iface) {
129 continue;
130 }
131
132 enc_iface = g_dbus_object_get_interface(
133 obj, "org.qemu.Display1.Chardev.VCEncoding");
134 if (enc_iface) {
135 const char *enc_str =
136 qemu_dbus_display1_chardev_vcencoding_get_encoding(
137 QEMU_DBUS_DISPLAY1_CHARDEV_VCENCODING(enc_iface));
138 int enc = qapi_enum_parse(&ChardevVCEncoding_lookup,
139 enc_str, -1, NULL);
140 if (enc >= 0) {
141 encoding = enc;
142 }
143 }
144
145 chardev_register(QEMU_DBUS_DISPLAY1_CHARDEV(iface), encoding);
146 }
147 g_list_free_full(objects, g_object_unref);
148 }