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

systemd / systemd / 23990547145

04 Apr 2026 09:30PM UTC coverage: 72.373% (+0.3%) from 72.107%
23990547145

push

github

web-flow
shutdown: enforce a minimum uptime to make boot loops less annoying (#41215)

Fixes: #9453

Split out of #41016

3 of 39 new or added lines in 2 files covered. (7.69%)

2565 existing lines in 66 files now uncovered.

319531 of 441505 relevant lines covered (72.37%)

1187721.39 hits per line

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

0.0
/src/bless-boot/bless-boot.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <unistd.h>
4

5
#include "alloc-util.h"
6
#include "build.h"
7
#include "devnum-util.h"
8
#include "efivars.h"
9
#include "fd-util.h"
10
#include "find-esp.h"
11
#include "format-table.h"
12
#include "fs-util.h"
13
#include "log.h"
14
#include "main-func.h"
15
#include "options.h"
16
#include "parse-util.h"
17
#include "path-util.h"
18
#include "pretty-print.h"
19
#include "stdio-util.h"
20
#include "string-util.h"
21
#include "strv.h"
22
#include "sync-util.h"
23
#include "verbs.h"
24
#include "virt.h"
25

26
static char **arg_path = NULL;
27

28
STATIC_DESTRUCTOR_REGISTER(arg_path, strv_freep);
×
29

30
typedef enum Status {
31
        STATUS_GOOD,
32
        STATUS_BAD,
33
        STATUS_INDETERMINATE,
34
} Status;
35

36
static int help(void) {
×
37
        _cleanup_free_ char *link = NULL;
×
38
        _cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
×
39
        int r;
×
40

41
        r = terminal_urlify_man("systemd-bless-boot.service", "8", &link);
×
42
        if (r < 0)
×
43
                return log_oom();
×
44

45
        r = option_parser_get_help_table(&options);
×
46
        if (r < 0)
×
47
                return r;
48

49
        r = verbs_get_help_table(&verbs);
×
50
        if (r < 0)
×
51
                return r;
52

53
        (void) table_sync_column_widths(0, options, verbs);
×
54

55
        printf("%s [OPTIONS...] COMMAND\n"
×
56
               "\n%sMark the boot process as good or bad.%s\n"
57
               "\nCommands:\n",
58
               program_invocation_short_name,
59
               ansi_highlight(),
60
               ansi_normal());
61
        r = table_print_or_warn(verbs);
×
UNCOV
62
        if (r < 0)
×
63
                return r;
64

UNCOV
65
        printf("\nOptions:\n");
×
66
        r = table_print_or_warn(options);
×
UNCOV
67
        if (r < 0)
×
68
                return r;
69

70
        printf("\nSee the %s for details.\n", link);
×
71
        return 0;
72
}
73

UNCOV
74
VERB_COMMON_HELP_HIDDEN(help);
×
75

76
static int parse_argv(int argc, char *argv[], char ***ret_args) {
×
UNCOV
77
        int r;
×
78

79
        assert(argc >= 0);
×
UNCOV
80
        assert(argv);
×
81

82
        OptionParser state = { argc, argv };
×
83
        const char *arg;
×
84

UNCOV
85
        FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
×
86
                switch (c) {
×
87
                OPTION_COMMON_HELP:
×
UNCOV
88
                        return help();
×
89

90
                OPTION_COMMON_VERSION:
×
91
                        return version();
×
92

UNCOV
93
                OPTION_LONG("path", "PATH", "Path to the $BOOT partition (may be used multiple times)"):
×
UNCOV
94
                        r = strv_extend(&arg_path, arg);
×
UNCOV
95
                        if (r < 0)
×
96
                                return log_oom();
×
97
                        break;
98
                }
99

100
        *ret_args = option_parser_get_args(&state);
×
101
        return 1;
×
102
}
103

104
static int acquire_path(void) {
×
UNCOV
105
        _cleanup_free_ char *esp_path = NULL, *xbootldr_path = NULL;
×
106
        dev_t esp_devid = 0, xbootldr_devid = 0;
×
UNCOV
107
        char **a;
×
UNCOV
108
        int r;
×
109

UNCOV
110
        if (!strv_isempty(arg_path))
×
111
                return 0;
112

UNCOV
113
        r = find_esp_and_warn_full(
×
114
                        /* root= */ NULL,
115
                        /* path= */ NULL,
116
                        /* unprivileged_mode= */ false,
117
                        &esp_path,
118
                        /* ret_part= */ NULL,
119
                        /* ret_pstart= */ NULL,
120
                        /* ret_psize= */ NULL,
121
                        /* ret_uuid= */ NULL,
122
                        &esp_devid);
UNCOV
123
        if (r < 0 && r != -ENOKEY) /* ENOKEY means not found, and is the only error the function won't log about on its own */
×
124
                return r;
125

UNCOV
126
        r = find_xbootldr_and_warn_full(
×
127
                        /* root= */ NULL,
128
                        /* path= */ NULL,
129
                        /* unprivileged_mode= */ false,
130
                        &xbootldr_path,
131
                        /* ret_uuid= */ NULL,
132
                        &xbootldr_devid);
133
        if (r < 0 && r != -ENOKEY)
×
134
                return r;
135

UNCOV
136
        if (!esp_path && !xbootldr_path)
×
137
                return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
×
138
                                       "Couldn't find $BOOT partition. It is recommended to mount it to /boot.\n"
139
                                       "Alternatively, use --path= to specify path to mount point.");
140

UNCOV
141
        if (esp_path && xbootldr_path && !devnum_set_and_equal(esp_devid, xbootldr_devid)) /* in case the two paths refer to the same inode, suppress one */
×
142
                a = strv_new(esp_path, xbootldr_path);
×
143
        else if (esp_path)
×
144
                a = strv_new(esp_path);
×
145
        else
146
                a = strv_new(xbootldr_path);
×
UNCOV
147
        if (!a)
×
148
                return log_oom();
×
149

UNCOV
150
        strv_free_and_replace(arg_path, a);
×
151

152
        if (DEBUG_LOGGING) {
×
UNCOV
153
                _cleanup_free_ char *j = NULL;
×
154

UNCOV
155
                j = strv_join(arg_path, ":");
×
UNCOV
156
                log_debug("Using %s as boot loader drop-in search path.", strna(j));
×
157
        }
158

159
        return 0;
160
}
161

UNCOV
162
static int parse_counter(
×
163
                const char *path,
164
                const char **p,
165
                uint64_t *ret_left,
166
                uint64_t *ret_done) {
167

UNCOV
168
        uint64_t left, done;
×
169
        const char *z, *e;
×
170
        size_t k;
×
UNCOV
171
        int r;
×
172

173
        assert(path);
×
174
        assert(p);
×
175

176
        e = *p;
×
UNCOV
177
        assert(e);
×
178
        assert(*e == '+');
×
179

180
        e++;
×
181

UNCOV
182
        k = strspn(e, DIGITS);
×
UNCOV
183
        if (k == 0)
×
184
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
185
                                       "Can't parse empty 'tries left' counter from LoaderBootCountPath: %s",
186
                                       path);
187

UNCOV
188
        z = strndupa_safe(e, k);
×
189
        r = safe_atou64(z, &left);
×
UNCOV
190
        if (r < 0)
×
191
                return log_error_errno(r, "Failed to parse 'tries left' counter from LoaderBootCountPath: %s", path);
×
192

UNCOV
193
        e += k;
×
194

195
        if (*e == '-') {
×
196
                e++;
×
197

UNCOV
198
                k = strspn(e, DIGITS);
×
UNCOV
199
                if (k == 0) /* If there's a "-" there also needs to be at least one digit */
×
200
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
201
                                               "Can't parse empty 'tries done' counter from LoaderBootCountPath: %s",
202
                                               path);
203

UNCOV
204
                z = strndupa_safe(e, k);
×
205
                r = safe_atou64(z, &done);
×
UNCOV
206
                if (r < 0)
×
207
                        return log_error_errno(r, "Failed to parse 'tries done' counter from LoaderBootCountPath: %s", path);
×
208

209
                e += k;
×
210
        } else
UNCOV
211
                done = 0;
×
212

UNCOV
213
        if (done == 0)
×
214
                log_warning("The 'tries done' counter is currently at zero. This can't really be, after all we are running, and this boot must hence count as one. Proceeding anyway.");
×
215

UNCOV
216
        *p = e;
×
217

218
        if (ret_left)
×
UNCOV
219
                *ret_left = left;
×
220

UNCOV
221
        if (ret_done)
×
UNCOV
222
                *ret_done = done;
×
223

224
        return 0;
225
}
226

UNCOV
227
static int acquire_boot_count_path(
×
228
                char **ret_path,
229
                char **ret_prefix,
230
                uint64_t *ret_left,
231
                uint64_t *ret_done,
232
                char **ret_suffix) {
233

234
        int r;
×
235

236
        _cleanup_free_ char *path = NULL;
×
237
        r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("LoaderBootCountPath"), &path);
×
UNCOV
238
        if (r == -ENOENT)
×
239
                return -EUNATCH; /* in this case, let the caller print a message */
240
        if (r < 0)
×
UNCOV
241
                return log_error_errno(r, "Failed to read LoaderBootCountPath EFI variable: %m");
×
242

UNCOV
243
        if (!path_is_normalized(path))
×
244
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
245
                                       "Path read from LoaderBootCountPath is not normalized, refusing: %s",
246
                                       path);
247

UNCOV
248
        if (!path_is_absolute(path))
×
249
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
250
                                       "Path read from LoaderBootCountPath is not absolute, refusing: %s",
251
                                       path);
252

253
        const char *last = NULL;
×
254
        r = path_find_last_component(path, /* accept_dot_dot= */ false, /* next= */ NULL, &last);
×
255
        if (r < 0)
×
256
                return log_error_errno(r, "Failed to extract filename from LoaderBootCountPath '%s': %m", path);
×
UNCOV
257
        if (r == 0)
×
258
                return log_error_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL), "LoaderBootCountPath '%s' refers to the root directory.", path);
×
259
        if (strlen(last) > (size_t) r)
×
260
                return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "LoaderBootCountPath '%s' refers to directory path, refusing.", path);
×
261

UNCOV
262
        const char *e = strrchr(last, '+');
×
UNCOV
263
        if (!e)
×
264
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
265
                                       "Path read from LoaderBootCountPath does not contain a counter, refusing: %s",
266
                                       path);
267

268
        _cleanup_free_ char *prefix = NULL;
×
UNCOV
269
        if (ret_prefix) {
×
UNCOV
270
                prefix = strndup(path, e - path);
×
271
                if (!prefix)
×
272
                        return log_oom();
×
273
        }
274

UNCOV
275
        uint64_t left, done;
×
276
        r = parse_counter(path, &e, &left, &done);
×
277
        if (r < 0)
×
278
                return r;
279

280
        _cleanup_free_ char *suffix = NULL;
×
UNCOV
281
        if (ret_suffix) {
×
282
                suffix = strdup(e);
×
UNCOV
283
                if (!suffix)
×
UNCOV
284
                        return log_oom();
×
285

286
                *ret_suffix = TAKE_PTR(suffix);
×
287
        }
288

289
        if (ret_path)
×
290
                *ret_path = TAKE_PTR(path);
×
291
        if (ret_prefix)
×
292
                *ret_prefix = TAKE_PTR(prefix);
×
UNCOV
293
        if (ret_left)
×
UNCOV
294
                *ret_left = left;
×
UNCOV
295
        if (ret_done)
×
UNCOV
296
                *ret_done = done;
×
297

298
        return 0;
299
}
300

301
static int make_good(const char *prefix, const char *suffix, char **ret) {
×
302
        _cleanup_free_ char *good = NULL;
×
303

UNCOV
304
        assert(prefix);
×
UNCOV
305
        assert(suffix);
×
UNCOV
306
        assert(ret);
×
307

308
        /* Generate the path we'd use on good boots. This one is easy. If we are successful, we simple drop the counter
309
         * pair entirely from the name. After all, we know all is good, and the logs will contain information about the
310
         * tries we needed to come here, hence it's safe to drop the counters from the name. */
311

312
        good = strjoin(prefix, suffix);
×
313
        if (!good)
×
314
                return -ENOMEM;
315

316
        *ret = TAKE_PTR(good);
×
317
        return 0;
×
318
}
319

320
static int make_bad(const char *prefix, uint64_t done, const char *suffix, char **ret) {
×
321
        char *bad;
×
322

UNCOV
323
        assert(prefix);
×
UNCOV
324
        assert(suffix);
×
UNCOV
325
        assert(ret);
×
326

327
        /* Generate the path we'd use on bad boots. Let's simply set the 'left' counter to zero, and keep the 'done'
328
         * counter. The information might be interesting to boot loaders, after all. */
329

330
        if (done == 0)
×
UNCOV
331
                bad = strjoin(prefix, "+0", suffix);
×
332
        else
333
                bad = asprintf_safe("%s+0-%" PRIu64 "%s", prefix, done, suffix);
×
334
        if (!bad)
×
335
                return -ENOMEM;
336

UNCOV
337
        *ret = bad;
×
338
        return 0;
×
339
}
340

341
VERB(verb_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT, "Show status of current boot loader entry");
UNCOV
342
static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
×
343
        _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
×
344
        uint64_t left, done;
×
UNCOV
345
        int r;
×
346

UNCOV
347
        r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix);
×
UNCOV
348
        if (r == -EUNATCH) { /* No boot count in place, then let's consider this a "clean" boot,
×
349
                              * since "good", "bad", or "indeterminate" don't apply. */
UNCOV
350
                puts("clean");
×
351
                return 0;
352
        }
353
        if (r < 0)
×
354
                return r;
355

356
        r = acquire_path();
×
357
        if (r < 0)
×
358
                return r;
359

360
        r = make_good(prefix, suffix, &good);
×
361
        if (r < 0)
×
362
                return log_oom();
×
363

364
        r = make_bad(prefix, done, suffix, &bad);
×
UNCOV
365
        if (r < 0)
×
UNCOV
366
                return log_oom();
×
367

UNCOV
368
        log_debug("Booted file: %s\n"
×
369
                  "The same modified for 'good': %s\n"
370
                  "The same modified for 'bad':  %s\n",
371
                  path,
372
                  good,
373
                  bad);
374

375
        log_debug("Tries left: %" PRIu64"\n"
×
376
                  "Tries done: %" PRIu64"\n",
377
                  left, done);
378

379
        STRV_FOREACH(p, arg_path) {
×
380
                _cleanup_close_ int fd = -EBADF;
×
381

UNCOV
382
                fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
×
383
                if (fd < 0) {
×
UNCOV
384
                        if (errno == ENOENT)
×
UNCOV
385
                                continue;
×
386

UNCOV
387
                        return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
×
388
                }
389

UNCOV
390
                if (faccessat(fd, skip_leading_slash(path), F_OK, 0) >= 0) {
×
391
                        /* If the item we booted with still exists under its name, it means we have not
392
                         * change the current boot's marking so far. This may have two reasons: because we
393
                         * simply didn't do that yet but still plan to, or because the left tries counter is
394
                         * already at zero, hence we cannot further decrease it to mark it even
395
                         * "worse"... Here we check the current counter to detect the latter case and return
396
                         * "dirty", since the item is already marked bad from a previous boot, but otherwise
397
                         * report "indeterminate" since we just didn't make a decision yet. */
398
                        puts(left == 0 ? "dirty" : "indeterminate");
×
399
                        return 0;
400
                }
401
                if (errno != ENOENT)
×
UNCOV
402
                        return log_error_errno(errno, "Failed to check if '%s' exists: %m", path);
×
403

UNCOV
404
                if (faccessat(fd, skip_leading_slash(good), F_OK, 0) >= 0) {
×
405
                        puts("good");
×
406
                        return 0;
407
                }
408

409
                if (errno != ENOENT)
×
UNCOV
410
                        return log_error_errno(errno, "Failed to check if '%s' exists: %m", good);
×
411

412
                if (faccessat(fd, skip_leading_slash(bad), F_OK, 0) >= 0) {
×
413
                        puts("bad");
×
414
                        return 0;
415
                }
UNCOV
416
                if (errno != ENOENT)
×
UNCOV
417
                        return log_error_errno(errno, "Failed to check if '%s' exists: %m", bad);
×
418

419
                /* We didn't find any of the three? If so, let's try the next directory, before we give up. */
420
        }
421

422
        return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Couldn't determine boot state.");
×
423
}
424

425
static int rename_in_dir_idempotent(int fd, const char *from, const char *to) {
×
426
        int r;
×
427

UNCOV
428
        assert(fd >= 0);
×
UNCOV
429
        assert(from);
×
UNCOV
430
        assert(to);
×
431

432
        /* A wrapper around rename_noreplace() which executes no operation if the source and target are the
433
         * same. */
434

UNCOV
435
        if (streq(from, to)) {
×
UNCOV
436
                if (faccessat(fd, from, F_OK, AT_SYMLINK_NOFOLLOW) < 0)
×
UNCOV
437
                        return -errno;
×
438

439
                return 0;
440
        }
441

UNCOV
442
         r = rename_noreplace(fd, from, fd, to);
×
UNCOV
443
         if (r < 0)
×
UNCOV
444
                 return r;
×
445

446
         return 1;
447
}
448

449
VERB_FULL(verb_set, "good", NULL, VERB_ANY, 1, 0, STATUS_GOOD,
450
          "Mark this boot as good");
451
VERB_FULL(verb_set, "bad", NULL, VERB_ANY, 1, 0, STATUS_BAD,
452
          "Mark this boot as bad");
453
VERB_FULL(verb_set, "indeterminate", NULL, VERB_ANY, 1, 0, STATUS_INDETERMINATE,
454
          "Undo any marking as good or bad");
455
static int verb_set(int argc, char *argv[], uintptr_t data, void *userdata) {
×
456
        _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
×
UNCOV
457
        const char *target, *source1, *source2;
×
458
        uint64_t left, done;
×
UNCOV
459
        Status status = data;
×
460
        int r;
×
461

462
        assert(IN_SET(status, STATUS_GOOD, STATUS_BAD, STATUS_INDETERMINATE));
×
463

UNCOV
464
        r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix);
×
UNCOV
465
        if (r == -EUNATCH) /* acquire_boot_count_path() won't log on its own for this specific error */
×
466
                return log_error_errno(r, "Not booted with boot counting in effect.");
×
467
        if (r < 0)
×
468
                return r;
469

470
        r = acquire_path();
×
471
        if (r < 0)
×
472
                return r;
473

474
        r = make_good(prefix, suffix, &good);
×
475
        if (r < 0)
×
476
                return log_oom();
×
477

UNCOV
478
        r = make_bad(prefix, done, suffix, &bad);
×
479
        if (r < 0)
×
480
                return log_oom();
×
481

482
        /* Figure out what rename to what */
483
        switch (status) {
×
484
        case STATUS_GOOD:
×
485
                target = good;
×
486
                source1 = path;
×
487
                source2 = bad;      /* Maybe this boot was previously marked as 'bad'? */
×
488
                break;
×
489
        case STATUS_BAD:
×
490
                target = bad;
×
491
                source1 = path;
×
492
                source2 = good;     /* Maybe this boot was previously marked as 'good'? */
×
UNCOV
493
                break;
×
494
        case STATUS_INDETERMINATE:
×
495
                if (left == 0)
×
496
                        return log_error_errno(r, "Current boot entry was already marked bad in a previous boot, cannot reset to indeterminate.");
×
497

UNCOV
498
                target = path;
×
UNCOV
499
                source1 = good;
×
500
                source2 = bad;
×
501
                break;
×
502
        }
503

504
        STRV_FOREACH(p, arg_path) {
×
505
                _cleanup_close_ int fd = -EBADF;
×
506

507
                fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
×
508
                if (fd < 0)
×
509
                        return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
×
510

UNCOV
511
                r = rename_in_dir_idempotent(fd, skip_leading_slash(source1), skip_leading_slash(target));
×
512
                if (r == -EEXIST)
×
513
                        goto exists;
×
514
                if (r == -ENOENT) {
×
515

UNCOV
516
                        r = rename_in_dir_idempotent(fd, skip_leading_slash(source2), skip_leading_slash(target));
×
517
                        if (r == -EEXIST)
×
518
                                goto exists;
×
UNCOV
519
                        if (r == -ENOENT) {
×
520

521
                                if (faccessat(fd, skip_leading_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
×
UNCOV
522
                                        goto exists;
×
523

524
                                if (errno != ENOENT)
×
UNCOV
525
                                        return log_error_errno(errno, "Failed to determine if %s already exists: %m", target);
×
526

527
                                /* We found none of the snippets here, try the next directory */
UNCOV
528
                                continue;
×
529
                        }
530
                        if (r < 0)
×
UNCOV
531
                                return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target);
×
532

533
                        if (r > 0)
×
534
                                log_debug("Successfully renamed '%s' to '%s'.", source2, target);
×
535
                        else
536
                                log_debug("Not renaming, as '%s' already matches target name.", source2);
×
UNCOV
537
                } else if (r < 0)
×
538
                        return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target);
×
UNCOV
539
                else if (r > 0)
×
UNCOV
540
                        log_debug("Successfully renamed '%s' to '%s'.", source1, target);
×
541
                else
542
                        log_debug("Not renaming, as '%s' already matches target name.", source1);
×
543

544
                /* First, fsync() the directory these files are located in */
UNCOV
545
                r = fsync_parent_at(fd, skip_leading_slash(target));
×
546
                if (r < 0)
×
547
                        log_debug_errno(r, "Failed to synchronize image directory, ignoring: %m");
×
548

549
                /* Secondly, syncfs() the whole file system these files are located in */
UNCOV
550
                if (syncfs(fd) < 0)
×
UNCOV
551
                        log_debug_errno(errno, "Failed to synchronize $BOOT partition, ignoring: %m");
×
552

553
                log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done);
×
554
                return 0;
555
        }
556

UNCOV
557
        return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Can't find boot counter source file for '%s'.", target);
×
558

UNCOV
559
exists:
×
560
        log_debug("Operation already executed before, not doing anything.");
×
561
        return 0;
562
}
563

564
static int run(int argc, char *argv[]) {
×
UNCOV
565
        char **args = NULL;
×
566
        int r;
×
567

568
        log_setup();
×
569

570
        r = parse_argv(argc, argv, &args);
×
571
        if (r <= 0)
×
UNCOV
572
                return r;
×
573

574
        if (detect_container() > 0)
×
575
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
576
                                       "Marking a boot is not supported in containers.");
577

578
        if (!is_efi_boot())
×
UNCOV
579
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
580
                                       "Marking a boot is only supported on EFI systems.");
581

UNCOV
582
        return dispatch_verb_with_args(args, NULL);
×
583
}
584

UNCOV
585
DEFINE_MAIN_FUNCTION(run);
×
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