nixos/stage-1-systemd: Handover between the systemds directly

This commit is contained in:
Janne Heß 2022-04-04 12:57:32 +01:00
parent adab6ce552
commit 3df2691e6b
No known key found for this signature in database
GPG key ID: 69165158F05265DF
4 changed files with 151 additions and 51 deletions

View file

@ -55,11 +55,15 @@ let
substituteInPlace $out/dry-activate --subst-var out substituteInPlace $out/dry-activate --subst-var out
chmod u+x $out/activate $out/dry-activate chmod u+x $out/activate $out/dry-activate
unset activationScript dryActivationScript unset activationScript dryActivationScript
${pkgs.stdenv.shellDryRun} $out/activate
${pkgs.stdenv.shellDryRun} $out/dry-activate
${if config.boot.initrd.systemd.enable then ''
cp ${config.system.build.bootStage2} $out/prepare-root
substituteInPlace $out/prepare-root --subst-var-by systemConfig $out
ln -s "$systemd/lib/systemd/systemd" $out/init
'' else ''
cp ${config.system.build.bootStage2} $out/init cp ${config.system.build.bootStage2} $out/init
substituteInPlace $out/init --subst-var-by systemConfig $out substituteInPlace $out/init --subst-var-by systemConfig $out
''}
ln -s ${config.system.build.etc}/etc $out/etc ln -s ${config.system.build.etc}/etc $out/etc
ln -s ${config.system.path} $out/sw ln -s ${config.system.path} $out/sw

View file

@ -5,6 +5,7 @@ systemConfig=@systemConfig@
export HOME=/root PATH="@path@" export HOME=/root PATH="@path@"
if [ "${IN_NIXOS_SYSTEMD_STAGE1:-}" != true ]; then
# Process the kernel command line. # Process the kernel command line.
for o in $(</proc/cmdline); do for o in $(</proc/cmdline); do
case $o in case $o in
@ -28,6 +29,7 @@ echo
if [ -z "$container" ]; then if [ -z "$container" ]; then
mount -n -o remount,rw none / mount -n -o remount,rw none /
fi fi
fi
# Likewise, stage 1 mounts /proc, /dev and /sys, so if we don't have a # Likewise, stage 1 mounts /proc, /dev and /sys, so if we don't have a
@ -39,6 +41,12 @@ if [ ! -e /proc/1 ]; then
local options="$3" local options="$3"
local fsType="$4" local fsType="$4"
# We must not overwrite this mount because it's bind-mounted
# from stage 1's /run
if [ "${IN_NIXOS_SYSTEMD_STAGE1:-}" = true ] && [ "${mountPoint}" = /run ]; then
return
fi
install -m 0755 -d "$mountPoint" install -m 0755 -d "$mountPoint"
mount -n -t "$fsType" -o "$options" "$device" "$mountPoint" mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
} }
@ -46,7 +54,11 @@ if [ ! -e /proc/1 ]; then
fi fi
if [ "${IN_NIXOS_SYSTEMD_STAGE1:-}" = true ]; then
echo "booting system configuration ${systemConfig}"
else
echo "booting system configuration $systemConfig" > /dev/kmsg echo "booting system configuration $systemConfig" > /dev/kmsg
fi
# Make /nix/store a read-only bind mount to enforce immutability of # Make /nix/store a read-only bind mount to enforce immutability of
@ -68,6 +80,7 @@ if [ -n "@readOnlyStore@" ]; then
fi fi
if [ "${IN_NIXOS_SYSTEMD_STAGE1:-}" != true ]; then
# Use /etc/resolv.conf supplied by systemd-nspawn, if applicable. # Use /etc/resolv.conf supplied by systemd-nspawn, if applicable.
if [ -n "@useHostResolvConf@" ] && [ -e /etc/resolv.conf ]; then if [ -n "@useHostResolvConf@" ] && [ -e /etc/resolv.conf ]; then
resolvconf -m 1000 -a host </etc/resolv.conf resolvconf -m 1000 -a host </etc/resolv.conf
@ -87,6 +100,7 @@ else
mkdir -p /run/log mkdir -p /run/log
exec > >(tee -i /run/log/stage-2-init.log) 2>&1 exec > >(tee -i /run/log/stage-2-init.log) 2>&1
fi fi
fi
# Required by the activation script # Required by the activation script
@ -116,6 +130,9 @@ ln -sfn "$systemConfig" /run/booted-system
: >> /etc/machine-id : >> /etc/machine-id
# No need to restore the stdout/stderr streams we never redirected and
# especially no need to start systemd
if [ "${IN_NIXOS_SYSTEMD_STAGE1:-}" != true ]; then
# Reset the logging file descriptors. # Reset the logging file descriptors.
exec 1>&$logOutFd 2>&$logErrFd exec 1>&$logOutFd 2>&$logErrFd
exec {logOutFd}>&- {logErrFd}>&- exec {logOutFd}>&- {logErrFd}>&-
@ -124,3 +141,4 @@ exec {logOutFd}>&- {logErrFd}>&-
# Start systemd in a clean environment. # Start systemd in a clean environment.
echo "starting systemd..." echo "starting systemd..."
exec @systemdExecutable@ "$@" exec @systemdExecutable@ "$@"
fi

View file

@ -445,6 +445,67 @@ in {
'')]; '')];
services."systemd-makefs@".unitConfig.IgnoreOnIsolate = true; services."systemd-makefs@".unitConfig.IgnoreOnIsolate = true;
services."systemd-growfs@".unitConfig.IgnoreOnIsolate = true; services."systemd-growfs@".unitConfig.IgnoreOnIsolate = true;
services.initrd-nixos-activation = {
after = [ "initrd-fs.target" ];
requiredBy = [ "initrd.target" ];
unitConfig.AssertPathExists = "/etc/initrd-release";
serviceConfig.Type = "oneshot";
description = "NixOS Activation";
script = /* bash */ ''
set -uo pipefail
export PATH="/bin:${cfg.package.util-linux}/bin"
# Figure out what closure to boot
closure=
for o in $(< /proc/cmdline); do
case $o in
init=*)
IFS== read -r -a initParam <<< "$o"
closure="$(dirname "''${initParam[1]}")"
;;
esac
done
# Sanity check
if [ -z "''${closure:-}" ]; then
echo 'No init= parameter on the kernel command line' >&2
exit 1
fi
# If we are not booting a NixOS closure (e.g. init=/bin/sh),
# we don't know what root to prepare so we don't do anything
if ! [ -x "/sysroot$closure/prepare-root" ]; then
echo "NEW_INIT=''${initParam[1]}" > /etc/switch-root.conf
echo "$closure does not look like a NixOS installation - not activating"
exit 0
fi
echo 'NEW_INIT=' > /etc/switch-root.conf
# We need to propagate /run for things like /run/booted-system
# and /run/current-system.
mkdir -p /sysroot/run
mount --bind /run /sysroot/run
# Initialize the system
export IN_NIXOS_SYSTEMD_STAGE1=true
exec chroot /sysroot $closure/prepare-root
'';
};
# This will either call systemctl with the new init as the last parameter (which
# is the case when not booting a NixOS system) or with an empty string, causing
# systemd to bypass its verification code that checks whether the next file is a systemd
# and using its compiled-in value
services.initrd-switch-root.serviceConfig = {
EnvironmentFile = "-/etc/switch-root.conf";
ExecStart = [
""
''systemctl --no-block switch-root /sysroot "''${NEW_INIT}"''
];
};
}; };
}; };
} }

View file

@ -14,6 +14,23 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
testScript = '' testScript = ''
import subprocess import subprocess
with subtest("handover to stage-2 systemd works"):
machine.wait_for_unit("multi-user.target")
machine.succeed("systemd-analyze | grep -q '(initrd)'") # direct handover
machine.succeed("touch /testfile") # / is writable
machine.fail("touch /nix/store/testfile") # /nix/store is not writable
# Special filesystems are mounted by systemd
machine.succeed("[ -e /run/booted-system ]") # /run
machine.succeed("[ -e /sys/class ]") # /sys
machine.succeed("[ -e /dev/null ]") # /dev
machine.succeed("[ -e /proc/1 ]") # /proc
# stage-2-init mounted more special filesystems
machine.succeed("[ -e /dev/shm ]") # /dev/shm
machine.succeed("[ -e /dev/pts/ptmx ]") # /dev/pts
machine.succeed("[ -e /run/keys ]") # /run/keys
with subtest("growfs works"):
oldAvail = machine.succeed("df --output=avail / | sed 1d") oldAvail = machine.succeed("df --output=avail / | sed 1d")
machine.shutdown() machine.shutdown()