summary refs log tree commit diff
path: root/nixos/modules/system/boot/loader/generations-dir
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/system/boot/loader/generations-dir')
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh106
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir.nix62
2 files changed, 168 insertions, 0 deletions
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh b/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh
new file mode 100644
index 00000000000..8ae23dc988c
--- /dev/null
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir-builder.sh
@@ -0,0 +1,106 @@
+#! @bash@/bin/sh -e
+
+shopt -s nullglob
+
+export PATH=/empty
+for i in @path@; do PATH=$PATH:$i/bin; done
+
+default=$1
+if test -z "$1"; then
+    echo "Syntax: generations-dir-builder.sh <DEFAULT-CONFIG>"
+    exit 1
+fi
+
+echo "updating the boot generations directory..."
+
+mkdir -p /boot
+
+rm -Rf /boot/system* || true
+
+target=/boot/grub/menu.lst
+tmp=$target.tmp
+
+# 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 /boot/kernels.
+declare -A filesCopied
+
+copyToKernelsDir() {
+    local src="$1"
+    local dst="/boot/kernels/$(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 $src $dstTmp
+        mv $dstTmp $dst
+    fi
+    filesCopied[$dst]=1
+    result=$dst
+}
+
+
+# Copy its kernel and initrd to /boot/kernels.
+addEntry() {
+    local path="$1"
+    local generation="$2"
+    local outdir=/boot/system-$generation
+
+    if ! test -e $path/kernel -a -e $path/initrd; then
+        return
+    fi
+
+    local kernel=$(readlink -f $path/kernel)
+    local initrd=$(readlink -f $path/initrd)
+
+    if test -n "@copyKernels@"; then
+        copyToKernelsDir $kernel; kernel=$result
+        copyToKernelsDir $initrd; initrd=$result
+    fi
+
+    mkdir -p $outdir
+    ln -sf $(readlink -f $path) $outdir/system
+    ln -sf $(readlink -f $path/init) $outdir/init
+    ln -sf $initrd $outdir/initrd
+    ln -sf $kernel $outdir/kernel
+
+    if test $(readlink -f "$path") = "$default"; then
+      cp "$kernel" /boot/nixos-kernel
+      cp "$initrd" /boot/nixos-initrd
+      cp "$(readlink -f "$path/init")" /boot/nixos-init
+
+      mkdir -p /boot/default
+      # ln -sfT: overrides target even if it exists.
+      ln -sfT $(readlink -f $path) /boot/default/system
+      ln -sfT $(readlink -f $path/init) /boot/default/init
+      ln -sfT $initrd /boot/default/initrd
+      ln -sfT $kernel /boot/default/kernel
+    fi
+}
+
+if test -n "@copyKernels@"; then
+    mkdir -p /boot/kernels
+fi
+
+# Add all 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); do
+    link=/nix/var/nix/profiles/system-$generation-link
+    addEntry $link $generation
+done
+
+# Remove obsolete files from /boot/kernels.
+for fn in /boot/kernels/*; do
+    if ! test "${filesCopied[$fn]}" = 1; then
+        rm -vf -- "$fn"
+    fi
+done
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
new file mode 100644
index 00000000000..1437ab38770
--- /dev/null
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
@@ -0,0 +1,62 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  generationsDirBuilder = pkgs.substituteAll {
+    src = ./generations-dir-builder.sh;
+    isExecutable = true;
+    inherit (pkgs) bash;
+    path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep];
+    inherit (config.boot.loader.generationsDir) copyKernels;
+  };
+
+in
+
+{
+  options = {
+
+    boot.loader.generationsDir = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to create symlinks to the system generations under
+          <literal>/boot</literal>.  When enabled,
+          <literal>/boot/default/kernel</literal>,
+          <literal>/boot/default/initrd</literal>, etc., are updated to
+          point to the current generation's kernel image, initial RAM
+          disk, and other bootstrap files.
+
+          This optional is not necessary with boot loaders such as GNU GRUB
+          for which the menu is updated to point to the latest bootstrap
+          files.  However, it is needed for U-Boot on platforms where the
+          boot command line is stored in flash memory rather than in a
+          menu file.
+        '';
+      };
+
+      copyKernels = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether copy the necessary boot files into /boot, so
+          /nix/store is not needed by the boot loader.
+        '';
+      };
+
+    };
+
+  };
+
+
+  config = mkIf config.boot.loader.generationsDir.enable {
+
+    system.build.installBootLoader = generationsDirBuilder;
+    system.boot.loader.id = "generationsDir";
+    system.boot.loader.kernelFile = pkgs.stdenv.hostPlatform.linux-kernel.target;
+
+  };
+}