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