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