fix gptfdisk, integrate some bash aliases

This commit is contained in:
Niklas Gollenstede 2023-01-03 18:14:25 +01:00
parent a4ae2ab551
commit e3b5b17620
13 changed files with 260 additions and 80 deletions

Binary file not shown.

View File

@ -9,18 +9,19 @@ Just to provide an example of what a host configuration using this set of librar
To prepare a virtual machine disk, as `sudo` user with `nix` installed, run in `..`:
```bash
nix run '.#example' -- sudo install-system /home/$(id -un)/vm/disks/example.img && sudo chown $(id -un): /home/$(id -un)/vm/disks/example.img
nix run '.#example-raidz' -- sudo install-system /tmp/nixos-main.img:raidz1=/tmp/nixos-rz1.img:raidz2=/tmp/nixos-rz2.img:raidz3=/tmp/nixos-rz3.img
nix run '.#example' -- sudo install-system /home/$(id -un)/vm/disks/example/
( sudo chown $(id -un): /home/$(id -un)/vm/disks/example/* )
nix run '.#example-raidz' -- sudo install-system /tmp/nixos-example-raidz/
```
Then to boot the system in a qemu VM with KVM:
```bash
nix run '.#example' -- sudo run-qemu /home/$(id -un)/vm/disks/example.img
nix run '.#example' -- sudo run-qemu /home/$(id -un)/vm/disks/example/
```
Or as user with vBox access, run this and use the UI or the printed commands:
```bash
nix run '.#example' -- register-vbox /home/$(id -un)/vm/disks/example.img
nix run '.#example' -- register-vbox /home/$(id -un)/vm/disks/example/primary.img
```
Alternative to running directly as `root` (esp. if `nix` is not installed for root), the above commands can also be run with `sudo` as additional argument before the `--`.
Alternative to running with `sudo` (if `nix` is installed for root), the above commands can also be run as `root` without the `sudo` argument.
## Implementation
@ -31,7 +32,7 @@ dirname: inputs: { config, pkgs, lib, name, ... }: let inherit (inputs.self) lib
#suffix = builtins.head (builtins.match ''example-(.*)'' name); # make differences in config based on this when using »wip.preface.instances«
hash = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName);
in { imports = [ ({ ## Hardware
wip.preface.instances = [ "example-explicit" "example" "example-minimal" "example-raidz" ];
wip.preface.instances = [ "example-explicit" "example" "example-minimal" "example-raidz" "test-zfs-hibernate" ];
wip.preface.hardware = "x86_64"; system.stateVersion = "22.05";
@ -60,7 +61,7 @@ in { imports = [ ({ ## Hardware
fileSystems."/nix/store" = { options = ["bind,ro"]; device = "/system/nix/store"; neededForBoot = true; };
}) (lib.mkIf (name == "example") { ## More complex but automatic FS setup
}) (lib.mkIf (name == "example" || name == "test-zfs-hibernate") { ## More complex but automatic FS setup
#wip.fs.disks.devices.primary.size = "16G"; # (default)
wip.fs.boot.enable = true; wip.fs.boot.size = "512M";
@ -68,6 +69,10 @@ in { imports = [ ({ ## Hardware
wip.fs.keystore.enable = true;
wip.fs.temproot.enable = true;
wip.fs.temproot.swap.size = "2G";
wip.fs.temproot.swap.asPartition = true;
wip.fs.temproot.swap.encrypted = true;
wip.fs.temproot.temp.type = "tmpfs";
wip.fs.temproot.local.type = "bind";
@ -143,4 +148,28 @@ in { imports = [ ({ ## Hardware
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
}) (lib.mkIf (name == "test-zfs-hibernate") {
# This was an attempt to reliably get ZFS to corrupt when importing a ZFS pool before resuming from hibernation in initrd. It isn't reproducible, though: https://github.com/NixOS/nixpkgs/pull/208037#issuecomment-1368240321
wip.fs.temproot.temp.type = lib.mkForce "zfs";
wip.fs.temproot.local.type = lib.mkForce "zfs";
wip.fs.keystore.keys."luks/rpool-${hash}/0" = lib.mkForce null;
wip.fs.disks.devices.mirror.size = "16G";
wip.fs.disks.partitions."mirror-${hash}" = { type = "bf00"; disk = "mirror"; };
environment.systemPackages = [ (pkgs.writeShellScriptBin "test-zfs-hibernate" ''
set -ex
</dev/urandom head -c 10G >/tmp/dump
sync ; echo 3 > /proc/sys/vm/drop_caches ; sleep 5
zpool attach rpool-${hash} /dev/disk/by-partlabel/rpool-${hash} /dev/disk/by-partlabel/mirror-${hash}
sleep 2 # the above command should still be in progress
: before ; date
systemctl hibernate
: hibernating ; date
sleep 3 ; : awake ; date
'') ];
boot.zfs = if (builtins.substring 0 5 inputs.nixpkgs.lib.version) == "22.05" then { } else { allowHibernation = true; };
}) ]; }

View File

@ -1,7 +1,7 @@
dirname: inputs@{ self, nixpkgs, ...}: let
inherit (nixpkgs) lib;
inherit (import "${dirname}/vars.nix" dirname inputs) mapMerge mapMergeUnique mergeAttrsUnique flipNames;
inherit (import "${dirname}/imports.nix" dirname inputs) getModifiedPackages getNixFiles importWrapped;
inherit (import "${dirname}/imports.nix" dirname inputs) getNixFiles importWrapped;
inherit (import "${dirname}/scripts.nix" dirname inputs) substituteImplicit;
setup-scripts = (import "${dirname}/setup-scripts" "${dirname}/setup-scripts" inputs);
prefix = inputs.config.prefix;
@ -34,14 +34,12 @@ in rec {
importRepo = inputs: repoPath': outputs: let
repoPath = builtins.path { path = repoPath'; name = "source"; }; # referring to the current flake directory as »./.« is quite intuitive (and »inputs.self.outPath« causes infinite recursion), but without this it adds another hash to the path (because it copies it)
in let result = (outputs (
(let it = importWrapped inputs "${repoPath}/lib"; in if it.exists then rec {
(let it = importWrapped inputs "${repoPath}/lib"; in if it.exists then {
lib = it.result;
} else { }) // (let it = importWrapped inputs "${repoPath}/overlays"; in if it.exists then rec {
overlays = it.result;
overlay = final: prev: builtins.foldl' (prev: overlay: prev // (overlay final prev)) prev (builtins.attrValues overlays);
} else { }) // (let it = importWrapped inputs "${repoPath}/modules"; in if it.exists then rec {
nixosModules = it.result;
nixosModule = { imports = builtins.attrValues nixosModules; };
} else { }) // (let it = importWrapped inputs "${repoPath}/overlays"; in if it.exists then {
overlays = { default = final: prev: builtins.foldl' (prev: overlay: prev // (overlay final prev)) prev (builtins.attrValues it.result); } // it.result;
} else { }) // (let it = importWrapped inputs "${repoPath}/modules"; in if it.exists then {
nixosModules = { default = { imports = builtins.attrValues it.result; }; } // it.result;
} else { })
)); in if (builtins.isList result) then mergeOutputs result else result;
@ -70,7 +68,7 @@ in rec {
imported = (importWrapped inputs entryPath).required ({ config = null; pkgs = null; lib = null; name = null; nodes = null; extraModules = null; } // args);
module = builtins.elemAt imported.imports 0; props = module.${prefix}.preface;
in if (
imported?imports && (builtins.isList imported.imports) && (imported.imports != [ ]) && module?${prefix} && module.${prefix}?preface && props?hardware
imported?imports && (builtins.isList imported.imports) && (imported.imports != [ ]) && module?${prefix}.preface && props?hardware
) then (props) else throw "File ${entryPath} must fulfill the structure: dirname: inputs: { ... }: { imports = [ { ${prefix}.preface = { hardware = str; ... } } ]; }";
# Builds the System Configuration for a single host. Since each host depends on the context of all other host (in the same "network"), this is essentially only callable through »mkNixosConfigurations«.
@ -91,6 +89,7 @@ in rec {
]; _file = "${dirname}/flakes.nix#modules"; } ];
extraModules = modules ++ [ { imports = [ ({ # These are passed as »extraModules« module argument and can thus conveniently be reused when defining containers and such (Therefore define as much stuff as possible here).
# TODO: or should these be set as »baseModules«? Does that implicitly pass these into any derived configs?
}) ({ config, ... }: {
@ -102,7 +101,7 @@ in rec {
self = null; # avoid changing (and thus restarting) the containers on every trivial change
} else inputs);
system.nixos.revision = lib.mkIf (inputs?nixpkgs && inputs.nixpkgs?rev) inputs.nixpkgs.rev; # (evaluating the default value fails under some circumstances)
system.nixos.revision = lib.mkIf (inputs?nixpkgs.rev) inputs.nixpkgs.rev; # (evaluating the default value fails under some circumstances)
}) ({
options.${prefix}.preface.hardware = lib.mkOption { description = "The name of the system's CPU instruction set (the first part of what is often referred to as »system«)."; type = lib.types.str; readOnly = true; };
@ -167,11 +166,11 @@ in rec {
# 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.
systems ? ({ dir = "${inputs.self}/hosts"; exclude = [ ]; }),
# List of overlays to set as »config.nixpkgs.overlays«. Defaults to ».overlays.default« of all »overlayInputs«/»inputs« (incl. »inputs.self«).
overlays ? (lib.remove null (map (input: if input?overlays && input.overlays?default then input.overlays.default else if input?overlay then input.overlay else null) (builtins.attrValues overlayInputs))),
overlays ? (lib.remove null (map (input: if input?overlays.default then input.overlays.default else if input?overlay then input.overlay else null) (builtins.attrValues overlayInputs))),
# (Subset of) »inputs« that »overlays« will be used from. Example: »{ inherit (inputs) self flakeA flakeB; }«.
overlayInputs ? inputs,
# List of Modules to import for all hosts, in addition to the default ones in »nixpkgs«. The host-individual module should selectively enable these. Defaults to ».nixosModules.default« of all »moduleInputs«/»inputs« (including »inputs.self«).
modules ? (lib.remove null (map (input: if input?nixosModules && input.nixosModules?default then input.nixosModules.default else if input?nixosModule then input.nixosModule else null) (builtins.attrValues moduleInputs))),
modules ? (lib.remove null (map (input: if input?nixosModules.default then input.nixosModules.default else if input?nixosModule then input.nixosModule else null) (builtins.attrValues moduleInputs))),
# (Subset of) »inputs« that »modules« will be used from. Example: »{ inherit (inputs) self flakeA flakeB; }«.
moduleInputs ? inputs,
# Additional arguments passed to each module evaluated for the host config (if that module is defined as a function).

View File

@ -97,8 +97,10 @@ in rec {
} // args);
# Given a list of »overlays« and »pkgs« with them applied, returns the subset of »pkgs« that was directly modified by the overlays.
# (But this only works for top-level / non-scoped packages.)
getModifiedPackages = pkgs: overlays: let
names = builtins.concatLists (map (overlay: builtins.attrNames (overlay { } { })) (builtins.attrValues overlays));
getNames = overlay: builtins.attrNames (overlay { } { });
names = if overlays?default then getNames overlays.default else builtins.concatLists (map getNames (builtins.attrValues overlays));
in mapMergeUnique (name: if lib.isDerivation pkgs.${name} then { ${name} = pkgs.${name}; } else { }) names;
## Given a path to a module in »nixpkgs/nixos/modules/«, when placed in another module's »imports«, this adds an option »disableModule.${modulePath}« that defaults to being false, but when explicitly set to »true«, disables all »config« values set by the module.

View File

@ -39,12 +39,18 @@ function do-disk-setup { # 1: diskPaths
function partition-disks { # 1: diskPaths
local beLoud=/dev/null ; if [[ ${args[debug]:-} ]] ; then beLoud=/dev/stdout ; fi
local beSilent=/dev/stderr ; if [[ ${args[quiet]:-} ]] ; then beSilent=/dev/null ; fi
declare -g -A blockDevs=( ) # this ends up in the caller's scope
if [[ $1 == */ ]] ; then
mkdir -p "$1"
for name in "@{!config.wip.fs.disks.devices[@]}" ; do blockDevs[$name]=${1}${name}.img ; done
else
local path ; for path in ${1//:/ } ; 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
done
fi
local name ; for name in "@{!config.wip.fs.disks.devices[@]}" ; do
if [[ ! ${blockDevs[$name]:-} ]] ; then echo "Path for block device $name not provided" 1>&2 ; return 1 ; fi
@ -97,7 +103,6 @@ function partition-disk { # 1: name, 2: blockDev, 3?: devSize
local -a sgdisk=( --zap-all ) # delete existing part tables
if [[ ${disk[gptOffset]} != 0 ]] ; then
echo 'Setting »gptOffset != 0« is currently not supported, as sgdisk with the patch applied somehow fails to read files' 1>&2 ; return 1
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/${disk[sectorSize]} - 1 - 32 - ${disk[gptOffset]} )) )
fi

View File

@ -58,6 +58,8 @@ function nixos-install-cmd {( set -eu # 1: mnt, 2: topLevel
function install-system-to {( set -u # 1: mnt
mnt=$1 ; topLevel=${2:-}
targetSystem=${args[toplevel]:-@{config.system.build.toplevel}}
beLoud=/dev/null ; if [[ ${args[debug]:-} ]] ; then beLoud=/dev/stdout ; fi
beSilent=/dev/stderr ; if [[ ${args[quiet]:-} ]] ; then beSilent=/dev/null ; fi
trap - EXIT # start with empty traps for sub-shell
# Link/create files that some tooling expects:
@ -90,14 +92,19 @@ function install-system-to {( set -u # 1: mnt
# Copy system closure to new nix store:
if [[ ${SUDO_USER:-} ]] ; then chown -R $SUDO_USER: $mnt/nix/store $mnt/nix/var || exit ; fi
( cmd=( nix --extra-experimental-features nix-command --offline copy --no-check-sigs --to $mnt ${topLevel:-$targetSystem} ) ; if [[ ${args[quiet]:-} ]] ; then "${cmd[@]}" --quiet &>/dev/null || exit ; else set -x ; time "${cmd[@]}" || exit ; fi ) || exit ; rm -rf $mnt/nix/var/nix/gcroots || exit
(
cmd=( nix --extra-experimental-features nix-command --offline copy --no-check-sigs --to $mnt ${topLevel:-$targetSystem} )
if [[ ${args[quiet]:-} ]] ; then
( set -o pipefail ; "${cmd[@]}" --quiet 2>&1 >/dev/null | { grep -Pe '^error:' || true ; } ) || exit
else set -x ; time "${cmd[@]}" || exit ; fi
) || exit ; rm -rf $mnt/nix/var/nix/gcroots || exit
# TODO: if the target has @{config.nix.settings.auto-optimise-store} 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 || exit ; chown :30000 $mnt/nix/store || exit ; fi
# Run the main install command (primarily for the bootloader):
mount -o bind,ro /nix/store $mnt/nix/store || exit ; prepend_trap '! mountpoint -q $mnt/nix/store || umount -l $mnt/nix/store' EXIT || exit # all the things required to _run_ the system are copied, but (may) need some more things to initially install it and/or enter the chroot (like qemu, see above)
run-hook-script 'Pre Installation' @{config.wip.fs.disks.preInstallCommands!writeText.preInstallCommands} || exit
code=0 ; nixos-install-cmd $mnt "${topLevel:-$targetSystem}" || code=$?
code=0 ; nixos-install-cmd $mnt "${topLevel:-$targetSystem}" >$beLoud 2>$beSilent || code=$?
run-hook-script 'Post Installation' @{config.wip.fs.disks.postInstallCommands!writeText.postInstallCommands} || exit
# Done!

View File

@ -64,7 +64,11 @@ function run-qemu {( set -eu # 1: diskImages
#qemu+=( -M virt -m 1024 -smp 4 -cpu cortex-a53 ) ; args[no-nat]=1
fi # else things are going to be quite slow
disks=( ${diskImages//:/ } ) ; for index in ${!disks[@]} ; do
if [[ $diskImages == */ ]] ; then
disks=( ${diskImages}primary.img ) ; for name in "@{!config.wip.fs.disks.devices[@]}" ; do if [[ $name != primary ]] ; then disks+=( ${diskImages}${name}.img ) ; fi ; done
#disks=( "@{!config.wip.fs.disks.devices[@]}" ) ; disks=( "${disks[@]/#/$diskImages}" )
else disks=( ${diskImages//:/ } ) ; fi
for index in ${!disks[@]} ; do
# qemu+=( -drive format=raw,if=ide,file="${disks[$index]/*=/}" ) # »if=ide« is the default, which these days isn't great for driver support inside the VM
qemu+=( # not sure how correct the interpretations if the command are, and whether this works for more than one disk
-drive format=raw,file="${disks[$index]/*=/}",media=disk,if=none,index=${index},id=drive${index} # create the disk drive, without attaching it, name it driveX

View File

@ -12,14 +12,14 @@ Things that really should be (more like) this by default.
dirname: inputs: specialArgs@{ config, pkgs, lib, name, ... }: let inherit (inputs.self) lib; in let
prefix = inputs.config.prefix;
cfg = config.${prefix}.base;
inputDefinesSystem = cfg.includeInputs?self && cfg.includeInputs.self?nixosConfigurations && cfg.includeInputs.self.nixosConfigurations?${name};
in {
options.${prefix} = { base = {
enable = lib.mkEnableOption "saner defaults";
includeInputs = lib.mkOption { description = "The system's build inputs, to be included in the flake registry, and on the »NIX_PATH« entry, such that they are available for self-rebuilds and e.g. as »pkgs« on the CLI."; type = lib.types.attrsOf lib.types.anything; apply = lib.filterAttrs (k: v: v != null); default = { }; };
panic_on_fail = lib.mkEnableOption "Kernel parameter »boot.panic_on_fail«" // { default = true; example = false; }; # It's stupidly hard to remove items from lists ...
autoUpgrade = lib.mkEnableOption "automatic NixOS updates and garbage collection" // { default = inputDefinesSystem; defaultText = lib.literalExpression "config.${prefix}.base.includeInputs.self.nixosConfigurations?\${name}"; example = false; };
autoUpgrade = lib.mkEnableOption "automatic NixOS updates and garbage collection" // { default = cfg.includeInputs?self.nixosConfigurations.${name}; defaultText = lib.literalExpression "config.${prefix}.base.includeInputs?self.nixosConfigurations.\${name}"; example = false; };
bashInit = lib.mkEnableOption "pretty defaults for interactive bash shells" // { default = true; example = false; };
}; };
# Bugfix:
@ -36,7 +36,15 @@ in {
environment.etc."machine-id".text = lib.mkDefault (builtins.substring 0 32 (builtins.hashString "sha256" "${config.networking.hostName}:machine-id")); # this works, but it "should be considered "confidential", and must not be exposed in untrusted environments" (not sure _why_ though)
documentation.man.enable = lib.mkDefault config.documentation.enable;
nix.settings.auto-optimise-store = lib.mkDefault true; # file deduplication, see https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-store-optimise.html#description
boot.loader.timeout = lib.mkDefault 1; # save 4 seconds on startup
system.extraSystemBuilderCmds = (if !config.boot.initrd.enable then "" else ''
ln -sT ${builtins.unsafeDiscardStringContext config.system.build.bootStage1} $out/boot-stage-1.sh # (this is super annoying to locate otherwise)
''); # (to deactivate this, set »system.extraSystemBuilderCmds = lib.mkAfter "rm -f $out/boot-stage-1.sh";«)
system.activationScripts.diff-systems = { text = ''
if [[ -e /run/current-system ]] ; then ${pkgs.nix}/bin/nix --extra-experimental-features nix-command store diff-closures /run/current-system "$systemConfig" ; fi
''; deps = [ "etc" ]; }; # (to deactivate this, set »system.activationScripts.diff-systems = lib.mkForce "";«)
}) ({
# Robustness/debugging:
@ -46,7 +54,7 @@ in {
systemd.extraConfig = "StatusUnitFormat=name"; # Show unit names instead of descriptions during boot.
}) (lib.mkIf (inputDefinesSystem) { # non-flake
}) (lib.mkIf (cfg.includeInputs?self.nixosConfigurations.${name}) { # non-flake
# Importing »<nixpkgs>« as non-flake returns a lambda returning the evaluated Nix Package Collection (»pkgs«). The most accurate representation of what that should be on the target host is the »pkgs« constructed when building it:
system.extraSystemBuilderCmds = ''
@ -92,12 +100,49 @@ in {
};
}) ({
# Free convenience:
}) (lib.mkIf (cfg.bashInit) {
# (almost) Free Convenience:
environment.shellAliases = { "with" = lib.mkDefault ''nix-shell --run "bash --login" -p''; }; # »with« doesn't seem to be a common linux command yet, and it makes sense here: with $package => do stuff in shell
environment.shellAliases = {
programs.bash.promptInit = lib.mkDefault ''
"with" = pkgs.writeShellScript "with" ''
help='Synopsys: With the Nix packages »PKGS« (as attribute path read from the imported »nixpkgs« specified on the »NIX_PATH«), run »CMD« with »ARGS«, or »bash --login« if no »CMD« is supplied.
Usage: with [-h] PKGS... [-- [CMD [ARGS...]]]'
pkgs=( ) ; while (( "$#" > 0 )) ; do {
if [[ $1 == -h ]] ; then echo "$help" ; exit 0 ; fi
if [[ $1 == -- ]] ; then shift ; break ; fi ; pkgs+=( "$1" )
} ; shift ; done
if (( ''${#pkgs[@]} == 0 )) ; then echo "$help" ; exit 1 ; fi
if (( "$#" == 0 )) ; then set -- bash --login ; fi
nix-shell --run "$( printf ' %q' "$@" )" -p "''${pkgs[@]}"
#function run { bash -xc "$( printf ' %q' "$@" )" ; }
''; # »with« doesn't seem to be a common linux command yet, and it makes sense here: with package(s) => do stuff
ls = "ls --color=auto"; # (default)
l = "ls -alhF"; # (added F)
ll = "ls -alF"; # (added aF)
lt = "tree -a -p -g -u -s -D -F --timefmt '%Y-%m-%d %H:%M:%S'"; # ll like tree
lp = pkgs.writeShellScript "lp" ''abs="$(cd "$(dirname "$1")" ; pwd)"/"$(basename "$1")" ; ${pkgs.util-linux}/bin/namei -lx "$abs"''; # similar to »ll -d« on all path element from »$1« to »/«
ips = "ip -c -br addr"; # colorized listing of all interface's IPs
mounts = pkgs.writeShellScript "mounts" ''${pkgs.util-linux}/bin/mount | ${pkgs.gnugrep}/bin/grep -vPe '/.zfs/snapshot/| on /var/lib/docker/|^/var/lib/snapd/snaps/' | LC_ALL=C ${pkgs.coreutils}/bin/sort -k3 | ${pkgs.util-linux}/bin/column -t -N Device/Source,on,Mountpoint,type,Type,Options -H on,type -W Device/Source,Mountpoint,Options''; # the output of »mount«, cleaned up and formatted as a sorted table
netns-exec = pkgs.writeShellScript "netns-exec" ''ns=$1 ; shift ; /run/wrappers/bin/firejail --noprofile --quiet --netns="$ns" -- "$@"''; # execute a command in a different netns (like »ip netns exec«), without requiring root permissions (but does require »config.programs.firejail.enable=true«)
nixos-list-generations = "nix-env --list-generations --profile /nix/var/nix/profiles/system";
sc = "systemctl";
scs = "systemctl status";
scc = "systemctl cat";
scu = "systemctl start"; # up
scd = "systemctl stop"; # down
scr = "systemctl restart";
scf = "systemctl list-units --failed";
scj = "journalctl -b -f -u";
};
programs.bash.promptInit = ''
# Provide a nice prompt if the terminal supports it.
if [ "''${TERM:-}" != "dumb" ] ; then
if [[ "$UID" == '0' ]] ; then if [[ ! "''${SUDO_USER:-}" ]] ; then # direct root: red username + green hostname
@ -114,7 +159,7 @@ in {
export TERM_RECURSION_DEPTH=$(( 1 + ''${TERM_RECURSION_DEPTH:-0} ))
''; # The non-interactive version of bash does not remove »\[« and »\]« from PS1, but without those the terminal gets confused about the cursor position after the prompt once one types more than a bit of text there (at least via serial or SSH).
environment.interactiveShellInit = lib.mkDefault ''
environment.interactiveShellInit = lib.mkBefore ''
# In RePl mode: remove duplicates from history; don't save commands with a leading space.
HISTCONTROL=ignoredups:ignorespace
@ -123,14 +168,15 @@ in {
stty rows 34 cols 145 # Fairly large font on 1080p. (Setting this too large for the screen warps the output really badly.)
fi
'';
system.extraSystemBuilderCmds = (if !config.boot.initrd.enable then "" else ''
ln -sT ${builtins.unsafeDiscardStringContext config.system.build.bootStage1} $out/boot-stage-1.sh # (this is super annoying to locate otherwise)
'');
# deactivate by setting »system.activationScripts.diff-systems = lib.mkForce "";«
system.activationScripts.diff-systems = ''
if [[ -e /run/current-system ]] ; then ${pkgs.nix}/bin/nix --extra-experimental-features nix-command store diff-closures /run/current-system "$systemConfig" ; fi
}) (lib.mkIf (cfg.bashInit) { # other »interactiveShellInit« (and »shellAliases«) would go in here, being able to overwrite stuff from above, but still also being included in the alias completion below
environment.interactiveShellInit = lib.mkAfter ''
# enable completion for aliases
source ${ pkgs.fetchFromGitHub {
owner = "cykerway"; repo = "complete-alias";
rev = "4fcd018faa9413e60ee4ec9f48ebeac933c8c372"; # v1.18 (2021-07-17)
sha256 = "sha256-fZisrhdu049rCQ5Q90sFWFo8GS/PRgS29B1eG8dqlaI=";
} }/complete_alias
complete -F _complete_alias "''${!BASH_ALIASES[@]}"
'';
}) ]);

View File

@ -24,9 +24,11 @@ in {
options.${prefix} = { bootloader.extlinux = {
enable = lib.mkEnableOption (lib.mdDoc ''
a simple bootloader for legacy-BIOS environments, like (by default) Qemu.
extlinux, a simple bootloader for legacy-BIOS environments, like (by default) Qemu.
This uses the same implementation as `boot.loader.generic-extlinux-compatible` to generate the bootloader configuration, but then actually also installs `extlinux` itself, instead of relying on something else (like an externally installed u-boot) to read and execute the configuration.
Any options affecting the config file generation by `boot.loader.generic-extlinux-compatible` apply, but `boot.loader.generic-extlinux-compatible.enable` should not be set to `true`.
Since the bootloader runs before selecting a generation or specialisation to run, all sub-options, similar to e.g. {option}`boot.loader.timeout`, apply globally to the system, from whichever configuration last applied its bootloader (e.g. by newly `nixos-rebuild switch/boot`ing it or by calling its `.../bin/switch-to-configuration switch/boot`)
'');
package = lib.mkOption { description = lib.mdDoc ''
The `syslinux` package to install `extlinux` from use.
@ -38,13 +40,15 @@ in {
The `/dev/disk/by-{id,label,partlabel,partuuid,uuid}/*` path of the *disk partition* holding the filesystem that `extlinux` is installed to. This must be formatted with a filesystem that `extlinux` supports and mounted as (a parent of) {option}`.targetDir`. The disk on which the partition lies will have the bootloader section of its MBR replaced by `extlinux`'s.
''; type = lib.types.strMatching ''^/.*[^/]$''; default = targetMount.device; };
allowInstableTargetPart = lib.mkOption { internal = true; type = lib.types.bool; };
showUI = (lib.mkEnableOption (lib.mdDoc "a simple graphical user interface to select the configuration to start during early boot.")) // { default = true; example = false; };
showUI = (lib.mkEnableOption (lib.mdDoc ''
a simple graphical user interface to select the configuration to start during early boot
'')) // { default = true; example = false; };
}; };
config = let
confFile = "/nixos/modules/system/boot/loader/generic-extlinux-compatible";
writeConfig = (import "${inputs.nixpkgs}/${confFile}" { inherit config pkgs lib; }).config.content.system.build.installBootLoader;
generic-extlinux-compatible = "${inputs.nixpkgs}/nixos/modules/system/boot/loader/generic-extlinux-compatible";
extlinux-conf-builder = (import generic-extlinux-compatible { inherit config pkgs lib; }).config.content.system.build.installBootLoader;
esc = lib.escapeShellArg;
@ -52,10 +56,14 @@ in {
assertions = [ {
assertion = cfg.allowInstableTargetPart || (builtins.match ''^/dev/disk/by-(id|label|partlabel|partuuid|uuid)/.*[^/]$'' cfg.targetPart) != null;
message = ''`config.${prefix}.bootloader.extlinux.targetPart` is not set to a stable path in `/dev/disk/by-{id,label,partlabel,partuuid,uuid}/`. Not using a unique identifier (or even using a path that can unexpectedly change) is very risky.'';
message = ''
`config.${prefix}.bootloader.extlinux.targetPart` is not set to a stable path in `/dev/disk/by-{id,label,partlabel,partuuid,uuid}/`. Not using a unique identifier (or even using a path that can unexpectedly change) is very risky.
'';
} {
assertion = fsSupported targetMount.fsType;
message = ''`config.${prefix}.bootloader.extlinux.targetPart`'s closest mount (`${targetMount.mountPoint}`) is of type `${targetMount.fsType}`, which is not one of extlinux's supported types (${lib.concatStringsSep ", " supportedFSes}).'';
message = ''
`config.${prefix}.bootloader.extlinux.targetPart`'s closest mount (`${targetMount.mountPoint}`) is of type `${targetMount.fsType}`, which is not one of extlinux's supported types (${lib.concatStringsSep ", " supportedFSes}).
'';
} ];
${prefix} = {
@ -65,7 +73,8 @@ in {
system.boot.loader.id = "extlinux";
system.build.installBootLoader = "${pkgs.writeShellScript "install-extlinux.sh" ''
${writeConfig} "$1" -d ${esc cfg.targetDir}
if [[ ! ''${1:-} || $1 != /nix/store/* ]] ; then echo "Usage: $0 TOPLEVEL_PATH" ; exit 1 ; fi
${extlinux-conf-builder} "$1" -d ${esc cfg.targetDir}
partition=${esc cfg.targetPart}
diskDev=$( realpath "$partition" ) || exit ; if [[ $diskDev == /dev/sd* ]] ; then
@ -75,7 +84,9 @@ in {
fi
if [[ $( cat ${esc cfg.targetDir}/extlinux/installedVersion 2>/dev/null || true ) != ${esc cfg.package} ]] ; then
${esc cfg.package}/bin/extlinux --install ${esc cfg.targetDir}/extlinux || exit
if ! output=$( ${esc cfg.package}/bin/extlinux --install --heads=64 --sectors=32 ${esc cfg.targetDir}/extlinux 2>&1 ) ; then
printf '%s\n' "$output" ; exit 1
fi
printf '%s\n' ${esc cfg.package} >${esc cfg.targetDir}/extlinux/installedVersion
fi
if ! ${pkgs.diffutils}/bin/cmp --quiet --bytes=440 $diskDev ${esc cfg.package}/share/syslinux/mbr.bin ; then
@ -88,9 +99,11 @@ in {
cp ${esc cfg.package}/share/syslinux/$lib.c32 ${esc cfg.targetDir}/extlinux/$lib.c32
fi
done
if ! ${pkgs.gnugrep}/bin/grep -qP '^UI $' ${esc cfg.targetDir}/extlinux/extlinux.conf ; then
if ! ${pkgs.gnugrep}/bin/grep -qP '^UI ' ${esc cfg.targetDir}/extlinux/extlinux.conf ; then # `extlinux-conf-builder` above would have recreated this, so the check should always be true
${pkgs.perl}/bin/perl -i -pe 's/TIMEOUT/UI menu.c32\nTIMEOUT/' ${esc cfg.targetDir}/extlinux/extlinux.conf
fi
else
: # delete library files?
fi
''}";

View File

@ -74,7 +74,7 @@ in {
esc = lib.escapeShellArg;
in pkgs.runCommand "partitioning-${config.networking.hostName}" { } ''
${lib.wip.substituteImplicit { inherit pkgs; scripts = [ partition-disk ]; context = { inherit config; native = pkgs; }; }} # inherit (builtins) trace;
mkdir $out ; declare -A args=([debug]=1)
set -u ; mkdir -p $out ; declare -A args=([debug]=1)
${lib.concatStrings (lib.mapAttrsToList (name: disk: ''
name=${esc name} ; img=$name.img
${pkgs.coreutils}/bin/truncate -s ${esc disk.size} "$img"

View File

@ -81,8 +81,8 @@ On a less beefy system, but also with less data to manage, `tmpfs` works fine fo
#wip.fs.keystore.keys."luks/local-${hash}/0" = "random"; # (implied by the »-encrypted« suffix above)
#wip.fs.disks.partitions."local-${hash}".size = "50%"; # (default, fixed after installation)
wip.fs.temproot.remote.type = "zfs";
wip.fs.keystore.keys."luks/rpool-${hash}/0" = "random";
#wip.fs.keystore.keys."zfs/rpool-${hash}/remote" = "random"; # (default)
#wip.fs.keystore.keys."luks/rpool-${hash}/0" = "random"; # Would also enable LUKS encryption of the pool, but there isn't too much point in encrypting twice.
#wip.fs.zfs.pools."rpool-${hash}".vdevArgs = [ "rpool-${hash}" ]; # (default)
#wip.fs.disks.partitions."rpool-${hash}" = { type = "bf00"; size = null; order = 500; }; # (default)
@ -322,7 +322,12 @@ in {
boot.initrd.kernelModules = [ type ]; # This is not generally, but sometimes, required to boot. Strange. (Kernel message: »request_module fs-f2fs succeeded, but still no fs?«)
})) ] ++ (map (type: (lib.mkIf (cfg.${type}.type == "bind") {
})) (lib.mkIf (cfg.remote.type == "none") {
systemd.tmpfiles.rules = [ (lib.wip.mkTmpfile { type = "L+"; path = "/remote"; argument = "/local"; }) ]; # for compatibility (but use a symlink to make clear that this is not actually a separate mount)
}) ] ++ (map (type: (lib.mkIf (cfg.${type}.type == "bind") {
fileSystems = (lib.mapAttrs (target: args@{ source, uid, gid, mode, extraFsConfig, ... }: extraFsConfig // (rec {
device = "${cfg.${type}.bind.source}/${source}";

View File

@ -11,9 +11,12 @@ GPT-FDisk patched to be able to move not only the primary, but also the backup p
#*/# end of MarkDown, beginning of NixPkgs overlay:
dirname: inputs: final: prev: let
inherit (final) pkgs; inherit (inputs.self) lib;
debug = false;
in {
gptfdisk = prev.gptfdisk.overrideAttrs (old: let
gptfdisk = (
if debug then pkgs.enableDebugging else (x: x)
) (prev.gptfdisk.overrideAttrs (old: let
pname = "gptfdisk";
in rec {
version = "1.0.9";
@ -21,10 +24,12 @@ in {
url = "https://downloads.sourceforge.net/gptfdisk/${pname}-${version}.tar.gz";
sha256 = "1hjh5m77fmfq5m44yy61kchv7mbfgx026aw3jy5qxszsjckavzns";
};
/* patches = [ # (don't include »old.patches«, as the only one was upstreamed)
patches = [ # (don't include »old.patches«, as the only one was upstreamed in v1.0.9)
../patches/gptfdisk-move-secondary-table.patch
]; */
});
];
} // (if debug then {
dontStrip = true;
} else { })));
libblockdev = prev.libblockdev.override { inherit (prev) gptfdisk; };

View File

@ -1,5 +1,5 @@
diff --git a/gpt.cc b/gpt.cc
index 76cd9ad..d61064f 100644
index 76cd9ad..4798db2 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -1500,7 +1500,7 @@ int GPTData::DestroyGPT(void) {
@ -35,7 +35,15 @@ index 76cd9ad..d61064f 100644
// Blank the partition array
void GPTData::BlankPartitions(void) {
uint32_t i;
@@ -2285,7 +2302,7 @@ uint64_t GPTData::FindFirstAvailable(uint64_t start) {
@@ -2066,6 +2083,7 @@ void GPTData::MoveSecondHeaderToEnd() {
} // if
mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
+ // TODO: Whenever this gets called, it moves the backup table to be the same distance from the backup header as the primary one it from its header. This seems highly problematic, since MoveMainTable does not call this, but then further actions may or may not do so. Moving the primary table may thus imply moving the backup table, or it may leave it where it was. There is also no guarantee that the space where the backup table is moved to is actually available.
} // GPTData::FixSecondHeaderLocation()
// Sets the partition's name to the specified UnicodeString without
@@ -2285,7 +2303,7 @@ uint64_t GPTData::FindFirstAvailable(uint64_t start) {
} // GPTData::FindFirstAvailable()
// Returns the LBA of the start of the first partition on the disk (by
@ -44,7 +52,7 @@ index 76cd9ad..d61064f 100644
uint64_t GPTData::FindFirstUsedLBA(void) {
uint32_t i;
uint64_t firstFound = UINT64_MAX;
@@ -2298,6 +2315,20 @@ uint64_t GPTData::FindFirstUsedLBA(void) {
@@ -2298,6 +2316,20 @@ uint64_t GPTData::FindFirstUsedLBA(void) {
return firstFound;
} // GPTData::FindFirstUsedLBA()
@ -95,7 +103,7 @@ index 5d19372..17b3380 100644
uint64_t FindLastAvailable();
uint64_t FindLastInFree(uint64_t start, bool align = false);
diff --git a/gptcl.cc b/gptcl.cc
index 34c9421..232285a 100644
index 34c9421..f4361b7 100644
--- a/gptcl.cc
+++ b/gptcl.cc
@@ -68,7 +68,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
@ -134,6 +142,20 @@ index 34c9421..232285a 100644
};
// Create popt context...
@@ -156,12 +158,12 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
// Assume first non-option argument is the device filename....
device = (char*) poptGetArg(poptCon);
- poptResetContext(poptCon);
if (device != NULL) {
JustLooking(); // reset as necessary
BeQuiet(); // Tell called functions to be less verbose & interactive
if (LoadPartitions((string) device)) {
+ device = NULL; poptResetContext(poptCon);
if ((WhichWasUsed() == use_mbr) || (WhichWasUsed() == use_bsd))
saveNonGPT = 0; // flag so we don't overwrite unless directed to do so
sSize = GetBlockSize();
@@ -280,13 +282,21 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
alignEnd = true;
break;
@ -246,6 +268,49 @@ index 32e2f88..8ed6274 100644
void CreatePartition(void);
void DeletePartition(void);
void ChangePartType(void);
diff --git a/guid.cc b/guid.cc
index 1e73ab7..387019a 100644
--- a/guid.cc
+++ b/guid.cc
@@ -139,29 +139,28 @@ void GUIDData::Zero(void) {
// (immediately after creating the UUID on Windows 7 being an important
// exception).
void GUIDData::Randomize(void) {
- int i, uuidGenerated = 0;
-#ifdef _UUID_UUID_H
+#ifndef _WIN32
uuid_generate(uuidData);
ReverseBytes(&uuidData[0], 4);
ReverseBytes(&uuidData[4], 2);
ReverseBytes(&uuidData[6], 2);
- uuidGenerated = 1;
-#endif
+#else
+
#if defined (_RPC_H) || defined (__RPC_H__)
UUID MsUuid;
if (UuidCreate(&MsUuid) == RPC_S_OK) {
memcpy(uuidData, &MsUuid, 16);
uuidGenerated = 1;
} // if
+#else
+ cerr << "Warning! Unable to generate a proper UUID! Creating an improper one as a last\n"
+ << "resort! Windows 7 may crash if you save this partition table!\a\n";
+ for (int i = 0; i < 16; i++)
+ uuidData[i] = (unsigned char) (256.0 * (rand() / (RAND_MAX + 1.0)));
+#endif
#endif
- if (!uuidGenerated) {
- cerr << "Warning! Unable to generate a proper UUID! Creating an improper one as a last\n"
- << "resort! Windows 7 may crash if you save this partition table!\a\n";
- for (i = 0; i < 16; i++)
- uuidData[i] = (unsigned char) (256.0 * (rand() / (RAND_MAX + 1.0)));
- } // if
} // GUIDData::Randomize
// Equality operator; returns 1 if the GUIDs are equal, 0 if they're unequal
diff --git a/sgdisk.8 b/sgdisk.8
index b966a13..6f8b375 100644
--- a/sgdisk.8