/* SPDX-License-Identifier: LGPL-2.1-or-later */ #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" #include "string-util.h" #include "strv.h" #include "utf8.h" struct recurse_dir_userdata { const char *keymap_name; Set *keymaps; }; static int keymap_recurse_dir_callback( RecurseDirEvent event, const char *path, int dir_fd, int inode_fd, const struct dirent *de, const struct statx *sx, void *userdata) { struct recurse_dir_userdata *data = userdata; _cleanup_free_ char *p = NULL; int r; assert(de); /* If 'keymap_name' is non-NULL, return true if keymap 'keymap_name' is found. Otherwise, add all * keymaps to 'keymaps'. */ if (event != RECURSE_DIR_ENTRY) return RECURSE_DIR_CONTINUE; if (!IN_SET(de->d_type, DT_REG, DT_LNK)) return RECURSE_DIR_CONTINUE; const char *e = endswith(de->d_name, ".map") ?: endswith(de->d_name, ".map.gz"); if (!e) return RECURSE_DIR_CONTINUE; p = strndup(de->d_name, e - de->d_name); if (!p) return -ENOMEM; if (data->keymap_name) return streq(p, data->keymap_name) ? 1 : RECURSE_DIR_CONTINUE; assert(data->keymaps); if (!keymap_is_valid(p)) return 0; r = set_consume(data->keymaps, TAKE_PTR(p)); if (r < 0) return r; return RECURSE_DIR_CONTINUE; } int get_keymaps(char ***ret) { _cleanup_(set_free_freep) Set *keymaps = NULL; int r; keymaps = set_new(&string_hash_ops); if (!keymaps) return -ENOMEM; NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { r = recurse_dir_at( AT_FDCWD, dir, /* statx_mask= */ 0, /* n_depth_max= */ UINT_MAX, RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, keymap_recurse_dir_callback, &(struct recurse_dir_userdata) { .keymaps = keymaps, }); if (r < 0) { if (r == -ENOENT) continue; if (ERRNO_IS_RESOURCE(r)) return log_warning_errno(r, "Failed to read keymap list from %s: %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); if (!l) return -ENOMEM; keymaps = set_free(keymaps); /* If we got the strv above, then do a set_free() rather than * set_free_free() since the entries of the set are now owned by the * strv */ if (strv_isempty(l)) return -ENOENT; strv_sort(l); *ret = TAKE_PTR(l); return 0; } bool keymap_is_valid(const char *name) { if (isempty(name)) return false; if (strlen(name) >= 128) return false; if (!utf8_is_valid(name)) return false; if (!filename_is_valid(name)) return false; if (!string_is_safe(name)) return false; return true; } int keymap_exists(const char *name) { int r = 0; if (!keymap_is_valid(name)) return -EINVAL; NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { r = recurse_dir_at( AT_FDCWD, dir, /* statx_mask= */ 0, /* n_depth_max= */ UINT_MAX, RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, keymap_recurse_dir_callback, &(struct recurse_dir_userdata) { .keymap_name = name, }); if (r == -ENOENT) continue; if (ERRNO_IS_RESOURCE(r)) return r; if (r < 0) { log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", dir); continue; } if (r > 0) break; } return r > 0; }