]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/timer.c
unit: serialize active timestamps
[thirdparty/systemd.git] / src / 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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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->meta.load_state == UNIT_STUB);
44
45 t->next_elapse = (usec_t) -1;
46 }
47
48 static void timer_done(Unit *u) {
49 Timer *t = TIMER(u);
50 TimerValue *v;
51
52 assert(t);
53
54 while ((v = t->values)) {
55 LIST_REMOVE(TimerValue, value, t->values, v);
56 free(v);
57 }
58
59 unit_unwatch_timer(u, &t->timer_watch);
60 }
61
62 static int timer_verify(Timer *t) {
63 assert(t);
64
65 if (t->meta.load_state != UNIT_LOADED)
66 return 0;
67
68 if (!t->values) {
69 log_error("%s lacks value setting. Refusing.", t->meta.id);
70 return -EINVAL;
71 }
72
73 return 0;
74 }
75
76 static int timer_add_default_dependencies(Timer *t) {
77 int r;
78
79 assert(t);
80
81 if (t->meta.manager->running_as == MANAGER_SYSTEM)
82 if ((r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
83 return r;
84
85 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTED_BY, SPECIAL_SHUTDOWN_TARGET, NULL, true);
86 }
87
88 static int timer_load(Unit *u) {
89 Timer *t = TIMER(u);
90 int r;
91
92 assert(u);
93 assert(u->meta.load_state == UNIT_STUB);
94
95 if ((r = unit_load_fragment_and_dropin(u)) < 0)
96 return r;
97
98 if (u->meta.load_state == UNIT_LOADED) {
99
100 if (!t->unit)
101 if ((r = unit_load_related_unit(u, ".service", &t->unit)))
102 return r;
103
104 if ((r = unit_add_dependency(u, UNIT_BEFORE, t->unit, true)) < 0)
105 return r;
106
107 if (t->meta.default_dependencies)
108 if ((r = timer_add_default_dependencies(t)) < 0)
109 return r;
110 }
111
112 return timer_verify(t);
113 }
114
115 static void timer_dump(Unit *u, FILE *f, const char *prefix) {
116 Timer *t = TIMER(u);
117 TimerValue *v;
118 char
119 timespan1[FORMAT_TIMESPAN_MAX];
120
121 fprintf(f,
122 "%sTimer State: %s\n"
123 "%sUnit: %s\n",
124 prefix, timer_state_to_string(t->state),
125 prefix, t->unit->meta.id);
126
127 LIST_FOREACH(value, v, t->values)
128 fprintf(f,
129 "%s%s: %s\n",
130 prefix,
131 timer_base_to_string(v->base),
132 strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
133 }
134
135 static void timer_set_state(Timer *t, TimerState state) {
136 TimerState old_state;
137 assert(t);
138
139 old_state = t->state;
140 t->state = state;
141
142 if (state != TIMER_WAITING)
143 unit_unwatch_timer(UNIT(t), &t->timer_watch);
144
145 if (state != old_state)
146 log_debug("%s changed %s -> %s",
147 t->meta.id,
148 timer_state_to_string(old_state),
149 timer_state_to_string(state));
150
151 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state]);
152 }
153
154 static void timer_enter_waiting(Timer *t, bool initial);
155
156 static int timer_coldplug(Unit *u) {
157 Timer *t = TIMER(u);
158
159 assert(t);
160 assert(t->state == TIMER_DEAD);
161
162 if (t->deserialized_state != t->state) {
163
164 if (t->deserialized_state == TIMER_WAITING ||
165 t->deserialized_state == TIMER_RUNNING ||
166 t->deserialized_state == TIMER_ELAPSED)
167 timer_enter_waiting(t, false);
168 else
169 timer_set_state(t, t->deserialized_state);
170 }
171
172 return 0;
173 }
174
175 static void timer_enter_dead(Timer *t, bool success) {
176 assert(t);
177
178 if (!success)
179 t->failure = true;
180
181 timer_set_state(t, t->failure ? TIMER_FAILED : TIMER_DEAD);
182 }
183
184 static void timer_enter_waiting(Timer *t, bool initial) {
185 TimerValue *v;
186 usec_t base = 0, delay, n;
187 bool found = false;
188 int r;
189
190 n = now(CLOCK_MONOTONIC);
191
192 LIST_FOREACH(value, v, t->values) {
193
194 if (v->disabled)
195 continue;
196
197 switch (v->base) {
198
199 case TIMER_ACTIVE:
200 if (state_translation_table[t->state] == UNIT_ACTIVE)
201 base = t->meta.inactive_exit_timestamp.monotonic;
202 else
203 base = n;
204 break;
205
206 case TIMER_BOOT:
207 /* CLOCK_MONOTONIC equals the uptime on Linux */
208 base = 0;
209 break;
210
211 case TIMER_STARTUP:
212 base = t->meta.manager->startup_timestamp.monotonic;
213 break;
214
215 case TIMER_UNIT_ACTIVE:
216
217 if (t->unit->meta.inactive_exit_timestamp.monotonic <= 0)
218 continue;
219
220 base = t->unit->meta.inactive_exit_timestamp.monotonic;
221 break;
222
223 case TIMER_UNIT_INACTIVE:
224
225 if (t->unit->meta.inactive_enter_timestamp.monotonic <= 0)
226 continue;
227
228 base = t->unit->meta.inactive_enter_timestamp.monotonic;
229 break;
230
231 default:
232 assert_not_reached("Unknown timer base");
233 }
234
235 v->next_elapse = base + v->value;
236
237 if (!initial && v->next_elapse < n) {
238 v->disabled = true;
239 continue;
240 }
241
242 if (!found)
243 t->next_elapse = v->next_elapse;
244 else
245 t->next_elapse = MIN(t->next_elapse, v->next_elapse);
246
247 found = true;
248 }
249
250 if (!found) {
251 timer_set_state(t, TIMER_ELAPSED);
252 return;
253 }
254
255 delay = n < t->next_elapse ? t->next_elapse - n : 0;
256
257 if ((r = unit_watch_timer(UNIT(t), delay, &t->timer_watch)) < 0)
258 goto fail;
259
260 timer_set_state(t, TIMER_WAITING);
261 return;
262
263 fail:
264 log_warning("%s failed to enter waiting state: %s", t->meta.id, strerror(-r));
265 timer_enter_dead(t, false);
266 }
267
268 static void timer_enter_running(Timer *t) {
269 DBusError error;
270 int r;
271
272 assert(t);
273 dbus_error_init(&error);
274
275 /* Don't start job if we are supposed to go down */
276 if (t->meta.job && t->meta.job->type == JOB_STOP)
277 return;
278
279 if ((r = manager_add_job(t->meta.manager, JOB_START, t->unit, JOB_REPLACE, true, &error, NULL)) < 0)
280 goto fail;
281
282 timer_set_state(t, TIMER_RUNNING);
283 return;
284
285 fail:
286 log_warning("%s failed to queue unit startup job: %s", t->meta.id, bus_error(&error, r));
287 timer_enter_dead(t, false);
288
289 dbus_error_free(&error);
290 }
291
292 static int timer_start(Unit *u) {
293 Timer *t = TIMER(u);
294
295 assert(t);
296 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
297
298 if (t->unit->meta.load_state != UNIT_LOADED)
299 return -ENOENT;
300
301 t->failure = false;
302 timer_enter_waiting(t, true);
303 return 0;
304 }
305
306 static int timer_stop(Unit *u) {
307 Timer *t = TIMER(u);
308
309 assert(t);
310 assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
311
312 timer_enter_dead(t, true);
313 return 0;
314 }
315
316 static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
317 Timer *t = TIMER(u);
318
319 assert(u);
320 assert(f);
321 assert(fds);
322
323 unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
324
325 return 0;
326 }
327
328 static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
329 Timer *t = TIMER(u);
330
331 assert(u);
332 assert(key);
333 assert(value);
334 assert(fds);
335
336 if (streq(key, "state")) {
337 TimerState state;
338
339 if ((state = timer_state_from_string(value)) < 0)
340 log_debug("Failed to parse state value %s", value);
341 else
342 t->deserialized_state = state;
343 } else
344 log_debug("Unknown serialization key '%s'", key);
345
346 return 0;
347 }
348
349 static UnitActiveState timer_active_state(Unit *u) {
350 assert(u);
351
352 return state_translation_table[TIMER(u)->state];
353 }
354
355 static const char *timer_sub_state_to_string(Unit *u) {
356 assert(u);
357
358 return timer_state_to_string(TIMER(u)->state);
359 }
360
361 static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
362 Timer *t = TIMER(u);
363
364 assert(t);
365 assert(elapsed == 1);
366
367 if (t->state != TIMER_WAITING)
368 return;
369
370 log_debug("Timer elapsed on %s", u->meta.id);
371 timer_enter_running(t);
372 }
373
374 void timer_unit_notify(Unit *u, UnitActiveState new_state) {
375 char *n;
376 int r;
377 Iterator i;
378
379 if (u->meta.type == UNIT_TIMER)
380 return;
381
382 SET_FOREACH(n, u->meta.names, i) {
383 char *k;
384 Unit *p;
385 Timer *t;
386 TimerValue *v;
387
388 if (!(k = unit_name_change_suffix(n, ".timer"))) {
389 r = -ENOMEM;
390 goto fail;
391 }
392
393 p = manager_get_unit(u->meta.manager, k);
394 free(k);
395
396 if (!p)
397 continue;
398
399 if (p->meta.load_state != UNIT_LOADED)
400 continue;
401
402 t = TIMER(p);
403
404 if (t->unit != u)
405 continue;
406
407 /* Reenable all timers that depend on unit state */
408 LIST_FOREACH(value, v, t->values)
409 if (v->base == TIMER_UNIT_ACTIVE ||
410 v->base == TIMER_UNIT_INACTIVE)
411 v->disabled = false;
412
413 switch (t->state) {
414
415 case TIMER_WAITING:
416 case TIMER_ELAPSED:
417
418 /* Recalculate sleep time */
419 timer_enter_waiting(t, false);
420 break;
421
422 case TIMER_RUNNING:
423
424 if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
425 log_debug("%s got notified about unit deactivation.", t->meta.id);
426 timer_enter_waiting(t, false);
427 }
428
429 break;
430
431 case TIMER_DEAD:
432 case TIMER_FAILED:
433 break;
434
435 default:
436 assert_not_reached("Unknown timer state");
437 }
438 }
439
440 return;
441
442 fail:
443 log_error("Failed find timer unit: %s", strerror(-r));
444 }
445
446 static void timer_reset_failed(Unit *u) {
447 Timer *t = TIMER(u);
448
449 assert(t);
450
451 if (t->state == TIMER_FAILED)
452 timer_set_state(t, TIMER_DEAD);
453
454 t->failure = false;
455 }
456
457 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
458 [TIMER_DEAD] = "dead",
459 [TIMER_WAITING] = "waiting",
460 [TIMER_RUNNING] = "running",
461 [TIMER_ELAPSED] = "elapsed",
462 [TIMER_FAILED] = "failed"
463 };
464
465 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
466
467 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
468 [TIMER_ACTIVE] = "OnActiveSec",
469 [TIMER_BOOT] = "OnBootSec",
470 [TIMER_STARTUP] = "OnStartupSec",
471 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
472 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec"
473 };
474
475 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
476
477 const UnitVTable timer_vtable = {
478 .suffix = ".timer",
479
480 .init = timer_init,
481 .done = timer_done,
482 .load = timer_load,
483
484 .coldplug = timer_coldplug,
485
486 .dump = timer_dump,
487
488 .start = timer_start,
489 .stop = timer_stop,
490
491 .serialize = timer_serialize,
492 .deserialize_item = timer_deserialize_item,
493
494 .active_state = timer_active_state,
495 .sub_state_to_string = timer_sub_state_to_string,
496
497 .timer_event = timer_timer_event,
498
499 .reset_failed = timer_reset_failed,
500
501 .bus_interface = "org.freedesktop.systemd1.Timer",
502 .bus_message_handler = bus_timer_message_handler,
503 .bus_invalidating_properties = bus_timer_invalidating_properties
504 };