[systemd-devel] [PATCH] Implement OnTransactionFinishedSec= for timers
Christian Seiler
christian at iwakd.de
Thu Sep 27 02:25:18 PDT 2012
Add OnTransactionFinishedSec= setting for timers that allows for timers to
fire at a specific interval after the transaction in which they were started
has completed.
This is specifically useful for running services after boot. For example,
having a timer with OnTransactionFinishedSec=0 and enabling it in
default.target will case the timer to start as soon as the system has
reached default.target, but not before.
Signed-off-by: Christian Seiler <christian at iwakd.de>
---
man/systemd.timer.xml | 17 +++++++++++--
src/core/load-fragment-gperf.gperf.m4 | 1 +
src/core/manager.c | 21 +++++++++++++++
src/core/manager.h | 5 ++++
src/core/timer.c | 48 ++++++++++++++++++++++++++++++-----
src/core/timer.h | 9 +++++++
6 files changed, 92 insertions(+), 9 deletions(-)
diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml
index 6fc26a5..c20e21a 100644
--- a/man/systemd.timer.xml
+++ b/man/systemd.timer.xml
@@ -104,6 +104,7 @@
<term><varname>OnStartupSec=</varname></term>
<term><varname>OnUnitActiveSec=</varname></term>
<term><varname>OnUnitInactiveSec=</varname></term>
+ <term><varname>OnTransactionFinishedSec=</varname></term>
<listitem><para>Defines timers
relative to different starting points:
@@ -122,7 +123,10 @@
activated. <varname>OnUnitInactiveSec=</varname>
defines a timer relative to when the
unit the timer is activating was last
- deactivated.</para>
+ deactivated. <varname>OnTransactionFinishedSec=</varname>
+ defines a timer relative to the end
+ of the transaction in which the timer
+ was started.</para>
<para>Multiple directives may be
combined of the same and of different
@@ -152,7 +156,16 @@
elapse and the configured unit is
started. This is not the case for
timers defined in the other
- directives.</para></listitem>
+ directives.</para>
+
+ <para>The <varname>OnTransactionFinishedSec=</varname>
+ setting is useful for starting a timer
+ after the system finished booting (in
+ contrast to <varname>OnBootSec=</varname>,
+ which defines timers relative to the
+ point the system started booting) if the
+ timer is enabled in
+ <filename>default.target</filename>.</para></listitem>
<para>These are monotonic timers,
independent of wall-clock time and timezones. If the
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 8187cd4..f68fa12 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -235,6 +235,7 @@ Timer.OnBootSec, config_parse_timer, 0,
Timer.OnStartupSec, config_parse_timer, 0, 0
Timer.OnUnitActiveSec, config_parse_timer, 0, 0
Timer.OnUnitInactiveSec, config_parse_timer, 0, 0
+Timer.OnTransactionFinishedSec, config_parse_timer, 0, 0
Timer.Unit, config_parse_timer_unit, 0, 0
m4_dnl
Path.PathExists, config_parse_path_spec, 0, 0
diff --git a/src/core/manager.c b/src/core/manager.c
index 3cd9915..78e3856 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -461,6 +461,7 @@ static void manager_clear_jobs_and_units(Manager *m) {
assert(!m->dbus_job_queue);
assert(!m->cleanup_queue);
assert(!m->gc_queue);
+ assert(!m->transaction_timer_queue);
assert(hashmap_isempty(m->jobs));
assert(hashmap_isempty(m->units));
@@ -944,6 +945,23 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
return n;
}
+unsigned manager_dispatch_transaction_timer_queue(Manager *m) {
+ Timer *t;
+ unsigned n = 0;
+ usec_t finish_time;
+
+ finish_time = now(CLOCK_MONOTONIC);
+
+ while ((t = m->transaction_timer_queue)) {
+ assert(t->in_transaction_timer_queue);
+
+ timer_notify_transaction_finished(t, finish_time);
+ n++;
+ }
+
+ return n;
+}
+
static int manager_process_notify_fd(Manager *m) {
ssize_t n;
@@ -2027,6 +2045,9 @@ void manager_check_finished(Manager *m) {
/* Notify Type=idle units that we are done now */
close_pipe(m->idle_pipe);
+ /* Recalculate all timers that are based on end-of-transaction times */
+ manager_dispatch_transaction_timer_queue(m);
+
/* Turn off confirm spawn now */
m->confirm_spawn = false;
diff --git a/src/core/manager.h b/src/core/manager.h
index 913752f..d9d4785 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -34,6 +34,7 @@
typedef struct Manager Manager;
typedef enum WatchType WatchType;
typedef struct Watch Watch;
+typedef struct Timer Timer;
typedef enum ManagerExitCode {
MANAGER_RUNNING,
@@ -113,6 +114,9 @@ struct Manager {
LIST_HEAD(Unit, dbus_unit_queue);
LIST_HEAD(Job, dbus_job_queue);
+ /* Timers to ping after transaction is done */
+ LIST_HEAD(Timer, transaction_timer_queue);
+
/* Units to remove */
LIST_HEAD(Unit, cleanup_queue);
@@ -262,6 +266,7 @@ void manager_clear_jobs(Manager *m);
unsigned manager_dispatch_load_queue(Manager *m);
unsigned manager_dispatch_run_queue(Manager *m);
unsigned manager_dispatch_dbus_queue(Manager *m);
+unsigned manager_dispatch_transaction_timer_queue(Manager *m);
int manager_set_default_controllers(Manager *m, char **controllers);
int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit);
diff --git a/src/core/timer.c b/src/core/timer.c
index 7080b32..16749ba 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -43,6 +43,7 @@ static void timer_init(Unit *u) {
assert(u->load_state == UNIT_STUB);
t->next_elapse = (usec_t) -1;
+ t->last_transaction_finished = (usec_t) -1;
}
static void timer_done(Unit *u) {
@@ -58,6 +59,9 @@ static void timer_done(Unit *u) {
unit_unwatch_timer(u, &t->timer_watch);
+ if (t->in_transaction_timer_queue)
+ LIST_REMOVE(Timer, transaction_timer_queue, u->manager->transaction_timer_queue, t);
+
unit_ref_unset(&t->unit);
}
@@ -166,7 +170,7 @@ static void timer_set_state(Timer *t, TimerState state) {
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
}
-static void timer_enter_waiting(Timer *t, bool initial);
+static void timer_enter_waiting(Timer *t, bool initial, bool transaction_just_finished);
static int timer_coldplug(Unit *u) {
Timer *t = TIMER(u);
@@ -177,7 +181,7 @@ static int timer_coldplug(Unit *u) {
if (t->deserialized_state != t->state) {
if (t->deserialized_state == TIMER_WAITING)
- timer_enter_waiting(t, false);
+ timer_enter_waiting(t, false, false);
else
timer_set_state(t, t->deserialized_state);
}
@@ -194,7 +198,7 @@ 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_waiting(Timer *t, bool initial) {
+static void timer_enter_waiting(Timer *t, bool initial, bool transaction_just_finished) {
TimerValue *v;
usec_t base = 0, delay, n;
bool found = false;
@@ -241,6 +245,24 @@ static void timer_enter_waiting(Timer *t, bool initial) {
base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
break;
+ case TIMER_TRANSACTION_FINISHED:
+
+ if (t->last_transaction_finished == (usec_t) -1) {
+ if (initial) {
+ LIST_PREPEND(Timer, transaction_timer_queue, UNIT(t)->manager->transaction_timer_queue, t);
+ t->in_transaction_timer_queue = true;
+ }
+ continue;
+ }
+
+ base = t->last_transaction_finished;
+
+ /* if we are called the first time after a transaction just finished,
+ * ensure that the event will be considered (initial = false!) */
+ if (transaction_just_finished && base + v->value < n)
+ base = n - v->value;
+ break;
+
default:
assert_not_reached("Unknown timer base");
}
@@ -312,7 +334,8 @@ static int timer_start(Unit *u) {
return -ENOENT;
t->result = TIMER_SUCCESS;
- timer_enter_waiting(t, true);
+ t->last_transaction_finished = (usec_t) -1;
+ timer_enter_waiting(t, true, false);
return 0;
}
@@ -425,14 +448,14 @@ void timer_unit_notify(Unit *u, UnitActiveState new_state) {
case TIMER_ELAPSED:
/* Recalculate sleep time */
- timer_enter_waiting(t, false);
+ timer_enter_waiting(t, false, false);
break;
case TIMER_RUNNING:
if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
log_debug("%s got notified about unit deactivation.", UNIT(t)->id);
- timer_enter_waiting(t, false);
+ timer_enter_waiting(t, false, false);
}
break;
@@ -447,6 +470,16 @@ void timer_unit_notify(Unit *u, UnitActiveState new_state) {
}
}
+void timer_notify_transaction_finished(Timer *t, usec_t finish_time)
+{
+ t->last_transaction_finished = finish_time;
+ if (t->in_transaction_timer_queue) {
+ LIST_REMOVE(Timer, transaction_timer_queue, UNIT(t)->manager->transaction_timer_queue, t);
+ t->in_transaction_timer_queue = false;
+ }
+ timer_enter_waiting(t, false, true);
+}
+
static void timer_reset_failed(Unit *u) {
Timer *t = TIMER(u);
@@ -473,7 +506,8 @@ static const char* const timer_base_table[_TIMER_BASE_MAX] = {
[TIMER_BOOT] = "OnBootSec",
[TIMER_STARTUP] = "OnStartupSec",
[TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
- [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec"
+ [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
+ [TIMER_TRANSACTION_FINISHED] = "OnTransactionFinishedSec"
};
DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
diff --git a/src/core/timer.h b/src/core/timer.h
index c6d1d42..94a4213 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -41,6 +41,7 @@ typedef enum TimerBase {
TIMER_STARTUP,
TIMER_UNIT_ACTIVE,
TIMER_UNIT_INACTIVE,
+ TIMER_TRANSACTION_FINISHED,
_TIMER_BASE_MAX,
_TIMER_BASE_INVALID = -1
} TimerBase;
@@ -66,6 +67,7 @@ struct Timer {
Unit meta;
LIST_HEAD(TimerValue, values);
+ usec_t last_transaction_finished;
usec_t next_elapse;
TimerState state, deserialized_state;
@@ -74,10 +76,17 @@ struct Timer {
Watch timer_watch;
TimerResult result;
+
+ /* Transaction timer queue */
+ LIST_FIELDS(Timer, transaction_timer_queue);
+
+ bool in_transaction_timer_queue:1;
};
void timer_unit_notify(Unit *u, UnitActiveState new_state);
+void timer_notify_transaction_finished(Timer *t, usec_t finish_time);
+
extern const UnitVTable timer_vtable;
const char *timer_state_to_string(TimerState i);
--
1.7.11.4
More information about the systemd-devel
mailing list