d0811f
From de8f6fb530db706d14e9ece52b2acfd77c823133 Mon Sep 17 00:00:00 2001
d0811f
From: Kristijan Gjoshev <crypter@mail.com>
d0811f
Date: Sat, 1 Feb 2020 18:27:08 +0100
d0811f
Subject: [PATCH 3/3] timer: add new feature FixedRandomDelay=
d0811f
d0811f
FixedRandomDelay=yes will use
d0811f
`siphash24(sd_id128_get_machine() || MANAGER_IS_SYSTEM(m) || getuid() || u->id)`,
d0811f
where || is concatenation, instead of a random number to choose a value between
d0811f
0 and RandomizedDelaySec= as the timer delay.
d0811f
This essentially sets up a fixed, but seemingly random, offset for each timer
d0811f
iteration rather than having a random offset recalculated each time it fires.
d0811f
d0811f
Closes #10355
d0811f
d0811f
Co-author: Anita Zhang <the.anitazha@gmail.com>
d0811f
---
d0811f
 docs/TRANSIENT-SETTINGS.md                    |  1 +
d0811f
 man/org.freedesktop.systemd1.xml              |  6 ++++
d0811f
 man/systemd.timer.xml                         | 12 +++++++
d0811f
 src/core/dbus-timer.c                         |  4 +++
d0811f
 src/core/timer.c                              | 34 ++++++++++++++++++-
d0811f
 src/core/timer.h                              |  1 +
d0811f
 src/shared/bus-unit-util.c                    |  3 +-
d0811f
 test/fuzz/fuzz-unit-file/directives.service   |  1 +
d0811f
 .../systemd-tmpfiles-clean.timer              |  1 +
d0811f
 9 files changed, 61 insertions(+), 2 deletions(-)
d0811f
d0811f
diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md
d0811f
index 19944d08b8..f4639b2e87 100644
d0811f
--- a/docs/TRANSIENT-SETTINGS.md
d0811f
+++ b/docs/TRANSIENT-SETTINGS.md
d0811f
@@ -368,6 +368,7 @@ Most timer unit settings are available to transient units.
d0811f
 ✓ RemainAfterElapse=
d0811f
 ✓ AccuracySec=
d0811f
 ✓ RandomizedDelaySec=
d0811f
+✓ FixedRandomDelay=
d0811f
   Unit=
d0811f
 ```
d0811f
 
d0811f
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
d0811f
index 6b16ae16da..ab4cbaa2fb 100644
d0811f
--- a/man/org.freedesktop.systemd1.xml
d0811f
+++ b/man/org.freedesktop.systemd1.xml
d0811f
@@ -6866,6 +6866,8 @@ node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer {
d0811f
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
d0811f
       readonly t RandomizedDelayUSec = ...;
d0811f
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
d0811f
+      readonly b FixedRandomDelay = ...;
d0811f
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
d0811f
       readonly b Persistent = ...;
d0811f
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
d0811f
       readonly b WakeSystem = ...;
d0811f
@@ -6891,6 +6893,8 @@ node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer {
d0811f
 
d0811f
     
d0811f
 
d0811f
+    
d0811f
+
d0811f
     
d0811f
 
d0811f
     
d0811f
@@ -6931,6 +6935,8 @@ node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer {
d0811f
 
d0811f
     <variablelist class="dbus-property" generated="True" extra-ref="RandomizedDelayUSec"/>
d0811f
 
d0811f
+    <variablelist class="dbus-property" generated="True" extra-ref="FixedRandomDelay"/>
d0811f
+
d0811f
     <variablelist class="dbus-property" generated="True" extra-ref="Persistent"/>
d0811f
 
d0811f
     <variablelist class="dbus-property" generated="True" extra-ref="WakeSystem"/>
d0811f
diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml
d0811f
index 5822402712..6f731e2311 100644
d0811f
--- a/man/systemd.timer.xml
d0811f
+++ b/man/systemd.timer.xml
d0811f
@@ -268,6 +268,18 @@
d0811f
         <varname>AccuracySec=1us</varname>.</para></listitem>
d0811f
       </varlistentry>
d0811f
 
d0811f
+      <varlistentry>
d0811f
+        <term><varname>FixedRandomDelay=</varname></term>
d0811f
+
d0811f
+        <listitem><para>Takes a boolean argument. If true, some amount of time between 0 and
d0811f
+        <varname>RandomizedDelaySec=</varname> is chosen and added as the delay for each timer iteration. As this
d0811f
+        delay will not be recalculated on each run, this effectively creates a fixed offset for each iteration.
d0811f
+        The distribution between 0 and <varname>RandomizedDelaySec=</varname> is deterministic and based on
d0811f
+        a combination of the machine ID, whether the timer is run by the user/system manager, the service manager's
d0811f
+        user ID, and the timer's unit name. Has no effect if
d0811f
+        <varname>RandomizedDelaySec=</varname> is set to 0. Defaults to <option>false</option>.</para></listitem>
d0811f
+      </varlistentry>
d0811f
+
d0811f
       <varlistentry>
d0811f
         <term><varname>OnClockChange=</varname></term>
d0811f
         <term><varname>OnTimezoneChange=</varname></term>
d0811f
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
d0811f
index da35fa8678..ee54ba8772 100644
d0811f
--- a/src/core/dbus-timer.c
d0811f
+++ b/src/core/dbus-timer.c
d0811f
@@ -131,6 +131,7 @@ const sd_bus_vtable bus_timer_vtable[] = {
d0811f
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
d0811f
         SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
d0811f
         SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST),
d0811f
+        SD_BUS_PROPERTY("FixedRandomDelay", "b", bus_property_get_bool, offsetof(Timer, fixed_random_delay), SD_BUS_VTABLE_PROPERTY_CONST),
d0811f
         SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
d0811f
         SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
d0811f
         SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST),
d0811f
@@ -232,6 +233,9 @@ static int bus_timer_set_transient_property(
d0811f
         if (streq(name, "RandomizedDelayUSec"))
d0811f
                 return bus_set_transient_usec(u, name, &t->random_usec, message, flags, error);
d0811f
 
d0811f
+        if (streq(name, "FixedRandomDelay"))
d0811f
+                return bus_set_transient_bool(u, name, &t->fixed_random_delay, message, flags, error);
d0811f
+
d0811f
         if (streq(name, "WakeSystem"))
d0811f
                 return bus_set_transient_bool(u, name, &t->wake_system, message, flags, error);
d0811f
 
d0811f
diff --git a/src/core/timer.c b/src/core/timer.c
d0811f
index 03a9c14f76..b2c5e26f63 100644
d0811f
--- a/src/core/timer.c
d0811f
+++ b/src/core/timer.c
d0811f
@@ -169,6 +169,36 @@ static int timer_setup_persistent(Timer *t) {
d0811f
         return 0;
d0811f
 }
d0811f
 
d0811f
+static uint64_t timer_get_fixed_delay_hash(Timer *t) {
d0811f
+        static const uint8_t hash_key[] = {
d0811f
+                0x51, 0x0a, 0xdb, 0x76, 0x29, 0x51, 0x42, 0xc2,
d0811f
+                0x80, 0x35, 0xea, 0xe6, 0x8e, 0x3a, 0x37, 0xbd
d0811f
+        };
d0811f
+
d0811f
+        struct siphash state;
d0811f
+        sd_id128_t machine_id;
d0811f
+        uid_t uid;
d0811f
+        int r;
d0811f
+
d0811f
+        assert(t);
d0811f
+
d0811f
+        uid = getuid();
d0811f
+        r = sd_id128_get_machine(&machine_id);
d0811f
+        if (r < 0) {
d0811f
+                log_unit_debug_errno(UNIT(t), r,
d0811f
+                                     "Failed to get machine ID for the fixed delay calculation, proceeding with 0: %m");
d0811f
+                machine_id = SD_ID128_NULL;
d0811f
+        }
d0811f
+
d0811f
+        siphash24_init(&state, hash_key);
d0811f
+        siphash24_compress(&machine_id, sizeof(sd_id128_t), &state);
d0811f
+        siphash24_compress_boolean(MANAGER_IS_SYSTEM(UNIT(t)->manager), &state);
d0811f
+        siphash24_compress(&uid, sizeof(uid_t), &state);
d0811f
+        siphash24_compress_string(UNIT(t)->id, &state);
d0811f
+
d0811f
+        return siphash24_finalize(&state);
d0811f
+}
d0811f
+
d0811f
 static int timer_load(Unit *u) {
d0811f
         Timer *t = TIMER(u);
d0811f
         int r;
d0811f
@@ -215,6 +245,7 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
d0811f
                 "%sWakeSystem: %s\n"
d0811f
                 "%sAccuracy: %s\n"
d0811f
                 "%sRemainAfterElapse: %s\n"
d0811f
+                "%sFixedRandomDelay: %s\n"
d0811f
                 "%sOnClockChange: %s\n"
d0811f
                 "%sOnTimeZoneChange: %s\n",
d0811f
                 prefix, timer_state_to_string(t->state),
d0811f
@@ -224,6 +255,7 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
d0811f
                 prefix, yes_no(t->wake_system),
d0811f
                 prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1),
d0811f
                 prefix, yes_no(t->remain_after_elapse),
d0811f
+                prefix, yes_no(t->fixed_random_delay),
d0811f
                 prefix, yes_no(t->on_clock_change),
d0811f
                 prefix, yes_no(t->on_timezone_change));
d0811f
 
d0811f
@@ -332,7 +364,7 @@ static void add_random(Timer *t, usec_t *v) {
d0811f
         if (*v == USEC_INFINITY)
d0811f
                 return;
d0811f
 
d0811f
-        add = random_u64() % t->random_usec;
d0811f
+        add = (t->fixed_random_delay ? timer_get_fixed_delay_hash(t) : random_u64()) % t->random_usec;
d0811f
 
d0811f
         if (*v + add < *v) /* overflow */
d0811f
                 *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
d0811f
diff --git a/src/core/timer.h b/src/core/timer.h
d0811f
index ab66a201ad..ce4046a210 100644
d0811f
--- a/src/core/timer.h
d0811f
+++ b/src/core/timer.h
d0811f
@@ -59,6 +59,7 @@ struct Timer {
d0811f
         bool remain_after_elapse;
d0811f
         bool on_clock_change;
d0811f
         bool on_timezone_change;
d0811f
+        bool fixed_random_delay;
d0811f
 
d0811f
         char *stamp_path;
d0811f
 };
d0811f
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
d0811f
index f2652ed9a5..68de4a2ed1 100644
d0811f
--- a/src/shared/bus-unit-util.c
d0811f
+++ b/src/shared/bus-unit-util.c
d0811f
@@ -1779,7 +1779,8 @@ static int bus_append_timer_property(sd_bus_message *m, const char *field, const
d0811f
                               "RemainAfterElapse",
d0811f
                               "Persistent",
d0811f
                               "OnTimezoneChange",
d0811f
-                              "OnClockChange"))
d0811f
+                              "OnClockChange",
d0811f
+                              "FixedRandomDelay"))
d0811f
                 return bus_append_parse_boolean(m, field, eq);
d0811f
 
d0811f
         if (STR_IN_SET(field, "AccuracySec",
d0811f
diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
d0811f
index dbff9ab2cc..95304ea0c6 100644
d0811f
--- a/test/fuzz/fuzz-unit-file/directives.service
d0811f
+++ b/test/fuzz/fuzz-unit-file/directives.service
d0811f
@@ -175,6 +175,7 @@ PipeSize=
d0811f
 Priority=
d0811f
 PropagatesReloadTo=
d0811f
 RandomizedDelaySec=
d0811f
+FixedRandomDelay=
d0811f
 RebootArgument=
d0811f
 ReceiveBuffer=
d0811f
 RefuseManualStart=
d0811f
diff --git a/test/fuzz/fuzz-unit-file/systemd-tmpfiles-clean.timer b/test/fuzz/fuzz-unit-file/systemd-tmpfiles-clean.timer
d0811f
index 7db361cd69..64b8808adc 100644
d0811f
--- a/test/fuzz/fuzz-unit-file/systemd-tmpfiles-clean.timer
d0811f
+++ b/test/fuzz/fuzz-unit-file/systemd-tmpfiles-clean.timer
d0811f
@@ -32,6 +32,7 @@ OnCalendar=Fri 2012-11-23 11:12:13
d0811f
 Persistent=true
d0811f
 AccuracySec=24h
d0811f
 RandomizedDelaySec=234234234
d0811f
+FixedRandomDelay=true
d0811f
 
d0811f
 Persistent=no
d0811f
 Unit=foo.service
d0811f
-- 
d0811f
2.24.1
d0811f