]> git.mdlowis.com Git - proto/labwc.git/commitdiff
session: process environment.d and allow empty variables
authorAndrew J. Hesford <ajh@sideband.org>
Sat, 9 Mar 2024 17:03:30 +0000 (12:03 -0500)
committerJohan Malm <johanmalm@users.noreply.github.com>
Mon, 11 Mar 2024 20:01:14 +0000 (20:01 +0000)
1. All '*.env' files in an 'environment.d' directory alongside each
   potential 'environment' file will be parsed and added to the
   environment.

2. For the purposes of configuration merging, an environment definition
   exists at one level if either the 'environment' file is defined or
   its corresponding 'environment.d' contains any valid '*.env' file.

3. Variable declarations of the form "VARIABLE=", with no following
   value, will be written to the environment as empty strings.

docs/labwc-config.5.scd
include/common/string-helpers.h
src/common/string-helpers.c
src/config/session.c

index b7b107c46a0192325933e6ad5b5f889f494b5c12..e658e7d8014255040171df5be77eda4d65cfdf98 100644 (file)
@@ -37,13 +37,23 @@ option.
 All configuration and theme files except autostart and shutdown are re-loaded on
 receiving signal SIGHUP.
 
-The *environment* file is parsed as *variable=value* and sets environment
-variables accordingly. It is recommended to specify keyboard layout settings and
-cursor size/theme here; see environment variable section below for details. Note
-that the environment file is treated differently by openbox where it is simply
-sourced prior to running openbox.
-Note: Tilde (~) and environment variables in the value are expanded, but
-subshell syntax and apostrophes are ignored.
+Environment variables may be set within *environment* files, wherein each line
+defines shell variables in the format *variable=value*. It is recommended to
+specify keyboard layout settings and cursor size/theme here; see environment
+variable section below for details. Within an XDG Base Directory, a file named
+"environment" will be parsed first, followed by any file matching the glob
+"environment.d/\*.env". Files within the environment.d directory are parsed in
+an arbitrary order; any variables that must be set in a particular sequence
+should be set within the same file. Unless the --merge-config option is
+specified, labwc will consider a particular XDG Base Directory to have provided
+an environment file if that directory contains either the "environment"
+directory or at least one "environment.d/\*.env" file.
+
+Note: environment files are treated differently by Openbox, which will simply
+source the file as a valid shell script before running the window manager. Files
+are instead parsed directly by labwc, although any environment variables
+referenced as $VARIABLE or ${VARIABLE} will be substituted and the tilde (~)
+will be expanded as the user's home directory.
 
 The *autostart* file is executed as a shell script after labwc has read its
 configuration and set variables defined in the environment file. Additionally,
index 6d0ec9056a8139e5a05f9ceb8a68fc8c11d0c826..e95b4e4a22d08fa155df00b3d77e897f5d60488c 100644 (file)
@@ -59,7 +59,18 @@ char *strdup_printf(const char *fmt, ...);
  * The separator is arbitrary. When the separator is NULL, a single space will
  * be used.
  */
-char *str_join(const char * const parts[],
+char *str_join(const char *const parts[],
        const char *restrict fmt, const char *restrict sep);
 
+/**
+ * str_endswith - indicate whether a string ends with a given suffix
+ * @string: string to test
+ * @suffix: suffix to expect in string
+ *
+ * If suffix is "" or NULL, this method always returns true; otherwise, this
+ * method returns true if and only if the full suffix exists at the end of the
+ * string.
+ */
+bool str_endswith(const char *const string, const char *const suffix);
+
 #endif /* LABWC_STRING_HELPERS_H */
index 0a1a8c81341809d078659c91acbddf45581e052e..10978507f176722eeef76ee5c6d0df73815b2ef0 100644 (file)
@@ -86,7 +86,7 @@ strdup_printf(const char *fmt, ...)
 }
 
 char *
-str_join(const char * const parts[],
+str_join(const char *const parts[],
                const char *restrict fmt, const char *restrict sep)
 {
        assert(parts);
@@ -154,3 +154,19 @@ str_join(const char * const parts[],
        return buf;
 }
 
+bool
+str_endswith(const char *const string, const char *const suffix)
+{
+       size_t len_str = string ? strlen(string) : 0;
+       size_t len_sfx = suffix ? strlen(suffix) : 0;
+
+       if (len_str < len_sfx) {
+               return false;
+       }
+
+       if (len_sfx == 0) {
+               return true;
+       }
+
+       return strcmp(string + len_str - len_sfx, suffix) == 0;
+}
index 56cef7177e081502faa56852badd6e481a65d30a..e657a9abcd128c7efa205fdc24cb5ceebe56f6da 100644 (file)
@@ -1,11 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0-only
 #define _POSIX_C_SOURCE 200809L
 #include <assert.h>
+#include <dirent.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <wlr/backend/drm.h>
 #include <wlr/backend/multi.h>
 #include <wlr/util/log.h>
@@ -49,9 +51,10 @@ process_line(char *line)
        buf_add(&value, string_strip(++p));
        buf_expand_shell_variables(&value);
        buf_expand_tilde(&value);
-       if (string_null_or_empty(key) || !value.len) {
+       if (string_null_or_empty(key)) {
                goto error;
        }
+
        setenv(key, value.buf, 1);
 error:
        free(value.buf);
@@ -80,6 +83,71 @@ read_environment_file(const char *filename)
        return true;
 }
 
+static char *
+strdup_env_path_validate(const char *prefix, struct dirent *dirent)
+{
+       assert(prefix);
+
+       /* Valid environment files always end in '.env' */
+       if (!str_endswith(dirent->d_name, ".env")) {
+               return NULL;
+       }
+
+       char *full_path = strdup_printf("%s/%s", prefix, dirent->d_name);
+       if (!full_path) {
+               return NULL;
+       }
+
+       /* Valid environment files must be regular files */
+       struct stat statbuf;
+       if (stat(full_path, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
+               return full_path;
+       }
+
+       free(full_path);
+       return NULL;
+}
+
+static bool
+read_environment_dir(const char *path_prefix)
+{
+       bool success = false;
+       char *path = strdup_printf("%s.d", path_prefix);
+
+       errno = 0;
+       DIR *envdir = opendir(path);
+
+       if (!envdir) {
+               if (errno != ENOENT) {
+                       const char *err_msg = strerror(errno);
+                       wlr_log(WLR_INFO,
+                               "failed to read environment directory: %s",
+                               err_msg ? err_msg : "reason unknown");
+               }
+
+               goto env_dir_cleanup;
+       }
+
+       struct dirent *dirent;
+       while ((dirent = readdir(envdir)) != NULL) {
+               char *env_file_path = strdup_env_path_validate(path, dirent);
+               if (!env_file_path) {
+                       continue;
+               }
+
+               if (read_environment_file(env_file_path)) {
+                       success = true;
+               }
+
+               free(env_file_path);
+       }
+
+env_dir_cleanup:
+       closedir(envdir);
+       free(path);
+       return success;
+}
+
 static void
 backend_check_drm(struct wlr_backend *backend, void *is_drm)
 {
@@ -176,7 +244,13 @@ session_environment_init(void)
 
        for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) {
                struct path *path = wl_container_of(elm, path, link);
+
+               /* Process an environment file itself */
                bool success = read_environment_file(path->string);
+
+               /* Process a correponding environment.d directory */
+               success |= read_environment_dir(path->string);
+
                if (success && !should_merge_config) {
                        break;
                }