mirror of
https://github.com/FeralInteractive/gamemode.git
synced 2025-06-06 23:57:22 +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.
|
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
|
## Contributions
|
||||||
|
|
||||||
|
@ -94,6 +94,10 @@ struct GameModeConfig {
|
|||||||
long nv_perf_level;
|
long nv_perf_level;
|
||||||
long amd_core_clock_percentage;
|
long amd_core_clock_percentage;
|
||||||
long amd_mem_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;
|
} values;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -171,6 +175,21 @@ static bool get_long_value_hex(const char *value_name, const char *value, long *
|
|||||||
return true;
|
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
|
* 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) {
|
} else if (strcmp(name, "amd_mem_clock_percentage") == 0) {
|
||||||
valid = get_long_value(name, value, &self->values.amd_mem_clock_percentage);
|
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) {
|
} else if (strcmp(section, "custom") == 0) {
|
||||||
/* Custom subsection */
|
/* Custom subsection */
|
||||||
if (strcmp(name, "start") == 0) {
|
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
|
* Check if the value is found in our whitelist
|
||||||
* Currently is a simple strstr check, but could be modified for wildcards etc.
|
* Currently is a simple strstr check, but could be modified for wildcards etc.
|
||||||
*/
|
*/
|
||||||
found = false;
|
found = config_string_list_contains(client, self->values.whitelist);
|
||||||
for (unsigned int i = 0; i < CONFIG_LIST_MAX && self->values.whitelist[i][0]; i++) {
|
|
||||||
if (strstr(client, self->values.whitelist[i])) {
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* release the lock */
|
/* 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
|
* Check if the value is found in our whitelist
|
||||||
* Currently is a simple strstr check, but could be modified for wildcards etc.
|
* Currently is a simple strstr check, but could be modified for wildcards etc.
|
||||||
*/
|
*/
|
||||||
bool found = false;
|
bool found = config_string_list_contains(client, self->values.blacklist);
|
||||||
for (unsigned int i = 0; i < CONFIG_LIST_MAX && self->values.blacklist[i][0]; i++) {
|
|
||||||
if (strstr(client, self->values.blacklist[i])) {
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* release the lock */
|
/* release the lock */
|
||||||
pthread_rwlock_unlock(&self->rwlock);
|
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(nv_perf_level)
|
||||||
DEFINE_CONFIG_GET(amd_core_clock_percentage)
|
DEFINE_CONFIG_GET(amd_core_clock_percentage)
|
||||||
DEFINE_CONFIG_GET(amd_mem_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_nv_perf_level(GameModeConfig *self);
|
||||||
long config_get_amd_core_clock_percentage(GameModeConfig *self);
|
long config_get_amd_core_clock_percentage(GameModeConfig *self);
|
||||||
long config_get_amd_mem_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;
|
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;
|
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;
|
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);
|
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("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("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("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 };
|
SD_BUS_VTABLE_END };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,12 +127,6 @@ static int run_basic_client_tests(void)
|
|||||||
{
|
{
|
||||||
LOG_MSG(":: Basic client tests\n");
|
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 */
|
/* Verify that gamemode_request_start correctly start gamemode */
|
||||||
if (gamemode_request_start() != 0) {
|
if (gamemode_request_start() != 0) {
|
||||||
LOG_ERROR("gamemode_request_start failed: %s\n", gamemode_error_string());
|
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;
|
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
|
* 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
|
* 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");
|
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 */
|
/* Run the basic tests */
|
||||||
if (run_basic_client_tests() != 0)
|
if (run_basic_client_tests() != 0)
|
||||||
status = -1;
|
status = -1;
|
||||||
@ -607,6 +706,10 @@ int game_mode_run_client_tests()
|
|||||||
if (run_gamemoderun_and_reaper_tests(config) != 0)
|
if (run_gamemoderun_and_reaper_tests(config) != 0)
|
||||||
status = -1;
|
status = -1;
|
||||||
|
|
||||||
|
/* Run the supervisor tests */
|
||||||
|
if (run_supervisor_tests() != 0)
|
||||||
|
status = -1;
|
||||||
|
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
LOG_MSG(": Client tests failed, skipping feature tests\n");
|
LOG_MSG(": Client tests failed, skipping feature tests\n");
|
||||||
} else {
|
} else {
|
||||||
|
@ -291,7 +291,7 @@ static void game_mode_context_auto_expire(GameModeContext *self)
|
|||||||
if (kill(client->pid, 0) != 0) {
|
if (kill(client->pid, 0) != 0) {
|
||||||
LOG_MSG("Removing expired game [%i]...\n", client->pid);
|
LOG_MSG("Removing expired game [%i]...\n", client->pid);
|
||||||
pthread_rwlock_unlock(&self->rwlock);
|
pthread_rwlock_unlock(&self->rwlock);
|
||||||
game_mode_context_unregister(self, client->pid);
|
game_mode_context_unregister(self, client->pid, client->pid);
|
||||||
removing = true;
|
removing = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -335,20 +335,43 @@ static int game_mode_context_num_clients(GameModeContext *self)
|
|||||||
return atomic_load(&self->refcount);
|
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 */
|
/* Construct a new client if we can */
|
||||||
GameModeClient *cl = NULL;
|
GameModeClient *cl = NULL;
|
||||||
char *executable = 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 */
|
/* Cap the total number of active clients */
|
||||||
if (game_mode_context_num_clients(self) + 1 > MAX_GAMES) {
|
if (game_mode_context_num_clients(self) + 1 > MAX_GAMES) {
|
||||||
LOG_ERROR("Max games (%d) reached, not registering %d\n", MAX_GAMES, client);
|
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 */
|
/* Check the PID first to spare a potentially expensive lookup for the exe */
|
||||||
pthread_rwlock_rdlock(&self->rwlock); // ensure our pointer is sane
|
pthread_rwlock_rdlock(&self->rwlock); // ensure our pointer is sane
|
||||||
const GameModeClient *existing = game_mode_context_has_client(self, client);
|
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 */
|
/* Apply io priorities */
|
||||||
game_mode_apply_ioprio(self, client);
|
game_mode_apply_ioprio(self, client);
|
||||||
|
|
||||||
return true;
|
return 0;
|
||||||
|
|
||||||
error_cleanup:
|
error_cleanup:
|
||||||
if (errno != 0)
|
if (errno != 0)
|
||||||
LOG_ERROR("Failed to register client [%d]: %s\n", client, strerror(errno));
|
LOG_ERROR("Failed to register client [%d]: %s\n", client, strerror(errno));
|
||||||
free(executable);
|
free(executable);
|
||||||
game_mode_client_free(cl);
|
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 *cl = NULL;
|
||||||
GameModeClient *prev = NULL;
|
GameModeClient *prev = NULL;
|
||||||
bool found = false;
|
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. */
|
/* Requires locking. */
|
||||||
pthread_rwlock_wrlock(&self->rwlock);
|
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"
|
" -- 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",
|
" -- (we will log this event). This hint will be displayed only once.\n",
|
||||||
client);
|
client);
|
||||||
return false;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When we hit bottom then end the game mode */
|
/* 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);
|
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;
|
GameModeClient *cl = NULL;
|
||||||
int ret = 0;
|
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,
|
* 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
|
* 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
|
* Register a new game client with the context
|
||||||
*
|
*
|
||||||
* @param pid Process ID for the remote client
|
* @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
|
* Unregister an existing remote game client from the context
|
||||||
*
|
*
|
||||||
* @param pid Process ID for the remote client
|
* @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
|
* 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
|
* @returns Positive if gamemode is active
|
||||||
* 1 if gamemode is active but the client is not registered
|
* 1 if gamemode is active but the client is not registered
|
||||||
* 2 if gamemode is active and the client is 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
|
* Query the config of a gamemode context
|
||||||
|
@ -61,6 +61,16 @@ inhibit_screensaver=1
|
|||||||
;amd_core_clock_percentage=0
|
;amd_core_clock_percentage=0
|
||||||
;amd_mem_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]
|
||||||
; Custom scripts (executed using the shell) when gamemode starts and ends
|
; Custom scripts (executed using the shell) when gamemode starts and ends
|
||||||
;start=notify-send "GameMode started"
|
;start=notify-send "GameMode started"
|
||||||
|
@ -43,7 +43,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||||||
static char error_string[512] = { 0 };
|
static char error_string[512] = { 0 };
|
||||||
|
|
||||||
// Simple requestor function for a gamemode
|
// 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_message *msg = NULL;
|
||||||
sd_bus *bus = NULL;
|
sd_bus *bus = NULL;
|
||||||
@ -66,8 +66,9 @@ static int gamemode_request(const char *function)
|
|||||||
function,
|
function,
|
||||||
NULL,
|
NULL,
|
||||||
&msg,
|
&msg,
|
||||||
"i",
|
arg ? "ii" : "i",
|
||||||
getpid());
|
getpid(),
|
||||||
|
arg);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
snprintf(error_string,
|
snprintf(error_string,
|
||||||
sizeof(error_string),
|
sizeof(error_string),
|
||||||
@ -97,17 +98,35 @@ extern const char *real_gamemode_error_string(void)
|
|||||||
// Wrapper to call RegisterGame
|
// Wrapper to call RegisterGame
|
||||||
extern int real_gamemode_request_start(void)
|
extern int real_gamemode_request_start(void)
|
||||||
{
|
{
|
||||||
return gamemode_request("RegisterGame");
|
return gamemode_request("RegisterGame", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper to call UnregisterGame
|
// Wrapper to call UnregisterGame
|
||||||
extern int real_gamemode_request_end(void)
|
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)
|
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
|
* 2 if gamemode is active and this client is registered
|
||||||
* -1 if the query failed
|
* -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
|
* const char* gamemode_error_string() - Get an error string
|
||||||
* returns a string describing any of the above errors
|
* returns a string describing any of the above errors
|
||||||
*/
|
*/
|
||||||
@ -61,6 +75,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
static char internal_gamemode_client_error_string[512] = { 0 };
|
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;
|
static volatile int internal_libgamemode_loaded = 1;
|
||||||
|
|
||||||
/* Typedefs for the functions to load */
|
/* Typedefs for the functions to load */
|
||||||
typedef int (*internal_gamemode_request_start)(void);
|
typedef int (*api_call_return_int)(void);
|
||||||
typedef int (*internal_gamemode_request_end)(void);
|
typedef const char *(*api_call_return_cstring)(void);
|
||||||
typedef int (*internal_gamemode_query_status)(void);
|
typedef int (*api_call_pid_return_int)(pid_t);
|
||||||
typedef const char *(*internal_gamemode_error_string)(void);
|
|
||||||
|
|
||||||
/* Storage for functors */
|
/* Storage for functors */
|
||||||
static internal_gamemode_request_start REAL_internal_gamemode_request_start = NULL;
|
static api_call_return_int REAL_internal_gamemode_request_start = NULL;
|
||||||
static internal_gamemode_request_end REAL_internal_gamemode_request_end = NULL;
|
static api_call_return_int REAL_internal_gamemode_request_end = NULL;
|
||||||
static internal_gamemode_query_status REAL_internal_gamemode_query_status = NULL;
|
static api_call_return_int REAL_internal_gamemode_query_status = NULL;
|
||||||
static internal_gamemode_error_string REAL_internal_gamemode_error_string = 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.
|
* Internal helper to perform the symbol binding safely.
|
||||||
*
|
*
|
||||||
* Returns 0 on success and -1 on failure
|
* Returns 0 on success and -1 on failure
|
||||||
*/
|
*/
|
||||||
__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(void *handle,
|
__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol(
|
||||||
const char *name,
|
void *handle, const char *name, void **out_func, size_t func_size, bool required)
|
||||||
void **out_func,
|
|
||||||
size_t func_size)
|
|
||||||
{
|
{
|
||||||
void *symbol_lookup = NULL;
|
void *symbol_lookup = NULL;
|
||||||
char *dl_error = NULL;
|
char *dl_error = NULL;
|
||||||
@ -99,7 +115,7 @@ __attribute__((always_inline)) static inline int internal_bind_libgamemode_symbo
|
|||||||
/* Safely look up the symbol */
|
/* Safely look up the symbol */
|
||||||
symbol_lookup = dlsym(handle, name);
|
symbol_lookup = dlsym(handle, name);
|
||||||
dl_error = dlerror();
|
dl_error = dlerror();
|
||||||
if (dl_error || !symbol_lookup) {
|
if (required && (dl_error || !symbol_lookup)) {
|
||||||
snprintf(internal_gamemode_client_error_string,
|
snprintf(internal_gamemode_client_error_string,
|
||||||
sizeof(internal_gamemode_client_error_string),
|
sizeof(internal_gamemode_client_error_string),
|
||||||
"dlsym failed - %s",
|
"dlsym failed - %s",
|
||||||
@ -147,6 +163,18 @@ __attribute__((always_inline)) static inline int internal_load_libgamemode(void)
|
|||||||
(void **)&REAL_internal_gamemode_error_string,
|
(void **)&REAL_internal_gamemode_error_string,
|
||||||
sizeof(REAL_internal_gamemode_error_string),
|
sizeof(REAL_internal_gamemode_error_string),
|
||||||
true },
|
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;
|
void *libgamemode = NULL;
|
||||||
@ -175,8 +203,8 @@ __attribute__((always_inline)) static inline int internal_load_libgamemode(void)
|
|||||||
if (internal_bind_libgamemode_symbol(libgamemode,
|
if (internal_bind_libgamemode_symbol(libgamemode,
|
||||||
binder->name,
|
binder->name,
|
||||||
binder->functor,
|
binder->functor,
|
||||||
binder->func_size) != 0 &&
|
binder->func_size,
|
||||||
binder->required) {
|
binder->required)) {
|
||||||
internal_libgamemode_loaded = -1;
|
internal_libgamemode_loaded = -1;
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
@ -275,4 +303,58 @@ __attribute__((always_inline)) static inline int gamemode_query_status(void)
|
|||||||
return REAL_internal_gamemode_query_status();
|
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
|
#endif // CLIENT_GAMEMODE_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user