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 const char* nonempty(const char *s) {
96 return isempty(s) ? NULL : s;
99 static void free_and_replace(char **s, char *v) {
104 static void context_free_x11(Context *c) {
105 free_and_replace(&c->x11_layout, NULL);
106 free_and_replace(&c->x11_model, NULL);
107 free_and_replace(&c->x11_variant, NULL);
108 free_and_replace(&c->x11_options, NULL);
111 static void context_free_vconsole(Context *c) {
112 free_and_replace(&c->vc_keymap, NULL);
113 free_and_replace(&c->vc_keymap_toggle, NULL);
116 static void context_free_locale(Context *c) {
119 for (p = 0; p < _LOCALE_MAX; p++)
120 free_and_replace(&c->locale[p], NULL);
123 static void context_free(Context *c) {
124 context_free_locale(c);
126 context_free_vconsole(c);
128 bus_verify_polkit_async_registry_free(c->polkit_registry);
131 static void locale_simplify(Context *c) {
134 for (p = LOCALE_LANG+1; p < _LOCALE_MAX; p++)
135 if (isempty(c->locale[p]) || streq_ptr(c->locale[LOCALE_LANG], c->locale[p]))
136 free_and_replace(&c->locale[p], NULL);
139 static int locale_read_data(Context *c) {
142 context_free_locale(c);
144 r = parse_env_file("/etc/locale.conf", NEWLINE,
145 "LANG", &c->locale[LOCALE_LANG],
146 "LANGUAGE", &c->locale[LOCALE_LANGUAGE],
147 "LC_CTYPE", &c->locale[LOCALE_LC_CTYPE],
148 "LC_NUMERIC", &c->locale[LOCALE_LC_NUMERIC],
149 "LC_TIME", &c->locale[LOCALE_LC_TIME],
150 "LC_COLLATE", &c->locale[LOCALE_LC_COLLATE],
151 "LC_MONETARY", &c->locale[LOCALE_LC_MONETARY],
152 "LC_MESSAGES", &c->locale[LOCALE_LC_MESSAGES],
153 "LC_PAPER", &c->locale[LOCALE_LC_PAPER],
154 "LC_NAME", &c->locale[LOCALE_LC_NAME],
155 "LC_ADDRESS", &c->locale[LOCALE_LC_ADDRESS],
156 "LC_TELEPHONE", &c->locale[LOCALE_LC_TELEPHONE],
157 "LC_MEASUREMENT", &c->locale[LOCALE_LC_MEASUREMENT],
158 "LC_IDENTIFICATION", &c->locale[LOCALE_LC_IDENTIFICATION],
164 /* Fill in what we got passed from systemd. */
165 for (p = 0; p < _LOCALE_MAX; p++) {
168 r = free_and_strdup(&c->locale[p],
169 nonempty(getenv(names[p])));
181 static int vconsole_read_data(Context *c) {
184 context_free_vconsole(c);
186 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
187 "KEYMAP", &c->vc_keymap,
188 "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
191 if (r < 0 && r != -ENOENT)
197 static int x11_read_data(Context *c) {
198 _cleanup_fclose_ FILE *f;
200 bool in_section = false;
205 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
207 return errno == ENOENT ? 0 : -errno;
209 while (fgets(line, sizeof(line), f)) {
215 if (l[0] == 0 || l[0] == '#')
218 if (in_section && first_word(l, "Option")) {
219 _cleanup_strv_free_ char **a = NULL;
221 r = strv_split_quoted(&a, l);
225 if (strv_length(a) == 3) {
226 if (streq(a[1], "XkbLayout")) {
227 free_and_replace(&c->x11_layout, a[2]);
229 } else if (streq(a[1], "XkbModel")) {
230 free_and_replace(&c->x11_model, a[2]);
232 } else if (streq(a[1], "XkbVariant")) {
233 free_and_replace(&c->x11_variant, a[2]);
235 } else if (streq(a[1], "XkbOptions")) {
236 free_and_replace(&c->x11_options, a[2]);
241 } else if (!in_section && first_word(l, "Section")) {
242 _cleanup_strv_free_ char **a = NULL;
244 r = strv_split_quoted(&a, l);
248 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
251 } else if (in_section && first_word(l, "EndSection"))
258 static int context_read_data(Context *c) {
261 r = locale_read_data(c);
262 q = vconsole_read_data(c);
263 p = x11_read_data(c);
265 return r < 0 ? r : q < 0 ? q : p;
268 static int locale_write_data(Context *c) {
270 _cleanup_strv_free_ char **l = NULL;
272 r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
273 if (r < 0 && r != -ENOENT)
276 for (p = 0; p < _LOCALE_MAX; p++) {
277 _cleanup_free_ char *t = NULL;
282 if (isempty(c->locale[p])) {
283 l = strv_env_unset(l, names[p]);
287 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
290 u = strv_env_set(l, t);
298 if (strv_isempty(l)) {
299 if (unlink("/etc/locale.conf") < 0)
300 return errno == ENOENT ? 0 : -errno;
305 return write_env_file_label("/etc/locale.conf", l);
308 static int locale_update_system_manager(Context *c, sd_bus *bus) {
309 _cleanup_free_ char **l_unset = NULL;
310 _cleanup_strv_free_ char **l_set = NULL;
311 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
312 sd_bus_error error = SD_BUS_ERROR_NULL;
313 unsigned c_set, c_unset, p;
318 l_unset = new0(char*, _LOCALE_MAX);
322 l_set = new0(char*, _LOCALE_MAX);
326 for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
329 if (isempty(c->locale[p]))
330 l_unset[c_set++] = (char*) names[p];
334 if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
337 l_set[c_unset++] = s;
341 assert(c_set + c_unset == _LOCALE_MAX);
342 r = sd_bus_message_new_method_call(bus, &m,
343 "org.freedesktop.systemd1",
344 "/org/freedesktop/systemd1",
345 "org.freedesktop.systemd1.Manager",
346 "UnsetAndSetEnvironment");
350 r = sd_bus_message_append_strv(m, l_unset);
354 r = sd_bus_message_append_strv(m, l_set);
358 r = sd_bus_call(bus, m, 0, &error, NULL);
360 log_error("Failed to update the manager environment: %s", strerror(-r));
365 static int vconsole_write_data(Context *c) {
367 _cleanup_strv_free_ char **l = NULL;
369 r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
370 if (r < 0 && r != -ENOENT)
373 if (isempty(c->vc_keymap))
374 l = strv_env_unset(l, "KEYMAP");
376 _cleanup_free_ char *s = NULL;
379 s = strappend("KEYMAP=", c->vc_keymap);
383 u = strv_env_set(l, s);
391 if (isempty(c->vc_keymap_toggle))
392 l = strv_env_unset(l, "KEYMAP_TOGGLE");
394 _cleanup_free_ char *s = NULL;
397 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
401 u = strv_env_set(l, s);
409 if (strv_isempty(l)) {
410 if (unlink("/etc/vconsole.conf") < 0)
411 return errno == ENOENT ? 0 : -errno;
416 return write_env_file_label("/etc/vconsole.conf", l);
419 static int write_data_x11(Context *c) {
420 _cleanup_fclose_ FILE *f = NULL;
421 _cleanup_free_ char *temp_path = NULL;
424 if (isempty(c->x11_layout) &&
425 isempty(c->x11_model) &&
426 isempty(c->x11_variant) &&
427 isempty(c->x11_options)) {
429 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
430 return errno == ENOENT ? 0 : -errno;
435 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
437 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
441 fchmod(fileno(f), 0644);
443 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
444 "# manually too freely.\n"
445 "Section \"InputClass\"\n"
446 " Identifier \"system-keyboard\"\n"
447 " MatchIsKeyboard \"on\"\n", f);
449 if (!isempty(c->x11_layout))
450 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
452 if (!isempty(c->x11_model))
453 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
455 if (!isempty(c->x11_variant))
456 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
458 if (!isempty(c->x11_options))
459 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
461 fputs("EndSection\n", f);
464 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
466 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
473 static int vconsole_reload(sd_bus *bus) {
474 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
479 r = sd_bus_call_method(bus,
480 "org.freedesktop.systemd1",
481 "/org/freedesktop/systemd1",
482 "org.freedesktop.systemd1.Manager",
486 "ss", "systemd-vconsole-setup.service", "replace");
489 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
493 static const char* strnulldash(const char *s) {
494 return isempty(s) || streq(s, "-") ? NULL : s;
497 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
508 if (!fgets(line, sizeof(line), f)) {
511 return errno ? -errno : -EIO;
519 if (l[0] == 0 || l[0] == '#')
522 r = strv_split_quoted(&b, l);
526 if (strv_length(b) < 5) {
527 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
538 static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
539 bool modified = false;
543 if (isempty(c->vc_keymap)) {
546 !isempty(c->x11_layout) ||
547 !isempty(c->x11_model) ||
548 !isempty(c->x11_variant) ||
549 !isempty(c->x11_options);
553 _cleanup_fclose_ FILE *f = NULL;
556 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
561 _cleanup_strv_free_ char **a = NULL;
564 r = read_next_mapping(f, &n, &a);
570 if (!streq(c->vc_keymap, a[0]))
573 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
574 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
575 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
576 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
578 if (free_and_strdup(&c->x11_layout, strnulldash(a[1])) < 0 ||
579 free_and_strdup(&c->x11_model, strnulldash(a[2])) < 0 ||
580 free_and_strdup(&c->x11_variant, strnulldash(a[3])) < 0 ||
581 free_and_strdup(&c->x11_options, strnulldash(a[4])) < 0)
594 r = write_data_x11(c);
596 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
598 sd_bus_emit_properties_changed(bus,
599 "/org/freedesktop/locale1",
600 "org.freedesktop.locale1",
601 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
607 static int find_converted_keymap(Context *c, char **new_keymap) {
609 _cleanup_free_ char *n;
612 n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
614 n = strdup(c->x11_layout);
618 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
619 _cleanup_free_ char *p = NULL, *pz = NULL;
621 p = strjoin(dir, "xkb/", n, ".map", NULL);
622 pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
626 if (access(p, F_OK) == 0 || access(pz, F_OK) == 0) {
636 static int find_legacy_keymap(Context *c, char **new_keymap) {
637 _cleanup_fclose_ FILE *f;
639 unsigned best_matching = 0;
642 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
647 _cleanup_strv_free_ char **a = NULL;
648 unsigned matching = 0;
651 r = read_next_mapping(f, &n, &a);
657 /* Determine how well matching this entry is */
658 if (streq_ptr(c->x11_layout, a[1]))
659 /* If we got an exact match, this is best */
664 x = strcspn(c->x11_layout, ",");
666 /* We have multiple X layouts, look for an
667 * entry that matches our key with everything
668 * but the first layout stripped off. */
671 strneq(c->x11_layout, a[1], x))
676 /* If that didn't work, strip off the
677 * other layouts from the entry, too */
678 w = strcspn(a[1], ",");
680 if (x > 0 && x == w &&
681 memcmp(c->x11_layout, a[1], x) == 0)
687 if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
690 if (streq_ptr(c->x11_variant, a[3])) {
693 if (streq_ptr(c->x11_options, a[4]))
699 /* The best matching entry so far, then let's save that */
700 if (matching > best_matching) {
701 best_matching = matching;
703 r = free_and_strdup(new_keymap, a[0]);
712 static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
713 bool modified = false;
718 if (isempty(c->x11_layout)) {
721 !isempty(c->vc_keymap) ||
722 !isempty(c->vc_keymap_toggle);
726 char *new_keymap = NULL;
728 r = find_converted_keymap(c, &new_keymap);
732 r = find_legacy_keymap(c, &new_keymap);
737 if (!streq_ptr(c->vc_keymap, new_keymap)) {
738 free_and_replace(&c->vc_keymap, new_keymap);
739 free_and_replace(&c->vc_keymap_toggle, NULL);
746 r = vconsole_write_data(c);
748 log_error("Failed to set virtual console keymap: %s", strerror(-r));
750 sd_bus_emit_properties_changed(bus,
751 "/org/freedesktop/locale1",
752 "org.freedesktop.locale1",
753 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
755 return vconsole_reload(bus);
761 static int property_get_locale(
764 const char *interface,
765 const char *property,
766 sd_bus_message *reply,
768 sd_bus_error *error) {
770 Context *c = userdata;
771 _cleanup_strv_free_ char **l = NULL;
774 l = new0(char*, _LOCALE_MAX+1);
778 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
781 if (isempty(c->locale[p]))
784 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
790 return sd_bus_message_append_strv(reply, l);
793 static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
794 Context *c = userdata;
795 _cleanup_strv_free_ char **l = NULL;
798 bool modified = false;
799 bool passed[_LOCALE_MAX] = {};
803 r = bus_message_read_strv_extend(m, &l);
807 r = sd_bus_message_read_basic(m, 'b', &interactive);
811 /* Check whether a variable changed and if so valid */
815 for (p = 0; p < _LOCALE_MAX; p++) {
818 k = strlen(names[p]);
819 if (startswith(*i, names[p]) &&
821 locale_is_valid((*i) + k + 1)) {
825 if (!streq_ptr(*i + k + 1, c->locale[p]))
833 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
836 /* Check whether a variable is unset */
838 for (p = 0; p < _LOCALE_MAX; p++)
839 if (!isempty(c->locale[p]) && !passed[p]) {
845 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-locale", interactive, &c->polkit_registry, error);
849 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
852 for (p = 0; p < _LOCALE_MAX; p++) {
855 k = strlen(names[p]);
856 if (startswith(*i, names[p]) && (*i)[k] == '=') {
857 r = free_and_strdup(&c->locale[p], *i + k + 1);
864 for (p = 0; p < _LOCALE_MAX; p++) {
868 free_and_replace(&c->locale[p], NULL);
873 r = locale_write_data(c);
875 log_error("Failed to set locale: %s", strerror(-r));
876 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
879 locale_update_system_manager(c, bus);
881 log_info("Changed locale information.");
883 sd_bus_emit_properties_changed(bus,
884 "/org/freedesktop/locale1",
885 "org.freedesktop.locale1",
889 return sd_bus_reply_method_return(m, NULL);
892 static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
893 Context *c = userdata;
894 const char *keymap, *keymap_toggle;
895 int convert, interactive;
898 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
905 if (isempty(keymap_toggle))
906 keymap_toggle = NULL;
908 if (!streq_ptr(keymap, c->vc_keymap) ||
909 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
911 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
912 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
913 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
915 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
919 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
921 if (free_and_strdup(&c->vc_keymap, keymap) < 0 ||
922 free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0)
925 r = vconsole_write_data(c);
927 log_error("Failed to set virtual console keymap: %s", strerror(-r));
928 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
931 log_info("Changed virtual console keymap to '%s'", strempty(c->vc_keymap));
933 r = vconsole_reload(bus);
935 log_error("Failed to request keymap reload: %s", strerror(-r));
937 sd_bus_emit_properties_changed(bus,
938 "/org/freedesktop/locale1",
939 "org.freedesktop.locale1",
940 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
943 r = vconsole_convert_to_x11(c, bus);
945 log_error("Failed to convert keymap data: %s", strerror(-r));
949 return sd_bus_reply_method_return(m, NULL);
952 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
953 Context *c = userdata;
954 const char *layout, *model, *variant, *options;
955 int convert, interactive;
958 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
968 if (isempty(variant))
971 if (isempty(options))
974 if (!streq_ptr(layout, c->x11_layout) ||
975 !streq_ptr(model, c->x11_model) ||
976 !streq_ptr(variant, c->x11_variant) ||
977 !streq_ptr(options, c->x11_options)) {
979 if ((layout && !string_is_safe(layout)) ||
980 (model && !string_is_safe(model)) ||
981 (variant && !string_is_safe(variant)) ||
982 (options && !string_is_safe(options)))
983 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
985 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
989 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
991 if (free_and_strdup(&c->x11_layout, layout) < 0 ||
992 free_and_strdup(&c->x11_model, model) < 0 ||
993 free_and_strdup(&c->x11_variant, variant) < 0 ||
994 free_and_strdup(&c->x11_options, options) < 0)
997 r = write_data_x11(c);
999 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1000 return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1003 log_info("Changed X11 keyboard layout to '%s'", strempty(c->x11_layout));
1005 sd_bus_emit_properties_changed(bus,
1006 "/org/freedesktop/locale1",
1007 "org.freedesktop.locale1",
1008 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
1011 r = x11_convert_to_vconsole(c, bus);
1013 log_error("Failed to convert keymap data: %s", strerror(-r));
1017 return sd_bus_reply_method_return(m, NULL);
1020 static const sd_bus_vtable locale_vtable[] = {
1021 SD_BUS_VTABLE_START(0),
1022 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1023 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1024 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1025 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1026 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1027 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1028 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1029 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
1030 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1031 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1035 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
1036 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1043 r = sd_bus_default_system(&bus);
1045 log_error("Failed to get system bus connection: %s", strerror(-r));
1049 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
1051 log_error("Failed to register object: %s", strerror(-r));
1055 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
1057 log_error("Failed to register name: %s", strerror(-r));
1061 r = sd_bus_attach_event(bus, event, 0);
1063 log_error("Failed to attach bus to event loop: %s", strerror(-r));
1073 int main(int argc, char *argv[]) {
1074 _cleanup_(context_free) Context context = {};
1075 _cleanup_event_unref_ sd_event *event = NULL;
1076 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1079 log_set_target(LOG_TARGET_AUTO);
1080 log_parse_environment();
1087 log_error("This program takes no arguments.");
1092 r = sd_event_default(&event);
1094 log_error("Failed to allocate event loop: %s", strerror(-r));
1098 sd_event_set_watchdog(event, true);
1100 r = connect_bus(&context, event, &bus);
1104 r = context_read_data(&context);
1106 log_error("Failed to read locale data: %s", strerror(-r));
1110 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
1112 log_error("Failed to run event loop: %s", strerror(-r));
1117 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;