[systemd-devel] [PATCH 2/2] fsck: Add support for EFI variable based fsck indication

Jan Janssen medhefgo at web.de
Sun Mar 15 03:56:55 PDT 2015


---
 man/systemctl.xml                  | 26 ++++++++++++++++
 shell-completion/bash/systemctl.in |  8 ++++-
 shell-completion/zsh/_systemctl.in |  2 ++
 src/fsck/fsck.c                    | 63 +++++++++++++++++++++++++++++++++++++
 src/shared/efivars.h               | 21 +++++++++++--
 src/systemctl/systemctl.c          | 64 +++++++++++++++++++++++++++++++++-----
 6 files changed, 173 insertions(+), 11 deletions(-)

diff --git a/man/systemctl.xml b/man/systemctl.xml
index 3e2bcde..8449d83 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -466,6 +466,32 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>--fsck-mode=</option></term>
+
+        <listitem>
+          <para>Control file system check behavior for next boot on EFI systems.</para>
+
+          <para>One of <literal>auto</literal>, <literal>force</literal> and
+          <literal>skip</literal>. See <citerefentry><refentrytitle>systemd-fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+          for details. Note that this requires the system to be booted in EFI mode and
+          that kernel command line parameters take precedence.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--fsck-repair=</option></term>
+
+        <listitem>
+          <para>Control file system check repair behavior for next boot on EFI systems.</para>
+
+          <para>One of <literal>preen</literal>, <literal>yes</literal> and
+          <literal>no</literal>. See <citerefentry><refentrytitle>systemd-fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+          for details. Note that this requires the system to be booted in EFI mode and
+          that kernel command line parameters take precedence.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>--root=</option></term>
 
         <listitem>
diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in
index f14fe7a..cea28cd 100644
--- a/shell-completion/bash/systemctl.in
+++ b/shell-completion/bash/systemctl.in
@@ -93,7 +93,7 @@ _systemctl () {
                [STANDALONE]='--all -a --reverse --after --before --defaults --fail --ignore-dependencies --failed --force -f --full -l --global
                              --help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall
                              --quiet -q --privileged -P --system --user --version --runtime --recursive -r --firmware'
-                      [ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --root'
+                      [ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --root --fsck-mode --fsck-repair'
         )
 
         if __contains_word "--user" ${COMP_WORDS[*]}; then
@@ -118,6 +118,12 @@ _systemctl () {
                         --kill-who)
                                 comps='all control main'
                         ;;
+                        --fsck-mode)
+                                comps='auto force skip'
+                        ;;
+                        --fsck-repair)
+                                comps='preen yes no'
+                        ;;
                         --root)
                                 comps=$(compgen -A directory -- "$cur" )
                                 compopt -o filenames
diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in
index 1caf9a4..b8c82cc 100644
--- a/shell-completion/zsh/_systemctl.in
+++ b/shell-completion/zsh/_systemctl.in
@@ -377,6 +377,8 @@ _arguments -s \
     '--no-ask-password[Do not ask for system passwords]' \
     '--firmware[Reboot to EFI setup on machines that support it]' \
     '--kill-who=[Who to send signal to]:killwho:(main control all)' \
+    '--fsck-mode=[Control filesystem check mode next boot on EFI systems]:fsckmode:(auto force skip)' \
+    '--fsck-repair=[Mode of operation to use with filesystem check]:fsckrepair:(preen yes no)' \
     {-s+,--signal=}'[Which signal to send]:signal:_signals' \
     {-f,--force}'[When enabling unit files, override existing symlinks. When shutting down, execute action immediately]' \
     '--root=[Enable unit files in the specified root directory]:directory:_directories' \
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index 6e46633..ef56bb0 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -40,6 +40,7 @@
 #include "path-util.h"
 #include "socket-util.h"
 #include "fsckd/fsckd.h"
+#include "efivars.h"
 
 static bool arg_skip = false;
 static bool arg_force = false;
@@ -130,6 +131,67 @@ static void test_files(void) {
 
 }
 
+static void parse_efi_vars(void) {
+        int r;
+        size_t s;
+        _cleanup_free_ void *v = NULL;
+
+        if (!is_efi_boot())
+                return;
+
+        r = efi_get_variable(EFI_VENDOR_SYSTEMD, "FsckModeOneShot", NULL, &v, &s);
+        if (r < 0 || s != sizeof(EfiSystemdFsckMode))
+                log_warning("Failed to read FsckModeOneShot EFI variable.");
+        else {
+                EfiSystemdFsckMode value = *(EfiSystemdFsckMode *)v;
+
+                switch (value) {
+                case EFI_SYSTEMD_FSCK_MODE_AUTO:
+                        arg_force = arg_skip = false;
+                        break;
+                case EFI_SYSTEMD_FSCK_MODE_FORCE:
+                        arg_force = true;
+                        break;
+                case EFI_SYSTEMD_FSCK_MODE_SKIP:
+                        arg_skip = true;
+                        break;
+                default:
+                        log_warning("Invalid value for FsckModeOneShot EFI variable. Ignoring.");
+                }
+
+                r = efi_set_variable(EFI_VENDOR_SYSTEMD, "FsckModeOneShot", NULL, 0);
+                if (r < 0)
+                        log_warning_errno(r, "Error while deleting FsckModeOneShot EFI variable: %m");
+        }
+
+        free(v);
+        v = NULL;
+        r = efi_get_variable(EFI_VENDOR_SYSTEMD, "FsckRepairOneShot", NULL, &v, &s);
+        if (r < 0 || s != sizeof(EfiSystemdFsckRepair))
+                log_warning("Failed to read FsckRepairOneShot EFI variable.");
+        else {
+                EfiSystemdFsckRepair value = *(EfiSystemdFsckRepair *)v;
+
+                switch (value) {
+                case EFI_SYSTEMD_FSCK_REPAIR_PREEN:
+                        arg_repair = "-a";
+                        break;
+                case EFI_SYSTEMD_FSCK_REPAIR_YES:
+                        arg_repair = "-y";
+                        break;
+                case EFI_SYSTEMD_FSCK_REPAIR_NO:
+                        arg_repair = "-n";
+                        break;
+                default:
+                        log_warning("Invalid value for FsckRepairOneShot EFI variable. Ignoring.");
+                }
+
+                r = efi_set_variable(EFI_VENDOR_SYSTEMD, "FsckRepairOneShot", NULL, 0);
+                if (r < 0)
+                        log_warning_errno(r, "Error while deleting FsckRepairOneShot EFI variable: %m");
+        }
+}
+
 static int process_progress(int fd, pid_t fsck_pid, dev_t device_num) {
         _cleanup_fclose_ FILE *f = NULL;
         usec_t last = 0;
@@ -218,6 +280,7 @@ int main(int argc, char *argv[]) {
 
         umask(0022);
 
+        parse_efi_vars();
         q = parse_proc_cmdline(parse_proc_cmdline_item);
         if (q < 0)
                 log_warning_errno(q, "Failed to parse kernel command line, ignoring: %m");
diff --git a/src/shared/efivars.h b/src/shared/efivars.h
index 7bdfb74..3b7a314 100644
--- a/src/shared/efivars.h
+++ b/src/shared/efivars.h
@@ -26,13 +26,30 @@
 #include "sd-id128.h"
 #include "time-util.h"
 
-#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
-#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
+#define EFI_VENDOR_LOADER  SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
+#define EFI_VENDOR_GLOBAL  SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
+#define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(45,0e,ad,32,7c,d1,47,81,8b,25,da,83,30,cc,1e,22)
 #define EFI_VARIABLE_NON_VOLATILE        0x0000000000000001
 #define EFI_VARIABLE_BOOTSERVICE_ACCESS  0x0000000000000002
 #define EFI_VARIABLE_RUNTIME_ACCESS      0x0000000000000004
 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001
 
+typedef enum EfiSystemdFsckMode {
+        EFI_SYSTEMD_FSCK_MODE_AUTO,
+        EFI_SYSTEMD_FSCK_MODE_FORCE,
+        EFI_SYSTEMD_FSCK_MODE_SKIP,
+        _EFI_SYSTEMD_FSCK_MODE_MAX,
+        _EFI_SYSTEMD_FSCK_MODE_INVALID = -1,
+} EfiSystemdFsckMode;
+
+typedef enum EfiSystemdFsckRepair {
+        EFI_SYSTEMD_FSCK_REPAIR_PREEN,
+        EFI_SYSTEMD_FSCK_REPAIR_YES,
+        EFI_SYSTEMD_FSCK_REPAIR_NO,
+        _EFI_SYSTEMD_FSCK_REPAIR_MAX,
+        _EFI_SYSTEMD_FSCK_REPAIR_INVALID = -1,
+} EfiSystemdFsckRepair;
+
 bool is_efi_boot(void);
 int is_efi_secure_boot(void);
 int is_efi_secure_boot_setup_mode(void);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 8aee3c4..bddbd8d 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -135,6 +135,8 @@ static unsigned arg_lines = 10;
 static OutputMode arg_output = OUTPUT_SHORT;
 static bool arg_plain = false;
 static bool arg_firmware = false;
+static EfiSystemdFsckMode arg_fsck_mode = _EFI_SYSTEMD_FSCK_MODE_INVALID;
+static EfiSystemdFsckRepair arg_fsck_repair = _EFI_SYSTEMD_FSCK_REPAIR_INVALID;
 static bool original_stdout_is_tty;
 
 static int daemon_reload(sd_bus *bus, char **args);
@@ -2916,6 +2918,7 @@ static int check_inhibitors(sd_bus *bus, enum action a) {
 static int start_special(sd_bus *bus, char **args) {
         enum action a;
         int r;
+        bool need_efi = false;
 
         assert(args);
 
@@ -2925,21 +2928,35 @@ static int start_special(sd_bus *bus, char **args) {
         if (r < 0)
                 return r;
 
-        if ((arg_firmware || arg_force >= 2) && geteuid() != 0)
+        need_efi = arg_firmware
+                        || arg_fsck_mode != _EFI_SYSTEMD_FSCK_MODE_INVALID
+                        || arg_fsck_repair != _EFI_SYSTEMD_FSCK_REPAIR_INVALID;
+        if ((need_efi || arg_force >= 2) && geteuid() != 0)
                 return log_error_errno(EPERM, "Must be root.");
+        else if (need_efi && detect_container(NULL) > 0)
+                return log_error_errno(ENOTSUP, "Cannot perform operation from within a container.");
+        else if (need_efi && !is_efi_boot())
+                return log_error_errno(ENOTSUP, "Operation requires the system to be booted in EFI mode.");
+        else if (arg_firmware && a != ACTION_REBOOT)
+                return log_error_errno(EINVAL, "Must use reboot command to reboot to firmware.");
+
+        if (arg_fsck_mode != _EFI_SYSTEMD_FSCK_MODE_INVALID) {
+                r = efi_set_variable(EFI_VENDOR_SYSTEMD, "FsckModeOneShot", &arg_fsck_mode, sizeof(arg_fsck_mode));
+                if (r < 0)
+                        return log_error_errno(r, "Error writing FsckModeOneShot EFI variable.");
+        }
+
+        if (arg_fsck_repair != _EFI_SYSTEMD_FSCK_REPAIR_INVALID) {
+                r = efi_set_variable(EFI_VENDOR_SYSTEMD, "FsckRepairOneShot", &arg_fsck_repair, sizeof(arg_fsck_repair));
+                if (r < 0)
+                        return log_error_errno(r, "Error writing FsckRepairOneShot EFI variable.");
+        }
 
         if (arg_firmware) {
                 size_t s;
                 uint64_t b;
                 _cleanup_free_ void *v = NULL;
 
-                if (a != ACTION_REBOOT)
-                        return log_error_errno(EINVAL, "Must use reboot command to reboot to firmware.");
-                else if (detect_container(NULL) > 0)
-                        return log_error_errno(ENOTSUP, "Cannot reboot to firmware from within a container.");
-                else if (!is_efi_boot())
-                        return log_error_errno(ENOTSUP, "Reboot to firmware requires the system to be booted in EFI mode.");
-
                 r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
                 if (r < 0 || s != sizeof(uint64_t))
                         return log_error_errno(r, "Error reading OsIndicationsSupported EFI variable.");
@@ -5999,6 +6016,9 @@ static void systemctl_help(void) {
                "  -f --force          When enabling unit files, override existing symlinks\n"
                "                      When shutting down, execute action immediately\n"
                "     --firmware       Reboot to EFI setup on machines that support it\n"
+               "     --fsck-mode=MODE Control filesystem check mode next boot on EFI systems\n"
+               "     --fsck-repair=MODE\n"
+               "                      Mode of operation to use with filesystem check\n"
                "     --preset-mode=   Apply only enable, only disable, or all presets\n"
                "     --root=PATH      Enable unit files in the specified root directory\n"
                "  -n --lines=INTEGER  Number of journal entries to show\n"
@@ -6185,6 +6205,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_JOB_MODE,
                 ARG_PRESET_MODE,
                 ARG_FIRMWARE,
+                ARG_FSCK_MODE,
+                ARG_FSCK_REPAIR,
         };
 
         static const struct option options[] = {
@@ -6228,6 +6250,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "recursive",           no_argument,       NULL, 'r'                     },
                 { "preset-mode",         required_argument, NULL, ARG_PRESET_MODE         },
                 { "firmware",            no_argument,       NULL, ARG_FIRMWARE            },
+                { "fsck-mode",           required_argument, NULL, ARG_FSCK_MODE           },
+                { "fsck-repair",         required_argument, NULL, ARG_FSCK_REPAIR         },
                 {}
         };
 
@@ -6504,6 +6528,30 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_FSCK_MODE:
+
+                        if (streq(optarg, "auto"))
+                                arg_fsck_mode = EFI_SYSTEMD_FSCK_MODE_AUTO;
+                        else if (streq(optarg, "force"))
+                                arg_fsck_mode = EFI_SYSTEMD_FSCK_MODE_FORCE;
+                        else if (streq(optarg, "skip"))
+                                arg_fsck_mode = EFI_SYSTEMD_FSCK_MODE_SKIP;
+                        else
+                                return log_error_errno(EINVAL, "Failed to parse fsck-mode: %s.", optarg);
+                        break;
+
+                case ARG_FSCK_REPAIR:
+
+                        if (streq(optarg, "preen"))
+                                arg_fsck_repair = EFI_SYSTEMD_FSCK_REPAIR_PREEN;
+                        else if (streq(optarg, "yes"))
+                                arg_fsck_repair = EFI_SYSTEMD_FSCK_REPAIR_YES;
+                        else if (streq(optarg, "no"))
+                                arg_fsck_repair = EFI_SYSTEMD_FSCK_REPAIR_NO;
+                        else
+                                return log_error_errno(EINVAL, "Failed to parse fsck-repair: %s.", optarg);
+                        break;
+
                 case ARG_FIRMWARE:
                         arg_firmware = true;
                         break;
-- 
2.3.2



More information about the systemd-devel mailing list