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