You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
884 lines
34 KiB
884 lines
34 KiB
From cdcbd56d4eacba3b3ee4d8b0c38d6509a307a2b7 Mon Sep 17 00:00:00 2001 |
|
From: Yu Watanabe <watanabe.yu+github@gmail.com> |
|
Date: Tue, 14 Jun 2022 09:07:00 +0900 |
|
Subject: [PATCH] locale-setup: merge locale handling in PID1 and localed |
|
|
|
Related: #2087652 |
|
--- |
|
src/basic/locale-util.c | 11 ++ |
|
src/basic/locale-util.h | 1 + |
|
src/core/locale-setup.c | 95 -------------- |
|
src/core/locale-setup.h | 4 - |
|
src/core/meson.build | 2 - |
|
src/locale/keymap-util.c | 102 +-------------- |
|
src/locale/keymap-util.h | 7 +- |
|
src/locale/localectl.c | 47 +++---- |
|
src/locale/localed.c | 87 +++---------- |
|
src/shared/locale-setup.c | 256 ++++++++++++++++++++++++++++++++++++++ |
|
src/shared/locale-setup.h | 28 +++++ |
|
src/shared/meson.build | 2 + |
|
12 files changed, 333 insertions(+), 309 deletions(-) |
|
delete mode 100644 src/core/locale-setup.c |
|
delete mode 100644 src/core/locale-setup.h |
|
create mode 100644 src/shared/locale-setup.c |
|
create mode 100644 src/shared/locale-setup.h |
|
|
|
diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c |
|
index 8098369db5..21f0982bb5 100644 |
|
--- a/src/basic/locale-util.c |
|
+++ b/src/basic/locale-util.c |
|
@@ -335,6 +335,17 @@ void locale_variables_free(char *l[_VARIABLE_LC_MAX]) { |
|
l[i] = mfree(l[i]); |
|
} |
|
|
|
+void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]) { |
|
+ assert(l); |
|
+ |
|
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { |
|
+ if (p == VARIABLE_LANG) |
|
+ continue; |
|
+ if (isempty(l[p]) || streq_ptr(l[VARIABLE_LANG], l[p])) |
|
+ l[p] = mfree(l[p]); |
|
+ } |
|
+} |
|
+ |
|
static const char * const locale_variable_table[_VARIABLE_LC_MAX] = { |
|
[VARIABLE_LANG] = "LANG", |
|
[VARIABLE_LANGUAGE] = "LANGUAGE", |
|
diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h |
|
index bab927146b..8990cb6a75 100644 |
|
--- a/src/basic/locale-util.h |
|
+++ b/src/basic/locale-util.h |
|
@@ -53,3 +53,4 @@ void locale_variables_free(char* l[_VARIABLE_LC_MAX]); |
|
static inline void locale_variables_freep(char*(*l)[_VARIABLE_LC_MAX]) { |
|
locale_variables_free(*l); |
|
} |
|
+void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]); |
|
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c |
|
deleted file mode 100644 |
|
index 716febbefa..0000000000 |
|
--- a/src/core/locale-setup.c |
|
+++ /dev/null |
|
@@ -1,95 +0,0 @@ |
|
-/* SPDX-License-Identifier: LGPL-2.1-or-later */ |
|
- |
|
-#include <errno.h> |
|
-#include <stdlib.h> |
|
- |
|
-#include "env-file.h" |
|
-#include "env-util.h" |
|
-#include "locale-setup.h" |
|
-#include "locale-util.h" |
|
-#include "proc-cmdline.h" |
|
-#include "string-util.h" |
|
-#include "strv.h" |
|
-#include "util.h" |
|
-#include "virt.h" |
|
- |
|
-int locale_setup(char ***environment) { |
|
- _cleanup_(locale_variables_freep) char *variables[_VARIABLE_LC_MAX] = {}; |
|
- _cleanup_strv_free_ char **add = NULL; |
|
- int r; |
|
- |
|
- r = proc_cmdline_get_key_many(PROC_CMDLINE_STRIP_RD_PREFIX, |
|
- "locale.LANG", &variables[VARIABLE_LANG], |
|
- "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], |
|
- "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], |
|
- "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], |
|
- "locale.LC_TIME", &variables[VARIABLE_LC_TIME], |
|
- "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE], |
|
- "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY], |
|
- "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], |
|
- "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER], |
|
- "locale.LC_NAME", &variables[VARIABLE_LC_NAME], |
|
- "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], |
|
- "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], |
|
- "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], |
|
- "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]); |
|
- if (r < 0 && r != -ENOENT) |
|
- log_warning_errno(r, "Failed to read /proc/cmdline: %m"); |
|
- |
|
- /* Hmm, nothing set on the kernel cmd line? Then let's try /etc/locale.conf */ |
|
- if (r <= 0) { |
|
- r = parse_env_file(NULL, "/etc/locale.conf", |
|
- "LANG", &variables[VARIABLE_LANG], |
|
- "LANGUAGE", &variables[VARIABLE_LANGUAGE], |
|
- "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], |
|
- "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], |
|
- "LC_TIME", &variables[VARIABLE_LC_TIME], |
|
- "LC_COLLATE", &variables[VARIABLE_LC_COLLATE], |
|
- "LC_MONETARY", &variables[VARIABLE_LC_MONETARY], |
|
- "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], |
|
- "LC_PAPER", &variables[VARIABLE_LC_PAPER], |
|
- "LC_NAME", &variables[VARIABLE_LC_NAME], |
|
- "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], |
|
- "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], |
|
- "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], |
|
- "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]); |
|
- if (r < 0 && r != -ENOENT) |
|
- log_warning_errno(r, "Failed to read /etc/locale.conf: %m"); |
|
- } |
|
- |
|
- for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++) { |
|
- char *s; |
|
- |
|
- if (!variables[i]) |
|
- continue; |
|
- |
|
- s = strjoin(locale_variable_to_string(i), "=", variables[i]); |
|
- if (!s) |
|
- return -ENOMEM; |
|
- |
|
- if (strv_consume(&add, s) < 0) |
|
- return -ENOMEM; |
|
- } |
|
- |
|
- if (strv_isempty(add)) { |
|
- /* If no locale is configured then default to compile-time default. */ |
|
- |
|
- add = strv_new("LANG=" SYSTEMD_DEFAULT_LOCALE); |
|
- if (!add) |
|
- return -ENOMEM; |
|
- } |
|
- |
|
- if (strv_isempty(*environment)) |
|
- strv_free_and_replace(*environment, add); |
|
- else { |
|
- char **merged; |
|
- |
|
- merged = strv_env_merge(*environment, add); |
|
- if (!merged) |
|
- return -ENOMEM; |
|
- |
|
- strv_free_and_replace(*environment, merged); |
|
- } |
|
- |
|
- return 0; |
|
-} |
|
diff --git a/src/core/locale-setup.h b/src/core/locale-setup.h |
|
deleted file mode 100644 |
|
index d554ad3060..0000000000 |
|
--- a/src/core/locale-setup.h |
|
+++ /dev/null |
|
@@ -1,4 +0,0 @@ |
|
-/* SPDX-License-Identifier: LGPL-2.1-or-later */ |
|
-#pragma once |
|
- |
|
-int locale_setup(char ***environment); |
|
diff --git a/src/core/meson.build b/src/core/meson.build |
|
index 97ac431763..7704478d43 100644 |
|
--- a/src/core/meson.build |
|
+++ b/src/core/meson.build |
|
@@ -83,8 +83,6 @@ libcore_sources = ''' |
|
load-dropin.h |
|
load-fragment.c |
|
load-fragment.h |
|
- locale-setup.c |
|
- locale-setup.h |
|
manager-dump.c |
|
manager-dump.h |
|
manager-serialize.c |
|
diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c |
|
index 10d2ed7aec..a3af396ebe 100644 |
|
--- a/src/locale/keymap-util.c |
|
+++ b/src/locale/keymap-util.c |
|
@@ -65,13 +65,8 @@ static void context_free_vconsole(Context *c) { |
|
c->vc_keymap_toggle = mfree(c->vc_keymap_toggle); |
|
} |
|
|
|
-static void context_free_locale(Context *c) { |
|
- for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) |
|
- c->locale[p] = mfree(c->locale[p]); |
|
-} |
|
- |
|
void context_clear(Context *c) { |
|
- context_free_locale(c); |
|
+ locale_context_clear(&c->locale_context); |
|
context_free_x11(c); |
|
context_free_vconsole(c); |
|
|
|
@@ -82,15 +77,8 @@ void context_clear(Context *c) { |
|
bus_verify_polkit_async_registry_free(c->polkit_registry); |
|
}; |
|
|
|
-void locale_simplify(char *locale[_VARIABLE_LC_MAX]) { |
|
- for (LocaleVariable p = VARIABLE_LANG+1; p < _VARIABLE_LC_MAX; p++) |
|
- if (isempty(locale[p]) || streq_ptr(locale[VARIABLE_LANG], locale[p])) |
|
- locale[p] = mfree(locale[p]); |
|
-} |
|
- |
|
int locale_read_data(Context *c, sd_bus_message *m) { |
|
- struct stat st; |
|
- int r; |
|
+ assert(c); |
|
|
|
/* Do not try to re-read the file within single bus operation. */ |
|
if (m) { |
|
@@ -101,57 +89,7 @@ int locale_read_data(Context *c, sd_bus_message *m) { |
|
c->locale_cache = sd_bus_message_ref(m); |
|
} |
|
|
|
- r = stat("/etc/locale.conf", &st); |
|
- if (r < 0 && errno != ENOENT) |
|
- return -errno; |
|
- |
|
- if (r >= 0) { |
|
- usec_t t; |
|
- |
|
- /* If mtime is not changed, then we do not need to re-read the file. */ |
|
- t = timespec_load(&st.st_mtim); |
|
- if (c->locale_mtime != USEC_INFINITY && t == c->locale_mtime) |
|
- return 0; |
|
- |
|
- c->locale_mtime = t; |
|
- context_free_locale(c); |
|
- |
|
- r = parse_env_file(NULL, "/etc/locale.conf", |
|
- "LANG", &c->locale[VARIABLE_LANG], |
|
- "LANGUAGE", &c->locale[VARIABLE_LANGUAGE], |
|
- "LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE], |
|
- "LC_NUMERIC", &c->locale[VARIABLE_LC_NUMERIC], |
|
- "LC_TIME", &c->locale[VARIABLE_LC_TIME], |
|
- "LC_COLLATE", &c->locale[VARIABLE_LC_COLLATE], |
|
- "LC_MONETARY", &c->locale[VARIABLE_LC_MONETARY], |
|
- "LC_MESSAGES", &c->locale[VARIABLE_LC_MESSAGES], |
|
- "LC_PAPER", &c->locale[VARIABLE_LC_PAPER], |
|
- "LC_NAME", &c->locale[VARIABLE_LC_NAME], |
|
- "LC_ADDRESS", &c->locale[VARIABLE_LC_ADDRESS], |
|
- "LC_TELEPHONE", &c->locale[VARIABLE_LC_TELEPHONE], |
|
- "LC_MEASUREMENT", &c->locale[VARIABLE_LC_MEASUREMENT], |
|
- "LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION]); |
|
- if (r < 0) |
|
- return r; |
|
- } else { |
|
- c->locale_mtime = USEC_INFINITY; |
|
- context_free_locale(c); |
|
- |
|
- /* Fill in what we got passed from systemd. */ |
|
- for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { |
|
- const char *name; |
|
- |
|
- name = locale_variable_to_string(p); |
|
- assert(name); |
|
- |
|
- r = free_and_strdup(&c->locale[p], empty_to_null(getenv(name))); |
|
- if (r < 0) |
|
- return r; |
|
- } |
|
- } |
|
- |
|
- locale_simplify(c->locale); |
|
- return 0; |
|
+ return locale_context_load(&c->locale_context, LOCALE_LOAD_LOCALE_CONF | LOCALE_LOAD_ENVIRONMENT | LOCALE_LOAD_SIMPLIFY); |
|
} |
|
|
|
int vconsole_read_data(Context *c, sd_bus_message *m) { |
|
@@ -285,40 +223,6 @@ int x11_read_data(Context *c, sd_bus_message *m) { |
|
return 0; |
|
} |
|
|
|
-int locale_write_data(Context *c, char ***settings) { |
|
- _cleanup_strv_free_ char **l = NULL; |
|
- struct stat st; |
|
- int r; |
|
- |
|
- /* Set values will be returned as strv in *settings on success. */ |
|
- |
|
- for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) |
|
- if (!isempty(c->locale[p])) { |
|
- r = strv_env_assign(&l, locale_variable_to_string(p), c->locale[p]); |
|
- if (r < 0) |
|
- return r; |
|
- } |
|
- |
|
- if (strv_isempty(l)) { |
|
- if (unlink("/etc/locale.conf") < 0) |
|
- return errno == ENOENT ? 0 : -errno; |
|
- |
|
- c->locale_mtime = USEC_INFINITY; |
|
- return 0; |
|
- } |
|
- |
|
- r = write_env_file_label("/etc/locale.conf", l); |
|
- if (r < 0) |
|
- return r; |
|
- |
|
- *settings = TAKE_PTR(l); |
|
- |
|
- if (stat("/etc/locale.conf", &st) >= 0) |
|
- c->locale_mtime = timespec_load(&st.st_mtim); |
|
- |
|
- return 0; |
|
-} |
|
- |
|
int vconsole_write_data(Context *c) { |
|
_cleanup_strv_free_ char **l = NULL; |
|
struct stat st; |
|
diff --git a/src/locale/keymap-util.h b/src/locale/keymap-util.h |
|
index c087dbcbbe..5470d1bb9b 100644 |
|
--- a/src/locale/keymap-util.h |
|
+++ b/src/locale/keymap-util.h |
|
@@ -4,13 +4,12 @@ |
|
#include "sd-bus.h" |
|
|
|
#include "hashmap.h" |
|
-#include "locale-util.h" |
|
+#include "locale-setup.h" |
|
#include "time-util.h" |
|
|
|
typedef struct Context { |
|
sd_bus_message *locale_cache; |
|
- usec_t locale_mtime; |
|
- char *locale[_VARIABLE_LC_MAX]; |
|
+ LocaleContext locale_context; |
|
|
|
sd_bus_message *x11_cache; |
|
usec_t x11_mtime; |
|
@@ -40,8 +39,6 @@ int vconsole_convert_to_x11(Context *c); |
|
int vconsole_write_data(Context *c); |
|
int x11_convert_to_vconsole(Context *c); |
|
int x11_write_data(Context *c); |
|
-void locale_simplify(char *locale[_VARIABLE_LC_MAX]); |
|
-int locale_write_data(Context *c, char ***settings); |
|
|
|
bool locale_gen_check_available(void); |
|
int locale_gen_enable_locale(const char *locale); |
|
diff --git a/src/locale/localectl.c b/src/locale/localectl.c |
|
index 661d54c27d..6bfb564f97 100644 |
|
--- a/src/locale/localectl.c |
|
+++ b/src/locale/localectl.c |
|
@@ -12,7 +12,7 @@ |
|
#include "fd-util.h" |
|
#include "fileio.h" |
|
#include "kbd-util.h" |
|
-#include "locale-util.h" |
|
+#include "locale-setup.h" |
|
#include "main-func.h" |
|
#include "memory-util.h" |
|
#include "pager.h" |
|
@@ -52,44 +52,25 @@ static void status_info_clear(StatusInfo *info) { |
|
} |
|
|
|
static void print_overridden_variables(void) { |
|
- _cleanup_(locale_variables_freep) char *variables[_VARIABLE_LC_MAX] = {}; |
|
- bool print_warning = true; |
|
+ _cleanup_(locale_context_clear) LocaleContext c = { .mtime = USEC_INFINITY }; |
|
+ _cleanup_strv_free_ char **env = NULL; |
|
int r; |
|
|
|
if (arg_transport != BUS_TRANSPORT_LOCAL) |
|
return; |
|
|
|
- r = proc_cmdline_get_key_many( |
|
- PROC_CMDLINE_STRIP_RD_PREFIX, |
|
- "locale.LANG", &variables[VARIABLE_LANG], |
|
- "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], |
|
- "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], |
|
- "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], |
|
- "locale.LC_TIME", &variables[VARIABLE_LC_TIME], |
|
- "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE], |
|
- "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY], |
|
- "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], |
|
- "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER], |
|
- "locale.LC_NAME", &variables[VARIABLE_LC_NAME], |
|
- "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], |
|
- "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], |
|
- "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], |
|
- "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]); |
|
- if (r < 0 && r != -ENOENT) { |
|
- log_warning_errno(r, "Failed to read /proc/cmdline: %m"); |
|
- return; |
|
- } |
|
- |
|
- for (LocaleVariable j = 0; j < _VARIABLE_LC_MAX; j++) |
|
- if (variables[j]) { |
|
- if (print_warning) { |
|
- log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n" |
|
- " Command Line: %s=%s", locale_variable_to_string(j), variables[j]); |
|
+ (void) locale_context_load(&c, LOCALE_LOAD_PROC_CMDLINE); |
|
|
|
- print_warning = false; |
|
- } else |
|
- log_warning(" %s=%s", locale_variable_to_string(j), variables[j]); |
|
- } |
|
+ r = locale_context_build_env(&c, &env, NULL); |
|
+ if (r < 0) |
|
+ return (void) log_warning_errno(r, "Failed to build locale settings from kernel command line, ignoring: %m"); |
|
+ |
|
+ STRV_FOREACH(p, env) |
|
+ if (p == env) |
|
+ log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n" |
|
+ " Command Line: %s", *p); |
|
+ else |
|
+ log_warning(" %s", *p); |
|
} |
|
|
|
static void print_status_info(StatusInfo *i) { |
|
diff --git a/src/locale/localed.c b/src/locale/localed.c |
|
index 89bf9c6fba..9718c5b95f 100644 |
|
--- a/src/locale/localed.c |
|
+++ b/src/locale/localed.c |
|
@@ -21,7 +21,6 @@ |
|
#include "dlfcn-util.h" |
|
#include "kbd-util.h" |
|
#include "keymap-util.h" |
|
-#include "locale-util.h" |
|
#include "macro.h" |
|
#include "main-func.h" |
|
#include "missing_capability.h" |
|
@@ -33,44 +32,13 @@ |
|
#include "strv.h" |
|
#include "user-util.h" |
|
|
|
-static int locale_update_system_manager(Context *c, sd_bus *bus) { |
|
- _cleanup_free_ char **l_unset = NULL; |
|
- _cleanup_strv_free_ char **l_set = NULL; |
|
+static int locale_update_system_manager(sd_bus *bus, char **l_set, char **l_unset) { |
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; |
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
|
- size_t c_set = 0, c_unset = 0; |
|
int r; |
|
|
|
assert(bus); |
|
|
|
- l_unset = new0(char*, _VARIABLE_LC_MAX); |
|
- if (!l_unset) |
|
- return log_oom(); |
|
- |
|
- l_set = new0(char*, _VARIABLE_LC_MAX); |
|
- if (!l_set) |
|
- return log_oom(); |
|
- |
|
- for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { |
|
- const char *name; |
|
- |
|
- name = locale_variable_to_string(p); |
|
- assert(name); |
|
- |
|
- if (isempty(c->locale[p])) |
|
- l_unset[c_set++] = (char*) name; |
|
- else { |
|
- char *s; |
|
- |
|
- s = strjoin(name, "=", c->locale[p]); |
|
- if (!s) |
|
- return log_oom(); |
|
- |
|
- l_set[c_unset++] = s; |
|
- } |
|
- } |
|
- |
|
- assert(c_set + c_unset == _VARIABLE_LC_MAX); |
|
r = sd_bus_message_new_method_call(bus, &m, |
|
"org.freedesktop.systemd1", |
|
"/org/freedesktop/systemd1", |
|
@@ -188,21 +156,9 @@ static int property_get_locale( |
|
if (!l) |
|
return -ENOMEM; |
|
|
|
- for (LocaleVariable p = 0, q = 0; p < _VARIABLE_LC_MAX; p++) { |
|
- char *t; |
|
- const char *name; |
|
- |
|
- name = locale_variable_to_string(p); |
|
- assert(name); |
|
- |
|
- if (isempty(c->locale[p])) |
|
- continue; |
|
- |
|
- if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0) |
|
- return -ENOMEM; |
|
- |
|
- l[q++] = t; |
|
- } |
|
+ r = locale_context_build_env(&c->locale_context, &l, NULL); |
|
+ if (r < 0) |
|
+ return r; |
|
|
|
return sd_bus_message_append_strv(reply, l); |
|
} |
|
@@ -342,9 +298,8 @@ static int locale_gen_process_locale(char *new_locale[static _VARIABLE_LC_MAX], |
|
|
|
static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) { |
|
_cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {}; |
|
- _cleanup_strv_free_ char **settings = NULL, **l = NULL; |
|
+ _cleanup_strv_free_ char **l = NULL, **l_set = NULL, **l_unset = NULL; |
|
Context *c = userdata; |
|
- bool modified = false; |
|
int interactive, r; |
|
bool use_localegen; |
|
|
|
@@ -402,22 +357,13 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er |
|
} |
|
|
|
/* Merge with the current settings */ |
|
- for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) |
|
- if (!isempty(c->locale[p]) && isempty(new_locale[p])) { |
|
- new_locale[p] = strdup(c->locale[p]); |
|
- if (!new_locale[p]) |
|
- return -ENOMEM; |
|
- } |
|
- |
|
- locale_simplify(new_locale); |
|
+ r = locale_context_merge(&c->locale_context, new_locale); |
|
+ if (r < 0) |
|
+ return r; |
|
|
|
- for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) |
|
- if (!streq_ptr(c->locale[p], new_locale[p])) { |
|
- modified = true; |
|
- break; |
|
- } |
|
+ locale_variables_simplify(new_locale); |
|
|
|
- if (!modified) { |
|
+ if (locale_context_equal(&c->locale_context, new_locale)) { |
|
log_debug("Locale settings were not modified."); |
|
return sd_bus_reply_method_return(m, NULL); |
|
} |
|
@@ -443,22 +389,21 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er |
|
return r; |
|
} |
|
|
|
- for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) |
|
- free_and_replace(c->locale[p], new_locale[p]); |
|
+ locale_context_take(&c->locale_context, new_locale); |
|
|
|
/* Write locale configuration */ |
|
- r = locale_write_data(c, &settings); |
|
+ r = locale_context_save(&c->locale_context, &l_set, &l_unset); |
|
if (r < 0) { |
|
log_error_errno(r, "Failed to set locale: %m"); |
|
return sd_bus_error_set_errnof(error, r, "Failed to set locale: %m"); |
|
} |
|
|
|
- (void) locale_update_system_manager(c, sd_bus_message_get_bus(m)); |
|
+ (void) locale_update_system_manager(sd_bus_message_get_bus(m), l_set, l_unset); |
|
|
|
- if (settings) { |
|
+ if (!strv_isempty(l_set)) { |
|
_cleanup_free_ char *line = NULL; |
|
|
|
- line = strv_join(settings, ", "); |
|
+ line = strv_join(l_set, ", "); |
|
log_info("Changed locale to %s.", strnull(line)); |
|
} else |
|
log_info("Changed locale to unset."); |
|
@@ -827,7 +772,7 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { |
|
|
|
static int run(int argc, char *argv[]) { |
|
_cleanup_(context_clear) Context context = { |
|
- .locale_mtime = USEC_INFINITY, |
|
+ .locale_context.mtime = USEC_INFINITY, |
|
.vc_mtime = USEC_INFINITY, |
|
.x11_mtime = USEC_INFINITY, |
|
}; |
|
diff --git a/src/shared/locale-setup.c b/src/shared/locale-setup.c |
|
new file mode 100644 |
|
index 0000000000..b8c6647e7c |
|
--- /dev/null |
|
+++ b/src/shared/locale-setup.c |
|
@@ -0,0 +1,256 @@ |
|
+/* SPDX-License-Identifier: LGPL-2.1-or-later */ |
|
+ |
|
+#include <errno.h> |
|
+#include <sys/stat.h> |
|
+ |
|
+#include "env-file-label.h" |
|
+#include "env-file.h" |
|
+#include "env-util.h" |
|
+#include "locale-setup.h" |
|
+#include "proc-cmdline.h" |
|
+#include "strv.h" |
|
+ |
|
+void locale_context_clear(LocaleContext *c) { |
|
+ assert(c); |
|
+ |
|
+ c->mtime = USEC_INFINITY; |
|
+ |
|
+ for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++) |
|
+ c->locale[i] = mfree(c->locale[i]); |
|
+} |
|
+ |
|
+int locale_context_load(LocaleContext *c, LocaleLoadFlag flag) { |
|
+ int r; |
|
+ |
|
+ assert(c); |
|
+ |
|
+ if (FLAGS_SET(flag, LOCALE_LOAD_PROC_CMDLINE)) { |
|
+ locale_context_clear(c); |
|
+ |
|
+ r = proc_cmdline_get_key_many(PROC_CMDLINE_STRIP_RD_PREFIX, |
|
+ "locale.LANG", &c->locale[VARIABLE_LANG], |
|
+ "locale.LANGUAGE", &c->locale[VARIABLE_LANGUAGE], |
|
+ "locale.LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE], |
|
+ "locale.LC_NUMERIC", &c->locale[VARIABLE_LC_NUMERIC], |
|
+ "locale.LC_TIME", &c->locale[VARIABLE_LC_TIME], |
|
+ "locale.LC_COLLATE", &c->locale[VARIABLE_LC_COLLATE], |
|
+ "locale.LC_MONETARY", &c->locale[VARIABLE_LC_MONETARY], |
|
+ "locale.LC_MESSAGES", &c->locale[VARIABLE_LC_MESSAGES], |
|
+ "locale.LC_PAPER", &c->locale[VARIABLE_LC_PAPER], |
|
+ "locale.LC_NAME", &c->locale[VARIABLE_LC_NAME], |
|
+ "locale.LC_ADDRESS", &c->locale[VARIABLE_LC_ADDRESS], |
|
+ "locale.LC_TELEPHONE", &c->locale[VARIABLE_LC_TELEPHONE], |
|
+ "locale.LC_MEASUREMENT", &c->locale[VARIABLE_LC_MEASUREMENT], |
|
+ "locale.LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION]); |
|
+ if (r < 0 && r != -ENOENT) |
|
+ log_debug_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); |
|
+ if (r > 0) |
|
+ goto finalize; |
|
+ } |
|
+ |
|
+ if (FLAGS_SET(flag, LOCALE_LOAD_LOCALE_CONF)) { |
|
+ struct stat st; |
|
+ usec_t t; |
|
+ |
|
+ r = stat("/etc/locale.conf", &st); |
|
+ if (r < 0 && errno != ENOENT) |
|
+ return log_debug_errno(errno, "Failed to stat /etc/locale.conf: %m"); |
|
+ |
|
+ if (r >= 0) { |
|
+ /* If mtime is not changed, then we do not need to re-read the file. */ |
|
+ t = timespec_load(&st.st_mtim); |
|
+ if (c->mtime != USEC_INFINITY && t == c->mtime) |
|
+ return 0; |
|
+ |
|
+ locale_context_clear(c); |
|
+ c->mtime = t; |
|
+ |
|
+ r = parse_env_file(NULL, "/etc/locale.conf", |
|
+ "LANG", &c->locale[VARIABLE_LANG], |
|
+ "LANGUAGE", &c->locale[VARIABLE_LANGUAGE], |
|
+ "LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE], |
|
+ "LC_NUMERIC", &c->locale[VARIABLE_LC_NUMERIC], |
|
+ "LC_TIME", &c->locale[VARIABLE_LC_TIME], |
|
+ "LC_COLLATE", &c->locale[VARIABLE_LC_COLLATE], |
|
+ "LC_MONETARY", &c->locale[VARIABLE_LC_MONETARY], |
|
+ "LC_MESSAGES", &c->locale[VARIABLE_LC_MESSAGES], |
|
+ "LC_PAPER", &c->locale[VARIABLE_LC_PAPER], |
|
+ "LC_NAME", &c->locale[VARIABLE_LC_NAME], |
|
+ "LC_ADDRESS", &c->locale[VARIABLE_LC_ADDRESS], |
|
+ "LC_TELEPHONE", &c->locale[VARIABLE_LC_TELEPHONE], |
|
+ "LC_MEASUREMENT", &c->locale[VARIABLE_LC_MEASUREMENT], |
|
+ "LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION]); |
|
+ if (r < 0) |
|
+ return log_debug_errno(r, "Failed to read /etc/locale.conf: %m"); |
|
+ |
|
+ goto finalize; |
|
+ } |
|
+ } |
|
+ |
|
+ if (FLAGS_SET(flag, LOCALE_LOAD_ENVIRONMENT)) { |
|
+ locale_context_clear(c); |
|
+ |
|
+ /* Fill in what we got passed from systemd. */ |
|
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { |
|
+ const char *name = ASSERT_PTR(locale_variable_to_string(p)); |
|
+ |
|
+ r = free_and_strdup(&c->locale[p], empty_to_null(getenv(name))); |
|
+ if (r < 0) |
|
+ return log_oom_debug(); |
|
+ } |
|
+ |
|
+ goto finalize; |
|
+ } |
|
+ |
|
+ /* Nothing loaded. */ |
|
+ locale_context_clear(c); |
|
+ return 0; |
|
+ |
|
+finalize: |
|
+ if (FLAGS_SET(flag, LOCALE_LOAD_SIMPLIFY)) |
|
+ locale_variables_simplify(c->locale); |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
+int locale_context_build_env(const LocaleContext *c, char ***ret_set, char ***ret_unset) { |
|
+ _cleanup_strv_free_ char **set = NULL, **unset = NULL; |
|
+ int r; |
|
+ |
|
+ assert(c); |
|
+ |
|
+ if (!ret_set && !ret_unset) |
|
+ return 0; |
|
+ |
|
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { |
|
+ const char *name = ASSERT_PTR(locale_variable_to_string(p)); |
|
+ |
|
+ if (isempty(c->locale[p])) { |
|
+ if (!ret_unset) |
|
+ continue; |
|
+ r = strv_extend(&unset, name); |
|
+ } else { |
|
+ if (!ret_set) |
|
+ continue; |
|
+ r = strv_env_assign(&set, name, c->locale[p]); |
|
+ } |
|
+ if (r < 0) |
|
+ return r; |
|
+ } |
|
+ |
|
+ if (ret_set) |
|
+ *ret_set = TAKE_PTR(set); |
|
+ if (ret_unset) |
|
+ *ret_unset = TAKE_PTR(unset); |
|
+ return 0; |
|
+} |
|
+ |
|
+int locale_context_save(LocaleContext *c, char ***ret_set, char ***ret_unset) { |
|
+ _cleanup_strv_free_ char **set = NULL, **unset = NULL; |
|
+ struct stat st; |
|
+ int r; |
|
+ |
|
+ assert(c); |
|
+ |
|
+ /* Set values will be returned as strv in *ret on success. */ |
|
+ |
|
+ r = locale_context_build_env(c, &set, ret_unset ? &unset : NULL); |
|
+ if (r < 0) |
|
+ return r; |
|
+ |
|
+ if (strv_isempty(set)) { |
|
+ if (unlink("/etc/locale.conf") < 0) |
|
+ return errno == ENOENT ? 0 : -errno; |
|
+ |
|
+ c->mtime = USEC_INFINITY; |
|
+ if (ret_set) |
|
+ *ret_set = NULL; |
|
+ if (ret_unset) |
|
+ *ret_unset = NULL; |
|
+ return 0; |
|
+ } |
|
+ |
|
+ r = write_env_file_label("/etc/locale.conf", set); |
|
+ if (r < 0) |
|
+ return r; |
|
+ |
|
+ if (stat("/etc/locale.conf", &st) >= 0) |
|
+ c->mtime = timespec_load(&st.st_mtim); |
|
+ |
|
+ if (ret_set) |
|
+ *ret_set = TAKE_PTR(set); |
|
+ if (ret_unset) |
|
+ *ret_unset = TAKE_PTR(unset); |
|
+ return 0; |
|
+} |
|
+ |
|
+int locale_context_merge(const LocaleContext *c, char *l[_VARIABLE_LC_MAX]) { |
|
+ assert(c); |
|
+ assert(l); |
|
+ |
|
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) |
|
+ if (!isempty(c->locale[p]) && isempty(l[p])) { |
|
+ l[p] = strdup(c->locale[p]); |
|
+ if (!l[p]) |
|
+ return -ENOMEM; |
|
+ } |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
+void locale_context_take(LocaleContext *c, char *l[_VARIABLE_LC_MAX]) { |
|
+ assert(c); |
|
+ assert(l); |
|
+ |
|
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) |
|
+ free_and_replace(c->locale[p], l[p]); |
|
+} |
|
+ |
|
+bool locale_context_equal(const LocaleContext *c, char *l[_VARIABLE_LC_MAX]) { |
|
+ assert(c); |
|
+ assert(l); |
|
+ |
|
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) |
|
+ if (!streq_ptr(c->locale[p], l[p])) |
|
+ return false; |
|
+ |
|
+ return true; |
|
+} |
|
+ |
|
+int locale_setup(char ***environment) { |
|
+ _cleanup_(locale_context_clear) LocaleContext c = { .mtime = USEC_INFINITY }; |
|
+ _cleanup_strv_free_ char **add = NULL; |
|
+ int r; |
|
+ |
|
+ assert(environment); |
|
+ |
|
+ r = locale_context_load(&c, LOCALE_LOAD_PROC_CMDLINE | LOCALE_LOAD_LOCALE_CONF); |
|
+ if (r < 0) |
|
+ return r; |
|
+ |
|
+ r = locale_context_build_env(&c, &add, NULL); |
|
+ if (r < 0) |
|
+ return r; |
|
+ |
|
+ if (strv_isempty(add)) { |
|
+ /* If no locale is configured then default to compile-time default. */ |
|
+ |
|
+ add = strv_new("LANG=" SYSTEMD_DEFAULT_LOCALE); |
|
+ if (!add) |
|
+ return -ENOMEM; |
|
+ } |
|
+ |
|
+ if (strv_isempty(*environment)) |
|
+ strv_free_and_replace(*environment, add); |
|
+ else { |
|
+ char **merged; |
|
+ |
|
+ merged = strv_env_merge(*environment, add); |
|
+ if (!merged) |
|
+ return -ENOMEM; |
|
+ |
|
+ strv_free_and_replace(*environment, merged); |
|
+ } |
|
+ |
|
+ return 0; |
|
+} |
|
diff --git a/src/shared/locale-setup.h b/src/shared/locale-setup.h |
|
new file mode 100644 |
|
index 0000000000..ec3fc8c364 |
|
--- /dev/null |
|
+++ b/src/shared/locale-setup.h |
|
@@ -0,0 +1,28 @@ |
|
+/* SPDX-License-Identifier: LGPL-2.1-or-later */ |
|
+#pragma once |
|
+ |
|
+#include "locale-util.h" |
|
+#include "time-util.h" |
|
+ |
|
+typedef struct LocaleContext { |
|
+ usec_t mtime; |
|
+ char *locale[_VARIABLE_LC_MAX]; |
|
+} LocaleContext; |
|
+ |
|
+typedef enum LocaleLoadFlag { |
|
+ LOCALE_LOAD_PROC_CMDLINE = 1 << 0, |
|
+ LOCALE_LOAD_LOCALE_CONF = 1 << 1, |
|
+ LOCALE_LOAD_ENVIRONMENT = 1 << 2, |
|
+ LOCALE_LOAD_SIMPLIFY = 1 << 3, |
|
+} LocaleLoadFlag; |
|
+ |
|
+void locale_context_clear(LocaleContext *c); |
|
+int locale_context_load(LocaleContext *c, LocaleLoadFlag flag); |
|
+int locale_context_build_env(const LocaleContext *c, char ***ret_set, char ***ret_unset); |
|
+int locale_context_save(LocaleContext *c, char ***ret_set, char ***ret_unset); |
|
+ |
|
+int locale_context_merge(const LocaleContext *c, char *l[_VARIABLE_LC_MAX]); |
|
+void locale_context_take(LocaleContext *c, char *l[_VARIABLE_LC_MAX]); |
|
+bool locale_context_equal(const LocaleContext *c, char *l[_VARIABLE_LC_MAX]); |
|
+ |
|
+int locale_setup(char ***environment); |
|
diff --git a/src/shared/meson.build b/src/shared/meson.build |
|
index 006310a917..62365682cf 100644 |
|
--- a/src/shared/meson.build |
|
+++ b/src/shared/meson.build |
|
@@ -195,6 +195,8 @@ shared_sources = files(''' |
|
linux/ethtool.h |
|
local-addresses.c |
|
local-addresses.h |
|
+ locale-setup.c |
|
+ locale-setup.h |
|
lockfile-util.c |
|
lockfile-util.h |
|
log-link.h
|
|
|