config-parser: Honor XDG_CONFIG_DIRS.
authorOssama Othman <ossama.othman@intel.com>
Fri, 10 May 2013 20:13:11 +0000 (13:13 -0700)
committerOssama Othman <ossama.othman@intel.com>
Tue, 14 May 2013 05:25:58 +0000 (22:25 -0700)
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.

Change-Id: If473edf71839434eb0de88061a3ceca593a934ad
Signed-off-by: Ossama Othman <ossama.othman@intel.com>
14 files changed:
clients/desktop-shell.c
clients/tablet-shell.c
clients/terminal.c
clients/window.c
man/weston.ini.man
shared/config-parser.c
shared/config-parser.h
src/compositor-android.c
src/compositor-drm.c
src/compositor-wayland.c
src/compositor-x11.c
src/compositor.c
src/compositor.h
src/shell.c

index 51808a9..c4d531d 100644 (file)
@@ -1106,7 +1106,7 @@ add_default_launcher(struct desktop *desktop)
 int main(int argc, char *argv[])
 {
        struct desktop desktop = { 0 };
-       char *config_file;
+       int config_fd;
        struct output *output;
        int ret;
 
@@ -1138,11 +1138,11 @@ int main(int argc, char *argv[])
 
        grab_surface_create(&desktop);
 
-       config_file = config_file_path("weston.ini");
-       ret = parse_config_file(config_file,
+       config_fd = open_config_file("weston.ini");
+       ret = parse_config_file(config_fd,
                                config_sections, ARRAY_LENGTH(config_sections),
                                &desktop);
-       free(config_file);
+       close(config_fd);
        if (ret < 0)
                add_default_launcher(&desktop);
 
index 993da7c..a5cef54 100644 (file)
@@ -456,7 +456,7 @@ int main(int argc, char *argv[])
 {
        struct tablet tablet = { 0 };
        struct display *display;
-       char *config_file;
+       int config_fd;
        struct output *output;
 
        display = display_create(argc, argv);
@@ -475,11 +475,11 @@ int main(int argc, char *argv[])
                        window_get_wl_surface(tablet.homescreen->window));
        wl_list_init(&tablet.homescreen->launcher_list);
 
-       config_file = config_file_path("weston.ini");
-       parse_config_file(config_file,
+       config_fd = open_config_file("weston.ini");
+       parse_config_file(config_fd,
                          config_sections, ARRAY_LENGTH(config_sections),
                          &tablet);
-       free(config_file);
+       close(config_fd);
 
        signal(SIGCHLD, sigchild_handler);
 
index 664df5d..268480b 100644 (file)
@@ -2671,17 +2671,17 @@ int main(int argc, char *argv[])
 {
        struct display *d;
        struct terminal *terminal;
-       char *config_file;
+       int config_fd;
 
        option_shell = getenv("SHELL");
        if (!option_shell)
                option_shell = "/bin/bash";
 
-       config_file = config_file_path("weston.ini");
-       parse_config_file(config_file,
+       config_fd = open_config_file("weston.ini");
+       parse_config_file(config_fd,
                          config_sections, ARRAY_LENGTH(config_sections),
                          NULL);
-       free(config_file);
+       close(config_fd);
 
        argc = parse_options(terminal_options,
                             ARRAY_LENGTH(terminal_options), argc, argv);
index 5dbd992..c88ecc3 100644 (file)
@@ -749,7 +749,7 @@ static const struct cursor_alternatives cursors[] = {
 static void
 create_cursors(struct display *display)
 {
-       char *config_file;
+       int config_fd;
        char *theme = NULL;
        unsigned int i, j;
        struct wl_cursor *cursor;
@@ -760,9 +760,9 @@ create_cursors(struct display *display)
                { "shell", shell_keys, ARRAY_LENGTH(shell_keys), NULL },
        };
 
-       config_file = config_file_path("weston.ini");
-       parse_config_file(config_file, cs, ARRAY_LENGTH(cs), NULL);
-       free(config_file);
+       config_fd = open_config_file("weston.ini");
+       parse_config_file(config_fd, cs, ARRAY_LENGTH(cs), NULL);
+       close(config_fd);
 
        display->cursor_theme = wl_cursor_theme_load(theme, 32, display->shm);
        display->cursors =
index 7699e35..461f739 100644 (file)
@@ -24,7 +24,10 @@ server is started:
 .nf
 .BR "$XDG_CONFIG_HOME/weston.ini   " "(if $XDG_CONFIG_HOME is set)"
 .BR "$HOME/.config/weston.ini      " "(if $HOME is set)"
-.BR "<current dir>/weston.ini      " "(if both variables were not set)"
+.B  "weston/weston.ini in each"
+.BR "\ \ \ \ $XDG_CONFIG_DIR           " "(if $XDG_CONFIG_DIRS is set)"
+.BR "/etc/xdg/weston/weston.ini    " "(if $XDG_CONFIG_DIRS is not set)"
+.BR "<current dir>/weston.ini      " "(if no variables were set)"
 .fi
 .RE
 .PP
@@ -32,7 +35,12 @@ where environment variable
 .B $HOME
 is the user's home directory, and
 .B $XDG_CONFIG_HOME
-is the user specific configuration directory.
+is the user specific configuration directory, and
+.B $XDG_CONFIG_DIRS
+is a colon
+.B ':'
+delimited listed of configuration base directories, such as
+.BR /etc/xdg-foo:/etc/xdg .
 .PP
 The
 .I weston.ini
index 10ff86a..652da1f 100644 (file)
  * 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;
 }
index 7fa9c3f..5fb77be 100644 (file)
 #ifndef CONFIGPARSER_H
 #define CONFIGPARSER_H
 
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
 enum config_key_type {
        CONFIG_KEY_INTEGER,             /* typeof data = int */
        CONFIG_KEY_UNSIGNED_INTEGER,    /* typeof data = unsigned int */
@@ -44,12 +48,12 @@ struct config_section {
 };
 
 int
-parse_config_file(const char *path,
+parse_config_file(int config_fd,
                  const struct config_section *sections, int num_sections,
                  void *data);
 
-char *
-config_file_path(const char *name);
+int
+open_config_file(const char *name);
 
 enum weston_option_type {
        WESTON_OPTION_INTEGER,
@@ -69,5 +73,9 @@ int
 parse_options(const struct weston_option *options,
              int count, int argc, char *argv[]);
 
+#ifdef  __cplusplus
+}
+#endif
+
 #endif /* CONFIGPARSER_H */
 
index a55c9b0..ed64ce6 100644 (file)
@@ -454,7 +454,7 @@ android_compositor_destroy(struct weston_compositor *base)
 
 static struct weston_compositor *
 android_compositor_create(struct wl_display *display, int argc, char *argv[],
-                         const char *config_file)
+                         int config_fd)
 {
        struct android_compositor *compositor;
        struct android_output *output;
@@ -466,7 +466,7 @@ android_compositor_create(struct wl_display *display, int argc, char *argv[],
                return NULL;
 
        if (weston_compositor_init(&compositor->base, display, argc, argv,
-                                  config_file) < 0)
+                                  config_fd) < 0)
                goto err_free;
 
        compositor->base.destroy = android_compositor_destroy;
@@ -504,7 +504,7 @@ err_free:
 
 WL_EXPORT struct weston_compositor *
 backend_init(struct wl_display *display, int argc, char *argv[],
-            const char *config_file)
+            int config_fd)
 {
-       return android_compositor_create(display, argc, argv, config_file);
+       return android_compositor_create(display, argc, argv, config_fd);
 }
index 345de57..3f94d4c 100644 (file)
@@ -2273,7 +2273,7 @@ hide_sprites_binding(struct wl_seat *seat, uint32_t time, uint32_t key,
 static struct weston_compositor *
 drm_compositor_create(struct wl_display *display,
                      int connector, const char *seat, int tty,
-                     int argc, char *argv[], const char *config_file)
+                     int argc, char *argv[], int config_fd)
 {
        struct drm_compositor *ec;
        struct udev_device *drm_device;
@@ -2294,7 +2294,7 @@ drm_compositor_create(struct wl_display *display,
        ec->sprites_are_broken = 1;
 
        if (weston_compositor_init(&ec->base, display, argc, argv,
-                                  config_file) < 0) {
+                                  config_fd) < 0) {
                weston_log("weston_compositor_init failed\n");
                goto err_base;
        }
@@ -2560,7 +2560,7 @@ output_section_done(void *data)
 
 WL_EXPORT struct weston_compositor *
 backend_init(struct wl_display *display, int argc, char *argv[],
-            const char *config_file)
+            int config_fd)
 {
        int connector = 0, tty = 0;
        const char *seat = default_seat;
@@ -2587,9 +2587,9 @@ backend_init(struct wl_display *display, int argc, char *argv[],
                ARRAY_LENGTH(drm_config_keys), output_section_done },
        };
 
-       parse_config_file(config_file, config_section,
+       parse_config_file(config_fd, config_section,
                                ARRAY_LENGTH(config_section), NULL);
 
        return drm_compositor_create(display, connector, seat, tty, argc, argv,
-                                    config_file);
+                                    config_fd);
 }
index 7fe7d5d..7e8c80e 100644 (file)
@@ -807,7 +807,7 @@ wayland_destroy(struct weston_compositor *ec)
 static struct weston_compositor *
 wayland_compositor_create(struct wl_display *display,
                          int width, int height, const char *display_name,
-                         int argc, char *argv[], const char *config_file)
+                         int argc, char *argv[], int config_fd)
 {
        struct wayland_compositor *c;
        struct wl_event_loop *loop;
@@ -820,7 +820,7 @@ wayland_compositor_create(struct wl_display *display,
        memset(c, 0, sizeof *c);
 
        if (weston_compositor_init(&c->base, display, argc, argv,
-                                  config_file) < 0)
+                                  config_fd) < 0)
                goto err_free;
 
        c->parent.wl_display = wl_display_connect(display_name);
@@ -882,7 +882,7 @@ err_free:
 
 WL_EXPORT struct weston_compositor *
 backend_init(struct wl_display *display, int argc, char *argv[],
-            const char *config_file)
+            int config_fd)
 {
        int width = 1024, height = 640;
        char *display_name = NULL;
@@ -897,5 +897,5 @@ backend_init(struct wl_display *display, int argc, char *argv[],
                      ARRAY_LENGTH(wayland_options), argc, argv);
 
        return wayland_compositor_create(display, width, height, display_name,
-                                        argc, argv, config_file);
+                                        argc, argv, config_fd);
 }
index 9bd7a43..834eab8 100644 (file)
@@ -1159,7 +1159,7 @@ static struct weston_compositor *
 x11_compositor_create(struct wl_display *display,
                      int fullscreen,
                      int no_input,
-                     int argc, char *argv[], const char *config_file)
+                     int argc, char *argv[], int config_fd)
 {
        struct x11_compositor *c;
        struct x11_configured_output *o;
@@ -1177,7 +1177,7 @@ x11_compositor_create(struct wl_display *display,
        memset(c, 0, sizeof *c);
 
        if (weston_compositor_init(&c->base, display, argc, argv,
-                                  config_file) < 0)
+                                  config_fd) < 0)
                goto err_free;
 
        c->dpy = XOpenDisplay(NULL);
@@ -1337,7 +1337,7 @@ err_free:
 
 WL_EXPORT struct weston_compositor *
 backend_init(struct wl_display *display, int argc, char *argv[],
-            const char *config_file)
+            int config_fd)
 {
        int fullscreen = 0;
        int no_input = 0;
@@ -1365,11 +1365,11 @@ backend_init(struct wl_display *display, int argc, char *argv[],
                ARRAY_LENGTH(x11_config_keys), output_section_done },
        };
 
-       parse_config_file(config_file, config_section,
+       parse_config_file(config_fd, config_section,
                                ARRAY_LENGTH(config_section), NULL);
 
        return x11_compositor_create(display,
                                     fullscreen,
                                     no_input,
-                                    argc, argv, config_file);
+                                    argc, argv, config_fd);
 }
index 0e0835f..a1d7761 100644 (file)
@@ -2807,7 +2807,7 @@ weston_compositor_init(struct weston_compositor *ec,
                       struct wl_display *display,
                       int argc,
                       char *argv[],
-                      const char *config_file)
+                      int config_fd)
 {
        struct wl_event_loop *loop;
        struct xkb_rule_names xkb_names;
@@ -2824,7 +2824,9 @@ weston_compositor_init(struct weston_compositor *ec,
        };
 
        memset(&xkb_names, 0, sizeof(xkb_names));
-       parse_config_file(config_file, cs, ARRAY_LENGTH(cs), ec);
+
+       ec->config_fd = config_fd;
+       parse_config_file(config_fd, cs, ARRAY_LENGTH(cs), ec);
 
        ec->wl_display = display;
        wl_signal_init(&ec->destroy_signal);
@@ -2907,6 +2909,8 @@ weston_compositor_shutdown(struct weston_compositor *ec)
        wl_array_release(&ec->vtxcnt);
 
        wl_event_loop_destroy(ec->input_loop);
+
+       close(ec->config_fd);
 }
 
 WL_EXPORT void
@@ -3185,8 +3189,8 @@ int main(int argc, char *argv[])
        struct sigaction segv_action;
        struct weston_compositor
                *(*backend_init)(struct wl_display *display,
-                                int argc, char *argv[], const char *config_file);
-       int i;
+                                int argc, char *argv[], int config_fd);
+       int i, config_fd;
        char *backend = NULL;
        const char *modules = "desktop-shell.so", *option_modules = NULL;
        char *log = NULL;
@@ -3194,7 +3198,6 @@ int main(int argc, char *argv[])
        int32_t help = 0;
        char *socket_name = "wayland-0";
        int32_t version = 0;
-       char *config_file;
 
        const struct config_key core_config_keys[] = {
                { "modules", CONFIG_KEY_STRING, &modules },
@@ -3261,14 +3264,14 @@ int main(int argc, char *argv[])
                        backend = "drm-backend.so";
        }
 
-       config_file = config_file_path("weston.ini");
-       parse_config_file(config_file, cs, ARRAY_LENGTH(cs), NULL);
+       config_fd = open_config_file("weston.ini");
+       parse_config_file(config_fd, cs, ARRAY_LENGTH(cs), NULL);
 
        backend_init = load_module(backend, "backend_init");
        if (!backend_init)
                exit(EXIT_FAILURE);
 
-       ec = backend_init(display, argc, argv, config_file);
+       ec = backend_init(display, argc, argv, config_fd);
        if (ec == NULL) {
                weston_log("fatal: failed to create compositor\n");
                exit(EXIT_FAILURE);
@@ -3287,8 +3290,6 @@ int main(int argc, char *argv[])
                goto out;
        }
 
-       free(config_file);
-
        ec->option_idle_time = idle_time;
        ec->idle_time = idle_time;
 
index 740009c..bc8ca45 100644 (file)
@@ -370,6 +370,8 @@ struct weston_compositor {
        struct xkb_rule_names xkb_names;
        struct xkb_context *xkb_context;
        struct weston_xkb_info xkb_info;
+
+       int config_fd;
 };
 
 struct weston_region {
@@ -701,7 +703,7 @@ weston_compositor_get_time(void);
 
 int
 weston_compositor_init(struct weston_compositor *ec, struct wl_display *display,
-                      int argc, char *argv[], const char *config_file);
+                      int argc, char *argv[], int config_fd);
 void
 weston_compositor_shutdown(struct weston_compositor *ec);
 void
@@ -833,7 +835,7 @@ gles2_renderer_destroy(struct weston_compositor *ec);
 
 struct weston_compositor *
 backend_init(struct wl_display *display, int argc, char *argv[],
-           const char *config_file);
+           int config_fd);
 
 int
 module_init(struct weston_compositor *compositor);
index fe845bb..605a03d 100644 (file)
@@ -358,7 +358,7 @@ get_animation_type(char *animation)
 static void
 shell_configuration(struct desktop_shell *shell)
 {
-       char *config_file;
+       int config_fd;
        char *path = NULL;
        int duration = 60;
        unsigned int num_workspaces = DEFAULT_NUM_WORKSPACES;
@@ -382,9 +382,9 @@ shell_configuration(struct desktop_shell *shell)
                { "screensaver", saver_keys, ARRAY_LENGTH(saver_keys), NULL },
        };
 
-       config_file = config_file_path("weston.ini");
-       parse_config_file(config_file, cs, ARRAY_LENGTH(cs), shell);
-       free(config_file);
+       config_fd = open_config_file("weston.ini");
+       parse_config_file(config_fd, cs, ARRAY_LENGTH(cs), shell);
+       close(config_fd);
 
        shell->screensaver.path = path;
        shell->screensaver.duration = duration;