chiark / gitweb /
timer: recalculate next elapse for calendar timer units when the system clock is...
[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-errors.h"
30
31 static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
32         [TIMER_DEAD] = UNIT_INACTIVE,
33         [TIMER_WAITING] = UNIT_ACTIVE,
34         [TIMER_RUNNING] = UNIT_ACTIVE,
35         [TIMER_ELAPSED] = UNIT_ACTIVE,
36         [TIMER_FAILED] = UNIT_FAILED
37 };
38
39 static void timer_init(Unit *u) {
40         Timer *t = TIMER(u);
41
42         assert(u);
43         assert(u->load_state == UNIT_STUB);
44
45         t->next_elapse_monotonic = (usec_t) -1;
46         t->next_elapse_realtime = (usec_t) -1;
47         watch_init(&t->monotonic_watch);
48         watch_init(&t->realtime_watch);
49 }
50
51 static void timer_done(Unit *u) {
52         Timer *t = TIMER(u);
53         TimerValue *v;
54
55         assert(t);
56
57         while ((v = t->values)) {
58                 LIST_REMOVE(TimerValue, value, t->values, v);
59
60                 if (v->calendar_spec)
61                         calendar_spec_free(v->calendar_spec);
62
63                 free(v);
64         }
65
66         unit_unwatch_timer(u, &t->monotonic_watch);
67         unit_unwatch_timer(u, &t->realtime_watch);
68
69         unit_ref_unset(&t->unit);
70 }
71
72 static int timer_verify(Timer *t) {
73         assert(t);
74
75         if (UNIT(t)->load_state != UNIT_LOADED)
76                 return 0;
77
78         if (!t->values) {
79                 log_error("%s lacks value setting. Refusing.", UNIT(t)->id);
80                 return -EINVAL;
81         }
82
83         return 0;
84 }
85
86 static int timer_add_default_dependencies(Timer *t) {
87         int r;
88
89         assert(t);
90
91         if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
92                 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true);
93                 if (r < 0)
94                         return r;
95
96                 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
97                 if (r < 0)
98                         return r;
99         }
100
101         return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
102 }
103
104 static int timer_load(Unit *u) {
105         Timer *t = TIMER(u);
106         int r;
107
108         assert(u);
109         assert(u->load_state == UNIT_STUB);
110
111         r = unit_load_fragment_and_dropin(u);
112         if (r < 0)
113                 return r;
114
115         if (u->load_state == UNIT_LOADED) {
116
117                 if (!UNIT_DEREF(t->unit)) {
118                         Unit *x;
119
120                         r = unit_load_related_unit(u, ".service", &x);
121                         if (r < 0)
122                                 return r;
123
124                         unit_ref_set(&t->unit, x);
125                 }
126
127                 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true);
128                 if (r < 0)
129                         return r;
130
131                 if (UNIT(t)->default_dependencies) {
132                         r = timer_add_default_dependencies(t);
133                         if (r < 0)
134                                 return r;
135                 }
136         }
137
138         return timer_verify(t);
139 }
140
141 static void timer_dump(Unit *u, FILE *f, const char *prefix) {
142         Timer *t = TIMER(u);
143         TimerValue *v;
144
145         fprintf(f,
146                 "%sTimer State: %s\n"
147                 "%sResult: %s\n"
148                 "%sUnit: %s\n",
149                 prefix, timer_state_to_string(t->state),
150                 prefix, timer_result_to_string(t->result),
151                 prefix, UNIT_DEREF(t->unit)->id);
152
153         LIST_FOREACH(value, v, t->values) {
154
155                 if (v->base == TIMER_CALENDAR) {
156                         _cleanup_free_ char *p = NULL;
157
158                         calendar_spec_to_string(v->calendar_spec, &p);
159
160                         fprintf(f,
161                                 "%s%s: %s\n",
162                                 prefix,
163                                 timer_base_to_string(v->base),
164                                 strna(p));
165                 } else  {
166                         char timespan1[FORMAT_TIMESPAN_MAX];
167
168                         fprintf(f,
169                                 "%s%s: %s\n",
170                                 prefix,
171                                 timer_base_to_string(v->base),
172                                 strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
173                 }
174         }
175 }
176
177 static void timer_set_state(Timer *t, TimerState state) {
178         TimerState old_state;
179         assert(t);
180
181         old_state = t->state;
182         t->state = state;
183
184         if (state != TIMER_WAITING) {
185                 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
186                 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
187         }
188
189         if (state != old_state)
190                 log_debug("%s changed %s -> %s",
191                           UNIT(t)->id,
192                           timer_state_to_string(old_state),
193                           timer_state_to_string(state));
194
195         unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
196 }
197
198 static void timer_enter_waiting(Timer *t, bool initial);
199
200 static int timer_coldplug(Unit *u) {
201         Timer *t = TIMER(u);
202
203         assert(t);
204         assert(t->state == TIMER_DEAD);
205
206         if (t->deserialized_state != t->state) {
207
208                 if (t->deserialized_state == TIMER_WAITING)
209                         timer_enter_waiting(t, false);
210                 else
211                         timer_set_state(t, t->deserialized_state);
212         }
213
214         return 0;
215 }
216
217 static void timer_enter_dead(Timer *t, TimerResult f) {
218         assert(t);
219
220         if (f != TIMER_SUCCESS)
221                 t->result = f;
222
223         timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
224 }
225
226 static void timer_enter_waiting(Timer *t, bool initial) {
227         TimerValue *v;
228         usec_t base = 0;
229         dual_timestamp ts;
230         bool found_monotonic = false, found_realtime = false;
231         int r;
232
233         dual_timestamp_get(&ts);
234         t->next_elapse_monotonic = t->next_elapse_realtime = 0;
235
236         LIST_FOREACH(value, v, t->values) {
237
238                 if (v->disabled)
239                         continue;
240
241                 if (v->base == TIMER_CALENDAR) {
242
243                         r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
244                         if (r < 0)
245                                 continue;
246
247                         if (!initial && v->next_elapse < ts.realtime) {
248                                 v->disabled = true;
249                                 continue;
250                         }
251
252                         if (!found_realtime)
253                                 t->next_elapse_realtime = v->next_elapse;
254                         else
255                                 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
256
257                         found_realtime = true;
258
259                 } else  {
260                         switch (v->base) {
261
262                         case TIMER_ACTIVE:
263                                 if (state_translation_table[t->state] == UNIT_ACTIVE)
264                                         base = UNIT(t)->inactive_exit_timestamp.monotonic;
265                                 else
266                                         base = ts.monotonic;
267                                 break;
268
269                         case TIMER_BOOT:
270                                 /* CLOCK_MONOTONIC equals the uptime on Linux */
271                                 base = 0;
272                                 break;
273
274                         case TIMER_STARTUP:
275                                 base = UNIT(t)->manager->userspace_timestamp.monotonic;
276                                 break;
277
278                         case TIMER_UNIT_ACTIVE:
279
280                                 if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
281                                         continue;
282
283                                 base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
284                                 break;
285
286                         case TIMER_UNIT_INACTIVE:
287
288                                 if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
289                                         continue;
290
291                                 base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
292                                 break;
293
294                         default:
295                                 assert_not_reached("Unknown timer base");
296                         }
297
298                         v->next_elapse = base + v->value;
299
300                         if (!initial && v->next_elapse < ts.monotonic) {
301                                 v->disabled = true;
302                                 continue;
303                         }
304
305                         if (!found_monotonic)
306                                 t->next_elapse_monotonic = v->next_elapse;
307                         else
308                                 t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
309
310                         found_monotonic = true;
311                 }
312         }
313
314         if (!found_monotonic && !found_realtime) {
315                 log_debug("%s: Timer is elapsed.", UNIT(t)->id);
316                 timer_set_state(t, TIMER_ELAPSED);
317                 return;
318         }
319
320         if (found_monotonic) {
321                 char buf[FORMAT_TIMESPAN_MAX];
322                 log_debug("%s: Monotonic timer elapses in %s the next time.", UNIT(t)->id, format_timespan(buf, sizeof(buf), t->next_elapse_monotonic - ts.monotonic));
323
324                 r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch);
325                 if (r < 0)
326                         goto fail;
327         } else
328                 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
329
330         if (found_realtime) {
331                 char buf[FORMAT_TIMESTAMP_MAX];
332                 log_debug("%s: Realtime timer elapses at %s the next time.", UNIT(t)->id, format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
333
334                 r = unit_watch_timer(UNIT(t), CLOCK_REALTIME, false, t->next_elapse_realtime, &t->realtime_watch);
335                 if (r < 0)
336                         goto fail;
337         } else
338                 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
339
340         timer_set_state(t, TIMER_WAITING);
341         return;
342
343 fail:
344         log_warning("%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r));
345         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
346 }
347
348 static void timer_enter_running(Timer *t) {
349         DBusError error;
350         int r;
351
352         assert(t);
353         dbus_error_init(&error);
354
355         /* Don't start job if we are supposed to go down */
356         if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
357                 return;
358
359         r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL);
360         if (r < 0)
361                 goto fail;
362
363         timer_set_state(t, TIMER_RUNNING);
364         return;
365
366 fail:
367         log_warning("%s failed to queue unit startup job: %s", UNIT(t)->id, bus_error(&error, r));
368         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
369
370         dbus_error_free(&error);
371 }
372
373 static int timer_start(Unit *u) {
374         Timer *t = TIMER(u);
375
376         assert(t);
377         assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
378
379         if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
380                 return -ENOENT;
381
382         t->result = TIMER_SUCCESS;
383         timer_enter_waiting(t, true);
384         return 0;
385 }
386
387 static int timer_stop(Unit *u) {
388         Timer *t = TIMER(u);
389
390         assert(t);
391         assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
392
393         timer_enter_dead(t, TIMER_SUCCESS);
394         return 0;
395 }
396
397 static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
398         Timer *t = TIMER(u);
399
400         assert(u);
401         assert(f);
402         assert(fds);
403
404         unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
405         unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
406
407         return 0;
408 }
409
410 static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
411         Timer *t = TIMER(u);
412
413         assert(u);
414         assert(key);
415         assert(value);
416         assert(fds);
417
418         if (streq(key, "state")) {
419                 TimerState state;
420
421                 state = timer_state_from_string(value);
422                 if (state < 0)
423                         log_debug("Failed to parse state value %s", value);
424                 else
425                         t->deserialized_state = state;
426         } else if (streq(key, "result")) {
427                 TimerResult f;
428
429                 f = timer_result_from_string(value);
430                 if (f < 0)
431                         log_debug("Failed to parse result value %s", value);
432                 else if (f != TIMER_SUCCESS)
433                         t->result = f;
434
435         } else
436                 log_debug("Unknown serialization key '%s'", key);
437
438         return 0;
439 }
440
441 static UnitActiveState timer_active_state(Unit *u) {
442         assert(u);
443
444         return state_translation_table[TIMER(u)->state];
445 }
446
447 static const char *timer_sub_state_to_string(Unit *u) {
448         assert(u);
449
450         return timer_state_to_string(TIMER(u)->state);
451 }
452
453 static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
454         Timer *t = TIMER(u);
455
456         assert(t);
457         assert(elapsed == 1);
458
459         if (t->state != TIMER_WAITING)
460                 return;
461
462         log_debug("Timer elapsed on %s", u->id);
463         timer_enter_running(t);
464 }
465
466 void timer_unit_notify(Unit *u, UnitActiveState new_state) {
467         Iterator i;
468         Unit *k;
469
470         if (u->type == UNIT_TIMER)
471                 return;
472
473         SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
474                 Timer *t;
475                 TimerValue *v;
476
477                 if (k->type != UNIT_TIMER)
478                         continue;
479
480                 if (k->load_state != UNIT_LOADED)
481                         continue;
482
483                 t = TIMER(k);
484
485                 /* Reenable all timers that depend on unit state */
486                 LIST_FOREACH(value, v, t->values)
487                         if (v->base == TIMER_UNIT_ACTIVE ||
488                             v->base == TIMER_UNIT_INACTIVE)
489                                 v->disabled = false;
490
491                 switch (t->state) {
492
493                 case TIMER_WAITING:
494                 case TIMER_ELAPSED:
495
496                         /* Recalculate sleep time */
497                         timer_enter_waiting(t, false);
498                         break;
499
500                 case TIMER_RUNNING:
501
502                         if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
503                                 log_debug("%s got notified about unit deactivation.", UNIT(t)->id);
504                                 timer_enter_waiting(t, false);
505                         }
506
507                         break;
508
509                 case TIMER_DEAD:
510                 case TIMER_FAILED:
511                         break;
512
513                 default:
514                         assert_not_reached("Unknown timer state");
515                 }
516         }
517 }
518
519 static void timer_reset_failed(Unit *u) {
520         Timer *t = TIMER(u);
521
522         assert(t);
523
524         if (t->state == TIMER_FAILED)
525                 timer_set_state(t, TIMER_DEAD);
526
527         t->result = TIMER_SUCCESS;
528 }
529
530 static void timer_time_change(Unit *u) {
531         Timer *t = TIMER(u);
532
533         assert(u);
534
535         if (t->state != TIMER_WAITING)
536                 return;
537
538         log_info("%s: time change, recalculating next elapse.", u->id);
539         timer_enter_waiting(t, false);
540 }
541
542 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
543         [TIMER_DEAD] = "dead",
544         [TIMER_WAITING] = "waiting",
545         [TIMER_RUNNING] = "running",
546         [TIMER_ELAPSED] = "elapsed",
547         [TIMER_FAILED] = "failed"
548 };
549
550 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
551
552 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
553         [TIMER_ACTIVE] = "OnActiveSec",
554         [TIMER_BOOT] = "OnBootSec",
555         [TIMER_STARTUP] = "OnStartupSec",
556         [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
557         [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
558         [TIMER_CALENDAR] = "OnCalendar"
559 };
560
561 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
562
563 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
564         [TIMER_SUCCESS] = "success",
565         [TIMER_FAILURE_RESOURCES] = "resources"
566 };
567
568 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
569
570 const UnitVTable timer_vtable = {
571         .object_size = sizeof(Timer),
572         .sections =
573                 "Unit\0"
574                 "Timer\0"
575                 "Install\0",
576
577         .init = timer_init,
578         .done = timer_done,
579         .load = timer_load,
580
581         .coldplug = timer_coldplug,
582
583         .dump = timer_dump,
584
585         .start = timer_start,
586         .stop = timer_stop,
587
588         .serialize = timer_serialize,
589         .deserialize_item = timer_deserialize_item,
590
591         .active_state = timer_active_state,
592         .sub_state_to_string = timer_sub_state_to_string,
593
594         .timer_event = timer_timer_event,
595
596         .reset_failed = timer_reset_failed,
597         .time_change = timer_time_change,
598
599         .bus_interface = "org.freedesktop.systemd1.Timer",
600         .bus_message_handler = bus_timer_message_handler,
601         .bus_invalidating_properties =  bus_timer_invalidating_properties
602 };