summaryrefslogtreecommitdiffstats
path: root/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch')
-rw-r--r--debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch105
1 files changed, 105 insertions, 0 deletions
diff --git a/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch b/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch
new file mode 100644
index 0000000..9503c1e
--- /dev/null
+++ b/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch
@@ -0,0 +1,105 @@
+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
+Date: Mon, 22 Mar 2021 12:51:47 +0100
+Subject: shared/calendarspec: when mktime() moves us backwards, jump forward
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+When trying to calculate the next firing of 'Sun *-*-* 01:00:00', we'd fall
+into an infinite loop, because mktime() moves us "backwards":
+
+Before this patch:
+tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00
+tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00
+tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00
+...
+
+We rely on mktime() normalizing the time. The man page does not say that it'll
+move the time forward, but our algorithm relies on this. So let's catch this
+case explicitly.
+
+With this patch:
+$ TZ=Europe/Dublin faketime 2021-03-21 build/systemd-analyze calendar --iterations=5 'Sun *-*-* 01:00:00'
+Normalized form: Sun *-*-* 01:00:00
+ Next elapse: Sun 2021-03-21 01:00:00 GMT
+ (in UTC): Sun 2021-03-21 01:00:00 UTC
+ From now: 59min left
+ Iter. #2: Sun 2021-04-04 01:00:00 IST
+ (in UTC): Sun 2021-04-04 00:00:00 UTC
+ From now: 1 weeks 6 days left <---- note the 2 week jump here
+ Iter. #3: Sun 2021-04-11 01:00:00 IST
+ (in UTC): Sun 2021-04-11 00:00:00 UTC
+ From now: 2 weeks 6 days left
+ Iter. #4: Sun 2021-04-18 01:00:00 IST
+ (in UTC): Sun 2021-04-18 00:00:00 UTC
+ From now: 3 weeks 6 days left
+ Iter. #5: Sun 2021-04-25 01:00:00 IST
+ (in UTC): Sun 2021-04-25 00:00:00 UTC
+ From now: 1 months 4 days left
+
+Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1941335.
+
+(cherry picked from commit 129cb6e249bef30dc33e08f98f0b27a6de976f6f)
+---
+ src/shared/calendarspec.c | 19 +++++++++++--------
+ src/test/test-calendarspec.c | 3 +++
+ test/test-functions | 1 +
+ 3 files changed, 15 insertions(+), 8 deletions(-)
+
+diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c
+index 80acc57..c8d97c3 100644
+--- a/src/shared/calendarspec.c
++++ b/src/shared/calendarspec.c
+@@ -1185,15 +1185,18 @@ static int tm_within_bounds(struct tm *tm, bool utc) {
+ return negative_errno();
+
+ /* Did any normalization take place? If so, it was out of bounds before */
+- bool good = t.tm_year == tm->tm_year &&
+- t.tm_mon == tm->tm_mon &&
+- t.tm_mday == tm->tm_mday &&
+- t.tm_hour == tm->tm_hour &&
+- t.tm_min == tm->tm_min &&
+- t.tm_sec == tm->tm_sec;
+- if (!good)
++ int cmp = CMP(t.tm_year, tm->tm_year) ?:
++ CMP(t.tm_mon, tm->tm_mon) ?:
++ CMP(t.tm_mday, tm->tm_mday) ?:
++ CMP(t.tm_hour, tm->tm_hour) ?:
++ CMP(t.tm_min, tm->tm_min) ?:
++ CMP(t.tm_sec, tm->tm_sec);
++
++ if (cmp < 0)
++ return -EDEADLK; /* Refuse to go backward */
++ if (cmp > 0)
+ *tm = t;
+- return good;
++ return cmp == 0;
+ }
+
+ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
+diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c
+index e0b7f22..1b04186 100644
+--- a/src/test/test-calendarspec.c
++++ b/src/test/test-calendarspec.c
+@@ -218,6 +218,9 @@ int main(int argc, char* argv[]) {
+ // Confirm that timezones in the Spec work regardless of current timezone
+ test_next("2017-09-09 20:42:00 Pacific/Auckland", "", 12345, 1504946520000000);
+ test_next("2017-09-09 20:42:00 Pacific/Auckland", "EET", 12345, 1504946520000000);
++ /* Check that we don't start looping if mktime() moves us backwards */
++ test_next("Sun *-*-* 01:00:00 Europe/Dublin", "", 1616412478000000, 1617494400000000);
++ test_next("Sun *-*-* 01:00:00 Europe/Dublin", "IST", 1616412478000000, 1617494400000000);
+
+ assert_se(calendar_spec_from_string("test", &c) < 0);
+ assert_se(calendar_spec_from_string(" utc", &c) < 0);
+diff --git a/test/test-functions b/test/test-functions
+index 52b52bf..beaf4fa 100644
+--- a/test/test-functions
++++ b/test/test-functions
+@@ -1120,6 +1120,7 @@ install_zoneinfo() {
+ inst_any /usr/share/zoneinfo/Asia/Vladivostok
+ inst_any /usr/share/zoneinfo/Australia/Sydney
+ inst_any /usr/share/zoneinfo/Europe/Berlin
++ inst_any /usr/share/zoneinfo/Europe/Dublin
+ inst_any /usr/share/zoneinfo/Europe/Kiev
+ inst_any /usr/share/zoneinfo/Pacific/Auckland
+ inst_any /usr/share/zoneinfo/Pacific/Honolulu