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