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