diff --git a/lib/client_impl.c b/lib/client_impl.c index eea6329..66bdeab 100644 --- a/lib/client_impl.c +++ b/lib/client_impl.c @@ -31,6 +31,7 @@ POSSIBILITY OF SUCH DAMAGE. #define _GNU_SOURCE +#include #include #include #include @@ -38,9 +39,11 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include +// For developmental purposes +#define DO_TRACE 0 + // D-Bus name, path, iface #define DAEMON_DBUS_NAME "com.feralinteractive.GameMode" #define DAEMON_DBUS_PATH "/com/feralinteractive/GameMode" @@ -50,6 +53,29 @@ POSSIBILITY OF SUCH DAMAGE. #define PORTAL_DBUS_PATH "/org/freedesktop/portal/desktop" #define PORTAL_DBUS_IFACE "org.freedesktop.portal.GameMode" +// Cleanup macros +#define _cleanup_(x) __attribute__((cleanup(x))) +#define _cleanup_bus_ _cleanup_(hop_off_the_bus) +#define _cleanup_msg_ _cleanup_(cleanup_msg) +#define _cleanup_dpc_ _cleanup_(cleanup_pending_call) + +#ifdef NDEBUG +#define DEBUG(...) +#define LOG_ERROR +#else +#define DEBUG(...) fprintf(stderr, __VA_ARGS__) +#define LOG_ERROR fprintf(stderr, "ERROR: %s \n", error_string) +#endif + +#ifdef DO_TRACE +#define TRACE(...) fprintf(stderr, __VA_ARGS__) +#else +#define TRACE(...) +#endif + +// Prototypes +static int log_error(const char *fmt, ...) __attribute__((format(printf, 1, 2))); + // Storage for error strings static char error_string[512] = { 0 }; @@ -64,68 +90,138 @@ static int in_flatpak(void) return r == 0 && sb.st_size > 0; } -// Simple requestor function for a gamemode -static int gamemode_request(const char *function, int arg) +static int log_error(const char *fmt, ...) { - sd_bus_message *msg = NULL; - sd_bus *bus = NULL; - sd_bus_error err; - memset(&err, 0, sizeof(err)); + va_list args; + int n; - int result = -1; + va_start(args, fmt); + n = vsnprintf(error_string, sizeof(error_string), fmt, args); + va_end(args); - // Open the user bus - int ret = sd_bus_open_user(&bus); - if (ret < 0) { - snprintf(error_string, - sizeof(error_string), - "Could not connect to bus: %s", - strerror(-ret)); - } else { - int native = !in_flatpak(); + if (n < 0) + DEBUG("Failed to format error string"); + else if ((size_t)n >= sizeof(error_string)) + DEBUG("Error log overflow"); - // If we are inside a flatpak we need to talk to the portal instead - const char *dest = native ? DAEMON_DBUS_NAME : PORTAL_DBUS_NAME; - const char *path = native ? DAEMON_DBUS_PATH : PORTAL_DBUS_PATH; - const char *iface = native ? DAEMON_DBUS_IFACE : PORTAL_DBUS_IFACE; + TRACE("ERROR: %s \n", error_string); - // Attempt to send the requested function - ret = sd_bus_call_method(bus, - dest, - path, - iface, - function, - &err, - &msg, - arg ? "ii" : "i", - getpid(), - arg); - if (ret < 0) { - snprintf(error_string, - sizeof(error_string), - "Could not call method %s on %s\n" - "\t%s\n" - "\t%s\n" - "\t%s\n", - function, - dest, - err.name, - err.message, - strerror(-ret)); - } else { - // Read the reply - ret = sd_bus_message_read(msg, "i", &result); - if (ret < 0) { - snprintf(error_string, - sizeof(error_string), - "Failure to parse response: %s", - strerror(-ret)); - } - } - sd_bus_unref(bus); + return -1; +} + +static void hop_off_the_bus(DBusConnection **bus) +{ + if (bus == NULL) + return; + + dbus_connection_unref(*bus); +} + +static DBusConnection *hop_on_the_bus(void) +{ + DBusConnection *bus; + DBusError err; + + dbus_error_init(&err); + + bus = dbus_bus_get(DBUS_BUS_SESSION, &err); + + if (bus == NULL) { + log_error("Could not connect to bus: %s", err.message); + dbus_error_free(&err); } - return result; + return bus; +} + +/* cleanup functions */ +static void cleanup_msg(DBusMessage **msg) +{ + if (msg == NULL) + return; + + dbus_message_unref(*msg); +} + +static void cleanup_pending_call(DBusPendingCall **call) +{ + if (call == NULL) + return; + + dbus_pending_call_unref(*call); +} + +/* internal API */ +static int gamemode_request(const char *method, pid_t for_pid) +{ + _cleanup_bus_ DBusConnection *bus = NULL; + _cleanup_msg_ DBusMessage *msg = NULL; + _cleanup_dpc_ DBusPendingCall *call = NULL; + DBusMessageIter iter; + DBusError err; + dbus_int32_t pid; + int native; + int res = -1; + + native = !in_flatpak(); + pid = (dbus_int32_t)getpid(); + + TRACE("GM: [%d] request '%s' received (for pid: %d) [portal: %s]\n", + (int)pid, + method, + (int)for_pid, + (native ? "n" : "y")); + + bus = hop_on_the_bus(); + + if (bus == NULL) + return -1; + + // If we are inside a flatpak we need to talk to the portal instead + const char *dest = native ? DAEMON_DBUS_NAME : PORTAL_DBUS_NAME; + const char *path = native ? DAEMON_DBUS_PATH : PORTAL_DBUS_PATH; + const char *iface = native ? DAEMON_DBUS_IFACE : PORTAL_DBUS_IFACE; + + msg = dbus_message_new_method_call(dest, path, iface, method); + + if (!msg) + return log_error("Could not create dbus message"); + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &pid); + + if (for_pid != 0) { + dbus_int32_t p = (dbus_int32_t)for_pid; + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &p); + } + + dbus_connection_send_with_reply(bus, msg, &call, -1); + dbus_connection_flush(bus); + dbus_message_unref(msg); + msg = NULL; + + dbus_pending_call_block(call); + msg = dbus_pending_call_steal_reply(call); + + if (msg == NULL) + return log_error("Did not receive a reply"); + + dbus_error_init(&err); + + if (dbus_set_error_from_message(&err, msg)) + log_error("Could not call method '%s' on '%s': %s", method, dest, err.message); + else if (!dbus_message_iter_init(msg, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) + log_error("Failed to parse response"); + else + dbus_message_iter_get_basic(&iter, &res); + + TRACE("GM: [%d] request '%s' done: %d\n", (int)pid, method, res); + + if (dbus_error_is_set(&err)) + dbus_error_free(&err); + + return res; } // Get the error string diff --git a/lib/meson.build b/lib/meson.build index 5d25cc8..7378e59 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -12,7 +12,7 @@ gamemode = shared_library( 'client_impl.c', ], dependencies: [ - dep_systemd, + dep_dbus, ], install: true, soversion: lt_current, diff --git a/meson.build b/meson.build index 8a1a728..b7bb874 100644 --- a/meson.build +++ b/meson.build @@ -79,15 +79,19 @@ path_libdir = join_paths(path_prefix, get_option('libdir')) path_libexecdir = join_paths(path_prefix, get_option('libexecdir')) # Find systemd via pkgconfig +with_systemd = get_option('with-systemd') dep_systemd = dependency('libsystemd') +# For the client, libdbus is used +dep_dbus = dependency('dbus-1') + # Allow meson to figure out how the compiler sets up threading dep_threads = dependency('threads') # On non glibc systems this might be a stub, i.e. for musl libdl = cc.find_library('dl', required: false) -with_systemd = get_option('with-systemd') +# Determine the location for the systemd unit if with_systemd == true # If the path isn't explicitly set, ask systemd for the systemd user unit directory path_systemd_unit_dir = get_option('with-systemd-user-unit-dir')