]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/boot/bless-boot.c
Merge pull request #12753 from jrouleau/fix/hibernate-resume-timeout
[thirdparty/systemd.git] / src / boot / bless-boot.c
index 2455f3703f598f46e59135cc5938dd8c0327c798..f2d033fc407f624bbdfb7be438ad2466e79b898f 100644 (file)
@@ -16,7 +16,9 @@
 #include "verbs.h"
 #include "virt.h"
 
-static char *arg_path = NULL;
+static char **arg_path = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_path, strv_freep);
 
 static int help(int argc, char *argv[], void *userdata) {
 
@@ -25,7 +27,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "Mark the boot process as good or bad.\n\n"
                "  -h --help          Show this help\n"
                "     --version       Print version\n"
-               "     --path=PATH     Path to the EFI System Partition (ESP)\n"
+               "     --path=PATH     Path to the $BOOT partition (may be used multiple times)\n"
                "\n"
                "Commands:\n"
                "     good            Mark this boot as good\n"
@@ -65,7 +67,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return version();
 
                 case ARG_PATH:
-                        r = free_and_strdup(&arg_path, optarg);
+                        r = strv_extend(&arg_path, optarg);
                         if (r < 0)
                                 return log_oom();
                         break;
@@ -80,20 +82,42 @@ static int parse_argv(int argc, char *argv[]) {
         return 1;
 }
 
-static int acquire_esp(void) {
-        _cleanup_free_ char *np = NULL;
+static int acquire_path(void) {
+        _cleanup_free_ char *esp_path = NULL, *xbootldr_path = NULL;
+        char **a;
         int r;
 
-        r = find_esp_and_warn(arg_path, false, &np, NULL, NULL, NULL, NULL);
-        if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn in this one error case, but in all others */
-                return log_error_errno(r,
-                                       "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
-                                       "Alternatively, use --path= to specify path to mount point.");
-        if (r < 0)
+        if (!strv_isempty(arg_path))
+                return 0;
+
+        r = find_esp_and_warn(NULL, false, &esp_path, NULL, NULL, NULL, NULL);
+        if (r < 0 && r != -ENOKEY) /* ENOKEY means not found, and is the only error the function won't log about on its own */
+                return r;
+
+        r = find_xbootldr_and_warn(NULL, false, &xbootldr_path, NULL);
+        if (r < 0 && r != -ENOKEY)
                 return r;
 
-        free_and_replace(arg_path, np);
-        log_debug("Using EFI System Partition at %s.", arg_path);
+        if (!esp_path && !xbootldr_path)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+                                       "Couldn't find $BOOT partition. It is recommended to mount it to /boot.\n"
+                                       "Alternatively, use --path= to specify path to mount point.");
+
+        if (esp_path)
+                a = strv_new(esp_path, xbootldr_path);
+        else
+                a = strv_new(xbootldr_path);
+        if (!a)
+                return log_oom();
+
+        strv_free_and_replace(arg_path, a);
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *j;
+
+                j = strv_join(arg_path, ":");
+                log_debug("Using %s as boot loader drop-in search path.", j);
+        }
 
         return 0;
 }
@@ -119,10 +143,10 @@ static int parse_counter(
         e++;
 
         k = strspn(e, DIGITS);
-        if (k == 0) {
-                log_error("Can't parse empty 'tries left' counter from LoaderBootCountPath: %s", path);
-                return -EINVAL;
-        }
+        if (k == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Can't parse empty 'tries left' counter from LoaderBootCountPath: %s",
+                                       path);
 
         z = strndupa(e, k);
         r = safe_atou64(z, &left);
@@ -135,10 +159,10 @@ static int parse_counter(
                 e++;
 
                 k = strspn(e, DIGITS);
-                if (k == 0) /* If there's a "-" there also needs to be at least one digit */
-                        log_error("Can't parse empty 'tries done' counter from LoaderBootCountPath: %s", path);
-                        return -EINVAL;
-                }
+                if (k == 0) /* If there's a "-" there also needs to be at least one digit */
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Can't parse empty 'tries done' counter from LoaderBootCountPath: %s",
+                                               path);
 
                 z = strndupa(e, k);
                 r = safe_atou64(z, &done);
@@ -183,22 +207,22 @@ static int acquire_boot_count_path(
 
         efi_tilt_backslashes(path);
 
-        if (!path_is_normalized(path)) {
-                log_error("Path read from LoaderBootCountPath is not normalized, refusing: %s", path);
-                return -EINVAL;
-        }
+        if (!path_is_normalized(path))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Path read from LoaderBootCountPath is not normalized, refusing: %s",
+                                       path);
 
-        if (!path_is_absolute(path)) {
-                log_error("Path read from LoaderBootCountPath is not absolute, refusing: %s", path);
-                return -EINVAL;
-        }
+        if (!path_is_absolute(path))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Path read from LoaderBootCountPath is not absolute, refusing: %s",
+                                       path);
 
         last = last_path_component(path);
         e = strrchr(last, '+');
-        if (!e) {
-                log_error("Path read from LoaderBootCountPath does not contain a counter, refusing: %s", path);
-                return -EINVAL;
-        }
+        if (!e)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Path read from LoaderBootCountPath does not contain a counter, refusing: %s",
+                                       path);
 
         if (ret_prefix) {
                 prefix = strndup(path, e - path);
@@ -280,10 +304,9 @@ static const char *skip_slash(const char *path) {
 }
 
 static int verb_status(int argc, char *argv[], void *userdata) {
-
         _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
-        _cleanup_close_ int fd = -1;
         uint64_t left, done;
+        char **p;
         int r;
 
         r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix);
@@ -294,7 +317,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = acquire_esp();
+        r = acquire_path();
         if (r < 0)
                 return r;
 
@@ -306,50 +329,61 @@ static int verb_status(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return log_oom();
 
-        log_debug("Booted file: %s%s\n"
-                  "The same modified for 'good': %s%s\n"
-                  "The same modified for 'bad':  %s%s\n",
-                  arg_path, path,
-                  arg_path, good,
-                  arg_path, bad);
+        log_debug("Booted file: %s\n"
+                  "The same modified for 'good': %s\n"
+                  "The same modified for 'bad':  %s\n",
+                  path,
+                  good,
+                  bad);
 
         log_debug("Tries left: %" PRIu64"\n"
                   "Tries done: %" PRIu64"\n",
                   left, done);
 
-        fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
-        if (fd < 0)
-                return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path);
+        STRV_FOREACH(p, arg_path) {
+                _cleanup_close_ int fd = -1;
 
-        if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) {
-                puts("indeterminate");
-                return 0;
-        }
-        if (errno != ENOENT)
-                return log_error_errno(errno, "Failed to check if '%s' exists: %m", path);
+                fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+                if (fd < 0) {
+                        if (errno == ENOENT)
+                                continue;
 
-        if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) {
-                puts("good");
-                return 0;
-        }
-        if (errno != ENOENT)
-                return log_error_errno(errno, "Failed to check if '%s' exists: %m", good);
+                        return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
+                }
 
-        if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) {
-                puts("bad");
-                return 0;
+                if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) {
+                        puts("indeterminate");
+                        return 0;
+                }
+                if (errno != ENOENT)
+                        return log_error_errno(errno, "Failed to check if '%s' exists: %m", path);
+
+                if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) {
+                        puts("good");
+                        return 0;
+                }
+
+                if (errno != ENOENT)
+                        return log_error_errno(errno, "Failed to check if '%s' exists: %m", good);
+
+                if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) {
+                        puts("bad");
+                        return 0;
+                }
+                if (errno != ENOENT)
+                        return log_error_errno(errno, "Failed to check if '%s' exists: %m", bad);
+
+                /* We didn't find any of the three? If so, let's try the next directory, before we give up. */
         }
-        if (errno != ENOENT)
-                return log_error_errno(errno, "Failed to check if '%s' exists: %m", bad);
 
-        return log_error_errno(errno, "Couldn't determine boot state: %m");
+        return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Couldn't determine boot state: %m");
 }
 
 static int verb_set(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL, *parent = NULL;
         const char *target, *source1, *source2;
-        _cleanup_close_ int fd = -1;
         uint64_t done;
+        char **p;
         int r;
 
         r = acquire_boot_count_path(&path, &prefix, NULL, &done, &suffix);
@@ -358,7 +392,7 @@ static int verb_set(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = acquire_esp();
+        r = acquire_path();
         if (r < 0)
                 return r;
 
@@ -370,10 +404,6 @@ static int verb_set(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return log_oom();
 
-        fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
-        if (fd < 0)
-                return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path);
-
         /* Figure out what rename to what */
         if (streq(argv[0], "good")) {
                 target = good;
@@ -390,45 +420,58 @@ static int verb_set(int argc, char *argv[], void *userdata) {
                 source2 = bad;
         }
 
-        r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target));
-        if (r == -EEXIST)
-                goto exists;
-        else if (r == -ENOENT) {
+        STRV_FOREACH(p, arg_path) {
+                _cleanup_close_ int fd = -1;
+
+                fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
+                if (fd < 0)
+                        return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p);
 
-                r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target));
+                r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target));
                 if (r == -EEXIST)
                         goto exists;
                 else if (r == -ENOENT) {
 
-                        if (access(target, F_OK) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
+                        r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target));
+                        if (r == -EEXIST)
                                 goto exists;
+                        else if (r == -ENOENT) {
+
+                                if (faccessat(fd, skip_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
+                                        goto exists;
+
+                                if (errno != ENOENT)
+                                        return log_error_errno(errno, "Failed to determine if %s already exists: %m", target);
+
+                                /* We found none of the snippets here, try the next directory */
+                                continue;
+                        } else if (r < 0)
+                                return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target);
+                        else
+                                log_debug("Successfully renamed '%s' to '%s'.", source2, target);
 
-                        return log_error_errno(r, "Can't find boot counter source file for '%s': %m", target);
                 } else if (r < 0)
-                        return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target);
+                        return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target);
                 else
-                        log_debug("Successfully renamed '%s' to '%s'.", source2, target);
+                        log_debug("Successfully renamed '%s' to '%s'.", source1, target);
 
-        } else if (r < 0)
-                return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target);
-        else
-                log_debug("Successfully renamed '%s' to '%s'.", source1, target);
-
-        /* First, fsync() the directory these files are located in */
-        parent = dirname_malloc(path);
-        if (!parent)
-                return log_oom();
+                /* First, fsync() the directory these files are located in */
+                parent = dirname_malloc(target);
+                if (!parent)
+                        return log_oom();
 
-        r = fsync_path_at(fd, skip_slash(parent));
-        if (r < 0)
-                log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
+                r = fsync_path_at(fd, skip_slash(parent));
+                if (r < 0)
+                        log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
 
-        /* Secondly, syncfs() the whole file system these files are located in */
-        if (syncfs(fd) < 0)
-                log_debug_errno(errno, "Failed to synchronize ESP, ignoring: %m");
+                /* Secondly, syncfs() the whole file system these files are located in */
+                if (syncfs(fd) < 0)
+                        log_debug_errno(errno, "Failed to synchronize $BOOT partition, ignoring: %m");
 
-        log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done);
+                log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done);
+        }
 
+        log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Can't find boot counter source file for '%s': %m", target);
         return 1;
 
 exists:
@@ -436,14 +479,13 @@ exists:
         return 0;
 }
 
-int main(int argc, char *argv[]) {
-
+static int run(int argc, char *argv[]) {
         static const Verb verbs[] = {
-                { "help",          VERB_ANY, VERB_ANY, 0,                 help        },
-                { "status",        VERB_ANY, 1,        VERB_DEFAULT,      verb_status },
-                { "good",          VERB_ANY, 1,        VERB_MUST_BE_ROOT, verb_set    },
-                { "bad",           VERB_ANY, 1,        VERB_MUST_BE_ROOT, verb_set    },
-                { "indeterminate", VERB_ANY, 1,        VERB_MUST_BE_ROOT, verb_set    },
+                { "help",          VERB_ANY, VERB_ANY, 0,            help        },
+                { "status",        VERB_ANY, 1,        VERB_DEFAULT, verb_status },
+                { "good",          VERB_ANY, 1,        0,            verb_set    },
+                { "bad",           VERB_ANY, 1,        0,            verb_set    },
+                { "indeterminate", VERB_ANY, 1,        0,            verb_set    },
                 {}
         };
 
@@ -454,24 +496,17 @@ int main(int argc, char *argv[]) {
 
         r = parse_argv(argc, argv);
         if (r <= 0)
-                goto finish;
-
-        if (detect_container() > 0) {
-                log_error("Marking a boot is not supported in containers.");
-                r = -EOPNOTSUPP;
-                goto finish;
-        }
-
-        if (!is_efi_boot()) {
-                log_error("Marking a boot is only supported on EFI systems.");
-                r = -EOPNOTSUPP;
-                goto finish;
-        }
+                return r;
 
-        r = dispatch_verb(argc, argv, verbs, NULL);
+        if (detect_container() > 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "Marking a boot is not supported in containers.");
 
-finish:
-        free(arg_path);
+        if (!is_efi_boot())
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "Marking a boot is only supported on EFI systems.");
 
-        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+        return dispatch_verb(argc, argv, verbs, NULL);
 }
+
+DEFINE_MAIN_FUNCTION(run);