chiark / gitweb /
Merge nss-myhostname
[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_monotonic = (usec_t) -1;
46         t->next_elapse_realtime = (usec_t) -1;
47         watch_init(&t->monotonic_watch);
48         watch_init(&t->realtime_watch);
49 }
50
51 static void timer_done(Unit *u) {
52         Timer *t = TIMER(u);
53         TimerValue *v;
54
55         assert(t);
56
57         while ((v = t->values)) {
58                 LIST_REMOVE(TimerValue, value, t->values, v);
59
60                 if (v->calendar_spec)
61                         calendar_spec_free(v->calendar_spec);
62
63                 free(v);
64         }
65
66         unit_unwatch_timer(u, &t->monotonic_watch);
67         unit_unwatch_timer(u, &t->realtime_watch);
68
69         unit_ref_unset(&t->unit);
70 }
71
72 static int timer_verify(Timer *t) {
73         assert(t);
74
75         if (UNIT(t)->load_state != UNIT_LOADED)
76                 return 0;
77
78         if (!t->values) {
79                 log_error_unit(UNIT(t)->id,
80                                "%s lacks value setting. Refusing.", UNIT(t)->id);
81                 return -EINVAL;
82         }
83
84         return 0;
85 }
86
87 static int timer_add_default_dependencies(Timer *t) {
88         int r;
89
90         assert(t);
91
92         if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
93                 r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true);
94                 if (r < 0)
95                         return r;
96
97                 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
98                 if (r < 0)
99                         return r;
100         }
101
102         return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
103 }
104
105 static int timer_load(Unit *u) {
106         Timer *t = TIMER(u);
107         int r;
108
109         assert(u);
110         assert(u->load_state == UNIT_STUB);
111
112         r = unit_load_fragment_and_dropin(u);
113         if (r < 0)
114                 return r;
115
116         if (u->load_state == UNIT_LOADED) {
117
118                 if (!UNIT_DEREF(t->unit)) {
119                         Unit *x;
120
121                         r = unit_load_related_unit(u, ".service", &x);
122                         if (r < 0)
123                                 return r;
124
125                         unit_ref_set(&t->unit, x);
126                 }
127
128                 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true);
129                 if (r < 0)
130                         return r;
131
132                 if (UNIT(t)->default_dependencies) {
133                         r = timer_add_default_dependencies(t);
134                         if (r < 0)
135                                 return r;
136                 }
137         }
138
139         return timer_verify(t);
140 }
141
142 static void timer_dump(Unit *u, FILE *f, const char *prefix) {
143         Timer *t = TIMER(u);
144         TimerValue *v;
145
146         fprintf(f,
147                 "%sTimer State: %s\n"
148                 "%sResult: %s\n"
149                 "%sUnit: %s\n",
150                 prefix, timer_state_to_string(t->state),
151                 prefix, timer_result_to_string(t->result),
152                 prefix, UNIT_DEREF(t->unit)->id);
153
154         LIST_FOREACH(value, v, t->values) {
155
156                 if (v->base == TIMER_CALENDAR) {
157                         _cleanup_free_ char *p = NULL;
158
159                         calendar_spec_to_string(v->calendar_spec, &p);
160
161                         fprintf(f,
162                                 "%s%s: %s\n",
163                                 prefix,
164                                 timer_base_to_string(v->base),
165                                 strna(p));
166                 } else  {
167                         char timespan1[FORMAT_TIMESPAN_MAX];
168
169                         fprintf(f,
170                                 "%s%s: %s\n",
171                                 prefix,
172                                 timer_base_to_string(v->base),
173                                 strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
174                 }
175         }
176 }
177
178 static void timer_set_state(Timer *t, TimerState state) {
179         TimerState old_state;
180         assert(t);
181
182         old_state = t->state;
183         t->state = state;
184
185         if (state != TIMER_WAITING) {
186                 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
187                 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
188         }
189
190         if (state != old_state)
191                 log_debug_unit(UNIT(t)->id,
192                                "%s changed %s -> %s", UNIT(t)->id,
193                                timer_state_to_string(old_state),
194                                timer_state_to_string(state));
195
196         unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
197 }
198
199 static void timer_enter_waiting(Timer *t, bool initial);
200
201 static int timer_coldplug(Unit *u) {
202         Timer *t = TIMER(u);
203
204         assert(t);
205         assert(t->state == TIMER_DEAD);
206
207         if (t->deserialized_state != t->state) {
208
209                 if (t->deserialized_state == TIMER_WAITING)
210                         timer_enter_waiting(t, false);
211                 else
212                         timer_set_state(t, t->deserialized_state);
213         }
214
215         return 0;
216 }
217
218 static void timer_enter_dead(Timer *t, TimerResult f) {
219         assert(t);
220
221         if (f != TIMER_SUCCESS)
222                 t->result = f;
223
224         timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
225 }
226
227 static void timer_enter_waiting(Timer *t, bool initial) {
228         TimerValue *v;
229         usec_t base = 0;
230         dual_timestamp ts;
231         bool found_monotonic = false, found_realtime = false;
232         int r;
233
234         dual_timestamp_get(&ts);
235         t->next_elapse_monotonic = t->next_elapse_realtime = 0;
236
237         LIST_FOREACH(value, v, t->values) {
238
239                 if (v->disabled)
240                         continue;
241
242                 if (v->base == TIMER_CALENDAR) {
243
244                         r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
245                         if (r < 0)
246                                 continue;
247
248                         if (!initial && v->next_elapse < ts.realtime) {
249                                 v->disabled = true;
250                                 continue;
251                         }
252
253                         if (!found_realtime)
254                                 t->next_elapse_realtime = v->next_elapse;
255                         else
256                                 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
257
258                         found_realtime = true;
259
260                 } else  {
261                         switch (v->base) {
262
263                         case TIMER_ACTIVE:
264                                 if (state_translation_table[t->state] == UNIT_ACTIVE)
265                                         base = UNIT(t)->inactive_exit_timestamp.monotonic;
266                                 else
267                                         base = ts.monotonic;
268                                 break;
269
270                         case TIMER_BOOT:
271                                 /* CLOCK_MONOTONIC equals the uptime on Linux */
272                                 base = 0;
273                                 break;
274
275                         case TIMER_STARTUP:
276                                 base = UNIT(t)->manager->userspace_timestamp.monotonic;
277                                 break;
278
279                         case TIMER_UNIT_ACTIVE:
280
281                                 if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
282                                         continue;
283
284                                 base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
285                                 break;
286
287                         case TIMER_UNIT_INACTIVE:
288
289                                 if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
290                                         continue;
291
292                                 base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
293                                 break;
294
295                         default:
296                                 assert_not_reached("Unknown timer base");
297                         }
298
299                         v->next_elapse = base + v->value;
300
301                         if (!initial && v->next_elapse < ts.monotonic) {
302                                 v->disabled = true;
303                                 continue;
304                         }
305
306                         if (!found_monotonic)
307                                 t->next_elapse_monotonic = v->next_elapse;
308                         else
309                                 t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
310
311                         found_monotonic = true;
312                 }
313         }
314
315         if (!found_monotonic && !found_realtime) {
316                 log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
317                 timer_set_state(t, TIMER_ELAPSED);
318                 return;
319         }
320
321         if (found_monotonic) {
322                 char buf[FORMAT_TIMESPAN_MAX];
323                 log_debug_unit(UNIT(t)->id,
324                                "%s: Monotonic timer elapses in %s the next time.",
325                                UNIT(t)->id,
326                                format_timespan(buf, sizeof(buf), t->next_elapse_monotonic - ts.monotonic));
327
328                 r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch);
329                 if (r < 0)
330                         goto fail;
331         } else
332                 unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
333
334         if (found_realtime) {
335                 char buf[FORMAT_TIMESTAMP_MAX];
336                 log_debug_unit(UNIT(t)->id,
337                                "%s: Realtime timer elapses at %s the next time.",
338                                UNIT(t)->id,
339                                format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
340
341                 r = unit_watch_timer(UNIT(t), CLOCK_REALTIME, false, t->next_elapse_realtime, &t->realtime_watch);
342                 if (r < 0)
343                         goto fail;
344         } else
345                 unit_unwatch_timer(UNIT(t), &t->realtime_watch);
346
347         timer_set_state(t, TIMER_WAITING);
348         return;
349
350 fail:
351         log_warning_unit(UNIT(t)->id,
352                          "%s failed to enter waiting state: %s",
353                          UNIT(t)->id, strerror(-r));
354         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
355 }
356
357 static void timer_enter_running(Timer *t) {
358         DBusError error;
359         int r;
360
361         assert(t);
362         dbus_error_init(&error);
363
364         /* Don't start job if we are supposed to go down */
365         if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
366                 return;
367
368         r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL);
369         if (r < 0)
370                 goto fail;
371
372         timer_set_state(t, TIMER_RUNNING);
373         return;
374
375 fail:
376         log_warning_unit(UNIT(t)->id,
377                          "%s failed to queue unit startup job: %s",
378                          UNIT(t)->id, bus_error(&error, r));
379         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
380
381         dbus_error_free(&error);
382 }
383
384 static int timer_start(Unit *u) {
385         Timer *t = TIMER(u);
386
387         assert(t);
388         assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
389
390         if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
391                 return -ENOENT;
392
393         t->result = TIMER_SUCCESS;
394         timer_enter_waiting(t, true);
395         return 0;
396 }
397
398 static int timer_stop(Unit *u) {
399         Timer *t = TIMER(u);
400
401         assert(t);
402         assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
403
404         timer_enter_dead(t, TIMER_SUCCESS);
405         return 0;
406 }
407
408 static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
409         Timer *t = TIMER(u);
410
411         assert(u);
412         assert(f);
413         assert(fds);
414
415         unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
416         unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
417
418         return 0;
419 }
420
421 static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
422         Timer *t = TIMER(u);
423
424         assert(u);
425         assert(key);
426         assert(value);
427         assert(fds);
428
429         if (streq(key, "state")) {
430                 TimerState state;
431
432                 state = timer_state_from_string(value);
433                 if (state < 0)
434                         log_debug_unit(u->id, "Failed to parse state value %s", value);
435                 else
436                         t->deserialized_state = state;
437         } else if (streq(key, "result")) {
438                 TimerResult f;
439
440                 f = timer_result_from_string(value);
441                 if (f < 0)
442                         log_debug_unit(u->id, "Failed to parse result value %s", value);
443                 else if (f != TIMER_SUCCESS)
444                         t->result = f;
445
446         } else
447                 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
448
449         return 0;
450 }
451
452 static UnitActiveState timer_active_state(Unit *u) {
453         assert(u);
454
455         return state_translation_table[TIMER(u)->state];
456 }
457
458 static const char *timer_sub_state_to_string(Unit *u) {
459         assert(u);
460
461         return timer_state_to_string(TIMER(u)->state);
462 }
463
464 static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
465         Timer *t = TIMER(u);
466
467         assert(t);
468         assert(elapsed == 1);
469
470         if (t->state != TIMER_WAITING)
471                 return;
472
473         log_debug_unit(u->id, "Timer elapsed on %s", u->id);
474         timer_enter_running(t);
475 }
476
477 void timer_unit_notify(Unit *u, UnitActiveState new_state) {
478         Iterator i;
479         Unit *k;
480
481         if (u->type == UNIT_TIMER)
482                 return;
483
484         SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
485                 Timer *t;
486                 TimerValue *v;
487
488                 if (k->type != UNIT_TIMER)
489                         continue;
490
491                 if (k->load_state != UNIT_LOADED)
492                         continue;
493
494                 t = TIMER(k);
495
496                 /* Reenable all timers that depend on unit state */
497                 LIST_FOREACH(value, v, t->values)
498                         if (v->base == TIMER_UNIT_ACTIVE ||
499                             v->base == TIMER_UNIT_INACTIVE)
500                                 v->disabled = false;
501
502                 switch (t->state) {
503
504                 case TIMER_WAITING:
505                 case TIMER_ELAPSED:
506
507                         /* Recalculate sleep time */
508                         timer_enter_waiting(t, false);
509                         break;
510
511                 case TIMER_RUNNING:
512
513                         if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
514                                 log_debug_unit(UNIT(t)->id,
515                                                "%s got notified about unit deactivation.",
516                                                UNIT(t)->id);
517                                 timer_enter_waiting(t, false);
518                         }
519
520                         break;
521
522                 case TIMER_DEAD:
523                 case TIMER_FAILED:
524                         break;
525
526                 default:
527                         assert_not_reached("Unknown timer state");
528                 }
529         }
530 }
531
532 static void timer_reset_failed(Unit *u) {
533         Timer *t = TIMER(u);
534
535         assert(t);
536
537         if (t->state == TIMER_FAILED)
538                 timer_set_state(t, TIMER_DEAD);
539
540         t->result = TIMER_SUCCESS;
541 }
542
543 static void timer_time_change(Unit *u) {
544         Timer *t = TIMER(u);
545
546         assert(u);
547
548         if (t->state != TIMER_WAITING)
549                 return;
550
551         log_info_unit(u->id,
552                       "%s: time change, recalculating next elapse.", u->id);
553         timer_enter_waiting(t, false);
554 }
555
556 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
557         [TIMER_DEAD] = "dead",
558         [TIMER_WAITING] = "waiting",
559         [TIMER_RUNNING] = "running",
560         [TIMER_ELAPSED] = "elapsed",
561         [TIMER_FAILED] = "failed"
562 };
563
564 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
565
566 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
567         [TIMER_ACTIVE] = "OnActiveSec",
568         [TIMER_BOOT] = "OnBootSec",
569         [TIMER_STARTUP] = "OnStartupSec",
570         [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
571         [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
572         [TIMER_CALENDAR] = "OnCalendar"
573 };
574
575 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
576
577 static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
578         [TIMER_SUCCESS] = "success",
579         [TIMER_FAILURE_RESOURCES] = "resources"
580 };
581
582 DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
583
584 const UnitVTable timer_vtable = {
585         .object_size = sizeof(Timer),
586         .sections =
587                 "Unit\0"
588                 "Timer\0"
589                 "Install\0",
590
591         .init = timer_init,
592         .done = timer_done,
593         .load = timer_load,
594
595         .coldplug = timer_coldplug,
596
597         .dump = timer_dump,
598
599         .start = timer_start,
600         .stop = timer_stop,
601
602         .serialize = timer_serialize,
603         .deserialize_item = timer_deserialize_item,
604
605         .active_state = timer_active_state,
606         .sub_state_to_string = timer_sub_state_to_string,
607
608         .timer_event = timer_timer_event,
609
610         .reset_failed = timer_reset_failed,
611         .time_change = timer_time_change,
612
613         .bus_interface = "org.freedesktop.systemd1.Timer",
614         .bus_message_handler = bus_timer_message_handler,
615         .bus_invalidating_properties =  bus_timer_invalidating_properties
616 };