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 "dbus-common.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 void timer_free_values(Timer *t) {
56 while ((v = t->values)) {
57 LIST_REMOVE(TimerValue, value, t->values, v);
60 calendar_spec_free(v->calendar_spec);
66 static void timer_done(Unit *u) {
73 unit_unwatch_timer(u, &t->monotonic_watch);
74 unit_unwatch_timer(u, &t->realtime_watch);
77 static int timer_verify(Timer *t) {
80 if (UNIT(t)->load_state != UNIT_LOADED)
84 log_error_unit(UNIT(t)->id,
85 "%s lacks value setting. Refusing.", UNIT(t)->id);
92 static int timer_add_default_dependencies(Timer *t) {
97 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
101 if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
102 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
107 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
110 static int timer_load(Unit *u) {
115 assert(u->load_state == UNIT_STUB);
117 r = unit_load_fragment_and_dropin(u);
121 if (u->load_state == UNIT_LOADED) {
123 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
126 r = unit_load_related_unit(u, ".service", &x);
130 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
135 if (UNIT(t)->default_dependencies) {
136 r = timer_add_default_dependencies(t);
142 return timer_verify(t);
145 static void timer_dump(Unit *u, FILE *f, const char *prefix) {
150 trigger = UNIT_TRIGGER(u);
153 "%sTimer State: %s\n"
156 prefix, timer_state_to_string(t->state),
157 prefix, timer_result_to_string(t->result),
158 prefix, trigger ? trigger->id : "n/a");
160 LIST_FOREACH(value, v, t->values) {
162 if (v->base == TIMER_CALENDAR) {
163 _cleanup_free_ char *p = NULL;
165 calendar_spec_to_string(v->calendar_spec, &p);
170 timer_base_to_string(v->base),
173 char timespan1[FORMAT_TIMESPAN_MAX];
178 timer_base_to_string(v->base),
179 strna(format_timespan(timespan1, sizeof(timespan1), v->value, 0)));
184 static void timer_set_state(Timer *t, TimerState state) {
185 TimerState old_state;
188 old_state = t->state;
191 if (state != TIMER_WAITING) {
192 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
193 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
196 if (state != old_state)
197 log_debug_unit(UNIT(t)->id,
198 "%s changed %s -> %s", UNIT(t)->id,
199 timer_state_to_string(old_state),
200 timer_state_to_string(state));
202 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
205 static void timer_enter_waiting(Timer *t, bool initial);
207 static int timer_coldplug(Unit *u) {
211 assert(t->state == TIMER_DEAD);
213 if (t->deserialized_state != t->state) {
215 if (t->deserialized_state == TIMER_WAITING)
216 timer_enter_waiting(t, false);
218 timer_set_state(t, t->deserialized_state);
224 static void timer_enter_dead(Timer *t, TimerResult f) {
227 if (f != TIMER_SUCCESS)
230 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
233 static void timer_enter_waiting(Timer *t, bool initial) {
237 bool found_monotonic = false, found_realtime = false;
240 dual_timestamp_get(&ts);
241 t->next_elapse_monotonic = t->next_elapse_realtime = 0;
243 LIST_FOREACH(value, v, t->values) {
248 if (v->base == TIMER_CALENDAR) {
250 r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
254 if (!initial && v->next_elapse < ts.realtime) {
260 t->next_elapse_realtime = v->next_elapse;
262 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
264 found_realtime = true;
270 if (state_translation_table[t->state] == UNIT_ACTIVE)
271 base = UNIT(t)->inactive_exit_timestamp.monotonic;
277 /* CLOCK_MONOTONIC equals the uptime on Linux */
282 base = UNIT(t)->manager->userspace_timestamp.monotonic;
285 case TIMER_UNIT_ACTIVE:
287 if (UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic <= 0)
290 base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
293 case TIMER_UNIT_INACTIVE:
295 if (UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic <= 0)
298 base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
302 assert_not_reached("Unknown timer base");
305 v->next_elapse = base + v->value;
307 if (!initial && v->next_elapse < ts.monotonic) {
312 if (!found_monotonic)
313 t->next_elapse_monotonic = v->next_elapse;
315 t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
317 found_monotonic = true;
321 if (!found_monotonic && !found_realtime) {
322 log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
323 timer_set_state(t, TIMER_ELAPSED);
327 if (found_monotonic) {
328 char buf[FORMAT_TIMESPAN_MAX];
329 log_debug_unit(UNIT(t)->id,
330 "%s: Monotonic timer elapses in %s.",
332 format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0, 0));
334 r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch);
338 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
340 if (found_realtime) {
341 char buf[FORMAT_TIMESTAMP_MAX];
342 log_debug_unit(UNIT(t)->id,
343 "%s: Realtime timer elapses at %s.",
345 format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
347 r = unit_watch_timer(UNIT(t), CLOCK_REALTIME, false, t->next_elapse_realtime, &t->realtime_watch);
351 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
353 timer_set_state(t, TIMER_WAITING);
357 log_warning_unit(UNIT(t)->id,
358 "%s failed to enter waiting state: %s",
359 UNIT(t)->id, strerror(-r));
360 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
363 static void timer_enter_running(Timer *t) {
368 dbus_error_init(&error);
370 /* Don't start job if we are supposed to go down */
371 if (unit_pending_inactive(UNIT(t)))
374 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
375 JOB_REPLACE, true, &error, NULL);
379 timer_set_state(t, TIMER_RUNNING);
383 log_warning_unit(UNIT(t)->id,
384 "%s failed to queue unit startup job: %s",
385 UNIT(t)->id, bus_error(&error, r));
386 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
388 dbus_error_free(&error);
391 static int timer_start(Unit *u) {
395 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
397 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
400 t->result = TIMER_SUCCESS;
401 timer_enter_waiting(t, true);
405 static int timer_stop(Unit *u) {
409 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
411 timer_enter_dead(t, TIMER_SUCCESS);
415 static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
422 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
423 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
428 static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
436 if (streq(key, "state")) {
439 state = timer_state_from_string(value);
441 log_debug_unit(u->id, "Failed to parse state value %s", value);
443 t->deserialized_state = state;
444 } else if (streq(key, "result")) {
447 f = timer_result_from_string(value);
449 log_debug_unit(u->id, "Failed to parse result value %s", value);
450 else if (f != TIMER_SUCCESS)
454 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
459 static UnitActiveState timer_active_state(Unit *u) {
462 return state_translation_table[TIMER(u)->state];
465 static const char *timer_sub_state_to_string(Unit *u) {
468 return timer_state_to_string(TIMER(u)->state);
471 static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
475 assert(elapsed == 1);
477 if (t->state != TIMER_WAITING)
480 log_debug_unit(u->id, "Timer elapsed on %s", u->id);
481 timer_enter_running(t);
484 static void timer_trigger_notify(Unit *u, Unit *other) {
491 log_error("NOTIFY!");
493 if (other->load_state != UNIT_LOADED)
496 /* Reenable all timers that depend on unit state */
497 LIST_FOREACH(value, v, t->values)
498 if (v->base == TIMER_UNIT_ACTIVE ||
499 v->base == TIMER_UNIT_INACTIVE)
507 /* Recalculate sleep time */
508 timer_enter_waiting(t, false);
513 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
514 log_debug_unit(UNIT(t)->id,
515 "%s got notified about unit deactivation.",
517 timer_enter_waiting(t, false);
526 assert_not_reached("Unknown timer state");
530 static void timer_reset_failed(Unit *u) {
535 if (t->state == TIMER_FAILED)
536 timer_set_state(t, TIMER_DEAD);
538 t->result = TIMER_SUCCESS;
541 static void timer_time_change(Unit *u) {
546 if (t->state != TIMER_WAITING)
549 log_debug_unit(u->id,
550 "%s: time change, recalculating next elapse.", u->id);
551 timer_enter_waiting(t, false);
554 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
555 [TIMER_DEAD] = "dead",
556 [TIMER_WAITING] = "waiting",
557 [TIMER_RUNNING] = "running",
558 [TIMER_ELAPSED] = "elapsed",
559 [TIMER_FAILED] = "failed"
562 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
564 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
565 [TIMER_ACTIVE] = "OnActiveSec",
566 [TIMER_BOOT] = "OnBootSec",
567 [TIMER_STARTUP] = "OnStartupSec",
568 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
569 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
570 [TIMER_CALENDAR] = "OnCalendar"
573 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
575 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
576 [TIMER_SUCCESS] = "success",
577 [TIMER_FAILURE_RESOURCES] = "resources"
580 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
582 const UnitVTable timer_vtable = {
583 .object_size = sizeof(Timer),
593 .coldplug = timer_coldplug,
597 .start = timer_start,
600 .serialize = timer_serialize,
601 .deserialize_item = timer_deserialize_item,
603 .active_state = timer_active_state,
604 .sub_state_to_string = timer_sub_state_to_string,
606 .timer_event = timer_timer_event,
608 .trigger_notify = timer_trigger_notify,
610 .reset_failed = timer_reset_failed,
611 .time_change = timer_time_change,
613 .bus_interface = "org.freedesktop.systemd1.Timer",
614 .bus_message_handler = bus_timer_message_handler,
615 .bus_invalidating_properties = bus_timer_invalidating_properties