Merge commit staging+systemd into closure-size
Many non-conflict problems weren't (fully) resolved in this commit yet.
This commit is contained in:
@@ -115,10 +115,6 @@ in
|
||||
''
|
||||
# Various log/runtime directories.
|
||||
|
||||
touch /run/utmp # must exist
|
||||
chgrp ${toString config.ids.gids.utmp} /run/utmp
|
||||
chmod 664 /run/utmp
|
||||
|
||||
mkdir -m 0755 -p /run/nix/current-load # for distributed builds
|
||||
mkdir -m 0700 -p /run/nix/remote-stores
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ sub parseFstab {
|
||||
sub parseUnit {
|
||||
my ($filename) = @_;
|
||||
my $info = {};
|
||||
parseKeyValues($info, read_file($filename));
|
||||
parseKeyValues($info, read_file($filename)) if -f $filename;
|
||||
parseKeyValues($info, read_file("${filename}.d/overrides.conf")) if -f "${filename}.d/overrides.conf";
|
||||
return $info;
|
||||
}
|
||||
@@ -157,7 +157,8 @@ while (my ($unit, $state) = each %{$activePrev}) {
|
||||
|
||||
if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
|
||||
if (! -e $newUnitFile || abs_path($newUnitFile) eq "/dev/null") {
|
||||
$unitsToStop{$unit} = 1;
|
||||
my $unitInfo = parseUnit($prevUnitFile);
|
||||
$unitsToStop{$unit} = 1 if boolIsTrue($unitInfo->{'X-StopOnRemoval'} // "yes");
|
||||
}
|
||||
|
||||
elsif ($unit =~ /\.target$/) {
|
||||
|
||||
@@ -50,7 +50,7 @@ let
|
||||
|
||||
ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd
|
||||
|
||||
ln -s ${config.hardware.firmware} $out/firmware
|
||||
ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
|
||||
''}
|
||||
|
||||
echo "$activationScript" > $out/activate
|
||||
@@ -81,6 +81,8 @@ let
|
||||
substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
|
||||
chmod +x $out/bin/switch-to-configuration
|
||||
|
||||
echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies
|
||||
|
||||
${config.system.extraSystemBuilderCmds}
|
||||
'';
|
||||
|
||||
@@ -97,8 +99,11 @@ let
|
||||
# makes it bootable.
|
||||
baseSystem = showWarnings (
|
||||
if [] == failed then pkgs.stdenv.mkDerivation {
|
||||
name = "nixos-${config.system.nixosVersion}";
|
||||
name = let hn = config.networking.hostName;
|
||||
nn = if (hn != "") then hn else "unnamed";
|
||||
in "nixos-system-${nn}-${config.system.nixosVersion}";
|
||||
preferLocalBuild = true;
|
||||
allowSubstitutes = false;
|
||||
buildCommand = systemBuilder;
|
||||
|
||||
inherit (pkgs) utillinux coreutils;
|
||||
@@ -188,6 +193,16 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
system.extraDependencies = mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [];
|
||||
description = ''
|
||||
A list of packages that should be included in the system
|
||||
closure but not otherwise made available to users. This is
|
||||
primarily used by the installation tests.
|
||||
'';
|
||||
};
|
||||
|
||||
system.replaceRuntimeDependencies = mkOption {
|
||||
default = [];
|
||||
example = lib.literalExample "[ ({ original = pkgs.openssl; replacement = pkgs.callPackage /path/to/openssl { ... }; }) ]";
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
|
||||
options = {
|
||||
|
||||
systemd.coredump = {
|
||||
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Enables storing core dumps in systemd.
|
||||
Note that this alone is not enough to enable core dumps. The maximum
|
||||
file size for core dumps must be specified in limits.conf as well. See
|
||||
<option>security.pam.loginLimits</option> as well as the limits.conf(5)
|
||||
man page.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
default = "";
|
||||
type = types.lines;
|
||||
example = "Storage=journal";
|
||||
description = ''
|
||||
Extra config options for systemd-coredump. See coredump.conf(5) man page
|
||||
for available options.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = mkIf config.systemd.coredump.enable {
|
||||
|
||||
environment.etc."systemd/coredump.conf".text =
|
||||
''
|
||||
[Coredump]
|
||||
${config.systemd.coredump.extraConfig}
|
||||
'';
|
||||
|
||||
# Have the kernel pass core dumps to systemd's coredump helper binary.
|
||||
# From systemd's 50-coredump.conf file. See:
|
||||
# <https://github.com/systemd/systemd/blob/v218/sysctl.d/50-coredump.conf.in>
|
||||
boot.kernel.sysctl."kernel.core_pattern" = "|${pkgs.systemd}/lib/systemd/systemd-coredump %p %u %g %s %t %e";
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -49,9 +49,8 @@ in
|
||||
type = types.int;
|
||||
default = 4;
|
||||
description = ''
|
||||
The kernel console log level. Only log messages with a
|
||||
priority numerically less than this will appear on the
|
||||
console.
|
||||
The kernel console log level. Log messages with a priority
|
||||
numerically less than this will not appear on the console.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -159,7 +158,7 @@ in
|
||||
|
||||
boot.kernel.sysctl."kernel.printk" = config.boot.consoleLogLevel;
|
||||
|
||||
boot.kernelModules = [ "loop" "configs" ];
|
||||
boot.kernelModules = [ "loop" "configs" "atkbd" ];
|
||||
|
||||
boot.initrd.availableKernelModules =
|
||||
[ # Note: most of these (especially the SATA/PATA modules)
|
||||
@@ -217,7 +216,7 @@ in
|
||||
];
|
||||
|
||||
# The Linux kernel >= 2.6.27 provides firmware.
|
||||
hardware.firmware = [ "${kernel}/lib/firmware" ];
|
||||
hardware.firmware = [ kernel ];
|
||||
|
||||
# Create /etc/modules-load.d/nixos.conf, which is read by
|
||||
# systemd-modules-load.service to load required kernel modules.
|
||||
|
||||
@@ -15,7 +15,7 @@ with lib;
|
||||
efiSysMountPoint = mkOption {
|
||||
default = "/boot";
|
||||
|
||||
type = types.string;
|
||||
type = types.str;
|
||||
|
||||
description = "Where the EFI System Partition is mounted.";
|
||||
};
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
blCfg = config.boot.loader;
|
||||
cfg = blCfg.generic-extlinux-compatible;
|
||||
|
||||
timeoutStr = if blCfg.timeout == null then "-1" else toString blCfg.timeout;
|
||||
|
||||
builder = import ./extlinux-conf-builder.nix { inherit pkgs; };
|
||||
in
|
||||
{
|
||||
options = {
|
||||
boot.loader.generic-extlinux-compatible = {
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to generate an extlinux-compatible configuration file
|
||||
under <literal>/boot/extlinux.conf</literal>. For instance,
|
||||
U-Boot's generic distro boot support uses this file format.
|
||||
|
||||
See <link xlink:href="http://git.denx.de/?p=u-boot.git;a=blob;f=doc/README.distro;hb=refs/heads/master">U-boot's documentation</link>
|
||||
for more information.
|
||||
'';
|
||||
};
|
||||
|
||||
configurationLimit = mkOption {
|
||||
default = 20;
|
||||
example = 10;
|
||||
type = types.int;
|
||||
description = ''
|
||||
Maximum number of configurations in the boot menu.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
system.build.installBootLoader = "${builder} -g ${toString cfg.configurationLimit} -t ${timeoutStr} -c";
|
||||
system.boot.loader.id = "generic-extlinux-compatible";
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{ pkgs }:
|
||||
|
||||
pkgs.substituteAll {
|
||||
src = ./extlinux-conf-builder.sh;
|
||||
isExecutable = true;
|
||||
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
|
||||
inherit (pkgs) bash;
|
||||
kernelDTB = pkgs.stdenv.platform.kernelDTB or false;
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
#! @bash@/bin/sh -e
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
export PATH=/empty
|
||||
for i in @path@; do PATH=$PATH:$i/bin; done
|
||||
|
||||
usage() {
|
||||
echo "usage: $0 -t <timeout> -c <path-to-default-configuration> [-d <boot-dir>] [-g <num-generations>]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
timeout= # Timeout in centiseconds
|
||||
default= # Default configuration
|
||||
target=/boot # Target directory
|
||||
numGenerations=0 # Number of other generations to include in the menu
|
||||
|
||||
while getopts "t:c:d:g:" opt; do
|
||||
case "$opt" in
|
||||
t) # U-Boot interprets '0' as infinite and negative as instant boot
|
||||
if [ "$OPTARG" -lt 0 ]; then
|
||||
timeout=0
|
||||
elif [ "$OPTARG" = 0 ]; then
|
||||
timeout=-10
|
||||
else
|
||||
timeout=$((OPTARG * 10))
|
||||
fi
|
||||
;;
|
||||
c) default="$OPTARG" ;;
|
||||
d) target="$OPTARG" ;;
|
||||
g) numGenerations="$OPTARG" ;;
|
||||
\?) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ "$timeout" = "" -o "$default" = "" ] && usage
|
||||
|
||||
mkdir -p $target/nixos
|
||||
mkdir -p $target/extlinux
|
||||
|
||||
# Convert a path to a file in the Nix store such as
|
||||
# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
|
||||
cleanName() {
|
||||
local path="$1"
|
||||
echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
|
||||
}
|
||||
|
||||
# Copy a file from the Nix store to $target/nixos.
|
||||
declare -A filesCopied
|
||||
|
||||
copyToKernelsDir() {
|
||||
local src=$(readlink -f "$1")
|
||||
local dst="$target/nixos/$(cleanName $src)"
|
||||
# Don't copy the file if $dst already exists. This means that we
|
||||
# have to create $dst atomically to prevent partially copied
|
||||
# kernels or initrd if this script is ever interrupted.
|
||||
if ! test -e $dst; then
|
||||
local dstTmp=$dst.tmp.$$
|
||||
cp -r $src $dstTmp
|
||||
mv $dstTmp $dst
|
||||
fi
|
||||
filesCopied[$dst]=1
|
||||
result=$dst
|
||||
}
|
||||
|
||||
# Copy its kernel, initrd and dtbs to $target/nixos, and echo out an
|
||||
# extlinux menu entry
|
||||
addEntry() {
|
||||
local path=$(readlink -f "$1")
|
||||
local tag="$2" # Generation number or 'default'
|
||||
|
||||
if ! test -e $path/kernel -a -e $path/initrd; then
|
||||
return
|
||||
fi
|
||||
|
||||
copyToKernelsDir "$path/kernel"; kernel=$result
|
||||
copyToKernelsDir "$path/initrd"; initrd=$result
|
||||
if [ -n "@kernelDTB@" ]; then
|
||||
# XXX UGLY: maybe the system config should have a top-level "dtbs" entry?
|
||||
copyToKernelsDir $(readlink -m "$path/kernel/../dtbs"); dtbs=$result
|
||||
fi
|
||||
|
||||
timestampEpoch=$(stat -L -c '%Z' $path)
|
||||
|
||||
timestamp=$(date "+%Y-%m-%d %H:%M" -d @$timestampEpoch)
|
||||
nixosVersion="$(cat $path/nixos-version)"
|
||||
extraParams="$(cat $path/kernel-params)"
|
||||
|
||||
echo
|
||||
echo "LABEL nixos-$tag"
|
||||
if [ "$tag" = "default" ]; then
|
||||
echo " MENU LABEL NixOS - Default"
|
||||
else
|
||||
echo " MENU LABEL NixOS - Configuration $tag ($timestamp - $nixosVersion)"
|
||||
fi
|
||||
echo " LINUX ../nixos/$(basename $kernel)"
|
||||
echo " INITRD ../nixos/$(basename $initrd)"
|
||||
if [ -n "@kernelDTB@" ]; then
|
||||
echo " FDTDIR ../nixos/$(basename $dtbs)"
|
||||
fi
|
||||
echo " APPEND systemConfig=$path init=$path/init $extraParams"
|
||||
}
|
||||
|
||||
tmpFile="$target/extlinux/extlinux.conf.tmp.$$"
|
||||
|
||||
cat > $tmpFile <<EOF
|
||||
# Generated file, all changes will be lost on nixos-rebuild!
|
||||
|
||||
# Change this to e.g. nixos-42 to temporarily boot to an older configuration.
|
||||
DEFAULT nixos-default
|
||||
|
||||
MENU TITLE ------------------------------------------------------------
|
||||
TIMEOUT $timeout
|
||||
EOF
|
||||
|
||||
addEntry $default default >> $tmpFile
|
||||
|
||||
if [ "$numGenerations" -gt 0 ]; then
|
||||
# Add up to $numGenerations generations of the system profile to the menu,
|
||||
# in reverse (most recent to least recent) order.
|
||||
for generation in $(
|
||||
(cd /nix/var/nix/profiles && ls -d system-*-link) \
|
||||
| sed 's/system-\([0-9]\+\)-link/\1/' \
|
||||
| sort -n -r \
|
||||
| head -n $numGenerations); do
|
||||
link=/nix/var/nix/profiles/system-$generation-link
|
||||
addEntry $link $generation
|
||||
done >> $tmpFile
|
||||
fi
|
||||
|
||||
mv -f $tmpFile $target/extlinux/extlinux.conf
|
||||
|
||||
# Remove obsolete files from $target/nixos.
|
||||
for fn in $target/nixos/*; do
|
||||
if ! test "${filesCopied[$fn]}" = 1; then
|
||||
echo "Removing no longer needed boot file: $fn"
|
||||
chmod +w -- "$fn"
|
||||
rm -rf -- "$fn"
|
||||
fi
|
||||
done
|
||||
@@ -10,7 +10,8 @@ let
|
||||
|
||||
realGrub = if cfg.version == 1 then pkgs.grub
|
||||
else if cfg.zfsSupport then pkgs.grub2.override { zfsSupport = true; }
|
||||
else pkgs.grub2;
|
||||
else if cfg.enableTrustedBoot then pkgs.trustedGrub
|
||||
else pkgs.grub2;
|
||||
|
||||
grub =
|
||||
# Don't include GRUB if we're only generating a GRUB menu (e.g.,
|
||||
@@ -21,25 +22,36 @@ let
|
||||
|
||||
grubEfi =
|
||||
# EFI version of Grub v2
|
||||
if (cfg.devices != ["nodev"]) && cfg.efiSupport && (cfg.version == 2)
|
||||
if cfg.efiSupport && (cfg.version == 2)
|
||||
then realGrub.override { efiSupport = cfg.efiSupport; }
|
||||
else null;
|
||||
|
||||
f = x: if x == null then "" else "" + x;
|
||||
|
||||
grubConfig = pkgs.writeText "grub-config.xml" (builtins.toXML
|
||||
{ splashImage = f config.boot.loader.grub.splashImage;
|
||||
grubConfig = args:
|
||||
let
|
||||
efiSysMountPoint = if args.efiSysMountPoint == null then args.path else args.efiSysMountPoint;
|
||||
efiSysMountPoint' = replaceChars [ "/" ] [ "-" ] efiSysMountPoint;
|
||||
in
|
||||
pkgs.writeText "grub-config.xml" (builtins.toXML
|
||||
{ splashImage = f cfg.splashImage;
|
||||
grub = f grub;
|
||||
grubTarget = f (grub.grubTarget or "");
|
||||
shell = "${pkgs.stdenv.shell}";
|
||||
fullName = (builtins.parseDrvName realGrub.name).name;
|
||||
fullVersion = (builtins.parseDrvName realGrub.name).version;
|
||||
grubEfi = f grubEfi;
|
||||
grubTargetEfi = if cfg.efiSupport && (cfg.version == 2) then f (grubEfi.grubTarget or "") else "";
|
||||
inherit (efi) efiSysMountPoint canTouchEfiVariables;
|
||||
bootPath = args.path;
|
||||
storePath = config.boot.loader.grub.storePath;
|
||||
bootloaderId = if args.efiBootloaderId == null then "NixOS${efiSysMountPoint'}" else args.efiBootloaderId;
|
||||
inherit efiSysMountPoint;
|
||||
inherit (args) devices;
|
||||
inherit (efi) canTouchEfiVariables;
|
||||
inherit (cfg)
|
||||
version extraConfig extraPerEntryConfig extraEntries
|
||||
extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels timeout
|
||||
default devices fsIdentifier efiSupport;
|
||||
default fsIdentifier efiSupport gfxmodeEfi gfxmodeBios;
|
||||
path = (makeSearchPath "bin" ([
|
||||
pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils pkgs.btrfsProgs
|
||||
pkgs.utillinux ] ++ (if cfg.efiSupport && (cfg.version == 2) then [pkgs.efibootmgr ] else [])
|
||||
@@ -48,6 +60,9 @@ let
|
||||
]);
|
||||
});
|
||||
|
||||
bootDeviceCounters = fold (device: attr: attr // { "${device}" = (attr."${device}" or 0) + 1; }) {}
|
||||
(concatMap (args: args.devices) cfg.mirroredBoots);
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
@@ -97,10 +112,68 @@ in
|
||||
description = ''
|
||||
The devices on which the boot loader, GRUB, will be
|
||||
installed. Can be used instead of <literal>device</literal> to
|
||||
install grub into multiple devices (e.g., if as softraid arrays holding /boot).
|
||||
install GRUB onto multiple devices.
|
||||
'';
|
||||
};
|
||||
|
||||
mirroredBoots = mkOption {
|
||||
default = [ ];
|
||||
example = [
|
||||
{ path = "/boot1"; devices = [ "/dev/sda" ]; }
|
||||
{ path = "/boot2"; devices = [ "/dev/sdb" ]; }
|
||||
];
|
||||
description = ''
|
||||
Mirror the boot configuration to multiple partitions and install grub
|
||||
to the respective devices corresponding to those partitions.
|
||||
'';
|
||||
|
||||
type = types.listOf types.optionSet;
|
||||
|
||||
options = {
|
||||
|
||||
path = mkOption {
|
||||
example = "/boot1";
|
||||
type = types.str;
|
||||
description = ''
|
||||
The path to the boot directory where GRUB will be written. Generally
|
||||
this boot path should double as an EFI path.
|
||||
'';
|
||||
};
|
||||
|
||||
efiSysMountPoint = mkOption {
|
||||
default = null;
|
||||
example = "/boot1/efi";
|
||||
type = types.nullOr types.str;
|
||||
description = ''
|
||||
The path to the efi system mount point. Usually this is the same
|
||||
partition as the above path and can be left as null.
|
||||
'';
|
||||
};
|
||||
|
||||
efiBootloaderId = mkOption {
|
||||
default = null;
|
||||
example = "NixOS-fsid";
|
||||
type = types.nullOr types.str;
|
||||
description = ''
|
||||
The id of the bootloader to store in efi nvram.
|
||||
The default is to name it NixOS and append the path or efiSysMountPoint.
|
||||
This is only used if <literal>boot.loader.efi.canTouchEfiVariables</literal> is true.
|
||||
'';
|
||||
};
|
||||
|
||||
devices = mkOption {
|
||||
default = [ ];
|
||||
example = [ "/dev/sda" "/dev/sdb" ];
|
||||
type = types.listOf types.str;
|
||||
description = ''
|
||||
The path to the devices which will have the GRUB MBR written.
|
||||
Note these are typically device paths and not paths to partitions.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
configurationName = mkOption {
|
||||
default = "";
|
||||
example = "Stable 2.6.21";
|
||||
@@ -110,12 +183,21 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
storePath = mkOption {
|
||||
default = "/nix/store";
|
||||
type = types.str;
|
||||
description = ''
|
||||
Path to the Nix store when looking for kernels at boot.
|
||||
Only makes sense when copyKernels is false.
|
||||
'';
|
||||
};
|
||||
|
||||
extraPrepareConfig = mkOption {
|
||||
default = "";
|
||||
type = types.lines;
|
||||
description = ''
|
||||
Additional bash commands to be run at the script that
|
||||
prepares the grub menu entries.
|
||||
prepares the GRUB menu entries.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -189,6 +271,24 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
gfxmodeEfi = mkOption {
|
||||
default = "auto";
|
||||
example = "1024x768";
|
||||
type = types.str;
|
||||
description = ''
|
||||
The gfxmode to pass to GRUB when loading a graphical boot interface under EFI.
|
||||
'';
|
||||
};
|
||||
|
||||
gfxmodeBios = mkOption {
|
||||
default = "1024x768";
|
||||
example = "auto";
|
||||
type = types.str;
|
||||
description = ''
|
||||
The gfxmode to pass to GRUB when loading a graphical boot interface under BIOS.
|
||||
'';
|
||||
};
|
||||
|
||||
configurationLimit = mkOption {
|
||||
default = 100;
|
||||
example = 120;
|
||||
@@ -230,10 +330,10 @@ in
|
||||
type = types.addCheck types.str
|
||||
(type: type == "uuid" || type == "label" || type == "provided");
|
||||
description = ''
|
||||
Determines how grub will identify devices when generating the
|
||||
Determines how GRUB will identify devices when generating the
|
||||
configuration file. A value of uuid / label signifies that grub
|
||||
will always resolve the uuid or label of the device before using
|
||||
it in the configuration. A value of provided means that grub will
|
||||
it in the configuration. A value of provided means that GRUB will
|
||||
use the device name as show in <command>df</command> or
|
||||
<command>mount</command>. Note, zfs zpools / datasets are ignored
|
||||
and will always be mounted using their labels.
|
||||
@@ -244,7 +344,7 @@ in
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether grub should be build against libzfs.
|
||||
Whether GRUB should be build against libzfs.
|
||||
ZFS support is only available for GRUB v2.
|
||||
This option is ignored for GRUB v1.
|
||||
'';
|
||||
@@ -254,7 +354,7 @@ in
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether grub should be build with EFI support.
|
||||
Whether GRUB should be build with EFI support.
|
||||
EFI support is only available for GRUB v2.
|
||||
This option is ignored for GRUB v1.
|
||||
'';
|
||||
@@ -264,11 +364,20 @@ in
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Enable support for encrypted partitions. Grub should automatically
|
||||
Enable support for encrypted partitions. GRUB should automatically
|
||||
unlock the correct encrypted partition and look for filesystems.
|
||||
'';
|
||||
};
|
||||
|
||||
enableTrustedBoot = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Enable trusted boot. GRUB will measure all critical components during
|
||||
the boot process to offer TCG (TPM) support.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
@@ -284,20 +393,25 @@ in
|
||||
sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59";
|
||||
}
|
||||
# GRUB 1.97 doesn't support gzipped XPMs.
|
||||
else ./winkler-gnu-blue-640x480.png);
|
||||
else "${pkgs.nixos-artwork}/share/artwork/gnome/Gnome_Dark.png");
|
||||
}
|
||||
|
||||
(mkIf cfg.enable {
|
||||
|
||||
boot.loader.grub.devices = optional (cfg.device != "") cfg.device;
|
||||
|
||||
system.build.installBootLoader =
|
||||
if cfg.devices == [] then
|
||||
throw "You must set the option ‘boot.loader.grub.device’ to make the system bootable."
|
||||
else
|
||||
"PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ListCompare ])} " +
|
||||
(if cfg.enableCryptodisk then "GRUB_ENABLE_CRYPTODISK=y " else "") +
|
||||
"${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig}";
|
||||
boot.loader.grub.mirroredBoots = optionals (cfg.devices != [ ]) [
|
||||
{ path = "/boot"; inherit (cfg) devices; inherit (efi) efiSysMountPoint; }
|
||||
];
|
||||
|
||||
system.build.installBootLoader = pkgs.writeScript "install-grub.sh" (''
|
||||
#!${pkgs.stdenv.shell}
|
||||
set -e
|
||||
export PERL5LIB=${makePerlPath (with pkgs.perlPackages; [ FileSlurp XMLLibXML XMLSAX ListCompare ])}
|
||||
${optionalString cfg.enableCryptodisk "export GRUB_ENABLE_CRYPTODISK=y"}
|
||||
'' + flip concatMapStrings cfg.mirroredBoots (args: ''
|
||||
${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig args} $@
|
||||
''));
|
||||
|
||||
system.build.grub = grub;
|
||||
|
||||
@@ -312,13 +426,53 @@ in
|
||||
${pkgs.coreutils}/bin/cp -pf "${v}" "/boot/${n}"
|
||||
'') config.boot.loader.grub.extraFiles);
|
||||
|
||||
assertions = [{ assertion = !cfg.zfsSupport || cfg.version == 2;
|
||||
message = "Only grub version 2 provides zfs support";}]
|
||||
++ flip map cfg.devices (dev: {
|
||||
assertion = dev == "nodev" || hasPrefix "/" dev;
|
||||
message = "Grub devices must be absolute paths, not ${dev}";
|
||||
});
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = !cfg.zfsSupport || cfg.version == 2;
|
||||
message = "Only GRUB version 2 provides ZFS support";
|
||||
}
|
||||
{
|
||||
assertion = cfg.mirroredBoots != [ ];
|
||||
message = "You must set the option ‘boot.loader.grub.devices’ or "
|
||||
+ "'boot.loader.grub.mirroredBoots' to make the system bootable.";
|
||||
}
|
||||
{
|
||||
assertion = all (c: c < 2) (mapAttrsToList (_: c: c) bootDeviceCounters);
|
||||
message = "You cannot have duplicated devices in mirroredBoots";
|
||||
}
|
||||
{
|
||||
assertion = !cfg.enableTrustedBoot || cfg.version == 2;
|
||||
message = "Trusted GRUB is only available for GRUB 2";
|
||||
}
|
||||
{
|
||||
assertion = !cfg.efiSupport || !cfg.enableTrustedBoot;
|
||||
message = "Trusted GRUB does not have EFI support";
|
||||
}
|
||||
{
|
||||
assertion = !cfg.zfsSupport || !cfg.enableTrustedBoot;
|
||||
message = "Trusted GRUB does not have ZFS support";
|
||||
}
|
||||
{
|
||||
assertion = !cfg.enableTrustedBoot;
|
||||
message = "Trusted GRUB can break your system. Remove assertion if you want to test trustedGRUB nevertheless.";
|
||||
}
|
||||
] ++ flip concatMap cfg.mirroredBoots (args: [
|
||||
{
|
||||
assertion = args.devices != [ ];
|
||||
message = "A boot path cannot have an empty devices string in ${arg.path}";
|
||||
}
|
||||
{
|
||||
assertion = hasPrefix "/" args.path;
|
||||
message = "Boot paths must be absolute, not ${args.path}";
|
||||
}
|
||||
{
|
||||
assertion = if args.efiSysMountPoint == null then true else hasPrefix "/" args.efiSysMountPoint;
|
||||
message = "Efi paths must be absolute, not ${args.efiSysMountPoint}";
|
||||
}
|
||||
] ++ flip map args.devices (device: {
|
||||
assertion = device == "nodev" || hasPrefix "/" device;
|
||||
message = "GRUB devices must be absolute paths, not ${dev} in ${args.path}";
|
||||
}));
|
||||
})
|
||||
|
||||
];
|
||||
|
||||
@@ -7,6 +7,7 @@ use File::Path;
|
||||
use File::stat;
|
||||
use File::Copy;
|
||||
use File::Slurp;
|
||||
use File::Temp;
|
||||
require List::Compare;
|
||||
use POSIX;
|
||||
use Cwd;
|
||||
@@ -54,24 +55,29 @@ my $defaultEntry = int(get("default"));
|
||||
my $fsIdentifier = get("fsIdentifier");
|
||||
my $grubEfi = get("grubEfi");
|
||||
my $grubTargetEfi = get("grubTargetEfi");
|
||||
my $bootPath = get("bootPath");
|
||||
my $storePath = get("storePath");
|
||||
my $canTouchEfiVariables = get("canTouchEfiVariables");
|
||||
my $efiSysMountPoint = get("efiSysMountPoint");
|
||||
my $gfxmodeEfi = get("gfxmodeEfi");
|
||||
my $gfxmodeBios = get("gfxmodeBios");
|
||||
my $bootloaderId = get("bootloaderId");
|
||||
$ENV{'PATH'} = get("path");
|
||||
|
||||
die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
|
||||
|
||||
print STDERR "updating GRUB $grubVersion menu...\n";
|
||||
|
||||
mkpath("/boot/grub", 0, 0700);
|
||||
mkpath("$bootPath/grub", 0, 0700);
|
||||
|
||||
# Discover whether /boot is on the same filesystem as / and
|
||||
# Discover whether the bootPath is on the same filesystem as / and
|
||||
# /nix/store. If not, then all kernels and initrds must be copied to
|
||||
# /boot.
|
||||
if (stat("/boot")->dev != stat("/nix/store")->dev) {
|
||||
# the bootPath.
|
||||
if (stat($bootPath)->dev != stat("/nix/store")->dev) {
|
||||
$copyKernels = 1;
|
||||
}
|
||||
|
||||
# Discover information about the location of /boot
|
||||
# Discover information about the location of the bootPath
|
||||
struct(Fs => {
|
||||
device => '$',
|
||||
type => '$',
|
||||
@@ -180,7 +186,7 @@ sub GrubFs {
|
||||
if ($status != 0) {
|
||||
die "Failed to retrieve subvolume info for @{[$fs->mount]}\n";
|
||||
}
|
||||
my @ids = join("", @id_info) =~ m/Object ID:[ \t\n]*([^ \t\n]*)/;
|
||||
my @ids = join("", @id_info) =~ m/Subvolume ID:[ \t\n]*([^ \t\n]*)/;
|
||||
if ($#ids > 0) {
|
||||
die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n"
|
||||
} elsif ($#ids == 0) {
|
||||
@@ -206,10 +212,10 @@ sub GrubFs {
|
||||
}
|
||||
return Grub->new(path => $path, search => $search);
|
||||
}
|
||||
my $grubBoot = GrubFs("/boot");
|
||||
my $grubBoot = GrubFs($bootPath);
|
||||
my $grubStore;
|
||||
if ($copyKernels == 0) {
|
||||
$grubStore = GrubFs("/nix/store");
|
||||
$grubStore = GrubFs($storePath);
|
||||
}
|
||||
|
||||
# Generate the header.
|
||||
@@ -221,7 +227,7 @@ if ($grubVersion == 1) {
|
||||
timeout $timeout
|
||||
";
|
||||
if ($splashImage) {
|
||||
copy $splashImage, "/boot/background.xpm.gz" or die "cannot copy $splashImage to /boot\n";
|
||||
copy $splashImage, "$bootPath/background.xpm.gz" or die "cannot copy $splashImage to $bootPath\n";
|
||||
$conf .= "splashimage " . $grubBoot->path . "/background.xpm.gz\n";
|
||||
}
|
||||
}
|
||||
@@ -231,6 +237,7 @@ else {
|
||||
$conf .= "
|
||||
" . $grubStore->search;
|
||||
}
|
||||
# FIXME: should use grub-mkconfig.
|
||||
$conf .= "
|
||||
" . $grubBoot->search . "
|
||||
if [ -s \$prefix/grubenv ]; then
|
||||
@@ -239,24 +246,35 @@ else {
|
||||
|
||||
# ‘grub-reboot’ sets a one-time saved entry, which we process here and
|
||||
# then delete.
|
||||
if [ \"\${saved_entry}\" ]; then
|
||||
# The next line *has* to look exactly like this, otherwise KDM's
|
||||
# reboot feature won't work properly with GRUB 2.
|
||||
if [ \"\${next_entry}\" ]; then
|
||||
# FIXME: KDM expects the next line to be present.
|
||||
set default=\"\${saved_entry}\"
|
||||
set saved_entry=
|
||||
set prev_saved_entry=
|
||||
save_env saved_entry
|
||||
save_env prev_saved_entry
|
||||
set default=\"\${next_entry}\"
|
||||
set next_entry=
|
||||
save_env next_entry
|
||||
set timeout=1
|
||||
else
|
||||
set default=$defaultEntry
|
||||
set timeout=$timeout
|
||||
fi
|
||||
|
||||
if loadfont " . $grubBoot->path . "/grub/fonts/unicode.pf2; then
|
||||
set gfxmode=640x480
|
||||
insmod gfxterm
|
||||
# Setup the graphics stack for bios and efi systems
|
||||
if [ \"\${grub_platform}\" = \"efi\" ]; then
|
||||
insmod efi_gop
|
||||
insmod efi_uga
|
||||
else
|
||||
insmod vbe
|
||||
fi
|
||||
insmod font
|
||||
if loadfont " . $grubBoot->path . "/grub/fonts/unicode.pf2; then
|
||||
insmod gfxterm
|
||||
if [ \"\${grub_platform}\" = \"efi\" ]; then
|
||||
set gfxmode=$gfxmodeEfi
|
||||
set gfxpayload=keep
|
||||
else
|
||||
set gfxmode=$gfxmodeBios
|
||||
set gfxpayload=text
|
||||
fi
|
||||
terminal_output gfxterm
|
||||
fi
|
||||
";
|
||||
@@ -264,7 +282,7 @@ else {
|
||||
if ($splashImage) {
|
||||
# FIXME: GRUB 1.97 doesn't resize the background image if it
|
||||
# doesn't match the video resolution.
|
||||
copy $splashImage, "/boot/background.png" or die "cannot copy $splashImage to /boot\n";
|
||||
copy $splashImage, "$bootPath/background.png" or die "cannot copy $splashImage to $bootPath\n";
|
||||
$conf .= "
|
||||
insmod png
|
||||
if background_image " . $grubBoot->path . "/background.png; then
|
||||
@@ -285,14 +303,14 @@ $conf .= "$extraConfig\n";
|
||||
$conf .= "\n";
|
||||
|
||||
my %copied;
|
||||
mkpath("/boot/kernels", 0, 0755) if $copyKernels;
|
||||
mkpath("$bootPath/kernels", 0, 0755) if $copyKernels;
|
||||
|
||||
sub copyToKernelsDir {
|
||||
my ($path) = @_;
|
||||
return $grubStore->path . substr($path, length("/nix/store")) unless $copyKernels;
|
||||
$path =~ /\/nix\/store\/(.*)/ or die;
|
||||
my $name = $1; $name =~ s/\//-/g;
|
||||
my $dst = "/boot/kernels/$name";
|
||||
my $dst = "$bootPath/kernels/$name";
|
||||
# Don't copy the file if $dst already exists. This means that we
|
||||
# have to create $dst atomically to prevent partially copied
|
||||
# kernels or initrd if this script is ever interrupted.
|
||||
@@ -396,14 +414,14 @@ if ($extraPrepareConfig ne "") {
|
||||
}
|
||||
|
||||
# Atomically update the GRUB config.
|
||||
my $confFile = $grubVersion == 1 ? "/boot/grub/menu.lst" : "/boot/grub/grub.cfg";
|
||||
my $confFile = $grubVersion == 1 ? "$bootPath/grub/menu.lst" : "$bootPath/grub/grub.cfg";
|
||||
my $tmpFile = $confFile . ".tmp";
|
||||
writeFile($tmpFile, $conf);
|
||||
rename $tmpFile, $confFile or die "cannot rename $tmpFile to $confFile\n";
|
||||
|
||||
|
||||
# Remove obsolete files from /boot/kernels.
|
||||
foreach my $fn (glob "/boot/kernels/*") {
|
||||
# Remove obsolete files from $bootPath/kernels.
|
||||
foreach my $fn (glob "$bootPath/kernels/*") {
|
||||
next if defined $copied{$fn};
|
||||
print STDERR "removing obsolete file $fn\n";
|
||||
unlink $fn;
|
||||
@@ -415,15 +433,18 @@ foreach my $fn (glob "/boot/kernels/*") {
|
||||
#
|
||||
|
||||
struct(GrubState => {
|
||||
name => '$',
|
||||
version => '$',
|
||||
efi => '$',
|
||||
devices => '$',
|
||||
efiMountPoint => '$',
|
||||
});
|
||||
sub readGrubState {
|
||||
my $defaultGrubState = GrubState->new(version => "", efi => "", devices => "", efiMountPoint => "" );
|
||||
open FILE, "</boot/grub/state" or return $defaultGrubState;
|
||||
my $defaultGrubState = GrubState->new(name => "", version => "", efi => "", devices => "", efiMountPoint => "" );
|
||||
open FILE, "<$bootPath/grub/state" or return $defaultGrubState;
|
||||
local $/ = "\n";
|
||||
my $name = <FILE>;
|
||||
chomp($name);
|
||||
my $version = <FILE>;
|
||||
chomp($version);
|
||||
my $efi = <FILE>;
|
||||
@@ -433,7 +454,7 @@ sub readGrubState {
|
||||
my $efiMountPoint = <FILE>;
|
||||
chomp($efiMountPoint);
|
||||
close FILE;
|
||||
my $grubState = GrubState->new(version => $version, efi => $efi, devices => $devices, efiMountPoint => $efiMountPoint );
|
||||
my $grubState = GrubState->new(name => $name, version => $version, efi => $efi, devices => $devices, efiMountPoint => $efiMountPoint );
|
||||
return $grubState
|
||||
}
|
||||
|
||||
@@ -478,12 +499,16 @@ my $efiTarget = getEfiTarget();
|
||||
my $prevGrubState = readGrubState();
|
||||
my @prevDeviceTargets = split/:/, $prevGrubState->devices;
|
||||
|
||||
my $devicesDiffer = scalar (List::Compare->new( '-u', '-a', \@deviceTargets, \@prevDeviceTargets)->get_symmetric_difference() );
|
||||
my $versionDiffer = (get("fullVersion") eq \$prevGrubState->version);
|
||||
my $efiDiffer = ($efiTarget eq \$prevGrubState->efi);
|
||||
my $efiMountPointDiffer = ($efiSysMountPoint eq \$prevGrubState->efiMountPoint);
|
||||
my $requireNewInstall = $devicesDiffer || $versionDiffer || $efiDiffer || $efiMountPointDiffer || (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1");
|
||||
my $devicesDiffer = scalar (List::Compare->new( '-u', '-a', \@deviceTargets, \@prevDeviceTargets)->get_symmetric_difference());
|
||||
my $nameDiffer = get("fullName") ne $prevGrubState->name;
|
||||
my $versionDiffer = get("fullVersion") ne $prevGrubState->version;
|
||||
my $efiDiffer = $efiTarget ne $prevGrubState->efi;
|
||||
my $efiMountPointDiffer = $efiSysMountPoint ne $prevGrubState->efiMountPoint;
|
||||
my $requireNewInstall = $devicesDiffer || $nameDiffer || $versionDiffer || $efiDiffer || $efiMountPointDiffer || (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1");
|
||||
|
||||
# install a symlink so that grub can detect the boot drive
|
||||
my $tmpDir = File::Temp::tempdir(CLEANUP => 1) or die "Failed to create temporary space";
|
||||
symlink "$bootPath", "$tmpDir/boot" or die "Failed to symlink $tmpDir/boot";
|
||||
|
||||
# install non-EFI GRUB
|
||||
if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
|
||||
@@ -491,10 +516,10 @@ if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
|
||||
next if $dev eq "nodev";
|
||||
print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n";
|
||||
if ($grubTarget eq "") {
|
||||
system("$grub/sbin/grub-install", "--recheck", Cwd::abs_path($dev)) == 0
|
||||
system("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", Cwd::abs_path($dev)) == 0
|
||||
or die "$0: installation of GRUB on $dev failed\n";
|
||||
} else {
|
||||
system("$grub/sbin/grub-install", "--recheck", "--target=$grubTarget", Cwd::abs_path($dev)) == 0
|
||||
system("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", "--target=$grubTarget", Cwd::abs_path($dev)) == 0
|
||||
or die "$0: installation of GRUB on $dev failed\n";
|
||||
}
|
||||
}
|
||||
@@ -505,10 +530,10 @@ if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
|
||||
if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both")) {
|
||||
print STDERR "installing the GRUB $grubVersion EFI boot loader into $efiSysMountPoint...\n";
|
||||
if ($canTouchEfiVariables eq "true") {
|
||||
system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--efi-directory=$efiSysMountPoint") == 0
|
||||
system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint", "--bootloader-id=$bootloaderId") == 0
|
||||
or die "$0: installation of GRUB EFI into $efiSysMountPoint failed\n";
|
||||
} else {
|
||||
system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--efi-directory=$efiSysMountPoint", "--no-nvram") == 0
|
||||
system("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint", "--no-nvram") == 0
|
||||
or die "$0: installation of GRUB EFI into $efiSysMountPoint failed\n";
|
||||
}
|
||||
}
|
||||
@@ -516,7 +541,8 @@ if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both"))
|
||||
|
||||
# update GRUB state file
|
||||
if ($requireNewInstall != 0) {
|
||||
open FILE, ">/boot/grub/state" or die "cannot create /boot/grub/state: $!\n";
|
||||
open FILE, ">$bootPath/grub/state" or die "cannot create $bootPath/grub/state: $!\n";
|
||||
print FILE get("fullName"), "\n" or die;
|
||||
print FILE get("fullVersion"), "\n" or die;
|
||||
print FILE $efiTarget, "\n" or die;
|
||||
print FILE join( ":", @deviceTargets ), "\n" or die;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB |
@@ -1,6 +0,0 @@
|
||||
This is a resized version of
|
||||
|
||||
http://www.gnu.org/graphics/winkler-gnu-blue.png
|
||||
|
||||
by Kyle Winkler and released under the Free Art License
|
||||
(http://artlibre.org/licence.php/lalgb.html).
|
||||
@@ -60,22 +60,26 @@ addEntry() {
|
||||
fi
|
||||
|
||||
local kernel=$(readlink -f $path/kernel)
|
||||
# local initrd=$(readlink -f $path/initrd)
|
||||
local initrd=$(readlink -f $path/initrd)
|
||||
|
||||
if test -n "@copyKernels@"; then
|
||||
copyToKernelsDir $kernel; kernel=$result
|
||||
# copyToKernelsDir $initrd; initrd=$result
|
||||
copyToKernelsDir $initrd; initrd=$result
|
||||
fi
|
||||
|
||||
echo $(readlink -f $path) > $outdir/$generation-system
|
||||
echo $(readlink -f $path/init) > $outdir/$generation-init
|
||||
cp $path/kernel-params $outdir/$generation-cmdline.txt
|
||||
# echo $initrd > $outdir/$generation-initrd
|
||||
echo $initrd > $outdir/$generation-initrd
|
||||
echo $kernel > $outdir/$generation-kernel
|
||||
|
||||
if test $(readlink -f "$path") = "$default"; then
|
||||
copyForced $kernel /boot/kernel.img
|
||||
# copyForced $initrd /boot/initrd
|
||||
if [ @version@ -eq 1 ]; then
|
||||
copyForced $kernel /boot/kernel.img
|
||||
else
|
||||
copyForced $kernel /boot/kernel7.img
|
||||
fi
|
||||
copyForced $initrd /boot/initrd
|
||||
cp "$(readlink -f "$path/init")" /boot/nixos-init
|
||||
echo "`cat $path/kernel-params` init=$path/init" >/boot/cmdline.txt
|
||||
|
||||
@@ -98,8 +102,11 @@ fwdir=@firmware@/share/raspberrypi/boot/
|
||||
copyForced $fwdir/bootcode.bin /boot/bootcode.bin
|
||||
copyForced $fwdir/fixup.dat /boot/fixup.dat
|
||||
copyForced $fwdir/fixup_cd.dat /boot/fixup_cd.dat
|
||||
copyForced $fwdir/fixup_db.dat /boot/fixup_db.dat
|
||||
copyForced $fwdir/start.elf /boot/start.elf
|
||||
copyForced $fwdir/start_cd.elf /boot/start_cd.elf
|
||||
copyForced $fwdir/start_db.elf /boot/start_db.elf
|
||||
copyForced $fwdir/start_x.elf /boot/start_x.elf
|
||||
|
||||
# Remove obsolete files from /boot/old.
|
||||
for fn in /boot/old/*linux* /boot/old/*initrd*; do
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.boot.loader.raspberryPi;
|
||||
|
||||
builder = pkgs.substituteAll {
|
||||
src = ./builder.sh;
|
||||
@@ -10,6 +11,7 @@ let
|
||||
inherit (pkgs) bash;
|
||||
path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
|
||||
firmware = pkgs.raspberrypifw;
|
||||
version = cfg.version;
|
||||
};
|
||||
|
||||
platform = pkgs.stdenv.platform;
|
||||
@@ -29,11 +31,23 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
boot.loader.raspberryPi.version = mkOption {
|
||||
default = 2;
|
||||
type = types.int;
|
||||
description = ''
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = mkIf config.boot.loader.raspberryPi.enable {
|
||||
system.build.installBootLoader = builder;
|
||||
system.boot.loader.id = "raspberrypi";
|
||||
system.boot.loader.kernelFile = platform.kernelTarget;
|
||||
assertions = [
|
||||
{ assertion = (cfg.version == 1 || cfg.version == 2);
|
||||
message = "loader.raspberryPi.version should be 1 or 2";
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ with lib;
|
||||
let
|
||||
luks = config.boot.initrd.luks;
|
||||
|
||||
openCommand = { name, device, keyFile, keyFileSize, allowDiscards, yubikey, ... }: ''
|
||||
openCommand = { name, device, header, keyFile, keyFileSize, allowDiscards, yubikey, ... }: ''
|
||||
# Wait for luksRoot to appear, e.g. if on a usb drive.
|
||||
# XXX: copied and adapted from stage-1-init.sh - should be
|
||||
# available as a function.
|
||||
@@ -33,6 +33,7 @@ let
|
||||
|
||||
open_normally() {
|
||||
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
|
||||
${optionalString (header != null) "--header=${header}"} \
|
||||
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
|
||||
}
|
||||
|
||||
@@ -211,7 +212,7 @@ in
|
||||
};
|
||||
|
||||
boot.initrd.luks.cryptoModules = mkOption {
|
||||
type = types.listOf types.string;
|
||||
type = types.listOf types.str;
|
||||
default =
|
||||
[ "aes" "aes_generic" "blowfish" "twofish"
|
||||
"serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512"
|
||||
@@ -241,20 +242,30 @@ in
|
||||
|
||||
name = mkOption {
|
||||
example = "luksroot";
|
||||
type = types.string;
|
||||
type = types.str;
|
||||
description = "Named to be used for the generated device in /dev/mapper.";
|
||||
};
|
||||
|
||||
device = mkOption {
|
||||
example = "/dev/sda2";
|
||||
type = types.string;
|
||||
type = types.str;
|
||||
description = "Path of the underlying block device.";
|
||||
};
|
||||
|
||||
header = mkOption {
|
||||
default = null;
|
||||
example = "/root/header.img";
|
||||
type = types.nullOr types.str;
|
||||
description = ''
|
||||
The name of the file or block device that
|
||||
should be used as header for the encrypted device.
|
||||
'';
|
||||
};
|
||||
|
||||
keyFile = mkOption {
|
||||
default = null;
|
||||
example = "/dev/sdb1";
|
||||
type = types.nullOr types.string;
|
||||
type = types.nullOr types.str;
|
||||
description = ''
|
||||
The name of the file (can be a raw device or a partition) that
|
||||
should be used as the decryption key for the encrypted device. If
|
||||
@@ -286,8 +297,8 @@ in
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to allow TRIM requests to the underlying device. This option
|
||||
has security implications, please read the LUKS documentation before
|
||||
activating in.
|
||||
has security implications; please read the LUKS documentation before
|
||||
activating it.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -303,43 +314,43 @@ in
|
||||
twoFactor = mkOption {
|
||||
default = true;
|
||||
type = types.bool;
|
||||
description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false)";
|
||||
description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false).";
|
||||
};
|
||||
|
||||
slot = mkOption {
|
||||
default = 2;
|
||||
type = types.int;
|
||||
description = "Which slot on the Yubikey to challenge";
|
||||
description = "Which slot on the Yubikey to challenge.";
|
||||
};
|
||||
|
||||
saltLength = mkOption {
|
||||
default = 16;
|
||||
type = types.int;
|
||||
description = "Length of the new salt in byte (64 is the effective maximum)";
|
||||
description = "Length of the new salt in byte (64 is the effective maximum).";
|
||||
};
|
||||
|
||||
keyLength = mkOption {
|
||||
default = 64;
|
||||
type = types.int;
|
||||
description = "Length of the LUKS slot key derived with PBKDF2 in byte";
|
||||
description = "Length of the LUKS slot key derived with PBKDF2 in byte.";
|
||||
};
|
||||
|
||||
iterationStep = mkOption {
|
||||
default = 0;
|
||||
type = types.int;
|
||||
description = "How much the iteration count for PBKDF2 is increased at each successful authentication";
|
||||
description = "How much the iteration count for PBKDF2 is increased at each successful authentication.";
|
||||
};
|
||||
|
||||
gracePeriod = mkOption {
|
||||
default = 2;
|
||||
type = types.int;
|
||||
description = "Time in seconds to wait before attempting to find the Yubikey";
|
||||
description = "Time in seconds to wait before attempting to find the Yubikey.";
|
||||
};
|
||||
|
||||
ramfsMountPoint = mkOption {
|
||||
default = "/crypt-ramfs";
|
||||
type = types.string;
|
||||
description = "Path where the ramfs used to update the LUKS key will be mounted in stage-1";
|
||||
type = types.str;
|
||||
description = "Path where the ramfs used to update the LUKS key will be mounted during early boot.";
|
||||
};
|
||||
|
||||
/* TODO: Add to the documentation of the current module:
|
||||
@@ -358,19 +369,19 @@ in
|
||||
|
||||
fsType = mkOption {
|
||||
default = "vfat";
|
||||
type = types.string;
|
||||
description = "The filesystem of the unencrypted device";
|
||||
type = types.str;
|
||||
description = "The filesystem of the unencrypted device.";
|
||||
};
|
||||
|
||||
mountPoint = mkOption {
|
||||
default = "/crypt-storage";
|
||||
type = types.string;
|
||||
description = "Path where the unencrypted device will be mounted in stage-1";
|
||||
type = types.str;
|
||||
description = "Path where the unencrypted device will be mounted during early boot.";
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
default = "/crypt-storage/default";
|
||||
type = types.string;
|
||||
type = types.str;
|
||||
description = ''
|
||||
Absolute path of the salt on the unencrypted device with
|
||||
that device's root directory as "/".
|
||||
@@ -419,10 +430,10 @@ in
|
||||
mkdir -p $out/etc/ssl
|
||||
cp -pdv ${pkgs.openssl}/etc/ssl/openssl.cnf $out/etc/ssl
|
||||
|
||||
cat > $out/bin/openssl-wrap <<EOF
|
||||
#!$out/bin/sh
|
||||
EOF
|
||||
chmod +x $out/bin/openssl-wrap
|
||||
cat > $out/bin/openssl-wrap <<EOF
|
||||
#!$out/bin/sh
|
||||
EOF
|
||||
chmod +x $out/bin/openssl-wrap
|
||||
''}
|
||||
'';
|
||||
|
||||
@@ -432,10 +443,10 @@ EOF
|
||||
$out/bin/ykchalresp -V
|
||||
$out/bin/ykinfo -V
|
||||
cat > $out/bin/openssl-wrap <<EOF
|
||||
#!$out/bin/sh
|
||||
export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
|
||||
$out/bin/openssl "\$@"
|
||||
EOF
|
||||
#!$out/bin/sh
|
||||
export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
|
||||
$out/bin/openssl "\$@"
|
||||
EOF
|
||||
$out/bin/openssl-wrap version
|
||||
''}
|
||||
'';
|
||||
|
||||
@@ -35,6 +35,7 @@ with lib;
|
||||
fi
|
||||
|
||||
'';
|
||||
meta.priority = 4;
|
||||
};
|
||||
description = ''
|
||||
Wrapper around modprobe that sets the path to the modules
|
||||
@@ -84,11 +85,7 @@ with lib;
|
||||
'')}
|
||||
${config.boot.extraModprobeConfig}
|
||||
'';
|
||||
environment.etc."modprobe.d/usb-load-ehci-first.conf".text =
|
||||
''
|
||||
softdep uhci_hcd pre: ehci_hcd
|
||||
softdep ohci_hcd pre: ehci_hcd
|
||||
'';
|
||||
environment.etc."modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases;
|
||||
|
||||
environment.systemPackages = [ config.system.sbin.modprobe pkgs.kmod ];
|
||||
|
||||
@@ -101,7 +98,7 @@ with lib;
|
||||
echo ${config.system.sbin.modprobe}/sbin/modprobe > /proc/sys/kernel/modprobe
|
||||
'';
|
||||
|
||||
environment.variables.MODULE_DIR = "/run/current-system/kernel-modules/lib/modules";
|
||||
environment.sessionVariables.MODULE_DIR = "/run/current-system/kernel-modules/lib/modules";
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ let
|
||||
commonNetworkOptions = {
|
||||
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
default = true;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to manage network configuration using <command>systemd-network</command>.
|
||||
@@ -482,6 +482,11 @@ let
|
||||
};
|
||||
};
|
||||
|
||||
commonMatchText = def: ''
|
||||
[Match]
|
||||
${attrsToSection def.matchConfig}
|
||||
'';
|
||||
|
||||
linkToUnit = name: def:
|
||||
{ inherit (def) enable;
|
||||
text = commonMatchText def +
|
||||
@@ -636,9 +641,6 @@ in
|
||||
environment.etc."systemd/network".source =
|
||||
generateUnits "network" cfg.units [] [];
|
||||
|
||||
users.extraUsers.systemd-network.uid = config.ids.uids.systemd-network;
|
||||
users.extraGroups.systemd-network.gid = config.ids.gids.systemd-network;
|
||||
|
||||
systemd.services.systemd-networkd = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "network-interfaces.target" ];
|
||||
|
||||
@@ -30,9 +30,6 @@ with lib;
|
||||
DNS=${concatStringsSep " " config.networking.nameservers}
|
||||
'';
|
||||
|
||||
users.extraUsers.systemd-resolve.uid = config.ids.uids.systemd-resolve;
|
||||
users.extraGroups.systemd-resolve.gid = config.ids.gids.systemd-resolve;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ ln -s @modulesClosure@/lib/modules /lib/modules
|
||||
echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
|
||||
for i in @kernelModules@; do
|
||||
echo "loading module $(basename $i)..."
|
||||
modprobe $i || true
|
||||
modprobe $i
|
||||
done
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ ln -sfn @udevRules@ /etc/udev/rules.d
|
||||
mkdir -p /dev/.mdadm
|
||||
systemd-udevd --daemon
|
||||
udevadm trigger --action=add
|
||||
udevadm settle || true
|
||||
udevadm settle
|
||||
|
||||
|
||||
# Load boot-time keymap before any LVM/LUKS initialization
|
||||
@@ -182,9 +182,9 @@ if test -e /sys/power/resume -a -e /sys/power/disk; then
|
||||
for sd in @resumeDevices@; do
|
||||
# Try to detect resume device. According to Ubuntu bug:
|
||||
# https://bugs.launchpad.net/ubuntu/+source/pm-utils/+bug/923326/comments/1
|
||||
# When there are multiple swap devices, we can't know where will hibernate
|
||||
# image reside. We can check all of them for swsuspend blkid.
|
||||
resumeInfo="$(udevadm info -q property "$sd" )"
|
||||
# when there are multiple swap devices, we can't know where the hibernate
|
||||
# image will reside. We can check all of them for swsuspend blkid.
|
||||
resumeInfo="$(test -e "$sd" && udevadm info -q property "$sd")"
|
||||
if [ "$(echo "$resumeInfo" | sed -n 's/^ID_FS_TYPE=//p')" = "swsuspend" ]; then
|
||||
resumeDev="$sd"
|
||||
break
|
||||
@@ -290,10 +290,23 @@ mountFS() {
|
||||
if [ -z "$fsType" ]; then fsType=auto; fi
|
||||
fi
|
||||
|
||||
echo "$device /mnt-root$mountPoint $fsType $options" >> /etc/fstab
|
||||
# Filter out x- options, which busybox doesn't do yet.
|
||||
local optionsFiltered="$(IFS=,; for i in $options; do if [ "${i:0:2}" != "x-" ]; then echo -n $i,; fi; done)"
|
||||
|
||||
echo "$device /mnt-root$mountPoint $fsType $optionsFiltered" >> /etc/fstab
|
||||
|
||||
checkFS "$device" "$fsType"
|
||||
|
||||
# Optionally resize the filesystem.
|
||||
case $options in
|
||||
*x-nixos.autoresize*)
|
||||
if [ "$fsType" = ext2 -o "$fsType" = ext3 -o "$fsType" = ext4 ]; then
|
||||
echo "resizing $device..."
|
||||
resize2fs "$device"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Create backing directories for unionfs-fuse.
|
||||
if [ "$fsType" = unionfs-fuse ]; then
|
||||
for i in $(IFS=:; echo ${options##*,dirs=}); do
|
||||
@@ -303,7 +316,7 @@ mountFS() {
|
||||
|
||||
echo "mounting $device on $mountPoint..."
|
||||
|
||||
mkdir -p "/mnt-root$mountPoint" || true
|
||||
mkdir -p "/mnt-root$mountPoint"
|
||||
|
||||
# For CIFS mounts, retry a few times before giving up.
|
||||
local n=0
|
||||
@@ -317,7 +330,7 @@ mountFS() {
|
||||
|
||||
|
||||
# Try to find and mount the root device.
|
||||
mkdir /mnt-root
|
||||
mkdir -p $targetRoot
|
||||
|
||||
exec 3< @fsInfo@
|
||||
|
||||
@@ -375,7 +388,7 @@ while read -u 3 mountPoint; do
|
||||
|
||||
# Wait once more for the udev queue to empty, just in case it's
|
||||
# doing something with $device right now.
|
||||
udevadm settle || true
|
||||
udevadm settle
|
||||
|
||||
mountFS "$device" "$mountPoint" "$options" "$fsType"
|
||||
done
|
||||
@@ -388,9 +401,9 @@ exec 3>&-
|
||||
|
||||
# Emit a udev rule for /dev/root to prevent systemd from complaining.
|
||||
if [ -e /mnt-root/iso ]; then
|
||||
eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=/mnt-root/iso || true)
|
||||
eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=/mnt-root/iso)
|
||||
else
|
||||
eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=$targetRoot || true)
|
||||
eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=$targetRoot)
|
||||
fi
|
||||
if [ "$ROOT_MAJOR" -a "$ROOT_MINOR" -a "$ROOT_MAJOR" != 0 ]; then
|
||||
mkdir -p /run/udev/rules.d
|
||||
@@ -399,7 +412,7 @@ fi
|
||||
|
||||
|
||||
# Stop udevd.
|
||||
udevadm control --exit || true
|
||||
udevadm control --exit
|
||||
|
||||
# Kill any remaining processes, just to be sure we're not taking any
|
||||
# with us into stage 2. But keep storage daemons like unionfs-fuse.
|
||||
|
||||
@@ -70,6 +70,12 @@ let
|
||||
copy_bin_and_libs ${pkgs.kmod}/bin/kmod
|
||||
ln -sf kmod $out/bin/modprobe
|
||||
|
||||
# Copy resize2fs if needed.
|
||||
${optionalString (any (fs: fs.autoResize) (attrValues config.fileSystems)) ''
|
||||
# We need mke2fs in the initrd.
|
||||
copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs
|
||||
''}
|
||||
|
||||
${config.boot.initrd.extraUtilsCommands}
|
||||
|
||||
# Copy ld manually since it isn't detected correctly
|
||||
@@ -241,6 +247,9 @@ let
|
||||
};
|
||||
symlink = "/etc/modprobe.d/ubuntu.conf";
|
||||
}
|
||||
{ object = pkgs.kmod-debian-aliases;
|
||||
symlink = "/etc/modprobe.d/debian.conf";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
@@ -358,7 +367,7 @@ in
|
||||
boot.initrd.supportedFilesystems = mkOption {
|
||||
default = [ ];
|
||||
example = [ "btrfs" ];
|
||||
type = types.listOf types.string;
|
||||
type = types.listOf types.str;
|
||||
description = "Names of supported filesystem types in the initial ramdisk.";
|
||||
};
|
||||
|
||||
@@ -390,7 +399,6 @@ in
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
system.build.bootStage1 = bootStage1;
|
||||
system.build.initialRamdisk = initialRamdisk;
|
||||
system.build.extraUtils = extraUtils;
|
||||
|
||||
@@ -85,8 +85,10 @@ done
|
||||
|
||||
|
||||
# More special file systems, initialise required directories.
|
||||
mkdir -m 0755 /dev/shm
|
||||
mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" tmpfs /dev/shm
|
||||
if ! mountpoint -q /dev/shm; then
|
||||
mkdir -m 0755 /dev/shm
|
||||
mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" tmpfs /dev/shm
|
||||
fi
|
||||
mkdir -m 0755 -p /dev/pts
|
||||
[ -e /proc/bus/usb ] && mount -t usbfs usbfs /proc/bus/usb # UML doesn't have USB by default
|
||||
mkdir -m 01777 -p /tmp
|
||||
@@ -162,7 +164,9 @@ $systemConfig/activate
|
||||
# Restore the system time from the hardware clock. We do this after
|
||||
# running the activation script to be sure that /etc/localtime points
|
||||
# at the current time zone.
|
||||
hwclock --hctosys
|
||||
if [ -e /dev/rtc ]; then
|
||||
hwclock --hctosys
|
||||
fi
|
||||
|
||||
|
||||
# Record the boot configuration.
|
||||
|
||||
@@ -13,20 +13,93 @@ rec {
|
||||
pathSafeName = lib.replaceChars ["@" ":" "\\"] ["-" "-" "-"] name;
|
||||
in
|
||||
if unit.enable then
|
||||
pkgs.runCommand "unit-${pathSafeName}" { preferLocalBuild = true; inherit (unit) text; }
|
||||
pkgs.runCommand "unit-${pathSafeName}"
|
||||
{ preferLocalBuild = true;
|
||||
allowSubstitutes = false;
|
||||
inherit (unit) text;
|
||||
}
|
||||
''
|
||||
mkdir -p $out
|
||||
echo -n "$text" > $out/${shellEscape name}
|
||||
''
|
||||
else
|
||||
pkgs.runCommand "unit-${pathSafeName}-disabled" { preferLocalBuild = true; }
|
||||
pkgs.runCommand "unit-${pathSafeName}-disabled"
|
||||
{ preferLocalBuild = true;
|
||||
allowSubstitutes = false;
|
||||
}
|
||||
''
|
||||
mkdir -p $out
|
||||
ln -s /dev/null $out/${shellEscape name}
|
||||
'';
|
||||
|
||||
boolValues = [true false "yes" "no"];
|
||||
|
||||
digits = map toString (range 0 9);
|
||||
|
||||
isByteFormat = s:
|
||||
let
|
||||
l = reverseList (stringToCharacters s);
|
||||
suffix = head l;
|
||||
nums = tail l;
|
||||
in elem suffix (["K" "M" "G" "T"] ++ digits)
|
||||
&& all (num: elem num digits) nums;
|
||||
|
||||
assertByteFormat = name: group: attr:
|
||||
optional (attr ? ${name} && ! isByteFormat attr.${name})
|
||||
"Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT].";
|
||||
|
||||
hexChars = stringToCharacters "0123456789abcdefABCDEF";
|
||||
|
||||
isMacAddress = s: stringLength s == 17
|
||||
&& flip all (splitString ":" s) (bytes:
|
||||
all (byte: elem byte hexChars) (stringToCharacters bytes)
|
||||
);
|
||||
|
||||
assertMacAddress = name: group: attr:
|
||||
optional (attr ? ${name} && ! isMacAddress attr.${name})
|
||||
"Systemd ${group} field `${name}' must be a valid mac address.";
|
||||
|
||||
|
||||
assertValueOneOf = name: values: group: attr:
|
||||
optional (attr ? ${name} && !elem attr.${name} values)
|
||||
"Systemd ${group} field `${name}' cannot have value `${attr.${name}}'.";
|
||||
|
||||
assertHasField = name: group: attr:
|
||||
optional (!(attr ? ${name}))
|
||||
"Systemd ${group} field `${name}' must exist.";
|
||||
|
||||
assertRange = name: min: max: group: attr:
|
||||
optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name}))
|
||||
"Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
|
||||
|
||||
assertOnlyFields = fields: group: attr:
|
||||
let badFields = filter (name: ! elem name fields) (attrNames attr); in
|
||||
optional (badFields != [ ])
|
||||
"Systemd ${group} has extra fields [${concatStringsSep " " badFields}].";
|
||||
|
||||
checkUnitConfig = group: checks: v:
|
||||
let errors = concatMap (c: c group v) checks; in
|
||||
if errors == [] then true
|
||||
else builtins.trace (concatStringsSep "\n" errors) false;
|
||||
|
||||
toOption = x:
|
||||
if x == true then "true"
|
||||
else if x == false then "false"
|
||||
else toString x;
|
||||
|
||||
attrsToSection = as:
|
||||
concatStrings (concatLists (mapAttrsToList (name: value:
|
||||
map (x: ''
|
||||
${name}=${toOption x}
|
||||
'')
|
||||
(if isList value then value else [value]))
|
||||
as));
|
||||
|
||||
generateUnits = type: units: upstreamUnits: upstreamWants:
|
||||
pkgs.runCommand "${type}-units" { preferLocalBuild = true; } ''
|
||||
pkgs.runCommand "${type}-units"
|
||||
{ preferLocalBuild = true;
|
||||
allowSubstitutes = false;
|
||||
} ''
|
||||
mkdir -p $out
|
||||
|
||||
# Copy the upstream systemd units we're interested in.
|
||||
|
||||
@@ -1,58 +1,9 @@
|
||||
{ config, lib }:
|
||||
|
||||
with lib;
|
||||
with import ./systemd-lib.nix { inherit config lib pkgs; };
|
||||
|
||||
let
|
||||
|
||||
boolValues = [true false "yes" "no"];
|
||||
|
||||
assertValueOneOf = name: values: group: attr:
|
||||
optional (attr ? ${name} && !elem attr.${name} values)
|
||||
"Systemd ${group} field `${name}' cannot have value `${attr.${name}}'.";
|
||||
|
||||
assertHasField = name: group: attr:
|
||||
optional (!(attr ? ${name}))
|
||||
"Systemd ${group} field `${name}' must exist.";
|
||||
|
||||
assertOnlyFields = fields: group: attr:
|
||||
let badFields = filter (name: ! elem name fields) (attrNames attr); in
|
||||
optional (badFields != [ ])
|
||||
"Systemd ${group} has extra fields [${concatStringsSep " " badFields}].";
|
||||
|
||||
assertRange = name: min: max: group: attr:
|
||||
optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name}))
|
||||
"Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
|
||||
|
||||
digits = map toString (range 0 9);
|
||||
|
||||
isByteFormat = s:
|
||||
let
|
||||
l = reverseList (stringToCharacters s);
|
||||
suffix = head l;
|
||||
nums = tail l;
|
||||
in elem suffix (["K" "M" "G" "T"] ++ digits)
|
||||
&& all (num: elem num digits) nums;
|
||||
|
||||
assertByteFormat = name: group: attr:
|
||||
optional (attr ? ${name} && ! isByteFormat attr.${name})
|
||||
"Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT].";
|
||||
|
||||
hexChars = stringToCharacters "0123456789abcdefABCDEF";
|
||||
|
||||
isMacAddress = s: stringLength s == 17
|
||||
&& flip all (splitString ":" s) (bytes:
|
||||
all (byte: elem byte hexChars) (stringToCharacters bytes)
|
||||
);
|
||||
|
||||
assertMacAddress = name: group: attr:
|
||||
optional (attr ? ${name} && ! isMacAddress attr.${name})
|
||||
"Systemd ${group} field `${name}' must be a valid mac address.";
|
||||
|
||||
checkUnitConfig = group: checks: v:
|
||||
let errors = concatMap (c: c group v) checks; in
|
||||
if errors == [] then true
|
||||
else builtins.trace (concatStringsSep "\n" errors) false;
|
||||
|
||||
checkService = checkUnitConfig "Service" [
|
||||
(assertValueOneOf "Type" [
|
||||
"simple" "forking" "oneshot" "dbus" "notify" "idle"
|
||||
@@ -91,13 +42,13 @@ in rec {
|
||||
|
||||
requiredBy = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.string;
|
||||
type = types.listOf types.str;
|
||||
description = "Units that require (i.e. depend on and need to go down with) this unit.";
|
||||
};
|
||||
|
||||
wantedBy = mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.string;
|
||||
type = types.listOf types.str;
|
||||
description = "Units that want (i.e. depend on) this unit.";
|
||||
};
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ let
|
||||
"systemd-journal-flush.service"
|
||||
"systemd-journal-gatewayd.socket"
|
||||
"systemd-journal-gatewayd.service"
|
||||
"systemd-journald-audit.socket"
|
||||
"systemd-journald-dev-log.socket"
|
||||
"syslog.socket"
|
||||
|
||||
@@ -109,8 +110,6 @@ let
|
||||
"systemd-hibernate.service"
|
||||
"systemd-suspend.service"
|
||||
"systemd-hybrid-sleep.service"
|
||||
"systemd-shutdownd.socket"
|
||||
"systemd-shutdownd.service"
|
||||
|
||||
# Reboot stuff.
|
||||
"reboot.target"
|
||||
@@ -276,19 +275,6 @@ let
|
||||
};
|
||||
};
|
||||
|
||||
toOption = x:
|
||||
if x == true then "true"
|
||||
else if x == false then "false"
|
||||
else toString x;
|
||||
|
||||
attrsToSection = as:
|
||||
concatStrings (concatLists (mapAttrsToList (name: value:
|
||||
map (x: ''
|
||||
${name}=${toOption x}
|
||||
'')
|
||||
(if isList value then value else [value]))
|
||||
as));
|
||||
|
||||
commonUnitText = def: ''
|
||||
[Unit]
|
||||
${attrsToSection def.unitConfig}
|
||||
@@ -369,11 +355,6 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
commonMatchText = def: ''
|
||||
[Match]
|
||||
${attrsToSection def.matchConfig}
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
@@ -463,6 +444,17 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.generators = mkOption {
|
||||
type = types.attrsOf types.path;
|
||||
default = {};
|
||||
example = { "systemd-gpt-auto-generator" = "/dev/null"; };
|
||||
description = ''
|
||||
Definition of systemd generators.
|
||||
For each <literal>NAME = VALUE</literal> pair of the attrSet, a link is generated from
|
||||
<literal>/etc/systemd/system-generators/NAME</literal> to <literal>VALUE</literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.defaultUnit = mkOption {
|
||||
default = "multi-user.target";
|
||||
type = types.str;
|
||||
@@ -509,7 +501,7 @@ in
|
||||
|
||||
services.journald.rateLimitBurst = mkOption {
|
||||
default = 100;
|
||||
type = types.uniq types.int;
|
||||
type = types.int;
|
||||
description = ''
|
||||
Configures the rate limiting burst limit (number of messages per
|
||||
interval) that is applied to all messages generated on the system.
|
||||
@@ -619,20 +611,17 @@ in
|
||||
|
||||
environment.systemPackages = [ systemd ];
|
||||
|
||||
environment.etc."systemd/system".source =
|
||||
generateUnits "system" cfg.units upstreamSystemUnits upstreamSystemWants;
|
||||
environment.etc = {
|
||||
"systemd/system".source = generateUnits "system" cfg.units upstreamSystemUnits upstreamSystemWants;
|
||||
|
||||
environment.etc."systemd/user".source =
|
||||
generateUnits "user" cfg.user.units upstreamUserUnits [];
|
||||
"systemd/user".source = generateUnits "user" cfg.user.units upstreamUserUnits [];
|
||||
|
||||
environment.etc."systemd/system.conf".text =
|
||||
''
|
||||
"systemd/system.conf".text = ''
|
||||
[Manager]
|
||||
${config.systemd.extraConfig}
|
||||
'';
|
||||
|
||||
environment.etc."systemd/journald.conf".text =
|
||||
''
|
||||
"systemd/journald.conf".text = ''
|
||||
[Journal]
|
||||
RateLimitInterval=${config.services.journald.rateLimitInterval}
|
||||
RateLimitBurst=${toString config.services.journald.rateLimitBurst}
|
||||
@@ -643,29 +632,44 @@ in
|
||||
${config.services.journald.extraConfig}
|
||||
'';
|
||||
|
||||
environment.etc."systemd/logind.conf".text =
|
||||
''
|
||||
"systemd/logind.conf".text = ''
|
||||
[Login]
|
||||
${config.services.logind.extraConfig}
|
||||
'';
|
||||
|
||||
environment.etc."systemd/sleep.conf".text =
|
||||
''
|
||||
"systemd/sleep.conf".text = ''
|
||||
[Sleep]
|
||||
'';
|
||||
|
||||
"tmpfiles.d/systemd.conf".source = "${systemd}/example/tmpfiles.d/systemd.conf";
|
||||
"tmpfiles.d/x11.conf".source = "${systemd}/example/tmpfiles.d/x11.conf";
|
||||
|
||||
"tmpfiles.d/nixos.conf".text = ''
|
||||
# This file is created automatically and should not be modified.
|
||||
# Please change the option ‘systemd.tmpfiles.rules’ instead.
|
||||
|
||||
${concatStringsSep "\n" cfg.tmpfiles.rules}
|
||||
'';
|
||||
} // mapAttrs' (n: v: nameValuePair "systemd/system-generators/${n}" {"source"=v;}) cfg.generators;
|
||||
|
||||
system.activationScripts.systemd = stringAfter [ "groups" ]
|
||||
''
|
||||
mkdir -m 0755 -p /var/lib/udev
|
||||
mkdir -p /var/log/journal
|
||||
chmod 0755 /var/log/journal
|
||||
|
||||
# Make all journals readable to users in the wheel and adm
|
||||
# groups, in addition to those in the systemd-journal group.
|
||||
# Users can always read their own journals.
|
||||
${pkgs.acl.bin}/bin/setfacl -nm g:wheel:rx,d:g:wheel:rx,g:adm:rx,d:g:adm:rx /var/log/journal || true
|
||||
if ! [ -e /etc/machine-id ]; then
|
||||
${systemd}/bin/systemd-machine-id-setup
|
||||
fi
|
||||
|
||||
# Keep a persistent journal. Note that systemd-tmpfiles will
|
||||
# set proper ownership/permissions.
|
||||
mkdir -m 0700 -p /var/log/journal
|
||||
'';
|
||||
|
||||
users.extraUsers.systemd-network.uid = config.ids.uids.systemd-network;
|
||||
users.extraGroups.systemd-network.gid = config.ids.gids.systemd-network;
|
||||
users.extraUsers.systemd-resolve.uid = config.ids.uids.systemd-resolve;
|
||||
users.extraGroups.systemd-resolve.gid = config.ids.gids.systemd-resolve;
|
||||
|
||||
# Target for ‘charon send-keys’ to hook into.
|
||||
users.extraGroups.keys.gid = config.ids.gids.keys;
|
||||
|
||||
@@ -729,6 +733,14 @@ in
|
||||
})
|
||||
(filterAttrs (name: service: service.startAt != "") cfg.services);
|
||||
|
||||
# Generate timer units for all services that have a ‘startAt’ value.
|
||||
systemd.user.timers =
|
||||
mapAttrs (name: service:
|
||||
{ wantedBy = [ "timers.target" ];
|
||||
timerConfig.OnCalendar = service.startAt;
|
||||
})
|
||||
(filterAttrs (name: service: service.startAt != "") cfg.user.services);
|
||||
|
||||
systemd.sockets.systemd-journal-gatewayd.wantedBy =
|
||||
optional config.services.journald.enableHttpGateway "sockets.target";
|
||||
|
||||
@@ -740,24 +752,21 @@ in
|
||||
startSession = true;
|
||||
};
|
||||
|
||||
environment.etc."tmpfiles.d/x11.conf".source = "${systemd}/example/tmpfiles.d/x11.conf";
|
||||
|
||||
environment.etc."tmpfiles.d/nixos.conf".text =
|
||||
''
|
||||
# This file is created automatically and should not be modified.
|
||||
# Please change the option ‘systemd.tmpfiles.rules’ instead.
|
||||
|
||||
z /var/log/journal 2755 root systemd-journal - -
|
||||
z /var/log/journal/%m 2755 root systemd-journal - -
|
||||
z /var/log/journal/%m/* 0640 root systemd-journal - -
|
||||
|
||||
${concatStringsSep "\n" cfg.tmpfiles.rules}
|
||||
'';
|
||||
|
||||
# Some overrides to upstream units.
|
||||
systemd.services."systemd-backlight@".restartIfChanged = false;
|
||||
systemd.services."systemd-rfkill@".restartIfChanged = false;
|
||||
systemd.services."user@".restartIfChanged = false;
|
||||
|
||||
systemd.services.systemd-remount-fs.restartIfChanged = false;
|
||||
systemd.services.systemd-journal-flush.restartIfChanged = false;
|
||||
systemd.services.systemd-random-seed.restartIfChanged = false;
|
||||
systemd.services.systemd-remount-fs.restartIfChanged = false;
|
||||
systemd.services.systemd-update-utmp.restartIfChanged = false;
|
||||
systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
|
||||
systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
|
||||
systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
|
||||
|
||||
# Don't bother with certain units in containers.
|
||||
systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container";
|
||||
systemd.services.systemd-random-seed.unitConfig.ConditionVirtualization = "!container";
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ let
|
||||
builder = ./make-etc.sh;
|
||||
|
||||
preferLocalBuild = true;
|
||||
allowSubstitutes = false;
|
||||
|
||||
/* !!! Use toXML. */
|
||||
sources = map (x: x.source) etc';
|
||||
|
||||
Reference in New Issue
Block a user