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"
43 /* We don't list LC_ALL here on purpose. People should be
44 * using LANG instead. */
57 LOCALE_LC_MEASUREMENT,
58 LOCALE_LC_IDENTIFICATION,
62 static const char * const names[_LOCALE_MAX] = {
63 [LOCALE_LANG] = "LANG",
64 [LOCALE_LANGUAGE] = "LANGUAGE",
65 [LOCALE_LC_CTYPE] = "LC_CTYPE",
66 [LOCALE_LC_NUMERIC] = "LC_NUMERIC",
67 [LOCALE_LC_TIME] = "LC_TIME",
68 [LOCALE_LC_COLLATE] = "LC_COLLATE",
69 [LOCALE_LC_MONETARY] = "LC_MONETARY",
70 [LOCALE_LC_MESSAGES] = "LC_MESSAGES",
71 [LOCALE_LC_PAPER] = "LC_PAPER",
72 [LOCALE_LC_NAME] = "LC_NAME",
73 [LOCALE_LC_ADDRESS] = "LC_ADDRESS",
74 [LOCALE_LC_TELEPHONE] = "LC_TELEPHONE",
75 [LOCALE_LC_MEASUREMENT] = "LC_MEASUREMENT",
76 [LOCALE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
79 typedef struct Context {
80 char *locale[_LOCALE_MAX];
88 char *vc_keymap_toggle;
90 Hashmap *polkit_registry;
93 static int free_and_copy(char **s, const char *v) {
99 r = strdup_or_null(isempty(v) ? NULL : v, &t);
109 static void free_and_replace(char **s, char *v) {
114 static 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);
121 static void context_free_vconsole(Context *c) {
122 free_and_replace(&c->vc_keymap, NULL);
123 free_and_replace(&c->vc_keymap_toggle, NULL);
126 static void context_free_locale(Context *c) {
129 for (p = 0; p < _LOCALE_MAX; p++)
130 free_and_replace(&c->locale[p], NULL);
133 static void context_free(Context *c, sd_bus *bus) {
134 context_free_locale(c);
136 context_free_vconsole(c);
138 bus_verify_polkit_async_registry_free(bus, c->polkit_registry);
141 static void locale_simplify(Context *c) {
144 for (p = LOCALE_LANG+1; p < _LOCALE_MAX; p++)
145 if (isempty(c->locale[p]) || streq_ptr(c->locale[LOCALE_LANG], c->locale[p])) {
151 static int locale_read_data(Context *c) {
154 context_free_locale(c);
156 r = parse_env_file("/etc/locale.conf", NEWLINE,
157 "LANG", &c->locale[LOCALE_LANG],
158 "LANGUAGE", &c->locale[LOCALE_LANGUAGE],
159 "LC_CTYPE", &c->locale[LOCALE_LC_CTYPE],
160 "LC_NUMERIC", &c->locale[LOCALE_LC_NUMERIC],
161 "LC_TIME", &c->locale[LOCALE_LC_TIME],
162 "LC_COLLATE", &c->locale[LOCALE_LC_COLLATE],
163 "LC_MONETARY", &c->locale[LOCALE_LC_MONETARY],
164 "LC_MESSAGES", &c->locale[LOCALE_LC_MESSAGES],
165 "LC_PAPER", &c->locale[LOCALE_LC_PAPER],
166 "LC_NAME", &c->locale[LOCALE_LC_NAME],
167 "LC_ADDRESS", &c->locale[LOCALE_LC_ADDRESS],
168 "LC_TELEPHONE", &c->locale[LOCALE_LC_TELEPHONE],
169 "LC_MEASUREMENT", &c->locale[LOCALE_LC_MEASUREMENT],
170 "LC_IDENTIFICATION", &c->locale[LOCALE_LC_IDENTIFICATION],
176 /* Fill in what we got passed from systemd. */
177 for (p = 0; p < _LOCALE_MAX; p++) {
180 r = free_and_copy(&c->locale[p], getenv(names[p]));
192 static int vconsole_read_data(Context *c) {
195 context_free_vconsole(c);
197 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
198 "KEYMAP", &c->vc_keymap,
199 "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
202 if (r < 0 && r != -ENOENT)
208 static int x11_read_data(Context *c) {
211 bool in_section = false;
215 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
217 return errno == ENOENT ? 0 : -errno;
219 while (fgets(line, sizeof(line), f)) {
225 if (l[0] == 0 || l[0] == '#')
228 if (in_section && first_word(l, "Option")) {
231 a = strv_split_quoted(l);
237 if (strv_length(a) == 3) {
238 if (streq(a[1], "XkbLayout")) {
239 free_and_replace(&c->x11_layout, a[2]);
241 } else if (streq(a[1], "XkbModel")) {
242 free_and_replace(&c->x11_model, a[2]);
244 } else if (streq(a[1], "XkbVariant")) {
245 free_and_replace(&c->x11_variant, a[2]);
247 } else if (streq(a[1], "XkbOptions")) {
248 free_and_replace(&c->x11_options, a[2]);
255 } else if (!in_section && first_word(l, "Section")) {
258 a = strv_split_quoted(l);
264 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
268 } else if (in_section && first_word(l, "EndSection"))
277 static int context_read_data(Context *c) {
280 r = locale_read_data(c);
281 q = vconsole_read_data(c);
282 p = x11_read_data(c);
284 return r < 0 ? r : q < 0 ? q : p;
287 static int locale_write_data(Context *c) {
291 r = load_env_file("/etc/locale.conf", NULL, &l);
292 if (r < 0 && r != -ENOENT)
295 for (p = 0; p < _LOCALE_MAX; p++) {
300 if (isempty(c->locale[p])) {
301 l = strv_env_unset(l, names[p]);
305 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0) {
310 u = strv_env_set(l, t);
320 if (strv_isempty(l)) {
323 if (unlink("/etc/locale.conf") < 0)
324 return errno == ENOENT ? 0 : -errno;
329 r = write_env_file_label("/etc/locale.conf", l);
335 static int locale_update_system_manager(Context *c, sd_bus *bus) {
336 _cleanup_free_ char **l_unset = NULL;
337 _cleanup_strv_free_ char **l_set = NULL;
338 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
339 sd_bus_error error = SD_BUS_ERROR_NULL;
340 unsigned c_set, c_unset, p;
345 l_unset = new0(char*, _LOCALE_MAX);
349 l_set = new0(char*, _LOCALE_MAX);
353 for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
356 if (isempty(c->locale[p]))
357 l_unset[c_set++] = (char*) names[p];
361 if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
364 l_set[c_unset++] = s;
368 assert(c_set + c_unset == _LOCALE_MAX);
369 r = sd_bus_message_new_method_call(bus,
370 "org.freedesktop.systemd1",
371 "/org/freedesktop/systemd1",
372 "org.freedesktop.systemd1.Manager",
373 "UnsetAndSetEnvironment", &m);
377 r = sd_bus_message_append_strv(m, l_unset);
381 r = sd_bus_message_append_strv(m, l_set);
385 r = sd_bus_send_with_reply_and_block(bus, m, 0, &error, NULL);
387 log_error("Failed to update the manager environment: %s", strerror(-r));
392 static int vconsole_write_data(Context *c) {
396 r = load_env_file("/etc/vconsole.conf", NULL, &l);
397 if (r < 0 && r != -ENOENT)
400 if (isempty(c->vc_keymap))
401 l = strv_env_unset(l, "KEYMAP");
405 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);
432 u = strv_env_set(l, s);
442 if (strv_isempty(l)) {
445 if (unlink("/etc/vconsole.conf") < 0)
446 return errno == ENOENT ? 0 : -errno;
451 r = write_env_file_label("/etc/vconsole.conf", l);
457 static int write_data_x11(Context *c) {
462 if (isempty(c->x11_layout) &&
463 isempty(c->x11_model) &&
464 isempty(c->x11_variant) &&
465 isempty(c->x11_options)) {
467 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
468 return errno == ENOENT ? 0 : -errno;
473 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
475 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
479 fchmod(fileno(f), 0644);
481 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
482 "# manually too freely.\n"
483 "Section \"InputClass\"\n"
484 " Identifier \"system-keyboard\"\n"
485 " MatchIsKeyboard \"on\"\n", f);
487 if (!isempty(c->x11_layout))
488 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
490 if (!isempty(c->x11_model))
491 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
493 if (!isempty(c->x11_variant))
494 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
496 if (!isempty(c->x11_options))
497 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
499 fputs("EndSection\n", f);
502 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
504 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
515 static int vconsole_reload(sd_bus *bus) {
516 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
521 r = sd_bus_call_method(bus,
522 "org.freedesktop.systemd1",
523 "/org/freedesktop/systemd1",
524 "org.freedesktop.systemd1.Manager",
528 "ss", "systemd-vconsole-setup.service", "replace");
531 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
535 static char *strnulldash(const char *s) {
536 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
539 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
549 if (!fgets(line, sizeof(line), f)) {
552 return errno ? -errno : -EIO;
560 if (l[0] == 0 || l[0] == '#')
563 b = strv_split_quoted(l);
567 if (strv_length(b) < 5) {
568 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
579 static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
580 bool modified = false;
584 if (isempty(c->vc_keymap)) {
587 !isempty(c->x11_layout) ||
588 !isempty(c->x11_model) ||
589 !isempty(c->x11_variant) ||
590 !isempty(c->x11_options);
597 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
605 r = read_next_mapping(f, &n, &a);
614 if (!streq(c->vc_keymap, a[0])) {
619 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
620 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
621 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
622 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
624 if (free_and_copy(&c->x11_layout, strnulldash(a[1])) < 0 ||
625 free_and_copy(&c->x11_model, strnulldash(a[2])) < 0 ||
626 free_and_copy(&c->x11_variant, strnulldash(a[3])) < 0 ||
627 free_and_copy(&c->x11_options, strnulldash(a[4])) < 0) {
646 r = write_data_x11(c);
648 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
650 sd_bus_emit_properties_changed(bus,
651 "/org/freedesktop/locale1",
652 "org.freedesktop.locale1",
653 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
659 static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
660 bool modified = false;
664 if (isempty(c->x11_layout)) {
667 !isempty(c->vc_keymap) ||
668 !isempty(c->vc_keymap_toggle);
672 _cleanup_fclose_ FILE *f;
674 unsigned best_matching = 0;
675 char *new_keymap = NULL;
677 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
682 _cleanup_strv_free_ char **a = NULL;
683 unsigned matching = 0;
686 r = read_next_mapping(f, &n, &a);
692 /* Determine how well matching this entry is */
693 if (streq_ptr(c->x11_layout, a[1]))
694 /* If we got an exact match, this is best */
699 x = strcspn(c->x11_layout, ",");
701 /* We have multiple X layouts, look
702 * for an entry that matches our key
703 * with the everything but the first
704 * layout stripped off. */
707 strneq(c->x11_layout, a[1], x))
712 /* If that didn't work, strip
713 * off the other layouts from
715 w = strcspn(a[1], ",");
717 if (x > 0 && x == w &&
718 memcmp(c->x11_layout, a[1], x) == 0)
724 streq_ptr(c->x11_model, a[2])) {
727 if (streq_ptr(c->x11_variant, a[3])) {
730 if (streq_ptr(c->x11_options, a[4]))
735 /* The best matching entry so far, then let's
737 if (matching > best_matching) {
738 best_matching = matching;
741 new_keymap = strdup(a[0]);
747 if (!streq_ptr(c->vc_keymap, new_keymap)) {
748 free_and_replace(&c->vc_keymap, new_keymap);
749 free_and_replace(&c->vc_keymap_toggle, NULL);
758 r = vconsole_write_data(c);
760 log_error("Failed to set virtual console keymap: %s", strerror(-r));
762 sd_bus_emit_properties_changed(bus,
763 "/org/freedesktop/locale1",
764 "org.freedesktop.locale1",
765 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
767 return vconsole_reload(bus);
773 static int property_get_locale(sd_bus *bus, const char *path, const char *interface,
774 const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata) {
775 Context *c = userdata;
776 _cleanup_strv_free_ char **l = NULL;
779 l = new0(char*, _LOCALE_MAX+1);
783 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
786 if (isempty(c->locale[p]))
789 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
795 return sd_bus_message_append_strv(reply, l);
798 static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata) {
799 Context *c = userdata;
800 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
801 _cleanup_strv_free_ char **l = NULL;
804 bool modified = false;
805 bool passed[_LOCALE_MAX] = {};
809 r = bus_message_read_strv_extend(m, &l);
811 return sd_bus_reply_method_errno(bus, m, r, NULL);
813 r = sd_bus_message_read_basic(m, 'b', &interactive);
815 return sd_bus_reply_method_errno(bus, m, r, NULL);
817 /* Check whether a variable changed and if so valid */
821 for (p = 0; p < _LOCALE_MAX; p++) {
824 k = strlen(names[p]);
825 if (startswith(*i, names[p]) &&
827 string_is_safe((*i) + k + 1)) {
831 if (!streq_ptr(*i + k + 1, c->locale[p]))
839 sd_bus_reply_method_errorf(bus, m, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
842 /* Check whether a variable is unset */
844 for (p = 0; p < _LOCALE_MAX; p++)
845 if (!isempty(c->locale[p]) && !passed[p]) {
852 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
853 "org.freedesktop.locale1.set-locale", interactive,
854 &error, method_set_locale, c);
856 return sd_bus_reply_method_errno(bus, m, r, &error);
858 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
861 for (p = 0; p < _LOCALE_MAX; p++) {
864 k = strlen(names[p]);
865 if (startswith(*i, names[p]) && (*i)[k] == '=') {
868 t = strdup(*i + k + 1);
879 for (p = 0; p < _LOCALE_MAX; p++) {
883 free_and_replace(&c->locale[p], NULL);
888 r = locale_write_data(c);
890 log_error("Failed to set locale: %s", strerror(-r));
891 return sd_bus_reply_method_errnof(bus, m, r, "Failed to set locale: %s", strerror(-r));
894 locale_update_system_manager(c, bus);
896 log_info("Changed locale information.");
898 sd_bus_emit_properties_changed(bus,
899 "/org/freedesktop/locale1",
900 "org.freedesktop.locale1",
904 return sd_bus_reply_method_return(bus, m, NULL);
907 static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata) {
908 Context *c = userdata;
909 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
910 const char *keymap, *keymap_toggle;
911 unsigned convert, interactive;
914 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
916 return sd_bus_reply_method_errno(bus, m, r, NULL);
921 if (isempty(keymap_toggle))
922 keymap_toggle = NULL;
924 if (!streq_ptr(keymap, c->vc_keymap) ||
925 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
927 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
928 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
929 return sd_bus_reply_method_errnof(bus, m, r, "Received invalid keymap data: %s", -EINVAL);
931 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
932 "org.freedesktop.locale1.set-keyboard",
933 interactive, &error, method_set_vc_keyboard, c);
935 return sd_bus_reply_method_errno(bus, m, r, &error);
937 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
939 if (free_and_copy(&c->vc_keymap, keymap) < 0 ||
940 free_and_copy(&c->vc_keymap_toggle, keymap_toggle) < 0)
943 r = vconsole_write_data(c);
945 log_error("Failed to set virtual console keymap: %s", strerror(-r));
946 return sd_bus_reply_method_errnof(bus, m, r, "Failed to set virtual console keymap: %s", strerror(-r));
949 log_info("Changed virtual console keymap to '%s'", strempty(c->vc_keymap));
951 r = vconsole_reload(bus);
953 log_error("Failed to request keymap reload: %s", strerror(-r));
955 sd_bus_emit_properties_changed(bus,
956 "/org/freedesktop/locale1",
957 "org.freedesktop.locale1",
958 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
961 r = vconsole_convert_to_x11(c, bus);
963 log_error("Failed to convert keymap data: %s", strerror(-r));
967 return sd_bus_reply_method_return(bus, m, NULL);
970 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata) {
971 Context *c = userdata;
972 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
973 const char *layout, *model, *variant, *options;
974 unsigned convert, interactive;
977 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
979 return sd_bus_reply_method_errno(bus, m, r, NULL);
987 if (isempty(variant))
990 if (isempty(options))
993 if (!streq_ptr(layout, c->x11_layout) ||
994 !streq_ptr(model, c->x11_model) ||
995 !streq_ptr(variant, c->x11_variant) ||
996 !streq_ptr(options, c->x11_options)) {
998 if ((layout && !string_is_safe(layout)) ||
999 (model && !string_is_safe(model)) ||
1000 (variant && !string_is_safe(variant)) ||
1001 (options && !string_is_safe(options)))
1002 return sd_bus_reply_method_errnof(bus, m, r, "Received invalid keyboard data: %s", -EINVAL);
1004 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
1005 "org.freedesktop.locale1.set-keyboard",
1006 interactive, &error, method_set_x11_keyboard, c);
1008 return sd_bus_reply_method_errno(bus, m, r, &error);
1010 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1012 if (free_and_copy(&c->x11_layout, layout) < 0 ||
1013 free_and_copy(&c->x11_model, model) < 0 ||
1014 free_and_copy(&c->x11_variant, variant) < 0 ||
1015 free_and_copy(&c->x11_options, options) < 0)
1018 r = write_data_x11(c);
1020 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1021 return sd_bus_reply_method_errnof(bus, m, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1024 log_info("Changed X11 keyboard layout to '%s'", strempty(c->x11_layout));
1026 sd_bus_emit_properties_changed(bus,
1027 "/org/freedesktop/locale1",
1028 "org.freedesktop.locale1",
1029 "X11Layout" "X11Model" "X11Variant" "X11Options", NULL);
1032 r = x11_convert_to_vconsole(c, bus);
1034 log_error("Failed to convert keymap data: %s", strerror(-r));
1038 return sd_bus_reply_method_return(bus, m, NULL);
1041 static const sd_bus_vtable locale_vtable[] = {
1042 SD_BUS_VTABLE_START(0),
1043 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1044 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1045 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1046 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1047 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1048 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1049 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1050 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, 0),
1051 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, 0),
1052 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, 0),
1056 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
1057 _cleanup_bus_unref_ sd_bus *bus = NULL;
1064 r = sd_bus_open_system(&bus);
1066 log_error("Failed to get system bus connection: %s", strerror(-r));
1070 r = sd_bus_add_object_vtable(bus, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
1072 log_error("Failed to register object: %s", strerror(-r));
1076 r = sd_bus_request_name(bus, "org.freedesktop.locale1", SD_BUS_NAME_DO_NOT_QUEUE);
1078 log_error("Failed to register name: %s", strerror(-r));
1082 if (r != SD_BUS_NAME_PRIMARY_OWNER) {
1083 log_error("Failed to acquire name.");
1087 r = sd_bus_attach_event(bus, event, 0);
1089 log_error("Failed to attach bus to event loop: %s", strerror(-r));
1099 int main(int argc, char *argv[]) {
1100 Context context = {};
1101 _cleanup_event_unref_ sd_event *event = NULL;
1102 _cleanup_bus_unref_ sd_bus *bus = NULL;
1105 log_set_target(LOG_TARGET_AUTO);
1106 log_parse_environment();
1113 log_error("This program takes no arguments.");
1118 r = sd_event_new(&event);
1120 log_error("Failed to allocate event loop: %s", strerror(-r));
1124 r = connect_bus(&context, event, &bus);
1128 r = context_read_data(&context);
1130 log_error("Failed to read locale data: %s", strerror(-r));
1134 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC);
1136 log_error("Failed to run event loop: %s", strerror(-r));
1143 context_free(&context, bus);
1145 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;