]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/locale/localed.c
journal-verify: get rid of strerror
[thirdparty/systemd.git] / src / locale / localed.c
CommitLineData
1822350d
KS
1/***
2 This file is part of systemd.
3
4 Copyright 2011 Lennart Poettering
8d451309 5 Copyright 2013 Kay Sievers
1822350d
KS
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
1822350d
KS
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
1822350d 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
1822350d
KS
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
1822350d
KS
21#include <errno.h>
22#include <string.h>
23#include <unistd.h>
24
3ffd4af2
LP
25#ifdef HAVE_XKBCOMMON
26#include <xkbcommon/xkbcommon.h>
5de34470 27#include <dlfcn.h>
3ffd4af2
LP
28#endif
29
8d451309
KS
30#include "sd-bus.h"
31
b5efdb8a 32#include "alloc-util.h"
8d451309
KS
33#include "bus-error.h"
34#include "bus-message.h"
bb15fafe
LP
35#include "bus-util.h"
36#include "def.h"
4897d1dc 37#include "keymap-util.h"
75683450 38#include "locale-util.h"
4897d1dc 39#include "macro.h"
bb15fafe 40#include "path-util.h"
d7b8eec7 41#include "selinux-util.h"
4897d1dc 42#include "string-util.h"
bb15fafe 43#include "strv.h"
ee104e11 44#include "user-util.h"
d4f5a1f4 45
4897d1dc 46static Hashmap *polkit_registry = NULL;
1822350d 47
8d451309
KS
48static int locale_update_system_manager(Context *c, sd_bus *bus) {
49 _cleanup_free_ char **l_unset = NULL;
50 _cleanup_strv_free_ char **l_set = NULL;
4afd3348 51 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
8d451309
KS
52 sd_bus_error error = SD_BUS_ERROR_NULL;
53 unsigned c_set, c_unset, p;
54 int r;
1822350d
KS
55
56 assert(bus);
57
78c97cbe 58 l_unset = new0(char*, _VARIABLE_LC_MAX);
8d451309
KS
59 if (!l_unset)
60 return -ENOMEM;
1822350d 61
78c97cbe 62 l_set = new0(char*, _VARIABLE_LC_MAX);
8d451309
KS
63 if (!l_set)
64 return -ENOMEM;
65
78c97cbe
ZJS
66 for (p = 0, c_set = 0, c_unset = 0; p < _VARIABLE_LC_MAX; p++) {
67 const char *name;
68
69 name = locale_variable_to_string(p);
70 assert(name);
1822350d 71
8d451309 72 if (isempty(c->locale[p]))
78c97cbe 73 l_unset[c_set++] = (char*) name;
1822350d
KS
74 else {
75 char *s;
76
78c97cbe 77 if (asprintf(&s, "%s=%s", name, c->locale[p]) < 0)
8d451309 78 return -ENOMEM;
1822350d
KS
79
80 l_set[c_unset++] = s;
81 }
82 }
83
78c97cbe 84 assert(c_set + c_unset == _VARIABLE_LC_MAX);
151b9b96 85 r = sd_bus_message_new_method_call(bus, &m,
8d451309
KS
86 "org.freedesktop.systemd1",
87 "/org/freedesktop/systemd1",
88 "org.freedesktop.systemd1.Manager",
151b9b96 89 "UnsetAndSetEnvironment");
8d451309
KS
90 if (r < 0)
91 return r;
1822350d 92
8d451309
KS
93 r = sd_bus_message_append_strv(m, l_unset);
94 if (r < 0)
95 return r;
1822350d 96
8d451309
KS
97 r = sd_bus_message_append_strv(m, l_set);
98 if (r < 0)
99 return r;
1822350d 100
c49b30a2 101 r = sd_bus_call(bus, m, 0, &error, NULL);
8d451309 102 if (r < 0)
da927ba9 103 log_error_errno(r, "Failed to update the manager environment: %m");
1822350d 104
8d451309 105 return 0;
1822350d
KS
106}
107
8d451309 108static int vconsole_reload(sd_bus *bus) {
4afd3348 109 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1822350d 110 int r;
1822350d
KS
111
112 assert(bus);
113
8d451309 114 r = sd_bus_call_method(bus,
1822350d
KS
115 "org.freedesktop.systemd1",
116 "/org/freedesktop/systemd1",
117 "org.freedesktop.systemd1.Manager",
8d451309
KS
118 "RestartUnit",
119 &error,
120 NULL,
121 "ss", "systemd-vconsole-setup.service", "replace");
1822350d 122
8d451309
KS
123 if (r < 0)
124 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1822350d
KS
125 return r;
126}
127
4897d1dc 128static int vconsole_convert_to_x11_and_emit(Context *c, sd_bus *bus) {
78bd12a0 129 int r;
0732ef7a 130
4897d1dc 131 assert(bus);
4e829d21 132
4897d1dc
ZJS
133 r = vconsole_convert_to_x11(c);
134 if (r <= 0)
135 return r;
4e829d21 136
4897d1dc
ZJS
137 /* modified */
138 r = x11_write_data(c);
139 if (r < 0)
140 return log_error_errno(r, "Failed to write X11 keyboard layout: %m");
4e829d21 141
4897d1dc
ZJS
142 sd_bus_emit_properties_changed(bus,
143 "/org/freedesktop/locale1",
144 "org.freedesktop.locale1",
145 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
4e829d21 146
4897d1dc 147 return 1;
4e829d21
ZJS
148}
149
4897d1dc 150static int x11_convert_to_vconsole_and_emit(Context *c, sd_bus *bus) {
0732ef7a 151 int r;
1822350d 152
8d451309 153 assert(bus);
1822350d 154
4897d1dc
ZJS
155 r = x11_convert_to_vconsole(c);
156 if (r <= 0)
157 return r;
502f9614 158
4897d1dc
ZJS
159 /* modified */
160 r = vconsole_write_data(c);
161 if (r < 0)
162 log_error_errno(r, "Failed to save virtual console keymap: %m");
1822350d 163
4897d1dc
ZJS
164 sd_bus_emit_properties_changed(bus,
165 "/org/freedesktop/locale1",
166 "org.freedesktop.locale1",
167 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
1822350d 168
4897d1dc 169 return vconsole_reload(bus);
1822350d
KS
170}
171
ebcf1f97
LP
172static int property_get_locale(
173 sd_bus *bus,
174 const char *path,
175 const char *interface,
176 const char *property,
177 sd_bus_message *reply,
178 void *userdata,
179 sd_bus_error *error) {
180
8d451309 181 Context *c = userdata;
b47d419c 182 _cleanup_strv_free_ char **l = NULL;
8d451309 183 int p, q;
1822350d 184
78c97cbe 185 l = new0(char*, _VARIABLE_LC_MAX+1);
1822350d
KS
186 if (!l)
187 return -ENOMEM;
188
78c97cbe 189 for (p = 0, q = 0; p < _VARIABLE_LC_MAX; p++) {
1822350d 190 char *t;
78c97cbe
ZJS
191 const char *name;
192
193 name = locale_variable_to_string(p);
194 assert(name);
1822350d 195
8d451309 196 if (isempty(c->locale[p]))
1822350d
KS
197 continue;
198
78c97cbe 199 if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0)
1822350d 200 return -ENOMEM;
1822350d 201
8d451309 202 l[q++] = t;
1822350d
KS
203 }
204
8d451309 205 return sd_bus_message_append_strv(reply, l);
1822350d
KS
206}
207
19070062 208static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
8d451309 209 Context *c = userdata;
8d451309
KS
210 _cleanup_strv_free_ char **l = NULL;
211 char **i;
4e829d21 212 const char *lang = NULL;
8d451309
KS
213 int interactive;
214 bool modified = false;
78c97cbe 215 bool have[_VARIABLE_LC_MAX] = {};
8d451309 216 int p;
1822350d
KS
217 int r;
218
19070062
LP
219 assert(m);
220 assert(c);
221
8d451309
KS
222 r = bus_message_read_strv_extend(m, &l);
223 if (r < 0)
ebcf1f97 224 return r;
1822350d 225
8d451309
KS
226 r = sd_bus_message_read_basic(m, 'b', &interactive);
227 if (r < 0)
ebcf1f97 228 return r;
1822350d 229
502f9614 230 /* Check whether a variable changed and if it is valid */
8d451309
KS
231 STRV_FOREACH(i, l) {
232 bool valid = false;
1822350d 233
78c97cbe 234 for (p = 0; p < _VARIABLE_LC_MAX; p++) {
8d451309 235 size_t k;
78c97cbe 236 const char *name;
1822350d 237
78c97cbe
ZJS
238 name = locale_variable_to_string(p);
239 assert(name);
240
241 k = strlen(name);
242 if (startswith(*i, name) &&
8d451309 243 (*i)[k] == '=' &&
75683450 244 locale_is_valid((*i) + k + 1)) {
8d451309 245 valid = true;
4e829d21
ZJS
246 have[p] = true;
247
78c97cbe 248 if (p == VARIABLE_LANG)
4e829d21 249 lang = (*i) + k + 1;
1822350d 250
8d451309
KS
251 if (!streq_ptr(*i + k + 1, c->locale[p]))
252 modified = true;
1822350d 253
8d451309
KS
254 break;
255 }
1822350d
KS
256 }
257
8d451309 258 if (!valid)
ebcf1f97 259 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
8d451309 260 }
1822350d 261
4e829d21
ZJS
262 /* If LANG was specified, but not LANGUAGE, check if we should
263 * set it based on the language fallback table. */
78c97cbe 264 if (have[VARIABLE_LANG] && !have[VARIABLE_LANGUAGE]) {
4e829d21
ZJS
265 _cleanup_free_ char *language = NULL;
266
267 assert(lang);
268
269 (void) find_language_fallback(lang, &language);
270 if (language) {
271 log_debug("Converted LANG=%s to LANGUAGE=%s", lang, language);
78c97cbe 272 if (!streq_ptr(language, c->locale[VARIABLE_LANGUAGE])) {
4e829d21
ZJS
273 r = strv_extendf(&l, "LANGUAGE=%s", language);
274 if (r < 0)
275 return r;
276
78c97cbe 277 have[VARIABLE_LANGUAGE] = true;
4e829d21
ZJS
278 modified = true;
279 }
280 }
281 }
282
8d451309 283 /* Check whether a variable is unset */
28efac0d 284 if (!modified)
78c97cbe 285 for (p = 0; p < _VARIABLE_LC_MAX; p++)
4e829d21 286 if (!isempty(c->locale[p]) && !have[p]) {
8d451309
KS
287 modified = true;
288 break;
289 }
8d451309
KS
290
291 if (modified) {
502f9614
ZJS
292 _cleanup_strv_free_ char **settings = NULL;
293
c529695e
LP
294 r = bus_verify_polkit_async(
295 m,
296 CAP_SYS_ADMIN,
297 "org.freedesktop.locale1.set-locale",
403ed0e5 298 NULL,
c529695e
LP
299 interactive,
300 UID_INVALID,
4897d1dc 301 &polkit_registry,
c529695e 302 error);
8d451309 303 if (r < 0)
ebcf1f97 304 return r;
8d451309
KS
305 if (r == 0)
306 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1822350d 307
28efac0d 308 STRV_FOREACH(i, l)
78c97cbe 309 for (p = 0; p < _VARIABLE_LC_MAX; p++) {
1822350d 310 size_t k;
78c97cbe
ZJS
311 const char *name;
312
313 name = locale_variable_to_string(p);
314 assert(name);
1822350d 315
78c97cbe
ZJS
316 k = strlen(name);
317 if (startswith(*i, name) && (*i)[k] == '=') {
af76d302
ZJS
318 r = free_and_strdup(&c->locale[p], *i + k + 1);
319 if (r < 0)
320 return r;
1822350d
KS
321 break;
322 }
323 }
1822350d 324
78c97cbe 325 for (p = 0; p < _VARIABLE_LC_MAX; p++) {
4e829d21 326 if (have[p])
8d451309 327 continue;
1822350d 328
87699fe3 329 c->locale[p] = mfree(c->locale[p]);
8d451309 330 }
1822350d 331
8d451309 332 locale_simplify(c);
1822350d 333
502f9614 334 r = locale_write_data(c, &settings);
8d451309 335 if (r < 0) {
da927ba9 336 log_error_errno(r, "Failed to set locale: %m");
ebcf1f97 337 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
8d451309 338 }
1822350d 339
19070062 340 locale_update_system_manager(c, sd_bus_message_get_bus(m));
1822350d 341
502f9614
ZJS
342 if (settings) {
343 _cleanup_free_ char *line;
344
345 line = strv_join(settings, ", ");
346 log_info("Changed locale to %s.", strnull(line));
347 } else
348 log_info("Changed locale to unset.");
1822350d 349
19070062
LP
350 (void) sd_bus_emit_properties_changed(
351 sd_bus_message_get_bus(m),
8d451309
KS
352 "/org/freedesktop/locale1",
353 "org.freedesktop.locale1",
354 "Locale", NULL);
502f9614
ZJS
355 } else
356 log_debug("Locale settings were not modified.");
357
1822350d 358
df2d202e 359 return sd_bus_reply_method_return(m, NULL);
8d451309 360}
1822350d 361
19070062 362static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
8d451309 363 Context *c = userdata;
8d451309 364 const char *keymap, *keymap_toggle;
102d8f81 365 int convert, interactive;
8d451309 366 int r;
f2cc3753 367
19070062
LP
368 assert(m);
369 assert(c);
370
8d451309
KS
371 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
372 if (r < 0)
ebcf1f97 373 return r;
1822350d 374
3c6f7c34
LP
375 keymap = empty_to_null(keymap);
376 keymap_toggle = empty_to_null(keymap_toggle);
1822350d 377
8d451309
KS
378 if (!streq_ptr(keymap, c->vc_keymap) ||
379 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
1822350d 380
ae6c3cc0
LP
381 if ((keymap && (!filename_is_valid(keymap) || !string_is_safe(keymap))) ||
382 (keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle))))
d14ab08b 383 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
1822350d 384
c529695e
LP
385 r = bus_verify_polkit_async(
386 m,
387 CAP_SYS_ADMIN,
388 "org.freedesktop.locale1.set-keyboard",
403ed0e5 389 NULL,
c529695e
LP
390 interactive,
391 UID_INVALID,
4897d1dc 392 &polkit_registry,
c529695e 393 error);
8d451309 394 if (r < 0)
ebcf1f97 395 return r;
8d451309
KS
396 if (r == 0)
397 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1822350d 398
af76d302
ZJS
399 if (free_and_strdup(&c->vc_keymap, keymap) < 0 ||
400 free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0)
8d451309 401 return -ENOMEM;
0b507b17 402
8d451309
KS
403 r = vconsole_write_data(c);
404 if (r < 0) {
da927ba9 405 log_error_errno(r, "Failed to set virtual console keymap: %m");
ebcf1f97 406 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
8d451309 407 }
1822350d 408
502f9614
ZJS
409 log_info("Changed virtual console keymap to '%s' toggle '%s'",
410 strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
1822350d 411
19070062 412 r = vconsole_reload(sd_bus_message_get_bus(m));
8d451309 413 if (r < 0)
da927ba9 414 log_error_errno(r, "Failed to request keymap reload: %m");
1822350d 415
19070062
LP
416 (void) sd_bus_emit_properties_changed(
417 sd_bus_message_get_bus(m),
8d451309
KS
418 "/org/freedesktop/locale1",
419 "org.freedesktop.locale1",
420 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
1822350d 421
8d451309 422 if (convert) {
4897d1dc 423 r = vconsole_convert_to_x11_and_emit(c, sd_bus_message_get_bus(m));
1822350d 424 if (r < 0)
da927ba9 425 log_error_errno(r, "Failed to convert keymap data: %m");
1822350d 426 }
8d451309 427 }
1822350d 428
df2d202e 429 return sd_bus_reply_method_return(m, NULL);
8d451309 430}
1822350d 431
d4f5a1f4 432#ifdef HAVE_XKBCOMMON
5de34470 433
3cb063fd 434_printf_(3, 0)
d4f5a1f4 435static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
8433e339
JS
436 const char *fmt;
437
63c372cb 438 fmt = strjoina("libxkbcommon: ", format);
8433e339 439 log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
d4f5a1f4
DH
440}
441
5de34470
LP
442#define LOAD_SYMBOL(symbol, dl, name) \
443 ({ \
444 (symbol) = (typeof(symbol)) dlvsym((dl), (name), "V_0.5.0"); \
445 (symbol) ? 0 : -EOPNOTSUPP; \
446 })
447
d4f5a1f4 448static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
5de34470
LP
449
450 /* We dlopen() the library in order to make the dependency soft. The library (and what it pulls in) is huge
451 * after all, hence let's support XKB maps when the library is around, and refuse otherwise. The function
452 * pointers to the shared library are below: */
453
454 struct xkb_context* (*symbol_xkb_context_new)(enum xkb_context_flags flags) = NULL;
455 void (*symbol_xkb_context_unref)(struct xkb_context *context) = NULL;
456 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;
457 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;
458 void (*symbol_xkb_keymap_unref)(struct xkb_keymap *keymap) = NULL;
459
d4f5a1f4
DH
460 const struct xkb_rule_names rmlvo = {
461 .model = model,
462 .layout = layout,
463 .variant = variant,
464 .options = options,
465 };
466 struct xkb_context *ctx = NULL;
467 struct xkb_keymap *km = NULL;
5de34470 468 void *dl;
d4f5a1f4
DH
469 int r;
470
5de34470
LP
471 /* Compile keymap from RMLVO information to check out its validity */
472
473 dl = dlopen("libxkbcommon.so.0", RTLD_LAZY);
474 if (!dl)
475 return -EOPNOTSUPP;
476
477 r = LOAD_SYMBOL(symbol_xkb_context_new, dl, "xkb_context_new");
478 if (r < 0)
479 goto finish;
480
481 r = LOAD_SYMBOL(symbol_xkb_context_unref, dl, "xkb_context_unref");
482 if (r < 0)
483 goto finish;
484
485 r = LOAD_SYMBOL(symbol_xkb_context_set_log_fn, dl, "xkb_context_set_log_fn");
486 if (r < 0)
487 goto finish;
d4f5a1f4 488
5de34470
LP
489 r = LOAD_SYMBOL(symbol_xkb_keymap_new_from_names, dl, "xkb_keymap_new_from_names");
490 if (r < 0)
491 goto finish;
492
493 r = LOAD_SYMBOL(symbol_xkb_keymap_unref, dl, "xkb_keymap_unref");
494 if (r < 0)
495 goto finish;
496
497 ctx = symbol_xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
d4f5a1f4
DH
498 if (!ctx) {
499 r = -ENOMEM;
5de34470 500 goto finish;
d4f5a1f4
DH
501 }
502
5de34470 503 symbol_xkb_context_set_log_fn(ctx, log_xkb);
d4f5a1f4 504
5de34470 505 km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
d4f5a1f4
DH
506 if (!km) {
507 r = -EINVAL;
5de34470 508 goto finish;
d4f5a1f4
DH
509 }
510
511 r = 0;
512
5de34470
LP
513finish:
514 if (symbol_xkb_keymap_unref && km)
515 symbol_xkb_keymap_unref(km);
516
517 if (symbol_xkb_context_unref && ctx)
518 symbol_xkb_context_unref(ctx);
519
520 (void) dlclose(dl);
d4f5a1f4
DH
521 return r;
522}
5de34470 523
d4f5a1f4 524#else
5de34470 525
d4f5a1f4
DH
526static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
527 return 0;
528}
5de34470 529
d4f5a1f4
DH
530#endif
531
19070062 532static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
8d451309 533 Context *c = userdata;
8d451309 534 const char *layout, *model, *variant, *options;
102d8f81 535 int convert, interactive;
8d451309 536 int r;
1822350d 537
19070062
LP
538 assert(m);
539 assert(c);
540
8d451309
KS
541 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
542 if (r < 0)
ebcf1f97 543 return r;
1822350d 544
3c6f7c34
LP
545 layout = empty_to_null(layout);
546 model = empty_to_null(model);
547 variant = empty_to_null(variant);
548 options = empty_to_null(options);
1822350d 549
8d451309
KS
550 if (!streq_ptr(layout, c->x11_layout) ||
551 !streq_ptr(model, c->x11_model) ||
552 !streq_ptr(variant, c->x11_variant) ||
553 !streq_ptr(options, c->x11_options)) {
1822350d 554
8d451309
KS
555 if ((layout && !string_is_safe(layout)) ||
556 (model && !string_is_safe(model)) ||
557 (variant && !string_is_safe(variant)) ||
558 (options && !string_is_safe(options)))
d14ab08b 559 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
1822350d 560
c529695e
LP
561 r = bus_verify_polkit_async(
562 m,
563 CAP_SYS_ADMIN,
564 "org.freedesktop.locale1.set-keyboard",
403ed0e5 565 NULL,
c529695e
LP
566 interactive,
567 UID_INVALID,
4897d1dc 568 &polkit_registry,
c529695e 569 error);
8d451309 570 if (r < 0)
ebcf1f97 571 return r;
8d451309
KS
572 if (r == 0)
573 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
574
8623d3a3 575 r = verify_xkb_rmlvo(model, layout, variant, options);
8433e339
JS
576 if (r < 0) {
577 log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m",
578 strempty(model), strempty(layout), strempty(variant), strempty(options));
5de34470
LP
579
580 if (r == -EOPNOTSUPP)
581 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system.");
582
583 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid.");
8433e339 584 }
8623d3a3 585
af76d302
ZJS
586 if (free_and_strdup(&c->x11_layout, layout) < 0 ||
587 free_and_strdup(&c->x11_model, model) < 0 ||
588 free_and_strdup(&c->x11_variant, variant) < 0 ||
589 free_and_strdup(&c->x11_options, options) < 0)
8d451309 590 return -ENOMEM;
1822350d 591
e78af5ff 592 r = x11_write_data(c);
8d451309 593 if (r < 0) {
da927ba9 594 log_error_errno(r, "Failed to set X11 keyboard layout: %m");
ebcf1f97 595 return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1822350d 596 }
1822350d 597
502f9614
ZJS
598 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
599 strempty(c->x11_layout),
600 strempty(c->x11_model),
601 strempty(c->x11_variant),
602 strempty(c->x11_options));
1822350d 603
19070062
LP
604 (void) sd_bus_emit_properties_changed(
605 sd_bus_message_get_bus(m),
8d451309
KS
606 "/org/freedesktop/locale1",
607 "org.freedesktop.locale1",
c168eb67 608 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
1822350d 609
8d451309 610 if (convert) {
4897d1dc 611 r = x11_convert_to_vconsole_and_emit(c, sd_bus_message_get_bus(m));
8d451309 612 if (r < 0)
da927ba9 613 log_error_errno(r, "Failed to convert keymap data: %m");
8d451309 614 }
1822350d
KS
615 }
616
df2d202e 617 return sd_bus_reply_method_return(m, NULL);
1822350d
KS
618}
619
8d451309
KS
620static const sd_bus_vtable locale_vtable[] = {
621 SD_BUS_VTABLE_START(0),
6d1bd3b2
LP
622 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
623 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
624 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
625 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
626 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
627 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
628 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
adacb957
LP
629 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
630 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
631 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
8d451309
KS
632 SD_BUS_VTABLE_END
633};
634
635static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
4afd3348 636 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1822350d
KS
637 int r;
638
8d451309
KS
639 assert(c);
640 assert(event);
1822350d
KS
641 assert(_bus);
642
76b54375 643 r = sd_bus_default_system(&bus);
f647962d
MS
644 if (r < 0)
645 return log_error_errno(r, "Failed to get system bus connection: %m");
1822350d 646
19befb2d 647 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
f647962d
MS
648 if (r < 0)
649 return log_error_errno(r, "Failed to register object: %m");
1822350d 650
5bb658a1 651 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
f647962d
MS
652 if (r < 0)
653 return log_error_errno(r, "Failed to register name: %m");
1822350d 654
8d451309 655 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
656 if (r < 0)
657 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1822350d 658
8d451309
KS
659 *_bus = bus;
660 bus = NULL;
1822350d 661
8d451309 662 return 0;
1822350d
KS
663}
664
665int main(int argc, char *argv[]) {
28efac0d 666 _cleanup_(context_free) Context context = {};
4afd3348
LP
667 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
668 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1822350d 669 int r;
1822350d
KS
670
671 log_set_target(LOG_TARGET_AUTO);
672 log_parse_environment();
673 log_open();
8d451309 674
1822350d 675 umask(0022);
c3dacc8b 676 mac_selinux_init();
1822350d 677
1822350d
KS
678 if (argc != 1) {
679 log_error("This program takes no arguments.");
680 r = -EINVAL;
681 goto finish;
682 }
683
afc6adb5 684 r = sd_event_default(&event);
1822350d 685 if (r < 0) {
da927ba9 686 log_error_errno(r, "Failed to allocate event loop: %m");
1822350d
KS
687 goto finish;
688 }
689
cde93897
LP
690 sd_event_set_watchdog(event, true);
691
8d451309 692 r = connect_bus(&context, event, &bus);
1822350d
KS
693 if (r < 0)
694 goto finish;
695
8d451309
KS
696 r = context_read_data(&context);
697 if (r < 0) {
da927ba9 698 log_error_errno(r, "Failed to read locale data: %m");
8d451309
KS
699 goto finish;
700 }
1822350d 701
37224a5f 702 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
4897d1dc 703 if (r < 0)
da927ba9 704 log_error_errno(r, "Failed to run event loop: %m");
1822350d 705
1822350d 706finish:
4897d1dc
ZJS
707 bus_verify_polkit_async_registry_free(polkit_registry);
708
1822350d
KS
709 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
710}