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", &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", &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);
1038 /* Check whether a variable changed and if so valid */
1039 STRV_FOREACH(i, l) {
1042 for (p = 0; p < _PROP_MAX; p++) {
1045 k = strlen(names[p]);
1046 if (startswith(*i, names[p]) &&
1048 string_is_safe((*i) + k + 1)) {
1052 if (!streq_ptr(*i + k + 1, data[p]))
1061 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1065 /* Check whether a variable is unset */
1067 for (p = 0; p < _PROP_MAX; p++)
1068 if (!isempty(data[p]) && !passed[p]) {
1076 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error);
1079 return bus_send_error_reply(connection, message, &error, r);
1082 STRV_FOREACH(i, l) {
1083 for (p = 0; p < _PROP_MAX; p++) {
1086 k = strlen(names[p]);
1087 if (startswith(*i, names[p]) && (*i)[k] == '=') {
1090 t = strdup(*i + k + 1);
1106 for (p = 0; p < _PROP_MAX; p++) {
1116 r = write_data_locale();
1118 log_error("Failed to set locale: %s", strerror(-r));
1119 return bus_send_error_reply(connection, message, NULL, r);
1122 push_data(connection);
1124 log_info("Changed locale information.");
1126 changed = bus_properties_changed_new(
1127 "/org/freedesktop/locale1",
1128 "org.freedesktop.locale1",
1135 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
1137 const char *keymap, *keymap_toggle;
1138 dbus_bool_t convert, interactive;
1140 if (!dbus_message_get_args(
1143 DBUS_TYPE_STRING, &keymap,
1144 DBUS_TYPE_STRING, &keymap_toggle,
1145 DBUS_TYPE_BOOLEAN, &convert,
1146 DBUS_TYPE_BOOLEAN, &interactive,
1148 return bus_send_error_reply(connection, message, &error, -EINVAL);
1150 if (isempty(keymap))
1153 if (isempty(keymap_toggle))
1154 keymap_toggle = NULL;
1156 if (!streq_ptr(keymap, state.vc_keymap) ||
1157 !streq_ptr(keymap_toggle, state.vc_keymap_toggle)) {
1159 if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
1160 (keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
1161 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1163 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1165 return bus_send_error_reply(connection, message, &error, r);
1167 if (free_and_set(&state.vc_keymap, keymap) < 0 ||
1168 free_and_set(&state.vc_keymap_toggle, keymap_toggle) < 0)
1171 r = write_data_vconsole();
1173 log_error("Failed to set virtual console keymap: %s", strerror(-r));
1174 return bus_send_error_reply(connection, message, NULL, r);
1177 log_info("Changed virtual console keymap to '%s'", strempty(state.vc_keymap));
1179 r = load_vconsole_keymap(connection, NULL);
1181 log_error("Failed to request keymap reload: %s", strerror(-r));
1183 changed = bus_properties_changed_new(
1184 "/org/freedesktop/locale1",
1185 "org.freedesktop.locale1",
1187 "VConsoleKeymapToggle\0");
1192 r = convert_vconsole_to_x11(connection);
1195 log_error("Failed to convert keymap data: %s", strerror(-r));
1199 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) {
1201 const char *layout, *model, *variant, *options;
1202 dbus_bool_t convert, interactive;
1204 if (!dbus_message_get_args(
1207 DBUS_TYPE_STRING, &layout,
1208 DBUS_TYPE_STRING, &model,
1209 DBUS_TYPE_STRING, &variant,
1210 DBUS_TYPE_STRING, &options,
1211 DBUS_TYPE_BOOLEAN, &convert,
1212 DBUS_TYPE_BOOLEAN, &interactive,
1214 return bus_send_error_reply(connection, message, &error, -EINVAL);
1216 if (isempty(layout))
1222 if (isempty(variant))
1225 if (isempty(options))
1228 if (!streq_ptr(layout, state.x11_layout) ||
1229 !streq_ptr(model, state.x11_model) ||
1230 !streq_ptr(variant, state.x11_variant) ||
1231 !streq_ptr(options, state.x11_options)) {
1233 if ((layout && !string_is_safe(layout)) ||
1234 (model && !string_is_safe(model)) ||
1235 (variant && !string_is_safe(variant)) ||
1236 (options && !string_is_safe(options)))
1237 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1239 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1241 return bus_send_error_reply(connection, message, &error, r);
1243 if (free_and_set(&state.x11_layout, layout) < 0 ||
1244 free_and_set(&state.x11_model, model) < 0 ||
1245 free_and_set(&state.x11_variant, variant) < 0 ||
1246 free_and_set(&state.x11_options, options) < 0)
1249 r = write_data_x11();
1251 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1252 return bus_send_error_reply(connection, message, NULL, r);
1255 log_info("Changed X11 keyboard layout to '%s'", strempty(state.x11_layout));
1257 changed = bus_properties_changed_new(
1258 "/org/freedesktop/locale1",
1259 "org.freedesktop.locale1",
1268 r = convert_x11_to_vconsole(connection);
1271 log_error("Failed to convert keymap data: %s", strerror(-r));
1275 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
1277 if (!(reply = dbus_message_new_method_return(message)))
1280 if (!bus_maybe_send_reply(connection, message, reply))
1283 dbus_message_unref(reply);
1288 if (!dbus_connection_send(connection, changed, NULL))
1291 dbus_message_unref(changed);
1294 return DBUS_HANDLER_RESULT_HANDLED;
1298 dbus_message_unref(reply);
1301 dbus_message_unref(changed);
1303 dbus_error_free(&error);
1305 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1308 static int connect_bus(DBusConnection **_bus) {
1309 static const DBusObjectPathVTable locale_vtable = {
1310 .message_function = locale_message_handler
1313 DBusConnection *bus = NULL;
1318 dbus_error_init(&error);
1320 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1322 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
1327 dbus_connection_set_exit_on_disconnect(bus, FALSE);
1329 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL) ||
1330 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
1335 r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
1336 if (dbus_error_is_set(&error)) {
1337 log_error("Failed to register name on bus: %s", bus_error_message(&error));
1342 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1343 log_error("Failed to acquire name.");
1354 dbus_connection_close(bus);
1355 dbus_connection_unref(bus);
1357 dbus_error_free(&error);
1362 int main(int argc, char *argv[]) {
1364 DBusConnection *bus = NULL;
1365 bool exiting = false;
1367 log_set_target(LOG_TARGET_AUTO);
1368 log_parse_environment();
1373 if (argc == 2 && streq(argv[1], "--introspect")) {
1374 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
1375 "<node>\n", stdout);
1376 fputs(locale_interface, stdout);
1377 fputs("</node>\n", stdout);
1382 log_error("This program takes no arguments.");
1389 log_error("Failed to read locale data: %s", strerror(-r));
1393 r = connect_bus(&bus);
1397 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1400 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
1403 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
1405 bus_async_unregister_and_exit(bus, "org.freedesktop.locale1");
1415 dbus_connection_flush(bus);
1416 dbus_connection_close(bus);
1417 dbus_connection_unref(bus);
1420 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;