Use paths specified from environment variables for /etc configuration files

Some configuration files that need updates are directly under in /etc. To
update them atomically, we need write access to /etc. For Ubuntu Core this is
an issue as /etc is not writable. Only a selection of subdirectories can be
writable. The general solution is symlinks or bind mounts to writable places.
But for atomic writes in /etc, that does not work. So Ubuntu has had a patch
for that that did not age well.

Instead we would like to introduce some environment variables for alternate
paths.

 * SYSTEMD_ETC_HOSTNAME: /etc/hostname
 * SYSTEMD_ETC_MACHINE_INFO: /etc/machine-info
 * SYSTEMD_ETC_LOCALTIME: /etc/localtime
 * SYSTEMD_ETC_LOCALE_CONF: /etc/locale.conf
 * SYSTEMD_ETC_VCONSOLE_CONF: /etc/vconsole.conf
 * SYSTEMD_ETC_ADJTIME: /etc/adjtime

While it is for now expected that there is a symlink from the standard, we
still try to read them from that alternate path. This is important for
`/etc/localtime`, which is a symlink, so we cannot have an indirect symlink or
bind mount for it.

Since machine-id is typically written only once and not updated. This commit
does not cover it. An initrd can properly create it and bind mount it.
This commit is contained in:
Valentin David
2025-06-23 15:32:11 +02:00
committed by Lennart Poettering
parent fa34fda2eb
commit 0dc39dffbd
19 changed files with 376 additions and 48 deletions
+36
View File
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/stat.h>
#include "env-file.h"
#include "env-file-label.h"
#include "locale-setup.h"
#include "selinux-util.h"
int write_env_file_label(int dir_fd, const char *fname, char **headers, char **l) {
int r;
r = mac_selinux_create_file_prepare(fname, S_IFREG);
if (r < 0)
return r;
r = write_env_file(dir_fd, fname, headers, l);
mac_selinux_create_file_clear();
return r;
}
int write_vconsole_conf_label(char **l) {
int r;
r = mac_selinux_create_file_prepare(etc_vconsole_conf(), S_IFREG);
if (r < 0)
return r;
r = write_vconsole_conf(AT_FDCWD, etc_vconsole_conf(), l);
mac_selinux_create_file_clear();
return r;
}
+1 -1
View File
@@ -139,7 +139,7 @@ int read_etc_hostname(const char *path, bool substitute_wildcards, char **ret) {
assert(ret);
if (!path)
path = "/etc/hostname";
path = etc_hostname();
f = fopen(path, "re");
if (!f)
+23 -5
View File
@@ -67,7 +67,7 @@ static int locale_context_load_conf(LocaleContext *c, LocaleLoadFlag flag) {
if (!FLAGS_SET(flag, LOCALE_LOAD_LOCALE_CONF))
return 0;
fd = RET_NERRNO(open("/etc/locale.conf", O_CLOEXEC | O_PATH));
fd = RET_NERRNO(open(etc_locale_conf(), O_CLOEXEC | O_PATH));
if (fd == -ENOENT)
return 0;
if (fd < 0)
@@ -83,7 +83,7 @@ static int locale_context_load_conf(LocaleContext *c, LocaleLoadFlag flag) {
c->st = st;
locale_context_clear(c);
r = parse_env_file_fd(fd, "/etc/locale.conf",
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],
@@ -199,7 +199,7 @@ int locale_context_save(LocaleContext *c, char ***ret_set, char ***ret_unset) {
return r;
if (strv_isempty(set)) {
if (unlink("/etc/locale.conf") < 0)
if (unlink(etc_locale_conf()) < 0)
return errno == ENOENT ? 0 : -errno;
c->st = (struct stat) {};
@@ -213,14 +213,14 @@ int locale_context_save(LocaleContext *c, char ***ret_set, char ***ret_unset) {
r = write_env_file(
AT_FDCWD,
"/etc/locale.conf",
etc_locale_conf(),
/* headers= */ NULL,
set,
WRITE_ENV_FILE_LABEL);
if (r < 0)
return r;
if (stat("/etc/locale.conf", &c->st) < 0)
if (stat(etc_locale_conf(), &c->st) < 0)
return -errno;
if (ret_set)
@@ -300,3 +300,21 @@ int locale_setup(char ***environment) {
return 0;
}
const char* etc_locale_conf(void) {
static const char *cached = NULL;
if (!cached)
cached = secure_getenv("SYSTEMD_ETC_LOCALE_CONF") ?: "/etc/locale.conf";
return cached;
}
const char* etc_vconsole_conf(void) {
static const char *cached = NULL;
if (!cached)
cached = secure_getenv("SYSTEMD_ETC_VCONSOLE_CONF") ?: "/etc/vconsole.conf";
return cached;
}
+3
View File
@@ -28,3 +28,6 @@ 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);
const char* etc_locale_conf(void);
const char* etc_vconsole_conf(void);