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