diff --git a/lib/client_impl.c b/lib/client_impl.c index dfd14150..fd6cc3c5 100644 --- a/lib/client_impl.c +++ b/lib/client_impl.c @@ -260,7 +260,14 @@ static int make_request(DBusConnection *bus, int native, int use_pidfds, const c dbus_message_iter_append_basic(&iter, type, &p); } - dbus_connection_send_with_reply(bus, msg, &call, -1); + // send_with_reply can return TRUE with call == NULL if the connection is + // already disconnected, or if the message carries unix fds the transport + // cannot send. Blocking on a NULL pending call aborts in libdbus. + if (!dbus_connection_send_with_reply(bus, msg, &call, -1) || call == NULL) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, "Could not send D-Bus message"); + return -1; + } + dbus_connection_flush(bus); dbus_message_unref(msg); msg = NULL; diff --git a/lib/meson.build b/lib/meson.build index 7cdcf8a6..f68ddc61 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -77,3 +77,17 @@ gamemode_dep = declare_dependency( libgamemodeauto_dep = declare_dependency( link_with: libgamemodeauto, ) + +test_client_dead_bus = executable( + 'test-client-dead-bus', + 'test-client-dead-bus.c', + dependencies: [ + dep_dbus, + ], + link_with: libgamemode, +) + +test( + 'client does not abort after the session bus fd is closed', + test_client_dead_bus, +) diff --git a/lib/test-client-dead-bus.c b/lib/test-client-dead-bus.c new file mode 100644 index 00000000..6a976840 --- /dev/null +++ b/lib/test-client-dead-bus.c @@ -0,0 +1,68 @@ +/* + +Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Feral Interactive nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + */ + +#include +#include +#include + +extern int real_gamemode_request_end(void); + +int main(void) +{ + DBusError err; + DBusConnection *bus; + int fd = -1; + + dbus_error_init(&err); + bus = dbus_bus_get(DBUS_BUS_SESSION, &err); + if (bus == NULL) { + fprintf(stderr, "skip: no session bus (%s)\n", err.message ? err.message : "unknown"); + dbus_error_free(&err); + return 77; + } + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if (!dbus_connection_get_socket(bus, &fd) || fd < 0) { + fprintf(stderr, "skip: session bus has no socket\n"); + return 77; + } + + // Reproduce gamescopereaper CloseAllFds: the socket is gone, but libdbus + // still believes the connection is up. UnregisterGame used to abort here. + if (close(fd) != 0) { + perror("close"); + return 1; + } + + (void)real_gamemode_request_end(); + return 0; +}