1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
7 Copyright 2013 Kay Sievers
9 systemd is free software; you can redistribute it and/or modify it
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
12 (at your option) any later version.
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
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
35 #include "fileio-label.h"
38 #include "bus-error.h"
39 #include "bus-message.h"
40 #include "event-util.h"
41 #include "locale-util.h"
44 /* We don't list LC_ALL here on purpose. People should be
45 * using LANG instead. */
58 LOCALE_LC_MEASUREMENT,
59 LOCALE_LC_IDENTIFICATION,
63 static const char * const names[_LOCALE_MAX] = {
64 [LOCALE_LANG] = "LANG",
65 [LOCALE_LANGUAGE] = "LANGUAGE",
66 [LOCALE_LC_CTYPE] = "LC_CTYPE",
67 [LOCALE_LC_NUMERIC] = "LC_NUMERIC",
68 [LOCALE_LC_TIME] = "LC_TIME",
69 [LOCALE_LC_COLLATE] = "LC_COLLATE",
70 [LOCALE_LC_MONETARY] = "LC_MONETARY",
71 [LOCALE_LC_MESSAGES] = "LC_MESSAGES",
72 [LOCALE_LC_PAPER] = "LC_PAPER",
73 [LOCALE_LC_NAME] = "LC_NAME",
74 [LOCALE_LC_ADDRESS] = "LC_ADDRESS",
75 [LOCALE_LC_TELEPHONE] = "LC_TELEPHONE",
76 [LOCALE_LC_MEASUREMENT] = "LC_MEASUREMENT",
77 [LOCALE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
80 typedef struct Context {
81 char *locale[_LOCALE_MAX];
89 char *vc_keymap_toggle;
91 Hashmap *polkit_registry;
94 static int free_and_copy(char **s, const char *v) {
100 r = strdup_or_null(isempty(v) ? NULL : v, &t);
110 static void free_and_replace(char **s, char *v) {
115 static void context_free_x11(Context *c) {
116 free_and_replace(&c->x11_layout, NULL);
117 free_and_replace(&c->x11_model, NULL);
118 free_and_replace(&c->x11_variant, NULL);
119 free_and_replace(&c->x11_options, NULL);
122 static void context_free_vconsole(Context *c) {
123 free_and_replace(&c->vc_keymap, NULL);
124 free_and_replace(&c->vc_keymap_toggle, NULL);
127 static void context_free_locale(Context *c) {
130 for (p = 0; p < _LOCALE_MAX; p++)
131 free_and_replace(&c->locale[p], NULL);
134 static void context_free(Context *c, sd_bus *bus) {
135 context_free_locale(c);
137 context_free_vconsole(c);
139 bus_verify_polkit_async_registry_free(bus, c->polkit_registry);
142 static void locale_simplify(Context *c) {
145 for (p = LOCALE_LANG+1; p < _LOCALE_MAX; p++)
146 if (isempty(c->locale[p]) || streq_ptr(c->locale[LOCALE_LANG], c->locale[p])) {
152 static int locale_read_data(Context *c) {
155 context_free_locale(c);
157 r = parse_env_file("/etc/locale.conf", NEWLINE,
158 "LANG", &c->locale[LOCALE_LANG],
159 "LANGUAGE", &c->locale[LOCALE_LANGUAGE],
160 "LC_CTYPE", &c->locale[LOCALE_LC_CTYPE],
161 "LC_NUMERIC", &c->locale[LOCALE_LC_NUMERIC],
162 "LC_TIME", &c->locale[LOCALE_LC_TIME],
163 "LC_COLLATE", &c->locale[LOCALE_LC_COLLATE],
164 "LC_MONETARY", &c->locale[LOCALE_LC_MONETARY],
165 "LC_MESSAGES", &c->locale[LOCALE_LC_MESSAGES],
166 "LC_PAPER", &c->locale[LOCALE_LC_PAPER],
167 "LC_NAME", &c->locale[LOCALE_LC_NAME],
168 "LC_ADDRESS", &c->locale[LOCALE_LC_ADDRESS],
169 "LC_TELEPHONE", &c->locale[LOCALE_LC_TELEPHONE],
170 "LC_MEASUREMENT", &c->locale[LOCALE_LC_MEASUREMENT],
171 "LC_IDENTIFICATION", &c->locale[LOCALE_LC_IDENTIFICATION],
177 /* Fill in what we got passed from systemd. */
178 for (p = 0; p < _LOCALE_MAX; p++) {
181 r = free_and_copy(&c->locale[p], getenv(names[p]));
193 static int vconsole_read_data(Context *c) {
196 context_free_vconsole(c);
198 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
199 "KEYMAP", &c->vc_keymap,
200 "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
203 if (r < 0 && r != -ENOENT)
209 static int x11_read_data(Context *c) {
212 bool in_section = false;
217 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
219 return errno == ENOENT ? 0 : -errno;
221 while (fgets(line, sizeof(line), f)) {
227 if (l[0] == 0 || l[0] == '#')
230 if (in_section && first_word(l, "Option")) {
233 r = strv_split_quoted(&a, l);
239 if (strv_length(a) == 3) {
240 if (streq(a[1], "XkbLayout")) {
241 free_and_replace(&c->x11_layout, a[2]);
243 } else if (streq(a[1], "XkbModel")) {
244 free_and_replace(&c->x11_model, a[2]);
246 } else if (streq(a[1], "XkbVariant")) {
247 free_and_replace(&c->x11_variant, a[2]);
249 } else if (streq(a[1], "XkbOptions")) {
250 free_and_replace(&c->x11_options, a[2]);
257 } else if (!in_section && first_word(l, "Section")) {
260 r = strv_split_quoted(&a, l);
266 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
270 } else if (in_section && first_word(l, "EndSection"))
279 static int context_read_data(Context *c) {
282 r = locale_read_data(c);
283 q = vconsole_read_data(c);
284 p = x11_read_data(c);
286 return r < 0 ? r : q < 0 ? q : p;
289 static int locale_write_data(Context *c) {
293 r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
294 if (r < 0 && r != -ENOENT)
297 for (p = 0; p < _LOCALE_MAX; p++) {
302 if (isempty(c->locale[p])) {
303 l = strv_env_unset(l, names[p]);
307 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0) {
312 u = strv_env_set(l, t);
322 if (strv_isempty(l)) {
325 if (unlink("/etc/locale.conf") < 0)
326 return errno == ENOENT ? 0 : -errno;
331 r = write_env_file_label("/etc/locale.conf", l);
337 static int locale_update_system_manager(Context *c, sd_bus *bus) {
338 _cleanup_free_ char **l_unset = NULL;
339 _cleanup_strv_free_ char **l_set = NULL;
340 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
341 sd_bus_error error = SD_BUS_ERROR_NULL;
342 unsigned c_set, c_unset, p;
347 l_unset = new0(char*, _LOCALE_MAX);
351 l_set = new0(char*, _LOCALE_MAX);
355 for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
358 if (isempty(c->locale[p]))
359 l_unset[c_set++] = (char*) names[p];
363 if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
366 l_set[c_unset++] = s;
370 assert(c_set + c_unset == _LOCALE_MAX);
371 r = sd_bus_message_new_method_call(bus, &m,
372 "org.freedesktop.systemd1",
373 "/org/freedesktop/systemd1",
374 "org.freedesktop.systemd1.Manager",
375 "UnsetAndSetEnvironment");
379 r = sd_bus_message_append_strv(m, l_unset);
383 r = sd_bus_message_append_strv(m, l_set);
387 r = sd_bus_call(bus, m, 0, &error, NULL);
389 log_error("Failed to update the manager environment: %s", strerror(-r));
394 static int vconsole_write_data(Context *c) {
396 _cleanup_strv_free_ char **l = NULL;
398 r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
399 if (r < 0 && r != -ENOENT)
402 if (isempty(c->vc_keymap))
403 l = strv_env_unset(l, "KEYMAP");
407 s = strappend("KEYMAP=", c->vc_keymap);
411 u = strv_env_set(l, s);
421 if (isempty(c->vc_keymap_toggle))
422 l = strv_env_unset(l, "KEYMAP_TOGGLE");
426 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
430 u = strv_env_set(l, s);
440 if (strv_isempty(l)) {
441 if (unlink("/etc/vconsole.conf") < 0)
442 return errno == ENOENT ? 0 : -errno;
447 r = write_env_file_label("/etc/vconsole.conf", l);
451 static int write_data_x11(Context *c) {
452 _cleanup_fclose_ FILE *f = NULL;
453 _cleanup_free_ char *temp_path = NULL;
456 if (isempty(c->x11_layout) &&
457 isempty(c->x11_model) &&
458 isempty(c->x11_variant) &&
459 isempty(c->x11_options)) {
461 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
462 return errno == ENOENT ? 0 : -errno;
467 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
469 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
473 fchmod(fileno(f), 0644);
475 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
476 "# manually too freely.\n"
477 "Section \"InputClass\"\n"
478 " Identifier \"system-keyboard\"\n"
479 " MatchIsKeyboard \"on\"\n", f);
481 if (!isempty(c->x11_layout))
482 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
484 if (!isempty(c->x11_model))
485 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
487 if (!isempty(c->x11_variant))
488 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
490 if (!isempty(c->x11_options))
491 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
493 fputs("EndSection\n", f);
496 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
498 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
505 static int vconsole_reload(sd_bus *bus) {
506 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
511 r = sd_bus_call_method(bus,
512 "org.freedesktop.systemd1",
513 "/org/freedesktop/systemd1",
514 "org.freedesktop.systemd1.Manager",
518 "ss", "systemd-vconsole-setup.service", "replace");
521 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
525 static char *strnulldash(const char *s) {
526 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
529 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
540 if (!fgets(line, sizeof(line), f)) {
543 return errno ? -errno : -EIO;
551 if (l[0] == 0 || l[0] == '#')
554 r = strv_split_quoted(&b, l);
558 if (strv_length(b) < 5) {
559 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
570 static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
571 bool modified = false;
575 if (isempty(c->vc_keymap)) {
578 !isempty(c->x11_layout) ||
579 !isempty(c->x11_model) ||
580 !isempty(c->x11_variant) ||
581 !isempty(c->x11_options);
585 _cleanup_fclose_ FILE *f = NULL;
588 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
593 _cleanup_strv_free_ char **a = NULL;
596 r = read_next_mapping(f, &n, &a);
602 if (!streq(c->vc_keymap, a[0]))
605 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
606 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
607 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
608 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
610 if (free_and_copy(&c->x11_layout, strnulldash(a[1])) < 0 ||
611 free_and_copy(&c->x11_model, strnulldash(a[2])) < 0 ||
612 free_and_copy(&c->x11_variant, strnulldash(a[3])) < 0 ||
613 free_and_copy(&c->x11_options, strnulldash(a[4])) < 0)
626 r = write_data_x11(c);
628 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
630 sd_bus_emit_properties_changed(bus,
631 "/org/freedesktop/locale1",
632 "org.freedesktop.locale1",
633 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
639 static int find_converted_keymap(Context *c, char **new_keymap) {
641 _cleanup_free_ char *n;
644 n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
646 n = strdup(c->x11_layout);
650 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
651 _cleanup_free_ char *p = NULL, *pz = NULL;
653 p = strjoin(dir, "xkb/", n, ".map", NULL);
654 pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
658 if (access(p, F_OK) == 0 || access(pz, F_OK) == 0) {
668 static int find_legacy_keymap(Context *c, char **new_keymap) {
669 _cleanup_fclose_ FILE *f;
671 unsigned best_matching = 0;
674 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
679 _cleanup_strv_free_ char **a = NULL;
680 unsigned matching = 0;
683 r = read_next_mapping(f, &n, &a);
689 /* Determine how well matching this entry is */
690 if (streq_ptr(c->x11_layout, a[1]))
691 /* If we got an exact match, this is best */
696 x = strcspn(c->x11_layout, ",");
698 /* We have multiple X layouts, look for an
699 * entry that matches our key with everything
700 * but the first layout stripped off. */
703 strneq(c->x11_layout, a[1], x))
708 /* If that didn't work, strip off the
709 * other layouts from the entry, too */
710 w = strcspn(a[1], ",");
712 if (x > 0 && x == w &&
713 memcmp(c->x11_layout, a[1], x) == 0)
719 if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
722 if (streq_ptr(c->x11_variant, a[3])) {
725 if (streq_ptr(c->x11_options, a[4]))
731 /* The best matching entry so far, then let's save that */
732 if (matching > best_matching) {
733 best_matching = matching;
736 *new_keymap = strdup(a[0]);
745 static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
746 bool modified = false;
751 if (isempty(c->x11_layout)) {
754 !isempty(c->vc_keymap) ||
755 !isempty(c->vc_keymap_toggle);
759 char *new_keymap = NULL;
761 r = find_converted_keymap(c, &new_keymap);
765 r = find_legacy_keymap(c, &new_keymap);
770 if (!streq_ptr(c->vc_keymap, new_keymap)) {
771 free_and_replace(&c->vc_keymap, new_keymap);
772 free_and_replace(&c->vc_keymap_toggle, NULL);
779 r = vconsole_write_data(c);
781 log_error("Failed to set virtual console keymap: %s", strerror(-r));
783 sd_bus_emit_properties_changed(bus,
784 "/org/freedesktop/locale1",
785 "org.freedesktop.locale1",
786 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
788 return vconsole_reload(bus);
794 static int property_get_locale(
797 const char *interface,
798 const char *property,
799 sd_bus_message *reply,
801 sd_bus_error *error) {
803 Context *c = userdata;
804 _cleanup_strv_free_ char **l = NULL;
807 l = new0(char*, _LOCALE_MAX+1);
811 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
814 if (isempty(c->locale[p]))
817 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
823 return sd_bus_message_append_strv(reply, l);
826 static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
827 Context *c = userdata;
828 _cleanup_strv_free_ char **l = NULL;
831 bool modified = false;
832 bool passed[_LOCALE_MAX] = {};
836 r = bus_message_read_strv_extend(m, &l);
840 r = sd_bus_message_read_basic(m, 'b', &interactive);
844 /* Check whether a variable changed and if so valid */
848 for (p = 0; p < _LOCALE_MAX; p++) {
851 k = strlen(names[p]);
852 if (startswith(*i, names[p]) &&
854 locale_is_valid((*i) + k + 1)) {
858 if (!streq_ptr(*i + k + 1, c->locale[p]))
866 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
869 /* Check whether a variable is unset */
871 for (p = 0; p < _LOCALE_MAX; p++)
872 if (!isempty(c->locale[p]) && !passed[p]) {
879 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
880 "org.freedesktop.locale1.set-locale", interactive,
881 error, method_set_locale, c);
885 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
888 for (p = 0; p < _LOCALE_MAX; p++) {
891 k = strlen(names[p]);
892 if (startswith(*i, names[p]) && (*i)[k] == '=') {
895 t = strdup(*i + k + 1);
906 for (p = 0; p < _LOCALE_MAX; p++) {
910 free_and_replace(&c->locale[p], NULL);
915 r = locale_write_data(c);
917 log_error("Failed to set locale: %s", strerror(-r));
918 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
921 locale_update_system_manager(c, bus);
923 log_info("Changed locale information.");
925 sd_bus_emit_properties_changed(bus,
926 "/org/freedesktop/locale1",
927 "org.freedesktop.locale1",
931 return sd_bus_reply_method_return(m, NULL);
934 static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
935 Context *c = userdata;
936 const char *keymap, *keymap_toggle;
937 int convert, interactive;
940 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
947 if (isempty(keymap_toggle))
948 keymap_toggle = NULL;
950 if (!streq_ptr(keymap, c->vc_keymap) ||
951 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
953 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
954 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
955 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
957 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
958 "org.freedesktop.locale1.set-keyboard",
959 interactive, error, method_set_vc_keyboard, c);
963 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
965 if (free_and_copy(&c->vc_keymap, keymap) < 0 ||
966 free_and_copy(&c->vc_keymap_toggle, keymap_toggle) < 0)
969 r = vconsole_write_data(c);
971 log_error("Failed to set virtual console keymap: %s", strerror(-r));
972 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
975 log_info("Changed virtual console keymap to '%s'", strempty(c->vc_keymap));
977 r = vconsole_reload(bus);
979 log_error("Failed to request keymap reload: %s", strerror(-r));
981 sd_bus_emit_properties_changed(bus,
982 "/org/freedesktop/locale1",
983 "org.freedesktop.locale1",
984 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
987 r = vconsole_convert_to_x11(c, bus);
989 log_error("Failed to convert keymap data: %s", strerror(-r));
993 return sd_bus_reply_method_return(m, NULL);
996 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
997 Context *c = userdata;
998 const char *layout, *model, *variant, *options;
999 int convert, interactive;
1002 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
1006 if (isempty(layout))
1012 if (isempty(variant))
1015 if (isempty(options))
1018 if (!streq_ptr(layout, c->x11_layout) ||
1019 !streq_ptr(model, c->x11_model) ||
1020 !streq_ptr(variant, c->x11_variant) ||
1021 !streq_ptr(options, c->x11_options)) {
1023 if ((layout && !string_is_safe(layout)) ||
1024 (model && !string_is_safe(model)) ||
1025 (variant && !string_is_safe(variant)) ||
1026 (options && !string_is_safe(options)))
1027 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
1029 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
1030 "org.freedesktop.locale1.set-keyboard",
1031 interactive, error, method_set_x11_keyboard, c);
1035 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1037 if (free_and_copy(&c->x11_layout, layout) < 0 ||
1038 free_and_copy(&c->x11_model, model) < 0 ||
1039 free_and_copy(&c->x11_variant, variant) < 0 ||
1040 free_and_copy(&c->x11_options, options) < 0)
1043 r = write_data_x11(c);
1045 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1046 return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1049 log_info("Changed X11 keyboard layout to '%s'", strempty(c->x11_layout));
1051 sd_bus_emit_properties_changed(bus,
1052 "/org/freedesktop/locale1",
1053 "org.freedesktop.locale1",
1054 "X11Layout" "X11Model" "X11Variant" "X11Options", NULL);
1057 r = x11_convert_to_vconsole(c, bus);
1059 log_error("Failed to convert keymap data: %s", strerror(-r));
1063 return sd_bus_reply_method_return(m, NULL);
1066 static const sd_bus_vtable locale_vtable[] = {
1067 SD_BUS_VTABLE_START(0),
1068 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1069 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1070 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1071 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1072 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1073 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1074 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1075 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
1076 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1077 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1081 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
1082 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1089 r = sd_bus_default_system(&bus);
1091 log_error("Failed to get system bus connection: %s", strerror(-r));
1095 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
1097 log_error("Failed to register object: %s", strerror(-r));
1101 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
1103 log_error("Failed to register name: %s", strerror(-r));
1107 r = sd_bus_attach_event(bus, event, 0);
1109 log_error("Failed to attach bus to event loop: %s", strerror(-r));
1119 int main(int argc, char *argv[]) {
1120 Context context = {};
1121 _cleanup_event_unref_ sd_event *event = NULL;
1122 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1125 log_set_target(LOG_TARGET_AUTO);
1126 log_parse_environment();
1133 log_error("This program takes no arguments.");
1138 r = sd_event_default(&event);
1140 log_error("Failed to allocate event loop: %s", strerror(-r));
1144 sd_event_set_watchdog(event, true);
1146 r = connect_bus(&context, event, &bus);
1150 r = context_read_data(&context);
1152 log_error("Failed to read locale data: %s", strerror(-r));
1156 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
1158 log_error("Failed to run event loop: %s", strerror(-r));
1163 context_free(&context, bus);
1165 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;