timedated: roll back state on config write failure

SetTimezone() and SetLocalRTC() updated the Context before writing
/etc/localtime or /etc/adjtime. If the write failed, D-Bus properties
showed the new value and a repeated request with the same value returned
success without retrying the file update.

Keep the write helpers Context-based, but save the old value before
installing the requested state. Restore the old Context value when the
backing file update fails.

Add TEST-45 coverage for failed alternate localtime and adjtime paths.

Follow-up for: f401e48c2d
This commit is contained in:
dongshengyuan
2026-08-19 02:29:39 +09:00
committed by Yu Watanabe
parent 31ef154f15
commit 844c44a0d4
2 changed files with 26 additions and 3 deletions
+11 -3
View File
@@ -672,6 +672,7 @@ static int property_get_ntp(
static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = ASSERT_PTR(userdata);
_cleanup_free_ char *old_zone = NULL, *zone = NULL;
int interactive, r;
const char *z;
@@ -701,13 +702,17 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
r = free_and_strdup(&c->zone, z);
if (r < 0)
return r;
zone = strdup(z);
if (!zone)
return -ENOMEM;
old_zone = TAKE_PTR(c->zone);
c->zone = TAKE_PTR(zone);
/* 1. Write new configuration file */
r = context_write_data_timezone(c);
if (r < 0) {
free_and_replace(c->zone, old_zone);
log_error_errno(r, "Failed to set time zone: %m");
return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m");
}
@@ -778,11 +783,14 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
return 1;
if (lrtc != c->local_rtc) {
bool old_local_rtc = c->local_rtc;
c->local_rtc = lrtc;
/* 1. Write new configuration file */
r = context_write_data_local_rtc(c);
if (r < 0) {
c->local_rtc = old_local_rtc;
log_error_errno(r, "Failed to set RTC to %s: %m", lrtc ? "local" : "UTC");
return sd_bus_error_set_errnof(error, r, "Failed to set RTC to %s: %m", lrtc ? "local" : "UTC");
}
+15
View File
@@ -484,6 +484,7 @@ teardown_timedated_alternate_paths() {
set +eu
rm -rf /run/systemd/system/systemd-timedated.service.d
rm -rf /run/alternate-path
systemctl daemon-reload
systemctl restart systemd-timedated
}
@@ -507,11 +508,25 @@ EOF
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)"
rm -f /run/alternate-path/mylocaltime
mkdir /run/alternate-path/mylocaltime
assert_rc 1 timedatectl --no-pager set-timezone UTC
assert_eq "$(timedatectl show -p Timezone --value)" "Europe/Kyiv"
assert_rc 1 timedatectl --no-pager set-timezone UTC
rmdir /run/alternate-path/mylocaltime
assert_rc 0 timedatectl --no-pager set-timezone UTC
assert_rc 0 timedatectl --no-pager set-timezone Europe/Kyiv
# 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)"
mkdir /run/alternate-path/myadjtime
assert_rc 1 timedatectl set-local-rtc 1
assert_in "RTC in local TZ: no" "$(timedatectl --no-pager)"
assert_rc 1 timedatectl set-local-rtc 1
rmdir /run/alternate-path/myadjtime
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