Files
systemd/src/shared/locale-setup.c
T
Zbigniew Jędrzejewski-Szmek a5937dcf39 tree-wide: change initialization to use EBADF instead of EBADFD
Those fds never were, so it's not fair to say that they are in "bad state".
Let's use the shorter and more direct errno.
2022-12-19 15:00:59 +01:00

295 lines
9.7 KiB
C

/* 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 "errno-util.h"
#include "fd-util.h"
#include "locale-setup.h"
#include "proc-cmdline.h"
#include "stat-util.h"
#include "strv.h"
void locale_context_clear(LocaleContext *c) {
assert(c);
c->st = (struct stat) {};
for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++)
c->locale[i] = mfree(c->locale[i]);
}
static int locale_context_load_proc(LocaleContext *c, LocaleLoadFlag flag) {
int r;
assert(c);
if (!FLAGS_SET(flag, LOCALE_LOAD_PROC_CMDLINE))
return 0;
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 == -ENOENT)
return 0;
if (r < 0)
return log_debug_errno(r, "Failed to read /proc/cmdline: %m");
return r;
}
static int locale_context_load_conf(LocaleContext *c, LocaleLoadFlag flag) {
_cleanup_close_ int fd = -EBADF;
struct stat st;
int r;
assert(c);
if (!FLAGS_SET(flag, LOCALE_LOAD_LOCALE_CONF))
return 0;
fd = RET_NERRNO(open("/etc/locale.conf", O_CLOEXEC | O_PATH));
if (fd == -ENOENT)
return 0;
if (fd < 0)
return log_debug_errno(errno, "Failed to open /etc/locale.conf: %m");
if (fstat(fd, &st) < 0)
return log_debug_errno(errno, "Failed to stat /etc/locale.conf: %m");
/* If the file is not changed, then we do not need to re-read the file. */
if (stat_inode_unmodified(&c->st, &st))
return 0;
c->st = st;
locale_context_clear(c);
r = parse_env_file_fd(fd, "/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");
return 1; /* loaded */
}
static int locale_context_load_env(LocaleContext *c, LocaleLoadFlag flag) {
int r;
assert(c);
if (!FLAGS_SET(flag, LOCALE_LOAD_ENVIRONMENT))
return 0;
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();
}
return 1; /* loaded */
}
int locale_context_load(LocaleContext *c, LocaleLoadFlag flag) {
int r;
assert(c);
r = locale_context_load_proc(c, flag);
if (r > 0)
goto finalize;
r = locale_context_load_conf(c, flag);
if (r != 0)
goto finalize;
r = locale_context_load_env(c, flag);
finalize:
if (r <= 0) {
/* Nothing loaded, or error. */
locale_context_clear(c);
return r;
}
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;
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->st = (struct stat) {};
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", &c->st) < 0)
return -errno;
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 = {};
_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;
}