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);
808 _cleanup_fclose_ FILE *f;
810 unsigned best_matching = 0;
811 char *new_keymap = NULL;
813 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
818 _cleanup_strv_free_ char **a = NULL;
819 unsigned matching = 0;
822 r = read_next_mapping(f, &n, &a);
828 /* Determine how well matching this entry is */
829 if (streq_ptr(state.x11_layout, a[1]))
830 /* If we got an exact match, this is best */
835 x = strcspn(state.x11_layout, ",");
837 /* We have multiple X layouts, look
838 * for an entry that matches our key
839 * with the everything but the first
840 * layout stripped off. */
843 strneq(state.x11_layout, a[1], x))
848 /* If that didn't work, strip
849 * off the other layouts from
852 w = strcspn(a[1], ",");
854 if (x > 0 && x == w &&
855 memcmp(state.x11_layout, a[1], x) == 0)
861 streq_ptr(state.x11_model, a[2])) {
864 if (streq_ptr(state.x11_variant, a[3])) {
867 if (streq_ptr(state.x11_options, a[4]))
872 /* The best matching entry so far, then let's
874 if (matching > best_matching) {
875 best_matching = matching;
878 new_keymap = strdup(a[0]);
884 if (!streq_ptr(state.vc_keymap, new_keymap)) {
885 free(state.vc_keymap);
886 state.vc_keymap = new_keymap;
888 free(state.vc_keymap_toggle);
889 state.vc_keymap_toggle = NULL;
899 _cleanup_dbus_message_unref_ DBusMessage *changed = NULL;
902 r = write_data_vconsole();
904 log_error("Failed to set virtual console keymap: %s", strerror(-r));
906 changed = bus_properties_changed_new(
907 "/org/freedesktop/locale1",
908 "org.freedesktop.locale1",
910 "VConsoleKeymapToggle\0");
914 b = dbus_connection_send(connection, changed, NULL);
918 return load_vconsole_keymap(connection, NULL);
924 static int append_locale(DBusMessageIter *i, const char *property, void *userdata) {
926 _cleanup_strv_free_ char **l = NULL;
928 l = new0(char*, _PROP_MAX+1);
932 for (p = 0, c = 0; p < _PROP_MAX; p++) {
935 if (isempty(data[p]))
938 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0)
944 return bus_property_append_strv(i, property, (void*) l);
947 static const BusProperty bus_locale_properties[] = {
948 { "Locale", append_locale, "as", 0 },
949 { "X11Layout", bus_property_append_string, "s", offsetof(State, x11_layout), true },
950 { "X11Model", bus_property_append_string, "s", offsetof(State, x11_model), true },
951 { "X11Variant", bus_property_append_string, "s", offsetof(State, x11_variant), true },
952 { "X11Options", bus_property_append_string, "s", offsetof(State, x11_options), true },
953 { "VConsoleKeymap", bus_property_append_string, "s", offsetof(State, vc_keymap), true },
954 { "VConsoleKeymapToggle", bus_property_append_string, "s", offsetof(State, vc_keymap_toggle), true },
958 static const BusBoundProperties bps[] = {
959 { "org.freedesktop.locale1", bus_locale_properties, &state },
963 static DBusHandlerResult locale_message_handler(
964 DBusConnection *connection,
965 DBusMessage *message,
968 DBusMessage *reply = NULL, *changed = NULL;
975 dbus_error_init(&error);
977 if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) {
978 char **l = NULL, **i;
979 dbus_bool_t interactive;
980 DBusMessageIter iter;
981 bool modified = false;
982 bool passed[_PROP_MAX] = {};
985 if (!dbus_message_iter_init(message, &iter))
986 return bus_send_error_reply(connection, message, NULL, -EINVAL);
988 r = bus_parse_strv_iter(&iter, &l);
993 return bus_send_error_reply(connection, message, NULL, r);
996 if (!dbus_message_iter_next(&iter) ||
997 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
999 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1002 dbus_message_iter_get_basic(&iter, &interactive);
1004 /* Check whether a variable changed and if so valid */
1005 STRV_FOREACH(i, l) {
1008 for (p = 0; p < _PROP_MAX; p++) {
1011 k = strlen(names[p]);
1012 if (startswith(*i, names[p]) &&
1014 string_is_safe((*i) + k + 1)) {
1018 if (!streq_ptr(*i + k + 1, data[p]))
1027 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1031 /* Check whether a variable is unset */
1033 for (p = 0; p < _PROP_MAX; p++)
1034 if (!isempty(data[p]) && !passed[p]) {
1042 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error);
1045 return bus_send_error_reply(connection, message, &error, r);
1048 STRV_FOREACH(i, l) {
1049 for (p = 0; p < _PROP_MAX; p++) {
1052 k = strlen(names[p]);
1053 if (startswith(*i, names[p]) && (*i)[k] == '=') {
1056 t = strdup(*i + k + 1);
1072 for (p = 0; p < _PROP_MAX; p++) {
1082 r = write_data_locale();
1084 log_error("Failed to set locale: %s", strerror(-r));
1085 return bus_send_error_reply(connection, message, NULL, r);
1088 push_data(connection);
1090 log_info("Changed locale information.");
1092 changed = bus_properties_changed_new(
1093 "/org/freedesktop/locale1",
1094 "org.freedesktop.locale1",
1101 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
1103 const char *keymap, *keymap_toggle;
1104 dbus_bool_t convert, interactive;
1106 if (!dbus_message_get_args(
1109 DBUS_TYPE_STRING, &keymap,
1110 DBUS_TYPE_STRING, &keymap_toggle,
1111 DBUS_TYPE_BOOLEAN, &convert,
1112 DBUS_TYPE_BOOLEAN, &interactive,
1114 return bus_send_error_reply(connection, message, &error, -EINVAL);
1116 if (isempty(keymap))
1119 if (isempty(keymap_toggle))
1120 keymap_toggle = NULL;
1122 if (!streq_ptr(keymap, state.vc_keymap) ||
1123 !streq_ptr(keymap_toggle, state.vc_keymap_toggle)) {
1125 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
1126 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
1127 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1129 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1131 return bus_send_error_reply(connection, message, &error, r);
1133 if (free_and_set(&state.vc_keymap, keymap) < 0 ||
1134 free_and_set(&state.vc_keymap_toggle, keymap_toggle) < 0)
1137 r = write_data_vconsole();
1139 log_error("Failed to set virtual console keymap: %s", strerror(-r));
1140 return bus_send_error_reply(connection, message, NULL, r);
1143 log_info("Changed virtual console keymap to '%s'", strempty(state.vc_keymap));
1145 r = load_vconsole_keymap(connection, NULL);
1147 log_error("Failed to request keymap reload: %s", strerror(-r));
1149 changed = bus_properties_changed_new(
1150 "/org/freedesktop/locale1",
1151 "org.freedesktop.locale1",
1153 "VConsoleKeymapToggle\0");
1158 r = convert_vconsole_to_x11(connection);
1161 log_error("Failed to convert keymap data: %s", strerror(-r));
1165 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) {
1167 const char *layout, *model, *variant, *options;
1168 dbus_bool_t convert, interactive;
1170 if (!dbus_message_get_args(
1173 DBUS_TYPE_STRING, &layout,
1174 DBUS_TYPE_STRING, &model,
1175 DBUS_TYPE_STRING, &variant,
1176 DBUS_TYPE_STRING, &options,
1177 DBUS_TYPE_BOOLEAN, &convert,
1178 DBUS_TYPE_BOOLEAN, &interactive,
1180 return bus_send_error_reply(connection, message, &error, -EINVAL);
1182 if (isempty(layout))
1188 if (isempty(variant))
1191 if (isempty(options))
1194 if (!streq_ptr(layout, state.x11_layout) ||
1195 !streq_ptr(model, state.x11_model) ||
1196 !streq_ptr(variant, state.x11_variant) ||
1197 !streq_ptr(options, state.x11_options)) {
1199 if ((layout && !string_is_safe(layout)) ||
1200 (model && !string_is_safe(model)) ||
1201 (variant && !string_is_safe(variant)) ||
1202 (options && !string_is_safe(options)))
1203 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1205 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1207 return bus_send_error_reply(connection, message, &error, r);
1209 if (free_and_set(&state.x11_layout, layout) < 0 ||
1210 free_and_set(&state.x11_model, model) < 0 ||
1211 free_and_set(&state.x11_variant, variant) < 0 ||
1212 free_and_set(&state.x11_options, options) < 0)
1215 r = write_data_x11();
1217 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1218 return bus_send_error_reply(connection, message, NULL, r);
1221 log_info("Changed X11 keyboard layout to '%s'", strempty(state.x11_layout));
1223 changed = bus_properties_changed_new(
1224 "/org/freedesktop/locale1",
1225 "org.freedesktop.locale1",
1234 r = convert_x11_to_vconsole(connection);
1237 log_error("Failed to convert keymap data: %s", strerror(-r));
1241 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
1243 if (!(reply = dbus_message_new_method_return(message)))
1246 if (!bus_maybe_send_reply(connection, message, reply))
1249 dbus_message_unref(reply);
1254 if (!dbus_connection_send(connection, changed, NULL))
1257 dbus_message_unref(changed);
1260 return DBUS_HANDLER_RESULT_HANDLED;
1264 dbus_message_unref(reply);
1267 dbus_message_unref(changed);
1269 dbus_error_free(&error);
1271 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1274 static int connect_bus(DBusConnection **_bus) {
1275 static const DBusObjectPathVTable locale_vtable = {
1276 .message_function = locale_message_handler
1279 DBusConnection *bus = NULL;
1284 dbus_error_init(&error);
1286 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1288 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
1293 dbus_connection_set_exit_on_disconnect(bus, FALSE);
1295 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL) ||
1296 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
1301 r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
1302 if (dbus_error_is_set(&error)) {
1303 log_error("Failed to register name on bus: %s", bus_error_message(&error));
1308 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1309 log_error("Failed to acquire name.");
1320 dbus_connection_close(bus);
1321 dbus_connection_unref(bus);
1323 dbus_error_free(&error);
1328 int main(int argc, char *argv[]) {
1330 DBusConnection *bus = NULL;
1331 bool exiting = false;
1333 log_set_target(LOG_TARGET_AUTO);
1334 log_parse_environment();
1339 if (argc == 2 && streq(argv[1], "--introspect")) {
1340 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
1341 "<node>\n", stdout);
1342 fputs(locale_interface, stdout);
1343 fputs("</node>\n", stdout);
1348 log_error("This program takes no arguments.");
1355 log_error("Failed to read locale data: %s", strerror(-r));
1359 r = connect_bus(&bus);
1363 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1366 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
1369 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
1371 bus_async_unregister_and_exit(bus, "org.freedesktop.locale1");
1381 dbus_connection_flush(bus);
1382 dbus_connection_close(bus);
1383 dbus_connection_unref(bus);
1386 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;