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"
36 #include "fileio-label.h"
40 " <interface name=\"org.freedesktop.locale1\">\n" \
41 " <property name=\"Locale\" type=\"as\" access=\"read\"/>\n" \
42 " <property name=\"VConsoleKeymap\" type=\"s\" access=\"read\"/>\n" \
43 " <property name=\"VConsoleKeymapToggle\" type=\"s\" access=\"read\"/>\n" \
44 " <property name=\"X11Layout\" type=\"s\" access=\"read\"/>\n" \
45 " <property name=\"X11Model\" type=\"s\" access=\"read\"/>\n" \
46 " <property name=\"X11Variant\" type=\"s\" access=\"read\"/>\n" \
47 " <property name=\"X11Options\" type=\"s\" access=\"read\"/>\n" \
48 " <method name=\"SetLocale\">\n" \
49 " <arg name=\"locale\" type=\"as\" direction=\"in\"/>\n" \
50 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
52 " <method name=\"SetVConsoleKeyboard\">\n" \
53 " <arg name=\"keymap\" type=\"s\" direction=\"in\"/>\n" \
54 " <arg name=\"keymap_toggle\" type=\"s\" direction=\"in\"/>\n" \
55 " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \
56 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
58 " <method name=\"SetX11Keyboard\">\n" \
59 " <arg name=\"layout\" type=\"s\" direction=\"in\"/>\n" \
60 " <arg name=\"model\" type=\"s\" direction=\"in\"/>\n" \
61 " <arg name=\"variant\" type=\"s\" direction=\"in\"/>\n" \
62 " <arg name=\"options\" type=\"s\" direction=\"in\"/>\n" \
63 " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \
64 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
68 #define INTROSPECTION \
69 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
72 BUS_PROPERTIES_INTERFACE \
73 BUS_INTROSPECTABLE_INTERFACE \
77 #define INTERFACES_LIST \
78 BUS_GENERIC_INTERFACES_LIST \
79 "org.freedesktop.locale1\0"
81 const char locale_interface[] _introspect_("locale1") = INTERFACE;
84 /* We don't list LC_ALL here on purpose. People should be
85 * using LANG instead. */
100 PROP_LC_IDENTIFICATION,
104 static const char * const names[_PROP_MAX] = {
105 [PROP_LANG] = "LANG",
106 [PROP_LANGUAGE] = "LANGUAGE",
107 [PROP_LC_CTYPE] = "LC_CTYPE",
108 [PROP_LC_NUMERIC] = "LC_NUMERIC",
109 [PROP_LC_TIME] = "LC_TIME",
110 [PROP_LC_COLLATE] = "LC_COLLATE",
111 [PROP_LC_MONETARY] = "LC_MONETARY",
112 [PROP_LC_MESSAGES] = "LC_MESSAGES",
113 [PROP_LC_PAPER] = "LC_PAPER",
114 [PROP_LC_NAME] = "LC_NAME",
115 [PROP_LC_ADDRESS] = "LC_ADDRESS",
116 [PROP_LC_TELEPHONE] = "LC_TELEPHONE",
117 [PROP_LC_MEASUREMENT] = "LC_MEASUREMENT",
118 [PROP_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
121 static char *data[_PROP_MAX] = {
137 typedef struct State {
138 char *x11_layout, *x11_model, *x11_variant, *x11_options;
139 char *vc_keymap, *vc_keymap_toggle;
144 static usec_t remain_until = 0;
146 static int free_and_set(char **s, const char *v) {
152 r = strdup_or_null(isempty(v) ? NULL : v, &t);
162 static void free_data_locale(void) {
165 for (p = 0; p < _PROP_MAX; p++) {
171 static void free_data_x11(void) {
172 free(state.x11_layout);
173 free(state.x11_model);
174 free(state.x11_variant);
175 free(state.x11_options);
177 state.x11_layout = state.x11_model = state.x11_variant = state.x11_options = NULL;
180 static void free_data_vconsole(void) {
181 free(state.vc_keymap);
182 free(state.vc_keymap_toggle);
184 state.vc_keymap = state.vc_keymap_toggle = NULL;
187 static void simplify(void) {
190 for (p = 1; p < _PROP_MAX; p++)
191 if (isempty(data[p]) || streq_ptr(data[PROP_LANG], data[p])) {
197 static int read_data_locale(void) {
202 r = parse_env_file("/etc/locale.conf", NEWLINE,
203 "LANG", &data[PROP_LANG],
204 "LANGUAGE", &data[PROP_LANGUAGE],
205 "LC_CTYPE", &data[PROP_LC_CTYPE],
206 "LC_NUMERIC", &data[PROP_LC_NUMERIC],
207 "LC_TIME", &data[PROP_LC_TIME],
208 "LC_COLLATE", &data[PROP_LC_COLLATE],
209 "LC_MONETARY", &data[PROP_LC_MONETARY],
210 "LC_MESSAGES", &data[PROP_LC_MESSAGES],
211 "LC_PAPER", &data[PROP_LC_PAPER],
212 "LC_NAME", &data[PROP_LC_NAME],
213 "LC_ADDRESS", &data[PROP_LC_ADDRESS],
214 "LC_TELEPHONE", &data[PROP_LC_TELEPHONE],
215 "LC_MEASUREMENT", &data[PROP_LC_MEASUREMENT],
216 "LC_IDENTIFICATION", &data[PROP_LC_IDENTIFICATION],
222 /* Fill in what we got passed from systemd. */
224 for (p = 0; p < _PROP_MAX; p++) {
229 e = getenv(names[p]);
248 static void free_data(void) {
250 free_data_vconsole();
254 static int read_data_vconsole(void) {
257 free_data_vconsole();
259 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
260 "KEYMAP", &state.vc_keymap,
261 "KEYMAP_TOGGLE", &state.vc_keymap_toggle,
264 if (r < 0 && r != -ENOENT)
270 static int read_data_x11(void) {
273 bool in_section = false;
277 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
279 return errno == ENOENT ? 0 : -errno;
281 while (fgets(line, sizeof(line), f)) {
287 if (l[0] == 0 || l[0] == '#')
290 if (in_section && first_word(l, "Option")) {
293 a = strv_split_quoted(l);
299 if (strv_length(a) == 3) {
301 if (streq(a[1], "XkbLayout")) {
302 free(state.x11_layout);
303 state.x11_layout = a[2];
305 } else if (streq(a[1], "XkbModel")) {
306 free(state.x11_model);
307 state.x11_model = a[2];
309 } else if (streq(a[1], "XkbVariant")) {
310 free(state.x11_variant);
311 state.x11_variant = a[2];
313 } else if (streq(a[1], "XkbOptions")) {
314 free(state.x11_options);
315 state.x11_options = a[2];
322 } else if (!in_section && first_word(l, "Section")) {
325 a = strv_split_quoted(l);
331 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
335 } else if (in_section && first_word(l, "EndSection"))
344 static int read_data(void) {
347 r = read_data_locale();
348 q = read_data_vconsole();
351 return r < 0 ? r : q < 0 ? q : p;
354 static int write_data_locale(void) {
358 r = load_env_file("/etc/locale.conf", NULL, &l);
359 if (r < 0 && r != -ENOENT)
362 for (p = 0; p < _PROP_MAX; p++) {
367 if (isempty(data[p])) {
368 l = strv_env_unset(l, names[p]);
372 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
377 u = strv_env_set(l, t);
387 if (strv_isempty(l)) {
390 if (unlink("/etc/locale.conf") < 0)
391 return errno == ENOENT ? 0 : -errno;
396 r = write_env_file_label("/etc/locale.conf", l);
402 static void push_data(DBusConnection *bus) {
403 char **l_set = NULL, **l_unset = NULL, **t;
404 int c_set = 0, c_unset = 0, p;
406 DBusMessage *m = NULL, *reply = NULL;
407 DBusMessageIter iter, sub;
409 dbus_error_init(&error);
413 l_set = new0(char*, _PROP_MAX);
414 l_unset = new0(char*, _PROP_MAX);
415 if (!l_set || !l_unset) {
420 for (p = 0; p < _PROP_MAX; p++) {
423 if (isempty(data[p]))
424 l_unset[c_set++] = (char*) names[p];
428 if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) {
433 l_set[c_unset++] = s;
437 assert(c_set + c_unset == _PROP_MAX);
438 m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment");
440 log_error("Could not allocate message.");
444 dbus_message_iter_init_append(m, &iter);
446 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
451 STRV_FOREACH(t, l_unset)
452 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
457 if (!dbus_message_iter_close_container(&iter, &sub) ||
458 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
463 STRV_FOREACH(t, l_set)
464 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
469 if (!dbus_message_iter_close_container(&iter, &sub)) {
474 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
476 log_error("Failed to set locale information: %s", bus_error_message(&error));
482 dbus_message_unref(m);
485 dbus_message_unref(reply);
487 dbus_error_free(&error);
493 static int write_data_vconsole(void) {
497 r = load_env_file("/etc/vconsole.conf", NULL, &l);
498 if (r < 0 && r != -ENOENT)
501 if (isempty(state.vc_keymap))
502 l = strv_env_unset(l, "KEYMAP");
506 s = strappend("KEYMAP=", state.vc_keymap);
512 u = strv_env_set(l, s);
522 if (isempty(state.vc_keymap_toggle))
523 l = strv_env_unset(l, "KEYMAP_TOGGLE");
527 s = strappend("KEYMAP_TOGGLE=", state.vc_keymap_toggle);
533 u = strv_env_set(l, s);
543 if (strv_isempty(l)) {
546 if (unlink("/etc/vconsole.conf") < 0)
547 return errno == ENOENT ? 0 : -errno;
552 r = write_env_file_label("/etc/vconsole.conf", l);
558 static int write_data_x11(void) {
563 if (isempty(state.x11_layout) &&
564 isempty(state.x11_model) &&
565 isempty(state.x11_variant) &&
566 isempty(state.x11_options)) {
568 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
569 return errno == ENOENT ? 0 : -errno;
574 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
576 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
580 fchmod(fileno(f), 0644);
582 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
583 "# manually too freely.\n"
584 "Section \"InputClass\"\n"
585 " Identifier \"system-keyboard\"\n"
586 " MatchIsKeyboard \"on\"\n", f);
588 if (!isempty(state.x11_layout))
589 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", state.x11_layout);
591 if (!isempty(state.x11_model))
592 fprintf(f, " Option \"XkbModel\" \"%s\"\n", state.x11_model);
594 if (!isempty(state.x11_variant))
595 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", state.x11_variant);
597 if (!isempty(state.x11_options))
598 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", state.x11_options);
600 fputs("EndSection\n", f);
603 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
605 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
616 static int load_vconsole_keymap(DBusConnection *bus, DBusError *error) {
617 DBusMessage *m = NULL, *reply = NULL;
618 const char *name = "systemd-vconsole-setup.service", *mode = "replace";
625 dbus_error_init(&_error);
629 m = dbus_message_new_method_call(
630 "org.freedesktop.systemd1",
631 "/org/freedesktop/systemd1",
632 "org.freedesktop.systemd1.Manager",
635 log_error("Could not allocate message.");
640 if (!dbus_message_append_args(m,
641 DBUS_TYPE_STRING, &name,
642 DBUS_TYPE_STRING, &mode,
643 DBUS_TYPE_INVALID)) {
644 log_error("Could not append arguments to message.");
649 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
651 log_error("Failed to issue method call: %s", bus_error_message(error));
660 dbus_message_unref(m);
663 dbus_message_unref(reply);
665 if (error == &_error)
666 dbus_error_free(error);
671 static char *strnulldash(const char *s) {
672 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
675 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
685 if (!fgets(line, sizeof(line), f)) {
688 return errno ? -errno : -EIO;
696 if (l[0] == 0 || l[0] == '#')
699 b = strv_split_quoted(l);
703 if (strv_length(b) < 5) {
704 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
715 static int convert_vconsole_to_x11(DBusConnection *connection) {
716 bool modified = false;
720 if (isempty(state.vc_keymap)) {
723 !isempty(state.x11_layout) ||
724 !isempty(state.x11_model) ||
725 !isempty(state.x11_variant) ||
726 !isempty(state.x11_options);
733 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
741 r = read_next_mapping(f, &n, &a);
750 if (!streq(state.vc_keymap, a[0])) {
755 if (!streq_ptr(state.x11_layout, strnulldash(a[1])) ||
756 !streq_ptr(state.x11_model, strnulldash(a[2])) ||
757 !streq_ptr(state.x11_variant, strnulldash(a[3])) ||
758 !streq_ptr(state.x11_options, strnulldash(a[4]))) {
760 if (free_and_set(&state.x11_layout, strnulldash(a[1])) < 0 ||
761 free_and_set(&state.x11_model, strnulldash(a[2])) < 0 ||
762 free_and_set(&state.x11_variant, strnulldash(a[3])) < 0 ||
763 free_and_set(&state.x11_options, strnulldash(a[4])) < 0) {
781 DBusMessage *changed;
784 r = write_data_x11();
786 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
788 changed = bus_properties_changed_new(
789 "/org/freedesktop/locale1",
790 "org.freedesktop.locale1",
799 b = dbus_connection_send(connection, changed, NULL);
800 dbus_message_unref(changed);
809 static int convert_x11_to_vconsole(DBusConnection *connection) {
810 bool modified = false;
814 if (isempty(state.x11_layout)) {
817 !isempty(state.vc_keymap) ||
818 !isempty(state.vc_keymap_toggle);
824 unsigned best_matching = 0;
825 char *new_keymap = NULL;
827 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
833 unsigned matching = 0;
836 r = read_next_mapping(f, &n, &a);
845 /* Determine how well matching this entry is */
846 if (streq_ptr(state.x11_layout, a[1]))
847 /* If we got an exact match, this is best */
852 x = strcspn(state.x11_layout, ",");
854 /* We have multiple X layouts, look
855 * for an entry that matches our key
856 * with the everything but the first
857 * layout stripped off. */
860 strneq(state.x11_layout, a[1], x))
865 /* If that didn't work, strip
866 * off the other layouts from
869 w = strcspn(a[1], ",");
871 if (x > 0 && x == w &&
872 memcmp(state.x11_layout, a[1], x) == 0)
878 streq_ptr(state.x11_model, a[2])) {
881 if (streq_ptr(state.x11_variant, a[3])) {
884 if (streq_ptr(state.x11_options, a[4]))
889 /* The best matching entry so far, then let's
891 if (matching > best_matching) {
892 best_matching = matching;
895 new_keymap = strdup(a[0]);
909 if (!streq_ptr(state.vc_keymap, new_keymap)) {
910 free(state.vc_keymap);
911 state.vc_keymap = new_keymap;
913 free(state.vc_keymap_toggle);
914 state.vc_keymap_toggle = NULL;
923 DBusMessage *changed;
926 r = write_data_vconsole();
928 log_error("Failed to set virtual console keymap: %s", strerror(-r));
930 changed = bus_properties_changed_new(
931 "/org/freedesktop/locale1",
932 "org.freedesktop.locale1",
934 "VConsoleKeymapToggle\0");
939 b = dbus_connection_send(connection, changed, NULL);
940 dbus_message_unref(changed);
945 return load_vconsole_keymap(connection, NULL);
951 static int append_locale(DBusMessageIter *i, const char *property, void *userdata) {
955 l = new0(char*, _PROP_MAX+1);
959 for (p = 0; p < _PROP_MAX; p++) {
962 if (isempty(data[p]))
965 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
973 r = bus_property_append_strv(i, property, (void*) l);
979 static const BusProperty bus_locale_properties[] = {
980 { "Locale", append_locale, "as", 0 },
981 { "X11Layout", bus_property_append_string, "s", offsetof(State, x11_layout), true },
982 { "X11Model", bus_property_append_string, "s", offsetof(State, x11_model), true },
983 { "X11Variant", bus_property_append_string, "s", offsetof(State, x11_variant), true },
984 { "X11Options", bus_property_append_string, "s", offsetof(State, x11_options), true },
985 { "VConsoleKeymap", bus_property_append_string, "s", offsetof(State, vc_keymap), true },
986 { "VConsoleKeymapToggle", bus_property_append_string, "s", offsetof(State, vc_keymap_toggle), true },
990 static const BusBoundProperties bps[] = {
991 { "org.freedesktop.locale1", bus_locale_properties, &state },
995 static DBusHandlerResult locale_message_handler(
996 DBusConnection *connection,
997 DBusMessage *message,
1000 DBusMessage *reply = NULL, *changed = NULL;
1007 dbus_error_init(&error);
1009 if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) {
1010 char **l = NULL, **i;
1011 dbus_bool_t interactive;
1012 DBusMessageIter iter;
1013 bool modified = false;
1014 bool passed[_PROP_MAX] = {};
1017 if (!dbus_message_iter_init(message, &iter))
1018 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1020 r = bus_parse_strv_iter(&iter, &l);
1025 return bus_send_error_reply(connection, message, NULL, r);
1028 if (!dbus_message_iter_next(&iter) ||
1029 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
1031 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1034 dbus_message_iter_get_basic(&iter, &interactive);
1036 /* Check whether a variable changed and if so valid */
1037 STRV_FOREACH(i, l) {
1040 for (p = 0; p < _PROP_MAX; p++) {
1043 k = strlen(names[p]);
1044 if (startswith(*i, names[p]) &&
1046 string_is_safe((*i) + k + 1)) {
1050 if (!streq_ptr(*i + k + 1, data[p]))
1059 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1063 /* Check whether a variable is unset */
1065 for (p = 0; p < _PROP_MAX; p++)
1066 if (!isempty(data[p]) && !passed[p]) {
1074 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error);
1077 return bus_send_error_reply(connection, message, &error, r);
1080 STRV_FOREACH(i, l) {
1081 for (p = 0; p < _PROP_MAX; p++) {
1084 k = strlen(names[p]);
1085 if (startswith(*i, names[p]) && (*i)[k] == '=') {
1088 t = strdup(*i + k + 1);
1104 for (p = 0; p < _PROP_MAX; p++) {
1114 r = write_data_locale();
1116 log_error("Failed to set locale: %s", strerror(-r));
1117 return bus_send_error_reply(connection, message, NULL, r);
1120 push_data(connection);
1122 log_info("Changed locale information.");
1124 changed = bus_properties_changed_new(
1125 "/org/freedesktop/locale1",
1126 "org.freedesktop.locale1",
1133 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
1135 const char *keymap, *keymap_toggle;
1136 dbus_bool_t convert, interactive;
1138 if (!dbus_message_get_args(
1141 DBUS_TYPE_STRING, &keymap,
1142 DBUS_TYPE_STRING, &keymap_toggle,
1143 DBUS_TYPE_BOOLEAN, &convert,
1144 DBUS_TYPE_BOOLEAN, &interactive,
1146 return bus_send_error_reply(connection, message, &error, -EINVAL);
1148 if (isempty(keymap))
1151 if (isempty(keymap_toggle))
1152 keymap_toggle = NULL;
1154 if (!streq_ptr(keymap, state.vc_keymap) ||
1155 !streq_ptr(keymap_toggle, state.vc_keymap_toggle)) {
1157 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
1158 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
1159 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1161 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1163 return bus_send_error_reply(connection, message, &error, r);
1165 if (free_and_set(&state.vc_keymap, keymap) < 0 ||
1166 free_and_set(&state.vc_keymap_toggle, keymap_toggle) < 0)
1169 r = write_data_vconsole();
1171 log_error("Failed to set virtual console keymap: %s", strerror(-r));
1172 return bus_send_error_reply(connection, message, NULL, r);
1175 log_info("Changed virtual console keymap to '%s'", strempty(state.vc_keymap));
1177 r = load_vconsole_keymap(connection, NULL);
1179 log_error("Failed to request keymap reload: %s", strerror(-r));
1181 changed = bus_properties_changed_new(
1182 "/org/freedesktop/locale1",
1183 "org.freedesktop.locale1",
1185 "VConsoleKeymapToggle\0");
1190 r = convert_vconsole_to_x11(connection);
1193 log_error("Failed to convert keymap data: %s", strerror(-r));
1197 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) {
1199 const char *layout, *model, *variant, *options;
1200 dbus_bool_t convert, interactive;
1202 if (!dbus_message_get_args(
1205 DBUS_TYPE_STRING, &layout,
1206 DBUS_TYPE_STRING, &model,
1207 DBUS_TYPE_STRING, &variant,
1208 DBUS_TYPE_STRING, &options,
1209 DBUS_TYPE_BOOLEAN, &convert,
1210 DBUS_TYPE_BOOLEAN, &interactive,
1212 return bus_send_error_reply(connection, message, &error, -EINVAL);
1214 if (isempty(layout))
1220 if (isempty(variant))
1223 if (isempty(options))
1226 if (!streq_ptr(layout, state.x11_layout) ||
1227 !streq_ptr(model, state.x11_model) ||
1228 !streq_ptr(variant, state.x11_variant) ||
1229 !streq_ptr(options, state.x11_options)) {
1231 if ((layout && !string_is_safe(layout)) ||
1232 (model && !string_is_safe(model)) ||
1233 (variant && !string_is_safe(variant)) ||
1234 (options && !string_is_safe(options)))
1235 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1237 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1239 return bus_send_error_reply(connection, message, &error, r);
1241 if (free_and_set(&state.x11_layout, layout) < 0 ||
1242 free_and_set(&state.x11_model, model) < 0 ||
1243 free_and_set(&state.x11_variant, variant) < 0 ||
1244 free_and_set(&state.x11_options, options) < 0)
1247 r = write_data_x11();
1249 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1250 return bus_send_error_reply(connection, message, NULL, r);
1253 log_info("Changed X11 keyboard layout to '%s'", strempty(state.x11_layout));
1255 changed = bus_properties_changed_new(
1256 "/org/freedesktop/locale1",
1257 "org.freedesktop.locale1",
1266 r = convert_x11_to_vconsole(connection);
1269 log_error("Failed to convert keymap data: %s", strerror(-r));
1273 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
1275 if (!(reply = dbus_message_new_method_return(message)))
1278 if (!bus_maybe_send_reply(connection, message, reply))
1281 dbus_message_unref(reply);
1286 if (!dbus_connection_send(connection, changed, NULL))
1289 dbus_message_unref(changed);
1292 return DBUS_HANDLER_RESULT_HANDLED;
1296 dbus_message_unref(reply);
1299 dbus_message_unref(changed);
1301 dbus_error_free(&error);
1303 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1306 static int connect_bus(DBusConnection **_bus) {
1307 static const DBusObjectPathVTable locale_vtable = {
1308 .message_function = locale_message_handler
1311 DBusConnection *bus = NULL;
1316 dbus_error_init(&error);
1318 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1320 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
1325 dbus_connection_set_exit_on_disconnect(bus, FALSE);
1327 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL) ||
1328 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
1333 r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
1334 if (dbus_error_is_set(&error)) {
1335 log_error("Failed to register name on bus: %s", bus_error_message(&error));
1340 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1341 log_error("Failed to acquire name.");
1352 dbus_connection_close(bus);
1353 dbus_connection_unref(bus);
1355 dbus_error_free(&error);
1360 int main(int argc, char *argv[]) {
1362 DBusConnection *bus = NULL;
1363 bool exiting = false;
1365 log_set_target(LOG_TARGET_AUTO);
1366 log_parse_environment();
1371 if (argc == 2 && streq(argv[1], "--introspect")) {
1372 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
1373 "<node>\n", stdout);
1374 fputs(locale_interface, stdout);
1375 fputs("</node>\n", stdout);
1380 log_error("This program takes no arguments.");
1387 log_error("Failed to read locale data: %s", strerror(-r));
1391 r = connect_bus(&bus);
1395 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1398 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
1401 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
1403 bus_async_unregister_and_exit(bus, "org.freedesktop.locale1");
1413 dbus_connection_flush(bus);
1414 dbus_connection_close(bus);
1415 dbus_connection_unref(bus);
1418 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;