• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.98
/src/core/timer.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sys/stat.h>
4
#include <sys/types.h>
5
#include <unistd.h>
6

7
#include <errno.h>
8

9
#include "alloc-util.h"
10
#include "bus-error.h"
11
#include "bus-util.h"
12
#include "dbus-timer.h"
13
#include "dbus-unit.h"
14
#include "fs-util.h"
15
#include "manager.h"
16
#include "parse-util.h"
17
#include "random-util.h"
18
#include "serialize.h"
19
#include "special.h"
20
#include "string-table.h"
21
#include "string-util.h"
22
#include "timer.h"
23
#include "unit-name.h"
24
#include "unit.h"
25
#include "user-util.h"
26
#include "virt.h"
27

28
static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
29
        [TIMER_DEAD]    = UNIT_INACTIVE,
30
        [TIMER_WAITING] = UNIT_ACTIVE,
31
        [TIMER_RUNNING] = UNIT_ACTIVE,
32
        [TIMER_ELAPSED] = UNIT_ACTIVE,
33
        [TIMER_FAILED]  = UNIT_FAILED,
34
};
35

36
static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
37

38
static void timer_init(Unit *u) {
494✔
39
        Timer *t = ASSERT_PTR(TIMER(u));
494✔
40

41
        assert(u->load_state == UNIT_STUB);
494✔
42

43
        t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
494✔
44
        t->next_elapse_realtime = USEC_INFINITY;
494✔
45
        t->accuracy_usec = u->manager->defaults.timer_accuracy_usec;
494✔
46
        t->remain_after_elapse = true;
494✔
47
}
494✔
48

49
void timer_free_values(Timer *t) {
494✔
50
        TimerValue *v;
494✔
51

52
        assert(t);
494✔
53

54
        while ((v = LIST_POP(value, t->values))) {
1,282✔
55
                calendar_spec_free(v->calendar_spec);
788✔
56
                free(v);
788✔
57
        }
58
}
494✔
59

60
static void timer_done(Unit *u) {
494✔
61
        Timer *t = ASSERT_PTR(TIMER(u));
494✔
62

63
        timer_free_values(t);
494✔
64

65
        t->monotonic_event_source = sd_event_source_disable_unref(t->monotonic_event_source);
494✔
66
        t->realtime_event_source = sd_event_source_disable_unref(t->realtime_event_source);
494✔
67

68
        t->stamp_path = mfree(t->stamp_path);
494✔
69
}
494✔
70

71
static int timer_verify(Timer *t) {
494✔
72
        assert(t);
494✔
73
        assert(UNIT(t)->load_state == UNIT_LOADED);
494✔
74

75
        if (!t->values && !t->on_clock_change && !t->on_timezone_change)
494✔
76
                return log_unit_error_errno(UNIT(t), SYNTHETIC_ERRNO(ENOEXEC), "Timer unit lacks value setting. Refusing.");
×
77

78
        return 0;
79
}
80

81
static int timer_add_default_dependencies(Timer *t) {
494✔
82
        int r;
494✔
83

84
        assert(t);
494✔
85

86
        if (!UNIT(t)->default_dependencies)
494✔
87
                return 0;
88

89
        r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
494✔
90
        if (r < 0)
494✔
91
                return r;
92

93
        if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
494✔
94
                r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
318✔
95
                if (r < 0)
318✔
96
                        return r;
97

98
                LIST_FOREACH(value, v, t->values) {
538✔
99
                        if (v->base != TIMER_CALENDAR)
428✔
100
                                continue;
220✔
101

102
                        FOREACH_STRING(target, SPECIAL_TIME_SYNC_TARGET, SPECIAL_TIME_SET_TARGET) {
624✔
103
                                r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, target, true, UNIT_DEPENDENCY_DEFAULT);
416✔
104
                                if (r < 0)
416✔
105
                                        return r;
×
106
                        }
107

108
                        break;
208✔
109
                }
110
        }
111

112
        return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
494✔
113
}
114

115
static int timer_add_trigger_dependencies(Timer *t) {
494✔
116
        Unit *x;
494✔
117
        int r;
494✔
118

119
        assert(t);
494✔
120

121
        if (UNIT_TRIGGER(UNIT(t)))
494✔
122
                return 0;
494✔
123

124
        r = unit_load_related_unit(UNIT(t), ".service", &x);
494✔
125
        if (r < 0)
494✔
126
                return r;
127

128
        return unit_add_two_dependencies(UNIT(t), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
494✔
129
}
130

131
static int timer_setup_persistent(Timer *t) {
494✔
132
        _cleanup_free_ char *stamp_path = NULL;
494✔
133
        int r;
494✔
134

135
        assert(t);
494✔
136

137
        if (!t->persistent)
494✔
138
                return 0;
139

140
        if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
206✔
141

142
                r = unit_add_mounts_for(UNIT(t), "/var/lib/systemd/timers", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES);
206✔
143
                if (r < 0)
206✔
144
                        return r;
145

146
                stamp_path = strjoin("/var/lib/systemd/timers/stamp-", UNIT(t)->id);
206✔
147
        } else {
148
                const char *e;
×
149

150
                e = getenv("XDG_DATA_HOME");
×
151
                if (e)
×
152
                        stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id);
×
153
                else {
154

155
                        _cleanup_free_ char *h = NULL;
×
156

157
                        r = get_home_dir(&h);
×
158
                        if (r < 0)
×
159
                                return log_unit_error_errno(UNIT(t), r, "Failed to determine home directory: %m");
×
160

161
                        stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id);
×
162
                }
163
        }
164

165
        if (!stamp_path)
206✔
166
                return log_oom();
×
167

168
        return free_and_replace(t->stamp_path, stamp_path);
206✔
169
}
170

171
static uint64_t timer_get_fixed_delay_hash(Timer *t) {
×
172
        static const uint8_t hash_key[] = {
×
173
                0x51, 0x0a, 0xdb, 0x76, 0x29, 0x51, 0x42, 0xc2,
174
                0x80, 0x35, 0xea, 0xe6, 0x8e, 0x3a, 0x37, 0xbd
175
        };
176

177
        struct siphash state;
×
178
        sd_id128_t machine_id;
×
179
        uid_t uid;
×
180
        int r;
×
181

182
        assert(t);
×
183

184
        uid = getuid();
×
185
        r = sd_id128_get_machine(&machine_id);
×
186
        if (r < 0) {
×
187
                log_unit_debug_errno(UNIT(t), r,
×
188
                                     "Failed to get machine ID for the fixed delay calculation, proceeding with 0: %m");
189
                machine_id = SD_ID128_NULL;
×
190
        }
191

192
        siphash24_init(&state, hash_key);
×
193
        siphash24_compress_typesafe(machine_id, &state);
×
194
        siphash24_compress_boolean(MANAGER_IS_SYSTEM(UNIT(t)->manager), &state);
×
195
        siphash24_compress_typesafe(uid, &state);
×
196
        siphash24_compress_string(UNIT(t)->id, &state);
×
197

198
        return siphash24_finalize(&state);
×
199
}
200

201
static int timer_load(Unit *u) {
494✔
202
        Timer *t = ASSERT_PTR(TIMER(u));
494✔
203
        int r;
494✔
204

205
        assert(u->load_state == UNIT_STUB);
494✔
206

207
        r = unit_load_fragment_and_dropin(u, true);
494✔
208
        if (r < 0)
494✔
209
                return r;
210

211
        if (u->load_state != UNIT_LOADED)
494✔
212
                return 0;
213

214
        /* This is a new unit? Then let's add in some extras */
215
        r = timer_add_trigger_dependencies(t);
494✔
216
        if (r < 0)
494✔
217
                return r;
218

219
        r = timer_setup_persistent(t);
494✔
220
        if (r < 0)
494✔
221
                return r;
222

223
        r = timer_add_default_dependencies(t);
494✔
224
        if (r < 0)
494✔
225
                return r;
226

227
        return timer_verify(t);
494✔
228
}
229

230
static void timer_dump(Unit *u, FILE *f, const char *prefix) {
×
231
        Timer *t = ASSERT_PTR(TIMER(u));
×
232
        Unit *trigger;
×
233

234
        assert(f);
×
235
        assert(prefix);
×
236

237
        trigger = UNIT_TRIGGER(u);
×
238

239
        fprintf(f,
×
240
                "%sTimer State: %s\n"
241
                "%sResult: %s\n"
242
                "%sUnit: %s\n"
243
                "%sPersistent: %s\n"
244
                "%sWakeSystem: %s\n"
245
                "%sAccuracy: %s\n"
246
                "%sRemainAfterElapse: %s\n"
247
                "%sFixedRandomDelay: %s\n"
248
                "%sOnClockChange: %s\n"
249
                "%sOnTimeZoneChange: %s\n"
250
                "%sDeferReactivation: %s\n",
251
                prefix, timer_state_to_string(t->state),
252
                prefix, timer_result_to_string(t->result),
253
                prefix, trigger ? trigger->id : "n/a",
254
                prefix, yes_no(t->persistent),
×
255
                prefix, yes_no(t->wake_system),
×
256
                prefix, FORMAT_TIMESPAN(t->accuracy_usec, 1),
×
257
                prefix, yes_no(t->remain_after_elapse),
×
258
                prefix, yes_no(t->fixed_random_delay),
×
259
                prefix, yes_no(t->on_clock_change),
×
260
                prefix, yes_no(t->on_timezone_change),
×
261
                prefix, yes_no(t->defer_reactivation));
×
262

263
        LIST_FOREACH(value, v, t->values)
×
264
                if (v->base == TIMER_CALENDAR) {
×
265
                        _cleanup_free_ char *p = NULL;
×
266

267
                        (void) calendar_spec_to_string(v->calendar_spec, &p);
×
268

269
                        fprintf(f,
×
270
                                "%s%s: %s\n",
271
                                prefix,
272
                                timer_base_to_string(v->base),
273
                                strna(p));
274
                } else
275
                        fprintf(f,
×
276
                                "%s%s: %s\n",
277
                                prefix,
278
                                timer_base_to_string(v->base),
279
                                FORMAT_TIMESPAN(v->value, 0));
×
280
}
×
281

282
static void timer_set_state(Timer *t, TimerState state) {
658✔
283
        TimerState old_state;
658✔
284

285
        assert(t);
658✔
286

287
        if (t->state != state)
658✔
288
                bus_unit_send_pending_change_signal(UNIT(t), false);
644✔
289

290
        old_state = t->state;
658✔
291
        t->state = state;
658✔
292

293
        if (state != TIMER_WAITING) {
658✔
294
                t->monotonic_event_source = sd_event_source_disable_unref(t->monotonic_event_source);
229✔
295
                t->realtime_event_source = sd_event_source_disable_unref(t->realtime_event_source);
229✔
296
                t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
229✔
297
                t->next_elapse_realtime = USEC_INFINITY;
229✔
298
        }
299

300
        if (state != old_state)
658✔
301
                log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state));
644✔
302

303
        unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
658✔
304
}
658✔
305

306
static void timer_enter_waiting(Timer *t, bool time_change);
307

308
static int timer_coldplug(Unit *u) {
246✔
309
        Timer *t = ASSERT_PTR(TIMER(u));
246✔
310

311
        assert(t->state == TIMER_DEAD);
246✔
312

313
        if (t->deserialized_state == t->state)
246✔
314
                return 0;
315

316
        if (t->deserialized_state == TIMER_WAITING)
156✔
317
                timer_enter_waiting(t, false);
156✔
318
        else
319
                timer_set_state(t, t->deserialized_state);
×
320

321
        return 0;
322
}
323

324
static void timer_enter_dead(Timer *t, TimerResult f) {
224✔
325
        assert(t);
224✔
326

327
        if (t->result == TIMER_SUCCESS)
224✔
328
                t->result = f;
224✔
329

330
        unit_log_result(UNIT(t), t->result == TIMER_SUCCESS, timer_result_to_string(t->result));
224✔
331
        timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
448✔
332
}
224✔
333

334
static void timer_enter_elapsed(Timer *t, bool leave_around) {
×
335
        assert(t);
×
336

337
        /* If a unit is marked with RemainAfterElapse=yes we leave it
338
         * around even after it elapsed once, so that starting it
339
         * later again does not necessarily mean immediate
340
         * retriggering. We unconditionally leave units with
341
         * TIMER_UNIT_ACTIVE or TIMER_UNIT_INACTIVE triggers around,
342
         * since they might be restarted automatically at any time
343
         * later on. */
344

345
        if (t->remain_after_elapse || leave_around)
×
346
                timer_set_state(t, TIMER_ELAPSED);
×
347
        else
348
                timer_enter_dead(t, TIMER_SUCCESS);
×
349
}
×
350

351
static void add_random(Timer *t, usec_t *v) {
429✔
352
        usec_t add;
429✔
353

354
        assert(t);
429✔
355
        assert(v);
429✔
356

357
        if (t->random_usec == 0)
429✔
358
                return;
359
        if (*v == USEC_INFINITY)
75✔
360
                return;
361

362
        add = (t->fixed_random_delay ? timer_get_fixed_delay_hash(t) : random_u64()) % t->random_usec;
150✔
363

364
        if (*v + add < *v) /* overflow */
75✔
365
                *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
×
366
        else
367
                *v += add;
75✔
368

369
        log_unit_debug(UNIT(t), "Adding %s random time.", FORMAT_TIMESPAN(add, 0));
75✔
370
}
371

372
static void timer_enter_waiting(Timer *t, bool time_change) {
429✔
373
        bool found_monotonic = false, found_realtime = false;
429✔
374
        bool leave_around = false;
429✔
375
        triple_timestamp ts;
429✔
376
        Unit *trigger;
429✔
377
        int r;
429✔
378

379
        assert(t);
429✔
380

381
        trigger = UNIT_TRIGGER(UNIT(t));
429✔
382
        if (!trigger) {
429✔
383
                log_unit_error(UNIT(t), "Unit to trigger vanished.");
×
384
                goto fail;
×
385
        }
386

387
        triple_timestamp_now(&ts);
429✔
388
        t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
429✔
389

390
        LIST_FOREACH(value, v, t->values) {
1,128✔
391
                if (v->disabled)
699✔
392
                        continue;
5✔
393

394
                if (v->base == TIMER_CALENDAR) {
694✔
395
                        usec_t b, rebased;
159✔
396

397
                        /* If DeferReactivation= is enabled, schedule the job based on the last time
398
                         * the trigger unit entered inactivity. Otherwise, if we know the last time
399
                         * this was triggered, schedule the job based relative to that. If we don't,
400
                         * just start from the activation time or realtime. */
401

402
                        if (t->defer_reactivation &&
159✔
403
                            dual_timestamp_is_set(&trigger->inactive_enter_timestamp)) {
×
404
                                if (dual_timestamp_is_set(&t->last_trigger))
×
405
                                        b = MAX(trigger->inactive_enter_timestamp.realtime,
×
406
                                                t->last_trigger.realtime);
407
                                else
408
                                        b = trigger->inactive_enter_timestamp.realtime;
409
                        } else if (dual_timestamp_is_set(&t->last_trigger))
159✔
410
                                b = t->last_trigger.realtime;
411
                        else if (dual_timestamp_is_set(&UNIT(t)->inactive_exit_timestamp))
145✔
412
                                b = UNIT(t)->inactive_exit_timestamp.realtime;
413
                        else
414
                                b = ts.realtime;
41✔
415

416
                        r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
159✔
417
                        if (r < 0)
159✔
418
                                continue;
×
419

420
                        /* To make the delay due to RandomizedDelaySec= work even at boot, if the scheduled
421
                         * time has already passed, set the time when systemd first started as the scheduled
422
                         * time. Note that we base this on the monotonic timestamp of the boot, not the
423
                         * realtime one, since the wallclock might have been off during boot. */
424
                        rebased = map_clock_usec(UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic,
159✔
425
                                                 CLOCK_MONOTONIC, CLOCK_REALTIME);
426
                        if (v->next_elapse < rebased)
159✔
427
                                v->next_elapse = rebased;
×
428

429
                        if (!found_realtime)
159✔
430
                                t->next_elapse_realtime = v->next_elapse;
159✔
431
                        else
432
                                t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
×
433

434
                        found_realtime = true;
435

436
                } else {
437
                        usec_t base;
535✔
438

439
                        switch (v->base) {
535✔
440

441
                        case TIMER_ACTIVE:
×
442
                                if (state_translation_table[t->state] == UNIT_ACTIVE)
×
443
                                        base = UNIT(t)->inactive_exit_timestamp.monotonic;
×
444
                                else
445
                                        base = ts.monotonic;
×
446
                                break;
447

448
                        case TIMER_BOOT:
75✔
449
                                if (detect_container() <= 0) {
75✔
450
                                        /* CLOCK_MONOTONIC equals the uptime on Linux */
451
                                        base = 0;
452
                                        break;
453
                                }
454
                                /* In a container we don't want to include the time the host
455
                                 * was already up when the container started, so count from
456
                                 * our own startup. */
457
                                _fallthrough_;
244✔
458
                        case TIMER_STARTUP:
459
                                base = UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic;
244✔
460
                                break;
244✔
461

462
                        case TIMER_UNIT_ACTIVE:
270✔
463
                                leave_around = true;
270✔
464
                                base = MAX(trigger->inactive_exit_timestamp.monotonic, t->last_trigger.monotonic);
270✔
465
                                if (base <= 0)
270✔
466
                                        continue;
260✔
467
                                break;
468

469
                        case TIMER_UNIT_INACTIVE:
×
470
                                leave_around = true;
×
471
                                base = MAX(trigger->inactive_enter_timestamp.monotonic, t->last_trigger.monotonic);
×
472
                                if (base <= 0)
×
473
                                        continue;
×
474
                                break;
475

476
                        default:
×
477
                                assert_not_reached();
×
478
                        }
479

480
                        v->next_elapse = usec_add(usec_shift_clock(base, CLOCK_MONOTONIC, TIMER_MONOTONIC_CLOCK(t)), v->value);
550✔
481

482
                        if (dual_timestamp_is_set(&t->last_trigger) &&
275✔
483
                            !time_change &&
15✔
484
                            v->next_elapse < triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)) &&
30✔
485
                            IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
5✔
486
                                /* This is a one time trigger, disable it now */
487
                                v->disabled = true;
5✔
488
                                continue;
5✔
489
                        }
490

491
                        if (!found_monotonic)
270✔
492
                                t->next_elapse_monotonic_or_boottime = v->next_elapse;
270✔
493
                        else
494
                                t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
×
495

496
                        found_monotonic = true;
497
                }
498
        }
499

500
        if (!found_monotonic && !found_realtime && !t->on_timezone_change && !t->on_clock_change) {
429✔
501
                log_unit_debug(UNIT(t), "Timer is elapsed.");
×
502
                timer_enter_elapsed(t, leave_around);
×
503
                return;
429✔
504
        }
505

506
        if (found_monotonic) {
429✔
507
                usec_t left;
270✔
508

509
                add_random(t, &t->next_elapse_monotonic_or_boottime);
270✔
510

511
                left = usec_sub_unsigned(t->next_elapse_monotonic_or_boottime, triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)));
540✔
512
                log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", FORMAT_TIMESPAN(left, 0));
270✔
513

514
                if (t->monotonic_event_source) {
270✔
515
                        r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
14✔
516
                        if (r < 0) {
14✔
517
                                log_unit_warning_errno(UNIT(t), r, "Failed to reschedule monotonic event source: %m");
×
518
                                goto fail;
×
519
                        }
520

521
                        r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
14✔
522
                        if (r < 0) {
14✔
523
                                log_unit_warning_errno(UNIT(t), r, "Failed to enable monotonic event source: %m");
×
524
                                goto fail;
×
525
                        }
526
                } else {
527
                        r = sd_event_add_time(
1,024✔
528
                                        UNIT(t)->manager->event,
256✔
529
                                        &t->monotonic_event_source,
530
                                        t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
256✔
531
                                        t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
532
                                        timer_dispatch, t);
533
                        if (r < 0) {
256✔
534
                                log_unit_warning_errno(UNIT(t), r, "Failed to add monotonic event source: %m");
×
535
                                goto fail;
×
536
                        }
537

538
                        (void) sd_event_source_set_description(t->monotonic_event_source, "timer-monotonic");
256✔
539
                }
540

541
        } else {
542
                r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
159✔
543
                if (r < 0) {
159✔
544
                        log_unit_warning_errno(UNIT(t), r, "Failed to disable monotonic event source: %m");
×
545
                        goto fail;
×
546
                }
547
        }
548

549
        if (found_realtime) {
429✔
550
                add_random(t, &t->next_elapse_realtime);
159✔
551

552
                log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", FORMAT_TIMESTAMP(t->next_elapse_realtime));
159✔
553

554
                if (t->realtime_event_source) {
159✔
555
                        r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
×
556
                        if (r < 0) {
×
557
                                log_unit_warning_errno(UNIT(t), r, "Failed to reschedule realtime event source: %m");
×
558
                                goto fail;
×
559
                        }
560

561
                        r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
×
562
                        if (r < 0) {
×
563
                                log_unit_warning_errno(UNIT(t), r, "Failed to enable realtime event source: %m");
×
564
                                goto fail;
×
565
                        }
566
                } else {
567
                        r = sd_event_add_time(
636✔
568
                                        UNIT(t)->manager->event,
159✔
569
                                        &t->realtime_event_source,
570
                                        t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
159✔
571
                                        t->next_elapse_realtime, t->accuracy_usec,
572
                                        timer_dispatch, t);
573
                        if (r < 0) {
159✔
574
                                log_unit_warning_errno(UNIT(t), r, "Failed to add realtime event source: %m");
×
575
                                goto fail;
×
576
                        }
577

578
                        (void) sd_event_source_set_description(t->realtime_event_source, "timer-realtime");
159✔
579
                }
580

581
        } else if (t->realtime_event_source) {
270✔
582

583
                r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
×
584
                if (r < 0) {
×
585
                        log_unit_warning_errno(UNIT(t), r, "Failed to disable realtime event source: %m");
×
586
                        goto fail;
×
587
                }
588
        }
589

590
        timer_set_state(t, TIMER_WAITING);
429✔
591
        return;
592

593
fail:
×
594
        timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
×
595
}
596

597
static void timer_enter_running(Timer *t) {
5✔
598
        _cleanup_(activation_details_unrefp) ActivationDetails *details = NULL;
5✔
599
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
5✔
600
        Unit *trigger;
5✔
601
        Job *job;
5✔
602
        int r;
5✔
603

604
        assert(t);
5✔
605

606
        /* Don't start job if we are supposed to go down */
607
        if (unit_stop_pending(UNIT(t)))
5✔
608
                return;
609

610
        trigger = UNIT_TRIGGER(UNIT(t));
5✔
611
        if (!trigger) {
5✔
612
                log_unit_error(UNIT(t), "Unit to trigger vanished.");
×
613
                goto fail;
×
614
        }
615

616
        details = activation_details_new(UNIT(t));
5✔
617
        if (!details) {
5✔
618
                log_oom();
×
619
                goto fail;
×
620
        }
621

622
        r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, &error, &job);
5✔
623
        if (r < 0) {
5✔
624
                log_unit_warning(UNIT(t), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
×
625
                goto fail;
×
626
        }
627

628
        dual_timestamp_now(&t->last_trigger);
5✔
629
        ACTIVATION_DETAILS_TIMER(details)->last_trigger = t->last_trigger;
5✔
630

631
        job_set_activation_details(job, details);
5✔
632

633
        if (t->stamp_path)
5✔
634
                touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID);
×
635

636
        timer_set_state(t, TIMER_RUNNING);
5✔
637
        return;
638

639
fail:
×
640
        timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
×
641
}
642

643
static int timer_start(Unit *u) {
254✔
644
        Timer *t = ASSERT_PTR(TIMER(u));
254✔
645
        int r;
254✔
646

647
        assert(IN_SET(t->state, TIMER_DEAD, TIMER_FAILED));
254✔
648

649
        r = unit_test_trigger_loaded(u);
254✔
650
        if (r < 0)
254✔
651
                return r;
652

653
        r = unit_acquire_invocation_id(u);
254✔
654
        if (r < 0)
254✔
655
                return r;
656

657
        t->last_trigger = DUAL_TIMESTAMP_NULL;
254✔
658

659
        /* Reenable all timers that depend on unit activation time */
660
        LIST_FOREACH(value, v, t->values)
705✔
661
                if (v->base == TIMER_ACTIVE)
451✔
662
                        v->disabled = false;
×
663

664
        if (t->stamp_path) {
254✔
665
                struct stat st;
57✔
666

667
                if (stat(t->stamp_path, &st) >= 0) {
57✔
668
                        usec_t ft;
14✔
669

670
                        /* Load the file timestamp, but only if it is actually in the past. If it is in the future,
671
                         * something is wrong with the system clock. */
672

673
                        ft = timespec_load(&st.st_mtim);
14✔
674
                        if (ft < now(CLOCK_REALTIME))
14✔
675
                                t->last_trigger.realtime = ft;
14✔
676
                        else
677
                                log_unit_warning(u, "Not using persistent file timestamp %s as it is in the future.",
×
678
                                                 FORMAT_TIMESTAMP(ft));
679

680
                } else if (errno == ENOENT)
43✔
681
                        /* The timer has never run before, make sure a stamp file exists. */
682
                        (void) touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
43✔
683
        }
684

685
        t->result = TIMER_SUCCESS;
254✔
686
        timer_enter_waiting(t, false);
254✔
687
        return 1;
688
}
689

690
static int timer_stop(Unit *u) {
224✔
691
        Timer *t = ASSERT_PTR(TIMER(u));
224✔
692

693
        assert(IN_SET(t->state, TIMER_WAITING, TIMER_RUNNING, TIMER_ELAPSED));
224✔
694

695
        timer_enter_dead(t, TIMER_SUCCESS);
224✔
696
        return 1;
224✔
697
}
698

699
static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
204✔
700
        Timer *t = ASSERT_PTR(TIMER(u));
204✔
701

702
        assert(f);
204✔
703
        assert(fds);
204✔
704

705
        (void) serialize_item(f, "state", timer_state_to_string(t->state));
204✔
706
        (void) serialize_item(f, "result", timer_result_to_string(t->result));
204✔
707

708
        if (dual_timestamp_is_set(&t->last_trigger))
204✔
709
                (void) serialize_usec(f, "last-trigger-realtime", t->last_trigger.realtime);
×
710

711
        if (t->last_trigger.monotonic > 0)
204✔
712
                (void) serialize_usec(f, "last-trigger-monotonic", t->last_trigger.monotonic);
×
713

714
        return 0;
204✔
715
}
716

717
static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
324✔
718
        Timer *t = ASSERT_PTR(TIMER(u));
324✔
719

720
        assert(key);
324✔
721
        assert(value);
324✔
722
        assert(fds);
324✔
723

724
        if (streq(key, "state")) {
324✔
725
                TimerState state;
162✔
726

727
                state = timer_state_from_string(value);
162✔
728
                if (state < 0)
162✔
729
                        log_unit_debug(u, "Failed to parse state value: %s", value);
×
730
                else
731
                        t->deserialized_state = state;
162✔
732

733
        } else if (streq(key, "result")) {
162✔
734
                TimerResult f;
162✔
735

736
                f = timer_result_from_string(value);
162✔
737
                if (f < 0)
162✔
738
                        log_unit_debug(u, "Failed to parse result value: %s", value);
×
739
                else if (f != TIMER_SUCCESS)
162✔
740
                        t->result = f;
×
741

742
        } else if (streq(key, "last-trigger-realtime"))
×
743
                (void) deserialize_usec(value, &t->last_trigger.realtime);
×
744
        else if (streq(key, "last-trigger-monotonic"))
×
745
                (void) deserialize_usec(value, &t->last_trigger.monotonic);
×
746
        else
747
                log_unit_debug(u, "Unknown serialization key: %s", key);
×
748

749
        return 0;
324✔
750
}
751

752
static UnitActiveState timer_active_state(Unit *u) {
11,718✔
753
        Timer *t = ASSERT_PTR(TIMER(u));
11,718✔
754

755
        return state_translation_table[t->state];
11,718✔
756
}
757

758
static const char *timer_sub_state_to_string(Unit *u) {
415✔
759
        Timer *t = ASSERT_PTR(TIMER(u));
415✔
760

761
        return timer_state_to_string(t->state);
415✔
762
}
763

764
static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
5✔
765
        Timer *t = ASSERT_PTR(TIMER(userdata));
5✔
766

767
        if (t->state != TIMER_WAITING)
5✔
768
                return 0;
769

770
        log_unit_debug(UNIT(t), "Timer elapsed.");
5✔
771
        timer_enter_running(t);
5✔
772
        return 0;
5✔
773
}
774

775
static void timer_trigger_notify(Unit *u, Unit *other) {
15✔
776
        Timer *t = ASSERT_PTR(TIMER(u));
15✔
777

778
        assert(other);
15✔
779

780
        /* Filter out invocations with bogus state */
781
        assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
15✔
782

783
        /* Reenable all timers that depend on unit state */
784
        LIST_FOREACH(value, v, t->values)
45✔
785
                if (IN_SET(v->base, TIMER_UNIT_ACTIVE, TIMER_UNIT_INACTIVE))
30✔
786
                        v->disabled = false;
15✔
787

788
        switch (t->state) {
15✔
789

790
        case TIMER_WAITING:
5✔
791
        case TIMER_ELAPSED:
792

793
                /* Recalculate sleep time */
794
                timer_enter_waiting(t, false);
5✔
795
                break;
5✔
796

797
        case TIMER_RUNNING:
10✔
798

799
                if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
10✔
800
                        log_unit_debug(UNIT(t), "Got notified about unit deactivation.");
5✔
801
                        timer_enter_waiting(t, false);
5✔
802
                }
803
                break;
804

805
        case TIMER_DEAD:
806
        case TIMER_FAILED:
807
                break;
808

809
        default:
×
810
                assert_not_reached();
×
811
        }
812
}
15✔
813

814
static void timer_reset_failed(Unit *u) {
3✔
815
        Timer *t = ASSERT_PTR(TIMER(u));
3✔
816

817
        if (t->state == TIMER_FAILED)
3✔
818
                timer_set_state(t, TIMER_DEAD);
×
819

820
        t->result = TIMER_SUCCESS;
3✔
821
}
3✔
822

823
static void timer_time_change(Unit *u) {
3✔
824
        Timer *t = ASSERT_PTR(TIMER(u));
3✔
825
        usec_t ts;
3✔
826

827
        if (t->state != TIMER_WAITING)
3✔
828
                return;
829

830
        /* If we appear to have triggered in the future, the system clock must
831
         * have been set backwards.  So let's rewind our own clock and allow
832
         * the future triggers to happen again :).  Exactly the same as when
833
         * you start a timer unit with Persistent=yes. */
834
        ts = now(CLOCK_REALTIME);
3✔
835
        if (t->last_trigger.realtime > ts)
3✔
836
                t->last_trigger.realtime = ts;
×
837

838
        if (t->on_clock_change) {
3✔
839
                log_unit_debug(u, "Time change, triggering activation.");
×
840
                timer_enter_running(t);
×
841
        } else {
842
                log_unit_debug(u, "Time change, recalculating next elapse.");
3✔
843
                timer_enter_waiting(t, true);
3✔
844
        }
845
}
846

847
static void timer_timezone_change(Unit *u) {
21✔
848
        Timer *t = ASSERT_PTR(TIMER(u));
21✔
849

850
        if (t->state != TIMER_WAITING)
21✔
851
                return;
852

853
        if (t->on_timezone_change) {
6✔
854
                log_unit_debug(u, "Timezone change, triggering activation.");
×
855
                timer_enter_running(t);
×
856
        } else {
857
                log_unit_debug(u, "Timezone change, recalculating next elapse.");
6✔
858
                timer_enter_waiting(t, false);
6✔
859
        }
860
}
861

862
static int timer_clean(Unit *u, ExecCleanMask mask) {
×
863
        Timer *t = ASSERT_PTR(TIMER(u));
×
864
        int r;
×
865

866
        assert(mask != 0);
×
867

868
        if (t->state != TIMER_DEAD)
×
869
                return -EBUSY;
870

871
        if (mask != EXEC_CLEAN_STATE)
×
872
                return -EUNATCH;
873

874
        r = timer_setup_persistent(t);
×
875
        if (r < 0)
×
876
                return r;
877

878
        if (!t->stamp_path)
×
879
                return -EUNATCH;
880

881
        if (unlink(t->stamp_path) && errno != ENOENT)
×
882
                return log_unit_error_errno(u, errno, "Failed to clean stamp file of timer: %m");
×
883

884
        return 0;
885
}
886

887
static int timer_can_clean(Unit *u, ExecCleanMask *ret) {
9✔
888
        Timer *t = ASSERT_PTR(TIMER(u));
9✔
889

890
        assert(ret);
9✔
891

892
        *ret = t->persistent ? EXEC_CLEAN_STATE : 0;
9✔
893
        return 0;
9✔
894
}
895

896
static int timer_can_start(Unit *u) {
254✔
897
        Timer *t = ASSERT_PTR(TIMER(u));
254✔
898
        int r;
254✔
899

900
        r = unit_test_start_limit(u);
254✔
901
        if (r < 0) {
254✔
902
                timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
×
903
                return r;
×
904
        }
905

906
        return 1;
907
}
908

909
static void activation_details_timer_serialize(ActivationDetails *details, FILE *f) {
×
910
        ActivationDetailsTimer *t = ASSERT_PTR(ACTIVATION_DETAILS_TIMER(details));
×
911

912
        assert(f);
×
913
        assert(t);
×
914

915
        (void) serialize_dual_timestamp(f, "activation-details-timer-last-trigger", &t->last_trigger);
×
916
}
×
917

918
static int activation_details_timer_deserialize(const char *key, const char *value, ActivationDetails **details) {
×
919
        int r;
×
920

921
        assert(key);
×
922
        assert(value);
×
923

924
        if (!details || !*details)
×
925
                return -EINVAL;
926

927
        ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(*details);
×
928
        if (!t)
×
929
                return -EINVAL;
930

931
        if (!streq(key, "activation-details-timer-last-trigger"))
×
932
                return -EINVAL;
933

934
        r = deserialize_dual_timestamp(value, &t->last_trigger);
×
935
        if (r < 0)
×
936
                return r;
×
937

938
        return 0;
939
}
940

941
static int activation_details_timer_append_env(ActivationDetails *details, char ***strv) {
5✔
942
        ActivationDetailsTimer *t = ASSERT_PTR(ACTIVATION_DETAILS_TIMER(details));
5✔
943
        int r;
5✔
944

945
        assert(strv);
5✔
946
        assert(t);
5✔
947

948
        if (!dual_timestamp_is_set(&t->last_trigger))
5✔
949
                return 0;
950

951
        r = strv_extendf(strv, "TRIGGER_TIMER_REALTIME_USEC=" USEC_FMT, t->last_trigger.realtime);
5✔
952
        if (r < 0)
5✔
953
                return r;
954

955
        r = strv_extendf(strv, "TRIGGER_TIMER_MONOTONIC_USEC=" USEC_FMT, t->last_trigger.monotonic);
5✔
956
        if (r < 0)
5✔
957
                return r;
×
958

959
        return 2; /* Return the number of variables added to the env block */
960
}
961

962
static int activation_details_timer_append_pair(ActivationDetails *details, char ***strv) {
2✔
963
        ActivationDetailsTimer *t = ASSERT_PTR(ACTIVATION_DETAILS_TIMER(details));
2✔
964
        int r;
2✔
965

966
        assert(strv);
2✔
967
        assert(t);
2✔
968

969
        if (!dual_timestamp_is_set(&t->last_trigger))
2✔
970
                return 0;
971

972
        r = strv_extend(strv, "trigger_timer_realtime_usec");
2✔
973
        if (r < 0)
2✔
974
                return r;
975

976
        r = strv_extendf(strv, USEC_FMT, t->last_trigger.realtime);
2✔
977
        if (r < 0)
2✔
978
                return r;
979

980
        r = strv_extend(strv, "trigger_timer_monotonic_usec");
2✔
981
        if (r < 0)
2✔
982
                return r;
983

984
        r = strv_extendf(strv, USEC_FMT, t->last_trigger.monotonic);
2✔
985
        if (r < 0)
2✔
986
                return r;
×
987

988
        return 2; /* Return the number of pairs added to the env block */
989
}
990

991
uint64_t timer_next_elapse_monotonic(const Timer *t) {
375✔
992
        assert(t);
375✔
993

994
        return (uint64_t) usec_shift_clock(t->next_elapse_monotonic_or_boottime,
375✔
995
                                           TIMER_MONOTONIC_CLOCK(t), CLOCK_MONOTONIC);
375✔
996
}
997

998
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
999
        [TIMER_ACTIVE]        = "OnActiveSec",
1000
        [TIMER_BOOT]          = "OnBootSec",
1001
        [TIMER_STARTUP]       = "OnStartupSec",
1002
        [TIMER_UNIT_ACTIVE]   = "OnUnitActiveSec",
1003
        [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
1004
        [TIMER_CALENDAR]      = "OnCalendar",
1005
};
1006

1007
DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
70✔
1008

1009
char* timer_base_to_usec_string(TimerBase i) {
6✔
1010
        _cleanup_free_ char *buf = NULL;
12✔
1011
        const char *s;
6✔
1012
        size_t l;
6✔
1013

1014
        s = timer_base_to_string(i);
6✔
1015

1016
        if (endswith(s, "Sec")) {
6✔
1017
                /* s/Sec/USec/ */
1018
                l = strlen(s);
6✔
1019
                buf = new(char, l+2);
6✔
1020
                if (!buf)
6✔
1021
                        return NULL;
1022

1023
                memcpy(buf, s, l-3);
6✔
1024
                memcpy(buf+l-3, "USec", 5);
6✔
1025
        } else {
1026
                buf = strdup(s);
×
1027
                if (!buf)
×
1028
                        return NULL;
×
1029
        }
1030

1031
        return TAKE_PTR(buf);
1032
}
1033

1034
static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
1035
        [TIMER_SUCCESS]                 = "success",
1036
        [TIMER_FAILURE_RESOURCES]       = "resources",
1037
        [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
1038
};
1039

1040
DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
1,017✔
1041

1042
const UnitVTable timer_vtable = {
1043
        .object_size = sizeof(Timer),
1044

1045
        .sections =
1046
                "Unit\0"
1047
                "Timer\0"
1048
                "Install\0",
1049
        .private_section = "Timer",
1050

1051
        .can_transient = true,
1052
        .can_fail = true,
1053
        .can_trigger = true,
1054

1055
        .init = timer_init,
1056
        .done = timer_done,
1057
        .load = timer_load,
1058

1059
        .coldplug = timer_coldplug,
1060

1061
        .dump = timer_dump,
1062

1063
        .start = timer_start,
1064
        .stop = timer_stop,
1065

1066
        .clean = timer_clean,
1067
        .can_clean = timer_can_clean,
1068

1069
        .serialize = timer_serialize,
1070
        .deserialize_item = timer_deserialize_item,
1071

1072
        .active_state = timer_active_state,
1073
        .sub_state_to_string = timer_sub_state_to_string,
1074

1075
        .trigger_notify = timer_trigger_notify,
1076

1077
        .reset_failed = timer_reset_failed,
1078
        .time_change = timer_time_change,
1079
        .timezone_change = timer_timezone_change,
1080

1081
        .bus_set_property = bus_timer_set_property,
1082

1083
        .can_start = timer_can_start,
1084
};
1085

1086
const ActivationDetailsVTable activation_details_timer_vtable = {
1087
        .object_size = sizeof(ActivationDetailsTimer),
1088

1089
        .serialize = activation_details_timer_serialize,
1090
        .deserialize = activation_details_timer_deserialize,
1091
        .append_env = activation_details_timer_append_env,
1092
        .append_pair = activation_details_timer_append_pair,
1093
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc