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