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