localed.c revision 7568345034f2890af745747783c5abfbf6eccf0f
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen/***
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen This file is part of systemd.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen Copyright 2011 Lennart Poettering
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen Copyright 2013 Kay Sievers
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
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
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
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/>.
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen***/
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
2a73e0d39a9bec82c3800071e375d27164727e71Tom Gundersen#include <errno.h>
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen#include <string.h>
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen#include <unistd.h>
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen#include "sd-bus.h"
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen#include "util.h"
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen#include "mkdir.h"
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen#include "strv.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "def.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "env-util.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "fileio.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "fileio-label.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "label.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "bus-util.h"
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen#include "bus-error.h"
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen#include "bus-message.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "event-util.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen#include "locale-util.h"
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenenum {
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen /* We don't list LC_ALL here on purpose. People should be
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen * using LANG instead. */
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen LOCALE_LANG,
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen LOCALE_LANGUAGE,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen LOCALE_LC_CTYPE,
97f2d76d4f4dfab8b0629c09926a05a1e5621125Tom Gundersen LOCALE_LC_NUMERIC,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen LOCALE_LC_TIME,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen LOCALE_LC_COLLATE,
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen LOCALE_LC_MONETARY,
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen LOCALE_LC_MESSAGES,
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen LOCALE_LC_PAPER,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen LOCALE_LC_NAME,
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen LOCALE_LC_ADDRESS,
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen LOCALE_LC_TELEPHONE,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen LOCALE_LC_MEASUREMENT,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen LOCALE_LC_IDENTIFICATION,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen _LOCALE_MAX
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen};
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic const char * const names[_LOCALE_MAX] = {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen [LOCALE_LANG] = "LANG",
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen [LOCALE_LANGUAGE] = "LANGUAGE",
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen [LOCALE_LC_CTYPE] = "LC_CTYPE",
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen [LOCALE_LC_NUMERIC] = "LC_NUMERIC",
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen [LOCALE_LC_TIME] = "LC_TIME",
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen [LOCALE_LC_COLLATE] = "LC_COLLATE",
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen [LOCALE_LC_MONETARY] = "LC_MONETARY",
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen [LOCALE_LC_MESSAGES] = "LC_MESSAGES",
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen [LOCALE_LC_PAPER] = "LC_PAPER",
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen [LOCALE_LC_NAME] = "LC_NAME",
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen [LOCALE_LC_ADDRESS] = "LC_ADDRESS",
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen [LOCALE_LC_TELEPHONE] = "LC_TELEPHONE",
9dc670ea766c711741f462b29572f2e5f8f3f6bcTom Gundersen [LOCALE_LC_MEASUREMENT] = "LC_MEASUREMENT",
9dc670ea766c711741f462b29572f2e5f8f3f6bcTom Gundersen [LOCALE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
9dc670ea766c711741f462b29572f2e5f8f3f6bcTom Gundersen};
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersentypedef struct Context {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char *locale[_LOCALE_MAX];
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char *x11_layout;
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen char *x11_model;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char *x11_variant;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char *x11_options;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char *vc_keymap;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char *vc_keymap_toggle;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen Hashmap *polkit_registry;
5b9d4dc05560ddda89e48b6b39365824b15e1300Tom Gundersen} Context;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic int free_and_copy(char **s, const char *v) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen int r;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char *t;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen assert(s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen r = strdup_or_null(isempty(v) ? NULL : v, &t);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (r < 0)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return r;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free(*s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen *s = t;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return 0;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void free_and_replace(char **s, char *v) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free(*s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen *s = v;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void context_free_x11(Context *c) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free_and_replace(&c->x11_layout, NULL);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen free_and_replace(&c->x11_model, NULL);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen free_and_replace(&c->x11_variant, NULL);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen free_and_replace(&c->x11_options, NULL);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen}
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void context_free_vconsole(Context *c) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free_and_replace(&c->vc_keymap, NULL);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free_and_replace(&c->vc_keymap_toggle, NULL);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void context_free_locale(Context *c) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen int p;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen for (p = 0; p < _LOCALE_MAX; p++)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free_and_replace(&c->locale[p], NULL);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void context_free(Context *c, sd_bus *bus) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen context_free_locale(c);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen context_free_x11(c);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen context_free_vconsole(c);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen bus_verify_polkit_async_registry_free(bus, c->polkit_registry);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen};
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic void locale_simplify(Context *c) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen int p;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen for (p = LOCALE_LANG+1; p < _LOCALE_MAX; p++)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (isempty(c->locale[p]) || streq_ptr(c->locale[LOCALE_LANG], c->locale[p])) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free(c->locale[p]);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen c->locale[p] = NULL;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen }
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen}
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersenstatic int locale_read_data(Context *c) {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen int r;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen context_free_locale(c);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen r = parse_env_file("/etc/locale.conf", NEWLINE,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LANG", &c->locale[LOCALE_LANG],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LANGUAGE", &c->locale[LOCALE_LANGUAGE],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_CTYPE", &c->locale[LOCALE_LC_CTYPE],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_NUMERIC", &c->locale[LOCALE_LC_NUMERIC],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_TIME", &c->locale[LOCALE_LC_TIME],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_COLLATE", &c->locale[LOCALE_LC_COLLATE],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_MONETARY", &c->locale[LOCALE_LC_MONETARY],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_MESSAGES", &c->locale[LOCALE_LC_MESSAGES],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_PAPER", &c->locale[LOCALE_LC_PAPER],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_NAME", &c->locale[LOCALE_LC_NAME],
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "LC_ADDRESS", &c->locale[LOCALE_LC_ADDRESS],
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 NULL);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (r == -ENOENT) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen int p;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen /* Fill in what we got passed from systemd. */
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen for (p = 0; p < _LOCALE_MAX; p++) {
97f2d76d4f4dfab8b0629c09926a05a1e5621125Tom Gundersen assert(names[p]);
97f2d76d4f4dfab8b0629c09926a05a1e5621125Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen r = free_and_copy(&c->locale[p], getenv(names[p]));
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (r < 0)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return r;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen }
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen r = 0;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen }
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen locale_simplify(c);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return r;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen}
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic int vconsole_read_data(Context *c) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen int r;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen context_free_vconsole(c);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
97f2d76d4f4dfab8b0629c09926a05a1e5621125Tom Gundersen r = parse_env_file("/etc/vconsole.conf", NEWLINE,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "KEYMAP", &c->vc_keymap,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen "KEYMAP_TOGGLE", &c->vc_keymap_toggle,
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen NULL);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (r < 0 && r != -ENOENT)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return r;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen return 0;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen}
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersenstatic int x11_read_data(Context *c) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen FILE *f;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char line[LINE_MAX];
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen bool in_section = false;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen context_free_x11(c);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (!f)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return errno == ENOENT ? 0 : -errno;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen while (fgets(line, sizeof(line), f)) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char *l;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char_array_0(line);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen l = strstrip(line);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (l[0] == 0 || l[0] == '#')
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen continue;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (in_section && first_word(l, "Option")) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char **a;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen a = strv_split_quoted(l);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (!a) {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen fclose(f);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen return -ENOMEM;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen }
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (strv_length(a) == 3) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (streq(a[1], "XkbLayout")) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free_and_replace(&c->x11_layout, a[2]);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen a[2] = NULL;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen } else if (streq(a[1], "XkbModel")) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free_and_replace(&c->x11_model, a[2]);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen a[2] = NULL;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen } else if (streq(a[1], "XkbVariant")) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free_and_replace(&c->x11_variant, a[2]);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen a[2] = NULL;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen } else if (streq(a[1], "XkbOptions")) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free_and_replace(&c->x11_options, a[2]);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen a[2] = NULL;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen }
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen }
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen strv_free(a);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen } else if (!in_section && first_word(l, "Section")) {
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen char **a;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen a = strv_split_quoted(l);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen if (!a) {
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen fclose(f);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen return -ENOMEM;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen }
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen if (strv_length(a) == 2 && streq(a[1], "InputClass"))
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen in_section = true;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen strv_free(a);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen } else if (in_section && first_word(l, "EndSection"))
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen in_section = false;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen }
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen fclose(f);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen return 0;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen}
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersenstatic int context_read_data(Context *c) {
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen int r, q, p;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen r = locale_read_data(c);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen q = vconsole_read_data(c);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen p = x11_read_data(c);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen return r < 0 ? r : q < 0 ? q : p;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen}
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersenstatic int locale_write_data(Context *c) {
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen int r, p;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen char **l = NULL;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen r = load_env_file(NULL, "/etc/locale.conf", NULL, &l);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen if (r < 0 && r != -ENOENT)
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen return r;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen for (p = 0; p < _LOCALE_MAX; p++) {
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen char *t, **u;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen assert(names[p]);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen if (isempty(c->locale[p])) {
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen l = strv_env_unset(l, names[p]);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen continue;
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen }
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0) {
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen strv_free(l);
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen return -ENOMEM;
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen }
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen u = strv_env_set(l, t);
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen free(t);
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen strv_free(l);
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen if (!u)
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen return -ENOMEM;
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen l = u;
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen }
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen if (strv_isempty(l)) {
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen strv_free(l);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (unlink("/etc/locale.conf") < 0)
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen return errno == ENOENT ? 0 : -errno;
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return 0;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen }
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen r = write_env_file_label("/etc/locale.conf", l);
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen strv_free(l);
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen return r;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen}
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersenstatic int locale_update_system_manager(Context *c, sd_bus *bus) {
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen _cleanup_free_ char **l_unset = NULL;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen _cleanup_strv_free_ char **l_set = NULL;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen sd_bus_error error = SD_BUS_ERROR_NULL;
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen unsigned c_set, c_unset, p;
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen int r;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen assert(bus);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen l_unset = new0(char*, _LOCALE_MAX);
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen if (!l_unset)
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen return -ENOMEM;
f1ac700248f231b7bdac2aafe8c35650efddb89fTom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen l_set = new0(char*, _LOCALE_MAX);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (!l_set)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return -ENOMEM;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen for (p = 0, c_set = 0, c_unset = 0; p < _LOCALE_MAX; p++) {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen assert(names[p]);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (isempty(c->locale[p]))
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen l_unset[c_set++] = (char*) names[p];
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen else {
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen char *s;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (asprintf(&s, "%s=%s", names[p], c->locale[p]) < 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return -ENOMEM;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen l_set[c_unset++] = s;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen }
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen }
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen assert(c_set + c_unset == _LOCALE_MAX);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen r = sd_bus_message_new_method_call(bus, &m,
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen "org.freedesktop.systemd1",
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen "/org/freedesktop/systemd1",
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen "org.freedesktop.systemd1.Manager",
55428d84f31b52da1c50b7469f14e15740547f20Tom Gundersen "UnsetAndSetEnvironment");
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (r < 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return r;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen r = sd_bus_message_append_strv(m, l_unset);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (r < 0)
55428d84f31b52da1c50b7469f14e15740547f20Tom Gundersen return r;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen r = sd_bus_message_append_strv(m, l_set);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (r < 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return r;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen r = sd_bus_call(bus, m, 0, &error, NULL);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (r < 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen log_error("Failed to update the manager environment: %s", strerror(-r));
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return 0;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen}
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersenstatic int vconsole_write_data(Context *c) {
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen int r;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen _cleanup_strv_free_ char **l = NULL;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (r < 0 && r != -ENOENT)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return r;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (isempty(c->vc_keymap))
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen l = strv_env_unset(l, "KEYMAP");
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen else {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen char *s, **u;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen s = strappend("KEYMAP=", c->vc_keymap);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen if (!s)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return -ENOMEM;
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen u = strv_env_set(l, s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen free(s);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen strv_free(l);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen if (!u)
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen return -ENOMEM;
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen l = u;
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen }
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen if (isempty(c->vc_keymap_toggle))
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen l = strv_env_unset(l, "KEYMAP_TOGGLE");
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen else {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen char *s, **u;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (!s)
a501033335ed402c8f7e86fe41a15531ba69abd7Tom Gundersen return -ENOMEM;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen u = strv_env_set(l, s);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen free(s);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen strv_free(l);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen if (!u)
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen return -ENOMEM;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen l = u;
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen }
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen if (strv_isempty(l)) {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (unlink("/etc/vconsole.conf") < 0)
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen return errno == ENOENT ? 0 : -errno;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen return 0;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen }
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen r = write_env_file_label("/etc/vconsole.conf", l);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen return r;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen}
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersenstatic int write_data_x11(Context *c) {
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen _cleanup_fclose_ FILE *f = NULL;
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen _cleanup_free_ char *temp_path = NULL;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen int r;
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen if (isempty(c->x11_layout) &&
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen isempty(c->x11_model) &&
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen isempty(c->x11_variant) &&
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen isempty(c->x11_options)) {
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen return errno == ENOENT ? 0 : -errno;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen return 0;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen }
daeb71a36a98834664e4d95773a3629b746f4db8Tom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen mkdir_p_label("/etc/X11/xorg.conf.d", 0755);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (r < 0)
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen return r;
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen fchmod(fileno(f), 0644);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
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
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (!isempty(c->x11_layout))
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout);
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen if (!isempty(c->x11_model))
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model);
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (!isempty(c->x11_variant))
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
16b9b87aeee9353b5b8dae6089a69752422a5b09Tom Gundersen if (!isempty(c->x11_options))
5fde13d748749f0e06e2e6cdd15f0980a79ea82cTom Gundersen fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options);
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen
43b3a5ef61859f06cdbaf26765cab8e1adac4296Tom Gundersen fputs("EndSection\n", f);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen fflush(f);
af6f0d422c521374ee6a2dd92df5935a5a476ae5Tom Gundersen
if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
r = -errno;
unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
unlink(temp_path);
return r;
} else
return 0;
}
static int vconsole_reload(sd_bus *bus) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
r = sd_bus_call_method(bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"RestartUnit",
&error,
NULL,
"ss", "systemd-vconsole-setup.service", "replace");
if (r < 0)
log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
return r;
}
static char *strnulldash(const char *s) {
return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
}
static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
assert(f);
assert(n);
assert(a);
for (;;) {
char line[LINE_MAX];
char *l, **b;
errno = 0;
if (!fgets(line, sizeof(line), f)) {
if (ferror(f))
return errno ? -errno : -EIO;
return 0;
}
(*n) ++;
l = strstrip(line);
if (l[0] == 0 || l[0] == '#')
continue;
b = strv_split_quoted(l);
if (!b)
return -ENOMEM;
if (strv_length(b) < 5) {
log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
strv_free(b);
continue;
}
*a = b;
return 1;
}
}
static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
bool modified = false;
assert(bus);
if (isempty(c->vc_keymap)) {
modified =
!isempty(c->x11_layout) ||
!isempty(c->x11_model) ||
!isempty(c->x11_variant) ||
!isempty(c->x11_options);
context_free_x11(c);
} else {
_cleanup_fclose_ FILE *f = NULL;
unsigned n = 0;
f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
if (!f)
return -errno;
for (;;) {
_cleanup_strv_free_ char **a = NULL;
int r;
r = read_next_mapping(f, &n, &a);
if (r < 0)
return r;
if (r == 0)
break;
if (!streq(c->vc_keymap, a[0]))
continue;
if (!streq_ptr(c->x11_layout, strnulldash(a[1])) ||
!streq_ptr(c->x11_model, strnulldash(a[2])) ||
!streq_ptr(c->x11_variant, strnulldash(a[3])) ||
!streq_ptr(c->x11_options, strnulldash(a[4]))) {
if (free_and_copy(&c->x11_layout, strnulldash(a[1])) < 0 ||
free_and_copy(&c->x11_model, strnulldash(a[2])) < 0 ||
free_and_copy(&c->x11_variant, strnulldash(a[3])) < 0 ||
free_and_copy(&c->x11_options, strnulldash(a[4])) < 0)
return -ENOMEM;
modified = true;
}
break;
}
}
if (modified) {
int r;
r = write_data_x11(c);
if (r < 0)
log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
sd_bus_emit_properties_changed(bus,
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"X11Layout", "X11Model", "X11Variant", "X11Options", NULL);
}
return 0;
}
static int find_converted_keymap(Context *c, char **new_keymap) {
const char *dir;
_cleanup_free_ char *n;
if (c->x11_variant)
n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
else
n = strdup(c->x11_layout);
if (!n)
return -ENOMEM;
NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
_cleanup_free_ char *p = NULL, *pz = NULL;
p = strjoin(dir, "xkb/", n, ".map", NULL);
pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
if (!p || !pz)
return -ENOMEM;
if (access(p, F_OK) == 0 || access(pz, F_OK) == 0) {
*new_keymap = n;
n = NULL;
return 1;
}
}
return 0;
}
static int find_legacy_keymap(Context *c, char **new_keymap) {
_cleanup_fclose_ FILE *f;
unsigned n = 0;
unsigned best_matching = 0;
f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
if (!f)
return -errno;
for (;;) {
_cleanup_strv_free_ char **a = NULL;
unsigned matching = 0;
int r;
r = read_next_mapping(f, &n, &a);
if (r < 0)
return r;
if (r == 0)
break;
/* Determine how well matching this entry is */
if (streq_ptr(c->x11_layout, a[1]))
/* If we got an exact match, this is best */
matching = 10;
else {
size_t x;
x = strcspn(c->x11_layout, ",");
/* We have multiple X layouts, look for an
* entry that matches our key with everything
* but the first layout stripped off. */
if (x > 0 &&
strlen(a[1]) == x &&
strneq(c->x11_layout, a[1], x))
matching = 5;
else {
size_t w;
/* If that didn't work, strip off the
* other layouts from the entry, too */
w = strcspn(a[1], ",");
if (x > 0 && x == w &&
memcmp(c->x11_layout, a[1], x) == 0)
matching = 1;
}
}
if (matching > 0) {
if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) {
matching++;
if (streq_ptr(c->x11_variant, a[3])) {
matching++;
if (streq_ptr(c->x11_options, a[4]))
matching++;
}
}
}
/* The best matching entry so far, then let's save that */
if (matching > best_matching) {
best_matching = matching;
free(*new_keymap);
*new_keymap = strdup(a[0]);
if (!*new_keymap)
return -ENOMEM;
}
}
return 0;
}
static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
bool modified = false;
int r;
assert(bus);
if (isempty(c->x11_layout)) {
modified =
!isempty(c->vc_keymap) ||
!isempty(c->vc_keymap_toggle);
context_free_x11(c);
} else {
char *new_keymap = NULL;
r = find_converted_keymap(c, &new_keymap);
if (r < 0)
return r;
else if (r == 0) {
r = find_legacy_keymap(c, &new_keymap);
if (r < 0)
return r;
}
if (!streq_ptr(c->vc_keymap, new_keymap)) {
free_and_replace(&c->vc_keymap, new_keymap);
free_and_replace(&c->vc_keymap_toggle, NULL);
modified = true;
} else
free(new_keymap);
}
if (modified) {
r = vconsole_write_data(c);
if (r < 0)
log_error("Failed to set virtual console keymap: %s", strerror(-r));
sd_bus_emit_properties_changed(bus,
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"VConsoleKeymap", "VConsoleKeymapToggle", NULL);
return vconsole_reload(bus);
}
return 0;
}
static int property_get_locale(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Context *c = userdata;
_cleanup_strv_free_ char **l = NULL;
int p, q;
l = new0(char*, _LOCALE_MAX+1);
if (!l)
return -ENOMEM;
for (p = 0, q = 0; p < _LOCALE_MAX; p++) {
char *t;
if (isempty(c->locale[p]))
continue;
if (asprintf(&t, "%s=%s", names[p], c->locale[p]) < 0)
return -ENOMEM;
l[q++] = t;
}
return sd_bus_message_append_strv(reply, l);
}
static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
_cleanup_strv_free_ char **l = NULL;
char **i;
int interactive;
bool modified = false;
bool passed[_LOCALE_MAX] = {};
int p;
int r;
r = bus_message_read_strv_extend(m, &l);
if (r < 0)
return r;
r = sd_bus_message_read_basic(m, 'b', &interactive);
if (r < 0)
return r;
/* Check whether a variable changed and if so valid */
STRV_FOREACH(i, l) {
bool valid = false;
for (p = 0; p < _LOCALE_MAX; p++) {
size_t k;
k = strlen(names[p]);
if (startswith(*i, names[p]) &&
(*i)[k] == '=' &&
locale_is_valid((*i) + k + 1)) {
valid = true;
passed[p] = true;
if (!streq_ptr(*i + k + 1, c->locale[p]))
modified = true;
break;
}
}
if (!valid)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
}
/* Check whether a variable is unset */
if (!modified) {
for (p = 0; p < _LOCALE_MAX; p++)
if (!isempty(c->locale[p]) && !passed[p]) {
modified = true;
break;
}
}
if (modified) {
r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
"org.freedesktop.locale1.set-locale", interactive,
error, method_set_locale, c);
if (r < 0)
return r;
if (r == 0)
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;
k = strlen(names[p]);
if (startswith(*i, names[p]) && (*i)[k] == '=') {
char *t;
t = strdup(*i + k + 1);
if (!t)
return -ENOMEM;
free(c->locale[p]);
c->locale[p] = t;
break;
}
}
}
for (p = 0; p < _LOCALE_MAX; p++) {
if (passed[p])
continue;
free_and_replace(&c->locale[p], NULL);
}
locale_simplify(c);
r = locale_write_data(c);
if (r < 0) {
log_error("Failed to set locale: %s", strerror(-r));
return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r));
}
locale_update_system_manager(c, bus);
log_info("Changed locale information.");
sd_bus_emit_properties_changed(bus,
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"Locale", NULL);
}
return sd_bus_reply_method_return(m, NULL);
}
static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
const char *keymap, *keymap_toggle;
int convert, interactive;
int r;
r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive);
if (r < 0)
return r;
if (isempty(keymap))
keymap = NULL;
if (isempty(keymap_toggle))
keymap_toggle = NULL;
if (!streq_ptr(keymap, c->vc_keymap) ||
!streq_ptr(keymap_toggle, c->vc_keymap_toggle)) {
if ((keymap && (!filename_is_safe(keymap) || !string_is_safe(keymap))) ||
(keymap_toggle && (!filename_is_safe(keymap_toggle) || !string_is_safe(keymap_toggle))))
return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data");
r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
"org.freedesktop.locale1.set-keyboard",
interactive, error, method_set_vc_keyboard, c);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
if (free_and_copy(&c->vc_keymap, keymap) < 0 ||
free_and_copy(&c->vc_keymap_toggle, keymap_toggle) < 0)
return -ENOMEM;
r = vconsole_write_data(c);
if (r < 0) {
log_error("Failed to set virtual console keymap: %s", strerror(-r));
return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r));
}
log_info("Changed virtual console keymap to '%s'", strempty(c->vc_keymap));
r = vconsole_reload(bus);
if (r < 0)
log_error("Failed to request keymap reload: %s", strerror(-r));
sd_bus_emit_properties_changed(bus,
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"VConsoleKeymap", "VConsoleKeymapToggle", NULL);
if (convert) {
r = vconsole_convert_to_x11(c, bus);
if (r < 0)
log_error("Failed to convert keymap data: %s", strerror(-r));
}
}
return sd_bus_reply_method_return(m, NULL);
}
static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
const char *layout, *model, *variant, *options;
int convert, interactive;
int r;
r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive);
if (r < 0)
return r;
if (isempty(layout))
layout = NULL;
if (isempty(model))
model = NULL;
if (isempty(variant))
variant = NULL;
if (isempty(options))
options = NULL;
if (!streq_ptr(layout, c->x11_layout) ||
!streq_ptr(model, c->x11_model) ||
!streq_ptr(variant, c->x11_variant) ||
!streq_ptr(options, c->x11_options)) {
if ((layout && !string_is_safe(layout)) ||
(model && !string_is_safe(model)) ||
(variant && !string_is_safe(variant)) ||
(options && !string_is_safe(options)))
return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
r = bus_verify_polkit_async(bus, &c->polkit_registry, m,
"org.freedesktop.locale1.set-keyboard",
interactive, error, method_set_x11_keyboard, c);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
if (free_and_copy(&c->x11_layout, layout) < 0 ||
free_and_copy(&c->x11_model, model) < 0 ||
free_and_copy(&c->x11_variant, variant) < 0 ||
free_and_copy(&c->x11_options, options) < 0)
return -ENOMEM;
r = write_data_x11(c);
if (r < 0) {
log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r));
}
log_info("Changed X11 keyboard layout to '%s'", strempty(c->x11_layout));
sd_bus_emit_properties_changed(bus,
"/org/freedesktop/locale1",
"org.freedesktop.locale1",
"X11Layout" "X11Model" "X11Variant" "X11Options", NULL);
if (convert) {
r = x11_convert_to_vconsole(c, bus);
if (r < 0)
log_error("Failed to convert keymap data: %s", strerror(-r));
}
}
return sd_bus_reply_method_return(m, NULL);
}
static const sd_bus_vtable locale_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
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("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
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),
SD_BUS_VTABLE_END
};
static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
_cleanup_bus_unref_ sd_bus *bus = NULL;
int r;
assert(c);
assert(event);
assert(_bus);
r = sd_bus_default_system(&bus);
if (r < 0) {
log_error("Failed to get system bus connection: %s", strerror(-r));
return r;
}
r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
if (r < 0) {
log_error("Failed to register object: %s", strerror(-r));
return r;
}
r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0);
if (r < 0) {
log_error("Failed to register name: %s", strerror(-r));
return r;
}
r = sd_bus_attach_event(bus, event, 0);
if (r < 0) {
log_error("Failed to attach bus to event loop: %s", strerror(-r));
return r;
}
*_bus = bus;
bus = NULL;
return 0;
}
int main(int argc, char *argv[]) {
Context context = {};
_cleanup_event_unref_ sd_event *event = NULL;
_cleanup_bus_unref_ sd_bus *bus = NULL;
int r;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
log_open();
umask(0022);
label_init("/etc");
if (argc != 1) {
log_error("This program takes no arguments.");
r = -EINVAL;
goto finish;
}
r = sd_event_default(&event);
if (r < 0) {
log_error("Failed to allocate event loop: %s", strerror(-r));
goto finish;
}
sd_event_set_watchdog(event, true);
r = connect_bus(&context, event, &bus);
if (r < 0)
goto finish;
r = context_read_data(&context);
if (r < 0) {
log_error("Failed to read locale data: %s", strerror(-r));
goto finish;
}
r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL);
if (r < 0) {
log_error("Failed to run event loop: %s", strerror(-r));
goto finish;
}
finish:
context_free(&context, bus);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}