From 1430c0b831c47f90649d12dac0e45a47a96688dc Mon Sep 17 00:00:00 2001 From: Marc Di Luzio Date: Sat, 9 Feb 2019 15:49:46 +0000 Subject: [PATCH] Implement supervisor features using new config variables This allows direct control over who can make requests on behalf of other processes require_supervisor can also be used to allow a supervisor to take direct control of gamemode on the system (perhaps a GUI, or game launcher) --- daemon/daemon_config.c | 61 ++++++++++++++++++++++++++++++++++++++++++ daemon/daemon_config.h | 7 +++++ daemon/gamemode.c | 60 +++++++++++++++++++++++++++++++++++------ example/gamemode.ini | 10 +++++++ 4 files changed, 130 insertions(+), 8 deletions(-) diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index 4570523..41ae584 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -94,6 +94,10 @@ struct GameModeConfig { long nv_perf_level; long amd_core_clock_percentage; long amd_mem_clock_percentage; + + long require_supervisor; + char supervisor_whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX]; + char supervisor_blacklist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX]; } values; }; @@ -261,6 +265,15 @@ static int inih_handler(void *user, const char *section, const char *name, const } else if (strcmp(name, "amd_mem_clock_percentage") == 0) { valid = get_long_value(name, value, &self->values.amd_mem_clock_percentage); } + } else if (strcmp(section, "supervisor") == 0) { + /* Supervisor subsection */ + if (strcmp(name, "supervisor_whitelist") == 0) { + valid = append_value_to_list(name, value, self->values.supervisor_whitelist); + } else if (strcmp(name, "supervisor_blacklist") == 0) { + valid = append_value_to_list(name, value, self->values.supervisor_blacklist); + } else if (strcmp(name, "require_supervisor") == 0) { + valid = get_long_value(name, value, &self->values.require_supervisor); + } } else if (strcmp(section, "custom") == 0) { /* Custom subsection */ if (strcmp(name, "start") == 0) { @@ -568,3 +581,51 @@ DEFINE_CONFIG_GET(nv_mem_clock_mhz_offset) DEFINE_CONFIG_GET(nv_perf_level) DEFINE_CONFIG_GET(amd_core_clock_percentage) DEFINE_CONFIG_GET(amd_mem_clock_percentage) + +/* + char supervisor_whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX]; + char supervisor_blacklist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX]; +*/ +DEFINE_CONFIG_GET(require_supervisor) + +/* + * Checks if the supervisor is whitelisted + */ +bool config_get_supervisor_whitelisted(GameModeConfig *self, const char *supervisor) +{ + /* Take the read lock for the internal data */ + pthread_rwlock_rdlock(&self->rwlock); + + /* If the whitelist is empty then everything passes */ + bool found = true; + if (self->values.supervisor_whitelist[0][0]) { + /* + * Check if the value is found in our whitelist + * Currently is a simple strstr check, but could be modified for wildcards etc. + */ + found = config_string_list_contains(supervisor, self->values.supervisor_whitelist); + } + + /* release the lock */ + pthread_rwlock_unlock(&self->rwlock); + return found; +} + +/* + * Checks if the supervisor is blacklisted + */ +bool config_get_supervisor_blacklisted(GameModeConfig *self, const char *supervisor) +{ + /* Take the read lock for the internal data */ + pthread_rwlock_rdlock(&self->rwlock); + + /* + * Check if the value is found in our whitelist + * Currently is a simple strstr check, but could be modified for wildcards etc. + */ + bool found = config_string_list_contains(supervisor, self->values.supervisor_blacklist); + + /* release the lock */ + pthread_rwlock_unlock(&self->rwlock); + return found; +} diff --git a/daemon/daemon_config.h b/daemon/daemon_config.h index 01acd9d..f0d6900 100644 --- a/daemon/daemon_config.h +++ b/daemon/daemon_config.h @@ -141,3 +141,10 @@ long config_get_nv_mem_clock_mhz_offset(GameModeConfig *self); long config_get_nv_perf_level(GameModeConfig *self); long config_get_amd_core_clock_percentage(GameModeConfig *self); long config_get_amd_mem_clock_percentage(GameModeConfig *self); + +/** + * Functions to get supervisor config permissions + */ +long config_get_require_supervisor(GameModeConfig *self); +bool config_get_supervisor_whitelisted(GameModeConfig *self, const char *supervisor); +bool config_get_supervisor_blacklisted(GameModeConfig *self, const char *supervisor); diff --git a/daemon/gamemode.c b/daemon/gamemode.c index aff402d..5acffb9 100644 --- a/daemon/gamemode.c +++ b/daemon/gamemode.c @@ -504,13 +504,36 @@ int game_mode_context_query_status(GameModeContext *self, pid_t client) /** * Stub to register on behalf of caller + * TODO: long config_get_require_supervisor(GameModeConfig *self); */ int game_mode_context_register_by_pid(GameModeContext *self, pid_t callerpid, pid_t gamepid) { - (void)self; - (void)callerpid; - (void)gamepid; - return 0; + 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, try and register the game */ + return game_mode_context_register(self, gamepid); + +error_cleanup: + free(executable); + return status; } /** @@ -518,10 +541,31 @@ 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) { - (void)self; - (void)callerpid; - (void)gamepid; - return 0; + 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, try and register the game */ + return game_mode_context_unregister(self, gamepid); + +error_cleanup: + free(executable); + return status; } /** diff --git a/example/gamemode.ini b/example/gamemode.ini index da101e4..beac406 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -61,6 +61,16 @@ inhibit_screensaver=1 ;amd_core_clock_percentage=0 ;amd_mem_clock_percentage=0 +[supervisor] +; This section controls the new gamemode functions gamemode_request_start_for and gamemode_request_end_for +; The whilelist and blacklist control which supervisor programs are allowed to make the above requests +;supervisor_whitelist= +;supervisor_blacklist= + +; In case you want to allow a supervisor to take full control of gamemode, this option can be set +; This will only allow gamemode clients to be registered by using the above functions by a supervisor client +;require_supervisor=0 + [custom] ; Custom scripts (executed using the shell) when gamemode starts and ends ;start=notify-send "GameMode started"