core/timer: Prevent timer looping when unit cannot start
authorMichal Koutný <mkoutny@suse.com>
Tue, 16 Jan 2018 18:22:46 +0000 (19:22 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 22 Jan 2018 16:13:00 +0000 (17:13 +0100)
When a unit job finishes early (e.g. when fork(2) fails) triggered unit goes
through states
        stopped->failed (or failed->failed),
in case a ExecStart= command fails unit passes through
        stopped->starting->failed.

The former transition doesn't result in unit active/inactive timestamp being
updated and timer (OnUnitActiveSec= or OnUnitInactiveSec=) would use an expired
timestamp triggering immediately again (repeatedly).

This patch exploits timer's last trigger timestamp to ensure the timer isn't
triggered more frequently than OnUnitActiveSec=/OnUnitInactiveSec= period.

Steps to reproduce:

0) Create sample units:

cat >~/.config/systemd/user/looper.service <<EOD
[Service]
ExecStart=/usr/bin/sleep 2
EOD

cat >~/.config/systemd/user/looper.timer <<EOD
[Timer]
AccuracySec=5
OnUnitActiveSec=5
EOD

1) systemctl --user daemon-reload

2) systemctl --user start looper.timer
   # to have first activation timestamp/sentinel
   systemctl --user start looper.service

o  Observe the service is being regularly triggered.

3) systemctl set-property user@$UID.service TasksMax=2

o  Observe the tight looping as long as the looper.service cannot be started.

Ref: #5969

src/core/timer.c

index 03935ee..133cbb9 100644 (file)
@@ -431,6 +431,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
 
                                 if (base <= 0)
                                         continue;
+                                base = MAX(base, t->last_trigger.monotonic);
 
                                 break;
 
@@ -443,6 +444,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
 
                                 if (base <= 0)
                                         continue;
+                                base = MAX(base, t->last_trigger.monotonic);
 
                                 break;