From 361c5b4bebd125a2f5f539e3d3abeaca9c759b6f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 15 Mar 2024 03:12:07 +0900 Subject: [PATCH 1/2] locale: use O_PATH directory fd and faccessat() in find_converted_keymap() Previously, it is assumed that the paths in KBD_KEYMAP_DIRS are ended with a slash. But, in the next commit, paths will become controllable by users, and each path may not be ended with a slash. This should not change any effective behaviors. Just refactoring and preparation. --- src/locale/localed-util.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/locale/localed-util.c b/src/locale/localed-util.c index 0489df573b..de88df163d 100644 --- a/src/locale/localed-util.c +++ b/src/locale/localed-util.c @@ -735,7 +735,7 @@ 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; assert(xc); assert(!isempty(xc->layout)); @@ -748,18 +748,25 @@ int find_converted_keymap(const X11Context *xc, char **ret) { if (!n) return -ENOMEM; + p = strjoin("xkb/", n, ".map"); + pz = strjoin("xkb/", n, ".map.gz"); + if (!p || !pz) + return -ENOMEM; + NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { - _cleanup_free_ char *p = NULL, *pz = NULL; + _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; } From e852f10cb4a4bf8f2a735d133c6f45a4abbde5af Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 14 Mar 2024 18:40:14 +0900 Subject: [PATCH 2/2] kbd-util: allow to override the default keymap directories This introduces $SYSTEMD_KEYMAP_DIRECTORIES environment variable to override the hardcoded keymap directories. I think it is not necessary to provide the first class configuration option for controlling the keymap directories, but it is not good to hardcode the paths. So, let's introduce an environment variable to override that. Prompted by #31759. Closes #31759. --- docs/ENVIRONMENT.md | 7 +++++- src/locale/localed-util.c | 14 ++++++++---- src/shared/kbd-util.c | 45 ++++++++++++++++++++++++++++++++------- src/shared/kbd-util.h | 8 ++----- 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index cd6d66a81f..cb2c6a8df5 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 de88df163d..56996596fe 100644 --- a/src/locale/localed-util.c +++ b/src/locale/localed-util.c @@ -736,6 +736,8 @@ int vconsole_convert_to_x11(const VCContext *vc, X11Context *ret) { int find_converted_keymap(const X11Context *xc, char **ret) { _cleanup_free_ char *n = NULL, *p = NULL, *pz = NULL; + _cleanup_strv_free_ char **keymap_dirs = NULL; + int r; assert(xc); assert(!isempty(xc->layout)); @@ -753,20 +755,24 @@ int find_converted_keymap(const X11Context *xc, char **ret) { if (!p || !pz) return -ENOMEM; - NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { + r = keymap_directories(&keymap_dirs); + if (r < 0) + return r; + + STRV_FOREACH(dir, keymap_dirs) { _cleanup_close_ int dir_fd = -EBADF; bool uncompressed; - dir_fd = open(dir, O_CLOEXEC | O_DIRECTORY | O_PATH); + 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); + log_debug_errno(errno, "Failed to open %s, ignoring: %m", *dir); continue; } 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); + 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);