better CLI argument handling, some small fixes

This commit is contained in:
Niklas Gollenstede 2022-08-31 08:38:33 +02:00
parent 7ab9215b0a
commit 1d93a8acc0
12 changed files with 121 additions and 54 deletions

View File

@ -81,7 +81,7 @@ in rec {
inherit name; nodes = peers; # NixOPS inherit name; nodes = peers; # NixOPS
}; };
in let system = { inherit preface; } // (nixosSystem { in let system = { inherit preface; } // (nixosSystem {
system = targetSystem; system = buildSystem;
modules = [ ( modules = [ (
(importWrapped inputs entryPath).module (importWrapped inputs entryPath).module
) { ) {
@ -101,10 +101,10 @@ in rec {
}; }))); }; })));
apply = lib.filterAttrs (k: v: v != null); apply = lib.filterAttrs (k: v: v != null);
}; config.${prefix}.setup.scripts = lib.mapAttrs (name: path: lib.mkOptionDefault { inherit path; }) (setup-scripts); }; config.${prefix}.setup.scripts = lib.mapAttrs (name: path: lib.mkOptionDefault { inherit path; }) (setup-scripts);
} ({ config, pkgs, ... }: { } ({ config, options, pkgs, inputs ? { }, ... }: {
options.${prefix}.setup.appliedScripts = lib.mkOption { options.${prefix}.setup.appliedScripts = lib.mkOption {
type = lib.types.functionTo lib.types.str; readOnly = true; type = lib.types.functionTo lib.types.str; readOnly = true;
default = context: substituteImplicit { inherit pkgs; scripts = lib.sort (a: b: a.order < b.order) (lib.attrValues config.${prefix}.setup.scripts); context = system // context; }; # inherit (builtins) trace; default = context: substituteImplicit { inherit pkgs; scripts = lib.sort (a: b: a.order < b.order) (lib.attrValues config.${prefix}.setup.scripts); context = { inherit config options pkgs inputs preface; } // context; }; # inherit (builtins) trace;
}; };
}) ({ config, ... }: { }) ({ config, ... }: {
@ -149,7 +149,7 @@ in rec {
throw "»${prefix}.preface.id«s are not unique! The following hosts share their IDs with some other host: ${builtins.concatStringsSep ", " (builtins.attrNames duplicate)}" throw "»${prefix}.preface.id«s are not unique! The following hosts share their IDs with some other host: ${builtins.concatStringsSep ", " (builtins.attrNames duplicate)}"
) else configs; ) else configs;
# Builds a system of NixOS hosts and exports them plus managing functions 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 = 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 host's top level config files.
@ -170,8 +170,10 @@ in rec {
nixosSystem ? inputs.nixpkgs.lib.nixosSystem, nixosSystem ? inputs.nixpkgs.lib.nixosSystem,
# If provided, then cross compilation is enabled for all hosts whose target architecture is different from this. Since cross compilation currently fails for (some stuff in) NixOS, better don't set »localSystem«. Without it, building for other platforms works fine (just slowly) if »boot.binfmt.emulatedSystems« on the building system is configured for the respective target(s). # If provided, then cross compilation is enabled for all hosts whose target architecture is different from this. Since cross compilation currently fails for (some stuff in) NixOS, better don't set »localSystem«. Without it, building for other platforms works fine (just slowly) if »boot.binfmt.emulatedSystems« on the building system is configured for the respective target(s).
localSystem ? null, localSystem ? null,
## If provided, then change the name of each output attribute by passing it through this function. Allows exporting of multiple variants of a repo's hosts from a single flake:
renameOutputs ? null,
... }: let ... }: let
otherArgs = (builtins.removeAttrs args [ "systems" ]) // { inherit inputs systems overlays modules specialArgs nixosSystem localSystem; }; otherArgs = (builtins.removeAttrs args [ "systems" "renameOutputs" ]) // { inherit inputs systems overlays modules specialArgs nixosSystem localSystem; };
nixosConfigurations = if builtins.isList systems then mergeAttrsUnique (map (systems: mkNixosConfigurations (otherArgs // systems)) systems) else mkNixosConfigurations (otherArgs // systems); nixosConfigurations = if builtins.isList systems then mergeAttrsUnique (map (systems: mkNixosConfigurations (otherArgs // systems)) systems) else mkNixosConfigurations (otherArgs // systems);
in let outputs = { in let outputs = {
inherit nixosConfigurations; inherit nixosConfigurations;
@ -179,7 +181,7 @@ in rec {
pkgs = (import inputs.nixpkgs { inherit overlays; system = localSystem; }); pkgs = (import inputs.nixpkgs { inherit overlays; system = localSystem; });
tools = lib.unique (map (p: p.outPath) (lib.filter lib.isDerivation pkgs.stdenv.allowedRequisites)); tools = lib.unique (map (p: p.outPath) (lib.filter lib.isDerivation pkgs.stdenv.allowedRequisites));
PATH = lib.concatMapStringsSep ":" (pkg: "${pkg}/bin") tools; PATH = lib.concatMapStringsSep ":" (pkg: "${pkg}/bin") tools;
in { in rec {
# Do per-host setup and maintenance things: # Do per-host setup and maintenance things:
# SYNOPSIS: nix run REPO#HOST [-- [sudo] [bash | -x [-c SCRIPT | FUNC ...ARGS]]] # SYNOPSIS: nix run REPO#HOST [-- [sudo] [bash | -x [-c SCRIPT | FUNC ...ARGS]]]
@ -250,7 +252,14 @@ in rec {
${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)}
''} ''}
''; '';
checks.all-systems = packages.all-systems;
})); in outputs; })); in if renameOutputs == null then outputs else {
nixosConfigurations = mapMerge (k: v: { ${renameOutputs k} = v; }) outputs.nixosConfigurations;
} // (forEachSystem [ "aarch64-linux" "x86_64-linux" ] (localSystem: {
apps = mapMerge (k: v: { ${renameOutputs k} = v; }) outputs.apps.${localSystem};
devShells = mapMerge (k: v: { ${renameOutputs k} = v; }) outputs.devShells.${localSystem};
packages.${renameOutputs "all-systems"} = outputs.packages.${localSystem}.all-systems;
checks.${renameOutputs "all-systems"} = outputs.checks.${localSystem}.all-systems;
}));
} }

View File

@ -41,7 +41,7 @@ function gen-key-constant {( set -eu # 1: _, 2: value
## Obtains a key by prompting for a password. ## Obtains a key by prompting for a password.
function gen-key-password {( set -eu # 1: usage function gen-key-password {( set -eu # 1: usage
usage=$1 usage=$1
( prompt-new-password "as key for @{config.networking.hostName}/$usage" || exit 1 ) ( prompt-new-password "as key for @{config.networking.hostName}:$usage" || exit 1 )
)} )}
## Generates a key by prompting for (or reusing) a »$user«'s password, combining it with »$keystore/home/$user.key«. ## Generates a key by prompting for (or reusing) a »$user«'s password, combining it with »$keystore/home/$user.key«.
@ -50,7 +50,8 @@ function gen-key-home-composite {( set -eu # 1: usage, 2: user
if [[ ${!userPasswords[@]} && ${userPasswords[$user]:-} ]] ; then if [[ ${!userPasswords[@]} && ${userPasswords[$user]:-} ]] ; then
password=${userPasswords[$user]} password=${userPasswords[$user]}
else else
password=$(prompt-new-password "that will be used as component of the key for @{config.networking.hostName}/$usage") password=$(prompt-new-password "that will be used as component of the key for »@{config.networking.hostName}:$usage«")
if [[ ! $password ]] ; then exit 1 ; fi
fi fi
( cat "$keystore"/home/"$user".key && cat <<<"$password" ) | sha256sum | head -c 64 ( cat "$keystore"/home/"$user".key && cat <<<"$password" ) | sha256sum | head -c 64
)} )}
@ -63,16 +64,18 @@ function gen-key-home-yubikey {( set -eu # 1: usage, 2: serialAndSlotAndUser(as
if [[ ${!userPasswords[@]} && ${userPasswords[$user]:-} ]] ; then if [[ ${!userPasswords[@]} && ${userPasswords[$user]:-} ]] ; then
password=${userPasswords[$user]} password=${userPasswords[$user]}
else else
password=$(prompt-new-password "as YubiKey challenge for @{config.networking.hostName}/$usage") password=$(prompt-new-password "as YubiKey challenge for »@{config.networking.hostName}:$usage«")
if [[ ! $password ]] ; then exit 1 ; fi
fi fi
gen-key-yubikey-challenge "$usage" "$serial:$slot:home-$password" true "${user}'s password for ${usage}" gen-key-yubikey-challenge "$usage" "$serial:$slot:home-$user=$password" true "»${user}«'s password (for key »${usage}«)"
)} )}
## Generates a reproducible secret by prompting for a pin/password and then challenging slot »$slot« of YubiKey »$serial«. ## Generates a reproducible secret by prompting for a pin/password and then challenging slot »$slot« of YubiKey »$serial«.
function gen-key-yubikey-pin {( set -eu # 1: usage, 2: serialAndSlot(as »serial:slot«) function gen-key-yubikey-pin {( set -eu # 1: usage, 2: serialAndSlot(as »serial:slot«)
usage=$1 ; serialAndSlot=$2 usage=$1 ; serialAndSlot=$2
password=$(prompt-new-password "/ pin as challenge to YubiKey »$serialAndSlot« as key for @{config.networking.hostName}/$usage") password=$(prompt-new-password "/ pin as challenge to YubiKey »$serialAndSlot« as key for »@{config.networking.hostName}:$usage«")
gen-key-yubikey-challenge "$usage" "$serialAndSlot:$password" true "pin for ${usage}" if [[ ! $password ]] ; then exit 1 ; fi
gen-key-yubikey-challenge "$usage" "$serialAndSlot:$password" true "password / pin as key for »@{config.networking.hostName}:$usage«"
)} )}
## Generates a reproducible secret for a certain »$use«case and optionally »$salt« on a »$host« by challenging slot »$slot« of YubiKey »$serial«. ## Generates a reproducible secret for a certain »$use«case and optionally »$salt« on a »$host« by challenging slot »$slot« of YubiKey »$serial«.
@ -91,19 +94,19 @@ function gen-key-yubikey-challenge {( set -eu # 1: _, 2: serialAndSlotAndChallen
serial=$( <<<"$args" cut -d: -f1 ) ; slot=$( <<<"$args" cut -d: -f2 ) serial=$( <<<"$args" cut -d: -f1 ) ; slot=$( <<<"$args" cut -d: -f2 )
challenge=${args/$serial:$slot:/} challenge=${args/$serial:$slot:/}
if [[ "$serial" != "$(@{native.yubikey-personalization}/bin/ykinfo -sq)" ]] ; then printf 'Please insert / change to YubiKey with serial %s!\n' "$serial" 1>&2 ; fi if [[ "$serial" != "$( @{native.yubikey-personalization}/bin/ykinfo -sq )" ]] ; then printf 'Please insert / change to YubiKey with serial %s!\n' "$serial" 1>&2 ; fi
if [[ ! "${3:-}" ]] ; then if [[ ! "${3:-}" ]] ; then
read -p 'Challenging YubiKey '"$serial"' slot '"$slot"' twice with '"${message:-challenge »"$challenge":1/2«}"'. Enter to continue, or Ctrl+C to abort:' read -p 'Challenging YubiKey '"$serial"' slot '"$slot"' twice with '"${message:-challenge »"$challenge":1/2«}"'. Enter to continue, or Ctrl+C to abort:'
else else
read -p 'Challenging YubiKey '"$serial"' slot '"$slot"' once with '"${message:-challenge »"$challenge"«}"'. Enter to continue, or Ctrl+C to abort:' read -p 'Challenging YubiKey '"$serial"' slot '"$slot"' once with '"${message:-challenge »"$challenge"«}"'. Enter to continue, or Ctrl+C to abort:'
fi fi
if [[ "$serial" != "$(@{native.yubikey-personalization}/bin/ykinfo -sq)" ]] ; then printf 'YubiKey with serial %s not present, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi if [[ "$serial" != "$( @{native.yubikey-personalization}/bin/ykinfo -sq )" ]] ; then printf 'YubiKey with serial %s not present, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi
if [[ ! "${3:-}" ]] ; then if [[ ! "${3:-}" ]] ; then
secret="$(@{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge":1)""$(@{native.yubikey-personalization}/bin/ykchalresp -2 "$challenge":2)" secret="$( @{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge":1 )""$( sleep .5 || : ; @{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge":2 || @{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge":2 )" # the second consecutive challenge tends to fail if it follows immediately
if [[ ${#secret} != 80 ]] ; then printf 'YubiKey challenge failed, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi if [[ ${#secret} != 80 ]] ; then printf 'YubiKey challenge failed, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi
else else
secret="$(@{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge")" secret="$( @{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge" )"
if [[ ${#secret} != 40 ]] ; then printf 'YubiKey challenge failed, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi if [[ ${#secret} != 40 ]] ; then printf 'YubiKey challenge failed, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi
fi fi
printf %s "$secret" | head -c 64 printf %s "$secret" | head -c 64

View File

@ -36,7 +36,8 @@ function do-disk-setup { # 1: diskPaths
## Partitions the »diskPaths« instances of all »config.wip.fs.disks.devices« to ensure that all specified »config.wip.fs.disks.partitions« exist. ## Partitions the »diskPaths« instances of all »config.wip.fs.disks.devices« to ensure that all specified »config.wip.fs.disks.partitions« exist.
# Parses »diskPaths«, creates and loop-mounts images for non-/dev/ paths, and tries to abort if any partition already exists on the host. # Parses »diskPaths«, creates and loop-mounts images for non-/dev/ paths, and tries to abort if any partition already exists on the host.
function partition-disks { { # 1: diskPaths function partition-disks { { # 1: diskPaths
beQuiet=/dev/null ; if [[ ${args[debug]:-} ]] ; then beQuiet=/dev/stdout ; fi beLoud=/dev/null ; if [[ ${args[debug]:-} ]] ; then beLoud=/dev/stdout ; fi
beSilent=/dev/stderr ; if [[ ${args[quiet]:-} ]] ; then beSilent=/dev/null ; fi
declare -g -A blockDevs=( ) # this ends up in the caller's scope declare -g -A blockDevs=( ) # this ends up in the caller's scope
local path ; for path in ${1//:/ } ; do local path ; for path in ${1//:/ } ; 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
@ -75,26 +76,28 @@ function partition-disks { { # 1: diskPaths
if [[ ${disk[serial]} != "$actual" ]] ; then echo "Block device $blockDev's serial ($actual) does not match the serial (${disk[serial]}) declared for ${disk[name]}" ; exit 1 ; fi if [[ ${disk[serial]} != "$actual" ]] ; then echo "Block device $blockDev's serial ($actual) does not match the serial (${disk[serial]}) declared for ${disk[name]}" ; exit 1 ; fi
fi fi
# can (and probably should) restore the backup: # can (and probably should) restore the backup:
( PATH=@{native.gptfdisk}/bin ; set -x ; sgdisk --zap-all --load-backup=@{config.wip.fs.disks.partitioning}/"${disk[name]}".backup ${disk[allowLarger]:+--move-second-header} "${blockDevs[${disk[name]}]}" >$beQuiet ) ( PATH=@{native.gptfdisk}/bin ; ${_set_x:-:} ; sgdisk --zap-all --load-backup=@{config.wip.fs.disks.partitioning}/"${disk[name]}".backup ${disk[allowLarger]:+--move-second-header} "${blockDevs[${disk[name]}]}" >$beLoud 2>$beSilent )
#partition-disk "${disk[name]}" "${blockDevs[${disk[name]}]}" #partition-disk "${disk[name]}" "${blockDevs[${disk[name]}]}"
done done
@{native.parted}/bin/partprobe "${blockDevs[@]}" @{native.parted}/bin/partprobe "${blockDevs[@]}" &>$beLoud
@{native.systemd}/bin/udevadm settle -t 15 || true # sometimes partitions aren't quite made available yet @{native.systemd}/bin/udevadm settle -t 15 || true # sometimes partitions aren't quite made available yet
# ensure that filesystem creation does not complain about the devices already being occupied by a previous filesystem # ensure that filesystem creation does not complain about the devices already being occupied by a previous filesystem
wipefs --all "@{config.wip.fs.disks.partitions!attrNames[@]/#/'/dev/disk/by-partlabel/'}" >$beQuiet wipefs --all "@{config.wip.fs.disks.partitions!attrNames[@]/#/'/dev/disk/by-partlabel/'}" >$beLoud 2>$beSilent
)} )}
## Given a declared disk device's »name« and a path to an actual »blockDev« (or image) file, partitions the device as declared in the config. ## Given a declared disk device's »name« and a path to an actual »blockDev« (or image) file, partitions the device as declared in the config.
function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
name=$1 ; blockDev=$2 name=$1 ; blockDev=$2
beLoud=/dev/null ; if [[ ${args[debug]:-} ]] ; then beLoud=/dev/stdout ; fi
beSilent=/dev/stderr ; if [[ ${args[quiet]:-} ]] ; then beSilent=/dev/null ; fi
eval 'declare -A disk='"@{config.wip.fs.disks.devices[$name]}" eval 'declare -A disk='"@{config.wip.fs.disks.devices[$name]}"
devSize=${3:-$( @{native.util-linux}/bin/blockdev --getsize64 "$blockDev" )} devSize=${3:-$( @{native.util-linux}/bin/blockdev --getsize64 "$blockDev" )}
declare -a sgdisk=( --zap-all ) # delete existing part tables declare -a sgdisk=( --zap-all ) # delete existing part tables
if [[ ${disk[gptOffset]} != 0 ]] ; then if [[ ${disk[gptOffset]} != 0 ]] ; then
sgdisk+=( --move-main-table=$(( 2 + ${disk[gptOffset]} )) ) # this is incorrectly documented as --adjust-main-table in the man pages (at least versions 1.05 to 1.09 incl) sgdisk+=( --move-main-table=$(( 2 + ${disk[gptOffset]} )) ) # this is incorrectly documented as --adjust-main-table in the man pages (at least versions 1.05 to 1.09 incl)
sgdisk+=( --move-backup-table=$(( devSize/512 - 1 - 32 - ${disk[gptOffset]} )) ) sgdisk+=( --move-backup-table=$(( devSize/${disk[sectorSize]} - 1 - 32 - ${disk[gptOffset]} )) )
fi fi
sgdisk+=( --disk-guid="${disk[guid]}" ) sgdisk+=( --disk-guid="${disk[guid]}" )
@ -117,7 +120,7 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
sgdisk+=( --hybrid "${disk[mbrParts]}" ) # --hybrid: create MBR in addition to GPT; ${disk[mbrParts]}: make these GPT part 1 MBR parts 2[3[4]] sgdisk+=( --hybrid "${disk[mbrParts]}" ) # --hybrid: create MBR in addition to GPT; ${disk[mbrParts]}: make these GPT part 1 MBR parts 2[3[4]]
fi fi
( PATH=@{native.gptfdisk}/bin ; set -x ; sgdisk "${sgdisk[@]}" "$blockDev" >$beQuiet ) # running all at once is much faster ( PATH=@{native.gptfdisk}/bin ; ${_set_x:-:} ; sgdisk "${sgdisk[@]}" "$blockDev" >$beLoud ) # running all at once is much faster
if [[ ${disk[mbrParts]:-} ]] ; then if [[ ${disk[mbrParts]:-} ]] ; then
printf " printf "
@ -126,7 +129,7 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
# move the selected »mbrParts« to slots 1[2[3]] instead of 2[3[4]] (by re-creating part1 in the last sector, then sorting) # move the selected »mbrParts« to slots 1[2[3]] instead of 2[3[4]] (by re-creating part1 in the last sector, then sorting)
n;p;1 # new ; primary ; part1 n;p;1 # new ; primary ; part1
$(( ($devSize/512) - 1)) # start (size 1sec) $(( ($devSize/${disk[sectorSize]}) - 1)) # start (size 1sec)
x;f;r # expert mode ; fix order ; return x;f;r # expert mode ; fix order ; return
d;$(( (${#disk[mbrParts]} + 1) / 2 + 1 )) # delete ; part(last) d;$(( (${#disk[mbrParts]} + 1) / 2 + 1 )) # delete ; part(last)
@ -137,7 +140,7 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
${disk[extraFDiskCommands]} ${disk[extraFDiskCommands]}
p;w;q # print ; write ; quit p;w;q # print ; write ; quit
" | @{native.gnused}/bin/sed -E 's/^ *| *(#.*)?$//g' | @{native.gnused}/bin/sed -E 's/\n\n+| *; */\n/g' | tee >((echo -n '++ ' ; tr $'\n' '|' ; echo) 1>&2) | ( PATH=@{native.util-linux}/bin ; set -x ; fdisk "$blockDev" &>$beQuiet ) " | @{native.gnused}/bin/sed -E 's/^ *| *(#.*)?$//g' | @{native.gnused}/bin/sed -E 's/\n\n+| *; */\n/g' | tee >((echo -n '++ ' ; tr $'\n' '|' ; echo) 1>&2) | ( PATH=@{native.util-linux}/bin ; ${_set_x:-:} ; fdisk "$blockDev" &>$beLoud )
fi fi
)} )}
@ -154,7 +157,8 @@ function is-partition-on-disks {( set -eu # 1: partition, ...: blockDevs
## For each filesystem in »config.fileSystems« whose ».device« is in »/dev/disk/by-partlabel/«, this creates the specified file system on that partition. ## For each filesystem in »config.fileSystems« whose ».device« is in »/dev/disk/by-partlabel/«, this creates the specified file system on that partition.
function format-partitions {( set -eu function format-partitions {( set -eu
beQuiet=/dev/null ; if [[ ${args[debug]:-} ]] ; then beQuiet=/dev/stdout ; fi beLoud=/dev/null ; if [[ ${args[debug]:-} ]] ; then beLoud=/dev/stdout ; fi
beSilent=/dev/stderr ; if [[ ${args[quiet]:-} ]] ; then beSilent=/dev/null ; fi
for fsDecl in "@{config.fileSystems[@]}" ; do for fsDecl in "@{config.fileSystems[@]}" ; do
eval 'declare -A fs='"$fsDecl" eval 'declare -A fs='"$fsDecl"
if [[ ${fs[device]} == /dev/disk/by-partlabel/* ]] ; then if [[ ${fs[device]} == /dev/disk/by-partlabel/* ]] ; then
@ -162,7 +166,7 @@ function format-partitions {( set -eu
elif [[ ${fs[device]} == /dev/mapper/* ]] ; then elif [[ ${fs[device]} == /dev/mapper/* ]] ; then
if [[ ! @{config.boot.initrd.luks.devices!catAttrSets.device[${fs[device]/'/dev/mapper/'/}]:-} ]] ; then echo "LUKS device ${fs[device]} used by mount ${fs[mountPoint]} does not point at one of the device mappings ${!config.boot.initrd.luks.devices!catAttrSets.device[@]}" ; exit 1 ; fi if [[ ! @{config.boot.initrd.luks.devices!catAttrSets.device[${fs[device]/'/dev/mapper/'/}]:-} ]] ; then echo "LUKS device ${fs[device]} used by mount ${fs[mountPoint]} does not point at one of the device mappings ${!config.boot.initrd.luks.devices!catAttrSets.device[@]}" ; exit 1 ; fi
else continue ; fi else continue ; fi
( PATH=@{native.e2fsprogs}/bin:@{native.f2fs-tools}/bin:@{native.xfsprogs}/bin:@{native.dosfstools}/bin:$PATH ; set -x ; mkfs.${fs[fsType]} ${fs[formatOptions]} "${fs[device]}" >$beQuiet ) ( PATH=@{native.e2fsprogs}/bin:@{native.f2fs-tools}/bin:@{native.xfsprogs}/bin:@{native.dosfstools}/bin:$PATH ; ${_set_x:-:} ; mkfs.${fs[fsType]} ${fs[formatOptions]} "${fs[device]}" >$beLoud 2>$beSilent )
@{native.parted}/bin/partprobe "${fs[device]}" || true @{native.parted}/bin/partprobe "${fs[device]}" || true
done done
for swapDev in "@{config.swapDevices!catAttrs.device[@]}" ; do for swapDev in "@{config.swapDevices!catAttrs.device[@]}" ; do
@ -171,7 +175,7 @@ function format-partitions {( set -eu
elif [[ $swapDev == /dev/mapper/* ]] ; then elif [[ $swapDev == /dev/mapper/* ]] ; then
if [[ ! @{config.boot.initrd.luks.devices!catAttrSets.device[${swapDev/'/dev/mapper/'/}]:-} ]] ; then echo "LUKS device $swapDev used for SWAP does not point at one of the device mappings @{!config.boot.initrd.luks.devices!catAttrSets.device[@]}" ; exit 1 ; fi if [[ ! @{config.boot.initrd.luks.devices!catAttrSets.device[${swapDev/'/dev/mapper/'/}]:-} ]] ; then echo "LUKS device $swapDev used for SWAP does not point at one of the device mappings @{!config.boot.initrd.luks.devices!catAttrSets.device[@]}" ; exit 1 ; fi
else continue ; fi else continue ; fi
( set -x ; mkswap "$swapDev" >$beQuiet ) ( set -x ; mkswap "$swapDev" >$beLoud 2>$beSilent )
done done
)} )}

View File

@ -5,6 +5,7 @@
## Entry point to the installation, see »./README.md«. ## Entry point to the installation, see »./README.md«.
function install-system {( set -eu # 1: blockDev function install-system {( set -eu # 1: blockDev
trap - EXIT # start with empty traps for sub-shell
prepare-installer "$@" prepare-installer "$@"
do-disk-setup "${argv[0]}" do-disk-setup "${argv[0]}"
install-system-to $mnt install-system-to $mnt
@ -15,7 +16,7 @@ function prepare-installer { # ...
generic-arg-parse "$@" generic-arg-parse "$@"
beQuiet=/dev/null ; if [[ ${args[debug]:-} ]] ; then set -x ; beQuiet=/dev/stdout ; fi if [[ ${args[debug]:-} ]] ; then set -x ; fi
: ${argv[0]:?"Required: Target disk or image paths."} : ${argv[0]:?"Required: Target disk or image paths."}
@ -32,9 +33,15 @@ function prepare-installer { # ...
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." ; exit 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." ; exit 1 ; fi
done done
if [[ ${SUDO_USER:-} ]] ; then function nix {( set +x ; declare -a args=("$@") ; PATH=$hostPath su - "$SUDO_USER" -c "$(declare -p args)"' ; nix "${args[@]}"' )} ; fi if [[ ${SUDO_USER:-} ]] ; then # use Nix as the user who called this script, as Nix may not be set up for root
function nix {( set +x ; declare -a args=("$@") ; PATH=$hostPath su - "$SUDO_USER" -c "$(declare -p args)"' ; nix "${args[@]}"' )}
else # use Nix by absolute path, as it won't be on »$PATH«
PATH=$PATH:@{native.nix}/bin
fi
if [[ ${args[debug]:-} ]] ; then set +e ; set -E ; trap 'code= ; cat 2>/dev/null || true ; @{native.bashInteractive}/bin/bash --init-file @{config.environment.etc.bashrc.source} || code=$? ; if [[ $code ]] ; then exit $code ; fi' ERR ; fi # On error, instead of exiting straight away, open a shell to allow diagnosing/fixing the issue. Only exit if that shell reports failure (e.g. CtrlC + CtrlD). Unfortunately, the exiting has to be repeated for level of each nested sub-shells. The cat eats anything lined up on stdin, which would otherwise be run in the shell (TODO: but it blocks if there is nothing on stdin, requiring Ctrl+D to be pressed). _set_x='set -x' ; if [[ ${args[quiet]:-} ]] ; then _set_x=: ; fi
if [[ ${args[debug]:-} ]] ; then set +e ; set -E ; trap 'code= ; timeout .2s cat &>/dev/null || true ; @{native.bashInteractive}/bin/bash --init-file @{config.environment.etc.bashrc.source} || code=$? ; if [[ $code ]] ; then exit $code ; fi' ERR ; fi # On error, instead of exiting straight away, open a shell to allow diagnosing/fixing the issue. Only exit if that shell reports failure (e.g. CtrlC + CtrlD). Unfortunately, the exiting has to be repeated for each level of each nested sub-shells. The »timeout cat« eats anything lined up on stdin, which would otherwise be sent to bash and interpreted as commands.
export PATH=$PATH:@{native.util-linux}/bin # Doing a system installation requires a lot of stuff from »util-linux«. This should probably be moved into the individual functions that actually use the tools ... export PATH=$PATH:@{native.util-linux}/bin # Doing a system installation requires a lot of stuff from »util-linux«. This should probably be moved into the individual functions that actually use the tools ...
@ -48,9 +55,9 @@ function nixos-install-cmd {( set -eu # 1: mnt, 2: topLevel
## Copies the system's dependencies to the disks mounted at »$mnt« and installs the bootloader. If »$inspect« is set, a root shell will be opened in »$mnt« afterwards. ## Copies the system's dependencies to the disks mounted at »$mnt« and installs the bootloader. If »$inspect« is set, a root shell will be opened in »$mnt« afterwards.
# »$topLevel« may point to an alternative top-level dependency to install. # »$topLevel« may point to an alternative top-level dependency to install.
function install-system-to {( set -eu # 1: mnt, 2?: topLevel function install-system-to {( set -eu # 1: mnt
mnt=$1 ; topLevel=${2:-} mnt=$1 ; topLevel=${2:-}
targetSystem=@{config.system.build.toplevel} targetSystem=${args[toplevel]:-@{config.system.build.toplevel}}
trap - EXIT # start with empty traps for sub-shell trap - EXIT # start with empty traps for sub-shell
# Link/create files that some tooling expects: # Link/create files that some tooling expects:
@ -79,7 +86,7 @@ function install-system-to {( set -eu # 1: mnt, 2?: topLevel
# Copy system closure to new nix store: # Copy system closure to new nix store:
if [[ ${SUDO_USER:-} ]] ; then chown -R $SUDO_USER: $mnt/nix/store $mnt/nix/var ; fi if [[ ${SUDO_USER:-} ]] ; then chown -R $SUDO_USER: $mnt/nix/store $mnt/nix/var ; fi
( set -x ; time nix --extra-experimental-features nix-command copy --no-check-sigs --to $mnt ${topLevel:-$targetSystem} ) ; rm -rf $mnt/nix/var/nix/gcroots ( cmd=( nix --extra-experimental-features nix-command --offline copy --no-check-sigs --to $mnt ${topLevel:-$targetSystem} ) ; if [[ ${args[quiet]:-} ]] ; then "${cmd[@]}" ; else set -x ; time "${cmd[@]}" ; fi ) ; rm -rf $mnt/nix/var/nix/gcroots
# TODO: if the target has @{config.nix.autoOptimiseStore} and the host doesn't (there is no .links dir?), optimize now # TODO: if the target has @{config.nix.autoOptimiseStore} and the host doesn't (there is no .links dir?), optimize now
if [[ ${SUDO_USER:-} ]] ; then chown -R root:root $mnt/nix $mnt/nix/var ; chown :30000 $mnt/nix/store ; fi if [[ ${SUDO_USER:-} ]] ; then chown -R root:root $mnt/nix $mnt/nix/var ; chown :30000 $mnt/nix/store ; fi

View File

@ -35,7 +35,10 @@ function populate-keystore { { # (void)
done done
for usage in "${!methods[@]}" ; do for usage in "${!methods[@]}" ; do
if [[ "${methods[$usage]}" == home-composite || "${methods[$usage]}" == copy ]] ; then continue ; fi if [[ "${methods[$usage]}" == home-composite || "${methods[$usage]}" == copy ]] ; then continue ; fi
gen-key-"${methods[$usage]}" "$usage" "${options[$usage]}" | write-secret "$keystore"/"$usage".key || return 1 for attempt in 2 3 x ; do
if gen-key-"${methods[$usage]}" "$usage" "${options[$usage]}" | write-secret "$keystore"/"$usage".key ; then break ; fi
if [[ $attempt == x ]] ; then return 1 ; fi ; echo "Retrying ($attempt/3):"
done
done done
for usage in "${!methods[@]}" ; do for usage in "${!methods[@]}" ; do
if [[ "${methods[$usage]}" != home-composite ]] ; then continue ; fi if [[ "${methods[$usage]}" != home-composite ]] ; then continue ; fi
@ -57,10 +60,10 @@ function create-luks-layers {( set -eu # (void)
primaryKey="$keystore"/luks/"$luksName"/0.key primaryKey="$keystore"/luks/"$luksName"/0.key
keyOptions=( --pbkdf=pbkdf2 --pbkdf-force-iterations=1000 ) keyOptions=( --pbkdf=pbkdf2 --pbkdf-force-iterations=1000 )
( PATH=@{native.cryptsetup}/bin ; set -x ; cryptsetup --batch-mode luksFormat --key-file="$primaryKey" "${keyOptions[@]}" -c aes-xts-plain64 -s 512 -h sha256 "$rawDev" ) ( PATH=@{native.cryptsetup}/bin ; ${_set_x:-:} ; cryptsetup --batch-mode luksFormat --key-file="$primaryKey" "${keyOptions[@]}" -c aes-xts-plain64 -s 512 -h sha256 "$rawDev" )
for index in 1 2 3 4 5 6 7 ; do for index in 1 2 3 4 5 6 7 ; do
if [[ -e "$keystore"/luks/"$luksName"/"$index".key ]] ; then if [[ -e "$keystore"/luks/"$luksName"/"$index".key ]] ; then
( PATH=@{native.cryptsetup}/bin ; set -x ; cryptsetup luksAddKey --key-file="$primaryKey" "${keyOptions[@]}" "$rawDev" "$keystore"/luks/"$luksName"/"$index".key ) ( PATH=@{native.cryptsetup}/bin ; ${_set_x:-:} ; cryptsetup luksAddKey --key-file="$primaryKey" "${keyOptions[@]}" "$rawDev" "$keystore"/luks/"$luksName"/"$index".key )
fi fi
done done
done done
@ -74,7 +77,7 @@ function open-luks-layers { # (void)
rawDev=@{config.boot.initrd.luks.devices!catAttrSets.device[$luksName]} rawDev=@{config.boot.initrd.luks.devices!catAttrSets.device[$luksName]}
primaryKey="$keystore"/luks/"$luksName"/0.key primaryKey="$keystore"/luks/"$luksName"/0.key
( PATH=@{native.cryptsetup}/bin ; set -x ; cryptsetup --batch-mode luksOpen --key-file="$primaryKey" "$rawDev" "$luksName" ) && ( PATH=@{native.cryptsetup}/bin ; ${_set_x:-:} ; cryptsetup --batch-mode luksOpen --key-file="$primaryKey" "$rawDev" "$luksName" ) &&
prepend_trap "@{native.cryptsetup}/bin/cryptsetup close $luksName" EXIT prepend_trap "@{native.cryptsetup}/bin/cryptsetup close $luksName" EXIT
done done
} }

View File

@ -74,10 +74,11 @@ function run-qemu {( set -eu # 1: diskImages
done done
if [[ @{config.boot.loader.systemd-boot.enable} || ${args[efi]:-} ]] ; then # UEFI. Otherwise it boots something much like a classic BIOS? if [[ @{config.boot.loader.systemd-boot.enable} || ${args[efi]:-} ]] ; then # UEFI. Otherwise it boots something much like a classic BIOS?
#qemu+=( -bios @{pkgs.OVMF.fd}/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). ovmf=$( @{native.nixVersions.nix_2_9}/bin/nix --extra-experimental-features 'nix-command flakes' build --no-link --print-out-paths @{inputs.nixpkgs}'#'legacyPackages.@{pkgs.system}.OVMF.fd )
qemu+=( -drive file=@{pkgs.OVMF.fd}/FV/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on ) #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+=( -drive file=/tmp/qemu-@{config.networking.hostName}-VARS.fd,if=pflash,format=raw,unit=1 ) qemu+=( -drive file=${ovmf}/FV/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on )
if [[ ! -e /tmp/qemu-@{config.networking.hostName}-VARS.fd ]] ; then cat @{pkgs.OVMF.fd}/FV/OVMF_VARS.fd > /tmp/qemu-@{config.networking.hostName}-VARS.fd ; fi qemu+=( -drive file="${args[efi-vars]:-/tmp/qemu-@{config.networking.hostName}-VARS.fd}",if=pflash,format=raw,unit=1 )
if [[ ! -e "${args[efi-vars]:-/tmp/qemu-@{config.networking.hostName}-VARS.fd}" ]] ; then cat ${ovmf}/FV/OVMF_VARS.fd > "${args[efi-vars]:-/tmp/qemu-@{config.networking.hostName}-VARS.fd}" ; fi
# https://lists.gnu.org/archive/html/qemu-discuss/2018-04/msg00045.html # https://lists.gnu.org/archive/html/qemu-discuss/2018-04/msg00045.html
fi fi
if [[ @{config.wip.preface.hardware} == aarch64 ]] ; then if [[ @{config.wip.preface.hardware} == aarch64 ]] ; then
@ -87,7 +88,7 @@ function run-qemu {( set -eu # 1: diskImages
# Add »config.boot.kernelParams = [ "console=tty1" "console=ttyS0" ]« to log to serial (»ttyS0«) and/or the display (»tty1«), preferring the last »console« option for the initrd shell (if enabled and requested). # Add »config.boot.kernelParams = [ "console=tty1" "console=ttyS0" ]« to log to serial (»ttyS0«) and/or the display (»tty1«), preferring the last »console« option for the initrd shell (if enabled and requested).
logSerial= ; if [[ ' '"@{config.boot.kernelParams[@]}"' ' == *' console=ttyS0'@( |,)* ]] ; then logSerial=1 ; fi logSerial= ; if [[ ' '"@{config.boot.kernelParams[@]}"' ' == *' console=ttyS0'@( |,)* ]] ; then logSerial=1 ; fi
logScreen= ; if [[ ' '"@{config.boot.kernelParams[@]}"' ' == *' console=tty1 '* ]] ; then logScreen=1 ; fi logScreen= ; if [[ ' '"@{config.boot.kernelParams[@]}"' ' == *' console=tty1 '* ]] ; then logScreen=1 ; fi
if [[ $logSerial ]] ; then if [[ ! ${args[no-serial]:-} && $logSerial ]] ; then
if [[ $logScreen || ${args[graphic]:-} ]] ; then if [[ $logScreen || ${args[graphic]:-} ]] ; then
qemu+=( -serial mon:stdio ) qemu+=( -serial mon:stdio )
else else

View File

@ -19,6 +19,40 @@ function generic-arg-parse { # ...
shift ; done shift ; done
} }
## Shows the help text for a program and exits, if »--help« was passed as argument and parsed, or does nothing otherwise.
# Expects to be called between parsing and verifying the arguments.
# Uses »allowedArgs« for the list of the named arguments (the values are the descriptions).
# »name« should be the program name/path (usually »$0«), »args« the form/names of any positional arguments expected (e.g. »SOURCE... DEST«) and is included in the "Usage" description,
# »description« the introductory text shown before the "Usage", and »suffix« any text printed after the argument list.
function generic-arg-help { # 1: name, 2?: args, 3?: description, 4?: suffix
if [[ ! ${args[help]:-} ]] ; then : ${allowedArgs[help]:=1} ; return 0 ; fi
[[ ! ${3:-} ]] || echo "$3"
printf 'Usage:\n %s [ARG[=value]]... [--] %s\n\nWhere »ARG« may be any of:\n' "$1" "${2:-}"
local name ; while IFS= read -r name ; do
printf ' %s\n %s\n' "$name" "${allowedArgs[$name]}"
done < <( printf '%s\n' "${!allowedArgs[@]}" | LC_ALL=C sort )
printf ' %s\n %s\n' "--help" "Do nothing but print this message and exit with success."
[[ ! ${4:-} ]] || echo "$4"
exit 0
}
## Performs a basic verification of the named arguments passed by the user and parsed by »generic-arg-parse« against the names in »allowedArgs«.
# Entries in »allowedArgs« should have the form »[--name]="description"« for boolean flags, and »[--name=VAL]="description"« for string arguments.
# »description« is used by »generic-arg-help«. Boolean flags may only have the values »1« (as set by »generic-ags-parse« for flags without value) or be empty.
# »VAL« is purely nominal. Any argument passed that is not in »allowedArgs« raises an error.
function generic-arg-verify { # 1: exitCode
local exitCode=${1:-1}
local names=' '"${!allowedArgs[@]}"
for name in "${!args[@]}" ; do
if [[ ${allowedArgs[--$name]:-} ]] ; then
if [[ ${args[$name]} == '' || ${args[$name]} == 1 ]] ; then continue ; fi
echo "Argument »--$name« should be a boolean, but its value is: ${args[$name]}" ; return $exitCode
fi
if [[ $names == *' --'"$name"'='* || $names == *' --'"$name"'[='* ]] ; then continue ; fi
echo "Unexpected argument »--$name«.${allowedArgs[help]:+ Call with »--help« for a list of valid arguments.}" ; return $exitCode
done
}
## Prepends a command to a trap. Especially useful fo define »finally« commands via »prepend_trap '<command>' EXIT«. ## Prepends a command to a trap. Especially useful fo define »finally« commands via »prepend_trap '<command>' EXIT«.
# NOTE: When calling this in a sub-shell whose parents already has traps installed, make sure to do »trap - trapName« first. On a new shell, this should be a no-op, but without it, the parent shell's traps will be added to the sub-shell as well (due to strange behavior of »trap -p« (in bash ~5.1.8)). # NOTE: When calling this in a sub-shell whose parents already has traps installed, make sure to do »trap - trapName« first. On a new shell, this should be a no-op, but without it, the parent shell's traps will be added to the sub-shell as well (due to strange behavior of »trap -p« (in bash ~5.1.8)).
function prepend_trap { # 1: command, ...: trapNames function prepend_trap { # 1: command, ...: trapNames

View File

@ -20,7 +20,7 @@ function create-zpools { # 1: mnt
if ! is-partition-on-disks "$part" "${blockDevs[@]}" ; then echo "Partition alias $part used by zpool ${pool[name]} does not point at one of the target disks ${blockDevs[@]}" ; exit 1 ; fi if ! is-partition-on-disks "$part" "${blockDevs[@]}" ; then echo "Partition alias $part used by zpool ${pool[name]} does not point at one of the target disks ${blockDevs[@]}" ; exit 1 ; fi
fi fi
done done
( PATH=@{native.zfs}/bin ; set -x ; zpool create "${args[@]}" -R "$mnt" "${pool[name]}" "${vdevs[@]}" ) ( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zpool create "${args[@]}" -R "$mnt" "${pool[name]}" "${vdevs[@]}" )
) && { ) && {
prepend_trap "@{native.zfs}/bin/zpool export '$poolName'" EXIT prepend_trap "@{native.zfs}/bin/zpool export '$poolName'" EXIT
} ; done && } ; done &&
@ -62,7 +62,7 @@ function ensure-datasets {( set -eu # 1: mnt, 2?: filterExp
names=$(IFS=, ; echo "${!props[*]}") ; values=$(IFS=$'\n' ; echo "${props[*]}") names=$(IFS=, ; echo "${!props[*]}") ; values=$(IFS=$'\n' ; echo "${props[*]}")
if [[ $values != "$($zfs get -o value -H "$names" "${dataset[name]}")" ]] ; then ( if [[ $values != "$($zfs get -o value -H "$names" "${dataset[name]}")" ]] ; then (
declare -a args=( ) ; for name in "${!props[@]}" ; do args+=( "${name}=${props[$name]}" ) ; done declare -a args=( ) ; for name in "${!props[@]}" ; do args+=( "${name}=${props[$name]}" ) ; done
( PATH=@{native.zfs}/bin ; set -x ; zfs set "${args[@]}" "${dataset[name]}" ) ( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs set "${args[@]}" "${dataset[name]}" )
) ; fi ) ; fi
if [[ $cryptRoot && $($zfs get -o value -H encryptionroot "${dataset[name]}") != "$cryptRoot" ]] ; then ( # inherit key from parent (which the parent would also already have done if necessary) if [[ $cryptRoot && $($zfs get -o value -H encryptionroot "${dataset[name]}") != "$cryptRoot" ]] ; then ( # inherit key from parent (which the parent would also already have done if necessary)
@ -72,28 +72,28 @@ function ensure-datasets {( set -eu # 1: mnt, 2?: filterExp
if [[ $($zfs get -o value -H keystatus "${dataset[name]}") != available ]] ; then if [[ $($zfs get -o value -H keystatus "${dataset[name]}") != available ]] ; then
$zfs load-key -L file://"$cryptKey" "${dataset[name]}" # will unload with cryptRoot $zfs load-key -L file://"$cryptKey" "${dataset[name]}" # will unload with cryptRoot
fi fi
( PATH=@{native.zfs}/bin ; set -x ; zfs change-key -i "${dataset[name]}" ) ( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs change-key -i "${dataset[name]}" )
) ; fi ) ; fi
else ( # create dataset else ( # create dataset
if [[ ${props[keyformat]:-} == ephemeral ]] ; then if [[ ${props[keyformat]:-} == ephemeral ]] ; then
props[encryption]=aes-256-gcm ; props[keyformat]=hex ; props[keylocation]=file:///dev/stdin ; explicitKeylocation=file:///dev/null props[encryption]=aes-256-gcm ; props[keyformat]=hex ; props[keylocation]=file:///dev/stdin ; explicitKeylocation=file:///dev/null
declare -a args=( ) ; for name in "${!props[@]}" ; do args+=( -o "${name}=${props[$name]}" ) ; done declare -a args=( ) ; for name in "${!props[@]}" ; do args+=( -o "${name}=${props[$name]}" ) ; done
</dev/urandom tr -dc 0-9a-f | head -c 64 | ( PATH=@{native.zfs}/bin ; set -x ; zfs create "${args[@]}" "${dataset[name]}" ) </dev/urandom tr -dc 0-9a-f | head -c 64 | ( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs create "${args[@]}" "${dataset[name]}" )
$zfs unload-key "${dataset[name]}" $zfs unload-key "${dataset[name]}"
else else
if [[ $cryptRoot && $cryptRoot != ${dataset[name]} && $($zfs get -o value -H keystatus "$cryptRoot") != available ]] ; then if [[ $cryptRoot && $cryptRoot != ${dataset[name]} && $($zfs get -o value -H keystatus "$cryptRoot") != available ]] ; then
$zfs load-key -L file://"$cryptKey" "$cryptRoot" ; trap "$zfs unload-key $cryptRoot || true" EXIT $zfs load-key -L file://"$cryptKey" "$cryptRoot" ; trap "$zfs unload-key $cryptRoot || true" EXIT
fi fi
declare -a args=( ) ; for name in "${!props[@]}" ; do args+=( -o "${name}=${props[$name]}" ) ; done declare -a args=( ) ; for name in "${!props[@]}" ; do args+=( -o "${name}=${props[$name]}" ) ; done
( PATH=@{native.zfs}/bin ; set -x ; zfs create "${args[@]}" "${dataset[name]}" ) ( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs create "${args[@]}" "${dataset[name]}" )
fi fi
if [[ ${props[canmount]} != off ]] ; then ( if [[ ${props[canmount]} != off ]] ; then (
mount -t zfs -o zfsutil "${dataset[name]}" $tmpMnt ; trap "umount '${dataset[name]}'" EXIT mount -t zfs -o zfsutil "${dataset[name]}" $tmpMnt ; trap "umount '${dataset[name]}'" EXIT
chmod 000 "$tmpMnt" ; ( chown "${dataset[uid]}:${dataset[gid]}" -- "$tmpMnt" ; chmod "${dataset[mode]}" -- "$tmpMnt" ) chmod 000 "$tmpMnt" ; ( chown "${dataset[uid]}:${dataset[gid]}" -- "$tmpMnt" ; chmod "${dataset[mode]}" -- "$tmpMnt" )
) ; fi ) ; fi
if [[ $explicitKeylocation && $explicitKeylocation != "${props[keylocation]:-}" ]] ; then if [[ $explicitKeylocation && $explicitKeylocation != "${props[keylocation]:-}" ]] ; then
( PATH=@{native.zfs}/bin ; set -x ; zfs set keylocation="$explicitKeylocation" "${dataset[name]}" ) ( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs set keylocation="$explicitKeylocation" "${dataset[name]}" )
fi fi
$zfs snapshot -r "${dataset[name]}"@empty $zfs snapshot -r "${dataset[name]}"@empty
) ; fi ) ; fi
@ -101,7 +101,7 @@ function ensure-datasets {( set -eu # 1: mnt, 2?: filterExp
eval 'declare -A allows='"${dataset[permissions]}" eval 'declare -A allows='"${dataset[permissions]}"
for who in "${!allows[@]}" ; do for who in "${!allows[@]}" ; do
# »zfs allow $dataset« seems to be the only way to view permissions, and that is not very parsable -.- # »zfs allow $dataset« seems to be the only way to view permissions, and that is not very parsable -.-
( PATH=@{native.zfs}/bin ; set -x ; zfs allow -$who "${allows[$who]}" "${dataset[name]}" >&2 ) ( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs allow -$who "${allows[$who]}" "${dataset[name]}" >&2 )
done done
done done

View File

@ -2,7 +2,7 @@
# NixOS Modules # NixOS Modules
A NixOS module is a collection of any number of NixOS option definitions and value assignments to those or other options. A NixOS module is a collection of any number of NixOS option definitions and value assignments to those or other options.
While the set of imported modules and thereby defined options is static (in this case starting with the modules passed to `mkNixosSystem` in `../flake.nix`), the value assignments can generally be contingent on other values (as long as there are no logical loops), making for a highly flexible system construction. While the set of imported modules, and thereby that of the defined options, is static (in this case starting with the modules passed to `mkNixosSystem` in `../flake.nix`), the value assignments can generally be contingent on other values (as long as there are no logical loops), making for a highly flexible system construction.
Since modules can't be imported (or excluded) dynamically, most modules have an `enable` option, which, if false, effectively disables whatever that module does. Since modules can't be imported (or excluded) dynamically, most modules have an `enable` option, which, if false, effectively disables whatever that module does.
Ultimately, the goal of a NixOS configuration is to build an operating system, which is basically a structured collection of program and configuration files. Ultimately, the goal of a NixOS configuration is to build an operating system, which is basically a structured collection of program and configuration files.

View File

@ -23,6 +23,7 @@ in {
name = lib.mkOption { description = "Name that this device is being referred to as in other places."; type = lib.types.str; default = name; readOnly = true; }; name = lib.mkOption { description = "Name that this device is being referred to as in other places."; type = lib.types.str; default = name; readOnly = true; };
guid = lib.mkOption { description = "GPT disk GUID of the disk."; type = types.guid; default = lib.wip.sha256guid ("gpt-disk:${name}"+":${globalConfig.networking.hostName}"); }; guid = lib.mkOption { description = "GPT disk GUID of the disk."; type = types.guid; default = lib.wip.sha256guid ("gpt-disk:${name}"+":${globalConfig.networking.hostName}"); };
size = lib.mkOption { description = "The size of the disk, either as number in bytes or as argument to »parseSizeSuffix«. When installing to a physical device, its size must match; images are created with this size."; type = lib.types.either lib.types.ints.unsigned lib.types.str; apply = lib.wip.parseSizeSuffix; default = "16G"; }; size = lib.mkOption { description = "The size of the disk, either as number in bytes or as argument to »parseSizeSuffix«. When installing to a physical device, its size must match; images are created with this size."; type = lib.types.either lib.types.ints.unsigned lib.types.str; apply = lib.wip.parseSizeSuffix; default = "16G"; };
sectorSize = lib.mkOption { description = "The disk's logical sector size in bytes. Used to convert (e.g.) partition sizes in sectors to bytes or vice versa."; type = lib.types.ints.unsigned; default = 512; };
allowLarger = lib.mkOption { description = "Whether to allow installation to a physical disk that is larger than the declared size."; type = lib.types.bool; default = true; }; allowLarger = lib.mkOption { description = "Whether to allow installation to a physical disk that is larger than the declared size."; type = lib.types.bool; default = true; };
serial = lib.mkOption { description = "Serial number of the specific hardware device to use. If set the device path passed to the installer must point to the device with this serial. Use » udevadm info --query=property --name=$DISK | grep -oP 'ID_SERIAL_SHORT=\K.*' || echo '<none>' « to get the serial."; type = lib.types.nullOr lib.types.str; default = null; }; serial = lib.mkOption { description = "Serial number of the specific hardware device to use. If set the device path passed to the installer must point to the device with this serial. Use » udevadm info --query=property --name=$DISK | grep -oP 'ID_SERIAL_SHORT=\K.*' || echo '<none>' « to get the serial."; type = lib.types.nullOr lib.types.str; default = null; };
alignment = lib.mkOption { description = "Default alignment quantifier for partitions on this device. Should be at least the optimal physical write size of the device, but going larger at worst wastes this many times the number of partitions disk sectors."; type = lib.types.int; default = 16384; }; alignment = lib.mkOption { description = "Default alignment quantifier for partitions on this device. Should be at least the optimal physical write size of the device, but going larger at worst wastes this many times the number of partitions disk sectors."; type = lib.types.int; default = 16384; };
@ -73,7 +74,7 @@ in {
esc = lib.escapeShellArg; esc = lib.escapeShellArg;
in pkgs.runCommand "partitioning-${config.networking.hostName}" { } '' in pkgs.runCommand "partitioning-${config.networking.hostName}" { } ''
${lib.wip.substituteImplicit { inherit pkgs; scripts = [ partition-disk ]; context = { inherit config; native = pkgs; }; }} # inherit (builtins) trace; ${lib.wip.substituteImplicit { inherit pkgs; scripts = [ partition-disk ]; context = { inherit config; native = pkgs; }; }} # inherit (builtins) trace;
mkdir $out ; beQuiet=/dev/stdout mkdir $out ; declare -A args=([debug]=1)
${lib.concatStrings (lib.mapAttrsToList (name: disk: '' ${lib.concatStrings (lib.mapAttrsToList (name: disk: ''
name=${esc name} ; img=$name.img name=${esc name} ; img=$name.img
${pkgs.coreutils}/bin/truncate -s ${esc disk.size} "$img" ${pkgs.coreutils}/bin/truncate -s ${esc disk.size} "$img"

View File

@ -31,7 +31,8 @@ in {
owner = "sbabic"; repo = pname; rev = "ba7564f5006d09bec51058cf4f5ac90d4dc18b3c"; # 2018-11-18 owner = "sbabic"; repo = pname; rev = "ba7564f5006d09bec51058cf4f5ac90d4dc18b3c"; # 2018-11-18
hash = "sha256-6cHkr3s7/2BVXBTn9bUfPFbYAfv9VYh6C9GAbWILNjs="; hash = "sha256-6cHkr3s7/2BVXBTn9bUfPFbYAfv9VYh6C9GAbWILNjs=";
}; };
nativeBuildInputs = [ pkgs.cmake pkgs.zlib ]; nativeBuildInputs = [ pkgs.cmake ];
buildInputs = [ pkgs.cmake pkgs.zlib ];
outputs = [ "out" "lib" ]; outputs = [ "out" "lib" ];
meta = { meta = {

View File

@ -52,4 +52,8 @@ in {
printf "%s\n%s\n" "#define CONFIG_EXTRA_ENV_SETTINGS $CONFIG_EXTRA_ENV_SETTINGS" "$(cat include/env_default.h)" >include/env_default.h printf "%s\n%s\n" "#define CONFIG_EXTRA_ENV_SETTINGS $CONFIG_EXTRA_ENV_SETTINGS" "$(cat include/env_default.h)" >include/env_default.h
''; '';
}); });
ubootTools = prev.ubootTools.overrideAttrs (old: {
buildInputs = (old.buildInputs or [ ]) ++ [ final.openssl ];
});
} }