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 if (errno == ENOENT) {
278 f = fopen("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf", "re");
293 while (fgets(line, sizeof(line), f)) {
299 if (l[0] == 0 || l[0] == '#')
302 if (in_section && first_word(l, "Option")) {
305 a = strv_split_quoted(l);
311 if (strv_length(a) == 3) {
313 if (streq(a[1], "XkbLayout")) {
314 free(state.x11_layout);
315 state.x11_layout = a[2];
317 } else if (streq(a[1], "XkbModel")) {
318 free(state.x11_model);
319 state.x11_model = a[2];
321 } else if (streq(a[1], "XkbVariant")) {
322 free(state.x11_variant);
323 state.x11_variant = a[2];
325 } else if (streq(a[1], "XkbOptions")) {
326 free(state.x11_options);
327 state.x11_options = a[2];
334 } else if (!in_section && first_word(l, "Section")) {
337 a = strv_split_quoted(l);
343 if (strv_length(a) == 2 && streq(a[1], "InputClass"))
347 } else if (in_section && first_word(l, "EndSection"))
356 static int read_data(void) {
359 r = read_data_locale();
360 q = read_data_vconsole();
363 return r < 0 ? r : q < 0 ? q : p;
366 static int write_data_locale(void) {
370 r = load_env_file("/etc/locale.conf", &l);
371 if (r < 0 && r != -ENOENT)
374 for (p = 0; p < _PROP_MAX; p++) {
379 if (isempty(data[p])) {
380 l = strv_env_unset(l, names[p]);
384 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
389 u = strv_env_set(l, t);
399 if (strv_isempty(l)) {
402 if (unlink("/etc/locale.conf") < 0)
403 return errno == ENOENT ? 0 : -errno;
408 r = write_env_file("/etc/locale.conf", l);
414 static void push_data(DBusConnection *bus) {
415 char **l_set = NULL, **l_unset = NULL, **t;
416 int c_set = 0, c_unset = 0, p;
418 DBusMessage *m = NULL, *reply = NULL;
419 DBusMessageIter iter, sub;
421 dbus_error_init(&error);
425 l_set = new0(char*, _PROP_MAX);
426 l_unset = new0(char*, _PROP_MAX);
427 if (!l_set || !l_unset) {
428 log_error("Out of memory");
432 for (p = 0; p < _PROP_MAX; p++) {
435 if (isempty(data[p]))
436 l_unset[c_set++] = (char*) names[p];
440 if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) {
441 log_error("Out of memory");
445 l_set[c_unset++] = s;
449 assert(c_set + c_unset == _PROP_MAX);
450 m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment");
452 log_error("Could not allocate message.");
456 dbus_message_iter_init_append(m, &iter);
458 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
459 log_error("Out of memory.");
463 STRV_FOREACH(t, l_unset)
464 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
465 log_error("Out of memory.");
469 if (!dbus_message_iter_close_container(&iter, &sub) ||
470 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
471 log_error("Out of memory.");
475 STRV_FOREACH(t, l_set)
476 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
477 log_error("Out of memory.");
481 if (!dbus_message_iter_close_container(&iter, &sub)) {
482 log_error("Out of memory.");
486 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
488 log_error("Failed to set locale information: %s", bus_error_message(&error));
494 dbus_message_unref(m);
497 dbus_message_unref(reply);
499 dbus_error_free(&error);
505 static int write_data_vconsole(void) {
509 r = load_env_file("/etc/vconsole.conf", &l);
510 if (r < 0 && r != -ENOENT)
513 if (isempty(state.vc_keymap))
514 l = strv_env_unset(l, "KEYMAP");
518 s = strappend("KEYMAP=", state.vc_keymap);
524 u = strv_env_set(l, s);
534 if (isempty(state.vc_keymap_toggle))
535 l = strv_env_unset(l, "KEYMAP_TOGGLE");
539 s = strappend("KEYMAP_TOGGLE=", state.vc_keymap_toggle);
545 u = strv_env_set(l, s);
555 if (strv_isempty(l)) {
558 if (unlink("/etc/vconsole.conf") < 0)
559 return errno == ENOENT ? 0 : -errno;
564 r = write_env_file("/etc/vconsole.conf", l);
570 static int write_data_x11(void) {
575 if (isempty(state.x11_layout) &&
576 isempty(state.x11_model) &&
577 isempty(state.x11_variant) &&
578 isempty(state.x11_options)) {
581 unlink("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf");
583 /* Symlink this to /dev/null, so that s-s-k (if it is
584 * still running) doesn't recreate this. */
585 symlink("/dev/null", "/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf");
588 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
589 return errno == ENOENT ? 0 : -errno;
594 mkdir_parents("/etc/X11/xorg.conf.d", 0755);
596 r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
600 fchmod(fileno(f), 0644);
602 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
603 "# manually too freely.\n"
604 "Section \"InputClass\"\n"
605 " Identifier \"system-keyboard\"\n"
606 " MatchIsKeyboard \"on\"\n", f);
608 if (!isempty(state.x11_layout))
609 fprintf(f, " Option \"XkbLayout\" \"%s\"\n", state.x11_layout);
611 if (!isempty(state.x11_model))
612 fprintf(f, " Option \"XkbModel\" \"%s\"\n", state.x11_model);
614 if (!isempty(state.x11_variant))
615 fprintf(f, " Option \"XkbVariant\" \"%s\"\n", state.x11_variant);
617 if (!isempty(state.x11_options))
618 fprintf(f, " Option \"XkbOptions\" \"%s\"\n", state.x11_options);
620 fputs("EndSection\n", f);
623 if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
625 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
630 unlink("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf");
632 /* Symlink this to /dev/null, so that s-s-k (if it is
633 * still running) doesn't recreate this. */
634 symlink("/dev/null", "/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf");
646 static int load_vconsole_keymap(DBusConnection *bus, DBusError *error) {
647 DBusMessage *m = NULL, *reply = NULL;
648 const char *name = "systemd-vconsole-setup.service", *mode = "replace";
655 dbus_error_init(&_error);
659 m = dbus_message_new_method_call(
660 "org.freedesktop.systemd1",
661 "/org/freedesktop/systemd1",
662 "org.freedesktop.systemd1.Manager",
665 log_error("Could not allocate message.");
670 if (!dbus_message_append_args(m,
671 DBUS_TYPE_STRING, &name,
672 DBUS_TYPE_STRING, &mode,
673 DBUS_TYPE_INVALID)) {
674 log_error("Could not append arguments to message.");
679 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
681 log_error("Failed to issue method call: %s", bus_error_message(error));
690 dbus_message_unref(m);
693 dbus_message_unref(reply);
695 if (error == &_error)
696 dbus_error_free(error);
701 static char *strnulldash(const char *s) {
702 return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
705 static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
715 if (!fgets(line, sizeof(line), f)) {
718 return errno ? -errno : -EIO;
726 if (l[0] == 0 || l[0] == '#')
729 b = strv_split_quoted(l);
733 if (strv_length(b) < 5) {
734 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
745 static int convert_vconsole_to_x11(DBusConnection *connection) {
746 bool modified = false;
750 if (isempty(state.vc_keymap)) {
753 !isempty(state.x11_layout) ||
754 !isempty(state.x11_model) ||
755 !isempty(state.x11_variant) ||
756 !isempty(state.x11_options);
763 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
771 r = read_next_mapping(f, &n, &a);
780 if (!streq(state.vc_keymap, a[0])) {
785 if (!streq_ptr(state.x11_layout, strnulldash(a[1])) ||
786 !streq_ptr(state.x11_model, strnulldash(a[2])) ||
787 !streq_ptr(state.x11_variant, strnulldash(a[3])) ||
788 !streq_ptr(state.x11_options, strnulldash(a[4]))) {
790 if (free_and_set(&state.x11_layout, strnulldash(a[1])) < 0 ||
791 free_and_set(&state.x11_model, strnulldash(a[2])) < 0 ||
792 free_and_set(&state.x11_variant, strnulldash(a[3])) < 0 ||
793 free_and_set(&state.x11_options, strnulldash(a[4])) < 0) {
811 DBusMessage *changed;
814 r = write_data_x11();
816 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
818 changed = bus_properties_changed_new(
819 "/org/freedesktop/locale1",
820 "org.freedesktop.locale1",
829 b = dbus_connection_send(connection, changed, NULL);
830 dbus_message_unref(changed);
839 static int convert_x11_to_vconsole(DBusConnection *connection) {
840 bool modified = false;
844 if (isempty(state.x11_layout)) {
847 !isempty(state.vc_keymap) ||
848 !isempty(state.vc_keymap_toggle);
854 unsigned best_matching = 0;
855 char *new_keymap = NULL;
857 f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
863 unsigned matching = 0;
866 r = read_next_mapping(f, &n, &a);
875 /* Determine how well matching this entry is */
876 if (streq_ptr(state.x11_layout, a[1]))
877 /* If we got an exact match, this is best */
882 x = strcspn(state.x11_layout, ",");
884 /* We have multiple X layouts, look
885 * for an entry that matches our key
886 * with the everything but the first
887 * layout stripped off. */
890 strncmp(state.x11_layout, a[1], x) == 0)
895 /* If that didn't work, strip
896 * off the other layouts from
899 w = strcspn(a[1], ",");
901 if (x > 0 && x == w &&
902 memcmp(state.x11_layout, a[1], x) == 0)
908 streq_ptr(state.x11_model, a[2])) {
911 if (streq_ptr(state.x11_variant, a[3])) {
914 if (streq_ptr(state.x11_options, a[4]))
919 /* The best matching entry so far, then let's
921 if (matching > best_matching) {
922 best_matching = matching;
925 new_keymap = strdup(a[0]);
939 if (!streq_ptr(state.vc_keymap, new_keymap)) {
940 free(state.vc_keymap);
941 state.vc_keymap = new_keymap;
943 free(state.vc_keymap_toggle);
944 state.vc_keymap_toggle = NULL;
953 DBusMessage *changed;
956 r = write_data_vconsole();
958 log_error("Failed to set virtual console keymap: %s", strerror(-r));
960 changed = bus_properties_changed_new(
961 "/org/freedesktop/locale1",
962 "org.freedesktop.locale1",
964 "VConsoleKeymapToggle\0");
969 b = dbus_connection_send(connection, changed, NULL);
970 dbus_message_unref(changed);
975 return load_vconsole_keymap(connection, NULL);
981 static int append_locale(DBusMessageIter *i, const char *property, void *userdata) {
985 l = new0(char*, _PROP_MAX+1);
989 for (p = 0; p < _PROP_MAX; p++) {
992 if (isempty(data[p]))
995 if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
1003 r = bus_property_append_strv(i, property, (void*) l);
1009 static const BusProperty bus_locale_properties[] = {
1010 { "Locale", append_locale, "as", 0 },
1011 { "X11Layout", bus_property_append_string, "s", offsetof(State, x11_layout), true },
1012 { "X11Model", bus_property_append_string, "s", offsetof(State, x11_model), true },
1013 { "X11Variant", bus_property_append_string, "s", offsetof(State, x11_variant), true },
1014 { "X11Options", bus_property_append_string, "s", offsetof(State, x11_options), true },
1015 { "VConsoleKeymap", bus_property_append_string, "s", offsetof(State, vc_keymap), true },
1016 { "VConsoleKeymapToggle", bus_property_append_string, "s", offsetof(State, vc_keymap_toggle), true },
1020 static const BusBoundProperties bps[] = {
1021 { "org.freedesktop.locale1", bus_locale_properties, &state },
1025 static DBusHandlerResult locale_message_handler(
1026 DBusConnection *connection,
1027 DBusMessage *message,
1030 DBusMessage *reply = NULL, *changed = NULL;
1037 dbus_error_init(&error);
1039 if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) {
1040 char **l = NULL, **i;
1041 dbus_bool_t interactive;
1042 DBusMessageIter iter;
1043 bool modified = false;
1044 bool passed[_PROP_MAX];
1047 if (!dbus_message_iter_init(message, &iter))
1048 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1050 r = bus_parse_strv_iter(&iter, &l);
1055 return bus_send_error_reply(connection, message, NULL, r);
1058 if (!dbus_message_iter_next(&iter) ||
1059 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
1061 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1064 dbus_message_iter_get_basic(&iter, &interactive);
1068 /* Check whether a variable changed and if so valid */
1069 STRV_FOREACH(i, l) {
1072 for (p = 0; p < _PROP_MAX; p++) {
1075 k = strlen(names[p]);
1076 if (startswith(*i, names[p]) && (*i)[k] == '=') {
1080 if (!streq_ptr(*i + k + 1, data[p]))
1089 return bus_send_error_reply(connection, message, NULL, -EINVAL);
1093 /* Check whether a variable is unset */
1095 for (p = 0; p < _PROP_MAX; p++)
1096 if (!isempty(data[p]) && !passed[p]) {
1104 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error);
1107 return bus_send_error_reply(connection, message, &error, r);
1110 STRV_FOREACH(i, l) {
1111 for (p = 0; p < _PROP_MAX; p++) {
1114 k = strlen(names[p]);
1115 if (startswith(*i, names[p]) && (*i)[k] == '=') {
1118 t = strdup(*i + k + 1);
1134 for (p = 0; p < _PROP_MAX; p++) {
1144 r = write_data_locale();
1146 log_error("Failed to set locale: %s", strerror(-r));
1147 return bus_send_error_reply(connection, message, NULL, r);
1150 push_data(connection);
1152 log_info("Changed locale information.");
1154 changed = bus_properties_changed_new(
1155 "/org/freedesktop/locale1",
1156 "org.freedesktop.locale1",
1161 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
1163 const char *keymap, *keymap_toggle;
1164 dbus_bool_t convert, interactive;
1166 if (!dbus_message_get_args(
1169 DBUS_TYPE_STRING, &keymap,
1170 DBUS_TYPE_STRING, &keymap_toggle,
1171 DBUS_TYPE_BOOLEAN, &convert,
1172 DBUS_TYPE_BOOLEAN, &interactive,
1174 return bus_send_error_reply(connection, message, &error, -EINVAL);
1176 if (isempty(keymap))
1179 if (isempty(keymap_toggle))
1180 keymap_toggle = NULL;
1182 if (!streq_ptr(keymap, state.vc_keymap) ||
1183 !streq_ptr(keymap_toggle, state.vc_keymap_toggle)) {
1185 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1187 return bus_send_error_reply(connection, message, &error, r);
1189 if (free_and_set(&state.vc_keymap, keymap) < 0 ||
1190 free_and_set(&state.vc_keymap_toggle, keymap_toggle) < 0)
1193 r = write_data_vconsole();
1195 log_error("Failed to set virtual console keymap: %s", strerror(-r));
1196 return bus_send_error_reply(connection, message, NULL, r);
1199 log_info("Changed virtual console keymap to '%s'", strempty(state.vc_keymap));
1201 r = load_vconsole_keymap(connection, NULL);
1203 log_error("Failed to request keymap reload: %s", strerror(-r));
1205 changed = bus_properties_changed_new(
1206 "/org/freedesktop/locale1",
1207 "org.freedesktop.locale1",
1209 "VConsoleKeymapToggle\0");
1214 r = convert_vconsole_to_x11(connection);
1217 log_error("Failed to convert keymap data: %s", strerror(-r));
1221 } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) {
1223 const char *layout, *model, *variant, *options;
1224 dbus_bool_t convert, interactive;
1226 if (!dbus_message_get_args(
1229 DBUS_TYPE_STRING, &layout,
1230 DBUS_TYPE_STRING, &model,
1231 DBUS_TYPE_STRING, &variant,
1232 DBUS_TYPE_STRING, &options,
1233 DBUS_TYPE_BOOLEAN, &convert,
1234 DBUS_TYPE_BOOLEAN, &interactive,
1236 return bus_send_error_reply(connection, message, &error, -EINVAL);
1238 if (isempty(layout))
1244 if (isempty(variant))
1247 if (isempty(options))
1250 if (!streq_ptr(layout, state.x11_layout) ||
1251 !streq_ptr(model, state.x11_model) ||
1252 !streq_ptr(variant, state.x11_variant) ||
1253 !streq_ptr(options, state.x11_options)) {
1255 r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
1257 return bus_send_error_reply(connection, message, &error, r);
1259 if (free_and_set(&state.x11_layout, layout) < 0 ||
1260 free_and_set(&state.x11_model, model) < 0 ||
1261 free_and_set(&state.x11_variant, variant) < 0 ||
1262 free_and_set(&state.x11_options, options) < 0)
1265 r = write_data_x11();
1267 log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
1268 return bus_send_error_reply(connection, message, NULL, r);
1271 log_info("Changed X11 keyboard layout to '%s'", strempty(state.x11_layout));
1273 changed = bus_properties_changed_new(
1274 "/org/freedesktop/locale1",
1275 "org.freedesktop.locale1",
1284 r = convert_x11_to_vconsole(connection);
1287 log_error("Failed to convert keymap data: %s", strerror(-r));
1291 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
1293 if (!(reply = dbus_message_new_method_return(message)))
1296 if (!dbus_connection_send(connection, reply, NULL))
1299 dbus_message_unref(reply);
1304 if (!dbus_connection_send(connection, changed, NULL))
1307 dbus_message_unref(changed);
1310 return DBUS_HANDLER_RESULT_HANDLED;
1314 dbus_message_unref(reply);
1317 dbus_message_unref(changed);
1319 dbus_error_free(&error);
1321 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1324 static int connect_bus(DBusConnection **_bus) {
1325 static const DBusObjectPathVTable locale_vtable = {
1326 .message_function = locale_message_handler
1329 DBusConnection *bus = NULL;
1334 dbus_error_init(&error);
1336 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1338 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
1343 dbus_connection_set_exit_on_disconnect(bus, FALSE);
1345 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL) ||
1346 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
1347 log_error("Not enough memory");
1352 r = dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
1353 if (dbus_error_is_set(&error)) {
1354 log_error("Failed to register name on bus: %s", bus_error_message(&error));
1359 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1360 log_error("Failed to acquire name.");
1371 dbus_connection_close(bus);
1372 dbus_connection_unref(bus);
1374 dbus_error_free(&error);
1379 int main(int argc, char *argv[]) {
1381 DBusConnection *bus = NULL;
1382 bool exiting = false;
1384 log_set_target(LOG_TARGET_AUTO);
1385 log_parse_environment();
1390 if (argc == 2 && streq(argv[1], "--introspect")) {
1391 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
1392 "<node>\n", stdout);
1393 fputs(locale_interface, stdout);
1394 fputs("</node>\n", stdout);
1399 log_error("This program takes no arguments.");
1406 log_error("Failed to read locale data: %s", strerror(-r));
1410 r = connect_bus(&bus);
1414 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1417 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
1420 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
1422 bus_async_unregister_and_exit(bus, "org.freedesktop.locale1");
1432 dbus_connection_flush(bus);
1433 dbus_connection_close(bus);
1434 dbus_connection_unref(bus);
1437 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;