1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
7 Copyright 2013 Kay Sievers
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/capability.h>
36 #include "fileio-label.h"
39 #include "bus-error.h"
40 #include "bus-message.h"
41 #include "event-util.h"
42 #include "locale-util.h"
45 /* We don't list LC_ALL here on purpose. People should be
46 * using LANG instead. */
59 LOCALE_LC_MEASUREMENT
,
60 LOCALE_LC_IDENTIFICATION
,
64 static const char * const names
[_LOCALE_MAX
] = {
65 [LOCALE_LANG
] = "LANG",
66 [LOCALE_LANGUAGE
] = "LANGUAGE",
67 [LOCALE_LC_CTYPE
] = "LC_CTYPE",
68 [LOCALE_LC_NUMERIC
] = "LC_NUMERIC",
69 [LOCALE_LC_TIME
] = "LC_TIME",
70 [LOCALE_LC_COLLATE
] = "LC_COLLATE",
71 [LOCALE_LC_MONETARY
] = "LC_MONETARY",
72 [LOCALE_LC_MESSAGES
] = "LC_MESSAGES",
73 [LOCALE_LC_PAPER
] = "LC_PAPER",
74 [LOCALE_LC_NAME
] = "LC_NAME",
75 [LOCALE_LC_ADDRESS
] = "LC_ADDRESS",
76 [LOCALE_LC_TELEPHONE
] = "LC_TELEPHONE",
77 [LOCALE_LC_MEASUREMENT
] = "LC_MEASUREMENT",
78 [LOCALE_LC_IDENTIFICATION
] = "LC_IDENTIFICATION"
81 typedef struct Context
{
82 char *locale
[_LOCALE_MAX
];
90 char *vc_keymap_toggle
;
92 Hashmap
*polkit_registry
;
95 static int free_and_copy(char **s
, const char *v
) {
101 r
= strdup_or_null(isempty(v
) ? NULL
: v
, &t
);
111 static void free_and_replace(char **s
, char *v
) {
116 static void context_free_x11(Context
*c
) {
117 free_and_replace(&c
->x11_layout
, NULL
);
118 free_and_replace(&c
->x11_model
, NULL
);
119 free_and_replace(&c
->x11_variant
, NULL
);
120 free_and_replace(&c
->x11_options
, NULL
);
123 static void context_free_vconsole(Context
*c
) {
124 free_and_replace(&c
->vc_keymap
, NULL
);
125 free_and_replace(&c
->vc_keymap_toggle
, NULL
);
128 static void context_free_locale(Context
*c
) {
131 for (p
= 0; p
< _LOCALE_MAX
; p
++)
132 free_and_replace(&c
->locale
[p
], NULL
);
135 static void context_free(Context
*c
, sd_bus
*bus
) {
136 context_free_locale(c
);
138 context_free_vconsole(c
);
140 bus_verify_polkit_async_registry_free(bus
, c
->polkit_registry
);
143 static void locale_simplify(Context
*c
) {
146 for (p
= LOCALE_LANG
+1; p
< _LOCALE_MAX
; p
++)
147 if (isempty(c
->locale
[p
]) || streq_ptr(c
->locale
[LOCALE_LANG
], c
->locale
[p
])) {
153 static int locale_read_data(Context
*c
) {
156 context_free_locale(c
);
158 r
= parse_env_file("/etc/locale.conf", NEWLINE
,
159 "LANG", &c
->locale
[LOCALE_LANG
],
160 "LANGUAGE", &c
->locale
[LOCALE_LANGUAGE
],
161 "LC_CTYPE", &c
->locale
[LOCALE_LC_CTYPE
],
162 "LC_NUMERIC", &c
->locale
[LOCALE_LC_NUMERIC
],
163 "LC_TIME", &c
->locale
[LOCALE_LC_TIME
],
164 "LC_COLLATE", &c
->locale
[LOCALE_LC_COLLATE
],
165 "LC_MONETARY", &c
->locale
[LOCALE_LC_MONETARY
],
166 "LC_MESSAGES", &c
->locale
[LOCALE_LC_MESSAGES
],
167 "LC_PAPER", &c
->locale
[LOCALE_LC_PAPER
],
168 "LC_NAME", &c
->locale
[LOCALE_LC_NAME
],
169 "LC_ADDRESS", &c
->locale
[LOCALE_LC_ADDRESS
],
170 "LC_TELEPHONE", &c
->locale
[LOCALE_LC_TELEPHONE
],
171 "LC_MEASUREMENT", &c
->locale
[LOCALE_LC_MEASUREMENT
],
172 "LC_IDENTIFICATION", &c
->locale
[LOCALE_LC_IDENTIFICATION
],
178 /* Fill in what we got passed from systemd. */
179 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
182 r
= free_and_copy(&c
->locale
[p
], getenv(names
[p
]));
194 static int vconsole_read_data(Context
*c
) {
197 context_free_vconsole(c
);
199 r
= parse_env_file("/etc/vconsole.conf", NEWLINE
,
200 "KEYMAP", &c
->vc_keymap
,
201 "KEYMAP_TOGGLE", &c
->vc_keymap_toggle
,
204 if (r
< 0 && r
!= -ENOENT
)
210 static int x11_read_data(Context
*c
) {
213 bool in_section
= false;
218 f
= fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
220 return errno
== ENOENT
? 0 : -errno
;
222 while (fgets(line
, sizeof(line
), f
)) {
228 if (l
[0] == 0 || l
[0] == '#')
231 if (in_section
&& first_word(l
, "Option")) {
234 r
= strv_split_quoted(&a
, l
);
240 if (strv_length(a
) == 3) {
241 if (streq(a
[1], "XkbLayout")) {
242 free_and_replace(&c
->x11_layout
, a
[2]);
244 } else if (streq(a
[1], "XkbModel")) {
245 free_and_replace(&c
->x11_model
, a
[2]);
247 } else if (streq(a
[1], "XkbVariant")) {
248 free_and_replace(&c
->x11_variant
, a
[2]);
250 } else if (streq(a
[1], "XkbOptions")) {
251 free_and_replace(&c
->x11_options
, a
[2]);
258 } else if (!in_section
&& first_word(l
, "Section")) {
261 r
= strv_split_quoted(&a
, l
);
267 if (strv_length(a
) == 2 && streq(a
[1], "InputClass"))
271 } else if (in_section
&& first_word(l
, "EndSection"))
280 static int context_read_data(Context
*c
) {
283 r
= locale_read_data(c
);
284 q
= vconsole_read_data(c
);
285 p
= x11_read_data(c
);
287 return r
< 0 ? r
: q
< 0 ? q
: p
;
290 static int locale_write_data(Context
*c
) {
294 r
= load_env_file(NULL
, "/etc/locale.conf", NULL
, &l
);
295 if (r
< 0 && r
!= -ENOENT
)
298 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
303 if (isempty(c
->locale
[p
])) {
304 l
= strv_env_unset(l
, names
[p
]);
308 if (asprintf(&t
, "%s=%s", names
[p
], c
->locale
[p
]) < 0) {
313 u
= strv_env_set(l
, t
);
323 if (strv_isempty(l
)) {
326 if (unlink("/etc/locale.conf") < 0)
327 return errno
== ENOENT
? 0 : -errno
;
332 r
= write_env_file_label("/etc/locale.conf", l
);
338 static int locale_update_system_manager(Context
*c
, sd_bus
*bus
) {
339 _cleanup_free_
char **l_unset
= NULL
;
340 _cleanup_strv_free_
char **l_set
= NULL
;
341 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
342 sd_bus_error error
= SD_BUS_ERROR_NULL
;
343 unsigned c_set
, c_unset
, p
;
348 l_unset
= new0(char*, _LOCALE_MAX
);
352 l_set
= new0(char*, _LOCALE_MAX
);
356 for (p
= 0, c_set
= 0, c_unset
= 0; p
< _LOCALE_MAX
; p
++) {
359 if (isempty(c
->locale
[p
]))
360 l_unset
[c_set
++] = (char*) names
[p
];
364 if (asprintf(&s
, "%s=%s", names
[p
], c
->locale
[p
]) < 0)
367 l_set
[c_unset
++] = s
;
371 assert(c_set
+ c_unset
== _LOCALE_MAX
);
372 r
= sd_bus_message_new_method_call(bus
, &m
,
373 "org.freedesktop.systemd1",
374 "/org/freedesktop/systemd1",
375 "org.freedesktop.systemd1.Manager",
376 "UnsetAndSetEnvironment");
380 r
= sd_bus_message_append_strv(m
, l_unset
);
384 r
= sd_bus_message_append_strv(m
, l_set
);
388 r
= sd_bus_call(bus
, m
, 0, &error
, NULL
);
390 log_error("Failed to update the manager environment: %s", strerror(-r
));
395 static int vconsole_write_data(Context
*c
) {
397 _cleanup_strv_free_
char **l
= NULL
;
399 r
= load_env_file(NULL
, "/etc/vconsole.conf", NULL
, &l
);
400 if (r
< 0 && r
!= -ENOENT
)
403 if (isempty(c
->vc_keymap
))
404 l
= strv_env_unset(l
, "KEYMAP");
408 s
= strappend("KEYMAP=", c
->vc_keymap
);
412 u
= strv_env_set(l
, s
);
422 if (isempty(c
->vc_keymap_toggle
))
423 l
= strv_env_unset(l
, "KEYMAP_TOGGLE");
427 s
= strappend("KEYMAP_TOGGLE=", c
->vc_keymap_toggle
);
431 u
= strv_env_set(l
, s
);
441 if (strv_isempty(l
)) {
442 if (unlink("/etc/vconsole.conf") < 0)
443 return errno
== ENOENT
? 0 : -errno
;
448 r
= write_env_file_label("/etc/vconsole.conf", l
);
452 static int write_data_x11(Context
*c
) {
453 _cleanup_fclose_
FILE *f
= NULL
;
454 _cleanup_free_
char *temp_path
= NULL
;
457 if (isempty(c
->x11_layout
) &&
458 isempty(c
->x11_model
) &&
459 isempty(c
->x11_variant
) &&
460 isempty(c
->x11_options
)) {
462 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
463 return errno
== ENOENT
? 0 : -errno
;
468 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
470 r
= fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f
, &temp_path
);
474 fchmod(fileno(f
), 0644);
476 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
477 "# manually too freely.\n"
478 "Section \"InputClass\"\n"
479 " Identifier \"system-keyboard\"\n"
480 " MatchIsKeyboard \"on\"\n", f
);
482 if (!isempty(c
->x11_layout
))
483 fprintf(f
, " Option \"XkbLayout\" \"%s\"\n", c
->x11_layout
);
485 if (!isempty(c
->x11_model
))
486 fprintf(f
, " Option \"XkbModel\" \"%s\"\n", c
->x11_model
);
488 if (!isempty(c
->x11_variant
))
489 fprintf(f
, " Option \"XkbVariant\" \"%s\"\n", c
->x11_variant
);
491 if (!isempty(c
->x11_options
))
492 fprintf(f
, " Option \"XkbOptions\" \"%s\"\n", c
->x11_options
);
494 fputs("EndSection\n", f
);
497 if (ferror(f
) || rename(temp_path
, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
499 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
506 static int vconsole_reload(sd_bus
*bus
) {
507 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
512 r
= sd_bus_call_method(bus
,
513 "org.freedesktop.systemd1",
514 "/org/freedesktop/systemd1",
515 "org.freedesktop.systemd1.Manager",
519 "ss", "systemd-vconsole-setup.service", "replace");
522 log_error("Failed to issue method call: %s", bus_error_message(&error
, -r
));
526 static char *strnulldash(const char *s
) {
527 return s
== NULL
|| *s
== 0 || (s
[0] == '-' && s
[1] == 0) ? NULL
: (char*) s
;
530 static int read_next_mapping(FILE *f
, unsigned *n
, char ***a
) {
541 if (!fgets(line
, sizeof(line
), f
)) {
544 return errno
? -errno
: -EIO
;
552 if (l
[0] == 0 || l
[0] == '#')
555 r
= strv_split_quoted(&b
, l
);
559 if (strv_length(b
) < 5) {
560 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP
":%u, ignoring.", *n
);
571 static int vconsole_convert_to_x11(Context
*c
, sd_bus
*bus
) {
572 bool modified
= false;
576 if (isempty(c
->vc_keymap
)) {
579 !isempty(c
->x11_layout
) ||
580 !isempty(c
->x11_model
) ||
581 !isempty(c
->x11_variant
) ||
582 !isempty(c
->x11_options
);
586 _cleanup_fclose_
FILE *f
= NULL
;
589 f
= fopen(SYSTEMD_KBD_MODEL_MAP
, "re");
594 _cleanup_strv_free_
char **a
= NULL
;
597 r
= read_next_mapping(f
, &n
, &a
);
603 if (!streq(c
->vc_keymap
, a
[0]))
606 if (!streq_ptr(c
->x11_layout
, strnulldash(a
[1])) ||
607 !streq_ptr(c
->x11_model
, strnulldash(a
[2])) ||
608 !streq_ptr(c
->x11_variant
, strnulldash(a
[3])) ||
609 !streq_ptr(c
->x11_options
, strnulldash(a
[4]))) {
611 if (free_and_copy(&c
->x11_layout
, strnulldash(a
[1])) < 0 ||
612 free_and_copy(&c
->x11_model
, strnulldash(a
[2])) < 0 ||
613 free_and_copy(&c
->x11_variant
, strnulldash(a
[3])) < 0 ||
614 free_and_copy(&c
->x11_options
, strnulldash(a
[4])) < 0)
627 r
= write_data_x11(c
);
629 log_error("Failed to set X11 keyboard layout: %s", strerror(-r
));
631 sd_bus_emit_properties_changed(bus
,
632 "/org/freedesktop/locale1",
633 "org.freedesktop.locale1",
634 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL
);
640 static int find_converted_keymap(Context
*c
, char **new_keymap
) {
642 _cleanup_free_
char *n
;
645 n
= strjoin(c
->x11_layout
, "-", c
->x11_variant
, NULL
);
647 n
= strdup(c
->x11_layout
);
651 NULSTR_FOREACH(dir
, KBD_KEYMAP_DIRS
) {
652 _cleanup_free_
char *p
= NULL
, *pz
= NULL
;
654 p
= strjoin(dir
, "xkb/", n
, ".map", NULL
);
655 pz
= strjoin(dir
, "xkb/", n
, ".map.gz", NULL
);
659 if (access(p
, F_OK
) == 0 || access(pz
, F_OK
) == 0) {
669 static int find_legacy_keymap(Context
*c
, char **new_keymap
) {
670 _cleanup_fclose_
FILE *f
;
672 unsigned best_matching
= 0;
675 f
= fopen(SYSTEMD_KBD_MODEL_MAP
, "re");
680 _cleanup_strv_free_
char **a
= NULL
;
681 unsigned matching
= 0;
684 r
= read_next_mapping(f
, &n
, &a
);
690 /* Determine how well matching this entry is */
691 if (streq_ptr(c
->x11_layout
, a
[1]))
692 /* If we got an exact match, this is best */
697 x
= strcspn(c
->x11_layout
, ",");
699 /* We have multiple X layouts, look for an
700 * entry that matches our key with everything
701 * but the first layout stripped off. */
704 strneq(c
->x11_layout
, a
[1], x
))
709 /* If that didn't work, strip off the
710 * other layouts from the entry, too */
711 w
= strcspn(a
[1], ",");
713 if (x
> 0 && x
== w
&&
714 memcmp(c
->x11_layout
, a
[1], x
) == 0)
720 if (isempty(c
->x11_model
) || streq_ptr(c
->x11_model
, a
[2])) {
723 if (streq_ptr(c
->x11_variant
, a
[3])) {
726 if (streq_ptr(c
->x11_options
, a
[4]))
732 /* The best matching entry so far, then let's save that */
733 if (matching
> best_matching
) {
734 best_matching
= matching
;
737 *new_keymap
= strdup(a
[0]);
746 static int x11_convert_to_vconsole(Context
*c
, sd_bus
*bus
) {
747 bool modified
= false;
752 if (isempty(c
->x11_layout
)) {
755 !isempty(c
->vc_keymap
) ||
756 !isempty(c
->vc_keymap_toggle
);
760 char *new_keymap
= NULL
;
762 r
= find_converted_keymap(c
, &new_keymap
);
766 r
= find_legacy_keymap(c
, &new_keymap
);
771 if (!streq_ptr(c
->vc_keymap
, new_keymap
)) {
772 free_and_replace(&c
->vc_keymap
, new_keymap
);
773 free_and_replace(&c
->vc_keymap_toggle
, NULL
);
780 r
= vconsole_write_data(c
);
782 log_error("Failed to set virtual console keymap: %s", strerror(-r
));
784 sd_bus_emit_properties_changed(bus
,
785 "/org/freedesktop/locale1",
786 "org.freedesktop.locale1",
787 "VConsoleKeymap", "VConsoleKeymapToggle", NULL
);
789 return vconsole_reload(bus
);
795 static int property_get_locale(
798 const char *interface
,
799 const char *property
,
800 sd_bus_message
*reply
,
802 sd_bus_error
*error
) {
804 Context
*c
= userdata
;
805 _cleanup_strv_free_
char **l
= NULL
;
808 l
= new0(char*, _LOCALE_MAX
+1);
812 for (p
= 0, q
= 0; p
< _LOCALE_MAX
; p
++) {
815 if (isempty(c
->locale
[p
]))
818 if (asprintf(&t
, "%s=%s", names
[p
], c
->locale
[p
]) < 0)
824 return sd_bus_message_append_strv(reply
, l
);
827 static int method_set_locale(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
828 Context
*c
= userdata
;
829 _cleanup_strv_free_
char **l
= NULL
;
832 bool modified
= false;
833 bool passed
[_LOCALE_MAX
] = {};
837 r
= bus_message_read_strv_extend(m
, &l
);
841 r
= sd_bus_message_read_basic(m
, 'b', &interactive
);
845 /* Check whether a variable changed and if so valid */
849 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
852 k
= strlen(names
[p
]);
853 if (startswith(*i
, names
[p
]) &&
855 locale_is_valid((*i
) + k
+ 1)) {
859 if (!streq_ptr(*i
+ k
+ 1, c
->locale
[p
]))
867 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid Locale data.");
870 /* Check whether a variable is unset */
872 for (p
= 0; p
< _LOCALE_MAX
; p
++)
873 if (!isempty(c
->locale
[p
]) && !passed
[p
]) {
880 r
= bus_verify_polkit_async(bus
, &c
->polkit_registry
, m
, CAP_SYS_ADMIN
,
881 "org.freedesktop.locale1.set-locale", interactive
,
882 error
, method_set_locale
, c
);
886 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
889 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
892 k
= strlen(names
[p
]);
893 if (startswith(*i
, names
[p
]) && (*i
)[k
] == '=') {
896 t
= strdup(*i
+ k
+ 1);
907 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
911 free_and_replace(&c
->locale
[p
], NULL
);
916 r
= locale_write_data(c
);
918 log_error("Failed to set locale: %s", strerror(-r
));
919 return sd_bus_error_set_errnof(error
, r
, "Failed to set locale: %s", strerror(-r
));
922 locale_update_system_manager(c
, bus
);
924 log_info("Changed locale information.");
926 sd_bus_emit_properties_changed(bus
,
927 "/org/freedesktop/locale1",
928 "org.freedesktop.locale1",
932 return sd_bus_reply_method_return(m
, NULL
);
935 static int method_set_vc_keyboard(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
936 Context
*c
= userdata
;
937 const char *keymap
, *keymap_toggle
;
938 int convert
, interactive
;
941 r
= sd_bus_message_read(m
, "ssbb", &keymap
, &keymap_toggle
, &convert
, &interactive
);
948 if (isempty(keymap_toggle
))
949 keymap_toggle
= NULL
;
951 if (!streq_ptr(keymap
, c
->vc_keymap
) ||
952 !streq_ptr(keymap_toggle
, c
->vc_keymap_toggle
)) {
954 if ((keymap
&& (!filename_is_safe(keymap
) || !string_is_safe(keymap
))) ||
955 (keymap_toggle
&& (!filename_is_safe(keymap_toggle
) || !string_is_safe(keymap_toggle
))))
956 return sd_bus_error_set_errnof(error
, -EINVAL
, "Received invalid keymap data");
958 r
= bus_verify_polkit_async(bus
, &c
->polkit_registry
, m
, CAP_SYS_ADMIN
,
959 "org.freedesktop.locale1.set-keyboard",
960 interactive
, error
, method_set_vc_keyboard
, c
);
964 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
966 if (free_and_copy(&c
->vc_keymap
, keymap
) < 0 ||
967 free_and_copy(&c
->vc_keymap_toggle
, keymap_toggle
) < 0)
970 r
= vconsole_write_data(c
);
972 log_error("Failed to set virtual console keymap: %s", strerror(-r
));
973 return sd_bus_error_set_errnof(error
, r
, "Failed to set virtual console keymap: %s", strerror(-r
));
976 log_info("Changed virtual console keymap to '%s'", strempty(c
->vc_keymap
));
978 r
= vconsole_reload(bus
);
980 log_error("Failed to request keymap reload: %s", strerror(-r
));
982 sd_bus_emit_properties_changed(bus
,
983 "/org/freedesktop/locale1",
984 "org.freedesktop.locale1",
985 "VConsoleKeymap", "VConsoleKeymapToggle", NULL
);
988 r
= vconsole_convert_to_x11(c
, bus
);
990 log_error("Failed to convert keymap data: %s", strerror(-r
));
994 return sd_bus_reply_method_return(m
, NULL
);
997 static int method_set_x11_keyboard(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
998 Context
*c
= userdata
;
999 const char *layout
, *model
, *variant
, *options
;
1000 int convert
, interactive
;
1003 r
= sd_bus_message_read(m
, "ssssbb", &layout
, &model
, &variant
, &options
, &convert
, &interactive
);
1007 if (isempty(layout
))
1013 if (isempty(variant
))
1016 if (isempty(options
))
1019 if (!streq_ptr(layout
, c
->x11_layout
) ||
1020 !streq_ptr(model
, c
->x11_model
) ||
1021 !streq_ptr(variant
, c
->x11_variant
) ||
1022 !streq_ptr(options
, c
->x11_options
)) {
1024 if ((layout
&& !string_is_safe(layout
)) ||
1025 (model
&& !string_is_safe(model
)) ||
1026 (variant
&& !string_is_safe(variant
)) ||
1027 (options
&& !string_is_safe(options
)))
1028 return sd_bus_error_set_errnof(error
, -EINVAL
, "Received invalid keyboard data");
1030 r
= bus_verify_polkit_async(bus
, &c
->polkit_registry
, m
, CAP_SYS_ADMIN
,
1031 "org.freedesktop.locale1.set-keyboard",
1032 interactive
, error
, method_set_x11_keyboard
, c
);
1036 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1038 if (free_and_copy(&c
->x11_layout
, layout
) < 0 ||
1039 free_and_copy(&c
->x11_model
, model
) < 0 ||
1040 free_and_copy(&c
->x11_variant
, variant
) < 0 ||
1041 free_and_copy(&c
->x11_options
, options
) < 0)
1044 r
= write_data_x11(c
);
1046 log_error("Failed to set X11 keyboard layout: %s", strerror(-r
));
1047 return sd_bus_error_set_errnof(error
, r
, "Failed to set X11 keyboard layout: %s", strerror(-r
));
1050 log_info("Changed X11 keyboard layout to '%s'", strempty(c
->x11_layout
));
1052 sd_bus_emit_properties_changed(bus
,
1053 "/org/freedesktop/locale1",
1054 "org.freedesktop.locale1",
1055 "X11Layout" "X11Model" "X11Variant" "X11Options", NULL
);
1058 r
= x11_convert_to_vconsole(c
, bus
);
1060 log_error("Failed to convert keymap data: %s", strerror(-r
));
1064 return sd_bus_reply_method_return(m
, NULL
);
1067 static const sd_bus_vtable locale_vtable
[] = {
1068 SD_BUS_VTABLE_START(0),
1069 SD_BUS_PROPERTY("Locale", "as", property_get_locale
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1070 SD_BUS_PROPERTY("X11Layout", "s", NULL
, offsetof(Context
, x11_layout
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1071 SD_BUS_PROPERTY("X11Model", "s", NULL
, offsetof(Context
, x11_model
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1072 SD_BUS_PROPERTY("X11Variant", "s", NULL
, offsetof(Context
, x11_variant
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1073 SD_BUS_PROPERTY("X11Options", "s", NULL
, offsetof(Context
, x11_options
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1074 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL
, offsetof(Context
, vc_keymap
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1075 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL
, offsetof(Context
, vc_keymap_toggle
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1076 SD_BUS_METHOD("SetLocale", "asb", NULL
, method_set_locale
, SD_BUS_VTABLE_UNPRIVILEGED
),
1077 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL
, method_set_vc_keyboard
, SD_BUS_VTABLE_UNPRIVILEGED
),
1078 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL
, method_set_x11_keyboard
, SD_BUS_VTABLE_UNPRIVILEGED
),
1082 static int connect_bus(Context
*c
, sd_event
*event
, sd_bus
**_bus
) {
1083 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
1090 r
= sd_bus_default_system(&bus
);
1092 log_error("Failed to get system bus connection: %s", strerror(-r
));
1096 r
= sd_bus_add_object_vtable(bus
, NULL
, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable
, c
);
1098 log_error("Failed to register object: %s", strerror(-r
));
1102 r
= sd_bus_request_name(bus
, "org.freedesktop.locale1", 0);
1104 log_error("Failed to register name: %s", strerror(-r
));
1108 r
= sd_bus_attach_event(bus
, event
, 0);
1110 log_error("Failed to attach bus to event loop: %s", strerror(-r
));
1120 int main(int argc
, char *argv
[]) {
1121 Context context
= {};
1122 _cleanup_event_unref_ sd_event
*event
= NULL
;
1123 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
1126 log_set_target(LOG_TARGET_AUTO
);
1127 log_parse_environment();
1134 log_error("This program takes no arguments.");
1139 r
= sd_event_default(&event
);
1141 log_error("Failed to allocate event loop: %s", strerror(-r
));
1145 sd_event_set_watchdog(event
, true);
1147 r
= connect_bus(&context
, event
, &bus
);
1151 r
= context_read_data(&context
);
1153 log_error("Failed to read locale data: %s", strerror(-r
));
1157 r
= bus_event_loop_with_idle(event
, bus
, "org.freedesktop.locale1", DEFAULT_EXIT_USEC
, NULL
, NULL
);
1159 log_error("Failed to run event loop: %s", strerror(-r
));
1164 context_free(&context
, bus
);
1166 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;