summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2015-11-17 20:13:36 +0100
committerLennart Poettering <lennart@poettering.net>2015-11-17 20:48:23 +0100
commit3e0c30ac56269c6fe7b6c0105e7ad826a27d21c6 (patch)
tree86910d551dad425c918de2a4534a8a51342f92da
parentdf446f96031e281ed676052ccdede0c774cb1e0c (diff)
core: add RemainAfterElapse= setting to timer units
Previously, after a timer unit elapsed we'd leave it around for good, which has the nice benefit that starting a timer that shall trigger at a specific point in time multiple times will only result in one trigger instead of possibly many. With this change a new option RemainAfterElapse= is added. It defaults to "true", to mimic the old behaviour. If set to "false" timer units will be unloaded after they elapsed. This is specifically useful for transient timer units.
-rw-r--r--man/systemd.timer.xml9
-rw-r--r--src/core/dbus-timer.c17
-rw-r--r--src/core/load-fragment-gperf.gperf.m41
-rw-r--r--src/core/timer.c47
-rw-r--r--src/core/timer.h1
5 files changed, 61 insertions, 14 deletions
diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml
index 8cf6c4683b..3841588820 100644
--- a/man/systemd.timer.xml
+++ b/man/systemd.timer.xml
@@ -243,6 +243,15 @@
again after any work that is to be done is finished. Defaults
to <varname>false</varname>.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>RemainAfterExit=</varname></term>
+
+ <listitem><para>Takes a boolean argument. If true, an elapsed
+ timer will stay loaded, and its state remains
+ queriable. Defaults to
+ <varname>yes</varname>.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index a8a280d961..2e6c479c3a 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -182,6 +182,7 @@ const sd_bus_vtable bus_timer_vtable[] = {
SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), 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),
SD_BUS_VTABLE_END
};
@@ -283,7 +284,6 @@ static int bus_timer_set_transient_property(
return 1;
} else if (streq(name, "WakeSystem")) {
-
int b;
r = sd_bus_message_read(message, "b", &b);
@@ -292,11 +292,24 @@ static int bus_timer_set_transient_property(
if (mode != UNIT_CHECK) {
t->wake_system = b;
- unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(t->wake_system));
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b));
}
return 1;
+ } else if (streq(name, "RemainAfterElapse")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ t->remain_after_elapse = b;
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b));
+ }
+
+ return 1;
}
return 0;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 799418033d..74ef72de88 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -344,6 +344,7 @@ Timer.OnUnitActiveSec, config_parse_timer, 0,
Timer.OnUnitInactiveSec, config_parse_timer, 0, 0
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.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
Timer.Unit, config_parse_trigger_unit, 0, 0
m4_dnl
diff --git a/src/core/timer.c b/src/core/timer.c
index 51b1d875be..0587452cfb 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -55,6 +55,7 @@ static void timer_init(Unit *u) {
t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
t->next_elapse_realtime = USEC_INFINITY;
t->accuracy_usec = u->manager->default_timer_accuracy_usec;
+ t->remain_after_elapse = true;
}
void timer_free_values(Timer *t) {
@@ -217,13 +218,15 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
"%sUnit: %s\n"
"%sPersistent: %s\n"
"%sWakeSystem: %s\n"
- "%sAccuracy: %s\n",
+ "%sAccuracy: %s\n"
+ "%sRemainAfterElapse: %s\n",
prefix, timer_state_to_string(t->state),
prefix, timer_result_to_string(t->result),
prefix, trigger ? trigger->id : "n/a",
prefix, yes_no(t->persistent),
prefix, yes_no(t->wake_system),
- prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
+ prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1),
+ prefix, yes_no(t->remain_after_elapse));
LIST_FOREACH(value, v, t->values) {
@@ -275,13 +278,13 @@ static int timer_coldplug(Unit *u) {
assert(t);
assert(t->state == TIMER_DEAD);
- if (t->deserialized_state != t->state) {
+ if (t->deserialized_state == t->state)
+ return 0;
- if (t->deserialized_state == TIMER_WAITING)
- timer_enter_waiting(t, false);
- else
- timer_set_state(t, t->deserialized_state);
- }
+ if (t->deserialized_state == TIMER_WAITING)
+ timer_enter_waiting(t, false);
+ else
+ timer_set_state(t, t->deserialized_state);
return 0;
}
@@ -295,6 +298,23 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
}
+static void timer_enter_elapsed(Timer *t, bool leave_around) {
+ assert(t);
+
+ /* If a unit is marked with RemainAfterElapse=yes we leave it
+ * around even after it elapsed once, so that starting it
+ * later again does not necessarily mean immediate
+ * retriggering. We unconditionally leave units with
+ * TIMER_UNIT_ACTIVE or TIMER_UNIT_INACTIVE triggers around,
+ * since they might be restarted automatically at any time
+ * later on. */
+
+ if (t->remain_after_elapse || leave_around)
+ timer_set_state(t, TIMER_ELAPSED);
+ else
+ timer_enter_dead(t, TIMER_SUCCESS);
+}
+
static usec_t monotonic_to_boottime(usec_t t) {
usec_t a, b;
@@ -314,6 +334,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
bool found_monotonic = false, found_realtime = false;
usec_t ts_realtime, ts_monotonic;
usec_t base = 0;
+ bool leave_around = false;
TimerValue *v;
int r;
@@ -374,7 +395,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
break;
case TIMER_UNIT_ACTIVE:
-
+ leave_around = true;
base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
if (base <= 0)
@@ -386,7 +407,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
break;
case TIMER_UNIT_INACTIVE:
-
+ leave_around = true;
base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
if (base <= 0)
@@ -423,14 +444,16 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (!found_monotonic && !found_realtime) {
log_unit_debug(UNIT(t), "Timer is elapsed.");
- timer_set_state(t, TIMER_ELAPSED);
+ timer_enter_elapsed(t, leave_around);
return;
}
if (found_monotonic) {
char buf[FORMAT_TIMESPAN_MAX];
+ usec_t left;
- log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
+ left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0;
+ log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
if (t->monotonic_event_source) {
r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
diff --git a/src/core/timer.h b/src/core/timer.h
index ac5af6a93c..6bc9fbed3d 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -73,6 +73,7 @@ struct Timer {
bool persistent;
bool wake_system;
+ bool remain_after_elapse;
char *stamp_path;
};