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,
* 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 */
}
char *
-str_join(const char * const parts[],
+str_join(const char *const parts[],
const char *restrict fmt, const char *restrict sep)
{
assert(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;
+}
// 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>
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);
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)
{
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;
}