1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <dbus/dbus.h>
31 #include "dbus-common.h"
37 " <interface name=\"org.freedesktop.locale1\">\n" \
38 " <property name=\"Locale\" type=\"as\" access=\"read\"/>\n" \
39 " <property name=\"VConsoleKeymap\" type=\"s\" access=\"read\"/>\n" \
40 " <property name=\"VConsoleKeymapToggle\" type=\"s\" access=\"read\"/>\n" \
41 " <property name=\"X11Layout\" type=\"s\" access=\"read\"/>\n" \
42 " <property name=\"X11Model\" type=\"s\" access=\"read\"/>\n" \
43 " <property name=\"X11Variant\" type=\"s\" access=\"read\"/>\n" \
44 " <property name=\"X11Options\" type=\"s\" access=\"read\"/>\n" \
45 " <method name=\"SetLocale\">\n" \
46 " <arg name=\"locale\" type=\"as\" direction=\"in\"/>\n" \
47 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
49 " <method name=\"SetVConsoleKeyboard\">\n" \
50 " <arg name=\"keymap\" type=\"s\" direction=\"in\"/>\n" \
51 " <arg name=\"keymap_toggle\" type=\"s\" direction=\"in\"/>\n" \
52 " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \
53 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
55 " <method name=\"SetX11Keyboard\">\n" \
56 " <arg name=\"layout\" type=\"s\" direction=\"in\"/>\n" \
57 " <arg name=\"model\" type=\"s\" direction=\"in\"/>\n" \
58 " <arg name=\"variant\" type=\"s\" direction=\"in\"/>\n" \
59 " <arg name=\"options\" type=\"s\" direction=\"in\"/>\n" \
60 " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \
61 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
65 #define INTROSPECTION \
66 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
69 BUS_PROPERTIES_INTERFACE \
70 BUS_INTROSPECTABLE_INTERFACE \
74 #define INTERFACES_LIST \
75 BUS_GENERIC_INTERFACES_LIST \
76 "org.freedesktop.locale1\0"
78 const char locale_interface[] _introspect_("locale1") = INTERFACE;
81 /* We don't list LC_ALL here on purpose. People should be
82 * using LANG instead. */
97 PROP_LC_IDENTIFICATION,
101 static const char * const names[_PROP_MAX] = {
102 [PROP_LANG] = "LANG",
103 [PROP_LANGUAGE] = "LANGUAGE",
104 [PROP_LC_CTYPE] = "LC_CTYPE",
105 [PROP_LC_NUMERIC] = "LC_NUMERIC",
106 [PROP_LC_TIME] = "LC_TIME",
107 [PROP_LC_COLLATE] = "LC_COLLATE",
108 [PROP_LC_MONETARY] = "LC_MONETARY",
109 [PROP_LC_MESSAGES] = "LC_MESSAGES",
110 [PROP_LC_PAPER] = "LC_PAPER",
111 [PROP_LC_NAME] = "LC_NAME",
112 [PROP_LC_ADDRESS] = "LC_ADDRESS",
113 [PROP_LC_TELEPHONE] = "LC_TELEPHONE",
114 [PROP_LC_MEASUREMENT] = "LC_MEASUREMENT",
115 [PROP_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
118 static char *data[_PROP_MAX] = {
134 typedef struct State {
135 char *x11_layout, *x11_model, *x11_variant, *x11_options;
136 char *vc_keymap, *vc_keymap_toggle;
141 static usec_t remain_until = 0;
143 static int free_and_set(char **s, const char *v) {
149 r = strdup_or_null(isempty(v) ? NULL : v, &t);
159 static void free_data_locale(void) {
162 for (p = 0; p < _PROP_MAX; p++) {
168 static void free_data_x11(void) {
169 free(state.x11_layout);
170 free(state.x11_model);
171 free(state.x11_variant);
172 free(state.x11_options);
174 state.x11_layout = state.x11_model = state.x11_variant = state.x11_options = NULL;
177 static void free_data_vconsole(void) {
178 free(state.vc_keymap);
179 free(state.vc_keymap_toggle);
181 state.vc_keymap = state.vc_keymap_toggle = NULL;
184 static void simplify(void) {
187 for (p = 1; p < _PROP_MAX; p++)
188 if (isempty(data[p]) || streq_ptr(data[PROP_LANG], data[p])) {
194 static int read_data_locale(void) {
199 r = parse_env_file("/etc/locale.conf", NEWLINE,
200 "LANG", &data[PROP_LANG],
201 "LANGUAGE", &data[PROP_LANGUAGE],
202 "LC_CTYPE", &data[PROP_LC_CTYPE],
203 "LC_NUMERIC", &data[PROP_LC_NUMERIC],
204 "LC_TIME", &data[PROP_LC_TIME],
205 "LC_COLLATE", &data[PROP_LC_COLLATE],
206 "LC_MONETARY", &data[PROP_LC_MONETARY],
207 "LC_MESSAGES", &data[PROP_LC_MESSAGES],
208 "LC_PAPER", &data[PROP_LC_PAPER],
209 "LC_NAME", &data[PROP_LC_NAME],
210 "LC_ADDRESS", &data[PROP_LC_ADDRESS],
211 "LC_TELEPHONE", &data[PROP_LC_TELEPHONE],
212 "LC_MEASUREMENT", &data[PROP_LC_MEASUREMENT],
213 "LC_IDENTIFICATION", &data[PROP_LC_IDENTIFICATION],
219 /* Fill in what we got passed from systemd. */
221 for (p = 0; p < _PROP_MAX; p++) {
226 e = getenv(names[p]);
245 static void free_data(void) {
247 free_data_vconsole();
251 static int read_data_vconsole(void) {
254 free_data_vconsole();
256 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
257 "KEYMAP", &state.vc_keymap,
258 "KEYMAP_TOGGLE", &state.vc_keymap_toggle,
261 if (r < 0 && r != -ENOENT)
267 static int read_data_x11(void) {
270 bool in_section = false;
274 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
276 return errno == ENOENT ? 0 : -errno;
278 while (fgets(line, sizeof(line), f)) {
284 if (l[0] == 0 || l[0] == '#')
287 if (in_section && first_word(l, "Option")) {
290 a = strv_split_quoted(l);
296 if (strv_length(a) == 3) {
298 if (streq(a[1], "XkbLayout")) {
299 free(state.x11_layout);
300 state.x11_layout = a[2];
302 } else if (streq(a[1], "XkbModel")) {
303 free(state.x11_model);
304 state.x11_model = a[2];
306 } else if (streq(a[1], "XkbVariant")) {
307 free(state.x11_variant);
308 state.x11_variant = a[2];
310 } else if (streq(a[1], "XkbOptions")) {
311 free(state.x11_options);
312 state.x11_options = a[2];
319 } else if (!in_section && first_word(l, "Section")) {
322 a = strv_split_quoted(l);
328 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
332 } else if (in_section && first_word(l, "EndSection"))
341 static int read_data(void) {
344 r = read_data_locale();
345 q = read_data_vconsole();
348 return r < 0 ? r : q < 0 ? q : p;
351 static int write_data_locale(void) {
355 r = load_env_file("/etc/locale.conf", &l);
356 if (r < 0 && r != -ENOENT)
359 for (p = 0; p < _PROP_MAX; p++) {
364 if (isempty(data[p])) {
365 l = strv_env_unset(l, names[p]);
369 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
374 u = strv_env_set(l, t);
384 if (strv_isempty(l)) {
387 if (unlink("/etc/locale.conf") < 0)
388 return errno == ENOENT ? 0 : -errno;
393 r = write_env_file("/etc/locale.conf", l);
399 static void push_data(DBusConnection *bus) {
400 char **l_set = NULL, **l_unset = NULL, **t;
401 int c_set = 0, c_unset = 0, p;
403 DBusMessage *m = NULL, *reply = NULL;
404 DBusMessageIter iter, sub;
406 dbus_error_init(&error);
410 l_set = new0(char*, _PROP_MAX);
411 l_unset = new0(char*, _PROP_MAX);
412 if (!l_set || !l_unset) {
417 for (p = 0; p < _PROP_MAX; p++) {
420 if (isempty(data[p]))
421 l_unset[c_set++] = (char*) names[p];
425 if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) {
430 l_set[c_unset++] = s;
434 assert(c_set + c_unset == _PROP_MAX);
435 m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment");
437 log_error("Could not allocate message.");
441 dbus_message_iter_init_append(m, &iter);
443 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
448 STRV_FOREACH(t, l_unset)
449 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
454 if (!dbus_message_iter_close_container(&iter, &sub) ||
455 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
460 STRV_FOREACH(t, l_set)
461 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
466 if (!dbus_message_iter_close_container(&iter, &sub)) {
471 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
473 log_error("Failed to set locale information: %s", bus_error_message(&error));
479 dbus_message_unref(m);
482 dbus_message_unref(reply);
484 dbus_error_free(&error);
490 static int write_data_vconsole(void) {
494 r = load_env_file("/etc/vconsole.conf", &l);
495 if (r < 0 && r != -ENOENT)
498 if (isempty(state.vc_keymap))
499 l = strv_env_unset(l, "KEYMAP");
503 s = strappend("KEYMAP=", state.vc_keymap);
509 u = strv_env_set(l, s);
519 if (isempty(state.vc_keymap_toggle))
520 l = strv_env_unset(l, "KEYMAP_TOGGLE");
524 s = strappend("KEYMAP_TOGGLE=", state.vc_keymap_toggle);
530 u = strv_env_set(l, s);
540 if (strv_isempty(l)) {
543 if (unlink("/etc/vconsole.conf") < 0)
544 return errno == ENOENT ? 0 : -errno;
549 r = write_env_file("/etc/vconsole.conf", l);
555 static int write_data_x11(void) {
560 if (isempty(state.x11_layout) &&
561 isempty(state.x11_model) &&
562 isempty(state.x11_variant) &&
563 isempty(state.x11_options)) {
565 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
566 return errno == ENOENT ? 0 : -errno;
571 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
573 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
577 fchmod(fileno(f), 0644);
579 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
580 "# manually too freely.\n"
581 "Section \"InputClass\"\n"
582 " Identifier \"system-keyboard\"\n"
583 " MatchIsKeyboard \"on\"\n", f);
585 if (!isempty(state.x11_layout))
586 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", state.x11_layout);
588 if (!isempty(state.x11_model))
589 fprintf(f, " Option \"XkbModel\" \"%s\"\n", state.x11_model);
591 if (!isempty(state.x11_variant))
592 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", state.x11_variant);
594 if (!isempty(state.x11_options))
595 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", state.x11_options);
597 fputs("EndSection\n", f);
600 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
602 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
613 static int load_vconsole_keymap(DBusConnection *bus, DBusError *error) {
614 DBusMessage *m = NULL, *reply = NULL;
615 const char *name = "systemd-vconsole-setup.service", *mode = "replace";
622 dbus_error_init(&_error);
626 m = dbus_message_new_method_call(
627 "org.freedesktop.systemd1",
628 "/org/freedesktop/systemd1",
629 "org.freedesktop.systemd1.Manager",
632 log_error("Could not allocate message.");
637 if (!dbus_message_append_args(m,
638 DBUS_TYPE_STRING, &name,
639 DBUS_TYPE_STRING, &mode,
640 DBUS_TYPE_INVALID)) {
641 log_error("Could not append arguments to message.");
646 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
648 log_error("Failed to issue method call: %s", bus_error_message(error));
657 dbus_message_unref(m);
660 dbus_message_unref(reply);
662 if (error == &_error)
663 dbus_error_free(error);
668 static char *strnulldash(const char *s) {
669 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
672 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
682 if (!fgets(line, sizeof(line), f)) {
685 return errno ? -errno : -EIO;
693 if (l[0] == 0 || l[0] == '#')
696 b = strv_split_quoted(l);
700 if (strv_length(b) < 5) {
701 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
712 static int convert_vconsole_to_x11(DBusConnection *connection) {
713 bool modified = false;
717 if (isempty(state.vc_keymap)) {
720 !isempty(state.x11_layout) ||
721 !isempty(state.x11_model) ||
722 !isempty(state.x11_variant) ||
723 !isempty(state.x11_options);
730 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
738 r = read_next_mapping(f, &n, &a);
747 if (!streq(state.vc_keymap, a[0])) {
752 if (!streq_ptr(state.x11_layout, strnulldash(a[1])) ||
753 !streq_ptr(state.x11_model, strnulldash(a[2])) ||
754 !streq_ptr(state.x11_variant, strnulldash(a[3])) ||
755 !streq_ptr(state.x11_options, strnulldash(a[4]))) {
757 if (free_and_set(&state.x11_layout, strnulldash(a[1])) < 0 ||
758 free_and_set(&state.x11_model, strnulldash(a[2])) < 0 ||
759 free_and_set(&state.x11_variant, strnulldash(a[3])) < 0 ||
760 free_and_set(&state.x11_options, strnulldash(a[4])) < 0) {
778 DBusMessage *changed;
781 r = write_data_x11();
783 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
785 changed = bus_properties_changed_new(
786 "/org/freedesktop/locale1",
787 "org.freedesktop.locale1",
796 b = dbus_connection_send(connection, changed, NULL);
797 dbus_message_unref(changed);
806 static int convert_x11_to_vconsole(DBusConnection *connection) {
807 bool modified = false;
811 if (isempty(state.x11_layout)) {
814 !isempty(state.vc_keymap) ||
815 !isempty(state.vc_keymap_toggle);
821 unsigned best_matching = 0;
822 char *new_keymap = NULL;
824 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
830 unsigned matching = 0;
833 r = read_next_mapping(f, &n, &a);
842 /* Determine how well matching this entry is */
843 if (streq_ptr(state.x11_layout, a[1]))
844 /* If we got an exact match, this is best */
849 x = strcspn(state.x11_layout, ",");
851 /* We have multiple X layouts, look
852 * for an entry that matches our key
853 * with the everything but the first
854 * layout stripped off. */
857 strneq(state.x11_layout, a[1], x))
862 /* If that didn't work, strip
863 * off the other layouts from
866 w = strcspn(a[1], ",");
868 if (x > 0 && x == w &&
869 memcmp(state.x11_layout, a[1], x) == 0)
875 streq_ptr(state.x11_model, a[2])) {
878 if (streq_ptr(state.x11_variant, a[3])) {
881 if (streq_ptr(state.x11_options, a[4]))
886 /* The best matching entry so far, then let's
888 if (matching > best_matching) {
889 best_matching = matching;
892 new_keymap = strdup(a[0]);
906 if (!streq_ptr(state.vc_keymap, new_keymap)) {
907 free(state.vc_keymap);
908 state.vc_keymap = new_keymap;
910 free(state.vc_keymap_toggle);
911 state.vc_keymap_toggle = NULL;
920 DBusMessage *changed;
923 r = write_data_vconsole();
925 log_error("Failed to set virtual console keymap: %s", strerror(-r));
927 changed = bus_properties_changed_new(
928 "/org/freedesktop/locale1",
929 "org.freedesktop.locale1",
931 "VConsoleKeymapToggle\0");
936 b = dbus_connection_send(connection, changed, NULL);
937 dbus_message_unref(changed);
942 return load_vconsole_keymap(connection, NULL);
948 static int append_locale(DBusMessageIter *i, const char *property, void *userdata) {
952 l = new0(char*, _PROP_MAX+1);
956 for (p = 0; p < _PROP_MAX; p++) {
959 if (isempty(data[p]))
962 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
970 r = bus_property_append_strv(i, property, (void*) l);
976 static const BusProperty bus_locale_properties[] = {
977 { "Locale", append_locale, "as", 0 },
978 { "X11Layout", bus_property_append_string, "s", offsetof(State, x11_layout), true },
979 { "X11Model", bus_property_append_string, "s", offsetof(State, x11_model), true },
980 { "X11Variant", bus_property_append_string, "s", offsetof(State, x11_variant), true },
981 { "X11Options", bus_property_append_string, "s", offsetof(State, x11_options), true },
982 { "VConsoleKeymap", bus_property_append_string, "s", offsetof(State, vc_keymap), true },
983 { "VConsoleKeymapToggle", bus_property_append_string, "s", offsetof(State, vc_keymap_toggle), true },
987 static const BusBoundProperties bps[] = {
988 { "org.freedesktop.locale1", bus_locale_properties, &state },
992 static DBusHandlerResult locale_message_handler(
993 DBusConnection *connection,
994 DBusMessage *message,
997 DBusMessage *reply = NULL, *changed = NULL;
1004 dbus_error_init(&error);
1006 if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) {
1007 char **l = NULL, **i;
1008 dbus_bool_t interactive;
1009 DBusMessageIter iter;
1010 bool modified = false;
1011 bool passed[_PROP_MAX];
1014 if (!dbus_message_iter_init(message, &iter))
1015 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1017 r = bus_parse_strv_iter(&iter, &l);
1022 return bus_send_error_reply(connection, message, NULL, r);
1025 if (!dbus_message_iter_next(&iter) ||
1026 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
1028 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1031 dbus_message_iter_get_basic(&iter, &interactive);
1035 /* Check whether a variable changed and if so valid */
1036 STRV_FOREACH(i, l) {
1039 for (p = 0; p < _PROP_MAX; p++) {
1042 k = strlen(names[p]);
1043 if (startswith(*i, names[p]) &&
1045 string_is_safe((*i) + k + 1)) {
1049 if (!streq_ptr(*i + k + 1, data[p]))
1058 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1062 /* Check whether a variable is unset */
1064 for (p = 0; p < _PROP_MAX; p++)
1065 if (!isempty(data[p]) && !passed[p]) {
1073 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error);
1076 return bus_send_error_reply(connection, message, &error, r);
1079 STRV_FOREACH(i, l) {
1080 for (p = 0; p < _PROP_MAX; p++) {
1083 k = strlen(names[p]);
1084 if (startswith(*i, names[p]) && (*i)[k] == '=') {
1087 t = strdup(*i + k + 1);
1103 for (p = 0; p < _PROP_MAX; p++) {
1113 r = write_data_locale();
1115 log_error("Failed to set locale: %s", strerror(-r));
1116 return bus_send_error_reply(connection, message, NULL, r);
1119 push_data(connection);
1121 log_info("Changed locale information.");
1123 changed = bus_properties_changed_new(
1124 "/org/freedesktop/locale1",
1125 "org.freedesktop.locale1",
1132 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
1134 const char *keymap, *keymap_toggle;
1135 dbus_bool_t convert, interactive;
1137 if (!dbus_message_get_args(
1140 DBUS_TYPE_STRING, &keymap,
1141 DBUS_TYPE_STRING, &keymap_toggle,
1142 DBUS_TYPE_BOOLEAN, &convert,
1143 DBUS_TYPE_BOOLEAN, &interactive,
1145 return bus_send_error_reply(connection, message, &error, -EINVAL);
1147 if (isempty(keymap))
1150 if (isempty(keymap_toggle))
1151 keymap_toggle = NULL;
1153 if (!streq_ptr(keymap, state.vc_keymap) ||
1154 !streq_ptr(keymap_toggle, state.vc_keymap_toggle)) {
1156 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
1157 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
1158 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1160 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1162 return bus_send_error_reply(connection, message, &error, r);
1164 if (free_and_set(&state.vc_keymap, keymap) < 0 ||
1165 free_and_set(&state.vc_keymap_toggle, keymap_toggle) < 0)
1168 r = write_data_vconsole();
1170 log_error("Failed to set virtual console keymap: %s", strerror(-r));
1171 return bus_send_error_reply(connection, message, NULL, r);
1174 log_info("Changed virtual console keymap to '%s'", strempty(state.vc_keymap));
1176 r = load_vconsole_keymap(connection, NULL);
1178 log_error("Failed to request keymap reload: %s", strerror(-r));
1180 changed = bus_properties_changed_new(
1181 "/org/freedesktop/locale1",
1182 "org.freedesktop.locale1",
1184 "VConsoleKeymapToggle\0");
1189 r = convert_vconsole_to_x11(connection);
1192 log_error("Failed to convert keymap data: %s", strerror(-r));
1196 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) {
1198 const char *layout, *model, *variant, *options;
1199 dbus_bool_t convert, interactive;
1201 if (!dbus_message_get_args(
1204 DBUS_TYPE_STRING, &layout,
1205 DBUS_TYPE_STRING, &model,
1206 DBUS_TYPE_STRING, &variant,
1207 DBUS_TYPE_STRING, &options,
1208 DBUS_TYPE_BOOLEAN, &convert,
1209 DBUS_TYPE_BOOLEAN, &interactive,
1211 return bus_send_error_reply(connection, message, &error, -EINVAL);
1213 if (isempty(layout))
1219 if (isempty(variant))
1222 if (isempty(options))
1225 if (!streq_ptr(layout, state.x11_layout) ||
1226 !streq_ptr(model, state.x11_model) ||
1227 !streq_ptr(variant, state.x11_variant) ||
1228 !streq_ptr(options, state.x11_options)) {
1230 if ((layout && !string_is_safe(layout)) ||
1231 (model && !string_is_safe(model)) ||
1232 (variant && !string_is_safe(variant)) ||
1233 (options && !string_is_safe(options)))
1234 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1236 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1238 return bus_send_error_reply(connection, message, &error, r);
1240 if (free_and_set(&state.x11_layout, layout) < 0 ||
1241 free_and_set(&state.x11_model, model) < 0 ||
1242 free_and_set(&state.x11_variant, variant) < 0 ||
1243 free_and_set(&state.x11_options, options) < 0)
1246 r = write_data_x11();
1248 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1249 return bus_send_error_reply(connection, message, NULL, r);
1252 log_info("Changed X11 keyboard layout to '%s'", strempty(state.x11_layout));
1254 changed = bus_properties_changed_new(
1255 "/org/freedesktop/locale1",
1256 "org.freedesktop.locale1",
1265 r = convert_x11_to_vconsole(connection);
1268 log_error("Failed to convert keymap data: %s", strerror(-r));
1272 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
1274 if (!(reply = dbus_message_new_method_return(message)))
1277 if (!dbus_connection_send(connection, reply, NULL))
1280 dbus_message_unref(reply);
1285 if (!dbus_connection_send(connection, changed, NULL))
1288 dbus_message_unref(changed);
1291 return DBUS_HANDLER_RESULT_HANDLED;
1295 dbus_message_unref(reply);
1298 dbus_message_unref(changed);
1300 dbus_error_free(&error);
1302 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1305 static int connect_bus(DBusConnection **_bus) {
1306 static const DBusObjectPathVTable locale_vtable = {
1307 .message_function = locale_message_handler
1310 DBusConnection *bus = NULL;
1315 dbus_error_init(&error);
1317 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1319 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
1324 dbus_connection_set_exit_on_disconnect(bus, FALSE);
1326 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL) ||
1327 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
1332 r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
1333 if (dbus_error_is_set(&error)) {
1334 log_error("Failed to register name on bus: %s", bus_error_message(&error));
1339 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1340 log_error("Failed to acquire name.");
1351 dbus_connection_close(bus);
1352 dbus_connection_unref(bus);
1354 dbus_error_free(&error);
1359 int main(int argc, char *argv[]) {
1361 DBusConnection *bus = NULL;
1362 bool exiting = false;
1364 log_set_target(LOG_TARGET_AUTO);
1365 log_parse_environment();
1370 if (argc == 2 && streq(argv[1], "--introspect")) {
1371 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
1372 "<node>\n", stdout);
1373 fputs(locale_interface, stdout);
1374 fputs("</node>\n", stdout);
1379 log_error("This program takes no arguments.");
1386 log_error("Failed to read locale data: %s", strerror(-r));
1390 r = connect_bus(&bus);
1394 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1397 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
1400 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
1402 bus_async_unregister_and_exit(bus, "org.freedesktop.locale1");
1412 dbus_connection_flush(bus);
1413 dbus_connection_close(bus);
1414 dbus_connection_unref(bus);
1417 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;