localed.c revision 7914d6bba47a21b98617d04c992a9075ff5af4c0
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 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/>.
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <interface name=\"org.freedesktop.locale1\">\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <property name=\"Locale\" type=\"as\" access=\"read\"/>\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <property name=\"VConsoleKeymap\" type=\"s\" access=\"read\"/>\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <property name=\"VConsoleKeymapToggle\" type=\"s\" access=\"read\"/>\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <property name=\"X11Layout\" type=\"s\" access=\"read\"/>\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <property name=\"X11Model\" type=\"s\" access=\"read\"/>\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <property name=\"X11Variant\" type=\"s\" access=\"read\"/>\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <property name=\"X11Options\" type=\"s\" access=\"read\"/>\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <method name=\"SetLocale\">\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <arg name=\"locale\" type=\"as\" direction=\"in\"/>\n" \
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " </method>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <method name=\"SetVConsoleKeyboard\">\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"keymap\" type=\"s\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"keymap_toggle\" type=\"s\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " </method>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <method name=\"SetX11Keyboard\">\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"layout\" type=\"s\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"model\" type=\"s\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"variant\" type=\"s\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"options\" type=\"s\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " </method>\n" \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering " </interface>\n"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "org.freedesktop.locale1\0"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringconst char locale_interface[] _introspect_("locale1") = INTERFACE;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering /* We don't list LC_ALL here on purpose. People should be
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * using LANG instead. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const char * const names[_PROP_MAX] = {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [PROP_LC_MEASUREMENT] = "LC_MEASUREMENT",
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [PROP_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringtypedef struct State {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering char *x11_layout, *x11_model, *x11_variant, *x11_options;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int free_and_set(char **s, const char *v) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = strdup_or_null(isempty(v) ? NULL : v, &t);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void free_data_locale(void) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (p = 0; p < _PROP_MAX; p++) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void free_data_x11(void) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering state.x11_layout = state.x11_model = state.x11_variant = state.x11_options = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void free_data_vconsole(void) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering state.vc_keymap = state.vc_keymap_toggle = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void simplify(void) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (isempty(data[p]) || streq_ptr(data[PROP_LANG], data[p])) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int read_data_locale(void) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = parse_env_file("/etc/locale.conf", NEWLINE,
e88baee88fad8bc59d33b55a7a2d640ef9e16cd6Zbigniew Jędrzejewski-Szmek "LANGUAGE", &data[PROP_LANGUAGE],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_TELEPHONE", &data[PROP_LC_TELEPHONE],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_MEASUREMENT", &data[PROP_LC_MEASUREMENT],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "LC_IDENTIFICATION", &data[PROP_LC_IDENTIFICATION],
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Fill in what we got passed from systemd. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (p = 0; p < _PROP_MAX; p++) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void free_data(void) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int read_data_vconsole(void) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = parse_env_file("/etc/vconsole.conf", NEWLINE,
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering "KEYMAP_TOGGLE", &state.vc_keymap_toggle,
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0 && r != -ENOENT)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int read_data_x11(void) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (l[0] == 0 || l[0] == '#')
ac50788b0f5aeee09e7d45db28ae8ab7f39cd52eZbigniew Jędrzejewski-Szmek if (in_section && first_word(l, "Option")) {
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 read_data(void) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r < 0 ? r : q < 0 ? q : p;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int write_data_locale(void) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = load_env_file("/etc/locale.conf", NULL, &l);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0 && r != -ENOENT)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering for (p = 0; p < _PROP_MAX; p++) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = write_env_file_label("/etc/locale.conf", l);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void push_data(DBusConnection *bus) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering char **l_set = NULL, **l_unset = NULL, **t;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (p = 0; p < _PROP_MAX; p++) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_error("Could not allocate message.");
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek dbus_message_iter_init_append(m, &iter);
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!dbus_message_iter_close_container(&iter, &sub) ||
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!dbus_message_iter_close_container(&iter, &sub)) {
d682b3a7e7c7c2941a4d3e193f1e330dbc9fae89Lennart Poettering reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
d682b3a7e7c7c2941a4d3e193f1e330dbc9fae89Lennart Poettering log_error("Failed to set locale information: %s", bus_error_message(&error));
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering r = load_env_file("/etc/vconsole.conf", NULL, &l);
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering if (r < 0 && r != -ENOENT)
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering s = strappend("KEYMAP=", state.vc_keymap);
strv_free(l);
return -ENOMEM;
strv_free(l);
return -ENOMEM;
u = strv_env_set(l, s);
free(s);
strv_free(l);
return -ENOMEM;
if (strv_isempty(l)) {
strv_free(l);
strv_free(l);
static int write_data_x11(void) {
FILE *f;
char *temp_path;
fflush(f);
r = -errno;
fclose(f);
if (!error) {
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
r = -ENOMEM;
goto finish;
if (!dbus_message_append_args(m,
r = -ENOMEM;
goto finish;
if (!reply) {
r = -EIO;
goto finish;
if (reply)
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 =
FILE *f;
return -errno;
r = read_next_mapping(f, &n, &a);
fclose(f);
strv_free(a);
strv_free(a);
fclose(f);
return -ENOMEM;
modified = true;
strv_free(a);
fclose(f);
if (modified) {
dbus_bool_t b;
r = write_data_x11();
"/org/freedesktop/locale1",
if (!changed)
return -ENOMEM;
return -ENOMEM;
bool modified = false;
modified =
FILE *f;
unsigned best_matching = 0;
return -errno;
unsigned matching = 0;
r = read_next_mapping(f, &n, &a);
fclose(f);
size_t x;
size_t w;
if (matching > 0 &&
matching++;
matching++;
matching++;
if (!new_keymap) {
strv_free(a);
fclose(f);
return -ENOMEM;
strv_free(a);
fclose(f);
modified = true;
if (modified) {
dbus_bool_t b;
r = write_data_vconsole();
"/org/freedesktop/locale1",
if (!changed)
return -ENOMEM;
return -ENOMEM;
return -ENOMEM;
for (p = 0; p < _PROP_MAX; p++) {
strv_free(l);
return -ENOMEM;
strv_free(l);
{ "VConsoleKeymapToggle", bus_property_append_string, "s", offsetof(State, vc_keymap_toggle), true },
{ NULL, }
{ NULL, }
void *userdata) {
char **l = NULL, **i;
bool modified = false;
if (r == -ENOMEM)
goto oom;
strv_free(l);
STRV_FOREACH(i, l) {
bool valid = false;
for (p = 0; p < _PROP_MAX; p++) {
size_t k;
valid = true;
passed[p] = true;
modified = true;
if (!valid) {
strv_free(l);
if (!modified) {
for (p = 0; p < _PROP_MAX; p++)
modified = true;
if (modified) {
r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, NULL, &error);
strv_free(l);
STRV_FOREACH(i, l) {
for (p = 0; p < _PROP_MAX; p++) {
size_t k;
strv_free(l);
goto oom;
data[p] = t;
strv_free(l);
for (p = 0; p < _PROP_MAX; p++) {
if (passed[p])
simplify();
r = write_data_locale();
"/org/freedesktop/locale1",
if (!changed)
goto oom;
strv_free(l);
} else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
if (!dbus_message_get_args(
&error,
r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
goto oom;
r = write_data_vconsole();
"/org/freedesktop/locale1",
if (!changed)
goto oom;
if (convert) {
if (!dbus_message_get_args(
&error,
r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, NULL, &error);
goto oom;
r = write_data_x11();
"/org/freedesktop/locale1",
if (!changed)
goto oom;
if (convert) {
goto oom;
goto oom;
if (changed) {
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
oom:
if (reply)
if (changed)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
if (!bus) {
r = -ECONNREFUSED;
goto fail;
r = log_oom();
goto fail;
r = -EEXIST;
goto fail;
if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
r = -EEXIST;
goto fail;
if (_bus)
fail:
bool exiting = false;
log_open();
r = -EINVAL;
goto finish;
r = read_data();
goto finish;
goto finish;
if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
exiting = true;
free_data();
if (bus) {