]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/timer.c
timer: timer can be a transient unit
[thirdparty/systemd.git] / src / core / timer.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23
24 #include "unit.h"
25 #include "unit-name.h"
26 #include "timer.h"
27 #include "dbus-timer.h"
28 #include "special.h"
29 #include "bus-util.h"
30 #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 0;
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 0;
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 };