mirror of
https://github.com/NiklasGollenstede/nixos-installer.git
synced 2024-11-21 07:23:59 +01:00
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:
parent
d0ba074777
commit
65c1691644
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -76,6 +76,7 @@
|
||||
"gdisk", // program
|
||||
"getsize64", // cli arg
|
||||
"getty", // program
|
||||
"github", // name
|
||||
"gnugrep", // package
|
||||
"gnused", // package
|
||||
"gollenstede", // name
|
||||
@ -112,6 +113,7 @@
|
||||
"mountpoint", // program / function
|
||||
"msize", // option
|
||||
"mtab", // linux
|
||||
"nameserver", // concat
|
||||
"namespacing", // word
|
||||
"netdev", // cli arg
|
||||
"niklas", // name
|
||||
@ -137,6 +139,7 @@
|
||||
"optimise", // B/E
|
||||
"ostype", // virtual box
|
||||
"overlayed", // word
|
||||
"overridable", // word
|
||||
"ovmf", // package
|
||||
"partlabel", // linux
|
||||
"partprobe", // program / function
|
||||
@ -162,6 +165,7 @@
|
||||
"reexec", // option
|
||||
"refreservation", // zfs
|
||||
"relatime", // mount option
|
||||
"resolv", // abbr
|
||||
"rootfs", // linux
|
||||
"rpool", // zfs
|
||||
"sandboxing", // word
|
||||
|
BIN
flake.lock
BIN
flake.lock
Binary file not shown.
@ -2,7 +2,7 @@
|
||||
"Fully automated NixOS CLI installer"
|
||||
); 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"; };
|
||||
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.)
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
dirname: inputs@{ self, nixpkgs, functions, ...}: let
|
||||
inherit (nixpkgs) lib;
|
||||
inherit (functions.lib) extractBashFunction forEachSystem getModulesFromInputs getNixFiles getOverlaysFromInputs importWrapped mapMerge mapMergeUnique mergeAttrsUnique substituteImplicit;
|
||||
setup-scripts = (import "${dirname}/setup-scripts" "${dirname}/setup-scripts" inputs);
|
||||
inherit (functions.lib) forEachSystem getModulesFromInputs getNixFiles getOverlaysFromInputs importWrapped mapMerge mapMergeUnique mergeAttrsUnique; # trace;
|
||||
inherit (inputs.config.rename) installer; preface' = inputs.config.rename.preface;
|
||||
|
||||
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.)
|
||||
|
||||
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)
|
||||
{ _module.args.name = lib.mkOverride 99 name; } # (specialisations can somehow end up with the name »configuration«, which is very incorrect)
|
||||
(let
|
||||
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; }
|
||||
]; _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.
|
||||
# All arguments are optional, as long as the default can be derived from the other arguments as passed.
|
||||
mkSystemsFlake = 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.
|
||||
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 hosts' top level config files.
|
||||
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.
|
||||
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;
|
||||
} // (forEachSystem setupPlatforms (buildSystem: let
|
||||
pkgs = (import inputs.nixpkgs { inherit overlays; system = buildSystem; });
|
||||
tools = lib.unique (map (p: p.outPath) (lib.filter lib.isDerivation pkgs.stdenv.allowedRequisites));
|
||||
in rec {
|
||||
|
||||
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
|
||||
packages = let all-systems = pkgs.runCommandLocal "all-systems" { } ''
|
||||
${''
|
||||
mkdir -p $out/systems
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: "ln -sT ${system.config.system.build.toplevel} $out/systems/${getName name}") nixosConfigurations)}
|
||||
''}
|
||||
${''
|
||||
mkdir -p $out/scripts
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: "ln -sT ${apps.${name}.program} $out/scripts/${getName name}") nixosConfigurations)}
|
||||
''}
|
||||
${lib.optionalString (inputs != { }) ''
|
||||
mkdir -p $out/inputs
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: { outPath, ... }: "ln -sT ${outPath} $out/inputs/${name}") inputs)}
|
||||
''}
|
||||
mkdir -p $out/systems
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: "ln -sT ${system.config.system.build.toplevel} $out/systems/${getName name}") nixosConfigurations)}
|
||||
mkdir -p $out/scripts
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: system: "ln -sT ${apps.${name}.program} $out/scripts/${getName name}") nixosConfigurations)}
|
||||
mkdir -p $out/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 ; });
|
||||
checks.all-systems = packages.all-systems;
|
||||
|
||||
@ -169,7 +165,7 @@ in rec {
|
||||
apps = mapMergeUnique (k: v: { ${renameOutputs k} = v; }) outputs.apps.${buildSystem};
|
||||
packages.${renameOutputs "all-systems"} = outputs.packages.${buildSystem}.all-systems;
|
||||
checks.${renameOutputs "all-systems"} = outputs.checks.${buildSystem}.all-systems;
|
||||
}));
|
||||
})));
|
||||
|
||||
# This makes the »./setup-scripts/*« callable from the command line:
|
||||
writeSystemScripts = {
|
||||
|
@ -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.
|
||||
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«
|
||||
|
||||
@ -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."
|
||||
|
||||
## 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
|
||||
if [[ $1 == */ ]] ; then
|
||||
mkdir -p "$1"
|
||||
for name in "@{!config.setup.disks.devices[@]}" ; do blockDevs[$name]=${1}${name}.img ; done
|
||||
if [[ ${args[disks]} == */ ]] ; then
|
||||
mkdir -p "${args[disks]}"
|
||||
for name in "@{!config.setup.disks.devices[@]}" ; do blockDevs[$name]=${args[disks]}${name}.img ; done
|
||||
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
|
||||
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
|
||||
@ -73,7 +73,7 @@ function ensure-disks { # 1: diskPaths, 2?: skipLosetup
|
||||
local outFile=${blockDevs[$name]} &&
|
||||
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 [[ ${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
|
||||
else
|
||||
local size=$( @{native.util-linux}/bin/blockdev --getsize64 "${blockDevs[$name]}" || : ) ; local waste=$(( size - ${disk[size]} ))
|
||||
|
@ -3,12 +3,9 @@
|
||||
# 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.
|
||||
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).
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
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,
|
||||
@ -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.
|
||||
Various »FLAG«s below affect how the installation is performed (in VM, verbosity, debugging, ...).
|
||||
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
|
||||
trap - EXIT # start with empty traps for sub-shell
|
||||
prepare-installer "$@" || exit
|
||||
do-disk-setup "$1" || exit
|
||||
do-disk-setup || 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).
|
||||
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 [[ ! ${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
|
||||
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
|
||||
|
||||
@ -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
|
||||
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[@]}"' )}
|
||||
else # use Nix by absolute path, as it won't be on »$PATH«
|
||||
PATH=$PATH:@{native.nix}/bin
|
||||
@ -75,28 +75,32 @@ 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-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.
|
||||
function reexec-in-qemu {
|
||||
|
||||
# (not sure whether this works for block devices)
|
||||
ensure-disks "$1" 1 || return
|
||||
qemu=( -m 3072 ) ; declare -A qemuDevs=( )
|
||||
local index=2 ; local name ; for name in "${!blockDevs[@]}" ; do
|
||||
#if [[ ${blockDevs[$name]} != /dev/* ]] ; then
|
||||
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
|
||||
#-device ahci,acpi-index=${index},id=ahci${index} # create an (ich9-)AHCI bus named »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)
|
||||
)
|
||||
qemuDevs[$name]=/dev/vd$( printf "\x$(printf %x $(( index - 1 + 97 )) )" ) # a is used by the (unused) root disk
|
||||
let index+=1
|
||||
done
|
||||
## (Re-)executes the current system's script in a qemu VM.
|
||||
function exec-in-qemu { # 1: entry, ...: argv
|
||||
|
||||
qemu=( ) ; apply-vm-args
|
||||
args[vm]='' ; args[no-vm]=1
|
||||
newArgs=( ) ; 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}" )
|
||||
|
||||
if [[ ${args[disks]:-} ]] ; then
|
||||
# (not sure whether this works for block devices)
|
||||
arg_skipLosetup=1 ensure-disks || return
|
||||
args[disks]=''
|
||||
local index=2 # 1/a is used by the (unused) root disk
|
||||
local name ; for name in "${!blockDevs[@]}" ; do
|
||||
#if [[ ${blockDevs[$name]} != /dev/* ]] ; then
|
||||
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
|
||||
#-device ahci,acpi-index=${index},id=ahci${index} # create an (ich9-)AHCI bus named »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)
|
||||
)
|
||||
args[disks]+="$name"=/dev/vd"$( printf "\x$(printf %x $(( index - 1 + 97 )) )" )": ; let index+=1
|
||||
done
|
||||
args[disks]=${args[disks]%:}
|
||||
fi
|
||||
|
||||
newArgs=( ) ; (( $# == 0 )) || newArgs+=( "$@" )
|
||||
for arg in "${!args[@]}" ; do newArgs+=( --"$arg"="${args[$arg]}" ) ; done
|
||||
|
||||
#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
|
||||
@ -107,7 +111,7 @@ function reexec-in-qemu {
|
||||
local scripts=$self ; if [[ @{pkgs.system} != "@{native.system}" ]] ; then
|
||||
scripts=$( build-lazy @{inputs.self}'#'apps.@{pkgs.system}.@{config.installer.outputName:?}.derivation ) || return
|
||||
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
|
||||
|
||||
@ -119,10 +123,10 @@ function reexec-in-qemu {
|
||||
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.
|
||||
|
||||
#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
|
||||
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)
|
||||
)}
|
||||
|
||||
@ -201,7 +205,7 @@ function install-system-to {( set -u # 1: mnt, 2?: topLevel
|
||||
else
|
||||
( set +x ; echo "[1;32mInstallation done![0m This shell is in a chroot in the mounted system for inspection. Exiting the shell will unmount the system." 1>&2 )
|
||||
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
|
||||
|
||||
mkdir -p $mnt/var/lib/systemd/timesync && touch $mnt/var/lib/systemd/timesync/clock || true # save current time
|
||||
|
@ -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 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 mem "num" "VM RAM in MiB (»qemu -m«)."
|
||||
declare-flag run-qemu no-kvm "" "Do not rey to use (or complain about the unavailability of) KVM."
|
||||
declare-flag run-qemu no-kvm "" "Do not try 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 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«.
|
||||
@ -67,12 +66,9 @@ $ ... --nic=socket,listen=:4321 # once
|
||||
$ ... --nic=socket,connect=:4321 # once
|
||||
Example 2 (connect many VMs, unprivileged):
|
||||
$ 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 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)."
|
||||
function run-qemu {
|
||||
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
|
||||
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.
|
||||
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).
|
||||
@ -159,18 +152,15 @@ function run-qemu {
|
||||
qemu+=( -nic model=virtio-net-pci,mac=$mac,type="${args[nic]}" )
|
||||
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«
|
||||
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
|
||||
apply-vm-args
|
||||
|
||||
if [[ ${args[install]:-} == 1 ]] ; then local disk ; for disk in "${disks[@]}" ; do
|
||||
if [[ ! -e $disk ]] ; then args[install]=always ; fi
|
||||
done ; fi
|
||||
if [[ ${args[install]:-} == always ]] ; then
|
||||
local verbosity=--quiet ; if [[ ${args[trace]:-} ]] ; then verbosity=--trace ; fi ; if [[ ${args[debug]:-} ]] ; then verbosity=--debug ; fi
|
||||
hostPath=${hostPath:-} ${args[dry-run]:+echo} "$self" install-system "$diskImages" $verbosity --no-inspect || return
|
||||
fi
|
||||
if [[ ${args[install]:-} == always ]] && [[ ! ${args[dry-run]:-} ]] ; then (
|
||||
if [[ ! ${args[trace]:-} ]] && [[! ${args[debug]:-} ]] ; then args[quiet]=1 ; fi
|
||||
args[no-inspect]=1 ; install-system "$diskImages" || exit
|
||||
) || return ; fi
|
||||
|
||||
qemu+=( "${argv[@]}" )
|
||||
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
|
||||
}
|
||||
|
||||
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'
|
||||
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"
|
||||
|
@ -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.
|
||||
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
|
||||
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
|
||||
function prompt {
|
||||
read -s -p "Please enter $what: " value || exit ; echo 1>&2
|
||||
|
@ -170,7 +170,7 @@ in let module = {
|
||||
initrd-cleanup.preStart = ''
|
||||
umount ${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;
|
||||
|
@ -61,6 +61,7 @@ in let hostModule = {
|
||||
if [[ ! ''${args[initrd-console]:-} ]] ; then noConsole=1 ; fi
|
||||
if [[ ''${args[initrd-console]:-} ]] ; then touch $tmp/xchg/initrd-console ; 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
|
||||
|
||||
@ -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_NET_OPTS= QEMU_OPTS=
|
||||
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
|
||||
${launch} "''${argv[@]}" || exit
|
||||
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:
|
||||
boot.initrd.systemd.enable = lib.mkVMOverride false;
|
||||
boot.initrd.postMountCommands = ''
|
||||
|
||||
set -x
|
||||
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
|
||||
done
|
||||
@ -108,14 +111,24 @@ in let hostModule = {
|
||||
toplevel=$(dirname $stage2Init)
|
||||
ln -sfT $toplevel /run/current-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
|
||||
mkdir -p -m 755 /etc.work /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)
|
||||
|
||||
# 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:
|
||||
rm -f /etc/passwd /etc/group
|
||||
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?)
|
||||
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
|
||||
|
||||
sync ; sync
|
||||
@ -135,6 +148,7 @@ in let hostModule = {
|
||||
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.extraUtilsCommands = ''copy_bin_and_libs ${pkgs.perl}/bin/perl'';
|
||||
|
||||
}) ({
|
||||
|
||||
@ -148,6 +162,7 @@ in let hostModule = {
|
||||
"/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
|
||||
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.options = [ "-nic user,model=virtio-net-pci" ]; # NAT
|
||||
|
||||
}) ({
|
||||
|
||||
specialisation = lib.mkForce { };
|
||||
|
@ -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
|
||||
inherit (final) pkgs; lib = inputs.self.lib.__internal__;
|
||||
debug = false;
|
||||
enable = lib.versionOlder prev.gptfdisk.version "1.0.10"; # patch merged in that version
|
||||
in {
|
||||
|
||||
gptfdisk = (
|
||||
gptfdisk = if !enable then prev.gptfdisk else (
|
||||
if debug then pkgs.enableDebugging else (x: x)
|
||||
) (prev.gptfdisk.overrideAttrs (old: let
|
||||
pname = "gptfdisk";
|
||||
@ -31,6 +32,6 @@ in {
|
||||
dontStrip = true;
|
||||
} else { })));
|
||||
|
||||
libblockdev = prev.libblockdev.override { inherit (prev) gptfdisk; };
|
||||
libblockdev = prev.libblockdev.override (lib.optionalAttrs enable { inherit (prev) gptfdisk; });
|
||||
|
||||
}
|
||||
|
8
overlays/nixos-install-tools.nix
Normal file
8
overlays/nixos-install-tools.nix
Normal 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)); }));
|
||||
}
|
Loading…
Reference in New Issue
Block a user