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