diff options
author | Ossama Othman <ossama.othman@intel.com> | 2013-05-14 09:48:26 -0700 |
---|---|---|
committer | Kristian Høgsberg <krh@bitplanet.net> | 2013-05-14 14:36:37 -0400 |
commit | a50e6e4c500e3080b8df7ec14c7e42741477a423 (patch) | |
tree | 0cae5b3f325814e7f2d859eaa3f2c2415c69eb33 /shared/config-parser.c | |
parent | 95eb3a2eb470bd341ab078c7e48a28ffebc6dde1 (diff) |
config-parser: Honor XDG_CONFIG_DIRS
This set of changes adds support for searching for a given config file
in the directories listed in $XDG_CONFIG_DIRS if it wasn't found in
$XDG_CONFIG_HOME or ~/.config. This allows packages to install custom
config files in /etc/xdg/weston, for example, thus allowing them to
avoid dealing with home directories.
To avoid a TOCTOU race the config file is actually open()ed during the
search. Its file descriptor is returned and stored in the compositor
for later use when performing subsequent config file parses.
Signed-off-by: Ossama Othman <ossama.othman@intel.com>
Diffstat (limited to 'shared/config-parser.c')
-rw-r--r-- | shared/config-parser.c | 96 |
1 files changed, 66 insertions, 30 deletions
diff --git a/shared/config-parser.c b/shared/config-parser.c index 10ff86a9..652da1fc 100644 --- a/shared/config-parser.c +++ b/shared/config-parser.c @@ -20,11 +20,17 @@ * OF THIS SOFTWARE. */ +#define _GNU_SOURCE /* for stchrnul() */ #include <string.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <ctype.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> #include "config-parser.h" @@ -86,7 +92,7 @@ handle_key(const struct config_key *key, const char *value) } int -parse_config_file(const char *path, +parse_config_file(int fd, const struct config_section *sections, int num_sections, void *data) { @@ -95,12 +101,17 @@ parse_config_file(const char *path, const struct config_section *current = NULL; int i; - fp = fopen(path, "r"); + if (fd == -1) + return -1; + + fp = fdopen(dup(fd), "r"); if (fp == NULL) { - fprintf(stderr, "couldn't open %s\n", path); + perror("couldn't open config file"); return -1; } + rewind(fp); + while (fgets(line, sizeof line, fp)) { if (line[0] == '#' || line[0] == '\n') { continue; @@ -151,37 +162,62 @@ parse_config_file(const char *path, return 0; } -char * -config_file_path(const char *name) +int +open_config_file(const char *name) { - const char dotconf[] = "/.config/"; - const char *config_dir; - const char *home_dir; - char *path; - size_t size; - - config_dir = getenv("XDG_CONFIG_HOME"); - if (!config_dir) { - home_dir = getenv("HOME"); - if (!home_dir) { - fprintf(stderr, "HOME is not set, using cwd.\n"); - return strdup(name); - } + const char *config_dir = getenv("XDG_CONFIG_HOME"); + const char *home_dir = getenv("HOME"); + const char *config_dirs = getenv("XDG_CONFIG_DIRS"); + char path[PATH_MAX]; + const char *p, *next; + int fd; + + /* Precedence is given to config files in the home directory, + * and then to directories listed in XDG_CONFIG_DIRS and + * finally to the current working directory. */ + + /* $XDG_CONFIG_HOME */ + if (config_dir) { + snprintf(path, sizeof path, "%s/%s", config_dir, name); + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd >= 0) + return fd; + } + + /* $HOME/.config */ + if (home_dir) { + snprintf(path, sizeof path, "%s/.config/%s", home_dir, name); + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd >= 0) + return fd; + } - size = strlen(home_dir) + sizeof dotconf + strlen(name); - path = malloc(size); - if (!path) - return NULL; + /* For each $XDG_CONFIG_DIRS: weston/<config_file> */ + if (!config_dirs) + config_dirs = "/etc/xdg"; /* See XDG base dir spec. */ - snprintf(path, size, "%s%s%s", home_dir, dotconf, name); - return path; + for (p = config_dirs; *p != '\0'; p = next) { + next = strchrnul(p, ':'); + snprintf(path, sizeof path, + "%.*s/weston/%s", (int)(next - p), p, name); + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd >= 0) + return fd; + + if (*next == ':') + next++; } - size = strlen(config_dir) + 1 + strlen(name) + 1; - path = malloc(size); - if (!path) - return NULL; + /* Current working directory. */ + snprintf(path, sizeof path, "./%s", name); + fd = open(path, O_RDONLY | O_CLOEXEC); + + if (fd >= 0) + fprintf(stderr, + "using config in current working directory: %s\n", + path); + else + fprintf(stderr, "config file \"%s\" not found.\n", name); - snprintf(path, size, "%s/%s", config_dir, name); - return path; + return fd; } |