diff --git a/man/systemctl.xml b/man/systemctl.xml index ce485aee14..122d58d66d 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -2795,14 +2795,43 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err When used with kexec, append the specified string to the kernel command line options of the kexec kernel. The kernel command line is taken from the boot loader entry of the currently booted kernel (as selected automatically when no kexec kernel is preloaded, see - kexec above). This string is appended verbatim, separated from the existing - options by a single space. systemctl kexec will fail if this option is specified - when a kexec kernel is already loaded. + kexec above). This string is appended, separated from the existing + options by a single space. This option may be specified more than once, in which case the + strings are appended in the order given. systemctl kexec will fail if this + option is specified when a kexec kernel is already loaded. + + The kexec kernel command line is assembled in a fixed order, independent of the order in + which the options are specified on the command line: first the command line from the boot loader + entry, then the command line of the currently running kernel (if + is used), and finally the strings specified with + . Afterwards, arguments that are exactly identical and appear + more than once are removed, keeping the last occurrence. Note that only byte-for-byte identical + arguments are deduplicated: for example foo=bar and + foo=quux are both kept, as they are not identical (the latter takes effect, as + the kernel uses the last assignment of a given parameter). + + + + + Like , but instead of taking the string to append as an + argument, the command line of the currently running kernel is reused. This is useful to boot the + kexec kernel with the same command line options the running system was booted with, including any + modifications applied at boot time. + As with , the reused command line is added to the command line + taken from the boot loader entry, and systemctl kexec will fail if this option + is specified when a kexec kernel is already loaded. This option may be combined with + ; see above for the order in + which the various command line sources are combined and deduplicated. + + + + + diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in index 748126ea14..319ca134ea 100644 --- a/shell-completion/bash/systemctl.in +++ b/shell-completion/bash/systemctl.in @@ -134,7 +134,7 @@ _systemctl () { --help -h --no-ask-password --no-block --legend=no --no-pager --no-reload --no-wall --now --quiet -q --system --user --version --runtime --recursive -r --firmware-setup --show-types --plain --failed --value --fail --dry-run --wait --no-warn --with-dependencies - --show-transaction -T --mkdir --read-only' + --show-transaction -T --mkdir --read-only --kernel-cmdline-reuse' [ARG]='--host -H --kill-whom --property -p -P --signal -s --type -t --state --job-mode --root --preset-mode -n --lines -o --output -M --machine --message --timestamp --check-inhibitors --what --image --boot-loader-menu --boot-loader-entry --reboot-argument --kernel-cmdline --drop-in --when' diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in index ca8b218c2c..39791c0a39 100644 --- a/shell-completion/zsh/_systemctl.in +++ b/shell-completion/zsh/_systemctl.in @@ -543,4 +543,5 @@ _arguments -s \ '--failed[Show failed units]' \ '--timestamp=[Change format of printed timestamps]:style:_systemctl_timestamp' \ '--when=[Schedule halt/power-off/reboot/kexec action after a certain timestamp]:timestamp:(show cancel auto)' \ + '--kernel-cmdline-reuse[Reuse the current kernel command line when loading the kexec kernel]' \ '*::systemctl command:_systemctl_commands' diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c index a8702ad438..ab0285289b 100644 --- a/src/systemctl/systemctl-start-special.c +++ b/src/systemctl/systemctl-start-special.c @@ -9,6 +9,7 @@ #include "bus-error.h" #include "bus-locator.h" #include "efivars.h" +#include "extract-word.h" #include "fd-util.h" #include "log.h" #include "parse-util.h" @@ -24,13 +25,38 @@ #include "systemctl-trivial-method.h" #include "systemctl-util.h" +static int deduplicate_kernel_cmdline(char **cmdline) { + int r; + + assert(cmdline); + + /* Split, reverse, deduplicate, reverse again: the last occurrence of each identical argument wins. */ + + _cleanup_strv_free_ char **l = NULL; + r = strv_split_full(&l, *cmdline, /* separators= */ NULL, + EXTRACT_KEEP_QUOTE | EXTRACT_RETAIN_ESCAPE | EXTRACT_RELAX); + if (r < 0) + return r; + + strv_reverse(l); + strv_uniq(l); + strv_reverse(l); + + _cleanup_free_ char *joined = strv_join(l, " "); + if (!joined) + return -ENOMEM; + + free_and_replace(*cmdline, joined); + return 0; +} + static int load_kexec_kernel(void) { int r; if (kexec_loaded()) { - if (arg_kernel_cmdline) + if (arg_kernel_cmdline || arg_reuse_kernel_cmdline) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "--kernel-cmdline= specified but kexec kernel already loaded"); + "--kernel-cmdline=/--kernel-cmdline-reuse specified but kexec kernel already loaded"); log_debug("Kexec kernel already loaded."); return 0; } @@ -81,9 +107,19 @@ static int load_kexec_kernel(void) { if (!options) return log_oom(); + /* Assemble in a fixed order: UKI -> current cmdline -> appended cmdline, and dedup at the end */ + if (!isempty(arg_reuse_kernel_cmdline) && !strextend_with_separator(&options, " ", arg_reuse_kernel_cmdline)) + return log_oom(); + if (!isempty(arg_kernel_cmdline) && !strextend_with_separator(&options, " ", arg_kernel_cmdline)) return log_oom(); + if (!isempty(arg_reuse_kernel_cmdline) || !isempty(arg_kernel_cmdline)) { + r = deduplicate_kernel_cmdline(&options); + if (r < 0) + return log_error_errno(r, "Failed to deduplicate kernel command line: %m"); + } + log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "%s %s kernel=\"%s\" cmdline=\"%s\"%s%s%s", arg_dry_run ? "Would call" : "Calling", diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 99244de767..ef90e62a00 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -19,6 +19,7 @@ #include "parse-argument.h" #include "parse-util.h" #include "path-util.h" +#include "proc-cmdline.h" #include "static-destruct.h" #include "string-table.h" #include "string-util.h" @@ -71,6 +72,7 @@ usec_t arg_when = 0; bool arg_stdin = false; const char *arg_reboot_argument = NULL; char *arg_kernel_cmdline = NULL; +char *arg_reuse_kernel_cmdline = NULL; enum action arg_action = ACTION_SYSTEMCTL; BusTransport arg_transport = BUS_TRANSPORT_LOCAL; const char *arg_host = NULL; @@ -102,6 +104,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_reboot_argument, unsetp); STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep); +STATIC_DESTRUCTOR_REGISTER(arg_reuse_kernel_cmdline, freep); STATIC_DESTRUCTOR_REGISTER(arg_host, unsetp); STATIC_DESTRUCTOR_REGISTER(arg_boot_loader_entry, unsetp); STATIC_DESTRUCTOR_REGISTER(arg_clean_what, strv_freep); @@ -764,10 +767,31 @@ static int systemctl_parse_argv(int argc, char *argv[], int log_level_shift, cha return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--kernel-cmdline= argument contains invalid characters: %s", opts.arg); - r = free_and_strdup_warn(&arg_kernel_cmdline, opts.arg); + if (!strextend_with_separator(&arg_kernel_cmdline, " ", opts.arg)) + return log_oom(); + break; + + OPTION_LONG("kernel-cmdline-reuse", NULL, + "Like --kernel-cmdline=, but reuse the current kernel command line"): { + _cleanup_free_ char *cmdline = NULL; + + r = proc_cmdline(&cmdline); + if (r < 0) + return log_error_errno(r, "Failed to read current kernel command line: %m"); + + const char *stripped = empty_to_null(strstrip(cmdline)); + if (!stripped) + break; + + if (!string_is_safe(stripped, STRING_ALLOW_GLOBS|STRING_ALLOW_BACKSLASHES|STRING_ALLOW_QUOTES)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Current kernel command line contains invalid characters: %s", stripped); + + r = free_and_strdup_warn(&arg_reuse_kernel_cmdline, stripped); if (r < 0) return r; break; + } OPTION_LONG("plain", NULL, "Print unit dependencies as a list instead of a tree"): arg_plain = true; diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index e157ab12f0..8f05bdb46e 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -77,6 +77,7 @@ extern usec_t arg_when; extern bool arg_stdin; extern const char *arg_reboot_argument; extern char *arg_kernel_cmdline; +extern char *arg_reuse_kernel_cmdline; extern enum action arg_action; extern BusTransport arg_transport; extern const char *arg_host;