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

systemd / systemd / 23927985597

02 Apr 2026 07:45PM UTC coverage: 72.362% (+0.02%) from 72.343%
23927985597

push

github

daandemeyer
ci: Drop base64 encoding in claude review workflow

Doesn't seem to work nearly as good as the previous solution which
just told claude not to escape stuff.

319121 of 441004 relevant lines covered (72.36%)

1167673.48 hits per line

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

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

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

7
#include "sd-bus.h"
8

9
#include "alloc-util.h"
10
#include "bus-error.h"
11
#include "calendarspec.h"
12
#include "dbus-timer.h"
13
#include "dbus-unit.h"
14
#include "fs-util.h"
15
#include "manager.h"
16
#include "random-util.h"
17
#include "serialize.h"
18
#include "siphash24.h"
19
#include "special.h"
20
#include "string-table.h"
21
#include "string-util.h"
22
#include "strv.h"
23
#include "timer.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) {
573✔
39
        Timer *t = ASSERT_PTR(TIMER(u));
573✔
40

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

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

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

52
        assert(t);
578✔
53

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

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

63
        timer_free_values(t);
573✔
64

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

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

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

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

78
        return 0;
79
}
80

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

84
        assert(t);
569✔
85

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

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

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

98
                LIST_FOREACH(value, v, t->values) {
577✔
99
                        if (v->base != TIMER_CALENDAR)
455✔
100
                                continue;
233✔
101

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

108
                        break;
222✔
109
                }
110
        }
111

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

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

119
        assert(t);
569✔
120

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

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

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

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

135
        assert(t);
569✔
136

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

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

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

146
                stamp_path = strjoin("/var/lib/systemd/timers/stamp-", UNIT(t)->id);
216✔
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)
216✔
166
                return log_oom();
×
167

168
        return free_and_replace(t->stamp_path, stamp_path);
216✔
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) {
580✔
202
        Timer *t = ASSERT_PTR(TIMER(u));
580✔
203
        int r;
580✔
204

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

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

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

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

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

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

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

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

234
        assert(f);
3✔
235
        assert(prefix);
3✔
236

237
        trigger = UNIT_TRIGGER(u);
3✔
238

239
        fprintf(f,
3✔
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),
3✔
255
                prefix, yes_no(t->wake_system),
3✔
256
                prefix, FORMAT_TIMESPAN(t->accuracy_usec, 1),
3✔
257
                prefix, yes_no(t->remain_after_elapse),
3✔
258
                prefix, yes_no(t->fixed_random_delay),
3✔
259
                prefix, yes_no(t->on_clock_change),
3✔
260
                prefix, yes_no(t->on_timezone_change),
3✔
261
                prefix, yes_no(t->defer_reactivation));
3✔
262

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

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

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

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

285
        assert(t);
756✔
286

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

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

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

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

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

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

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

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

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

316
        if (t->deserialized_state == TIMER_WAITING)
183✔
317
                timer_enter_waiting(t, false);
183✔
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) {
247✔
325
        assert(t);
247✔
326

327
        if (t->result == TIMER_SUCCESS || f == TIMER_FAILURE_START_LIMIT_HIT)
247✔
328
                t->result = f;
247✔
329

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

334
static void timer_enter_elapsed(Timer *t, bool leave_around) {
1✔
335
        assert(t);
1✔
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)
1✔
346
                timer_set_state(t, TIMER_ELAPSED);
1✔
347
        else
348
                timer_enter_dead(t, TIMER_SUCCESS);
×
349
}
1✔
350

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

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

357
        if (t->random_delay_usec == 0)
499✔
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_delay_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) {
499✔
373
        bool found_monotonic = false, found_realtime = false;
499✔
374
        bool leave_around = false;
499✔
375
        triple_timestamp ts;
499✔
376
        Unit *trigger;
499✔
377
        int r;
499✔
378

379
        assert(t);
499✔
380

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

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

390
        LIST_FOREACH(value, v, t->values) {
1,337✔
391
                if (v->disabled)
838✔
392
                        continue;
6✔
393

394
                if (v->base == TIMER_CALENDAR) {
832✔
395
                        bool rebase_after_boot_time = false;
165✔
396
                        usec_t b, random_offset = 0;
165✔
397
                        usec_t boot_monotonic = UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic;
165✔
398

399
                        if (t->random_offset_usec != 0)
165✔
400
                                random_offset = timer_get_fixed_delay_hash(t) % t->random_offset_usec;
×
401

402
                        /* If DeferReactivation= is enabled, schedule the job based on the last time
403
                         * the trigger unit entered inactivity. Otherwise, if we know the last time
404
                         * this was triggered, schedule the job based relative to that. If we don't,
405
                         * just start from the activation time or realtime.
406
                         *
407
                         * Unless we have a real last-trigger time, we subtract the random_offset because
408
                         * any event that elapsed within the last random_offset has actually been delayed
409
                         * and thus hasn't truly elapsed yet. */
410

411
                        if (t->defer_reactivation &&
165✔
412
                            dual_timestamp_is_set(&trigger->inactive_enter_timestamp)) {
3✔
413
                                if (dual_timestamp_is_set(&t->last_trigger))
2✔
414
                                        b = MAX(trigger->inactive_enter_timestamp.realtime,
2✔
415
                                                t->last_trigger.realtime);
416
                                else
417
                                        b = trigger->inactive_enter_timestamp.realtime;
418
                        } else if (dual_timestamp_is_set(&t->last_trigger)) {
163✔
419
                                b = t->last_trigger.realtime;
14✔
420

421
                                /* Check if the last_trigger timestamp is older than the current machine
422
                                 * boot. If so, this means the timestamp came from a stamp file of a
423
                                 * persistent timer and we need to rebase it to make RandomizedDelaySec=
424
                                 * work (see below). */
425
                                if (t->last_trigger.monotonic < boot_monotonic)
14✔
426
                                        rebase_after_boot_time = true;
14✔
427
                        } else if (dual_timestamp_is_set(&UNIT(t)->inactive_exit_timestamp))
149✔
428
                                b = UNIT(t)->inactive_exit_timestamp.realtime - random_offset;
102✔
429
                        else {
430
                                b = ts.realtime - random_offset;
47✔
431
                                rebase_after_boot_time = true;
47✔
432
                        }
433

434
                        r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
165✔
435
                        if (r < 0)
165✔
436
                                continue;
×
437

438
                        v->next_elapse += random_offset;
165✔
439

440
                        if (rebase_after_boot_time) {
165✔
441
                                /* To make the delay due to RandomizedDelaySec= work even at boot, if the scheduled
442
                                 * time has already passed, set the time when systemd first started as the scheduled
443
                                 * time. Note that we base this on the monotonic timestamp of the boot, not the
444
                                 * realtime one, since the wallclock might have been off during boot. */
445
                                usec_t rebased = map_clock_usec(boot_monotonic, CLOCK_MONOTONIC, CLOCK_REALTIME);
61✔
446
                                if (v->next_elapse < rebased)
61✔
447
                                        v->next_elapse = rebased;
×
448
                        }
449

450
                        if (!found_realtime)
165✔
451
                                t->next_elapse_realtime = v->next_elapse;
165✔
452
                        else
453
                                t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
×
454

455
                        found_realtime = true;
456

457
                } else {
458
                        usec_t base;
667✔
459

460
                        switch (v->base) {
667✔
461

462
                        case TIMER_ACTIVE:
2✔
463
                                if (state_translation_table[t->state] == UNIT_ACTIVE)
2✔
464
                                        base = UNIT(t)->inactive_exit_timestamp.monotonic;
×
465
                                else
466
                                        base = ts.monotonic;
2✔
467
                                break;
468

469
                        case TIMER_BOOT:
76✔
470
                                if (detect_container() <= 0) {
76✔
471
                                        /* CLOCK_MONOTONIC equals the uptime on Linux */
472
                                        base = 0;
473
                                        break;
474
                                }
475
                                /* In a container we don't want to include the time the host
476
                                 * was already up when the container started, so count from
477
                                 * our own startup. */
478
                                _fallthrough_;
308✔
479
                        case TIMER_STARTUP:
480
                                base = UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic;
308✔
481
                                break;
308✔
482

483
                        case TIMER_UNIT_ACTIVE:
334✔
484
                                leave_around = true;
334✔
485
                                base = MAX(trigger->inactive_exit_timestamp.monotonic, t->last_trigger.monotonic);
334✔
486
                                if (base <= 0)
334✔
487
                                        continue;
322✔
488
                                break;
489

490
                        case TIMER_UNIT_INACTIVE:
2✔
491
                                leave_around = true;
2✔
492
                                base = MAX(trigger->inactive_enter_timestamp.monotonic, t->last_trigger.monotonic);
2✔
493
                                if (base <= 0)
2✔
494
                                        continue;
2✔
495
                                break;
496

497
                        default:
×
498
                                assert_not_reached();
×
499
                        }
500

501
                        if (!time_change)
343✔
502
                                v->next_elapse = usec_add(usec_shift_clock(base, CLOCK_MONOTONIC, TIMER_MONOTONIC_CLOCK(t)), v->value);
999✔
503

504
                        if (dual_timestamp_is_set(&t->last_trigger) &&
343✔
505
                            !time_change &&
18✔
506
                            v->next_elapse < triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)) &&
36✔
507
                            IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
6✔
508
                                /* This is a one time trigger, disable it now */
509
                                v->disabled = true;
6✔
510
                                continue;
6✔
511
                        }
512

513
                        if (!found_monotonic)
337✔
514
                                t->next_elapse_monotonic_or_boottime = v->next_elapse;
334✔
515
                        else
516
                                t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
3✔
517

518
                        found_monotonic = true;
519
                }
520
        }
521

522
        if (!found_monotonic && !found_realtime && !t->on_timezone_change && !t->on_clock_change) {
499✔
523
                log_unit_debug(UNIT(t), "Timer is elapsed.");
1✔
524
                timer_enter_elapsed(t, leave_around);
1✔
525
                return;
500✔
526
        }
527

528
        if (found_monotonic) {
498✔
529
                usec_t left;
334✔
530

531
                add_random_delay(t, &t->next_elapse_monotonic_or_boottime);
334✔
532

533
                left = usec_sub_unsigned(t->next_elapse_monotonic_or_boottime, triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)));
668✔
534
                log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", FORMAT_TIMESPAN(left, 0));
334✔
535

536
                if (t->monotonic_event_source) {
334✔
537
                        r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
28✔
538
                        if (r < 0) {
28✔
539
                                log_unit_warning_errno(UNIT(t), r, "Failed to reschedule monotonic event source: %m");
×
540
                                goto fail;
×
541
                        }
542

543
                        r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
28✔
544
                        if (r < 0) {
28✔
545
                                log_unit_warning_errno(UNIT(t), r, "Failed to enable monotonic event source: %m");
×
546
                                goto fail;
×
547
                        }
548
                } else {
549
                        r = sd_event_add_time(
1,224✔
550
                                        UNIT(t)->manager->event,
306✔
551
                                        &t->monotonic_event_source,
552
                                        t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
306✔
553
                                        t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
306✔
554
                                        timer_dispatch, t);
555
                        if (r < 0) {
306✔
556
                                log_unit_warning_errno(UNIT(t), r, "Failed to add monotonic event source: %m");
×
557
                                goto fail;
×
558
                        }
559

560
                        (void) sd_event_source_set_description(t->monotonic_event_source, "timer-monotonic");
306✔
561
                }
562

563
        } else {
564
                r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
164✔
565
                if (r < 0) {
164✔
566
                        log_unit_warning_errno(UNIT(t), r, "Failed to disable monotonic event source: %m");
×
567
                        goto fail;
×
568
                }
569
        }
570

571
        if (found_realtime) {
498✔
572
                add_random_delay(t, &t->next_elapse_realtime);
165✔
573

574
                log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", FORMAT_TIMESTAMP(t->next_elapse_realtime));
165✔
575

576
                if (t->realtime_event_source) {
165✔
577
                        r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
×
578
                        if (r < 0) {
×
579
                                log_unit_warning_errno(UNIT(t), r, "Failed to reschedule realtime event source: %m");
×
580
                                goto fail;
×
581
                        }
582

583
                        r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
×
584
                        if (r < 0) {
×
585
                                log_unit_warning_errno(UNIT(t), r, "Failed to enable realtime event source: %m");
×
586
                                goto fail;
×
587
                        }
588
                } else {
589
                        r = sd_event_add_time(
660✔
590
                                        UNIT(t)->manager->event,
165✔
591
                                        &t->realtime_event_source,
592
                                        t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
165✔
593
                                        t->next_elapse_realtime, t->accuracy_usec,
165✔
594
                                        timer_dispatch, t);
595
                        if (r < 0) {
165✔
596
                                log_unit_warning_errno(UNIT(t), r, "Failed to add realtime event source: %m");
×
597
                                goto fail;
×
598
                        }
599

600
                        (void) sd_event_source_set_description(t->realtime_event_source, "timer-realtime");
165✔
601
                }
602

603
        } else if (t->realtime_event_source) {
333✔
604

605
                r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
×
606
                if (r < 0) {
×
607
                        log_unit_warning_errno(UNIT(t), r, "Failed to disable realtime event source: %m");
×
608
                        goto fail;
×
609
                }
610
        }
611

612
        timer_set_state(t, TIMER_WAITING);
498✔
613
        return;
614

615
fail:
×
616
        timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
×
617
}
618

619
static void timer_enter_running(Timer *t) {
10✔
620
        _cleanup_(activation_details_unrefp) ActivationDetails *details = NULL;
10✔
621
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
10✔
622
        Unit *trigger;
10✔
623
        Job *job;
10✔
624
        int r;
10✔
625

626
        assert(t);
10✔
627

628
        /* Don't start job if we are supposed to go down */
629
        if (unit_stop_pending(UNIT(t)))
10✔
630
                return;
631

632
        trigger = UNIT_TRIGGER(UNIT(t));
10✔
633
        if (!trigger) {
10✔
634
                log_unit_error(UNIT(t), "Unit to trigger vanished.");
×
635
                goto fail;
×
636
        }
637

638
        details = activation_details_new(UNIT(t));
10✔
639
        if (!details) {
10✔
640
                log_oom();
×
641
                goto fail;
×
642
        }
643

644
        r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, &error, &job);
10✔
645
        if (r < 0) {
10✔
646
                log_unit_warning(UNIT(t), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
×
647
                goto fail;
×
648
        }
649

650
        dual_timestamp_now(&t->last_trigger);
10✔
651
        ACTIVATION_DETAILS_TIMER(details)->last_trigger = t->last_trigger;
10✔
652

653
        job_set_activation_details(job, details);
10✔
654

655
        if (t->stamp_path)
10✔
656
                touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID);
×
657

658
        timer_set_state(t, TIMER_RUNNING);
10✔
659
        return;
660

661
fail:
×
662
        timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
×
663
}
664

665
static int timer_start(Unit *u) {
280✔
666
        Timer *t = ASSERT_PTR(TIMER(u));
280✔
667
        int r;
280✔
668

669
        assert(IN_SET(t->state, TIMER_DEAD, TIMER_FAILED));
280✔
670

671
        r = unit_acquire_invocation_id(u);
280✔
672
        if (r < 0)
280✔
673
                return r;
674

675
        /* Reenable all timers that depend on unit activation time */
676
        LIST_FOREACH(value, v, t->values)
782✔
677
                if (v->base == TIMER_ACTIVE)
502✔
678
                        v->disabled = false;
2✔
679

680
        if (t->stamp_path) {
280✔
681
                struct stat st;
61✔
682

683
                if (stat(t->stamp_path, &st) >= 0) {
61✔
684
                        usec_t ft;
14✔
685

686
                        /* Load the file timestamp, but only if it is actually in the past. If it is in the future,
687
                         * something is wrong with the system clock. */
688

689
                        ft = timespec_load(&st.st_mtim);
14✔
690
                        if (ft < now(CLOCK_REALTIME))
14✔
691
                                t->last_trigger.realtime = ft;
14✔
692
                        else
693
                                log_unit_warning(u, "Not using persistent file timestamp %s as it is in the future.",
×
694
                                                 FORMAT_TIMESTAMP(ft));
695

696
                } else if (errno == ENOENT)
47✔
697
                        /* The timer has never run before, make sure a stamp file exists. */
698
                        (void) touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
47✔
699
        }
700

701
        t->result = TIMER_SUCCESS;
280✔
702
        timer_enter_waiting(t, false);
280✔
703
        return 1;
280✔
704
}
705

706
static int timer_stop(Unit *u) {
247✔
707
        Timer *t = ASSERT_PTR(TIMER(u));
247✔
708

709
        assert(IN_SET(t->state, TIMER_WAITING, TIMER_RUNNING, TIMER_ELAPSED));
247✔
710

711
        timer_enter_dead(t, TIMER_SUCCESS);
247✔
712
        return 1;
247✔
713
}
714

715
static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
238✔
716
        Timer *t = ASSERT_PTR(TIMER(u));
238✔
717

718
        assert(f);
238✔
719
        assert(fds);
238✔
720

721
        (void) serialize_item(f, "state", timer_state_to_string(t->state));
238✔
722
        (void) serialize_item(f, "result", timer_result_to_string(t->result));
238✔
723

724
        if (dual_timestamp_is_set(&t->last_trigger))
238✔
725
                (void) serialize_usec(f, "last-trigger-realtime", t->last_trigger.realtime);
×
726

727
        if (t->last_trigger.monotonic > 0)
238✔
728
                (void) serialize_usec(f, "last-trigger-monotonic", t->last_trigger.monotonic);
×
729

730
        return 0;
238✔
731
}
732

733
static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
378✔
734
        Timer *t = ASSERT_PTR(TIMER(u));
378✔
735

736
        assert(key);
378✔
737
        assert(value);
378✔
738
        assert(fds);
378✔
739

740
        if (streq(key, "state")) {
378✔
741
                TimerState state;
189✔
742

743
                state = timer_state_from_string(value);
189✔
744
                if (state < 0)
189✔
745
                        log_unit_debug(u, "Failed to parse state value: %s", value);
×
746
                else
747
                        t->deserialized_state = state;
189✔
748

749
        } else if (streq(key, "result")) {
189✔
750
                TimerResult f;
189✔
751

752
                f = timer_result_from_string(value);
189✔
753
                if (f < 0)
189✔
754
                        log_unit_debug(u, "Failed to parse result value: %s", value);
×
755
                else if (f != TIMER_SUCCESS)
189✔
756
                        t->result = f;
×
757

758
        } else if (streq(key, "last-trigger-realtime"))
×
759
                (void) deserialize_usec(value, &t->last_trigger.realtime);
×
760
        else if (streq(key, "last-trigger-monotonic"))
×
761
                (void) deserialize_usec(value, &t->last_trigger.monotonic);
×
762
        else
763
                log_unit_debug(u, "Unknown serialization key: %s", key);
×
764

765
        return 0;
378✔
766
}
767

768
static UnitActiveState timer_active_state(Unit *u) {
13,105✔
769
        Timer *t = ASSERT_PTR(TIMER(u));
13,105✔
770

771
        return state_translation_table[t->state];
13,105✔
772
}
773

774
static const char *timer_sub_state_to_string(Unit *u) {
417✔
775
        Timer *t = ASSERT_PTR(TIMER(u));
417✔
776

777
        return timer_state_to_string(t->state);
417✔
778
}
779

780
static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
10✔
781
        Timer *t = ASSERT_PTR(TIMER(userdata));
10✔
782

783
        if (t->state != TIMER_WAITING)
10✔
784
                return 0;
785

786
        log_unit_debug(UNIT(t), "Timer elapsed.");
10✔
787
        timer_enter_running(t);
10✔
788
        return 0;
10✔
789
}
790

791
static void timer_trigger_notify(Unit *u, Unit *other) {
29✔
792
        Timer *t = ASSERT_PTR(TIMER(u));
29✔
793

794
        assert(other);
29✔
795

796
        /* Filter out invocations with bogus state */
797
        assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
29✔
798

799
        /* Reenable all timers that depend on unit state */
800
        LIST_FOREACH(value, v, t->values)
94✔
801
                if (IN_SET(v->base, TIMER_UNIT_ACTIVE, TIMER_UNIT_INACTIVE))
65✔
802
                        v->disabled = false;
24✔
803

804
        switch (t->state) {
29✔
805

806
        case TIMER_WAITING:
6✔
807
        case TIMER_ELAPSED:
808

809
                /* Recalculate sleep time */
810
                timer_enter_waiting(t, false);
6✔
811
                break;
6✔
812

813
        case TIMER_RUNNING:
23✔
814

815
                if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
23✔
816
                        log_unit_debug(UNIT(t), "Got notified about unit deactivation.");
8✔
817
                        timer_enter_waiting(t, false);
8✔
818
                }
819
                break;
820

821
        case TIMER_DEAD:
822
        case TIMER_FAILED:
823
                break;
824

825
        default:
×
826
                assert_not_reached();
×
827
        }
828
}
29✔
829

830
static void timer_reset_failed(Unit *u) {
3✔
831
        Timer *t = ASSERT_PTR(TIMER(u));
3✔
832

833
        if (t->state == TIMER_FAILED)
3✔
834
                timer_set_state(t, TIMER_DEAD);
×
835

836
        t->result = TIMER_SUCCESS;
3✔
837
}
3✔
838

839
static void timer_time_change(Unit *u) {
11✔
840
        Timer *t = ASSERT_PTR(TIMER(u));
11✔
841
        usec_t ts;
11✔
842

843
        if (t->state != TIMER_WAITING)
11✔
844
                return;
845

846
        /* If we appear to have triggered in the future, the system clock must
847
         * have been set backwards.  So let's rewind our own clock and allow
848
         * the future triggers to happen again :).  Exactly the same as when
849
         * you start a timer unit with Persistent=yes. */
850
        ts = now(CLOCK_REALTIME);
10✔
851
        if (t->last_trigger.realtime > ts)
10✔
852
                t->last_trigger.realtime = ts;
×
853

854
        if (t->on_clock_change) {
10✔
855
                log_unit_debug(u, "Time change, triggering activation.");
×
856
                timer_enter_running(t);
×
857
        } else {
858
                log_unit_debug(u, "Time change, recalculating next elapse.");
10✔
859
                timer_enter_waiting(t, true);
10✔
860
        }
861
}
862

863
static void timer_timezone_change(Unit *u) {
67✔
864
        Timer *t = ASSERT_PTR(TIMER(u));
67✔
865

866
        if (t->state != TIMER_WAITING)
67✔
867
                return;
868

869
        if (t->on_timezone_change) {
12✔
870
                log_unit_debug(u, "Timezone change, triggering activation.");
×
871
                timer_enter_running(t);
×
872
        } else {
873
                log_unit_debug(u, "Timezone change, recalculating next elapse.");
12✔
874
                timer_enter_waiting(t, false);
12✔
875
        }
876
}
877

878
static int timer_clean(Unit *u, ExecCleanMask mask) {
×
879
        Timer *t = ASSERT_PTR(TIMER(u));
×
880
        int r;
×
881

882
        assert(mask != 0);
×
883

884
        if (t->state != TIMER_DEAD)
×
885
                return -EBUSY;
886

887
        if (mask != EXEC_CLEAN_STATE)
×
888
                return -EUNATCH;
889

890
        r = timer_setup_persistent(t);
×
891
        if (r < 0)
×
892
                return r;
893

894
        if (!t->stamp_path)
×
895
                return -EUNATCH;
896

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

900
        return 0;
901
}
902

903
static int timer_can_clean(Unit *u, ExecCleanMask *ret) {
7✔
904
        Timer *t = ASSERT_PTR(TIMER(u));
7✔
905

906
        assert(ret);
7✔
907

908
        *ret = t->persistent ? EXEC_CLEAN_STATE : 0;
7✔
909
        return 0;
7✔
910
}
911

912
static int timer_test_startable(Unit *u) {
280✔
913
        Timer *t = ASSERT_PTR(TIMER(u));
280✔
914
        int r;
280✔
915

916
        r = unit_test_trigger_loaded(u);
280✔
917
        if (r < 0)
280✔
918
                return r;
919

920
        r = unit_test_start_limit(u);
280✔
921
        if (r < 0) {
280✔
922
                timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
×
923
                return r;
×
924
        }
925

926
        return true;
927
}
928

929
static void activation_details_timer_serialize(const ActivationDetails *details, FILE *f) {
×
930
        const ActivationDetailsTimer *t = ASSERT_PTR(ACTIVATION_DETAILS_TIMER(details));
×
931

932
        assert(f);
×
933
        assert(t);
×
934

935
        (void) serialize_dual_timestamp(f, "activation-details-timer-last-trigger", &t->last_trigger);
×
936
}
×
937

938
static int activation_details_timer_deserialize(const char *key, const char *value, ActivationDetails **details) {
×
939
        int r;
×
940

941
        assert(key);
×
942
        assert(value);
×
943
        POINTER_MAY_BE_NULL(details);
×
944

945
        if (!details || !*details)
×
946
                return -EINVAL;
947

948
        ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(*details);
×
949
        if (!t)
×
950
                return -EINVAL;
951

952
        if (!streq(key, "activation-details-timer-last-trigger"))
×
953
                return -EINVAL;
954

955
        r = deserialize_dual_timestamp(value, &t->last_trigger);
×
956
        if (r < 0)
×
957
                return r;
×
958

959
        return 0;
960
}
961

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

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

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

972
        r = strv_extendf(strv, "TRIGGER_TIMER_REALTIME_USEC=" USEC_FMT, t->last_trigger.realtime);
10✔
973
        if (r < 0)
10✔
974
                return r;
975

976
        r = strv_extendf(strv, "TRIGGER_TIMER_MONOTONIC_USEC=" USEC_FMT, t->last_trigger.monotonic);
10✔
977
        if (r < 0)
10✔
978
                return r;
×
979

980
        return 2; /* Return the number of variables added to the env block */
981
}
982

983
static int activation_details_timer_append_pair(const ActivationDetails *details, char ***strv) {
37✔
984
        const ActivationDetailsTimer *t = ASSERT_PTR(ACTIVATION_DETAILS_TIMER(details));
37✔
985
        int r;
37✔
986

987
        assert(strv);
37✔
988
        assert(t);
37✔
989

990
        if (!dual_timestamp_is_set(&t->last_trigger))
37✔
991
                return 0;
992

993
        r = strv_extend(strv, "trigger_timer_realtime_usec");
37✔
994
        if (r < 0)
37✔
995
                return r;
996

997
        r = strv_extendf(strv, USEC_FMT, t->last_trigger.realtime);
37✔
998
        if (r < 0)
37✔
999
                return r;
1000

1001
        r = strv_extend(strv, "trigger_timer_monotonic_usec");
37✔
1002
        if (r < 0)
37✔
1003
                return r;
1004

1005
        r = strv_extendf(strv, USEC_FMT, t->last_trigger.monotonic);
37✔
1006
        if (r < 0)
37✔
1007
                return r;
×
1008

1009
        return 2; /* Return the number of pairs added to the env block */
1010
}
1011

1012
uint64_t timer_next_elapse_monotonic(const Timer *t) {
379✔
1013
        assert(t);
379✔
1014

1015
        return (uint64_t) usec_shift_clock(t->next_elapse_monotonic_or_boottime,
379✔
1016
                                           TIMER_MONOTONIC_CLOCK(t), CLOCK_MONOTONIC);
379✔
1017
}
1018

1019
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
1020
        [TIMER_ACTIVE]        = "OnActiveSec",
1021
        [TIMER_BOOT]          = "OnBootSec",
1022
        [TIMER_STARTUP]       = "OnStartupSec",
1023
        [TIMER_UNIT_ACTIVE]   = "OnUnitActiveSec",
1024
        [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
1025
        [TIMER_CALENDAR]      = "OnCalendar",
1026
};
1027

1028
DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
94✔
1029

1030
char* timer_base_to_usec_string(TimerBase i) {
4✔
1031
        _cleanup_free_ char *buf = NULL;
8✔
1032
        const char *s;
4✔
1033
        size_t l;
4✔
1034

1035
        s = timer_base_to_string(i);
4✔
1036

1037
        if (endswith(s, "Sec")) {
4✔
1038
                /* s/Sec/USec/ */
1039
                l = strlen(s);
4✔
1040
                buf = new(char, l+2);
4✔
1041
                if (!buf)
4✔
1042
                        return NULL;
1043

1044
                memcpy(buf, s, l-3);
4✔
1045
                memcpy(buf+l-3, "USec", 5);
4✔
1046
        } else {
1047
                buf = strdup(s);
×
1048
                if (!buf)
×
1049
                        return NULL;
×
1050
        }
1051

1052
        return TAKE_PTR(buf);
1053
}
1054

1055
static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
1056
        [TIMER_SUCCESS]                 = "success",
1057
        [TIMER_FAILURE_RESOURCES]       = "resources",
1058
        [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
1059
};
1060

1061
DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
1,108✔
1062

1063
const UnitVTable timer_vtable = {
1064
        .object_size = sizeof(Timer),
1065

1066
        .sections =
1067
                "Unit\0"
1068
                "Timer\0"
1069
                "Install\0",
1070
        .private_section = "Timer",
1071

1072
        .can_transient = true,
1073
        .can_fail = true,
1074
        .can_trigger = true,
1075

1076
        .init = timer_init,
1077
        .done = timer_done,
1078
        .load = timer_load,
1079

1080
        .coldplug = timer_coldplug,
1081

1082
        .dump = timer_dump,
1083

1084
        .start = timer_start,
1085
        .stop = timer_stop,
1086

1087
        .clean = timer_clean,
1088
        .can_clean = timer_can_clean,
1089

1090
        .serialize = timer_serialize,
1091
        .deserialize_item = timer_deserialize_item,
1092

1093
        .active_state = timer_active_state,
1094
        .sub_state_to_string = timer_sub_state_to_string,
1095

1096
        .trigger_notify = timer_trigger_notify,
1097

1098
        .reset_failed = timer_reset_failed,
1099
        .time_change = timer_time_change,
1100
        .timezone_change = timer_timezone_change,
1101

1102
        .bus_set_property = bus_timer_set_property,
1103

1104
        .test_startable = timer_test_startable,
1105
};
1106

1107
const ActivationDetailsVTable activation_details_timer_vtable = {
1108
        .object_size = sizeof(ActivationDetailsTimer),
1109

1110
        .serialize = activation_details_timer_serialize,
1111
        .deserialize = activation_details_timer_deserialize,
1112
        .append_env = activation_details_timer_append_env,
1113
        .append_pair = activation_details_timer_append_pair,
1114
};
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