gamemode-config.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. /*
  2. Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6. * Redistributions of source code must retain the above copyright notice,
  7. this list of conditions and the following disclaimer.
  8. * Redistributions in binary form must reproduce the above copyright
  9. notice, this list of conditions and the following disclaimer in the
  10. documentation and/or other materials provided with the distribution.
  11. * Neither the name of Feral Interactive nor the names of its contributors
  12. may be used to endorse or promote products derived from this software
  13. without specific prior written permission.
  14. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  15. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  18. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  19. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  20. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  21. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  22. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  23. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  24. POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #define _GNU_SOURCE
  27. #include "gamemode-config.h"
  28. #include "common-helpers.h"
  29. #include "common-logging.h"
  30. #include "build-config.h"
  31. /* Ben Hoyt's inih library */
  32. #include <ini.h>
  33. #include <dirent.h>
  34. #include <libgen.h>
  35. #include <math.h>
  36. #include <pthread.h>
  37. #include <pwd.h>
  38. #include <sys/inotify.h>
  39. #include <sys/stat.h>
  40. /* Name and possible location of the config file */
  41. #define CONFIG_NAME "gamemode.ini"
  42. /* Default value for the reaper frequency */
  43. #define DEFAULT_REAPER_FREQ 5
  44. #define DEFAULT_IGPU_POWER_THRESHOLD 0.3f
  45. /* Helper macro for defining the config variable getter */
  46. #define DEFINE_CONFIG_GET(name) \
  47. long config_get_##name(GameModeConfig *self) \
  48. { \
  49. long value = 0; \
  50. memcpy_locked_config(self, &value, &self->values.name, sizeof(long)); \
  51. return value; \
  52. }
  53. /* The number of current locations for config files */
  54. #define CONFIG_NUM_LOCATIONS 4
  55. /**
  56. * The config holds various details as needed
  57. * and a rwlock to allow config_reload to be called
  58. */
  59. struct GameModeConfig {
  60. pthread_rwlock_t rwlock;
  61. int inotfd;
  62. int inotwd[CONFIG_NUM_LOCATIONS];
  63. struct {
  64. char whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  65. char blacklist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  66. long script_timeout;
  67. char startscripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  68. char endscripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  69. char defaultgov[CONFIG_VALUE_MAX];
  70. char desiredgov[CONFIG_VALUE_MAX];
  71. char igpu_desiredgov[CONFIG_VALUE_MAX];
  72. float igpu_power_threshold;
  73. char softrealtime[CONFIG_VALUE_MAX];
  74. long renice;
  75. char ioprio[CONFIG_VALUE_MAX];
  76. long inhibit_screensaver;
  77. long disable_splitlock;
  78. long reaper_frequency;
  79. char apply_gpu_optimisations[CONFIG_VALUE_MAX];
  80. long gpu_device;
  81. long nv_core_clock_mhz_offset;
  82. long nv_mem_clock_mhz_offset;
  83. long nv_powermizer_mode;
  84. char amd_performance_level[CONFIG_VALUE_MAX];
  85. char cpu_park_cores[CONFIG_VALUE_MAX];
  86. char cpu_pin_cores[CONFIG_VALUE_MAX];
  87. long require_supervisor;
  88. char supervisor_whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  89. char supervisor_blacklist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  90. } values;
  91. };
  92. /*
  93. * Add values to a char list
  94. */
  95. static bool append_value_to_list(const char *list_name, const char *value,
  96. char list[CONFIG_LIST_MAX][CONFIG_VALUE_MAX])
  97. {
  98. unsigned int i = 0;
  99. while (*list[i] && ++i < CONFIG_LIST_MAX)
  100. ;
  101. if (i < CONFIG_LIST_MAX) {
  102. strncpy(list[i], value, CONFIG_VALUE_MAX);
  103. if (list[i][CONFIG_VALUE_MAX - 1] != '\0') {
  104. LOG_ERROR("Config: Could not add [%s] to [%s], exceeds length limit of %d\n",
  105. value,
  106. list_name,
  107. CONFIG_VALUE_MAX);
  108. memset(list[i], 0, sizeof(list[i]));
  109. return false;
  110. }
  111. } else {
  112. LOG_ERROR("Config: Could not add [%s] to [%s], exceeds number of %d\n",
  113. value,
  114. list_name,
  115. CONFIG_LIST_MAX);
  116. return false;
  117. }
  118. return true;
  119. }
  120. /*
  121. * Get a long value from a string
  122. */
  123. static bool get_long_value(const char *value_name, const char *value, long *output)
  124. {
  125. char *end = NULL;
  126. long config_value = strtol(value, &end, 10);
  127. if (errno == ERANGE) {
  128. LOG_ERROR("Config: %s overflowed, given [%s]\n", value_name, value);
  129. return false;
  130. } else if (!(*value != '\0' && end && *end == '\0')) {
  131. LOG_ERROR("Config: %s was invalid, given [%s]\n", value_name, value);
  132. return false;
  133. } else {
  134. *output = config_value;
  135. }
  136. return true;
  137. }
  138. /*
  139. * Get a long value from a hex string
  140. */
  141. __attribute__((unused)) static bool get_long_value_hex(const char *value_name, const char *value,
  142. long *output)
  143. {
  144. char *end = NULL;
  145. long config_value = strtol(value, &end, 16);
  146. if (errno == ERANGE) {
  147. LOG_ERROR("Config: %s overflowed, given [%s]\n", value_name, value);
  148. return false;
  149. } else if (!(*value != '\0' && end && *end == '\0')) {
  150. LOG_ERROR("Config: %s was invalid, given [%s]\n", value_name, value);
  151. return false;
  152. } else {
  153. *output = config_value;
  154. }
  155. return true;
  156. }
  157. /*
  158. * Get a long value from a string
  159. */
  160. static bool get_float_value(const char *value_name, const char *value, float *output)
  161. {
  162. char *end = NULL;
  163. float config_value = strtof(value, &end);
  164. if (errno == ERANGE) {
  165. LOG_ERROR("Config: %s overflowed, given [%s]\n", value_name, value);
  166. return false;
  167. } else if (!(*value != '\0' && end && *end == '\0')) {
  168. LOG_ERROR("Config: %s was invalid, given [%s]\n", value_name, value);
  169. return false;
  170. } else {
  171. *output = config_value;
  172. }
  173. return true;
  174. }
  175. /**
  176. * Simple strstr scheck
  177. * Could be expanded for wildcard or regex
  178. */
  179. static bool config_string_list_contains(const char *needle,
  180. char haystack[CONFIG_LIST_MAX][CONFIG_VALUE_MAX])
  181. {
  182. for (unsigned int i = 0; i < CONFIG_LIST_MAX && haystack[i][0]; i++) {
  183. if (strstr(needle, haystack[i])) {
  184. return true;
  185. }
  186. }
  187. return false;
  188. }
  189. /*
  190. * Get a string value
  191. */
  192. static bool get_string_value(const char *value, char output[CONFIG_VALUE_MAX])
  193. {
  194. strncpy(output, value, CONFIG_VALUE_MAX - 1);
  195. output[CONFIG_VALUE_MAX - 1] = '\0';
  196. return true;
  197. }
  198. /* Controls whether to read the protected config variables */
  199. static bool load_protected = false;
  200. /*
  201. * Handler for the inih callback
  202. */
  203. static int inih_handler(void *user, const char *section, const char *name, const char *value)
  204. {
  205. GameModeConfig *self = (GameModeConfig *)user;
  206. bool valid = false;
  207. if (strcmp(section, "filter") == 0) {
  208. /* Filter subsection */
  209. if (strcmp(name, "whitelist") == 0) {
  210. valid = append_value_to_list(name, value, self->values.whitelist);
  211. } else if (strcmp(name, "blacklist") == 0) {
  212. valid = append_value_to_list(name, value, self->values.blacklist);
  213. }
  214. } else if (strcmp(section, "general") == 0) {
  215. /* General subsection */
  216. if (strcmp(name, "reaper_freq") == 0) {
  217. valid = get_long_value(name, value, &self->values.reaper_frequency);
  218. } else if (strcmp(name, "defaultgov") == 0) {
  219. valid = get_string_value(value, self->values.defaultgov);
  220. } else if (strcmp(name, "desiredgov") == 0) {
  221. valid = get_string_value(value, self->values.desiredgov);
  222. } else if (strcmp(name, "igpu_desiredgov") == 0) {
  223. valid = get_string_value(value, self->values.igpu_desiredgov);
  224. } else if (strcmp(name, "igpu_power_threshold") == 0) {
  225. valid = get_float_value(name, value, &self->values.igpu_power_threshold);
  226. } else if (strcmp(name, "softrealtime") == 0) {
  227. valid = get_string_value(value, self->values.softrealtime);
  228. } else if (strcmp(name, "renice") == 0) {
  229. valid = get_long_value(name, value, &self->values.renice);
  230. } else if (strcmp(name, "ioprio") == 0) {
  231. valid = get_string_value(value, self->values.ioprio);
  232. } else if (strcmp(name, "inhibit_screensaver") == 0) {
  233. valid = get_long_value(name, value, &self->values.inhibit_screensaver);
  234. } else if (strcmp(name, "disable_splitlock") == 0) {
  235. valid = get_long_value(name, value, &self->values.disable_splitlock);
  236. }
  237. } else if (strcmp(section, "gpu") == 0) {
  238. /* Protect the user - don't allow these config options from unsafe config locations */
  239. if (!load_protected) {
  240. LOG_ERROR(
  241. "The [gpu] config section is not configurable from unsafe config files! Option %s "
  242. "will be ignored!\n",
  243. name);
  244. LOG_ERROR("Consider moving this option to /etc/gamemode.ini\n");
  245. }
  246. /* GPU subsection */
  247. if (strcmp(name, "apply_gpu_optimisations") == 0) {
  248. valid = get_string_value(value, self->values.apply_gpu_optimisations);
  249. } else if (strcmp(name, "gpu_device") == 0) {
  250. valid = get_long_value(name, value, &self->values.gpu_device);
  251. } else if (strcmp(name, "nv_core_clock_mhz_offset") == 0) {
  252. valid = get_long_value(name, value, &self->values.nv_core_clock_mhz_offset);
  253. } else if (strcmp(name, "nv_mem_clock_mhz_offset") == 0) {
  254. valid = get_long_value(name, value, &self->values.nv_mem_clock_mhz_offset);
  255. } else if (strcmp(name, "nv_powermizer_mode") == 0) {
  256. valid = get_long_value(name, value, &self->values.nv_powermizer_mode);
  257. } else if (strcmp(name, "amd_performance_level") == 0) {
  258. valid = get_string_value(value, self->values.amd_performance_level);
  259. }
  260. } else if (strcmp(section, "cpu") == 0) {
  261. if (strcmp(name, "park_cores") == 0) {
  262. valid = get_string_value(value, self->values.cpu_park_cores);
  263. } else if (strcmp(name, "pin_cores") == 0) {
  264. valid = get_string_value(value, self->values.cpu_pin_cores);
  265. }
  266. } else if (strcmp(section, "supervisor") == 0) {
  267. /* Supervisor subsection */
  268. if (strcmp(name, "supervisor_whitelist") == 0) {
  269. valid = append_value_to_list(name, value, self->values.supervisor_whitelist);
  270. } else if (strcmp(name, "supervisor_blacklist") == 0) {
  271. valid = append_value_to_list(name, value, self->values.supervisor_blacklist);
  272. } else if (strcmp(name, "require_supervisor") == 0) {
  273. valid = get_long_value(name, value, &self->values.require_supervisor);
  274. }
  275. } else if (strcmp(section, "custom") == 0) {
  276. /* Custom subsection */
  277. if (strcmp(name, "start") == 0) {
  278. valid = append_value_to_list(name, value, self->values.startscripts);
  279. } else if (strcmp(name, "end") == 0) {
  280. valid = append_value_to_list(name, value, self->values.endscripts);
  281. } else if (strcmp(name, "script_timeout") == 0) {
  282. valid = get_long_value(name, value, &self->values.script_timeout);
  283. }
  284. }
  285. if (!valid) {
  286. /* Simply ignore the value, but with a log */
  287. LOG_MSG("Config: Value ignored [%s] %s=%s\n", section, name, value);
  288. }
  289. return 1;
  290. }
  291. /*
  292. * Load the config file
  293. */
  294. static void load_config_files(GameModeConfig *self)
  295. {
  296. /* grab the current dir */
  297. char *config_location_local = get_current_dir_name();
  298. /* Get home config location */
  299. char *config_location_home = NULL;
  300. const char *cfg = getenv("XDG_CONFIG_HOME");
  301. if (cfg) {
  302. config_location_home = realpath(cfg, NULL);
  303. } else {
  304. cfg = getenv("HOME");
  305. if (cfg) {
  306. char *cfg_full = NULL;
  307. if (asprintf(&cfg_full, "%s/.config", cfg) > 0) {
  308. config_location_home = realpath(cfg_full, NULL);
  309. free(cfg_full);
  310. }
  311. } else {
  312. struct passwd *p = getpwuid(getuid());
  313. if (p) {
  314. config_location_home = realpath(p->pw_dir, NULL);
  315. }
  316. }
  317. }
  318. /* Take the write lock for the internal data */
  319. pthread_rwlock_wrlock(&self->rwlock);
  320. /* Clear our config values */
  321. memset(&self->values, 0, sizeof(self->values));
  322. /* Set some non-zero defaults */
  323. self->values.igpu_power_threshold = DEFAULT_IGPU_POWER_THRESHOLD;
  324. self->values.inhibit_screensaver = 1; /* Defaults to on */
  325. self->values.disable_splitlock = 1; /* Defaults to on */
  326. self->values.reaper_frequency = DEFAULT_REAPER_FREQ;
  327. self->values.gpu_device = 0;
  328. self->values.nv_powermizer_mode = -1;
  329. self->values.nv_core_clock_mhz_offset = -1;
  330. self->values.nv_mem_clock_mhz_offset = -1;
  331. self->values.script_timeout = 10; /* Default to 10 seconds for scripts */
  332. /*
  333. * Locations to load, in order
  334. * Arrays merge and values overwrite
  335. */
  336. struct ConfigLocation {
  337. const char *path;
  338. bool protected;
  339. };
  340. struct ConfigLocation locations[CONFIG_NUM_LOCATIONS] = {
  341. { SYSCONFDIR, true }, /* shipped default config */
  342. { "/etc", true }, /* administrator config */
  343. { config_location_home, false }, /* $XDG_CONFIG_HOME or $HOME/.config/ */
  344. { config_location_local, false } /* local data eg. $PWD */
  345. };
  346. /* Load each file in order and overwrite values */
  347. for (unsigned int i = 0; i < CONFIG_NUM_LOCATIONS; i++) {
  348. char *path = NULL;
  349. if (locations[i].path && asprintf(&path, "%s/" CONFIG_NAME, locations[i].path) > 0) {
  350. FILE *f = NULL;
  351. DIR *d = NULL;
  352. if ((f = fopen(path, "r"))) {
  353. LOG_MSG("Loading config file [%s]\n", path);
  354. load_protected = locations[i].protected;
  355. int error = ini_parse_file(f, inih_handler, (void *)self);
  356. /* Failure here isn't fatal */
  357. if (error) {
  358. LOG_MSG("Failed to parse config file - error on line %d!\n", error);
  359. }
  360. fclose(f);
  361. /* Register for inotify */
  362. /* Watch for modification, deletion, moves, or attribute changes */
  363. uint32_t fileflags = IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF;
  364. if ((self->inotwd[i] = inotify_add_watch(self->inotfd, path, fileflags)) == -1) {
  365. LOG_ERROR("Failed to watch %s, error: %s", path, strerror(errno));
  366. }
  367. } else if ((d = opendir(locations[i].path))) {
  368. /* We didn't find a file, so we'll wait on the directory */
  369. /* Notify if a file is created, or move to the directory, or if the directory itself
  370. * is removed or moved away */
  371. uint32_t dirflags = IN_CREATE | IN_MOVED_TO | IN_DELETE_SELF | IN_MOVE_SELF;
  372. if ((self->inotwd[i] =
  373. inotify_add_watch(self->inotfd, locations[i].path, dirflags)) == -1) {
  374. LOG_ERROR("Failed to watch %s, error: %s", path, strerror(errno));
  375. }
  376. closedir(d);
  377. }
  378. free(path);
  379. }
  380. }
  381. /* clean up memory */
  382. free(config_location_home);
  383. free(config_location_local);
  384. /* Release the lock */
  385. pthread_rwlock_unlock(&self->rwlock);
  386. }
  387. /*
  388. * Copy a config parameter with a lock
  389. */
  390. static void memcpy_locked_config(GameModeConfig *self, void *dst, void *src, size_t n)
  391. {
  392. /* Take the read lock */
  393. pthread_rwlock_rdlock(&self->rwlock);
  394. /* copy the data */
  395. memcpy(dst, src, n);
  396. /* release the lock */
  397. pthread_rwlock_unlock(&self->rwlock);
  398. }
  399. /*
  400. * Create a context object
  401. */
  402. GameModeConfig *config_create(void)
  403. {
  404. GameModeConfig *newconfig = (GameModeConfig *)malloc(sizeof(GameModeConfig));
  405. return newconfig;
  406. }
  407. /*
  408. * Initialise the config
  409. */
  410. void config_init(GameModeConfig *self)
  411. {
  412. pthread_rwlock_init(&self->rwlock, NULL);
  413. self->inotfd = inotify_init1(IN_NONBLOCK);
  414. if (self->inotfd == -1)
  415. LOG_ERROR(
  416. "inotify_init failed: %s, gamemode will not be able to watch config files for edits!\n",
  417. strerror(errno));
  418. for (unsigned int i = 0; i < CONFIG_NUM_LOCATIONS; i++) {
  419. self->inotwd[i] = -1;
  420. }
  421. /* load the initial config */
  422. load_config_files(self);
  423. }
  424. /*
  425. * Destroy internal parts of config
  426. */
  427. static void internal_destroy(GameModeConfig *self)
  428. {
  429. pthread_rwlock_destroy(&self->rwlock);
  430. for (unsigned int i = 0; i < CONFIG_NUM_LOCATIONS; i++) {
  431. if (self->inotwd[i] != -1) {
  432. /* TODO: Error handle */
  433. inotify_rm_watch(self->inotfd, self->inotwd[i]);
  434. }
  435. }
  436. if (self->inotfd != -1)
  437. close(self->inotfd);
  438. }
  439. /*
  440. * Re-load the config file
  441. */
  442. void config_reload(GameModeConfig *self)
  443. {
  444. internal_destroy(self);
  445. config_init(self);
  446. }
  447. /*
  448. * Check if the config needs to be reloaded
  449. */
  450. bool config_needs_reload(GameModeConfig *self)
  451. {
  452. bool need = false;
  453. /* Take a read lock while we use the inotify fd */
  454. pthread_rwlock_rdlock(&self->rwlock);
  455. const size_t buflen = sizeof(struct inotify_event) + NAME_MAX + 1;
  456. char buffer[buflen] __attribute__((aligned(__alignof__(struct inotify_event))));
  457. ssize_t len = read(self->inotfd, buffer, buflen);
  458. if (len == -1) {
  459. /* EAGAIN is returned when there's nothing to read on a non-blocking fd */
  460. if (errno != EAGAIN)
  461. LOG_ERROR("Could not read inotify fd: %s\n", strerror(errno));
  462. } else if (len > 0) {
  463. /* Iterate over each event we've been given */
  464. size_t i = 0;
  465. while (i < (size_t)len) {
  466. struct inotify_event *event = (struct inotify_event *)&buffer[i];
  467. /* We have picked up an event and need to handle it */
  468. if (event->mask & IN_ISDIR) {
  469. /* If the event is a dir event we need to take a look */
  470. if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
  471. /* The directory itself changed, trigger a reload */
  472. need = true;
  473. break;
  474. }
  475. } else {
  476. /* When the event has a filename (ie. is from a dir watch), check the name */
  477. if (event->len > 0) {
  478. if (strncmp(basename(event->name), CONFIG_NAME, strlen(CONFIG_NAME)) == 0) {
  479. /* This is a gamemode config file, trigger a reload */
  480. need = true;
  481. break;
  482. }
  483. } else {
  484. /* Otherwise this is for one of our watches on a specific config file, so
  485. * trigger the reload regardless */
  486. need = true;
  487. break;
  488. }
  489. }
  490. i += sizeof(struct inotify_event) + event->len;
  491. }
  492. }
  493. /* Return the read lock */
  494. pthread_rwlock_unlock(&self->rwlock);
  495. return need;
  496. }
  497. /*
  498. * Destroy the config
  499. */
  500. void config_destroy(GameModeConfig *self)
  501. {
  502. internal_destroy(self);
  503. /* Finally, free the memory */
  504. free(self);
  505. }
  506. /*
  507. * Checks if the client is whitelisted
  508. */
  509. bool config_get_client_whitelisted(GameModeConfig *self, const char *client)
  510. {
  511. /* Take the read lock for the internal data */
  512. pthread_rwlock_rdlock(&self->rwlock);
  513. /* If the whitelist is empty then everything passes */
  514. bool found = true;
  515. if (self->values.whitelist[0][0]) {
  516. /*
  517. * Check if the value is found in our whitelist
  518. * Currently is a simple strstr check, but could be modified for wildcards etc.
  519. */
  520. found = config_string_list_contains(client, self->values.whitelist);
  521. }
  522. /* release the lock */
  523. pthread_rwlock_unlock(&self->rwlock);
  524. return found;
  525. }
  526. /*
  527. * Checks if the client is blacklisted
  528. */
  529. bool config_get_client_blacklisted(GameModeConfig *self, const char *client)
  530. {
  531. /* Take the read lock for the internal data */
  532. pthread_rwlock_rdlock(&self->rwlock);
  533. /*
  534. * Check if the value is found in our whitelist
  535. * Currently is a simple strstr check, but could be modified for wildcards etc.
  536. */
  537. bool found = config_string_list_contains(client, self->values.blacklist);
  538. /* release the lock */
  539. pthread_rwlock_unlock(&self->rwlock);
  540. return found;
  541. }
  542. /*
  543. * Gets the reaper frequency
  544. */
  545. DEFINE_CONFIG_GET(reaper_frequency)
  546. /*
  547. * Gets the screensaver inhibit setting
  548. */
  549. bool config_get_inhibit_screensaver(GameModeConfig *self)
  550. {
  551. long val;
  552. memcpy_locked_config(self, &val, &self->values.inhibit_screensaver, sizeof(long));
  553. return val == 1;
  554. }
  555. /*
  556. * Gets the disable splitlock setting
  557. */
  558. bool config_get_disable_splitlock(GameModeConfig *self)
  559. {
  560. long val;
  561. memcpy_locked_config(self, &val, &self->values.disable_splitlock, sizeof(long));
  562. return val == 1;
  563. }
  564. /*
  565. * Get a set of scripts to call when gamemode starts
  566. */
  567. void config_get_gamemode_start_scripts(GameModeConfig *self,
  568. char scripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX])
  569. {
  570. memcpy_locked_config(self,
  571. scripts,
  572. self->values.startscripts,
  573. sizeof(self->values.startscripts));
  574. }
  575. /*
  576. * Get a set of scripts to call when gamemode ends
  577. */
  578. void config_get_gamemode_end_scripts(GameModeConfig *self,
  579. char scripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX])
  580. {
  581. memcpy_locked_config(self, scripts, self->values.endscripts, sizeof(self->values.startscripts));
  582. }
  583. /*
  584. * Get the script timemout value
  585. */
  586. DEFINE_CONFIG_GET(script_timeout)
  587. /*
  588. * Get the chosen default governor
  589. */
  590. void config_get_default_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX])
  591. {
  592. memcpy_locked_config(self, governor, self->values.defaultgov, sizeof(self->values.defaultgov));
  593. }
  594. /*
  595. * Get the chosen desired governor
  596. */
  597. void config_get_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX])
  598. {
  599. memcpy_locked_config(self, governor, self->values.desiredgov, sizeof(self->values.desiredgov));
  600. }
  601. /*
  602. * Get the chosen iGPU desired governor
  603. */
  604. void config_get_igpu_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX])
  605. {
  606. memcpy_locked_config(self,
  607. governor,
  608. self->values.igpu_desiredgov,
  609. sizeof(self->values.igpu_desiredgov));
  610. }
  611. /*
  612. * Get the chosen iGPU power threshold
  613. */
  614. float config_get_igpu_power_threshold(GameModeConfig *self)
  615. {
  616. float value = 0;
  617. memcpy_locked_config(self, &value, &self->values.igpu_power_threshold, sizeof(float));
  618. /* Validate the threshold value */
  619. if (isnan(value) || value < 0) {
  620. LOG_ONCE(ERROR,
  621. "Configured iGPU power threshold value '%f' is invalid, ignoring iGPU default "
  622. "governor.\n",
  623. value);
  624. value = FP_INFINITE;
  625. }
  626. return value;
  627. }
  628. /*
  629. * Get the chosen soft realtime behavior
  630. */
  631. void config_get_soft_realtime(GameModeConfig *self, char softrealtime[CONFIG_VALUE_MAX])
  632. {
  633. memcpy_locked_config(self,
  634. softrealtime,
  635. self->values.softrealtime,
  636. sizeof(self->values.softrealtime));
  637. }
  638. /*
  639. * Get the renice value
  640. */
  641. long config_get_renice_value(GameModeConfig *self)
  642. {
  643. long value = 0;
  644. memcpy_locked_config(self, &value, &self->values.renice, sizeof(long));
  645. /* Validate the renice value */
  646. if ((value < 1 || value > 20) && value != 0) {
  647. LOG_ONCE(ERROR, "Configured renice value '%ld' is invalid, will not renice.\n", value);
  648. value = 0;
  649. }
  650. return value;
  651. }
  652. /*
  653. * Get the ioprio value
  654. */
  655. long config_get_ioprio_value(GameModeConfig *self)
  656. {
  657. long value = 0;
  658. char ioprio_value[CONFIG_VALUE_MAX] = { 0 };
  659. memcpy_locked_config(self, ioprio_value, &self->values.ioprio, sizeof(self->values.ioprio));
  660. /* account for special string values */
  661. if (0 == strncmp(ioprio_value, "off", sizeof(self->values.ioprio)))
  662. value = IOPRIO_DONT_SET;
  663. else if (0 == strncmp(ioprio_value, "default", sizeof(self->values.ioprio)))
  664. value = IOPRIO_RESET_DEFAULT;
  665. else
  666. value = atoi(ioprio_value);
  667. /* Validate values */
  668. if (IOPRIO_RESET_DEFAULT == value) {
  669. LOG_ONCE(MSG, "IO priority will be reset to default behavior (based on CPU priority).\n");
  670. value = 0;
  671. } else {
  672. /* maybe clamp the value */
  673. long invalid_ioprio = value;
  674. value = CLAMP(0, 7, value);
  675. if (value != invalid_ioprio)
  676. LOG_ONCE(ERROR,
  677. "IO priority value %ld invalid, clamping to %ld\n",
  678. invalid_ioprio,
  679. value);
  680. }
  681. return value;
  682. }
  683. /*
  684. * Get various config info for gpu optimisations
  685. */
  686. void config_get_apply_gpu_optimisations(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
  687. {
  688. memcpy_locked_config(self,
  689. value,
  690. &self->values.apply_gpu_optimisations,
  691. sizeof(self->values.apply_gpu_optimisations));
  692. }
  693. /* Define the getters for GPU values */
  694. DEFINE_CONFIG_GET(gpu_device)
  695. DEFINE_CONFIG_GET(nv_core_clock_mhz_offset)
  696. DEFINE_CONFIG_GET(nv_mem_clock_mhz_offset)
  697. DEFINE_CONFIG_GET(nv_powermizer_mode)
  698. void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
  699. {
  700. memcpy_locked_config(self,
  701. value,
  702. &self->values.amd_performance_level,
  703. sizeof(self->values.amd_performance_level));
  704. }
  705. /*
  706. char supervisor_whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  707. char supervisor_blacklist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
  708. */
  709. DEFINE_CONFIG_GET(require_supervisor)
  710. /*
  711. * Get various config info for cpu optimisations
  712. */
  713. void config_get_cpu_park_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
  714. {
  715. memcpy_locked_config(self,
  716. value,
  717. &self->values.cpu_park_cores,
  718. sizeof(self->values.cpu_park_cores));
  719. }
  720. void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
  721. {
  722. memcpy_locked_config(self,
  723. value,
  724. &self->values.cpu_pin_cores,
  725. sizeof(self->values.cpu_pin_cores));
  726. }
  727. /*
  728. * Checks if the supervisor is whitelisted
  729. */
  730. bool config_get_supervisor_whitelisted(GameModeConfig *self, const char *supervisor)
  731. {
  732. /* Take the read lock for the internal data */
  733. pthread_rwlock_rdlock(&self->rwlock);
  734. /* If the whitelist is empty then everything passes */
  735. bool found = true;
  736. if (self->values.supervisor_whitelist[0][0]) {
  737. /*
  738. * Check if the value is found in our whitelist
  739. * Currently is a simple strstr check, but could be modified for wildcards etc.
  740. */
  741. found = config_string_list_contains(supervisor, self->values.supervisor_whitelist);
  742. }
  743. /* release the lock */
  744. pthread_rwlock_unlock(&self->rwlock);
  745. return found;
  746. }
  747. /*
  748. * Checks if the supervisor is blacklisted
  749. */
  750. bool config_get_supervisor_blacklisted(GameModeConfig *self, const char *supervisor)
  751. {
  752. /* Take the read lock for the internal data */
  753. pthread_rwlock_rdlock(&self->rwlock);
  754. /*
  755. * Check if the value is found in our whitelist
  756. * Currently is a simple strstr check, but could be modified for wildcards etc.
  757. */
  758. bool found = config_string_list_contains(supervisor, self->values.supervisor_blacklist);
  759. /* release the lock */
  760. pthread_rwlock_unlock(&self->rwlock);
  761. return found;
  762. }