1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "unit-name.h"
27 #include "dbus-timer.h"
29 #include "bus-errors.h"
31 static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
32 [TIMER_DEAD] = UNIT_INACTIVE,
33 [TIMER_WAITING] = UNIT_ACTIVE,
34 [TIMER_RUNNING] = UNIT_ACTIVE,
35 [TIMER_ELAPSED] = UNIT_ACTIVE,
36 [TIMER_FAILED] = UNIT_FAILED
39 static void timer_init(Unit *u) {
43 assert(u->load_state == UNIT_STUB);
45 t->next_elapse_monotonic = (usec_t) -1;
46 t->next_elapse_realtime = (usec_t) -1;
47 watch_init(&t->monotonic_watch);
48 watch_init(&t->realtime_watch);
51 static void timer_done(Unit *u) {
57 while ((v = t->values)) {
58 LIST_REMOVE(TimerValue, value, t->values, v);
61 calendar_spec_free(v->calendar_spec);
66 unit_unwatch_timer(u, &t->monotonic_watch);
67 unit_unwatch_timer(u, &t->realtime_watch);
69 unit_ref_unset(&t->unit);
72 static int timer_verify(Timer *t) {
75 if (UNIT(t)->load_state != UNIT_LOADED)
79 log_error("%s lacks value setting. Refusing.", UNIT(t)->id);
86 static int timer_add_default_dependencies(Timer *t) {
91 if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
92 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true);
96 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
101 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
104 static int timer_load(Unit *u) {
109 assert(u->load_state == UNIT_STUB);
111 r = unit_load_fragment_and_dropin(u);
115 if (u->load_state == UNIT_LOADED) {
117 if (!UNIT_DEREF(t->unit)) {
120 r = unit_load_related_unit(u, ".service", &x);
124 unit_ref_set(&t->unit, x);
127 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true);
131 if (UNIT(t)->default_dependencies) {
132 r = timer_add_default_dependencies(t);
138 return timer_verify(t);
141 static void timer_dump(Unit *u, FILE *f, const char *prefix) {
146 "%sTimer State: %s\n"
149 prefix, timer_state_to_string(t->state),
150 prefix, timer_result_to_string(t->result),
151 prefix, UNIT_DEREF(t->unit)->id);
153 LIST_FOREACH(value, v, t->values) {
155 if (v->base == TIMER_CALENDAR) {
156 _cleanup_free_ char *p = NULL;
158 calendar_spec_to_string(v->calendar_spec, &p);
163 timer_base_to_string(v->base),
166 char timespan1[FORMAT_TIMESPAN_MAX];
171 timer_base_to_string(v->base),
172 strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
177 static void timer_set_state(Timer *t, TimerState state) {
178 TimerState old_state;
181 old_state = t->state;
184 if (state != TIMER_WAITING) {
185 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
186 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
189 if (state != old_state)
190 log_debug("%s changed %s -> %s",
192 timer_state_to_string(old_state),
193 timer_state_to_string(state));
195 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
198 static void timer_enter_waiting(Timer *t, bool initial);
200 static int timer_coldplug(Unit *u) {
204 assert(t->state == TIMER_DEAD);
206 if (t->deserialized_state != t->state) {
208 if (t->deserialized_state == TIMER_WAITING)
209 timer_enter_waiting(t, false);
211 timer_set_state(t, t->deserialized_state);
217 static void timer_enter_dead(Timer *t, TimerResult f) {
220 if (f != TIMER_SUCCESS)
223 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
226 static void timer_enter_waiting(Timer *t, bool initial) {
230 bool found_monotonic = false, found_realtime = false;
233 dual_timestamp_get(&ts);
234 t->next_elapse_monotonic = t->next_elapse_realtime = 0;
236 LIST_FOREACH(value, v, t->values) {
241 if (v->base == TIMER_CALENDAR) {
243 r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
247 if (!initial && v->next_elapse < ts.realtime) {
253 t->next_elapse_realtime = v->next_elapse;
255 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
257 found_realtime = true;
263 if (state_translation_table[t->state] == UNIT_ACTIVE)
264 base = UNIT(t)->inactive_exit_timestamp.monotonic;
270 /* CLOCK_MONOTONIC equals the uptime on Linux */
275 base = UNIT(t)->manager->userspace_timestamp.monotonic;
278 case TIMER_UNIT_ACTIVE:
280 if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
283 base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
286 case TIMER_UNIT_INACTIVE:
288 if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
291 base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
295 assert_not_reached("Unknown timer base");
298 v->next_elapse = base + v->value;
300 if (!initial && v->next_elapse < ts.monotonic) {
305 if (!found_monotonic)
306 t->next_elapse_monotonic = v->next_elapse;
308 t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
310 found_monotonic = true;
314 if (!found_monotonic && !found_realtime) {
315 log_debug("%s: Timer is elapsed.", UNIT(t)->id);
316 timer_set_state(t, TIMER_ELAPSED);
320 if (found_monotonic) {
321 char buf[FORMAT_TIMESPAN_MAX];
322 log_debug("%s: Monotonic timer elapses in %s the next time.", UNIT(t)->id, format_timespan(buf, sizeof(buf), t->next_elapse_monotonic - ts.monotonic));
324 r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch);
328 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
330 if (found_realtime) {
331 char buf[FORMAT_TIMESTAMP_MAX];
332 log_debug("%s: Realtime timer elapses at %s the next time.", UNIT(t)->id, format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
334 r = unit_watch_timer(UNIT(t), CLOCK_REALTIME, false, t->next_elapse_realtime, &t->realtime_watch);
338 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
340 timer_set_state(t, TIMER_WAITING);
344 log_warning("%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r));
345 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
348 static void timer_enter_running(Timer *t) {
353 dbus_error_init(&error);
355 /* Don't start job if we are supposed to go down */
356 if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
359 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL);
363 timer_set_state(t, TIMER_RUNNING);
367 log_warning("%s failed to queue unit startup job: %s", UNIT(t)->id, bus_error(&error, r));
368 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
370 dbus_error_free(&error);
373 static int timer_start(Unit *u) {
377 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
379 if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
382 t->result = TIMER_SUCCESS;
383 timer_enter_waiting(t, true);
387 static int timer_stop(Unit *u) {
391 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
393 timer_enter_dead(t, TIMER_SUCCESS);
397 static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
404 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
405 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
410 static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
418 if (streq(key, "state")) {
421 state = timer_state_from_string(value);
423 log_debug("Failed to parse state value %s", value);
425 t->deserialized_state = state;
426 } else if (streq(key, "result")) {
429 f = timer_result_from_string(value);
431 log_debug("Failed to parse result value %s", value);
432 else if (f != TIMER_SUCCESS)
436 log_debug("Unknown serialization key '%s'", key);
441 static UnitActiveState timer_active_state(Unit *u) {
444 return state_translation_table[TIMER(u)->state];
447 static const char *timer_sub_state_to_string(Unit *u) {
450 return timer_state_to_string(TIMER(u)->state);
453 static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
457 assert(elapsed == 1);
459 if (t->state != TIMER_WAITING)
462 log_debug("Timer elapsed on %s", u->id);
463 timer_enter_running(t);
466 void timer_unit_notify(Unit *u, UnitActiveState new_state) {
470 if (u->type == UNIT_TIMER)
473 SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
477 if (k->type != UNIT_TIMER)
480 if (k->load_state != UNIT_LOADED)
485 /* Reenable all timers that depend on unit state */
486 LIST_FOREACH(value, v, t->values)
487 if (v->base == TIMER_UNIT_ACTIVE ||
488 v->base == TIMER_UNIT_INACTIVE)
496 /* Recalculate sleep time */
497 timer_enter_waiting(t, false);
502 if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
503 log_debug("%s got notified about unit deactivation.", UNIT(t)->id);
504 timer_enter_waiting(t, false);
514 assert_not_reached("Unknown timer state");
519 static void timer_reset_failed(Unit *u) {
524 if (t->state == TIMER_FAILED)
525 timer_set_state(t, TIMER_DEAD);
527 t->result = TIMER_SUCCESS;
530 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
531 [TIMER_DEAD] = "dead",
532 [TIMER_WAITING] = "waiting",
533 [TIMER_RUNNING] = "running",
534 [TIMER_ELAPSED] = "elapsed",
535 [TIMER_FAILED] = "failed"
538 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
540 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
541 [TIMER_ACTIVE] = "OnActiveSec",
542 [TIMER_BOOT] = "OnBootSec",
543 [TIMER_STARTUP] = "OnStartupSec",
544 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
545 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
546 [TIMER_CALENDAR] = "OnCalendar"
549 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
551 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
552 [TIMER_SUCCESS] = "success",
553 [TIMER_FAILURE_RESOURCES] = "resources"
556 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
558 const UnitVTable timer_vtable = {
559 .object_size = sizeof(Timer),
569 .coldplug = timer_coldplug,
573 .start = timer_start,
576 .serialize = timer_serialize,
577 .deserialize_item = timer_deserialize_item,
579 .active_state = timer_active_state,
580 .sub_state_to_string = timer_sub_state_to_string,
582 .timer_event = timer_timer_event,
584 .reset_failed = timer_reset_failed,
586 .bus_interface = "org.freedesktop.systemd1.Timer",
587 .bus_message_handler = bus_timer_message_handler,
588 .bus_invalidating_properties = bus_timer_invalidating_properties