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