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