update to nixos-24.05,

make installation in VM more versatile,
allow imports in host config file based on `name`
This commit is contained in:
Niklas Gollenstede 2024-05-31 22:41:35 +02:00
parent d0ba074777
commit 65c1691644
12 changed files with 125 additions and 91 deletions

View File

@ -76,6 +76,7 @@
"gdisk", // program "gdisk", // program
"getsize64", // cli arg "getsize64", // cli arg
"getty", // program "getty", // program
"github", // name
"gnugrep", // package "gnugrep", // package
"gnused", // package "gnused", // package
"gollenstede", // name "gollenstede", // name
@ -112,6 +113,7 @@
"mountpoint", // program / function "mountpoint", // program / function
"msize", // option "msize", // option
"mtab", // linux "mtab", // linux
"nameserver", // concat
"namespacing", // word "namespacing", // word
"netdev", // cli arg "netdev", // cli arg
"niklas", // name "niklas", // name
@ -137,6 +139,7 @@
"optimise", // B/E "optimise", // B/E
"ostype", // virtual box "ostype", // virtual box
"overlayed", // word "overlayed", // word
"overridable", // word
"ovmf", // package "ovmf", // package
"partlabel", // linux "partlabel", // linux
"partprobe", // program / function "partprobe", // program / function
@ -162,6 +165,7 @@
"reexec", // option "reexec", // option
"refreservation", // zfs "refreservation", // zfs
"relatime", // mount option "relatime", // mount option
"resolv", // abbr
"rootfs", // linux "rootfs", // linux
"rpool", // zfs "rpool", // zfs
"sandboxing", // word "sandboxing", // word

Binary file not shown.

View File

@ -2,7 +2,7 @@
"Fully automated NixOS CLI installer" "Fully automated NixOS CLI installer"
); inputs = { ); inputs = {
nixpkgs = { url = "github:NixOS/nixpkgs/nixos-23.11"; }; nixpkgs = { url = "github:NixOS/nixpkgs/nixos-24.05"; };
functions = { url = "github:NiklasGollenstede/nix-functions"; inputs.nixpkgs.follows = "nixpkgs"; }; functions = { url = "github:NiklasGollenstede/nix-functions"; inputs.nixpkgs.follows = "nixpkgs"; };
config.url = "github:NiklasGollenstede/nixos-installer?dir=example/defaultConfig"; # "path:./example/defaultConfig"; # (The latter only works on each host after using this flake directly (not as dependency or another flake). The former effectively points to the last commit, i.e. it takes two commits to apply changes to the default config.) config.url = "github:NiklasGollenstede/nixos-installer?dir=example/defaultConfig"; # "path:./example/defaultConfig"; # (The latter only works on each host after using this flake directly (not as dependency or another flake). The former effectively points to the last commit, i.e. it takes two commits to apply changes to the default config.)

View File

@ -1,7 +1,6 @@
dirname: inputs@{ self, nixpkgs, functions, ...}: let dirname: inputs@{ self, nixpkgs, functions, ...}: let
inherit (nixpkgs) lib; inherit (nixpkgs) lib;
inherit (functions.lib) extractBashFunction forEachSystem getModulesFromInputs getNixFiles getOverlaysFromInputs importWrapped mapMerge mapMergeUnique mergeAttrsUnique substituteImplicit; inherit (functions.lib) forEachSystem getModulesFromInputs getNixFiles getOverlaysFromInputs importWrapped mapMerge mapMergeUnique mergeAttrsUnique; # trace;
setup-scripts = (import "${dirname}/setup-scripts" "${dirname}/setup-scripts" inputs);
inherit (inputs.config.rename) installer; preface' = inputs.config.rename.preface; inherit (inputs.config.rename) installer; preface' = inputs.config.rename.preface;
getModuleConfig = module: inputs: args: if builtins.isFunction module then ( getModuleConfig = module: inputs: args: if builtins.isFunction module then (
@ -33,8 +32,12 @@ in rec {
#system = null; # (This actually does nothing more than setting »config.nixpkgs.system« (which is the same as »config.nixpkgs.buildPlatform.system«) and can be null/unset here.) #system = null; # (This actually does nothing more than setting »config.nixpkgs.system« (which is the same as »config.nixpkgs.buildPlatform.system«) and can be null/unset here.)
modules = (nixosArgs.modules or [ ]) ++ [ { imports = [ # Anything specific to only this evaluation of the module tree should go here. modules = (nixosArgs.modules or [ ]) ++ [ { imports = [ # Anything specific to only this evaluation of the module tree should go here.
(if (builtins.isPath mainModule) || (builtins.isString mainModule) then (importWrapped inputs mainModule).module else mainModule) (let
{ _module.args.name = lib.mkOverride 99 name; } # (specialisations can somehow end up with the name »configuration«, which is very incorrect) module = if (builtins.isPath mainModule) || (builtins.isString mainModule) then (importWrapped inputs mainModule).module else mainModule;
bindName = func: lib.setFunctionArgs (args: func (args // { inherit name; })) (builtins.removeAttrs (lib.functionArgs func) [ "name" ]);
bindToModule = module: if lib.isFunction module then bindName module else if module?imports then module // { imports = map bindToModule module.imports; } else module;
in bindToModule module) # ensure that in the main module, the "name" parameter is available during the import stage already
{ _module.args.name = lib.mkOverride 0 name; } # (specialisations can somehow end up with the name »configuration«, which is very incorrect)
{ networking.hostName = name; } { networking.hostName = name; }
]; _file = "${dirname}/nixos.nix#modules"; } ]; ]; _file = "${dirname}/nixos.nix#modules"; } ];
@ -101,8 +104,8 @@ in rec {
# Builds a system of NixOS hosts and exports them, plus »apps« and »devShells« to manage them, as flake outputs. # Builds a system of NixOS hosts and exports them, plus »apps« and »devShells« to manage them, as flake outputs.
# All arguments are optional, as long as the default can be derived from the other arguments as passed. # All arguments are optional, as long as the default can be derived from the other arguments as passed.
mkSystemsFlake = args@{ mkSystemsFlake = lib.makeOverridable (args@{
# An attrset of imported Nix flakes, for example the argument(s) passed to the flake »outputs« function. All other arguments are optional (and have reasonable defaults) if this is provided and contains »self« and the standard »nixpkgs«. This is also the second argument passed to the individual host's top level config files. # An attrset of imported Nix flakes, for example the argument(s) passed to the flake »outputs« function. All other arguments are optional (and have reasonable defaults) if this is provided and contains »self« and the standard »nixpkgs«. This is also the second argument passed to the individual hosts' top level config files.
inputs ? { }, inputs ? { },
# Arguments »{ files, dir, exclude, }« to »mkNixosConfigurations«, see there for details. May also be a list of those attrsets, in which case those multiple sets of hosts will be built separately by »mkNixosConfigurations«, allowing for separate sets of »peers« passed to »mkNixosConfiguration«. Each call will receive all other arguments, and the resulting sets of hosts will be merged. # Arguments »{ files, dir, exclude, }« to »mkNixosConfigurations«, see there for details. May also be a list of those attrsets, in which case those multiple sets of hosts will be built separately by »mkNixosConfigurations«, allowing for separate sets of »peers« passed to »mkNixosConfiguration«. Each call will receive all other arguments, and the resulting sets of hosts will be merged.
hosts ? ({ dir = "${getFlakeDir inputs.self "Can't determine flake dir from »inputs.self«. Supply »mkSystemsFlake.hosts.dir« explicitly!"}/hosts"; exclude = [ ]; }), hosts ? ({ dir = "${getFlakeDir inputs.self "Can't determine flake dir from »inputs.self«. Supply »mkSystemsFlake.hosts.dir« explicitly!"}/hosts"; exclude = [ ]; }),
@ -141,25 +144,18 @@ in rec {
inherit nixosConfigurations; inherit nixosConfigurations;
} // (forEachSystem setupPlatforms (buildSystem: let } // (forEachSystem setupPlatforms (buildSystem: let
pkgs = (import inputs.nixpkgs { inherit overlays; system = buildSystem; }); pkgs = (import inputs.nixpkgs { inherit overlays; system = buildSystem; });
tools = lib.unique (map (p: p.outPath) (lib.filter lib.isDerivation pkgs.stdenv.allowedRequisites));
in rec { in rec {
apps = lib.mapAttrs (name: system: rec { type = "app"; derivation = writeSystemScripts { inherit name pkgs system; }; program = "${derivation}"; }) nixosConfigurations; apps = lib.mapAttrs (name: system: rec { type = "app"; derivation = writeSystemScripts { inherit name pkgs system; }; program = "${derivation}"; }) nixosConfigurations;
# dummy that just pulls in all system builds # dummy that just pulls in all system builds
packages = let all-systems = pkgs.runCommandLocal "all-systems" { } '' packages = let all-systems = pkgs.runCommandLocal "all-systems" { } ''
${''
mkdir -p $out/systems mkdir -p $out/systems
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: "ln -sT ${system.config.system.build.toplevel} $out/systems/${getName name}") nixosConfigurations)} ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: "ln -sT ${system.config.system.build.toplevel} $out/systems/${getName name}") nixosConfigurations)}
''}
${''
mkdir -p $out/scripts mkdir -p $out/scripts
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: "ln -sT ${apps.${name}.program} $out/scripts/${getName name}") nixosConfigurations)} ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: "ln -sT ${apps.${name}.program} $out/scripts/${getName name}") nixosConfigurations)}
''}
${lib.optionalString (inputs != { }) ''
mkdir -p $out/inputs mkdir -p $out/inputs
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: { outPath, ... }: "ln -sT ${outPath} $out/inputs/${name}") inputs)} ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: { outPath, ... }: "ln -sT ${outPath} $out/inputs/${name}") inputs)}
''}
''; in { inherit all-systems; } // (lib.optionalAttrs asDefaultPackage { default = all-systems ; }); ''; in { inherit all-systems; } // (lib.optionalAttrs asDefaultPackage { default = all-systems ; });
checks.all-systems = packages.all-systems; checks.all-systems = packages.all-systems;
@ -169,7 +165,7 @@ in rec {
apps = mapMergeUnique (k: v: { ${renameOutputs k} = v; }) outputs.apps.${buildSystem}; apps = mapMergeUnique (k: v: { ${renameOutputs k} = v; }) outputs.apps.${buildSystem};
packages.${renameOutputs "all-systems"} = outputs.packages.${buildSystem}.all-systems; packages.${renameOutputs "all-systems"} = outputs.packages.${buildSystem}.all-systems;
checks.${renameOutputs "all-systems"} = outputs.checks.${buildSystem}.all-systems; checks.${renameOutputs "all-systems"} = outputs.checks.${buildSystem}.all-systems;
})); })));
# This makes the »./setup-scripts/*« callable from the command line: # This makes the »./setup-scripts/*« callable from the command line:
writeSystemScripts = { writeSystemScripts = {

View File

@ -8,7 +8,7 @@ declare-flag install-system skip-formatting "" "Skip partitioning, formatting, a
## Prepares the disks of the target system for the copying of files. ## Prepares the disks of the target system for the copying of files.
function do-disk-setup { # 1: diskPaths function do-disk-setup { # 1: diskPaths
ensure-disks "$1" || return ensure-disks || return
export mnt=/tmp/nixos-install-@{config.networking.hostName} && mkdir -p "$mnt" && prepend_trap "rmdir $mnt" EXIT || return # »mnt=/run/user/0/...« would be more appropriate, but »nixos-install« does not like the »700« permissions on »/run/user/0« export mnt=/tmp/nixos-install-@{config.networking.hostName} && mkdir -p "$mnt" && prepend_trap "rmdir $mnt" EXIT || return # »mnt=/run/user/0/...« would be more appropriate, but »nixos-install« does not like the »700« permissions on »/run/user/0«
@ -51,14 +51,14 @@ function do-disk-setup { # 1: diskPaths
declare-flag install-system image-owner "" "When using image files, »chown« them to this »owner[:group]« before the installation." declare-flag install-system image-owner "" "When using image files, »chown« them to this »owner[:group]« before the installation."
## Parses and expands »diskPaths« to ensure that a disk or image exists for each »config.setup.disks.devices«, creates and loop-mounts images for non-/dev/ paths, and checks whether physical device sizes match. ## Parses and expands »diskPaths« to ensure that a disk or image exists for each »config.setup.disks.devices«, creates and loop-mounts images for non-/dev/ paths, and checks whether physical device sizes match.
function ensure-disks { # 1: diskPaths, 2?: skipLosetup function ensure-disks {
declare -g -A blockDevs=( ) # this ends up in the caller's scope declare -g -A blockDevs=( ) # this ends up in the caller's scope
if [[ $1 == */ ]] ; then if [[ ${args[disks]} == */ ]] ; then
mkdir -p "$1" mkdir -p "${args[disks]}"
for name in "@{!config.setup.disks.devices[@]}" ; do blockDevs[$name]=${1}${name}.img ; done for name in "@{!config.setup.disks.devices[@]}" ; do blockDevs[$name]=${args[disks]}${name}.img ; done
else else
local path ; for path in ${1//:/ } ; do local path ; for path in ${args[disks]//:/ } ; do
local name=${path/=*/} ; if [[ $name != "$path" ]] ; then path=${path/$name=/} ; else name=primary ; fi local name=${path/=*/} ; if [[ $name != "$path" ]] ; then path=${path/$name=/} ; else name=primary ; fi
if [[ ${blockDevs[$name]:-} ]] ; then echo "Path for block device $name specified more than once. Duplicate definition: $path" 1>&2 ; \return 1 ; fi if [[ ${blockDevs[$name]:-} ]] ; then echo "Path for block device $name specified more than once. Duplicate definition: $path" 1>&2 ; \return 1 ; fi
blockDevs[$name]=$path blockDevs[$name]=$path
@ -73,7 +73,7 @@ function ensure-disks { # 1: diskPaths, 2?: skipLosetup
local outFile=${blockDevs[$name]} && local outFile=${blockDevs[$name]} &&
install -m 640 -T /dev/null "$outFile" && truncate -s "${disk[size]}" "$outFile" || return install -m 640 -T /dev/null "$outFile" && truncate -s "${disk[size]}" "$outFile" || return
if [[ ${args[image-owner]:-} ]] ; then chown "${args[image-owner]}" "$outFile" || return ; fi if [[ ${args[image-owner]:-} ]] ; then chown "${args[image-owner]}" "$outFile" || return ; fi
if [[ ${2:-} ]] ; then continue ; fi if [[ ${arg_skipLosetup:-} ]] ; then continue ; fi
blockDevs[$name]=$( @{native.util-linux}/bin/losetup --show -f "$outFile" ) && prepend_trap "@{native.util-linux}/bin/losetup -d '${blockDevs[$name]}'" EXIT || return blockDevs[$name]=$( @{native.util-linux}/bin/losetup --show -f "$outFile" ) && prepend_trap "@{native.util-linux}/bin/losetup -d '${blockDevs[$name]}'" EXIT || return
else else
local size=$( @{native.util-linux}/bin/blockdev --getsize64 "${blockDevs[$name]}" || : ) ; local waste=$(( size - ${disk[size]} )) local size=$( @{native.util-linux}/bin/blockdev --getsize64 "${blockDevs[$name]}" || : ) ; local waste=$(( size - ${disk[size]} ))

View File

@ -3,12 +3,9 @@
# NixOS Installation # NixOS Installation
## ##
declare-command install-system diskPaths << 'EOD' declare-command install-system '[--disks=]diskPaths' << 'EOD'
This command installs a NixOS system to local disks or image files. This command installs a NixOS system to local disks or image files.
It gets all the information it needs from the system's NixOS configuration -- except for the path(s) of the target disk(s) / image file(s). It gets all the information it needs from the system's NixOS configuration -- except for the path(s) of the target disk(s) / image file(s); see the »--disks=« flag.
If »diskPaths« points to something in »/dev/«, then it is directly formatted and written to as block device, otherwise »diskPaths« is (re-)created as raw image and then used as loop device.
For hosts that install to multiple disks, pass a :-separated list of »<disk-name>=<path>« pairs (the name may be omitted only for the "default" disk).
Since the installation needs to format and mount (image files as) disks, it needs some way of elevating permissions. It can: Since the installation needs to format and mount (image files as) disks, it needs some way of elevating permissions. It can:
* be run as »root«, requiring Nix to be installed system-wide / for root, * be run as »root«, requiring Nix to be installed system-wide / for root,
@ -25,10 +22,13 @@ What the installation does is defined solely by the target host's NixOS configur
The "Installation" section of each host's documentation should contain host specific details, if any. The "Installation" section of each host's documentation should contain host specific details, if any.
Various »FLAG«s below affect how the installation is performed (in VM, verbosity, debugging, ...). Various »FLAG«s below affect how the installation is performed (in VM, verbosity, debugging, ...).
EOD EOD
declare-flag install-system disks "diskPaths" "The disk(s) (to be) used by this system installation.
If »diskPaths« points to something in »/dev/«, then it is directly used as block device, otherwise »diskPaths« is (re-)created as raw image file and then used as loop device.
For hosts that install to multiple disks, pass a :-separated list of »<disk-name>=<path>« pairs (the name may be omitted only for the "default" disk)."
function install-system {( # 1: diskPaths function install-system {( # 1: diskPaths
trap - EXIT # start with empty traps for sub-shell trap - EXIT # start with empty traps for sub-shell
prepare-installer "$@" || exit prepare-installer "$@" || exit
do-disk-setup "$1" || exit do-disk-setup || exit
install-system-to $mnt || exit install-system-to $mnt || exit
)} )}
@ -42,15 +42,15 @@ declare-flag install-system no-vm "" "Never perform the installation in a VM. Fa
## Does some argument validation, performs some sanity checks, includes a hack to make installation work when nix isn't installed for root, and runs the installation in qemu (if requested). ## Does some argument validation, performs some sanity checks, includes a hack to make installation work when nix isn't installed for root, and runs the installation in qemu (if requested).
function prepare-installer { # 1: diskPaths function prepare-installer { # 1: diskPaths
: ${1:?"The first positional argument must specify the path(s) to the disk(s) and/or image file(s) to install to"} if [[ ! ${args[disks]:-} ]] ; then args[disks]=${1:?"The disks flag or the first positional argument must specify the path(s) to the disk(s) and/or image file(s) to install to"} ; shift ; fi
umask g-w,o-w # Ensure that files created without explicit permissions are not writable for group and other (0022). umask g-w,o-w # Ensure that files created without explicit permissions are not writable for group and other.
if [[ "$(id -u)" != '0' ]] ; then if [[ "$(id -u)" != '0' ]] ; then
if [[ ! ${args[no-vm]:-} ]] ; then reexec-in-qemu "$@" || return ; \exit 0 ; fi if [[ ! ${args[no-vm]:-} ]] ; then exec-in-qemu install-system || return ; \exit 0 ; fi
echo 'Script must be run as root or in qemu (without »--no-vm«).' 1>&2 ; \return 1 echo 'Script must be run as root or in qemu (without »--no-vm«).' 1>&2 ; \return 1
fi fi
if [[ ${args[vm]:-} ]] ; then reexec-in-qemu "$@" || return ; \exit 0 ; fi if [[ ${args[vm]:-} ]] ; then exec-in-qemu install-system || return ; \exit 0 ; fi
if [[ -e "/run/keystore-@{config.networking.hostName!hashString.sha256:0:8}" ]] ; then echo "Keystore »/run/keystore-@{config.networking.hostName!hashString.sha256:0:8}/« is already open. Close it and remove the mountpoint before running the installer." 1>&2 ; \return 1 ; fi if [[ -e "/run/keystore-@{config.networking.hostName!hashString.sha256:0:8}" ]] ; then echo "Keystore »/run/keystore-@{config.networking.hostName!hashString.sha256:0:8}/« is already open. Close it and remove the mountpoint before running the installer." 1>&2 ; \return 1 ; fi
@ -62,7 +62,7 @@ function prepare-installer { # 1: diskPaths
if @{native.zfs}/bin/zfs get -o value -H name "$poolName" &>/dev/null ; then echo "ZFS pool »$poolName« is already imported. Export the pool before running the installer." 1>&2 ; \return 1 ; fi if @{native.zfs}/bin/zfs get -o value -H name "$poolName" &>/dev/null ; then echo "ZFS pool »$poolName« is already imported. Export the pool before running the installer." 1>&2 ; \return 1 ; fi
done done
if [[ ${SUDO_USER:-} && ! $( PATH=$hostPath which nix 2>/dev/null ) && $( PATH=$hostPath which su 2>/dev/null ) ]] ; then # use Nix as the user who called this script, as Nix may not be set up for root if [[ ${SUDO_USER:-} && ! $( PATH=$hostPath which nix 2>/dev/null ) && $( PATH=$hostPath which su 2>/dev/null ) ]] ; then # use Nix as the user who called this script, if Nix is not be set up for root
function nix {( set +x ; declare -a args=("$@") ; PATH=$hostPath su - "$SUDO_USER" -s "@{native.bashInteractive!getExe}" -c "$(declare -p args)"' ; nix "${args[@]}"' )} function nix {( set +x ; declare -a args=("$@") ; PATH=$hostPath su - "$SUDO_USER" -s "@{native.bashInteractive!getExe}" -c "$(declare -p args)"' ; nix "${args[@]}"' )}
else # use Nix by absolute path, as it won't be on »$PATH« else # use Nix by absolute path, as it won't be on »$PATH«
PATH=$PATH:@{native.nix}/bin PATH=$PATH:@{native.nix}/bin
@ -75,13 +75,18 @@ function prepare-installer { # 1: diskPaths
declare-flag install-system vm-shared "dir-path" "When installing inside the VM, specifies a host path that is read-write mounted at »/tmp/shared« inside the VM." declare-flag install-system vm-shared "dir-path" "When installing inside the VM, specifies a host path that is read-write mounted at »/tmp/shared« inside the VM."
declare-flag install-system vm-args "qemu-args" "When installing inside the VM, extra arguments to pass to qemu." declare-flag install-system vm-args "qemu-args" "When installing inside the VM, extra arguments to pass to qemu."
## Re-executes the current system's installation in a qemu VM. ## (Re-)executes the current system's script in a qemu VM.
function reexec-in-qemu { function exec-in-qemu { # 1: entry, ...: argv
qemu=( ) ; apply-vm-args
args[vm]='' ; args[no-vm]=1
if [[ ${args[disks]:-} ]] ; then
# (not sure whether this works for block devices) # (not sure whether this works for block devices)
ensure-disks "$1" 1 || return arg_skipLosetup=1 ensure-disks || return
qemu=( -m 3072 ) ; declare -A qemuDevs=( ) args[disks]=''
local index=2 ; local name ; for name in "${!blockDevs[@]}" ; do local index=2 # 1/a is used by the (unused) root disk
local name ; for name in "${!blockDevs[@]}" ; do
#if [[ ${blockDevs[$name]} != /dev/* ]] ; then #if [[ ${blockDevs[$name]} != /dev/* ]] ; then
qemu+=( # not sure how correct the interpretations of the command are qemu+=( # not sure how correct the interpretations of the command are
-drive format=raw,file="$( realpath "${blockDevs[$name]}" )",media=disk,if=none,index=${index},id=drive${index} # create the disk drive, without attaching it, name it driveX -drive format=raw,file="$( realpath "${blockDevs[$name]}" )",media=disk,if=none,index=${index},id=drive${index} # create the disk drive, without attaching it, name it driveX
@ -89,14 +94,13 @@ function reexec-in-qemu {
#-device ide-hd,drive=drive${index},bus=ahci${index}.${index} # attach IDE?! disk driveX as device X on bus »ahciX« #-device ide-hd,drive=drive${index},bus=ahci${index}.${index} # attach IDE?! disk driveX as device X on bus »ahciX«
-device virtio-blk-pci,drive=drive${index},disable-modern=on,disable-legacy=off # alternative to the two lines above (implies to be faster, but seems to require guest drivers) -device virtio-blk-pci,drive=drive${index},disable-modern=on,disable-legacy=off # alternative to the two lines above (implies to be faster, but seems to require guest drivers)
) )
qemuDevs[$name]=/dev/vd$( printf "\x$(printf %x $(( index - 1 + 97 )) )" ) # a is used by the (unused) root disk args[disks]+="$name"=/dev/vd"$( printf "\x$(printf %x $(( index - 1 + 97 )) )" )": ; let index+=1
let index+=1
done done
args[disks]=${args[disks]%:}
fi
args[vm]='' ; args[no-vm]=1 newArgs=( ) ; (( $# == 0 )) || newArgs+=( "$@" )
newArgs=( ) ; for arg in "${!args[@]}" ; do newArgs+=( --"$arg"="${args[$arg]}" ) ; done for arg in "${!args[@]}" ; do newArgs+=( --"$arg"="${args[$arg]}" ) ; done
devSpec= ; for name in "${!qemuDevs[@]}" ; do devSpec+="$name"="${qemuDevs[$name]}": ; done
newArgs+=( ${devSpec%:} ) ; shift ; (( $# == 0 )) || args+=( "$@" ) # (( ${#argv[@]} > 1 )) && args+=( "${argv[@]:1}" )
#local output=@{inputs.self}'#'nixosConfigurations.@{config.installer.outputName:?}.config.system.build.vmExec #local output=@{inputs.self}'#'nixosConfigurations.@{config.installer.outputName:?}.config.system.build.vmExec
local output=@{config.system.build.vmExec.drvPath!unsafeDiscardStringContext} # this is more accurate, but also means another system needs to get evaluated every time local output=@{config.system.build.vmExec.drvPath!unsafeDiscardStringContext} # this is more accurate, but also means another system needs to get evaluated every time
@ -107,7 +111,7 @@ function reexec-in-qemu {
local scripts=$self ; if [[ @{pkgs.system} != "@{native.system}" ]] ; then local scripts=$self ; if [[ @{pkgs.system} != "@{native.system}" ]] ; then
scripts=$( build-lazy @{inputs.self}'#'apps.@{pkgs.system}.@{config.installer.outputName:?}.derivation ) || return scripts=$( build-lazy @{inputs.self}'#'apps.@{pkgs.system}.@{config.installer.outputName:?}.derivation ) || return
fi fi
local command="$scripts install-system $( printf '%q ' "${newArgs[@]}" ) || exit" local command="$scripts $( printf '%q ' "${newArgs[@]}" ) || exit"
local runInVm ; runInVm=$( build-lazy $output )/bin/run-@{config.system.name}-vm-exec || return local runInVm ; runInVm=$( build-lazy $output )/bin/run-@{config.system.name}-vm-exec || return
@ -119,10 +123,10 @@ function reexec-in-qemu {
function nixos-install-cmd {( # 1: mnt, 2: topLevel function nixos-install-cmd {( # 1: mnt, 2: topLevel
# »nixos-install« by default does some stateful things (see »--no-root-passwd« »--no-channel-copy«), builds and copies the system config, registers the system (»nix-env --profile /nix/var/nix/profiles/system --set $targetSystem«), and then calls »NIXOS_INSTALL_BOOTLOADER=1 nixos-enter -- $topLevel/bin/switch-to-configuration boot«, which is essentially the same as »NIXOS_INSTALL_BOOTLOADER=1 nixos-enter -- @{config.system.build.installBootLoader} $targetSystem«, i.e. the side effects of »nixos-enter« and then calling the bootloader-installer. # »nixos-install« by default does some stateful things (see »--no-root-passwd« »--no-channel-copy«), builds and copies the system config, registers the system (»nix-env --profile /nix/var/nix/profiles/system --set $targetSystem«), and then calls »NIXOS_INSTALL_BOOTLOADER=1 nixos-enter -- $topLevel/bin/switch-to-configuration boot«, which is essentially the same as »NIXOS_INSTALL_BOOTLOADER=1 nixos-enter -- @{config.system.build.installBootLoader} $targetSystem«, i.e. the side effects of »nixos-enter« and then calling the bootloader-installer.
#PATH=@{native.nix}/bin:$PATH:@{config.systemd.package}/bin TMPDIR=/tmp LC_ALL=C @{native.nixos-install-tools}/bin/nixos-install --system "$2" --no-root-passwd --no-channel-copy --root "$1" || exit # We did most of this, so just install the bootloader: #PATH=@{native.nix}/bin:$PATH:@{config.systemd.package}/bin TMPDIR=/tmp LC_ALL=C @{native.nixos-install-tools-no-doc}/bin/nixos-install --system "$2" --no-root-passwd --no-channel-copy --root "$1" || exit # We did most of this, so just install the bootloader:
export NIXOS_INSTALL_BOOTLOADER=1 # tells some bootloader installers (systemd & grub) to not skip parts of the installation export NIXOS_INSTALL_BOOTLOADER=1 # tells some bootloader installers (systemd & grub) to not skip parts of the installation
LC_ALL=C PATH=@{native.busybox}/bin:$PATH:@{native.util-linux}/bin @{native.nixos-install-tools}/bin/nixos-enter --silent --root "$1" -c "source /etc/set-environment ; ${_set_x:-:} ; @{config.system.build.installBootLoader} $2" || exit LC_ALL=C PATH=@{native.busybox}/bin:$PATH:@{native.util-linux}/bin @{native.nixos-install-tools-no-doc}/bin/nixos-enter --silent --root "$1" -c "source /etc/set-environment ; ${_set_x:-:} ; @{config.system.build.installBootLoader} $2" || exit
# (newer versions of »mount« seem to be unable to do »--make-private« on »rootfs« (in the initrd), but busybox's mount still works) # (newer versions of »mount« seem to be unable to do »--make-private« on »rootfs« (in the initrd), but busybox's mount still works)
)} )}
@ -201,7 +205,7 @@ function install-system-to {( set -u # 1: mnt, 2?: topLevel
else else
( set +x ; echo "Installation done! This shell is in a chroot in the mounted system for inspection. Exiting the shell will unmount the system." 1>&2 ) ( set +x ; echo "Installation done! This shell is in a chroot in the mounted system for inspection. Exiting the shell will unmount the system." 1>&2 )
fi fi
LC_ALL=C PATH=@{native.busybox}/bin:$PATH:@{native.util-linux}/bin @{native.nixos-install-tools}/bin/nixos-enter --root $mnt -- /nix/var/nix/profiles/system/sw/bin/bash -c 'source /etc/set-environment ; NIXOS_INSTALL_BOOTLOADER=1 CHROOT_DIR="'"$mnt"'" mnt=/ exec "'"$self"'" bash' || exit # +o monitor LC_ALL=C PATH=@{native.busybox}/bin:$PATH:@{native.util-linux}/bin @{native.nixos-install-tools-no-doc}/bin/nixos-enter --root $mnt -- /nix/var/nix/profiles/system/sw/bin/bash -c 'source /etc/set-environment ; NIXOS_INSTALL_BOOTLOADER=1 CHROOT_DIR="'"$mnt"'" mnt=/ exec "'"$self"'" bash' || exit # +o monitor
fi fi
mkdir -p $mnt/var/lib/systemd/timesync && touch $mnt/var/lib/systemd/timesync/clock || true # save current time mkdir -p $mnt/var/lib/systemd/timesync && touch $mnt/var/lib/systemd/timesync/clock || true # save current time

View File

@ -57,8 +57,7 @@ declare-flag run-qemu efi "" "Treat the target system as EFI syste
declare-flag run-qemu efi-vars "path" "For »--efi« systems, path to a file storing the EFI variables. The default is in »XDG_RUNTIME_DIR«, i.e. it does not persist across host reboots." declare-flag run-qemu efi-vars "path" "For »--efi« systems, path to a file storing the EFI variables. The default is in »XDG_RUNTIME_DIR«, i.e. it does not persist across host reboots."
declare-flag run-qemu graphic "" "Open a graphical window even of the target system logs to serial and not (explicitly) TTY1." declare-flag run-qemu graphic "" "Open a graphical window even of the target system logs to serial and not (explicitly) TTY1."
declare-flag run-qemu install "[1|always]" "If any of the guest system's disk images does not exist, perform the its installation before starting the VM. If set to »always«, always install before starting the VM. With this flag set, »diskImages« defaults to paths in »/tmp/." declare-flag run-qemu install "[1|always]" "If any of the guest system's disk images does not exist, perform the its installation before starting the VM. If set to »always«, always install before starting the VM. With this flag set, »diskImages« defaults to paths in »/tmp/."
declare-flag run-qemu mem "num" "VM RAM in MiB (»qemu -m«)." declare-flag run-qemu no-kvm "" "Do not try to use (or complain about the unavailability of) KVM."
declare-flag run-qemu no-kvm "" "Do not rey to use (or complain about the unavailability of) KVM."
declare-flag run-qemu nat-fw "forwards" "Port forwards to the guest's NATed NIC. E.g: »--nat-fw=:8000-:8000,:8001-:8001,127.0.0.1:2022-:22«." declare-flag run-qemu nat-fw "forwards" "Port forwards to the guest's NATed NIC. E.g: »--nat-fw=:8000-:8000,:8001-:8001,127.0.0.1:2022-:22«."
declare-flag run-qemu no-nat "" "Do not provide a NATed NIC to the guest." declare-flag run-qemu no-nat "" "Do not provide a NATed NIC to the guest."
declare-flag run-qemu nic "type[,options]" "Create an additional network interface using the »-nic« flag. Automatically sets a decent »model« and a »mac« derived from »config.networking.hostId«. declare-flag run-qemu nic "type[,options]" "Create an additional network interface using the »-nic« flag. Automatically sets a decent »model« and a »mac« derived from »config.networking.hostId«.
@ -67,12 +66,9 @@ $ ... --nic=socket,listen=:4321 # once
$ ... --nic=socket,connect=:4321 # once $ ... --nic=socket,connect=:4321 # once
Example 2 (connect many VMs, unprivileged): Example 2 (connect many VMs, unprivileged):
$ nix shell nixpkgs#vde2 --command vde_switch -sock /tmp/vm-net $ nix shell nixpkgs#vde2 --command vde_switch -sock /tmp/vm-net
$ ... --nic=vde,sock=/tmp/vm-net # multiple times $ ... --nic=vde,sock=/tmp/vm-net # multiple times"
"
declare-flag run-qemu no-serial "" "Do not connect the calling terminal to a serial adapter the guest can log to and open a terminal on the guests serial, as would be the default if the guests logs to ttyS0." declare-flag run-qemu no-serial "" "Do not connect the calling terminal to a serial adapter the guest can log to and open a terminal on the guests serial, as would be the default if the guests logs to ttyS0."
declare-flag run-qemu share "decls" "Host dirs to make available as network shares for the guest, as space separated list of »name:host-path,options. E.g. »--share='foo:/home/user/foo,readonly=on bar:/tmp/bar«. In the VM hte share can be mounted with: »$ mount -t 9p -o trans=virtio -o version=9p2000.L -o msize=4194304 -o ro foo /foo«." declare-flag run-qemu share "decls" "Host dirs to make available as network shares for the guest, as space separated list of »name:host-path,options. E.g. »--share='foo:/home/user/foo,readonly=on bar:/tmp/bar«. In the VM hte share can be mounted with: »$ mount -t 9p -o trans=virtio -o version=9p2000.L -o msize=4194304 -o ro foo /foo«."
declare-flag run-qemu smp "num" "Number of guest CPU cores."
declare-flag run-qemu usb-port "path" "A physical USB port (or hub) to pass to the guest (e.g. a YubiKey for unlocking). Specified as »<bus>-<port>«, where bus and port refer to the physical USB port »/sys/bus/usb/devices/<bus>-<port>« (see »lsusb -tvv«). E.g.: »--usb-port=3-1.1.1.4«."
declare-flag run-qemu virtio-blk "" "Pass the system's disks/images as virtio disks, instead of using AHCI+IDE. Default iff »boot.initrd.availableKernelModules« includes »virtio_blk« (because it requires that driver)." declare-flag run-qemu virtio-blk "" "Pass the system's disks/images as virtio disks, instead of using AHCI+IDE. Default iff »boot.initrd.availableKernelModules« includes »virtio_blk« (because it requires that driver)."
function run-qemu { function run-qemu {
if [[ ${args[install]:-} && ! ${argv[0]:-} ]] ; then argv[0]=/tmp/nixos-vm/@{config.installer.outputName:-@{config.system.name}}/ ; fi if [[ ${args[install]:-} && ! ${argv[0]:-} ]] ; then argv[0]=/tmp/nixos-vm/@{config.installer.outputName:-@{config.system.name}}/ ; fi
@ -99,9 +95,6 @@ function run-qemu {
qemu+=( -machine type=virt ) # aarch64 has no default, but this seems good qemu+=( -machine type=virt ) # aarch64 has no default, but this seems good
fi ; qemu+=( -cpu max ) fi ; qemu+=( -cpu max )
qemu+=( -m ${args[mem]:-2048} )
if [[ ${args[smp]:-} ]] ; then qemu+=( -smp ${args[smp]} ) ; fi
if [[ @{config.virtualisation.useEFIBoot:-} || @{config.boot.loader.systemd-boot.enable} || ${args[efi]:-} ]] ; then # UEFI. Otherwise it boots SeaBIOS. if [[ @{config.virtualisation.useEFIBoot:-} || @{config.boot.loader.systemd-boot.enable} || ${args[efi]:-} ]] ; then # UEFI. Otherwise it boots SeaBIOS.
local ovmf ; ovmf=$( build-lazy @{pkgs.OVMF.drvPath!unsafeDiscardStringContext} fd ) || return local ovmf ; ovmf=$( build-lazy @{pkgs.OVMF.drvPath!unsafeDiscardStringContext} fd ) || return
#qemu+=( -bios ${ovmf}/FV/OVMF.fd ) # This works, but is a legacy fallback that stores the EFI vars in /NvVars on the EFI partition (which is really bad). #qemu+=( -bios ${ovmf}/FV/OVMF.fd ) # This works, but is a legacy fallback that stores the EFI vars in /NvVars on the EFI partition (which is really bad).
@ -159,18 +152,15 @@ function run-qemu {
qemu+=( -nic model=virtio-net-pci,mac=$mac,type="${args[nic]}" ) qemu+=( -nic model=virtio-net-pci,mac=$mac,type="${args[nic]}" )
fi fi
# To pass a USB device (e.g. a YubiKey for unlocking), add pass »--usb-port=${bus}-${port}«, where bus and port refer to the physical USB port »/sys/bus/usb/devices/${bus}-${port}« (see »lsusb -tvv«). E.g.: »--usb-port=3-1.1.1.4« apply-vm-args
if [[ ${args[usb-port]:-} ]] ; then local decl ; for decl in ${args[usb-port]//:/ } ; do
qemu+=( -usb -device usb-host,hostbus="${decl/-*/}",hostport="${decl/*-/}" )
done ; fi
if [[ ${args[install]:-} == 1 ]] ; then local disk ; for disk in "${disks[@]}" ; do if [[ ${args[install]:-} == 1 ]] ; then local disk ; for disk in "${disks[@]}" ; do
if [[ ! -e $disk ]] ; then args[install]=always ; fi if [[ ! -e $disk ]] ; then args[install]=always ; fi
done ; fi done ; fi
if [[ ${args[install]:-} == always ]] ; then if [[ ${args[install]:-} == always ]] && [[ ! ${args[dry-run]:-} ]] ; then (
local verbosity=--quiet ; if [[ ${args[trace]:-} ]] ; then verbosity=--trace ; fi ; if [[ ${args[debug]:-} ]] ; then verbosity=--debug ; fi if [[ ! ${args[trace]:-} ]] && [[! ${args[debug]:-} ]] ; then args[quiet]=1 ; fi
hostPath=${hostPath:-} ${args[dry-run]:+echo} "$self" install-system "$diskImages" $verbosity --no-inspect || return args[no-inspect]=1 ; install-system "$diskImages" || exit
fi ) || return ; fi
qemu+=( "${argv[@]}" ) qemu+=( "${argv[@]}" )
if [[ ${args[dry-run]:-} ]] ; then if [[ ${args[dry-run]:-} ]] ; then
@ -182,6 +172,18 @@ function run-qemu {
# https://askubuntu.com/questions/54814/how-can-i-ctrl-alt-f-to-get-to-a-tty-in-a-qemu-session # https://askubuntu.com/questions/54814/how-can-i-ctrl-alt-f-to-get-to-a-tty-in-a-qemu-session
} }
declare-flag run-qemu,install-system,'*' vm-mem "num" "VM RAM in MiB (»qemu -m«)."
declare-flag run-qemu,install-system,'*' vm-smp "num" "Number of guest CPU cores."
declare-flag run-qemu,install-system,'*' vm-usb-port "path" "A physical USB port (or hub) to pass to the guest (e.g. a YubiKey for unlocking). Specified as »<bus>-<port>«, where bus and port refer to the physical USB port »/sys/bus/usb/devices/<bus>-<port>« (see »lsusb -tvv«). E.g.: »--vm-usb-port=3-1.1.1.4«."
function apply-vm-args {
qemu+=( -m ${args[vm-mem]:-2048} )
if [[ ${args[vm-smp]:-} ]] ; then qemu+=( -smp ${args[vm-smp]} ) ; fi
if [[ ${args[vm-usb-port]:-} ]] ; then local decl ; for decl in ${args[vm-usb-port]//:/ } ; do
qemu+=( -usb -device usb-host,hostbus="${decl/-*/}",hostport="${decl/*-/}" )
done ; fi
}
declare-command add-bootkey-to-keydev blockDev << 'EOD' declare-command add-bootkey-to-keydev blockDev << 'EOD'
Creates a random static key on a new key partition on the GPT partitioned »$blockDev«. The drive can then be used as headless but removable disk unlock method (»usbPartition«/»usb-part«). Creates a random static key on a new key partition on the GPT partitioned »$blockDev«. The drive can then be used as headless but removable disk unlock method (»usbPartition«/»usb-part«).
To create/clear the GPT beforehand, run: $ sgdisk --zap-all "$blockDev" To create/clear the GPT beforehand, run: $ sgdisk --zap-all "$blockDev"

View File

@ -47,8 +47,9 @@ function prompt-new-password {( set -u # 1: usage
)} )}
## If »secretFile« does not exist, interactively prompts up to three times for the secret to be stored in that file. ## If »secretFile« does not exist, interactively prompts up to three times for the secret to be stored in that file.
declare-flag '*' no-optional-prompts "" "Skip prompting for (and thus saving) secret marked as optional."
function prompt-secret-as {( set -u # 1: what, 2: secretFile, 3?: owner[:[group]], 4?: mode function prompt-secret-as {( set -u # 1: what, 2: secretFile, 3?: owner[:[group]], 4?: mode
if [[ -e $2 ]] ; then \return ; fi if [[ ${arg_optional:-} && ${args[no-optional-prompts]:-} ]] ; then \return ; fi ; if [[ -e $2 ]] ; then \return ; fi
what=$1 ; shift what=$1 ; shift
function prompt { function prompt {
read -s -p "Please enter $what: " value || exit ; echo 1>&2 read -s -p "Please enter $what: " value || exit ; echo 1>&2

View File

@ -170,7 +170,7 @@ in let module = {
initrd-cleanup.preStart = '' initrd-cleanup.preStart = ''
umount ${keystore} || true umount ${keystore} || true
rmdir ${keystore} || true rmdir ${keystore} || true
${config.systemd.package}/lib/systemd/systemd-cryptsetup detach keystore-${hash} systemd-cryptsetup detach keystore-${hash}
''; '';
}; };
boot.initrd.luks.devices."keystore-${hash}".keyFileTimeout = 10; boot.initrd.luks.devices."keystore-${hash}".keyFileTimeout = 10;

View File

@ -61,6 +61,7 @@ in let hostModule = {
if [[ ! ''${args[initrd-console]:-} ]] ; then noConsole=1 ; fi if [[ ! ''${args[initrd-console]:-} ]] ; then noConsole=1 ; fi
if [[ ''${args[initrd-console]:-} ]] ; then touch $tmp/xchg/initrd-console ; fi if [[ ''${args[initrd-console]:-} ]] ; then touch $tmp/xchg/initrd-console ; fi
if [[ ''${args[quiet]:-} ]] ; then touch $tmp/xchg/quiet ; fi if [[ ''${args[quiet]:-} ]] ; then touch $tmp/xchg/quiet ; fi
</etc/hosts grep -oP '127.0.0.[12] (?!localhost)\K.*' >$tmp/xchg/host
${cfg.virtualisation.qemu.package}/bin/qemu-img create -f qcow2 $tmp/dummyImage 4M &>/dev/null # do this silently ${cfg.virtualisation.qemu.package}/bin/qemu-img create -f qcow2 $tmp/dummyImage 4M &>/dev/null # do this silently
@ -69,7 +70,9 @@ in let hostModule = {
export QEMU_KERNEL_PARAMS="init=${config.system.build.toplevel}/init ''${noConsole:+console=tty1} edd=off boot.shell_on_fail" export QEMU_KERNEL_PARAMS="init=${config.system.build.toplevel}/init ''${noConsole:+console=tty1} edd=off boot.shell_on_fail"
export QEMU_NET_OPTS= QEMU_OPTS= export QEMU_NET_OPTS= QEMU_OPTS=
if [[ ''${args[quiet]:-} ]] ; then if [[ ''${args[quiet]:-} ]] ; then
${launch} "''${argv[@]}" &> >( ${pkgs.coreutils}/bin/tr -dc '[[:print:]]\r\n\t' | { while IFS= read line ; do if [[ $line == magic:cm4alv0wly79p6i4aq32hy36i* ]] ; then break ; fi ; done ; cat ; } ) || { e=$? ; echo "Execution of VM failed!" 1>&2 ; exit $e ; } ${launch} "''${argv[@]}" &> >( ${pkgs.coreutils}/bin/tr -dc '[[:print:]]\r\n\t' | {
while IFS= read line ; do if [[ $line == magic:cm4alv0wly79p6i4aq32hy36i* ]] ; then break ; fi ; done ; cat ;
} ) || { e=$? ; echo "Execution of VM failed!" 1>&2 ; exit $e ; }
else else
${launch} "''${argv[@]}" || exit ${launch} "''${argv[@]}" || exit
fi fi
@ -90,7 +93,7 @@ in let hostModule = {
# Instead of tearing down the initrd environment, adjust some mounts and run the »command« in the initrd: # Instead of tearing down the initrd environment, adjust some mounts and run the »command« in the initrd:
boot.initrd.systemd.enable = lib.mkVMOverride false; boot.initrd.systemd.enable = lib.mkVMOverride false;
boot.initrd.postMountCommands = '' boot.initrd.postMountCommands = ''
set -x
for fs in tmp/shared tmp/xchg nix/store.lower nix/var/nix/db.lower ; do for fs in tmp/shared tmp/xchg nix/store.lower nix/var/nix/db.lower ; do
mkdir -p /$fs && mount --move $targetRoot/$fs /$fs || fail mkdir -p /$fs && mount --move $targetRoot/$fs /$fs || fail
done done
@ -108,14 +111,24 @@ in let hostModule = {
toplevel=$(dirname $stage2Init) toplevel=$(dirname $stage2Init)
ln -sfT $toplevel /run/current-system ln -sfT $toplevel /run/current-system
ln -sfT $toplevel /run/booted-system ln -sfT $toplevel /run/booted-system
ln -sfT $toplevel/kernel-modules/lib/modules /lib/modules rm -rf /lib/modules ; ln -sfT $toplevel/kernel-modules/lib/modules /lib/modules
# Also mostly dor debugging shells: # Set up /etc:
mv /etc /etc.initrd mv /etc /etc.initrd
mkdir -p -m 755 /etc.work /etc.upper /etc mkdir -p -m 755 /etc.work /etc.upper /etc
mount -t overlay overlay -o lowerdir=$toplevel/etc,workdir=/etc.work,upperdir=/etc.upper /etc mount -t overlay overlay -o lowerdir=$toplevel/etc,workdir=/etc.work,upperdir=/etc.upper /etc
( cd /etc.initrd ; cp -a mtab udev /etc/ ) # (keep these) ( cd /etc.initrd ; cp -a mtab udev /etc/ ) # (keep these)
# Set up NATed networking:
cat /etc/hosts >/etc/hosts.cp ; rm /etc/hosts ; mv /etc/hosts.cp /etc/hosts
perl -pe 's/127.0.0.[12](?! localhost)/# /' -i /etc/hosts
perl -pe 's/::1(?! localhost)/# /' -i /etc/hosts
{ printf '10.0.2.2 ' ; cat /tmp/xchg/host ; } >>/etc/hosts
ip addr add 10.0.2.15/24 dev eth0
ip link set dev eth0 up
ip route add default via 10.0.2.2 dev eth0
echo nameserver 1.1.1.1 >/etc/resolv.conf # 10.0.2.3 doesn't reply
# »nix copy« complains without »nixbld« group: # »nix copy« complains without »nixbld« group:
rm -f /etc/passwd /etc/group rm -f /etc/passwd /etc/group
printf '%s\n' 'root:x:0:0:root:/root:/bin/bash' >/etc/passwd printf '%s\n' 'root:x:0:0:root:/root:/bin/bash' >/etc/passwd
@ -126,7 +139,7 @@ in let hostModule = {
console=/dev/ttyS0 ; if [[ -e /tmp/xchg/initrd-console ]] ; then console=/dev/console ; fi # (does this even make a difference?) console=/dev/ttyS0 ; if [[ -e /tmp/xchg/initrd-console ]] ; then console=/dev/console ; fi # (does this even make a difference?)
if [[ -e /tmp/xchg/quiet ]] ; then printf '\n%s\n' 'magic:cm4alv0wly79p6i4aq32hy36i...' >$console ; fi if [[ -e /tmp/xchg/quiet ]] ; then printf '\n%s\n' 'magic:cm4alv0wly79p6i4aq32hy36i...' >$console ; fi
exit=0 ; bash /tmp/xchg/script <$console >$console 2>$console || exit=$? set +x ; exit=0 ; bash /tmp/xchg/script <$console >$console 2>$console || exit=$?
echo $exit >/tmp/xchg/exit echo $exit >/tmp/xchg/exit
sync ; sync sync ; sync
@ -135,6 +148,7 @@ in let hostModule = {
sleep infinity # the VM will halt very soon sleep infinity # the VM will halt very soon
''; '';
boot.initrd.kernelModules = [ "overlay" ]; # for writable »/etc«, chown of »/nix/store« and locks in »/nix/var/nix/db« boot.initrd.kernelModules = [ "overlay" ]; # for writable »/etc«, chown of »/nix/store« and locks in »/nix/var/nix/db«
#boot.initrd.extraUtilsCommands = ''copy_bin_and_libs ${pkgs.perl}/bin/perl'';
}) ({ }) ({
@ -148,6 +162,7 @@ in let hostModule = {
"/nix/store".mountPoint = lib.mkForce "/nix/store.lower"; "/nix/store".mountPoint = lib.mkForce "/nix/store.lower";
}; # mount -t 9p -o trans=virtio -o version=9p2000.L -o msize=4194304 nix-var-nix-db /nix/var/nix/db }; # mount -t 9p -o trans=virtio -o version=9p2000.L -o msize=4194304 nix-var-nix-db /nix/var/nix/db
virtualisation.qemu.options = [ "-virtfs local,path=/nix/var/nix/db,security_model=none,mount_tag=nix-var-nix-db,readonly=on" ]; # (doing this manually to pass »readonly«, to not ever corrupt the host's Nix DBs) virtualisation.qemu.options = [ "-virtfs local,path=/nix/var/nix/db,security_model=none,mount_tag=nix-var-nix-db,readonly=on" ]; # (doing this manually to pass »readonly«, to not ever corrupt the host's Nix DBs)
boot.resumeDevice = lib.mkVMOverride "";
}) ({ }) ({
@ -159,10 +174,13 @@ in let hostModule = {
}) ({ }) ({
virtualisation = if (builtins.substring 0 5 pkgs.lib.version) > "22.05" then { host.pkgs = lib.mkDefault pkgs.buildPackages; } else { }; virtualisation.host.pkgs = lib.mkDefault pkgs.buildPackages;
}) ({
virtualisation.qemu.package = lib.mkIf (pkgs.buildPackages.system != pkgs.system) (cfg.virtualisation.host or { pkgs = pkgs.buildPackages; }).pkgs.qemu_full; virtualisation.qemu.package = lib.mkIf (pkgs.buildPackages.system != pkgs.system) (cfg.virtualisation.host or { pkgs = pkgs.buildPackages; }).pkgs.qemu_full;
}) ({
#virtualisation.qemu.options = [ "-nic user,model=virtio-net-pci" ]; # NAT
}) ({ }) ({
specialisation = lib.mkForce { }; specialisation = lib.mkForce { };

View File

@ -12,9 +12,10 @@ GPT-FDisk patched to be able to move not only the primary, but also the backup p
dirname: inputs: final: prev: let dirname: inputs: final: prev: let
inherit (final) pkgs; lib = inputs.self.lib.__internal__; inherit (final) pkgs; lib = inputs.self.lib.__internal__;
debug = false; debug = false;
enable = lib.versionOlder prev.gptfdisk.version "1.0.10"; # patch merged in that version
in { in {
gptfdisk = ( gptfdisk = if !enable then prev.gptfdisk else (
if debug then pkgs.enableDebugging else (x: x) if debug then pkgs.enableDebugging else (x: x)
) (prev.gptfdisk.overrideAttrs (old: let ) (prev.gptfdisk.overrideAttrs (old: let
pname = "gptfdisk"; pname = "gptfdisk";
@ -31,6 +32,6 @@ in {
dontStrip = true; dontStrip = true;
} else { }))); } else { })));
libblockdev = prev.libblockdev.override { inherit (prev) gptfdisk; }; libblockdev = prev.libblockdev.override (lib.optionalAttrs enable { inherit (prev) gptfdisk; });
} }

View File

@ -0,0 +1,8 @@
dirname: inputs: final: prev: let
lib = inputs.self.lib.__internal__;
in {
## The options reference manual takes long to generate, is also available online (in this exact form, as this version is the generic nixpkgs one), and has failed to generate on occasion.
nixos-install-tools-no-doc = (prev.nixos-install-tools.override (old: { nixos = args: lib.recursiveUpdate (old.nixos args) { config.system.build.manual.nixos-configuration-reference-manpage = null; }; }));
#nixos-install-tools-no-doc = (prev.nixos-install-tools.overrideAttrs (old: { pkgs = builtins.toJSON (builtins.tail (builtins.fromJSON old.pkgs)); }));
}