From 6cc5a6159a0067fefbe0b7912c187018aa4b460a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 6 Jun 2023 15:56:42 +0400 Subject: [PATCH] ui/dbus: win32 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D-Bus doesn't support fd-passing on Windows (AF_UNIX doesn't have SCM_RIGHTS yet, but there are other means to share objects. I have proposed various solutions upstream, but none seem fitting enough atm). To make the "-display dbus" work on Windows, implement an alternative D-Bus interface where all the 'h' (FDs) arguments are replaced with 'ay' (WSASocketW data), and sockets are passed to the other end via WSADuplicateSocket(). Signed-off-by: Marc-André Lureau Message-Id: <20230606115658.677673-6-marcandre.lureau@redhat.com> --- audio/dbusaudio.c | 44 +++++++++++++++++++++++++++------ meson.build | 4 +-- ui/dbus-chardev.c | 22 +++++++++++++---- ui/dbus-console.c | 59 ++++++++++++++++++++++++++++++++++++++------ ui/dbus-display1.xml | 28 +++++++++++++++++++++ ui/dbus.h | 6 +++++ ui/meson.build | 9 ++++++- 7 files changed, 149 insertions(+), 23 deletions(-) diff --git a/audio/dbusaudio.c b/audio/dbusaudio.c index de59467d9e..7a11fbfb42 100644 --- a/audio/dbusaudio.c +++ b/audio/dbusaudio.c @@ -33,6 +33,7 @@ #include #endif +#include "ui/dbus.h" #include "ui/dbus-display1.h" #define AUDIO_CAP "dbus" @@ -422,7 +423,6 @@ dbus_audio_fini(void *opaque) g_free(da); } -#ifdef G_OS_UNIX static void listener_out_vanished_cb(GDBusConnection *connection, gboolean remote_peer_vanished, @@ -448,7 +448,9 @@ listener_in_vanished_cb(GDBusConnection *connection, static gboolean dbus_audio_register_listener(AudioState *s, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_listener, bool out) { @@ -475,6 +477,11 @@ dbus_audio_register_listener(AudioState *s, return DBUS_METHOD_INVOCATION_HANDLED; } +#ifdef G_OS_WIN32 + if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) { + return DBUS_METHOD_INVOCATION_HANDLED; + } +#else fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err); if (err) { g_dbus_method_invocation_return_error(invocation, @@ -484,6 +491,7 @@ dbus_audio_register_listener(AudioState *s, err->message); return DBUS_METHOD_INVOCATION_HANDLED; } +#endif socket = g_socket_new_from_fd(fd, &err); if (err) { @@ -492,15 +500,28 @@ dbus_audio_register_listener(AudioState *s, DBUS_DISPLAY_ERROR_FAILED, "Couldn't make a socket: %s", err->message); +#ifdef G_OS_WIN32 + closesocket(fd); +#else + close(fd); +#endif return DBUS_METHOD_INVOCATION_HANDLED; } socket_conn = g_socket_connection_factory_create_connection(socket); if (out) { qemu_dbus_display1_audio_complete_register_out_listener( - da->iface, invocation, NULL); + da->iface, invocation +#ifdef G_OS_UNIX + , NULL +#endif + ); } else { qemu_dbus_display1_audio_complete_register_in_listener( - da->iface, invocation, NULL); + da->iface, invocation +#ifdef G_OS_UNIX + , NULL +#endif + ); } listener_conn = @@ -578,24 +599,33 @@ dbus_audio_register_listener(AudioState *s, static gboolean dbus_audio_register_out_listener(AudioState *s, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_listener) { return dbus_audio_register_listener(s, invocation, - fd_list, arg_listener, true); +#ifdef G_OS_UNIX + fd_list, +#endif + arg_listener, true); } static gboolean dbus_audio_register_in_listener(AudioState *s, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_listener) { return dbus_audio_register_listener(s, invocation, - fd_list, arg_listener, false); -} +#ifdef G_OS_UNIX + fd_list, #endif + arg_listener, false); +} static void dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p) @@ -610,14 +640,12 @@ dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p) da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH); da->iface = qemu_dbus_display1_audio_skeleton_new(); -#ifdef G_OS_UNIX g_object_connect(da->iface, "swapped-signal::handle-register-in-listener", dbus_audio_register_in_listener, s, "swapped-signal::handle-register-out-listener", dbus_audio_register_out_listener, s, NULL); -#endif g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio), G_DBUS_INTERFACE_SKELETON(da->iface)); diff --git a/meson.build b/meson.build index b409788832..9a1ce43471 100644 --- a/meson.build +++ b/meson.build @@ -838,6 +838,8 @@ if gdbus_codegen.found() and get_option('cfi') gdbus_codegen_error = '@0@ uses gdbus-codegen, which does not support control flow integrity' endif +xml_pp = find_program('scripts/xml-preprocess.py') + lttng = not_found if 'ust' in get_option('trace_backends') lttng = dependency('lttng-ust', required: true, version: '>= 2.1', @@ -1985,8 +1987,6 @@ dbus_display = get_option('dbus_display') \ error_message: '-display dbus requires glib>=2.64') \ .require(gdbus_codegen.found(), error_message: gdbus_codegen_error.format('-display dbus')) \ - .require(targetos != 'windows', - error_message: '-display dbus is not available on Windows') \ .allowed() have_virtfs = get_option('virtfs') \ diff --git a/ui/dbus-chardev.c b/ui/dbus-chardev.c index 7154d81a9a..1d3a7122a1 100644 --- a/ui/dbus-chardev.c +++ b/ui/dbus-chardev.c @@ -110,18 +110,24 @@ dbus_chardev_init(DBusDisplay *dpy) dbus_display_chardev_foreach, dpy); } -#ifdef G_OS_UNIX static gboolean dbus_chr_register( DBusChardev *dc, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_stream, QemuDBusDisplay1Chardev *object) { g_autoptr(GError) err = NULL; int fd; +#ifdef G_OS_WIN32 + if (!dbus_win32_import_socket(invocation, arg_stream, &fd)) { + return DBUS_METHOD_INVOCATION_HANDLED; + } +#else fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_stream), &err); if (err) { g_dbus_method_invocation_return_error( @@ -131,13 +137,18 @@ dbus_chr_register( "Couldn't get peer FD: %s", err->message); return DBUS_METHOD_INVOCATION_HANDLED; } +#endif if (qemu_chr_add_client(CHARDEV(dc), fd) < 0) { g_dbus_method_invocation_return_error(invocation, DBUS_DISPLAY_ERROR, DBUS_DISPLAY_ERROR_FAILED, "Couldn't register FD!"); +#ifdef G_OS_WIN32 + closesocket(fd); +#else close(fd); +#endif return DBUS_METHOD_INVOCATION_HANDLED; } @@ -145,10 +156,13 @@ dbus_chr_register( "owner", g_dbus_method_invocation_get_sender(invocation), NULL); - qemu_dbus_display1_chardev_complete_register(object, invocation, NULL); + qemu_dbus_display1_chardev_complete_register(object, invocation +#ifndef G_OS_WIN32 + , NULL +#endif + ); return DBUS_METHOD_INVOCATION_HANDLED; } -#endif static gboolean dbus_chr_send_break( @@ -179,10 +193,8 @@ dbus_chr_open(Chardev *chr, ChardevBackend *backend, dc->iface = qemu_dbus_display1_chardev_skeleton_new(); g_object_set(dc->iface, "name", backend->u.dbus.data->name, NULL); g_object_connect(dc->iface, -#ifdef G_OS_UNIX "swapped-signal::handle-register", dbus_chr_register, dc, -#endif "swapped-signal::handle-send-break", dbus_chr_send_break, dc, NULL); diff --git a/ui/dbus-console.c b/ui/dbus-console.c index d5f6c93637..4a1c1fb55e 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -165,7 +165,6 @@ dbus_display_console_class_init(DBusDisplayConsoleClass *klass) gobject_class->dispose = dbus_display_console_dispose; } -#ifdef G_OS_UNIX static void listener_vanished_cb(DBusDisplayListener *listener) { @@ -177,7 +176,6 @@ listener_vanished_cb(DBusDisplayListener *listener) g_hash_table_remove(ddc->listeners, name); qkbd_state_lift_all_keys(ddc->kbd); } -#endif static gboolean dbus_console_set_ui_info(DBusDisplayConsole *ddc, @@ -211,11 +209,47 @@ dbus_console_set_ui_info(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } -#ifdef G_OS_UNIX +#ifdef G_OS_WIN32 +bool +dbus_win32_import_socket(GDBusMethodInvocation *invocation, + GVariant *arg_listener, int *socket) +{ + gsize n; + WSAPROTOCOL_INFOW *info = (void *)g_variant_get_fixed_array(arg_listener, &n, 1); + + if (!info || n != sizeof(*info)) { + g_dbus_method_invocation_return_error( + invocation, + DBUS_DISPLAY_ERROR, + DBUS_DISPLAY_ERROR_FAILED, + "Failed to get socket infos"); + return false; + } + + *socket = WSASocketW(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + info, 0, 0); + if (*socket == INVALID_SOCKET) { + g_autofree gchar *emsg = g_win32_error_message(WSAGetLastError()); + g_dbus_method_invocation_return_error( + invocation, + DBUS_DISPLAY_ERROR, + DBUS_DISPLAY_ERROR_FAILED, + "Couldn't create socket: %s", emsg); + return false; + } + + return true; +} +#endif + static gboolean dbus_console_register_listener(DBusDisplayConsole *ddc, GDBusMethodInvocation *invocation, +#ifdef G_OS_UNIX GUnixFDList *fd_list, +#endif GVariant *arg_listener) { const char *sender = g_dbus_method_invocation_get_sender(invocation); @@ -237,6 +271,11 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } +#ifdef G_OS_WIN32 + if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) { + return DBUS_METHOD_INVOCATION_HANDLED; + } +#else fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err); if (err) { g_dbus_method_invocation_return_error( @@ -246,6 +285,7 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, "Couldn't get peer fd: %s", err->message); return DBUS_METHOD_INVOCATION_HANDLED; } +#endif socket = g_socket_new_from_fd(fd, &err); if (err) { @@ -254,13 +294,21 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, DBUS_DISPLAY_ERROR, DBUS_DISPLAY_ERROR_FAILED, "Couldn't make a socket: %s", err->message); +#ifdef G_OS_WIN32 + closesocket(fd); +#else close(fd); +#endif return DBUS_METHOD_INVOCATION_HANDLED; } socket_conn = g_socket_connection_factory_create_connection(socket); qemu_dbus_display1_console_complete_register_listener( - ddc->iface, invocation, NULL); + ddc->iface, invocation +#ifdef G_OS_UNIX + , NULL +#endif + ); listener_conn = g_dbus_connection_new_sync( G_IO_STREAM(socket_conn), @@ -287,7 +335,6 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, trace_dbus_registered_listener(sender); return DBUS_METHOD_INVOCATION_HANDLED; } -#endif static gboolean dbus_kbd_press(DBusDisplayConsole *ddc, @@ -516,10 +563,8 @@ dbus_display_console_new(DBusDisplay *display, QemuConsole *con) "device-address", device_addr, NULL); g_object_connect(ddc->iface, -#ifdef G_OS_UNIX "swapped-signal::handle-register-listener", dbus_console_register_listener, ddc, -#endif "swapped-signal::handle-set-uiinfo", dbus_console_set_ui_info, ddc, NULL); diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml index cc0c9b68bf..cd596f774e 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -57,7 +57,13 @@ :dbus:iface:`org.qemu.Display1.Listener` interface. --> + + + + + + + + + + + + + + + + + + + @@ -760,7 +782,13 @@ The current handler, if any, will be replaced. --> + + + + + +