| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifndef CLIENT_GAMEMODE_H |
| #define CLIENT_GAMEMODE_H |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <stdbool.h> |
| #include <stdio.h> |
|
|
| #include <dlfcn.h> |
| #include <string.h> |
|
|
| #include <assert.h> |
|
|
| #include <sys/types.h> |
|
|
| static char internal_gamemode_client_error_string[512] = { 0 }; |
|
|
| |
| |
| |
| |
| |
| |
| static volatile int internal_libgamemode_loaded = 1; |
|
|
| |
| typedef int (*api_call_return_int)(void); |
| typedef const char *(*api_call_return_cstring)(void); |
| typedef int (*api_call_pid_return_int)(pid_t); |
|
|
| |
| 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; |
|
|
| |
| |
| |
| |
| |
| __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; |
|
|
| |
| symbol_lookup = dlsym(handle, name); |
| dl_error = dlerror(); |
| if (required && (dl_error || !symbol_lookup)) { |
| snprintf(internal_gamemode_client_error_string, |
| sizeof(internal_gamemode_client_error_string), |
| "dlsym failed - %s", |
| dl_error); |
| return -1; |
| } |
|
|
| |
| memcpy(out_func, &symbol_lookup, func_size); |
| return 0; |
| } |
|
|
| |
| |
| |
| |
| |
| __attribute__((always_inline)) static inline int internal_load_libgamemode(void) |
| { |
| |
| if (internal_libgamemode_loaded != 1) { |
| return internal_libgamemode_loaded; |
| } |
|
|
| |
| struct binding { |
| const char *name; |
| void **functor; |
| size_t func_size; |
| bool required; |
| } bindings[] = { |
| { "real_gamemode_request_start", |
| (void **)&REAL_internal_gamemode_request_start, |
| sizeof(REAL_internal_gamemode_request_start), |
| true }, |
| { "real_gamemode_request_end", |
| (void **)&REAL_internal_gamemode_request_end, |
| sizeof(REAL_internal_gamemode_request_end), |
| true }, |
| { "real_gamemode_query_status", |
| (void **)&REAL_internal_gamemode_query_status, |
| sizeof(REAL_internal_gamemode_query_status), |
| false }, |
| { "real_gamemode_error_string", |
| (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; |
|
|
| |
| libgamemode = dlopen("libgamemode.so.0", RTLD_NOW); |
| if (!libgamemode) { |
| |
| |
| |
| libgamemode = dlopen("libgamemode.so", RTLD_NOW); |
| if (!libgamemode) { |
| snprintf(internal_gamemode_client_error_string, |
| sizeof(internal_gamemode_client_error_string), |
| "dlopen failed - %s", |
| dlerror()); |
| internal_libgamemode_loaded = -1; |
| return -1; |
| } |
| } |
|
|
| |
| for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) { |
| struct binding *binder = &bindings[i]; |
|
|
| if (internal_bind_libgamemode_symbol(libgamemode, |
| binder->name, |
| binder->functor, |
| binder->func_size, |
| binder->required)) { |
| internal_libgamemode_loaded = -1; |
| return -1; |
| }; |
| } |
|
|
| |
| internal_libgamemode_loaded = 0; |
| return 0; |
| } |
|
|
| |
| |
| |
| __attribute__((always_inline)) static inline const char *gamemode_error_string(void) |
| { |
| |
| |
| if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') { |
| return internal_gamemode_client_error_string; |
| } |
|
|
| |
| assert(REAL_internal_gamemode_error_string != NULL); |
|
|
| return REAL_internal_gamemode_error_string(); |
| } |
|
|
| |
| |
| |
| |
| |
| #ifdef GAMEMODE_AUTO |
| __attribute__((constructor)) |
| #else |
| __attribute__((always_inline)) static inline |
| #endif |
| int gamemode_request_start(void) |
| { |
| |
| if (internal_load_libgamemode() < 0) { |
| #ifdef GAMEMODE_AUTO |
| fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); |
| #endif |
| return -1; |
| } |
|
|
| |
| assert(REAL_internal_gamemode_request_start != NULL); |
|
|
| if (REAL_internal_gamemode_request_start() < 0) { |
| #ifdef GAMEMODE_AUTO |
| fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); |
| #endif |
| return -1; |
| } |
|
|
| return 0; |
| } |
|
|
| |
| #ifdef GAMEMODE_AUTO |
| __attribute__((destructor)) |
| #else |
| __attribute__((always_inline)) static inline |
| #endif |
| int gamemode_request_end(void) |
| { |
| |
| if (internal_load_libgamemode() < 0) { |
| #ifdef GAMEMODE_AUTO |
| fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); |
| #endif |
| return -1; |
| } |
|
|
| |
| assert(REAL_internal_gamemode_request_end != NULL); |
|
|
| if (REAL_internal_gamemode_request_end() < 0) { |
| #ifdef GAMEMODE_AUTO |
| fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); |
| #endif |
| return -1; |
| } |
|
|
| return 0; |
| } |
|
|
| |
| __attribute__((always_inline)) static inline int gamemode_query_status(void) |
| { |
| |
| if (internal_load_libgamemode() < 0) { |
| return -1; |
| } |
|
|
| if (REAL_internal_gamemode_query_status == NULL) { |
| snprintf(internal_gamemode_client_error_string, |
| sizeof(internal_gamemode_client_error_string), |
| "gamemode_query_status missing (older host?)"); |
| return -1; |
| } |
|
|
| return REAL_internal_gamemode_query_status(); |
| } |
|
|
| |
| __attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid) |
| { |
| |
| 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); |
| } |
|
|
| |
| __attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid) |
| { |
| |
| 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); |
| } |
|
|
| |
| __attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid) |
| { |
| |
| 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 |
|
|