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