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, char ***settings) {
270 _cleanup_strv_free_ char **l = NULL;
272 /* Set values will be returned as strv in *settings on success. */
274 r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
275 if (r < 0 && r != -ENOENT)
278 for (p = 0; p < _LOCALE_MAX; p++) {
279 _cleanup_free_ char *t = NULL;
284 if (isempty(c->locale[p])) {
285 l = strv_env_unset(l, names[p]);
289 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
292 u = strv_env_set(l, t);
300 if (strv_isempty(l)) {
301 if (unlink("/etc/locale.conf") < 0)
302 return errno == ENOENT ? 0 : -errno;
307 r = write_env_file_label("/etc/locale.conf", l);
316 static int locale_update_system_manager(Context *c, sd_bus *bus) {
317 _cleanup_free_ char **l_unset = NULL;
318 _cleanup_strv_free_ char **l_set = NULL;
319 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
320 sd_bus_error error = SD_BUS_ERROR_NULL;
321 unsigned c_set, c_unset, p;
326 l_unset = new0(char*, _LOCALE_MAX);
330 l_set = new0(char*, _LOCALE_MAX);
334 for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
337 if (isempty(c->locale[p]))
338 l_unset[c_set++] = (char*) names[p];
342 if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
345 l_set[c_unset++] = s;
349 assert(c_set + c_unset == _LOCALE_MAX);
350 r = sd_bus_message_new_method_call(bus, &m,
351 "org.freedesktop.systemd1",
352 "/org/freedesktop/systemd1",
353 "org.freedesktop.systemd1.Manager",
354 "UnsetAndSetEnvironment");
358 r = sd_bus_message_append_strv(m, l_unset);
362 r = sd_bus_message_append_strv(m, l_set);
366 r = sd_bus_call(bus, m, 0, &error, NULL);
368 log_error("Failed to update the manager environment: %s", strerror(-r));
373 static int vconsole_write_data(Context *c) {
375 _cleanup_strv_free_ char **l = NULL;
377 r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
378 if (r < 0 && r != -ENOENT)
381 if (isempty(c->vc_keymap))
382 l = strv_env_unset(l, "KEYMAP");
384 _cleanup_free_ char *s = NULL;
387 s = strappend("KEYMAP=", c->vc_keymap);
391 u = strv_env_set(l, s);
399 if (isempty(c->vc_keymap_toggle))
400 l = strv_env_unset(l, "KEYMAP_TOGGLE");
402 _cleanup_free_ char *s = NULL;
405 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
409 u = strv_env_set(l, s);
417 if (strv_isempty(l)) {
418 if (unlink("/etc/vconsole.conf") < 0)
419 return errno == ENOENT ? 0 : -errno;
424 return write_env_file_label("/etc/vconsole.conf", l);
427 static int write_data_x11(Context *c) {
428 _cleanup_fclose_ FILE *f = NULL;
429 _cleanup_free_ char *temp_path = NULL;
432 if (isempty(c->x11_layout) &&
433 isempty(c->x11_model) &&
434 isempty(c->x11_variant) &&
435 isempty(c->x11_options)) {
437 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
438 return errno == ENOENT ? 0 : -errno;
443 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
445 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
449 fchmod(fileno(f), 0644);
451 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
452 "# manually too freely.\n"
453 "Section \"InputClass\"\n"
454 " Identifier \"system-keyboard\"\n"
455 " MatchIsKeyboard \"on\"\n", f);
457 if (!isempty(c->x11_layout))
458 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
460 if (!isempty(c->x11_model))
461 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
463 if (!isempty(c->x11_variant))
464 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
466 if (!isempty(c->x11_options))
467 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
469 fputs("EndSection\n", f);
472 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
474 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
481 static int vconsole_reload(sd_bus *bus) {
482 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
487 r = sd_bus_call_method(bus,
488 "org.freedesktop.systemd1",
489 "/org/freedesktop/systemd1",
490 "org.freedesktop.systemd1.Manager",
494 "ss", "systemd-vconsole-setup.service", "replace");
497 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
501 static const char* strnulldash(const char *s) {
502 return isempty(s) || streq(s, "-") ? NULL : s;
505 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
516 if (!fgets(line, sizeof(line), f)) {
519 return errno ? -errno : -EIO;
527 if (l[0] == 0 || l[0] == '#')
530 r = strv_split_quoted(&b, l);
534 if (strv_length(b) < 5) {
535 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
546 static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
547 bool modified = false;
551 if (isempty(c->vc_keymap)) {
554 !isempty(c->x11_layout) ||
555 !isempty(c->x11_model) ||
556 !isempty(c->x11_variant) ||
557 !isempty(c->x11_options);
561 _cleanup_fclose_ FILE *f = NULL;
564 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
569 _cleanup_strv_free_ char **a = NULL;
572 r = read_next_mapping(f, &n, &a);
578 if (!streq(c->vc_keymap, a[0]))
581 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
582 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
583 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
584 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
586 if (free_and_strdup(&c->x11_layout, strnulldash(a[1])) < 0 ||
587 free_and_strdup(&c->x11_model, strnulldash(a[2])) < 0 ||
588 free_and_strdup(&c->x11_variant, strnulldash(a[3])) < 0 ||
589 free_and_strdup(&c->x11_options, strnulldash(a[4])) < 0)
602 r = write_data_x11(c);
604 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
606 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
607 strempty(c->x11_layout),
608 strempty(c->x11_model),
609 strempty(c->x11_variant),
610 strempty(c->x11_options));
612 sd_bus_emit_properties_changed(bus,
613 "/org/freedesktop/locale1",
614 "org.freedesktop.locale1",
615 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
617 log_debug("X11 keyboard layout was not modified.");
622 static int find_converted_keymap(Context *c, char **new_keymap) {
624 _cleanup_free_ char *n;
627 n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
629 n = strdup(c->x11_layout);
633 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
634 _cleanup_free_ char *p = NULL, *pz = NULL;
637 p = strjoin(dir, "xkb/", n, ".map", NULL);
638 pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
642 uncompressed = access(p, F_OK) == 0;
643 if (uncompressed || access(pz, F_OK) == 0) {
644 log_debug("Found converted keymap %s at %s",
645 n, uncompressed ? p : pz);
656 static int find_legacy_keymap(Context *c, char **new_keymap) {
657 _cleanup_fclose_ FILE *f;
659 unsigned best_matching = 0;
662 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
667 _cleanup_strv_free_ char **a = NULL;
668 unsigned matching = 0;
671 r = read_next_mapping(f, &n, &a);
677 /* Determine how well matching this entry is */
678 if (streq_ptr(c->x11_layout, a[1]))
679 /* If we got an exact match, this is best */
684 x = strcspn(c->x11_layout, ",");
686 /* We have multiple X layouts, look for an
687 * entry that matches our key with everything
688 * but the first layout stripped off. */
691 strneq(c->x11_layout, a[1], x))
696 /* If that didn't work, strip off the
697 * other layouts from the entry, too */
698 w = strcspn(a[1], ",");
700 if (x > 0 && x == w &&
701 memcmp(c->x11_layout, a[1], x) == 0)
707 if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
710 if (streq_ptr(c->x11_variant, a[3])) {
713 if (streq_ptr(c->x11_options, a[4]))
719 /* The best matching entry so far, then let's save that */
720 if (matching >= MAX(best_matching, 1u)) {
721 log_debug("Found legacy keymap %s with score %u",
724 if (matching > best_matching) {
725 best_matching = matching;
727 r = free_and_strdup(new_keymap, a[0]);
737 static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
738 bool modified = false;
743 if (isempty(c->x11_layout)) {
746 !isempty(c->vc_keymap) ||
747 !isempty(c->vc_keymap_toggle);
751 char *new_keymap = NULL;
753 r = find_converted_keymap(c, &new_keymap);
757 r = find_legacy_keymap(c, &new_keymap);
762 if (!streq_ptr(c->vc_keymap, new_keymap)) {
763 free_and_replace(&c->vc_keymap, new_keymap);
764 free_and_replace(&c->vc_keymap_toggle, NULL);
771 r = vconsole_write_data(c);
773 log_error("Failed to set virtual console keymap: %s", strerror(-r));
775 log_info("Changed virtual console keymap to '%s' toggle '%s'",
776 strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
778 sd_bus_emit_properties_changed(bus,
779 "/org/freedesktop/locale1",
780 "org.freedesktop.locale1",
781 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
783 return vconsole_reload(bus);
785 log_debug("Virtual console keymap was not modified.");
790 static int property_get_locale(
793 const char *interface,
794 const char *property,
795 sd_bus_message *reply,
797 sd_bus_error *error) {
799 Context *c = userdata;
800 _cleanup_strv_free_ char **l = NULL;
803 l = new0(char*, _LOCALE_MAX+1);
807 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
810 if (isempty(c->locale[p]))
813 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
819 return sd_bus_message_append_strv(reply, l);
822 static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
823 Context *c = userdata;
824 _cleanup_strv_free_ char **l = NULL;
827 bool modified = false;
828 bool passed[_LOCALE_MAX] = {};
832 r = bus_message_read_strv_extend(m, &l);
836 r = sd_bus_message_read_basic(m, 'b', &interactive);
840 /* Check whether a variable changed and if it is valid */
844 for (p = 0; p < _LOCALE_MAX; p++) {
847 k = strlen(names[p]);
848 if (startswith(*i, names[p]) &&
850 locale_is_valid((*i) + k + 1)) {
854 if (!streq_ptr(*i + k + 1, c->locale[p]))
862 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
865 /* Check whether a variable is unset */
867 for (p = 0; p < _LOCALE_MAX; p++)
868 if (!isempty(c->locale[p]) && !passed[p]) {
874 _cleanup_strv_free_ char **settings = NULL;
876 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-locale", interactive, &c->polkit_registry, error);
880 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
883 for (p = 0; p < _LOCALE_MAX; p++) {
886 k = strlen(names[p]);
887 if (startswith(*i, names[p]) && (*i)[k] == '=') {
888 r = free_and_strdup(&c->locale[p], *i + k + 1);
895 for (p = 0; p < _LOCALE_MAX; p++) {
899 free_and_replace(&c->locale[p], NULL);
904 r = locale_write_data(c, &settings);
906 log_error("Failed to set locale: %s", strerror(-r));
907 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
910 locale_update_system_manager(c, bus);
913 _cleanup_free_ char *line;
915 line = strv_join(settings, ", ");
916 log_info("Changed locale to %s.", strnull(line));
918 log_info("Changed locale to unset.");
920 sd_bus_emit_properties_changed(bus,
921 "/org/freedesktop/locale1",
922 "org.freedesktop.locale1",
925 log_debug("Locale settings were not modified.");
928 return sd_bus_reply_method_return(m, NULL);
931 static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
932 Context *c = userdata;
933 const char *keymap, *keymap_toggle;
934 int convert, interactive;
937 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
944 if (isempty(keymap_toggle))
945 keymap_toggle = NULL;
947 if (!streq_ptr(keymap, c->vc_keymap) ||
948 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
950 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
951 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
952 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
954 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
958 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
960 if (free_and_strdup(&c->vc_keymap, keymap) < 0 ||
961 free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0)
964 r = vconsole_write_data(c);
966 log_error("Failed to set virtual console keymap: %s", strerror(-r));
967 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
970 log_info("Changed virtual console keymap to '%s' toggle '%s'",
971 strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
973 r = vconsole_reload(bus);
975 log_error("Failed to request keymap reload: %s", strerror(-r));
977 sd_bus_emit_properties_changed(bus,
978 "/org/freedesktop/locale1",
979 "org.freedesktop.locale1",
980 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
983 r = vconsole_convert_to_x11(c, bus);
985 log_error("Failed to convert keymap data: %s", strerror(-r));
989 return sd_bus_reply_method_return(m, NULL);
992 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
993 Context *c = userdata;
994 const char *layout, *model, *variant, *options;
995 int convert, interactive;
998 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
1002 if (isempty(layout))
1008 if (isempty(variant))
1011 if (isempty(options))
1014 if (!streq_ptr(layout, c->x11_layout) ||
1015 !streq_ptr(model, c->x11_model) ||
1016 !streq_ptr(variant, c->x11_variant) ||
1017 !streq_ptr(options, c->x11_options)) {
1019 if ((layout && !string_is_safe(layout)) ||
1020 (model && !string_is_safe(model)) ||
1021 (variant && !string_is_safe(variant)) ||
1022 (options && !string_is_safe(options)))
1023 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
1025 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
1029 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1031 if (free_and_strdup(&c->x11_layout, layout) < 0 ||
1032 free_and_strdup(&c->x11_model, model) < 0 ||
1033 free_and_strdup(&c->x11_variant, variant) < 0 ||
1034 free_and_strdup(&c->x11_options, options) < 0)
1037 r = write_data_x11(c);
1039 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1040 return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1043 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
1044 strempty(c->x11_layout),
1045 strempty(c->x11_model),
1046 strempty(c->x11_variant),
1047 strempty(c->x11_options));
1049 sd_bus_emit_properties_changed(bus,
1050 "/org/freedesktop/locale1",
1051 "org.freedesktop.locale1",
1052 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
1055 r = x11_convert_to_vconsole(c, bus);
1057 log_error("Failed to convert keymap data: %s", strerror(-r));
1061 return sd_bus_reply_method_return(m, NULL);
1064 static const sd_bus_vtable locale_vtable[] = {
1065 SD_BUS_VTABLE_START(0),
1066 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1067 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1068 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1069 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1070 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1071 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1072 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1073 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
1074 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1075 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1079 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
1080 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1087 r = sd_bus_default_system(&bus);
1089 log_error("Failed to get system bus connection: %s", strerror(-r));
1093 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
1095 log_error("Failed to register object: %s", strerror(-r));
1099 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
1101 log_error("Failed to register name: %s", strerror(-r));
1105 r = sd_bus_attach_event(bus, event, 0);
1107 log_error("Failed to attach bus to event loop: %s", strerror(-r));
1117 int main(int argc, char *argv[]) {
1118 _cleanup_(context_free) Context context = {};
1119 _cleanup_event_unref_ sd_event *event = NULL;
1120 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1123 log_set_target(LOG_TARGET_AUTO);
1124 log_parse_environment();
1131 log_error("This program takes no arguments.");
1136 r = sd_event_default(&event);
1138 log_error("Failed to allocate event loop: %s", strerror(-r));
1142 sd_event_set_watchdog(event, true);
1144 r = connect_bus(&context, event, &bus);
1148 r = context_read_data(&context);
1150 log_error("Failed to read locale data: %s", strerror(-r));
1154 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
1156 log_error("Failed to run event loop: %s", strerror(-r));
1161 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;