Sfoglia il codice sorgente

Provide a cascaded merge-overwrite config approach for #6

	gamemoded will now load and merge settings from the following locations - arrays will merge and single settings will overwrite.

	1. /usr/share/gamemode/
	2. /etc/
	3. $XDG_CONFIG_HOME or $HOME/.config/
	4. $PWD
Marc Di Luzio 6 anni fa
parent
commit
808f5c9159
3 ha cambiato i file con 79 aggiunte e 25 eliminazioni
  1. 7 1
      README.md
  2. 58 23
      daemon/daemon_config.c
  3. 14 1
      data/gamemoded.1

+ 7 - 1
README.md

@@ -79,7 +79,13 @@ Or, distribute `libgamemodeauto.so` and either add `-lgamemodeauto` to your link
 ---
 ## Configuration
 
-The daemon can currently be configured using a `gamemode.ini` file in `/usr/share/gamemode/`. [gamemode.ini](https://github.com/FeralInteractive/gamemode/blob/master/example/gamemode.ini) is an example of what this file would look like, with explanations for all the variables.
+The daemon can currently be configured using a `gamemode.ini` file. [gamemode.ini](https://github.com/FeralInteractive/gamemode/blob/master/example/gamemode.ini) is an example of what this file would look like, with explanations for all the variables.
+
+Config files are loaded and merged from the following directories, in order:
+1. `/usr/share/gamemode/`
+2. `/etc/`
+3. `$XDG_CONFIG_HOME` or `$HOME/.config/`
+4. `$PWD`
 
 The file parsing uses [inih](https://github.com/benhoyt/inih).
 

+ 58 - 23
daemon/daemon_config.c

@@ -38,12 +38,13 @@ POSSIBILITY OF SUCH DAMAGE.
 
 #include <linux/limits.h>
 #include <pthread.h>
+#include <pwd.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/types.h>
 
 /* Name and possible location of the config file */
 #define CONFIG_NAME "gamemode.ini"
-#define CONFIG_DIR "/usr/share/gamemode/"
 
 /* Default value for the reaper frequency */
 #define DEFAULT_REAPER_FREQ 5
@@ -160,8 +161,32 @@ static int inih_handler(void *user, const char *section, const char *name, const
 /*
  * Load the config file
  */
-static void load_config_file(GameModeConfig *self)
+static void load_config_files(GameModeConfig *self)
 {
+	/* grab the current dir */
+	char *config_location_local = get_current_dir_name();
+
+	/* Get home config location */
+	char *config_location_home = NULL;
+	const char *cfg = getenv("XDG_CONFIG_HOME");
+	if (cfg) {
+		config_location_home = realpath(cfg, NULL);
+	} else {
+		cfg = getenv("HOME");
+		if (cfg) {
+			char *cfg_full = NULL;
+			if (asprintf(&cfg_full, "%s/.config", cfg) > 0) {
+				config_location_home = realpath(cfg_full, NULL);
+				free(cfg_full);
+			}
+		} else {
+			struct passwd *p = getpwuid(getuid());
+			if (p) {
+				config_location_home = realpath(p->pw_dir, NULL);
+			}
+		}
+	}
+
 	/* Take the write lock for the internal data */
 	pthread_rwlock_wrlock(&self->rwlock);
 
@@ -172,28 +197,38 @@ static void load_config_file(GameModeConfig *self)
 	memset(self->endscripts, 0, sizeof(self->endscripts));
 	self->reaper_frequency = DEFAULT_REAPER_FREQ;
 
-	/* try locally first */
-	FILE *f = fopen(CONFIG_NAME, "r");
-	if (!f) {
-		f = fopen(CONFIG_DIR CONFIG_NAME, "r");
-		if (!f) {
-			/* Failure here isn't fatal */
-			LOG_ERROR("Note: No config file found [%s] in working directory or in [%s]\n",
-			          CONFIG_NAME,
-			          CONFIG_DIR);
+	/*
+	 * Locations to load, in order
+	 * Arrays merge and values overwrite
+	 */
+	const char *locations[] = {
+		"/usr/share/gamemode", /* shipped default config */
+		"/etc",                /* administrator config */
+		config_location_home,  /* user defined config eg. $XDG_CONFIG_HOME or $HOME/.config/ */
+		config_location_local  /* local data eg. $PWD */
+	};
+
+	/* Load each file in order and overwrite values */
+	for (unsigned int i = 0; i < sizeof(locations) / sizeof(locations[0]); i++) {
+		char *path = NULL;
+		if (locations[i] && asprintf(&path, "%s/" CONFIG_NAME, locations[i]) > 0) {
+			FILE *f = fopen(path, "r");
+			if (f) {
+				LOG_MSG("Loading config file [%s]\n", path);
+				int error = ini_parse_file(f, inih_handler, (void *)self);
+
+				/* Failure here isn't fatal */
+				if (error) {
+					LOG_MSG("Failed to parse config file - error on line %d!\n", error);
+				}
+			}
+			free(path);
 		}
 	}
 
-	if (f) {
-		int error = ini_parse_file(f, inih_handler, (void *)self);
-
-		/* Failure here isn't fatal */
-		if (error) {
-			LOG_MSG("Failed to parse config file - error on line %d!\n", error);
-		}
-
-		fclose(f);
-	}
+	/* clean up memory */
+	free(config_location_home);
+	free(config_location_local);
 
 	/* Release the lock */
 	pthread_rwlock_unlock(&self->rwlock);
@@ -217,7 +252,7 @@ void config_init(GameModeConfig *self)
 	pthread_rwlock_init(&self->rwlock, NULL);
 
 	/* load the initial config */
-	load_config_file(self);
+	load_config_files(self);
 }
 
 /*
@@ -225,7 +260,7 @@ void config_init(GameModeConfig *self)
  */
 void config_reload(GameModeConfig *self)
 {
-	load_config_file(self);
+	load_config_files(self);
 }
 
 /*

+ 14 - 1
data/gamemoded.1

@@ -74,7 +74,20 @@ Or, distribute \fBlibgamemodeauto.so\fR and either link with \fB\-lgamemodeauto\
 
 .SH CONFIG
 
-\fBgamemoded\fR can be configured with a \fBgamemode.ini\fR file found in \fB/usr/share/gamemode/\fR. The daemon will load the config file on start-up if it exists.
+\fBgamemoded\fR will load and merge \fBgamemode.ini\fR config files from these directories in the following order:
+
+.RS 4
+/usr/share/gamemode/
+.RE
+.RS 4
+/etc/
+.RE
+.RS 4
+$XDG_CONFIG_HOME or $HOME/.config/
+.RE
+.RS 4
+$PWD
+.RE
 
 Behaviour of the config file can be explained by presenting a commented example: