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