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 " <interface name=\"org.freedesktop.locale1\">\n" \
37 " <property name=\"Locale\" type=\"as\" access=\"read\"/>\n" \
38 " <property name=\"VConsoleKeymap\" type=\"s\" access=\"read\"/>\n" \
39 " <property name=\"VConsoleKeymapToggle\" type=\"s\" access=\"read\"/>\n" \
40 " <property name=\"X11Layout\" type=\"s\" access=\"read\"/>\n" \
41 " <property name=\"X11Model\" type=\"s\" access=\"read\"/>\n" \
42 " <property name=\"X11Variant\" type=\"s\" access=\"read\"/>\n" \
43 " <property name=\"X11Options\" type=\"s\" access=\"read\"/>\n" \
44 " <method name=\"SetLocale\">\n" \
45 " <arg name=\"locale\" type=\"as\" direction=\"in\"/>\n" \
46 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
48 " <method name=\"SetVConsoleKeyboard\">\n" \
49 " <arg name=\"keymap\" type=\"s\" direction=\"in\"/>\n" \
50 " <arg name=\"keymap_toggle\" type=\"s\" direction=\"in\"/>\n" \
51 " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \
52 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
54 " <method name=\"SetX11Keyboard\">\n" \
55 " <arg name=\"layout\" type=\"s\" direction=\"in\"/>\n" \
56 " <arg name=\"model\" type=\"s\" direction=\"in\"/>\n" \
57 " <arg name=\"variant\" type=\"s\" direction=\"in\"/>\n" \
58 " <arg name=\"options\" type=\"s\" direction=\"in\"/>\n" \
59 " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \
60 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
64 #define INTROSPECTION \
65 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
68 BUS_PROPERTIES_INTERFACE \
69 BUS_INTROSPECTABLE_INTERFACE \
73 #define INTERFACES_LIST \
74 BUS_GENERIC_INTERFACES_LIST \
75 "org.freedesktop.locale1\0"
77 const char locale_interface[] _introspect_("locale1") = INTERFACE;
80 /* We don't list LC_ALL here on purpose. People should be
81 * using LANG instead. */
96 PROP_LC_IDENTIFICATION,
100 static const char * const names[_PROP_MAX] = {
101 [PROP_LANG] = "LANG",
102 [PROP_LANGUAGE] = "LANGUAGE",
103 [PROP_LC_CTYPE] = "LC_CTYPE",
104 [PROP_LC_NUMERIC] = "LC_NUMERIC",
105 [PROP_LC_TIME] = "LC_TIME",
106 [PROP_LC_COLLATE] = "LC_COLLATE",
107 [PROP_LC_MONETARY] = "LC_MONETARY",
108 [PROP_LC_MESSAGES] = "LC_MESSAGES",
109 [PROP_LC_PAPER] = "LC_PAPER",
110 [PROP_LC_NAME] = "LC_NAME",
111 [PROP_LC_ADDRESS] = "LC_ADDRESS",
112 [PROP_LC_TELEPHONE] = "LC_TELEPHONE",
113 [PROP_LC_MEASUREMENT] = "LC_MEASUREMENT",
114 [PROP_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
117 static char *data[_PROP_MAX] = {
133 typedef struct State {
134 char *x11_layout, *x11_model, *x11_variant, *x11_options;
135 char *vc_keymap, *vc_keymap_toggle;
140 static usec_t remain_until = 0;
142 static int free_and_set(char **s, const char *v) {
148 r = strdup_or_null(isempty(v) ? NULL : v, &t);
158 static void free_data_locale(void) {
161 for (p = 0; p < _PROP_MAX; p++) {
167 static void free_data_x11(void) {
168 free(state.x11_layout);
169 free(state.x11_model);
170 free(state.x11_variant);
171 free(state.x11_options);
173 state.x11_layout = state.x11_model = state.x11_variant = state.x11_options = NULL;
176 static void free_data_vconsole(void) {
177 free(state.vc_keymap);
178 free(state.vc_keymap_toggle);
180 state.vc_keymap = state.vc_keymap_toggle = NULL;
183 static void simplify(void) {
186 for (p = 1; p < _PROP_MAX; p++)
187 if (isempty(data[p]) || streq_ptr(data[PROP_LANG], data[p])) {
193 static int read_data_locale(void) {
198 r = parse_env_file("/etc/locale.conf", NEWLINE,
199 "LANG", &data[PROP_LANG],
200 "LANGUAGE", &data[PROP_LANGUAGE],
201 "LC_CTYPE", &data[PROP_LC_CTYPE],
202 "LC_NUMERIC", &data[PROP_LC_NUMERIC],
203 "LC_TIME", &data[PROP_LC_TIME],
204 "LC_COLLATE", &data[PROP_LC_COLLATE],
205 "LC_MONETARY", &data[PROP_LC_MONETARY],
206 "LC_MESSAGES", &data[PROP_LC_MESSAGES],
207 "LC_PAPER", &data[PROP_LC_PAPER],
208 "LC_NAME", &data[PROP_LC_NAME],
209 "LC_ADDRESS", &data[PROP_LC_ADDRESS],
210 "LC_TELEPHONE", &data[PROP_LC_TELEPHONE],
211 "LC_MEASUREMENT", &data[PROP_LC_MEASUREMENT],
212 "LC_IDENTIFICATION", &data[PROP_LC_IDENTIFICATION],
218 /* Fill in what we got passed from systemd. */
220 for (p = 0; p < _PROP_MAX; p++) {
225 e = getenv(names[p]);
244 static void free_data(void) {
246 free_data_vconsole();
250 static int read_data_vconsole(void) {
253 free_data_vconsole();
255 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
256 "KEYMAP", &state.vc_keymap,
257 "KEYMAP_TOGGLE", &state.vc_keymap_toggle,
260 if (r < 0 && r != -ENOENT)
266 static int read_data_x11(void) {
269 bool in_section = false;
273 f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
275 return errno == ENOENT ? 0 : -errno;
277 while (fgets(line, sizeof(line), f)) {
283 if (l[0] == 0 || l[0] == '#')
286 if (in_section && first_word(l, "Option")) {
289 a = strv_split_quoted(l);
295 if (strv_length(a) == 3) {
297 if (streq(a[1], "XkbLayout")) {
298 free(state.x11_layout);
299 state.x11_layout = a[2];
301 } else if (streq(a[1], "XkbModel")) {
302 free(state.x11_model);
303 state.x11_model = a[2];
305 } else if (streq(a[1], "XkbVariant")) {
306 free(state.x11_variant);
307 state.x11_variant = a[2];
309 } else if (streq(a[1], "XkbOptions")) {
310 free(state.x11_options);
311 state.x11_options = a[2];
318 } else if (!in_section && first_word(l, "Section")) {
321 a = strv_split_quoted(l);
327 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
331 } else if (in_section && first_word(l, "EndSection"))
340 static int read_data(void) {
343 r = read_data_locale();
344 q = read_data_vconsole();
347 return r < 0 ? r : q < 0 ? q : p;
350 static int write_data_locale(void) {
354 r = load_env_file("/etc/locale.conf", &l);
355 if (r < 0 && r != -ENOENT)
358 for (p = 0; p < _PROP_MAX; p++) {
363 if (isempty(data[p])) {
364 l = strv_env_unset(l, names[p]);
368 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
373 u = strv_env_set(l, t);
383 if (strv_isempty(l)) {
386 if (unlink("/etc/locale.conf") < 0)
387 return errno == ENOENT ? 0 : -errno;
392 r = write_env_file("/etc/locale.conf", l);
398 static void push_data(DBusConnection *bus) {
399 char **l_set = NULL, **l_unset = NULL, **t;
400 int c_set = 0, c_unset = 0, p;
402 DBusMessage *m = NULL, *reply = NULL;
403 DBusMessageIter iter, sub;
405 dbus_error_init(&error);
409 l_set = new0(char*, _PROP_MAX);
410 l_unset = new0(char*, _PROP_MAX);
411 if (!l_set || !l_unset) {
416 for (p = 0; p < _PROP_MAX; p++) {
419 if (isempty(data[p]))
420 l_unset[c_set++] = (char*) names[p];
424 if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) {
429 l_set[c_unset++] = s;
433 assert(c_set + c_unset == _PROP_MAX);
434 m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment");
436 log_error("Could not allocate message.");
440 dbus_message_iter_init_append(m, &iter);
442 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
447 STRV_FOREACH(t, l_unset)
448 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
453 if (!dbus_message_iter_close_container(&iter, &sub) ||
454 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
459 STRV_FOREACH(t, l_set)
460 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
465 if (!dbus_message_iter_close_container(&iter, &sub)) {
470 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
472 log_error("Failed to set locale information: %s", bus_error_message(&error));
478 dbus_message_unref(m);
481 dbus_message_unref(reply);
483 dbus_error_free(&error);
489 static int write_data_vconsole(void) {
493 r = load_env_file("/etc/vconsole.conf", &l);
494 if (r < 0 && r != -ENOENT)
497 if (isempty(state.vc_keymap))
498 l = strv_env_unset(l, "KEYMAP");
502 s = strappend("KEYMAP=", state.vc_keymap);
508 u = strv_env_set(l, s);
518 if (isempty(state.vc_keymap_toggle))
519 l = strv_env_unset(l, "KEYMAP_TOGGLE");
523 s = strappend("KEYMAP_TOGGLE=", state.vc_keymap_toggle);
529 u = strv_env_set(l, s);
539 if (strv_isempty(l)) {
542 if (unlink("/etc/vconsole.conf") < 0)
543 return errno == ENOENT ? 0 : -errno;
548 r = write_env_file("/etc/vconsole.conf", l);
554 static int write_data_x11(void) {
559 if (isempty(state.x11_layout) &&
560 isempty(state.x11_model) &&
561 isempty(state.x11_variant) &&
562 isempty(state.x11_options)) {
564 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
565 return errno == ENOENT ? 0 : -errno;
570 mkdir_parents_label("/etc/X11/xorg.conf.d", 0755);
572 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
576 fchmod(fileno(f), 0644);
578 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
579 "# manually too freely.\n"
580 "Section \"InputClass\"\n"
581 " Identifier \"system-keyboard\"\n"
582 " MatchIsKeyboard \"on\"\n", f);
584 if (!isempty(state.x11_layout))
585 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", state.x11_layout);
587 if (!isempty(state.x11_model))
588 fprintf(f, " Option \"XkbModel\" \"%s\"\n", state.x11_model);
590 if (!isempty(state.x11_variant))
591 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", state.x11_variant);
593 if (!isempty(state.x11_options))
594 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", state.x11_options);
596 fputs("EndSection\n", f);
599 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
601 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
612 static int load_vconsole_keymap(DBusConnection *bus, DBusError *error) {
613 DBusMessage *m = NULL, *reply = NULL;
614 const char *name = "systemd-vconsole-setup.service", *mode = "replace";
621 dbus_error_init(&_error);
625 m = dbus_message_new_method_call(
626 "org.freedesktop.systemd1",
627 "/org/freedesktop/systemd1",
628 "org.freedesktop.systemd1.Manager",
631 log_error("Could not allocate message.");
636 if (!dbus_message_append_args(m,
637 DBUS_TYPE_STRING, &name,
638 DBUS_TYPE_STRING, &mode,
639 DBUS_TYPE_INVALID)) {
640 log_error("Could not append arguments to message.");
645 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
647 log_error("Failed to issue method call: %s", bus_error_message(error));
656 dbus_message_unref(m);
659 dbus_message_unref(reply);
661 if (error == &_error)
662 dbus_error_free(error);
667 static char *strnulldash(const char *s) {
668 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
671 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
681 if (!fgets(line, sizeof(line), f)) {
684 return errno ? -errno : -EIO;
692 if (l[0] == 0 || l[0] == '#')
695 b = strv_split_quoted(l);
699 if (strv_length(b) < 5) {
700 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
711 static int convert_vconsole_to_x11(DBusConnection *connection) {
712 bool modified = false;
716 if (isempty(state.vc_keymap)) {
719 !isempty(state.x11_layout) ||
720 !isempty(state.x11_model) ||
721 !isempty(state.x11_variant) ||
722 !isempty(state.x11_options);
729 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
737 r = read_next_mapping(f, &n, &a);
746 if (!streq(state.vc_keymap, a[0])) {
751 if (!streq_ptr(state.x11_layout, strnulldash(a[1])) ||
752 !streq_ptr(state.x11_model, strnulldash(a[2])) ||
753 !streq_ptr(state.x11_variant, strnulldash(a[3])) ||
754 !streq_ptr(state.x11_options, strnulldash(a[4]))) {
756 if (free_and_set(&state.x11_layout, strnulldash(a[1])) < 0 ||
757 free_and_set(&state.x11_model, strnulldash(a[2])) < 0 ||
758 free_and_set(&state.x11_variant, strnulldash(a[3])) < 0 ||
759 free_and_set(&state.x11_options, strnulldash(a[4])) < 0) {
777 DBusMessage *changed;
780 r = write_data_x11();
782 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
784 changed = bus_properties_changed_new(
785 "/org/freedesktop/locale1",
786 "org.freedesktop.locale1",
795 b = dbus_connection_send(connection, changed, NULL);
796 dbus_message_unref(changed);
805 static int convert_x11_to_vconsole(DBusConnection *connection) {
806 bool modified = false;
810 if (isempty(state.x11_layout)) {
813 !isempty(state.vc_keymap) ||
814 !isempty(state.vc_keymap_toggle);
820 unsigned best_matching = 0;
821 char *new_keymap = NULL;
823 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
829 unsigned matching = 0;
832 r = read_next_mapping(f, &n, &a);
841 /* Determine how well matching this entry is */
842 if (streq_ptr(state.x11_layout, a[1]))
843 /* If we got an exact match, this is best */
848 x = strcspn(state.x11_layout, ",");
850 /* We have multiple X layouts, look
851 * for an entry that matches our key
852 * with the everything but the first
853 * layout stripped off. */
856 strncmp(state.x11_layout, a[1], x) == 0)
861 /* If that didn't work, strip
862 * off the other layouts from
865 w = strcspn(a[1], ",");
867 if (x > 0 && x == w &&
868 memcmp(state.x11_layout, a[1], x) == 0)
874 streq_ptr(state.x11_model, a[2])) {
877 if (streq_ptr(state.x11_variant, a[3])) {
880 if (streq_ptr(state.x11_options, a[4]))
885 /* The best matching entry so far, then let's
887 if (matching > best_matching) {
888 best_matching = matching;
891 new_keymap = strdup(a[0]);
905 if (!streq_ptr(state.vc_keymap, new_keymap)) {
906 free(state.vc_keymap);
907 state.vc_keymap = new_keymap;
909 free(state.vc_keymap_toggle);
910 state.vc_keymap_toggle = NULL;
919 DBusMessage *changed;
922 r = write_data_vconsole();
924 log_error("Failed to set virtual console keymap: %s", strerror(-r));
926 changed = bus_properties_changed_new(
927 "/org/freedesktop/locale1",
928 "org.freedesktop.locale1",
930 "VConsoleKeymapToggle\0");
935 b = dbus_connection_send(connection, changed, NULL);
936 dbus_message_unref(changed);
941 return load_vconsole_keymap(connection, NULL);
947 static int append_locale(DBusMessageIter *i, const char *property, void *userdata) {
951 l = new0(char*, _PROP_MAX+1);
955 for (p = 0; p < _PROP_MAX; p++) {
958 if (isempty(data[p]))
961 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
969 r = bus_property_append_strv(i, property, (void*) l);
975 static const BusProperty bus_locale_properties[] = {
976 { "Locale", append_locale, "as", 0 },
977 { "X11Layout", bus_property_append_string, "s", offsetof(State, x11_layout), true },
978 { "X11Model", bus_property_append_string, "s", offsetof(State, x11_model), true },
979 { "X11Variant", bus_property_append_string, "s", offsetof(State, x11_variant), true },
980 { "X11Options", bus_property_append_string, "s", offsetof(State, x11_options), true },
981 { "VConsoleKeymap", bus_property_append_string, "s", offsetof(State, vc_keymap), true },
982 { "VConsoleKeymapToggle", bus_property_append_string, "s", offsetof(State, vc_keymap_toggle), true },
986 static const BusBoundProperties bps[] = {
987 { "org.freedesktop.locale1", bus_locale_properties, &state },
991 static DBusHandlerResult locale_message_handler(
992 DBusConnection *connection,
993 DBusMessage *message,
996 DBusMessage *reply = NULL, *changed = NULL;
1003 dbus_error_init(&error);
1005 if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) {
1006 char **l = NULL, **i;
1007 dbus_bool_t interactive;
1008 DBusMessageIter iter;
1009 bool modified = false;
1010 bool passed[_PROP_MAX];
1013 if (!dbus_message_iter_init(message, &iter))
1014 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1016 r = bus_parse_strv_iter(&iter, &l);
1021 return bus_send_error_reply(connection, message, NULL, r);
1024 if (!dbus_message_iter_next(&iter) ||
1025 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
1027 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1030 dbus_message_iter_get_basic(&iter, &interactive);
1034 /* Check whether a variable changed and if so valid */
1035 STRV_FOREACH(i, l) {
1038 for (p = 0; p < _PROP_MAX; p++) {
1041 k = strlen(names[p]);
1042 if (startswith(*i, names[p]) &&
1044 string_is_safe((*i) + k + 1)) {
1048 if (!streq_ptr(*i + k + 1, data[p]))
1057 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1061 /* Check whether a variable is unset */
1063 for (p = 0; p < _PROP_MAX; p++)
1064 if (!isempty(data[p]) && !passed[p]) {
1072 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error);
1075 return bus_send_error_reply(connection, message, &error, r);
1078 STRV_FOREACH(i, l) {
1079 for (p = 0; p < _PROP_MAX; p++) {
1082 k = strlen(names[p]);
1083 if (startswith(*i, names[p]) && (*i)[k] == '=') {
1086 t = strdup(*i + k + 1);
1102 for (p = 0; p < _PROP_MAX; p++) {
1112 r = write_data_locale();
1114 log_error("Failed to set locale: %s", strerror(-r));
1115 return bus_send_error_reply(connection, message, NULL, r);
1118 push_data(connection);
1120 log_info("Changed locale information.");
1122 changed = bus_properties_changed_new(
1123 "/org/freedesktop/locale1",
1124 "org.freedesktop.locale1",
1131 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
1133 const char *keymap, *keymap_toggle;
1134 dbus_bool_t convert, interactive;
1136 if (!dbus_message_get_args(
1139 DBUS_TYPE_STRING, &keymap,
1140 DBUS_TYPE_STRING, &keymap_toggle,
1141 DBUS_TYPE_BOOLEAN, &convert,
1142 DBUS_TYPE_BOOLEAN, &interactive,
1144 return bus_send_error_reply(connection, message, &error, -EINVAL);
1146 if (isempty(keymap))
1149 if (isempty(keymap_toggle))
1150 keymap_toggle = NULL;
1152 if (!streq_ptr(keymap, state.vc_keymap) ||
1153 !streq_ptr(keymap_toggle, state.vc_keymap_toggle)) {
1155 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
1156 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
1157 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1159 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1161 return bus_send_error_reply(connection, message, &error, r);
1163 if (free_and_set(&state.vc_keymap, keymap) < 0 ||
1164 free_and_set(&state.vc_keymap_toggle, keymap_toggle) < 0)
1167 r = write_data_vconsole();
1169 log_error("Failed to set virtual console keymap: %s", strerror(-r));
1170 return bus_send_error_reply(connection, message, NULL, r);
1173 log_info("Changed virtual console keymap to '%s'", strempty(state.vc_keymap));
1175 r = load_vconsole_keymap(connection, NULL);
1177 log_error("Failed to request keymap reload: %s", strerror(-r));
1179 changed = bus_properties_changed_new(
1180 "/org/freedesktop/locale1",
1181 "org.freedesktop.locale1",
1183 "VConsoleKeymapToggle\0");
1188 r = convert_vconsole_to_x11(connection);
1191 log_error("Failed to convert keymap data: %s", strerror(-r));
1195 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) {
1197 const char *layout, *model, *variant, *options;
1198 dbus_bool_t convert, interactive;
1200 if (!dbus_message_get_args(
1203 DBUS_TYPE_STRING, &layout,
1204 DBUS_TYPE_STRING, &model,
1205 DBUS_TYPE_STRING, &variant,
1206 DBUS_TYPE_STRING, &options,
1207 DBUS_TYPE_BOOLEAN, &convert,
1208 DBUS_TYPE_BOOLEAN, &interactive,
1210 return bus_send_error_reply(connection, message, &error, -EINVAL);
1212 if (isempty(layout))
1218 if (isempty(variant))
1221 if (isempty(options))
1224 if (!streq_ptr(layout, state.x11_layout) ||
1225 !streq_ptr(model, state.x11_model) ||
1226 !streq_ptr(variant, state.x11_variant) ||
1227 !streq_ptr(options, state.x11_options)) {
1229 if ((layout && !string_is_safe(layout)) ||
1230 (model && !string_is_safe(model)) ||
1231 (variant && !string_is_safe(variant)) ||
1232 (options && !string_is_safe(options)))
1233 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1235 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1237 return bus_send_error_reply(connection, message, &error, r);
1239 if (free_and_set(&state.x11_layout, layout) < 0 ||
1240 free_and_set(&state.x11_model, model) < 0 ||
1241 free_and_set(&state.x11_variant, variant) < 0 ||
1242 free_and_set(&state.x11_options, options) < 0)
1245 r = write_data_x11();
1247 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1248 return bus_send_error_reply(connection, message, NULL, r);
1251 log_info("Changed X11 keyboard layout to '%s'", strempty(state.x11_layout));
1253 changed = bus_properties_changed_new(
1254 "/org/freedesktop/locale1",
1255 "org.freedesktop.locale1",
1264 r = convert_x11_to_vconsole(connection);
1267 log_error("Failed to convert keymap data: %s", strerror(-r));
1271 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
1273 if (!(reply = dbus_message_new_method_return(message)))
1276 if (!dbus_connection_send(connection, reply, NULL))
1279 dbus_message_unref(reply);
1284 if (!dbus_connection_send(connection, changed, NULL))
1287 dbus_message_unref(changed);
1290 return DBUS_HANDLER_RESULT_HANDLED;
1294 dbus_message_unref(reply);
1297 dbus_message_unref(changed);
1299 dbus_error_free(&error);
1301 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1304 static int connect_bus(DBusConnection **_bus) {
1305 static const DBusObjectPathVTable locale_vtable = {
1306 .message_function = locale_message_handler
1309 DBusConnection *bus = NULL;
1314 dbus_error_init(&error);
1316 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1318 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
1323 dbus_connection_set_exit_on_disconnect(bus, FALSE);
1325 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL) ||
1326 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
1331 r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
1332 if (dbus_error_is_set(&error)) {
1333 log_error("Failed to register name on bus: %s", bus_error_message(&error));
1338 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1339 log_error("Failed to acquire name.");
1350 dbus_connection_close(bus);
1351 dbus_connection_unref(bus);
1353 dbus_error_free(&error);
1358 int main(int argc, char *argv[]) {
1360 DBusConnection *bus = NULL;
1361 bool exiting = false;
1363 log_set_target(LOG_TARGET_AUTO);
1364 log_parse_environment();
1369 if (argc == 2 && streq(argv[1], "--introspect")) {
1370 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
1371 "<node>\n", stdout);
1372 fputs(locale_interface, stdout);
1373 fputs("</node>\n", stdout);
1378 log_error("This program takes no arguments.");
1385 log_error("Failed to read locale data: %s", strerror(-r));
1389 r = connect_bus(&bus);
1393 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1396 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
1399 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
1401 bus_async_unregister_and_exit(bus, "org.freedesktop.locale1");
1411 dbus_connection_flush(bus);
1412 dbus_connection_close(bus);
1413 dbus_connection_unref(bus);
1416 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;