]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/timer.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / core / timer.c
index f8f5f4b2e48d00192c15398684df5a2b85e399cc..27f4f74b49e8d765694b06c810937581faec3a38 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -105,18 +106,18 @@ static int timer_add_default_dependencies(Timer *t) {
         if (!UNIT(t)->default_dependencies)
                 return 0;
 
-        r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
+        r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
         if (r < 0)
                 return r;
 
         if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
-                r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
+                r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
                 if (r < 0)
                         return r;
 
                 LIST_FOREACH(value, v, t->values) {
                         if (v->base == TIMER_CALENDAR) {
-                                r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true);
+                                r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
                                 if (r < 0)
                                         return r;
                                 break;
@@ -124,7 +125,23 @@ static int timer_add_default_dependencies(Timer *t) {
                 }
         }
 
-        return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+        return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+}
+
+static int timer_add_trigger_dependencies(Timer *t) {
+        Unit *x;
+        int r;
+
+        assert(t);
+
+        if (!hashmap_isempty(UNIT(t)->dependencies[UNIT_TRIGGERS]))
+                return 0;
+
+        r = unit_load_related_unit(UNIT(t), ".service", &x);
+        if (r < 0)
+                return r;
+
+        return unit_add_two_dependencies(UNIT(t), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
 }
 
 static int timer_setup_persistent(Timer *t) {
@@ -137,7 +154,7 @@ static int timer_setup_persistent(Timer *t) {
 
         if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
 
-                r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
+                r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers", UNIT_DEPENDENCY_FILE);
                 if (r < 0)
                         return r;
 
@@ -147,7 +164,7 @@ static int timer_setup_persistent(Timer *t) {
 
                 e = getenv("XDG_DATA_HOME");
                 if (e)
-                        t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id, NULL);
+                        t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id);
                 else {
 
                         _cleanup_free_ char *h = NULL;
@@ -156,7 +173,7 @@ static int timer_setup_persistent(Timer *t) {
                         if (r < 0)
                                 return log_unit_error_errno(UNIT(t), r, "Failed to determine home directory: %m");
 
-                        t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL);
+                        t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id);
                 }
         }
 
@@ -179,17 +196,9 @@ static int timer_load(Unit *u) {
 
         if (u->load_state == UNIT_LOADED) {
 
-                if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
-                        Unit *x;
-
-                        r = unit_load_related_unit(u, ".service", &x);
-                        if (r < 0)
-                                return r;
-
-                        r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
-                        if (r < 0)
-                                return r;
-                }
+                r = timer_add_trigger_dependencies(t);
+                if (r < 0)
+                        return r;
 
                 r = timer_setup_persistent(t);
                 if (r < 0)
@@ -232,7 +241,7 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
                 if (v->base == TIMER_CALENDAR) {
                         _cleanup_free_ char *p = NULL;
 
-                        calendar_spec_to_string(v->calendar_spec, &p);
+                        (void) calendar_spec_to_string(v->calendar_spec, &p);
 
                         fprintf(f,
                                 "%s%s: %s\n",
@@ -261,6 +270,8 @@ static void timer_set_state(Timer *t, TimerState state) {
         if (state != TIMER_WAITING) {
                 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
                 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
+                t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
+                t->next_elapse_realtime = USEC_INFINITY;
         }
 
         if (state != old_state)
@@ -291,9 +302,12 @@ static int timer_coldplug(Unit *u) {
 static void timer_enter_dead(Timer *t, TimerResult f) {
         assert(t);
 
-        if (f != TIMER_SUCCESS)
+        if (t->result == TIMER_SUCCESS)
                 t->result = f;
 
+        if (t->result != TIMER_SUCCESS)
+                log_unit_warning(UNIT(t), "Failed with result '%s'.", timer_result_to_string(t->result));
+
         timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
 }
 
@@ -314,21 +328,6 @@ static void timer_enter_elapsed(Timer *t, bool leave_around) {
                 timer_enter_dead(t, TIMER_SUCCESS);
 }
 
-static usec_t monotonic_to_boottime(usec_t t) {
-        usec_t a, b;
-
-        if (t <= 0)
-                return 0;
-
-        a = now(clock_boottime_or_monotonic());
-        b = now(CLOCK_MONOTONIC);
-
-        if (t + a > b)
-                return t + a - b;
-        else
-                return 0;
-}
-
 static void add_random(Timer *t, usec_t *v) {
         char s[FORMAT_TIMESPAN_MAX];
         usec_t add;
@@ -348,14 +347,14 @@ static void add_random(Timer *t, usec_t *v) {
         else
                 *v += add;
 
-        log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
+        log_unit_debug(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
 }
 
 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;
+        triple_timestamp ts;
+        usec_t base = 0;
         TimerValue *v;
         Unit *trigger;
         int r;
@@ -369,11 +368,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                 return;
         }
 
-        /* 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_or_monotonic() : CLOCK_MONOTONIC);
+        triple_timestamp_get(&ts);
         t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
 
         LIST_FOREACH(value, v, t->values) {
@@ -389,7 +384,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)
@@ -403,13 +398,14 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                         found_realtime = true;
 
                 } else  {
+
                         switch (v->base) {
 
                         case TIMER_ACTIVE:
                                 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:
@@ -420,7 +416,8 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                                 }
                                 /* In a container we don't want to include the time the host
                                  * was already up when the container started, so count from
-                                 * our own startup. Fall through. */
+                                 * our own startup. */
+                                /* fall through */
                         case TIMER_STARTUP:
                                 base = UNIT(t)->manager->userspace_timestamp.monotonic;
                                 break;
@@ -453,12 +450,11 @@ 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 = usec_add(usec_shift_clock(base, CLOCK_MONOTONIC, TIMER_MONOTONIC_CLOCK(t)), v->value);
 
-                        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 < triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)) &&
+                            IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
                                 /* This is a one time trigger, disable it now */
                                 v->disabled = true;
                                 continue;
@@ -485,7 +481,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
 
                 add_random(t, &t->next_elapse_monotonic_or_boottime);
 
-                left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0;
+                left = usec_sub_unsigned(t->next_elapse_monotonic_or_boottime, triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)));
                 log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
 
                 if (t->monotonic_event_source) {
@@ -599,9 +595,10 @@ static int timer_start(Unit *u) {
         Timer *t = TIMER(u);
         TimerValue *v;
         Unit *trigger;
+        int r;
 
         assert(t);
-        assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
+        assert(IN_SET(t->state, TIMER_DEAD, TIMER_FAILED));
 
         trigger = UNIT_TRIGGER(u);
         if (!trigger || trigger->load_state != UNIT_LOADED) {
@@ -609,6 +606,16 @@ static int timer_start(Unit *u) {
                 return -ENOENT;
         }
 
+        r = unit_start_limit_test(u);
+        if (r < 0) {
+                timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
+                return r;
+        }
+
+        r = unit_acquire_invocation_id(u);
+        if (r < 0)
+                return r;
+
         t->last_trigger = DUAL_TIMESTAMP_NULL;
 
         /* Reenable all timers that depend on unit activation time */
@@ -619,13 +626,27 @@ static int timer_start(Unit *u) {
         if (t->stamp_path) {
                 struct stat st;
 
-                if (stat(t->stamp_path, &st) >= 0)
-                        t->last_trigger.realtime = timespec_load(&st.st_atim);
-                else if (errno == ENOENT)
+                if (stat(t->stamp_path, &st) >= 0) {
+                        usec_t ft;
+
+                        /* Load the file timestamp, but only if it is actually in the past. If it is in the future,
+                         * something is wrong with the system clock. */
+
+                        ft = timespec_load(&st.st_mtim);
+                        if (ft < now(CLOCK_REALTIME))
+                                t->last_trigger.realtime = ft;
+                        else {
+                                char z[FORMAT_TIMESTAMP_MAX];
+
+                                log_unit_warning(u, "Not using persistent file timestamp %s as it is in the future.",
+                                                 format_timestamp(z, sizeof(z), ft));
+                        }
+
+                } else if (errno == ENOENT)
                         /* The timer has never run before,
                          * make sure a stamp file exists.
                          */
-                        touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
+                        (void) touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
         }
 
         t->result = TIMER_SUCCESS;
@@ -637,7 +658,7 @@ static int timer_stop(Unit *u) {
         Timer *t = TIMER(u);
 
         assert(t);
-        assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
+        assert(IN_SET(t->state, TIMER_WAITING, TIMER_RUNNING, TIMER_ELAPSED));
 
         timer_enter_dead(t, TIMER_SUCCESS);
         return 1;
@@ -742,8 +763,7 @@ static void timer_trigger_notify(Unit *u, Unit *other) {
 
         /* Reenable all timers that depend on unit state */
         LIST_FOREACH(value, v, t->values)
-                if (v->base == TIMER_UNIT_ACTIVE ||
-                    v->base == TIMER_UNIT_INACTIVE)
+                if (IN_SET(v->base, TIMER_UNIT_ACTIVE, TIMER_UNIT_INACTIVE))
                         v->disabled = false;
 
         switch (t->state) {
@@ -808,7 +828,8 @@ DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
 
 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
         [TIMER_SUCCESS] = "success",
-        [TIMER_FAILURE_RESOURCES] = "resources"
+        [TIMER_FAILURE_RESOURCES] = "resources",
+        [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);