]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/core/timer.c
journal: properly detect language specified in line
[thirdparty/systemd.git] / src / core / timer.c
... / ...
CommitLineData
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
33static 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
41static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
42
43static void timer_init(Unit *u) {
44 Timer *t = TIMER(u);
45
46 assert(u);
47 assert(u->load_state == UNIT_STUB);
48
49 t->next_elapse_monotonic_or_boottime = (usec_t) -1;
50 t->next_elapse_realtime = (usec_t) -1;
51 t->accuracy_usec = u->manager->default_timer_accuracy_usec;
52}
53
54void 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
69static 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
82static 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
96static 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
114static 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
155static 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
194static 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
240static 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
261static void timer_enter_waiting(Timer *t, bool initial);
262
263static 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
280static 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
289static 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
304static 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
477fail:
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
482static 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
519fail:
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
526static 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
549static 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
559static 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
578static 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
633static 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
646static 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
688static 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
699static 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
711static 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
719DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
720
721static 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
730DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
731
732static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
733 [TIMER_SUCCESS] = "success",
734 [TIMER_FAILURE_RESOURCES] = "resources"
735};
736
737DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
738
739const 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};