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) {
211 _cleanup_fclose_ FILE *f;
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")) {
232 _cleanup_strv_free_ char **a = NULL;
234 r = strv_split_quoted(&a, 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]);
254 } else if (!in_section && first_word(l, "Section")) {
255 _cleanup_strv_free_ char **a = NULL;
257 r = strv_split_quoted(&a, l);
261 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
264 } else if (in_section && first_word(l, "EndSection"))
271 static int context_read_data(Context *c) {
274 r = locale_read_data(c);
275 q = vconsole_read_data(c);
276 p = x11_read_data(c);
278 return r < 0 ? r : q < 0 ? q : p;
281 static int locale_write_data(Context *c) {
283 _cleanup_strv_free_ char **l = NULL;
285 r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
286 if (r < 0 && r != -ENOENT)
289 for (p = 0; p < _LOCALE_MAX; p++) {
290 _cleanup_free_ char *t = NULL;
295 if (isempty(c->locale[p])) {
296 l = strv_env_unset(l, names[p]);
300 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
303 u = strv_env_set(l, t);
311 if (strv_isempty(l)) {
312 if (unlink("/etc/locale.conf") < 0)
313 return errno == ENOENT ? 0 : -errno;
318 return write_env_file_label("/etc/locale.conf", l);
321 static int locale_update_system_manager(Context *c, sd_bus *bus) {
322 _cleanup_free_ char **l_unset = NULL;
323 _cleanup_strv_free_ char **l_set = NULL;
324 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
325 sd_bus_error error = SD_BUS_ERROR_NULL;
326 unsigned c_set, c_unset, p;
331 l_unset = new0(char*, _LOCALE_MAX);
335 l_set = new0(char*, _LOCALE_MAX);
339 for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
342 if (isempty(c->locale[p]))
343 l_unset[c_set++] = (char*) names[p];
347 if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
350 l_set[c_unset++] = s;
354 assert(c_set + c_unset == _LOCALE_MAX);
355 r = sd_bus_message_new_method_call(bus, &m,
356 "org.freedesktop.systemd1",
357 "/org/freedesktop/systemd1",
358 "org.freedesktop.systemd1.Manager",
359 "UnsetAndSetEnvironment");
363 r = sd_bus_message_append_strv(m, l_unset);
367 r = sd_bus_message_append_strv(m, l_set);
371 r = sd_bus_call(bus, m, 0, &error, NULL);
373 log_error("Failed to update the manager environment: %s", strerror(-r));
378 static int vconsole_write_data(Context *c) {
380 _cleanup_strv_free_ char **l = NULL;
382 r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
383 if (r < 0 && r != -ENOENT)
386 if (isempty(c->vc_keymap))
387 l = strv_env_unset(l, "KEYMAP");
389 _cleanup_free_ char *s = NULL;
392 s = strappend("KEYMAP=", c->vc_keymap);
396 u = strv_env_set(l, s);
404 if (isempty(c->vc_keymap_toggle))
405 l = strv_env_unset(l, "KEYMAP_TOGGLE");
407 _cleanup_free_ char *s = NULL;
410 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
414 u = strv_env_set(l, s);
422 if (strv_isempty(l)) {
423 if (unlink("/etc/vconsole.conf") < 0)
424 return errno == ENOENT ? 0 : -errno;
429 return write_env_file_label("/etc/vconsole.conf", l);
432 static int write_data_x11(Context *c) {
433 _cleanup_fclose_ FILE *f = NULL;
434 _cleanup_free_ char *temp_path = NULL;
437 if (isempty(c->x11_layout) &&
438 isempty(c->x11_model) &&
439 isempty(c->x11_variant) &&
440 isempty(c->x11_options)) {
442 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
443 return errno == ENOENT ? 0 : -errno;
448 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
450 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
454 fchmod(fileno(f), 0644);
456 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
457 "# manually too freely.\n"
458 "Section \"InputClass\"\n"
459 " Identifier \"system-keyboard\"\n"
460 " MatchIsKeyboard \"on\"\n", f);
462 if (!isempty(c->x11_layout))
463 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
465 if (!isempty(c->x11_model))
466 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
468 if (!isempty(c->x11_variant))
469 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
471 if (!isempty(c->x11_options))
472 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
474 fputs("EndSection\n", f);
477 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
479 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
486 static int vconsole_reload(sd_bus *bus) {
487 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
492 r = sd_bus_call_method(bus,
493 "org.freedesktop.systemd1",
494 "/org/freedesktop/systemd1",
495 "org.freedesktop.systemd1.Manager",
499 "ss", "systemd-vconsole-setup.service", "replace");
502 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
506 static char *strnulldash(const char *s) {
507 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
510 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
521 if (!fgets(line, sizeof(line), f)) {
524 return errno ? -errno : -EIO;
532 if (l[0] == 0 || l[0] == '#')
535 r = strv_split_quoted(&b, l);
539 if (strv_length(b) < 5) {
540 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
551 static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
552 bool modified = false;
556 if (isempty(c->vc_keymap)) {
559 !isempty(c->x11_layout) ||
560 !isempty(c->x11_model) ||
561 !isempty(c->x11_variant) ||
562 !isempty(c->x11_options);
566 _cleanup_fclose_ FILE *f = NULL;
569 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
574 _cleanup_strv_free_ char **a = NULL;
577 r = read_next_mapping(f, &n, &a);
583 if (!streq(c->vc_keymap, a[0]))
586 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
587 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
588 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
589 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
591 if (free_and_copy(&c->x11_layout, strnulldash(a[1])) < 0 ||
592 free_and_copy(&c->x11_model, strnulldash(a[2])) < 0 ||
593 free_and_copy(&c->x11_variant, strnulldash(a[3])) < 0 ||
594 free_and_copy(&c->x11_options, strnulldash(a[4])) < 0)
607 r = write_data_x11(c);
609 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
611 sd_bus_emit_properties_changed(bus,
612 "/org/freedesktop/locale1",
613 "org.freedesktop.locale1",
614 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
620 static int find_converted_keymap(Context *c, char **new_keymap) {
622 _cleanup_free_ char *n;
625 n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
627 n = strdup(c->x11_layout);
631 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
632 _cleanup_free_ char *p = NULL, *pz = NULL;
634 p = strjoin(dir, "xkb/", n, ".map", NULL);
635 pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
639 if (access(p, F_OK) == 0 || access(pz, F_OK) == 0) {
649 static int find_legacy_keymap(Context *c, char **new_keymap) {
650 _cleanup_fclose_ FILE *f;
652 unsigned best_matching = 0;
655 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
660 _cleanup_strv_free_ char **a = NULL;
661 unsigned matching = 0;
664 r = read_next_mapping(f, &n, &a);
670 /* Determine how well matching this entry is */
671 if (streq_ptr(c->x11_layout, a[1]))
672 /* If we got an exact match, this is best */
677 x = strcspn(c->x11_layout, ",");
679 /* We have multiple X layouts, look for an
680 * entry that matches our key with everything
681 * but the first layout stripped off. */
684 strneq(c->x11_layout, a[1], x))
689 /* If that didn't work, strip off the
690 * other layouts from the entry, too */
691 w = strcspn(a[1], ",");
693 if (x > 0 && x == w &&
694 memcmp(c->x11_layout, a[1], x) == 0)
700 if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
703 if (streq_ptr(c->x11_variant, a[3])) {
706 if (streq_ptr(c->x11_options, a[4]))
712 /* The best matching entry so far, then let's save that */
713 if (matching > best_matching) {
714 best_matching = matching;
717 *new_keymap = strdup(a[0]);
726 static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
727 bool modified = false;
732 if (isempty(c->x11_layout)) {
735 !isempty(c->vc_keymap) ||
736 !isempty(c->vc_keymap_toggle);
740 char *new_keymap = NULL;
742 r = find_converted_keymap(c, &new_keymap);
746 r = find_legacy_keymap(c, &new_keymap);
751 if (!streq_ptr(c->vc_keymap, new_keymap)) {
752 free_and_replace(&c->vc_keymap, new_keymap);
753 free_and_replace(&c->vc_keymap_toggle, NULL);
760 r = vconsole_write_data(c);
762 log_error("Failed to set virtual console keymap: %s", strerror(-r));
764 sd_bus_emit_properties_changed(bus,
765 "/org/freedesktop/locale1",
766 "org.freedesktop.locale1",
767 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
769 return vconsole_reload(bus);
775 static int property_get_locale(
778 const char *interface,
779 const char *property,
780 sd_bus_message *reply,
782 sd_bus_error *error) {
784 Context *c = userdata;
785 _cleanup_strv_free_ char **l = NULL;
788 l = new0(char*, _LOCALE_MAX+1);
792 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
795 if (isempty(c->locale[p]))
798 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
804 return sd_bus_message_append_strv(reply, l);
807 static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
808 Context *c = userdata;
809 _cleanup_strv_free_ char **l = NULL;
812 bool modified = false;
813 bool passed[_LOCALE_MAX] = {};
817 r = bus_message_read_strv_extend(m, &l);
821 r = sd_bus_message_read_basic(m, 'b', &interactive);
825 /* Check whether a variable changed and if so valid */
829 for (p = 0; p < _LOCALE_MAX; p++) {
832 k = strlen(names[p]);
833 if (startswith(*i, names[p]) &&
835 locale_is_valid((*i) + k + 1)) {
839 if (!streq_ptr(*i + k + 1, c->locale[p]))
847 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
850 /* Check whether a variable is unset */
852 for (p = 0; p < _LOCALE_MAX; p++)
853 if (!isempty(c->locale[p]) && !passed[p]) {
859 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-locale", interactive, &c->polkit_registry, error);
863 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
866 for (p = 0; p < _LOCALE_MAX; p++) {
869 k = strlen(names[p]);
870 if (startswith(*i, names[p]) && (*i)[k] == '=') {
873 t = strdup(*i + k + 1);
883 for (p = 0; p < _LOCALE_MAX; p++) {
887 free_and_replace(&c->locale[p], NULL);
892 r = locale_write_data(c);
894 log_error("Failed to set locale: %s", strerror(-r));
895 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
898 locale_update_system_manager(c, bus);
900 log_info("Changed locale information.");
902 sd_bus_emit_properties_changed(bus,
903 "/org/freedesktop/locale1",
904 "org.freedesktop.locale1",
908 return sd_bus_reply_method_return(m, NULL);
911 static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
912 Context *c = userdata;
913 const char *keymap, *keymap_toggle;
914 int convert, interactive;
917 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
924 if (isempty(keymap_toggle))
925 keymap_toggle = NULL;
927 if (!streq_ptr(keymap, c->vc_keymap) ||
928 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
930 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
931 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
932 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
934 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
938 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
940 if (free_and_copy(&c->vc_keymap, keymap) < 0 ||
941 free_and_copy(&c->vc_keymap_toggle, keymap_toggle) < 0)
944 r = vconsole_write_data(c);
946 log_error("Failed to set virtual console keymap: %s", strerror(-r));
947 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
950 log_info("Changed virtual console keymap to '%s'", strempty(c->vc_keymap));
952 r = vconsole_reload(bus);
954 log_error("Failed to request keymap reload: %s", strerror(-r));
956 sd_bus_emit_properties_changed(bus,
957 "/org/freedesktop/locale1",
958 "org.freedesktop.locale1",
959 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
962 r = vconsole_convert_to_x11(c, bus);
964 log_error("Failed to convert keymap data: %s", strerror(-r));
968 return sd_bus_reply_method_return(m, NULL);
971 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
972 Context *c = userdata;
973 const char *layout, *model, *variant, *options;
974 int convert, interactive;
977 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
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_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
1004 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
1008 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1010 if (free_and_copy(&c->x11_layout, layout) < 0 ||
1011 free_and_copy(&c->x11_model, model) < 0 ||
1012 free_and_copy(&c->x11_variant, variant) < 0 ||
1013 free_and_copy(&c->x11_options, options) < 0)
1016 r = write_data_x11(c);
1018 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1019 return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1022 log_info("Changed X11 keyboard layout to '%s'", strempty(c->x11_layout));
1024 sd_bus_emit_properties_changed(bus,
1025 "/org/freedesktop/locale1",
1026 "org.freedesktop.locale1",
1027 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
1030 r = x11_convert_to_vconsole(c, bus);
1032 log_error("Failed to convert keymap data: %s", strerror(-r));
1036 return sd_bus_reply_method_return(m, NULL);
1039 static const sd_bus_vtable locale_vtable[] = {
1040 SD_BUS_VTABLE_START(0),
1041 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1042 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1043 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1044 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1045 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1046 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1047 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1048 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
1049 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1050 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1054 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
1055 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1062 r = sd_bus_default_system(&bus);
1064 log_error("Failed to get system bus connection: %s", strerror(-r));
1068 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
1070 log_error("Failed to register object: %s", strerror(-r));
1074 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
1076 log_error("Failed to register name: %s", strerror(-r));
1080 r = sd_bus_attach_event(bus, event, 0);
1082 log_error("Failed to attach bus to event loop: %s", strerror(-r));
1092 int main(int argc, char *argv[]) {
1093 _cleanup_(context_free) Context context = {};
1094 _cleanup_event_unref_ sd_event *event = NULL;
1095 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1098 log_set_target(LOG_TARGET_AUTO);
1099 log_parse_environment();
1106 log_error("This program takes no arguments.");
1111 r = sd_event_default(&event);
1113 log_error("Failed to allocate event loop: %s", strerror(-r));
1117 sd_event_set_watchdog(event, true);
1119 r = connect_bus(&context, event, &bus);
1123 r = context_read_data(&context);
1125 log_error("Failed to read locale data: %s", strerror(-r));
1129 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
1131 log_error("Failed to run event loop: %s", strerror(-r));
1136 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;