]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/locale/localed.c
ask-password-api: Add more debug logging
[thirdparty/systemd.git] / src / locale / localed.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
1822350d 2
ca78ad1d 3#include <sys/stat.h>
1822350d 4
8d451309 5#include "sd-bus.h"
a7dad604 6#include "sd-event.h"
8d451309 7
b5efdb8a 8#include "alloc-util.h"
8d451309 9#include "bus-error.h"
59228d0d 10#include "bus-locator.h"
ac9f55ed 11#include "bus-log-control-api.h"
86b9a3e3 12#include "bus-object.h"
269e4d2d 13#include "bus-polkit.h"
a9399358 14#include "bus-unit-util.h"
76d62b63 15#include "bus-util.h"
28db6fbf 16#include "constants.h"
33354ffb 17#include "daemon-util.h"
a7dad604
DDM
18#include "hashmap.h"
19#include "label-util.h"
3e5203b3 20#include "localed-util.h"
93a1f792 21#include "log.h"
c6f09e6a 22#include "main-func.h"
fc021a5b 23#include "service-util.h"
4897d1dc 24#include "string-util.h"
bb15fafe 25#include "strv.h"
d4f5a1f4 26
8d451309 27static int vconsole_reload(sd_bus *bus) {
4afd3348 28 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1822350d 29 int r;
1822350d
KS
30
31 assert(bus);
32
59228d0d 33 r = bus_call_method(bus, bus_systemd_mgr, "RestartUnit", &error, NULL, "ss", "systemd-vconsole-setup.service", "replace");
8d451309 34 if (r < 0)
26a93376 35 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
4ae25393 36 return 0;
1822350d
KS
37}
38
ebcf1f97
LP
39static 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
76400a62 48 Context *c = ASSERT_PTR(userdata);
b47d419c 49 _cleanup_strv_free_ char **l = NULL;
ab4ab13c 50 int r;
df4fd2c7
YW
51
52 r = locale_read_data(c, reply);
53 if (r < 0)
54 return r;
1822350d 55
3d36b5d7
YW
56 r = locale_context_build_env(&c->locale_context, &l, NULL);
57 if (r < 0)
58 return r;
1822350d 59
8d451309 60 return sd_bus_message_append_strv(reply, l);
1822350d
KS
61}
62
df4fd2c7
YW
63static 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
76400a62 72 Context *c = ASSERT_PTR(userdata);
df4fd2c7
YW
73 int r;
74
76400a62
YW
75 assert(property);
76
df4fd2c7
YW
77 r = vconsole_read_data(c, reply);
78 if (r < 0)
79 return r;
80
81 if (streq(property, "VConsoleKeymap"))
ba4a886f 82 return sd_bus_message_append_basic(reply, 's', c->vc.keymap);
76400a62 83 if (streq(property, "VConsoleKeymapToggle"))
ba4a886f 84 return sd_bus_message_append_basic(reply, 's', c->vc.toggle);
df4fd2c7
YW
85
86 return -EINVAL;
87}
88
89static 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
76400a62 98 Context *c = ASSERT_PTR(userdata);
b41ec10a 99 const X11Context *xc;
df4fd2c7
YW
100 int r;
101
76400a62
YW
102 assert(property);
103
189cacab
YW
104 r = vconsole_read_data(c, reply);
105 if (r < 0)
106 return r;
107
df4fd2c7
YW
108 r = x11_read_data(c, reply);
109 if (r < 0)
110 return r;
111
f59d83af 112 xc = context_get_x11_context(c);
b41ec10a 113
df4fd2c7 114 if (streq(property, "X11Layout"))
b41ec10a 115 return sd_bus_message_append_basic(reply, 's', xc->layout);
76400a62 116 if (streq(property, "X11Model"))
b41ec10a 117 return sd_bus_message_append_basic(reply, 's', xc->model);
76400a62 118 if (streq(property, "X11Variant"))
b41ec10a 119 return sd_bus_message_append_basic(reply, 's', xc->variant);
76400a62 120 if (streq(property, "X11Options"))
b41ec10a 121 return sd_bus_message_append_basic(reply, 's', xc->options);
df4fd2c7
YW
122
123 return -EINVAL;
124}
125
a00a78b8
LP
126static int process_locale_list_item(
127 const char *assignment,
128 char *new_locale[static _VARIABLE_LC_MAX],
8f20232f 129 bool use_localegen,
a00a78b8
LP
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);
8f20232f 151 if (!use_localegen && locale_is_installed(e) <= 0)
a00a78b8
LP
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
454318d3 156 return strdup_to(&new_locale[p], e);
a00a78b8
LP
157 }
158
159 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment);
160}
161
76400a62 162static int locale_gen_process_locale(char *new_locale[static _VARIABLE_LC_MAX], sd_bus_error *error) {
8f20232f 163 int r;
76400a62 164
8f20232f
MK
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]);
76400a62
YW
182 }
183 if (r == -EINVAL) {
8f20232f
MK
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,
cee60fc3 187 "Cannot enable locale generation for invalid locale: %s",
8f20232f 188 new_locale[p]);
76400a62
YW
189 }
190 if (r < 0) {
8f20232f
MK
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
19070062 205static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
e6755a33 206 _cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {};
3d36b5d7 207 _cleanup_strv_free_ char **l = NULL, **l_set = NULL, **l_unset = NULL;
99534007 208 Context *c = ASSERT_PTR(userdata);
a00a78b8 209 int interactive, r;
8f20232f 210 bool use_localegen;
1822350d 211
19070062 212 assert(m);
19070062 213
a00a78b8 214 r = sd_bus_message_read_strv(m, &l);
8d451309 215 if (r < 0)
e0a72001 216 return bus_log_parse_error(r);
1822350d 217
8d451309
KS
218 r = sd_bus_message_read_basic(m, 'b', &interactive);
219 if (r < 0)
e0a72001 220 return bus_log_parse_error(r);
1822350d 221
8f20232f
MK
222 use_localegen = locale_gen_check_available();
223
4156e767 224 /* If single locale without variable name is provided, then we assume it is LANG=. */
a00a78b8
LP
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]);
8f20232f 228 if (!use_localegen && locale_is_installed(l[0]) <= 0)
a00a78b8 229 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]);
4156e767 230
a00a78b8 231 new_locale[VARIABLE_LANG] = strdup(l[0]);
4156e767 232 if (!new_locale[VARIABLE_LANG])
e0a72001 233 return log_oom();
4156e767
YW
234
235 l = strv_free(l);
236 }
237
df4fd2c7 238 /* Check whether a variable is valid */
8d451309 239 STRV_FOREACH(i, l) {
8f20232f 240 r = process_locale_list_item(*i, new_locale, use_localegen, error);
a00a78b8
LP
241 if (r < 0)
242 return r;
8d451309 243 }
1822350d 244
4e829d21
ZJS
245 /* If LANG was specified, but not LANGUAGE, check if we should
246 * set it based on the language fallback table. */
df4fd2c7
YW
247 if (!isempty(new_locale[VARIABLE_LANG]) &&
248 isempty(new_locale[VARIABLE_LANGUAGE])) {
4e829d21
ZJS
249 _cleanup_free_ char *language = NULL;
250
df4fd2c7 251 (void) find_language_fallback(new_locale[VARIABLE_LANG], &language);
4e829d21 252 if (language) {
df4fd2c7
YW
253 log_debug("Converted LANG=%s to LANGUAGE=%s", new_locale[VARIABLE_LANG], language);
254 free_and_replace(new_locale[VARIABLE_LANGUAGE], language);
4e829d21
ZJS
255 }
256 }
257
df4fd2c7
YW
258 r = locale_read_data(c, m);
259 if (r < 0) {
260 log_error_errno(r, "Failed to read locale data: %m");
1b09b81c 261 return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read locale data");
df4fd2c7 262 }
8d451309 263
df4fd2c7 264 /* Merge with the current settings */
3d36b5d7
YW
265 r = locale_context_merge(&c->locale_context, new_locale);
266 if (r < 0)
e0a72001 267 return log_oom();
1822350d 268
3d36b5d7 269 locale_variables_simplify(new_locale);
1822350d 270
3d36b5d7 271 if (locale_context_equal(&c->locale_context, new_locale)) {
df4fd2c7
YW
272 log_debug("Locale settings were not modified.");
273 return sd_bus_reply_method_return(m, NULL);
274 }
1822350d 275
7b36fb9f 276 r = bus_verify_polkit_async_full(
df4fd2c7 277 m,
df4fd2c7 278 "org.freedesktop.locale1.set-locale",
7b36fb9f 279 /* details= */ NULL,
7b36fb9f 280 /* good_user= */ UID_INVALID,
29556b75 281 interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
af7865c1 282 &c->polkit_registry,
df4fd2c7
YW
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 */
1822350d 288
8f20232f
MK
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
3d36b5d7 296 locale_context_take(&c->locale_context, new_locale);
1822350d 297
8f20232f 298 /* Write locale configuration */
3d36b5d7 299 r = locale_context_save(&c->locale_context, &l_set, &l_unset);
df4fd2c7
YW
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 }
502f9614 304
c8966e81
FB
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 */
a9399358 309 (void) bus_service_manager_reload(sd_bus_message_get_bus(m));
1822350d 310
3d36b5d7 311 if (!strv_isempty(l_set)) {
c2b2df60 312 _cleanup_free_ char *line = NULL;
df4fd2c7 313
3d36b5d7 314 line = strv_join(l_set, ", ");
df4fd2c7 315 log_info("Changed locale to %s.", strnull(line));
502f9614 316 } else
df4fd2c7
YW
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);
502f9614 324
df2d202e 325 return sd_bus_reply_method_return(m, NULL);
8d451309 326}
1822350d 327
19070062 328static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
f41338da 329 _cleanup_(x11_context_clear) X11Context converted = {};
99534007 330 Context *c = ASSERT_PTR(userdata);
b47ff73b 331 int convert, interactive, r;
71fa933b 332 bool x_needs_update;
ba4a886f 333 VCContext in;
f2cc3753 334
19070062 335 assert(m);
19070062 336
ba4a886f 337 r = sd_bus_message_read(m, "ssbb", &in.keymap, &in.toggle, &convert, &interactive);
8d451309 338 if (r < 0)
e0a72001 339 return bus_log_parse_error(r);
1822350d 340
ba4a886f 341 vc_context_empty_to_null(&in);
1822350d 342
384f22e3
YW
343 r = vc_context_verify_and_warn(&in, LOG_ERR, error);
344 if (r < 0)
345 return r;
df4fd2c7 346
800f65f8
YW
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
f41338da
YW
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) {
6d0f5027 360 r = vconsole_convert_to_x11(&in, x11_context_verify, &converted);
f41338da
YW
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
8669bbb5
YW
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
f41338da
YW
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)
b47ff73b 378 return sd_bus_reply_method_return(m, NULL);
1822350d 379
7b36fb9f 380 r = bus_verify_polkit_async_full(
b47ff73b 381 m,
b47ff73b 382 "org.freedesktop.locale1.set-keyboard",
7b36fb9f 383 /* details= */ NULL,
7b36fb9f 384 /* good_user= */ UID_INVALID,
29556b75 385 interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
af7865c1 386 &c->polkit_registry,
b47ff73b
YW
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 */
1822350d 392
ba4a886f
YW
393 r = vc_context_copy(&c->vc, &in);
394 if (r < 0)
395 return log_oom();
0b507b17 396
f41338da
YW
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();
4f34fd5d 414 }
4f34fd5d
YW
415 }
416
b47ff73b 417 r = vconsole_write_data(c);
50ec8245
YW
418 if (r < 0)
419 log_warning_errno(r, "Failed to write virtual console keymap, ignoring: %m");
1822350d 420
71fa933b 421 if (x_needs_update) {
4f34fd5d
YW
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
b47ff73b 427 log_info("Changed virtual console keymap to '%s' toggle '%s'",
ba4a886f 428 strempty(c->vc.keymap), strempty(c->vc.toggle));
1822350d 429
26a93376 430 (void) vconsole_reload(sd_bus_message_get_bus(m));
1822350d 431
b47ff73b
YW
432 (void) sd_bus_emit_properties_changed(
433 sd_bus_message_get_bus(m),
434 "/org/freedesktop/locale1",
435 "org.freedesktop.locale1",
4f34fd5d 436 "VConsoleKeymap", "VConsoleKeymapToggle",
71fa933b
YW
437 x_needs_update ? "X11Layout" : NULL,
438 x_needs_update ? "X11Model" : NULL,
439 x_needs_update ? "X11Variant" : NULL,
440 x_needs_update ? "X11Options" : NULL,
4f34fd5d 441 NULL);
1822350d 442
df2d202e 443 return sd_bus_reply_method_return(m, NULL);
8d451309 444}
1822350d 445
19070062 446static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
f41338da 447 _cleanup_(vc_context_clear) VCContext converted = {};
99534007 448 Context *c = ASSERT_PTR(userdata);
b47ff73b 449 int convert, interactive, r;
71fa933b 450 X11Context in;
1822350d 451
19070062 452 assert(m);
19070062 453
b41ec10a 454 r = sd_bus_message_read(m, "ssssbb", &in.layout, &in.model, &in.variant, &in.options, &convert, &interactive);
8d451309 455 if (r < 0)
e0a72001 456 return bus_log_parse_error(r);
1822350d 457
b41ec10a 458 x11_context_empty_to_null(&in);
1822350d 459
0327d5b2
YW
460 r = x11_context_verify_and_warn(&in, LOG_ERR, error);
461 if (r < 0)
462 return r;
fe28d887 463
189cacab
YW
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
800f65f8
YW
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
f41338da
YW
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
8669bbb5
YW
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
f41338da
YW
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)
800f65f8
YW
497 return sd_bus_reply_method_return(m, NULL);
498
7b36fb9f 499 r = bus_verify_polkit_async_full(
b47ff73b 500 m,
b47ff73b 501 "org.freedesktop.locale1.set-keyboard",
7b36fb9f 502 /* details= */ NULL,
7b36fb9f 503 /* good_user= */ UID_INVALID,
29556b75 504 interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
af7865c1 505 &c->polkit_registry,
b47ff73b
YW
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 */
1822350d 511
71fa933b
YW
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);
b41ec10a
YW
517 if (r < 0)
518 return log_oom();
1822350d 519
f41338da 520 if (convert)
90005a4f 521 vc_context_replace(&c->vc, &converted);
4f34fd5d 522
8589823f
YW
523 r = vconsole_write_data(c);
524 if (r < 0)
525 log_warning_errno(r, "Failed to update vconsole.conf, ignoring: %m");
4f34fd5d 526
b47ff73b 527 r = x11_write_data(c);
50ec8245
YW
528 if (r < 0)
529 log_warning_errno(r, "Failed to write X11 keyboard layout, ignoring: %m");
1822350d 530
b47ff73b 531 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
b41ec10a
YW
532 strempty(in.layout),
533 strempty(in.model),
534 strempty(in.variant),
535 strempty(in.options));
1822350d 536
b47ff73b
YW
537 (void) sd_bus_emit_properties_changed(
538 sd_bus_message_get_bus(m),
539 "/org/freedesktop/locale1",
540 "org.freedesktop.locale1",
4f34fd5d
YW
541 "X11Layout", "X11Model", "X11Variant", "X11Options",
542 convert ? "VConsoleKeymap" : NULL,
543 convert ? "VConsoleKeymapToggle" : NULL,
544 NULL);
1822350d 545
4f34fd5d
YW
546 if (convert)
547 (void) vconsole_reload(sd_bus_message_get_bus(m));
1822350d 548
df2d202e 549 return sd_bus_reply_method_return(m, NULL);
1822350d
KS
550}
551
8d451309
KS
552static const sd_bus_vtable locale_vtable[] = {
553 SD_BUS_VTABLE_START(0),
6d1bd3b2 554 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
df4fd2c7
YW
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),
5cc34d6d 561
71243af7
OJ
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),
5cc34d6d 577
8d451309
KS
578 SD_BUS_VTABLE_END
579};
580
5ceceff1
ZJS
581static const BusObjectImplementation manager_object = {
582 "/org/freedesktop/locale1",
583 "org.freedesktop.locale1",
584 .vtables = BUS_VTABLES(locale_vtable),
585};
586
8d451309 587static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
4afd3348 588 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1822350d
KS
589 int r;
590
8d451309
KS
591 assert(c);
592 assert(event);
1822350d
KS
593 assert(_bus);
594
76b54375 595 r = sd_bus_default_system(&bus);
f647962d
MS
596 if (r < 0)
597 return log_error_errno(r, "Failed to get system bus connection: %m");
1822350d 598
5ceceff1 599 r = bus_add_implementation(bus, &manager_object, c);
f647962d 600 if (r < 0)
5ceceff1 601 return r;
1822350d 602
ac9f55ed
LP
603 r = bus_log_control_api_register(bus);
604 if (r < 0)
605 return r;
606
0c0b9306 607 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL);
f647962d 608 if (r < 0)
0c0b9306 609 return log_error_errno(r, "Failed to request name: %m");
1822350d 610
8d451309 611 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
612 if (r < 0)
613 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1822350d 614
1cc6c93a 615 *_bus = TAKE_PTR(bus);
1822350d 616
8d451309 617 return 0;
1822350d
KS
618}
619
ec8bbd8a
LP
620static bool context_check_idle(void *userdata) {
621 Context *c = ASSERT_PTR(userdata);
622
623 return hashmap_isempty(c->polkit_registry);
624}
625
c6f09e6a 626static int run(int argc, char *argv[]) {
56b7f112 627 _cleanup_(context_clear) Context context = {};
4afd3348
LP
628 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
629 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1822350d 630 int r;
1822350d 631
d2acb93d 632 log_setup();
8d451309 633
fc021a5b
ZJS
634 r = service_parse_argv("systemd-localed.service",
635 "Manage system locale settings and key mappings.",
5ceceff1
ZJS
636 BUS_IMPLEMENTATIONS(&manager_object,
637 &log_control_object),
fc021a5b
ZJS
638 argc, argv);
639 if (r <= 0)
640 return r;
641
1822350d 642 umask(0022);
a9ba0e32 643
a452c807 644 r = mac_init();
a9ba0e32
CG
645 if (r < 0)
646 return r;
1822350d 647
afc6adb5 648 r = sd_event_default(&event);
c6f09e6a
YW
649 if (r < 0)
650 return log_error_errno(r, "Failed to allocate event loop: %m");
1822350d 651
50008ae4
YW
652 (void) sd_event_set_watchdog(event, true);
653
33354ffb 654 r = sd_event_set_signal_exit(event, true);
c6f09e6a 655 if (r < 0)
33354ffb 656 return log_error_errno(r, "Failed to install SIGINT/SIGTERM handlers: %m");
cde93897 657
8d451309 658 r = connect_bus(&context, event, &bus);
1822350d 659 if (r < 0)
c6f09e6a 660 return r;
1822350d 661
792a8bfb 662 r = sd_notify(false, NOTIFY_READY_MESSAGE);
33354ffb
MY
663 if (r < 0)
664 log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
665
ec8bbd8a
LP
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);
4897d1dc 673 if (r < 0)
c6f09e6a 674 return log_error_errno(r, "Failed to run event loop: %m");
1822350d 675
c6f09e6a 676 return 0;
1822350d 677}
c6f09e6a
YW
678
679DEFINE_MAIN_FUNCTION(run);