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

systemd / systemd / 28272947092

26 Jun 2026 08:38PM UTC coverage: 72.893% (+0.2%) from 72.703%
28272947092

push

github

poettering
sysupdate: Address review feedback on CheckNew varlink scaffolding

Follow-up to #42422:

 - Rename process_image() to context_process_image(), since it now
   operates on a Context object.
 - Use IN_SET() in image_type_can_sysupdate() instead of a switch.
 - Name the return parameters of context_list_components() ret_xyz, per
   our coding style.
 - Drop a redundant "else" after a return in vl_method_check_new().

9 of 11 new or added lines in 1 file covered. (81.82%)

12567 existing lines in 144 files now uncovered.

341026 of 467845 relevant lines covered (72.89%)

1339355.33 hits per line

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

75.57
/src/bootctl/bootctl.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sys/stat.h>
4

5
#include "sd-varlink.h"
6

7
#include "blockdev-util.h"
8
#include "boot-entry.h"
9
#include "bootctl.h"
10
#include "bootctl-cleanup.h"
11
#include "bootctl-install.h"
12
#include "bootctl-link.h"
13
#include "bootctl-random-seed.h"
14
#include "bootctl-reboot-to-firmware.h"
15
#include "bootctl-set-efivar.h"
16
#include "bootctl-status.h"
17
#include "bootctl-uki.h"
18
#include "bootctl-unlink.h"
19
#include "bootctl-util.h"
20
#include "bootspec-util.h"
21
#include "build.h"
22
#include "crypto-util.h"
23
#include "devnum-util.h"
24
#include "dissect-image.h"
25
#include "efi-loader.h"
26
#include "efivars.h"
27
#include "escape.h"
28
#include "fd-util.h"
29
#include "find-esp.h"
30
#include "format-table.h"
31
#include "image-policy.h"
32
#include "log.h"
33
#include "loop-util.h"
34
#include "main-func.h"
35
#include "mount-util.h"
36
#include "options.h"
37
#include "pager.h"
38
#include "parse-argument.h"
39
#include "parse-util.h"
40
#include "path-util.h"
41
#include "pretty-print.h"
42
#include "string-table.h"
43
#include "string-util.h"
44
#include "strv.h"
45
#include "varlink-io.systemd.BootControl.h"
46
#include "varlink-io.systemd.SysUpdate.Notify.h"
47
#include "varlink-util.h"
48
#include "verbs.h"
49
#include "virt.h"
50

51
static GracefulMode _arg_graceful = ARG_GRACEFUL_NO;
52

53
char *arg_esp_path = NULL;
54
char *arg_xbootldr_path = NULL;
55
bool arg_print_esp_path = false;
56
bool arg_print_dollar_boot_path = false;
57
bool arg_print_loader_path = false;
58
bool arg_print_stub_path = false;
59
bool arg_print_efi_architecture = false;
60
unsigned arg_print_root_device = 0;
61
int arg_touch_variables = -1;
62
bool arg_install_random_seed = true;
63
PagerFlags arg_pager_flags = 0;
64
bool arg_quiet = false;
65
int arg_make_entry_directory = false; /* tri-state: < 0 for automatic logic */
66
sd_id128_t arg_machine_id = SD_ID128_NULL;
67
char *arg_install_layout = NULL;
68
BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
69
char *arg_entry_token = NULL;
70
sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
71
bool arg_arch_all = false;
72
char *arg_root = NULL;
73
char *arg_image = NULL;
74
InstallSource arg_install_source = INSTALL_SOURCE_AUTO;
75
char *arg_efi_boot_option_description = NULL;
76
bool arg_efi_boot_option_description_with_device = false;
77
bool arg_dry_run = false;
78
ImagePolicy *arg_image_policy = NULL;
79
bool arg_varlink = false;
80
bool arg_secure_boot_auto_enroll = false;
81
char *arg_certificate = NULL;
82
CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
83
char *arg_certificate_source = NULL;
84
char *arg_private_key = NULL;
85
KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
86
char *arg_private_key_source = NULL;
87
bool arg_oldest = false;
88
uint64_t arg_keep_free = KEEP_FREE_BYTES_DEFAULT;
89
char *arg_entry_title = NULL;
90
char *arg_entry_version = NULL;
91
uint64_t arg_entry_commit = 0;
92
char **arg_extras = NULL;
93
unsigned arg_tries_left = UINT_MAX;
94

95
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
293✔
96
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
293✔
97
STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep);
293✔
98
STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
293✔
99
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
293✔
100
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
293✔
101
STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep);
293✔
102
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
293✔
103
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
293✔
104
STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
293✔
105
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
293✔
106
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
293✔
107
STATIC_DESTRUCTOR_REGISTER(arg_entry_title, freep);
293✔
108
STATIC_DESTRUCTOR_REGISTER(arg_entry_version, freep);
293✔
109
STATIC_DESTRUCTOR_REGISTER(arg_extras, strv_freep);
293✔
110

111
static const char* const install_source_table[_INSTALL_SOURCE_MAX] = {
112
        [INSTALL_SOURCE_IMAGE] = "image",
113
        [INSTALL_SOURCE_HOST]  = "host",
114
        [INSTALL_SOURCE_AUTO]  = "auto",
115
};
116

UNCOV
117
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(install_source, InstallSource);
×
118

119
int acquire_esp(int unprivileged_mode,
230✔
120
                bool graceful,
121
                int *ret_fd,
122
                uint32_t *ret_part,
123
                uint64_t *ret_pstart,
124
                uint64_t *ret_psize,
125
                sd_id128_t *ret_uuid,
126
                dev_t *ret_devid) {
127

128
        _cleanup_free_ char *np = NULL;
230✔
129
        int r;
230✔
130

131
        /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on
132
         * its own, except for ENOKEY (which is good, we want to show our own message in that case,
133
         * suggesting use of --esp-path=) and EACCESS (only when we request unprivileged mode; in this case
134
         * we simply eat up the error here, so that --list and --status work too, without noise about
135
         * this). */
136

137
        r = find_esp_and_warn_full(arg_root, arg_esp_path, unprivileged_mode, &np, ret_fd, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
230✔
138
        if (r == -ENOKEY) {
230✔
139
                if (graceful)
5✔
UNCOV
140
                        return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_INFO, r,
×
141
                                              "Couldn't find EFI system partition, skipping.");
142

143
                return log_error_errno(r,
5✔
144
                                       "Couldn't find EFI system partition. It is recommended to mount it to /boot/ or /efi/.\n"
145
                                       "Alternatively, use --esp-path= to specify path to mount point.");
146
        }
147
        if (r < 0)
225✔
148
                return r;
149

150
        free_and_replace(arg_esp_path, np);
224✔
151
        log_debug("Using EFI System Partition at %s.", arg_esp_path);
224✔
152

153
        return 1; /* for symmetry with acquire_xbootldr() below: found */
154
}
155

156
int acquire_xbootldr(
135✔
157
                int unprivileged_mode,
158
                int *ret_fd,
159
                sd_id128_t *ret_uuid,
160
                dev_t *ret_devid) {
161

162
        int r;
135✔
163

164
        _cleanup_free_ char *np = NULL;
135✔
165
        _cleanup_close_ int fd = -EBADF;
135✔
166
        r = find_xbootldr_and_warn_full(
178✔
167
                        arg_root,
168
                        arg_xbootldr_path,
169
                        unprivileged_mode,
170
                        &np,
171
                        ret_fd ? &fd : NULL,
172
                        ret_uuid,
173
                        ret_devid);
174
        if (r == -ENOKEY || (r >= 0 && arg_esp_path && path_equal(np, arg_esp_path))) {
135✔
175

176
                if (arg_esp_path)
88✔
177
                        log_debug("Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
75✔
178
                else
179
                        log_debug("Found neither an XBOOTLDR partition, nor an ESP.");
13✔
180

181
                arg_xbootldr_path = mfree(arg_xbootldr_path);
88✔
182

183
                if (ret_fd)
88✔
184
                        *ret_fd = -EBADF;
64✔
185
                if (ret_uuid)
88✔
186
                        *ret_uuid = SD_ID128_NULL;
18✔
187
                if (ret_devid)
88✔
188
                        *ret_devid = 0;
35✔
189

190
                return 0; /* not found */
191
        }
192
        if (r < 0)
46✔
193
                return r;
194

195
        free_and_replace(arg_xbootldr_path, np);
46✔
196
        log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path);
46✔
197

198
        if (ret_fd)
46✔
199
                *ret_fd = TAKE_FD(fd);
28✔
200

201
        return 1; /* found */
202
}
203

204
static int print_loader_or_stub_path(void) {
8✔
205
        _cleanup_free_ char *p = NULL;
8✔
206
        sd_id128_t uuid;
8✔
207
        int r;
8✔
208

209
        if (arg_print_loader_path) {
8✔
210
                r = efi_loader_get_device_part_uuid(&uuid);
1✔
211
                if (r == -ENOENT)
1✔
UNCOV
212
                        return log_error_errno(r, "No loader partition UUID passed.");
×
213
                if (r < 0)
1✔
UNCOV
214
                        return log_error_errno(r, "Unable to determine loader partition UUID: %m");
×
215

216
                r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("LoaderImageIdentifier"), &p);
1✔
217
                if (r == -ENOENT)
1✔
UNCOV
218
                        return log_error_errno(r, "No loader EFI binary path passed.");
×
219
                if (r < 0)
1✔
UNCOV
220
                        return log_error_errno(r, "Unable to determine loader EFI binary path: %m");
×
221
        } else {
222
                assert(arg_print_stub_path);
7✔
223

224
                r = efi_stub_get_device_part_uuid(&uuid);
7✔
225
                if (r == -ENOENT)
7✔
UNCOV
226
                        return log_error_errno(r, "No stub partition UUID passed.");
×
227
                if (r < 0)
7✔
UNCOV
228
                        return log_error_errno(r, "Unable to determine stub partition UUID: %m");
×
229

230
                r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &p);
7✔
231
                if (r == -ENOENT)
7✔
UNCOV
232
                        return log_error_errno(r, "No stub EFI binary path passed.");
×
233
                if (r < 0)
7✔
UNCOV
234
                        return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
×
235
        }
236

237
        sd_id128_t esp_uuid;
8✔
238
        r = acquire_esp(/* unprivileged_mode= */ false,
8✔
239
                        /* graceful= */ false,
240
                        /* ret_fd= */ NULL,
241
                        /* ret_part= */ NULL,
242
                        /* ret_pstart= */ NULL,
243
                        /* ret_psize= */ NULL,
244
                        &esp_uuid,
245
                        /* ret_devid= */ NULL);
246
        if (r < 0)
8✔
247
                return r;
248

249
        const char *found_path = NULL;
8✔
250
        if (sd_id128_equal(esp_uuid, uuid))
8✔
251
                found_path = arg_esp_path;
8✔
252
        else if (arg_print_stub_path) { /* In case of the stub, also look for things in the xbootldr partition */
×
UNCOV
253
                sd_id128_t xbootldr_uuid;
×
254

UNCOV
255
                r = acquire_xbootldr(/* unprivileged_mode= */ false,
×
256
                                     /* ret_fd= */ NULL,
257
                                     &xbootldr_uuid,
258
                                     /* ret_devid= */ NULL);
259
                if (r < 0)
×
UNCOV
260
                        return r;
×
261

262
                if (sd_id128_equal(xbootldr_uuid, uuid))
×
UNCOV
263
                        found_path = arg_xbootldr_path;
×
264
        }
265

266
        if (!found_path)
8✔
UNCOV
267
                return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Failed to discover partition " SD_ID128_FORMAT_STR " among mounted boot partitions.", SD_ID128_FORMAT_VAL(uuid));
×
268

269
        _cleanup_free_ char *j = path_join(found_path, p);
16✔
270
        if (!j)
8✔
UNCOV
271
                return log_oom();
×
272

273
        puts(j);
8✔
274
        return 0;
275
}
276

277
GracefulMode arg_graceful(void) {
95✔
278
        static bool chroot_checked = false;
95✔
279

280
        if (!chroot_checked && running_in_chroot() > 0) {
95✔
281
                if (_arg_graceful == ARG_GRACEFUL_NO)
×
UNCOV
282
                        log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Running in a chroot, enabling --graceful.");
×
283

UNCOV
284
                _arg_graceful = ARG_GRACEFUL_FORCE;
×
285
        }
286

287
        chroot_checked = true;
95✔
288

289
        return _arg_graceful;
95✔
290
}
291

292
static int help(void) {
3✔
293
        _cleanup_free_ char *link = NULL;
3✔
294
        int r;
3✔
295

296
        pager_open(arg_pager_flags);
3✔
297

298
        r = terminal_urlify_man("bootctl", "1", &link);
3✔
299
        if (r < 0)
3✔
UNCOV
300
                return log_oom();
×
301

302
        static const char *const verb_groups[] = {
3✔
303
                "Generic EFI Firmware/Boot Loader Commands",
304
                "Boot Loader Specification Commands",
305
                "Boot Loader Interface Commands",
306
                "systemd-boot Commands",
307
                "Kernel Image Commands",
308
        };
309

310
        static const char *const option_groups[] = {
3✔
311
                "Block Device Discovery Commands",
312
                "Options",
313
        };
314

315
        Table *verb_tables[ELEMENTSOF(verb_groups)] = {};
3✔
316
        CLEANUP_ELEMENTS(verb_tables, table_unref_array_clear);
3✔
317
        Table *option_tables[ELEMENTSOF(option_groups)] = {};
3✔
318
        CLEANUP_ELEMENTS(option_tables, table_unref_array_clear);
3✔
319

320
        for (size_t i = 0; i < ELEMENTSOF(verb_groups); i++) {
18✔
321
                r = verbs_get_help_table_group(verb_groups[i], &verb_tables[i]);
15✔
322
                if (r < 0)
15✔
323
                        return r;
324
        }
325

326
        for (size_t i = 0; i < ELEMENTSOF(option_groups); i++) {
9✔
327
                r = option_parser_get_help_table_group(option_groups[i], &option_tables[i]);
6✔
328
                if (r < 0)
6✔
329
                        return r;
330
        }
331

332
        (void) table_sync_column_widths(0,
3✔
333
                                        verb_tables[0], verb_tables[1], verb_tables[2],
334
                                        verb_tables[3], verb_tables[4],
335
                                        option_tables[0], option_tables[1]);
336

337
        printf("%s [OPTIONS...] COMMAND ...\n"
6✔
338
               "\n%sControl EFI firmware boot settings and manage boot loader.%s\n",
339
               program_invocation_short_name,
340
               ansi_highlight(),
341
               ansi_normal());
342

343
        for (size_t i = 0; i < ELEMENTSOF(verb_groups); i++) {
18✔
344
                printf("\n%s%s:%s\n", ansi_underline(), verb_groups[i], ansi_normal());
45✔
345

346
                r = table_print_or_warn(verb_tables[i]);
15✔
347
                if (r < 0)
15✔
348
                        return r;
349
        }
350

351
        for (size_t i = 0; i < ELEMENTSOF(option_groups); i++) {
9✔
352
                printf("\n%s%s:%s\n", ansi_underline(), option_groups[i], ansi_normal());
18✔
353

354
                r = table_print_or_warn(option_tables[i]);
6✔
355
                if (r < 0)
6✔
356
                        return r;
357
        }
358

359
        printf("\nSee the %s for details.\n", link);
3✔
360
        return 0;
361
}
362

UNCOV
363
VERB_COMMON_HELP(help);
×
364

365
VERB_GROUP("Generic EFI Firmware/Boot Loader Commands");
366

367
VERB_SCOPE(, verb_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT,
368
           "Show status of installed boot loader and EFI variables");
369

370
VERB_SCOPE(, verb_reboot_to_firmware, "reboot-to-firmware", "[BOOL]", VERB_ANY, 2, 0,
371
           "Query or set reboot-to-firmware EFI flag");
372

373
VERB_GROUP("Boot Loader Specification Commands");
374

375
VERB_SCOPE_NOARG(, verb_list, "list",
376
           "List boot loader entries");
377

378
VERB_SCOPE(, verb_unlink, "unlink", "ID", VERB_ANY, 2, 0,
379
           "Remove boot loader entry");
380

381
VERB_SCOPE(, verb_link, "link", "KERNEL", 2, 2, 0,
382
           "Create boot loader entry for specified kernel");
383

384
VERB_SCOPE_NOARG(, verb_link_auto, "link-auto",
385
           "Create boot loader entry for the kernel and extra resources staged in /var/lib/systemd/uki/");
386

387
VERB_SCOPE_NOARG(, verb_cleanup, "cleanup",
388
           "Remove files in ESP not referenced in any boot entry");
389

390
VERB_GROUP("Boot Loader Interface Commands");
391

392
VERB_SCOPE(, verb_set_efivar, "set-default", "ID", 2, 2, 0,
393
           "Set default boot loader entry");
394

395
VERB_SCOPE(, verb_set_efivar, "set-oneshot", "ID", 2, 2, 0,
396
           "Set default boot loader entry, for next boot only");
397

398
VERB_SCOPE(, verb_set_efivar, "set-sysfail", "ID", 2, 2, 0,
399
           "Set boot loader entry used in case of a system failure");
400

401
VERB_SCOPE(, verb_set_efivar, "set-timeout", "SECONDS", 2, 2, 0,
402
           "Set the menu timeout");
403

404
VERB_SCOPE(, verb_set_efivar, "set-timeout-oneshot", "SECONDS", 2, 2, 0,
405
           "Set the menu timeout for the next boot only");
406

407
VERB_SCOPE(, verb_set_efivar, "set-preferred", "ID", 2, 2, 0,
408
           /* help= */ NULL);
409

410
VERB_GROUP("systemd-boot Commands");
411

412
VERB_SCOPE(, verb_install, "install", NULL, VERB_ANY, 1, 0,
413
           "Install systemd-boot to the ESP and EFI variables");
414

415
VERB_SCOPE(, verb_install, "update", NULL, VERB_ANY, 1, 0,
416
           "Update systemd-boot in the ESP and EFI variables");
417

418
VERB_SCOPE_NOARG(, verb_remove, "remove",
419
           "Remove systemd-boot from the ESP and EFI variables");
420

421
VERB_SCOPE_NOARG(, verb_is_installed, "is-installed",
422
           "Test whether systemd-boot is installed in the ESP");
423

424
VERB_SCOPE_NOARG(, verb_random_seed, "random-seed",
425
           "Initialize or refresh random seed in ESP and EFI variables");
426

427
VERB_GROUP("Kernel Image Commands");
428

429
VERB_SCOPE(, verb_kernel_identify, "kernel-identify", "KERNEL-IMAGE", 2, 2, 0,
430
           "Identify kernel image type");
431

432
VERB_SCOPE(, verb_kernel_inspect, "kernel-inspect", "KERNEL-IMAGE", 2, 2, 0,
433
           "Prints details about the kernel image");
434

435
static int parse_argv(int argc, char *argv[], char ***ret_args) {
293✔
436
        int r;
293✔
437

438
        assert(argc >= 0);
293✔
439
        assert(argv);
293✔
440

441
        OptionParser opts = { argc, argv };
293✔
442

443
        FOREACH_OPTION_OR_RETURN(c, &opts)
1,220✔
444
                switch (c) {
347✔
445

446
                OPTION_GROUP("Block Device Discovery Commands"): {}
87✔
447

448
                OPTION('p', "print-esp-path", NULL, "Print path to the EFI System Partition mount point"): {}
87✔
449
                OPTION_LONG("print-path", NULL, /* help= */ NULL):  /* Compatibility alias */
87✔
450
                        arg_print_esp_path = true;
87✔
451
                        break;
87✔
452

453
                OPTION('x', "print-boot-path", NULL, "Print path to the $BOOT partition mount point"):
9✔
454
                        arg_print_dollar_boot_path = true;
9✔
455
                        break;
9✔
456

457
                OPTION_LONG("print-loader-path", NULL, "Print path to currently booted boot loader binary"):
1✔
458
                        arg_print_loader_path = true;
1✔
459
                        break;
1✔
460

461
                OPTION_LONG("print-stub-path", NULL, "Print path to currently booted unified kernel binary"):
7✔
462
                        arg_print_stub_path = true;
7✔
463
                        break;
7✔
464

465
                OPTION('R', "print-root-device", NULL,
9✔
466
                       "Print path to the block device node backing the root file system"
467
                       " (returns e.g. /dev/nvme0n1p5)"): {}
9✔
468
                OPTION_HELP_VERBATIM("-RR",
9✔
469
                                     "Print path to the whole disk block device node backing the root FS"
470
                                     " (returns e.g. /dev/nvme0n1)"):
471
                        arg_print_root_device++;
9✔
472
                        break;
9✔
473

474
                OPTION_LONG("print-efi-architecture", NULL, "Print the local EFI architecture string"):
1✔
475
                        arg_print_efi_architecture = true;
1✔
476
                        break;
1✔
477

478
                OPTION_GROUP("Options"): {}
3✔
479

480
                OPTION_COMMON_HELP:
3✔
481
                        return help();
3✔
482

483
                OPTION_COMMON_VERSION:
3✔
484
                        return version();
3✔
485

486
                OPTION_LONG("esp-path", "PATH", "Path to the EFI System Partition (ESP)"): {}
3✔
487
                OPTION_LONG("path", "PATH", /* help= */ NULL):  /* Compatibility alias */
3✔
488
                        r = free_and_strdup(&arg_esp_path, opts.arg);
3✔
489
                        if (r < 0)
3✔
UNCOV
490
                                return log_oom();
×
491
                        break;
492

493
                OPTION_LONG("boot-path", "PATH", "Path to the $BOOT partition"):
3✔
494
                        r = free_and_strdup(&arg_xbootldr_path, opts.arg);
3✔
495
                        if (r < 0)
3✔
UNCOV
496
                                return log_oom();
×
497
                        break;
498

499
                OPTION_LONG("root", "PATH", "Operate on an alternate filesystem root"):
34✔
500
                        r = parse_path_argument(opts.arg, /* suppress_root= */ true, &arg_root);
34✔
501
                        if (r < 0)
34✔
502
                                return r;
503
                        break;
504

505
                OPTION_LONG("image", "PATH", "Operate on disk image as filesystem root"):
29✔
506
                        r = parse_path_argument(opts.arg, /* suppress_root= */ false, &arg_image);
29✔
507
                        if (r < 0)
29✔
508
                                return r;
509
                        break;
510

UNCOV
511
                OPTION_LONG("image-policy", "POLICY", "Specify disk image dissection policy"):
×
UNCOV
512
                        r = parse_image_policy_argument(opts.arg, &arg_image_policy);
×
513
                        if (r < 0)
×
514
                                return r;
515
                        break;
516

517
                OPTION_LONG("install-source", "SOURCE",
×
518
                            "Where to pick files when using --root=/--image= (auto, image, host)"): {
519
                        InstallSource is = install_source_from_string(opts.arg);
×
520
                        if (is < 0)
×
UNCOV
521
                                return log_error_errno(is, "Unexpected parameter for --install-source=: %s", opts.arg);
×
522

UNCOV
523
                        arg_install_source = is;
×
UNCOV
524
                        break;
×
525
                }
526

527
                OPTION_LONG("variables", "BOOL", "Whether to modify EFI variables"):
20✔
528
                        r = parse_tristate_argument_with_auto("--variables=", opts.arg, &arg_touch_variables);
20✔
529
                        if (r < 0)
20✔
530
                                return r;
531
#if !ENABLE_EFI
532
                        if (arg_touch_variables > 0)
533
                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
534
                                                       "Compiled without support for EFI, --variables=%s cannot be specified.", opts.arg);
535
#endif
536
                        break;
537

538
                OPTION_LONG("no-variables", NULL, /* help= */ NULL):  /* Compatibility alias */
×
539
                        arg_touch_variables = false;
×
540
                        break;
×
541

UNCOV
542
                OPTION_LONG("random-seed", "BOOL", "Whether to create random-seed file during install"):
×
UNCOV
543
                        r = parse_boolean_argument("--random-seed=", opts.arg, &arg_install_random_seed);
×
544
                        if (r < 0)
×
545
                                return r;
546
                        break;
547

UNCOV
548
                OPTION_COMMON_NO_PAGER:
×
UNCOV
549
                        arg_pager_flags |= PAGER_DISABLE;
×
UNCOV
550
                        break;
×
551

552
                OPTION_LONG("graceful", NULL,
49✔
553
                            "Don't fail when the ESP cannot be found or EFI variables cannot be written"):
554
                        _arg_graceful = ARG_GRACEFUL_YES;
49✔
555
                        break;
49✔
556

557
                OPTION('q', "quiet", NULL, "Suppress output"):
6✔
558
                        arg_quiet = true;
6✔
559
                        break;
6✔
560

561
                OPTION_COMMON_ENTRY_TOKEN:
27✔
562
                        r = parse_boot_entry_token_type(opts.arg, &arg_entry_token_type, &arg_entry_token);
27✔
563
                        if (r < 0)
27✔
564
                                return r;
565
                        break;
566

567
                OPTION_COMMON_MAKE_ENTRY_DIRECTORY: {}
15✔
568
                OPTION_LONG("make-machine-id-directory", "BOOL", /* help= */ NULL):  /* Compatibility alias */
15✔
569
                        if (streq(opts.arg, "auto"))  /* retained for backwards compatibility */
15✔
UNCOV
570
                                arg_make_entry_directory = -1; /* yes if machine-id is permanent */
×
571
                        else {
572
                                r = parse_boolean_argument("--make-entry-directory=", opts.arg, NULL);
15✔
573
                                if (r < 0)
15✔
574
                                        return r;
575

576
                                arg_make_entry_directory = r;
15✔
577
                        }
578
                        break;
579

580
                OPTION_COMMON_JSON:
7✔
581
                        r = parse_json_argument(opts.arg, &arg_json_format_flags);
7✔
582
                        if (r <= 0)
7✔
583
                                return r;
584
                        break;
585

586
                OPTION_LONG("all-architectures", NULL, "Install all supported EFI architectures"):
12✔
587
                        arg_arch_all = true;
12✔
588
                        break;
12✔
589

590
                OPTION_LONG("efi-boot-option-description", "DESCRIPTION",
×
591
                            "Description of the entry in the boot option list"):
UNCOV
592
                        if (!string_is_safe(opts.arg, STRING_ALLOW_BACKSLASHES|STRING_ALLOW_QUOTES|STRING_ALLOW_GLOBS)) {
×
593
                                _cleanup_free_ char *escaped = cescape(opts.arg);
×
594
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
595
                                                       "Invalid --efi-boot-option-description=: %s", strna(escaped));
596
                        }
597
                        if (strlen(opts.arg) > EFI_BOOT_OPTION_DESCRIPTION_MAX)
×
598
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
599
                                                       "--efi-boot-option-description= too long: %zu > %zu",
600
                                                       strlen(opts.arg), EFI_BOOT_OPTION_DESCRIPTION_MAX);
UNCOV
601
                        r = free_and_strdup_warn(&arg_efi_boot_option_description, opts.arg);
×
602
                        if (r < 0)
×
603
                                return r;
604
                        break;
605

606
                OPTION_LONG("efi-boot-option-description-with-device", "BOOL",
×
607
                            "Suffix description with disk vendor/model/serial"):
UNCOV
608
                        r = parse_boolean_argument("--efi-boot-option-description-with-device=", opts.arg,
×
609
                                                   &arg_efi_boot_option_description_with_device);
UNCOV
610
                        if (r < 0)
×
611
                                return r;
612
                        break;
613

614
                OPTION_LONG("dry-run", NULL, "Dry run (unlink and cleanup)"):
1✔
615
                        arg_dry_run = true;
1✔
616
                        break;
1✔
617

618
                OPTION_LONG("secure-boot-auto-enroll", "BOOL", "Set up secure boot auto-enrollment"):
1✔
619
                        r = parse_boolean_argument("--secure-boot-auto-enroll=", opts.arg,
1✔
620
                                                   &arg_secure_boot_auto_enroll);
621
                        if (r < 0)
1✔
622
                                return r;
623
                        break;
624

625
                OPTION_COMMON_PRIVATE_KEY("Private key for Secure Boot auto-enrollment"):
1✔
626
                        r = free_and_strdup_warn(&arg_private_key, opts.arg);
1✔
627
                        if (r < 0)
1✔
628
                                return r;
629
                        break;
630

631
                OPTION_COMMON_PRIVATE_KEY_SOURCE:
×
UNCOV
632
                        r = parse_openssl_key_source_argument(opts.arg,
×
633
                                                              &arg_private_key_source,
634
                                                              &arg_private_key_source_type);
UNCOV
635
                        if (r < 0)
×
636
                                return r;
637
                        break;
638

639
                OPTION_COMMON_CERTIFICATE("PEM certificate to use when setting up Secure Boot auto-enrollment"):
1✔
640
                        r = free_and_strdup_warn(&arg_certificate, opts.arg);
1✔
641
                        if (r < 0)
1✔
642
                                return r;
643
                        break;
644

645
                OPTION_COMMON_CERTIFICATE_SOURCE:
×
UNCOV
646
                        r = parse_openssl_certificate_source_argument(opts.arg,
×
647
                                                                      &arg_certificate_source,
648
                                                                      &arg_certificate_source_type);
UNCOV
649
                        if (r < 0)
×
650
                                return r;
651
                        break;
652

653
                OPTION_LONG("oldest", "BOOL",
3✔
654
                            "Delete oldest boot menu entry"):
655
                        r = parse_boolean_argument("--oldest=", opts.arg, &arg_oldest);
3✔
656
                        if (r < 0)
3✔
657
                                return r;
658

659
                        break;
660

661
                OPTION_LONG("keep-free", "BYTES",
1✔
662
                            "How much space to keep free on ESP/XBOOTLDR"):
663

664
                        if (isempty(opts.arg))
1✔
665
                                arg_keep_free = KEEP_FREE_BYTES_DEFAULT;
×
666
                        else {
667
                                r = parse_size(opts.arg, 1024, &arg_keep_free);
1✔
668
                                if (r < 0)
1✔
UNCOV
669
                                        return log_error_errno(r, "Failed to parse --keep-free=: %s", opts.arg);
×
670
                        }
671

672
                        break;
673

674
                OPTION_LONG("entry-title", "TITLE",
1✔
675
                            "Selects the entry title for the new boot menu entry"):
676

677
                        if (isempty(opts.arg)) {
1✔
UNCOV
678
                                arg_entry_title = mfree(arg_entry_title);
×
679
                                break;
×
680
                        }
681

682
                        if (!efi_loader_entry_title_valid(opts.arg))
1✔
UNCOV
683
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid boot menu entry title: %s", opts.arg);
×
684

685
                        r = free_and_strdup_warn(&arg_entry_title, opts.arg);
1✔
686
                        if (r < 0)
1✔
687
                                return r;
688
                        break;
689

690
                OPTION_LONG("entry-version", "VERSION",
1✔
691
                            "Selects the entry version for the new boot menu entry"):
692
                        if (isempty(opts.arg)) {
1✔
UNCOV
693
                                arg_entry_version = mfree(arg_entry_version);
×
694
                                break;
×
695
                        }
696

697
                        if (!version_is_valid_versionspec(opts.arg))
1✔
UNCOV
698
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid boot menu entry version: %s", opts.arg);
×
699

700
                        r = free_and_strdup_warn(&arg_entry_version, opts.arg);
1✔
701
                        if (r < 0)
1✔
702
                                return r;
703
                        break;
704

705
                OPTION_LONG("entry-commit", "NR",
8✔
706
                            "Selects the entry commit version for the new boot menu entry"): {
707
                        if (isempty(opts.arg)) {
8✔
UNCOV
708
                                arg_entry_commit = 0;
×
709
                                break;
8✔
710
                        }
711

712
                        uint64_t n;
8✔
713
                        r = safe_atou64(opts.arg, &n);
8✔
714
                        if (r < 0)
8✔
UNCOV
715
                                return log_error_errno(r, "Failed to parse --entry-commit= parameter: %s", opts.arg);
×
716
                        if (!entry_commit_valid(n))
8✔
UNCOV
717
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid entry commit number.");
×
718

719
                        arg_entry_commit = n;
8✔
720
                        break;
8✔
721
                }
722

723
                OPTION('X', "extra", "PATH",
3✔
724
                       "Pass extra resource (confext, sysext, credential) to the invoked UKI of the boot menu entry"): {
725

726
                        if (isempty(opts.arg)) {
3✔
727
                                arg_extras = strv_free(arg_extras);
×
728
                                break;
3✔
729
                        }
730

UNCOV
731
                        _cleanup_free_ char *x = NULL;
×
732
                        r = parse_path_argument(opts.arg, /* suppress_root= */ false, &x);
3✔
733
                        if (r < 0)
3✔
734
                                return r;
735

736
                        _cleanup_free_ char *fn = NULL;
3✔
737
                        r = path_extract_filename(x, &fn);
3✔
738
                        if (r < 0)
3✔
UNCOV
739
                                return log_error_errno(r, "Failed to extract filename from '%s': %m", x);
×
740
                        if (!efi_loader_entry_resource_filename_valid(fn))
3✔
741
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Extra filename '%s' is not suitable for reference in a boot menu entry.", fn);
×
742

743
                        r = strv_consume(&arg_extras, TAKE_PTR(x));
3✔
744
                        if (r < 0)
3✔
UNCOV
745
                                return log_oom();
×
746

747
                        strv_uniq(arg_extras);
3✔
748
                        break;
3✔
749
                }
750

751
                OPTION_LONG("tries-left", "NR",
1✔
752
                            "Set boot menu entries tries-left counter to the specified value"): {
753
                        if (isempty(opts.arg)) {
1✔
UNCOV
754
                                arg_tries_left = UINT_MAX;
×
755
                                break;
1✔
756
                        }
757

758
                        unsigned u;
1✔
759
                        r = safe_atou(opts.arg, &u);
1✔
760
                        if (r < 0)
1✔
UNCOV
761
                                return log_error_errno(r, "Failed to parse tries left counter: %s", opts.arg);
×
762
                        if (u >= UINT_MAX)
1✔
UNCOV
763
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Tries left counter too large, refusing: %u", u);
×
764

765
                        arg_tries_left = u;
1✔
766
                        break;
1✔
767
                }}
768

769
        char **args = option_parser_get_args(&opts);
287✔
770

771
        if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) + arg_print_loader_path + arg_print_stub_path + arg_print_efi_architecture > 1)
287✔
UNCOV
772
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
773
                                                       "--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R, --print-loader-path, --print-stub-path, --print-efi-architecture cannot be combined.");
774

775
        if ((arg_root || arg_image) && args[0] && !STR_IN_SET(args[0], "status", "list",
287✔
776
                        "install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
UNCOV
777
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
778
                                       "Options --root= and --image= are not supported with verb %s.",
779
                                       args[0]);
780

781
        if (arg_root && arg_image)
287✔
UNCOV
782
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
×
783

784
        if (arg_install_source != INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
287✔
UNCOV
785
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
×
786

787
        if (arg_dry_run && args[0] && !STR_IN_SET(args[0], "unlink", "cleanup"))
287✔
UNCOV
788
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry-run is only supported with --unlink or --cleanup");
×
789

790
        if (arg_secure_boot_auto_enroll) {
287✔
791
#if HAVE_OPENSSL
792
                if (!arg_certificate)
1✔
UNCOV
793
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no certificate provided.");
×
794

795
                if (!arg_private_key)
1✔
UNCOV
796
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no private key provided.");
×
797
#else
798
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Secure boot auto-enrollment requested but OpenSSL support is disabled.");
799
#endif
800
        }
801

802
        r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
287✔
803
        if (r < 0)
287✔
UNCOV
804
                return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
×
805
        if (r > 0) {
287✔
806
                arg_varlink = true;
18✔
807
                arg_pager_flags |= PAGER_DISABLE;
18✔
808
        }
809

810
        *ret_args = args;
287✔
811
        return 1;
287✔
812
}
813

814
static int vl_server(void) {
18✔
815
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
18✔
816
        int r;
18✔
817

818
        /* Invocation as Varlink service */
819

820
        r = varlink_server_new(
18✔
821
                        &varlink_server,
822
                        SD_VARLINK_SERVER_ROOT_ONLY |
823
                        SD_VARLINK_SERVER_MYSELF_ONLY |
824
                        SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT,
825
                        /* userdata= */ NULL);
826
        if (r < 0)
18✔
827
                return log_error_errno(r, "Failed to allocate Varlink server: %m");
×
828

829
        r = sd_varlink_server_add_interface_many(
18✔
830
                        varlink_server,
831
                        &vl_interface_io_systemd_BootControl,
832
                        &vl_interface_io_systemd_SysUpdate_Notify);
833
        if (r < 0)
18✔
UNCOV
834
                return log_error_errno(r, "Failed to add Varlink interfaces: %m");
×
835

836
        r = sd_varlink_server_bind_method_many(
18✔
837
                        varlink_server,
838
                        "io.systemd.BootControl.ListBootEntries",        vl_method_list_boot_entries,
839
                        "io.systemd.BootControl.SetRebootToFirmware",    vl_method_set_reboot_to_firmware,
840
                        "io.systemd.BootControl.GetRebootToFirmware",    vl_method_get_reboot_to_firmware,
841
                        "io.systemd.BootControl.Install",                vl_method_install,
842
                        "io.systemd.BootControl.Link",                   vl_method_link,
843
                        "io.systemd.BootControl.LinkAuto",               vl_method_link_auto,
844
                        "io.systemd.BootControl.Unlink",                 vl_method_unlink,
845
                        "io.systemd.SysUpdate.Notify.OnCompletedUpdate", vl_method_on_completed_update);
846
        if (r < 0)
18✔
UNCOV
847
                return log_error_errno(r, "Failed to bind Varlink methods: %m");
×
848

849
        r = sd_varlink_server_loop_auto(varlink_server);
18✔
850
        if (r < 0)
18✔
UNCOV
851
                return log_error_errno(r, "Failed to run Varlink event loop: %m");
×
852

853
        return 0;
854
}
855

856
static int run(int argc, char *argv[]) {
293✔
857
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
293✔
858
        _cleanup_(umount_and_freep) char *mounted_dir = NULL;
293✔
859
        int r;
293✔
860

861
        log_setup();
293✔
862

863
        char **args = NULL;
293✔
864
        r = parse_argv(argc, argv, &args);
293✔
865
        if (r <= 0)
293✔
866
                return r;
867

868
        if (arg_varlink)
287✔
869
                return vl_server();
18✔
870

871
        if (arg_print_root_device > 0) {
269✔
872
                _cleanup_free_ char *path = NULL;
6✔
873
                dev_t devno;
6✔
874

875
                r = blockdev_get_root(LOG_ERR, &devno);
6✔
876
                if (r < 0)
6✔
877
                        return r;
878
                if (r == 0) {
6✔
UNCOV
879
                        log_error("Root file system not backed by a (single) whole block device.");
×
880
                        return 80; /* some recognizable error code */
881
                }
882

883
                if (arg_print_root_device > 1) {
6✔
884
                        r = block_get_whole_disk(devno, &devno);
3✔
885
                        if (r < 0)
3✔
UNCOV
886
                                log_debug_errno(r, "Unable to find whole block device for root block device, ignoring: %m");
×
887
                }
888

889
                r = device_path_make_canonical(S_IFBLK, devno, &path);
6✔
890
                if (r < 0)
6✔
UNCOV
891
                        return log_error_errno(r,
×
892
                                               "Failed to format canonical device path for devno '" DEVNUM_FORMAT_STR "': %m",
893
                                               DEVNUM_FORMAT_VAL(devno));
894

895
                puts(path);
6✔
896
                return 0;
897
        }
898

899
        if (arg_print_loader_path || arg_print_stub_path)
263✔
900
                return print_loader_or_stub_path();
8✔
901

902
        if (arg_print_efi_architecture) {
255✔
903
                printf("%s\n", get_efi_arch());
1✔
904
                return 0;
905
        }
906

907
        /* Open up and mount the image */
908
        if (arg_image) {
254✔
909
                assert(!arg_root);
26✔
910

911
                r = mount_image_privately_interactively(
26✔
912
                                arg_image,
913
                                arg_image_policy,
914
                                DISSECT_IMAGE_GENERIC_ROOT |
915
                                DISSECT_IMAGE_USR_NO_ROOT |
916
                                DISSECT_IMAGE_RELAX_VAR_CHECK |
917
                                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY,
918
                                &mounted_dir,
919
                                /* ret_dir_fd= */ NULL,
920
                                &loop_device);
921
                if (r < 0)
26✔
922
                        return r;
923

924
                arg_root = strdup(mounted_dir);
26✔
925
                if (!arg_root)
26✔
UNCOV
926
                        return log_oom();
×
927
        }
928

929
        return dispatch_verb(args, NULL);
254✔
930
}
931

932
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
293✔
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