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 c231b3c961..78fd0b3378 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 701c44c475..7c1a4917e5 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= @@ -276,7 +288,7 @@ when the system clock (CLOCK_REALTIME) jumps relative to the monotonic clock (CLOCK_MONOTONIC), or when the local system timezone is modified. These options can be used alone or in combination with other timer expressions (see above) within the same timer - unit. These options default to false. + unit. These options default to . @@ -301,7 +313,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 +329,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 +355,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. + . diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index 9720237140..8e69c17327 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 886655a78e..651f18b5a8 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 46d9b9b262..14fa317064 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 fe3a41f438..2bab2299fb 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 d3d17dc535..5bf91b9f4c 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