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:
committed by
Yu Watanabe
parent
31ef154f15
commit
844c44a0d4
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user