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