mirror of
https://github.com/FeralInteractive/gamemode.git
synced 2025-06-06 07:37:21 +02:00
Merge pull request #106 from mdiluz/supervisor-support
Add "supervisor" support
This commit is contained in:
commit
326be7ebbd
12
README.md
12
README.md
@ -148,6 +148,18 @@ Developers can integrate the request directly into an app. Note that none of the
|
||||
|
||||
Or, distribute `libgamemodeauto.so` and either add `-lgamemodeauto` to your linker arguments, or add it to an LD\_PRELOAD in a launch script.
|
||||
|
||||
### Supervisor support
|
||||
Developers can also create apps that manage GameMode on the system, for other processes:
|
||||
|
||||
```C
|
||||
#include "gamemode_client.h"
|
||||
|
||||
gamemode_request_start_for(gamePID);
|
||||
gamemode_request_end_for(gamePID);
|
||||
```
|
||||
|
||||
This functionality can also be controlled in the config file in the `supervisor` section.
|
||||
|
||||
---
|
||||
## Contributions
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
@ -171,6 +175,21 @@ static bool get_long_value_hex(const char *value_name, const char *value, long *
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple strstr scheck
|
||||
* Could be expanded for wildcard or regex
|
||||
*/
|
||||
static bool config_string_list_contains(const char *needle,
|
||||
char haystack[CONFIG_LIST_MAX][CONFIG_VALUE_MAX])
|
||||
{
|
||||
for (unsigned int i = 0; i < CONFIG_LIST_MAX && haystack[i][0]; i++) {
|
||||
if (strstr(needle, haystack[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a string value
|
||||
*/
|
||||
@ -246,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) {
|
||||
@ -417,12 +445,7 @@ bool config_get_client_whitelisted(GameModeConfig *self, const char *client)
|
||||
* Check if the value is found in our whitelist
|
||||
* Currently is a simple strstr check, but could be modified for wildcards etc.
|
||||
*/
|
||||
found = false;
|
||||
for (unsigned int i = 0; i < CONFIG_LIST_MAX && self->values.whitelist[i][0]; i++) {
|
||||
if (strstr(client, self->values.whitelist[i])) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
found = config_string_list_contains(client, self->values.whitelist);
|
||||
}
|
||||
|
||||
/* release the lock */
|
||||
@ -442,12 +465,7 @@ bool config_get_client_blacklisted(GameModeConfig *self, const char *client)
|
||||
* Check if the value is found in our whitelist
|
||||
* Currently is a simple strstr check, but could be modified for wildcards etc.
|
||||
*/
|
||||
bool found = false;
|
||||
for (unsigned int i = 0; i < CONFIG_LIST_MAX && self->values.blacklist[i][0]; i++) {
|
||||
if (strstr(client, self->values.blacklist[i])) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
bool found = config_string_list_contains(client, self->values.blacklist);
|
||||
|
||||
/* release the lock */
|
||||
pthread_rwlock_unlock(&self->rwlock);
|
||||
@ -563,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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -75,9 +75,9 @@ static int method_register_game(sd_bus_message *m, void *userdata,
|
||||
return ret;
|
||||
}
|
||||
|
||||
game_mode_context_register(context, (pid_t)pid);
|
||||
int status = game_mode_context_register(context, (pid_t)pid, (pid_t)pid);
|
||||
|
||||
return sd_bus_reply_method_return(m, "i", 0);
|
||||
return sd_bus_reply_method_return(m, "i", status);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,9 +95,9 @@ static int method_unregister_game(sd_bus_message *m, void *userdata,
|
||||
return ret;
|
||||
}
|
||||
|
||||
game_mode_context_unregister(context, (pid_t)pid);
|
||||
int status = game_mode_context_unregister(context, (pid_t)pid, (pid_t)pid);
|
||||
|
||||
return sd_bus_reply_method_return(m, "i", 0);
|
||||
return sd_bus_reply_method_return(m, "i", status);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,7 +115,70 @@ static int method_query_status(sd_bus_message *m, void *userdata,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int status = game_mode_context_query_status(context, (pid_t)pid);
|
||||
int status = game_mode_context_query_status(context, (pid_t)pid, (pid_t)pid);
|
||||
|
||||
return sd_bus_reply_method_return(m, "i", status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the RegisterGameByPID D-BUS Method
|
||||
*/
|
||||
static int method_register_game_by_pid(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 reply = game_mode_context_register(context, (pid_t)gamepid, (pid_t)callerpid);
|
||||
|
||||
return sd_bus_reply_method_return(m, "i", reply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UnregisterGameByPID D-BUS Method
|
||||
*/
|
||||
static int method_unregister_game_by_pid(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 reply = game_mode_context_unregister(context, (pid_t)gamepid, (pid_t)callerpid);
|
||||
|
||||
return sd_bus_reply_method_return(m, "i", reply);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the QueryStatus D-BUS Method
|
||||
*/
|
||||
static int method_query_status_by_pid(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(context, (pid_t)gamepid, (pid_t)callerpid);
|
||||
|
||||
return sd_bus_reply_method_return(m, "i", status);
|
||||
}
|
||||
@ -128,6 +191,12 @@ static const sd_bus_vtable gamemode_vtable[] =
|
||||
SD_BUS_METHOD("RegisterGame", "i", "i", method_register_game, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("UnregisterGame", "i", "i", method_unregister_game, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("QueryStatus", "i", "i", method_query_status, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("RegisterGameByPID", "ii", "i", method_register_game_by_pid,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("UnregisterGameByPID", "ii", "i", method_unregister_game_by_pid,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("QueryStatusByPID", "ii", "i", method_query_status_by_pid,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END };
|
||||
|
||||
/**
|
||||
|
@ -127,12 +127,6 @@ static int run_basic_client_tests(void)
|
||||
{
|
||||
LOG_MSG(":: Basic client tests\n");
|
||||
|
||||
/* First verify that gamemode is not currently active on the system
|
||||
* As well as it being currently installed and queryable
|
||||
*/
|
||||
if (verify_gamemode_initial() != 0)
|
||||
return -1;
|
||||
|
||||
/* Verify that gamemode_request_start correctly start gamemode */
|
||||
if (gamemode_request_start() != 0) {
|
||||
LOG_ERROR("gamemode_request_start failed: %s\n", gamemode_error_string());
|
||||
@ -576,6 +570,96 @@ static int game_mode_run_feature_tests(struct GameModeConfig *config)
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Run a set of tests on the supervisor code */
|
||||
static int run_supervisor_tests(void)
|
||||
{
|
||||
int supervisortests = 0;
|
||||
int ret = 0;
|
||||
|
||||
LOG_MSG(":: Supervisor tests\n");
|
||||
|
||||
/* Launch an external dummy process we can leave running and request gamemode for it */
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
/* Child simply pauses and exits */
|
||||
pause();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* Request gamemode for our dummy process */
|
||||
ret = gamemode_request_start_for(pid);
|
||||
if (ret != 0) {
|
||||
LOG_ERROR("gamemode_request_start_for gave unexpected value %d, (expected 0)!\n", ret);
|
||||
if (ret == -1)
|
||||
LOG_ERROR("GameMode error string: %s!\n", gamemode_error_string());
|
||||
supervisortests = -1;
|
||||
}
|
||||
|
||||
/* Check it's active */
|
||||
ret = gamemode_query_status();
|
||||
if (ret != 1) {
|
||||
LOG_ERROR(
|
||||
"gamemode_query_status after start request gave unexpected value %d, (expected 1)!\n",
|
||||
ret);
|
||||
if (ret == -1)
|
||||
LOG_ERROR("GameMode error string: %s!\n", gamemode_error_string());
|
||||
supervisortests = -1;
|
||||
}
|
||||
|
||||
/* Check it's active for the dummy */
|
||||
ret = gamemode_query_status_for(pid);
|
||||
if (ret != 2) {
|
||||
LOG_ERROR(
|
||||
"gamemode_query_status_for after start request gave unexpected value %d, (expected "
|
||||
"2)!\n",
|
||||
ret);
|
||||
if (ret == -1)
|
||||
LOG_ERROR("GameMode error string: %s!\n", gamemode_error_string());
|
||||
supervisortests = -1;
|
||||
}
|
||||
|
||||
/* request gamemode end for the client */
|
||||
ret = gamemode_request_end_for(pid);
|
||||
if (ret != 0) {
|
||||
LOG_ERROR("gamemode_request_end_for gave unexpected value %d, (expected 0)!\n", ret);
|
||||
if (ret == -1)
|
||||
LOG_ERROR("GameMode error string: %s!\n", gamemode_error_string());
|
||||
supervisortests = -1;
|
||||
}
|
||||
|
||||
/* Verify it's not active */
|
||||
ret = gamemode_query_status();
|
||||
if (ret != 0) {
|
||||
LOG_ERROR(
|
||||
"gamemode_query_status after end request gave unexpected value %d, (expected 0)!\n",
|
||||
ret);
|
||||
if (ret == -1)
|
||||
LOG_ERROR("GameMode error string: %s!\n", gamemode_error_string());
|
||||
supervisortests = -1;
|
||||
}
|
||||
|
||||
/* Wake up the child process */
|
||||
if (kill(pid, SIGUSR1) == -1) {
|
||||
LOG_ERROR("failed to send continue signal to other child process: %s\n", strerror(errno));
|
||||
supervisortests = -1;
|
||||
}
|
||||
|
||||
// Wait for the child to finish up
|
||||
int wstatus;
|
||||
usleep(100000);
|
||||
while (waitpid(pid, &wstatus, WNOHANG) == 0) {
|
||||
LOG_MSG("...Waiting for child to quit...\n");
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
if (supervisortests == 0)
|
||||
LOG_MSG(":: Passed\n\n");
|
||||
else
|
||||
LOG_ERROR(":: Failed!\n");
|
||||
|
||||
return supervisortests;
|
||||
}
|
||||
|
||||
/**
|
||||
* game_mode_run_client_tests runs a set of tests of the client code
|
||||
* we simply verify that the client can request the status and recieves the correct results
|
||||
@ -595,6 +679,21 @@ int game_mode_run_client_tests()
|
||||
|
||||
LOG_MSG(": Running tests\n\n");
|
||||
|
||||
/* First verify that gamemode is not currently active on the system
|
||||
* As well as it being currently installed and queryable
|
||||
*/
|
||||
if (verify_gamemode_initial() != 0)
|
||||
return -1;
|
||||
|
||||
/* Controls whether we require a supervisor to actually make requests */
|
||||
/* TODO: This effects all tests below */
|
||||
if (config_get_require_supervisor(config) != 0) {
|
||||
LOG_ERROR("Tests currently unsupported when require_supervisor is set\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TODO: Also check blacklist/whitelist values as these may mess up the tests below */
|
||||
|
||||
/* Run the basic tests */
|
||||
if (run_basic_client_tests() != 0)
|
||||
status = -1;
|
||||
@ -607,6 +706,10 @@ int game_mode_run_client_tests()
|
||||
if (run_gamemoderun_and_reaper_tests(config) != 0)
|
||||
status = -1;
|
||||
|
||||
/* Run the supervisor tests */
|
||||
if (run_supervisor_tests() != 0)
|
||||
status = -1;
|
||||
|
||||
if (status != 0) {
|
||||
LOG_MSG(": Client tests failed, skipping feature tests\n");
|
||||
} else {
|
||||
|
@ -291,7 +291,7 @@ static void game_mode_context_auto_expire(GameModeContext *self)
|
||||
if (kill(client->pid, 0) != 0) {
|
||||
LOG_MSG("Removing expired game [%i]...\n", client->pid);
|
||||
pthread_rwlock_unlock(&self->rwlock);
|
||||
game_mode_context_unregister(self, client->pid);
|
||||
game_mode_context_unregister(self, client->pid, client->pid);
|
||||
removing = true;
|
||||
break;
|
||||
}
|
||||
@ -335,20 +335,43 @@ static int game_mode_context_num_clients(GameModeContext *self)
|
||||
return atomic_load(&self->refcount);
|
||||
}
|
||||
|
||||
bool game_mode_context_register(GameModeContext *self, pid_t client)
|
||||
int game_mode_context_register(GameModeContext *self, pid_t client, pid_t requester)
|
||||
{
|
||||
errno = 0;
|
||||
|
||||
/* Construct a new client if we can */
|
||||
GameModeClient *cl = NULL;
|
||||
char *executable = NULL;
|
||||
|
||||
/* Check our requester config first */
|
||||
if (requester != client) {
|
||||
/* Lookup the executable first */
|
||||
executable = game_mode_context_find_exe(requester);
|
||||
if (!executable) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check our blacklist and whitelist */
|
||||
if (!config_get_supervisor_whitelisted(self->config, executable)) {
|
||||
LOG_MSG("Supervisor [%s] was rejected (not in whitelist)\n", executable);
|
||||
free(executable);
|
||||
return -2;
|
||||
} else if (config_get_supervisor_blacklisted(self->config, executable)) {
|
||||
LOG_MSG("Supervisor [%s] was rejected (in blacklist)\n", executable);
|
||||
free(executable);
|
||||
return -2;
|
||||
}
|
||||
} else if (config_get_require_supervisor(self->config)) {
|
||||
LOG_ERROR("Direct request made but require_supervisor was set, rejecting request!\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Cap the total number of active clients */
|
||||
if (game_mode_context_num_clients(self) + 1 > MAX_GAMES) {
|
||||
LOG_ERROR("Max games (%d) reached, not registering %d\n", MAX_GAMES, client);
|
||||
return false;
|
||||
goto error_cleanup;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
/* Check the PID first to spare a potentially expensive lookup for the exe */
|
||||
pthread_rwlock_rdlock(&self->rwlock); // ensure our pointer is sane
|
||||
const GameModeClient *existing = game_mode_context_has_client(self, client);
|
||||
@ -407,22 +430,47 @@ bool game_mode_context_register(GameModeContext *self, pid_t client)
|
||||
/* Apply io priorities */
|
||||
game_mode_apply_ioprio(self, client);
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
error_cleanup:
|
||||
if (errno != 0)
|
||||
LOG_ERROR("Failed to register client [%d]: %s\n", client, strerror(errno));
|
||||
free(executable);
|
||||
game_mode_client_free(cl);
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool game_mode_context_unregister(GameModeContext *self, pid_t client)
|
||||
int game_mode_context_unregister(GameModeContext *self, pid_t client, pid_t requester)
|
||||
{
|
||||
GameModeClient *cl = NULL;
|
||||
GameModeClient *prev = NULL;
|
||||
bool found = false;
|
||||
|
||||
/* Check our requester config first */
|
||||
if (requester != client) {
|
||||
/* Lookup the executable first */
|
||||
char *executable = game_mode_context_find_exe(requester);
|
||||
if (!executable) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check our blacklist and whitelist */
|
||||
if (!config_get_supervisor_whitelisted(self->config, executable)) {
|
||||
LOG_MSG("Supervisor [%s] was rejected (not in whitelist)\n", executable);
|
||||
free(executable);
|
||||
return -2;
|
||||
} else if (config_get_supervisor_blacklisted(self->config, executable)) {
|
||||
LOG_MSG("Supervisor [%s] was rejected (in blacklist)\n", executable);
|
||||
free(executable);
|
||||
return -2;
|
||||
}
|
||||
|
||||
free(executable);
|
||||
} else if (config_get_require_supervisor(self->config)) {
|
||||
LOG_ERROR("Direct request made but require_supervisor was set, rejecting request!\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Requires locking. */
|
||||
pthread_rwlock_wrlock(&self->rwlock);
|
||||
|
||||
@ -457,7 +505,7 @@ bool game_mode_context_unregister(GameModeContext *self, pid_t client)
|
||||
" -- with a nearby 'Removing expired game' which means we cleaned up properly\n"
|
||||
" -- (we will log this event). This hint will be displayed only once.\n",
|
||||
client);
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* When we hit bottom then end the game mode */
|
||||
@ -465,14 +513,35 @@ bool game_mode_context_unregister(GameModeContext *self, pid_t client)
|
||||
game_mode_context_leave(self);
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int game_mode_context_query_status(GameModeContext *self, pid_t client)
|
||||
int game_mode_context_query_status(GameModeContext *self, pid_t client, pid_t requester)
|
||||
{
|
||||
GameModeClient *cl = NULL;
|
||||
int ret = 0;
|
||||
|
||||
/* First check the requester settings if appropriate */
|
||||
if (client != requester) {
|
||||
char *executable = game_mode_context_find_exe(requester);
|
||||
if (!executable) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check our blacklist and whitelist */
|
||||
if (!config_get_supervisor_whitelisted(self->config, executable)) {
|
||||
LOG_MSG("Supervisor [%s] was rejected (not in whitelist)\n", executable);
|
||||
free(executable);
|
||||
return -2;
|
||||
} else if (config_get_supervisor_blacklisted(self->config, executable)) {
|
||||
LOG_MSG("Supervisor [%s] was rejected (in blacklist)\n", executable);
|
||||
free(executable);
|
||||
return -2;
|
||||
}
|
||||
|
||||
free(executable);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the current refcount on gamemode, this equates to whether gamemode is active or not,
|
||||
* see game_mode_context_register and game_mode_context_unregister
|
||||
|
@ -67,17 +67,23 @@ void game_mode_context_destroy(GameModeContext *self);
|
||||
* Register a new game client with the context
|
||||
*
|
||||
* @param pid Process ID for the remote client
|
||||
* @returns True if the new client could be registered
|
||||
* @param requester Process ID for the remote requestor
|
||||
* @returns 0 if the request was accepted and the client could be registered
|
||||
* -1 if the request was accepted but the client could not be registered
|
||||
* -2 if the request was rejected
|
||||
*/
|
||||
bool game_mode_context_register(GameModeContext *self, pid_t pid);
|
||||
int game_mode_context_register(GameModeContext *self, pid_t pid, pid_t requester);
|
||||
|
||||
/**
|
||||
* Unregister an existing remote game client from the context
|
||||
*
|
||||
* @param pid Process ID for the remote client
|
||||
* @returns True if the client was removed, and existed.
|
||||
* @param requester Process ID for the remote requestor
|
||||
* @returns 0 if the request was accepted and the client existed
|
||||
* -1 if the request was accepted but the client did not exist
|
||||
* -2 if the request was rejected
|
||||
*/
|
||||
bool game_mode_context_unregister(GameModeContext *self, pid_t pid);
|
||||
int game_mode_context_unregister(GameModeContext *self, pid_t pid, pid_t requester);
|
||||
|
||||
/**
|
||||
* Query the current status of gamemode
|
||||
@ -86,8 +92,9 @@ bool game_mode_context_unregister(GameModeContext *self, pid_t pid);
|
||||
* @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 requester was rejected
|
||||
*/
|
||||
int game_mode_context_query_status(GameModeContext *self, pid_t pid);
|
||||
int game_mode_context_query_status(GameModeContext *self, pid_t pid, pid_t requester);
|
||||
|
||||
/**
|
||||
* Query the config of a gamemode context
|
||||
|
@ -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"
|
||||
|
@ -43,7 +43,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
static char error_string[512] = { 0 };
|
||||
|
||||
// Simple requestor function for a gamemode
|
||||
static int gamemode_request(const char *function)
|
||||
static int gamemode_request(const char *function, int arg)
|
||||
{
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus *bus = NULL;
|
||||
@ -66,8 +66,9 @@ static int gamemode_request(const char *function)
|
||||
function,
|
||||
NULL,
|
||||
&msg,
|
||||
"i",
|
||||
getpid());
|
||||
arg ? "ii" : "i",
|
||||
getpid(),
|
||||
arg);
|
||||
if (ret < 0) {
|
||||
snprintf(error_string,
|
||||
sizeof(error_string),
|
||||
@ -97,17 +98,35 @@ extern const char *real_gamemode_error_string(void)
|
||||
// Wrapper to call RegisterGame
|
||||
extern int real_gamemode_request_start(void)
|
||||
{
|
||||
return gamemode_request("RegisterGame");
|
||||
return gamemode_request("RegisterGame", 0);
|
||||
}
|
||||
|
||||
// Wrapper to call UnregisterGame
|
||||
extern int real_gamemode_request_end(void)
|
||||
{
|
||||
return gamemode_request("UnregisterGame");
|
||||
return gamemode_request("UnregisterGame", 0);
|
||||
}
|
||||
|
||||
// Wrapper to call UnregisterGame
|
||||
// Wrapper to call QueryStatus
|
||||
extern int real_gamemode_query_status(void)
|
||||
{
|
||||
return gamemode_request("QueryStatus");
|
||||
return gamemode_request("QueryStatus", 0);
|
||||
}
|
||||
|
||||
// Wrapper to call RegisterGameByPID
|
||||
extern int real_gamemode_request_start_for(pid_t pid)
|
||||
{
|
||||
return gamemode_request("RegisterGameByPID", pid);
|
||||
}
|
||||
|
||||
// Wrapper to call UnregisterGameByPID
|
||||
extern int real_gamemode_request_end_for(pid_t pid)
|
||||
{
|
||||
return gamemode_request("UnregisterGameByPID", pid);
|
||||
}
|
||||
|
||||
// Wrapper to call QueryStatusByPID
|
||||
extern int real_gamemode_query_status_for(pid_t pid)
|
||||
{
|
||||
return gamemode_request("QueryStatusByPID", pid);
|
||||
}
|
||||
|
@ -48,6 +48,20 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
* 2 if gamemode is active and this client is registered
|
||||
* -1 if the query failed
|
||||
*
|
||||
* int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process
|
||||
* 0 if the request was sent successfully
|
||||
* -1 if the request failed
|
||||
* -2 if the request was rejected
|
||||
*
|
||||
* int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process
|
||||
* 0 if the request was sent successfully
|
||||
* -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
|
||||
*/
|
||||
@ -61,6 +75,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
static char internal_gamemode_client_error_string[512] = { 0 };
|
||||
|
||||
/**
|
||||
@ -72,26 +88,26 @@ static char internal_gamemode_client_error_string[512] = { 0 };
|
||||
static volatile int internal_libgamemode_loaded = 1;
|
||||
|
||||
/* Typedefs for the functions to load */
|
||||
typedef int (*internal_gamemode_request_start)(void);
|
||||
typedef int (*internal_gamemode_request_end)(void);
|
||||
typedef int (*internal_gamemode_query_status)(void);
|
||||
typedef const char *(*internal_gamemode_error_string)(void);
|
||||
typedef int (*api_call_return_int)(void);
|
||||
typedef const char *(*api_call_return_cstring)(void);
|
||||
typedef int (*api_call_pid_return_int)(pid_t);
|
||||
|
||||
/* Storage for functors */
|
||||
static internal_gamemode_request_start REAL_internal_gamemode_request_start = NULL;
|
||||
static internal_gamemode_request_end REAL_internal_gamemode_request_end = NULL;
|
||||
static internal_gamemode_query_status REAL_internal_gamemode_query_status = NULL;
|
||||
static internal_gamemode_error_string REAL_internal_gamemode_error_string = NULL;
|
||||
static api_call_return_int REAL_internal_gamemode_request_start = NULL;
|
||||
static api_call_return_int REAL_internal_gamemode_request_end = NULL;
|
||||
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.
|
||||
*
|
||||
* Returns 0 on success and -1 on failure
|
||||
*/
|
||||
__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(void *handle,
|
||||
const char *name,
|
||||
void **out_func,
|
||||
size_t func_size)
|
||||
__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(
|
||||
void *handle, const char *name, void **out_func, size_t func_size, bool required)
|
||||
{
|
||||
void *symbol_lookup = NULL;
|
||||
char *dl_error = NULL;
|
||||
@ -99,7 +115,7 @@ __attribute__((always_inline)) static inline int internal_bind_libgamemode_symbo
|
||||
/* Safely look up the symbol */
|
||||
symbol_lookup = dlsym(handle, name);
|
||||
dl_error = dlerror();
|
||||
if (dl_error || !symbol_lookup) {
|
||||
if (required && (dl_error || !symbol_lookup)) {
|
||||
snprintf(internal_gamemode_client_error_string,
|
||||
sizeof(internal_gamemode_client_error_string),
|
||||
"dlsym failed - %s",
|
||||
@ -147,6 +163,18 @@ __attribute__((always_inline)) static inline int internal_load_libgamemode(void)
|
||||
(void **)&REAL_internal_gamemode_error_string,
|
||||
sizeof(REAL_internal_gamemode_error_string),
|
||||
true },
|
||||
{ "real_gamemode_request_start_for",
|
||||
(void **)&REAL_internal_gamemode_request_start_for,
|
||||
sizeof(REAL_internal_gamemode_request_start_for),
|
||||
false },
|
||||
{ "real_gamemode_request_end_for",
|
||||
(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;
|
||||
@ -175,8 +203,8 @@ __attribute__((always_inline)) static inline int internal_load_libgamemode(void)
|
||||
if (internal_bind_libgamemode_symbol(libgamemode,
|
||||
binder->name,
|
||||
binder->functor,
|
||||
binder->func_size) != 0 &&
|
||||
binder->required) {
|
||||
binder->func_size,
|
||||
binder->required)) {
|
||||
internal_libgamemode_loaded = -1;
|
||||
return -1;
|
||||
};
|
||||
@ -275,4 +303,58 @@ __attribute__((always_inline)) static inline int gamemode_query_status(void)
|
||||
return REAL_internal_gamemode_query_status();
|
||||
}
|
||||
|
||||
/* Redirect to the real libgamemode */
|
||||
__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid)
|
||||
{
|
||||
/* Need to load gamemode */
|
||||
if (internal_load_libgamemode() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (REAL_internal_gamemode_request_start_for == NULL) {
|
||||
snprintf(internal_gamemode_client_error_string,
|
||||
sizeof(internal_gamemode_client_error_string),
|
||||
"gamemode_request_start_for missing (older host?)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return REAL_internal_gamemode_request_start_for(pid);
|
||||
}
|
||||
|
||||
/* Redirect to the real libgamemode */
|
||||
__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid)
|
||||
{
|
||||
/* Need to load gamemode */
|
||||
if (internal_load_libgamemode() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (REAL_internal_gamemode_request_end_for == NULL) {
|
||||
snprintf(internal_gamemode_client_error_string,
|
||||
sizeof(internal_gamemode_client_error_string),
|
||||
"gamemode_request_end_for missing (older host?)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user