From 5cecbae158ed05b2376a605c9c9ef71eeef81472 Mon Sep 17 00:00:00 2001 From: Anita Zhang Date: Fri, 30 Oct 2020 16:16:31 -0700 Subject: [PATCH 1/2] man: add . @@ -301,7 +301,7 @@ is nonetheless subject to the delay imposed by RandomizedDelaySec=. This is useful to catch up on missed runs of the service when the system was powered down. Note that this setting only has an effect on timers configured with OnCalendar=. Defaults to - false. + . Use systemctl clean --what=state … on the timer unit to remove the timestamp file maintained by this option from disk. In particular, use this command before uninstalling a timer @@ -317,7 +317,7 @@ from suspend, should it be suspended and if the system supports this. Note that this option will only make sure the system resumes on the appropriate times, it will not take care of suspending it again after any work that is to be done is finished. Defaults to - false. + . Note that this functionality requires privileges and is thus generally only available in the system service manager. @@ -343,7 +343,7 @@ RemainAfterElapse= is on, starting the timer a second time has no effect. However, if RemainAfterElapse= is off and the timer unit was already unloaded, it can be started again, and thus the service can be triggered multiple times. Defaults to - yes. + . From acf24a1a84e9496e3feb09449f4cc43fe67a9a64 Mon Sep 17 00:00:00 2001 From: Kristijan Gjoshev Date: Sat, 1 Feb 2020 18:27:08 +0100 Subject: [PATCH 2/2] 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