many small fixes/improvements, and:

add config.wip.fs.temproot.remote.type == none,
add config.wip.base.includeInputs,
add pkgs.uboot-with-mmc-env,
rename config.preface to config.wip.preface
This commit is contained in:
Niklas Gollenstede 2022-06-28 05:00:48 +02:00
parent b51cf19f4a
commit 8b0e200c73
23 changed files with 235 additions and 118 deletions

View File

@ -80,6 +80,7 @@
"keystatus", // zfs "keystatus", // zfs
"kmod", // linux "kmod", // linux
"lazytime", // f2fs "lazytime", // f2fs
"libblockdev", // package
"libubootenv", // package "libubootenv", // package
"logbias", // zfs "logbias", // zfs
"losetup", // program / function "losetup", // program / function
@ -88,6 +89,7 @@
"luks", // linux "luks", // linux
"macaddr", // cli arg "macaddr", // cli arg
"mkdir", // program / function "mkdir", // program / function
"mkenvimage", // program
"mktemp", // program / function "mktemp", // program / function
"modifyvm", // virtual box "modifyvm", // virtual box
"mountpoint", // program / function "mountpoint", // program / function
@ -109,10 +111,12 @@
"noheadings", // cli arg "noheadings", // cli arg
"nosuid", // mount option "nosuid", // mount option
"oneshot", // systemd "oneshot", // systemd
"optimise", // B/E
"ostype", // virtual box "ostype", // virtual box
"OVMF", // package "OVMF", // package
"partlabel", // linux "partlabel", // linux
"partprobe", // program / function "partprobe", // program / function
"passthru", // nix
"pbkdf", // cli arg "pbkdf", // cli arg
"pflash", // cli arg "pflash", // cli arg
"pkgs", // nix "pkgs", // nix
@ -155,6 +159,7 @@
"typecode", // cli arg "typecode", // cli arg
"uart", "uarts", // serial protocol "uart", "uarts", // serial protocol
"uartmode", // virtual box "uartmode", // virtual box
"uboot", // program
"udev", // program "udev", // program
"udevadm", // program "udevadm", // program
"udptunnel", // program "udptunnel", // program

View File

@ -25,7 +25,7 @@ The modules are inactive by default, and are, where possible, designed to be ind
[`patches/`](./patches/) contains patches which are either applied to the flake's inputs in [`flake.nix`](./flake.nix) or to packages in one of the [`overlays/`](./overlays/). [`patches/`](./patches/) contains patches which are either applied to the flake's inputs in [`flake.nix`](./flake.nix) or to packages in one of the [`overlays/`](./overlays/).
[`hosts/`](./hosts/) contains the main NixOS config modules for each host. Generally, there is one file for each host, but the [flake](./flake.nix) can be instructed to reuse the config for multiple hosts (in which case the module should probably interpret the `name` argument passed to it). [`hosts/`](./hosts/) contains the main NixOS config modules for each host. Generally, there is one file for each host, but the [flake](./flake.nix) can be instructed to reuse the config for multiple hosts (in which case the module should probably interpret the `name` argument passed to it).
Any `preface.*` options have to be set in the first sub-module in these files (`## Hardware` section). \ Any `wip.preface.*` options have to be set in the first sub-module in these files (`## Hardware` section). \
This flake only defines a single [`example`](./hosts/example.nix.md) host meant to demonstrate how other flakes can use the (NixOS) flake library framework. This flake only defines a single [`example`](./hosts/example.nix.md) host meant to demonstrate how other flakes can use the (NixOS) flake library framework.
[`example/`](./example/) contains an example of adjusting the [installation](./example/install.sh.md) script for the hosts and this flake's [default config](./example/defaultConfig/) (see [Namespacing](#namespacing-in-nixos)). [`example/`](./example/) contains an example of adjusting the [installation](./example/install.sh.md) script for the hosts and this flake's [default config](./example/defaultConfig/) (see [Namespacing](#namespacing-in-nixos)).

Binary file not shown.

View File

@ -17,10 +17,10 @@
# ./patches/nixpkgs-fix-systemd-boot-install.patch # ./patches/nixpkgs-fix-systemd-boot-install.patch
]; ];
}; in (import "${./.}/lib/flakes.nix" "${./.}/lib" inputs).patchFlakeInputsAndImportRepo inputs patches ./. (inputs@ { self, nixpkgs, ... }: repo@{ overlays, lib, ... }: let }; in (import "${./.}/lib/flakes.nix" "${./.}/lib" inputs).patchFlakeInputsAndImportRepo inputs patches ./. (inputs@{ self, nixpkgs, ... }: repo@{ overlays, lib, ... }: let
systemsFlake = lib.wip.mkSystemsFlake (rec { systemsFlake = lib.wip.mkSystemsFlake (rec {
#systems = { dir = "${inputs.self}/hosts"; exclude = [ ]; }; # (implicit) #systems = { dir = "${self}/hosts"; exclude = [ ]; }; # (implicit)
inherit inputs; inherit inputs;
scripts = (lib.attrValues lib.wip.setup-scripts) ++ [ ./example/install.sh.md ]; scripts = (lib.attrValues lib.wip.setup-scripts) ++ [ ./example/install.sh.md ];
}); });
@ -29,8 +29,8 @@ in [ # Run »nix flake show --allow-import-from-derivation« to see what this me
repo repo
(if true then systemsFlake else { }) (if true then systemsFlake else { })
(lib.wip.forEachSystem [ "aarch64-linux" "x86_64-linux" ] (localSystem: { (lib.wip.forEachSystem [ "aarch64-linux" "x86_64-linux" ] (localSystem: {
packages = lib.wip.getModifiedPackages (lib.wip.importPkgs inputs { system = localSystem; }) overlays; packages = builtins.removeAttrs (lib.wip.getModifiedPackages (lib.wip.importPkgs inputs { system = localSystem; }) overlays) [ "libblockdev" ];
defaultPackage = systemsFlake.packages.${localSystem}.all-systems; defaultPackage = systemsFlake.packages.${localSystem}.all-systems;
})) }))
{ patches = import "${inputs.self}/patches" "${inputs.self}/patches" inputs; } { patches = (lib.wip.importWrapped inputs "${self}/patches").result; }
]); } ]); }

View File

@ -28,12 +28,12 @@ Alternative to running directly as `root` (esp. if `nix` is not installed for ro
```nix ```nix
#*/# end of MarkDown, beginning of NixOS config flake input: #*/# end of MarkDown, beginning of NixOS config flake input:
dirname: inputs: { config, pkgs, lib, name, ... }: let inherit (inputs.self) lib; in let dirname: inputs: { config, pkgs, lib, name, ... }: let inherit (inputs.self) lib; in let
#suffix = builtins.head (builtins.match ''example-(.*)'' name); # make differences in config based on this when using »preface.instances« #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); hash = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName);
in { imports = [ ({ ## Hardware in { imports = [ ({ ## Hardware
preface.instances = [ "example" "example-raidz" ]; wip.preface.instances = [ "example" "example-minimal" "example-raidz" ];
preface.hardware = "x86_64"; system.stateVersion = "22.05"; wip.preface.hardware = "x86_64"; system.stateVersion = "22.05";
## What follows is a whole bunch of boilerplate-ish stuff, most of which multiple hosts would have in common and which would thus be moved to one or more modules: ## What follows is a whole bunch of boilerplate-ish stuff, most of which multiple hosts would have in common and which would thus be moved to one or more modules:
@ -81,6 +81,16 @@ in { imports = [ ({ ## Hardware
wip.fs.temproot.local.mounts."/var/log" = lib.mkForce null; # example: don't keep logs wip.fs.temproot.local.mounts."/var/log" = lib.mkForce null; # example: don't keep logs
}) (lib.mkIf (name == "example-minimal") { ## Minimal automatic FS setup
wip.fs.boot.enable = true;
wip.fs.temproot.enable = true;
wip.fs.temproot.temp.type = "tmpfs";
wip.fs.temproot.local.type = "bind";
wip.fs.temproot.local.bind.base = "f2fs";
wip.fs.temproot.remote.type = "none";
}) (lib.mkIf (name == "example-raidz") { ## Multi-disk ZFS setup }) (lib.mkIf (name == "example-raidz") { ## Multi-disk ZFS setup
#wip.fs.disks.devices.primary.size = "16G"; # (default) #wip.fs.disks.devices.primary.size = "16G"; # (default)
@ -104,7 +114,7 @@ in { imports = [ ({ ## Hardware
}) ({ ## Actual Config }) ({ ## Actual Config
# Some base config: # Some base config:
wip.base.enable = true; wip.base.includeNixpkgs = inputs.nixpkgs; wip.base.enable = true;
documentation.enable = false; # sometimes takes quite long to build documentation.enable = false; # sometimes takes quite long to build

View File

@ -3,4 +3,4 @@ dirname: inputs@{ self, nixpkgs, ...}: let
#categories = fix (wip: (import "${dirname}/imports.nix" dirname inputs).importAll (inputs // { self = inputs.self // { lib = nixpkgs.lib // { inherit wip; }; }; })) dirname; #categories = fix (wip: (import "${dirname}/imports.nix" dirname inputs).importAll (inputs // { self = inputs.self // { lib = nixpkgs.lib // { inherit wip; }; }; })) dirname;
categories = (import "${dirname}/imports.nix" dirname inputs).importAll inputs dirname; categories = (import "${dirname}/imports.nix" dirname inputs).importAll inputs dirname;
wip = (builtins.foldl' (a: b: a // b) { } (builtins.attrValues (builtins.removeAttrs categories [ "setup-scripts" ]))) // categories; wip = (builtins.foldl' (a: b: a // b) { } (builtins.attrValues (builtins.removeAttrs categories [ "setup-scripts" ]))) // categories;
in nixpkgs.lib // { inherit wip; } in nixpkgs.lib // { wip = wip // { prefix = inputs.config.prefix; }; }

View File

@ -3,6 +3,8 @@ dirname: inputs@{ self, nixpkgs, ...}: let
inherit (import "${dirname}/vars.nix" dirname inputs) mapMerge mergeAttrsUnique flipNames; inherit (import "${dirname}/vars.nix" dirname inputs) mapMerge mergeAttrsUnique flipNames;
inherit (import "${dirname}/imports.nix" dirname inputs) getModifiedPackages getNixFiles importWrapped; inherit (import "${dirname}/imports.nix" dirname inputs) getModifiedPackages getNixFiles importWrapped;
inherit (import "${dirname}/scripts.nix" dirname inputs) substituteImplicit; inherit (import "${dirname}/scripts.nix" dirname inputs) substituteImplicit;
setup-scripts = (import "${dirname}/setup-scripts" "${dirname}/setup-scripts" inputs);
prefix = inputs.config.prefix;
in rec { in rec {
# Simplified implementation of »flake-utils.lib.eachSystem«. # Simplified implementation of »flake-utils.lib.eachSystem«.
@ -10,7 +12,7 @@ in rec {
# Sooner or later this should be implemented in nix itself, for now require »inputs.nixpkgs« and a system that can run »x86_64-linux« (native or through qemu). # Sooner or later this should be implemented in nix itself, for now require »inputs.nixpkgs« and a system that can run »x86_64-linux« (native or through qemu).
patchFlakeInputs = inputs: patches: outputs: let patchFlakeInputs = inputs: patches: outputs: let
inherit ((import inputs.nixpkgs { system = "x86_64-linux"; }).pkgs) applyPatches fetchpatch; inherit ((import inputs.nixpkgs { overlays = [ ]; config = { }; system = "x86_64-linux"; }).pkgs) applyPatches fetchpatch;
in outputs (builtins.mapAttrs (name: input: if name != "self" && patches?${name} && patches.${name} != [ ] then (let in outputs (builtins.mapAttrs (name: input: if name != "self" && patches?${name} && patches.${name} != [ ] then (let
patched = applyPatches { patched = applyPatches {
name = "${name}-patched"; src = input; name = "${name}-patched"; src = input;
@ -26,22 +28,31 @@ in rec {
in { inherit (input) inputs; inherit outputs; inherit sourceInfo; } // outputs // sourceInfo) in { inherit (input) inputs; inherit outputs; inherit sourceInfo; } // outputs // sourceInfo)
)) else input) inputs); )) else input) inputs);
# Generates implicit flake outputs by importing conventional paths in the local repo. # Generates implicit flake outputs by importing conventional paths in the local repo. E.g.:
# outputs = inputs@{ self, nixpkgs, wiplib, ... }: wiplib.lib.wip.importRepo inputs ./. (repo@{ overlays, lib, ... }: let ... in [ repo ... ])
importRepo = inputs: repoPath': outputs: let 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) 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 list = (outputs inputs ((if builtins.pathExists "${repoPath}/lib/default.nix" then { in let result = (outputs (
lib = import "${repoPath}/lib" "${repoPath}/lib" inputs; (let it = importWrapped inputs "${repoPath}/lib"; in if it.exists then rec {
} else { }) // (if builtins.pathExists "${repoPath}/overlays/default.nix" then rec { lib = it.result;
overlays = import "${repoPath}/overlays" "${repoPath}/overlays" inputs; } else { }) // (let it = importWrapped inputs "${repoPath}/overlays"; in if it.exists then rec {
overlay = final: prev: builtins.foldl' (prev: overlay: prev // (overlay final prev)) prev (builtins.attrValues overlays); overlays = it.result;
} else { }) // (if builtins.pathExists "${repoPath}/modules/default.nix" then rec { overlay = final: prev: builtins.foldl' (prev: overlay: prev // (overlay final prev)) prev (builtins.attrValues overlays);
nixosModules = import "${repoPath}/modules" "${repoPath}/modules" inputs; } else { }) // (let it = importWrapped inputs "${repoPath}/modules"; in if it.exists then rec {
nixosModule = { imports = builtins.attrValues nixosModules; }; nixosModules = it.result;
} else { }))); in if (builtins.isList list) then mergeOutputs list else list; nixosModule = { imports = builtins.attrValues nixosModules; };
} else { })
)); in if (builtins.isList result) then mergeOutputs result else result;
# Combines »patchFlakeInputs« and »importRepo« in a single call. # Combines »patchFlakeInputs« and »importRepo« in a single call. E.g.:
# outputs = inputs: let patches = {
# nixpkgs = [
# # remote: { url = "https://github.com/NixOS/nixpkgs/pull/###.diff"; sha256 = inputs.nixpkgs.lib.fakeSha256; }
# # local: ./overlays/patches/nixpkgs-###.patch # (use long native path to having the path change if any of the other files in ./. change)
# ]; # ...
# }; in inputs.wiplib.lib.wip.patchFlakeInputsAndImportRepo inputs patches ./. (inputs@{ self, nixpkgs, ... }: repo@{ nixosModules, overlays, lib, ... }: let ... in [ repo ... ])
patchFlakeInputsAndImportRepo = inputs: patches: repoPath: outputs: ( patchFlakeInputsAndImportRepo = inputs: patches: repoPath: outputs: (
patchFlakeInputs inputs patches (inputs: importRepo inputs repoPath outputs) patchFlakeInputs inputs patches (inputs: importRepo inputs repoPath (outputs inputs))
); );
# Merges a list of flake output attribute sets. # Merges a list of flake output attribute sets.
@ -53,11 +64,11 @@ in rec {
# Given a path to a host config file, returns some properties defined in its first inline module (to be used where accessing them via »nodes.${name}.config...« isn't possible). # Given a path to a host config file, returns some properties defined in its first inline module (to be used where accessing them via »nodes.${name}.config...« isn't possible).
getSystemPreface = inputs: entryPath: args: let getSystemPreface = inputs: entryPath: args: let
imported = (importWrapped inputs entryPath) ({ config = null; pkgs = null; lib = null; name = null; nodes = null; } // args); imported = (importWrapped inputs entryPath).required ({ config = null; pkgs = null; lib = null; name = null; nodes = null; } // args);
module = builtins.elemAt imported.imports 0; props = module.preface; module = builtins.elemAt imported.imports 0; props = module.${prefix}.preface;
in if ( in if (
imported?imports && (builtins.isList imported.imports) && (imported.imports != [ ]) && module?preface && props?hardware imported?imports && (builtins.isList imported.imports) && (imported.imports != [ ]) && module?${prefix} && module.${prefix}?preface && props?hardware
) then (props) else throw "File ${entryPath} must fulfill the structure: dirname: inputs: { ... }: { imports = [ { preface = { hardware = str; ... } } ]; }"; ) 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«. # 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«.
# See »mkSystemsFlake« for documentation of the arguments. # See »mkSystemsFlake« for documentation of the arguments.
@ -72,13 +83,13 @@ in rec {
in { inherit preface; } // (nixosSystem { in { inherit preface; } // (nixosSystem {
system = targetSystem; system = targetSystem;
modules = [ ( modules = [ (
{ _file = entryPath; imports = [ (importWrapped inputs entryPath) ]; } # (preserve the location of reported errors) (importWrapped inputs entryPath).module
) { ) {
# The system architecture (often referred to as »system«). # The system architecture (often referred to as »system«).
options.preface.hardware = lib.mkOption { type = lib.types.str; readOnly = true; }; options.${prefix}.preface.hardware = lib.mkOption { type = lib.types.str; readOnly = true; };
} { } {
# List of host names to instantiate this host config for, instead of just for the file name. # List of host names to instantiate this host config for, instead of just for the file name.
options.preface.instances = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ name ]; }; options.${prefix}.preface.instances = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ name ]; };
} ({ config, ... }: { } ({ config, ... }: {
imports = modules; nixpkgs = { inherit overlays; } imports = modules; nixpkgs = { inherit overlays; }
@ -95,13 +106,13 @@ in rec {
}); });
# Given either a list (or attr set) of »files« (paths to ».nix« or ».nix.md« files for dirs with »default.nix« files in them) or a »dir« path (and optionally a list of file names to »exclude« from it), this builds the NixOS configuration for each host (per file) in the context of all configs provided. # Given either a list (or attr set) of »files« (paths to ».nix« or ».nix.md« files for dirs with »default.nix« files in them) or a »dir« path (and optionally a list of file names to »exclude« from it), this builds the NixOS configuration for each host (per file) in the context of all configs provided.
# If »files« is an attr set, exactly one host with the attribute's name as hostname is built for each attribute. Otherwise the default is to build for one host per configuration file, named as the file name without extension or the sub-directory name. Setting »preface.instances« can override this to build the same configuration for those multiple names instead (the specific »name« is passed as additional »specialArgs« to the modules and can thus be used to adjust the config per instance). # If »files« is an attr set, exactly one host with the attribute's name as hostname is built for each attribute. Otherwise the default is to build for one host per configuration file, named as the file name without extension or the sub-directory name. Setting »${prefix}.preface.instances« can override this to build the same configuration for those multiple names instead (the specific »name« is passed as additional »specialArgs« to the modules and can thus be used to adjust the config per instance).
# All other arguments are as specified by »mkSystemsFlake« and are passed to »mkNixosConfiguration«. # All other arguments are as specified by »mkSystemsFlake« and are passed to »mkNixosConfiguration«.
mkNixosConfigurations = args: let # { files, dir, exclude, ... } mkNixosConfigurations = args: let # { files, dir, exclude, ... }
files = args.files or (getNixFiles args.dir (args.exclude or [ ])); files = args.files or (getNixFiles args.dir (args.exclude or [ ]));
files' = if builtins.isAttrs files then files else (builtins.listToAttrs (map (entryPath: let files' = if builtins.isAttrs files then files else (builtins.listToAttrs (map (entryPath: let
stripped = builtins.match ''^(.*)[.]nix[.]md$'' (builtins.baseNameOf entryPath); stripped = builtins.match ''^(.*)[.]nix[.]md$'' (builtins.baseNameOf entryPath);
name = if stripped != null then (builtins.elemAt stripped 0) else (builtins.baseNameOf entryPath); name = builtins.unsafeDiscardStringContext (if stripped != null then (builtins.elemAt stripped 0) else (builtins.baseNameOf entryPath));
in { inherit name; value = entryPath; }) files)); in { inherit name; value = entryPath; }) files));
configs = mapMerge (name: entryPath: (let configs = mapMerge (name: entryPath: (let
@ -118,7 +129,7 @@ in rec {
ids = mapMerge (name: node: { "${toString node.preface.id}" = name; }) withId; ids = mapMerge (name: node: { "${toString node.preface.id}" = name; }) withId;
duplicate = builtins.removeAttrs withId (builtins.attrValues ids); duplicate = builtins.removeAttrs withId (builtins.attrValues ids);
in if duplicate != { } then ( in if duplicate != { } then (
throw "»my.system.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 managing functions as flake outputs.
@ -129,7 +140,7 @@ in rec {
# Root path of the NixOS configuration. »./.« in the »flake.nix« # Root path of the NixOS configuration. »./.« in the »flake.nix«
configPath ? inputs.self.outPath, configPath ? inputs.self.outPath,
# Arguments »{ files, dir, exclude, }« to »mkNixosConfigurations«, see there for details. May also be a list of those attrsets, in which case those multiple sets of hosts will be built separately by »mkNixosConfigurations«, allowing for separate sets of »peers« passed to »mkNixosConfiguration«. Each call will receive all other arguments, and the resulting sets of hosts will be merged. # Arguments »{ files, dir, exclude, }« to »mkNixosConfigurations«, see there for details. May also be a list of those attrsets, in which case those multiple sets of hosts will be built separately by »mkNixosConfigurations«, allowing for separate sets of »peers« passed to »mkNixosConfiguration«. Each call will receive all other arguments, and the resulting sets of hosts will be merged.
systems ? ({ dir = "${configPath}/hosts/"; exclude = [ ]; }), systems ? ({ dir = "${configPath}/hosts"; exclude = [ ]; }),
# List of overlays to set as »config.nixpkgs.overlays«. Defaults to the ».overlay(s)« of all »overlayInputs«/»inputs« (incl. »inputs.self«). # List of overlays to set as »config.nixpkgs.overlays«. Defaults to the ».overlay(s)« of all »overlayInputs«/»inputs« (incl. »inputs.self«).
overlays ? (builtins.concatLists (map (input: if input?overlay then [ input.overlay ] else if input?overlays then builtins.attrValues input.overlays else [ ]) (builtins.attrValues overlayInputs))), overlays ? (builtins.concatLists (map (input: if input?overlay then [ input.overlay ] else if input?overlays then builtins.attrValues input.overlays else [ ]) (builtins.attrValues overlayInputs))),
# (Subset of) »inputs« that »overlays« will be used from. Example: »{ inherit (inputs) self flakeA flakeB; }«. # (Subset of) »inputs« that »overlays« will be used from. Example: »{ inherit (inputs) self flakeA flakeB; }«.
@ -140,11 +151,11 @@ in rec {
moduleInputs ? (builtins.removeAttrs inputs [ "nixpkgs" ]), moduleInputs ? (builtins.removeAttrs inputs [ "nixpkgs" ]),
# Additional arguments passed to each module evaluated for the host config (if that module is defined as a function). # Additional arguments passed to each module evaluated for the host config (if that module is defined as a function).
specialArgs ? { }, specialArgs ? { },
# List of bash scripts defining functions that do installation and maintenance operations. See »apps« below for more information. # Optional list of bash scripts defining functions that do installation and maintenance operations. See »./setup-scripts/README.md« below for more information. To define additional functions (or overwrite individual default ones), use »(lib.attrValues lib.wip.setup-scripts) ++ [ ]« and place any extra scripts in the array.
scripts ? [ ], scripts ? (lib.attrValues setup-scripts),
# The function of that name as defined in »<nixpkgs>/flake.nix«, or equivalent. # The »nixosSystem« function defined in »<nixpkgs>/flake.nix«, or equivalent.
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« is configured on the building system 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,
... }: let ... }: let
otherArgs = (builtins.removeAttrs args [ "systems" ]) // { inherit systems overlays modules specialArgs scripts inputs configPath nixosSystem localSystem; }; otherArgs = (builtins.removeAttrs args [ "systems" ]) // { inherit systems overlays modules specialArgs scripts inputs configPath nixosSystem localSystem; };
@ -155,7 +166,7 @@ in rec {
pkgs = (import inputs.nixpkgs { inherit overlays; system = localSystem; }); pkgs = (import inputs.nixpkgs { inherit overlays; system = localSystem; });
nix = if lib.versionOlder pkgs.nix.version "2.4" then pkgs.nix_2_4 else pkgs.nix; nix = if lib.versionOlder pkgs.nix.version "2.4" then pkgs.nix_2_4 else pkgs.nix;
nix_wrapped = pkgs.writeShellScriptBin "nix" ''exec ${nix}/bin/nix --extra-experimental-features nix-command "$@"''; nix_wrapped = pkgs.writeShellScriptBin "nix" ''exec ${nix}/bin/nix --extra-experimental-features nix-command "$@"'';
in (if scripts == [ ] then { } else { in (if scripts == [ ] || scripts == null then { } else {
# E.g.: $ nix run .#$target -- install-system /tmp/system-$target.img # E.g.: $ nix run .#$target -- install-system /tmp/system-$target.img
# E.g.: $ nix run /etc/nixos/#$(hostname) -- sudo # E.g.: $ nix run /etc/nixos/#$(hostname) -- sudo

View File

@ -28,11 +28,28 @@ in rec {
# Any function with two (usually unnamed) arguments returning an attrset could be an overlay, so that's rather vague. # Any function with two (usually unnamed) arguments returning an attrset could be an overlay, so that's rather vague.
couldBeOverlay = thing: let result1 = thing (builtins.functionArgs thing); result2 = result1 (builtins.functionArgs result1); in builtins.isFunction thing && builtins.isFunction result1 && builtins.isAttrs result2; couldBeOverlay = thing: let result1 = thing (builtins.functionArgs thing); result2 = result1 (builtins.functionArgs result1); in builtins.isFunction thing && builtins.isFunction result1 && builtins.isAttrs result2;
# Builds an attrset that, for each folder or ».nix« or ».nix.md« file (other than »default.nix«) in this folder, as the name of that folder or the name of the file without extension(s), exports the result of importing that file/folder. # Builds an attrset that, for each folder (containing a »default.nix«) or ».nix« or ».nix.md« file (other than »./default.nix«) in this folder, as the name of that folder or the name of the file without extension(s), exports the result of importing that file/folder.
importAll = inputs: dir: builtins.mapAttrs (name: path: import path (if endsWith "/default.nix" path then "${dir}/${name}" else dir) inputs) (getNamedNixFiles dir [ "default.nix" ]); importAll = inputs: dir: builtins.mapAttrs (name: path: import path (if endsWith "/default.nix" path then "${dir}/${name}" else dir) inputs) (getNamedNixFiles dir [ "default.nix" ]);
# Import a Nix file that expects the standard `dirname: inputs: ` arguments. # Import a Nix file that expects the standard `dirname: inputs: ` arguments, providing some additional information and error handling.
importWrapped = inputs: path: import path (if (builtins.match ''^(.*)[.]nix([.]md)?$'' path) != null then builtins.dirOf path else path) inputs; importWrapped = inputs: path: rec {
# Whether the file is imported by an explicit full path (or one omitting ».nix« or »/default.nix«):
isExplicit = (builtins.match ''^(.*)[.]nix([.]md)?$'' path) != null;
# Whether the import path _implicitly_ refers to the »/default.nix« in a directory:
isImplicitDir = !isExplicit && builtins.pathExists "${path}/default.nix";
# The resolved path that will be imported:
fullPath = if isImplicitDir then "${path}/default.nix" else if isExplicit then path else "${path}.nix";
# The imported nix value:
result = import fullPath (if isImplicitDir then path else builtins.dirOf path) inputs;
# Whether the import path points to an existing file:
exists = isImplicitDir || (builtins.pathExists (if isExplicit then path else "${path}.nix"));
# Return »null« if not ».exists«:
optional = if exists then result else null;
# Throw if not ».exists«:
required = if exists then result else throw (if isExplicit then "File ${path} does not exist" else "Neither ${path}/default.nix nor ${path}.nix exist");
# ».result« interpreted as NixOS module, wrapped to preserve the import path:
module = { _file = fullPath; imports = [ required ]; };
};
## Returns an attrset that, for each file in »dir« (except »default.nix« and as filtered and named by »getNamedNixFiles dir except«), imports that file and exposes only if the result passes »filter«. If provided, the imported value is »wrapped« after filtering. ## Returns an attrset that, for each file in »dir« (except »default.nix« and as filtered and named by »getNamedNixFiles dir except«), imports that file and exposes only if the result passes »filter«. If provided, the imported value is »wrapped« after filtering.
# If a file/folder' import that is rejected by »filter« is an attrset (for example because it results from a call to this function), then all attributes whose values pass »filter« are prefixed with the file/folders name plus a slash and merged into the overall attrset. # If a file/folder' import that is rejected by »filter« is an attrset (for example because it results from a call to this function), then all attributes whose values pass »filter« are prefixed with the file/folders name plus a slash and merged into the overall attrset.
@ -72,7 +89,7 @@ in rec {
# Given a list of »overlays« and »pkgs« with them applied, returns the subset of »pkgs« that was directly modified by the overlays. # Given a list of »overlays« and »pkgs« with them applied, returns the subset of »pkgs« that was directly modified by the overlays.
getModifiedPackages = pkgs: overlays: let getModifiedPackages = pkgs: overlays: let
names = builtins.concatLists (map (overlay: builtins.attrNames (overlay { } { })) (builtins.attrValues overlays)); names = builtins.concatLists (map (overlay: builtins.attrNames (overlay { } { })) (builtins.attrValues overlays));
in mapMerge (name: { ${name} = pkgs.${name}; }) names; in mapMerge (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. ## 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.
# Every module should, but not all modules do, provide such an option themselves. # Every module should, but not all modules do, provide such an option themselves.
@ -101,7 +118,7 @@ in rec {
moduleArgs = { utils = import "${specialArgs.inputs.nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; }; } // specialArgs; moduleArgs = { utils = import "${specialArgs.inputs.nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; }; } // specialArgs;
module = import fullPath moduleArgs; module = import fullPath moduleArgs;
in { _file = fullPath; imports = [ in { _file = fullPath; imports = [
(mergeAttrsRecursive (([ { imports = module.imports or [ ]; options = module.options or { }; config = module.config or { }; } ]) ++ (lib.toList (override module)))) (mergeAttrsRecursive ([ { imports = module.imports or [ ]; options = module.options or { }; config = module.config or { }; } ] ++ (lib.toList (override module))))
{ disabledModules = [ modulePath ]; } { disabledModules = [ modulePath ]; }
]; }; ]; };
} }

View File

@ -20,19 +20,7 @@ in rec {
<<<${lib.escapeShellArg text} cat >$out/${lib.escapeShellArg "/etc/systemd/system/${unit}.d/override.conf"} <<<${lib.escapeShellArg text} cat >$out/${lib.escapeShellArg "/etc/systemd/system/${unit}.d/override.conf"}
''); '');
# Given »config.ids« (or equivalent) and a user name, returns the users numeric »uid:gid« pair as string.
getOwnership = { gids, uids, ... }: user: "${toString uids.${user}}:${toString gids.${user}}";
# Given »from« and »to« as »config.my.network.spec.hosts.*«,
# picks the first of »to«'s IPs whose required subnet is either empty/any, or a prefix to any of the subnets in »from«:
# ip = preferredRoute self.subNets other.routes;
# ip6 = preferredRoute self.subNets (builtins.filter (r: r.is6) other.routes);
# to.find(({ ip, prefix }) => from.any(_=>_.startsWith(prefix))).ip
preferredRoute = from: to: (lib.findFirst ({ prefix, ip, ... }: prefix == "" || (builtins.any (fromSub: startsWith prefix fromSub) from)) { ip = ""; } to).ip;
# Given a message and any value, traces both the message and the value, and returns the value. # Given a message and any value, traces both the message and the value, and returns the value.
trace = message: value: (builtins.trace (message +": "+ (lib.generators.toPretty { } value)) value); trace = message: value: (builtins.trace (message +": "+ (lib.generators.toPretty { } value)) value);
rpoolOf = hostName: "rpool-${builtins.substring 0 8 (builtins.hashString "sha256" hostName)}";
} }

View File

@ -3,7 +3,7 @@
This is a library of bash functions, mostly for NixOS system installation. This is a library of bash functions, mostly for NixOS system installation.
The (paths to these) scripts are meant to me passed in the `scripts` argument to [`mkSystemsFlake`](../flakes.nix#mkSystemsFlake) (see [`flake.nix`](../../flake.nix) for an example), which makes their functions available in the per-host `devShells`/`apps`. The (paths to these) scripts are meant to be passed in the `scripts` argument to [`mkSystemsFlake`](../flakes.nix#mkSystemsFlake) (see [`flake.nix`](../../flake.nix) for an example), which makes their functions available in the per-host [`devShells`/`apps`](../flakes.nix#mkSystemsFlake).
Host-specific nix variables are available to the bash functions as `@{...}` through [`substituteImplicit`](../scripts.nix#substituteImplicit) with the respective host as root context. Host-specific nix variables are available to the bash functions as `@{...}` through [`substituteImplicit`](../scripts.nix#substituteImplicit) with the respective host as root context.
Any script passed later in `scripts` can overwrite the functions of these (earlier) default scripts. Any script passed later in `scripts` can overwrite the functions of these (earlier) default scripts.
@ -21,16 +21,16 @@ function install-system {( set -eu # 1: diskPaths
# `install-system` Documentation # `install-system` Documentation
The above function performs the mostly automated installation of any `$HOST` from [`../../hosts/`](../../hosts/) to the local disk(s) (or image file(s)) `$DISK`. The above function performs the mostly automated installation of any `$HOST` from [`../../hosts/`](../../hosts/) to the local disk(s) (or image file(s)) `$DISK`.
On a NixOS host, this script can be run by root as: `#` `( cd /etc/nixos/ && nix run .#"$HOST" -- install-system "$DISK" )`. On a NixOS host, this can be run by root as: `#` `nix run .#"$HOST" -- install-system "$DISK"`.
Doing an installation on non-NixOS (but Linux), where nix isn't installed for root, is a bit of a hack, but works as well. Doing an installation on non-NixOS (but Linux), where nix isn't installed for root, is a bit of a hack, but works as well.
In this case, all `nix` commands will be run as `$SUDO_USER`, but this script and some other user-owned (or user-generated) code will (need to) be run as root. In this case, all `nix` commands will be run as `$SUDO_USER`, but this script and some other user-owned (or user-generated) code will (need to) be run as root.
If that is acceptable, run with `sudo` as first argument: `$` `( cd /etc/nixos/ && nix run .#"$HOST" -- sudo install-system "$DISK" )` (And then maybe `sudo bash -c 'chown $SUDO_USER: '"$DISK"` afterwards.) If that is acceptable, run with `sudo` as first argument: `$` `nix run .#"$HOST" -- sudo install-system "$DISK"` (And then maybe `sudo bash -c 'chown $SUDO_USER: '"$DISK"` afterwards.)
The `nix run` in the above commands substitutes a number of `@{`-prefixed variables based on the `$HOST` name and its configuration from [`../hosts/`](../hosts/), and then sources this script and calls the `install-system` function.
If `$DISK` points to something in `/dev/`, then it is directly formatted and written to as block device, otherwise `$DISK` is (re-)created as raw image and then used as loop device. If `$DISK` points to something in `/dev/`, then it is directly formatted and written to as block device, otherwise `$DISK` 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). 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).
Once done, the disk can be transferred -- or the image be copied -- to the final system, and should boot there. Once done, the disk can be transferred -- or the image be copied -- to the final system, and should boot there.
If the host's hardware target allows, a resulting image can also be passed to [`register-vbox`](../lib/setup-scripts/maintenance.sh#register-vbox) to create a bootable VirtualBox instance for the current user. If the host's hardware target allows, a resulting image can also be passed to [`register-vbox`](../maintenance.sh#register-vbox) to create a bootable VirtualBox instance for the current user, or to [`run-qemu`](../maintenance.sh#run-qemu) to start it in a qemu VM.
The "Installation" section of each host's documentation should contain host specific details, if any. The "Installation" section of each host's documentation should contain host specific details, if any.

View File

@ -73,7 +73,7 @@ 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 --load-backup=@{config.wip.fs.disks.partitioning}/"${disk[name]}".backup "${blockDevs[${disk[name]}]}" >$beQuiet ) ( PATH=@{native.gptfdisk}/bin ; set -x ; sgdisk --zap-all --load-backup=@{config.wip.fs.disks.partitioning}/"${disk[name]}".backup "${blockDevs[${disk[name]}]}" >$beQuiet )
#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[@]}"
@ -92,7 +92,7 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
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-secondary-table=$(( devSize/512 - 1 - 32 - ${disk[gptOffset]} )) ) sgdisk+=( --move-backup-table=$(( devSize/512 - 1 - 32 - ${disk[gptOffset]} )) )
fi fi
sgdisk+=( --disk-guid="${disk[guid]}" ) sgdisk+=( --disk-guid="${disk[guid]}" )
@ -124,13 +124,13 @@ 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/512) - 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)
# create GPT part (spanning primary GPT area) as last part # create GPT part (spanning primary GPT area and its padding) as last part
n;p;4 # new ; primary ; part4 n;p;4 # new ; primary ; part4
1;33 # start ; end 1;$(( 33 + ${disk[gptOffset]} )) # start ; end
t;4;ee # type ; part4 ; GPT t;4;ee # type ; part4 ; GPT
${disk[extraFDiskCommands]} ${disk[extraFDiskCommands]}

View File

@ -69,6 +69,7 @@ function install-system-to {( set -eu # 1: mnt, 2?: topLevel
mkdir -p -m 755 $mnt/nix/var/nix ; mkdir -p -m 1775 $mnt/nix/store mkdir -p -m 755 $mnt/nix/var/nix ; mkdir -p -m 1775 $mnt/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 copy --no-check-sigs --to $mnt ${topLevel:-$targetSystem} ) ; rm -rf $mnt/nix/var/nix/gcroots ( set -x ; time nix copy --no-check-sigs --to $mnt ${topLevel:-$targetSystem} ) ; 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
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
# Link/create files that some tooling expects: # Link/create files that some tooling expects:
@ -83,9 +84,9 @@ function install-system-to {( set -eu # 1: mnt, 2?: topLevel
mkdir -p -m 755 $mnt/nix/var/nix/profiles ; ln -sT $(realpath $targetSystem) $mnt/nix/var/nix/profiles/system-1-link ; ln -sT system-1-link $mnt/nix/var/nix/profiles/system mkdir -p -m 755 $mnt/nix/var/nix/profiles ; ln -sT $(realpath $targetSystem) $mnt/nix/var/nix/profiles/system-1-link ; ln -sT system-1-link $mnt/nix/var/nix/profiles/system
# Support cross architecture installation (not sure if this is actually required) # Support cross architecture installation (not sure if this is actually required)
if [[ $(cat /run/current-system/system 2>/dev/null || echo "x86_64-linux") != "@{config.preface.hardware}"-linux ]] ; then if [[ $(cat /run/current-system/system 2>/dev/null || echo "x86_64-linux") != "@{config.wip.preface.hardware}"-linux ]] ; then
mkdir -p $mnt/run/binfmt ; cp -a {,$mnt}/run/binfmt/"@{config.preface.hardware}"-linux || true mkdir -p $mnt/run/binfmt ; cp -a {,$mnt}/run/binfmt/"@{config.wip.preface.hardware}"-linux || true
# Ubuntu (by default) expects the "interpreter" at »/usr/bin/qemu-@{config.preface.hardware}-static«. # Ubuntu (by default) expects the "interpreter" at »/usr/bin/qemu-@{config.wip.preface.hardware}-static«.
fi fi
# Run the main install command (primarily for the bootloader): # Run the main install command (primarily for the bootloader):

View File

@ -51,12 +51,12 @@ function run-qemu {( set -eu # 1: diskImages
diskImages=${argv[0]} diskImages=${argv[0]}
if [[ ${args[debug]:-} ]] ; then set -x ; fi if [[ ${args[debug]:-} ]] ; then set -x ; fi
qemu=( @{native.qemu_full}/bin/qemu-system-@{config.preface.hardware} ) qemu=( @{native.qemu_full}/bin/qemu-system-@{config.wip.preface.hardware} )
qemu+=( -m ${args[mem]:-2048} -smp ${args[smp]:-4} ) qemu+=( -m ${args[mem]:-2048} -smp ${args[smp]:-4} )
if [[ @{config.preface.hardware}-linux == "@{native.system}" && ! ${args[no-kvm]:-} ]] ; then if [[ @{config.wip.preface.hardware}-linux == "@{native.system}" && ! ${args[no-kvm]:-} ]] ; then
qemu+=( -cpu host -enable-kvm ) # For KVM to work vBox may not be running anything at the same time (and vBox hangs on start if qemu runs). Pass »--no-kvm« and accept ~10x slowdown, or stop vBox. qemu+=( -cpu host -enable-kvm ) # For KVM to work vBox may not be running anything at the same time (and vBox hangs on start if qemu runs). Pass »--no-kvm« and accept ~10x slowdown, or stop vBox.
elif [[ @{config.preface.hardware} == aarch64 ]] ; then # assume it's a raspberry PI (or compatible) elif [[ @{config.wip.preface.hardware} == aarch64 ]] ; then # assume it's a raspberry PI (or compatible)
# TODO: this does not work yet: # TODO: this does not work yet:
qemu+=( -machine type=raspi3b -m 1024 ) ; args[no-nat]=1 qemu+=( -machine type=raspi3b -m 1024 ) ; args[no-nat]=1
# ... and neither does this: # ... and neither does this:
@ -74,7 +74,7 @@ function run-qemu {( set -eu # 1: diskImages
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 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
# 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.preface.hardware} == aarch64 ]] ; then if [[ @{config.wip.preface.hardware} == aarch64 ]] ; then
qemu+=( -kernel @{config.system.build.kernel}/Image -initrd @{config.system.build.initialRamdisk}/initrd -append "$(echo -n "@{config.boot.kernelParams[@]}")" ) qemu+=( -kernel @{config.system.build.kernel}/Image -initrd @{config.system.build.initialRamdisk}/initrd -append "$(echo -n "@{config.boot.kernelParams[@]}")" )
fi fi

View File

@ -105,7 +105,7 @@ in rec {
parseSizeSuffix = decl: let parseSizeSuffix = decl: let
match = builtins.match ''^([0-9]+)(K|M|G|T|P)?(i)?(B)?$'' decl; match = builtins.match ''^([0-9]+)(K|M|G|T|P)?(i)?(B)?$'' decl;
num = lib.toInt (builtins.head match); unit = builtins.elemAt match 1; num = lib.toInt (builtins.head match); unit = builtins.elemAt match 1;
exponent = if unit == null then 0 else { K = 1; M = 2; G = 3; t = 4; P = 5; }.${unit}; exponent = if unit == null then 0 else { K = 1; M = 2; G = 3; T = 4; P = 5; }.${unit};
base = if (builtins.elemAt match 3) == null || (builtins.elemAt match 2) != null then 1024 else 1000; base = if (builtins.elemAt match 3) == null || (builtins.elemAt match 2) != null then 1024 else 1000;
in if builtins.isInt decl then decl else if match != null then num * (pow base exponent) else throw "${decl} is not a number followed by a size suffix"; in if builtins.isInt decl then decl else if match != null then num * (pow base exponent) else throw "${decl} is not a number followed by a size suffix";

View File

@ -9,16 +9,21 @@ Things that really should be (more like) this by default.
```nix ```nix
#*/# end of MarkDown, beginning of NixOS module: #*/# end of MarkDown, beginning of NixOS module:
dirname: inputs: specialArgs@{ config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let dirname: inputs: specialArgs@{ config, pkgs, lib, name, ... }: let inherit (inputs.self) lib; in let
prefix = inputs.config.prefix; prefix = inputs.config.prefix;
cfg = config.${prefix}.base; cfg = config.${prefix}.base;
in { in {
options.${prefix} = { base = { options.${prefix} = { base = {
enable = lib.mkEnableOption "saner defaults"; enable = lib.mkEnableOption "saner defaults";
includeNixpkgs = lib.mkOption { description = "»nixpkgs« to include in the system build."; type = lib.types.nullOr lib.types.package; default = null; }; includeInputs = lib.mkOption { description = "Whether to include all build inputs to the configuration in the final system, such that they are available for self-rebuilds, in the flake registry, and on the »NIX_PATH« entry (e.g. as »pkgs« on the CLI)."; type = lib.types.bool; default = specialArgs?inputs && specialArgs.inputs?self && specialArgs.inputs?nixpkgs; };
}; }; }; };
# Bugfix:
imports = [ (lib.wip.overrideNixpkgsModule ({ inherit inputs; } // specialArgs) "misc/extra-arguments.nix" (old: { config._module.args.utils = old._module.args.utils // {
escapeSystemdPath = s: builtins.replaceStrings [ "/" "-" " " "." ] [ "-" "\\x2d" "\\x20" "\\x2e" ] (lib.removePrefix "/" s); # The original function does not escape ».«, resulting in mismatching names with units generated from paths with ».« in them.
}; })) ];
config = let config = let
hash = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName); hash = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName);
implied = true; # some mount points are implied (and forced) to be »neededForBoot« in »specialArgs.utils.pathsNeededForBoot« (this marks those here) implied = true; # some mount points are implied (and forced) to be »neededForBoot« in »specialArgs.utils.pathsNeededForBoot« (this marks those here)
@ -34,23 +39,41 @@ in {
}) ({ }) ({
# Robustness/debugging: # Robustness/debugging:
boot.kernelParams = [ "panic=10" "boot.panic_on_fail" ]; # Reboot on kernel panic, panic if boot fails. boot.kernelParams = [ "panic=10" "boot.panic_on_fail" ]; # Reboot on kernel panic (showing the printed messages for 10s), panic if boot fails.
# might additionally want to do this: https://stackoverflow.com/questions/62083796/automatic-reboot-on-systemd-emergency-mode # might additionally want to do this: https://stackoverflow.com/questions/62083796/automatic-reboot-on-systemd-emergency-mode
systemd.extraConfig = "StatusUnitFormat=name"; # Show unit names instead of descriptions during boot. systemd.extraConfig = "StatusUnitFormat=name"; # Show unit names instead of descriptions during boot.
}) (lib.mkIf (cfg.includeNixpkgs != null) { }) (lib.mkIf cfg.includeInputs { # non-flake
nix.registry.nixpkgs.flake = cfg.includeNixpkgs; # 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:
environment.etc."nix/channels/nixpkgs".source = cfg.includeNixpkgs.outPath; system.extraSystemBuilderCmds = ''
nix.nixPath = [ "nixpkgs=/etc/nix/channels/nixpkgs" "nixos-config=/etc/nixos" ]; ln -sT ${pkgs.writeText "pkgs.nix" ''
# Provide the exact same version of (nix)pkgs on the CLI as in the NixOS-configuration (but note that this ignores the args passed to it; and it'll be a bit slower, as it partially evaluates the host's configuration):
args: (builtins.getFlake ${builtins.toJSON specialArgs.inputs.self.outPath}).nixosConfigurations.${name}.pkgs
''} $out/pkgs # (nixpkgs with overlays)
''; # (use this indirection so that all open shells update automatically)
nix.nixPath = [ "nixpkgs=/run/current-system/pkgs" ]; # this intentionally replaces the defaults: nixpkgs is here, /etc/nixos/flake.nix is implicit, channels are impure
# TODO: decide whether to put any other flake inputs also on the nix path: con: they may very well not even have a »./default.nix«
nix.autoOptimiseStore = true; # because why not ...
environment.shellAliases = { "with" = ''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
}) (lib.mkIf cfg.includeInputs { # flake things
# "input" to the system build is definitely also a nix version that works with flakes:
nix.extraOptions = "experimental-features = nix-command flakes"; # apparently, even nix 2.8 (in nixos-22.05) needs this nix.extraOptions = "experimental-features = nix-command flakes"; # apparently, even nix 2.8 (in nixos-22.05) needs this
environment.shellAliases = { "with" = ''nix-shell --run "bash --login" -p''; };
system.extraSystemBuilderCmds = (if !specialArgs?inputs && !specialArgs.inputs?self then "" else ''
ln -sT ${specialArgs.inputs.self.outPath} $out/config # (build input for reference)
'');
environment.systemPackages = [ pkgs.git ]; # necessary as external dependency when working with flakes environment.systemPackages = [ pkgs.git ]; # necessary as external dependency when working with flakes
# »inputs.self« does not have a name (that is known here), so just register it as »/etc/nixos/« system config:
environment.etc.nixos.source = lib.mkDefault "/run/current-system/config"; # (use this indirection to prevent every change in the config to necessarily also change »/etc«)
system.extraSystemBuilderCmds = ''
ln -sT ${specialArgs.inputs.self.outPath} $out/config # (build input for reference)
'';
# Add all inputs to the flake registry:
nix.registry = lib.mapAttrs (name: input: lib.mkDefault { flake = input; }) (builtins.removeAttrs specialArgs.inputs [ "self" ]);
}) ({ }) ({
# Free convenience: # Free convenience:
@ -70,10 +93,10 @@ in {
fi fi
fi fi
export TERM_RECURSION_DEPTH=$(( 1 + ''${TERM_RECURSION_DEPTH:-0} )) 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. ''; # 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.mkDefault ''
if [[ "$(realpath /dev/stdin)" == /dev/ttyS* && $LINES == 24 && $COLUMNS == 80 ]] ; then if [[ "$(realpath /dev/stdin)" != /dev/tty[1-8] && $LINES == 24 && $COLUMNS == 80 ]] ; then
stty rows 34 cols 145 # Fairly large font on 1080p. Definitely a better default than 24x80. stty rows 34 cols 145 # Fairly large font on 1080p. Definitely a better default than 24x80.
fi fi
''; '';

View File

@ -9,9 +9,7 @@ Filesystem related "patches" of options in nixpkgs, i.e. additions of options th
```nix ```nix
#*/# end of MarkDown, beginning of NixOS module: #*/# end of MarkDown, beginning of NixOS module:
dirname: inputs: specialArgs@{ config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let dirname: inputs: specialArgs@{ config, pkgs, lib, utils, ... }: let inherit (inputs.self) lib; in let
utils = import "${inputs.nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; };
in { in {
options = { options = {

View File

@ -95,11 +95,11 @@ On a less beefy system, but also with less data to manage, `tmpfs` works fine fo
```nix ```nix
#*/# end of MarkDown, beginning of NixOS module: #*/# end of MarkDown, beginning of NixOS module:
dirname: inputs: specialArgs@{ config, pkgs, lib, ... }: let inherit (inputs.self) lib; in let dirname: inputs: specialArgs@{ config, pkgs, lib, utils, ... }: let inherit (inputs.self) lib; in let
prefix = inputs.config.prefix; prefix = inputs.config.prefix;
cfg = config.${prefix}.fs.temproot; cfg = config.${prefix}.fs.temproot;
utils = import "${inputs.nixpkgs.outPath}/nixos/lib/utils.nix" { inherit (specialArgs) lib config pkgs; };
hash = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName); hash = builtins.substring 0 8 (builtins.hashString "sha256" config.networking.hostName);
keep = if cfg.remote.type == "none" then "local" else "remote"; # preferred place for data that should be kept
optionsFor = type: desc: { optionsFor = type: desc: {
bind.source = lib.mkOption { description = "Prefix for bind-mount targets."; type = lib.types.str; default = "/.${type}"; }; bind.source = lib.mkOption { description = "Prefix for bind-mount targets."; type = lib.types.str; default = "/.${type}"; };
@ -151,7 +151,8 @@ in {
type = lib.mkOption { description = '' type = lib.mkOption { description = ''
"bind": Expects a filesystem to be mounted at ».bind.target« that gets backed. Bind-mounts any additional mounts to ».bind.source+"/"+<mount.source>« (creating those paths if necessary). "bind": Expects a filesystem to be mounted at ».bind.target« that gets backed. Bind-mounts any additional mounts to ».bind.source+"/"+<mount.source>« (creating those paths if necessary).
"zfs": ... "zfs": ...
''; type = lib.types.enum [ "bind" "zfs" ]; default = "bind"; }; "none": Don't provide a »/remote«. For hosts that have no secrets or important state.
''; type = lib.types.enum [ "bind" "zfs" "none" ]; default = "bind"; };
} // (optionsFor "remote" "remotely backed-up"); } // (optionsFor "remote" "remotely backed-up");
swap = { swap = {
@ -172,9 +173,9 @@ in {
"/tmp" = { mode = "1777"; }; "/tmp" = { mode = "1777"; };
}; };
fs.temproot.local.mounts = { fs.temproot.local.mounts = {
"/local" = { source = "system"; mode = "755"; };
"/nix" = { zfsProps = zfsNoSyncProps; mode = "755"; }; # this (or /nix/store) is required "/nix" = { zfsProps = zfsNoSyncProps; mode = "755"; }; # this (or /nix/store) is required
"/var/log" = { source = "logs"; mode = "755"; }; "/var/log" = { source = "logs"; mode = "755"; };
"/local" = { source = "system"; mode = "755"; };
# »/swap« is used by »cfg.swap.asPartition = false« # »/swap« is used by »cfg.swap.asPartition = false«
}; };
fs.temproot.remote.mounts = { fs.temproot.remote.mounts = {
@ -187,17 +188,17 @@ in {
}) (lib.mkIf cfg.persistenceFixes { # Cope with the consequences of having »/« (including »/{etc,var,root,...}«) cleared on every reboot. }) (lib.mkIf cfg.persistenceFixes { # Cope with the consequences of having »/« (including »/{etc,var,root,...}«) cleared on every reboot.
environment.etc.nixos.source = "/local/etc/nixos";
systemd.tmpfiles.rules = [ # keep in mind: this does not get applied super early ... systemd.tmpfiles.rules = [ # keep in mind: this does not get applied super early ...
# nixos config
"L+ /etc/nixos/ - - - - ../../remote/etc/nixos/"
# »/root/.nix-channels« is already being restored. # »/root/.nix-channels« is already being restored.
# »/var/lib/nixos« remains ephemeral. Where it matters, explicitly define a U/GID! # »/var/lib/nixos« remains ephemeral. Where it matters, explicitly define a U/GID!
# root's command history # root's command history
"f /remote/root/.bash_history 0600 root root -" "f /${keep}/root/.bash_history 0600 root root -"
"L+ /root/.bash_history - - - - ../remote/root/.bash_history" "L+ /root/.bash_history - - - - ../${keep}/root/.bash_history"
"f /remote/root/.local/share/nix/repl-history 0600 root root -" "f /${keep}/root/.local/share/nix/repl-history 0600 root root -"
"L+ /root/.local/share/nix/repl-history - - - - ../../../../remote/root/.local/share/nix/repl-history" "L+ /root/.local/share/nix/repl-history - - - - ../../../../${keep}/root/.local/share/nix/repl-history"
]; ];
fileSystems = { # this does get applied early fileSystems = { # this does get applied early
# (on systems without hardware clock, this allows systemd to provide an at least monolithic time after restarts) # (on systems without hardware clock, this allows systemd to provide an at least monolithic time after restarts)
@ -252,7 +253,7 @@ in {
${prefix} = { ${prefix} = {
fs.keystore.keys."luks/local-${hash}/0" = lib.mkIf (cfg.local.bind.base == "f2fs-encrypted") (lib.mkOptionDefault "random"); fs.keystore.keys."luks/local-${hash}/0" = lib.mkIf (cfg.local.bind.base == "f2fs-encrypted") (lib.mkOptionDefault "random");
fs.disks.partitions."local-${hash}" = { fs.disks.partitions."local-${hash}" = {
type = "8300"; order = 1000; disk = "primary"; size = "50%"; type = "8300"; order = lib.mkDefault 1000; disk = lib.mkDefault "primary"; size = lib.mkDefault (if cfg.remote.type == "none" then null else "50%");
}; };
}; };
fileSystems.${cfg.local.bind.source} = { fsType = "f2fs"; device = "/dev/${if useLuks then "mapper" else "disk/by-partlabel"}/local-${hash}"; formatOptions = lib.mkDefault (lib.concatStrings [ fileSystems.${cfg.local.bind.source} = { fsType = "f2fs"; device = "/dev/${if useLuks then "mapper" else "disk/by-partlabel"}/local-${hash}"; formatOptions = lib.mkDefault (lib.concatStrings [

View File

@ -42,7 +42,7 @@ in {
systemd.services."dropbear" = { systemd.services."dropbear" = {
description = "dropbear SSH server (listening)"; description = "dropbear SSH server (listening)";
wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; after = [ "network.target" ];
serviceConfig.PreExec = lib.mkIf (cfg.hostKeys == [ ]) "${pkgs.coreutils}/bin/mkdir -p /etc/dropbear/"; serviceConfig.ExecStartPre = lib.mkIf (cfg.hostKeys == [ ]) "${pkgs.coreutils}/bin/mkdir -p /etc/dropbear/";
serviceConfig.ExecStart = defaultArgs + " -F -E"; # don't fork, use stderr serviceConfig.ExecStart = defaultArgs + " -F -E"; # don't fork, use stderr
#serviceConfig.PIDFile = "/var/run/dropbear.pid"; serviceConfig.Type = "forking"; after = [ "network.target" ]; # alternative to »-E -F« (?) #serviceConfig.PIDFile = "/var/run/dropbear.pid"; serviceConfig.Type = "forking"; after = [ "network.target" ]; # alternative to »-E -F« (?)
}; };

View File

@ -24,4 +24,7 @@ in {
../patches/gptfdisk-move-secondary-table.patch ../patches/gptfdisk-move-secondary-table.patch
]; ];
}); });
libblockdev = prev.libblockdev.override { inherit (prev) gptfdisk; };
} }

View File

@ -32,6 +32,7 @@ in {
hash = "sha256-6cHkr3s7/2BVXBTn9bUfPFbYAfv9VYh6C9GAbWILNjs="; hash = "sha256-6cHkr3s7/2BVXBTn9bUfPFbYAfv9VYh6C9GAbWILNjs=";
}; };
nativeBuildInputs = [ pkgs.cmake pkgs.zlib ]; nativeBuildInputs = [ pkgs.cmake pkgs.zlib ];
outputs = [ "out" "lib" ];
meta = { meta = {
homepage = "https://github.com/sbabic/libubootenv"; homepage = "https://github.com/sbabic/libubootenv";

View File

@ -0,0 +1,55 @@
/*
# U-Boot with Env Compiled and on MMC
A function reconfiguring an u-boot package to save its env on MMC (e.g. internal boot storage or microSD) if it doesn't already, and to compile a custom default env into u-boot itself.
## Implementation
```nix
#*/# end of MarkDown, beginning of NixPkgs overlay:
dirname: inputs: final: prev: let
inherit (final) pkgs; inherit (inputs.self) lib;
in {
uboot-with-mmc-env = {
base ? null,
# The (maximum) total size, position (each in in bytes), and MMC device number of the u-boot env that can be set later:
envSize ? base.envSize or 16384,
envOffset ? base.envOffset or 4194304,
envMmcDev ? 1,
# Default boot variables for u-boot. This amends the variables that will be compiled into u-boot as the default env, but also is the base for the variables written by the ».mkEnv« function. As such, this should include everything necessary to boot something:
defaultEnv ? ({ }), # $src/scripts/get_default_env.sh can read this again
# Lines to append to the u-boot config:
extraConfig ? [ ],
}: base.overrideAttrs (old: let
envTxt = env: pkgs.writeText "uboot-env.txt" "${lib.concatStrings (lib.mapAttrsToList (k: v: if v == null then "" else "${k}=${toString v}\n") env)}";
defaultEnv' = (base.defaultEnv or { }) // defaultEnv;
in {
passthru = (old.passthru or { }) // {
inherit envSize envOffset; defaultEnv = defaultEnv';
# Creates a (user) env blob for this u-boot by merging »env« over its »defaultEnv«. The resulting file can be flashed to »CONFIG_ENV_OFFSET« to replace the default env.
mkEnv = env: pkgs.runCommandLocal "uboot-env.img" {
env = envTxt (defaultEnv' // env);
} "${pkgs.ubootTools}/bin/mkenvimage -p 0x00 -s ${toString envSize} -o $out $env";
};
extraConfig = (old.extraConfig or "") + "${lib.concatStringsSep "\n" ([
# (these need to be passed as 0x<hex>:)
"CONFIG_ENV_OFFSET=0x${lib.concatStrings (map toString (lib.toBaseDigits 16 envOffset))}"
"CONFIG_ENV_SIZE=0x${lib.concatStrings (map toString (lib.toBaseDigits 16 envSize))}"
# Ensure that env is configured to be stored on MMC(/microSD):
"CONFIG_ENV_IS_IN_MMC=y" "CONFIG_SYS_MMC_ENV_DEV=${toString envMmcDev}" # (not sure this is enough)
# CONFIG_EXTRA_ENV_SETTINGS here would be overwritten, and CONFIG_DEFAULT_ENV_FILE replaces some basics that should be kept.
] ++ extraConfig)}\n";
CONFIG_EXTRA_ENV_SETTINGS = ''${lib.concatMapStringsSep ''"\0"'' builtins.toJSON (lib.mapAttrsToList (k: v: if v == null then "" else ''${k}=${toString v}'') defaultEnv')}"\0"''; # (this is in addition to whatever u-boot derives from its other CONFIG_*)
postConfigure = (old.postConfigure or "") + ''
# Set CONFIG_EXTRA_ENV_SETTINGS just before it's used, to make sure it actually applies:
printf "%s\n%s\n" "#define CONFIG_EXTRA_ENV_SETTINGS $CONFIG_EXTRA_ENV_SETTINGS" "$(cat include/env_default.h)" >include/env_default.h
'';
});
}

View File

@ -3,11 +3,6 @@ dirname: inputs: let
getNamedPatchFiles = dir: builtins.removeAttrs (builtins.listToAttrs (map (name: let getNamedPatchFiles = dir: builtins.removeAttrs (builtins.listToAttrs (map (name: let
match = builtins.match ''^(.*)[.]patch$'' name; match = builtins.match ''^(.*)[.]patch$'' name;
in if (match != null) then { in if (match != null) then {
name = builtins.head match; value = "${dir}/${name}"; name = builtins.head match; value = builtins.path { path = "${dir}/${name}"; inherit name; }; # »builtins.path« puts the file in a separate, content-addressed store path, ensuring it's path only changes when the content changes, thus avoiding unnecessary rebuilds.
} else { name = ""; value = null; }) (builtins.attrNames (builtins.readDir dir)))) [ "" ]; } else { name = ""; value = null; }) (builtins.attrNames (builtins.readDir dir)))) [ "" ];
in (getNamedPatchFiles dirname) // { in (getNamedPatchFiles dirname)
# When referring to the patches by a path derived from »dirname«, then their paths change whenever that changes, which happens when any file in this repo changes. Changing patch paths mean that the derivations the patches are inputs to need to be rebuilt, so using local paths, which put their targets into a new store artifact (i.e. separate input) is much more efficient.
# TODO: automate this, somehow:
nixpkgs-fix-systemd-boot-install = ./nixpkgs-fix-systemd-boot-install.patch;
nixpkgs-test = ./nixpkgs-test.patch;
}

View File

@ -1,5 +1,5 @@
diff --git a/gpt.cc b/gpt.cc diff --git a/gpt.cc b/gpt.cc
index 76cd9ad..375c216 100644 index 76cd9ad..d61064f 100644
--- a/gpt.cc --- a/gpt.cc
+++ b/gpt.cc +++ b/gpt.cc
@@ -1500,7 +1500,7 @@ int GPTData::DestroyGPT(void) { @@ -1500,7 +1500,7 @@ int GPTData::DestroyGPT(void) {
@ -17,7 +17,7 @@ index 76cd9ad..375c216 100644
+// Change the start sector for the secondary partition table. +// Change the start sector for the secondary partition table.
+// Returns 1 on success, 0 on failure +// Returns 1 on success, 0 on failure
+int GPTData::MoveSecondaryTable(uint64_t pteSector) { +int GPTData::MoveSecondTable(uint64_t pteSector) {
+ uint64_t pteSize = GetTableSizeInSectors(); + uint64_t pteSize = GetTableSizeInSectors();
+ int retval = 1; + int retval = 1;
+ +
@ -30,7 +30,7 @@ index 76cd9ad..375c216 100644
+ retval = 0; + retval = 0;
+ } // if/else + } // if/else
+ return retval; + return retval;
+} // GPTData::MoveSecondaryTable() +} // GPTData::MoveSecondTable()
+ +
// Blank the partition array // Blank the partition array
void GPTData::BlankPartitions(void) { void GPTData::BlankPartitions(void) {
@ -66,14 +66,14 @@ index 76cd9ad..375c216 100644
// space on the disk. Returns 0 if there are no available blocks left // space on the disk. Returns 0 if there are no available blocks left
uint64_t GPTData::FindFirstInLargest(void) { uint64_t GPTData::FindFirstInLargest(void) {
diff --git a/gpt.h b/gpt.h diff --git a/gpt.h b/gpt.h
index 5d19372..c272d65 100644 index 5d19372..17b3380 100644
--- a/gpt.h --- a/gpt.h
+++ b/gpt.h +++ b/gpt.h
@@ -142,6 +142,7 @@ public: @@ -142,6 +142,7 @@ public:
// Adjust GPT structures WITHOUT user interaction... // Adjust GPT structures WITHOUT user interaction...
int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1); int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1);
int MoveMainTable(uint64_t pteSector); int MoveMainTable(uint64_t pteSector);
+ int MoveSecondaryTable(uint64_t pteSector); + int MoveSecondTable(uint64_t pteSector);
void BlankPartitions(void); void BlankPartitions(void);
int DeletePartition(uint32_t partNum); int DeletePartition(uint32_t partNum);
uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector); uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector);
@ -95,7 +95,7 @@ index 5d19372..c272d65 100644
uint64_t FindLastAvailable(); uint64_t FindLastAvailable();
uint64_t FindLastInFree(uint64_t start, bool align = false); uint64_t FindLastInFree(uint64_t start, bool align = false);
diff --git a/gptcl.cc b/gptcl.cc diff --git a/gptcl.cc b/gptcl.cc
index 34c9421..f6517e4 100644 index 34c9421..232285a 100644
--- a/gptcl.cc --- a/gptcl.cc
+++ b/gptcl.cc +++ b/gptcl.cc
@@ -68,7 +68,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) { @@ -68,7 +68,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
@ -107,13 +107,22 @@ index 34c9421..f6517e4 100644
uint64_t temp; // temporary variable; free to use in any case uint64_t temp; // temporary variable; free to use in any case
char *device; char *device;
string cmd, typeGUID, name; string cmd, typeGUID, name;
@@ -85,7 +85,7 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
{"recompute-chs", 'C', POPT_ARG_NONE, NULL, 'C', "recompute CHS values in protective/hybrid MBR", ""},
{"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"},
{"display-alignment", 'D', POPT_ARG_NONE, NULL, 'D', "show number of sectors per allocation block", ""},
- {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""},
+ {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second/backup header to end of disk", ""},
{"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""},
{"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""},
{"first-aligned-in-largest", 'F', POPT_ARG_NONE, NULL, 'F', "show start of the largest free block, aligned", ""},
@@ -94,7 +94,8 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) { @@ -94,7 +94,8 @@ int GPTDataCL::DoOptions(int argc, char* argv[]) {
{"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...][:EE]"}, {"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...][:EE]"},
{"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"}, {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"},
{"align-end", 'I', POPT_ARG_NONE, NULL, 'I', "align partition end points", ""}, {"align-end", 'I', POPT_ARG_NONE, NULL, 'I', "align partition end points", ""},
- {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "adjust the location of the main partition table", "sector"}, - {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "adjust the location of the main partition table", "sector"},
+ {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "change the start sector of the main partition table", "sector"}, + {"move-main-table", 'j', POPT_ARG_INT, &mainTableLBA, 'j', "change the start sector of the main partition table", "sector"},
+ {"move-secondary-table", 'k', POPT_ARG_INT, &secondTableLBA, 'k', "change the start sector of the secondary partition table", "sector"}, + {"move-backup-table", 'k', POPT_ARG_INT, &secondTableLBA, 'k', "change the start sector of the second/backup partition table", "sector"},
{"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"}, {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"},
{"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""}, {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""},
{"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"}, {"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"},
@ -144,7 +153,7 @@ index 34c9421..f6517e4 100644
+ } // if/else + } // if/else
+ break; + break;
+ case 'k': + case 'k':
+ if (MoveSecondaryTable(secondTableLBA)) { + if (MoveSecondTable(secondTableLBA)) {
+ JustLooking(0); + JustLooking(0);
+ saveData = 1; + saveData = 1;
+ } else { + } else {