]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/kbd-util.c
build(deps): bump pkg/arch from `b13e94a` to `b578e90`
[thirdparty/systemd.git] / src / shared / kbd-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "env-util.h"
4 #include "errno-util.h"
5 #include "kbd-util.h"
6 #include "log.h"
7 #include "path-util.h"
8 #include "recurse-dir.h"
9 #include "set.h"
10 #include "string-util.h"
11 #include "strv.h"
12 #include "utf8.h"
13
14 #define KBD_KEYMAP_DIRS \
15 "/usr/share/keymaps/", \
16 "/usr/share/kbd/keymaps/", \
17 "/usr/lib/kbd/keymaps/"
18
19 int keymap_directories(char ***ret) {
20 assert(ret);
21
22 if (getenv_path_list("SYSTEMD_KEYMAP_DIRECTORIES", ret) >= 0)
23 return 0;
24
25 char **paths = strv_new(KBD_KEYMAP_DIRS);
26 if (!paths)
27 return log_oom_debug();
28
29 *ret = TAKE_PTR(paths);
30 return 0;
31 }
32
33 struct recurse_dir_userdata {
34 const char *keymap_name;
35 Set *keymaps;
36 };
37
38 static int keymap_recurse_dir_callback(
39 RecurseDirEvent event,
40 const char *path,
41 int dir_fd,
42 int inode_fd,
43 const struct dirent *de,
44 const struct statx *sx,
45 void *userdata) {
46
47 struct recurse_dir_userdata *data = userdata;
48 _cleanup_free_ char *p = NULL;
49 int r;
50
51 assert(de);
52
53 /* If 'keymap_name' is non-NULL, return true if keymap 'keymap_name' is found. Otherwise, add all
54 * keymaps to 'keymaps'. */
55
56 if (event != RECURSE_DIR_ENTRY)
57 return RECURSE_DIR_CONTINUE;
58
59 if (!IN_SET(de->d_type, DT_REG, DT_LNK))
60 return RECURSE_DIR_CONTINUE;
61
62 const char *e = endswith(de->d_name, ".map") ?: endswith(de->d_name, ".map.gz");
63 if (!e)
64 return RECURSE_DIR_CONTINUE;
65
66 p = strndup(de->d_name, e - de->d_name);
67 if (!p)
68 return -ENOMEM;
69
70 if (data->keymap_name)
71 return streq(p, data->keymap_name) ? 1 : RECURSE_DIR_CONTINUE;
72
73 assert(data->keymaps);
74
75 if (!keymap_is_valid(p))
76 return 0;
77
78 r = set_consume(data->keymaps, TAKE_PTR(p));
79 if (r < 0)
80 return r;
81
82 return RECURSE_DIR_CONTINUE;
83 }
84
85 int get_keymaps(char ***ret) {
86 _cleanup_set_free_free_ Set *keymaps = NULL;
87 _cleanup_strv_free_ char **keymap_dirs = NULL;
88 int r;
89
90 r = keymap_directories(&keymap_dirs);
91 if (r < 0)
92 return r;
93
94 keymaps = set_new(&string_hash_ops);
95 if (!keymaps)
96 return -ENOMEM;
97
98 STRV_FOREACH(dir, keymap_dirs) {
99 r = recurse_dir_at(
100 AT_FDCWD,
101 *dir,
102 /* statx_mask= */ 0,
103 /* n_depth_max= */ UINT_MAX,
104 RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE,
105 keymap_recurse_dir_callback,
106 &(struct recurse_dir_userdata) {
107 .keymaps = keymaps,
108 });
109 if (r == -ENOENT)
110 continue;
111 if (ERRNO_IS_NEG_RESOURCE(r))
112 return log_warning_errno(r, "Failed to read keymap list from %s: %m", *dir);
113 if (r < 0)
114 log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", *dir);
115 }
116
117 _cleanup_strv_free_ char **l = set_get_strv(keymaps);
118 if (!l)
119 return -ENOMEM;
120
121 keymaps = set_free(keymaps); /* If we got the strv above, then do a set_free() rather than
122 * set_free_free() since the entries of the set are now owned by the
123 * strv */
124
125 if (strv_isempty(l))
126 return -ENOENT;
127
128 strv_sort(l);
129
130 *ret = TAKE_PTR(l);
131 return 0;
132 }
133
134 bool keymap_is_valid(const char *name) {
135 if (isempty(name))
136 return false;
137
138 if (strlen(name) >= 128)
139 return false;
140
141 if (!utf8_is_valid(name))
142 return false;
143
144 if (!filename_is_valid(name))
145 return false;
146
147 if (!string_is_safe(name))
148 return false;
149
150 return true;
151 }
152
153 int keymap_exists(const char *name) {
154 _cleanup_strv_free_ char **keymap_dirs = NULL;
155 int r;
156
157 if (!keymap_is_valid(name))
158 return -EINVAL;
159
160 r = keymap_directories(&keymap_dirs);
161 if (r < 0)
162 return r;
163
164 STRV_FOREACH(dir, keymap_dirs) {
165 r = recurse_dir_at(
166 AT_FDCWD,
167 *dir,
168 /* statx_mask= */ 0,
169 /* n_depth_max= */ UINT_MAX,
170 RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE,
171 keymap_recurse_dir_callback,
172 &(struct recurse_dir_userdata) {
173 .keymap_name = name,
174 });
175 if (r > 0)
176 return true;
177 if (ERRNO_IS_NEG_RESOURCE(r))
178 return r;
179 if (r < 0 && r != -ENOENT)
180 log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", *dir);
181 }
182
183 return false;
184 }