]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/locale/localed.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / locale / localed.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
1822350d
KS
2/***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
8d451309 6 Copyright 2013 Kay Sievers
1822350d
KS
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
1822350d
KS
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
5430f7f2 16 Lesser General Public License for more details.
1822350d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
1822350d
KS
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
1822350d
KS
22#include <errno.h>
23#include <string.h>
24#include <unistd.h>
25
349cc4a5 26#if HAVE_XKBCOMMON
3ffd4af2 27#include <xkbcommon/xkbcommon.h>
5de34470 28#include <dlfcn.h>
3ffd4af2
LP
29#endif
30
8d451309
KS
31#include "sd-bus.h"
32
b5efdb8a 33#include "alloc-util.h"
8d451309
KS
34#include "bus-error.h"
35#include "bus-message.h"
bb15fafe
LP
36#include "bus-util.h"
37#include "def.h"
4897d1dc 38#include "keymap-util.h"
75683450 39#include "locale-util.h"
4897d1dc 40#include "macro.h"
bb15fafe 41#include "path-util.h"
d7b8eec7 42#include "selinux-util.h"
4897d1dc 43#include "string-util.h"
bb15fafe 44#include "strv.h"
ee104e11 45#include "user-util.h"
d4f5a1f4 46
4897d1dc 47static Hashmap *polkit_registry = NULL;
1822350d 48
8d451309
KS
49static 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;
4afd3348 52 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
8d451309
KS
53 sd_bus_error error = SD_BUS_ERROR_NULL;
54 unsigned c_set, c_unset, p;
55 int r;
1822350d
KS
56
57 assert(bus);
58
78c97cbe 59 l_unset = new0(char*, _VARIABLE_LC_MAX);
8d451309
KS
60 if (!l_unset)
61 return -ENOMEM;
1822350d 62
78c97cbe 63 l_set = new0(char*, _VARIABLE_LC_MAX);
8d451309
KS
64 if (!l_set)
65 return -ENOMEM;
66
78c97cbe
ZJS
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);
1822350d 72
8d451309 73 if (isempty(c->locale[p]))
78c97cbe 74 l_unset[c_set++] = (char*) name;
1822350d
KS
75 else {
76 char *s;
77
78c97cbe 78 if (asprintf(&s, "%s=%s", name, c->locale[p]) < 0)
8d451309 79 return -ENOMEM;
1822350d
KS
80
81 l_set[c_unset++] = s;
82 }
83 }
84
78c97cbe 85 assert(c_set + c_unset == _VARIABLE_LC_MAX);
151b9b96 86 r = sd_bus_message_new_method_call(bus, &m,
8d451309
KS
87 "org.freedesktop.systemd1",
88 "/org/freedesktop/systemd1",
89 "org.freedesktop.systemd1.Manager",
151b9b96 90 "UnsetAndSetEnvironment");
8d451309
KS
91 if (r < 0)
92 return r;
1822350d 93
8d451309
KS
94 r = sd_bus_message_append_strv(m, l_unset);
95 if (r < 0)
96 return r;
1822350d 97
8d451309
KS
98 r = sd_bus_message_append_strv(m, l_set);
99 if (r < 0)
100 return r;
1822350d 101
c49b30a2 102 r = sd_bus_call(bus, m, 0, &error, NULL);
8d451309 103 if (r < 0)
da927ba9 104 log_error_errno(r, "Failed to update the manager environment: %m");
1822350d 105
8d451309 106 return 0;
1822350d
KS
107}
108
8d451309 109static int vconsole_reload(sd_bus *bus) {
4afd3348 110 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1822350d 111 int r;
1822350d
KS
112
113 assert(bus);
114
8d451309 115 r = sd_bus_call_method(bus,
1822350d
KS
116 "org.freedesktop.systemd1",
117 "/org/freedesktop/systemd1",
118 "org.freedesktop.systemd1.Manager",
8d451309
KS
119 "RestartUnit",
120 &error,
121 NULL,
122 "ss", "systemd-vconsole-setup.service", "replace");
1822350d 123
8d451309
KS
124 if (r < 0)
125 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1822350d
KS
126 return r;
127}
128
4897d1dc 129static int vconsole_convert_to_x11_and_emit(Context *c, sd_bus *bus) {
78bd12a0 130 int r;
0732ef7a 131
4897d1dc 132 assert(bus);
4e829d21 133
4897d1dc
ZJS
134 r = vconsole_convert_to_x11(c);
135 if (r <= 0)
136 return r;
4e829d21 137
4897d1dc
ZJS
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");
4e829d21 142
4897d1dc
ZJS
143 sd_bus_emit_properties_changed(bus,
144 "/org/freedesktop/locale1",
145 "org.freedesktop.locale1",
146 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
4e829d21 147
4897d1dc 148 return 1;
4e829d21
ZJS
149}
150
4897d1dc 151static int x11_convert_to_vconsole_and_emit(Context *c, sd_bus *bus) {
0732ef7a 152 int r;
1822350d 153
8d451309 154 assert(bus);
1822350d 155
4897d1dc
ZJS
156 r = x11_convert_to_vconsole(c);
157 if (r <= 0)
158 return r;
502f9614 159
4897d1dc
ZJS
160 /* modified */
161 r = vconsole_write_data(c);
162 if (r < 0)
163 log_error_errno(r, "Failed to save virtual console keymap: %m");
1822350d 164
4897d1dc
ZJS
165 sd_bus_emit_properties_changed(bus,
166 "/org/freedesktop/locale1",
167 "org.freedesktop.locale1",
168 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
1822350d 169
4897d1dc 170 return vconsole_reload(bus);
1822350d
KS
171}
172
ebcf1f97
LP
173static 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
8d451309 182 Context *c = userdata;
b47d419c 183 _cleanup_strv_free_ char **l = NULL;
8d451309 184 int p, q;
1822350d 185
78c97cbe 186 l = new0(char*, _VARIABLE_LC_MAX+1);
1822350d
KS
187 if (!l)
188 return -ENOMEM;
189
78c97cbe 190 for (p = 0, q = 0; p < _VARIABLE_LC_MAX; p++) {
1822350d 191 char *t;
78c97cbe
ZJS
192 const char *name;
193
194 name = locale_variable_to_string(p);
195 assert(name);
1822350d 196
8d451309 197 if (isempty(c->locale[p]))
1822350d
KS
198 continue;
199
78c97cbe 200 if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0)
1822350d 201 return -ENOMEM;
1822350d 202
8d451309 203 l[q++] = t;
1822350d
KS
204 }
205
8d451309 206 return sd_bus_message_append_strv(reply, l);
1822350d
KS
207}
208
19070062 209static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
8d451309 210 Context *c = userdata;
8d451309
KS
211 _cleanup_strv_free_ char **l = NULL;
212 char **i;
4e829d21 213 const char *lang = NULL;
8d451309
KS
214 int interactive;
215 bool modified = false;
78c97cbe 216 bool have[_VARIABLE_LC_MAX] = {};
8d451309 217 int p;
1822350d
KS
218 int r;
219
19070062
LP
220 assert(m);
221 assert(c);
222
8d451309
KS
223 r = bus_message_read_strv_extend(m, &l);
224 if (r < 0)
ebcf1f97 225 return r;
1822350d 226
8d451309
KS
227 r = sd_bus_message_read_basic(m, 'b', &interactive);
228 if (r < 0)
ebcf1f97 229 return r;
1822350d 230
502f9614 231 /* Check whether a variable changed and if it is valid */
8d451309
KS
232 STRV_FOREACH(i, l) {
233 bool valid = false;
1822350d 234
78c97cbe 235 for (p = 0; p < _VARIABLE_LC_MAX; p++) {
8d451309 236 size_t k;
78c97cbe 237 const char *name;
1822350d 238
78c97cbe
ZJS
239 name = locale_variable_to_string(p);
240 assert(name);
241
242 k = strlen(name);
243 if (startswith(*i, name) &&
8d451309 244 (*i)[k] == '=' &&
75683450 245 locale_is_valid((*i) + k + 1)) {
8d451309 246 valid = true;
4e829d21
ZJS
247 have[p] = true;
248
78c97cbe 249 if (p == VARIABLE_LANG)
4e829d21 250 lang = (*i) + k + 1;
1822350d 251
8d451309
KS
252 if (!streq_ptr(*i + k + 1, c->locale[p]))
253 modified = true;
1822350d 254
8d451309
KS
255 break;
256 }
1822350d
KS
257 }
258
8d451309 259 if (!valid)
ebcf1f97 260 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
8d451309 261 }
1822350d 262
4e829d21
ZJS
263 /* If LANG was specified, but not LANGUAGE, check if we should
264 * set it based on the language fallback table. */
78c97cbe 265 if (have[VARIABLE_LANG] && !have[VARIABLE_LANGUAGE]) {
4e829d21
ZJS
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);
78c97cbe 273 if (!streq_ptr(language, c->locale[VARIABLE_LANGUAGE])) {
4e829d21
ZJS
274 r = strv_extendf(&l, "LANGUAGE=%s", language);
275 if (r < 0)
276 return r;
277
78c97cbe 278 have[VARIABLE_LANGUAGE] = true;
4e829d21
ZJS
279 modified = true;
280 }
281 }
282 }
283
8d451309 284 /* Check whether a variable is unset */
28efac0d 285 if (!modified)
78c97cbe 286 for (p = 0; p < _VARIABLE_LC_MAX; p++)
4e829d21 287 if (!isempty(c->locale[p]) && !have[p]) {
8d451309
KS
288 modified = true;
289 break;
290 }
8d451309
KS
291
292 if (modified) {
502f9614
ZJS
293 _cleanup_strv_free_ char **settings = NULL;
294
c529695e
LP
295 r = bus_verify_polkit_async(
296 m,
297 CAP_SYS_ADMIN,
298 "org.freedesktop.locale1.set-locale",
403ed0e5 299 NULL,
c529695e
LP
300 interactive,
301 UID_INVALID,
4897d1dc 302 &polkit_registry,
c529695e 303 error);
8d451309 304 if (r < 0)
ebcf1f97 305 return r;
8d451309
KS
306 if (r == 0)
307 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1822350d 308
28efac0d 309 STRV_FOREACH(i, l)
78c97cbe 310 for (p = 0; p < _VARIABLE_LC_MAX; p++) {
1822350d 311 size_t k;
78c97cbe
ZJS
312 const char *name;
313
314 name = locale_variable_to_string(p);
315 assert(name);
1822350d 316
78c97cbe
ZJS
317 k = strlen(name);
318 if (startswith(*i, name) && (*i)[k] == '=') {
af76d302
ZJS
319 r = free_and_strdup(&c->locale[p], *i + k + 1);
320 if (r < 0)
321 return r;
1822350d
KS
322 break;
323 }
324 }
1822350d 325
78c97cbe 326 for (p = 0; p < _VARIABLE_LC_MAX; p++) {
4e829d21 327 if (have[p])
8d451309 328 continue;
1822350d 329
87699fe3 330 c->locale[p] = mfree(c->locale[p]);
8d451309 331 }
1822350d 332
8d451309 333 locale_simplify(c);
1822350d 334
502f9614 335 r = locale_write_data(c, &settings);
8d451309 336 if (r < 0) {
da927ba9 337 log_error_errno(r, "Failed to set locale: %m");
048c386e 338 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %m");
8d451309 339 }
1822350d 340
19070062 341 locale_update_system_manager(c, sd_bus_message_get_bus(m));
1822350d 342
502f9614
ZJS
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.");
1822350d 350
19070062
LP
351 (void) sd_bus_emit_properties_changed(
352 sd_bus_message_get_bus(m),
8d451309
KS
353 "/org/freedesktop/locale1",
354 "org.freedesktop.locale1",
355 "Locale", NULL);
502f9614
ZJS
356 } else
357 log_debug("Locale settings were not modified.");
358
1822350d 359
df2d202e 360 return sd_bus_reply_method_return(m, NULL);
8d451309 361}
1822350d 362
19070062 363static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
8d451309 364 Context *c = userdata;
8d451309 365 const char *keymap, *keymap_toggle;
102d8f81 366 int convert, interactive;
8d451309 367 int r;
f2cc3753 368
19070062
LP
369 assert(m);
370 assert(c);
371
8d451309
KS
372 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
373 if (r < 0)
ebcf1f97 374 return r;
1822350d 375
3c6f7c34
LP
376 keymap = empty_to_null(keymap);
377 keymap_toggle = empty_to_null(keymap_toggle);
1822350d 378
8d451309
KS
379 if (!streq_ptr(keymap, c->vc_keymap) ||
380 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
1822350d 381
ae6c3cc0
LP
382 if ((keymap && (!filename_is_valid(keymap) || !string_is_safe(keymap))) ||
383 (keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle))))
d14ab08b 384 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
1822350d 385
c529695e
LP
386 r = bus_verify_polkit_async(
387 m,
388 CAP_SYS_ADMIN,
389 "org.freedesktop.locale1.set-keyboard",
403ed0e5 390 NULL,
c529695e
LP
391 interactive,
392 UID_INVALID,
4897d1dc 393 &polkit_registry,
c529695e 394 error);
8d451309 395 if (r < 0)
ebcf1f97 396 return r;
8d451309
KS
397 if (r == 0)
398 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1822350d 399
af76d302
ZJS
400 if (free_and_strdup(&c->vc_keymap, keymap) < 0 ||
401 free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0)
8d451309 402 return -ENOMEM;
0b507b17 403
8d451309
KS
404 r = vconsole_write_data(c);
405 if (r < 0) {
da927ba9 406 log_error_errno(r, "Failed to set virtual console keymap: %m");
048c386e 407 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %m");
8d451309 408 }
1822350d 409
502f9614
ZJS
410 log_info("Changed virtual console keymap to '%s' toggle '%s'",
411 strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
1822350d 412
19070062 413 r = vconsole_reload(sd_bus_message_get_bus(m));
8d451309 414 if (r < 0)
da927ba9 415 log_error_errno(r, "Failed to request keymap reload: %m");
1822350d 416
19070062
LP
417 (void) sd_bus_emit_properties_changed(
418 sd_bus_message_get_bus(m),
8d451309
KS
419 "/org/freedesktop/locale1",
420 "org.freedesktop.locale1",
421 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
1822350d 422
8d451309 423 if (convert) {
4897d1dc 424 r = vconsole_convert_to_x11_and_emit(c, sd_bus_message_get_bus(m));
1822350d 425 if (r < 0)
da927ba9 426 log_error_errno(r, "Failed to convert keymap data: %m");
1822350d 427 }
8d451309 428 }
1822350d 429
df2d202e 430 return sd_bus_reply_method_return(m, NULL);
8d451309 431}
1822350d 432
349cc4a5 433#if HAVE_XKBCOMMON
5de34470 434
3cb063fd 435_printf_(3, 0)
d4f5a1f4 436static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
8433e339
JS
437 const char *fmt;
438
63c372cb 439 fmt = strjoina("libxkbcommon: ", format);
032b7541
ZJS
440#pragma GCC diagnostic push
441#pragma GCC diagnostic ignored "-Wformat-nonliteral"
8433e339 442 log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
032b7541 443#pragma GCC diagnostic pop
d4f5a1f4
DH
444}
445
5de34470
LP
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
d4f5a1f4 452static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
5de34470
LP
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
d4f5a1f4
DH
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;
5de34470 472 void *dl;
d4f5a1f4
DH
473 int r;
474
5de34470
LP
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;
d4f5a1f4 492
5de34470
LP
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);
d4f5a1f4
DH
502 if (!ctx) {
503 r = -ENOMEM;
5de34470 504 goto finish;
d4f5a1f4
DH
505 }
506
5de34470 507 symbol_xkb_context_set_log_fn(ctx, log_xkb);
d4f5a1f4 508
5de34470 509 km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
d4f5a1f4
DH
510 if (!km) {
511 r = -EINVAL;
5de34470 512 goto finish;
d4f5a1f4
DH
513 }
514
515 r = 0;
516
5de34470
LP
517finish:
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);
d4f5a1f4
DH
525 return r;
526}
5de34470 527
d4f5a1f4 528#else
5de34470 529
d4f5a1f4
DH
530static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
531 return 0;
532}
5de34470 533
d4f5a1f4
DH
534#endif
535
19070062 536static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
8d451309 537 Context *c = userdata;
8d451309 538 const char *layout, *model, *variant, *options;
102d8f81 539 int convert, interactive;
8d451309 540 int r;
1822350d 541
19070062
LP
542 assert(m);
543 assert(c);
544
8d451309
KS
545 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
546 if (r < 0)
ebcf1f97 547 return r;
1822350d 548
3c6f7c34
LP
549 layout = empty_to_null(layout);
550 model = empty_to_null(model);
551 variant = empty_to_null(variant);
552 options = empty_to_null(options);
1822350d 553
8d451309
KS
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)) {
1822350d 558
8d451309
KS
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)))
d14ab08b 563 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
1822350d 564
c529695e
LP
565 r = bus_verify_polkit_async(
566 m,
567 CAP_SYS_ADMIN,
568 "org.freedesktop.locale1.set-keyboard",
403ed0e5 569 NULL,
c529695e
LP
570 interactive,
571 UID_INVALID,
4897d1dc 572 &polkit_registry,
c529695e 573 error);
8d451309 574 if (r < 0)
ebcf1f97 575 return r;
8d451309
KS
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
8623d3a3 579 r = verify_xkb_rmlvo(model, layout, variant, options);
8433e339
JS
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));
5de34470
LP
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.");
8433e339 588 }
8623d3a3 589
af76d302
ZJS
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)
8d451309 594 return -ENOMEM;
1822350d 595
e78af5ff 596 r = x11_write_data(c);
8d451309 597 if (r < 0) {
da927ba9 598 log_error_errno(r, "Failed to set X11 keyboard layout: %m");
048c386e 599 return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %m");
1822350d 600 }
1822350d 601
502f9614
ZJS
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));
1822350d 607
19070062
LP
608 (void) sd_bus_emit_properties_changed(
609 sd_bus_message_get_bus(m),
8d451309
KS
610 "/org/freedesktop/locale1",
611 "org.freedesktop.locale1",
c168eb67 612 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
1822350d 613
8d451309 614 if (convert) {
4897d1dc 615 r = x11_convert_to_vconsole_and_emit(c, sd_bus_message_get_bus(m));
8d451309 616 if (r < 0)
da927ba9 617 log_error_errno(r, "Failed to convert keymap data: %m");
8d451309 618 }
1822350d
KS
619 }
620
df2d202e 621 return sd_bus_reply_method_return(m, NULL);
1822350d
KS
622}
623
8d451309
KS
624static const sd_bus_vtable locale_vtable[] = {
625 SD_BUS_VTABLE_START(0),
6d1bd3b2
LP
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),
adacb957
LP
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),
8d451309
KS
636 SD_BUS_VTABLE_END
637};
638
639static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
4afd3348 640 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1822350d
KS
641 int r;
642
8d451309
KS
643 assert(c);
644 assert(event);
1822350d
KS
645 assert(_bus);
646
76b54375 647 r = sd_bus_default_system(&bus);
f647962d
MS
648 if (r < 0)
649 return log_error_errno(r, "Failed to get system bus connection: %m");
1822350d 650
19befb2d 651 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
f647962d
MS
652 if (r < 0)
653 return log_error_errno(r, "Failed to register object: %m");
1822350d 654
5bb658a1 655 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
f647962d
MS
656 if (r < 0)
657 return log_error_errno(r, "Failed to register name: %m");
1822350d 658
8d451309 659 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
660 if (r < 0)
661 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1822350d 662
8d451309
KS
663 *_bus = bus;
664 bus = NULL;
1822350d 665
8d451309 666 return 0;
1822350d
KS
667}
668
669int main(int argc, char *argv[]) {
28efac0d 670 _cleanup_(context_free) Context context = {};
4afd3348
LP
671 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
672 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1822350d 673 int r;
1822350d
KS
674
675 log_set_target(LOG_TARGET_AUTO);
676 log_parse_environment();
677 log_open();
8d451309 678
1822350d 679 umask(0022);
c3dacc8b 680 mac_selinux_init();
1822350d 681
1822350d
KS
682 if (argc != 1) {
683 log_error("This program takes no arguments.");
684 r = -EINVAL;
685 goto finish;
686 }
687
afc6adb5 688 r = sd_event_default(&event);
1822350d 689 if (r < 0) {
da927ba9 690 log_error_errno(r, "Failed to allocate event loop: %m");
1822350d
KS
691 goto finish;
692 }
693
cde93897
LP
694 sd_event_set_watchdog(event, true);
695
8d451309 696 r = connect_bus(&context, event, &bus);
1822350d
KS
697 if (r < 0)
698 goto finish;
699
8d451309
KS
700 r = context_read_data(&context);
701 if (r < 0) {
da927ba9 702 log_error_errno(r, "Failed to read locale data: %m");
8d451309
KS
703 goto finish;
704 }
1822350d 705
37224a5f 706 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
4897d1dc 707 if (r < 0)
da927ba9 708 log_error_errno(r, "Failed to run event loop: %m");
1822350d 709
1822350d 710finish:
4897d1dc
ZJS
711 bus_verify_polkit_async_registry_free(polkit_registry);
712
1822350d
KS
713 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
714}