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_call(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) {
394 _cleanup_strv_free_ char **l = NULL;
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);
409 u = strv_env_set(l, s);
419 if (isempty(c->vc_keymap_toggle))
420 l = strv_env_unset(l, "KEYMAP_TOGGLE");
424 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
428 u = strv_env_set(l, s);
438 if (strv_isempty(l)) {
439 if (unlink("/etc/vconsole.conf") < 0)
440 return errno == ENOENT ? 0 : -errno;
445 r = write_env_file_label("/etc/vconsole.conf", l);
449 static int write_data_x11(Context *c) {
450 _cleanup_fclose_ FILE *f = NULL;
451 _cleanup_free_ char *temp_path = NULL;
454 if (isempty(c->x11_layout) &&
455 isempty(c->x11_model) &&
456 isempty(c->x11_variant) &&
457 isempty(c->x11_options)) {
459 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
460 return errno == ENOENT ? 0 : -errno;
465 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
467 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
471 fchmod(fileno(f), 0644);
473 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
474 "# manually too freely.\n"
475 "Section \"InputClass\"\n"
476 " Identifier \"system-keyboard\"\n"
477 " MatchIsKeyboard \"on\"\n", f);
479 if (!isempty(c->x11_layout))
480 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
482 if (!isempty(c->x11_model))
483 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
485 if (!isempty(c->x11_variant))
486 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
488 if (!isempty(c->x11_options))
489 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
491 fputs("EndSection\n", f);
494 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
496 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
503 static int vconsole_reload(sd_bus *bus) {
504 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
509 r = sd_bus_call_method(bus,
510 "org.freedesktop.systemd1",
511 "/org/freedesktop/systemd1",
512 "org.freedesktop.systemd1.Manager",
516 "ss", "systemd-vconsole-setup.service", "replace");
519 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
523 static char *strnulldash(const char *s) {
524 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
527 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
537 if (!fgets(line, sizeof(line), f)) {
540 return errno ? -errno : -EIO;
548 if (l[0] == 0 || l[0] == '#')
551 b = strv_split_quoted(l);
555 if (strv_length(b) < 5) {
556 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
567 static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
568 bool modified = false;
572 if (isempty(c->vc_keymap)) {
575 !isempty(c->x11_layout) ||
576 !isempty(c->x11_model) ||
577 !isempty(c->x11_variant) ||
578 !isempty(c->x11_options);
582 _cleanup_fclose_ FILE *f = NULL;
585 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
590 _cleanup_strv_free_ char **a = NULL;
593 r = read_next_mapping(f, &n, &a);
599 if (!streq(c->vc_keymap, a[0]))
602 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
603 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
604 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
605 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
607 if (free_and_copy(&c->x11_layout, strnulldash(a[1])) < 0 ||
608 free_and_copy(&c->x11_model, strnulldash(a[2])) < 0 ||
609 free_and_copy(&c->x11_variant, strnulldash(a[3])) < 0 ||
610 free_and_copy(&c->x11_options, strnulldash(a[4])) < 0)
623 r = write_data_x11(c);
625 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
627 sd_bus_emit_properties_changed(bus,
628 "/org/freedesktop/locale1",
629 "org.freedesktop.locale1",
630 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
636 static int find_converted_keymap(Context *c, char **new_keymap) {
638 _cleanup_free_ char *n;
641 n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
643 n = strdup(c->x11_layout);
647 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
648 _cleanup_free_ char *p = NULL, *pz = NULL;
650 p = strjoin(dir, "xkb/", n, ".map", NULL);
651 pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
655 if (access(p, F_OK) == 0 || access(pz, F_OK) == 0) {
665 static int find_legacy_keymap(Context *c, char **new_keymap) {
666 _cleanup_fclose_ FILE *f;
668 unsigned best_matching = 0;
671 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
676 _cleanup_strv_free_ char **a = NULL;
677 unsigned matching = 0;
680 r = read_next_mapping(f, &n, &a);
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 */
693 x = strcspn(c->x11_layout, ",");
695 /* We have multiple X layouts, look for an
696 * entry that matches our key with everything
697 * but the first layout stripped off. */
700 strneq(c->x11_layout, a[1], x))
705 /* If that didn't work, strip off the
706 * other layouts from the entry, too */
707 w = strcspn(a[1], ",");
709 if (x > 0 && x == w &&
710 memcmp(c->x11_layout, a[1], x) == 0)
716 streq_ptr(c->x11_model, a[2])) {
719 if (streq_ptr(c->x11_variant, a[3])) {
722 if (streq_ptr(c->x11_options, a[4]))
727 /* The best matching entry so far, then let's save that */
728 if (matching > best_matching) {
729 best_matching = matching;
732 *new_keymap = strdup(a[0]);
741 static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
742 bool modified = false;
747 if (isempty(c->x11_layout)) {
750 !isempty(c->vc_keymap) ||
751 !isempty(c->vc_keymap_toggle);
755 char *new_keymap = NULL;
757 r = find_converted_keymap(c, &new_keymap);
761 r = find_legacy_keymap(c, &new_keymap);
766 if (!streq_ptr(c->vc_keymap, new_keymap)) {
767 free_and_replace(&c->vc_keymap, new_keymap);
768 free_and_replace(&c->vc_keymap_toggle, NULL);
775 r = vconsole_write_data(c);
777 log_error("Failed to set virtual console keymap: %s", strerror(-r));
779 sd_bus_emit_properties_changed(bus,
780 "/org/freedesktop/locale1",
781 "org.freedesktop.locale1",
782 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
784 return vconsole_reload(bus);
790 static int property_get_locale(sd_bus *bus, const char *path, const char *interface,
791 const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata) {
792 Context *c = userdata;
793 _cleanup_strv_free_ char **l = NULL;
796 l = new0(char*, _LOCALE_MAX+1);
800 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
803 if (isempty(c->locale[p]))
806 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
812 return sd_bus_message_append_strv(reply, l);
815 static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata) {
816 Context *c = userdata;
817 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
818 _cleanup_strv_free_ char **l = NULL;
821 bool modified = false;
822 bool passed[_LOCALE_MAX] = {};
826 r = bus_message_read_strv_extend(m, &l);
828 return sd_bus_reply_method_errno(bus, m, r, NULL);
830 r = sd_bus_message_read_basic(m, 'b', &interactive);
832 return sd_bus_reply_method_errno(bus, m, r, NULL);
834 /* Check whether a variable changed and if so valid */
838 for (p = 0; p < _LOCALE_MAX; p++) {
841 k = strlen(names[p]);
842 if (startswith(*i, names[p]) &&
844 string_is_safe((*i) + k + 1)) {
848 if (!streq_ptr(*i + k + 1, c->locale[p]))
856 sd_bus_reply_method_errorf(bus, m, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
859 /* Check whether a variable is unset */
861 for (p = 0; p < _LOCALE_MAX; p++)
862 if (!isempty(c->locale[p]) && !passed[p]) {
869 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
870 "org.freedesktop.locale1.set-locale", interactive,
871 &error, method_set_locale, c);
873 return sd_bus_reply_method_errno(bus, m, r, &error);
875 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
878 for (p = 0; p < _LOCALE_MAX; p++) {
881 k = strlen(names[p]);
882 if (startswith(*i, names[p]) && (*i)[k] == '=') {
885 t = strdup(*i + k + 1);
896 for (p = 0; p < _LOCALE_MAX; p++) {
900 free_and_replace(&c->locale[p], NULL);
905 r = locale_write_data(c);
907 log_error("Failed to set locale: %s", strerror(-r));
908 return sd_bus_reply_method_errnof(bus, m, r, "Failed to set locale: %s", strerror(-r));
911 locale_update_system_manager(c, bus);
913 log_info("Changed locale information.");
915 sd_bus_emit_properties_changed(bus,
916 "/org/freedesktop/locale1",
917 "org.freedesktop.locale1",
921 return sd_bus_reply_method_return(bus, m, NULL);
924 static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata) {
925 Context *c = userdata;
926 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
927 const char *keymap, *keymap_toggle;
928 int convert, interactive;
931 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
933 return sd_bus_reply_method_errno(bus, m, r, NULL);
938 if (isempty(keymap_toggle))
939 keymap_toggle = NULL;
941 if (!streq_ptr(keymap, c->vc_keymap) ||
942 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
944 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
945 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
946 return sd_bus_reply_method_errnof(bus, m, r, "Received invalid keymap data: %s", -EINVAL);
948 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
949 "org.freedesktop.locale1.set-keyboard",
950 interactive, &error, method_set_vc_keyboard, c);
952 return sd_bus_reply_method_errno(bus, m, r, &error);
954 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
956 if (free_and_copy(&c->vc_keymap, keymap) < 0 ||
957 free_and_copy(&c->vc_keymap_toggle, keymap_toggle) < 0)
960 r = vconsole_write_data(c);
962 log_error("Failed to set virtual console keymap: %s", strerror(-r));
963 return sd_bus_reply_method_errnof(bus, m, r, "Failed to set virtual console keymap: %s", strerror(-r));
966 log_info("Changed virtual console keymap to '%s'", strempty(c->vc_keymap));
968 r = vconsole_reload(bus);
970 log_error("Failed to request keymap reload: %s", strerror(-r));
972 sd_bus_emit_properties_changed(bus,
973 "/org/freedesktop/locale1",
974 "org.freedesktop.locale1",
975 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
978 r = vconsole_convert_to_x11(c, bus);
980 log_error("Failed to convert keymap data: %s", strerror(-r));
984 return sd_bus_reply_method_return(bus, m, NULL);
987 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata) {
988 Context *c = userdata;
989 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
990 const char *layout, *model, *variant, *options;
991 int convert, interactive;
994 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
996 return sd_bus_reply_method_errno(bus, m, r, NULL);
1004 if (isempty(variant))
1007 if (isempty(options))
1010 if (!streq_ptr(layout, c->x11_layout) ||
1011 !streq_ptr(model, c->x11_model) ||
1012 !streq_ptr(variant, c->x11_variant) ||
1013 !streq_ptr(options, c->x11_options)) {
1015 if ((layout && !string_is_safe(layout)) ||
1016 (model && !string_is_safe(model)) ||
1017 (variant && !string_is_safe(variant)) ||
1018 (options && !string_is_safe(options)))
1019 return sd_bus_reply_method_errnof(bus, m, r, "Received invalid keyboard data: %s", -EINVAL);
1021 r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
1022 "org.freedesktop.locale1.set-keyboard",
1023 interactive, &error, method_set_x11_keyboard, c);
1025 return sd_bus_reply_method_errno(bus, m, r, &error);
1027 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1029 if (free_and_copy(&c->x11_layout, layout) < 0 ||
1030 free_and_copy(&c->x11_model, model) < 0 ||
1031 free_and_copy(&c->x11_variant, variant) < 0 ||
1032 free_and_copy(&c->x11_options, options) < 0)
1035 r = write_data_x11(c);
1037 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1038 return sd_bus_reply_method_errnof(bus, m, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1041 log_info("Changed X11 keyboard layout to '%s'", strempty(c->x11_layout));
1043 sd_bus_emit_properties_changed(bus,
1044 "/org/freedesktop/locale1",
1045 "org.freedesktop.locale1",
1046 "X11Layout" "X11Model" "X11Variant" "X11Options", NULL);
1049 r = x11_convert_to_vconsole(c, bus);
1051 log_error("Failed to convert keymap data: %s", strerror(-r));
1055 return sd_bus_reply_method_return(bus, m, NULL);
1058 static const sd_bus_vtable locale_vtable[] = {
1059 SD_BUS_VTABLE_START(0),
1060 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1061 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1062 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1063 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1064 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1065 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1066 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1067 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, 0),
1068 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, 0),
1069 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, 0),
1073 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
1074 _cleanup_bus_unref_ sd_bus *bus = NULL;
1081 r = sd_bus_default_system(&bus);
1083 log_error("Failed to get system bus connection: %s", strerror(-r));
1087 r = sd_bus_add_object_vtable(bus, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
1089 log_error("Failed to register object: %s", strerror(-r));
1093 r = sd_bus_request_name(bus, "org.freedesktop.locale1", SD_BUS_NAME_DO_NOT_QUEUE);
1095 log_error("Failed to register name: %s", strerror(-r));
1099 if (r != SD_BUS_NAME_PRIMARY_OWNER) {
1100 log_error("Failed to acquire name.");
1104 r = sd_bus_attach_event(bus, event, 0);
1106 log_error("Failed to attach bus to event loop: %s", strerror(-r));
1116 int main(int argc, char *argv[]) {
1117 Context context = {};
1118 _cleanup_event_unref_ sd_event *event = NULL;
1119 _cleanup_bus_unref_ sd_bus *bus = NULL;
1122 log_set_target(LOG_TARGET_AUTO);
1123 log_parse_environment();
1130 log_error("This program takes no arguments.");
1135 r = sd_event_default(&event);
1137 log_error("Failed to allocate event loop: %s", strerror(-r));
1141 r = connect_bus(&context, event, &bus);
1145 r = context_read_data(&context);
1147 log_error("Failed to read locale data: %s", strerror(-r));
1151 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC);
1153 log_error("Failed to run event loop: %s", strerror(-r));
1160 context_free(&context, bus);
1162 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;