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