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

systemd / systemd / 14119061320

27 Mar 2025 08:38PM UTC coverage: 71.976% (+0.02%) from 71.954%
14119061320

push

github

web-flow
test: Make it possible to run the integration tests standalone (#36868)

Currently, to run the integration tests, it's still necessary to
install various other build tools besides meson: A compiler, gperf,
libcap, ... which we want to avoid in CI systems where we receive
prebuilt systemd packages and only want to test them. Examples are
Debian's autopkgtest CI and Fedora CI. Let's make it possible for
these systems to run the integration tests without having to install
any other build dependency besides meson by extracting the logic
required to run the integration tests with meson into a separate
subdirectory and adding a standalone top-level meson.build file which
can be used to configure a meson tree with as its only purpose running
the integration tests.

Practically, we do the following:
- all the integration test directories and integration-test-wrapper.py
  are moved from test/ to test/integration-tests/.
- All the installation logic is kept out of test/integration-tests/ or
  any of its subdirectories and moved into test/meson.build instead.
- We add test/integration-tests/standalone/meson.build to run the
  integration tests standalone. This meson file includes
  test/integration-tests via a cute symlink hack to trick meson into
  including a parent directory with subdir().
- Documentation is included on how to use the new standalone mode.

296746 of 412283 relevant lines covered (71.98%)

719287.73 hits per line

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

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

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

10
#include "sd-id128.h"
11

12
#include "alloc-util.h"
13
#include "chattr-util.h"
14
#include "efivars.h"
15
#include "fd-util.h"
16
#include "fileio.h"
17
#include "io-util.h"
18
#include "macro.h"
19
#include "memory-util.h"
20
#include "missing_fs.h"
21
#include "stdio-util.h"
22
#include "strv.h"
23
#include "time-util.h"
24
#include "utf8.h"
25
#include "virt.h"
26

27
#if ENABLE_EFI
28

29
/* Reads from efivarfs sometimes fail with EINTR. Retry that many times. */
30
#define EFI_N_RETRIES_NO_DELAY 20
31
#define EFI_N_RETRIES_TOTAL 25
32
#define EFI_RETRY_DELAY (50 * USEC_PER_MSEC)
33

34
int efi_get_variable(
293✔
35
                const char *variable,
36
                uint32_t *ret_attribute,
37
                void **ret_value,
38
                size_t *ret_size) {
39

40
        _cleanup_close_ int fd = -EBADF;
293✔
41
        _cleanup_free_ void *buf = NULL;
293✔
42
        struct stat st;
293✔
43
        usec_t begin = 0; /* Unnecessary initialization to appease gcc */
293✔
44
        uint32_t a;
293✔
45
        ssize_t n;
293✔
46

47
        assert(variable);
293✔
48

49
        const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
1,465✔
50

51
        if (!ret_value && !ret_size && !ret_attribute) {
293✔
52
                /* If caller is not interested in anything, just check if the variable exists and is
53
                 * readable. */
54
                if (access(p, R_OK) < 0)
×
55
                        return -errno;
×
56

57
                return 0;
58
        }
59

60
        if (DEBUG_LOGGING) {
293✔
61
                log_debug("Reading EFI variable %s.", p);
293✔
62
                begin = now(CLOCK_MONOTONIC);
293✔
63
        }
64

65
        fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
293✔
66
        if (fd < 0)
293✔
67
                return log_debug_errno(errno, "open(\"%s\") failed: %m", p);
71✔
68

69
        if (fstat(fd, &st) < 0)
222✔
70
                return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
×
71
        if (st.st_size < 4)
222✔
72
                return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable %s is shorter than 4 bytes, refusing.", p);
×
73
        if (st.st_size > 4*1024*1024 + 4)
222✔
74
                return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable %s is ridiculously large, refusing.", p);
×
75

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

95
                        if (try >= EFI_N_RETRIES_NO_DELAY)
×
96
                                (void) usleep_safe(EFI_RETRY_DELAY);
×
97
                }
98

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

115
                if (n != sizeof(a))
219✔
116
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO),
×
117
                                               "Read %zi bytes from EFI variable %s, expected %zu.",  n, p, sizeof(a));
118
        }
119

120
        if (ret_value) {
219✔
121
                buf = malloc(st.st_size - 4 + 3);
219✔
122
                if (!buf)
219✔
123
                        return -ENOMEM;
124

125
                n = read(fd, buf, (size_t) st.st_size - 4);
219✔
126
                if (n < 0)
219✔
127
                        return log_debug_errno(errno, "Failed to read value of EFI variable %s: %m", p);
×
128
                assert(n <= st.st_size - 4);
219✔
129

130
                /* Always NUL-terminate (3 bytes, to properly protect UTF-16, even if truncated in the middle
131
                 * of a character) */
132
                ((char*) buf)[n] = 0;
219✔
133
                ((char*) buf)[n + 1] = 0;
219✔
134
                ((char*) buf)[n + 2] = 0;
219✔
135
        } else
136
                /* Assume that the reported size is accurate */
137
                n = st.st_size - 4;
3✔
138

139
        if (DEBUG_LOGGING) {
222✔
140
                usec_t end = now(CLOCK_MONOTONIC);
222✔
141
                if (end > begin + EFI_RETRY_DELAY)
222✔
142
                        log_debug("Detected slow EFI variable read access on %s: %s",
×
143
                                  variable, FORMAT_TIMESPAN(end - begin, 1));
144
        }
145

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

149
        if (ret_attribute)
222✔
150
                *ret_attribute = a;
2✔
151

152
        if (ret_value)
222✔
153
                *ret_value = TAKE_PTR(buf);
219✔
154

155
        if (ret_size)
222✔
156
                *ret_size = n;
222✔
157

158
        return 0;
159
}
160

161
int efi_get_variable_string(const char *variable, char **ret) {
214✔
162
        _cleanup_free_ void *s = NULL;
214✔
163
        size_t ss = 0;
214✔
164
        char *x;
214✔
165
        int r;
214✔
166

167
        assert(variable);
214✔
168

169
        r = efi_get_variable(variable, NULL, &s, &ss);
214✔
170
        if (r < 0)
214✔
171
                return r;
172

173
        x = utf16_to_utf8(s, ss);
163✔
174
        if (!x)
163✔
175
                return -ENOMEM;
176

177
        if (ret)
163✔
178
                *ret = x;
163✔
179

180
        return 0;
181
}
182

183
int efi_get_variable_path(const char *variable, char **ret) {
18✔
184
        int r;
18✔
185

186
        assert(variable);
18✔
187

188
        r = efi_get_variable_string(variable, ret);
18✔
189
        if (r < 0)
18✔
190
                return r;
191

192
        if (ret)
18✔
193
                efi_tilt_backslashes(*ret);
18✔
194

195
        return r;
196
}
197

198
static int efi_verify_variable(const char *variable, uint32_t attr, const void *value, size_t size) {
10✔
199
        _cleanup_free_ void *buf = NULL;
10✔
200
        size_t n;
10✔
201
        uint32_t a;
10✔
202
        int r;
10✔
203

204
        assert(variable);
10✔
205
        assert(value || size == 0);
10✔
206

207
        r = efi_get_variable(variable, &a, &buf, &n);
10✔
208
        if (r < 0)
10✔
209
                return r;
210

211
        return a == attr && memcmp_nn(buf, n, value, size) == 0;
2✔
212
}
213

214
int efi_set_variable(const char *variable, const void *value, size_t size) {
59✔
215
        static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
59✔
216

217
        struct var {
59✔
218
                uint32_t attr;
219
                char buf[];
220
        } _packed_ * _cleanup_free_ buf = NULL;
59✔
221
        _cleanup_close_ int fd = -EBADF;
59✔
222
        bool saved_flags_valid = false;
59✔
223
        unsigned saved_flags;
59✔
224
        int r;
59✔
225

226
        assert(variable);
59✔
227
        assert(value || size == 0);
59✔
228

229
        /* size 0 means removal, empty variable would not be enough for that */
230
        if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) {
59✔
231
                log_debug("Variable '%s' is already in wanted state, skipping write.", variable);
×
232
                return 0;
×
233
        }
234

235
        const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
295✔
236

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

241
        r = chattr_full(AT_FDCWD, p,
59✔
242
                        /* value = */ 0,
243
                        /* mask = */ FS_IMMUTABLE_FL,
244
                        /* ret_previous = */ &saved_flags,
245
                        /* ret_final = */ NULL,
246
                        /* flags = */ 0);
247
        if (r < 0 && r != -ENOENT)
59✔
248
                log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
×
249

250
        saved_flags_valid = r >= 0;
59✔
251

252
        if (size == 0) {
59✔
253
                if (unlink(p) < 0) {
49✔
254
                        r = -errno;
49✔
255
                        goto finish;
49✔
256
                }
257

258
                return 0;
259
        }
260

261
        fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
10✔
262
        if (fd < 0) {
10✔
263
                r = -errno;
×
264
                goto finish;
×
265
        }
266

267
        buf = malloc(sizeof(uint32_t) + size);
10✔
268
        if (!buf) {
10✔
269
                r = -ENOMEM;
×
270
                goto finish;
×
271
        }
272

273
        buf->attr = attr;
10✔
274
        memcpy(buf->buf, value, size);
10✔
275

276
        r = loop_write(fd, buf, sizeof(uint32_t) + size);
10✔
277
        if (r < 0)
10✔
278
                goto finish;
×
279

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

285
        r = 0;
286

287
finish:
59✔
288
        if (saved_flags_valid) {
59✔
289
                int q;
2✔
290

291
                /* Restore the original flags field, just in case */
292
                if (fd < 0)
2✔
293
                        q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL);
×
294
                else
295
                        q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL);
2✔
296
                if (q < 0)
2✔
297
                        log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
59✔
298
        }
299

300
        return r;
301
}
302

303
int efi_set_variable_string(const char *variable, const char *value) {
×
304
        _cleanup_free_ char16_t *u16 = NULL;
×
305

306
        u16 = utf8_to_utf16(value, SIZE_MAX);
×
307
        if (!u16)
×
308
                return -ENOMEM;
309

310
        return efi_set_variable(variable, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
×
311
}
312

313
bool is_efi_boot(void) {
3,309✔
314
        static int cache = -1;
3,309✔
315

316
        if (cache < 0) {
3,309✔
317
                if (detect_container() > 0)
785✔
318
                        cache = false;
63✔
319
                else {
320
                        cache = access("/sys/firmware/efi/", F_OK) >= 0;
722✔
321
                        if (!cache && errno != ENOENT)
722✔
322
                                log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m");
×
323
                }
324
        }
325

326
        return cache;
3,309✔
327
}
328

329
static int read_flag(const char *variable) {
9✔
330
        _cleanup_free_ void *v = NULL;
9✔
331
        uint8_t b;
9✔
332
        size_t s;
9✔
333
        int r;
9✔
334

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

338
        r = efi_get_variable(variable, NULL, &v, &s);
7✔
339
        if (r < 0)
7✔
340
                return r;
341

342
        if (s != 1)
×
343
                return -EINVAL;
344

345
        b = *(uint8_t *)v;
×
346
        return !!b;
×
347
}
348

349
bool is_efi_secure_boot(void) {
5✔
350
        static int cache = -1;
5✔
351
        int r;
5✔
352

353
        if (cache < 0) {
5✔
354
                r = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
3✔
355
                if (r == -ENOENT)
3✔
356
                        cache = false;
1✔
357
                else if (r < 0)
2✔
358
                        log_debug_errno(r, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
×
359
                else
360
                        cache = r;
2✔
361
        }
362

363
        return cache > 0;
5✔
364
}
365

366
SecureBootMode efi_get_secure_boot_mode(void) {
6✔
367
        static SecureBootMode cache = _SECURE_BOOT_INVALID;
6✔
368

369
        if (cache != _SECURE_BOOT_INVALID)
6✔
370
                return cache;
371

372
        int secure = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
6✔
373
        if (secure < 0) {
6✔
374
                if (secure != -ENOENT)
6✔
375
                        log_debug_errno(secure, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
×
376

377
                return (cache = SECURE_BOOT_UNSUPPORTED);
6✔
378
        }
379

380
        /* We can assume false for all these if they are abscent (AuditMode and
381
         * DeployedMode may not exist on older firmware). */
382
        int audit    = read_flag(EFI_GLOBAL_VARIABLE_STR("AuditMode"));
×
383
        int deployed = read_flag(EFI_GLOBAL_VARIABLE_STR("DeployedMode"));
×
384
        int setup    = read_flag(EFI_GLOBAL_VARIABLE_STR("SetupMode"));
×
385
        log_debug("Secure boot variables: SecureBoot=%d AuditMode=%d DeployedMode=%d SetupMode=%d",
×
386
                  secure, audit, deployed, setup);
387

388
        return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0));
×
389
}
390
#endif
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