]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/timer.c
update TODO
[thirdparty/systemd.git] / src / core / timer.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
5cb5a6ff 2
a7334b09
LP
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
a7334b09
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
d46de8a1
LP
22#include <errno.h>
23
87f0e418 24#include "unit.h"
871d7de4 25#include "unit-name.h"
5cb5a6ff 26#include "timer.h"
871d7de4 27#include "dbus-timer.h"
a40eb732 28#include "special.h"
398ef8ba 29#include "bus-errors.h"
871d7de4
LP
30
31static 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,
fdf20a31 36 [TIMER_FAILED] = UNIT_FAILED
871d7de4
LP
37};
38
39static void timer_init(Unit *u) {
40 Timer *t = TIMER(u);
41
42 assert(u);
ac155bb8 43 assert(u->load_state == UNIT_STUB);
871d7de4 44
36697dc0
LP
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);
871d7de4 49}
5cb5a6ff 50
74051b9b 51void timer_free_values(Timer *t) {
871d7de4
LP
52 TimerValue *v;
53
54 assert(t);
55
56 while ((v = t->values)) {
57 LIST_REMOVE(TimerValue, value, t->values, v);
36697dc0
LP
58
59 if (v->calendar_spec)
60 calendar_spec_free(v->calendar_spec);
61
871d7de4
LP
62 free(v);
63 }
74051b9b
LP
64}
65
66static void timer_done(Unit *u) {
67 Timer *t = TIMER(u);
68
69 assert(t);
70
71 timer_free_values(t);
871d7de4 72
36697dc0
LP
73 unit_unwatch_timer(u, &t->monotonic_watch);
74 unit_unwatch_timer(u, &t->realtime_watch);
57020a3a
LP
75
76 unit_ref_unset(&t->unit);
871d7de4
LP
77}
78
79static int timer_verify(Timer *t) {
80 assert(t);
81
1124fe6f 82 if (UNIT(t)->load_state != UNIT_LOADED)
871d7de4
LP
83 return 0;
84
85 if (!t->values) {
66870f90
ZJS
86 log_error_unit(UNIT(t)->id,
87 "%s lacks value setting. Refusing.", UNIT(t)->id);
871d7de4
LP
88 return -EINVAL;
89 }
90
91 return 0;
92}
93
6c155fe3
LP
94static int timer_add_default_dependencies(Timer *t) {
95 int r;
96
97 assert(t);
98
e3d84721
LP
99 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
100 if (r < 0)
101 return r;
2a77d31d 102
e3d84721 103 if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
36697dc0
LP
104 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
105 if (r < 0)
6c155fe3 106 return r;
2a77d31d 107 }
6c155fe3 108
ead8e478 109 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
6c155fe3
LP
110}
111
871d7de4
LP
112static int timer_load(Unit *u) {
113 Timer *t = TIMER(u);
114 int r;
115
116 assert(u);
ac155bb8 117 assert(u->load_state == UNIT_STUB);
871d7de4 118
36697dc0
LP
119 r = unit_load_fragment_and_dropin(u);
120 if (r < 0)
871d7de4
LP
121 return r;
122
ac155bb8 123 if (u->load_state == UNIT_LOADED) {
871d7de4 124
57020a3a
LP
125 if (!UNIT_DEREF(t->unit)) {
126 Unit *x;
127
128 r = unit_load_related_unit(u, ".service", &x);
129 if (r < 0)
871d7de4
LP
130 return r;
131
57020a3a
LP
132 unit_ref_set(&t->unit, x);
133 }
134
135 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true);
136 if (r < 0)
871d7de4 137 return r;
a40eb732 138
36697dc0
LP
139 if (UNIT(t)->default_dependencies) {
140 r = timer_add_default_dependencies(t);
141 if (r < 0)
a40eb732 142 return r;
36697dc0 143 }
871d7de4
LP
144 }
145
146 return timer_verify(t);
147}
148
149static void timer_dump(Unit *u, FILE *f, const char *prefix) {
150 Timer *t = TIMER(u);
871d7de4 151 TimerValue *v;
034c6ed7 152
871d7de4
LP
153 fprintf(f,
154 "%sTimer State: %s\n"
067d72c9 155 "%sResult: %s\n"
871d7de4
LP
156 "%sUnit: %s\n",
157 prefix, timer_state_to_string(t->state),
067d72c9 158 prefix, timer_result_to_string(t->result),
ac155bb8 159 prefix, UNIT_DEREF(t->unit)->id);
871d7de4 160
36697dc0
LP
161 LIST_FOREACH(value, v, t->values) {
162
163 if (v->base == TIMER_CALENDAR) {
164 _cleanup_free_ char *p = NULL;
165
166 calendar_spec_to_string(v->calendar_spec, &p);
167
168 fprintf(f,
169 "%s%s: %s\n",
170 prefix,
171 timer_base_to_string(v->base),
172 strna(p));
173 } else {
174 char timespan1[FORMAT_TIMESPAN_MAX];
175
176 fprintf(f,
177 "%s%s: %s\n",
178 prefix,
179 timer_base_to_string(v->base),
180 strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
181 }
182 }
871d7de4
LP
183}
184
185static void timer_set_state(Timer *t, TimerState state) {
186 TimerState old_state;
034c6ed7 187 assert(t);
871d7de4
LP
188
189 old_state = t->state;
190 t->state = state;
191
36697dc0
LP
192 if (state != TIMER_WAITING) {
193 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
194 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
195 }
871d7de4
LP
196
197 if (state != old_state)
66870f90
ZJS
198 log_debug_unit(UNIT(t)->id,
199 "%s changed %s -> %s", UNIT(t)->id,
200 timer_state_to_string(old_state),
201 timer_state_to_string(state));
871d7de4 202
e2f3b44c 203 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
871d7de4
LP
204}
205
206static void timer_enter_waiting(Timer *t, bool initial);
207
208static int timer_coldplug(Unit *u) {
209 Timer *t = TIMER(u);
210
211 assert(t);
212 assert(t->state == TIMER_DEAD);
213
214 if (t->deserialized_state != t->state) {
215
b363ca6f 216 if (t->deserialized_state == TIMER_WAITING)
871d7de4
LP
217 timer_enter_waiting(t, false);
218 else
219 timer_set_state(t, t->deserialized_state);
220 }
221
222 return 0;
223}
224
067d72c9 225static void timer_enter_dead(Timer *t, TimerResult f) {
871d7de4
LP
226 assert(t);
227
067d72c9
LP
228 if (f != TIMER_SUCCESS)
229 t->result = f;
871d7de4 230
067d72c9 231 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
871d7de4
LP
232}
233
234static void timer_enter_waiting(Timer *t, bool initial) {
235 TimerValue *v;
36697dc0
LP
236 usec_t base = 0;
237 dual_timestamp ts;
238 bool found_monotonic = false, found_realtime = false;
871d7de4
LP
239 int r;
240
36697dc0
LP
241 dual_timestamp_get(&ts);
242 t->next_elapse_monotonic = t->next_elapse_realtime = 0;
871d7de4
LP
243
244 LIST_FOREACH(value, v, t->values) {
245
246 if (v->disabled)
247 continue;
248
36697dc0
LP
249 if (v->base == TIMER_CALENDAR) {
250
251 r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
252 if (r < 0)
253 continue;
254
255 if (!initial && v->next_elapse < ts.realtime) {
256 v->disabled = true;
257 continue;
258 }
871d7de4 259
36697dc0
LP
260 if (!found_realtime)
261 t->next_elapse_realtime = v->next_elapse;
10717a1a 262 else
36697dc0 263 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
871d7de4 264
36697dc0 265 found_realtime = true;
871d7de4 266
36697dc0
LP
267 } else {
268 switch (v->base) {
871d7de4 269
36697dc0
LP
270 case TIMER_ACTIVE:
271 if (state_translation_table[t->state] == UNIT_ACTIVE)
272 base = UNIT(t)->inactive_exit_timestamp.monotonic;
273 else
274 base = ts.monotonic;
275 break;
871d7de4 276
36697dc0
LP
277 case TIMER_BOOT:
278 /* CLOCK_MONOTONIC equals the uptime on Linux */
279 base = 0;
280 break;
871d7de4 281
36697dc0
LP
282 case TIMER_STARTUP:
283 base = UNIT(t)->manager->userspace_timestamp.monotonic;
284 break;
871d7de4 285
36697dc0 286 case TIMER_UNIT_ACTIVE:
871d7de4 287
36697dc0
LP
288 if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
289 continue;
871d7de4 290
36697dc0
LP
291 base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
292 break;
871d7de4 293
36697dc0 294 case TIMER_UNIT_INACTIVE:
871d7de4 295
36697dc0
LP
296 if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
297 continue;
871d7de4 298
36697dc0
LP
299 base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
300 break;
871d7de4 301
36697dc0
LP
302 default:
303 assert_not_reached("Unknown timer base");
304 }
871d7de4 305
36697dc0
LP
306 v->next_elapse = base + v->value;
307
308 if (!initial && v->next_elapse < ts.monotonic) {
309 v->disabled = true;
310 continue;
311 }
312
313 if (!found_monotonic)
314 t->next_elapse_monotonic = v->next_elapse;
315 else
316 t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
317
318 found_monotonic = true;
319 }
871d7de4
LP
320 }
321
36697dc0 322 if (!found_monotonic && !found_realtime) {
66870f90 323 log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
871d7de4
LP
324 timer_set_state(t, TIMER_ELAPSED);
325 return;
326 }
327
36697dc0
LP
328 if (found_monotonic) {
329 char buf[FORMAT_TIMESPAN_MAX];
66870f90 330 log_debug_unit(UNIT(t)->id,
1a512bf4 331 "%s: Monotonic timer elapses in %s.",
66870f90 332 UNIT(t)->id,
b719810d 333 format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0));
871d7de4 334
36697dc0
LP
335 r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch);
336 if (r < 0)
337 goto fail;
338 } else
339 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
340
341 if (found_realtime) {
342 char buf[FORMAT_TIMESTAMP_MAX];
66870f90 343 log_debug_unit(UNIT(t)->id,
1a512bf4 344 "%s: Realtime timer elapses at %s.",
66870f90
ZJS
345 UNIT(t)->id,
346 format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
36697dc0
LP
347
348 r = unit_watch_timer(UNIT(t), CLOCK_REALTIME, false, t->next_elapse_realtime, &t->realtime_watch);
349 if (r < 0)
350 goto fail;
351 } else
352 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
871d7de4
LP
353
354 timer_set_state(t, TIMER_WAITING);
355 return;
356
357fail:
66870f90
ZJS
358 log_warning_unit(UNIT(t)->id,
359 "%s failed to enter waiting state: %s",
360 UNIT(t)->id, strerror(-r));
067d72c9 361 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
362}
363
364static void timer_enter_running(Timer *t) {
398ef8ba 365 DBusError error;
871d7de4 366 int r;
398ef8ba 367
871d7de4 368 assert(t);
398ef8ba 369 dbus_error_init(&error);
871d7de4 370
ba3e67a7 371 /* Don't start job if we are supposed to go down */
1124fe6f 372 if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
ba3e67a7
LP
373 return;
374
36697dc0
LP
375 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL);
376 if (r < 0)
871d7de4
LP
377 goto fail;
378
379 timer_set_state(t, TIMER_RUNNING);
380 return;
381
382fail:
66870f90
ZJS
383 log_warning_unit(UNIT(t)->id,
384 "%s failed to queue unit startup job: %s",
385 UNIT(t)->id, bus_error(&error, r));
067d72c9 386 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
398ef8ba
LP
387
388 dbus_error_free(&error);
871d7de4
LP
389}
390
391static int timer_start(Unit *u) {
392 Timer *t = TIMER(u);
393
394 assert(t);
fdf20a31 395 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
01f78473 396
ac155bb8 397 if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
01f78473 398 return -ENOENT;
871d7de4 399
067d72c9 400 t->result = TIMER_SUCCESS;
871d7de4
LP
401 timer_enter_waiting(t, true);
402 return 0;
403}
404
405static int timer_stop(Unit *u) {
406 Timer *t = TIMER(u);
407
408 assert(t);
409 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
410
067d72c9 411 timer_enter_dead(t, TIMER_SUCCESS);
871d7de4
LP
412 return 0;
413}
414
415static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
416 Timer *t = TIMER(u);
417
418 assert(u);
419 assert(f);
420 assert(fds);
421
422 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
067d72c9 423 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
871d7de4
LP
424
425 return 0;
426}
427
428static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
429 Timer *t = TIMER(u);
430
431 assert(u);
432 assert(key);
433 assert(value);
434 assert(fds);
435
436 if (streq(key, "state")) {
437 TimerState state;
438
36697dc0
LP
439 state = timer_state_from_string(value);
440 if (state < 0)
66870f90 441 log_debug_unit(u->id, "Failed to parse state value %s", value);
871d7de4
LP
442 else
443 t->deserialized_state = state;
067d72c9
LP
444 } else if (streq(key, "result")) {
445 TimerResult f;
446
447 f = timer_result_from_string(value);
448 if (f < 0)
66870f90 449 log_debug_unit(u->id, "Failed to parse result value %s", value);
067d72c9
LP
450 else if (f != TIMER_SUCCESS)
451 t->result = f;
452
871d7de4 453 } else
66870f90 454 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
871d7de4
LP
455
456 return 0;
034c6ed7
LP
457}
458
87f0e418 459static UnitActiveState timer_active_state(Unit *u) {
871d7de4
LP
460 assert(u);
461
462 return state_translation_table[TIMER(u)->state];
463}
464
465static const char *timer_sub_state_to_string(Unit *u) {
466 assert(u);
467
468 return timer_state_to_string(TIMER(u)->state);
469}
470
471static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
472 Timer *t = TIMER(u);
473
474 assert(t);
475 assert(elapsed == 1);
5cb5a6ff 476
871d7de4
LP
477 if (t->state != TIMER_WAITING)
478 return;
5cb5a6ff 479
66870f90 480 log_debug_unit(u->id, "Timer elapsed on %s", u->id);
871d7de4 481 timer_enter_running(t);
5cb5a6ff
LP
482}
483
871d7de4 484void timer_unit_notify(Unit *u, UnitActiveState new_state) {
871d7de4 485 Iterator i;
57020a3a 486 Unit *k;
871d7de4 487
ac155bb8 488 if (u->type == UNIT_TIMER)
871d7de4
LP
489 return;
490
ac155bb8 491 SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
871d7de4
LP
492 Timer *t;
493 TimerValue *v;
494
ac155bb8 495 if (k->type != UNIT_TIMER)
871d7de4
LP
496 continue;
497
ac155bb8 498 if (k->load_state != UNIT_LOADED)
01f78473
LP
499 continue;
500
57020a3a 501 t = TIMER(k);
871d7de4
LP
502
503 /* Reenable all timers that depend on unit state */
504 LIST_FOREACH(value, v, t->values)
505 if (v->base == TIMER_UNIT_ACTIVE ||
506 v->base == TIMER_UNIT_INACTIVE)
507 v->disabled = false;
508
509 switch (t->state) {
510
511 case TIMER_WAITING:
512 case TIMER_ELAPSED:
513
514 /* Recalculate sleep time */
515 timer_enter_waiting(t, false);
516 break;
517
518 case TIMER_RUNNING:
519
fdf20a31 520 if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
66870f90
ZJS
521 log_debug_unit(UNIT(t)->id,
522 "%s got notified about unit deactivation.",
523 UNIT(t)->id);
871d7de4
LP
524 timer_enter_waiting(t, false);
525 }
526
527 break;
528
529 case TIMER_DEAD:
fdf20a31 530 case TIMER_FAILED:
0b021426 531 break;
871d7de4
LP
532
533 default:
534 assert_not_reached("Unknown timer state");
535 }
536 }
871d7de4
LP
537}
538
fdf20a31 539static void timer_reset_failed(Unit *u) {
5632e374
LP
540 Timer *t = TIMER(u);
541
542 assert(t);
543
fdf20a31 544 if (t->state == TIMER_FAILED)
5632e374
LP
545 timer_set_state(t, TIMER_DEAD);
546
067d72c9 547 t->result = TIMER_SUCCESS;
5632e374
LP
548}
549
8742514c
LP
550static void timer_time_change(Unit *u) {
551 Timer *t = TIMER(u);
552
553 assert(u);
554
555 if (t->state != TIMER_WAITING)
556 return;
557
c5962bd1
MS
558 log_debug_unit(u->id,
559 "%s: time change, recalculating next elapse.", u->id);
8742514c
LP
560 timer_enter_waiting(t, false);
561}
562
871d7de4
LP
563static const char* const timer_state_table[_TIMER_STATE_MAX] = {
564 [TIMER_DEAD] = "dead",
565 [TIMER_WAITING] = "waiting",
566 [TIMER_RUNNING] = "running",
567 [TIMER_ELAPSED] = "elapsed",
fdf20a31 568 [TIMER_FAILED] = "failed"
871d7de4
LP
569};
570
571DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
572
573static const char* const timer_base_table[_TIMER_BASE_MAX] = {
03fae018
LP
574 [TIMER_ACTIVE] = "OnActiveSec",
575 [TIMER_BOOT] = "OnBootSec",
576 [TIMER_STARTUP] = "OnStartupSec",
577 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
36697dc0
LP
578 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
579 [TIMER_CALENDAR] = "OnCalendar"
871d7de4
LP
580};
581
582DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
583
067d72c9
LP
584static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
585 [TIMER_SUCCESS] = "success",
586 [TIMER_FAILURE_RESOURCES] = "resources"
587};
588
589DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
590
87f0e418 591const UnitVTable timer_vtable = {
7d17cfbc 592 .object_size = sizeof(Timer),
f975e971
LP
593 .sections =
594 "Unit\0"
595 "Timer\0"
596 "Install\0",
5cb5a6ff 597
871d7de4 598 .init = timer_init,
034c6ed7 599 .done = timer_done,
871d7de4
LP
600 .load = timer_load,
601
602 .coldplug = timer_coldplug,
603
604 .dump = timer_dump,
605
606 .start = timer_start,
607 .stop = timer_stop,
608
609 .serialize = timer_serialize,
610 .deserialize_item = timer_deserialize_item,
611
612 .active_state = timer_active_state,
613 .sub_state_to_string = timer_sub_state_to_string,
614
615 .timer_event = timer_timer_event,
5cb5a6ff 616
fdf20a31 617 .reset_failed = timer_reset_failed,
8742514c 618 .time_change = timer_time_change,
5632e374 619
c4e2ceae
LP
620 .bus_interface = "org.freedesktop.systemd1.Timer",
621 .bus_message_handler = bus_timer_message_handler,
622 .bus_invalidating_properties = bus_timer_invalidating_properties
5cb5a6ff 623};