]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bootctl: print SystemdOptions from efivarfs if newer than our cache 19896/head
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 11 Jun 2021 15:11:34 +0000 (17:11 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 15 Jun 2021 20:01:42 +0000 (22:01 +0200)
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.

src/basic/efivars.c
src/basic/efivars.h
src/boot/bootctl.c

index db57189feb6645b490e2511bc0257e50511c0eb7..032ae6f9a1ce209583517e84bfbda9464c0f21ca 100644 (file)
@@ -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
index 0770cc13fe0198bf044836660d950fd841caa937..cb6ad9e0a2d2286b29d96206ec972f0a2be4ca89 100644 (file)
@@ -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
index 7d12b5af7cdff048575f0f3ff62adefd54cc3701..df8b0542c9d2a00799a9f0a000bb6fbd8392bc30 100644 (file)
@@ -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)