diff options
-rw-r--r-- | docs/TRANSIENT-SETTINGS.md | 1 | ||||
-rw-r--r-- | man/org.freedesktop.systemd1.xml | 10 | ||||
-rw-r--r-- | man/systemd.timer.xml | 19 | ||||
-rw-r--r-- | src/core/dbus-timer.c | 4 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.in | 1 | ||||
-rw-r--r-- | src/core/timer.c | 23 | ||||
-rw-r--r-- | src/core/timer.h | 1 | ||||
-rw-r--r-- | src/shared/bus-unit-util.c | 3 | ||||
-rw-r--r-- | test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.service | 6 | ||||
-rw-r--r-- | test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.timer | 10 | ||||
-rw-r--r-- | test/TEST-74-AUX-UTILS/meson.build | 2 | ||||
-rw-r--r-- | test/fuzz/fuzz-unit-file/directives-all.service | 1 | ||||
-rw-r--r-- | test/fuzz/fuzz-unit-file/tmpfiles-clean.timer | 1 | ||||
-rwxr-xr-x | test/units/TEST-74-AUX-UTILS.defer_reactivation.sh | 23 |
14 files changed, 97 insertions, 8 deletions
diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md index 15f1cbc47c..e219131ce6 100644 --- a/docs/TRANSIENT-SETTINGS.md +++ b/docs/TRANSIENT-SETTINGS.md @@ -387,6 +387,7 @@ Most timer unit settings are available to transient units. ✓ AccuracySec= ✓ RandomizedDelaySec= ✓ FixedRandomDelay= +✓ DeferReactivation= Unit= ``` diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index ccc3e25fbc..1e34ddbc85 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -8804,6 +8804,8 @@ node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer { readonly b WakeSystem = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly b RemainAfterElapse = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly b DeferReactivation = ...; }; interface org.freedesktop.DBus.Peer { ... }; interface org.freedesktop.DBus.Introspectable { ... }; @@ -8832,6 +8834,8 @@ node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer { <!--property RemainAfterElapse is not documented!--> + <!--property DeferReactivation is not documented!--> + <!--Autogenerated cross-references for systemd.directives, do not edit--> <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/> @@ -8874,6 +8878,8 @@ node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer { <variablelist class="dbus-property" generated="True" extra-ref="RemainAfterElapse"/> + <variablelist class="dbus-property" generated="True" extra-ref="DeferReactivation"/> + <!--End of Autogenerated section--> <refsect2> @@ -12386,5 +12392,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ <title>Job Objects</title> <para><varname>ActivationDetails</varname> was added in version 252.</para> </refsect2> + <refsect2> + <title>Timer Objects</title> + <para><varname>DeferReactivation</varname> was added in version 257.</para> + </refsect2> </refsect1> </refentry> diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index ad79552ed6..2ea56d687f 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -306,6 +306,25 @@ </varlistentry> <varlistentry> + <term><varname>DeferReactivation=</varname></term> + + <listitem><para>Takes a boolean argument. When enabled, the timer schedules the next elapse based on + the trigger unit entering inactivity, instead of the last trigger time. + This is most apparent in the case where the service unit takes longer to run than the timer interval. + With this setting enabled, the timer will schedule the next elapse based on when the service finishes + running, and so it will have to wait until the next realtime elapse time to trigger. + Otherwise, the default behavior is for the timer unit to immediately trigger again once the service + finishes running. This happens because the timer schedules the next elapse based on the previous trigger + time, and since the interval is shorter than the service runtime, that elapse will be in the past, + causing it to immediately trigger once done.</para> + + <para>This setting has no effect if a realtime timer has not been specified with + <varname>OnCalendar=</varname>. Defaults to <option>false</option>.</para> + + <xi:include href="version-info.xml" xpointer="v257"/></listitem> + </varlistentry> + + <varlistentry> <term><varname>OnClockChange=</varname></term> <term><varname>OnTimezoneChange=</varname></term> diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index 4f78a521de..b9d0c16acd 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -118,6 +118,7 @@ const sd_bus_vtable bus_timer_vtable[] = { 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), + SD_BUS_PROPERTY("DeferReactivation", "b", bus_property_get_bool, offsetof(Timer, defer_reactivation), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; @@ -233,6 +234,9 @@ static int bus_timer_set_transient_property( if (streq(name, "OnClockChange")) return bus_set_transient_bool(u, name, &t->on_clock_change, message, flags, error); + if (streq(name, "DeferReactivation")) + return bus_set_transient_bool(u, name, &t->defer_reactivation, message, flags, error); + if (streq(name, "TimersMonotonic")) { const char *base_name; usec_t usec; diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 221099a39c..e94b518a9d 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -570,6 +570,7 @@ Timer.Persistent, config_parse_bool, 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.DeferReactivation, config_parse_bool, 0, offsetof(Timer, defer_reactivation) 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 7cb58cc2d9..e44298d5e8 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -245,7 +245,8 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { "%sRemainAfterElapse: %s\n" "%sFixedRandomDelay: %s\n" "%sOnClockChange: %s\n" - "%sOnTimeZoneChange: %s\n", + "%sOnTimeZoneChange: %s\n" + "%sDeferReactivation: %s\n", prefix, timer_state_to_string(t->state), prefix, timer_result_to_string(t->result), prefix, trigger ? trigger->id : "n/a", @@ -255,7 +256,8 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { 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)); + prefix, yes_no(t->on_timezone_change), + prefix, yes_no(t->defer_reactivation)); LIST_FOREACH(value, v, t->values) if (v->base == TIMER_CALENDAR) { @@ -391,12 +393,19 @@ static void timer_enter_waiting(Timer *t, bool time_change) { if (v->base == TIMER_CALENDAR) { usec_t b, rebased; - /* If we know the last time this was - * triggered, schedule the job based relative - * to that. If we don't, just start from - * the activation time. */ + /* If DeferReactivation= is enabled, schedule the job based on the last time + * the trigger unit entered inactivity. Otherwise, if we know the last time + * this was triggered, schedule the job based relative to that. If we don't, + * just start from the activation time or realtime. */ - if (dual_timestamp_is_set(&t->last_trigger)) + if (t->defer_reactivation && + dual_timestamp_is_set(&trigger->inactive_enter_timestamp)) { + if (dual_timestamp_is_set(&t->last_trigger)) + b = MAX(trigger->inactive_enter_timestamp.realtime, + t->last_trigger.realtime); + else + b = trigger->inactive_enter_timestamp.realtime; + } else if (dual_timestamp_is_set(&t->last_trigger)) b = t->last_trigger.realtime; else if (dual_timestamp_is_set(&UNIT(t)->inactive_exit_timestamp)) b = UNIT(t)->inactive_exit_timestamp.realtime; diff --git a/src/core/timer.h b/src/core/timer.h index 2624001c34..14a9931dff 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -61,6 +61,7 @@ struct Timer { bool on_clock_change; bool on_timezone_change; bool fixed_random_delay; + bool defer_reactivation; char *stamp_path; }; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 9fdf566205..b151826920 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -2594,7 +2594,8 @@ static int bus_append_timer_property(sd_bus_message *m, const char *field, const "Persistent", "OnTimezoneChange", "OnClockChange", - "FixedRandomDelay")) + "FixedRandomDelay", + "DeferReactivation")) return bus_append_parse_boolean(m, field, eq); if (STR_IN_SET(field, "AccuracySec", diff --git a/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.service b/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.service new file mode 100644 index 0000000000..a754626828 --- /dev/null +++ b/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.service @@ -0,0 +1,6 @@ +[Unit] +Description=Testing systemd timers + +[Service] +Type=simple +ExecStart=sh -c 'date +%%s >>/tmp/realtime-test.log ; sleep 5' diff --git a/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.timer b/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.timer new file mode 100644 index 0000000000..b870b41628 --- /dev/null +++ b/test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Testing systemd timers + +[Timer] +OnCalendar=*:*:0/5 +AccuracySec=1us +DeferReactivation=true + +[Install] +WantedBy=timers.target diff --git a/test/TEST-74-AUX-UTILS/meson.build b/test/TEST-74-AUX-UTILS/meson.build index 668ad492ca..543eee195f 100644 --- a/test/TEST-74-AUX-UTILS/meson.build +++ b/test/TEST-74-AUX-UTILS/meson.build @@ -7,3 +7,5 @@ integration_tests += [ 'vm' : true, }, ] + +testdata_subdirs += [meson.current_source_dir() / 'TEST-74-AUX-UTILS.units'] diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service index dbd56ec752..1cb212bcad 100644 --- a/test/fuzz/fuzz-unit-file/directives-all.service +++ b/test/fuzz/fuzz-unit-file/directives-all.service @@ -7,6 +7,7 @@ AllowedCPUs= AllowedMemoryNodes= AllowIsolate= Also= +DeferReactivation= AmbientCapabilities= AssertACPower= AssertArchitecture= diff --git a/test/fuzz/fuzz-unit-file/tmpfiles-clean.timer b/test/fuzz/fuzz-unit-file/tmpfiles-clean.timer index 5bf91b9f4c..5dc269243f 100644 --- a/test/fuzz/fuzz-unit-file/tmpfiles-clean.timer +++ b/test/fuzz/fuzz-unit-file/tmpfiles-clean.timer @@ -33,6 +33,7 @@ Persistent=true AccuracySec=24h RandomizedDelaySec=234234234 FixedRandomDelay=true +DeferReactivation=true Persistent=no Unit=foo.service diff --git a/test/units/TEST-74-AUX-UTILS.defer_reactivation.sh b/test/units/TEST-74-AUX-UTILS.defer_reactivation.sh new file mode 100755 index 0000000000..d1b113d1ab --- /dev/null +++ b/test/units/TEST-74-AUX-UTILS.defer_reactivation.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +systemctl start realtime-test.timer + +sleep 35 +mindelta=10 + +last= +while read -r time; do + if [ -n "$last" ]; then + delta=$((time - last)) + if [ "$delta" -lt $mindelta ]; then + echo "Timer fired too early: $delta < $mindelta" >/failed + break + fi + fi + last=$time +done </tmp/realtime-test.log + +test ! -s /failed |