Pablo Greco 48fc63
From d7eac3e0ae20510e2737dc4e23975391fef5ece3 Mon Sep 17 00:00:00 2001
Pablo Greco 48fc63
From: =?UTF-8?q?Michal=20Koutn=C3=BD?= <mkoutny@suse.com>
Pablo Greco 48fc63
Date: Tue, 16 Jan 2018 19:22:46 +0100
Pablo Greco 48fc63
Subject: [PATCH] core/timer: Prevent timer looping when unit cannot start
Pablo Greco 48fc63
Pablo Greco 48fc63
When a unit job finishes early (e.g. when fork(2) fails) triggered unit goes
Pablo Greco 48fc63
through states
Pablo Greco 48fc63
        stopped->failed (or failed->failed),
Pablo Greco 48fc63
in case a ExecStart= command fails unit passes through
Pablo Greco 48fc63
        stopped->starting->failed.
Pablo Greco 48fc63
Pablo Greco 48fc63
The former transition doesn't result in unit active/inactive timestamp being
Pablo Greco 48fc63
updated and timer (OnUnitActiveSec= or OnUnitInactiveSec=) would use an expired
Pablo Greco 48fc63
timestamp triggering immediately again (repeatedly).
Pablo Greco 48fc63
Pablo Greco 48fc63
This patch exploits timer's last trigger timestamp to ensure the timer isn't
Pablo Greco 48fc63
triggered more frequently than OnUnitActiveSec=/OnUnitInactiveSec= period.
Pablo Greco 48fc63
Pablo Greco 48fc63
Steps to reproduce:
Pablo Greco 48fc63
Pablo Greco 48fc63
0) Create sample units:
Pablo Greco 48fc63
Pablo Greco 48fc63
cat >~/.config/systemd/user/looper.service <
Pablo Greco 48fc63
[Service]
Pablo Greco 48fc63
ExecStart=/usr/bin/sleep 2
Pablo Greco 48fc63
EOD
Pablo Greco 48fc63
Pablo Greco 48fc63
cat >~/.config/systemd/user/looper.timer <
Pablo Greco 48fc63
[Timer]
Pablo Greco 48fc63
AccuracySec=5
Pablo Greco 48fc63
OnUnitActiveSec=5
Pablo Greco 48fc63
EOD
Pablo Greco 48fc63
Pablo Greco 48fc63
1) systemctl --user daemon-reload
Pablo Greco 48fc63
Pablo Greco 48fc63
2) systemctl --user start looper.timer
Pablo Greco 48fc63
   # to have first activation timestamp/sentinel
Pablo Greco 48fc63
   systemctl --user start looper.service
Pablo Greco 48fc63
Pablo Greco 48fc63
o  Observe the service is being regularly triggered.
Pablo Greco 48fc63
Pablo Greco 48fc63
3) systemctl set-property user@$UID.service TasksMax=2
Pablo Greco 48fc63
Pablo Greco 48fc63
o  Observe the tight looping as long as the looper.service cannot be started.
Pablo Greco 48fc63
Pablo Greco 48fc63
Ref: #5969
Pablo Greco 48fc63
Pablo Greco 48fc63
(cherry picked from commit 204d140c4def364c47d36226e4514a7e077fa196)
Pablo Greco 48fc63
(cherry picked from commit fe81f6f734ee46a4877df6dda6e31cdc24c00a3c)
Pablo Greco 48fc63
Pablo Greco 48fc63
Resolves: #1729230
Pablo Greco 48fc63
---
Pablo Greco 48fc63
 src/core/timer.c | 2 ++
Pablo Greco 48fc63
 1 file changed, 2 insertions(+)
Pablo Greco 48fc63
Pablo Greco 48fc63
diff --git a/src/core/timer.c b/src/core/timer.c
Pablo Greco 48fc63
index d32b007c7c..1d4868643a 100644
Pablo Greco 48fc63
--- a/src/core/timer.c
Pablo Greco 48fc63
+++ b/src/core/timer.c
Pablo Greco 48fc63
@@ -416,6 +416,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
Pablo Greco 48fc63
 
Pablo Greco 48fc63
                                 if (base <= 0)
Pablo Greco 48fc63
                                         continue;
Pablo Greco 48fc63
+                                base = MAX(base, t->last_trigger.monotonic);
Pablo Greco 48fc63
 
Pablo Greco 48fc63
                                 break;
Pablo Greco 48fc63
 
Pablo Greco 48fc63
@@ -428,6 +429,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
Pablo Greco 48fc63
 
Pablo Greco 48fc63
                                 if (base <= 0)
Pablo Greco 48fc63
                                         continue;
Pablo Greco 48fc63
+                                base = MAX(base, t->last_trigger.monotonic);
Pablo Greco 48fc63
 
Pablo Greco 48fc63
                                 break;
Pablo Greco 48fc63