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