From: Luca Boccassi Date: Sat, 20 Jun 2026 00:05:00 +0000 (+0100) Subject: systemctl: add --kernel-cmdline-reuse option X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ee7c04873e83c0a03edd8f6c6a6b1226e2e70249;p=thirdparty%2Fsystemd.git systemctl: add --kernel-cmdline-reuse option kexec-tools has a --reuse-cmdline option which is very convenient when doing a lot of reboots, add the same to systemctl. Dedup options, letting the last one wins in case of duplicates, so that 'systemctl kexec --reuse-cmdline' can be chained many times without continuosly expanding the cmdline with duplicates from the boot entry. --- diff --git a/man/systemctl.xml b/man/systemctl.xml index ce485aee14f..122d58d66d0 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 748126ea14a..319ca134ead 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 ca8b218c2ce..39791c0a39d 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 a8702ad438e..ab0285289bc 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 99244de7674..ef90e62a000 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 e157ab12f06..8f05bdb46e5 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;