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