diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index e5768eb971..52771226aa 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -348,12 +348,17 @@ All tools: `systemd-gpt-auto-generator` to ensure the root partition is mounted writable in accordance to the GPT partition flags. -`systemd-firstboot` and `localectl`: +`systemd-firstboot`, `localectl`, and `systemd-localed`: * `$SYSTEMD_LIST_NON_UTF8_LOCALES=1` — if set, non-UTF-8 locales are listed among the installed ones. By default non-UTF-8 locales are suppressed from the selection, since we are living in the 21st century. +* `$SYSTEMD_KEYMAP_DIRECTORIES=` — takes a colon (`:`) separated list of keymap + directories. The directories must be absolute and normalized. If unset, the + default keymap directories (/usr/share/keymaps/, /usr/share/kbd/keymaps/, and + /usr/lib/kbd/keymaps/) will be used. + `systemd-resolved`: * `$SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME` — if set to "0", `systemd-resolved` diff --git a/src/locale/localed-util.c b/src/locale/localed-util.c index 0489df573b..56996596fe 100644 --- a/src/locale/localed-util.c +++ b/src/locale/localed-util.c @@ -735,7 +735,9 @@ int vconsole_convert_to_x11(const VCContext *vc, X11Context *ret) { } int find_converted_keymap(const X11Context *xc, char **ret) { - _cleanup_free_ char *n = NULL; + _cleanup_free_ char *n = NULL, *p = NULL, *pz = NULL; + _cleanup_strv_free_ char **keymap_dirs = NULL; + int r; assert(xc); assert(!isempty(xc->layout)); @@ -748,18 +750,29 @@ int find_converted_keymap(const X11Context *xc, char **ret) { if (!n) return -ENOMEM; - NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { - _cleanup_free_ char *p = NULL, *pz = NULL; + p = strjoin("xkb/", n, ".map"); + pz = strjoin("xkb/", n, ".map.gz"); + if (!p || !pz) + return -ENOMEM; + + r = keymap_directories(&keymap_dirs); + if (r < 0) + return r; + + STRV_FOREACH(dir, keymap_dirs) { + _cleanup_close_ int dir_fd = -EBADF; bool uncompressed; - p = strjoin(dir, "xkb/", n, ".map"); - pz = strjoin(dir, "xkb/", n, ".map.gz"); - if (!p || !pz) - return -ENOMEM; + dir_fd = open(*dir, O_CLOEXEC | O_DIRECTORY | O_PATH); + if (dir_fd < 0) { + if (errno != ENOENT) + log_debug_errno(errno, "Failed to open %s, ignoring: %m", *dir); + continue; + } - uncompressed = access(p, F_OK) == 0; - if (uncompressed || access(pz, F_OK) == 0) { - log_debug("Found converted keymap %s at %s", n, uncompressed ? p : pz); + uncompressed = faccessat(dir_fd, p, F_OK, 0) >= 0; + if (uncompressed || faccessat(dir_fd, pz, F_OK, 0) >= 0) { + log_debug("Found converted keymap %s at %s/%s", n, *dir, uncompressed ? p : pz); *ret = TAKE_PTR(n); return 1; } diff --git a/src/shared/kbd-util.c b/src/shared/kbd-util.c index 2f2d161ca6..60e0429b82 100644 --- a/src/shared/kbd-util.c +++ b/src/shared/kbd-util.c @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "env-util.h" #include "errno-util.h" #include "kbd-util.h" #include "log.h" -#include "nulstr-util.h" #include "path-util.h" #include "recurse-dir.h" #include "set.h" @@ -11,6 +11,25 @@ #include "strv.h" #include "utf8.h" +#define KBD_KEYMAP_DIRS \ + "/usr/share/keymaps/", \ + "/usr/share/kbd/keymaps/", \ + "/usr/lib/kbd/keymaps/" + +int keymap_directories(char ***ret) { + assert(ret); + + if (getenv_path_list("SYSTEMD_KEYMAP_DIRECTORIES", ret) >= 0) + return 0; + + char **paths = strv_new(KBD_KEYMAP_DIRS); + if (!paths) + return log_oom_debug(); + + *ret = TAKE_PTR(paths); + return 0; +} + struct recurse_dir_userdata { const char *keymap_name; Set *keymaps; @@ -65,16 +84,21 @@ static int keymap_recurse_dir_callback( int get_keymaps(char ***ret) { _cleanup_set_free_free_ Set *keymaps = NULL; + _cleanup_strv_free_ char **keymap_dirs = NULL; int r; + r = keymap_directories(&keymap_dirs); + if (r < 0) + return r; + keymaps = set_new(&string_hash_ops); if (!keymaps) return -ENOMEM; - NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { + STRV_FOREACH(dir, keymap_dirs) { r = recurse_dir_at( AT_FDCWD, - dir, + *dir, /* statx_mask= */ 0, /* n_depth_max= */ UINT_MAX, RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, @@ -85,9 +109,9 @@ int get_keymaps(char ***ret) { if (r == -ENOENT) continue; if (ERRNO_IS_NEG_RESOURCE(r)) - return log_warning_errno(r, "Failed to read keymap list from %s: %m", dir); + return log_warning_errno(r, "Failed to read keymap list from %s: %m", *dir); if (r < 0) - log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", dir); + log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", *dir); } _cleanup_strv_free_ char **l = set_get_strv(keymaps); @@ -127,15 +151,20 @@ bool keymap_is_valid(const char *name) { } int keymap_exists(const char *name) { + _cleanup_strv_free_ char **keymap_dirs = NULL; int r; if (!keymap_is_valid(name)) return -EINVAL; - NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { + r = keymap_directories(&keymap_dirs); + if (r < 0) + return r; + + STRV_FOREACH(dir, keymap_dirs) { r = recurse_dir_at( AT_FDCWD, - dir, + *dir, /* statx_mask= */ 0, /* n_depth_max= */ UINT_MAX, RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, @@ -148,7 +177,7 @@ int keymap_exists(const char *name) { if (ERRNO_IS_NEG_RESOURCE(r)) return r; if (r < 0 && r != -ENOENT) - log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", dir); + log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", *dir); } return false; diff --git a/src/shared/kbd-util.h b/src/shared/kbd-util.h index aca0dee4bc..a8e365e3ca 100644 --- a/src/shared/kbd-util.h +++ b/src/shared/kbd-util.h @@ -3,11 +3,7 @@ #include -#define KBD_KEYMAP_DIRS \ - "/usr/share/keymaps/\0" \ - "/usr/share/kbd/keymaps/\0" \ - "/usr/lib/kbd/keymaps/\0" - -int get_keymaps(char ***l); +int keymap_directories(char ***ret); +int get_keymaps(char ***ret); bool keymap_is_valid(const char *name); int keymap_exists(const char *name);