Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
ui/dbus: win32 support
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 <marcandre.lureau@redhat.com>
Message-Id: <20230606115658.677673-6-marcandre.lureau@redhat.com>
  • Loading branch information
elmarco committed Jun 27, 2023
1 parent 9b286e7 commit 6cc5a61
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 23 deletions.
44 changes: 36 additions & 8 deletions audio/dbusaudio.c
Expand Up @@ -33,6 +33,7 @@
#include <gio/gunixfdlist.h>
#endif

#include "ui/dbus.h"
#include "ui/dbus-display1.h"

#define AUDIO_CAP "dbus"
Expand Down Expand Up @@ -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,
Expand All @@ -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)
{
Expand All @@ -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,
Expand All @@ -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) {
Expand All @@ -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 =
Expand Down Expand Up @@ -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)
Expand All @@ -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));
Expand Down
4 changes: 2 additions & 2 deletions meson.build
Expand Up @@ -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',
Expand Down Expand Up @@ -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') \
Expand Down
22 changes: 17 additions & 5 deletions ui/dbus-chardev.c
Expand Up @@ -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(
Expand All @@ -131,24 +137,32 @@ 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;
}

g_object_set(dc->iface,
"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(
Expand Down Expand Up @@ -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);
Expand Down
59 changes: 52 additions & 7 deletions ui/dbus-console.c
Expand Up @@ -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)
{
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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(
Expand All @@ -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) {
Expand All @@ -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),
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 6cc5a61

Please sign in to comment.