chiark / gitweb /
core: move ManagerRunningAs to shared
[elogind.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 == SYSTEMD_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->userspace_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         .object_size = sizeof(Timer),
490         .sections =
491                 "Unit\0"
492                 "Timer\0"
493                 "Install\0",
494
495         .init = timer_init,
496         .done = timer_done,
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,
513
514         .reset_failed = timer_reset_failed,
515
516         .bus_interface = "org.freedesktop.systemd1.Timer",
517         .bus_message_handler = bus_timer_message_handler,
518         .bus_invalidating_properties =  bus_timer_invalidating_properties
519 };