#include "fd-util.h"
#include "fs-util.h"
#include "log.h"
+#include "main-func.h"
#include "parse-util.h"
#include "path-util.h"
#include "util.h"
#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) {
"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"
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;
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;
}
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);
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);
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);
}
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);
if (r < 0)
return r;
- r = acquire_esp();
+ r = acquire_path();
if (r < 0)
return r;
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);
if (r < 0)
return r;
- r = acquire_esp();
+ r = acquire_path();
if (r < 0)
return r;
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;
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:
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 },
{}
};
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);