]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/locale/localed.c
bus-util: support details in CheckAuthorization calls
[thirdparty/systemd.git] / src / locale / localed.c
CommitLineData
1822350d
KS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
8d451309 7 Copyright 2013 Kay Sievers
1822350d
KS
8
9 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
1822350d
KS
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 17 Lesser General Public License for more details.
1822350d 18
5430f7f2 19 You should have received a copy of the GNU Lesser General Public License
1822350d
KS
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21***/
22
1822350d
KS
23#include <errno.h>
24#include <string.h>
25#include <unistd.h>
26
8d451309
KS
27#include "sd-bus.h"
28
1822350d 29#include "util.h"
49e942b2 30#include "mkdir.h"
1822350d 31#include "strv.h"
1822350d 32#include "def.h"
4d1a6904 33#include "env-util.h"
a5c32cff
HH
34#include "fileio.h"
35#include "fileio-label.h"
8d451309
KS
36#include "bus-util.h"
37#include "bus-error.h"
38#include "bus-message.h"
39#include "event-util.h"
75683450 40#include "locale-util.h"
d7b8eec7 41#include "selinux-util.h"
1822350d 42
d4f5a1f4
DH
43#ifdef HAVE_XKBCOMMON
44#include <xkbcommon/xkbcommon.h>
45#endif
46
1822350d
KS
47enum {
48 /* We don't list LC_ALL here on purpose. People should be
49 * using LANG instead. */
8d451309
KS
50 LOCALE_LANG,
51 LOCALE_LANGUAGE,
52 LOCALE_LC_CTYPE,
53 LOCALE_LC_NUMERIC,
54 LOCALE_LC_TIME,
55 LOCALE_LC_COLLATE,
56 LOCALE_LC_MONETARY,
57 LOCALE_LC_MESSAGES,
58 LOCALE_LC_PAPER,
59 LOCALE_LC_NAME,
60 LOCALE_LC_ADDRESS,
61 LOCALE_LC_TELEPHONE,
62 LOCALE_LC_MEASUREMENT,
63 LOCALE_LC_IDENTIFICATION,
64 _LOCALE_MAX
1822350d
KS
65};
66
8d451309
KS
67static const char * const names[_LOCALE_MAX] = {
68 [LOCALE_LANG] = "LANG",
69 [LOCALE_LANGUAGE] = "LANGUAGE",
70 [LOCALE_LC_CTYPE] = "LC_CTYPE",
71 [LOCALE_LC_NUMERIC] = "LC_NUMERIC",
72 [LOCALE_LC_TIME] = "LC_TIME",
73 [LOCALE_LC_COLLATE] = "LC_COLLATE",
74 [LOCALE_LC_MONETARY] = "LC_MONETARY",
75 [LOCALE_LC_MESSAGES] = "LC_MESSAGES",
76 [LOCALE_LC_PAPER] = "LC_PAPER",
77 [LOCALE_LC_NAME] = "LC_NAME",
78 [LOCALE_LC_ADDRESS] = "LC_ADDRESS",
79 [LOCALE_LC_TELEPHONE] = "LC_TELEPHONE",
80 [LOCALE_LC_MEASUREMENT] = "LC_MEASUREMENT",
81 [LOCALE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
1822350d
KS
82};
83
8d451309
KS
84typedef struct Context {
85 char *locale[_LOCALE_MAX];
1822350d 86
8d451309
KS
87 char *x11_layout;
88 char *x11_model;
89 char *x11_variant;
90 char *x11_options;
d200735e 91
8d451309
KS
92 char *vc_keymap;
93 char *vc_keymap_toggle;
1822350d 94
8d451309
KS
95 Hashmap *polkit_registry;
96} Context;
1822350d 97
af76d302
ZJS
98static const char* nonempty(const char *s) {
99 return isempty(s) ? NULL : s;
1822350d
KS
100}
101
81fd105a
ZJS
102static bool startswith_comma(const char *s, const char *prefix) {
103 const char *t;
104
105 return s && (t = startswith(s, prefix)) && (*t == ',');
106}
107
8d451309
KS
108static void context_free_x11(Context *c) {
109 free_and_replace(&c->x11_layout, NULL);
110 free_and_replace(&c->x11_model, NULL);
111 free_and_replace(&c->x11_variant, NULL);
112 free_and_replace(&c->x11_options, NULL);
113}
1822350d 114
8d451309
KS
115static void context_free_vconsole(Context *c) {
116 free_and_replace(&c->vc_keymap, NULL);
117 free_and_replace(&c->vc_keymap_toggle, NULL);
1822350d
KS
118}
119
8d451309
KS
120static void context_free_locale(Context *c) {
121 int p;
1822350d 122
8d451309
KS
123 for (p = 0; p < _LOCALE_MAX; p++)
124 free_and_replace(&c->locale[p], NULL);
1822350d
KS
125}
126
36e34057 127static void context_free(Context *c) {
8d451309
KS
128 context_free_locale(c);
129 context_free_x11(c);
130 context_free_vconsole(c);
131
36e34057 132 bus_verify_polkit_async_registry_free(c->polkit_registry);
8d451309
KS
133};
134
135static void locale_simplify(Context *c) {
1822350d
KS
136 int p;
137
8d451309 138 for (p = LOCALE_LANG+1; p < _LOCALE_MAX; p++)
af76d302
ZJS
139 if (isempty(c->locale[p]) || streq_ptr(c->locale[LOCALE_LANG], c->locale[p]))
140 free_and_replace(&c->locale[p], NULL);
1822350d
KS
141}
142
8d451309 143static int locale_read_data(Context *c) {
1822350d
KS
144 int r;
145
8d451309 146 context_free_locale(c);
1822350d
KS
147
148 r = parse_env_file("/etc/locale.conf", NEWLINE,
8d451309
KS
149 "LANG", &c->locale[LOCALE_LANG],
150 "LANGUAGE", &c->locale[LOCALE_LANGUAGE],
151 "LC_CTYPE", &c->locale[LOCALE_LC_CTYPE],
152 "LC_NUMERIC", &c->locale[LOCALE_LC_NUMERIC],
153 "LC_TIME", &c->locale[LOCALE_LC_TIME],
154 "LC_COLLATE", &c->locale[LOCALE_LC_COLLATE],
155 "LC_MONETARY", &c->locale[LOCALE_LC_MONETARY],
156 "LC_MESSAGES", &c->locale[LOCALE_LC_MESSAGES],
157 "LC_PAPER", &c->locale[LOCALE_LC_PAPER],
158 "LC_NAME", &c->locale[LOCALE_LC_NAME],
159 "LC_ADDRESS", &c->locale[LOCALE_LC_ADDRESS],
160 "LC_TELEPHONE", &c->locale[LOCALE_LC_TELEPHONE],
161 "LC_MEASUREMENT", &c->locale[LOCALE_LC_MEASUREMENT],
162 "LC_IDENTIFICATION", &c->locale[LOCALE_LC_IDENTIFICATION],
1822350d
KS
163 NULL);
164
165 if (r == -ENOENT) {
166 int p;
167
168 /* Fill in what we got passed from systemd. */
8d451309 169 for (p = 0; p < _LOCALE_MAX; p++) {
1822350d
KS
170 assert(names[p]);
171
af76d302
ZJS
172 r = free_and_strdup(&c->locale[p],
173 nonempty(getenv(names[p])));
8d451309
KS
174 if (r < 0)
175 return r;
1822350d
KS
176 }
177
178 r = 0;
179 }
180
8d451309 181 locale_simplify(c);
1822350d
KS
182 return r;
183}
184
8d451309 185static int vconsole_read_data(Context *c) {
1822350d
KS
186 int r;
187
8d451309 188 context_free_vconsole(c);
1822350d
KS
189
190 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
8d451309
KS
191 "KEYMAP", &c->vc_keymap,
192 "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
1822350d
KS
193 NULL);
194
195 if (r < 0 && r != -ENOENT)
196 return r;
197
198 return 0;
199}
200
8d451309 201static int x11_read_data(Context *c) {
28efac0d 202 _cleanup_fclose_ FILE *f;
1822350d
KS
203 char line[LINE_MAX];
204 bool in_section = false;
b2fadec6 205 int r;
1822350d 206
8d451309 207 context_free_x11(c);
1822350d
KS
208
209 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
f687b273
LP
210 if (!f)
211 return errno == ENOENT ? 0 : -errno;
1822350d
KS
212
213 while (fgets(line, sizeof(line), f)) {
214 char *l;
215
216 char_array_0(line);
217 l = strstrip(line);
218
219 if (l[0] == 0 || l[0] == '#')
220 continue;
221
222 if (in_section && first_word(l, "Option")) {
28efac0d 223 _cleanup_strv_free_ char **a = NULL;
1822350d 224
8adaf7bd 225 r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
28efac0d 226 if (r < 0)
b2fadec6 227 return r;
1822350d
KS
228
229 if (strv_length(a) == 3) {
1822350d 230 if (streq(a[1], "XkbLayout")) {
8d451309 231 free_and_replace(&c->x11_layout, a[2]);
1822350d
KS
232 a[2] = NULL;
233 } else if (streq(a[1], "XkbModel")) {
8d451309 234 free_and_replace(&c->x11_model, a[2]);
1822350d
KS
235 a[2] = NULL;
236 } else if (streq(a[1], "XkbVariant")) {
8d451309 237 free_and_replace(&c->x11_variant, a[2]);
1822350d
KS
238 a[2] = NULL;
239 } else if (streq(a[1], "XkbOptions")) {
8d451309 240 free_and_replace(&c->x11_options, a[2]);
1822350d
KS
241 a[2] = NULL;
242 }
243 }
244
1822350d 245 } else if (!in_section && first_word(l, "Section")) {
28efac0d 246 _cleanup_strv_free_ char **a = NULL;
1822350d 247
8adaf7bd 248 r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
28efac0d 249 if (r < 0)
1822350d 250 return -ENOMEM;
1822350d
KS
251
252 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
253 in_section = true;
254
1822350d
KS
255 } else if (in_section && first_word(l, "EndSection"))
256 in_section = false;
257 }
258
1822350d
KS
259 return 0;
260}
261
8d451309 262static int context_read_data(Context *c) {
1822350d
KS
263 int r, q, p;
264
8d451309
KS
265 r = locale_read_data(c);
266 q = vconsole_read_data(c);
267 p = x11_read_data(c);
1822350d
KS
268
269 return r < 0 ? r : q < 0 ? q : p;
270}
271
502f9614 272static int locale_write_data(Context *c, char ***settings) {
1822350d 273 int r, p;
28efac0d 274 _cleanup_strv_free_ char **l = NULL;
1822350d 275
502f9614
ZJS
276 /* Set values will be returned as strv in *settings on success. */
277
717603e3 278 r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
1822350d
KS
279 if (r < 0 && r != -ENOENT)
280 return r;
281
8d451309 282 for (p = 0; p < _LOCALE_MAX; p++) {
28efac0d
ZJS
283 _cleanup_free_ char *t = NULL;
284 char **u;
1822350d
KS
285
286 assert(names[p]);
287
8d451309 288 if (isempty(c->locale[p])) {
1822350d
KS
289 l = strv_env_unset(l, names[p]);
290 continue;
291 }
292
28efac0d 293 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
1822350d 294 return -ENOMEM;
1822350d
KS
295
296 u = strv_env_set(l, t);
1822350d
KS
297 if (!u)
298 return -ENOMEM;
299
28efac0d 300 strv_free(l);
1822350d
KS
301 l = u;
302 }
303
304 if (strv_isempty(l)) {
1822350d
KS
305 if (unlink("/etc/locale.conf") < 0)
306 return errno == ENOENT ? 0 : -errno;
307
308 return 0;
309 }
310
502f9614
ZJS
311 r = write_env_file_label("/etc/locale.conf", l);
312 if (r < 0)
313 return r;
314
315 *settings = l;
316 l = NULL;
317 return 0;
1822350d
KS
318}
319
8d451309
KS
320static int locale_update_system_manager(Context *c, sd_bus *bus) {
321 _cleanup_free_ char **l_unset = NULL;
322 _cleanup_strv_free_ char **l_set = NULL;
323 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
324 sd_bus_error error = SD_BUS_ERROR_NULL;
325 unsigned c_set, c_unset, p;
326 int r;
1822350d
KS
327
328 assert(bus);
329
8d451309
KS
330 l_unset = new0(char*, _LOCALE_MAX);
331 if (!l_unset)
332 return -ENOMEM;
1822350d 333
8d451309
KS
334 l_set = new0(char*, _LOCALE_MAX);
335 if (!l_set)
336 return -ENOMEM;
337
338 for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
1822350d
KS
339 assert(names[p]);
340
8d451309 341 if (isempty(c->locale[p]))
1822350d
KS
342 l_unset[c_set++] = (char*) names[p];
343 else {
344 char *s;
345
8d451309
KS
346 if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
347 return -ENOMEM;
1822350d
KS
348
349 l_set[c_unset++] = s;
350 }
351 }
352
8d451309 353 assert(c_set + c_unset == _LOCALE_MAX);
151b9b96 354 r = sd_bus_message_new_method_call(bus, &m,
8d451309
KS
355 "org.freedesktop.systemd1",
356 "/org/freedesktop/systemd1",
357 "org.freedesktop.systemd1.Manager",
151b9b96 358 "UnsetAndSetEnvironment");
8d451309
KS
359 if (r < 0)
360 return r;
1822350d 361
8d451309
KS
362 r = sd_bus_message_append_strv(m, l_unset);
363 if (r < 0)
364 return r;
1822350d 365
8d451309
KS
366 r = sd_bus_message_append_strv(m, l_set);
367 if (r < 0)
368 return r;
1822350d 369
c49b30a2 370 r = sd_bus_call(bus, m, 0, &error, NULL);
8d451309 371 if (r < 0)
da927ba9 372 log_error_errno(r, "Failed to update the manager environment: %m");
1822350d 373
8d451309 374 return 0;
1822350d
KS
375}
376
8d451309 377static int vconsole_write_data(Context *c) {
1822350d 378 int r;
98fce79d 379 _cleanup_strv_free_ char **l = NULL;
1822350d 380
717603e3 381 r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
1822350d
KS
382 if (r < 0 && r != -ENOENT)
383 return r;
384
8d451309 385 if (isempty(c->vc_keymap))
1822350d
KS
386 l = strv_env_unset(l, "KEYMAP");
387 else {
28efac0d
ZJS
388 _cleanup_free_ char *s = NULL;
389 char **u;
1822350d 390
8d451309 391 s = strappend("KEYMAP=", c->vc_keymap);
98fce79d 392 if (!s)
1822350d 393 return -ENOMEM;
1822350d
KS
394
395 u = strv_env_set(l, s);
1822350d
KS
396 if (!u)
397 return -ENOMEM;
398
28efac0d 399 strv_free(l);
1822350d
KS
400 l = u;
401 }
402
8d451309 403 if (isempty(c->vc_keymap_toggle))
1822350d
KS
404 l = strv_env_unset(l, "KEYMAP_TOGGLE");
405 else {
28efac0d
ZJS
406 _cleanup_free_ char *s = NULL;
407 char **u;
1822350d 408
8d451309 409 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
98fce79d 410 if (!s)
1822350d 411 return -ENOMEM;
1822350d
KS
412
413 u = strv_env_set(l, s);
1822350d
KS
414 if (!u)
415 return -ENOMEM;
416
28efac0d 417 strv_free(l);
1822350d
KS
418 l = u;
419 }
420
421 if (strv_isempty(l)) {
1822350d
KS
422 if (unlink("/etc/vconsole.conf") < 0)
423 return errno == ENOENT ? 0 : -errno;
424
425 return 0;
426 }
427
28efac0d 428 return write_env_file_label("/etc/vconsole.conf", l);
1822350d
KS
429}
430
e78af5ff 431static int x11_write_data(Context *c) {
98fce79d
ZJS
432 _cleanup_fclose_ FILE *f = NULL;
433 _cleanup_free_ char *temp_path = NULL;
1822350d
KS
434 int r;
435
8d451309
KS
436 if (isempty(c->x11_layout) &&
437 isempty(c->x11_model) &&
438 isempty(c->x11_variant) &&
439 isempty(c->x11_options)) {
1822350d 440
1822350d
KS
441 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
442 return errno == ENOENT ? 0 : -errno;
443
444 return 0;
445 }
446
9a9bb3ca 447 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
1822350d
KS
448
449 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
450 if (r < 0)
451 return r;
452
453 fchmod(fileno(f), 0644);
454
455 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
456 "# manually too freely.\n"
457 "Section \"InputClass\"\n"
458 " Identifier \"system-keyboard\"\n"
459 " MatchIsKeyboard \"on\"\n", f);
460
8d451309
KS
461 if (!isempty(c->x11_layout))
462 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
1822350d 463
8d451309
KS
464 if (!isempty(c->x11_model))
465 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
1822350d 466
8d451309
KS
467 if (!isempty(c->x11_variant))
468 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
1822350d 469
8d451309
KS
470 if (!isempty(c->x11_options))
471 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
1822350d
KS
472
473 fputs("EndSection\n", f);
1822350d 474
dacd6cee
LP
475 r = fflush_and_check(f);
476 if (r < 0)
477 goto fail;
478
479 if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
1822350d 480 r = -errno;
dacd6cee
LP
481 goto fail;
482 }
483
484 return 0;
485
486fail:
487 (void) unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
488
489 if (temp_path)
490 (void) unlink(temp_path);
491
492 return r;
1822350d
KS
493}
494
8d451309
KS
495static int vconsole_reload(sd_bus *bus) {
496 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1822350d 497 int r;
1822350d
KS
498
499 assert(bus);
500
8d451309 501 r = sd_bus_call_method(bus,
1822350d
KS
502 "org.freedesktop.systemd1",
503 "/org/freedesktop/systemd1",
504 "org.freedesktop.systemd1.Manager",
8d451309
KS
505 "RestartUnit",
506 &error,
507 NULL,
508 "ss", "systemd-vconsole-setup.service", "replace");
1822350d 509
8d451309
KS
510 if (r < 0)
511 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1822350d
KS
512 return r;
513}
514
af76d302
ZJS
515static const char* strnulldash(const char *s) {
516 return isempty(s) || streq(s, "-") ? NULL : s;
1822350d
KS
517}
518
4e829d21
ZJS
519static int read_next_mapping(const char* filename,
520 unsigned min_fields, unsigned max_fields,
521 FILE *f, unsigned *n, char ***a) {
1822350d
KS
522 assert(f);
523 assert(n);
524 assert(a);
525
526 for (;;) {
527 char line[LINE_MAX];
528 char *l, **b;
b2fadec6 529 int r;
4e829d21 530 size_t length;
1822350d
KS
531
532 errno = 0;
533 if (!fgets(line, sizeof(line), f)) {
534
535 if (ferror(f))
536 return errno ? -errno : -EIO;
537
538 return 0;
539 }
540
541 (*n) ++;
542
543 l = strstrip(line);
544 if (l[0] == 0 || l[0] == '#')
545 continue;
546
8adaf7bd 547 r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_QUOTES);
b2fadec6
ZJS
548 if (r < 0)
549 return r;
1822350d 550
4e829d21
ZJS
551 length = strv_length(b);
552 if (length < min_fields || length > max_fields) {
553 log_error("Invalid line %s:%u, ignoring.", filename, *n);
1822350d
KS
554 strv_free(b);
555 continue;
556
557 }
558
559 *a = b;
560 return 1;
561 }
562}
563
8d451309 564static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
1822350d
KS
565 bool modified = false;
566
8d451309 567 assert(bus);
1822350d 568
8d451309 569 if (isempty(c->vc_keymap)) {
1822350d
KS
570
571 modified =
8d451309
KS
572 !isempty(c->x11_layout) ||
573 !isempty(c->x11_model) ||
574 !isempty(c->x11_variant) ||
575 !isempty(c->x11_options);
1822350d 576
8d451309 577 context_free_x11(c);
1822350d 578 } else {
98fce79d 579 _cleanup_fclose_ FILE *f = NULL;
1822350d
KS
580 unsigned n = 0;
581
582 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
583 if (!f)
584 return -errno;
585
586 for (;;) {
98fce79d 587 _cleanup_strv_free_ char **a = NULL;
1822350d
KS
588 int r;
589
4e829d21 590 r = read_next_mapping(SYSTEMD_KBD_MODEL_MAP, 5, UINT_MAX, f, &n, &a);
98fce79d 591 if (r < 0)
1822350d 592 return r;
1822350d
KS
593 if (r == 0)
594 break;
595
98fce79d 596 if (!streq(c->vc_keymap, a[0]))
1822350d 597 continue;
1822350d 598
8d451309
KS
599 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
600 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
601 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
602 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
1822350d 603
af76d302
ZJS
604 if (free_and_strdup(&c->x11_layout, strnulldash(a[1])) < 0 ||
605 free_and_strdup(&c->x11_model, strnulldash(a[2])) < 0 ||
606 free_and_strdup(&c->x11_variant, strnulldash(a[3])) < 0 ||
607 free_and_strdup(&c->x11_options, strnulldash(a[4])) < 0)
1822350d 608 return -ENOMEM;
1822350d
KS
609
610 modified = true;
611 }
612
1822350d
KS
613 break;
614 }
1822350d
KS
615 }
616
617 if (modified) {
1822350d
KS
618 int r;
619
e78af5ff 620 r = x11_write_data(c);
f647962d
MS
621 if (r < 0)
622 return log_error_errno(r, "Failed to set X11 keyboard layout: %m");
1822350d 623
502f9614
ZJS
624 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
625 strempty(c->x11_layout),
626 strempty(c->x11_model),
627 strempty(c->x11_variant),
628 strempty(c->x11_options));
629
8d451309 630 sd_bus_emit_properties_changed(bus,
1822350d
KS
631 "/org/freedesktop/locale1",
632 "org.freedesktop.locale1",
8d451309 633 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
502f9614
ZJS
634 } else
635 log_debug("X11 keyboard layout was not modified.");
1822350d
KS
636
637 return 0;
638}
639
78bd12a0 640static int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap) {
0732ef7a
ZJS
641 const char *dir;
642 _cleanup_free_ char *n;
643
78bd12a0
ZJS
644 if (x11_variant)
645 n = strjoin(x11_layout, "-", x11_variant, NULL);
0732ef7a 646 else
78bd12a0 647 n = strdup(x11_layout);
0732ef7a
ZJS
648 if (!n)
649 return -ENOMEM;
650
651 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
652 _cleanup_free_ char *p = NULL, *pz = NULL;
502f9614 653 bool uncompressed;
0732ef7a
ZJS
654
655 p = strjoin(dir, "xkb/", n, ".map", NULL);
656 pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
657 if (!p || !pz)
658 return -ENOMEM;
659
502f9614
ZJS
660 uncompressed = access(p, F_OK) == 0;
661 if (uncompressed || access(pz, F_OK) == 0) {
662 log_debug("Found converted keymap %s at %s",
663 n, uncompressed ? p : pz);
664
0732ef7a
ZJS
665 *new_keymap = n;
666 n = NULL;
667 return 1;
668 }
669 }
670
671 return 0;
672}
673
674static int find_legacy_keymap(Context *c, char **new_keymap) {
675 _cleanup_fclose_ FILE *f;
676 unsigned n = 0;
677 unsigned best_matching = 0;
78bd12a0 678 int r;
0732ef7a
ZJS
679
680 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
681 if (!f)
682 return -errno;
683
684 for (;;) {
685 _cleanup_strv_free_ char **a = NULL;
686 unsigned matching = 0;
0732ef7a 687
4e829d21 688 r = read_next_mapping(SYSTEMD_KBD_MODEL_MAP, 5, UINT_MAX, f, &n, &a);
0732ef7a
ZJS
689 if (r < 0)
690 return r;
691 if (r == 0)
692 break;
693
694 /* Determine how well matching this entry is */
695 if (streq_ptr(c->x11_layout, a[1]))
696 /* If we got an exact match, this is best */
697 matching = 10;
698 else {
0732ef7a
ZJS
699 /* We have multiple X layouts, look for an
700 * entry that matches our key with everything
701 * but the first layout stripped off. */
81fd105a 702 if (startswith_comma(c->x11_layout, a[1]))
0732ef7a
ZJS
703 matching = 5;
704 else {
81fd105a 705 char *x;
0732ef7a
ZJS
706
707 /* If that didn't work, strip off the
708 * other layouts from the entry, too */
81fd105a
ZJS
709 x = strndupa(a[1], strcspn(a[1], ","));
710 if (startswith_comma(c->x11_layout, x))
0732ef7a
ZJS
711 matching = 1;
712 }
713 }
714
387066c2
MS
715 if (matching > 0) {
716 if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
0732ef7a
ZJS
717 matching++;
718
387066c2 719 if (streq_ptr(c->x11_variant, a[3])) {
0732ef7a 720 matching++;
387066c2
MS
721
722 if (streq_ptr(c->x11_options, a[4]))
723 matching++;
724 }
0732ef7a
ZJS
725 }
726 }
727
728 /* The best matching entry so far, then let's save that */
502f9614
ZJS
729 if (matching >= MAX(best_matching, 1u)) {
730 log_debug("Found legacy keymap %s with score %u",
731 a[0], matching);
0732ef7a 732
502f9614
ZJS
733 if (matching > best_matching) {
734 best_matching = matching;
735
736 r = free_and_strdup(new_keymap, a[0]);
737 if (r < 0)
738 return r;
739 }
0732ef7a
ZJS
740 }
741 }
742
78bd12a0
ZJS
743 if (best_matching < 10 && c->x11_layout) {
744 /* The best match is only the first part of the X11
745 * keymap. Check if we have a converted map which
746 * matches just the first layout.
747 */
748 char *l, *v = NULL, *converted;
749
750 l = strndupa(c->x11_layout, strcspn(c->x11_layout, ","));
751 if (c->x11_variant)
752 v = strndupa(c->x11_variant, strcspn(c->x11_variant, ","));
753 r = find_converted_keymap(l, v, &converted);
754 if (r < 0)
755 return r;
756 if (r > 0)
757 free_and_replace(new_keymap, converted);
758 }
759
0732ef7a
ZJS
760 return 0;
761}
762
4e829d21
ZJS
763static int find_language_fallback(const char *lang, char **language) {
764 _cleanup_fclose_ FILE *f = NULL;
765 unsigned n = 0;
766
767 assert(language);
768
769 f = fopen(SYSTEMD_LANGUAGE_FALLBACK_MAP, "re");
770 if (!f)
771 return -errno;
772
773 for (;;) {
774 _cleanup_strv_free_ char **a = NULL;
775 int r;
776
777 r = read_next_mapping(SYSTEMD_LANGUAGE_FALLBACK_MAP, 2, 2, f, &n, &a);
778 if (r <= 0)
779 return r;
780
781 if (streq(lang, a[0])) {
782 assert(strv_length(a) == 2);
783 *language = a[1];
784 a[1] = NULL;
785 return 1;
786 }
787 }
788
789 assert_not_reached("should not be here");
790}
791
8d451309 792static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
1822350d 793 bool modified = false;
0732ef7a 794 int r;
1822350d 795
8d451309 796 assert(bus);
1822350d 797
8d451309 798 if (isempty(c->x11_layout)) {
1822350d
KS
799
800 modified =
8d451309
KS
801 !isempty(c->vc_keymap) ||
802 !isempty(c->vc_keymap_toggle);
1822350d 803
8d451309 804 context_free_x11(c);
1822350d 805 } else {
1822350d
KS
806 char *new_keymap = NULL;
807
78bd12a0 808 r = find_converted_keymap(c->x11_layout, c->x11_variant, &new_keymap);
0732ef7a
ZJS
809 if (r < 0)
810 return r;
811 else if (r == 0) {
812 r = find_legacy_keymap(c, &new_keymap);
b47d419c 813 if (r < 0)
1822350d 814 return r;
1822350d
KS
815 }
816
8d451309
KS
817 if (!streq_ptr(c->vc_keymap, new_keymap)) {
818 free_and_replace(&c->vc_keymap, new_keymap);
819 free_and_replace(&c->vc_keymap_toggle, NULL);
1822350d
KS
820 modified = true;
821 } else
822 free(new_keymap);
823 }
824
825 if (modified) {
8d451309 826 r = vconsole_write_data(c);
1822350d 827 if (r < 0)
da927ba9 828 log_error_errno(r, "Failed to set virtual console keymap: %m");
1822350d 829
502f9614
ZJS
830 log_info("Changed virtual console keymap to '%s' toggle '%s'",
831 strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
832
8d451309 833 sd_bus_emit_properties_changed(bus,
1822350d
KS
834 "/org/freedesktop/locale1",
835 "org.freedesktop.locale1",
8d451309 836 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
1822350d 837
8d451309 838 return vconsole_reload(bus);
502f9614
ZJS
839 } else
840 log_debug("Virtual console keymap was not modified.");
1822350d
KS
841
842 return 0;
843}
844
ebcf1f97
LP
845static int property_get_locale(
846 sd_bus *bus,
847 const char *path,
848 const char *interface,
849 const char *property,
850 sd_bus_message *reply,
851 void *userdata,
852 sd_bus_error *error) {
853
8d451309 854 Context *c = userdata;
b47d419c 855 _cleanup_strv_free_ char **l = NULL;
8d451309 856 int p, q;
1822350d 857
8d451309 858 l = new0(char*, _LOCALE_MAX+1);
1822350d
KS
859 if (!l)
860 return -ENOMEM;
861
8d451309 862 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
1822350d
KS
863 char *t;
864
8d451309 865 if (isempty(c->locale[p]))
1822350d
KS
866 continue;
867
8d451309 868 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
1822350d 869 return -ENOMEM;
1822350d 870
8d451309 871 l[q++] = t;
1822350d
KS
872 }
873
8d451309 874 return sd_bus_message_append_strv(reply, l);
1822350d
KS
875}
876
19070062 877static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
8d451309 878 Context *c = userdata;
8d451309
KS
879 _cleanup_strv_free_ char **l = NULL;
880 char **i;
4e829d21 881 const char *lang = NULL;
8d451309
KS
882 int interactive;
883 bool modified = false;
4e829d21 884 bool have[_LOCALE_MAX] = {};
8d451309 885 int p;
1822350d
KS
886 int r;
887
19070062
LP
888 assert(m);
889 assert(c);
890
8d451309
KS
891 r = bus_message_read_strv_extend(m, &l);
892 if (r < 0)
ebcf1f97 893 return r;
1822350d 894
8d451309
KS
895 r = sd_bus_message_read_basic(m, 'b', &interactive);
896 if (r < 0)
ebcf1f97 897 return r;
1822350d 898
502f9614 899 /* Check whether a variable changed and if it is valid */
8d451309
KS
900 STRV_FOREACH(i, l) {
901 bool valid = false;
1822350d 902
8d451309
KS
903 for (p = 0; p < _LOCALE_MAX; p++) {
904 size_t k;
1822350d 905
8d451309
KS
906 k = strlen(names[p]);
907 if (startswith(*i, names[p]) &&
908 (*i)[k] == '=' &&
75683450 909 locale_is_valid((*i) + k + 1)) {
8d451309 910 valid = true;
4e829d21
ZJS
911 have[p] = true;
912
913 if (p == LOCALE_LANG)
914 lang = (*i) + k + 1;
1822350d 915
8d451309
KS
916 if (!streq_ptr(*i + k + 1, c->locale[p]))
917 modified = true;
1822350d 918
8d451309
KS
919 break;
920 }
1822350d
KS
921 }
922
8d451309 923 if (!valid)
ebcf1f97 924 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
8d451309 925 }
1822350d 926
4e829d21
ZJS
927 /* If LANG was specified, but not LANGUAGE, check if we should
928 * set it based on the language fallback table. */
929 if (have[LOCALE_LANG] && !have[LOCALE_LANGUAGE]) {
930 _cleanup_free_ char *language = NULL;
931
932 assert(lang);
933
934 (void) find_language_fallback(lang, &language);
935 if (language) {
936 log_debug("Converted LANG=%s to LANGUAGE=%s", lang, language);
937 if (!streq_ptr(language, c->locale[LOCALE_LANGUAGE])) {
938 r = strv_extendf(&l, "LANGUAGE=%s", language);
939 if (r < 0)
940 return r;
941
942 have[LOCALE_LANGUAGE] = true;
943 modified = true;
944 }
945 }
946 }
947
8d451309 948 /* Check whether a variable is unset */
28efac0d 949 if (!modified)
8d451309 950 for (p = 0; p < _LOCALE_MAX; p++)
4e829d21 951 if (!isempty(c->locale[p]) && !have[p]) {
8d451309
KS
952 modified = true;
953 break;
954 }
8d451309
KS
955
956 if (modified) {
502f9614
ZJS
957 _cleanup_strv_free_ char **settings = NULL;
958
c529695e
LP
959 r = bus_verify_polkit_async(
960 m,
961 CAP_SYS_ADMIN,
962 "org.freedesktop.locale1.set-locale",
403ed0e5 963 NULL,
c529695e
LP
964 interactive,
965 UID_INVALID,
966 &c->polkit_registry,
967 error);
8d451309 968 if (r < 0)
ebcf1f97 969 return r;
8d451309
KS
970 if (r == 0)
971 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1822350d 972
28efac0d 973 STRV_FOREACH(i, l)
8d451309 974 for (p = 0; p < _LOCALE_MAX; p++) {
1822350d
KS
975 size_t k;
976
977 k = strlen(names[p]);
8d451309 978 if (startswith(*i, names[p]) && (*i)[k] == '=') {
af76d302
ZJS
979 r = free_and_strdup(&c->locale[p], *i + k + 1);
980 if (r < 0)
981 return r;
1822350d
KS
982 break;
983 }
984 }
1822350d 985
8d451309 986 for (p = 0; p < _LOCALE_MAX; p++) {
4e829d21 987 if (have[p])
8d451309 988 continue;
1822350d 989
8d451309
KS
990 free_and_replace(&c->locale[p], NULL);
991 }
1822350d 992
8d451309 993 locale_simplify(c);
1822350d 994
502f9614 995 r = locale_write_data(c, &settings);
8d451309 996 if (r < 0) {
da927ba9 997 log_error_errno(r, "Failed to set locale: %m");
ebcf1f97 998 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
8d451309 999 }
1822350d 1000
19070062 1001 locale_update_system_manager(c, sd_bus_message_get_bus(m));
1822350d 1002
502f9614
ZJS
1003 if (settings) {
1004 _cleanup_free_ char *line;
1005
1006 line = strv_join(settings, ", ");
1007 log_info("Changed locale to %s.", strnull(line));
1008 } else
1009 log_info("Changed locale to unset.");
1822350d 1010
19070062
LP
1011 (void) sd_bus_emit_properties_changed(
1012 sd_bus_message_get_bus(m),
8d451309
KS
1013 "/org/freedesktop/locale1",
1014 "org.freedesktop.locale1",
1015 "Locale", NULL);
502f9614
ZJS
1016 } else
1017 log_debug("Locale settings were not modified.");
1018
1822350d 1019
df2d202e 1020 return sd_bus_reply_method_return(m, NULL);
8d451309 1021}
1822350d 1022
19070062 1023static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
8d451309 1024 Context *c = userdata;
8d451309 1025 const char *keymap, *keymap_toggle;
102d8f81 1026 int convert, interactive;
8d451309 1027 int r;
f2cc3753 1028
19070062
LP
1029 assert(m);
1030 assert(c);
1031
8d451309
KS
1032 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
1033 if (r < 0)
ebcf1f97 1034 return r;
1822350d 1035
8d451309
KS
1036 if (isempty(keymap))
1037 keymap = NULL;
1822350d 1038
8d451309
KS
1039 if (isempty(keymap_toggle))
1040 keymap_toggle = NULL;
1822350d 1041
8d451309
KS
1042 if (!streq_ptr(keymap, c->vc_keymap) ||
1043 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
1822350d 1044
ae6c3cc0
LP
1045 if ((keymap && (!filename_is_valid(keymap) || !string_is_safe(keymap))) ||
1046 (keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle))))
d14ab08b 1047 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
1822350d 1048
c529695e
LP
1049 r = bus_verify_polkit_async(
1050 m,
1051 CAP_SYS_ADMIN,
1052 "org.freedesktop.locale1.set-keyboard",
403ed0e5 1053 NULL,
c529695e
LP
1054 interactive,
1055 UID_INVALID,
1056 &c->polkit_registry,
1057 error);
8d451309 1058 if (r < 0)
ebcf1f97 1059 return r;
8d451309
KS
1060 if (r == 0)
1061 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1822350d 1062
af76d302
ZJS
1063 if (free_and_strdup(&c->vc_keymap, keymap) < 0 ||
1064 free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0)
8d451309 1065 return -ENOMEM;
0b507b17 1066
8d451309
KS
1067 r = vconsole_write_data(c);
1068 if (r < 0) {
da927ba9 1069 log_error_errno(r, "Failed to set virtual console keymap: %m");
ebcf1f97 1070 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
8d451309 1071 }
1822350d 1072
502f9614
ZJS
1073 log_info("Changed virtual console keymap to '%s' toggle '%s'",
1074 strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
1822350d 1075
19070062 1076 r = vconsole_reload(sd_bus_message_get_bus(m));
8d451309 1077 if (r < 0)
da927ba9 1078 log_error_errno(r, "Failed to request keymap reload: %m");
1822350d 1079
19070062
LP
1080 (void) sd_bus_emit_properties_changed(
1081 sd_bus_message_get_bus(m),
8d451309
KS
1082 "/org/freedesktop/locale1",
1083 "org.freedesktop.locale1",
1084 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
1822350d 1085
8d451309 1086 if (convert) {
19070062 1087 r = vconsole_convert_to_x11(c, sd_bus_message_get_bus(m));
1822350d 1088 if (r < 0)
da927ba9 1089 log_error_errno(r, "Failed to convert keymap data: %m");
1822350d 1090 }
8d451309 1091 }
1822350d 1092
df2d202e 1093 return sd_bus_reply_method_return(m, NULL);
8d451309 1094}
1822350d 1095
d4f5a1f4 1096#ifdef HAVE_XKBCOMMON
3cb063fd 1097_printf_(3, 0)
d4f5a1f4 1098static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
8433e339
JS
1099 const char *fmt;
1100
63c372cb 1101 fmt = strjoina("libxkbcommon: ", format);
8433e339 1102 log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
d4f5a1f4
DH
1103}
1104
1105static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
1106 const struct xkb_rule_names rmlvo = {
1107 .model = model,
1108 .layout = layout,
1109 .variant = variant,
1110 .options = options,
1111 };
1112 struct xkb_context *ctx = NULL;
1113 struct xkb_keymap *km = NULL;
1114 int r;
1115
1116 /* compile keymap from RMLVO information to check out its validity */
1117
1118 ctx = xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
1119 if (!ctx) {
1120 r = -ENOMEM;
1121 goto exit;
1122 }
1123
1124 xkb_context_set_log_fn(ctx, log_xkb);
1125
1126 km = xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
1127 if (!km) {
1128 r = -EINVAL;
1129 goto exit;
1130 }
1131
1132 r = 0;
1133
1134exit:
1135 xkb_keymap_unref(km);
1136 xkb_context_unref(ctx);
1137 return r;
1138}
1139#else
1140static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
1141 return 0;
1142}
1143#endif
1144
19070062 1145static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
8d451309 1146 Context *c = userdata;
8d451309 1147 const char *layout, *model, *variant, *options;
102d8f81 1148 int convert, interactive;
8d451309 1149 int r;
1822350d 1150
19070062
LP
1151 assert(m);
1152 assert(c);
1153
8d451309
KS
1154 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
1155 if (r < 0)
ebcf1f97 1156 return r;
1822350d 1157
8d451309
KS
1158 if (isempty(layout))
1159 layout = NULL;
0b507b17 1160
8d451309
KS
1161 if (isempty(model))
1162 model = NULL;
1822350d 1163
8d451309
KS
1164 if (isempty(variant))
1165 variant = NULL;
1822350d 1166
8d451309
KS
1167 if (isempty(options))
1168 options = NULL;
1822350d 1169
8d451309
KS
1170 if (!streq_ptr(layout, c->x11_layout) ||
1171 !streq_ptr(model, c->x11_model) ||
1172 !streq_ptr(variant, c->x11_variant) ||
1173 !streq_ptr(options, c->x11_options)) {
1822350d 1174
8d451309
KS
1175 if ((layout && !string_is_safe(layout)) ||
1176 (model && !string_is_safe(model)) ||
1177 (variant && !string_is_safe(variant)) ||
1178 (options && !string_is_safe(options)))
d14ab08b 1179 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
1822350d 1180
c529695e
LP
1181 r = bus_verify_polkit_async(
1182 m,
1183 CAP_SYS_ADMIN,
1184 "org.freedesktop.locale1.set-keyboard",
403ed0e5 1185 NULL,
c529695e
LP
1186 interactive,
1187 UID_INVALID,
1188 &c->polkit_registry,
1189 error);
8d451309 1190 if (r < 0)
ebcf1f97 1191 return r;
8d451309
KS
1192 if (r == 0)
1193 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1194
8623d3a3 1195 r = verify_xkb_rmlvo(model, layout, variant, options);
8433e339
JS
1196 if (r < 0) {
1197 log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m",
1198 strempty(model), strempty(layout), strempty(variant), strempty(options));
1199 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot compile XKB keymap, refusing");
1200 }
8623d3a3 1201
af76d302
ZJS
1202 if (free_and_strdup(&c->x11_layout, layout) < 0 ||
1203 free_and_strdup(&c->x11_model, model) < 0 ||
1204 free_and_strdup(&c->x11_variant, variant) < 0 ||
1205 free_and_strdup(&c->x11_options, options) < 0)
8d451309 1206 return -ENOMEM;
1822350d 1207
e78af5ff 1208 r = x11_write_data(c);
8d451309 1209 if (r < 0) {
da927ba9 1210 log_error_errno(r, "Failed to set X11 keyboard layout: %m");
ebcf1f97 1211 return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1822350d 1212 }
1822350d 1213
502f9614
ZJS
1214 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
1215 strempty(c->x11_layout),
1216 strempty(c->x11_model),
1217 strempty(c->x11_variant),
1218 strempty(c->x11_options));
1822350d 1219
19070062
LP
1220 (void) sd_bus_emit_properties_changed(
1221 sd_bus_message_get_bus(m),
8d451309
KS
1222 "/org/freedesktop/locale1",
1223 "org.freedesktop.locale1",
c168eb67 1224 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
1822350d 1225
8d451309 1226 if (convert) {
19070062 1227 r = x11_convert_to_vconsole(c, sd_bus_message_get_bus(m));
8d451309 1228 if (r < 0)
da927ba9 1229 log_error_errno(r, "Failed to convert keymap data: %m");
8d451309 1230 }
1822350d
KS
1231 }
1232
df2d202e 1233 return sd_bus_reply_method_return(m, NULL);
1822350d
KS
1234}
1235
8d451309
KS
1236static const sd_bus_vtable locale_vtable[] = {
1237 SD_BUS_VTABLE_START(0),
6d1bd3b2
LP
1238 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1239 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1240 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1241 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1242 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1243 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1244 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
adacb957
LP
1245 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
1246 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1247 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
8d451309
KS
1248 SD_BUS_VTABLE_END
1249};
1250
1251static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
03976f7b 1252 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
1822350d
KS
1253 int r;
1254
8d451309
KS
1255 assert(c);
1256 assert(event);
1822350d
KS
1257 assert(_bus);
1258
76b54375 1259 r = sd_bus_default_system(&bus);
f647962d
MS
1260 if (r < 0)
1261 return log_error_errno(r, "Failed to get system bus connection: %m");
1822350d 1262
19befb2d 1263 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
f647962d
MS
1264 if (r < 0)
1265 return log_error_errno(r, "Failed to register object: %m");
1822350d 1266
5bb658a1 1267 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
f647962d
MS
1268 if (r < 0)
1269 return log_error_errno(r, "Failed to register name: %m");
1822350d 1270
8d451309 1271 r = sd_bus_attach_event(bus, event, 0);
f647962d
MS
1272 if (r < 0)
1273 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1822350d 1274
8d451309
KS
1275 *_bus = bus;
1276 bus = NULL;
1822350d 1277
8d451309 1278 return 0;
1822350d
KS
1279}
1280
1281int main(int argc, char *argv[]) {
28efac0d 1282 _cleanup_(context_free) Context context = {};
8d451309 1283 _cleanup_event_unref_ sd_event *event = NULL;
03976f7b 1284 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
1822350d 1285 int r;
1822350d
KS
1286
1287 log_set_target(LOG_TARGET_AUTO);
1288 log_parse_environment();
1289 log_open();
8d451309 1290
1822350d 1291 umask(0022);
cc56fafe 1292 mac_selinux_init("/etc");
1822350d 1293
1822350d
KS
1294 if (argc != 1) {
1295 log_error("This program takes no arguments.");
1296 r = -EINVAL;
1297 goto finish;
1298 }
1299
afc6adb5 1300 r = sd_event_default(&event);
1822350d 1301 if (r < 0) {
da927ba9 1302 log_error_errno(r, "Failed to allocate event loop: %m");
1822350d
KS
1303 goto finish;
1304 }
1305
cde93897
LP
1306 sd_event_set_watchdog(event, true);
1307
8d451309 1308 r = connect_bus(&context, event, &bus);
1822350d
KS
1309 if (r < 0)
1310 goto finish;
1311
8d451309
KS
1312 r = context_read_data(&context);
1313 if (r < 0) {
da927ba9 1314 log_error_errno(r, "Failed to read locale data: %m");
8d451309
KS
1315 goto finish;
1316 }
1822350d 1317
37224a5f 1318 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
8d451309 1319 if (r < 0) {
da927ba9 1320 log_error_errno(r, "Failed to run event loop: %m");
8d451309 1321 goto finish;
1822350d
KS
1322 }
1323
1822350d 1324finish:
1822350d
KS
1325 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1326}