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 bool startswith_comma(const char *s, const char *prefix) {
107 return s && (t = startswith(s, prefix)) && (*t == ',');
110 static void context_free_x11(Context *c) {
111 free_and_replace(&c->x11_layout, NULL);
112 free_and_replace(&c->x11_model, NULL);
113 free_and_replace(&c->x11_variant, NULL);
114 free_and_replace(&c->x11_options, NULL);
117 static void context_free_vconsole(Context *c) {
118 free_and_replace(&c->vc_keymap, NULL);
119 free_and_replace(&c->vc_keymap_toggle, NULL);
122 static void context_free_locale(Context *c) {
125 for (p = 0; p < _LOCALE_MAX; p++)
126 free_and_replace(&c->locale[p], NULL);
129 static void context_free(Context *c) {
130 context_free_locale(c);
132 context_free_vconsole(c);
134 bus_verify_polkit_async_registry_free(c->polkit_registry);
137 static void locale_simplify(Context *c) {
140 for (p = LOCALE_LANG+1; p < _LOCALE_MAX; p++)
141 if (isempty(c->locale[p]) || streq_ptr(c->locale[LOCALE_LANG], c->locale[p]))
142 free_and_replace(&c->locale[p], NULL);
145 static int locale_read_data(Context *c) {
148 context_free_locale(c);
150 r = parse_env_file("/etc/locale.conf", NEWLINE,
151 "LANG", &c->locale[LOCALE_LANG],
152 "LANGUAGE", &c->locale[LOCALE_LANGUAGE],
153 "LC_CTYPE", &c->locale[LOCALE_LC_CTYPE],
154 "LC_NUMERIC", &c->locale[LOCALE_LC_NUMERIC],
155 "LC_TIME", &c->locale[LOCALE_LC_TIME],
156 "LC_COLLATE", &c->locale[LOCALE_LC_COLLATE],
157 "LC_MONETARY", &c->locale[LOCALE_LC_MONETARY],
158 "LC_MESSAGES", &c->locale[LOCALE_LC_MESSAGES],
159 "LC_PAPER", &c->locale[LOCALE_LC_PAPER],
160 "LC_NAME", &c->locale[LOCALE_LC_NAME],
161 "LC_ADDRESS", &c->locale[LOCALE_LC_ADDRESS],
162 "LC_TELEPHONE", &c->locale[LOCALE_LC_TELEPHONE],
163 "LC_MEASUREMENT", &c->locale[LOCALE_LC_MEASUREMENT],
164 "LC_IDENTIFICATION", &c->locale[LOCALE_LC_IDENTIFICATION],
170 /* Fill in what we got passed from systemd. */
171 for (p = 0; p < _LOCALE_MAX; p++) {
174 r = free_and_strdup(&c->locale[p],
175 nonempty(getenv(names[p])));
187 static int vconsole_read_data(Context *c) {
190 context_free_vconsole(c);
192 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
193 "KEYMAP", &c->vc_keymap,
194 "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
197 if (r < 0 && r != -ENOENT)
203 static int x11_read_data(Context *c) {
204 _cleanup_fclose_ FILE *f;
206 bool in_section = false;
211 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
213 return errno == ENOENT ? 0 : -errno;
215 while (fgets(line, sizeof(line), f)) {
221 if (l[0] == 0 || l[0] == '#')
224 if (in_section && first_word(l, "Option")) {
225 _cleanup_strv_free_ char **a = NULL;
227 r = strv_split_quoted(&a, l);
231 if (strv_length(a) == 3) {
232 if (streq(a[1], "XkbLayout")) {
233 free_and_replace(&c->x11_layout, a[2]);
235 } else if (streq(a[1], "XkbModel")) {
236 free_and_replace(&c->x11_model, a[2]);
238 } else if (streq(a[1], "XkbVariant")) {
239 free_and_replace(&c->x11_variant, a[2]);
241 } else if (streq(a[1], "XkbOptions")) {
242 free_and_replace(&c->x11_options, a[2]);
247 } else if (!in_section && first_word(l, "Section")) {
248 _cleanup_strv_free_ char **a = NULL;
250 r = strv_split_quoted(&a, l);
254 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
257 } else if (in_section && first_word(l, "EndSection"))
264 static int context_read_data(Context *c) {
267 r = locale_read_data(c);
268 q = vconsole_read_data(c);
269 p = x11_read_data(c);
271 return r < 0 ? r : q < 0 ? q : p;
274 static int locale_write_data(Context *c, char ***settings) {
276 _cleanup_strv_free_ char **l = NULL;
278 /* Set values will be returned as strv in *settings on success. */
280 r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
281 if (r < 0 && r != -ENOENT)
284 for (p = 0; p < _LOCALE_MAX; p++) {
285 _cleanup_free_ char *t = NULL;
290 if (isempty(c->locale[p])) {
291 l = strv_env_unset(l, names[p]);
295 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
298 u = strv_env_set(l, t);
306 if (strv_isempty(l)) {
307 if (unlink("/etc/locale.conf") < 0)
308 return errno == ENOENT ? 0 : -errno;
313 r = write_env_file_label("/etc/locale.conf", l);
322 static int locale_update_system_manager(Context *c, sd_bus *bus) {
323 _cleanup_free_ char **l_unset = NULL;
324 _cleanup_strv_free_ char **l_set = NULL;
325 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
326 sd_bus_error error = SD_BUS_ERROR_NULL;
327 unsigned c_set, c_unset, p;
332 l_unset = new0(char*, _LOCALE_MAX);
336 l_set = new0(char*, _LOCALE_MAX);
340 for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
343 if (isempty(c->locale[p]))
344 l_unset[c_set++] = (char*) names[p];
348 if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
351 l_set[c_unset++] = s;
355 assert(c_set + c_unset == _LOCALE_MAX);
356 r = sd_bus_message_new_method_call(bus, &m,
357 "org.freedesktop.systemd1",
358 "/org/freedesktop/systemd1",
359 "org.freedesktop.systemd1.Manager",
360 "UnsetAndSetEnvironment");
364 r = sd_bus_message_append_strv(m, l_unset);
368 r = sd_bus_message_append_strv(m, l_set);
372 r = sd_bus_call(bus, m, 0, &error, NULL);
374 log_error("Failed to update the manager environment: %s", strerror(-r));
379 static int vconsole_write_data(Context *c) {
381 _cleanup_strv_free_ char **l = NULL;
383 r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
384 if (r < 0 && r != -ENOENT)
387 if (isempty(c->vc_keymap))
388 l = strv_env_unset(l, "KEYMAP");
390 _cleanup_free_ char *s = NULL;
393 s = strappend("KEYMAP=", c->vc_keymap);
397 u = strv_env_set(l, s);
405 if (isempty(c->vc_keymap_toggle))
406 l = strv_env_unset(l, "KEYMAP_TOGGLE");
408 _cleanup_free_ char *s = NULL;
411 s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
415 u = strv_env_set(l, s);
423 if (strv_isempty(l)) {
424 if (unlink("/etc/vconsole.conf") < 0)
425 return errno == ENOENT ? 0 : -errno;
430 return write_env_file_label("/etc/vconsole.conf", l);
433 static int write_data_x11(Context *c) {
434 _cleanup_fclose_ FILE *f = NULL;
435 _cleanup_free_ char *temp_path = NULL;
438 if (isempty(c->x11_layout) &&
439 isempty(c->x11_model) &&
440 isempty(c->x11_variant) &&
441 isempty(c->x11_options)) {
443 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
444 return errno == ENOENT ? 0 : -errno;
449 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
451 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
455 fchmod(fileno(f), 0644);
457 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
458 "# manually too freely.\n"
459 "Section \"InputClass\"\n"
460 " Identifier \"system-keyboard\"\n"
461 " MatchIsKeyboard \"on\"\n", f);
463 if (!isempty(c->x11_layout))
464 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
466 if (!isempty(c->x11_model))
467 fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
469 if (!isempty(c->x11_variant))
470 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
472 if (!isempty(c->x11_options))
473 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
475 fputs("EndSection\n", f);
478 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
480 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
487 static int vconsole_reload(sd_bus *bus) {
488 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
493 r = sd_bus_call_method(bus,
494 "org.freedesktop.systemd1",
495 "/org/freedesktop/systemd1",
496 "org.freedesktop.systemd1.Manager",
500 "ss", "systemd-vconsole-setup.service", "replace");
503 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
507 static const char* strnulldash(const char *s) {
508 return isempty(s) || streq(s, "-") ? NULL : s;
511 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
522 if (!fgets(line, sizeof(line), f)) {
525 return errno ? -errno : -EIO;
533 if (l[0] == 0 || l[0] == '#')
536 r = strv_split_quoted(&b, l);
540 if (strv_length(b) < 5) {
541 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
552 static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
553 bool modified = false;
557 if (isempty(c->vc_keymap)) {
560 !isempty(c->x11_layout) ||
561 !isempty(c->x11_model) ||
562 !isempty(c->x11_variant) ||
563 !isempty(c->x11_options);
567 _cleanup_fclose_ FILE *f = NULL;
570 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
575 _cleanup_strv_free_ char **a = NULL;
578 r = read_next_mapping(f, &n, &a);
584 if (!streq(c->vc_keymap, a[0]))
587 if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
588 !streq_ptr(c->x11_model, strnulldash(a[2])) ||
589 !streq_ptr(c->x11_variant, strnulldash(a[3])) ||
590 !streq_ptr(c->x11_options, strnulldash(a[4]))) {
592 if (free_and_strdup(&c->x11_layout, strnulldash(a[1])) < 0 ||
593 free_and_strdup(&c->x11_model, strnulldash(a[2])) < 0 ||
594 free_and_strdup(&c->x11_variant, strnulldash(a[3])) < 0 ||
595 free_and_strdup(&c->x11_options, strnulldash(a[4])) < 0)
608 r = write_data_x11(c);
610 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
612 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
613 strempty(c->x11_layout),
614 strempty(c->x11_model),
615 strempty(c->x11_variant),
616 strempty(c->x11_options));
618 sd_bus_emit_properties_changed(bus,
619 "/org/freedesktop/locale1",
620 "org.freedesktop.locale1",
621 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
623 log_debug("X11 keyboard layout was not modified.");
628 static int find_converted_keymap(Context *c, char **new_keymap) {
630 _cleanup_free_ char *n;
633 n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
635 n = strdup(c->x11_layout);
639 NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
640 _cleanup_free_ char *p = NULL, *pz = NULL;
643 p = strjoin(dir, "xkb/", n, ".map", NULL);
644 pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
648 uncompressed = access(p, F_OK) == 0;
649 if (uncompressed || access(pz, F_OK) == 0) {
650 log_debug("Found converted keymap %s at %s",
651 n, uncompressed ? p : pz);
662 static int find_legacy_keymap(Context *c, char **new_keymap) {
663 _cleanup_fclose_ FILE *f;
665 unsigned best_matching = 0;
668 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
673 _cleanup_strv_free_ char **a = NULL;
674 unsigned matching = 0;
677 r = read_next_mapping(f, &n, &a);
683 /* Determine how well matching this entry is */
684 if (streq_ptr(c->x11_layout, a[1]))
685 /* If we got an exact match, this is best */
688 /* We have multiple X layouts, look for an
689 * entry that matches our key with everything
690 * but the first layout stripped off. */
691 if (startswith_comma(c->x11_layout, a[1]))
696 /* If that didn't work, strip off the
697 * other layouts from the entry, too */
698 x = strndupa(a[1], strcspn(a[1], ","));
699 if (startswith_comma(c->x11_layout, x))
705 if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
708 if (streq_ptr(c->x11_variant, a[3])) {
711 if (streq_ptr(c->x11_options, a[4]))
717 /* The best matching entry so far, then let's save that */
718 if (matching >= MAX(best_matching, 1u)) {
719 log_debug("Found legacy keymap %s with score %u",
722 if (matching > best_matching) {
723 best_matching = matching;
725 r = free_and_strdup(new_keymap, a[0]);
735 static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
736 bool modified = false;
741 if (isempty(c->x11_layout)) {
744 !isempty(c->vc_keymap) ||
745 !isempty(c->vc_keymap_toggle);
749 char *new_keymap = NULL;
751 r = find_converted_keymap(c, &new_keymap);
755 r = find_legacy_keymap(c, &new_keymap);
760 if (!streq_ptr(c->vc_keymap, new_keymap)) {
761 free_and_replace(&c->vc_keymap, new_keymap);
762 free_and_replace(&c->vc_keymap_toggle, NULL);
769 r = vconsole_write_data(c);
771 log_error("Failed to set virtual console keymap: %s", strerror(-r));
773 log_info("Changed virtual console keymap to '%s' toggle '%s'",
774 strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
776 sd_bus_emit_properties_changed(bus,
777 "/org/freedesktop/locale1",
778 "org.freedesktop.locale1",
779 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
781 return vconsole_reload(bus);
783 log_debug("Virtual console keymap was not modified.");
788 static int property_get_locale(
791 const char *interface,
792 const char *property,
793 sd_bus_message *reply,
795 sd_bus_error *error) {
797 Context *c = userdata;
798 _cleanup_strv_free_ char **l = NULL;
801 l = new0(char*, _LOCALE_MAX+1);
805 for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
808 if (isempty(c->locale[p]))
811 if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
817 return sd_bus_message_append_strv(reply, l);
820 static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
821 Context *c = userdata;
822 _cleanup_strv_free_ char **l = NULL;
825 bool modified = false;
826 bool passed[_LOCALE_MAX] = {};
830 r = bus_message_read_strv_extend(m, &l);
834 r = sd_bus_message_read_basic(m, 'b', &interactive);
838 /* Check whether a variable changed and if it is valid */
842 for (p = 0; p < _LOCALE_MAX; p++) {
845 k = strlen(names[p]);
846 if (startswith(*i, names[p]) &&
848 locale_is_valid((*i) + k + 1)) {
852 if (!streq_ptr(*i + k + 1, c->locale[p]))
860 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
863 /* Check whether a variable is unset */
865 for (p = 0; p < _LOCALE_MAX; p++)
866 if (!isempty(c->locale[p]) && !passed[p]) {
872 _cleanup_strv_free_ char **settings = NULL;
874 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-locale", interactive, &c->polkit_registry, error);
878 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
881 for (p = 0; p < _LOCALE_MAX; p++) {
884 k = strlen(names[p]);
885 if (startswith(*i, names[p]) && (*i)[k] == '=') {
886 r = free_and_strdup(&c->locale[p], *i + k + 1);
893 for (p = 0; p < _LOCALE_MAX; p++) {
897 free_and_replace(&c->locale[p], NULL);
902 r = locale_write_data(c, &settings);
904 log_error("Failed to set locale: %s", strerror(-r));
905 return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
908 locale_update_system_manager(c, bus);
911 _cleanup_free_ char *line;
913 line = strv_join(settings, ", ");
914 log_info("Changed locale to %s.", strnull(line));
916 log_info("Changed locale to unset.");
918 sd_bus_emit_properties_changed(bus,
919 "/org/freedesktop/locale1",
920 "org.freedesktop.locale1",
923 log_debug("Locale settings were not modified.");
926 return sd_bus_reply_method_return(m, NULL);
929 static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
930 Context *c = userdata;
931 const char *keymap, *keymap_toggle;
932 int convert, interactive;
935 r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
942 if (isempty(keymap_toggle))
943 keymap_toggle = NULL;
945 if (!streq_ptr(keymap, c->vc_keymap) ||
946 !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
948 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
949 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
950 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
952 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
956 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
958 if (free_and_strdup(&c->vc_keymap, keymap) < 0 ||
959 free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0)
962 r = vconsole_write_data(c);
964 log_error("Failed to set virtual console keymap: %s", strerror(-r));
965 return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
968 log_info("Changed virtual console keymap to '%s' toggle '%s'",
969 strempty(c->vc_keymap), strempty(c->vc_keymap_toggle));
971 r = vconsole_reload(bus);
973 log_error("Failed to request keymap reload: %s", strerror(-r));
975 sd_bus_emit_properties_changed(bus,
976 "/org/freedesktop/locale1",
977 "org.freedesktop.locale1",
978 "VConsoleKeymap", "VConsoleKeymapToggle", NULL);
981 r = vconsole_convert_to_x11(c, bus);
983 log_error("Failed to convert keymap data: %s", strerror(-r));
987 return sd_bus_reply_method_return(m, NULL);
990 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
991 Context *c = userdata;
992 const char *layout, *model, *variant, *options;
993 int convert, interactive;
996 r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
1000 if (isempty(layout))
1006 if (isempty(variant))
1009 if (isempty(options))
1012 if (!streq_ptr(layout, c->x11_layout) ||
1013 !streq_ptr(model, c->x11_model) ||
1014 !streq_ptr(variant, c->x11_variant) ||
1015 !streq_ptr(options, c->x11_options)) {
1017 if ((layout && !string_is_safe(layout)) ||
1018 (model && !string_is_safe(model)) ||
1019 (variant && !string_is_safe(variant)) ||
1020 (options && !string_is_safe(options)))
1021 return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
1023 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, 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_strdup(&c->x11_layout, layout) < 0 ||
1030 free_and_strdup(&c->x11_model, model) < 0 ||
1031 free_and_strdup(&c->x11_variant, variant) < 0 ||
1032 free_and_strdup(&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_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
1041 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
1042 strempty(c->x11_layout),
1043 strempty(c->x11_model),
1044 strempty(c->x11_variant),
1045 strempty(c->x11_options));
1047 sd_bus_emit_properties_changed(bus,
1048 "/org/freedesktop/locale1",
1049 "org.freedesktop.locale1",
1050 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
1053 r = x11_convert_to_vconsole(c, bus);
1055 log_error("Failed to convert keymap data: %s", strerror(-r));
1059 return sd_bus_reply_method_return(m, NULL);
1062 static const sd_bus_vtable locale_vtable[] = {
1063 SD_BUS_VTABLE_START(0),
1064 SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1065 SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1066 SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1067 SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1068 SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1069 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1070 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
1071 SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
1072 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1073 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
1077 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
1078 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1085 r = sd_bus_default_system(&bus);
1087 log_error("Failed to get system bus connection: %s", strerror(-r));
1091 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
1093 log_error("Failed to register object: %s", strerror(-r));
1097 r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
1099 log_error("Failed to register name: %s", strerror(-r));
1103 r = sd_bus_attach_event(bus, event, 0);
1105 log_error("Failed to attach bus to event loop: %s", strerror(-r));
1115 int main(int argc, char *argv[]) {
1116 _cleanup_(context_free) Context context = {};
1117 _cleanup_event_unref_ sd_event *event = NULL;
1118 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1121 log_set_target(LOG_TARGET_AUTO);
1122 log_parse_environment();
1129 log_error("This program takes no arguments.");
1134 r = sd_event_default(&event);
1136 log_error("Failed to allocate event loop: %s", strerror(-r));
1140 sd_event_set_watchdog(event, true);
1142 r = connect_bus(&context, event, &bus);
1146 r = context_read_data(&context);
1148 log_error("Failed to read locale data: %s", strerror(-r));
1152 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
1154 log_error("Failed to run event loop: %s", strerror(-r));
1159 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;