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;
216 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
218 return errno == ENOENT ? 0 : -errno;
220 while (fgets(line, sizeof(line), f)) {
226 if (l[0] == 0 || l[0] == '#')
229 if (in_section && first_word(l, "Option")) {
232 a = strv_split_quoted(l);
238 if (strv_length(a) == 3) {
239 if (streq(a[1], "XkbLayout")) {
240 free_and_replace(&c->x11_layout, a[2]);
242 } else if (streq(a[1], "XkbModel")) {
243 free_and_replace(&c->x11_model, a[2]);
245 } else if (streq(a[1], "XkbVariant")) {
246 free_and_replace(&c->x11_variant, a[2]);
248 } else if (streq(a[1], "XkbOptions")) {
249 free_and_replace(&c->x11_options, a[2]);
256 } else if (!in_section && first_word(l, "Section")) {
259 a = strv_split_quoted(l);
265 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
269 } else if (in_section && first_word(l, "EndSection"))
278 static int context_read_data(Context *c) {
281 r = locale_read_data(c);
282 q = vconsole_read_data(c);
283 p = x11_read_data(c);
285 return r < 0 ? r : q < 0 ? q : p;
288 static int locale_write_data(Context *c) {
292 r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
293 if (r < 0 && r != -ENOENT)
296 for (p = 0; p < _LOCALE_MAX; p++) {
301 if (isempty(c->locale[p])) {
302 l = strv_env_unset(l, names[p]);
306 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0) {
311 u = strv_env_set(l, t);
321 if (strv_isempty(l)) {
324 if (unlink("/etc/locale.conf") < 0)
325 return errno == ENOENT ? 0 : -errno;
330 r = write_env_file_label("/etc/locale.conf", l);
336 static int locale_update_system_manager(Context *c, sd_bus *bus) {
337 _cleanup_free_ char **l_unset = NULL;
338 _cleanup_strv_free_ char **l_set = NULL;
339 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
340 sd_bus_error error = SD_BUS_ERROR_NULL;
341 unsigned c_set, c_unset, p;
346 l_unset = new0(char*, _LOCALE_MAX);
350 l_set = new0(char*, _LOCALE_MAX);
354 for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
357 if (isempty(c->locale[p]))
358 l_unset[c_set++] = (char*) names[p];
362 if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
365 l_set[c_unset++] = s;
369 assert(c_set + c_unset == _LOCALE_MAX);
370 r = sd_bus_message_new_method_call(bus, &m,
371 "org.freedesktop.systemd1",
372 "/org/freedesktop/systemd1",
373 "org.freedesktop.systemd1.Manager",
374 "UnsetAndSetEnvironment");
378 r = sd_bus_message_append_strv(m, l_unset);
382 r = sd_bus_message_append_strv(m, l_set);
386 r = sd_bus_call(bus, m, 0, &error, NULL);
388 log_error("Failed to update the manager environment: %s", strerror(-r));
393 static int vconsole_write_data(Context *c) {
395 _cleanup_strv_free_ char **l = NULL;
397 r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
398 if (r < 0 && r != -ENOENT)
401 if (isempty(c->vc_keymap))
402 l = strv_env_unset(l, "KEYMAP");
406 s = strappend("KEYMAP=", c->vc_keymap);
410 u = strv_env_set(l, s);
420 if (isempty(c->vc_keymap_toggle))
421 l = strv_env_unset(l, "KEYMAP_TOGGLE");
425 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
429 u = strv_env_set(l, s);
439 if (strv_isempty(l)) {
440 if (unlink("/etc/vconsole.conf") < 0)
441 return errno == ENOENT ? 0 : -errno;
446 r = write_env_file_label("/etc/vconsole.conf", l);
450 static int write_data_x11(Context *c) {
451 _cleanup_fclose_ FILE *f = NULL;
452 _cleanup_free_ char *temp_path = NULL;
455 if (isempty(c->x11_layout) &&
456 isempty(c->x11_model) &&
457 isempty(c->x11_variant) &&
458 isempty(c->x11_options)) {
460 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
461 return errno == ENOENT ? 0 : -errno;
466 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
468 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
472 fchmod(fileno(f), 0644);
474 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
475 "# manually too freely.\n"
476 "Section \"InputClass\"\n"
477 " Identifier \"system-keyboard\"\n"
478 " MatchIsKeyboard \"on\"\n", f);
480 if (!isempty(c->x11_layout))
481 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
483 if (!isempty(c->x11_model))
484 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
486 if (!isempty(c->x11_variant))
487 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
489 if (!isempty(c->x11_options))
490 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
492 fputs("EndSection\n", f);
495 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
497 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
504 static int vconsole_reload(sd_bus *bus) {
505 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
510 r = sd_bus_call_method(bus,
511 "org.freedesktop.systemd1",
512 "/org/freedesktop/systemd1",
513 "org.freedesktop.systemd1.Manager",
517 "ss", "systemd-vconsole-setup.service", "replace");
520 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
524 static char *strnulldash(const char *s) {
525 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
528 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
538 if (!fgets(line, sizeof(line), f)) {
541 return errno ? -errno : -EIO;
549 if (l[0] == 0 || l[0] == '#')
552 b = strv_split_quoted(l);
556 if (strv_length(b) < 5) {
557 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
568 static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
569 bool modified = false;
573 if (isempty(c->vc_keymap)) {
576 !isempty(c->x11_layout) ||
577 !isempty(c->x11_model) ||
578 !isempty(c->x11_variant) ||
579 !isempty(c->x11_options);
583 _cleanup_fclose_ FILE *f = NULL;
586 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
591 _cleanup_strv_free_ char **a = NULL;
594 r = read_next_mapping(f, &n, &a);
600 if (!streq(c->vc_keymap, a[0]))
603 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
604 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
605 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
606 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
608 if (free_and_copy(&c->x11_layout, strnulldash(a[1])) < 0 ||
609 free_and_copy(&c->x11_model, strnulldash(a[2])) < 0 ||
610 free_and_copy(&c->x11_variant, strnulldash(a[3])) < 0 ||
611 free_and_copy(&c->x11_options, strnulldash(a[4])) < 0)
624 r = write_data_x11(c);
626 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
628 sd_bus_emit_properties_changed(bus,
629 "/org/freedesktop/locale1",
630 "org.freedesktop.locale1",
631 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
637 static int find_converted_keymap(Context *c, char **new_keymap) {
639 _cleanup_free_ char *n;
642 n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
644 n = strdup(c->x11_layout);
648 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
649 _cleanup_free_ char *p = NULL, *pz = NULL;
651 p = strjoin(dir, "xkb/", n, ".map", NULL);
652 pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
656 if (access(p, F_OK) == 0 || access(pz, F_OK) == 0) {
666 static int find_legacy_keymap(Context *c, char **new_keymap) {
667 _cleanup_fclose_ FILE *f;
669 unsigned best_matching = 0;
672 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
677 _cleanup_strv_free_ char **a = NULL;
678 unsigned matching = 0;
681 r = read_next_mapping(f, &n, &a);
687 /* Determine how well matching this entry is */
688 if (streq_ptr(c->x11_layout, a[1]))
689 /* If we got an exact match, this is best */
694 x = strcspn(c->x11_layout, ",");
696 /* We have multiple X layouts, look for an
697 * entry that matches our key with everything
698 * but the first layout stripped off. */
701 strneq(c->x11_layout, a[1], x))
706 /* If that didn't work, strip off the
707 * other layouts from the entry, too */
708 w = strcspn(a[1], ",");
710 if (x > 0 && x == w &&
711 memcmp(c->x11_layout, a[1], x) == 0)
717 if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
720 if (streq_ptr(c->x11_variant, a[3])) {
723 if (streq_ptr(c->x11_options, a[4]))
729 /* The best matching entry so far, then let's save that */
730 if (matching > best_matching) {
731 best_matching = matching;
734 *new_keymap = strdup(a[0]);
743 static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
744 bool modified = false;
749 if (isempty(c->x11_layout)) {
752 !isempty(c->vc_keymap) ||
753 !isempty(c->vc_keymap_toggle);
757 char *new_keymap = NULL;
759 r = find_converted_keymap(c, &new_keymap);
763 r = find_legacy_keymap(c, &new_keymap);
768 if (!streq_ptr(c->vc_keymap, new_keymap)) {
769 free_and_replace(&c->vc_keymap, new_keymap);
770 free_and_replace(&c->vc_keymap_toggle, NULL);
777 r = vconsole_write_data(c);
779 log_error("Failed to set virtual console keymap: %s", strerror(-r));
781 sd_bus_emit_properties_changed(bus,
782 "/org/freedesktop/locale1",
783 "org.freedesktop.locale1",
784 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
786 return vconsole_reload(bus);
792 static int property_get_locale(
795 const char *interface,
796 const char *property,
797 sd_bus_message *reply,
799 sd_bus_error *error) {
801 Context *c = userdata;
802 _cleanup_strv_free_ char **l = NULL;
805 l = new0(char*, _LOCALE_MAX+1);
809 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
812 if (isempty(c->locale[p]))
815 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
821 return sd_bus_message_append_strv(reply, l);
824 static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
825 Context *c = userdata;
826 _cleanup_strv_free_ char **l = NULL;
829 bool modified = false;
830 bool passed[_LOCALE_MAX] = {};
834 r = bus_message_read_strv_extend(m, &l);
838 r = sd_bus_message_read_basic(m, 'b', &interactive);
842 /* Check whether a variable changed and if so valid */
846 for (p = 0; p < _LOCALE_MAX; p++) {
849 k = strlen(names[p]);
850 if (startswith(*i, names[p]) &&
852 locale_is_valid((*i) + k + 1)) {
856 if (!streq_ptr(*i + k + 1, c->locale[p]))
864 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
867 /* Check whether a variable is unset */
869 for (p = 0; p < _LOCALE_MAX; p++)
870 if (!isempty(c->locale[p]) && !passed[p]) {
877 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
878 "org.freedesktop.locale1.set-locale", interactive,
879 error, method_set_locale, c);
883 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
886 for (p = 0; p < _LOCALE_MAX; p++) {
889 k = strlen(names[p]);
890 if (startswith(*i, names[p]) && (*i)[k] == '=') {
893 t = strdup(*i + k + 1);
904 for (p = 0; p < _LOCALE_MAX; p++) {
908 free_and_replace(&c->locale[p], NULL);
913 r = locale_write_data(c);
915 log_error("Failed to set locale: %s", strerror(-r));
916 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
919 locale_update_system_manager(c, bus);
921 log_info("Changed locale information.");
923 sd_bus_emit_properties_changed(bus,
924 "/org/freedesktop/locale1",
925 "org.freedesktop.locale1",
929 return sd_bus_reply_method_return(m, NULL);
932 static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
933 Context *c = userdata;
934 const char *keymap, *keymap_toggle;
935 int convert, interactive;
938 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
945 if (isempty(keymap_toggle))
946 keymap_toggle = NULL;
948 if (!streq_ptr(keymap, c->vc_keymap) ||
949 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
951 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
952 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
953 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
955 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
956 "org.freedesktop.locale1.set-keyboard",
957 interactive, error, method_set_vc_keyboard, c);
961 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
963 if (free_and_copy(&c->vc_keymap, keymap) < 0 ||
964 free_and_copy(&c->vc_keymap_toggle, keymap_toggle) < 0)
967 r = vconsole_write_data(c);
969 log_error("Failed to set virtual console keymap: %s", strerror(-r));
970 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
973 log_info("Changed virtual console keymap to '%s'", strempty(c->vc_keymap));
975 r = vconsole_reload(bus);
977 log_error("Failed to request keymap reload: %s", strerror(-r));
979 sd_bus_emit_properties_changed(bus,
980 "/org/freedesktop/locale1",
981 "org.freedesktop.locale1",
982 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
985 r = vconsole_convert_to_x11(c, bus);
987 log_error("Failed to convert keymap data: %s", strerror(-r));
991 return sd_bus_reply_method_return(m, NULL);
994 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
995 Context *c = userdata;
996 const char *layout, *model, *variant, *options;
997 int convert, interactive;
1000 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
1004 if (isempty(layout))
1010 if (isempty(variant))
1013 if (isempty(options))
1016 if (!streq_ptr(layout, c->x11_layout) ||
1017 !streq_ptr(model, c->x11_model) ||
1018 !streq_ptr(variant, c->x11_variant) ||
1019 !streq_ptr(options, c->x11_options)) {
1021 if ((layout && !string_is_safe(layout)) ||
1022 (model && !string_is_safe(model)) ||
1023 (variant && !string_is_safe(variant)) ||
1024 (options && !string_is_safe(options)))
1025 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
1027 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
1028 "org.freedesktop.locale1.set-keyboard",
1029 interactive, error, method_set_x11_keyboard, c);
1033 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1035 if (free_and_copy(&c->x11_layout, layout) < 0 ||
1036 free_and_copy(&c->x11_model, model) < 0 ||
1037 free_and_copy(&c->x11_variant, variant) < 0 ||
1038 free_and_copy(&c->x11_options, options) < 0)
1041 r = write_data_x11(c);
1043 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1044 return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1047 log_info("Changed X11 keyboard layout to '%s'", strempty(c->x11_layout));
1049 sd_bus_emit_properties_changed(bus,
1050 "/org/freedesktop/locale1",
1051 "org.freedesktop.locale1",
1052 "X11Layout" "X11Model" "X11Variant" "X11Options", NULL);
1055 r = x11_convert_to_vconsole(c, bus);
1057 log_error("Failed to convert keymap data: %s", strerror(-r));
1061 return sd_bus_reply_method_return(m, NULL);
1064 static const sd_bus_vtable locale_vtable[] = {
1065 SD_BUS_VTABLE_START(0),
1066 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1067 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1068 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1069 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1070 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1071 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1072 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1073 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
1074 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1075 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1079 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
1080 _cleanup_bus_unref_ sd_bus *bus = NULL;
1087 r = sd_bus_default_system(&bus);
1089 log_error("Failed to get system bus connection: %s", strerror(-r));
1093 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
1095 log_error("Failed to register object: %s", strerror(-r));
1099 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
1101 log_error("Failed to register name: %s", strerror(-r));
1105 r = sd_bus_attach_event(bus, event, 0);
1107 log_error("Failed to attach bus to event loop: %s", strerror(-r));
1117 int main(int argc, char *argv[]) {
1118 Context context = {};
1119 _cleanup_event_unref_ sd_event *event = NULL;
1120 _cleanup_bus_unref_ sd_bus *bus = NULL;
1123 log_set_target(LOG_TARGET_AUTO);
1124 log_parse_environment();
1131 log_error("This program takes no arguments.");
1136 r = sd_event_default(&event);
1138 log_error("Failed to allocate event loop: %s", strerror(-r));
1142 sd_event_set_watchdog(event, true);
1144 r = connect_bus(&context, event, &bus);
1148 r = context_read_data(&context);
1150 log_error("Failed to read locale data: %s", strerror(-r));
1154 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
1156 log_error("Failed to run event loop: %s", strerror(-r));
1161 context_free(&context, bus);
1163 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;