first existing unit listed in the environment variable, and
`timedatectl set-ntp off` disables and stops all listed units.
+* `$SYSTEMD_ETC_ADJTIME` - override the path to the hardware clock settings
+ file. The default is `/etc/adjtime`.
+
`systemd-sulogin-shell`:
* `$SYSTEMD_SULOGIN_FORCE=1` — This skips asking for the root password if the
`systemd.factory_reset=` kernel command line option: if set to false,
requesting a TPM clearing is skipped, and the command immediately exits
successfully.
+
+`systemd-timedated`, `systemd-firstboot`, `systemd`:
+
+* `$SYSTEMD_ETC_LOCALTIME` - override the path to the timezone symlink. The
+ default is `/etc/localtime`. The directory of the path should exist and not
+ be removed.
+
+`systemd-hostnamed`, `systemd-firstboot`:
+
+* `$SYSTEMD_ETC_HOSTNAME` - override the path to local system name
+ configuration file. The default is `/etc/hostname`.
+
+* `$SYSTEMD_ETC_MACHINE_INFO` - override the path to the machine metadata file. The
+ default is `/etc/machine-info`.
+
+`systemd-localed`, `systemd-firstboot`:
+
+* `$SYSTEMD_ETC_LOCALE_CONF` - override the path to the system-wide locale
+ configuration file. The default is `/etc/locale.conf`.
+
+* `$SYSTEMD_ETC_VCONSOLE_CONF` - override the path to the virtual console
+ configuration file. The default is `/etc/vconsole.conf`.
endswith_no_case(hostname, ".localhost.localdomain.");
}
+const char* etc_hostname(void) {
+ static const char *cached = NULL;
+
+ if (!cached)
+ cached = secure_getenv("SYSTEMD_ETC_HOSTNAME") ?: "/etc/hostname";
+
+ return cached;
+}
+
+const char* etc_machine_info(void) {
+ static const char *cached = NULL;
+
+ if (!cached)
+ cached = secure_getenv("SYSTEMD_ETC_MACHINE_INFO") ?: "/etc/machine-info";
+
+ return cached;
+}
+
int get_pretty_hostname(char **ret) {
_cleanup_free_ char *n = NULL;
int r;
assert(ret);
- r = parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &n);
+ r = parse_env_file(NULL, etc_machine_info(), "PRETTY_HOSTNAME", &n);
if (r < 0)
return r;
return STRCASE_IN_SET(hostname, "_localdnsproxy", "_localdnsproxy.");
}
+const char* etc_hostname(void);
+const char* etc_machine_info(void);
+
int get_pretty_hostname(char **ret);
int machine_spec_valid(const char *s);
assert(ret);
- r = readlink_malloc("/etc/localtime", &t);
+ r = readlink_malloc(etc_localtime(), &t);
if (r == -ENOENT)
/* If the symlink does not exist, assume "UTC", like glibc does */
return strdup_to(ret, "UTC");
return strdup_to(ret, e);
}
+const char* etc_localtime(void) {
+ static const char *cached = NULL;
+
+ if (!cached)
+ cached = secure_getenv("SYSTEMD_ETC_LOCALTIME") ?: "/etc/localtime";
+
+ return cached;
+}
+
int mktime_or_timegm_usec(
struct tm *tm, /* input + normalized output */
bool utc,
usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to);
int get_timezone(char **ret);
+const char* etc_localtime(void);
int mktime_or_timegm_usec(struct tm *tm, bool utc, usec_t *ret);
int localtime_or_gmtime_usec(usec_t t, bool utc, struct tm *ret);
assert(m);
/* Read the current stat() data of /etc/localtime so that we detect changes */
- if (lstat("/etc/localtime", &st) < 0) {
+ if (lstat(etc_localtime(), &st) < 0) {
log_debug_errno(errno, "Failed to stat /etc/localtime, ignoring: %m");
changed = m->etc_localtime_accessible;
m->etc_localtime_accessible = false;
* Note that we create the new event source first here, before releasing the old one. This should optimize
* behaviour as this way sd-event can reuse the old watch in case the inode didn't change. */
- r = sd_event_add_inotify(m->event, &new_event, "/etc/localtime",
+ r = sd_event_add_inotify(m->event, &new_event, etc_localtime(),
IN_ATTRIB|IN_MOVE_SELF|IN_CLOSE_WRITE|IN_DONT_FOLLOW, manager_dispatch_timezone_change, m);
if (r == -ENOENT) {
/* If the file doesn't exist yet, subscribe to /etc instead, and wait until it is created either by
* O_CREATE or by rename() */
+ _cleanup_free_ char *localtime_dir = NULL;
- log_debug_errno(r, "/etc/localtime doesn't exist yet, watching /etc instead.");
- r = sd_event_add_inotify(m->event, &new_event, "/etc",
+ int dir_r = path_extract_directory(etc_localtime(), &localtime_dir);
+ if (dir_r < 0)
+ return log_error_errno(dir_r, "Failed to extract directory from path '%s': %m", etc_localtime());
+
+ log_debug_errno(r, "%s doesn't exist yet, watching %s instead.", etc_localtime(), localtime_dir);
+
+ r = sd_event_add_inotify(m->event, &new_event, localtime_dir,
IN_CREATE|IN_MOVED_TO|IN_ONLYDIR, manager_dispatch_timezone_change, m);
}
if (r < 0)
#include "label.h"
#include "label-util.h"
#include "libcrypt-util.h"
+#include "locale-setup.h"
#include "locale-util.h"
#include "lock-util.h"
#include "loop-util.h"
assert(rfd >= 0);
- pfd = chase_and_open_parent_at(rfd, "/etc/locale.conf",
+ pfd = chase_and_open_parent_at(rfd, etc_locale_conf(),
CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
&f);
if (pfd < 0)
return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
if (arg_copy_locale && r == 0) {
- r = copy_file_atomic_at(AT_FDCWD, "/etc/locale.conf", pfd, f, 0644, COPY_REFLINK);
+ r = copy_file_atomic_at(AT_FDCWD, etc_locale_conf(), pfd, f, 0644, COPY_REFLINK);
if (r != -ENOENT) {
if (r < 0)
return log_error_errno(r, "Failed to copy host's /etc/locale.conf: %m");
assert(rfd >= 0);
- pfd = chase_and_open_parent_at(rfd, "/etc/vconsole.conf",
+ pfd = chase_and_open_parent_at(rfd, etc_vconsole_conf(),
CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
&f);
if (pfd < 0)
return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
if (arg_copy_keymap && r == 0) {
- r = copy_file_atomic_at(AT_FDCWD, "/etc/vconsole.conf", pfd, f, 0644, COPY_REFLINK);
+ r = copy_file_atomic_at(AT_FDCWD, etc_vconsole_conf(), pfd, f, 0644, COPY_REFLINK);
if (r != -ENOENT) {
if (r < 0)
return log_error_errno(r, "Failed to copy host's /etc/vconsole.conf: %m");
static int process_timezone(int rfd) {
_cleanup_close_ int pfd = -EBADF;
- _cleanup_free_ char *f = NULL;
+ _cleanup_free_ char *f = NULL, *relpath = NULL;
const char *e;
int r;
assert(rfd >= 0);
- pfd = chase_and_open_parent_at(rfd, "/etc/localtime",
+ pfd = chase_and_open_parent_at(rfd, etc_localtime(),
CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW,
&f);
if (pfd < 0)
if (arg_copy_timezone && r == 0) {
_cleanup_free_ char *s = NULL;
- r = readlink_malloc("/etc/localtime", &s);
+ r = readlink_malloc(etc_localtime(), &s);
if (r != -ENOENT) {
if (r < 0)
return log_error_errno(r, "Failed to read host's /etc/localtime: %m");
if (isempty(arg_timezone))
return 0;
- e = strjoina("../usr/share/zoneinfo/", arg_timezone);
+ e = strjoina("/usr/share/zoneinfo/", arg_timezone);
+ r = path_make_relative_parent(etc_localtime(), e, &relpath);
+ if (r < 0)
+ return r;
- r = symlinkat_atomic_full(e, pfd, f, SYMLINK_LABEL);
+ r = symlinkat_atomic_full(relpath, pfd, f, SYMLINK_LABEL);
if (r < 0)
return log_error_errno(r, "Failed to create /etc/localtime symlink: %m");
assert(rfd >= 0);
- pfd = chase_and_open_parent_at(rfd, "/etc/hostname",
+ pfd = chase_and_open_parent_at(rfd, etc_hostname(),
CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_WARN,
&f);
if (pfd < 0)
return 0;
FOREACH_STRING(p,
- "/etc/locale.conf",
- "/etc/vconsole.conf",
- "/etc/hostname",
+ etc_locale_conf(),
+ etc_vconsole_conf(),
+ etc_hostname(),
"/etc/machine-id",
"/etc/kernel/cmdline",
- "/etc/localtime") {
+ etc_localtime()) {
r = reset_one(rfd, p);
if (r < 0)
return r;
assert(c);
- if (stat("/etc/hostname", ¤t_stat) >= 0 &&
+ if (stat(etc_hostname(), ¤t_stat) >= 0 &&
stat_inode_unmodified(&c->etc_hostname_stat, ¤t_stat))
return;
assert(c);
- if (stat("/etc/machine-info", ¤t_stat) >= 0 &&
+ if (stat(etc_machine_info(), ¤t_stat) >= 0 &&
stat_inode_unmodified(&c->etc_machine_info_stat, ¤t_stat))
return;
(UINT64_C(1) << PROP_HARDWARE_SKU) |
(UINT64_C(1) << PROP_HARDWARE_VERSION));
- r = parse_env_file(NULL, "/etc/machine-info",
+ r = parse_env_file(NULL, etc_machine_info(),
"PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
"ICON_NAME", &c->data[PROP_ICON_NAME],
"CHASSIS", &c->data[PROP_CHASSIS],
s = &c->etc_hostname_stat;
if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
- if (unlink("/etc/hostname") < 0 && errno != ENOENT)
+ if (unlink(etc_hostname()) < 0 && errno != ENOENT)
return -errno;
TAKE_PTR(s);
return 0;
}
- r = write_string_file("/etc/hostname", c->data[PROP_STATIC_HOSTNAME], WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
+ r = write_string_file(etc_hostname(), c->data[PROP_STATIC_HOSTNAME], WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
if (r < 0)
return r;
* already, even if we can't make it hit the disk. */
s = &c->etc_machine_info_stat;
- r = load_env_file(NULL, "/etc/machine-info", &l);
+ r = load_env_file(NULL, etc_machine_info(), &l);
if (r < 0 && r != -ENOENT)
return r;
}
if (strv_isempty(l)) {
- if (unlink("/etc/machine-info") < 0 && errno != ENOENT)
+ if (unlink(etc_machine_info()) < 0 && errno != ENOENT)
return -errno;
TAKE_PTR(s);
r = write_env_file(
AT_FDCWD,
- "/etc/machine-info",
- /* headers= */ NULL,
+ etc_machine_info(),
+ /* headers= */NULL,
l,
WRITE_ENV_FILE_LABEL);
if (r < 0)
(void) vsock_get_local_cid(&local_cid);
(void) load_os_release_pairs(/* root= */ NULL, &os_release_pairs);
- (void) load_env_file_pairs(/* f=*/ NULL, "/etc/machine-info", &machine_info_pairs);
+ (void) load_env_file_pairs(/* f=*/ NULL, etc_machine_info(), &machine_info_pairs);
r = sd_json_buildo(
&v,
c->vc_cache = sd_bus_message_ref(m);
}
- fd = RET_NERRNO(open("/etc/vconsole.conf", O_CLOEXEC | O_PATH));
+ fd = RET_NERRNO(open(etc_vconsole_conf(), O_CLOEXEC | O_PATH));
if (fd == -ENOENT) {
c->vc_stat = (struct stat) {};
vc_context_clear(&c->vc);
x11_context_clear(&c->x11_from_vc);
r = parse_env_file_fd(
- fd, "/etc/vconsole.conf",
+ fd, etc_vconsole_conf(),
"KEYMAP", &c->vc.keymap,
"KEYMAP_TOGGLE", &c->vc.toggle,
"XKBLAYOUT", &c->x11_from_vc.layout,
xc = context_get_x11_context(c);
- r = load_env_file(NULL, "/etc/vconsole.conf", &l);
+ r = load_env_file(NULL, etc_vconsole_conf(), &l);
if (r < 0 && r != -ENOENT)
return r;
return r;
if (strv_isempty(l)) {
- if (unlink("/etc/vconsole.conf") < 0)
+ if (unlink(etc_vconsole_conf()) < 0)
return errno == ENOENT ? 0 : -errno;
c->vc_stat = (struct stat) {};
if (r < 0)
return r;
- if (stat("/etc/vconsole.conf", &c->vc_stat) < 0)
+ if (stat(etc_vconsole_conf(), &c->vc_stat) < 0)
return -errno;
return 0;
--- /dev/null
+/* 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;
+}
assert(ret);
if (!path)
- path = "/etc/hostname";
+ path = etc_hostname();
f = fopen(path, "re");
if (!f)
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)
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],
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) {};
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)
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;
+}
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);
if (access("/usr/share/zoneinfo/UTC", F_OK) < 0) {
- if (unlink("/etc/localtime") < 0 && errno != ENOENT)
+ if (unlink(etc_localtime()) < 0 && errno != ENOENT)
return -errno;
return 0;
}
- source = "../usr/share/zoneinfo/UTC";
+ source = "/usr/share/zoneinfo/UTC";
} else {
- p = path_join("../usr/share/zoneinfo", c->zone);
+ p = path_join("/usr/share/zoneinfo", c->zone);
if (!p)
return -ENOMEM;
source = p;
}
- return symlink_atomic(source, "/etc/localtime");
+ return symlinkat_atomic_full(source, AT_FDCWD, etc_localtime(),
+ !secure_getenv("SYSTEMD_ETC_LOCALTIME"));
+}
+
+static const char* etc_adjtime(void) {
+ static const char *cached = NULL;
+
+ if (!cached)
+ cached = secure_getenv("SYSTEMD_ETC_ADJTIME") ?: "/etc/adjtime";
+
+ return cached;
}
static int context_write_data_local_rtc(Context *c) {
assert(c);
- r = read_full_file("/etc/adjtime", &s, NULL);
+ r = read_full_file(etc_adjtime(), &s, NULL);
if (r < 0) {
if (r != -ENOENT)
return r;
*mempcpy_typesafe(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
if (streq(w, NULL_ADJTIME_UTC)) {
- if (unlink("/etc/adjtime") < 0)
+ if (unlink(etc_adjtime()) < 0)
if (errno != ENOENT)
return -errno;
if (r < 0)
return r;
- return write_string_file("/etc/adjtime", w, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
+ return write_string_file(etc_adjtime(), w, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL);
}
static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m) {
while test ! -f /tmp/clock-changed ; do sleep .5 ; done
+mkdir -p /etc/alternate-path
+rm -f /etc/alternate-path/localtime
+
+cat <<EOF >/run/systemd/system.conf
+[Manager]
+ManagerEnvironment=SYSTEMD_ETC_LOCALTIME=/etc/alternate-path/localtime
+EOF
+mkdir -p /run/systemd/system/systemd-timedated.service.d
+cat >/run/systemd/system/systemd-timedated.service.d/override.conf <<EOF
+[Service]
+Environment=SYSTEMD_ETC_LOCALTIME=/run/alternate-path/mylocaltime
+Environment=SYSTEMD_ETC_ADJTIME=/run/alternate-path/myadjtime
+EOF
+systemctl daemon-reload
+
+systemd-run --on-timezone-change touch /tmp/timezone-changed-alternate-path-1
+timedatectl set-timezone Europe/Berlin
+
+while test ! -f /tmp/timezone-changed-alternate-path-1 ; do sleep .5 ; done
+
+systemd-run --on-timezone-change touch /tmp/timezone-changed-alternate-path-2
+timedatectl set-timezone Europe/Kyiv
+
+while test ! -f /tmp/timezone-changed-alternate-path-2 ; do sleep .5 ; done
+
+rm /run/systemd/system.conf /run/systemd/system/systemd-timedated.service.d/override.conf
+systemctl daemon-reload
+
systemd-analyze log-level info
touch /testok
rm -f /run/systemd/network/ntp99.*
}
+teardown_timedated_alternate_paths() {
+ set +eu
+
+ rm -rf /run/systemd/system/systemd-timedated.service.d
+ systemctl daemon-reload
+ systemctl restart systemd-timedated
+}
+
+testcase_timedated_alternate_paths() {
+ trap teardown_timedated_alternate_paths RETURN
+
+ mkdir -p /run/alternate-path
+ mkdir -p /run/systemd/system/systemd-timedated.service.d
+ cat >/run/systemd/system/systemd-timedated.service.d/override.conf <<EOF
+[Service]
+Environment=SYSTEMD_ETC_LOCALTIME=/run/alternate-path/mylocaltime
+Environment=SYSTEMD_ETC_ADJTIME=/run/alternate-path/myadjtime
+EOF
+ systemctl daemon-reload
+ systemctl restart systemd-timedated
+
+ assert_in "Local time:" "$(timedatectl --no-pager)"
+
+ assert_eq "$(timedatectl --no-pager set-timezone Europe/Kyiv 2>&1)" ""
+ assert_eq "$(readlink /run/alternate-path/mylocaltime | sed 's#^.*zoneinfo/##')" "Europe/Kyiv"
+ assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
+
+ # Restart to force using get_timezine
+ systemctl restart systemd-timedated
+ assert_in "Time zone: Europe/Kyiv \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
+
+ assert_in "RTC in local TZ: no" "$(timedatectl --no-pager)"
+ assert_rc 0 timedatectl set-local-rtc 1
+ assert_in "RTC in local TZ: yes" "$(timedatectl --no-pager)"
+ assert_eq "$(cat /run/alternate-path/myadjtime)" "0.0 0 0
+0
+LOCAL"
+ assert_rc 0 timedatectl set-local-rtc 0
+ if [[ -e /run/alternate-path/myadjtime ]]; then
+ echo "/run/alternate-path/myadjtime still exists" >&2
+ exit 1
+ fi
+}
+
run_testcases
touch /testok
hostnamectl set-hostname "$SAVED"
}
+teardown_hostnamed_alternate_paths() {
+ set +eu
+
+ rm -rf /run/systemd/system/systemd-hostnamed.service.d
+ systemctl daemon-reload
+ systemctl restart systemd-hostnamed
+ if [[ -f /etc/hostname ]]; then
+ orig=$(cat /etc/hostname)
+ if [[ -n "${orig}" ]]; then
+ hostnamectl hostname "${orig}"
+ fi
+ fi
+}
+
+testcase_hostnamed_alternate_paths() {
+ trap teardown_hostnamed_alternate_paths RETURN
+
+ mkdir -p /run/alternate-path
+
+ mkdir -p /run/systemd/system/systemd-hostnamed.service.d
+ cat >/run/systemd/system/systemd-hostnamed.service.d/override.conf <<EOF
+[Service]
+Environment=SYSTEMD_ETC_HOSTNAME=/run/alternate-path/myhostname
+Environment=SYSTEMD_ETC_MACHINE_INFO=/run/alternate-path/mymachine-info
+EOF
+ systemctl daemon-reload
+ systemctl restart systemd-hostnamed
+
+ assert_rc 0 hostnamectl set-hostname heisenberg
+ assert_rc 0 hostnamectl chassis watch
+
+ output=$(hostnamectl)
+ assert_in "Static hostname: heisenberg" "$output"
+ assert_in "Chassis: watch" "$output"
+ assert_in "heisenberg" "$(cat /run/alternate-path/myhostname)"
+ assert_in "CHASSIS=watch" "$(cat /run/alternate-path/mymachine-info)"
+}
+
+
run_testcases
touch /testok
localectl set-locale en_US.UTF-8
}
+teardown_localed_alternate_paths() {
+ set +eu
+
+ rm -rf /run/systemd/system/systemd-localed.service.d
+ systemctl daemon-reload
+ systemctl restart systemd-localed
+}
+
+testcase_localed_alternate_paths() {
+ trap teardown_localed_alternate_paths RETURN
+
+ mkdir -p /run/alternate-path
+
+ mkdir -p /run/systemd/system/systemd-localed.service.d
+ cat >/run/systemd/system/systemd-localed.service.d/override.conf <<EOF
+[Service]
+Environment=SYSTEMD_ETC_LOCALE_CONF=/run/alternate-path/mylocale.conf
+Environment=SYSTEMD_ETC_VCONSOLE_CONF=/run/alternate-path/myvconsole.conf
+EOF
+ systemctl daemon-reload
+ systemctl restart systemd-localed
+
+ if localectl list-locales | grep "^de_DE.UTF-8$"; then
+ assert_rc 0 localectl set-locale "LANG=de_DE.UTF-8" "LC_CTYPE=C"
+ else
+ skip_locale=1
+ fi
+
+ if localectl list-keymaps | grep -F "^no$"; then
+ assert_rc 0 localectl set-keymap "no"
+ else
+ skip_keymap=1
+ fi
+
+ output=$(localectl)
+
+ if [[ -z "${skip_locale-}" ]]; then
+ assert_in "System Locale: LANG=de_DE.UTF-8" "$output"
+ assert_in "LANG=de_DE.UTF-8" "$(cat /run/alternate-path/mylocale.conf)"
+ fi
+
+ if [[ -z "${skip_keymap-}" ]]; then
+ assert_in "VC Keymap: no" "$output"
+ assert_in "KEYMAP=no" "$(cat /run/alternate-path/myvconsole.conf)"
+ fi
+}
+
# Make sure the content of kbd-model-map is the one that the tests expect
# regardless of the version installed on the distro where the testsuite is
# running on.
rm -fr "$ROOT"
fi
+ if [[ -d /etc/otherpath ]]; then
+ rm -rf /etc/otherpath
+ fi
+
restore_locale
}
(! systemd-firstboot --root="$ROOT" --root-shell=/bin/nonexistentshell)
(! systemd-firstboot --root="$ROOT" --machine-id=invalidmachineid)
(! systemd-firstboot --root="$ROOT" --timezone=Foo/Bar)
+
+mkdir -p "${ROOT}/etc/otherpath"
+mkdir -p /etc/otherpath
+echo "KEYMAP=us" >/etc/otherpath/vconsole.conf
+echo "LANG=en_US.UTF-8" >/etc/otherpath/locale.conf
+ln -s "../$(readlink /etc/localtime)" /etc/otherpath/localtime
+
+SYSTEMD_ETC_LOCALE_CONF=/etc/otherpath/locale.conf \
+SYSTEMD_ETC_VCONSOLE_CONF=/etc/otherpath/vconsole.conf \
+SYSTEMD_ETC_LOCALTIME=/etc/otherpath/localtime \
+SYSTEMD_ETC_HOSTNAME=/etc/otherpath/hostname \
+systemd-firstboot --root="$ROOT" --copy-locale --copy-keymap --copy-timezone --hostname="weirdpaths"
+
+diff "${ROOT}/etc/otherpath/locale.conf" "/etc/otherpath/locale.conf"
+diff "${ROOT}/etc/otherpath/vconsole.conf" "/etc/otherpath/vconsole.conf"
+grep -q "weirdpaths" "${ROOT}/etc/otherpath/hostname"
+
+[[ "$(readlink /etc/otherpath/localtime)" = "$(readlink "${ROOT}/etc/otherpath/localtime")" ]]
+
+SYSTEMD_ETC_LOCALE_CONF=/etc/otherpath/locale.conf \
+SYSTEMD_ETC_VCONSOLE_CONF=/etc/otherpath/vconsole.conf \
+SYSTEMD_ETC_LOCALTIME=/etc/otherpath/localtime \
+SYSTEMD_ETC_HOSTNAME=/etc/otherpath/hostname \
+systemd-firstboot --root="$ROOT" --force \
+ --hostname="weirdpaths2" \
+ --locale=no_NO.UTF-8 \
+ --keymap=no \
+ --timezone=Europe/Oslo
+
+grep -q "LANG=no_NO.UTF-8" "${ROOT}/etc/otherpath/locale.conf"
+grep -q "KEYMAP=no" "${ROOT}/etc/otherpath/vconsole.conf"
+grep -q "weirdpaths2" "${ROOT}/etc/otherpath/hostname"
+[[ "$(readlink "${ROOT}/etc/otherpath/localtime")" = "../../usr/share/zoneinfo/Europe/Oslo" ]]