<para>When used with <command>kexec</command>, 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
- <command>kexec</command> above). This string is appended verbatim, separated from the existing
- options by a single space. <command>systemctl kexec</command> will fail if this option is specified
- when a kexec kernel is already loaded.</para>
+ <command>kexec</command> 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. <command>systemctl kexec</command> will fail if this
+ option is specified when a kexec kernel is already loaded.</para>
+
+ <para>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
+ <option>--kernel-cmdline-reuse</option> is used), and finally the strings specified with
+ <option>--kernel-cmdline=</option>. 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 <literal>foo=bar</literal> and
+ <literal>foo=quux</literal> are both kept, as they are not identical (the latter takes effect, as
+ the kernel uses the last assignment of a given parameter).</para>
<xi:include href="version-info.xml" xpointer="v261"/>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--kernel-cmdline-reuse</option></term>
+
+ <listitem>
+ <para>Like <option>--kernel-cmdline=</option>, 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 <option>--kernel-cmdline=</option>, the reused command line is added to the command line
+ taken from the boot loader entry, and <command>systemctl kexec</command> will fail if this option
+ is specified when a kexec kernel is already loaded. This option may be combined with
+ <option>--kernel-cmdline=</option>; see <option>--kernel-cmdline=</option> above for the order in
+ which the various command line sources are combined and deduplicated.</para>
+
+ <xi:include href="version-info.xml" xpointer="v262"/>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--plain</option></term>
--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'
'--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'
#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"
#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;
}
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",
#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"
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;
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);
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;
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;