From: Andrew J. Hesford Date: Sat, 9 Mar 2024 17:03:30 +0000 (-0500) Subject: session: process environment.d and allow empty variables X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=e837445114b1723d877fc3ccc31b43bd61113891;p=proto%2Flabwc.git session: process environment.d and allow empty variables 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. --- diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index b7b107c4..e658e7d8 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -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, diff --git a/include/common/string-helpers.h b/include/common/string-helpers.h index 6d0ec905..e95b4e4a 100644 --- a/include/common/string-helpers.h +++ b/include/common/string-helpers.h @@ -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 */ diff --git a/src/common/string-helpers.c b/src/common/string-helpers.c index 0a1a8c81..10978507 100644 --- a/src/common/string-helpers.c +++ b/src/common/string-helpers.c @@ -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; +} diff --git a/src/config/session.c b/src/config/session.c index 56cef717..e657a9ab 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include #include #include +#include #include #include #include @@ -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; }