chiark / gitweb /
treewide: auto-convert the simple cases to log_*_errno()
[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_INFINITY;
50         t->next_elapse_realtime = USEC_INFINITY;
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_unit_error(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         TimerValue *v;
99
100         assert(t);
101
102         r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
103         if (r < 0)
104                 return r;
105
106         if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
107                 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
108                 if (r < 0)
109                         return r;
110
111                 LIST_FOREACH(value, v, t->values) {
112                         if (v->base == TIMER_CALENDAR) {
113                                 r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true);
114                                 if (r < 0)
115                                         return r;
116                                 break;
117                         }
118                 }
119         }
120
121         return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
122 }
123
124 static int timer_setup_persistent(Timer *t) {
125         int r;
126
127         assert(t);
128
129         if (!t->persistent)
130                 return 0;
131
132         if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
133
134                 r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
135                 if (r < 0)
136                         return r;
137
138                 t->stamp_path = strappend("/var/lib/systemd/timers/stamp-", UNIT(t)->id);
139         } else {
140                 const char *e;
141
142                 e = getenv("XDG_DATA_HOME");
143                 if (e)
144                         t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id, NULL);
145                 else {
146
147                         _cleanup_free_ char *h = NULL;
148
149                         r = get_home_dir(&h);
150                         if (r < 0) {
151                                 log_error_errno(-r, "Failed to determine home directory: %m");
152                                 return r;
153                         }
154
155                         t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL);
156                 }
157         }
158
159         if (!t->stamp_path)
160                 return log_oom();
161
162         return 0;
163 }
164
165 static int timer_load(Unit *u) {
166         Timer *t = TIMER(u);
167         int r;
168
169         assert(u);
170         assert(u->load_state == UNIT_STUB);
171
172         r = unit_load_fragment_and_dropin(u);
173         if (r < 0)
174                 return r;
175
176         if (u->load_state == UNIT_LOADED) {
177
178                 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
179                         Unit *x;
180
181                         r = unit_load_related_unit(u, ".service", &x);
182                         if (r < 0)
183                                 return r;
184
185                         r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
186                         if (r < 0)
187                                 return r;
188                 }
189
190                 r = timer_setup_persistent(t);
191                 if (r < 0)
192                         return r;
193
194                 if (u->default_dependencies) {
195                         r = timer_add_default_dependencies(t);
196                         if (r < 0)
197                                 return r;
198                 }
199         }
200
201         return timer_verify(t);
202 }
203
204 static void timer_dump(Unit *u, FILE *f, const char *prefix) {
205         char buf[FORMAT_TIMESPAN_MAX];
206         Timer *t = TIMER(u);
207         Unit *trigger;
208         TimerValue *v;
209
210         trigger = UNIT_TRIGGER(u);
211
212         fprintf(f,
213                 "%sTimer State: %s\n"
214                 "%sResult: %s\n"
215                 "%sUnit: %s\n"
216                 "%sPersistent: %s\n"
217                 "%sWakeSystem: %s\n"
218                 "%sAccuracy: %s\n",
219                 prefix, timer_state_to_string(t->state),
220                 prefix, timer_result_to_string(t->result),
221                 prefix, trigger ? trigger->id : "n/a",
222                 prefix, yes_no(t->persistent),
223                 prefix, yes_no(t->wake_system),
224                 prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
225
226         LIST_FOREACH(value, v, t->values) {
227
228                 if (v->base == TIMER_CALENDAR) {
229                         _cleanup_free_ char *p = NULL;
230
231                         calendar_spec_to_string(v->calendar_spec, &p);
232
233                         fprintf(f,
234                                 "%s%s: %s\n",
235                                 prefix,
236                                 timer_base_to_string(v->base),
237                                 strna(p));
238                 } else  {
239                         char timespan1[FORMAT_TIMESPAN_MAX];
240
241                         fprintf(f,
242                                 "%s%s: %s\n",
243                                 prefix,
244                                 timer_base_to_string(v->base),
245                                 format_timespan(timespan1, sizeof(timespan1), v->value, 0));
246                 }
247         }
248 }
249
250 static void timer_set_state(Timer *t, TimerState state) {
251         TimerState old_state;
252         assert(t);
253
254         old_state = t->state;
255         t->state = state;
256
257         if (state != TIMER_WAITING) {
258                 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
259                 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
260         }
261
262         if (state != old_state)
263                 log_unit_debug(UNIT(t)->id,
264                                "%s changed %s -> %s", UNIT(t)->id,
265                                timer_state_to_string(old_state),
266                                timer_state_to_string(state));
267
268         unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
269 }
270
271 static void timer_enter_waiting(Timer *t, bool initial);
272
273 static int timer_coldplug(Unit *u) {
274         Timer *t = TIMER(u);
275
276         assert(t);
277         assert(t->state == TIMER_DEAD);
278
279         if (t->deserialized_state != t->state) {
280
281                 if (t->deserialized_state == TIMER_WAITING)
282                         timer_enter_waiting(t, false);
283                 else
284                         timer_set_state(t, t->deserialized_state);
285         }
286
287         return 0;
288 }
289
290 static void timer_enter_dead(Timer *t, TimerResult f) {
291         assert(t);
292
293         if (f != TIMER_SUCCESS)
294                 t->result = f;
295
296         timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
297 }
298
299 static usec_t monotonic_to_boottime(usec_t t) {
300         usec_t a, b;
301
302         if (t <= 0)
303                 return 0;
304
305         a = now(CLOCK_BOOTTIME);
306         b = now(CLOCK_MONOTONIC);
307
308         if (t + a > b)
309                 return t + a - b;
310         else
311                 return 0;
312 }
313
314 static void timer_enter_waiting(Timer *t, bool initial) {
315         bool found_monotonic = false, found_realtime = false;
316         usec_t ts_realtime, ts_monotonic;
317         usec_t base = 0;
318         TimerValue *v;
319         int r;
320
321         /* If we shall wake the system we use the boottime clock
322          * rather than the monotonic clock. */
323
324         ts_realtime = now(CLOCK_REALTIME);
325         ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
326         t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
327
328         LIST_FOREACH(value, v, t->values) {
329
330                 if (v->disabled)
331                         continue;
332
333                 if (v->base == TIMER_CALENDAR) {
334                         usec_t b;
335
336                         /* If we know the last time this was
337                          * triggered, schedule the job based relative
338                          * to that. If we don't just start from
339                          * now. */
340
341                         b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime;
342
343                         r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
344                         if (r < 0)
345                                 continue;
346
347                         if (!found_realtime)
348                                 t->next_elapse_realtime = v->next_elapse;
349                         else
350                                 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
351
352                         found_realtime = true;
353
354                 } else  {
355                         switch (v->base) {
356
357                         case TIMER_ACTIVE:
358                                 if (state_translation_table[t->state] == UNIT_ACTIVE)
359                                         base = UNIT(t)->inactive_exit_timestamp.monotonic;
360                                 else
361                                         base = ts_monotonic;
362                                 break;
363
364                         case TIMER_BOOT:
365                                 /* CLOCK_MONOTONIC equals the uptime on Linux */
366                                 base = 0;
367                                 break;
368
369                         case TIMER_STARTUP:
370                                 base = UNIT(t)->manager->userspace_timestamp.monotonic;
371                                 break;
372
373                         case TIMER_UNIT_ACTIVE:
374
375                                 base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
376
377                                 if (base <= 0)
378                                         base = t->last_trigger.monotonic;
379
380                                 if (base <= 0)
381                                         continue;
382
383                                 break;
384
385                         case TIMER_UNIT_INACTIVE:
386
387                                 base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
388
389                                 if (base <= 0)
390                                         base = t->last_trigger.monotonic;
391
392                                 if (base <= 0)
393                                         continue;
394
395                                 break;
396
397                         default:
398                                 assert_not_reached("Unknown timer base");
399                         }
400
401                         if (t->wake_system)
402                                 base = monotonic_to_boottime(base);
403
404                         v->next_elapse = base + v->value;
405
406                         if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
407                                 /* This is a one time trigger, disable it now */
408                                 v->disabled = true;
409                                 continue;
410                         }
411
412                         if (!found_monotonic)
413                                 t->next_elapse_monotonic_or_boottime = v->next_elapse;
414                         else
415                                 t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
416
417                         found_monotonic = true;
418                 }
419         }
420
421         if (!found_monotonic && !found_realtime) {
422                 log_unit_debug(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
423                 timer_set_state(t, TIMER_ELAPSED);
424                 return;
425         }
426
427         if (found_monotonic) {
428                 char buf[FORMAT_TIMESPAN_MAX];
429
430                 log_unit_debug(UNIT(t)->id, "%s: Monotonic timer elapses in %s.",
431                                UNIT(t)->id,
432                                format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
433
434                 if (t->monotonic_event_source) {
435                         r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
436                         if (r < 0)
437                                 goto fail;
438
439                         r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
440                 } else
441                         r = sd_event_add_time(
442                                         UNIT(t)->manager->event,
443                                         &t->monotonic_event_source,
444                                         t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
445                                         t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
446                                         timer_dispatch, t);
447                 if (r < 0)
448                         goto fail;
449
450         } else if (t->monotonic_event_source) {
451
452                 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
453                 if (r < 0)
454                         goto fail;
455         }
456
457         if (found_realtime) {
458                 char buf[FORMAT_TIMESTAMP_MAX];
459                 log_unit_debug(UNIT(t)->id, "%s: Realtime timer elapses at %s.", UNIT(t)->id, format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
460
461                 if (t->realtime_event_source) {
462                         r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
463                         if (r < 0)
464                                 goto fail;
465
466                         r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
467                 } else
468                         r = sd_event_add_time(
469                                         UNIT(t)->manager->event,
470                                         &t->realtime_event_source,
471                                         t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
472                                         t->next_elapse_realtime, t->accuracy_usec,
473                                         timer_dispatch, t);
474                 if (r < 0)
475                         goto fail;
476
477         } else if (t->realtime_event_source) {
478
479                 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
480                 if (r < 0)
481                         goto fail;
482         }
483
484         timer_set_state(t, TIMER_WAITING);
485         return;
486
487 fail:
488         log_unit_warning(UNIT(t)->id, "%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r));
489         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
490 }
491
492 static void timer_enter_running(Timer *t) {
493         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
494         int r;
495
496         assert(t);
497
498         /* Don't start job if we are supposed to go down */
499         if (unit_stop_pending(UNIT(t)))
500                 return;
501
502         r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
503                             JOB_REPLACE, true, &error, NULL);
504         if (r < 0)
505                 goto fail;
506
507         dual_timestamp_get(&t->last_trigger);
508
509         if (t->stamp_path)
510                 touch_file(t->stamp_path, true, t->last_trigger.realtime, (uid_t) -1, (gid_t) -1, 0);
511
512         timer_set_state(t, TIMER_RUNNING);
513         return;
514
515 fail:
516         log_unit_warning(UNIT(t)->id,
517                          "%s failed to queue unit startup job: %s",
518                          UNIT(t)->id, 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_t) -1, (uid_t) -1, (gid_t) -1, 0);
549         }
550
551         t->result = TIMER_SUCCESS;
552         timer_enter_waiting(t, true);
553         return 0;
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 0;
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->id, "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->id, "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->id, "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->id, "Failed to parse last-trigger-monotonic value %s", value);
621
622         } else
623                 log_unit_debug(u->id, "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)->id, "Timer elapsed on %s", UNIT(t)->id);
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)->id, "%s got notified about unit deactivation.", UNIT(t)->id);
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->id, "%s: time change, recalculating next elapse.", u->id);
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
754         .init = timer_init,
755         .done = timer_done,
756         .load = timer_load,
757
758         .coldplug = timer_coldplug,
759
760         .dump = timer_dump,
761
762         .start = timer_start,
763         .stop = timer_stop,
764
765         .serialize = timer_serialize,
766         .deserialize_item = timer_deserialize_item,
767
768         .active_state = timer_active_state,
769         .sub_state_to_string = timer_sub_state_to_string,
770
771         .trigger_notify = timer_trigger_notify,
772
773         .reset_failed = timer_reset_failed,
774         .time_change = timer_time_change,
775
776         .bus_interface = "org.freedesktop.systemd1.Timer",
777         .bus_vtable = bus_timer_vtable,
778 };