summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/TRANSIENT-SETTINGS.md1
-rw-r--r--man/org.freedesktop.systemd1.xml10
-rw-r--r--man/systemd.timer.xml19
-rw-r--r--src/core/dbus-timer.c4
-rw-r--r--src/core/load-fragment-gperf.gperf.in1
-rw-r--r--src/core/timer.c23
-rw-r--r--src/core/timer.h1
-rw-r--r--src/shared/bus-unit-util.c3
-rw-r--r--test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.service6
-rw-r--r--test/TEST-74-AUX-UTILS/TEST-74-AUX-UTILS.units/realtime-test.timer10
-rw-r--r--test/TEST-74-AUX-UTILS/meson.build2
-rw-r--r--test/fuzz/fuzz-unit-file/directives-all.service1
-rw-r--r--test/fuzz/fuzz-unit-file/tmpfiles-clean.timer1
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.defer_reactivation.sh23
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