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