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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
18 You should have received a copy of the GNU 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
;