From: Valentin David Date: Mon, 10 Mar 2025 09:53:41 +0000 (+0100) Subject: Use paths specified from environment variables for /etc configuration files X-Git-Tag: v258-rc1~272 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0dc39dffbd4525e79d7a1537b5c95780ba4f9727;p=thirdparty%2Fsystemd.git 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. --- diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 87fb67502a8..ab3ac4895b1 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -294,6 +294,9 @@ All tools: 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 @@ -787,3 +790,25 @@ Tools using the Varlink protocol (such as `varlinkctl`) or sd-bus (such as `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`. diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index a3f820e3c9e..2238e86d766 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -157,13 +157,31 @@ bool is_localhost(const char *hostname) { 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; diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index 5f3930756d0..3cbf0966ea1 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -37,6 +37,9 @@ static inline bool is_dns_proxy_stub_hostname(const char *hostname) { 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); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 1a86f7fdee5..55931a25461 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1607,7 +1607,7 @@ int get_timezone(char **ret) { 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"); @@ -1623,6 +1623,15 @@ int get_timezone(char **ret) { 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, diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 90c17c39b17..d31e62d18ae 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -168,6 +168,7 @@ bool clock_supported(clockid_t clock); 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); diff --git a/src/core/manager.c b/src/core/manager.c index 53c62afaae6..000f19d6f68 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -420,7 +420,7 @@ static int manager_read_timezone_stat(Manager *m) { 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; @@ -457,14 +457,20 @@ static int manager_setup_timezone_change(Manager *m) { * 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) diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index a7654ca6159..4462086e782 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -31,6 +31,7 @@ #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" @@ -408,7 +409,7 @@ static int process_locale(int rfd) { 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) @@ -425,7 +426,7 @@ static int process_locale(int rfd) { 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"); @@ -520,7 +521,7 @@ static int process_keymap(int rfd) { 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) @@ -537,7 +538,7 @@ static int process_keymap(int rfd) { 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"); @@ -617,13 +618,13 @@ static int prompt_timezone(int rfd) { 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) @@ -642,7 +643,7 @@ static int process_timezone(int rfd) { 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"); @@ -663,9 +664,12 @@ static int process_timezone(int rfd) { 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"); @@ -712,7 +716,7 @@ static int process_hostname(int rfd) { 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) @@ -1228,12 +1232,12 @@ static int process_reset(int rfd) { 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; diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 789ab2be78a..d0e4ebeeda0 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -127,7 +127,7 @@ static void context_read_etc_hostname(Context *c) { 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; @@ -160,7 +160,7 @@ static void context_read_machine_info(Context *c) { 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; @@ -175,7 +175,7 @@ static void context_read_machine_info(Context *c) { (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], @@ -806,14 +806,14 @@ static int context_write_data_static_hostname(Context *c) { 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; @@ -839,7 +839,7 @@ static int context_write_data_machine_info(Context *c) { * 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; @@ -852,7 +852,7 @@ static int context_write_data_machine_info(Context *c) { } 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); @@ -861,8 +861,8 @@ static int context_write_data_machine_info(Context *c) { r = write_env_file( AT_FDCWD, - "/etc/machine-info", - /* headers= */ NULL, + etc_machine_info(), + /* headers= */NULL, l, WRITE_ENV_FILE_LABEL); if (r < 0) @@ -1687,7 +1687,7 @@ static int build_describe_response(Context *c, bool privileged, sd_json_variant (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, diff --git a/src/locale/localed-util.c b/src/locale/localed-util.c index 0a96e7a3c9a..72f317f0b0c 100644 --- a/src/locale/localed-util.c +++ b/src/locale/localed-util.c @@ -152,7 +152,7 @@ int vconsole_read_data(Context *c, sd_bus_message *m) { 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); @@ -174,7 +174,7 @@ int vconsole_read_data(Context *c, sd_bus_message *m) { 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, @@ -298,7 +298,7 @@ int vconsole_write_data(Context *c) { 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; @@ -307,7 +307,7 @@ int vconsole_write_data(Context *c) { 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) {}; @@ -318,7 +318,7 @@ int vconsole_write_data(Context *c) { 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; diff --git a/src/shared/env-file-label.c b/src/shared/env-file-label.c new file mode 100644 index 00000000000..c0c9668e9fe --- /dev/null +++ b/src/shared/env-file-label.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#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; +} diff --git a/src/shared/hostname-setup.c b/src/shared/hostname-setup.c index bb942838b7e..0d21e0482d1 100644 --- a/src/shared/hostname-setup.c +++ b/src/shared/hostname-setup.c @@ -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) diff --git a/src/shared/locale-setup.c b/src/shared/locale-setup.c index c59bbe2abce..6b66c154436 100644 --- a/src/shared/locale-setup.c +++ b/src/shared/locale-setup.c @@ -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; +} diff --git a/src/shared/locale-setup.h b/src/shared/locale-setup.h index ae32219aa4b..fec9046bb7b 100644 --- a/src/shared/locale-setup.h +++ b/src/shared/locale-setup.h @@ -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); diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index 3a75e6a33ed..cafd3494cb9 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -292,22 +292,32 @@ static int context_write_data_timezone(Context *c) { 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) { @@ -316,7 +326,7 @@ 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; @@ -368,7 +378,7 @@ static int context_write_data_local_rtc(Context *c) { *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; @@ -380,7 +390,7 @@ static int context_write_data_local_rtc(Context *c) { 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) { diff --git a/test/units/TEST-30-ONCLOCKCHANGE.sh b/test/units/TEST-30-ONCLOCKCHANGE.sh index 235b278f601..bf796a2c002 100755 --- a/test/units/TEST-30-ONCLOCKCHANGE.sh +++ b/test/units/TEST-30-ONCLOCKCHANGE.sh @@ -33,6 +33,34 @@ timedatectl set-time "$future_time" while test ! -f /tmp/clock-changed ; do sleep .5 ; done +mkdir -p /etc/alternate-path +rm -f /etc/alternate-path/localtime + +cat </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 </run/systemd/system/systemd-timedated.service.d/override.conf <&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 diff --git a/test/units/TEST-71-HOSTNAME.sh b/test/units/TEST-71-HOSTNAME.sh index f844ccfcdb0..d1eaa8106e3 100755 --- a/test/units/TEST-71-HOSTNAME.sh +++ b/test/units/TEST-71-HOSTNAME.sh @@ -278,6 +278,45 @@ test_wildcard() { 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 </run/systemd/system/systemd-localed.service.d/override.conf </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" ]]