]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
f05e1d0d | 2 | |
e852f10c | 3 | #include "env-util.h" |
1d230090 | 4 | #include "errno-util.h" |
f05e1d0d LP |
5 | #include "kbd-util.h" |
6 | #include "log.h" | |
7 | #include "path-util.h" | |
fef4fe1a | 8 | #include "recurse-dir.h" |
f05e1d0d LP |
9 | #include "set.h" |
10 | #include "string-util.h" | |
11 | #include "strv.h" | |
12 | #include "utf8.h" | |
13 | ||
e852f10c YW |
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 | ||
fef4fe1a LP |
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; | |
f05e1d0d | 48 | _cleanup_free_ char *p = NULL; |
f05e1d0d LP |
49 | int r; |
50 | ||
fef4fe1a | 51 | assert(de); |
9ef70c06 | 52 | |
fef4fe1a LP |
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; | |
f05e1d0d | 58 | |
fef4fe1a LP |
59 | if (!IN_SET(de->d_type, DT_REG, DT_LNK)) |
60 | return RECURSE_DIR_CONTINUE; | |
73d0806a | 61 | |
fef4fe1a | 62 | const char *e = endswith(de->d_name, ".map") ?: endswith(de->d_name, ".map.gz"); |
73d0806a | 63 | if (!e) |
fef4fe1a | 64 | return RECURSE_DIR_CONTINUE; |
f05e1d0d | 65 | |
fef4fe1a LP |
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; | |
f05e1d0d | 72 | |
fef4fe1a | 73 | assert(data->keymaps); |
9ef70c06 | 74 | |
f05e1d0d LP |
75 | if (!keymap_is_valid(p)) |
76 | return 0; | |
77 | ||
fef4fe1a LP |
78 | r = set_consume(data->keymaps, TAKE_PTR(p)); |
79 | if (r < 0) | |
80 | return r; | |
f05e1d0d | 81 | |
fef4fe1a | 82 | return RECURSE_DIR_CONTINUE; |
f05e1d0d LP |
83 | } |
84 | ||
85 | int get_keymaps(char ***ret) { | |
5d2a48da | 86 | _cleanup_set_free_free_ Set *keymaps = NULL; |
e852f10c | 87 | _cleanup_strv_free_ char **keymap_dirs = NULL; |
fef4fe1a LP |
88 | int r; |
89 | ||
e852f10c YW |
90 | r = keymap_directories(&keymap_dirs); |
91 | if (r < 0) | |
92 | return r; | |
93 | ||
f05e1d0d LP |
94 | keymaps = set_new(&string_hash_ops); |
95 | if (!keymaps) | |
96 | return -ENOMEM; | |
97 | ||
e852f10c | 98 | STRV_FOREACH(dir, keymap_dirs) { |
fef4fe1a LP |
99 | r = recurse_dir_at( |
100 | AT_FDCWD, | |
e852f10c | 101 | *dir, |
fef4fe1a LP |
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 | }); | |
bb44fd07 ZJS |
109 | if (r == -ENOENT) |
110 | continue; | |
111 | if (ERRNO_IS_NEG_RESOURCE(r)) | |
e852f10c | 112 | return log_warning_errno(r, "Failed to read keymap list from %s: %m", *dir); |
bb44fd07 | 113 | if (r < 0) |
e852f10c | 114 | log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", *dir); |
fef4fe1a | 115 | } |
f05e1d0d | 116 | |
3864b4b0 | 117 | _cleanup_strv_free_ char **l = set_get_strv(keymaps); |
fef4fe1a | 118 | if (!l) |
f05e1d0d | 119 | return -ENOMEM; |
f05e1d0d | 120 | |
fef4fe1a LP |
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 */ | |
f05e1d0d LP |
124 | |
125 | if (strv_isempty(l)) | |
126 | return -ENOENT; | |
127 | ||
128 | strv_sort(l); | |
129 | ||
130 | *ret = TAKE_PTR(l); | |
f05e1d0d LP |
131 | return 0; |
132 | } | |
133 | ||
134 | bool keymap_is_valid(const char *name) { | |
f05e1d0d LP |
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 | } | |
9ef70c06 ZJS |
152 | |
153 | int keymap_exists(const char *name) { | |
e852f10c | 154 | _cleanup_strv_free_ char **keymap_dirs = NULL; |
7d01eb35 | 155 | int r; |
9ef70c06 ZJS |
156 | |
157 | if (!keymap_is_valid(name)) | |
158 | return -EINVAL; | |
159 | ||
e852f10c YW |
160 | r = keymap_directories(&keymap_dirs); |
161 | if (r < 0) | |
162 | return r; | |
163 | ||
164 | STRV_FOREACH(dir, keymap_dirs) { | |
fef4fe1a LP |
165 | r = recurse_dir_at( |
166 | AT_FDCWD, | |
e852f10c | 167 | *dir, |
fef4fe1a LP |
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 | }); | |
9ef70c06 | 175 | if (r > 0) |
7d01eb35 ZJS |
176 | return true; |
177 | if (ERRNO_IS_NEG_RESOURCE(r)) | |
178 | return r; | |
179 | if (r < 0 && r != -ENOENT) | |
e852f10c | 180 | log_debug_errno(r, "Failed to read keymap list from %s, ignoring: %m", *dir); |
9ef70c06 ZJS |
181 | } |
182 | ||
7d01eb35 | 183 | return false; |
9ef70c06 | 184 | } |