daemon_config.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. Copyright (c) 2017, Feral Interactive
  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 "daemon_config.h"
  28. #include "logging.h"
  29. /* Ben Hoyt's inih library */
  30. #include "ini.h"
  31. #include <linux/limits.h>
  32. #include <pthread.h>
  33. #include <stdio.h>
  34. #include <string.h>
  35. /* Name and possible location of the config file */
  36. #define CONFIG_NAME "gamemode.ini"
  37. #define CONFIG_DIR "/usr/share/gamemode/"
  38. /* Maximum values in the whilelist and blacklist */
  39. #define MAX_LIST_VALUES 32
  40. /* Maximum length of values in the whilelist or blacklist */
  41. #define MAX_LIST_VALUE_LENGTH 256
  42. /**
  43. * The config holds various details as needed
  44. * and a rwlock to allow config_reload to be called
  45. */
  46. struct GameModeConfig {
  47. pthread_rwlock_t rwlock;
  48. int inotfd;
  49. int inotwd;
  50. char whitelist[MAX_LIST_VALUES][MAX_LIST_VALUE_LENGTH];
  51. char blacklist[MAX_LIST_VALUES][MAX_LIST_VALUE_LENGTH];
  52. };
  53. /*
  54. * Handler for the inih callback
  55. */
  56. static int inih_handler(void *user, const char *section, const char *name, const char *value)
  57. {
  58. GameModeConfig *self = (GameModeConfig *)user;
  59. bool valid = false;
  60. /* Filter subsection */
  61. if (strcmp(section, "filter") == 0) {
  62. if (strcmp(name, "whitelist") == 0) {
  63. valid = true;
  64. unsigned int i = 0;
  65. while (*self->whitelist[i] && ++i < MAX_LIST_VALUES)
  66. ;
  67. if (i < MAX_LIST_VALUES) {
  68. strncpy(self->whitelist[i], value, MAX_LIST_VALUE_LENGTH);
  69. } else {
  70. LOG_MSG("Could not add [%s] to the whitelist, exceeds limit of %d\n",
  71. value,
  72. MAX_LIST_VALUES);
  73. }
  74. } else if (strcmp(name, "blacklist") == 0) {
  75. valid = true;
  76. unsigned int i = 0;
  77. while (*self->blacklist[i] && ++i < MAX_LIST_VALUES)
  78. ;
  79. if (i < MAX_LIST_VALUES) {
  80. strncpy(self->blacklist[i], value, MAX_LIST_VALUE_LENGTH);
  81. } else {
  82. LOG_MSG("Could not add [%s] to the blacklist, exceeds limit of %d\n",
  83. value,
  84. MAX_LIST_VALUES);
  85. }
  86. }
  87. }
  88. if (!valid) {
  89. /* We hit an unknown section succeed but with a log */
  90. LOG_MSG("Unknown value in config file [%s] %s=%s\n", section, name, value);
  91. }
  92. return 1;
  93. }
  94. /*
  95. * Load the config file
  96. */
  97. static void load_config_file(GameModeConfig *self)
  98. {
  99. /* Take the write lock for the internal data */
  100. pthread_rwlock_wrlock(&self->rwlock);
  101. /* Clear our config values */
  102. memset(self->whitelist, 0, sizeof(self->whitelist));
  103. memset(self->blacklist, 0, sizeof(self->blacklist));
  104. /* try locally first */
  105. FILE *f = fopen(CONFIG_NAME, "r");
  106. if (!f) {
  107. f = fopen(CONFIG_DIR CONFIG_NAME, "r");
  108. if (!f) {
  109. /* Failure here isn't fatal */
  110. LOG_ERROR("Note: No config file found [%s] in working directory or in [%s]\n",
  111. CONFIG_NAME,
  112. CONFIG_DIR);
  113. }
  114. }
  115. if (f) {
  116. int error = ini_parse_file(f, inih_handler, (void *)self);
  117. /* Failure here isn't fatal */
  118. if (error) {
  119. LOG_MSG("Failed to parse config file - error on line %d!\n", error);
  120. }
  121. fclose(f);
  122. }
  123. /* Release the lock */
  124. pthread_rwlock_unlock(&self->rwlock);
  125. }
  126. /*
  127. * Create a context object
  128. */
  129. GameModeConfig *config_create(void)
  130. {
  131. GameModeConfig *newconfig = (GameModeConfig *)malloc(sizeof(GameModeConfig));
  132. return newconfig;
  133. }
  134. /*
  135. * Initialise the config
  136. */
  137. void config_init(GameModeConfig *self)
  138. {
  139. pthread_rwlock_init(&self->rwlock, NULL);
  140. /* load the initial config */
  141. load_config_file(self);
  142. }
  143. /*
  144. * Re-load the config file
  145. */
  146. void config_reload(GameModeConfig *self)
  147. {
  148. load_config_file(self);
  149. }
  150. /*
  151. * Destroy the config
  152. */
  153. void config_destroy(GameModeConfig *self)
  154. {
  155. pthread_rwlock_destroy(&self->rwlock);
  156. /* Finally, free the memory */
  157. free(self);
  158. }
  159. /*
  160. * Checks if the client is whitelisted
  161. */
  162. bool config_get_client_whitelisted(GameModeConfig *self, const char *client)
  163. {
  164. /* Take the read lock for the internal data */
  165. pthread_rwlock_rdlock(&self->rwlock);
  166. /* If the whitelist is empty then everything passes */
  167. bool found = true;
  168. if (self->whitelist[0][0]) {
  169. /*
  170. * Check if the value is found in our whitelist
  171. * Currently is a simple strstr check, but could be modified for wildcards etc.
  172. */
  173. found = false;
  174. for (unsigned int i = 0; i < MAX_LIST_VALUES && self->whitelist[i][0]; i++) {
  175. if (strstr(client, self->whitelist[i])) {
  176. found = true;
  177. }
  178. }
  179. }
  180. /* release the lock */
  181. pthread_rwlock_unlock(&self->rwlock);
  182. return found;
  183. }
  184. /*
  185. * Checks if the client is blacklisted
  186. */
  187. bool config_get_client_blacklisted(GameModeConfig *self, const char *client)
  188. {
  189. /* Take the read lock for the internal data */
  190. pthread_rwlock_rdlock(&self->rwlock);
  191. /*
  192. * Check if the value is found in our whitelist
  193. * Currently is a simple strstr check, but could be modified for wildcards etc.
  194. */
  195. bool found = false;
  196. for (unsigned int i = 0; i < MAX_LIST_VALUES && self->blacklist[i][0]; i++) {
  197. if (strstr(client, self->blacklist[i])) {
  198. found = true;
  199. }
  200. }
  201. /* release the lock */
  202. pthread_rwlock_unlock(&self->rwlock);
  203. return found;
  204. }