diff --git a/modules/config/system-path.nix b/modules/config/system-path.nix index f15f8bf8a5a..f0da2eab341 100644 --- a/modules/config/system-path.nix +++ b/modules/config/system-path.nix @@ -60,7 +60,7 @@ let pkgs.usbutils pkgs.utillinux pkgs.wirelesstools - ] ++ optional (pkgs.stdenv.system != "armv5tel-linux") [ pkgs.grub ] + ] ++ config.environment.extraPackages; diff --git a/modules/installer/generations-dir/generations-dir-builder.sh b/modules/installer/generations-dir/generations-dir-builder.sh new file mode 100644 index 00000000000..80f2b638e51 --- /dev/null +++ b/modules/installer/generations-dir/generations-dir-builder.sh @@ -0,0 +1,105 @@ +#! @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 " + 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/-/file to --. +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 +} + + +# Add an entry for a configuration to the Grub menu, and if +# appropriate, 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 $(readlink -f $path/initrd) $outdir/initrd + ln -sf $(readlink -f $path/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 -sf $(readlink -f $path) /boot/default/system + ln -sf $(readlink -f $path/init) /boot/default/init + ln -sf $(readlink -f $path/initrd) /boot/default/initrd + ln -sf $(readlink -f $path/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/modules/installer/generations-dir/generations-dir.nix b/modules/installer/generations-dir/generations-dir.nix new file mode 100644 index 00000000000..7ee7a1828da --- /dev/null +++ b/modules/installer/generations-dir/generations-dir.nix @@ -0,0 +1,60 @@ +{pkgs, config, ...}: + +###### interface +let + inherit (pkgs.lib) mkOption mkIf; + + options = { + boot = { + loader = { + generationsDir = { + + enable = mkOption { + default = false; + description = '' + Whether to enable the simple preparation of symlinks to the system + generations in /boot. + ''; + }; + + copyKernels = mkOption { + default = false; + description = " + Whether copy the necessary boot files into /boot, so + /nix/store is not needed by the boot loadear. + "; + }; + }; + }; + }; + }; + +in + +###### implementation +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 +{ + require = [ + options + + # config.system.build + # ../system/system-options.nix + ]; + + system = mkIf config.boot.loader.generationsDir.enable { + build = { + menuBuilder = generationsDirBuilder; + }; + boot.loader.id = "generationsDir"; + boot.loader.kernelFile = "uImage"; + }; +} diff --git a/modules/installer/grub/grub.nix b/modules/installer/grub/grub.nix index eaef9521f33..233c8fdc95e 100644 --- a/modules/installer/grub/grub.nix +++ b/modules/installer/grub/grub.nix @@ -2,90 +2,100 @@ ###### interface let - inherit (pkgs.lib) mkOption; + inherit (pkgs.lib) mkOption mkIf; options = { boot = { + loader = { + grub = { - grubDevice = mkOption { - default = ""; - example = "/dev/hda"; - description = " - The device on which the boot loader, Grub, will be installed. - If empty, Grub won't be installed and it's your responsibility - to make the system bootable. - "; - }; + enable = mkOption { + default = true; + description = '' + Whether to enable the GRUB boot loader. + ''; + }; - bootMount = mkOption { - default = ""; - example = "(hd0,0)"; - description = " - If the system partition may be wiped on reinstall, it is better - to have /boot on a small partition. To do it, we need to explain - to GRUB where the kernels live. Specify the partition here (in - GRUB notation. - "; - }; + grubDevice = mkOption { + default = ""; + example = "/dev/hda"; + description = " + The device on which the boot loader, Grub, will be installed. + If empty, Grub won't be installed and it's your responsibility + to make the system bootable. + "; + }; - configurationName = mkOption { - default = ""; - example = "Stable 2.6.21"; - description = " - Grub entry name instead of default. - "; - }; + bootMount = mkOption { + default = ""; + example = "(hd0,0)"; + description = " + If the system partition may be wiped on reinstall, it is better + to have /boot on a small partition. To do it, we need to explain + to GRUB where the kernels live. Specify the partition here (in + GRUB notation. + "; + }; - extraGrubEntries = mkOption { - default = ""; - example = " - title Windows - chainloader (hd0,1)+1 - "; - description = " - Any additional entries you want added to the Grub boot menu. - "; - }; + configurationName = mkOption { + default = ""; + example = "Stable 2.6.21"; + description = " + Grub entry name instead of default. + "; + }; - extraGrubEntriesBeforeNixos = mkOption { - default = false; - description = " - Wheter extraGrubEntries are put before the Nixos-default option - "; - }; + extraGrubEntries = mkOption { + default = ""; + example = " + title Windows + chainloader (hd0,1)+1 + "; + description = " + Any additional entries you want added to the Grub boot menu. + "; + }; - grubSplashImage = mkOption { - default = pkgs.fetchurl { - url = http://www.gnome-look.org/CONTENT/content-files/36909-soft-tux.xpm.gz; - sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59"; + extraGrubEntriesBeforeNixos = mkOption { + default = false; + description = " + Wheter extraGrubEntries are put before the Nixos-default option + "; + }; + + grubSplashImage = mkOption { + default = pkgs.fetchurl { + url = http://www.gnome-look.org/CONTENT/content-files/36909-soft-tux.xpm.gz; + sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59"; + }; + example = null; + description = " + Background image used for Grub. It must be a 640x480, + 14-colour image in XPM format, optionally compressed with + gzip or bzip2. Set to + null to run Grub in text mode. + "; + }; + + configurationLimit = mkOption { + default = 100; + example = 120; + description = " + Maximum of configurations in boot menu. GRUB has problems when + there are too many entries. + "; + }; + + copyKernels = mkOption { + default = false; + description = " + Whether the Grub menu builder should copy kernels and initial + ramdisks to /boot. This is necessary when /nix is on a + different file system than /boot. + "; + }; }; - example = null; - description = " - Background image used for Grub. It must be a 640x480, - 14-colour image in XPM format, optionally compressed with - gzip or bzip2. Set to - null to run Grub in text mode. - "; }; - - configurationLimit = mkOption { - default = 100; - example = 120; - description = " - Maximum of configurations in boot menu. GRUB has problems when - there are too many entries. - "; - }; - - copyKernels = mkOption { - default = false; - description = " - Whether the Grub menu builder should copy kernels and initial - ramdisks to /boot. This is necessary when /nix is on a - different file system than /boot. - "; - }; - }; }; in @@ -99,7 +109,7 @@ let isExecutable = true; inherit (pkgs) bash; path = [pkgs.coreutils pkgs.gnused pkgs.gnugrep]; - inherit (config.boot) copyKernels extraGrubEntries extraGrubEntriesBeforeNixos + inherit (config.boot.loader.grub) copyKernels extraGrubEntries extraGrubEntriesBeforeNixos grubSplashImage bootMount configurationLimit; }; in @@ -112,11 +122,18 @@ in # ../system/system-options.nix ]; - system = { + system = mkIf config.boot.loader.grub.enable { build = { - inherit grubMenuBuilder; + menuBuilder = grubMenuBuilder; }; + + # Common attribute for boot loaders so only one of them can be + # set at once + boot.loader.id = "grub"; + boot.loader.kernelFile = "vmlinuz"; }; + environment.extraPackages = mkIf config.boot.loader.grub.enable [ pkgs.grub ]; + # and many other things that have to be moved inside this file. } diff --git a/modules/module-list.nix b/modules/module-list.nix index 68f639ea5ff..39b1bb325b6 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -13,6 +13,7 @@ ./hardware/network/rt73.nix ./hardware/pcmcia.nix ./installer/grub/grub.nix + ./installer/generations-dir/generations-dir.nix ./installer/tools/nixos-checkout.nix ./installer/tools/tools.nix ./misc/assertions.nix diff --git a/modules/system/activation/switch-to-configuration.sh b/modules/system/activation/switch-to-configuration.sh index bcf31f9bd52..957bc124d7a 100644 --- a/modules/system/activation/switch-to-configuration.sh +++ b/modules/system/activation/switch-to-configuration.sh @@ -22,14 +22,20 @@ EOF fi if test "$action" = "switch" -o "$action" = "boot"; then - if test -n "@grubDevice@"; then - mkdir -m 0700 -p /boot/grub - @grubMenuBuilder@ @out@ - if test "$NIXOS_INSTALL_GRUB" = 1; then - @grub@/sbin/grub-install "@grubDevice@" --no-floppy --recheck - fi + if [ "@bootLoader@" == "grub" ]; then + if test -n "@grubDevice@"; then + mkdir -m 0700 -p /boot/grub + @menuBuilder@ @out@ + if test "$NIXOS_INSTALL_GRUB" = 1; then + @grub@/sbin/grub-install "@grubDevice@" --no-floppy --recheck + fi + else + echo "Warning: don't know how to make this configuration bootable; please set \`boot.grubDevice'." 1>&2 + fi + elif [ "@bootLoader@" == "generationsDir" ]; then + @menuBuilder@ @out@ else - echo "Warning: don't know how to make this configuration bootable; please set \`boot.grubDevice'." 1>&2 + echo "Warning: don't know how to make this configuration bootable; please enable a boot loader." 1>&2 fi fi diff --git a/modules/system/activation/top-level.nix b/modules/system/activation/top-level.nix index 19e737d0d42..992b988bc49 100644 --- a/modules/system/activation/top-level.nix +++ b/modules/system/activation/top-level.nix @@ -17,6 +17,20 @@ let Additional configurations to build. ''; }; + + system.boot.loader.id = pkgs.lib.mkOption { + default = ""; + description = '' + Id string of the used bootloader. + ''; + }; + + system.boot.loader.kernelFile = pkgs.lib.mkOption { + default = ""; + description = '' + Name of the kernel file to be passed to the bootloader. + ''; + }; }; @@ -33,13 +47,17 @@ let systemBuilder = let - kernelfile = if (pkgs.stdenv.system == "armv5tel-linux") - then "${config.boot.kernelPackages.kernel}/uImage" - else "${config.boot.kernelPackages.kernel}/vmlinuz"; - in '' + kernelPath = "${config.boot.kernelPackages.kernel}/" + + "${config.system.boot.loader.kernelFile}"; + in + '' ensureDir $out - ln -s ${kernelfile} $out/kernel + if [ ! -f ${kernelPath} ]; then + echo "The bootloader cannot find the proper kernel image." + echo "(Expecting ${kernelPath})" + fi + ln -s ${kernelPath} $out/kernel if [ -n "$grub" ]; then ln -s $grub $out/grub fi @@ -76,13 +94,9 @@ let name = "system"; buildCommand = systemBuilder; inherit children; - grub = if (pkgs.stdenv.system != "armv5tel-linux") then pkgs.grub - else null; - grubDevice = config.boot.grubDevice; kernelParams = config.boot.kernelParams ++ config.boot.extraKernelParams; - grubMenuBuilder = config.system.build.grubMenuBuilder; - configurationName = config.boot.configurationName; + menuBuilder = config.system.build.menuBuilder; # Most of these are needed by grub-install. path = [ pkgs.coreutils @@ -92,6 +106,12 @@ let pkgs.diffutils pkgs.upstart # for initctl ]; + + # Boot loaders + bootLoader = config.system.boot.loader.id; + grub = if config.boot.loader.grub.enable then pkgs.grub else null; + grubDevice = config.boot.loader.grub.grubDevice; + configurationName = config.boot.loader.grub.configurationName; };