{ config, pkgs, ... }: with pkgs.lib; let cfg = config.boot.loader.grub; realGrub = if cfg.version == 1 then pkgs.grub else pkgs.grub2; grub = # Don't include GRUB if we're only generating a GRUB menu (e.g., # in EC2 instances). if cfg.devices == ["nodev"] then null else realGrub; f = x: if x == null then "" else "" + x; grubConfig = pkgs.writeText "grub-config.xml" (builtins.toXML { splashImage = f config.boot.loader.grub.splashImage; grub = f grub; shell = "${pkgs.stdenv.shell}"; fullVersion = (builtins.parseDrvName realGrub.name).version; inherit (cfg) version extraConfig extraPerEntryConfig extraEntries extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels timeout default devices; path = (makeSearchPath "bin" [ pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils ]) + ":" + (makeSearchPath "sbin" [ pkgs.mdadm ]); }); in { ###### interface options = { boot.loader.grub = { enable = mkOption { default = true; description = '' Whether to enable the GNU GRUB boot loader. ''; }; version = mkOption { default = 1; example = 2; description = '' The version of GRUB to use: 1 for GRUB Legacy (versions 0.9x), or 2 for GRUB 2. ''; }; device = mkOption { default = ""; example = "/dev/hda"; type = with pkgs.lib.types; uniq string; description = '' The device on which the GRUB boot loader will be installed. The special value nodev means that a GRUB boot menu will be generated, but GRUB itself will not actually be installed. To install GRUB on multiple devices, use boot.loader.grub.devices. ''; }; devices = mkOption { default = []; example = [ "/dev/hda" ]; type = with pkgs.lib.types; listOf string; description = '' The devices on which the boot loader, GRUB, will be installed. Can be used instead of device to install grub into multiple devices (e.g., if as softraid arrays holding /boot). ''; }; # !!! How can we mark options as obsolete? bootDevice = mkOption { default = ""; description = "Obsolete."; }; configurationName = mkOption { default = ""; example = "Stable 2.6.21"; description = '' GRUB entry name instead of default. ''; }; extraPrepareConfig = mkOption { default = ""; description = '' Additional bash commands to be run at the script that prepares the grub menu entries. ''; }; extraConfig = mkOption { default = ""; example = "serial; terminal_output.serial"; description = '' Additional GRUB commands inserted in the configuration file just before the menu entries. ''; }; extraPerEntryConfig = mkOption { default = ""; example = "root (hd0)"; description = '' Additional GRUB commands inserted in the configuration file at the start of each NixOS menu entry. ''; }; extraEntries = mkOption { default = ""; example = '' # GRUB 1 example (not GRUB 2 compatible) title Windows chainloader (hd0,1)+1 # GRUB 2 example menuentry "Windows7" { title Windows7 insmod ntfs set root='(hd1,1)' chainloader +1 } ''; description = '' Any additional entries you want added to the GRUB boot menu. ''; }; extraEntriesBeforeNixOS = mkOption { default = false; description = '' Whether extraEntries are included before the default option. ''; }; extraFiles = mkOption { default = {}; example = literalExample '' { "memtest.bin" = "${pkgs.memtest86plus}/memtest.bin"; } ''; description = '' A set of files to be copied to /boot. Each attribute name denotes the destination file name in /boot, while the corresponding attribute value specifies the source file. ''; }; splashImage = mkOption { default = if cfg.version == 1 then pkgs.fetchurl { url = http://www.gnome-look.org/CONTENT/content-files/36909-soft-tux.xpm.gz; sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59"; } # GRUB 1.97 doesn't support gzipped XPMs. else ./winkler-gnu-blue-640x480.png; 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 done automatically if /boot is on a different partition than /. ''; }; timeout = mkOption { default = 5; description = '' Timeout (in seconds) until GRUB boots the default menu item. ''; }; default = mkOption { default = 0; description = '' Index of the default menu item to be booted. ''; }; }; }; ###### implementation config = mkIf cfg.enable { boot.loader.grub.devices = optional (cfg.device != "") cfg.device; system.build = mkAssert (cfg.devices != []) "You must set the ‘boot.loader.grub.device’ option to make the system bootable." { installBootLoader = "PERL5LIB=${makePerlPath [ pkgs.perlPackages.XMLLibXML pkgs.perlPackages.XMLSAX ]} " + "${pkgs.perl}/bin/perl ${./install-grub.pl} ${grubConfig}"; inherit grub; }; # Common attribute for boot loaders so only one of them can be # set at once. system.boot.loader.id = "grub"; environment.systemPackages = [ grub ]; boot.loader.grub.extraPrepareConfig = concatStrings (mapAttrsToList (n: v: '' ${pkgs.coreutils}/bin/cp -pf "${v}" "/boot/${n}" '') config.boot.loader.grub.extraFiles); }; }