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

systemd / systemd / 13082854884

31 Jan 2025 09:05AM UTC coverage: 71.823% (+0.02%) from 71.804%
13082854884

push

github

poettering
uki: introduce support for a .efifw section

UKIs can be used to bundle uefi firmwares that can be measured and
used on a confidential computing environment. There can be more than one
firmware blob bundle, each one for a specific platform. Also firmware images
can themselves be containers like IGVM files that can in turn bundle the
actual firmware blob. This change is specifically for uefi firmwares, not
IGVM container files.

This change adds support to introduce a .efifw section in UKI that can be
used for firmware blobs/images. There can be multiple such sections and each
section can contain a single firmware image.

The matching .hwids entry for a specific platform can be used to select the
most appropriate firmware blob.

ukify tool has been also changed to support addition of a firmware image
in UKI.

Since firmware gets measured automatically, we do not need to measure it
separately as a part of the UKI.

292987 of 407930 relevant lines covered (71.82%)

711545.13 hits per line

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

64.42
/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 "parse-util.h"
16
#include "random-util.h"
17
#include "serialize.h"
18
#include "special.h"
19
#include "string-table.h"
20
#include "string-util.h"
21
#include "timer.h"
22
#include "unit-name.h"
23
#include "unit.h"
24
#include "user-util.h"
25
#include "virt.h"
26

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

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

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

40
        assert(u->load_state == UNIT_STUB);
553✔
41

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

48
void timer_free_values(Timer *t) {
553✔
49
        TimerValue *v;
553✔
50

51
        assert(t);
553✔
52

53
        while ((v = LIST_POP(value, t->values))) {
1,411✔
54
                calendar_spec_free(v->calendar_spec);
858✔
55
                free(v);
858✔
56
        }
57
}
553✔
58

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

62
        timer_free_values(t);
553✔
63

64
        t->monotonic_event_source = sd_event_source_disable_unref(t->monotonic_event_source);
553✔
65
        t->realtime_event_source = sd_event_source_disable_unref(t->realtime_event_source);
553✔
66

67
        t->stamp_path = mfree(t->stamp_path);
553✔
68
}
553✔
69

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

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

77
        return 0;
78
}
79

80
static int timer_add_default_dependencies(Timer *t) {
553✔
81
        int r;
553✔
82

83
        assert(t);
553✔
84

85
        if (!UNIT(t)->default_dependencies)
553✔
86
                return 0;
87

88
        r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
1,106✔
89
        if (r < 0)
553✔
90
                return r;
91

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

97
                LIST_FOREACH(value, v, t->values) {
733✔
98
                        if (v->base != TIMER_CALENDAR)
574✔
99
                                continue;
318✔
100

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

107
                        break;
256✔
108
                }
109
        }
110

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

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

118
        assert(t);
553✔
119

120
        if (UNIT_TRIGGER(UNIT(t)))
553✔
121
                return 0;
553✔
122

123
        r = unit_load_related_unit(UNIT(t), ".service", &x);
1,106✔
124
        if (r < 0)
553✔
125
                return r;
126

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

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

134
        assert(t);
553✔
135

136
        if (!t->persistent)
553✔
137
                return 0;
138

139
        if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
254✔
140

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

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

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

154
                        _cleanup_free_ char *h = NULL;
×
155

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

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

164
        if (!stamp_path)
254✔
165
                return log_oom();
×
166

167
        return free_and_replace(t->stamp_path, stamp_path);
254✔
168
}
169

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

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

181
        assert(t);
×
182

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

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

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

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

204
        assert(u->load_state == UNIT_STUB);
553✔
205

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

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

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

218
        r = timer_setup_persistent(t);
553✔
219
        if (r < 0)
553✔
220
                return r;
221

222
        r = timer_add_default_dependencies(t);
553✔
223
        if (r < 0)
553✔
224
                return r;
225

226
        return timer_verify(t);
553✔
227
}
228

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

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

236
        trigger = UNIT_TRIGGER(u);
×
237

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

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

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

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

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

284
        assert(t);
679✔
285

286
        if (t->state != state)
679✔
287
                bus_unit_send_pending_change_signal(UNIT(t), false);
664✔
288

289
        old_state = t->state;
679✔
290
        t->state = state;
679✔
291

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

299
        if (state != old_state)
679✔
300
                log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state));
1,307✔
301

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

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

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

310
        assert(t->state == TIMER_DEAD);
344✔
311

312
        if (t->deserialized_state == t->state)
344✔
313
                return 0;
314

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

320
        return 0;
321
}
322

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

326
        if (t->result == TIMER_SUCCESS)
238✔
327
                t->result = f;
238✔
328

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

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

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

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

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

353
        assert(t);
437✔
354
        assert(v);
437✔
355

356
        if (t->random_usec == 0)
437✔
357
                return;
358
        if (*v == USEC_INFINITY)
73✔
359
                return;
360

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

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

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

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

378
        assert(t);
437✔
379

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

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

389
        LIST_FOREACH(value, v, t->values) {
1,104✔
390
                if (v->disabled)
667✔
391
                        continue;
4✔
392

393
                if (v->base == TIMER_CALENDAR) {
663✔
394
                        usec_t b, rebased;
207✔
395

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

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

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

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

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

433
                        found_realtime = true;
434

435
                } else {
436
                        usec_t base;
456✔
437

438
                        switch (v->base) {
456✔
439

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

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

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

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

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

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

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

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

495
                        found_monotonic = true;
496
                }
497
        }
498

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

505
        if (found_monotonic) {
437✔
506
                usec_t left;
230✔
507

508
                add_random(t, &t->next_elapse_monotonic_or_boottime);
230✔
509

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

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

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

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

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

548
        if (found_realtime) {
437✔
549
                add_random(t, &t->next_elapse_realtime);
207✔
550

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

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

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

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

580
        } else if (t->realtime_event_source) {
230✔
581

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

589
        timer_set_state(t, TIMER_WAITING);
437✔
590
        return;
591

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

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

603
        assert(t);
5✔
604

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

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

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

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

627
        dual_timestamp_now(&t->last_trigger);
4✔
628
        ACTIVATION_DETAILS_TIMER(details)->last_trigger = t->last_trigger;
4✔
629

630
        job_set_activation_details(job, details);
4✔
631

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

635
        timer_set_state(t, TIMER_RUNNING);
4✔
636
        return;
637

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

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

646
        assert(IN_SET(t->state, TIMER_DEAD, TIMER_FAILED));
268✔
647

648
        r = unit_test_trigger_loaded(u);
268✔
649
        if (r < 0)
268✔
650
                return r;
651

652
        r = unit_acquire_invocation_id(u);
268✔
653
        if (r < 0)
268✔
654
                return r;
655

656
        t->last_trigger = DUAL_TIMESTAMP_NULL;
268✔
657

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

663
        if (t->stamp_path) {
268✔
664
                struct stat st;
109✔
665

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

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

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

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

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

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

692
        assert(IN_SET(t->state, TIMER_WAITING, TIMER_RUNNING, TIMER_ELAPSED));
238✔
693

694
        timer_enter_dead(t, TIMER_SUCCESS);
238✔
695
        return 1;
238✔
696
}
697

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

701
        assert(f);
302✔
702
        assert(fds);
302✔
703

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

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

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

713
        return 0;
302✔
714
}
715

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

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

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

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

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

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

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

748
        return 0;
324✔
749
}
750

751
static UnitActiveState timer_active_state(Unit *u) {
15,022✔
752
        Timer *t = ASSERT_PTR(TIMER(u));
15,022✔
753

754
        return state_translation_table[t->state];
15,022✔
755
}
756

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

760
        return timer_state_to_string(t->state);
696✔
761
}
762

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

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

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

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

777
        assert(other);
12✔
778

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

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

787
        switch (t->state) {
12✔
788

789
        case TIMER_WAITING:
4✔
790
        case TIMER_ELAPSED:
791

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

796
        case TIMER_RUNNING:
8✔
797

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

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

808
        default:
×
809
                assert_not_reached();
×
810
        }
811
}
12✔
812

813
static void timer_reset_failed(Unit *u) {
×
814
        Timer *t = ASSERT_PTR(TIMER(u));
×
815

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

819
        t->result = TIMER_SUCCESS;
×
820
}
×
821

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

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

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

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

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

849
        if (t->state != TIMER_WAITING)
23✔
850
                return;
851

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

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

865
        assert(mask != 0);
×
866

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

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

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

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

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

883
        return 0;
884
}
885

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

889
        assert(ret);
9✔
890

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

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

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

905
        return 1;
906
}
907

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

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

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

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

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

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

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

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

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

937
        return 0;
938
}
939

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

944
        assert(strv);
4✔
945
        assert(t);
4✔
946

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

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

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

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

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

965
        assert(strv);
×
966
        assert(t);
×
967

968
        if (!dual_timestamp_is_set(&t->last_trigger))
×
969
                return 0;
970

971
        r = strv_extend(strv, "trigger_timer_realtime_usec");
×
972
        if (r < 0)
×
973
                return r;
974

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

979
        r = strv_extend(strv, "trigger_timer_monotonic_usec");
×
980
        if (r < 0)
×
981
                return r;
982

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

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

990
uint64_t timer_next_elapse_monotonic(const Timer *t) {
656✔
991
        assert(t);
656✔
992

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

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

1006
DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
70✔
1007

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

1013
        s = timer_base_to_string(i);
6✔
1014

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

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

1030
        return TAKE_PTR(buf);
1031
}
1032

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

1039
DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
1,410✔
1040

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

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

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

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

1058
        .coldplug = timer_coldplug,
1059

1060
        .dump = timer_dump,
1061

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

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

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

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

1074
        .trigger_notify = timer_trigger_notify,
1075

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

1080
        .bus_set_property = bus_timer_set_property,
1081

1082
        .can_start = timer_can_start,
1083
};
1084

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

1088
        .serialize = activation_details_timer_serialize,
1089
        .deserialize = activation_details_timer_deserialize,
1090
        .append_env = activation_details_timer_append_env,
1091
        .append_pair = activation_details_timer_append_pair,
1092
};
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