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

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

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

76.09
/src/basic/efivars.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <stdlib.h>
5
#include <sys/stat.h>
6
#include <time.h>
7
#include <unistd.h>
8

9
#include "alloc-util.h"
10
#include "chattr-util.h"
11
#include "efivars.h"
12
#include "fd-util.h"
13
#include "io-util.h"
14
#include "log.h"
15
#include "memory-util.h"
16
#include "string-util.h"
17
#include "time-util.h"
18
#include "utf8.h"
19
#include "virt.h"
20

21
#if ENABLE_EFI
22

23
/* Reads from efivarfs sometimes fail with EINTR. Retry that many times. */
24
#define EFI_N_RETRIES_NO_DELAY 20
25
#define EFI_N_RETRIES_TOTAL 25
26
#define EFI_RETRY_DELAY (50 * USEC_PER_MSEC)
27

28
int efi_get_variable(
306✔
29
                const char *variable,
30
                uint32_t *ret_attribute,
31
                void **ret_value,
32
                size_t *ret_size) {
33

34
        _cleanup_close_ int fd = -EBADF;
306✔
35
        _cleanup_free_ void *buf = NULL;
306✔
36
        struct stat st;
306✔
37
        usec_t begin = 0; /* Unnecessary initialization to appease gcc */
306✔
38
        uint32_t a;
306✔
39
        ssize_t n;
306✔
40

41
        assert(variable);
306✔
42

43
        const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
1,530✔
44

45
        if (!ret_value && !ret_size && !ret_attribute) {
306✔
46
                /* If caller is not interested in anything, just check if the variable exists and is
47
                 * readable. */
UNCOV
48
                if (access(p, R_OK) < 0)
×
UNCOV
49
                        return -errno;
×
50

51
                return 0;
52
        }
53

54
        if (DEBUG_LOGGING) {
306✔
55
                log_debug("Reading EFI variable %s.", p);
306✔
56
                begin = now(CLOCK_MONOTONIC);
306✔
57
        }
58

59
        fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
306✔
60
        if (fd < 0)
306✔
61
                return log_debug_errno(errno, "open(\"%s\") failed: %m", p);
100✔
62

63
        if (fstat(fd, &st) < 0)
206✔
UNCOV
64
                return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
×
65
        if (st.st_size < 4)
206✔
UNCOV
66
                return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable %s is shorter than 4 bytes, refusing.", p);
×
67
        if (st.st_size > 4*1024*1024 + 4)
206✔
UNCOV
68
                return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable %s is ridiculously large, refusing.", p);
×
69

70
        if (ret_value || ret_attribute) {
206✔
71
                /* The kernel ratelimits reads from the efivarfs because EFI is inefficient, and we'll
72
                 * occasionally fail with EINTR here. A slowdown is better than a failure for us, so
73
                 * retry a few times and eventually fail with -EBUSY.
74
                 *
75
                 * See https://github.com/torvalds/linux/blob/master/fs/efivarfs/file.c#L75
76
                 * and
77
                 * https://github.com/torvalds/linux/commit/bef3efbeb897b56867e271cdbc5f8adaacaeb9cd.
78
                 */
UNCOV
79
                for (unsigned try = 0;; try++) {
×
80
                        n = read(fd, &a, sizeof(a));
203✔
81
                        if (n >= 0)
203✔
82
                                break;
UNCOV
83
                        log_debug_errno(errno, "Reading from \"%s\" failed: %m", p);
×
UNCOV
84
                        if (errno != EINTR)
×
UNCOV
85
                                return -errno;
×
86
                        if (try >= EFI_N_RETRIES_TOTAL)
×
87
                                return -EBUSY;
88

UNCOV
89
                        if (try >= EFI_N_RETRIES_NO_DELAY)
×
90
                                (void) usleep_safe(EFI_RETRY_DELAY);
×
91
                }
92

93
                /* Unfortunately kernel reports EOF if there's an inconsistency between efivarfs var list
94
                 * and what's actually stored in firmware, c.f. #34304. A zero size env var is not allowed in
95
                 * efi and hence the variable doesn't really exist in the backing store as long as it is zero
96
                 * sized, and the kernel calls this "uncommitted". Hence we translate EOF back to ENOENT here,
97
                 * as with kernel behavior before
98
                 * https://github.com/torvalds/linux/commit/3fab70c165795431f00ddf9be8b84ddd07bd1f8f
99
                 *
100
                 * If the kernel changes behaviour (to flush dentries on resume), we can drop
101
                 * this at some point in the future. But note that the commit is 11
102
                 * years old at this point so we'll need to deal with the current behaviour for
103
                 * a long time.
104
                 */
105
                if (n == 0)
203✔
UNCOV
106
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
×
107
                                               "EFI variable %s is uncommitted", p);
108

109
                if (n != sizeof(a))
203✔
UNCOV
110
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO),
×
111
                                               "Read %zi bytes from EFI variable %s, expected %zu.",  n, p, sizeof(a));
112
        }
113

114
        if (ret_value) {
203✔
115
                buf = malloc(st.st_size - 4 + 3);
203✔
116
                if (!buf)
203✔
117
                        return -ENOMEM;
118

119
                n = read(fd, buf, (size_t) st.st_size - 4);
203✔
120
                if (n < 0)
203✔
UNCOV
121
                        return log_debug_errno(errno, "Failed to read value of EFI variable %s: %m", p);
×
122
                assert(n <= st.st_size - 4);
203✔
123

124
                /* Always NUL-terminate (3 bytes, to properly protect UTF-16, even if truncated in the middle
125
                 * of a character) */
126
                ((char*) buf)[n] = 0;
203✔
127
                ((char*) buf)[n + 1] = 0;
203✔
128
                ((char*) buf)[n + 2] = 0;
203✔
129
        } else
130
                /* Assume that the reported size is accurate */
131
                n = st.st_size - 4;
3✔
132

133
        if (DEBUG_LOGGING) {
206✔
134
                usec_t end = now(CLOCK_MONOTONIC);
206✔
135
                if (end > begin + EFI_RETRY_DELAY)
206✔
UNCOV
136
                        log_debug("Detected slow EFI variable read access on %s: %s",
×
137
                                  variable, FORMAT_TIMESPAN(end - begin, 1));
138
        }
139

140
        /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
141
         * with a smaller value. */
142

143
        if (ret_attribute)
206✔
144
                *ret_attribute = a;
2✔
145

146
        if (ret_value)
206✔
147
                *ret_value = TAKE_PTR(buf);
203✔
148

149
        if (ret_size)
206✔
150
                *ret_size = n;
206✔
151

152
        return 0;
153
}
154

155
int efi_get_variable_string(const char *variable, char **ret) {
229✔
156
        _cleanup_free_ void *s = NULL;
229✔
157
        size_t ss = 0;
229✔
158
        char *x;
229✔
159
        int r;
229✔
160

161
        assert(variable);
229✔
162

163
        r = efi_get_variable(variable, NULL, &s, &ss);
229✔
164
        if (r < 0)
229✔
165
                return r;
166

167
        x = utf16_to_utf8(s, ss);
147✔
168
        if (!x)
147✔
169
                return -ENOMEM;
170

171
        if (ret)
147✔
172
                *ret = x;
147✔
173

174
        return 0;
175
}
176

177
int efi_get_variable_path(const char *variable, char **ret) {
18✔
178
        int r;
18✔
179

180
        assert(variable);
18✔
181

182
        r = efi_get_variable_string(variable, ret);
18✔
183
        if (r < 0)
18✔
184
                return r;
185

186
        if (ret)
18✔
187
                efi_tilt_backslashes(*ret);
18✔
188

189
        return r;
190
}
191

192
static int efi_verify_variable(const char *variable, uint32_t attr, const void *value, size_t size) {
9✔
193
        _cleanup_free_ void *buf = NULL;
9✔
194
        size_t n;
9✔
195
        uint32_t a;
9✔
196
        int r;
9✔
197

198
        assert(variable);
9✔
199
        assert(value || size == 0);
9✔
200

201
        r = efi_get_variable(variable, &a, &buf, &n);
9✔
202
        if (r < 0)
9✔
203
                return r;
204

205
        return a == attr && memcmp_nn(buf, n, value, size) == 0;
2✔
206
}
207

208
int efi_set_variable(const char *variable, const void *value, size_t size) {
9✔
209
        static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
9✔
210

211
        struct var {
9✔
212
                uint32_t attr;
213
                char buf[];
214
        } _packed_ * _cleanup_free_ buf = NULL;
9✔
215
        _cleanup_close_ int fd = -EBADF;
9✔
216
        bool saved_flags_valid = false;
9✔
217
        unsigned saved_flags;
9✔
218
        int r;
9✔
219

220
        assert(variable);
9✔
221
        assert(value || size == 0);
9✔
222

223
        /* size 0 means removal, empty variable would not be enough for that */
224
        if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) {
9✔
UNCOV
225
                log_debug("Variable '%s' is already in wanted state, skipping write.", variable);
×
UNCOV
226
                return 0;
×
227
        }
228

229
        const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
45✔
230

231
        /* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default,
232
         * to protect them for accidental removal and modification. We are not changing these variables
233
         * accidentally however, hence let's unset the bit first. */
234

235
        r = chattr_full(AT_FDCWD, p,
9✔
236
                        /* value = */ 0,
237
                        /* mask = */ FS_IMMUTABLE_FL,
238
                        /* ret_previous = */ &saved_flags,
239
                        /* ret_final = */ NULL,
240
                        /* flags = */ 0);
241
        if (r < 0 && r != -ENOENT)
9✔
UNCOV
242
                log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
×
243

244
        saved_flags_valid = r >= 0;
9✔
245

246
        if (size == 0) {
9✔
UNCOV
247
                if (unlink(p) < 0) {
×
UNCOV
248
                        r = -errno;
×
249
                        goto finish;
×
250
                }
251

252
                return 0;
253
        }
254

255
        fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
9✔
256
        if (fd < 0) {
9✔
UNCOV
257
                r = -errno;
×
UNCOV
258
                goto finish;
×
259
        }
260

261
        buf = malloc(sizeof(uint32_t) + size);
9✔
262
        if (!buf) {
9✔
UNCOV
263
                r = -ENOMEM;
×
264
                goto finish;
×
265
        }
266

267
        buf->attr = attr;
9✔
268
        memcpy(buf->buf, value, size);
9✔
269

270
        r = loop_write(fd, buf, sizeof(uint32_t) + size);
9✔
271
        if (r < 0)
9✔
UNCOV
272
                goto finish;
×
273

274
        /* For some reason efivarfs doesn't update mtime automatically. Let's do it manually then. This is
275
         * useful for processes that cache EFI variables to detect when changes occurred. */
276
        if (futimens(fd, /* times = */ NULL) < 0)
9✔
277
                log_debug_errno(errno, "Failed to update mtime/atime on %s, ignoring: %m", p);
7✔
278

279
        r = 0;
280

281
finish:
9✔
282
        if (saved_flags_valid) {
9✔
283
                int q;
2✔
284

285
                /* Restore the original flags field, just in case */
286
                if (fd < 0)
2✔
UNCOV
287
                        q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL);
×
288
                else
289
                        q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL);
2✔
290
                if (q < 0)
2✔
291
                        log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
9✔
292
        }
293

294
        return r;
295
}
296

UNCOV
297
int efi_set_variable_string(const char *variable, const char *value) {
×
UNCOV
298
        _cleanup_free_ char16_t *u16 = NULL;
×
299

UNCOV
300
        u16 = utf8_to_utf16(value, SIZE_MAX);
×
UNCOV
301
        if (!u16)
×
302
                return -ENOMEM;
303

304
        return efi_set_variable(variable, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
×
305
}
306

307
bool is_efi_boot(void) {
571✔
308
        static int cache = -1;
571✔
309

310
        if (cache < 0) {
571✔
311
                if (detect_container() > 0)
346✔
312
                        cache = false;
63✔
313
                else {
314
                        cache = access("/sys/firmware/efi/", F_OK) >= 0;
283✔
315
                        if (!cache && errno != ENOENT)
283✔
UNCOV
316
                                log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m");
×
317
                }
318
        }
319

320
        return cache;
571✔
321
}
322

323
static int read_flag(const char *variable) {
9✔
324
        _cleanup_free_ void *v = NULL;
9✔
325
        uint8_t b;
9✔
326
        size_t s;
9✔
327
        int r;
9✔
328

329
        if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
9✔
330
                return 0;
331

332
        r = efi_get_variable(variable, NULL, &v, &s);
7✔
333
        if (r < 0)
7✔
334
                return r;
335

UNCOV
336
        if (s != 1)
×
337
                return -EINVAL;
338

UNCOV
339
        b = *(uint8_t *)v;
×
UNCOV
340
        return !!b;
×
341
}
342

343
bool is_efi_secure_boot(void) {
5✔
344
        static int cache = -1;
5✔
345
        int r;
5✔
346

347
        if (cache < 0) {
5✔
348
                r = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
3✔
349
                if (r == -ENOENT)
3✔
350
                        cache = false;
1✔
351
                else if (r < 0)
2✔
UNCOV
352
                        log_debug_errno(r, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
×
353
                else
354
                        cache = r;
2✔
355
        }
356

357
        return cache > 0;
5✔
358
}
359

360
SecureBootMode efi_get_secure_boot_mode(void) {
6✔
361
        static SecureBootMode cache = _SECURE_BOOT_INVALID;
6✔
362

363
        if (cache != _SECURE_BOOT_INVALID)
6✔
364
                return cache;
365

366
        int secure = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
6✔
367
        if (secure < 0) {
6✔
368
                if (secure != -ENOENT)
6✔
UNCOV
369
                        log_debug_errno(secure, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
×
370

371
                return (cache = SECURE_BOOT_UNSUPPORTED);
6✔
372
        }
373

374
        /* We can assume false for all these if they are abscent (AuditMode and
375
         * DeployedMode may not exist on older firmware). */
376
        int audit    = read_flag(EFI_GLOBAL_VARIABLE_STR("AuditMode"));
×
UNCOV
377
        int deployed = read_flag(EFI_GLOBAL_VARIABLE_STR("DeployedMode"));
×
UNCOV
378
        int setup    = read_flag(EFI_GLOBAL_VARIABLE_STR("SetupMode"));
×
UNCOV
379
        log_debug("Secure boot variables: SecureBoot=%d AuditMode=%d DeployedMode=%d SetupMode=%d",
×
380
                  secure, audit, deployed, setup);
381

UNCOV
382
        return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0));
×
383
}
384
#endif
385

386
char *efi_tilt_backslashes(char *s) {
18✔
387
        return string_replace_char(s, '\\', '/');
18✔
388
}
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