From: Zbigniew Jędrzejewski-Szmek Date: Fri, 11 Jun 2021 15:11:34 +0000 (+0200) Subject: bootctl: print SystemdOptions from efivarfs if newer than our cache X-Git-Tag: v249-rc1~2^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ad2d6880eaf96916e82ae83fc6d57c7f4daf620e;p=thirdparty%2Fsystemd.git bootctl: print SystemdOptions from efivarfs if newer than our cache The logic is that if the options are updated after boot, we *don't* use the new value. But we still want to print out the changed contents in bootctl as to not confuse people. Fixes #19597. Also https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=988450. $ build/bootctl systemd-efi-options quiet Note: SystemdOptions EFI variable has been modified since boot. New value: debug The hint is printed to stderr, so scripts should not be confused. --- diff --git a/src/basic/efivars.c b/src/basic/efivars.c index db57189feb6..032ae6f9a1c 100644 --- a/src/basic/efivars.c +++ b/src/basic/efivars.c @@ -305,8 +305,7 @@ bool is_efi_secure_boot_setup_mode(void) { return cache > 0; } -int cache_efi_options_variable(void) { - _cleanup_free_ char *line = NULL; +static int read_efi_options_variable(char **line) { int r; /* In SecureBoot mode this is probably not what you want. As your cmdline is cryptographically signed @@ -326,9 +325,17 @@ int cache_efi_options_variable(void) { return -EPERM; } - r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), &line); + r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), line); if (r == -ENOENT) return -ENODATA; + return r; +} + +int cache_efi_options_variable(void) { + _cleanup_free_ char *line = NULL; + int r; + + r = read_efi_options_variable(&line); if (r < 0) return r; @@ -340,6 +347,8 @@ int systemd_efi_options_variable(char **line) { const char *e; int r; + /* Returns the contents of the variable for current boot from the cache. */ + assert(line); /* For testing purposes it is sometimes useful to be able to override this */ @@ -360,4 +369,34 @@ int systemd_efi_options_variable(char **line) { return -ENODATA; return r; } + +static inline int compare_stat_mtime(const struct stat *a, const struct stat *b) { + return CMP(timespec_load(&a->st_mtim), timespec_load(&b->st_mtim)); +} + +int systemd_efi_options_efivarfs_if_newer(char **line) { + struct stat a = {}, b; + int r; + + if (stat(EFIVAR_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), &a) < 0 && errno != ENOENT) + return log_debug_errno(errno, "Failed to stat EFI variable SystemdOptions: %m"); + + if (stat(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), &b) < 0) { + if (errno != -ENOENT) + log_debug_errno(errno, "Failed to stat "EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions))": %m"); + } else if (compare_stat_mtime(&a, &b) > 0) + log_debug("Variable SystemdOptions in evifarfs is newer than in cache."); + else { + log_debug("Variable SystemdOptions in cache is up to date."); + *line = NULL; + return 0; + } + + r = read_efi_options_variable(line); + if (r < 0) + log_warning_errno(r, "Failed to read SystemdOptions EFI variable: %m"); + if (r == -ENOENT) + return -ENODATA; + return r; +} #endif diff --git a/src/basic/efivars.h b/src/basic/efivars.h index 0770cc13fe0..cb6ad9e0a2d 100644 --- a/src/basic/efivars.h +++ b/src/basic/efivars.h @@ -52,6 +52,7 @@ bool is_efi_secure_boot_setup_mode(void); int cache_efi_options_variable(void); int systemd_efi_options_variable(char **line); +int systemd_efi_options_efivarfs_if_newer(char **line); #else @@ -90,4 +91,8 @@ static inline int cache_efi_options_variable(void) { static inline int systemd_efi_options_variable(char **line) { return -ENODATA; } + +static inline int systemd_efi_options_efivarfs_if_newer(char **line) { + return -ENODATA; +} #endif diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 7d12b5af7cd..df8b0542c9d 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1860,14 +1860,25 @@ static int verb_systemd_efi_options(int argc, char *argv[], void *userdata) { int r; if (argc == 1) { - _cleanup_free_ char *line = NULL; + _cleanup_free_ char *line = NULL, *new = NULL; r = systemd_efi_options_variable(&line); - if (r < 0) - return log_error_errno(r, "Failed to query SystemdOptions EFI variable: %m"); - - puts(line); - + if (r == -ENODATA) + log_debug("No SystemdOptions EFI variable present in cache."); + else if (r < 0) + return log_error_errno(r, "Failed to read SystemdOptions EFI variable from cache: %m"); + else + puts(line); + + r = systemd_efi_options_efivarfs_if_newer(&new); + if (r == -ENODATA) { + if (line) + log_notice("Note: SystemdOptions EFI variable has been removed since boot."); + } else if (r < 0) + log_warning_errno(r, "Failed to check SystemdOptions EFI variable in efivarfs, ignoring: %m"); + else if (new && !streq_ptr(line, new)) + log_notice("Note: SystemdOptions EFI variable has been modified since boot. New value: %s", + new); } else { r = efi_set_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), argv[1]); if (r < 0)