]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/timer.c
man: clarify why User=/Group= don't work with mount units
[thirdparty/systemd.git] / src / core / 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
5430f7f2
LP
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
a7334b09
LP
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
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
67445f4e 83 if (UNIT(t)->manager->running_as == SYSTEMD_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"
067d72c9 136 "%sResult: %s\n"
871d7de4
LP
137 "%sUnit: %s\n",
138 prefix, timer_state_to_string(t->state),
067d72c9 139 prefix, timer_result_to_string(t->result),
ac155bb8 140 prefix, UNIT_DEREF(t->unit)->id);
871d7de4
LP
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)));
871d7de4
LP
148}
149
150static void timer_set_state(Timer *t, TimerState state) {
151 TimerState old_state;
034c6ed7 152 assert(t);
871d7de4
LP
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",
1124fe6f 162 UNIT(t)->id,
871d7de4
LP
163 timer_state_to_string(old_state),
164 timer_state_to_string(state));
165
e2f3b44c 166 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
871d7de4
LP
167}
168
169static void timer_enter_waiting(Timer *t, bool initial);
170
171static 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
b363ca6f 179 if (t->deserialized_state == TIMER_WAITING)
871d7de4
LP
180 timer_enter_waiting(t, false);
181 else
182 timer_set_state(t, t->deserialized_state);
183 }
184
185 return 0;
186}
187
067d72c9 188static void timer_enter_dead(Timer *t, TimerResult f) {
871d7de4
LP
189 assert(t);
190
067d72c9
LP
191 if (f != TIMER_SUCCESS)
192 t->result = f;
871d7de4 193
067d72c9 194 timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
871d7de4
LP
195}
196
197static 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:
10717a1a 213 if (state_translation_table[t->state] == UNIT_ACTIVE)
1124fe6f 214 base = UNIT(t)->inactive_exit_timestamp.monotonic;
10717a1a 215 else
871d7de4
LP
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:
915b3753 225 base = UNIT(t)->manager->userspace_timestamp.monotonic;
871d7de4
LP
226 break;
227
228 case TIMER_UNIT_ACTIVE:
229
ac155bb8 230 if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
871d7de4
LP
231 continue;
232
ac155bb8 233 base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
871d7de4
LP
234 break;
235
236 case TIMER_UNIT_INACTIVE:
237
ac155bb8 238 if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
871d7de4
LP
239 continue;
240
ac155bb8 241 base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
871d7de4
LP
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
276fail:
1124fe6f 277 log_warning("%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r));
067d72c9 278 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
871d7de4
LP
279}
280
281static void timer_enter_running(Timer *t) {
398ef8ba 282 DBusError error;
871d7de4 283 int r;
398ef8ba 284
871d7de4 285 assert(t);
398ef8ba 286 dbus_error_init(&error);
871d7de4 287
ba3e67a7 288 /* Don't start job if we are supposed to go down */
1124fe6f 289 if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
ba3e67a7
LP
290 return;
291
1124fe6f 292 if ((r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL)) < 0)
871d7de4
LP
293 goto fail;
294
295 timer_set_state(t, TIMER_RUNNING);
296 return;
297
298fail:
1124fe6f 299 log_warning("%s failed to queue unit startup job: %s", UNIT(t)->id, bus_error(&error, r));
067d72c9 300 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
398ef8ba
LP
301
302 dbus_error_free(&error);
871d7de4
LP
303}
304
305static int timer_start(Unit *u) {
306 Timer *t = TIMER(u);
307
308 assert(t);
fdf20a31 309 assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
01f78473 310
ac155bb8 311 if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
01f78473 312 return -ENOENT;
871d7de4 313
067d72c9 314 t->result = TIMER_SUCCESS;
871d7de4
LP
315 timer_enter_waiting(t, true);
316 return 0;
317}
318
319static 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
067d72c9 325 timer_enter_dead(t, TIMER_SUCCESS);
871d7de4
LP
326 return 0;
327}
328
329static 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));
067d72c9 337 unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
871d7de4
LP
338
339 return 0;
340}
341
342static 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;
067d72c9
LP
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
871d7de4
LP
366 } else
367 log_debug("Unknown serialization key '%s'", key);
368
369 return 0;
034c6ed7
LP
370}
371
87f0e418 372static UnitActiveState timer_active_state(Unit *u) {
871d7de4
LP
373 assert(u);
374
375 return state_translation_table[TIMER(u)->state];
376}
377
378static const char *timer_sub_state_to_string(Unit *u) {
379 assert(u);
380
381 return timer_state_to_string(TIMER(u)->state);
382}
383
384static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
385 Timer *t = TIMER(u);
386
387 assert(t);
388 assert(elapsed == 1);
5cb5a6ff 389
871d7de4
LP
390 if (t->state != TIMER_WAITING)
391 return;
5cb5a6ff 392
ac155bb8 393 log_debug("Timer elapsed on %s", u->id);
871d7de4 394 timer_enter_running(t);
5cb5a6ff
LP
395}
396
871d7de4 397void timer_unit_notify(Unit *u, UnitActiveState new_state) {
871d7de4 398 Iterator i;
57020a3a 399 Unit *k;
871d7de4 400
ac155bb8 401 if (u->type == UNIT_TIMER)
871d7de4
LP
402 return;
403
ac155bb8 404 SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
871d7de4
LP
405 Timer *t;
406 TimerValue *v;
407
ac155bb8 408 if (k->type != UNIT_TIMER)
871d7de4
LP
409 continue;
410
ac155bb8 411 if (k->load_state != UNIT_LOADED)
01f78473
LP
412 continue;
413
57020a3a 414 t = TIMER(k);
871d7de4
LP
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
fdf20a31 433 if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
1124fe6f 434 log_debug("%s got notified about unit deactivation.", UNIT(t)->id);
871d7de4
LP
435 timer_enter_waiting(t, false);
436 }
437
438 break;
439
440 case TIMER_DEAD:
fdf20a31 441 case TIMER_FAILED:
0b021426 442 break;
871d7de4
LP
443
444 default:
445 assert_not_reached("Unknown timer state");
446 }
447 }
871d7de4
LP
448}
449
fdf20a31 450static void timer_reset_failed(Unit *u) {
5632e374
LP
451 Timer *t = TIMER(u);
452
453 assert(t);
454
fdf20a31 455 if (t->state == TIMER_FAILED)
5632e374
LP
456 timer_set_state(t, TIMER_DEAD);
457
067d72c9 458 t->result = TIMER_SUCCESS;
5632e374
LP
459}
460
871d7de4
LP
461static 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",
fdf20a31 466 [TIMER_FAILED] = "failed"
871d7de4
LP
467};
468
469DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
470
471static const char* const timer_base_table[_TIMER_BASE_MAX] = {
03fae018
LP
472 [TIMER_ACTIVE] = "OnActiveSec",
473 [TIMER_BOOT] = "OnBootSec",
474 [TIMER_STARTUP] = "OnStartupSec",
475 [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
476 [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec"
871d7de4
LP
477};
478
479DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
480
067d72c9
LP
481static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
482 [TIMER_SUCCESS] = "success",
483 [TIMER_FAILURE_RESOURCES] = "resources"
484};
485
486DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
487
87f0e418 488const UnitVTable timer_vtable = {
7d17cfbc 489 .object_size = sizeof(Timer),
f975e971
LP
490 .sections =
491 "Unit\0"
492 "Timer\0"
493 "Install\0",
5cb5a6ff 494
871d7de4 495 .init = timer_init,
034c6ed7 496 .done = timer_done,
871d7de4
LP
497 .load = timer_load,
498
499 .coldplug = timer_coldplug,
500
501 .dump = timer_dump,
502
503 .start = timer_start,
504 .stop = timer_stop,
505
506 .serialize = timer_serialize,
507 .deserialize_item = timer_deserialize_item,
508
509 .active_state = timer_active_state,
510 .sub_state_to_string = timer_sub_state_to_string,
511
512 .timer_event = timer_timer_event,
5cb5a6ff 513
fdf20a31 514 .reset_failed = timer_reset_failed,
5632e374 515
c4e2ceae
LP
516 .bus_interface = "org.freedesktop.systemd1.Timer",
517 .bus_message_handler = bus_timer_message_handler,
518 .bus_invalidating_properties = bus_timer_invalidating_properties
5cb5a6ff 519};