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.
This commit is contained in:
Luca Boccassi
2026-06-22 14:02:52 +01:00
parent f8a1943758
commit ee7c04873e
6 changed files with 98 additions and 7 deletions
+32 -3
View File
@@ -2795,14 +2795,43 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<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>
+1 -1
View File
@@ -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'
+1
View File
@@ -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'
+38 -2
View File
@@ -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",
+25 -1
View File
@@ -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;
+1
View File
@@ -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;