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