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 const char* nonempty(const char *s
) {
96 return isempty(s
) ? NULL
: s
;
99 static void free_and_replace(char **s
, char *v
) {
104 static bool startswith_comma(const char *s
, const char *prefix
) {
107 return s
&& (t
= startswith(s
, prefix
)) && (*t
== ',');
110 static void context_free_x11(Context
*c
) {
111 free_and_replace(&c
->x11_layout
, NULL
);
112 free_and_replace(&c
->x11_model
, NULL
);
113 free_and_replace(&c
->x11_variant
, NULL
);
114 free_and_replace(&c
->x11_options
, NULL
);
117 static void context_free_vconsole(Context
*c
) {
118 free_and_replace(&c
->vc_keymap
, NULL
);
119 free_and_replace(&c
->vc_keymap_toggle
, NULL
);
122 static void context_free_locale(Context
*c
) {
125 for (p
= 0; p
< _LOCALE_MAX
; p
++)
126 free_and_replace(&c
->locale
[p
], NULL
);
129 static void context_free(Context
*c
) {
130 context_free_locale(c
);
132 context_free_vconsole(c
);
134 bus_verify_polkit_async_registry_free(c
->polkit_registry
);
137 static void locale_simplify(Context
*c
) {
140 for (p
= LOCALE_LANG
+1; p
< _LOCALE_MAX
; p
++)
141 if (isempty(c
->locale
[p
]) || streq_ptr(c
->locale
[LOCALE_LANG
], c
->locale
[p
]))
142 free_and_replace(&c
->locale
[p
], NULL
);
145 static int locale_read_data(Context
*c
) {
148 context_free_locale(c
);
150 r
= parse_env_file("/etc/locale.conf", NEWLINE
,
151 "LANG", &c
->locale
[LOCALE_LANG
],
152 "LANGUAGE", &c
->locale
[LOCALE_LANGUAGE
],
153 "LC_CTYPE", &c
->locale
[LOCALE_LC_CTYPE
],
154 "LC_NUMERIC", &c
->locale
[LOCALE_LC_NUMERIC
],
155 "LC_TIME", &c
->locale
[LOCALE_LC_TIME
],
156 "LC_COLLATE", &c
->locale
[LOCALE_LC_COLLATE
],
157 "LC_MONETARY", &c
->locale
[LOCALE_LC_MONETARY
],
158 "LC_MESSAGES", &c
->locale
[LOCALE_LC_MESSAGES
],
159 "LC_PAPER", &c
->locale
[LOCALE_LC_PAPER
],
160 "LC_NAME", &c
->locale
[LOCALE_LC_NAME
],
161 "LC_ADDRESS", &c
->locale
[LOCALE_LC_ADDRESS
],
162 "LC_TELEPHONE", &c
->locale
[LOCALE_LC_TELEPHONE
],
163 "LC_MEASUREMENT", &c
->locale
[LOCALE_LC_MEASUREMENT
],
164 "LC_IDENTIFICATION", &c
->locale
[LOCALE_LC_IDENTIFICATION
],
170 /* Fill in what we got passed from systemd. */
171 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
174 r
= free_and_strdup(&c
->locale
[p
],
175 nonempty(getenv(names
[p
])));
187 static int vconsole_read_data(Context
*c
) {
190 context_free_vconsole(c
);
192 r
= parse_env_file("/etc/vconsole.conf", NEWLINE
,
193 "KEYMAP", &c
->vc_keymap
,
194 "KEYMAP_TOGGLE", &c
->vc_keymap_toggle
,
197 if (r
< 0 && r
!= -ENOENT
)
203 static int x11_read_data(Context
*c
) {
204 _cleanup_fclose_
FILE *f
;
206 bool in_section
= false;
211 f
= fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
213 return errno
== ENOENT
? 0 : -errno
;
215 while (fgets(line
, sizeof(line
), f
)) {
221 if (l
[0] == 0 || l
[0] == '#')
224 if (in_section
&& first_word(l
, "Option")) {
225 _cleanup_strv_free_
char **a
= NULL
;
227 r
= strv_split_quoted(&a
, l
);
231 if (strv_length(a
) == 3) {
232 if (streq(a
[1], "XkbLayout")) {
233 free_and_replace(&c
->x11_layout
, a
[2]);
235 } else if (streq(a
[1], "XkbModel")) {
236 free_and_replace(&c
->x11_model
, a
[2]);
238 } else if (streq(a
[1], "XkbVariant")) {
239 free_and_replace(&c
->x11_variant
, a
[2]);
241 } else if (streq(a
[1], "XkbOptions")) {
242 free_and_replace(&c
->x11_options
, a
[2]);
247 } else if (!in_section
&& first_word(l
, "Section")) {
248 _cleanup_strv_free_
char **a
= NULL
;
250 r
= strv_split_quoted(&a
, l
);
254 if (strv_length(a
) == 2 && streq(a
[1], "InputClass"))
257 } else if (in_section
&& first_word(l
, "EndSection"))
264 static int context_read_data(Context
*c
) {
267 r
= locale_read_data(c
);
268 q
= vconsole_read_data(c
);
269 p
= x11_read_data(c
);
271 return r
< 0 ? r
: q
< 0 ? q
: p
;
274 static int locale_write_data(Context
*c
, char ***settings
) {
276 _cleanup_strv_free_
char **l
= NULL
;
278 /* Set values will be returned as strv in *settings on success. */
280 r
= load_env_file(NULL
, "/etc/locale.conf", NULL
, &l
);
281 if (r
< 0 && r
!= -ENOENT
)
284 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
285 _cleanup_free_
char *t
= NULL
;
290 if (isempty(c
->locale
[p
])) {
291 l
= strv_env_unset(l
, names
[p
]);
295 if (asprintf(&t
, "%s=%s", names
[p
], c
->locale
[p
]) < 0)
298 u
= strv_env_set(l
, t
);
306 if (strv_isempty(l
)) {
307 if (unlink("/etc/locale.conf") < 0)
308 return errno
== ENOENT
? 0 : -errno
;
313 r
= write_env_file_label("/etc/locale.conf", l
);
322 static int locale_update_system_manager(Context
*c
, sd_bus
*bus
) {
323 _cleanup_free_
char **l_unset
= NULL
;
324 _cleanup_strv_free_
char **l_set
= NULL
;
325 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
326 sd_bus_error error
= SD_BUS_ERROR_NULL
;
327 unsigned c_set
, c_unset
, p
;
332 l_unset
= new0(char*, _LOCALE_MAX
);
336 l_set
= new0(char*, _LOCALE_MAX
);
340 for (p
= 0, c_set
= 0, c_unset
= 0; p
< _LOCALE_MAX
; p
++) {
343 if (isempty(c
->locale
[p
]))
344 l_unset
[c_set
++] = (char*) names
[p
];
348 if (asprintf(&s
, "%s=%s", names
[p
], c
->locale
[p
]) < 0)
351 l_set
[c_unset
++] = s
;
355 assert(c_set
+ c_unset
== _LOCALE_MAX
);
356 r
= sd_bus_message_new_method_call(bus
, &m
,
357 "org.freedesktop.systemd1",
358 "/org/freedesktop/systemd1",
359 "org.freedesktop.systemd1.Manager",
360 "UnsetAndSetEnvironment");
364 r
= sd_bus_message_append_strv(m
, l_unset
);
368 r
= sd_bus_message_append_strv(m
, l_set
);
372 r
= sd_bus_call(bus
, m
, 0, &error
, NULL
);
374 log_error("Failed to update the manager environment: %s", strerror(-r
));
379 static int vconsole_write_data(Context
*c
) {
381 _cleanup_strv_free_
char **l
= NULL
;
383 r
= load_env_file(NULL
, "/etc/vconsole.conf", NULL
, &l
);
384 if (r
< 0 && r
!= -ENOENT
)
387 if (isempty(c
->vc_keymap
))
388 l
= strv_env_unset(l
, "KEYMAP");
390 _cleanup_free_
char *s
= NULL
;
393 s
= strappend("KEYMAP=", c
->vc_keymap
);
397 u
= strv_env_set(l
, s
);
405 if (isempty(c
->vc_keymap_toggle
))
406 l
= strv_env_unset(l
, "KEYMAP_TOGGLE");
408 _cleanup_free_
char *s
= NULL
;
411 s
= strappend("KEYMAP_TOGGLE=", c
->vc_keymap_toggle
);
415 u
= strv_env_set(l
, s
);
423 if (strv_isempty(l
)) {
424 if (unlink("/etc/vconsole.conf") < 0)
425 return errno
== ENOENT
? 0 : -errno
;
430 return write_env_file_label("/etc/vconsole.conf", l
);
433 static int x11_write_data(Context
*c
) {
434 _cleanup_fclose_
FILE *f
= NULL
;
435 _cleanup_free_
char *temp_path
= NULL
;
438 if (isempty(c
->x11_layout
) &&
439 isempty(c
->x11_model
) &&
440 isempty(c
->x11_variant
) &&
441 isempty(c
->x11_options
)) {
443 if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
444 return errno
== ENOENT
? 0 : -errno
;
449 mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
451 r
= fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f
, &temp_path
);
455 fchmod(fileno(f
), 0644);
457 fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
458 "# manually too freely.\n"
459 "Section \"InputClass\"\n"
460 " Identifier \"system-keyboard\"\n"
461 " MatchIsKeyboard \"on\"\n", f
);
463 if (!isempty(c
->x11_layout
))
464 fprintf(f
, " Option \"XkbLayout\" \"%s\"\n", c
->x11_layout
);
466 if (!isempty(c
->x11_model
))
467 fprintf(f
, " Option \"XkbModel\" \"%s\"\n", c
->x11_model
);
469 if (!isempty(c
->x11_variant
))
470 fprintf(f
, " Option \"XkbVariant\" \"%s\"\n", c
->x11_variant
);
472 if (!isempty(c
->x11_options
))
473 fprintf(f
, " Option \"XkbOptions\" \"%s\"\n", c
->x11_options
);
475 fputs("EndSection\n", f
);
478 if (ferror(f
) || rename(temp_path
, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
480 unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
487 static int vconsole_reload(sd_bus
*bus
) {
488 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
493 r
= sd_bus_call_method(bus
,
494 "org.freedesktop.systemd1",
495 "/org/freedesktop/systemd1",
496 "org.freedesktop.systemd1.Manager",
500 "ss", "systemd-vconsole-setup.service", "replace");
503 log_error("Failed to issue method call: %s", bus_error_message(&error
, -r
));
507 static const char* strnulldash(const char *s
) {
508 return isempty(s
) || streq(s
, "-") ? NULL
: s
;
511 static int read_next_mapping(FILE *f
, unsigned *n
, char ***a
) {
522 if (!fgets(line
, sizeof(line
), f
)) {
525 return errno
? -errno
: -EIO
;
533 if (l
[0] == 0 || l
[0] == '#')
536 r
= strv_split_quoted(&b
, l
);
540 if (strv_length(b
) < 5) {
541 log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP
":%u, ignoring.", *n
);
552 static int vconsole_convert_to_x11(Context
*c
, sd_bus
*bus
) {
553 bool modified
= false;
557 if (isempty(c
->vc_keymap
)) {
560 !isempty(c
->x11_layout
) ||
561 !isempty(c
->x11_model
) ||
562 !isempty(c
->x11_variant
) ||
563 !isempty(c
->x11_options
);
567 _cleanup_fclose_
FILE *f
= NULL
;
570 f
= fopen(SYSTEMD_KBD_MODEL_MAP
, "re");
575 _cleanup_strv_free_
char **a
= NULL
;
578 r
= read_next_mapping(f
, &n
, &a
);
584 if (!streq(c
->vc_keymap
, a
[0]))
587 if (!streq_ptr(c
->x11_layout
, strnulldash(a
[1])) ||
588 !streq_ptr(c
->x11_model
, strnulldash(a
[2])) ||
589 !streq_ptr(c
->x11_variant
, strnulldash(a
[3])) ||
590 !streq_ptr(c
->x11_options
, strnulldash(a
[4]))) {
592 if (free_and_strdup(&c
->x11_layout
, strnulldash(a
[1])) < 0 ||
593 free_and_strdup(&c
->x11_model
, strnulldash(a
[2])) < 0 ||
594 free_and_strdup(&c
->x11_variant
, strnulldash(a
[3])) < 0 ||
595 free_and_strdup(&c
->x11_options
, strnulldash(a
[4])) < 0)
608 r
= x11_write_data(c
);
610 log_error("Failed to set X11 keyboard layout: %s", strerror(-r
));
614 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
615 strempty(c
->x11_layout
),
616 strempty(c
->x11_model
),
617 strempty(c
->x11_variant
),
618 strempty(c
->x11_options
));
620 sd_bus_emit_properties_changed(bus
,
621 "/org/freedesktop/locale1",
622 "org.freedesktop.locale1",
623 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL
);
625 log_debug("X11 keyboard layout was not modified.");
630 static int find_converted_keymap(const char *x11_layout
, const char *x11_variant
, char **new_keymap
) {
632 _cleanup_free_
char *n
;
635 n
= strjoin(x11_layout
, "-", x11_variant
, NULL
);
637 n
= strdup(x11_layout
);
641 NULSTR_FOREACH(dir
, KBD_KEYMAP_DIRS
) {
642 _cleanup_free_
char *p
= NULL
, *pz
= NULL
;
645 p
= strjoin(dir
, "xkb/", n
, ".map", NULL
);
646 pz
= strjoin(dir
, "xkb/", n
, ".map.gz", NULL
);
650 uncompressed
= access(p
, F_OK
) == 0;
651 if (uncompressed
|| access(pz
, F_OK
) == 0) {
652 log_debug("Found converted keymap %s at %s",
653 n
, uncompressed
? p
: pz
);
664 static int find_legacy_keymap(Context
*c
, char **new_keymap
) {
665 _cleanup_fclose_
FILE *f
;
667 unsigned best_matching
= 0;
670 f
= fopen(SYSTEMD_KBD_MODEL_MAP
, "re");
675 _cleanup_strv_free_
char **a
= NULL
;
676 unsigned matching
= 0;
678 r
= read_next_mapping(f
, &n
, &a
);
684 /* Determine how well matching this entry is */
685 if (streq_ptr(c
->x11_layout
, a
[1]))
686 /* If we got an exact match, this is best */
689 /* We have multiple X layouts, look for an
690 * entry that matches our key with everything
691 * but the first layout stripped off. */
692 if (startswith_comma(c
->x11_layout
, a
[1]))
697 /* If that didn't work, strip off the
698 * other layouts from the entry, too */
699 x
= strndupa(a
[1], strcspn(a
[1], ","));
700 if (startswith_comma(c
->x11_layout
, x
))
706 if (isempty(c
->x11_model
) || streq_ptr(c
->x11_model
, a
[2])) {
709 if (streq_ptr(c
->x11_variant
, a
[3])) {
712 if (streq_ptr(c
->x11_options
, a
[4]))
718 /* The best matching entry so far, then let's save that */
719 if (matching
>= MAX(best_matching
, 1u)) {
720 log_debug("Found legacy keymap %s with score %u",
723 if (matching
> best_matching
) {
724 best_matching
= matching
;
726 r
= free_and_strdup(new_keymap
, a
[0]);
733 if (best_matching
< 10 && c
->x11_layout
) {
734 /* The best match is only the first part of the X11
735 * keymap. Check if we have a converted map which
736 * matches just the first layout.
738 char *l
, *v
= NULL
, *converted
;
740 l
= strndupa(c
->x11_layout
, strcspn(c
->x11_layout
, ","));
742 v
= strndupa(c
->x11_variant
, strcspn(c
->x11_variant
, ","));
743 r
= find_converted_keymap(l
, v
, &converted
);
747 free_and_replace(new_keymap
, converted
);
753 static int x11_convert_to_vconsole(Context
*c
, sd_bus
*bus
) {
754 bool modified
= false;
759 if (isempty(c
->x11_layout
)) {
762 !isempty(c
->vc_keymap
) ||
763 !isempty(c
->vc_keymap_toggle
);
767 char *new_keymap
= NULL
;
769 r
= find_converted_keymap(c
->x11_layout
, c
->x11_variant
, &new_keymap
);
773 r
= find_legacy_keymap(c
, &new_keymap
);
778 if (!streq_ptr(c
->vc_keymap
, new_keymap
)) {
779 free_and_replace(&c
->vc_keymap
, new_keymap
);
780 free_and_replace(&c
->vc_keymap_toggle
, NULL
);
787 r
= vconsole_write_data(c
);
789 log_error("Failed to set virtual console keymap: %s", strerror(-r
));
791 log_info("Changed virtual console keymap to '%s' toggle '%s'",
792 strempty(c
->vc_keymap
), strempty(c
->vc_keymap_toggle
));
794 sd_bus_emit_properties_changed(bus
,
795 "/org/freedesktop/locale1",
796 "org.freedesktop.locale1",
797 "VConsoleKeymap", "VConsoleKeymapToggle", NULL
);
799 return vconsole_reload(bus
);
801 log_debug("Virtual console keymap was not modified.");
806 static int property_get_locale(
809 const char *interface
,
810 const char *property
,
811 sd_bus_message
*reply
,
813 sd_bus_error
*error
) {
815 Context
*c
= userdata
;
816 _cleanup_strv_free_
char **l
= NULL
;
819 l
= new0(char*, _LOCALE_MAX
+1);
823 for (p
= 0, q
= 0; p
< _LOCALE_MAX
; p
++) {
826 if (isempty(c
->locale
[p
]))
829 if (asprintf(&t
, "%s=%s", names
[p
], c
->locale
[p
]) < 0)
835 return sd_bus_message_append_strv(reply
, l
);
838 static int method_set_locale(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
839 Context
*c
= userdata
;
840 _cleanup_strv_free_
char **l
= NULL
;
843 bool modified
= false;
844 bool passed
[_LOCALE_MAX
] = {};
848 r
= bus_message_read_strv_extend(m
, &l
);
852 r
= sd_bus_message_read_basic(m
, 'b', &interactive
);
856 /* Check whether a variable changed and if it is valid */
860 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
863 k
= strlen(names
[p
]);
864 if (startswith(*i
, names
[p
]) &&
866 locale_is_valid((*i
) + k
+ 1)) {
870 if (!streq_ptr(*i
+ k
+ 1, c
->locale
[p
]))
878 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid Locale data.");
881 /* Check whether a variable is unset */
883 for (p
= 0; p
< _LOCALE_MAX
; p
++)
884 if (!isempty(c
->locale
[p
]) && !passed
[p
]) {
890 _cleanup_strv_free_
char **settings
= NULL
;
892 r
= bus_verify_polkit_async(m
, CAP_SYS_ADMIN
, "org.freedesktop.locale1.set-locale", interactive
, &c
->polkit_registry
, error
);
896 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
899 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
902 k
= strlen(names
[p
]);
903 if (startswith(*i
, names
[p
]) && (*i
)[k
] == '=') {
904 r
= free_and_strdup(&c
->locale
[p
], *i
+ k
+ 1);
911 for (p
= 0; p
< _LOCALE_MAX
; p
++) {
915 free_and_replace(&c
->locale
[p
], NULL
);
920 r
= locale_write_data(c
, &settings
);
922 log_error("Failed to set locale: %s", strerror(-r
));
923 return sd_bus_error_set_errnof(error
, r
, "Failed to set locale: %s", strerror(-r
));
926 locale_update_system_manager(c
, bus
);
929 _cleanup_free_
char *line
;
931 line
= strv_join(settings
, ", ");
932 log_info("Changed locale to %s.", strnull(line
));
934 log_info("Changed locale to unset.");
936 sd_bus_emit_properties_changed(bus
,
937 "/org/freedesktop/locale1",
938 "org.freedesktop.locale1",
941 log_debug("Locale settings were not modified.");
944 return sd_bus_reply_method_return(m
, NULL
);
947 static int method_set_vc_keyboard(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
948 Context
*c
= userdata
;
949 const char *keymap
, *keymap_toggle
;
950 int convert
, interactive
;
953 r
= sd_bus_message_read(m
, "ssbb", &keymap
, &keymap_toggle
, &convert
, &interactive
);
960 if (isempty(keymap_toggle
))
961 keymap_toggle
= NULL
;
963 if (!streq_ptr(keymap
, c
->vc_keymap
) ||
964 !streq_ptr(keymap_toggle
, c
->vc_keymap_toggle
)) {
966 if ((keymap
&& (!filename_is_safe(keymap
) || !string_is_safe(keymap
))) ||
967 (keymap_toggle
&& (!filename_is_safe(keymap_toggle
) || !string_is_safe(keymap_toggle
))))
968 return sd_bus_error_set_errnof(error
, -EINVAL
, "Received invalid keymap data");
970 r
= bus_verify_polkit_async(m
, CAP_SYS_ADMIN
, "org.freedesktop.locale1.set-keyboard", interactive
, &c
->polkit_registry
, error
);
974 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
976 if (free_and_strdup(&c
->vc_keymap
, keymap
) < 0 ||
977 free_and_strdup(&c
->vc_keymap_toggle
, keymap_toggle
) < 0)
980 r
= vconsole_write_data(c
);
982 log_error("Failed to set virtual console keymap: %s", strerror(-r
));
983 return sd_bus_error_set_errnof(error
, r
, "Failed to set virtual console keymap: %s", strerror(-r
));
986 log_info("Changed virtual console keymap to '%s' toggle '%s'",
987 strempty(c
->vc_keymap
), strempty(c
->vc_keymap_toggle
));
989 r
= vconsole_reload(bus
);
991 log_error("Failed to request keymap reload: %s", strerror(-r
));
993 sd_bus_emit_properties_changed(bus
,
994 "/org/freedesktop/locale1",
995 "org.freedesktop.locale1",
996 "VConsoleKeymap", "VConsoleKeymapToggle", NULL
);
999 r
= vconsole_convert_to_x11(c
, bus
);
1001 log_error("Failed to convert keymap data: %s", strerror(-r
));
1005 return sd_bus_reply_method_return(m
, NULL
);
1008 static int method_set_x11_keyboard(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1009 Context
*c
= userdata
;
1010 const char *layout
, *model
, *variant
, *options
;
1011 int convert
, interactive
;
1014 r
= sd_bus_message_read(m
, "ssssbb", &layout
, &model
, &variant
, &options
, &convert
, &interactive
);
1018 if (isempty(layout
))
1024 if (isempty(variant
))
1027 if (isempty(options
))
1030 if (!streq_ptr(layout
, c
->x11_layout
) ||
1031 !streq_ptr(model
, c
->x11_model
) ||
1032 !streq_ptr(variant
, c
->x11_variant
) ||
1033 !streq_ptr(options
, c
->x11_options
)) {
1035 if ((layout
&& !string_is_safe(layout
)) ||
1036 (model
&& !string_is_safe(model
)) ||
1037 (variant
&& !string_is_safe(variant
)) ||
1038 (options
&& !string_is_safe(options
)))
1039 return sd_bus_error_set_errnof(error
, -EINVAL
, "Received invalid keyboard data");
1041 r
= bus_verify_polkit_async(m
, CAP_SYS_ADMIN
, "org.freedesktop.locale1.set-keyboard", interactive
, &c
->polkit_registry
, error
);
1045 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
1047 if (free_and_strdup(&c
->x11_layout
, layout
) < 0 ||
1048 free_and_strdup(&c
->x11_model
, model
) < 0 ||
1049 free_and_strdup(&c
->x11_variant
, variant
) < 0 ||
1050 free_and_strdup(&c
->x11_options
, options
) < 0)
1053 r
= x11_write_data(c
);
1055 log_error("Failed to set X11 keyboard layout: %s", strerror(-r
));
1056 return sd_bus_error_set_errnof(error
, r
, "Failed to set X11 keyboard layout: %s", strerror(-r
));
1059 log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'",
1060 strempty(c
->x11_layout
),
1061 strempty(c
->x11_model
),
1062 strempty(c
->x11_variant
),
1063 strempty(c
->x11_options
));
1065 sd_bus_emit_properties_changed(bus
,
1066 "/org/freedesktop/locale1",
1067 "org.freedesktop.locale1",
1068 "X11Layout", "X11Model", "X11Variant", "X11Options", NULL
);
1071 r
= x11_convert_to_vconsole(c
, bus
);
1073 log_error("Failed to convert keymap data: %s", strerror(-r
));
1077 return sd_bus_reply_method_return(m
, NULL
);
1080 static const sd_bus_vtable locale_vtable
[] = {
1081 SD_BUS_VTABLE_START(0),
1082 SD_BUS_PROPERTY("Locale", "as", property_get_locale
, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1083 SD_BUS_PROPERTY("X11Layout", "s", NULL
, offsetof(Context
, x11_layout
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1084 SD_BUS_PROPERTY("X11Model", "s", NULL
, offsetof(Context
, x11_model
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1085 SD_BUS_PROPERTY("X11Variant", "s", NULL
, offsetof(Context
, x11_variant
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1086 SD_BUS_PROPERTY("X11Options", "s", NULL
, offsetof(Context
, x11_options
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1087 SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL
, offsetof(Context
, vc_keymap
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1088 SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL
, offsetof(Context
, vc_keymap_toggle
), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
),
1089 SD_BUS_METHOD("SetLocale", "asb", NULL
, method_set_locale
, SD_BUS_VTABLE_UNPRIVILEGED
),
1090 SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL
, method_set_vc_keyboard
, SD_BUS_VTABLE_UNPRIVILEGED
),
1091 SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL
, method_set_x11_keyboard
, SD_BUS_VTABLE_UNPRIVILEGED
),
1095 static int connect_bus(Context
*c
, sd_event
*event
, sd_bus
**_bus
) {
1096 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
1103 r
= sd_bus_default_system(&bus
);
1105 log_error("Failed to get system bus connection: %s", strerror(-r
));
1109 r
= sd_bus_add_object_vtable(bus
, NULL
, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable
, c
);
1111 log_error("Failed to register object: %s", strerror(-r
));
1115 r
= sd_bus_request_name(bus
, "org.freedesktop.locale1", 0);
1117 log_error("Failed to register name: %s", strerror(-r
));
1121 r
= sd_bus_attach_event(bus
, event
, 0);
1123 log_error("Failed to attach bus to event loop: %s", strerror(-r
));
1133 int main(int argc
, char *argv
[]) {
1134 _cleanup_(context_free
) Context context
= {};
1135 _cleanup_event_unref_ sd_event
*event
= NULL
;
1136 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
1139 log_set_target(LOG_TARGET_AUTO
);
1140 log_parse_environment();
1144 mac_selinux_init("/etc");
1147 log_error("This program takes no arguments.");
1152 r
= sd_event_default(&event
);
1154 log_error("Failed to allocate event loop: %s", strerror(-r
));
1158 sd_event_set_watchdog(event
, true);
1160 r
= connect_bus(&context
, event
, &bus
);
1164 r
= context_read_data(&context
);
1166 log_error("Failed to read locale data: %s", strerror(-r
));
1170 r
= bus_event_loop_with_idle(event
, bus
, "org.freedesktop.locale1", DEFAULT_EXIT_USEC
, NULL
, NULL
);
1172 log_error("Failed to run event loop: %s", strerror(-r
));
1177 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;