<para>Note that <option>--boot</option> is the default mode of operation if the
<filename>systemd-nspawn@.service</filename> template unit file is used.</para>
+
+ <para>When <option>--boot</option> is used, the passed parameters are processed the same way the
+ kernel processes its command line before handing it to PID 1: any <literal>KEY=VALUE</literal>
+ assignment whose <literal>KEY</literal> does not contain a <literal>.</literal> is exported as
+ an environment variable for PID 1 (with <literal>-</literal> in the key replaced by
+ <literal>_</literal>, as if specified via <option>--setenv=</option>), while all other
+ parameters (including <literal>systemd.*=</literal> assignments) are passed as arguments to the
+ init program.</para>
</listitem>
</varlistentry>
return 0;
}
+static int split_boot_parameters(void) {
+ _cleanup_strv_free_ char **kept = NULL;
+ int r;
+
+ /* When the kernel hands the command line to PID 1, any KEY=VALUE assignment whose KEY does not
+ * contain a '.' is exported as an environment variable (with '-' replaced by '_'), rather than
+ * passed as an argument. Mimic the same split here so users can pass kernel-cmdline-style
+ * arguments after the container path and get the behavior they'd get on a real boot. */
+
+ if (arg_start_mode != START_BOOT)
+ return 0;
+
+ STRV_FOREACH(p, arg_parameters) {
+ _cleanup_free_ char *key = NULL, *value = NULL;
+
+ if (split_pair(*p, "=", &key, &value) >= 0 && !strchr(key, '.')) {
+ string_replace_char(key, '-', '_');
+
+ if (env_name_is_valid(key) && env_value_is_valid(value)) {
+ r = strv_env_assign(&arg_setenv, key, value);
+ if (r < 0)
+ return log_error_errno(r, "Cannot assign environment variable: %m");
+
+ arg_settings_mask |= SETTING_ENVIRONMENT;
+ continue;
+ }
+ }
+
+ r = strv_extend(&kept, *p);
+ if (r < 0)
+ return log_oom();
+ }
+
+ strv_free_and_replace(arg_parameters, kept);
+ return 0;
+}
+
static int verify_network_interfaces_initialized(void) {
int r;
r = test_network_interfaces_initialized(arg_network_interfaces);
if (r < 0)
goto finish;
+ r = split_boot_parameters();
+ if (r < 0)
+ goto finish;
+
r = resolve_network_interface_names(arg_network_interfaces);
if (r < 0)
goto finish;
rm -fr "$root" "$journal_dir"
}
+testcase_boot_param_split() {
+ local root outdir
+
+ root="$(mktemp -d /var/lib/machines/TEST-13-NSPAWN.boot-param-split.XXX)"
+ outdir="$(mktemp -d)"
+ create_dummy_container "$root"
+
+ # Replace the init binary with a stub that records the argv and environment nspawn passes to it,
+ # so we can verify that kernel-cmdline-style KEY=VALUE arguments are split between PID 1's
+ # environment and argv the same way the kernel splits them.
+ cat >"$root/usr/lib/systemd/systemd" <<'EOF'
+#!/bin/bash
+set -e
+printf '%s\n' "$@" >/output/argv
+env >/output/env
+EOF
+ chmod +x "$root/usr/lib/systemd/systemd"
+
+ # Cover the assignments that should land in env (FOO=bar, baz-qux=hello → baz_qux), the
+ # dotted assignments that should stay as argv (systemd.unit=…, some.thing=…), and the malformed
+ # entries that look env-like but must also stay as argv: empty key (=value), key starting with
+ # a digit (123=foo), key with characters that aren't valid in an env var name (foo!=bar).
+ systemd-nspawn --register=no \
+ --directory="$root" \
+ --bind="$outdir:/output" \
+ --boot \
+ FOO=bar baz-qux=hello systemd.unit=foo.target some.thing=yes plain-arg \
+ =empty-key 123=leading-digit 'foo!=bad-char'
+
+ diff <(printf 'systemd.unit=foo.target\nsome.thing=yes\nplain-arg\n=empty-key\n123=leading-digit\nfoo!=bad-char\n') "$outdir/argv"
+ grep '^FOO=bar$' >/dev/null "$outdir/env"
+ grep '^baz_qux=hello$' >/dev/null "$outdir/env"
+ (! grep -E '^(systemd\.unit|some\.thing)=' >/dev/null "$outdir/env")
+ (! grep -E '^(FOO|baz_qux)=' >/dev/null "$outdir/argv")
+ (! grep -E '^(123|foo!?)=' >/dev/null "$outdir/env")
+ (! grep -E '^=' >/dev/null "$outdir/env")
+
+ rm -fr "$root" "$outdir"
+}
+
testcase_cap_net_bind_service() {
local root