Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <string.h>
5 : : #include <sys/stat.h>
6 : : #include <sys/types.h>
7 : : #include <unistd.h>
8 : :
9 : : #if HAVE_XKBCOMMON
10 : : #include <xkbcommon/xkbcommon.h>
11 : : #include <dlfcn.h>
12 : : #endif
13 : :
14 : : #include "sd-bus.h"
15 : :
16 : : #include "alloc-util.h"
17 : : #include "bus-error.h"
18 : : #include "bus-message.h"
19 : : #include "bus-util.h"
20 : : #include "def.h"
21 : : #include "keymap-util.h"
22 : : #include "locale-util.h"
23 : : #include "macro.h"
24 : : #include "main-func.h"
25 : : #include "missing_capability.h"
26 : : #include "path-util.h"
27 : : #include "selinux-util.h"
28 : : #include "signal-util.h"
29 : : #include "string-util.h"
30 : : #include "strv.h"
31 : : #include "user-util.h"
32 : :
33 : 0 : static int locale_update_system_manager(Context *c, sd_bus *bus) {
34 : 0 : _cleanup_free_ char **l_unset = NULL;
35 : 0 : _cleanup_strv_free_ char **l_set = NULL;
36 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
37 : 0 : sd_bus_error error = SD_BUS_ERROR_NULL;
38 : : size_t c_set, c_unset;
39 : : LocaleVariable p;
40 : : int r;
41 : :
42 [ # # ]: 0 : assert(bus);
43 : :
44 : 0 : l_unset = new0(char*, _VARIABLE_LC_MAX);
45 [ # # ]: 0 : if (!l_unset)
46 : 0 : return log_oom();
47 : :
48 : 0 : l_set = new0(char*, _VARIABLE_LC_MAX);
49 [ # # ]: 0 : if (!l_set)
50 : 0 : return log_oom();
51 : :
52 [ # # ]: 0 : for (p = 0, c_set = 0, c_unset = 0; p < _VARIABLE_LC_MAX; p++) {
53 : : const char *name;
54 : :
55 : 0 : name = locale_variable_to_string(p);
56 [ # # ]: 0 : assert(name);
57 : :
58 [ # # ]: 0 : if (isempty(c->locale[p]))
59 : 0 : l_unset[c_set++] = (char*) name;
60 : : else {
61 : : char *s;
62 : :
63 : 0 : s = strjoin(name, "=", c->locale[p]);
64 [ # # ]: 0 : if (!s)
65 : 0 : return log_oom();
66 : :
67 : 0 : l_set[c_unset++] = s;
68 : : }
69 : : }
70 : :
71 [ # # ]: 0 : assert(c_set + c_unset == _VARIABLE_LC_MAX);
72 : 0 : r = sd_bus_message_new_method_call(bus, &m,
73 : : "org.freedesktop.systemd1",
74 : : "/org/freedesktop/systemd1",
75 : : "org.freedesktop.systemd1.Manager",
76 : : "UnsetAndSetEnvironment");
77 [ # # ]: 0 : if (r < 0)
78 [ # # ]: 0 : return bus_log_create_error(r);
79 : :
80 : 0 : r = sd_bus_message_append_strv(m, l_unset);
81 [ # # ]: 0 : if (r < 0)
82 [ # # ]: 0 : return bus_log_create_error(r);
83 : :
84 : 0 : r = sd_bus_message_append_strv(m, l_set);
85 [ # # ]: 0 : if (r < 0)
86 [ # # ]: 0 : return bus_log_create_error(r);
87 : :
88 : 0 : r = sd_bus_call(bus, m, 0, &error, NULL);
89 [ # # ]: 0 : if (r < 0)
90 [ # # ]: 0 : return log_error_errno(r, "Failed to update the manager environment: %s", bus_error_message(&error, r));
91 : :
92 : 0 : return 0;
93 : : }
94 : :
95 : 0 : static int vconsole_reload(sd_bus *bus) {
96 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
97 : : int r;
98 : :
99 [ # # ]: 0 : assert(bus);
100 : :
101 : 0 : r = sd_bus_call_method(bus,
102 : : "org.freedesktop.systemd1",
103 : : "/org/freedesktop/systemd1",
104 : : "org.freedesktop.systemd1.Manager",
105 : : "RestartUnit",
106 : : &error,
107 : : NULL,
108 : : "ss", "systemd-vconsole-setup.service", "replace");
109 : :
110 [ # # ]: 0 : if (r < 0)
111 [ # # ]: 0 : return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
112 : :
113 : 0 : return 0;
114 : : }
115 : :
116 : 0 : static int vconsole_convert_to_x11_and_emit(Context *c, sd_bus_message *m) {
117 : : int r;
118 : :
119 [ # # ]: 0 : assert(m);
120 : :
121 : 0 : r = x11_read_data(c, m);
122 [ # # ]: 0 : if (r < 0)
123 : 0 : return r;
124 : :
125 : 0 : r = vconsole_convert_to_x11(c);
126 [ # # ]: 0 : if (r <= 0)
127 : 0 : return r;
128 : :
129 : : /* modified */
130 : 0 : r = x11_write_data(c);
131 [ # # ]: 0 : if (r < 0)
132 [ # # ]: 0 : return log_error_errno(r, "Failed to write X11 keyboard layout: %m");
133 : :
134 : 0 : sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
135 : : "/org/freedesktop/locale1",
136 : : "org.freedesktop.locale1",
137 : : "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
138 : :
139 : 0 : return 1;
140 : : }
141 : :
142 : 0 : static int x11_convert_to_vconsole_and_emit(Context *c, sd_bus_message *m) {
143 : : int r;
144 : :
145 [ # # ]: 0 : assert(m);
146 : :
147 : 0 : r = vconsole_read_data(c, m);
148 [ # # ]: 0 : if (r < 0)
149 : 0 : return r;
150 : :
151 : 0 : r = x11_convert_to_vconsole(c);
152 [ # # ]: 0 : if (r <= 0)
153 : 0 : return r;
154 : :
155 : : /* modified */
156 : 0 : r = vconsole_write_data(c);
157 [ # # ]: 0 : if (r < 0)
158 [ # # ]: 0 : log_error_errno(r, "Failed to save virtual console keymap: %m");
159 : :
160 : 0 : sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
161 : : "/org/freedesktop/locale1",
162 : : "org.freedesktop.locale1",
163 : : "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
164 : :
165 : 0 : return vconsole_reload(sd_bus_message_get_bus(m));
166 : : }
167 : :
168 : 0 : static int property_get_locale(
169 : : sd_bus *bus,
170 : : const char *path,
171 : : const char *interface,
172 : : const char *property,
173 : : sd_bus_message *reply,
174 : : void *userdata,
175 : : sd_bus_error *error) {
176 : :
177 : 0 : Context *c = userdata;
178 : 0 : _cleanup_strv_free_ char **l = NULL;
179 : : int p, q, r;
180 : :
181 : 0 : r = locale_read_data(c, reply);
182 [ # # ]: 0 : if (r < 0)
183 : 0 : return r;
184 : :
185 : 0 : l = new0(char*, _VARIABLE_LC_MAX+1);
186 [ # # ]: 0 : if (!l)
187 : 0 : return -ENOMEM;
188 : :
189 [ # # ]: 0 : for (p = 0, q = 0; p < _VARIABLE_LC_MAX; p++) {
190 : : char *t;
191 : : const char *name;
192 : :
193 : 0 : name = locale_variable_to_string(p);
194 [ # # ]: 0 : assert(name);
195 : :
196 [ # # ]: 0 : if (isempty(c->locale[p]))
197 : 0 : continue;
198 : :
199 [ # # ]: 0 : if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0)
200 : 0 : return -ENOMEM;
201 : :
202 : 0 : l[q++] = t;
203 : : }
204 : :
205 : 0 : return sd_bus_message_append_strv(reply, l);
206 : : }
207 : :
208 : 0 : static int property_get_vconsole(
209 : : sd_bus *bus,
210 : : const char *path,
211 : : const char *interface,
212 : : const char *property,
213 : : sd_bus_message *reply,
214 : : void *userdata,
215 : : sd_bus_error *error) {
216 : :
217 : 0 : Context *c = userdata;
218 : : int r;
219 : :
220 : 0 : r = vconsole_read_data(c, reply);
221 [ # # ]: 0 : if (r < 0)
222 : 0 : return r;
223 : :
224 [ # # ]: 0 : if (streq(property, "VConsoleKeymap"))
225 : 0 : return sd_bus_message_append_basic(reply, 's', c->vc_keymap);
226 [ # # ]: 0 : else if (streq(property, "VConsoleKeymapToggle"))
227 : 0 : return sd_bus_message_append_basic(reply, 's', c->vc_keymap_toggle);
228 : :
229 : 0 : return -EINVAL;
230 : : }
231 : :
232 : 0 : static int property_get_xkb(
233 : : sd_bus *bus,
234 : : const char *path,
235 : : const char *interface,
236 : : const char *property,
237 : : sd_bus_message *reply,
238 : : void *userdata,
239 : : sd_bus_error *error) {
240 : :
241 : 0 : Context *c = userdata;
242 : : int r;
243 : :
244 : 0 : r = x11_read_data(c, reply);
245 [ # # ]: 0 : if (r < 0)
246 : 0 : return r;
247 : :
248 [ # # ]: 0 : if (streq(property, "X11Layout"))
249 : 0 : return sd_bus_message_append_basic(reply, 's', c->x11_layout);
250 [ # # ]: 0 : else if (streq(property, "X11Model"))
251 : 0 : return sd_bus_message_append_basic(reply, 's', c->x11_model);
252 [ # # ]: 0 : else if (streq(property, "X11Variant"))
253 : 0 : return sd_bus_message_append_basic(reply, 's', c->x11_variant);
254 [ # # ]: 0 : else if (streq(property, "X11Options"))
255 : 0 : return sd_bus_message_append_basic(reply, 's', c->x11_options);
256 : :
257 : 0 : return -EINVAL;
258 : : }
259 : :
260 : 0 : static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
261 : 0 : _cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {};
262 : 0 : _cleanup_strv_free_ char **settings = NULL, **l = NULL;
263 : 0 : Context *c = userdata;
264 : 0 : bool modified = false;
265 : : int interactive, p, r;
266 : : char **i;
267 : :
268 [ # # ]: 0 : assert(m);
269 [ # # ]: 0 : assert(c);
270 : :
271 : 0 : r = bus_message_read_strv_extend(m, &l);
272 [ # # ]: 0 : if (r < 0)
273 : 0 : return r;
274 : :
275 : 0 : r = sd_bus_message_read_basic(m, 'b', &interactive);
276 [ # # ]: 0 : if (r < 0)
277 : 0 : return r;
278 : :
279 : : /* If single locale without variable name is provided, then we assume it is LANG=. */
280 [ # # # # ]: 0 : if (strv_length(l) == 1 && !strchr(*l, '=')) {
281 [ # # ]: 0 : if (!locale_is_valid(*l))
282 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
283 : :
284 : 0 : new_locale[VARIABLE_LANG] = strdup(*l);
285 [ # # ]: 0 : if (!new_locale[VARIABLE_LANG])
286 : 0 : return -ENOMEM;
287 : :
288 : 0 : l = strv_free(l);
289 : : }
290 : :
291 : : /* Check whether a variable is valid */
292 [ # # # # ]: 0 : STRV_FOREACH(i, l) {
293 : 0 : bool valid = false;
294 : :
295 [ # # ]: 0 : for (p = 0; p < _VARIABLE_LC_MAX; p++) {
296 : : size_t k;
297 : : const char *name;
298 : :
299 : 0 : name = locale_variable_to_string(p);
300 [ # # ]: 0 : assert(name);
301 : :
302 : 0 : k = strlen(name);
303 [ # # ]: 0 : if (startswith(*i, name) &&
304 [ # # # # ]: 0 : (*i)[k] == '=' &&
305 : 0 : locale_is_valid((*i) + k + 1)) {
306 : 0 : valid = true;
307 : :
308 : 0 : new_locale[p] = strdup((*i) + k + 1);
309 [ # # ]: 0 : if (!new_locale[p])
310 : 0 : return -ENOMEM;
311 : :
312 : 0 : break;
313 : : }
314 : : }
315 : :
316 [ # # ]: 0 : if (!valid)
317 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
318 : : }
319 : :
320 : : /* If LANG was specified, but not LANGUAGE, check if we should
321 : : * set it based on the language fallback table. */
322 [ # # # # ]: 0 : if (!isempty(new_locale[VARIABLE_LANG]) &&
323 : 0 : isempty(new_locale[VARIABLE_LANGUAGE])) {
324 : 0 : _cleanup_free_ char *language = NULL;
325 : :
326 : 0 : (void) find_language_fallback(new_locale[VARIABLE_LANG], &language);
327 [ # # ]: 0 : if (language) {
328 [ # # ]: 0 : log_debug("Converted LANG=%s to LANGUAGE=%s", new_locale[VARIABLE_LANG], language);
329 : 0 : free_and_replace(new_locale[VARIABLE_LANGUAGE], language);
330 : : }
331 : : }
332 : :
333 : 0 : r = locale_read_data(c, m);
334 [ # # ]: 0 : if (r < 0) {
335 [ # # ]: 0 : log_error_errno(r, "Failed to read locale data: %m");
336 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to read locale data");
337 : : }
338 : :
339 : : /* Merge with the current settings */
340 [ # # ]: 0 : for (p = 0; p < _VARIABLE_LC_MAX; p++)
341 [ # # # # ]: 0 : if (!isempty(c->locale[p]) && isempty(new_locale[p])) {
342 : 0 : new_locale[p] = strdup(c->locale[p]);
343 [ # # ]: 0 : if (!new_locale[p])
344 : 0 : return -ENOMEM;
345 : : }
346 : :
347 : 0 : locale_simplify(new_locale);
348 : :
349 [ # # ]: 0 : for (p = 0; p < _VARIABLE_LC_MAX; p++)
350 [ # # ]: 0 : if (!streq_ptr(c->locale[p], new_locale[p])) {
351 : 0 : modified = true;
352 : 0 : break;
353 : : }
354 : :
355 [ # # ]: 0 : if (!modified) {
356 [ # # ]: 0 : log_debug("Locale settings were not modified.");
357 : 0 : return sd_bus_reply_method_return(m, NULL);
358 : : }
359 : :
360 : 0 : r = bus_verify_polkit_async(
361 : : m,
362 : : CAP_SYS_ADMIN,
363 : : "org.freedesktop.locale1.set-locale",
364 : : NULL,
365 : : interactive,
366 : : UID_INVALID,
367 : : &c->polkit_registry,
368 : : error);
369 [ # # ]: 0 : if (r < 0)
370 : 0 : return r;
371 [ # # ]: 0 : if (r == 0)
372 : 0 : return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
373 : :
374 [ # # ]: 0 : for (p = 0; p < _VARIABLE_LC_MAX; p++)
375 : 0 : free_and_replace(c->locale[p], new_locale[p]);
376 : :
377 : 0 : r = locale_write_data(c, &settings);
378 [ # # ]: 0 : if (r < 0) {
379 [ # # ]: 0 : log_error_errno(r, "Failed to set locale: %m");
380 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to set locale: %m");
381 : : }
382 : :
383 : 0 : (void) locale_update_system_manager(c, sd_bus_message_get_bus(m));
384 : :
385 [ # # ]: 0 : if (settings) {
386 : 0 : _cleanup_free_ char *line;
387 : :
388 : 0 : line = strv_join(settings, ", ");
389 [ # # ]: 0 : log_info("Changed locale to %s.", strnull(line));
390 : : } else
391 [ # # ]: 0 : log_info("Changed locale to unset.");
392 : :
393 : 0 : (void) sd_bus_emit_properties_changed(
394 : : sd_bus_message_get_bus(m),
395 : : "/org/freedesktop/locale1",
396 : : "org.freedesktop.locale1",
397 : : "Locale", NULL);
398 : :
399 : 0 : return sd_bus_reply_method_return(m, NULL);
400 : : }
401 : :
402 : 0 : static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
403 : 0 : Context *c = userdata;
404 : : const char *keymap, *keymap_toggle;
405 : : int convert, interactive, r;
406 : :
407 [ # # ]: 0 : assert(m);
408 [ # # ]: 0 : assert(c);
409 : :
410 : 0 : r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
411 [ # # ]: 0 : if (r < 0)
412 : 0 : return r;
413 : :
414 : 0 : keymap = empty_to_null(keymap);
415 : 0 : keymap_toggle = empty_to_null(keymap_toggle);
416 : :
417 : 0 : r = vconsole_read_data(c, m);
418 [ # # ]: 0 : if (r < 0) {
419 [ # # ]: 0 : log_error_errno(r, "Failed to read virtual console keymap data: %m");
420 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to read virtual console keymap data");
421 : : }
422 : :
423 [ # # # # ]: 0 : if (streq_ptr(keymap, c->vc_keymap) &&
424 : 0 : streq_ptr(keymap_toggle, c->vc_keymap_toggle))
425 : 0 : return sd_bus_reply_method_return(m, NULL);
426 : :
427 [ # # # # : 0 : if ((keymap && (!filename_is_valid(keymap) || !string_is_safe(keymap))) ||
# # ]
428 [ # # # # : 0 : (keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle))))
# # ]
429 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keymap data");
430 : :
431 : 0 : r = bus_verify_polkit_async(
432 : : m,
433 : : CAP_SYS_ADMIN,
434 : : "org.freedesktop.locale1.set-keyboard",
435 : : NULL,
436 : : interactive,
437 : : UID_INVALID,
438 : : &c->polkit_registry,
439 : : error);
440 [ # # ]: 0 : if (r < 0)
441 : 0 : return r;
442 [ # # ]: 0 : if (r == 0)
443 : 0 : return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
444 : :
445 [ # # # # ]: 0 : if (free_and_strdup(&c->vc_keymap, keymap) < 0 ||
446 : 0 : free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0)
447 : 0 : return -ENOMEM;
448 : :
449 : 0 : r = vconsole_write_data(c);
450 [ # # ]: 0 : if (r < 0) {
451 [ # # ]: 0 : log_error_errno(r, "Failed to set virtual console keymap: %m");
452 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %m");
453 : : }
454 : :
455 [ # # ]: 0 : log_info("Changed virtual console keymap to '%s' toggle '%s'",
456 : : strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
457 : :
458 : 0 : (void) vconsole_reload(sd_bus_message_get_bus(m));
459 : :
460 : 0 : (void) sd_bus_emit_properties_changed(
461 : : sd_bus_message_get_bus(m),
462 : : "/org/freedesktop/locale1",
463 : : "org.freedesktop.locale1",
464 : : "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
465 : :
466 [ # # ]: 0 : if (convert) {
467 : 0 : r = vconsole_convert_to_x11_and_emit(c, m);
468 [ # # ]: 0 : if (r < 0)
469 [ # # ]: 0 : log_error_errno(r, "Failed to convert keymap data: %m");
470 : : }
471 : :
472 : 0 : return sd_bus_reply_method_return(m, NULL);
473 : : }
474 : :
475 : : #if HAVE_XKBCOMMON
476 : :
477 : : _printf_(3, 0)
478 : 0 : static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
479 : : const char *fmt;
480 : :
481 [ # # # # : 0 : fmt = strjoina("libxkbcommon: ", format);
# # # # #
# # # ]
482 : : #pragma GCC diagnostic push
483 : : #pragma GCC diagnostic ignored "-Wformat-nonliteral"
484 : 0 : log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
485 : : #pragma GCC diagnostic pop
486 : 0 : }
487 : :
488 : : #define LOAD_SYMBOL(symbol, dl, name) \
489 : : ({ \
490 : : (symbol) = (typeof(symbol)) dlvsym((dl), (name), "V_0.5.0"); \
491 : : (symbol) ? 0 : -EOPNOTSUPP; \
492 : : })
493 : :
494 : 0 : static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
495 : :
496 : : /* We dlopen() the library in order to make the dependency soft. The library (and what it pulls in) is huge
497 : : * after all, hence let's support XKB maps when the library is around, and refuse otherwise. The function
498 : : * pointers to the shared library are below: */
499 : :
500 : 0 : struct xkb_context* (*symbol_xkb_context_new)(enum xkb_context_flags flags) = NULL;
501 : 0 : void (*symbol_xkb_context_unref)(struct xkb_context *context) = NULL;
502 : 0 : void (*symbol_xkb_context_set_log_fn)(struct xkb_context *context, void (*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args)) = NULL;
503 : 0 : struct xkb_keymap* (*symbol_xkb_keymap_new_from_names)(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags) = NULL;
504 : 0 : void (*symbol_xkb_keymap_unref)(struct xkb_keymap *keymap) = NULL;
505 : :
506 : 0 : const struct xkb_rule_names rmlvo = {
507 : : .model = model,
508 : : .layout = layout,
509 : : .variant = variant,
510 : : .options = options,
511 : : };
512 : 0 : struct xkb_context *ctx = NULL;
513 : 0 : struct xkb_keymap *km = NULL;
514 : : void *dl;
515 : : int r;
516 : :
517 : : /* Compile keymap from RMLVO information to check out its validity */
518 : :
519 : 0 : dl = dlopen("libxkbcommon.so.0", RTLD_LAZY);
520 [ # # ]: 0 : if (!dl)
521 : 0 : return -EOPNOTSUPP;
522 : :
523 [ # # ]: 0 : r = LOAD_SYMBOL(symbol_xkb_context_new, dl, "xkb_context_new");
524 [ # # ]: 0 : if (r < 0)
525 : 0 : goto finish;
526 : :
527 [ # # ]: 0 : r = LOAD_SYMBOL(symbol_xkb_context_unref, dl, "xkb_context_unref");
528 [ # # ]: 0 : if (r < 0)
529 : 0 : goto finish;
530 : :
531 [ # # ]: 0 : r = LOAD_SYMBOL(symbol_xkb_context_set_log_fn, dl, "xkb_context_set_log_fn");
532 [ # # ]: 0 : if (r < 0)
533 : 0 : goto finish;
534 : :
535 [ # # ]: 0 : r = LOAD_SYMBOL(symbol_xkb_keymap_new_from_names, dl, "xkb_keymap_new_from_names");
536 [ # # ]: 0 : if (r < 0)
537 : 0 : goto finish;
538 : :
539 [ # # ]: 0 : r = LOAD_SYMBOL(symbol_xkb_keymap_unref, dl, "xkb_keymap_unref");
540 [ # # ]: 0 : if (r < 0)
541 : 0 : goto finish;
542 : :
543 : 0 : ctx = symbol_xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
544 [ # # ]: 0 : if (!ctx) {
545 : 0 : r = -ENOMEM;
546 : 0 : goto finish;
547 : : }
548 : :
549 : 0 : symbol_xkb_context_set_log_fn(ctx, log_xkb);
550 : :
551 : 0 : km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
552 [ # # ]: 0 : if (!km) {
553 : 0 : r = -EINVAL;
554 : 0 : goto finish;
555 : : }
556 : :
557 : 0 : r = 0;
558 : :
559 : 0 : finish:
560 [ # # # # ]: 0 : if (symbol_xkb_keymap_unref && km)
561 : 0 : symbol_xkb_keymap_unref(km);
562 : :
563 [ # # # # ]: 0 : if (symbol_xkb_context_unref && ctx)
564 : 0 : symbol_xkb_context_unref(ctx);
565 : :
566 : 0 : (void) dlclose(dl);
567 : 0 : return r;
568 : : }
569 : :
570 : : #else
571 : :
572 : : static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
573 : : return 0;
574 : : }
575 : :
576 : : #endif
577 : :
578 : 0 : static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
579 : 0 : Context *c = userdata;
580 : : const char *layout, *model, *variant, *options;
581 : : int convert, interactive, r;
582 : :
583 [ # # ]: 0 : assert(m);
584 [ # # ]: 0 : assert(c);
585 : :
586 : 0 : r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
587 [ # # ]: 0 : if (r < 0)
588 : 0 : return r;
589 : :
590 : 0 : layout = empty_to_null(layout);
591 : 0 : model = empty_to_null(model);
592 : 0 : variant = empty_to_null(variant);
593 : 0 : options = empty_to_null(options);
594 : :
595 : 0 : r = x11_read_data(c, m);
596 [ # # ]: 0 : if (r < 0) {
597 [ # # ]: 0 : log_error_errno(r, "Failed to read x11 keyboard layout data: %m");
598 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to read x11 keyboard layout data");
599 : : }
600 : :
601 [ # # # # ]: 0 : if (streq_ptr(layout, c->x11_layout) &&
602 [ # # ]: 0 : streq_ptr(model, c->x11_model) &&
603 [ # # ]: 0 : streq_ptr(variant, c->x11_variant) &&
604 : 0 : streq_ptr(options, c->x11_options))
605 : 0 : return sd_bus_reply_method_return(m, NULL);
606 : :
607 [ # # # # ]: 0 : if ((layout && !string_is_safe(layout)) ||
608 [ # # # # ]: 0 : (model && !string_is_safe(model)) ||
609 [ # # # # ]: 0 : (variant && !string_is_safe(variant)) ||
610 [ # # # # ]: 0 : (options && !string_is_safe(options)))
611 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keyboard data");
612 : :
613 : 0 : r = verify_xkb_rmlvo(model, layout, variant, options);
614 [ # # ]: 0 : if (r < 0) {
615 [ # # ]: 0 : log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m",
616 : : strempty(model), strempty(layout), strempty(variant), strempty(options));
617 : :
618 [ # # ]: 0 : if (r == -EOPNOTSUPP)
619 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system.");
620 : :
621 : 0 : return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid.");
622 : : }
623 : :
624 : 0 : r = bus_verify_polkit_async(
625 : : m,
626 : : CAP_SYS_ADMIN,
627 : : "org.freedesktop.locale1.set-keyboard",
628 : : NULL,
629 : : interactive,
630 : : UID_INVALID,
631 : : &c->polkit_registry,
632 : : error);
633 [ # # ]: 0 : if (r < 0)
634 : 0 : return r;
635 [ # # ]: 0 : if (r == 0)
636 : 0 : return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
637 : :
638 [ # # # # ]: 0 : if (free_and_strdup(&c->x11_layout, layout) < 0 ||
639 [ # # ]: 0 : free_and_strdup(&c->x11_model, model) < 0 ||
640 [ # # ]: 0 : free_and_strdup(&c->x11_variant, variant) < 0 ||
641 : 0 : free_and_strdup(&c->x11_options, options) < 0)
642 : 0 : return -ENOMEM;
643 : :
644 : 0 : r = x11_write_data(c);
645 [ # # ]: 0 : if (r < 0) {
646 [ # # ]: 0 : log_error_errno(r, "Failed to set X11 keyboard layout: %m");
647 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %m");
648 : : }
649 : :
650 [ # # ]: 0 : log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
651 : : strempty(c->x11_layout),
652 : : strempty(c->x11_model),
653 : : strempty(c->x11_variant),
654 : : strempty(c->x11_options));
655 : :
656 : 0 : (void) sd_bus_emit_properties_changed(
657 : : sd_bus_message_get_bus(m),
658 : : "/org/freedesktop/locale1",
659 : : "org.freedesktop.locale1",
660 : : "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
661 : :
662 [ # # ]: 0 : if (convert) {
663 : 0 : r = x11_convert_to_vconsole_and_emit(c, m);
664 [ # # ]: 0 : if (r < 0)
665 [ # # ]: 0 : log_error_errno(r, "Failed to convert keymap data: %m");
666 : : }
667 : :
668 : 0 : return sd_bus_reply_method_return(m, NULL);
669 : : }
670 : :
671 : : static const sd_bus_vtable locale_vtable[] = {
672 : : SD_BUS_VTABLE_START(0),
673 : : SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
674 : : SD_BUS_PROPERTY("X11Layout", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
675 : : SD_BUS_PROPERTY("X11Model", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
676 : : SD_BUS_PROPERTY("X11Variant", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
677 : : SD_BUS_PROPERTY("X11Options", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
678 : : SD_BUS_PROPERTY("VConsoleKeymap", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
679 : : SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
680 : : SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
681 : : SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
682 : : SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
683 : : SD_BUS_VTABLE_END
684 : : };
685 : :
686 : 0 : static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
687 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
688 : : int r;
689 : :
690 [ # # ]: 0 : assert(c);
691 [ # # ]: 0 : assert(event);
692 [ # # ]: 0 : assert(_bus);
693 : :
694 : 0 : r = sd_bus_default_system(&bus);
695 [ # # ]: 0 : if (r < 0)
696 [ # # ]: 0 : return log_error_errno(r, "Failed to get system bus connection: %m");
697 : :
698 : 0 : r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
699 [ # # ]: 0 : if (r < 0)
700 [ # # ]: 0 : return log_error_errno(r, "Failed to register object: %m");
701 : :
702 : 0 : r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL);
703 [ # # ]: 0 : if (r < 0)
704 [ # # ]: 0 : return log_error_errno(r, "Failed to request name: %m");
705 : :
706 : 0 : r = sd_bus_attach_event(bus, event, 0);
707 [ # # ]: 0 : if (r < 0)
708 [ # # ]: 0 : return log_error_errno(r, "Failed to attach bus to event loop: %m");
709 : :
710 : 0 : *_bus = TAKE_PTR(bus);
711 : :
712 : 0 : return 0;
713 : : }
714 : :
715 : 0 : static int run(int argc, char *argv[]) {
716 : 0 : _cleanup_(context_clear) Context context = {
717 : : .locale_mtime = USEC_INFINITY,
718 : : .vc_mtime = USEC_INFINITY,
719 : : .x11_mtime = USEC_INFINITY,
720 : : };
721 : 0 : _cleanup_(sd_event_unrefp) sd_event *event = NULL;
722 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
723 : : int r;
724 : :
725 : 0 : log_setup_service();
726 : :
727 : 0 : umask(0022);
728 : 0 : mac_selinux_init();
729 : :
730 [ # # ]: 0 : if (argc != 1)
731 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
732 : :
733 [ # # ]: 0 : assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
734 : :
735 : 0 : r = sd_event_default(&event);
736 [ # # ]: 0 : if (r < 0)
737 [ # # ]: 0 : return log_error_errno(r, "Failed to allocate event loop: %m");
738 : :
739 : 0 : (void) sd_event_set_watchdog(event, true);
740 : :
741 : 0 : r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
742 [ # # ]: 0 : if (r < 0)
743 [ # # ]: 0 : return log_error_errno(r, "Failed to install SIGINT handler: %m");
744 : :
745 : 0 : r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
746 [ # # ]: 0 : if (r < 0)
747 [ # # ]: 0 : return log_error_errno(r, "Failed to install SIGTERM handler: %m");
748 : :
749 : 0 : r = connect_bus(&context, event, &bus);
750 [ # # ]: 0 : if (r < 0)
751 : 0 : return r;
752 : :
753 : 0 : r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
754 [ # # ]: 0 : if (r < 0)
755 [ # # ]: 0 : return log_error_errno(r, "Failed to run event loop: %m");
756 : :
757 : 0 : return 0;
758 : : }
759 : :
760 : 0 : DEFINE_MAIN_FUNCTION(run);
|