localed.c revision 4034a06ddb82ec9868cd52496fef2f5faa25575f
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering This file is part of systemd.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Copyright 2011 Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Copyright 2013 Kay Sievers
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is free software; you can redistribute it and/or modify it
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering under the terms of the GNU Lesser General Public License as published by
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (at your option) any later version.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is distributed in the hope that it will be useful, but
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Lesser General Public License for more details.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering You should have received a copy of the GNU Lesser General Public License
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
46b131574fdd7d77c15a0919ca9010cad7aa6ac7Lennart Poettering /* We don't list LC_ALL here on purpose. People should be
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek * using LANG instead. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const char * const names[_LOCALE_MAX] = {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [LOCALE_LC_MEASUREMENT] = "LC_MEASUREMENT",
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [LOCALE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringtypedef struct Context {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const char* nonempty(const char *s) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void free_and_replace(char **s, char *v) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic bool startswith_comma(const char *s, const char *prefix) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering const char *t;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering return s && (t = startswith(s, prefix)) && (*t == ',');
178cc7700c23ac088cd7190d7854282075028d91Lennart Poetteringstatic void context_free_x11(Context *c) {
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poetteringstatic void context_free_vconsole(Context *c) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free_and_replace(&c->vc_keymap_toggle, NULL);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void context_free_locale(Context *c) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (p = 0; p < _LOCALE_MAX; p++)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering bus_verify_polkit_async_registry_free(c->polkit_registry);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (p = LOCALE_LANG+1; p < _LOCALE_MAX; p++)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (isempty(c->locale[p]) || streq_ptr(c->locale[LOCALE_LANG], c->locale[p]))
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = parse_env_file("/etc/locale.conf", NEWLINE,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_NUMERIC", &c->locale[LOCALE_LC_NUMERIC],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_COLLATE", &c->locale[LOCALE_LC_COLLATE],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_MONETARY", &c->locale[LOCALE_LC_MONETARY],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_MESSAGES", &c->locale[LOCALE_LC_MESSAGES],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_ADDRESS", &c->locale[LOCALE_LC_ADDRESS],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_TELEPHONE", &c->locale[LOCALE_LC_TELEPHONE],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_MEASUREMENT", &c->locale[LOCALE_LC_MEASUREMENT],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_IDENTIFICATION", &c->locale[LOCALE_LC_IDENTIFICATION],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Fill in what we got passed from systemd. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (p = 0; p < _LOCALE_MAX; p++) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int vconsole_read_data(Context *c) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = parse_env_file("/etc/vconsole.conf", NEWLINE,
e88baee88fad8bc59d33b55a7a2d640ef9e16cd6Zbigniew Jędrzejewski-Szmek "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0 && r != -ENOENT)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (l[0] == 0 || l[0] == '#')
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (in_section && first_word(l, "Option")) {
ec5ff4445cca6a1d786b8da36cf6fe0acc0b94c8Filipe Brandenburger } else if (streq(a[1], "XkbOptions")) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering } else if (!in_section && first_word(l, "Section")) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (strv_length(a) == 2 && streq(a[1], "InputClass"))
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering } else if (in_section && first_word(l, "EndSection"))
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int context_read_data(Context *c) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r < 0 ? r : q < 0 ? q : p;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int locale_write_data(Context *c, char ***settings) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering /* Set values will be returned as strv in *settings on success. */
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0 && r != -ENOENT)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering for (p = 0; p < _LOCALE_MAX; p++) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = write_env_file_label("/etc/locale.conf", l);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int locale_update_system_manager(Context *c, sd_bus *bus) {
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering r = sd_bus_message_new_method_call(bus, &m,
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering "org.freedesktop.systemd1.Manager",
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering "UnsetAndSetEnvironment");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_bus_message_append_strv(m, l_unset);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_bus_message_append_strv(m, l_set);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_bus_call(bus, m, 0, &error, NULL);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_error_errno(r, "Failed to update the manager environment: %m");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int vconsole_write_data(Context *c) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
3b3154df7e2773332bb814e167187367a0ccae4aLennart Poettering if (r < 0 && r != -ENOENT)
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek s = strappend("KEYMAP=", c->vc_keymap);
4a61c3e51e96a747c30598d78ee3a24e7c569e9fZbigniew Jędrzejewski-Szmek if (isempty(c->vc_keymap_toggle))
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return write_env_file_label("/etc/vconsole.conf", l);
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
fflush(f);
r = -errno;
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
&error,
NULL,
static const char* strnulldash(const char *s) {
FILE *f, unsigned *n, char ***a) {
assert(f);
assert(n);
assert(a);
errno = 0;
if (ferror(f))
r = strv_split_quoted(&b, l, 0);
strv_free(b);
bool modified = false;
modified =
context_free_x11(c);
return -errno;
return -ENOMEM;
modified = true;
if (modified) {
r = x11_write_data(c);
"/org/freedesktop/locale1",
static int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap) {
const char *dir;
_cleanup_free_ char *n;
if (x11_variant)
return -ENOMEM;
bool uncompressed;
if (!p || !pz)
return -ENOMEM;
*new_keymap = n;
n = NULL;
unsigned best_matching = 0;
return -errno;
unsigned matching = 0;
if (matching > 0) {
matching++;
matching++;
matching++;
a[0], matching);
if (c->x11_variant)
return -errno;
bool modified = false;
modified =
context_free_x11(c);
modified = true;
if (modified) {
r = vconsole_write_data(c);
"/org/freedesktop/locale1",
static int property_get_locale(
const char *path,
const char *interface,
const char *property,
void *userdata,
return -ENOMEM;
for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
return -ENOMEM;
int interactive;
bool modified = false;
r = bus_message_read_strv_extend(m, &l);
STRV_FOREACH(i, l) {
bool valid = false;
for (p = 0; p < _LOCALE_MAX; p++) {
size_t k;
valid = true;
have[p] = true;
if (p == LOCALE_LANG)
modified = true;
if (!valid)
if (language) {
modified = true;
if (!modified)
for (p = 0; p < _LOCALE_MAX; p++)
modified = true;
if (modified) {
"org.freedesktop.locale1.set-locale",
&c->polkit_registry,
error);
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
STRV_FOREACH(i, l)
for (p = 0; p < _LOCALE_MAX; p++) {
size_t k;
for (p = 0; p < _LOCALE_MAX; p++) {
if (have[p])
locale_simplify(c);
if (settings) {
"/org/freedesktop/locale1",
static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
"org.freedesktop.locale1.set-keyboard",
&c->polkit_registry,
error);
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
return -ENOMEM;
r = vconsole_write_data(c);
"/org/freedesktop/locale1",
if (convert) {
#ifdef HAVE_XKBCOMMON
static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
const char *fmt;
static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
if (!ctx) {
r = -ENOMEM;
goto exit;
if (!km) {
r = -EINVAL;
goto exit;
exit:
static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
"org.freedesktop.locale1.set-keyboard",
&c->polkit_registry,
error);
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m",
return -ENOMEM;
r = x11_write_data(c);
"/org/freedesktop/locale1",
if (convert) {
SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
assert(c);
r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
log_open();
r = -EINVAL;
goto finish;
goto finish;
goto finish;
goto finish;
goto finish;