]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/timer.c
core: pull in dbus.socket from Type=dbus services
[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
b5efdb8a 24#include "alloc-util.h"
07630cea
LP
25#include "bus-error.h"
26#include "bus-util.h"
871d7de4 27#include "dbus-timer.h"
f4f15635 28#include "fs-util.h"
6bedfcbb 29#include "parse-util.h"
a40eb732 30#include "special.h"
8b43440b 31#include "string-table.h"
07630cea 32#include "string-util.h"
b1d4f8e1 33#include "timer.h"
07630cea
LP
34#include "unit-name.h"
35#include "unit.h"
b1d4f8e1 36#include "user-util.h"
c1d9ba99 37#include "virt.h"
871d7de4
LP
38
39static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
40 [TIMER_DEAD] = UNIT_INACTIVE,
41 [TIMER_WAITING] = UNIT_ACTIVE,
42 [TIMER_RUNNING] = UNIT_ACTIVE,
43 [TIMER_ELAPSED] = UNIT_ACTIVE,
fdf20a31 44 [TIMER_FAILED] = UNIT_FAILED
871d7de4
LP
45};
46
718db961
LP
47static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
48
871d7de4
LP
49static void timer_init(Unit *u) {
50 Timer *t = TIMER(u);
51
52 assert(u);
ac155bb8 53 assert(u->load_state == UNIT_STUB);
871d7de4 54
3a43da28
KS
55 t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
56 t->next_elapse_realtime = USEC_INFINITY;
bd8f585b 57 t->accuracy_usec = u->manager->default_timer_accuracy_usec;
871d7de4 58}
5cb5a6ff 59
74051b9b 60void timer_free_values(Timer *t) {
871d7de4
LP
61 TimerValue *v;
62
63 assert(t);
64
65 while ((v = t->values)) {
71fda00f 66 LIST_REMOVE(value, t->values, v);
3e044c49 67 calendar_spec_free(v->calendar_spec);
871d7de4
LP
68 free(v);
69 }
74051b9b
LP
70}
71
72static void timer_done(Unit *u) {
73 Timer *t = TIMER(u);
74
75 assert(t);
76
77 timer_free_values(t);
871d7de4 78
718db961
LP
79 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
80 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
06642d17
LP
81
82 free(t->stamp_path);
871d7de4
LP
83}
84
85static int timer_verify(Timer *t) {
86 assert(t);
87
1124fe6f 88 if (UNIT(t)->load_state != UNIT_LOADED)
871d7de4
LP
89 return 0;
90
91 if (!t->values) {
f2341e0a 92 log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing.");
871d7de4
LP
93 return -EINVAL;
94 }
95
96 return 0;
97}
98
6c155fe3
LP
99static int timer_add_default_dependencies(Timer *t) {
100 int r;
19f8d037 101 TimerValue *v;
6c155fe3
LP
102
103 assert(t);
104
e3d84721
LP
105 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
106 if (r < 0)
107 return r;
2a77d31d 108
b2c23da8 109 if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
36697dc0
LP
110 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
111 if (r < 0)
6c155fe3 112 return r;
19f8d037
TGR
113
114 LIST_FOREACH(value, v, t->values) {
115 if (v->base == TIMER_CALENDAR) {
116 r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true);
117 if (r < 0)
118 return r;
119 break;
120 }
121 }
2a77d31d 122 }
6c155fe3 123
ead8e478 124 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
6c155fe3
LP
125}
126
06642d17
LP
127static int timer_setup_persistent(Timer *t) {
128 int r;
129
130 assert(t);
131
132 if (!t->persistent)
133 return 0;
134
b2c23da8 135 if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
06642d17
LP
136
137 r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
138 if (r < 0)
139 return r;
140
141 t->stamp_path = strappend("/var/lib/systemd/timers/stamp-", UNIT(t)->id);
142 } else {
143 const char *e;
144
145 e = getenv("XDG_DATA_HOME");
146 if (e)
bd34b310 147 t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id, NULL);
06642d17
LP
148 else {
149
150 _cleanup_free_ char *h = NULL;
151
152 r = get_home_dir(&h);
23bbb0de 153 if (r < 0)
f2341e0a 154 return log_unit_error_errno(UNIT(t), r, "Failed to determine home directory: %m");
06642d17
LP
155
156 t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL);
157 }
158 }
159
160 if (!t->stamp_path)
161 return log_oom();
162
163 return 0;
164}
165
871d7de4
LP
166static int timer_load(Unit *u) {
167 Timer *t = TIMER(u);
168 int r;
169
170 assert(u);
ac155bb8 171 assert(u->load_state == UNIT_STUB);
871d7de4 172
36697dc0
LP
173 r = unit_load_fragment_and_dropin(u);
174 if (r < 0)
871d7de4
LP
175 return r;
176
ac155bb8 177 if (u->load_state == UNIT_LOADED) {
871d7de4 178
3ecaa09b 179 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
57020a3a
LP
180 Unit *x;
181
182 r = unit_load_related_unit(u, ".service", &x);
183 if (r < 0)
871d7de4
LP
184 return r;
185
3ecaa09b
LP
186 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
187 if (r < 0)
188 return r;
57020a3a
LP
189 }
190
06642d17
LP
191 r = timer_setup_persistent(t);
192 if (r < 0)
193 return r;
194
195 if (u->default_dependencies) {
36697dc0
LP
196 r = timer_add_default_dependencies(t);
197 if (r < 0)
a40eb732 198 return r;
36697dc0 199 }
871d7de4
LP
200 }
201
202 return timer_verify(t);
203}
204
205static void timer_dump(Unit *u, FILE *f, const char *prefix) {
9f5eb56a 206 char buf[FORMAT_TIMESPAN_MAX];
871d7de4 207 Timer *t = TIMER(u);
3ecaa09b 208 Unit *trigger;
871d7de4 209 TimerValue *v;
034c6ed7 210
3ecaa09b
LP
211 trigger = UNIT_TRIGGER(u);
212
871d7de4
LP
213 fprintf(f,
214 "%sTimer State: %s\n"
067d72c9 215 "%sResult: %s\n"
9f5eb56a 216 "%sUnit: %s\n"
dedabea4
LP
217 "%sPersistent: %s\n"
218 "%sWakeSystem: %s\n"
9f5eb56a 219 "%sAccuracy: %s\n",
871d7de4 220 prefix, timer_state_to_string(t->state),
067d72c9 221 prefix, timer_result_to_string(t->result),
9f5eb56a 222 prefix, trigger ? trigger->id : "n/a",
dedabea4
LP
223 prefix, yes_no(t->persistent),
224 prefix, yes_no(t->wake_system),
9f5eb56a 225 prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
871d7de4 226
36697dc0
LP
227 LIST_FOREACH(value, v, t->values) {
228
229 if (v->base == TIMER_CALENDAR) {
230 _cleanup_free_ char *p = NULL;
231
232 calendar_spec_to_string(v->calendar_spec, &p);
233
234 fprintf(f,
235 "%s%s: %s\n",
236 prefix,
237 timer_base_to_string(v->base),
238 strna(p));
239 } else {
240 char timespan1[FORMAT_TIMESPAN_MAX];
241
242 fprintf(f,
243 "%s%s: %s\n",
244 prefix,
245 timer_base_to_string(v->base),
b1d6dcf5 246 format_timespan(timespan1, sizeof(timespan1), v->value, 0));
36697dc0
LP
247 }
248 }
871d7de4
LP
249}
250
251static void timer_set_state(Timer *t, TimerState state) {
252 TimerState old_state;
034c6ed7 253 assert(t);
871d7de4
LP
254
255 old_state = t->state;
256 t->state = state;
257
36697dc0 258 if (state != TIMER_WAITING) {
718db961
LP
259 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
260 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
36697dc0 261 }
871d7de4
LP
262
263 if (state != old_state)
f2341e0a 264 log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state));
871d7de4 265
e2f3b44c 266 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
871d7de4
LP
267}
268
269static void timer_enter_waiting(Timer *t, bool initial);
270
be847e82 271static int timer_coldplug(Unit *u) {
871d7de4
LP
272 Timer *t = TIMER(u);
273
274 assert(t);
275 assert(t->state == TIMER_DEAD);
276
277 if (t->deserialized_state != t->state) {
278
be847e82
LP
279 if (t->deserialized_state == TIMER_WAITING)
280 timer_enter_waiting(t, false);
281 else
871d7de4
LP
282 timer_set_state(t, t->deserialized_state);
283 }
284
285 return 0;
286}
287
067d72c9 288static void timer_enter_dead(Timer *t, TimerResult f) {
871d7de4
LP
289 assert(t);
290
067d72c9
LP
291 if (f != TIMER_SUCCESS)
292 t->result = f;
871d7de4 293
067d72c9 294 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
871d7de4
LP
295}
296
dedabea4
LP
297static usec_t monotonic_to_boottime(usec_t t) {
298 usec_t a, b;
299
300 if (t <= 0)
301 return 0;
302
303 a = now(CLOCK_BOOTTIME);
304 b = now(CLOCK_MONOTONIC);
305
306 if (t + a > b)
307 return t + a - b;
308 else
309 return 0;
310}
311
871d7de4 312static void timer_enter_waiting(Timer *t, bool initial) {
36697dc0 313 bool found_monotonic = false, found_realtime = false;
dedabea4
LP
314 usec_t ts_realtime, ts_monotonic;
315 usec_t base = 0;
316 TimerValue *v;
871d7de4
LP
317 int r;
318
dedabea4
LP
319 /* If we shall wake the system we use the boottime clock
320 * rather than the monotonic clock. */
321
322 ts_realtime = now(CLOCK_REALTIME);
323 ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
324 t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
871d7de4
LP
325
326 LIST_FOREACH(value, v, t->values) {
327
328 if (v->disabled)
329 continue;
330
36697dc0 331 if (v->base == TIMER_CALENDAR) {
06642d17
LP
332 usec_t b;
333
334 /* If we know the last time this was
335 * triggered, schedule the job based relative
336 * to that. If we don't just start from
337 * now. */
36697dc0 338
dedabea4 339 b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime;
06642d17
LP
340
341 r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
36697dc0
LP
342 if (r < 0)
343 continue;
344
36697dc0
LP
345 if (!found_realtime)
346 t->next_elapse_realtime = v->next_elapse;
10717a1a 347 else
36697dc0 348 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
871d7de4 349
36697dc0 350 found_realtime = true;
871d7de4 351
36697dc0
LP
352 } else {
353 switch (v->base) {
871d7de4 354
36697dc0
LP
355 case TIMER_ACTIVE:
356 if (state_translation_table[t->state] == UNIT_ACTIVE)
357 base = UNIT(t)->inactive_exit_timestamp.monotonic;
358 else
dedabea4 359 base = ts_monotonic;
36697dc0 360 break;
871d7de4 361
36697dc0 362 case TIMER_BOOT:
c1d9ba99
MS
363 if (detect_container() <= 0) {
364 /* CLOCK_MONOTONIC equals the uptime on Linux */
365 base = 0;
366 break;
367 }
368 /* In a container we don't want to include the time the host
369 * was already up when the container started, so count from
370 * our own startup. Fall through. */
36697dc0
LP
371 case TIMER_STARTUP:
372 base = UNIT(t)->manager->userspace_timestamp.monotonic;
373 break;
871d7de4 374
36697dc0 375 case TIMER_UNIT_ACTIVE:
871d7de4 376
e41e1943
LP
377 base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
378
379 if (base <= 0)
06642d17 380 base = t->last_trigger.monotonic;
e41e1943
LP
381
382 if (base <= 0)
36697dc0 383 continue;
871d7de4 384
36697dc0 385 break;
871d7de4 386
36697dc0 387 case TIMER_UNIT_INACTIVE:
871d7de4 388
e41e1943
LP
389 base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
390
391 if (base <= 0)
06642d17 392 base = t->last_trigger.monotonic;
e41e1943
LP
393
394 if (base <= 0)
36697dc0 395 continue;
871d7de4 396
36697dc0 397 break;
871d7de4 398
36697dc0
LP
399 default:
400 assert_not_reached("Unknown timer base");
401 }
871d7de4 402
dedabea4
LP
403 if (t->wake_system)
404 base = monotonic_to_boottime(base);
405
36697dc0
LP
406 v->next_elapse = base + v->value;
407
dedabea4 408 if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
e41e1943 409 /* This is a one time trigger, disable it now */
36697dc0
LP
410 v->disabled = true;
411 continue;
412 }
413
414 if (!found_monotonic)
dedabea4 415 t->next_elapse_monotonic_or_boottime = v->next_elapse;
36697dc0 416 else
dedabea4 417 t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
36697dc0
LP
418
419 found_monotonic = true;
420 }
871d7de4
LP
421 }
422
36697dc0 423 if (!found_monotonic && !found_realtime) {
f2341e0a 424 log_unit_debug(UNIT(t), "Timer is elapsed.");
871d7de4
LP
425 timer_set_state(t, TIMER_ELAPSED);
426 return;
427 }
428
36697dc0
LP
429 if (found_monotonic) {
430 char buf[FORMAT_TIMESPAN_MAX];
dedabea4 431
f2341e0a 432 log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
871d7de4 433
718db961 434 if (t->monotonic_event_source) {
dedabea4 435 r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
718db961
LP
436 if (r < 0)
437 goto fail;
438
439 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
cbf60d0a
LP
440 if (r < 0)
441 goto fail;
442 } else {
443
6a0f1f6d
LP
444 r = sd_event_add_time(
445 UNIT(t)->manager->event,
446 &t->monotonic_event_source,
dedabea4
LP
447 t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
448 t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
6a0f1f6d 449 timer_dispatch, t);
cbf60d0a
LP
450 if (r < 0)
451 goto fail;
718db961 452
cbf60d0a
LP
453 (void) sd_event_source_set_description(t->monotonic_event_source, "timer-monotonic");
454 }
7dfbe2e3 455
718db961 456 } else if (t->monotonic_event_source) {
718db961 457
06642d17 458 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
718db961
LP
459 if (r < 0)
460 goto fail;
461 }
36697dc0
LP
462
463 if (found_realtime) {
464 char buf[FORMAT_TIMESTAMP_MAX];
f2341e0a 465 log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
36697dc0 466
718db961
LP
467 if (t->realtime_event_source) {
468 r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
469 if (r < 0)
470 goto fail;
471
472 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
cbf60d0a
LP
473 if (r < 0)
474 goto fail;
475 } else {
6a0f1f6d
LP
476 r = sd_event_add_time(
477 UNIT(t)->manager->event,
478 &t->realtime_event_source,
dedabea4 479 t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
6a0f1f6d
LP
480 t->next_elapse_realtime, t->accuracy_usec,
481 timer_dispatch, t);
cbf60d0a
LP
482 if (r < 0)
483 goto fail;
718db961 484
cbf60d0a
LP
485 (void) sd_event_source_set_description(t->realtime_event_source, "timer-realtime");
486 }
7dfbe2e3 487
718db961 488 } else if (t->realtime_event_source) {
718db961 489
06642d17 490 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
718db961
LP
491 if (r < 0)
492 goto fail;
493 }
871d7de4
LP
494
495 timer_set_state(t, TIMER_WAITING);
496 return;
497
498fail:
f2341e0a 499 log_unit_warning_errno(UNIT(t), r, "Failed to enter waiting state: %m");
067d72c9 500 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
501}
502
503static void timer_enter_running(Timer *t) {
718db961 504 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
871d7de4 505 int r;
398ef8ba 506
871d7de4
LP
507 assert(t);
508
ba3e67a7 509 /* Don't start job if we are supposed to go down */
31afa0a4 510 if (unit_stop_pending(UNIT(t)))
ba3e67a7
LP
511 return;
512
3ecaa09b
LP
513 r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
514 JOB_REPLACE, true, &error, NULL);
36697dc0 515 if (r < 0)
871d7de4
LP
516 goto fail;
517
06642d17
LP
518 dual_timestamp_get(&t->last_trigger);
519
472fc28f 520 if (t->stamp_path)
fed1e721 521 touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0);
e41e1943 522
871d7de4
LP
523 timer_set_state(t, TIMER_RUNNING);
524 return;
525
526fail:
f2341e0a 527 log_unit_warning(UNIT(t), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
067d72c9 528 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
529}
530
531static int timer_start(Unit *u) {
532 Timer *t = TIMER(u);
779042e7 533 TimerValue *v;
871d7de4
LP
534
535 assert(t);
fdf20a31 536 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
01f78473 537
3ecaa09b 538 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
01f78473 539 return -ENOENT;
871d7de4 540
06642d17
LP
541 t->last_trigger = DUAL_TIMESTAMP_NULL;
542
779042e7
MC
543 /* Reenable all timers that depend on unit activation time */
544 LIST_FOREACH(value, v, t->values)
545 if (v->base == TIMER_ACTIVE)
546 v->disabled = false;
547
06642d17
LP
548 if (t->stamp_path) {
549 struct stat st;
550
551 if (stat(t->stamp_path, &st) >= 0)
552 t->last_trigger.realtime = timespec_load(&st.st_atim);
472fc28f
TB
553 else if (errno == ENOENT)
554 /* The timer has never run before,
555 * make sure a stamp file exists.
556 */
46bcf492 557 touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
06642d17
LP
558 }
559
067d72c9 560 t->result = TIMER_SUCCESS;
871d7de4 561 timer_enter_waiting(t, true);
82a2b6bb 562 return 1;
871d7de4
LP
563}
564
565static int timer_stop(Unit *u) {
566 Timer *t = TIMER(u);
567
568 assert(t);
569 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
570
067d72c9 571 timer_enter_dead(t, TIMER_SUCCESS);
82a2b6bb 572 return 1;
871d7de4
LP
573}
574
575static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
576 Timer *t = TIMER(u);
577
578 assert(u);
579 assert(f);
580 assert(fds);
581
582 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
067d72c9 583 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
871d7de4 584
06642d17
LP
585 if (t->last_trigger.realtime > 0)
586 unit_serialize_item_format(u, f, "last-trigger-realtime", "%" PRIu64, t->last_trigger.realtime);
587
588 if (t->last_trigger.monotonic > 0)
589 unit_serialize_item_format(u, f, "last-trigger-monotonic", "%" PRIu64, t->last_trigger.monotonic);
590
871d7de4
LP
591 return 0;
592}
593
594static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
595 Timer *t = TIMER(u);
06642d17 596 int r;
871d7de4
LP
597
598 assert(u);
599 assert(key);
600 assert(value);
601 assert(fds);
602
603 if (streq(key, "state")) {
604 TimerState state;
605
36697dc0
LP
606 state = timer_state_from_string(value);
607 if (state < 0)
f2341e0a 608 log_unit_debug(u, "Failed to parse state value: %s", value);
871d7de4
LP
609 else
610 t->deserialized_state = state;
067d72c9
LP
611 } else if (streq(key, "result")) {
612 TimerResult f;
613
614 f = timer_result_from_string(value);
615 if (f < 0)
f2341e0a 616 log_unit_debug(u, "Failed to parse result value: %s", value);
067d72c9
LP
617 else if (f != TIMER_SUCCESS)
618 t->result = f;
06642d17
LP
619 } else if (streq(key, "last-trigger-realtime")) {
620
621 r = safe_atou64(value, &t->last_trigger.realtime);
622 if (r < 0)
f2341e0a 623 log_unit_debug(u, "Failed to parse last-trigger-realtime value: %s", value);
06642d17
LP
624
625 } else if (streq(key, "last-trigger-monotonic")) {
626
627 r = safe_atou64(value, &t->last_trigger.monotonic);
628 if (r < 0)
f2341e0a 629 log_unit_debug(u, "Failed to parse last-trigger-monotonic value: %s", value);
067d72c9 630
871d7de4 631 } else
f2341e0a 632 log_unit_debug(u, "Unknown serialization key: %s", key);
871d7de4
LP
633
634 return 0;
034c6ed7
LP
635}
636
44a6b1b6 637_pure_ static UnitActiveState timer_active_state(Unit *u) {
871d7de4
LP
638 assert(u);
639
640 return state_translation_table[TIMER(u)->state];
641}
642
44a6b1b6 643_pure_ static const char *timer_sub_state_to_string(Unit *u) {
871d7de4
LP
644 assert(u);
645
646 return timer_state_to_string(TIMER(u)->state);
647}
648
718db961
LP
649static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
650 Timer *t = TIMER(userdata);
871d7de4
LP
651
652 assert(t);
5cb5a6ff 653
871d7de4 654 if (t->state != TIMER_WAITING)
718db961 655 return 0;
5cb5a6ff 656
f2341e0a 657 log_unit_debug(UNIT(t), "Timer elapsed.");
871d7de4 658 timer_enter_running(t);
718db961 659 return 0;
5cb5a6ff
LP
660}
661
3ecaa09b
LP
662static void timer_trigger_notify(Unit *u, Unit *other) {
663 Timer *t = TIMER(u);
664 TimerValue *v;
871d7de4 665
3ecaa09b
LP
666 assert(u);
667 assert(other);
871d7de4 668
3ecaa09b
LP
669 if (other->load_state != UNIT_LOADED)
670 return;
871d7de4 671
3ecaa09b
LP
672 /* Reenable all timers that depend on unit state */
673 LIST_FOREACH(value, v, t->values)
674 if (v->base == TIMER_UNIT_ACTIVE ||
675 v->base == TIMER_UNIT_INACTIVE)
676 v->disabled = false;
01f78473 677
3ecaa09b 678 switch (t->state) {
871d7de4 679
3ecaa09b
LP
680 case TIMER_WAITING:
681 case TIMER_ELAPSED:
871d7de4 682
3ecaa09b
LP
683 /* Recalculate sleep time */
684 timer_enter_waiting(t, false);
685 break;
871d7de4 686
3ecaa09b 687 case TIMER_RUNNING:
871d7de4 688
3ecaa09b 689 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
f2341e0a 690 log_unit_debug(UNIT(t), "Got notified about unit deactivation.");
871d7de4 691 timer_enter_waiting(t, false);
3ecaa09b
LP
692 }
693 break;
871d7de4 694
3ecaa09b
LP
695 case TIMER_DEAD:
696 case TIMER_FAILED:
697 break;
871d7de4 698
3ecaa09b
LP
699 default:
700 assert_not_reached("Unknown timer state");
871d7de4 701 }
871d7de4
LP
702}
703
fdf20a31 704static void timer_reset_failed(Unit *u) {
5632e374
LP
705 Timer *t = TIMER(u);
706
707 assert(t);
708
fdf20a31 709 if (t->state == TIMER_FAILED)
5632e374
LP
710 timer_set_state(t, TIMER_DEAD);
711
067d72c9 712 t->result = TIMER_SUCCESS;
5632e374
LP
713}
714
8742514c
LP
715static void timer_time_change(Unit *u) {
716 Timer *t = TIMER(u);
717
718 assert(u);
719
720 if (t->state != TIMER_WAITING)
721 return;
722
f2341e0a 723 log_unit_debug(u, "Time change, recalculating next elapse.");
8742514c
LP
724 timer_enter_waiting(t, false);
725}
726
871d7de4 727static const char* const timer_base_table[_TIMER_BASE_MAX] = {
03fae018
LP
728 [TIMER_ACTIVE] = "OnActiveSec",
729 [TIMER_BOOT] = "OnBootSec",
730 [TIMER_STARTUP] = "OnStartupSec",
731 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
36697dc0
LP
732 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
733 [TIMER_CALENDAR] = "OnCalendar"
871d7de4
LP
734};
735
736DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
737
067d72c9
LP
738static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
739 [TIMER_SUCCESS] = "success",
740 [TIMER_FAILURE_RESOURCES] = "resources"
741};
742
743DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
744
87f0e418 745const UnitVTable timer_vtable = {
7d17cfbc 746 .object_size = sizeof(Timer),
718db961 747
f975e971
LP
748 .sections =
749 "Unit\0"
750 "Timer\0"
751 "Install\0",
d8a812d1 752 .private_section = "Timer",
5cb5a6ff 753
871d7de4 754 .init = timer_init,
034c6ed7 755 .done = timer_done,
871d7de4
LP
756 .load = timer_load,
757
758 .coldplug = timer_coldplug,
759
760 .dump = timer_dump,
761
762 .start = timer_start,
763 .stop = timer_stop,
764
765 .serialize = timer_serialize,
766 .deserialize_item = timer_deserialize_item,
767
768 .active_state = timer_active_state,
769 .sub_state_to_string = timer_sub_state_to_string,
770
3ecaa09b
LP
771 .trigger_notify = timer_trigger_notify,
772
fdf20a31 773 .reset_failed = timer_reset_failed,
8742514c 774 .time_change = timer_time_change,
5632e374 775
718db961 776 .bus_vtable = bus_timer_vtable,
d8a812d1
WC
777 .bus_set_property = bus_timer_set_property,
778
779 .can_transient = true,
5cb5a6ff 780};