]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bootctl: configure a sysfail entry
authorIgor Opaniuk <igor.opaniuk@foundries.io>
Mon, 24 Mar 2025 14:33:16 +0000 (15:33 +0100)
committerIgor Opaniuk <igor.opaniuk@foundries.io>
Mon, 12 May 2025 13:37:47 +0000 (15:37 +0200)
You can configure the sysfail boot entry using the bootctl command:
$ bootctl set-sysfail sysfail.conf

The value will be stored in the `LoaderEntrySysFail` EFI variable.

The `LoaderEntrySysFail` EFI variable would be unset automatically
during next boot by `systemd-boot-clear-sysfail.service` if no
system failure occured, otherwise it would be kept as it is and a system
failure reason will be saved to `LoaderSysFailReason` EFI variable.

Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
man/bootctl.xml
man/systemd-boot-clear-sysfail.service.xml [new file with mode: 0644]
presets/90-systemd.preset
src/boot/meson.build
src/bootctl/bootctl-install.c
src/bootctl/bootctl-set-efivar.c
src/bootctl/bootctl-status.c
src/bootctl/bootctl.c
units/meson.build
units/systemd-boot-clear-sysfail.service [new file with mode: 0644]

index 91d572e6430232781919369b08b334707c583bf9..f1968a0ba7725a046c1972fa25b568ab596c6c25 100644 (file)
       <varlistentry>
         <term><option>set-default</option> <replaceable>ID</replaceable></term>
         <term><option>set-oneshot</option> <replaceable>ID</replaceable></term>
+        <term><option>set-sysfail</option> <replaceable>ID</replaceable></term>
 
         <listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string or a glob
         pattern as argument. The <option>set-oneshot</option> command will set the default entry only for the next boot,
-        the <option>set-default</option> will set it persistently for all future boots.</para>
+        the <option>set-default</option> will set it persistently for all future boots. The <option>set-sysfail</option> command
+        will set the boot loader entry to be used in case of a system failure. System failure (SysFail) boot entries can
+        optionally modify the automatic selection order in the event of a failure, such as a boot firmware update failure with
+        the failure status recorded in the EFI system table.</para>
 
         <para><command>bootctl list</command> can be used to list available boot loader entries and their
         IDs.</para>
         <option>@oneshot</option> or <option>@current</option>, which correspond to the current default boot loader
         entry for all future boots, the current default boot loader entry for the next boot, and the currently booted
         boot loader entry. These special IDs are resolved to the current values of the EFI variables
-        <varname>LoaderEntryDefault</varname>, <varname>LoaderEntryOneShot</varname> and <varname>LoaderEntrySelected</varname>,
-        see <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink> for details.
+        <varname>LoaderEntryDefault</varname>, <varname>LoaderEntrySysFail</varname>, <varname>LoaderEntryOneShot</varname>
+        and <varname>LoaderEntrySelected</varname>, see <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">
+        Boot Loader Specification</ulink> for details.
         These special IDs are primarily useful as a quick way to persistently make the currently booted boot loader
         entry the default choice, or to upgrade the default boot loader entry for the next boot to the default boot
         loader entry for all future boots, but may be used for other operations too.</para>
diff --git a/man/systemd-boot-clear-sysfail.service.xml b/man/systemd-boot-clear-sysfail.service.xml
new file mode 100644 (file)
index 0000000..2249486
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-boot-clear-sysfail.service" conditional='ENABLE_BOOTLOADER'
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-boot-clear-sysfail.service</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-boot-clear-sysfail.service</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-boot-clear-sysfail.service</refname>
+    <refpurpose>Clear LoaderEntrySysFail entry </refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>systemd-boot-clear-sysfail.service</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>systemd-boot-clear-sysfail.service</filename> is a system service that automatically clears the
+    'LoaderEntrySysFail' boot loader entry if the boot was successful and the 'LoaderSysFailReason' EFI variable,
+    which indicates the reason for the system failure, is not set. </para>
+
+    <para>The <filename>systemd-boot-random-seed.service</filename> unit invokes the <command>bootctl --graceful
+    set-sysfail ""</command> command, which clears the LoaderEntrySysFail entry. The service is conditionalized
+    so that it is run only when a LoaderSysFailReason entry is not set.</para><para>For further details see
+    <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, regarding
+    the command this service invokes.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para><simplelist type="inline">
+      <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+    </simplelist></para>
+  </refsect1>
+
+</refentry>
index 9c13e9c3de5c526d2c5a39574437747299c35b2a..56f9e9370613e41cb118a9f99f6387d99a93c368 100644 (file)
@@ -19,6 +19,7 @@ enable machines.target
 
 enable getty@.service
 
+enable systemd-boot-clear-sysfail.service
 enable systemd-boot-update.service
 enable systemd-confext.service
 enable systemd-homed.service
index c2a894d323684d0089fc6c4eb390ed4f072535e7..fb0e393de90ee2b6db491d98908ce181c6a6207d 100644 (file)
@@ -307,6 +307,7 @@ libefi_sources = files(
         'secure-boot.c',
         'shim.c',
         'smbios.c',
+        'sysfail.c',
         'ticks.c',
         'url-discovery.c',
         'util.c',
index 8927464c4652a9ece1da2a4db29f6c5e5411d900..7b083c4ad9dcaada7d2cd50b17202376b57aa867 100644 (file)
@@ -1224,6 +1224,7 @@ static int remove_loader_variables(void) {
                        EFI_LOADER_VARIABLE_STR("LoaderConfigTimeout"),
                        EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"),
                        EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"),
+                       EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"),
                        EFI_LOADER_VARIABLE_STR("LoaderEntryLastBooted"),
                        EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"),
                        EFI_LOADER_VARIABLE_STR("LoaderSystemToken")) {
index d53458c49108413b56c8bb9331d4f1d254934cb2..ead81ff3e043f7a4a6bd279642709cd79813ceff 100644 (file)
@@ -89,6 +89,11 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
                 if (r < 0)
                         return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
 
+        } else if (streq(arg1, "@sysfail")) {
+                r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"), NULL, (void *) ret_target, ret_target_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get EFI variable 'LoaderEntrySysFail': %m");
+
         } else if (arg1[0] == '@' && !streq(arg1, "@saved"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
         else {
@@ -144,6 +149,9 @@ int verb_set_efivar(int argc, char *argv[], void *userdata) {
         if (streq(argv[0], "set-default")) {
                 variable = EFI_LOADER_VARIABLE_STR("LoaderEntryDefault");
                 arg_parser = parse_loader_entry_target_arg;
+        } else if (streq(argv[0], "set-sysfail")) {
+                variable = EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail");
+                arg_parser = parse_loader_entry_target_arg;
         } else if (streq(argv[0], "set-oneshot")) {
                 variable = EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot");
                 arg_parser = parse_loader_entry_target_arg;
index 38fe5273bec3a595340b2de41aaa09d92ca470e1..081f5d48f0f3b161380f5297dce575ff48485369 100644 (file)
@@ -420,7 +420,7 @@ int verb_status(int argc, char *argv[], void *userdata) {
                         { EFI_STUB_FEATURE_MULTI_PROFILE_UKI,         "Stub understands profile selector"                           },
                 };
                 _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL,
-                        *current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL;
+                        *current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL, *sysfail_entry = NULL, *sysfail_reason = NULL;
                 uint64_t loader_features = 0, stub_features = 0;
                 int have;
 
@@ -435,6 +435,8 @@ int verb_status(int argc, char *argv[], void *userdata) {
                 (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntrySelected"), &current_entry);
                 (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), &oneshot_entry);
                 (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"), &default_entry);
+                (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"), &sysfail_entry);
+                (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderSysFailReason"), &sysfail_reason);
 
                 SecureBootMode secure = efi_get_secure_boot_mode();
                 printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
@@ -484,7 +486,7 @@ int verb_status(int argc, char *argv[], void *userdata) {
 
                 if (loader) {
                         printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
-                        printf("      Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal());
+                        printf("       Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal());
                         for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
                                 print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
 
@@ -502,23 +504,28 @@ int verb_status(int argc, char *argv[], void *userdata) {
                                                SD_ID128_FORMAT_VAL(loader_partition_uuid),
                                                SD_ID128_FORMAT_VAL(esp_uuid));
 
-                                printf("    Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
+                                printf("     Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
                                        SD_ID128_FORMAT_VAL(loader_partition_uuid));
                         } else if (loader_path)
-                                printf("    Partition: n/a\n");
+                                printf("     Partition: n/a\n");
 
                         if (loader_path)
-                                printf("       Loader: %s%s\n", glyph(GLYPH_TREE_RIGHT), strna(loader_path));
+                                printf("        Loader: %s%s\n", glyph(GLYPH_TREE_RIGHT), strna(loader_path));
 
                         if (loader_url)
-                                printf(" Net Boot URL: %s\n", loader_url);
+                                printf("  Net Boot URL: %s\n", loader_url);
+
+                        if (sysfail_entry)
+                                printf("SysFail Reason: %s\n", sysfail_reason);
 
                         if (current_entry)
-                                printf("Current Entry: %s\n", current_entry);
+                                printf(" Current Entry: %s\n", current_entry);
                         if (default_entry)
-                                printf("Default Entry: %s\n", default_entry);
+                                printf(" Default Entry: %s\n", default_entry);
                         if (oneshot_entry && !streq_ptr(oneshot_entry, default_entry))
-                                printf("OneShot Entry: %s\n", oneshot_entry);
+                                printf(" OneShot Entry: %s\n", oneshot_entry);
+                        if (sysfail_entry)
+                                printf(" SysFail Entry: %s\n", sysfail_entry);
 
                         printf("\n");
                 }
index 047b79254d2269c242c466c4a6f96738c4835f39..790ab2b653d089612e76ab76b880abe15630bb3c 100644 (file)
@@ -259,6 +259,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "\n%3$sBoot Loader Interface Commands:%4$s\n"
                "  set-default ID       Set default boot loader entry\n"
                "  set-oneshot ID       Set default boot loader entry, for next boot only\n"
+               "  set-sysfail ID       Set boot loader entry used in case of a system failure\n"
                "  set-timeout SECONDS  Set the menu timeout\n"
                "  set-timeout-oneshot SECONDS\n"
                "                       Set the menu timeout for the next boot only\n"
@@ -660,6 +661,7 @@ static int bootctl_main(int argc, char *argv[]) {
                 { "set-oneshot",         2,        2,        0,            verb_set_efivar          },
                 { "set-timeout",         2,        2,        0,            verb_set_efivar          },
                 { "set-timeout-oneshot", 2,        2,        0,            verb_set_efivar          },
+                { "set-sysfail",         2,        2,        0,            verb_set_efivar          },
                 { "random-seed",         VERB_ANY, 1,        0,            verb_random_seed         },
                 { "reboot-to-firmware",  VERB_ANY, 2,        0,            verb_reboot_to_firmware  },
                 {}
index 3dd135942d37ce2108f6b78a3715192cc3e8b0b5..a8b004056233fcf632d4e404539b1ae0a7f82c8f 100644 (file)
@@ -286,6 +286,10 @@ units = [
           'conditions' : ['ENABLE_BOOTLOADER'],
           'symlinks' : ['sysinit.target.wants/'],
         },
+        {
+          'file' : 'systemd-boot-clear-sysfail.service',
+          'conditions' : ['ENABLE_BOOTLOADER'],
+        },
         {
           'file' : 'systemd-boot-update.service',
           'conditions' : ['ENABLE_BOOTLOADER'],
diff --git a/units/systemd-boot-clear-sysfail.service b/units/systemd-boot-clear-sysfail.service
new file mode 100644 (file)
index 0000000..c807359
--- /dev/null
@@ -0,0 +1,29 @@
+#  SPDX-License-Identifier: LGPL-2.1-or-later
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Clear SysFail Entry If The Boot Is Successful
+Documentation=man:systemd-boot-sysfail.service(8)
+
+DefaultDependencies=no
+Conflicts=shutdown.target
+Before=sysinit.target shutdown.target
+
+ConditionPathExists=!/etc/initrd-release
+# If LoaderSysFailReason is set we should not clear SysFail entry
+ConditionPathExists=!/sys/firmware/efi/efivars/LoaderSysFailReason-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+ConditionPathExists=/sys/firmware/efi/efivars/LoaderEntrySysFail-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=bootctl --graceful set-sysfail ""
+
+[Install]
+WantedBy=sysinit.target