]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/locale/localed.c
11d9130bbca3973aab2138f5629292c3c3d582f1
[thirdparty/systemd.git] / src / locale / localed.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <sys/stat.h>
4
5 #include "sd-bus.h"
6 #include "sd-event.h"
7
8 #include "alloc-util.h"
9 #include "bus-error.h"
10 #include "bus-locator.h"
11 #include "bus-log-control-api.h"
12 #include "bus-object.h"
13 #include "bus-polkit.h"
14 #include "bus-unit-util.h"
15 #include "bus-util.h"
16 #include "constants.h"
17 #include "daemon-util.h"
18 #include "hashmap.h"
19 #include "label-util.h"
20 #include "localed-util.h"
21 #include "log.h"
22 #include "main-func.h"
23 #include "service-util.h"
24 #include "string-util.h"
25 #include "strv.h"
26
27 static int vconsole_reload(sd_bus *bus) {
28 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
29 int r;
30
31 assert(bus);
32
33 r = bus_call_method(bus, bus_systemd_mgr, "RestartUnit", &error, NULL, "ss", "systemd-vconsole-setup.service", "replace");
34 if (r < 0)
35 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
36 return 0;
37 }
38
39 static int property_get_locale(
40 sd_bus *bus,
41 const char *path,
42 const char *interface,
43 const char *property,
44 sd_bus_message *reply,
45 void *userdata,
46 sd_bus_error *error) {
47
48 Context *c = ASSERT_PTR(userdata);
49 _cleanup_strv_free_ char **l = NULL;
50 int r;
51
52 r = locale_read_data(c, reply);
53 if (r < 0)
54 return r;
55
56 r = locale_context_build_env(&c->locale_context, &l, NULL);
57 if (r < 0)
58 return r;
59
60 return sd_bus_message_append_strv(reply, l);
61 }
62
63 static int property_get_vconsole(
64 sd_bus *bus,
65 const char *path,
66 const char *interface,
67 const char *property,
68 sd_bus_message *reply,
69 void *userdata,
70 sd_bus_error *error) {
71
72 Context *c = ASSERT_PTR(userdata);
73 int r;
74
75 assert(property);
76
77 r = vconsole_read_data(c, reply);
78 if (r < 0)
79 return r;
80
81 if (streq(property, "VConsoleKeymap"))
82 return sd_bus_message_append_basic(reply, 's', c->vc.keymap);
83 if (streq(property, "VConsoleKeymapToggle"))
84 return sd_bus_message_append_basic(reply, 's', c->vc.toggle);
85
86 return -EINVAL;
87 }
88
89 static int property_get_xkb(
90 sd_bus *bus,
91 const char *path,
92 const char *interface,
93 const char *property,
94 sd_bus_message *reply,
95 void *userdata,
96 sd_bus_error *error) {
97
98 Context *c = ASSERT_PTR(userdata);
99 const X11Context *xc;
100 int r;
101
102 assert(property);
103
104 r = vconsole_read_data(c, reply);
105 if (r < 0)
106 return r;
107
108 r = x11_read_data(c, reply);
109 if (r < 0)
110 return r;
111
112 xc = context_get_x11_context(c);
113
114 if (streq(property, "X11Layout"))
115 return sd_bus_message_append_basic(reply, 's', xc->layout);
116 if (streq(property, "X11Model"))
117 return sd_bus_message_append_basic(reply, 's', xc->model);
118 if (streq(property, "X11Variant"))
119 return sd_bus_message_append_basic(reply, 's', xc->variant);
120 if (streq(property, "X11Options"))
121 return sd_bus_message_append_basic(reply, 's', xc->options);
122
123 return -EINVAL;
124 }
125
126 static int process_locale_list_item(
127 const char *assignment,
128 char *new_locale[static _VARIABLE_LC_MAX],
129 bool use_localegen,
130 sd_bus_error *error) {
131
132 assert(assignment);
133 assert(new_locale);
134
135 for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) {
136 const char *name, *e;
137
138 assert_se(name = locale_variable_to_string(p));
139
140 e = startswith(assignment, name);
141 if (!e)
142 continue;
143
144 if (*e != '=')
145 continue;
146
147 e++;
148
149 if (!locale_is_valid(e))
150 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e);
151 if (!use_localegen && locale_is_installed(e) <= 0)
152 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e);
153 if (new_locale[p])
154 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name);
155
156 return strdup_to(&new_locale[p], e);
157 }
158
159 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment);
160 }
161
162 static int locale_gen_process_locale(char *new_locale[static _VARIABLE_LC_MAX], sd_bus_error *error) {
163 int r;
164
165 assert(new_locale);
166
167 for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) {
168 if (p == VARIABLE_LANGUAGE)
169 continue;
170 if (isempty(new_locale[p]))
171 continue;
172 if (locale_is_installed(new_locale[p]))
173 continue;
174
175 r = locale_gen_enable_locale(new_locale[p]);
176 if (r == -ENOEXEC) {
177 log_error_errno(r, "Refused to enable locale for generation: %m");
178 return sd_bus_error_setf(error,
179 SD_BUS_ERROR_INVALID_ARGS,
180 "Specified locale is not installed and non-UTF-8 locale will not be auto-generated: %s",
181 new_locale[p]);
182 }
183 if (r == -EINVAL) {
184 log_error_errno(r, "Failed to enable invalid locale %s for generation.", new_locale[p]);
185 return sd_bus_error_setf(error,
186 SD_BUS_ERROR_INVALID_ARGS,
187 "Cannot enable locale generation for invalid locale: %s",
188 new_locale[p]);
189 }
190 if (r < 0) {
191 log_error_errno(r, "Failed to enable locale for generation: %m");
192 return sd_bus_error_set_errnof(error, r, "Failed to enable locale generation: %m");
193 }
194
195 r = locale_gen_run();
196 if (r < 0) {
197 log_error_errno(r, "Failed to generate locale: %m");
198 return sd_bus_error_set_errnof(error, r, "Failed to generate locale: %m");
199 }
200 }
201
202 return 0;
203 }
204
205 static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
206 _cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {};
207 _cleanup_strv_free_ char **l = NULL, **l_set = NULL, **l_unset = NULL;
208 Context *c = ASSERT_PTR(userdata);
209 int interactive, r;
210 bool use_localegen;
211
212 assert(m);
213
214 r = sd_bus_message_read_strv(m, &l);
215 if (r < 0)
216 return bus_log_parse_error(r);
217
218 r = sd_bus_message_read_basic(m, 'b', &interactive);
219 if (r < 0)
220 return bus_log_parse_error(r);
221
222 use_localegen = locale_gen_check_available();
223
224 /* If single locale without variable name is provided, then we assume it is LANG=. */
225 if (strv_length(l) == 1 && !strchr(l[0], '=')) {
226 if (!locale_is_valid(l[0]))
227 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]);
228 if (!use_localegen && locale_is_installed(l[0]) <= 0)
229 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]);
230
231 new_locale[VARIABLE_LANG] = strdup(l[0]);
232 if (!new_locale[VARIABLE_LANG])
233 return log_oom();
234
235 l = strv_free(l);
236 }
237
238 /* Check whether a variable is valid */
239 STRV_FOREACH(i, l) {
240 r = process_locale_list_item(*i, new_locale, use_localegen, error);
241 if (r < 0)
242 return r;
243 }
244
245 /* If LANG was specified, but not LANGUAGE, check if we should
246 * set it based on the language fallback table. */
247 if (!isempty(new_locale[VARIABLE_LANG]) &&
248 isempty(new_locale[VARIABLE_LANGUAGE])) {
249 _cleanup_free_ char *language = NULL;
250
251 (void) find_language_fallback(new_locale[VARIABLE_LANG], &language);
252 if (language) {
253 log_debug("Converted LANG=%s to LANGUAGE=%s", new_locale[VARIABLE_LANG], language);
254 free_and_replace(new_locale[VARIABLE_LANGUAGE], language);
255 }
256 }
257
258 r = locale_read_data(c, m);
259 if (r < 0) {
260 log_error_errno(r, "Failed to read locale data: %m");
261 return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read locale data");
262 }
263
264 /* Merge with the current settings */
265 r = locale_context_merge(&c->locale_context, new_locale);
266 if (r < 0)
267 return log_oom();
268
269 locale_variables_simplify(new_locale);
270
271 if (locale_context_equal(&c->locale_context, new_locale)) {
272 log_debug("Locale settings were not modified.");
273 return sd_bus_reply_method_return(m, NULL);
274 }
275
276 r = bus_verify_polkit_async_full(
277 m,
278 "org.freedesktop.locale1.set-locale",
279 /* details= */ NULL,
280 /* good_user= */ UID_INVALID,
281 interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
282 &c->polkit_registry,
283 error);
284 if (r < 0)
285 return r;
286 if (r == 0)
287 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
288
289 /* Generate locale in case it is missing and the system is using locale-gen */
290 if (use_localegen) {
291 r = locale_gen_process_locale(new_locale, error);
292 if (r < 0)
293 return r;
294 }
295
296 locale_context_take(&c->locale_context, new_locale);
297
298 /* Write locale configuration */
299 r = locale_context_save(&c->locale_context, &l_set, &l_unset);
300 if (r < 0) {
301 log_error_errno(r, "Failed to set locale: %m");
302 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %m");
303 }
304
305 /* Since we just updated the locale configuration file, ask the system manager to read it again to
306 * update its default locale settings. It's important to not use UnsetAndSetEnvironment or a similar
307 * method because in this case unsetting variables means restoring them to PID1 default values, which
308 * may be outdated, since locale.conf has just changed and PID1 hasn't read it */
309 (void) bus_service_manager_reload(sd_bus_message_get_bus(m));
310
311 if (!strv_isempty(l_set)) {
312 _cleanup_free_ char *line = NULL;
313
314 line = strv_join(l_set, ", ");
315 log_info("Changed locale to %s.", strnull(line));
316 } else
317 log_info("Changed locale to unset.");
318
319 (void) sd_bus_emit_properties_changed(
320 sd_bus_message_get_bus(m),
321 "/org/freedesktop/locale1",
322 "org.freedesktop.locale1",
323 "Locale", NULL);
324
325 return sd_bus_reply_method_return(m, NULL);
326 }
327
328 static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
329 _cleanup_(x11_context_clear) X11Context converted = {};
330 Context *c = ASSERT_PTR(userdata);
331 int convert, interactive, r;
332 bool x_needs_update;
333 VCContext in;
334
335 assert(m);
336
337 r = sd_bus_message_read(m, "ssbb", &in.keymap, &in.toggle, &convert, &interactive);
338 if (r < 0)
339 return bus_log_parse_error(r);
340
341 vc_context_empty_to_null(&in);
342
343 r = vc_context_verify_and_warn(&in, LOG_ERR, error);
344 if (r < 0)
345 return r;
346
347 r = vconsole_read_data(c, m);
348 if (r < 0) {
349 log_error_errno(r, "Failed to read virtual console keymap data: %m");
350 return sd_bus_error_set_errnof(error, r, "Failed to read virtual console keymap data: %m");
351 }
352
353 r = x11_read_data(c, m);
354 if (r < 0) {
355 log_error_errno(r, "Failed to read X11 keyboard layout data: %m");
356 return sd_bus_error_set_errnof(error, r, "Failed to read X11 keyboard layout data: %m");
357 }
358
359 if (convert) {
360 r = vconsole_convert_to_x11(&in, x11_context_verify, &converted);
361 if (r < 0) {
362 log_error_errno(r, "Failed to convert keymap data: %m");
363 return sd_bus_error_set_errnof(error, r, "Failed to convert keymap data: %m");
364 }
365
366 if (x11_context_isempty(&converted))
367 log_notice("No conversion found for virtual console keymap \"%s\".", strempty(in.keymap));
368 else
369 log_info("The virtual console keymap '%s' is converted to X11 keyboard layout '%s' model '%s' variant '%s' options '%s'",
370 in.keymap, strempty(converted.layout), strempty(converted.model), strempty(converted.variant), strempty(converted.options));
371
372 /* save the result of conversion to emit changed properties later. */
373 x_needs_update = !x11_context_equal(&c->x11_from_vc, &converted) || !x11_context_equal(&c->x11_from_xorg, &converted);
374 } else
375 x_needs_update = !x11_context_equal(&c->x11_from_vc, &c->x11_from_xorg);
376
377 if (vc_context_equal(&c->vc, &in) && !x_needs_update)
378 return sd_bus_reply_method_return(m, NULL);
379
380 r = bus_verify_polkit_async_full(
381 m,
382 "org.freedesktop.locale1.set-keyboard",
383 /* details= */ NULL,
384 /* good_user= */ UID_INVALID,
385 interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
386 &c->polkit_registry,
387 error);
388 if (r < 0)
389 return r;
390 if (r == 0)
391 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
392
393 r = vc_context_copy(&c->vc, &in);
394 if (r < 0)
395 return log_oom();
396
397 if (x_needs_update) {
398 if (convert) {
399 r = x11_context_copy(&c->x11_from_vc, &converted);
400 if (r < 0)
401 return log_oom();
402 x11_context_replace(&c->x11_from_xorg, &converted);
403 } else {
404 const X11Context *xc = context_get_x11_context(c);
405
406 /* Even if the conversion is not requested, sync the two X11 contexts. */
407 r = x11_context_copy(&c->x11_from_vc, xc);
408 if (r < 0)
409 return log_oom();
410
411 r = x11_context_copy(&c->x11_from_xorg, xc);
412 if (r < 0)
413 return log_oom();
414 }
415 }
416
417 r = vconsole_write_data(c);
418 if (r < 0)
419 log_warning_errno(r, "Failed to write virtual console keymap, ignoring: %m");
420
421 if (x_needs_update) {
422 r = x11_write_data(c);
423 if (r < 0)
424 log_warning_errno(r, "Failed to write X11 keyboard layout, ignoring: %m");
425 }
426
427 log_info("Changed virtual console keymap to '%s' toggle '%s'",
428 strempty(c->vc.keymap), strempty(c->vc.toggle));
429
430 (void) vconsole_reload(sd_bus_message_get_bus(m));
431
432 (void) sd_bus_emit_properties_changed(
433 sd_bus_message_get_bus(m),
434 "/org/freedesktop/locale1",
435 "org.freedesktop.locale1",
436 "VConsoleKeymap", "VConsoleKeymapToggle",
437 x_needs_update ? "X11Layout" : NULL,
438 x_needs_update ? "X11Model" : NULL,
439 x_needs_update ? "X11Variant" : NULL,
440 x_needs_update ? "X11Options" : NULL,
441 NULL);
442
443 return sd_bus_reply_method_return(m, NULL);
444 }
445
446 static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
447 _cleanup_(vc_context_clear) VCContext converted = {};
448 Context *c = ASSERT_PTR(userdata);
449 int convert, interactive, r;
450 X11Context in;
451
452 assert(m);
453
454 r = sd_bus_message_read(m, "ssssbb", &in.layout, &in.model, &in.variant, &in.options, &convert, &interactive);
455 if (r < 0)
456 return bus_log_parse_error(r);
457
458 x11_context_empty_to_null(&in);
459
460 r = x11_context_verify_and_warn(&in, LOG_ERR, error);
461 if (r < 0)
462 return r;
463
464 r = vconsole_read_data(c, m);
465 if (r < 0) {
466 log_error_errno(r, "Failed to read virtual console keymap data: %m");
467 return sd_bus_error_set_errnof(error, r, "Failed to read virtual console keymap data: %m");
468 }
469
470 r = x11_read_data(c, m);
471 if (r < 0) {
472 log_error_errno(r, "Failed to read x11 keyboard layout data: %m");
473 return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read x11 keyboard layout data");
474 }
475
476 if (convert) {
477 r = x11_convert_to_vconsole(&in, &converted);
478 if (r < 0) {
479 log_error_errno(r, "Failed to convert keymap data: %m");
480 return sd_bus_error_set_errnof(error, r, "Failed to convert keymap data: %m");
481 }
482
483 if (vc_context_isempty(&converted))
484 /* We search for layout-variant match first, but then we also look
485 * for anything which matches just the layout. So it's accurate to say
486 * that we couldn't find anything which matches the layout. */
487 log_notice("No conversion to virtual console map found for \"%s\".", strempty(in.layout));
488 else
489 log_info("The X11 keyboard layout '%s' is converted to virtual console keymap '%s'",
490 in.layout, converted.keymap);
491
492 /* save the result of conversion to emit changed properties later. */
493 convert = !vc_context_equal(&c->vc, &converted);
494 }
495
496 if (x11_context_equal(&c->x11_from_vc, &in) && x11_context_equal(&c->x11_from_xorg, &in) && !convert)
497 return sd_bus_reply_method_return(m, NULL);
498
499 r = bus_verify_polkit_async_full(
500 m,
501 "org.freedesktop.locale1.set-keyboard",
502 /* details= */ NULL,
503 /* good_user= */ UID_INVALID,
504 interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
505 &c->polkit_registry,
506 error);
507 if (r < 0)
508 return r;
509 if (r == 0)
510 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
511
512 r = x11_context_copy(&c->x11_from_vc, &in);
513 if (r < 0)
514 return log_oom();
515
516 r = x11_context_copy(&c->x11_from_xorg, &in);
517 if (r < 0)
518 return log_oom();
519
520 if (convert)
521 vc_context_replace(&c->vc, &converted);
522
523 r = vconsole_write_data(c);
524 if (r < 0)
525 log_warning_errno(r, "Failed to update vconsole.conf, ignoring: %m");
526
527 r = x11_write_data(c);
528 if (r < 0)
529 log_warning_errno(r, "Failed to write X11 keyboard layout, ignoring: %m");
530
531 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
532 strempty(in.layout),
533 strempty(in.model),
534 strempty(in.variant),
535 strempty(in.options));
536
537 (void) sd_bus_emit_properties_changed(
538 sd_bus_message_get_bus(m),
539 "/org/freedesktop/locale1",
540 "org.freedesktop.locale1",
541 "X11Layout", "X11Model", "X11Variant", "X11Options",
542 convert ? "VConsoleKeymap" : NULL,
543 convert ? "VConsoleKeymapToggle" : NULL,
544 NULL);
545
546 if (convert)
547 (void) vconsole_reload(sd_bus_message_get_bus(m));
548
549 return sd_bus_reply_method_return(m, NULL);
550 }
551
552 static const sd_bus_vtable locale_vtable[] = {
553 SD_BUS_VTABLE_START(0),
554 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
555 SD_BUS_PROPERTY("X11Layout", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
556 SD_BUS_PROPERTY("X11Model", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
557 SD_BUS_PROPERTY("X11Variant", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
558 SD_BUS_PROPERTY("X11Options", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
559 SD_BUS_PROPERTY("VConsoleKeymap", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
560 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
561
562 SD_BUS_METHOD_WITH_ARGS("SetLocale",
563 SD_BUS_ARGS("as", locale, "b", interactive),
564 SD_BUS_NO_RESULT,
565 method_set_locale,
566 SD_BUS_VTABLE_UNPRIVILEGED),
567 SD_BUS_METHOD_WITH_ARGS("SetVConsoleKeyboard",
568 SD_BUS_ARGS("s", keymap, "s", keymap_toggle, "b", convert, "b", interactive),
569 SD_BUS_NO_RESULT,
570 method_set_vc_keyboard,
571 SD_BUS_VTABLE_UNPRIVILEGED),
572 SD_BUS_METHOD_WITH_ARGS("SetX11Keyboard",
573 SD_BUS_ARGS("s", layout, "s", model, "s", variant, "s", options, "b", convert, "b", interactive),
574 SD_BUS_NO_RESULT,
575 method_set_x11_keyboard,
576 SD_BUS_VTABLE_UNPRIVILEGED),
577
578 SD_BUS_VTABLE_END
579 };
580
581 static const BusObjectImplementation manager_object = {
582 "/org/freedesktop/locale1",
583 "org.freedesktop.locale1",
584 .vtables = BUS_VTABLES(locale_vtable),
585 };
586
587 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
588 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
589 int r;
590
591 assert(c);
592 assert(event);
593 assert(_bus);
594
595 r = sd_bus_default_system(&bus);
596 if (r < 0)
597 return log_error_errno(r, "Failed to get system bus connection: %m");
598
599 r = bus_add_implementation(bus, &manager_object, c);
600 if (r < 0)
601 return r;
602
603 r = bus_log_control_api_register(bus);
604 if (r < 0)
605 return r;
606
607 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL);
608 if (r < 0)
609 return log_error_errno(r, "Failed to request name: %m");
610
611 r = sd_bus_attach_event(bus, event, 0);
612 if (r < 0)
613 return log_error_errno(r, "Failed to attach bus to event loop: %m");
614
615 *_bus = TAKE_PTR(bus);
616
617 return 0;
618 }
619
620 static bool context_check_idle(void *userdata) {
621 Context *c = ASSERT_PTR(userdata);
622
623 return hashmap_isempty(c->polkit_registry);
624 }
625
626 static int run(int argc, char *argv[]) {
627 _cleanup_(context_clear) Context context = {};
628 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
629 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
630 int r;
631
632 log_setup();
633
634 r = service_parse_argv("systemd-localed.service",
635 "Manage system locale settings and key mappings.",
636 BUS_IMPLEMENTATIONS(&manager_object,
637 &log_control_object),
638 argc, argv);
639 if (r <= 0)
640 return r;
641
642 umask(0022);
643
644 r = mac_init();
645 if (r < 0)
646 return r;
647
648 r = sd_event_default(&event);
649 if (r < 0)
650 return log_error_errno(r, "Failed to allocate event loop: %m");
651
652 (void) sd_event_set_watchdog(event, true);
653
654 r = sd_event_set_signal_exit(event, true);
655 if (r < 0)
656 return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
657
658 r = connect_bus(&context, event, &bus);
659 if (r < 0)
660 return r;
661
662 r = sd_notify(false, NOTIFY_READY_MESSAGE);
663 if (r < 0)
664 log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
665
666 r = bus_event_loop_with_idle(
667 event,
668 bus,
669 "org.freedesktop.locale1",
670 DEFAULT_EXIT_USEC,
671 context_check_idle,
672 &context);
673 if (r < 0)
674 return log_error_errno(r, "Failed to run event loop: %m");
675
676 return 0;
677 }
678
679 DEFINE_MAIN_FUNCTION(run);