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