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/>.
26 #include <sys/capability.h>
36 #include "fileio-label.h"
39 #include "bus-error.h"
40 #include "bus-message.h"
41 #include "event-util.h"
42 #include "locale-util.h"
45 /* We don't list LC_ALL here on purpose. People should be
46 * using LANG instead. */
59 LOCALE_LC_MEASUREMENT,
60 LOCALE_LC_IDENTIFICATION,
64 static const char * const names[_LOCALE_MAX] = {
65 [LOCALE_LANG] = "LANG",
66 [LOCALE_LANGUAGE] = "LANGUAGE",
67 [LOCALE_LC_CTYPE] = "LC_CTYPE",
68 [LOCALE_LC_NUMERIC] = "LC_NUMERIC",
69 [LOCALE_LC_TIME] = "LC_TIME",
70 [LOCALE_LC_COLLATE] = "LC_COLLATE",
71 [LOCALE_LC_MONETARY] = "LC_MONETARY",
72 [LOCALE_LC_MESSAGES] = "LC_MESSAGES",
73 [LOCALE_LC_PAPER] = "LC_PAPER",
74 [LOCALE_LC_NAME] = "LC_NAME",
75 [LOCALE_LC_ADDRESS] = "LC_ADDRESS",
76 [LOCALE_LC_TELEPHONE] = "LC_TELEPHONE",
77 [LOCALE_LC_MEASUREMENT] = "LC_MEASUREMENT",
78 [LOCALE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
81 typedef struct Context {
82 char *locale[_LOCALE_MAX];
90 char *vc_keymap_toggle;
92 Hashmap *polkit_registry;
95 static int free_and_copy(char **s, const char *v) {
101 r = strdup_or_null(isempty(v) ? NULL : v, &t);
111 static void free_and_replace(char **s, char *v) {
116 static void context_free_x11(Context *c) {
117 free_and_replace(&c->x11_layout, NULL);
118 free_and_replace(&c->x11_model, NULL);
119 free_and_replace(&c->x11_variant, NULL);
120 free_and_replace(&c->x11_options, NULL);
123 static void context_free_vconsole(Context *c) {
124 free_and_replace(&c->vc_keymap, NULL);
125 free_and_replace(&c->vc_keymap_toggle, NULL);
128 static void context_free_locale(Context *c) {
131 for (p = 0; p < _LOCALE_MAX; p++)
132 free_and_replace(&c->locale[p], NULL);
135 static void context_free(Context *c) {
136 context_free_locale(c);
138 context_free_vconsole(c);
140 bus_verify_polkit_async_registry_free(c->polkit_registry);
143 static void locale_simplify(Context *c) {
146 for (p = LOCALE_LANG+1; p < _LOCALE_MAX; p++)
147 if (isempty(c->locale[p]) || streq_ptr(c->locale[LOCALE_LANG], c->locale[p])) {
153 static int locale_read_data(Context *c) {
156 context_free_locale(c);
158 r = parse_env_file("/etc/locale.conf", NEWLINE,
159 "LANG", &c->locale[LOCALE_LANG],
160 "LANGUAGE", &c->locale[LOCALE_LANGUAGE],
161 "LC_CTYPE", &c->locale[LOCALE_LC_CTYPE],
162 "LC_NUMERIC", &c->locale[LOCALE_LC_NUMERIC],
163 "LC_TIME", &c->locale[LOCALE_LC_TIME],
164 "LC_COLLATE", &c->locale[LOCALE_LC_COLLATE],
165 "LC_MONETARY", &c->locale[LOCALE_LC_MONETARY],
166 "LC_MESSAGES", &c->locale[LOCALE_LC_MESSAGES],
167 "LC_PAPER", &c->locale[LOCALE_LC_PAPER],
168 "LC_NAME", &c->locale[LOCALE_LC_NAME],
169 "LC_ADDRESS", &c->locale[LOCALE_LC_ADDRESS],
170 "LC_TELEPHONE", &c->locale[LOCALE_LC_TELEPHONE],
171 "LC_MEASUREMENT", &c->locale[LOCALE_LC_MEASUREMENT],
172 "LC_IDENTIFICATION", &c->locale[LOCALE_LC_IDENTIFICATION],
178 /* Fill in what we got passed from systemd. */
179 for (p = 0; p < _LOCALE_MAX; p++) {
182 r = free_and_copy(&c->locale[p], getenv(names[p]));
194 static int vconsole_read_data(Context *c) {
197 context_free_vconsole(c);
199 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
200 "KEYMAP", &c->vc_keymap,
201 "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
204 if (r < 0 && r != -ENOENT)
210 static int x11_read_data(Context *c) {
213 bool in_section = false;
218 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
220 return errno == ENOENT ? 0 : -errno;
222 while (fgets(line, sizeof(line), f)) {
228 if (l[0] == 0 || l[0] == '#')
231 if (in_section && first_word(l, "Option")) {
234 r = strv_split_quoted(&a, l);
240 if (strv_length(a) == 3) {
241 if (streq(a[1], "XkbLayout")) {
242 free_and_replace(&c->x11_layout, a[2]);
244 } else if (streq(a[1], "XkbModel")) {
245 free_and_replace(&c->x11_model, a[2]);
247 } else if (streq(a[1], "XkbVariant")) {
248 free_and_replace(&c->x11_variant, a[2]);
250 } else if (streq(a[1], "XkbOptions")) {
251 free_and_replace(&c->x11_options, a[2]);
258 } else if (!in_section && first_word(l, "Section")) {
261 r = strv_split_quoted(&a, l);
267 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
271 } else if (in_section && first_word(l, "EndSection"))
280 static int context_read_data(Context *c) {
283 r = locale_read_data(c);
284 q = vconsole_read_data(c);
285 p = x11_read_data(c);
287 return r < 0 ? r : q < 0 ? q : p;
290 static int locale_write_data(Context *c) {
294 r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
295 if (r < 0 && r != -ENOENT)
298 for (p = 0; p < _LOCALE_MAX; p++) {
303 if (isempty(c->locale[p])) {
304 l = strv_env_unset(l, names[p]);
308 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0) {
313 u = strv_env_set(l, t);
323 if (strv_isempty(l)) {
326 if (unlink("/etc/locale.conf") < 0)
327 return errno == ENOENT ? 0 : -errno;
332 r = write_env_file_label("/etc/locale.conf", l);
338 static int locale_update_system_manager(Context *c, sd_bus *bus) {
339 _cleanup_free_ char **l_unset = NULL;
340 _cleanup_strv_free_ char **l_set = NULL;
341 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
342 sd_bus_error error = SD_BUS_ERROR_NULL;
343 unsigned c_set, c_unset, p;
348 l_unset = new0(char*, _LOCALE_MAX);
352 l_set = new0(char*, _LOCALE_MAX);
356 for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
359 if (isempty(c->locale[p]))
360 l_unset[c_set++] = (char*) names[p];
364 if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
367 l_set[c_unset++] = s;
371 assert(c_set + c_unset == _LOCALE_MAX);
372 r = sd_bus_message_new_method_call(bus, &m,
373 "org.freedesktop.systemd1",
374 "/org/freedesktop/systemd1",
375 "org.freedesktop.systemd1.Manager",
376 "UnsetAndSetEnvironment");
380 r = sd_bus_message_append_strv(m, l_unset);
384 r = sd_bus_message_append_strv(m, l_set);
388 r = sd_bus_call(bus, m, 0, &error, NULL);
390 log_error("Failed to update the manager environment: %s", strerror(-r));
395 static int vconsole_write_data(Context *c) {
397 _cleanup_strv_free_ char **l = NULL;
399 r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
400 if (r < 0 && r != -ENOENT)
403 if (isempty(c->vc_keymap))
404 l = strv_env_unset(l, "KEYMAP");
408 s = strappend("KEYMAP=", c->vc_keymap);
412 u = strv_env_set(l, s);
422 if (isempty(c->vc_keymap_toggle))
423 l = strv_env_unset(l, "KEYMAP_TOGGLE");
427 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
431 u = strv_env_set(l, s);
441 if (strv_isempty(l)) {
442 if (unlink("/etc/vconsole.conf") < 0)
443 return errno == ENOENT ? 0 : -errno;
448 r = write_env_file_label("/etc/vconsole.conf", l);
452 static int write_data_x11(Context *c) {
453 _cleanup_fclose_ FILE *f = NULL;
454 _cleanup_free_ char *temp_path = NULL;
457 if (isempty(c->x11_layout) &&
458 isempty(c->x11_model) &&
459 isempty(c->x11_variant) &&
460 isempty(c->x11_options)) {
462 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
463 return errno == ENOENT ? 0 : -errno;
468 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
470 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
474 fchmod(fileno(f), 0644);
476 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
477 "# manually too freely.\n"
478 "Section \"InputClass\"\n"
479 " Identifier \"system-keyboard\"\n"
480 " MatchIsKeyboard \"on\"\n", f);
482 if (!isempty(c->x11_layout))
483 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
485 if (!isempty(c->x11_model))
486 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
488 if (!isempty(c->x11_variant))
489 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
491 if (!isempty(c->x11_options))
492 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
494 fputs("EndSection\n", f);
497 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
499 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
506 static int vconsole_reload(sd_bus *bus) {
507 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
512 r = sd_bus_call_method(bus,
513 "org.freedesktop.systemd1",
514 "/org/freedesktop/systemd1",
515 "org.freedesktop.systemd1.Manager",
519 "ss", "systemd-vconsole-setup.service", "replace");
522 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
526 static char *strnulldash(const char *s) {
527 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
530 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
541 if (!fgets(line, sizeof(line), f)) {
544 return errno ? -errno : -EIO;
552 if (l[0] == 0 || l[0] == '#')
555 r = strv_split_quoted(&b, l);
559 if (strv_length(b) < 5) {
560 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
571 static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
572 bool modified = false;
576 if (isempty(c->vc_keymap)) {
579 !isempty(c->x11_layout) ||
580 !isempty(c->x11_model) ||
581 !isempty(c->x11_variant) ||
582 !isempty(c->x11_options);
586 _cleanup_fclose_ FILE *f = NULL;
589 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
594 _cleanup_strv_free_ char **a = NULL;
597 r = read_next_mapping(f, &n, &a);
603 if (!streq(c->vc_keymap, a[0]))
606 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
607 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
608 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
609 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
611 if (free_and_copy(&c->x11_layout, strnulldash(a[1])) < 0 ||
612 free_and_copy(&c->x11_model, strnulldash(a[2])) < 0 ||
613 free_and_copy(&c->x11_variant, strnulldash(a[3])) < 0 ||
614 free_and_copy(&c->x11_options, strnulldash(a[4])) < 0)
627 r = write_data_x11(c);
629 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
631 sd_bus_emit_properties_changed(bus,
632 "/org/freedesktop/locale1",
633 "org.freedesktop.locale1",
634 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
640 static int find_converted_keymap(Context *c, char **new_keymap) {
642 _cleanup_free_ char *n;
645 n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
647 n = strdup(c->x11_layout);
651 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
652 _cleanup_free_ char *p = NULL, *pz = NULL;
654 p = strjoin(dir, "xkb/", n, ".map", NULL);
655 pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
659 if (access(p, F_OK) == 0 || access(pz, F_OK) == 0) {
669 static int find_legacy_keymap(Context *c, char **new_keymap) {
670 _cleanup_fclose_ FILE *f;
672 unsigned best_matching = 0;
675 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
680 _cleanup_strv_free_ char **a = NULL;
681 unsigned matching = 0;
684 r = read_next_mapping(f, &n, &a);
690 /* Determine how well matching this entry is */
691 if (streq_ptr(c->x11_layout, a[1]))
692 /* If we got an exact match, this is best */
697 x = strcspn(c->x11_layout, ",");
699 /* We have multiple X layouts, look for an
700 * entry that matches our key with everything
701 * but the first layout stripped off. */
704 strneq(c->x11_layout, a[1], x))
709 /* If that didn't work, strip off the
710 * other layouts from the entry, too */
711 w = strcspn(a[1], ",");
713 if (x > 0 && x == w &&
714 memcmp(c->x11_layout, a[1], x) == 0)
720 if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
723 if (streq_ptr(c->x11_variant, a[3])) {
726 if (streq_ptr(c->x11_options, a[4]))
732 /* The best matching entry so far, then let's save that */
733 if (matching > best_matching) {
734 best_matching = matching;
737 *new_keymap = strdup(a[0]);
746 static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
747 bool modified = false;
752 if (isempty(c->x11_layout)) {
755 !isempty(c->vc_keymap) ||
756 !isempty(c->vc_keymap_toggle);
760 char *new_keymap = NULL;
762 r = find_converted_keymap(c, &new_keymap);
766 r = find_legacy_keymap(c, &new_keymap);
771 if (!streq_ptr(c->vc_keymap, new_keymap)) {
772 free_and_replace(&c->vc_keymap, new_keymap);
773 free_and_replace(&c->vc_keymap_toggle, NULL);
780 r = vconsole_write_data(c);
782 log_error("Failed to set virtual console keymap: %s", strerror(-r));
784 sd_bus_emit_properties_changed(bus,
785 "/org/freedesktop/locale1",
786 "org.freedesktop.locale1",
787 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
789 return vconsole_reload(bus);
795 static int property_get_locale(
798 const char *interface,
799 const char *property,
800 sd_bus_message *reply,
802 sd_bus_error *error) {
804 Context *c = userdata;
805 _cleanup_strv_free_ char **l = NULL;
808 l = new0(char*, _LOCALE_MAX+1);
812 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
815 if (isempty(c->locale[p]))
818 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
824 return sd_bus_message_append_strv(reply, l);
827 static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
828 Context *c = userdata;
829 _cleanup_strv_free_ char **l = NULL;
832 bool modified = false;
833 bool passed[_LOCALE_MAX] = {};
837 r = bus_message_read_strv_extend(m, &l);
841 r = sd_bus_message_read_basic(m, 'b', &interactive);
845 /* Check whether a variable changed and if so valid */
849 for (p = 0; p < _LOCALE_MAX; p++) {
852 k = strlen(names[p]);
853 if (startswith(*i, names[p]) &&
855 locale_is_valid((*i) + k + 1)) {
859 if (!streq_ptr(*i + k + 1, c->locale[p]))
867 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
870 /* Check whether a variable is unset */
872 for (p = 0; p < _LOCALE_MAX; p++)
873 if (!isempty(c->locale[p]) && !passed[p]) {
880 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-locale", interactive, &c->polkit_registry, error);
884 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
887 for (p = 0; p < _LOCALE_MAX; p++) {
890 k = strlen(names[p]);
891 if (startswith(*i, names[p]) && (*i)[k] == '=') {
894 t = strdup(*i + k + 1);
905 for (p = 0; p < _LOCALE_MAX; p++) {
909 free_and_replace(&c->locale[p], NULL);
914 r = locale_write_data(c);
916 log_error("Failed to set locale: %s", strerror(-r));
917 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
920 locale_update_system_manager(c, bus);
922 log_info("Changed locale information.");
924 sd_bus_emit_properties_changed(bus,
925 "/org/freedesktop/locale1",
926 "org.freedesktop.locale1",
930 return sd_bus_reply_method_return(m, NULL);
933 static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
934 Context *c = userdata;
935 const char *keymap, *keymap_toggle;
936 int convert, interactive;
939 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
946 if (isempty(keymap_toggle))
947 keymap_toggle = NULL;
949 if (!streq_ptr(keymap, c->vc_keymap) ||
950 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
952 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
953 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
954 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
956 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
960 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
962 if (free_and_copy(&c->vc_keymap, keymap) < 0 ||
963 free_and_copy(&c->vc_keymap_toggle, keymap_toggle) < 0)
966 r = vconsole_write_data(c);
968 log_error("Failed to set virtual console keymap: %s", strerror(-r));
969 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
972 log_info("Changed virtual console keymap to '%s'", strempty(c->vc_keymap));
974 r = vconsole_reload(bus);
976 log_error("Failed to request keymap reload: %s", strerror(-r));
978 sd_bus_emit_properties_changed(bus,
979 "/org/freedesktop/locale1",
980 "org.freedesktop.locale1",
981 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
984 r = vconsole_convert_to_x11(c, bus);
986 log_error("Failed to convert keymap data: %s", strerror(-r));
990 return sd_bus_reply_method_return(m, NULL);
993 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
994 Context *c = userdata;
995 const char *layout, *model, *variant, *options;
996 int convert, interactive;
999 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
1003 if (isempty(layout))
1009 if (isempty(variant))
1012 if (isempty(options))
1015 if (!streq_ptr(layout, c->x11_layout) ||
1016 !streq_ptr(model, c->x11_model) ||
1017 !streq_ptr(variant, c->x11_variant) ||
1018 !streq_ptr(options, c->x11_options)) {
1020 if ((layout && !string_is_safe(layout)) ||
1021 (model && !string_is_safe(model)) ||
1022 (variant && !string_is_safe(variant)) ||
1023 (options && !string_is_safe(options)))
1024 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
1026 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
1030 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1032 if (free_and_copy(&c->x11_layout, layout) < 0 ||
1033 free_and_copy(&c->x11_model, model) < 0 ||
1034 free_and_copy(&c->x11_variant, variant) < 0 ||
1035 free_and_copy(&c->x11_options, options) < 0)
1038 r = write_data_x11(c);
1040 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1041 return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1044 log_info("Changed X11 keyboard layout to '%s'", strempty(c->x11_layout));
1046 sd_bus_emit_properties_changed(bus,
1047 "/org/freedesktop/locale1",
1048 "org.freedesktop.locale1",
1049 "X11Layout" "X11Model" "X11Variant" "X11Options", NULL);
1052 r = x11_convert_to_vconsole(c, bus);
1054 log_error("Failed to convert keymap data: %s", strerror(-r));
1058 return sd_bus_reply_method_return(m, NULL);
1061 static const sd_bus_vtable locale_vtable[] = {
1062 SD_BUS_VTABLE_START(0),
1063 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1064 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1065 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1066 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1067 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1068 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1069 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1070 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
1071 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1072 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1076 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
1077 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1084 r = sd_bus_default_system(&bus);
1086 log_error("Failed to get system bus connection: %s", strerror(-r));
1090 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
1092 log_error("Failed to register object: %s", strerror(-r));
1096 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
1098 log_error("Failed to register name: %s", strerror(-r));
1102 r = sd_bus_attach_event(bus, event, 0);
1104 log_error("Failed to attach bus to event loop: %s", strerror(-r));
1114 int main(int argc, char *argv[]) {
1115 Context context = {};
1116 _cleanup_event_unref_ sd_event *event = NULL;
1117 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1120 log_set_target(LOG_TARGET_AUTO);
1121 log_parse_environment();
1128 log_error("This program takes no arguments.");
1133 r = sd_event_default(&event);
1135 log_error("Failed to allocate event loop: %s", strerror(-r));
1139 sd_event_set_watchdog(event, true);
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, NULL, NULL);
1153 log_error("Failed to run event loop: %s", strerror(-r));
1158 context_free(&context);
1160 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;