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