localed.c revision 7568345034f2890af745747783c5abfbf6eccf0f
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen This file is part of systemd.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen Copyright 2011 Lennart Poettering
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen Copyright 2013 Kay Sievers
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen systemd is free software; you can redistribute it and/or modify it
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen under the terms of the GNU Lesser General Public License as published by
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen (at your option) any later version.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen systemd is distributed in the hope that it will be useful, but
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen Lesser General Public License for more details.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen You should have received a copy of the GNU Lesser General Public License
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen /* We don't list LC_ALL here on purpose. People should be
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen * using LANG instead. */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic const char * const names[_LOCALE_MAX] = {
9dc670ea766c711741f462b29572f2e5f8f3f6bcTom Gundersen [LOCALE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersentypedef struct Context {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic int free_and_copy(char **s, const char *v) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen r = strdup_or_null(isempty(v) ? NULL : v, &t);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void free_and_replace(char **s, char *v) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void context_free_vconsole(Context *c) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen for (p = 0; p < _LOCALE_MAX; p++)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void context_free(Context *c, sd_bus *bus) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen bus_verify_polkit_async_registry_free(bus, c->polkit_registry);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (isempty(c->locale[p]) || streq_ptr(c->locale[LOCALE_LANG], c->locale[p])) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen r = parse_env_file("/etc/locale.conf", NEWLINE,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_MONETARY", &c->locale[LOCALE_LC_MONETARY],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_MESSAGES", &c->locale[LOCALE_LC_MESSAGES],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_TELEPHONE", &c->locale[LOCALE_LC_TELEPHONE],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_MEASUREMENT", &c->locale[LOCALE_LC_MEASUREMENT],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_IDENTIFICATION", &c->locale[LOCALE_LC_IDENTIFICATION],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen /* Fill in what we got passed from systemd. */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen for (p = 0; p < _LOCALE_MAX; p++) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen r = free_and_copy(&c->locale[p], getenv(names[p]));
97f2d76d4f4dfab8b0629c09926a05a1e5621125Tom Gundersen r = parse_env_file("/etc/vconsole.conf", NEWLINE,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (r < 0 && r != -ENOENT)
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (l[0] == 0 || l[0] == '#')
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen } else if (!in_section && first_word(l, "Section")) {
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen if (strv_length(a) == 2 && streq(a[1], "InputClass"))
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen } else if (in_section && first_word(l, "EndSection"))
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen return r < 0 ? r : q < 0 ? q : p;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen if (r < 0 && r != -ENOENT)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen for (p = 0; p < _LOCALE_MAX; p++) {
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen char *t, **u;
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0) {
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen r = write_env_file_label("/etc/locale.conf", l);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersenstatic int locale_update_system_manager(Context *c, sd_bus *bus) {
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen "org.freedesktop.systemd1.Manager",
55428d84f31b52da1c50b7469f14e15740547f20Tom Gundersen "UnsetAndSetEnvironment");
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen log_error("Failed to update the manager environment: %s", strerror(-r));
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (r < 0 && r != -ENOENT)
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen char *s, **u;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen char *s, **u;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen r = write_env_file_label("/etc/vconsole.conf", l);
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen "# manually too freely.\n"
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen "Section \"InputClass\"\n"
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen " Identifier \"system-keyboard\"\n"
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen " MatchIsKeyboard \"on\"\n", f);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
r = -errno;
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
&error,
NULL,
static char *strnulldash(const char *s) {
assert(f);
assert(n);
assert(a);
errno = 0;
if (ferror(f))
b = strv_split_quoted(l);
return -ENOMEM;
strv_free(b);
bool modified = false;
modified =
context_free_x11(c);
return -errno;
r = read_next_mapping(f, &n, &a);
return -ENOMEM;
modified = true;
if (modified) {
r = write_data_x11(c);
"/org/freedesktop/locale1",
const char *dir;
_cleanup_free_ char *n;
if (c->x11_variant)
return -ENOMEM;
if (!p || !pz)
return -ENOMEM;
*new_keymap = n;
n = NULL;
unsigned best_matching = 0;
return -errno;
unsigned matching = 0;
r = read_next_mapping(f, &n, &a);
size_t x;
size_t w;
if (matching > 0) {
matching++;
matching++;
matching++;
if (!*new_keymap)
return -ENOMEM;
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;
passed[p] = true;
modified = true;
if (!valid)
if (!modified) {
for (p = 0; p < _LOCALE_MAX; p++)
modified = true;
if (modified) {
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;
return -ENOMEM;
c->locale[p] = t;
for (p = 0; p < _LOCALE_MAX; p++) {
if (passed[p])
locale_simplify(c);
r = locale_write_data(c);
"/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",
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) {
static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
"org.freedesktop.locale1.set-keyboard",
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
return -ENOMEM;
r = write_data_x11(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;