]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/timer.c
unit: rework trigger dependency logic
[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 "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 };