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