--- 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 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel