chiark / gitweb /
timer: support timers that can resume the system from suspend
[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 = USEC_PER_MINUTE;
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                 _cleanup_close_ int fd = -1;
501
502                 mkdir_parents_label(t->stamp_path, 0755);
503
504                 /* Update the file atime + mtime, if we can */
505                 fd = open(t->stamp_path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0644);
506                 if (fd >= 0) {
507                         struct timespec ts[2];
508
509                         timespec_store(&ts[0], t->last_trigger.realtime);
510                         ts[1] = ts[0];
511
512                         futimens(fd, ts);
513                 }
514         }
515
516         timer_set_state(t, TIMER_RUNNING);
517         return;
518
519 fail:
520         log_warning_unit(UNIT(t)->id,
521                          "%s failed to queue unit startup job: %s",
522                          UNIT(t)->id, bus_error_message(&error, r));
523         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
524 }
525
526 static int timer_start(Unit *u) {
527         Timer *t = TIMER(u);
528
529         assert(t);
530         assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
531
532         if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
533                 return -ENOENT;
534
535         t->last_trigger = DUAL_TIMESTAMP_NULL;
536
537         if (t->stamp_path) {
538                 struct stat st;
539
540                 if (stat(t->stamp_path, &st) >= 0)
541                         t->last_trigger.realtime = timespec_load(&st.st_atim);
542         }
543
544         t->result = TIMER_SUCCESS;
545         timer_enter_waiting(t, true);
546         return 0;
547 }
548
549 static int timer_stop(Unit *u) {
550         Timer *t = TIMER(u);
551
552         assert(t);
553         assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
554
555         timer_enter_dead(t, TIMER_SUCCESS);
556         return 0;
557 }
558
559 static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
560         Timer *t = TIMER(u);
561
562         assert(u);
563         assert(f);
564         assert(fds);
565
566         unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
567         unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
568
569         if (t->last_trigger.realtime > 0)
570                 unit_serialize_item_format(u, f, "last-trigger-realtime", "%" PRIu64, t->last_trigger.realtime);
571
572         if (t->last_trigger.monotonic > 0)
573                 unit_serialize_item_format(u, f, "last-trigger-monotonic", "%" PRIu64, t->last_trigger.monotonic);
574
575         return 0;
576 }
577
578 static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
579         Timer *t = TIMER(u);
580         int r;
581
582         assert(u);
583         assert(key);
584         assert(value);
585         assert(fds);
586
587         if (streq(key, "state")) {
588                 TimerState state;
589
590                 state = timer_state_from_string(value);
591                 if (state < 0)
592                         log_debug_unit(u->id, "Failed to parse state value %s", value);
593                 else
594                         t->deserialized_state = state;
595         } else if (streq(key, "result")) {
596                 TimerResult f;
597
598                 f = timer_result_from_string(value);
599                 if (f < 0)
600                         log_debug_unit(u->id, "Failed to parse result value %s", value);
601                 else if (f != TIMER_SUCCESS)
602                         t->result = f;
603         } else if (streq(key, "last-trigger-realtime")) {
604
605                 r = safe_atou64(value, &t->last_trigger.realtime);
606                 if (r < 0)
607                         log_debug_unit(u->id, "Failed to parse last-trigger-realtime value %s", value);
608
609         } else if (streq(key, "last-trigger-monotonic")) {
610
611                 r = safe_atou64(value, &t->last_trigger.monotonic);
612                 if (r < 0)
613                         log_debug_unit(u->id, "Failed to parse last-trigger-monotonic value %s", value);
614
615         } else
616                 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
617
618         return 0;
619 }
620
621 _pure_ static UnitActiveState timer_active_state(Unit *u) {
622         assert(u);
623
624         return state_translation_table[TIMER(u)->state];
625 }
626
627 _pure_ static const char *timer_sub_state_to_string(Unit *u) {
628         assert(u);
629
630         return timer_state_to_string(TIMER(u)->state);
631 }
632
633 static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
634         Timer *t = TIMER(userdata);
635
636         assert(t);
637
638         if (t->state != TIMER_WAITING)
639                 return 0;
640
641         log_debug_unit(UNIT(t)->id, "Timer elapsed on %s", UNIT(t)->id);
642         timer_enter_running(t);
643         return 0;
644 }
645
646 static void timer_trigger_notify(Unit *u, Unit *other) {
647         Timer *t = TIMER(u);
648         TimerValue *v;
649
650         assert(u);
651         assert(other);
652
653         if (other->load_state != UNIT_LOADED)
654                 return;
655
656         /* Reenable all timers that depend on unit state */
657         LIST_FOREACH(value, v, t->values)
658                 if (v->base == TIMER_UNIT_ACTIVE ||
659                     v->base == TIMER_UNIT_INACTIVE)
660                         v->disabled = false;
661
662         switch (t->state) {
663
664         case TIMER_WAITING:
665         case TIMER_ELAPSED:
666
667                 /* Recalculate sleep time */
668                 timer_enter_waiting(t, false);
669                 break;
670
671         case TIMER_RUNNING:
672
673                 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
674                         log_debug_unit(UNIT(t)->id, "%s got notified about unit deactivation.", UNIT(t)->id);
675                         timer_enter_waiting(t, false);
676                 }
677                 break;
678
679         case TIMER_DEAD:
680         case TIMER_FAILED:
681                 break;
682
683         default:
684                 assert_not_reached("Unknown timer state");
685         }
686 }
687
688 static void timer_reset_failed(Unit *u) {
689         Timer *t = TIMER(u);
690
691         assert(t);
692
693         if (t->state == TIMER_FAILED)
694                 timer_set_state(t, TIMER_DEAD);
695
696         t->result = TIMER_SUCCESS;
697 }
698
699 static void timer_time_change(Unit *u) {
700         Timer *t = TIMER(u);
701
702         assert(u);
703
704         if (t->state != TIMER_WAITING)
705                 return;
706
707         log_debug_unit(u->id, "%s: time change, recalculating next elapse.", u->id);
708         timer_enter_waiting(t, false);
709 }
710
711 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
712         [TIMER_DEAD] = "dead",
713         [TIMER_WAITING] = "waiting",
714         [TIMER_RUNNING] = "running",
715         [TIMER_ELAPSED] = "elapsed",
716         [TIMER_FAILED] = "failed"
717 };
718
719 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
720
721 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
722         [TIMER_ACTIVE] = "OnActiveSec",
723         [TIMER_BOOT] = "OnBootSec",
724         [TIMER_STARTUP] = "OnStartupSec",
725         [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
726         [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
727         [TIMER_CALENDAR] = "OnCalendar"
728 };
729
730 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
731
732 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
733         [TIMER_SUCCESS] = "success",
734         [TIMER_FAILURE_RESOURCES] = "resources"
735 };
736
737 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
738
739 const UnitVTable timer_vtable = {
740         .object_size = sizeof(Timer),
741
742         .sections =
743                 "Unit\0"
744                 "Timer\0"
745                 "Install\0",
746
747         .init = timer_init,
748         .done = timer_done,
749         .load = timer_load,
750
751         .coldplug = timer_coldplug,
752
753         .dump = timer_dump,
754
755         .start = timer_start,
756         .stop = timer_stop,
757
758         .serialize = timer_serialize,
759         .deserialize_item = timer_deserialize_item,
760
761         .active_state = timer_active_state,
762         .sub_state_to_string = timer_sub_state_to_string,
763
764         .trigger_notify = timer_trigger_notify,
765
766         .reset_failed = timer_reset_failed,
767         .time_change = timer_time_change,
768
769         .bus_interface = "org.freedesktop.systemd1.Timer",
770         .bus_vtable = bus_timer_vtable,
771 };