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