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] = {};
123 typedef struct State {
124 char *x11_layout, *x11_model, *x11_variant, *x11_options;
125 char *vc_keymap, *vc_keymap_toggle;
130 static usec_t remain_until = 0;
132 static int free_and_set(char **s, const char *v) {
138 r = strdup_or_null(isempty(v) ? NULL : v, &t);
148 static void free_data_locale(void) {
151 for (p = 0; p < _PROP_MAX; p++) {
157 static void free_data_x11(void) {
158 free(state.x11_layout);
159 free(state.x11_model);
160 free(state.x11_variant);
161 free(state.x11_options);
163 state.x11_layout = state.x11_model = state.x11_variant = state.x11_options = NULL;
166 static void free_data_vconsole(void) {
167 free(state.vc_keymap);
168 free(state.vc_keymap_toggle);
170 state.vc_keymap = state.vc_keymap_toggle = NULL;
173 static void simplify(void) {
176 for (p = 1; p < _PROP_MAX; p++)
177 if (isempty(data[p]) || streq_ptr(data[PROP_LANG], data[p])) {
183 static int read_data_locale(void) {
188 r = parse_env_file("/etc/locale.conf", NEWLINE,
189 "LANG", &data[PROP_LANG],
190 "LANGUAGE", &data[PROP_LANGUAGE],
191 "LC_CTYPE", &data[PROP_LC_CTYPE],
192 "LC_NUMERIC", &data[PROP_LC_NUMERIC],
193 "LC_TIME", &data[PROP_LC_TIME],
194 "LC_COLLATE", &data[PROP_LC_COLLATE],
195 "LC_MONETARY", &data[PROP_LC_MONETARY],
196 "LC_MESSAGES", &data[PROP_LC_MESSAGES],
197 "LC_PAPER", &data[PROP_LC_PAPER],
198 "LC_NAME", &data[PROP_LC_NAME],
199 "LC_ADDRESS", &data[PROP_LC_ADDRESS],
200 "LC_TELEPHONE", &data[PROP_LC_TELEPHONE],
201 "LC_MEASUREMENT", &data[PROP_LC_MEASUREMENT],
202 "LC_IDENTIFICATION", &data[PROP_LC_IDENTIFICATION],
208 /* Fill in what we got passed from systemd. */
210 for (p = 0; p < _PROP_MAX; p++) {
215 e = getenv(names[p]);
234 static void free_data(void) {
236 free_data_vconsole();
240 static int read_data_vconsole(void) {
243 free_data_vconsole();
245 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
246 "KEYMAP", &state.vc_keymap,
247 "KEYMAP_TOGGLE", &state.vc_keymap_toggle,
250 if (r < 0 && r != -ENOENT)
256 static int read_data_x11(void) {
259 bool in_section = false;
263 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
265 return errno == ENOENT ? 0 : -errno;
267 while (fgets(line, sizeof(line), f)) {
273 if (l[0] == 0 || l[0] == '#')
276 if (in_section && first_word(l, "Option")) {
279 a = strv_split_quoted(l);
285 if (strv_length(a) == 3) {
287 if (streq(a[1], "XkbLayout")) {
288 free(state.x11_layout);
289 state.x11_layout = a[2];
291 } else if (streq(a[1], "XkbModel")) {
292 free(state.x11_model);
293 state.x11_model = a[2];
295 } else if (streq(a[1], "XkbVariant")) {
296 free(state.x11_variant);
297 state.x11_variant = a[2];
299 } else if (streq(a[1], "XkbOptions")) {
300 free(state.x11_options);
301 state.x11_options = a[2];
308 } else if (!in_section && first_word(l, "Section")) {
311 a = strv_split_quoted(l);
317 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
321 } else if (in_section && first_word(l, "EndSection"))
330 static int read_data(void) {
333 r = read_data_locale();
334 q = read_data_vconsole();
337 return r < 0 ? r : q < 0 ? q : p;
340 static int write_data_locale(void) {
344 r = load_env_file("/etc/locale.conf", NULL, &l);
345 if (r < 0 && r != -ENOENT)
348 for (p = 0; p < _PROP_MAX; p++) {
353 if (isempty(data[p])) {
354 l = strv_env_unset(l, names[p]);
358 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
363 u = strv_env_set(l, t);
373 if (strv_isempty(l)) {
376 if (unlink("/etc/locale.conf") < 0)
377 return errno == ENOENT ? 0 : -errno;
382 r = write_env_file_label("/etc/locale.conf", l);
388 static void push_data(DBusConnection *bus) {
389 char **l_set = NULL, **l_unset = NULL, **t;
390 int c_set = 0, c_unset = 0, p;
392 DBusMessage *m = NULL, *reply = NULL;
393 DBusMessageIter iter, sub;
395 dbus_error_init(&error);
399 l_set = new0(char*, _PROP_MAX);
400 l_unset = new0(char*, _PROP_MAX);
401 if (!l_set || !l_unset) {
406 for (p = 0; p < _PROP_MAX; p++) {
409 if (isempty(data[p]))
410 l_unset[c_set++] = (char*) names[p];
414 if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) {
419 l_set[c_unset++] = s;
423 assert(c_set + c_unset == _PROP_MAX);
424 m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment");
426 log_error("Could not allocate message.");
430 dbus_message_iter_init_append(m, &iter);
432 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
437 STRV_FOREACH(t, l_unset)
438 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
443 if (!dbus_message_iter_close_container(&iter, &sub) ||
444 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
449 STRV_FOREACH(t, l_set)
450 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
455 if (!dbus_message_iter_close_container(&iter, &sub)) {
460 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
462 log_error("Failed to set locale information: %s", bus_error_message(&error));
468 dbus_message_unref(m);
471 dbus_message_unref(reply);
473 dbus_error_free(&error);
479 static int write_data_vconsole(void) {
483 r = load_env_file("/etc/vconsole.conf", NULL, &l);
484 if (r < 0 && r != -ENOENT)
487 if (isempty(state.vc_keymap))
488 l = strv_env_unset(l, "KEYMAP");
492 s = strappend("KEYMAP=", state.vc_keymap);
498 u = strv_env_set(l, s);
508 if (isempty(state.vc_keymap_toggle))
509 l = strv_env_unset(l, "KEYMAP_TOGGLE");
513 s = strappend("KEYMAP_TOGGLE=", state.vc_keymap_toggle);
519 u = strv_env_set(l, s);
529 if (strv_isempty(l)) {
532 if (unlink("/etc/vconsole.conf") < 0)
533 return errno == ENOENT ? 0 : -errno;
538 r = write_env_file_label("/etc/vconsole.conf", l);
544 static int write_data_x11(void) {
549 if (isempty(state.x11_layout) &&
550 isempty(state.x11_model) &&
551 isempty(state.x11_variant) &&
552 isempty(state.x11_options)) {
554 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
555 return errno == ENOENT ? 0 : -errno;
560 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
562 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
566 fchmod(fileno(f), 0644);
568 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
569 "# manually too freely.\n"
570 "Section \"InputClass\"\n"
571 " Identifier \"system-keyboard\"\n"
572 " MatchIsKeyboard \"on\"\n", f);
574 if (!isempty(state.x11_layout))
575 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", state.x11_layout);
577 if (!isempty(state.x11_model))
578 fprintf(f, " Option \"XkbModel\" \"%s\"\n", state.x11_model);
580 if (!isempty(state.x11_variant))
581 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", state.x11_variant);
583 if (!isempty(state.x11_options))
584 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", state.x11_options);
586 fputs("EndSection\n", f);
589 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
591 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
602 static int load_vconsole_keymap(DBusConnection *bus, DBusError *error) {
603 DBusMessage *m = NULL, *reply = NULL;
604 const char *name = "systemd-vconsole-setup.service", *mode = "replace";
611 dbus_error_init(&_error);
615 m = dbus_message_new_method_call(
616 "org.freedesktop.systemd1",
617 "/org/freedesktop/systemd1",
618 "org.freedesktop.systemd1.Manager",
621 log_error("Could not allocate message.");
626 if (!dbus_message_append_args(m,
627 DBUS_TYPE_STRING, &name,
628 DBUS_TYPE_STRING, &mode,
629 DBUS_TYPE_INVALID)) {
630 log_error("Could not append arguments to message.");
635 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
637 log_error("Failed to issue method call: %s", bus_error_message(error));
646 dbus_message_unref(m);
649 dbus_message_unref(reply);
651 if (error == &_error)
652 dbus_error_free(error);
657 static char *strnulldash(const char *s) {
658 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
661 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
671 if (!fgets(line, sizeof(line), f)) {
674 return errno ? -errno : -EIO;
682 if (l[0] == 0 || l[0] == '#')
685 b = strv_split_quoted(l);
689 if (strv_length(b) < 5) {
690 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
701 static int convert_vconsole_to_x11(DBusConnection *connection) {
702 bool modified = false;
706 if (isempty(state.vc_keymap)) {
709 !isempty(state.x11_layout) ||
710 !isempty(state.x11_model) ||
711 !isempty(state.x11_variant) ||
712 !isempty(state.x11_options);
719 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
727 r = read_next_mapping(f, &n, &a);
736 if (!streq(state.vc_keymap, a[0])) {
741 if (!streq_ptr(state.x11_layout, strnulldash(a[1])) ||
742 !streq_ptr(state.x11_model, strnulldash(a[2])) ||
743 !streq_ptr(state.x11_variant, strnulldash(a[3])) ||
744 !streq_ptr(state.x11_options, strnulldash(a[4]))) {
746 if (free_and_set(&state.x11_layout, strnulldash(a[1])) < 0 ||
747 free_and_set(&state.x11_model, strnulldash(a[2])) < 0 ||
748 free_and_set(&state.x11_variant, strnulldash(a[3])) < 0 ||
749 free_and_set(&state.x11_options, strnulldash(a[4])) < 0) {
767 DBusMessage *changed;
770 r = write_data_x11();
772 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
774 changed = bus_properties_changed_new(
775 "/org/freedesktop/locale1",
776 "org.freedesktop.locale1",
785 b = dbus_connection_send(connection, changed, NULL);
786 dbus_message_unref(changed);
795 static int convert_x11_to_vconsole(DBusConnection *connection) {
796 bool modified = false;
800 if (isempty(state.x11_layout)) {
803 !isempty(state.vc_keymap) ||
804 !isempty(state.vc_keymap_toggle);
810 unsigned best_matching = 0;
811 char *new_keymap = NULL;
813 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
819 unsigned matching = 0;
822 r = read_next_mapping(f, &n, &a);
831 /* Determine how well matching this entry is */
832 if (streq_ptr(state.x11_layout, a[1]))
833 /* If we got an exact match, this is best */
838 x = strcspn(state.x11_layout, ",");
840 /* We have multiple X layouts, look
841 * for an entry that matches our key
842 * with the everything but the first
843 * layout stripped off. */
846 strneq(state.x11_layout, a[1], x))
851 /* If that didn't work, strip
852 * off the other layouts from
855 w = strcspn(a[1], ",");
857 if (x > 0 && x == w &&
858 memcmp(state.x11_layout, a[1], x) == 0)
864 streq_ptr(state.x11_model, a[2])) {
867 if (streq_ptr(state.x11_variant, a[3])) {
870 if (streq_ptr(state.x11_options, a[4]))
875 /* The best matching entry so far, then let's
877 if (matching > best_matching) {
878 best_matching = matching;
881 new_keymap = strdup(a[0]);
895 if (!streq_ptr(state.vc_keymap, new_keymap)) {
896 free(state.vc_keymap);
897 state.vc_keymap = new_keymap;
899 free(state.vc_keymap_toggle);
900 state.vc_keymap_toggle = NULL;
909 DBusMessage *changed;
912 r = write_data_vconsole();
914 log_error("Failed to set virtual console keymap: %s", strerror(-r));
916 changed = bus_properties_changed_new(
917 "/org/freedesktop/locale1",
918 "org.freedesktop.locale1",
920 "VConsoleKeymapToggle\0");
925 b = dbus_connection_send(connection, changed, NULL);
926 dbus_message_unref(changed);
931 return load_vconsole_keymap(connection, NULL);
937 static int append_locale(DBusMessageIter *i, const char *property, void *userdata) {
941 l = new0(char*, _PROP_MAX+1);
945 for (p = 0; p < _PROP_MAX; p++) {
948 if (isempty(data[p]))
951 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
959 r = bus_property_append_strv(i, property, (void*) l);
965 static const BusProperty bus_locale_properties[] = {
966 { "Locale", append_locale, "as", 0 },
967 { "X11Layout", bus_property_append_string, "s", offsetof(State, x11_layout), true },
968 { "X11Model", bus_property_append_string, "s", offsetof(State, x11_model), true },
969 { "X11Variant", bus_property_append_string, "s", offsetof(State, x11_variant), true },
970 { "X11Options", bus_property_append_string, "s", offsetof(State, x11_options), true },
971 { "VConsoleKeymap", bus_property_append_string, "s", offsetof(State, vc_keymap), true },
972 { "VConsoleKeymapToggle", bus_property_append_string, "s", offsetof(State, vc_keymap_toggle), true },
976 static const BusBoundProperties bps[] = {
977 { "org.freedesktop.locale1", bus_locale_properties, &state },
981 static DBusHandlerResult locale_message_handler(
982 DBusConnection *connection,
983 DBusMessage *message,
986 DBusMessage *reply = NULL, *changed = NULL;
993 dbus_error_init(&error);
995 if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) {
996 char **l = NULL, **i;
997 dbus_bool_t interactive;
998 DBusMessageIter iter;
999 bool modified = false;
1000 bool passed[_PROP_MAX] = {};
1003 if (!dbus_message_iter_init(message, &iter))
1004 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1006 r = bus_parse_strv_iter(&iter, &l);
1011 return bus_send_error_reply(connection, message, NULL, r);
1014 if (!dbus_message_iter_next(&iter) ||
1015 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
1017 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1020 dbus_message_iter_get_basic(&iter, &interactive);
1022 /* Check whether a variable changed and if so valid */
1023 STRV_FOREACH(i, l) {
1026 for (p = 0; p < _PROP_MAX; p++) {
1029 k = strlen(names[p]);
1030 if (startswith(*i, names[p]) &&
1032 string_is_safe((*i) + k + 1)) {
1036 if (!streq_ptr(*i + k + 1, data[p]))
1045 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1049 /* Check whether a variable is unset */
1051 for (p = 0; p < _PROP_MAX; p++)
1052 if (!isempty(data[p]) && !passed[p]) {
1060 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error);
1063 return bus_send_error_reply(connection, message, &error, r);
1066 STRV_FOREACH(i, l) {
1067 for (p = 0; p < _PROP_MAX; p++) {
1070 k = strlen(names[p]);
1071 if (startswith(*i, names[p]) && (*i)[k] == '=') {
1074 t = strdup(*i + k + 1);
1090 for (p = 0; p < _PROP_MAX; p++) {
1100 r = write_data_locale();
1102 log_error("Failed to set locale: %s", strerror(-r));
1103 return bus_send_error_reply(connection, message, NULL, r);
1106 push_data(connection);
1108 log_info("Changed locale information.");
1110 changed = bus_properties_changed_new(
1111 "/org/freedesktop/locale1",
1112 "org.freedesktop.locale1",
1119 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
1121 const char *keymap, *keymap_toggle;
1122 dbus_bool_t convert, interactive;
1124 if (!dbus_message_get_args(
1127 DBUS_TYPE_STRING, &keymap,
1128 DBUS_TYPE_STRING, &keymap_toggle,
1129 DBUS_TYPE_BOOLEAN, &convert,
1130 DBUS_TYPE_BOOLEAN, &interactive,
1132 return bus_send_error_reply(connection, message, &error, -EINVAL);
1134 if (isempty(keymap))
1137 if (isempty(keymap_toggle))
1138 keymap_toggle = NULL;
1140 if (!streq_ptr(keymap, state.vc_keymap) ||
1141 !streq_ptr(keymap_toggle, state.vc_keymap_toggle)) {
1143 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
1144 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
1145 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1147 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1149 return bus_send_error_reply(connection, message, &error, r);
1151 if (free_and_set(&state.vc_keymap, keymap) < 0 ||
1152 free_and_set(&state.vc_keymap_toggle, keymap_toggle) < 0)
1155 r = write_data_vconsole();
1157 log_error("Failed to set virtual console keymap: %s", strerror(-r));
1158 return bus_send_error_reply(connection, message, NULL, r);
1161 log_info("Changed virtual console keymap to '%s'", strempty(state.vc_keymap));
1163 r = load_vconsole_keymap(connection, NULL);
1165 log_error("Failed to request keymap reload: %s", strerror(-r));
1167 changed = bus_properties_changed_new(
1168 "/org/freedesktop/locale1",
1169 "org.freedesktop.locale1",
1171 "VConsoleKeymapToggle\0");
1176 r = convert_vconsole_to_x11(connection);
1179 log_error("Failed to convert keymap data: %s", strerror(-r));
1183 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) {
1185 const char *layout, *model, *variant, *options;
1186 dbus_bool_t convert, interactive;
1188 if (!dbus_message_get_args(
1191 DBUS_TYPE_STRING, &layout,
1192 DBUS_TYPE_STRING, &model,
1193 DBUS_TYPE_STRING, &variant,
1194 DBUS_TYPE_STRING, &options,
1195 DBUS_TYPE_BOOLEAN, &convert,
1196 DBUS_TYPE_BOOLEAN, &interactive,
1198 return bus_send_error_reply(connection, message, &error, -EINVAL);
1200 if (isempty(layout))
1206 if (isempty(variant))
1209 if (isempty(options))
1212 if (!streq_ptr(layout, state.x11_layout) ||
1213 !streq_ptr(model, state.x11_model) ||
1214 !streq_ptr(variant, state.x11_variant) ||
1215 !streq_ptr(options, state.x11_options)) {
1217 if ((layout && !string_is_safe(layout)) ||
1218 (model && !string_is_safe(model)) ||
1219 (variant && !string_is_safe(variant)) ||
1220 (options && !string_is_safe(options)))
1221 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1223 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1225 return bus_send_error_reply(connection, message, &error, r);
1227 if (free_and_set(&state.x11_layout, layout) < 0 ||
1228 free_and_set(&state.x11_model, model) < 0 ||
1229 free_and_set(&state.x11_variant, variant) < 0 ||
1230 free_and_set(&state.x11_options, options) < 0)
1233 r = write_data_x11();
1235 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1236 return bus_send_error_reply(connection, message, NULL, r);
1239 log_info("Changed X11 keyboard layout to '%s'", strempty(state.x11_layout));
1241 changed = bus_properties_changed_new(
1242 "/org/freedesktop/locale1",
1243 "org.freedesktop.locale1",
1252 r = convert_x11_to_vconsole(connection);
1255 log_error("Failed to convert keymap data: %s", strerror(-r));
1259 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
1261 if (!(reply = dbus_message_new_method_return(message)))
1264 if (!bus_maybe_send_reply(connection, message, reply))
1267 dbus_message_unref(reply);
1272 if (!dbus_connection_send(connection, changed, NULL))
1275 dbus_message_unref(changed);
1278 return DBUS_HANDLER_RESULT_HANDLED;
1282 dbus_message_unref(reply);
1285 dbus_message_unref(changed);
1287 dbus_error_free(&error);
1289 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1292 static int connect_bus(DBusConnection **_bus) {
1293 static const DBusObjectPathVTable locale_vtable = {
1294 .message_function = locale_message_handler
1297 DBusConnection *bus = NULL;
1302 dbus_error_init(&error);
1304 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1306 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
1311 dbus_connection_set_exit_on_disconnect(bus, FALSE);
1313 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL) ||
1314 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
1319 r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
1320 if (dbus_error_is_set(&error)) {
1321 log_error("Failed to register name on bus: %s", bus_error_message(&error));
1326 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1327 log_error("Failed to acquire name.");
1338 dbus_connection_close(bus);
1339 dbus_connection_unref(bus);
1341 dbus_error_free(&error);
1346 int main(int argc, char *argv[]) {
1348 DBusConnection *bus = NULL;
1349 bool exiting = false;
1351 log_set_target(LOG_TARGET_AUTO);
1352 log_parse_environment();
1357 if (argc == 2 && streq(argv[1], "--introspect")) {
1358 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
1359 "<node>\n", stdout);
1360 fputs(locale_interface, stdout);
1361 fputs("</node>\n", stdout);
1366 log_error("This program takes no arguments.");
1373 log_error("Failed to read locale data: %s", strerror(-r));
1377 r = connect_bus(&bus);
1381 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1384 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
1387 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
1389 bus_async_unregister_and_exit(bus, "org.freedesktop.locale1");
1399 dbus_connection_flush(bus);
1400 dbus_connection_close(bus);
1401 dbus_connection_unref(bus);
1404 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;