diff --git a/daemon/dbus_messaging.c b/daemon/dbus_messaging.c index 157b4b3..d71ff47 100644 --- a/daemon/dbus_messaging.c +++ b/daemon/dbus_messaging.c @@ -162,6 +162,27 @@ static int method_unregister_game_by_pid(sd_bus_message *m, void *userdata, return sd_bus_reply_method_return(m, "i", reply); } +/** + * Handles the QueryStatus D-BUS Method + */ +static int method_query_status_for(sd_bus_message *m, void *userdata, + __attribute__((unused)) sd_bus_error *ret_error) +{ + int callerpid = 0; + int gamepid = 0; + GameModeContext *context = userdata; + + int ret = sd_bus_message_read(m, "ii", &callerpid, &gamepid); + if (ret < 0) { + LOG_ERROR("Failed to parse input parameters: %s\n", strerror(-ret)); + return ret; + } + + int status = game_mode_context_query_status_for(context, (pid_t)callerpid, (pid_t)gamepid); + + return sd_bus_reply_method_return(m, "i", status); +} + /** * D-BUS vtable to dispatch virtual methods */ @@ -174,6 +195,8 @@ static const sd_bus_vtable gamemode_vtable[] = SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("UnregisterGameByPID", "ii", "i", method_unregister_game_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("QueryStatusFor", "ii", "i", method_query_status_for, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; /** diff --git a/daemon/gamemode.c b/daemon/gamemode.c index 5acffb9..a8ede04 100644 --- a/daemon/gamemode.c +++ b/daemon/gamemode.c @@ -568,6 +568,38 @@ error_cleanup: return status; } +/** + * Request status on behalf of caller + */ +int game_mode_context_query_status_for(GameModeContext *self, pid_t callerpid, pid_t gamepid) +{ + int status = 0; + + /* Lookup the executable first */ + char *executable = game_mode_context_find_exe(callerpid); + if (!executable) { + status = -1; + goto error_cleanup; + } + + /* Check our blacklist and whitelist */ + if (!config_get_supervisor_whitelisted(self->config, executable)) { + LOG_MSG("Supervisor [%s] was rejected (not in whitelist)\n", executable); + status = -2; + goto error_cleanup; + } else if (config_get_supervisor_blacklisted(self->config, executable)) { + LOG_MSG("Supervisor [%s] was rejected (in blacklist)\n", executable); + status = -2; + goto error_cleanup; + } + /* Checks cleared, call the original query */ + return game_mode_context_query_status(self, gamepid); + +error_cleanup: + free(executable); + return status; +} + /** * Construct a new GameModeClient for the given process ID * diff --git a/daemon/gamemode.h b/daemon/gamemode.h index b092a2f..65df3c1 100644 --- a/daemon/gamemode.h +++ b/daemon/gamemode.h @@ -111,6 +111,17 @@ int game_mode_context_register_by_pid(GameModeContext *self, pid_t callerpid, pi */ int game_mode_context_unregister_by_pid(GameModeContext *self, pid_t callerpid, pid_t gamepid); +/** + * Query the current status of gamemode for another process + * + * @param pid Process ID for the remote client + * @returns Positive if gamemode is active + * 1 if gamemode is active but the client is not registered + * 2 if gamemode is active and the client is registered + * -2 if this supervisor was rejected + */ +int game_mode_context_query_status_for(GameModeContext *self, pid_t callerpid, pid_t gamepid); + /** * Query the config of a gamemode context * diff --git a/lib/client_impl.c b/lib/client_impl.c index 029d694..051ed0c 100644 --- a/lib/client_impl.c +++ b/lib/client_impl.c @@ -124,3 +124,9 @@ extern int real_gamemode_register_end_for(pid_t pid) { return gamemode_request("UnregisterGameByPID", pid); } + +// Wrapper to call QueryStatusFor +extern int real_gamemode_query_status_for(pid_t pid) +{ + return gamemode_request("QueryStatusFor", pid); +} diff --git a/lib/gamemode_client.h b/lib/gamemode_client.h index d379eec..2ae83a4 100644 --- a/lib/gamemode_client.h +++ b/lib/gamemode_client.h @@ -58,6 +58,10 @@ POSSIBILITY OF SUCH DAMAGE. * -1 if the request failed * -2 if the request was rejected * + * int gamemode_query_status_for(pid_t pid) - Query the current status of gamemode for another + * process 0 if gamemode is inactive 1 if gamemode is active 2 if gamemode is active and this client + * is registered -1 if the query failed + * * const char* gamemode_error_string() - Get an error string * returns a string describing any of the above errors */ @@ -95,6 +99,7 @@ static api_call_return_int REAL_internal_gamemode_query_status = NULL; static api_call_return_cstring REAL_internal_gamemode_error_string = NULL; static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL; static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL; +static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL; /** * Internal helper to perform the symbol binding safely. @@ -166,6 +171,10 @@ __attribute__((always_inline)) static inline int internal_load_libgamemode(void) (void **)&REAL_internal_gamemode_request_end_for, sizeof(REAL_internal_gamemode_request_end_for), false }, + { "real_gamemode_query_status_for", + (void **)&REAL_internal_gamemode_query_status_for, + sizeof(REAL_internal_gamemode_query_status_for), + false }, }; void *libgamemode = NULL; @@ -330,4 +339,22 @@ __attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t return REAL_internal_gamemode_request_end_for(pid); } +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_query_status_for == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_query_status_for missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_query_status_for(pid); +} + #endif // CLIENT_GAMEMODE_H