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