chiark / gitweb /
timer: support timers that can resume the system from suspend
authorLennart Poettering <lennart@poettering.net>
Mon, 24 Mar 2014 15:09:54 +0000 (16:09 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 24 Mar 2014 15:24:07 +0000 (16:24 +0100)
man/systemd.timer.xml
src/core/dbus-timer.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/timer.c
src/core/timer.h
src/systemctl/systemctl.c

index b60199c..58eaab0 100644 (file)
                                 <varname>OnCalendar=</varname>.
                                 </para></listitem>
                         </varlistentry>
+
+                        <varlistentry>
+                                <term><varname>WakeSystem=</varname></term>
+
+                                <listitem><para>Takes a boolean
+                                argument. If true an elapsing timer
+                                will cause the system to resume 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
+                                <varname>false</varname>.</para></listitem>
+                        </varlistentry>
                 </variablelist>
         </refsect1>
 
index c9b80a0..f1f8c54 100644 (file)
@@ -135,17 +135,51 @@ static int property_get_unit(
         return sd_bus_message_append(reply, "s", trigger ? trigger->id : "");
 }
 
+static int property_get_next_elapse_monotonic(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Timer *t = userdata;
+        usec_t x;
+
+        assert(bus);
+        assert(reply);
+        assert(t);
+
+        if (t->next_elapse_monotonic_or_boottime <= 0)
+                x = 0;
+        else if (t->wake_system) {
+                usec_t a, b;
+
+                a = now(CLOCK_MONOTONIC);
+                b = now(CLOCK_BOOTTIME);
+
+                if (t->next_elapse_monotonic_or_boottime + a > b)
+                        x = t->next_elapse_monotonic_or_boottime + a - b;
+                else
+                        x = 0;
+        } else
+                x = t->next_elapse_monotonic_or_boottime;
+
+        return sd_bus_message_append(reply, "t", x);
+}
+
 const sd_bus_vtable bus_timer_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Unit", "s", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
         SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, next_elapse_realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", bus_property_get_usec, offsetof(Timer, next_elapse_monotonic), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("LastTriggerUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, last_trigger.realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("LastTriggerUSecMonotonic", "t", bus_property_get_usec, offsetof(Timer, last_trigger.monotonic), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         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("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_VTABLE_END
 };
index 55fd3da..3a77234 100644 (file)
@@ -286,6 +286,7 @@ Timer.OnStartupSec,              config_parse_timer,                 0,
 Timer.OnUnitActiveSec,           config_parse_timer,                 0,                             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.AccuracySec,               config_parse_sec,                   0,                             offsetof(Timer, accuracy_usec)
 Timer.Unit,                      config_parse_trigger_unit,          0,                             0
 m4_dnl
index fa4e931..e7779d1 100644 (file)
@@ -1254,7 +1254,6 @@ int config_parse_timer(const char *unit,
         TimerValue *v;
         TimerBase b;
         CalendarSpec *c = NULL;
-        clockid_t id;
 
         assert(filename);
         assert(lvalue);
@@ -1281,8 +1280,6 @@ int config_parse_timer(const char *unit,
                                    rvalue);
                         return 0;
                 }
-
-                id = CLOCK_REALTIME;
         } else {
                 if (parse_sec(rvalue, &u) < 0) {
                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
@@ -1290,8 +1287,6 @@ int config_parse_timer(const char *unit,
                                    rvalue);
                         return 0;
                 }
-
-                id = CLOCK_MONOTONIC;
         }
 
         v = new0(TimerValue, 1);
@@ -1299,7 +1294,6 @@ int config_parse_timer(const char *unit,
                 return log_oom();
 
         v->base = b;
-        v->clock_id = id;
         v->value = u;
         v->calendar_spec = c;
 
index 95416f3..62baf57 100644 (file)
@@ -46,7 +46,7 @@ static void timer_init(Unit *u) {
         assert(u);
         assert(u->load_state == UNIT_STUB);
 
-        t->next_elapse_monotonic = (usec_t) -1;
+        t->next_elapse_monotonic_or_boottime = (usec_t) -1;
         t->next_elapse_realtime = (usec_t) -1;
         t->accuracy_usec = USEC_PER_MINUTE;
 }
@@ -203,10 +203,14 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sTimer State: %s\n"
                 "%sResult: %s\n"
                 "%sUnit: %s\n"
+                "%sPersistent: %s\n"
+                "%sWakeSystem: %s\n"
                 "%sAccuracy: %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));
 
         LIST_FOREACH(value, v, t->values) {
@@ -282,15 +286,34 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
         timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
 }
 
+static usec_t monotonic_to_boottime(usec_t t) {
+        usec_t a, b;
+
+        if (t <= 0)
+                return 0;
+
+        a = now(CLOCK_BOOTTIME);
+        b = now(CLOCK_MONOTONIC);
+
+        if (t + a > b)
+                return t + a - b;
+        else
+                return 0;
+}
+
 static void timer_enter_waiting(Timer *t, bool initial) {
-        TimerValue *v;
-        usec_t base = 0;
-        dual_timestamp ts;
         bool found_monotonic = false, found_realtime = false;
+        usec_t ts_realtime, ts_monotonic;
+        usec_t base = 0;
+        TimerValue *v;
         int r;
 
-        dual_timestamp_get(&ts);
-        t->next_elapse_monotonic = t->next_elapse_realtime = 0;
+        /* If we shall wake the system we use the boottime clock
+         * rather than the monotonic clock. */
+
+        ts_realtime = now(CLOCK_REALTIME);
+        ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
+        t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
 
         LIST_FOREACH(value, v, t->values) {
 
@@ -305,7 +328,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                          * to that. If we don't just start from
                          * now. */
 
-                        b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts.realtime;
+                        b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime;
 
                         r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
                         if (r < 0)
@@ -325,7 +348,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                                 if (state_translation_table[t->state] == UNIT_ACTIVE)
                                         base = UNIT(t)->inactive_exit_timestamp.monotonic;
                                 else
-                                        base = ts.monotonic;
+                                        base = ts_monotonic;
                                 break;
 
                         case TIMER_BOOT:
@@ -365,18 +388,21 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                                 assert_not_reached("Unknown timer base");
                         }
 
+                        if (t->wake_system)
+                                base = monotonic_to_boottime(base);
+
                         v->next_elapse = base + v->value;
 
-                        if (!initial && v->next_elapse < ts.monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
+                        if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
                                 /* This is a one time trigger, disable it now */
                                 v->disabled = true;
                                 continue;
                         }
 
                         if (!found_monotonic)
-                                t->next_elapse_monotonic = v->next_elapse;
+                                t->next_elapse_monotonic_or_boottime = v->next_elapse;
                         else
-                                t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
+                                t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
 
                         found_monotonic = true;
                 }
@@ -390,10 +416,13 @@ static void timer_enter_waiting(Timer *t, bool initial) {
 
         if (found_monotonic) {
                 char buf[FORMAT_TIMESPAN_MAX];
-                log_debug_unit(UNIT(t)->id, "%s: Monotonic timer elapses in %s.", UNIT(t)->id, format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0, 0));
+
+                log_debug_unit(UNIT(t)->id, "%s: Monotonic timer elapses in %s.",
+                               UNIT(t)->id,
+                               format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
 
                 if (t->monotonic_event_source) {
-                        r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic);
+                        r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
                         if (r < 0)
                                 goto fail;
 
@@ -402,8 +431,8 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                         r = sd_event_add_time(
                                         UNIT(t)->manager->event,
                                         &t->monotonic_event_source,
-                                        CLOCK_MONOTONIC,
-                                        t->next_elapse_monotonic, t->accuracy_usec,
+                                        t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
+                                        t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
                                         timer_dispatch, t);
                 if (r < 0)
                         goto fail;
@@ -429,7 +458,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                         r = sd_event_add_time(
                                         UNIT(t)->manager->event,
                                         &t->realtime_event_source,
-                                        CLOCK_REALTIME,
+                                        t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
                                         t->next_elapse_realtime, t->accuracy_usec,
                                         timer_dispatch, t);
                 if (r < 0)
index 712cced..de412a0 100644 (file)
@@ -50,7 +50,6 @@ typedef enum TimerBase {
 typedef struct TimerValue {
         TimerBase base;
         bool disabled;
-        clockid_t clock_id;
 
         usec_t value; /* only for monotonic events */
         CalendarSpec *calendar_spec; /* only for calendar events */
@@ -72,8 +71,9 @@ struct Timer {
         usec_t accuracy_usec;
 
         LIST_HEAD(TimerValue, values);
-        usec_t next_elapse_monotonic;
         usec_t next_elapse_realtime;
+        usec_t next_elapse_monotonic_or_boottime;
+        dual_timestamp last_trigger;
 
         TimerState state, deserialized_state;
 
@@ -83,8 +83,7 @@ struct Timer {
         TimerResult result;
 
         bool persistent;
-
-        dual_timestamp last_trigger;
+        bool wake_system;
 
         char *stamp_path;
 };
index cceb2b6..0887bc3 100644 (file)
@@ -964,7 +964,7 @@ static int get_last_trigger(
                         "org.freedesktop.systemd1",
                         path,
                         "org.freedesktop.systemd1.Timer",
-                        "LastTriggerUSecRealtime",
+                        "LastTriggerUSec",
                         &error,
                         't',
                         last);