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]) && (*i)[k] == '=') {
1046 if (!streq_ptr(*i + k + 1, data[p]))
1055 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1059 /* Check whether a variable is unset */
1061 for (p = 0; p < _PROP_MAX; p++)
1062 if (!isempty(data[p]) && !passed[p]) {
1070 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error);
1073 return bus_send_error_reply(connection, message, &error, r);
1076 STRV_FOREACH(i, l) {
1077 for (p = 0; p < _PROP_MAX; p++) {
1080 k = strlen(names[p]);
1081 if (startswith(*i, names[p]) && (*i)[k] == '=') {
1084 t = strdup(*i + k + 1);
1100 for (p = 0; p < _PROP_MAX; p++) {
1110 r = write_data_locale();
1112 log_error("Failed to set locale: %s", strerror(-r));
1113 return bus_send_error_reply(connection, message, NULL, r);
1116 push_data(connection);
1118 log_info("Changed locale information.");
1120 changed = bus_properties_changed_new(
1121 "/org/freedesktop/locale1",
1122 "org.freedesktop.locale1",
1127 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
1129 const char *keymap, *keymap_toggle;
1130 dbus_bool_t convert, interactive;
1132 if (!dbus_message_get_args(
1135 DBUS_TYPE_STRING, &keymap,
1136 DBUS_TYPE_STRING, &keymap_toggle,
1137 DBUS_TYPE_BOOLEAN, &convert,
1138 DBUS_TYPE_BOOLEAN, &interactive,
1140 return bus_send_error_reply(connection, message, &error, -EINVAL);
1142 if (isempty(keymap))
1145 if (isempty(keymap_toggle))
1146 keymap_toggle = NULL;
1148 if (!streq_ptr(keymap, state.vc_keymap) ||
1149 !streq_ptr(keymap_toggle, state.vc_keymap_toggle)) {
1151 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1153 return bus_send_error_reply(connection, message, &error, r);
1155 if (free_and_set(&state.vc_keymap, keymap) < 0 ||
1156 free_and_set(&state.vc_keymap_toggle, keymap_toggle) < 0)
1159 r = write_data_vconsole();
1161 log_error("Failed to set virtual console keymap: %s", strerror(-r));
1162 return bus_send_error_reply(connection, message, NULL, r);
1165 log_info("Changed virtual console keymap to '%s'", strempty(state.vc_keymap));
1167 r = load_vconsole_keymap(connection, NULL);
1169 log_error("Failed to request keymap reload: %s", strerror(-r));
1171 changed = bus_properties_changed_new(
1172 "/org/freedesktop/locale1",
1173 "org.freedesktop.locale1",
1175 "VConsoleKeymapToggle\0");
1180 r = convert_vconsole_to_x11(connection);
1183 log_error("Failed to convert keymap data: %s", strerror(-r));
1187 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) {
1189 const char *layout, *model, *variant, *options;
1190 dbus_bool_t convert, interactive;
1192 if (!dbus_message_get_args(
1195 DBUS_TYPE_STRING, &layout,
1196 DBUS_TYPE_STRING, &model,
1197 DBUS_TYPE_STRING, &variant,
1198 DBUS_TYPE_STRING, &options,
1199 DBUS_TYPE_BOOLEAN, &convert,
1200 DBUS_TYPE_BOOLEAN, &interactive,
1202 return bus_send_error_reply(connection, message, &error, -EINVAL);
1204 if (isempty(layout))
1210 if (isempty(variant))
1213 if (isempty(options))
1216 if (!streq_ptr(layout, state.x11_layout) ||
1217 !streq_ptr(model, state.x11_model) ||
1218 !streq_ptr(variant, state.x11_variant) ||
1219 !streq_ptr(options, state.x11_options)) {
1221 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1223 return bus_send_error_reply(connection, message, &error, r);
1225 if (free_and_set(&state.x11_layout, layout) < 0 ||
1226 free_and_set(&state.x11_model, model) < 0 ||
1227 free_and_set(&state.x11_variant, variant) < 0 ||
1228 free_and_set(&state.x11_options, options) < 0)
1231 r = write_data_x11();
1233 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1234 return bus_send_error_reply(connection, message, NULL, r);
1237 log_info("Changed X11 keyboard layout to '%s'", strempty(state.x11_layout));
1239 changed = bus_properties_changed_new(
1240 "/org/freedesktop/locale1",
1241 "org.freedesktop.locale1",
1250 r = convert_x11_to_vconsole(connection);
1253 log_error("Failed to convert keymap data: %s", strerror(-r));
1257 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
1259 if (!(reply = dbus_message_new_method_return(message)))
1262 if (!dbus_connection_send(connection, reply, NULL))
1265 dbus_message_unref(reply);
1270 if (!dbus_connection_send(connection, changed, NULL))
1273 dbus_message_unref(changed);
1276 return DBUS_HANDLER_RESULT_HANDLED;
1280 dbus_message_unref(reply);
1283 dbus_message_unref(changed);
1285 dbus_error_free(&error);
1287 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1290 static int connect_bus(DBusConnection **_bus) {
1291 static const DBusObjectPathVTable locale_vtable = {
1292 .message_function = locale_message_handler
1295 DBusConnection *bus = NULL;
1300 dbus_error_init(&error);
1302 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1304 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
1309 dbus_connection_set_exit_on_disconnect(bus, FALSE);
1311 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL) ||
1312 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
1317 r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
1318 if (dbus_error_is_set(&error)) {
1319 log_error("Failed to register name on bus: %s", bus_error_message(&error));
1324 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1325 log_error("Failed to acquire name.");
1336 dbus_connection_close(bus);
1337 dbus_connection_unref(bus);
1339 dbus_error_free(&error);
1344 int main(int argc, char *argv[]) {
1346 DBusConnection *bus = NULL;
1347 bool exiting = false;
1349 log_set_target(LOG_TARGET_AUTO);
1350 log_parse_environment();
1355 if (argc == 2 && streq(argv[1], "--introspect")) {
1356 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
1357 "<node>\n", stdout);
1358 fputs(locale_interface, stdout);
1359 fputs("</node>\n", stdout);
1364 log_error("This program takes no arguments.");
1371 log_error("Failed to read locale data: %s", strerror(-r));
1375 r = connect_bus(&bus);
1379 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1382 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
1385 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
1387 bus_async_unregister_and_exit(bus, "org.freedesktop.locale1");
1397 dbus_connection_flush(bus);
1398 dbus_connection_close(bus);
1399 dbus_connection_unref(bus);
1402 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;