mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-10 01:17:44 +03:00
locale-setup: merge locale handling in PID1 and localed
This commit is contained in:
parent
d2e96a4f87
commit
3d36b5d7e7
@ -339,6 +339,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",
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int locale_setup(char ***environment);
|
@ -85,8 +85,6 @@ libcore_sources = files(
|
||||
'load-dropin.h',
|
||||
'load-fragment.c',
|
||||
'load-fragment.h',
|
||||
'locale-setup.c',
|
||||
'locale-setup.h',
|
||||
'manager-dump.c',
|
||||
'manager-dump.h',
|
||||
'manager-serialize.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) {
|
||||
@ -280,40 +218,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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
(void) locale_context_load(&c, LOCALE_LOAD_PROC_CMDLINE);
|
||||
|
||||
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]);
|
||||
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");
|
||||
|
||||
print_warning = false;
|
||||
} else
|
||||
log_warning(" %s=%s", locale_variable_to_string(j), variables[j]);
|
||||
}
|
||||
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) {
|
||||
|
@ -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;
|
||||
}
|
||||
r = locale_context_merge(&c->locale_context, new_locale);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
locale_simplify(new_locale);
|
||||
locale_variables_simplify(new_locale);
|
||||
|
||||
for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
|
||||
if (!streq_ptr(c->locale[p], new_locale[p])) {
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
256
src/shared/locale-setup.c
Normal file
256
src/shared/locale-setup.c
Normal file
@ -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;
|
||||
}
|
28
src/shared/locale-setup.h
Normal file
28
src/shared/locale-setup.h
Normal file
@ -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);
|
@ -199,6 +199,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',
|
||||
|
Loading…
Reference in New Issue
Block a user