b677e7
From 3d4280d0a487109f8f648147083baf573e4418a3 Mon Sep 17 00:00:00 2001
b677e7
From: =?UTF-8?q?Michal=20Koutn=C3=BD?= <mkoutny@suse.com>
b677e7
Date: Fri, 2 Nov 2018 20:56:08 +0100
b677e7
Subject: [PATCH] core: Detect initial timer state from serialized data
b677e7
b677e7
We keep a mark whether a single-shot timer was triggered in the caller's
b677e7
variable initial. When such a timer elapses while we are
b677e7
serializing/deserializing the inner state, we consider the timer
b677e7
incorrectly as elapsed and don't trigger it later.
b677e7
b677e7
This patch exploits last_trigger timestamp that we already serialize,
b677e7
hence we can eliminate the argument initial completely.
b677e7
b677e7
A reproducer for OnBootSec= timers:
b677e7
        cat >repro.c <
b677e7
        /*
b677e7
         * Compile:	gcc repro.c -o repro
b677e7
         * Run:		./repro
b677e7
         */
b677e7
        #include <errno.h>
b677e7
        #include <fcntl.h>
b677e7
        #include <stdio.h>
b677e7
        #include <stdlib.h>
b677e7
        #include <sys/stat.h>
b677e7
        #include <sys/types.h>
b677e7
        #include <time.h>
b677e7
        #include <unistd.h>
b677e7
b677e7
        int main(int argc, char *argv[]) {
b677e7
        	char command[1024];
b677e7
        	int pause;
b677e7
b677e7
        	struct timespec now;
b677e7
b677e7
        	while (1) {
b677e7
        		usleep(rand() % 200000); // prevent periodic repeats
b677e7
               		clock_gettime(CLOCK_MONOTONIC, &now;;
b677e7
        		printf("%i\n", now.tv_sec);
b677e7
b677e7
        		system("rm -f $PWD/mark");
b677e7
        		snprintf(command, 1024, "systemd-run --user --on-boot=%i --timer-property=AccuracySec=100ms "
b677e7
        					"touch $PWD/mark", now.tv_sec + 1);
b677e7
        		system(command);
b677e7
        		system("systemctl --user list-timers");
b677e7
        		pause = (1000000000 - now.tv_nsec)/1000 - 70000; // fiddle to hit the middle of reloading
b677e7
        		usleep(pause > 0 ? pause : 0);
b677e7
        		system("systemctl --user daemon-reload");
b677e7
        		sync();
b677e7
        		sleep(2);
b677e7
        		if (open("./mark", 0) < 0)
b677e7
        			if (errno == ENOENT) {
b677e7
        				printf("mark file does not exist\n");
b677e7
        				break;
b677e7
        			}
b677e7
        	}
b677e7
b677e7
        	return 0;
b677e7
        }
b677e7
        EOD
b677e7
b677e7
(cherry picked from commit aa1f95d2647197eca84c33a0f10adaeada08467d)
b677e7
b677e7
Resolves: #1899402
b677e7
---
b677e7
 src/core/timer.c | 19 ++++++++++---------
b677e7
 1 file changed, 10 insertions(+), 9 deletions(-)
b677e7
b677e7
diff --git a/src/core/timer.c b/src/core/timer.c
b677e7
index ef240a6f19..1718ffc5a5 100644
b677e7
--- a/src/core/timer.c
b677e7
+++ b/src/core/timer.c
b677e7
@@ -262,7 +262,7 @@ static void timer_set_state(Timer *t, TimerState state) {
b677e7
         unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
b677e7
 }
b677e7
 
b677e7
-static void timer_enter_waiting(Timer *t, bool initial, bool time_change);
b677e7
+static void timer_enter_waiting(Timer *t, bool time_change);
b677e7
 
b677e7
 static int timer_coldplug(Unit *u) {
b677e7
         Timer *t = TIMER(u);
b677e7
@@ -274,7 +274,7 @@ static int timer_coldplug(Unit *u) {
b677e7
                 return 0;
b677e7
 
b677e7
         if (t->deserialized_state == TIMER_WAITING)
b677e7
-                timer_enter_waiting(t, false, false);
b677e7
+                timer_enter_waiting(t, false);
b677e7
         else
b677e7
                 timer_set_state(t, t->deserialized_state);
b677e7
 
b677e7
@@ -334,7 +334,7 @@ static void add_random(Timer *t, usec_t *v) {
b677e7
         log_unit_debug(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
b677e7
 }
b677e7
 
b677e7
-static void timer_enter_waiting(Timer *t, bool initial, bool time_change) {
b677e7
+static void timer_enter_waiting(Timer *t, bool time_change) {
b677e7
         bool found_monotonic = false, found_realtime = false;
b677e7
         bool leave_around = false;
b677e7
         triple_timestamp ts;
b677e7
@@ -444,7 +444,8 @@ static void timer_enter_waiting(Timer *t, bool initial, bool time_change) {
b677e7
 
b677e7
                         v->next_elapse = usec_add(usec_shift_clock(base, CLOCK_MONOTONIC, TIMER_MONOTONIC_CLOCK(t)), v->value);
b677e7
 
b677e7
-                        if (!initial && !time_change &&
b677e7
+                        if (dual_timestamp_is_set(&t->last_trigger) &&
b677e7
+                            !time_change &&
b677e7
                             v->next_elapse < triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)) &&
b677e7
                             IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
b677e7
                                 /* This is a one time trigger, disable it now */
b677e7
@@ -642,7 +643,7 @@ static int timer_start(Unit *u) {
b677e7
         }
b677e7
 
b677e7
         t->result = TIMER_SUCCESS;
b677e7
-        timer_enter_waiting(t, true, false);
b677e7
+        timer_enter_waiting(t, false);
b677e7
         return 1;
b677e7
 }
b677e7
 
b677e7
@@ -764,14 +765,14 @@ static void timer_trigger_notify(Unit *u, Unit *other) {
b677e7
         case TIMER_ELAPSED:
b677e7
 
b677e7
                 /* Recalculate sleep time */
b677e7
-                timer_enter_waiting(t, false, false);
b677e7
+                timer_enter_waiting(t, false);
b677e7
                 break;
b677e7
 
b677e7
         case TIMER_RUNNING:
b677e7
 
b677e7
                 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
b677e7
                         log_unit_debug(UNIT(t), "Got notified about unit deactivation.");
b677e7
-                        timer_enter_waiting(t, false, false);
b677e7
+                        timer_enter_waiting(t, false);
b677e7
                 }
b677e7
                 break;
b677e7
 
b677e7
@@ -813,7 +814,7 @@ static void timer_time_change(Unit *u) {
b677e7
                 t->last_trigger.realtime = ts;
b677e7
 
b677e7
         log_unit_debug(u, "Time change, recalculating next elapse.");
b677e7
-        timer_enter_waiting(t, false, true);
b677e7
+        timer_enter_waiting(t, true);
b677e7
 }
b677e7
 
b677e7
 static void timer_timezone_change(Unit *u) {
b677e7
@@ -825,7 +826,7 @@ static void timer_timezone_change(Unit *u) {
b677e7
                 return;
b677e7
 
b677e7
         log_unit_debug(u, "Timezone change, recalculating next elapse.");
b677e7
-        timer_enter_waiting(t, false, false);
b677e7
+        timer_enter_waiting(t, false);
b677e7
 }
b677e7
 
b677e7
 static const char* const timer_base_table[_TIMER_BASE_MAX] = {