2012-07-24 16:16:27 -07:00
|
|
|
|
use strict;
|
|
|
|
|
use warnings;
|
2014-08-31 09:18:13 -07:00
|
|
|
|
use Class::Struct;
|
2012-07-24 16:16:27 -07:00
|
|
|
|
use XML::LibXML;
|
|
|
|
|
use File::Basename;
|
|
|
|
|
use File::Path;
|
|
|
|
|
use File::stat;
|
|
|
|
|
use File::Copy;
|
2020-07-04 20:16:25 -07:00
|
|
|
|
use File::Copy::Recursive qw(rcopy pathrm);
|
2014-09-03 14:40:27 -07:00
|
|
|
|
use File::Slurp;
|
2015-07-05 18:34:45 -07:00
|
|
|
|
use File::Temp;
|
2020-04-23 13:44:21 -07:00
|
|
|
|
use JSON;
|
2020-07-04 20:16:25 -07:00
|
|
|
|
use File::Find;
|
2015-01-14 01:30:57 -08:00
|
|
|
|
require List::Compare;
|
2012-07-24 16:16:27 -07:00
|
|
|
|
use POSIX;
|
|
|
|
|
use Cwd;
|
|
|
|
|
|
2016-09-01 01:13:47 -07:00
|
|
|
|
# system.build.toplevel path
|
2015-05-29 12:01:50 -07:00
|
|
|
|
my $defaultConfig = $ARGV[1] or die;
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
2016-09-01 01:13:47 -07:00
|
|
|
|
# Grub config XML generated by grubConfig function in grub.nix
|
2012-07-24 16:16:27 -07:00
|
|
|
|
my $dom = XML::LibXML->load_xml(location => $ARGV[0]);
|
|
|
|
|
|
|
|
|
|
sub get { my ($name) = @_; return $dom->findvalue("/expr/attrs/attr[\@name = '$name']/*/\@value"); }
|
|
|
|
|
|
2020-04-23 13:42:11 -07:00
|
|
|
|
sub getList {
|
|
|
|
|
my ($name) = @_;
|
|
|
|
|
my @list = ();
|
|
|
|
|
foreach my $entry ($dom->findnodes("/expr/attrs/attr[\@name = '$name']/list/string/\@value")) {
|
|
|
|
|
$entry = $entry->findvalue(".") or die;
|
|
|
|
|
push(@list, $entry);
|
|
|
|
|
}
|
|
|
|
|
return @list;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-25 07:47:32 -07:00
|
|
|
|
sub readFile {
|
|
|
|
|
my ($fn) = @_; local $/ = undef;
|
|
|
|
|
open FILE, "<$fn" or return undef; my $s = <FILE>; close FILE;
|
|
|
|
|
local $/ = "\n"; chomp $s; return $s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub writeFile {
|
|
|
|
|
my ($fn, $s) = @_;
|
|
|
|
|
open FILE, ">$fn" or die "cannot create $fn: $!\n";
|
|
|
|
|
print FILE $s or die;
|
|
|
|
|
close FILE or die;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-31 09:18:13 -07:00
|
|
|
|
sub runCommand {
|
|
|
|
|
my ($cmd) = @_;
|
|
|
|
|
open FILE, "$cmd 2>/dev/null |" or die "Failed to execute: $cmd\n";
|
|
|
|
|
my @ret = <FILE>;
|
|
|
|
|
close FILE;
|
|
|
|
|
return ($?, @ret);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-24 16:16:27 -07:00
|
|
|
|
my $grub = get("grub");
|
|
|
|
|
my $grubVersion = int(get("version"));
|
2015-01-14 01:30:57 -08:00
|
|
|
|
my $grubTarget = get("grubTarget");
|
2012-07-24 16:16:27 -07:00
|
|
|
|
my $extraConfig = get("extraConfig");
|
2012-12-16 12:41:47 -08:00
|
|
|
|
my $extraPrepareConfig = get("extraPrepareConfig");
|
2012-07-24 16:16:27 -07:00
|
|
|
|
my $extraPerEntryConfig = get("extraPerEntryConfig");
|
|
|
|
|
my $extraEntries = get("extraEntries");
|
|
|
|
|
my $extraEntriesBeforeNixOS = get("extraEntriesBeforeNixOS") eq "true";
|
|
|
|
|
my $splashImage = get("splashImage");
|
2018-08-28 20:53:10 -07:00
|
|
|
|
my $splashMode = get("splashMode");
|
|
|
|
|
my $backgroundColor = get("backgroundColor");
|
2012-07-24 16:16:27 -07:00
|
|
|
|
my $configurationLimit = int(get("configurationLimit"));
|
|
|
|
|
my $copyKernels = get("copyKernels") eq "true";
|
|
|
|
|
my $timeout = int(get("timeout"));
|
2018-06-18 21:54:45 -07:00
|
|
|
|
my $defaultEntry = get("default");
|
2014-08-31 09:18:13 -07:00
|
|
|
|
my $fsIdentifier = get("fsIdentifier");
|
2015-01-14 01:30:57 -08:00
|
|
|
|
my $grubEfi = get("grubEfi");
|
|
|
|
|
my $grubTargetEfi = get("grubTargetEfi");
|
2015-05-25 14:57:20 -07:00
|
|
|
|
my $bootPath = get("bootPath");
|
2015-06-13 06:00:43 -07:00
|
|
|
|
my $storePath = get("storePath");
|
2015-01-14 01:30:57 -08:00
|
|
|
|
my $canTouchEfiVariables = get("canTouchEfiVariables");
|
2016-09-13 10:46:53 -07:00
|
|
|
|
my $efiInstallAsRemovable = get("efiInstallAsRemovable");
|
2015-01-14 01:30:57 -08:00
|
|
|
|
my $efiSysMountPoint = get("efiSysMountPoint");
|
2015-06-10 11:50:21 -07:00
|
|
|
|
my $gfxmodeEfi = get("gfxmodeEfi");
|
|
|
|
|
my $gfxmodeBios = get("gfxmodeBios");
|
2019-03-21 03:00:39 -07:00
|
|
|
|
my $gfxpayloadEfi = get("gfxpayloadEfi");
|
|
|
|
|
my $gfxpayloadBios = get("gfxpayloadBios");
|
2015-06-10 15:47:08 -07:00
|
|
|
|
my $bootloaderId = get("bootloaderId");
|
2016-10-08 20:59:42 -07:00
|
|
|
|
my $forceInstall = get("forceInstall");
|
2017-06-10 06:53:24 -07:00
|
|
|
|
my $font = get("font");
|
2020-07-04 20:16:25 -07:00
|
|
|
|
my $theme = get("theme");
|
2012-11-15 13:54:43 -08:00
|
|
|
|
$ENV{'PATH'} = get("path");
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
|
|
|
|
die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
|
|
|
|
|
|
2012-07-24 16:27:16 -07:00
|
|
|
|
print STDERR "updating GRUB $grubVersion menu...\n";
|
|
|
|
|
|
2015-05-25 14:57:20 -07:00
|
|
|
|
mkpath("$bootPath/grub", 0, 0700);
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
2015-05-25 14:57:20 -07:00
|
|
|
|
# Discover whether the bootPath is on the same filesystem as / and
|
2012-07-24 16:16:27 -07:00
|
|
|
|
# /nix/store. If not, then all kernels and initrds must be copied to
|
2015-05-25 14:57:20 -07:00
|
|
|
|
# the bootPath.
|
|
|
|
|
if (stat($bootPath)->dev != stat("/nix/store")->dev) {
|
2012-07-24 16:16:27 -07:00
|
|
|
|
$copyKernels = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-25 14:57:20 -07:00
|
|
|
|
# Discover information about the location of the bootPath
|
2014-08-31 09:18:13 -07:00
|
|
|
|
struct(Fs => {
|
|
|
|
|
device => '$',
|
|
|
|
|
type => '$',
|
|
|
|
|
mount => '$',
|
|
|
|
|
});
|
2014-09-03 14:40:27 -07:00
|
|
|
|
sub PathInMount {
|
|
|
|
|
my ($path, $mount) = @_;
|
|
|
|
|
my @splitMount = split /\//, $mount;
|
|
|
|
|
my @splitPath = split /\//, $path;
|
|
|
|
|
if ($#splitPath < $#splitMount) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
for (my $i = 0; $i <= $#splitMount; $i++) {
|
|
|
|
|
if ($splitMount[$i] ne $splitPath[$i]) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2016-09-01 01:13:47 -07:00
|
|
|
|
|
|
|
|
|
# Figure out what filesystem is used for the directory with init/initrd/kernel files
|
2014-08-31 09:18:13 -07:00
|
|
|
|
sub GetFs {
|
|
|
|
|
my ($dir) = @_;
|
2014-09-03 14:40:27 -07:00
|
|
|
|
my $bestFs = Fs->new(device => "", type => "", mount => "");
|
|
|
|
|
foreach my $fs (read_file("/proc/self/mountinfo")) {
|
|
|
|
|
chomp $fs;
|
|
|
|
|
my @fields = split / /, $fs;
|
|
|
|
|
my $mountPoint = $fields[4];
|
|
|
|
|
next unless -d $mountPoint;
|
|
|
|
|
my @mountOptions = split /,/, $fields[5];
|
|
|
|
|
|
|
|
|
|
# Skip the optional fields.
|
|
|
|
|
my $n = 6; $n++ while $fields[$n] ne "-"; $n++;
|
|
|
|
|
my $fsType = $fields[$n];
|
|
|
|
|
my $device = $fields[$n + 1];
|
|
|
|
|
my @superOptions = split /,/, $fields[$n + 2];
|
|
|
|
|
|
2017-07-13 14:43:48 -07:00
|
|
|
|
# Skip the bind-mount on /nix/store.
|
|
|
|
|
next if $mountPoint eq "/nix/store" && (grep { $_ eq "rw" } @superOptions);
|
2015-01-14 01:30:57 -08:00
|
|
|
|
# Skip mount point generated by systemd-efi-boot-generator?
|
|
|
|
|
next if $fsType eq "autofs";
|
2014-09-03 14:40:27 -07:00
|
|
|
|
|
|
|
|
|
# Ensure this matches the intended directory
|
|
|
|
|
next unless PathInMount($dir, $mountPoint);
|
|
|
|
|
|
|
|
|
|
# Is it better than our current match?
|
|
|
|
|
if (length($mountPoint) > length($bestFs->mount)) {
|
|
|
|
|
$bestFs = Fs->new(device => $device, type => $fsType, mount => $mountPoint);
|
|
|
|
|
}
|
2014-08-31 09:18:13 -07:00
|
|
|
|
}
|
2014-09-03 14:40:27 -07:00
|
|
|
|
return $bestFs;
|
2014-08-31 09:18:13 -07:00
|
|
|
|
}
|
|
|
|
|
struct (Grub => {
|
|
|
|
|
path => '$',
|
|
|
|
|
search => '$',
|
|
|
|
|
});
|
|
|
|
|
my $driveid = 1;
|
|
|
|
|
sub GrubFs {
|
|
|
|
|
my ($dir) = @_;
|
|
|
|
|
my $fs = GetFs($dir);
|
2016-09-01 01:14:23 -07:00
|
|
|
|
my $path = substr($dir, length($fs->mount));
|
|
|
|
|
if (substr($path, 0, 1) ne "/") {
|
|
|
|
|
$path = "/$path";
|
|
|
|
|
}
|
2014-08-31 09:18:13 -07:00
|
|
|
|
my $search = "";
|
|
|
|
|
|
|
|
|
|
if ($grubVersion > 1) {
|
|
|
|
|
# ZFS is completely separate logic as zpools are always identified by a label
|
|
|
|
|
# or custom UUID
|
|
|
|
|
if ($fs->type eq 'zfs') {
|
|
|
|
|
my $sid = index($fs->device, '/');
|
|
|
|
|
|
|
|
|
|
if ($sid < 0) {
|
|
|
|
|
$search = '--label ' . $fs->device;
|
|
|
|
|
$path = '/@' . $path;
|
|
|
|
|
} else {
|
|
|
|
|
$search = '--label ' . substr($fs->device, 0, $sid);
|
|
|
|
|
$path = '/' . substr($fs->device, $sid) . '/@' . $path;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
my %types = ('uuid' => '--fs-uuid', 'label' => '--label');
|
|
|
|
|
|
|
|
|
|
if ($fsIdentifier eq 'provided') {
|
|
|
|
|
# If the provided dev is identifying the partition using a label or uuid,
|
|
|
|
|
# we should get the label / uuid and do a proper search
|
|
|
|
|
my @matches = $fs->device =~ m/\/dev\/disk\/by-(label|uuid)\/(.*)/;
|
|
|
|
|
if ($#matches > 1) {
|
|
|
|
|
die "Too many matched devices"
|
|
|
|
|
} elsif ($#matches == 1) {
|
|
|
|
|
$search = "$types{$matches[0]} $matches[1]"
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
# Determine the identifying type
|
|
|
|
|
$search = $types{$fsIdentifier} . ' ';
|
|
|
|
|
|
|
|
|
|
# Based on the type pull in the identifier from the system
|
2016-09-01 01:36:38 -07:00
|
|
|
|
my ($status, @devInfo) = runCommand("@utillinux@/bin/blkid -o export @{[$fs->device]}");
|
2014-08-31 09:18:13 -07:00
|
|
|
|
if ($status != 0) {
|
2017-12-25 23:58:04 -08:00
|
|
|
|
die "Failed to get blkid info (returned $status) for @{[$fs->mount]} on @{[$fs->device]}";
|
2014-08-31 09:18:13 -07:00
|
|
|
|
}
|
|
|
|
|
my @matches = join("", @devInfo) =~ m/@{[uc $fsIdentifier]}=([^\n]*)/;
|
|
|
|
|
if ($#matches != 0) {
|
|
|
|
|
die "Couldn't find a $types{$fsIdentifier} for @{[$fs->device]}\n"
|
|
|
|
|
}
|
|
|
|
|
$search .= $matches[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# BTRFS is a special case in that we need to fix the referrenced path based on subvolumes
|
|
|
|
|
if ($fs->type eq 'btrfs') {
|
2016-09-01 01:36:38 -07:00
|
|
|
|
my ($status, @id_info) = runCommand("@btrfsprogs@/bin/btrfs subvol show @{[$fs->mount]}");
|
2014-08-31 09:18:13 -07:00
|
|
|
|
if ($status != 0) {
|
2014-09-02 18:56:53 -07:00
|
|
|
|
die "Failed to retrieve subvolume info for @{[$fs->mount]}\n";
|
2014-08-31 09:18:13 -07:00
|
|
|
|
}
|
2017-10-10 17:22:57 -07:00
|
|
|
|
my @ids = join("\n", @id_info) =~ m/^(?!\/\n).*Subvolume ID:[ \t\n]*([0-9]+)/s;
|
2014-08-30 15:44:13 -07:00
|
|
|
|
if ($#ids > 0) {
|
2014-08-31 09:18:13 -07:00
|
|
|
|
die "Btrfs subvol name for @{[$fs->device]} listed multiple times in mount\n"
|
2014-08-30 15:44:13 -07:00
|
|
|
|
} elsif ($#ids == 0) {
|
2016-09-01 01:36:38 -07:00
|
|
|
|
my ($status, @path_info) = runCommand("@btrfsprogs@/bin/btrfs subvol list @{[$fs->mount]}");
|
2014-08-30 15:44:13 -07:00
|
|
|
|
if ($status != 0) {
|
|
|
|
|
die "Failed to find @{[$fs->mount]} subvolume id from btrfs\n";
|
|
|
|
|
}
|
|
|
|
|
my @paths = join("", @path_info) =~ m/ID $ids[0] [^\n]* path ([^\n]*)/;
|
|
|
|
|
if ($#paths > 0) {
|
|
|
|
|
die "Btrfs returned multiple paths for a single subvolume id, mountpoint @{[$fs->mount]}\n";
|
|
|
|
|
} elsif ($#paths != 0) {
|
|
|
|
|
die "Btrfs did not return a path for the subvolume at @{[$fs->mount]}\n";
|
|
|
|
|
}
|
|
|
|
|
$path = "/$paths[0]$path";
|
2014-08-31 09:18:13 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (not $search eq "") {
|
|
|
|
|
$search = "search --set=drive$driveid " . $search;
|
|
|
|
|
$path = "(\$drive$driveid)$path";
|
|
|
|
|
$driveid += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Grub->new(path => $path, search => $search);
|
|
|
|
|
}
|
2015-05-25 14:57:20 -07:00
|
|
|
|
my $grubBoot = GrubFs($bootPath);
|
2014-09-23 05:34:44 -07:00
|
|
|
|
my $grubStore;
|
|
|
|
|
if ($copyKernels == 0) {
|
2015-06-13 06:00:43 -07:00
|
|
|
|
$grubStore = GrubFs($storePath);
|
2014-09-23 05:34:44 -07:00
|
|
|
|
}
|
2014-08-31 09:18:13 -07:00
|
|
|
|
|
2012-07-24 16:16:27 -07:00
|
|
|
|
# Generate the header.
|
|
|
|
|
my $conf .= "# Automatically generated. DO NOT EDIT THIS FILE!\n";
|
|
|
|
|
|
|
|
|
|
if ($grubVersion == 1) {
|
|
|
|
|
$conf .= "
|
|
|
|
|
default $defaultEntry
|
|
|
|
|
timeout $timeout
|
|
|
|
|
";
|
|
|
|
|
if ($splashImage) {
|
2020-07-02 13:18:49 -07:00
|
|
|
|
copy $splashImage, "$bootPath/background.xpm.gz" or die "cannot copy $splashImage to $bootPath: $!\n";
|
2018-09-02 05:34:55 -07:00
|
|
|
|
$conf .= "splashimage " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background.xpm.gz\n";
|
2012-07-24 16:16:27 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
2019-07-21 09:39:07 -07:00
|
|
|
|
my @users = ();
|
|
|
|
|
foreach my $user ($dom->findnodes('/expr/attrs/attr[@name = "users"]/attrs/attr')) {
|
|
|
|
|
my $name = $user->findvalue('@name') or die;
|
|
|
|
|
my $hashedPassword = $user->findvalue('./attrs/attr[@name = "hashedPassword"]/string/@value');
|
|
|
|
|
my $hashedPasswordFile = $user->findvalue('./attrs/attr[@name = "hashedPasswordFile"]/string/@value');
|
|
|
|
|
my $password = $user->findvalue('./attrs/attr[@name = "password"]/string/@value');
|
|
|
|
|
my $passwordFile = $user->findvalue('./attrs/attr[@name = "passwordFile"]/string/@value');
|
|
|
|
|
|
|
|
|
|
if ($hashedPasswordFile) {
|
|
|
|
|
open(my $f, '<', $hashedPasswordFile) or die "Can't read file '$hashedPasswordFile'!";
|
|
|
|
|
$hashedPassword = <$f>;
|
|
|
|
|
chomp $hashedPassword;
|
|
|
|
|
}
|
|
|
|
|
if ($passwordFile) {
|
|
|
|
|
open(my $f, '<', $passwordFile) or die "Can't read file '$passwordFile'!";
|
|
|
|
|
$password = <$f>;
|
|
|
|
|
chomp $password;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($hashedPassword) {
|
|
|
|
|
if (index($hashedPassword, "grub.pbkdf2.") == 0) {
|
|
|
|
|
$conf .= "\npassword_pbkdf2 $name $hashedPassword";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
die "Password hash for GRUB user '$name' is not valid!";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
elsif ($password) {
|
|
|
|
|
$conf .= "\npassword $name $password";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
die "GRUB user '$name' has no password!";
|
|
|
|
|
}
|
|
|
|
|
push(@users, $name);
|
|
|
|
|
}
|
|
|
|
|
if (@users) {
|
|
|
|
|
$conf .= "\nset superusers=\"" . join(' ',@users) . "\"\n";
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 00:01:41 -07:00
|
|
|
|
if ($copyKernels == 0) {
|
|
|
|
|
$conf .= "
|
|
|
|
|
" . $grubStore->search;
|
|
|
|
|
}
|
2015-07-26 16:07:13 -07:00
|
|
|
|
# FIXME: should use grub-mkconfig.
|
2012-07-24 16:16:27 -07:00
|
|
|
|
$conf .= "
|
2014-08-31 09:18:13 -07:00
|
|
|
|
" . $grubBoot->search . "
|
2012-07-24 16:16:27 -07:00
|
|
|
|
if [ -s \$prefix/grubenv ]; then
|
|
|
|
|
load_env
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ‘grub-reboot’ sets a one-time saved entry, which we process here and
|
|
|
|
|
# then delete.
|
2015-07-26 16:07:13 -07:00
|
|
|
|
if [ \"\${next_entry}\" ]; then
|
|
|
|
|
set default=\"\${next_entry}\"
|
|
|
|
|
set next_entry=
|
|
|
|
|
save_env next_entry
|
2012-07-24 16:16:27 -07:00
|
|
|
|
set timeout=1
|
|
|
|
|
else
|
|
|
|
|
set default=$defaultEntry
|
|
|
|
|
set timeout=$timeout
|
|
|
|
|
fi
|
|
|
|
|
|
2015-05-29 12:09:08 -07:00
|
|
|
|
# Setup the graphics stack for bios and efi systems
|
2015-06-10 11:50:21 -07:00
|
|
|
|
if [ \"\${grub_platform}\" = \"efi\" ]; then
|
|
|
|
|
insmod efi_gop
|
|
|
|
|
insmod efi_uga
|
|
|
|
|
else
|
|
|
|
|
insmod vbe
|
|
|
|
|
fi
|
2012-07-24 16:16:27 -07:00
|
|
|
|
";
|
|
|
|
|
|
2017-06-10 06:53:24 -07:00
|
|
|
|
if ($font) {
|
2020-07-02 13:18:49 -07:00
|
|
|
|
copy $font, "$bootPath/converted-font.pf2" or die "cannot copy $font to $bootPath: $!\n";
|
2018-06-05 07:37:38 -07:00
|
|
|
|
$conf .= "
|
|
|
|
|
insmod font
|
2018-09-02 05:34:55 -07:00
|
|
|
|
if loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/converted-font.pf2; then
|
2018-06-05 07:37:38 -07:00
|
|
|
|
insmod gfxterm
|
|
|
|
|
if [ \"\${grub_platform}\" = \"efi\" ]; then
|
|
|
|
|
set gfxmode=$gfxmodeEfi
|
2019-03-21 03:00:39 -07:00
|
|
|
|
set gfxpayload=$gfxpayloadEfi
|
2018-06-05 07:37:38 -07:00
|
|
|
|
else
|
|
|
|
|
set gfxmode=$gfxmodeBios
|
2019-03-21 03:00:39 -07:00
|
|
|
|
set gfxpayload=$gfxpayloadBios
|
2018-06-05 07:37:38 -07:00
|
|
|
|
fi
|
|
|
|
|
terminal_output gfxterm
|
|
|
|
|
fi
|
|
|
|
|
";
|
2017-06-10 06:53:24 -07:00
|
|
|
|
}
|
2012-07-24 16:16:27 -07:00
|
|
|
|
if ($splashImage) {
|
2018-05-13 16:51:50 -07:00
|
|
|
|
# Keeps the image's extension.
|
|
|
|
|
my ($filename, $dirs, $suffix) = fileparse($splashImage, qr"\..[^.]*$");
|
|
|
|
|
# The module for jpg is jpeg.
|
|
|
|
|
if ($suffix eq ".jpg") {
|
|
|
|
|
$suffix = ".jpeg";
|
|
|
|
|
}
|
2018-08-28 20:53:10 -07:00
|
|
|
|
if ($backgroundColor) {
|
|
|
|
|
$conf .= "
|
|
|
|
|
background_color '$backgroundColor'
|
|
|
|
|
";
|
|
|
|
|
}
|
2020-07-02 13:18:49 -07:00
|
|
|
|
copy $splashImage, "$bootPath/background$suffix" or die "cannot copy $splashImage to $bootPath: $!\n";
|
2012-07-24 16:16:27 -07:00
|
|
|
|
$conf .= "
|
2018-05-13 16:51:50 -07:00
|
|
|
|
insmod " . substr($suffix, 1) . "
|
2018-09-02 05:34:55 -07:00
|
|
|
|
if background_image --mode '$splashMode' " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/background$suffix; then
|
2012-07-24 16:16:27 -07:00
|
|
|
|
set color_normal=white/black
|
|
|
|
|
set color_highlight=black/white
|
|
|
|
|
else
|
|
|
|
|
set menu_color_normal=cyan/blue
|
|
|
|
|
set menu_color_highlight=white/blue
|
|
|
|
|
fi
|
|
|
|
|
";
|
|
|
|
|
}
|
2020-07-04 20:16:25 -07:00
|
|
|
|
|
|
|
|
|
rmtree("$bootPath/theme") or die "cannot clean up theme folder in $bootPath\n" if -e "$bootPath/theme";
|
|
|
|
|
|
|
|
|
|
if ($theme) {
|
|
|
|
|
# Copy theme
|
|
|
|
|
rcopy($theme, "$bootPath/theme") or die "cannot copy $theme to $bootPath\n";
|
|
|
|
|
$conf .= "
|
|
|
|
|
# Sets theme.
|
|
|
|
|
set theme=" . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/theme.txt
|
|
|
|
|
export theme
|
|
|
|
|
# Load theme fonts, if any
|
|
|
|
|
";
|
|
|
|
|
|
|
|
|
|
find( { wanted => sub {
|
|
|
|
|
if ($_ =~ /\.pf2$/i) {
|
|
|
|
|
$font = File::Spec->abs2rel($File::Find::name, $theme);
|
|
|
|
|
$conf .= "
|
|
|
|
|
loadfont " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/theme/$font
|
|
|
|
|
";
|
|
|
|
|
}
|
|
|
|
|
}, no_chdir => 1 }, $theme );
|
|
|
|
|
}
|
2012-07-24 16:16:27 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$conf .= "$extraConfig\n";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Generate the menu entries.
|
|
|
|
|
$conf .= "\n";
|
|
|
|
|
|
|
|
|
|
my %copied;
|
2015-05-25 14:57:20 -07:00
|
|
|
|
mkpath("$bootPath/kernels", 0, 0755) if $copyKernels;
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
|
|
|
|
sub copyToKernelsDir {
|
|
|
|
|
my ($path) = @_;
|
2014-09-03 09:29:22 -07:00
|
|
|
|
return $grubStore->path . substr($path, length("/nix/store")) unless $copyKernels;
|
2012-07-24 16:16:27 -07:00
|
|
|
|
$path =~ /\/nix\/store\/(.*)/ or die;
|
|
|
|
|
my $name = $1; $name =~ s/\//-/g;
|
2015-05-25 14:57:20 -07:00
|
|
|
|
my $dst = "$bootPath/kernels/$name";
|
2012-07-24 16:16:27 -07:00
|
|
|
|
# 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 (! -e $dst) {
|
|
|
|
|
my $tmp = "$dst.tmp";
|
2020-07-02 13:18:49 -07:00
|
|
|
|
copy $path, $tmp or die "cannot copy $path to $tmp: $!\n";
|
|
|
|
|
rename $tmp, $dst or die "cannot rename $tmp to $dst: $!\n";
|
2012-07-24 16:16:27 -07:00
|
|
|
|
}
|
|
|
|
|
$copied{$dst} = 1;
|
2018-09-02 05:34:55 -07:00
|
|
|
|
return ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/kernels/$name";
|
2012-07-24 16:16:27 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub addEntry {
|
2019-07-21 09:39:07 -07:00
|
|
|
|
my ($name, $path, $options) = @_;
|
2012-07-24 16:16:27 -07:00
|
|
|
|
return unless -e "$path/kernel" && -e "$path/initrd";
|
|
|
|
|
|
2016-02-12 04:14:34 -08:00
|
|
|
|
my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel"));
|
|
|
|
|
my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd"));
|
2018-03-27 16:57:52 -07:00
|
|
|
|
|
|
|
|
|
# Include second initrd with secrets
|
|
|
|
|
if (-e -x "$path/append-initrd-secrets") {
|
|
|
|
|
my $initrdName = basename($initrd);
|
|
|
|
|
my $initrdSecretsPath = "$bootPath/kernels/$initrdName-secrets";
|
|
|
|
|
|
|
|
|
|
mkpath(dirname($initrdSecretsPath), 0, 0755);
|
|
|
|
|
my $oldUmask = umask;
|
|
|
|
|
# Make sure initrd is not world readable (won't work if /boot is FAT)
|
|
|
|
|
umask 0137;
|
|
|
|
|
my $initrdSecretsPathTemp = File::Temp::mktemp("$initrdSecretsPath.XXXXXXXX");
|
2020-07-02 13:18:49 -07:00
|
|
|
|
system("$path/append-initrd-secrets", $initrdSecretsPathTemp) == 0 or die "failed to create initrd secrets: $!\n";
|
2018-03-27 16:57:52 -07:00
|
|
|
|
# Check whether any secrets were actually added
|
|
|
|
|
if (-e $initrdSecretsPathTemp && ! -z _) {
|
2020-07-02 13:18:49 -07:00
|
|
|
|
rename $initrdSecretsPathTemp, $initrdSecretsPath or die "failed to move initrd secrets into place: $!\n";
|
2018-03-27 16:57:52 -07:00
|
|
|
|
$copied{$initrdSecretsPath} = 1;
|
|
|
|
|
$initrd .= " " . ($grubBoot->path eq "/" ? "" : $grubBoot->path) . "/kernels/$initrdName-secrets";
|
|
|
|
|
} else {
|
|
|
|
|
unlink $initrdSecretsPathTemp;
|
|
|
|
|
rmdir dirname($initrdSecretsPathTemp);
|
|
|
|
|
}
|
|
|
|
|
umask $oldUmask;
|
2017-02-18 14:30:24 -08:00
|
|
|
|
}
|
2018-03-27 16:57:52 -07:00
|
|
|
|
|
2016-02-12 04:14:34 -08:00
|
|
|
|
my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef;
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
|
|
|
|
# FIXME: $confName
|
|
|
|
|
|
|
|
|
|
my $kernelParams =
|
|
|
|
|
"systemConfig=" . Cwd::abs_path($path) . " " .
|
|
|
|
|
"init=" . Cwd::abs_path("$path/init") . " " .
|
2012-07-25 07:47:32 -07:00
|
|
|
|
readFile("$path/kernel-params");
|
|
|
|
|
my $xenParams = $xen && -e "$path/xen-params" ? readFile("$path/xen-params") : "";
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
|
|
|
|
if ($grubVersion == 1) {
|
|
|
|
|
$conf .= "title $name\n";
|
|
|
|
|
$conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig;
|
|
|
|
|
$conf .= " kernel $xen $xenParams\n" if $xen;
|
|
|
|
|
$conf .= " " . ($xen ? "module" : "kernel") . " $kernel $kernelParams\n";
|
|
|
|
|
$conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n\n";
|
|
|
|
|
} else {
|
2019-07-21 09:39:07 -07:00
|
|
|
|
$conf .= "menuentry \"$name\" " . ($options||"") . " {\n";
|
2014-08-31 09:18:13 -07:00
|
|
|
|
$conf .= $grubBoot->search . "\n";
|
2014-09-01 00:01:41 -07:00
|
|
|
|
if ($copyKernels == 0) {
|
|
|
|
|
$conf .= $grubStore->search . "\n";
|
|
|
|
|
}
|
2012-07-24 16:16:27 -07:00
|
|
|
|
$conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig;
|
|
|
|
|
$conf .= " multiboot $xen $xenParams\n" if $xen;
|
|
|
|
|
$conf .= " " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n";
|
|
|
|
|
$conf .= " " . ($xen ? "module" : "initrd") . " $initrd\n";
|
|
|
|
|
$conf .= "}\n\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Add default entries.
|
|
|
|
|
$conf .= "$extraEntries\n" if $extraEntriesBeforeNixOS;
|
|
|
|
|
|
2019-07-21 09:39:07 -07:00
|
|
|
|
addEntry("NixOS - Default", $defaultConfig, "--unrestricted");
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
|
|
|
|
$conf .= "$extraEntries\n" unless $extraEntriesBeforeNixOS;
|
|
|
|
|
|
2018-08-19 10:47:21 -07:00
|
|
|
|
# Find all the children of the current default configuration
|
|
|
|
|
# Do not search for grand children
|
2020-03-05 14:07:20 -08:00
|
|
|
|
my @links = sort (glob "$defaultConfig/specialisation/*");
|
2018-08-19 10:47:21 -07:00
|
|
|
|
foreach my $link (@links) {
|
|
|
|
|
|
|
|
|
|
my $entryName = "";
|
|
|
|
|
|
|
|
|
|
my $cfgName = readFile("$link/configuration-name");
|
|
|
|
|
|
|
|
|
|
my $date = strftime("%F", localtime(lstat($link)->mtime));
|
|
|
|
|
my $version =
|
|
|
|
|
-e "$link/nixos-version"
|
|
|
|
|
? readFile("$link/nixos-version")
|
|
|
|
|
: basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]);
|
|
|
|
|
|
|
|
|
|
if ($cfgName) {
|
|
|
|
|
$entryName = $cfgName;
|
|
|
|
|
} else {
|
2020-03-05 14:07:20 -08:00
|
|
|
|
my $linkname = basename($link);
|
|
|
|
|
$entryName = "($linkname - $date - $version)";
|
2018-08-19 10:47:21 -07:00
|
|
|
|
}
|
|
|
|
|
addEntry("NixOS - $entryName", $link);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-02 08:46:45 -08:00
|
|
|
|
my $grubBootPath = $grubBoot->path;
|
2012-12-16 12:41:47 -08:00
|
|
|
|
# extraEntries could refer to @bootRoot@, which we have to substitute
|
2014-12-02 08:46:45 -08:00
|
|
|
|
$conf =~ s/\@bootRoot\@/$grubBootPath/g;
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
2013-10-09 10:13:26 -07:00
|
|
|
|
# Emit submenus for all system profiles.
|
|
|
|
|
sub addProfile {
|
|
|
|
|
my ($profile, $description) = @_;
|
|
|
|
|
|
|
|
|
|
# Add entries for all generations of this profile.
|
|
|
|
|
$conf .= "submenu \"$description\" {\n" if $grubVersion == 2;
|
|
|
|
|
|
|
|
|
|
sub nrFromGen { my ($x) = @_; $x =~ /\/\w+-(\d+)-link/; return $1; }
|
|
|
|
|
|
|
|
|
|
my @links = sort
|
|
|
|
|
{ nrFromGen($b) <=> nrFromGen($a) }
|
|
|
|
|
(glob "$profile-*-link");
|
|
|
|
|
|
|
|
|
|
my $curEntry = 0;
|
|
|
|
|
foreach my $link (@links) {
|
|
|
|
|
last if $curEntry++ >= $configurationLimit;
|
2015-12-30 03:53:36 -08:00
|
|
|
|
if (! -e "$link/nixos-version") {
|
|
|
|
|
warn "skipping corrupt system profile entry ‘$link’\n";
|
|
|
|
|
next;
|
|
|
|
|
}
|
2013-10-09 10:13:26 -07:00
|
|
|
|
my $date = strftime("%F", localtime(lstat($link)->mtime));
|
|
|
|
|
my $version =
|
|
|
|
|
-e "$link/nixos-version"
|
|
|
|
|
? readFile("$link/nixos-version")
|
|
|
|
|
: basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]);
|
|
|
|
|
addEntry("NixOS - Configuration " . nrFromGen($link) . " ($date - $version)", $link);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$conf .= "}\n" if $grubVersion == 2;
|
2012-07-24 16:16:27 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-09 10:13:26 -07:00
|
|
|
|
addProfile "/nix/var/nix/profiles/system", "NixOS - All configurations";
|
|
|
|
|
|
|
|
|
|
if ($grubVersion == 2) {
|
|
|
|
|
for my $profile (glob "/nix/var/nix/profiles/system-profiles/*") {
|
|
|
|
|
my $name = basename($profile);
|
|
|
|
|
next unless $name =~ /^\w+$/;
|
|
|
|
|
addProfile $profile, "NixOS - Profile '$name'";
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
2020-01-12 19:39:03 -08:00
|
|
|
|
# extraPrepareConfig could refer to @bootPath@, which we have to substitute
|
|
|
|
|
$extraPrepareConfig =~ s/\@bootPath\@/$bootPath/g;
|
|
|
|
|
|
2012-12-16 12:41:47 -08:00
|
|
|
|
# Run extraPrepareConfig in sh
|
|
|
|
|
if ($extraPrepareConfig ne "") {
|
|
|
|
|
system((get("shell"), "-c", $extraPrepareConfig));
|
|
|
|
|
}
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
2017-02-13 05:53:15 -08:00
|
|
|
|
# write the GRUB config.
|
2015-05-25 14:57:20 -07:00
|
|
|
|
my $confFile = $grubVersion == 1 ? "$bootPath/grub/menu.lst" : "$bootPath/grub/grub.cfg";
|
2012-07-24 16:16:27 -07:00
|
|
|
|
my $tmpFile = $confFile . ".tmp";
|
2012-07-25 07:47:32 -07:00
|
|
|
|
writeFile($tmpFile, $conf);
|
2017-02-13 05:53:15 -08:00
|
|
|
|
|
2017-03-22 07:40:22 -07:00
|
|
|
|
|
|
|
|
|
# check whether to install GRUB EFI or not
|
|
|
|
|
sub getEfiTarget {
|
|
|
|
|
if ($grubVersion == 1) {
|
|
|
|
|
return "no"
|
|
|
|
|
} elsif (($grub ne "") && ($grubEfi ne "")) {
|
|
|
|
|
# EFI can only be installed when target is set;
|
|
|
|
|
# A target is also required then for non-EFI grub
|
|
|
|
|
if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die }
|
|
|
|
|
else { return "both" }
|
|
|
|
|
} elsif (($grub ne "") && ($grubEfi eq "")) {
|
|
|
|
|
# TODO: It would be safer to disallow non-EFI grub installation if no taget is given.
|
|
|
|
|
# If no target is given, then grub auto-detects the target which can lead to errors.
|
|
|
|
|
# E.g. it seems as if grub would auto-detect a EFI target based on the availability
|
|
|
|
|
# of a EFI partition.
|
|
|
|
|
# However, it seems as auto-detection is currently relied on for non-x86_64 and non-i386
|
|
|
|
|
# architectures in NixOS. That would have to be fixed in the nixos modules first.
|
|
|
|
|
return "no"
|
|
|
|
|
} elsif (($grub eq "") && ($grubEfi ne "")) {
|
|
|
|
|
# EFI can only be installed when target is set;
|
|
|
|
|
if ($grubTargetEfi eq "") { die }
|
|
|
|
|
else {return "only" }
|
|
|
|
|
} else {
|
|
|
|
|
# prevent an installation if neither grub nor grubEfi is given
|
|
|
|
|
return "neither"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $efiTarget = getEfiTarget();
|
|
|
|
|
|
2017-02-13 05:53:15 -08:00
|
|
|
|
# Append entries detected by os-prober
|
2017-02-13 05:54:30 -08:00
|
|
|
|
if (get("useOSProber") eq "true") {
|
2017-03-22 07:40:22 -07:00
|
|
|
|
my $targetpackage = ($efiTarget eq "no") ? $grub : $grubEfi;
|
|
|
|
|
system(get("shell"), "-c", "pkgdatadir=$targetpackage/share/grub $targetpackage/etc/grub.d/30_os-prober >> $tmpFile");
|
2017-02-13 05:53:15 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Atomically switch to the new config
|
2020-07-02 13:18:49 -07:00
|
|
|
|
rename $tmpFile, $confFile or die "cannot rename $tmpFile to $confFile: $!\n";
|
2012-07-24 16:16:27 -07:00
|
|
|
|
|
|
|
|
|
|
2015-05-25 14:57:20 -07:00
|
|
|
|
# Remove obsolete files from $bootPath/kernels.
|
|
|
|
|
foreach my $fn (glob "$bootPath/kernels/*") {
|
2012-07-24 16:16:27 -07:00
|
|
|
|
next if defined $copied{$fn};
|
|
|
|
|
print STDERR "removing obsolete file $fn\n";
|
|
|
|
|
unlink $fn;
|
|
|
|
|
}
|
2012-07-25 07:47:32 -07:00
|
|
|
|
|
|
|
|
|
|
2015-01-14 01:30:57 -08:00
|
|
|
|
#
|
|
|
|
|
# Install GRUB if the parameters changed from the last time we installed it.
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
struct(GrubState => {
|
2015-07-05 09:54:35 -07:00
|
|
|
|
name => '$',
|
2015-01-14 01:30:57 -08:00
|
|
|
|
version => '$',
|
|
|
|
|
efi => '$',
|
|
|
|
|
devices => '$',
|
|
|
|
|
efiMountPoint => '$',
|
2020-04-23 13:44:21 -07:00
|
|
|
|
extraGrubInstallArgs => '@',
|
2015-01-14 01:30:57 -08:00
|
|
|
|
});
|
2020-04-23 13:44:21 -07:00
|
|
|
|
# If you add something to the state file, only add it to the end
|
|
|
|
|
# because it is read line-by-line.
|
2015-01-14 01:30:57 -08:00
|
|
|
|
sub readGrubState {
|
2020-04-23 13:44:21 -07:00
|
|
|
|
my $defaultGrubState = GrubState->new(name => "", version => "", efi => "", devices => "", efiMountPoint => "", extraGrubInstallArgs => () );
|
2015-05-25 14:57:20 -07:00
|
|
|
|
open FILE, "<$bootPath/grub/state" or return $defaultGrubState;
|
2015-01-14 01:30:57 -08:00
|
|
|
|
local $/ = "\n";
|
2015-07-05 09:54:35 -07:00
|
|
|
|
my $name = <FILE>;
|
|
|
|
|
chomp($name);
|
2015-01-14 01:30:57 -08:00
|
|
|
|
my $version = <FILE>;
|
|
|
|
|
chomp($version);
|
|
|
|
|
my $efi = <FILE>;
|
|
|
|
|
chomp($efi);
|
|
|
|
|
my $devices = <FILE>;
|
|
|
|
|
chomp($devices);
|
|
|
|
|
my $efiMountPoint = <FILE>;
|
|
|
|
|
chomp($efiMountPoint);
|
2020-04-23 13:44:21 -07:00
|
|
|
|
# Historically, arguments in the state file were one per each line, but that
|
|
|
|
|
# gets really messy when newlines are involved, structured arguments
|
|
|
|
|
# like lists are needed (they have to have a separator encoding), or even worse,
|
|
|
|
|
# when we need to remove a setting in the future. Thus, the 6th line is a JSON
|
|
|
|
|
# object that can store structured data, with named keys, and all new state
|
|
|
|
|
# should go in there.
|
|
|
|
|
my $jsonStateLine = <FILE>;
|
|
|
|
|
# For historical reasons we do not check the values above for un-definedness
|
|
|
|
|
# (that is, when the state file has too few lines and EOF is reached),
|
|
|
|
|
# because the above come from the first version of this logic and are thus
|
|
|
|
|
# guaranteed to be present.
|
|
|
|
|
$jsonStateLine = defined $jsonStateLine ? $jsonStateLine : '{}'; # empty JSON object
|
|
|
|
|
chomp($jsonStateLine);
|
2020-07-06 15:53:55 -07:00
|
|
|
|
if ($jsonStateLine eq "") {
|
|
|
|
|
$jsonStateLine = '{}'; # empty JSON object
|
|
|
|
|
}
|
2020-04-23 13:44:21 -07:00
|
|
|
|
my %jsonState = %{decode_json($jsonStateLine)};
|
2020-07-06 15:53:55 -07:00
|
|
|
|
my @extraGrubInstallArgs = exists($jsonState{'extraGrubInstallArgs'}) ? @{$jsonState{'extraGrubInstallArgs'}} : ();
|
2015-01-14 01:30:57 -08:00
|
|
|
|
close FILE;
|
2020-04-23 13:44:21 -07:00
|
|
|
|
my $grubState = GrubState->new(name => $name, version => $version, efi => $efi, devices => $devices, efiMountPoint => $efiMountPoint, extraGrubInstallArgs => \@extraGrubInstallArgs );
|
2015-01-14 01:30:57 -08:00
|
|
|
|
return $grubState
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-23 13:42:11 -07:00
|
|
|
|
my @deviceTargets = getList('devices');
|
2015-01-14 01:30:57 -08:00
|
|
|
|
my $prevGrubState = readGrubState();
|
2016-07-05 14:40:35 -07:00
|
|
|
|
my @prevDeviceTargets = split/,/, $prevGrubState->devices;
|
2020-04-23 13:44:21 -07:00
|
|
|
|
my @extraGrubInstallArgs = getList('extraGrubInstallArgs');
|
2020-07-12 17:16:43 -07:00
|
|
|
|
my @prevExtraGrubInstallArgs = @{$prevGrubState->extraGrubInstallArgs};
|
2015-01-14 01:30:57 -08:00
|
|
|
|
|
2015-07-05 11:54:36 -07:00
|
|
|
|
my $devicesDiffer = scalar (List::Compare->new( '-u', '-a', \@deviceTargets, \@prevDeviceTargets)->get_symmetric_difference());
|
2020-04-23 13:44:21 -07:00
|
|
|
|
my $extraGrubInstallArgsDiffer = scalar (List::Compare->new( '-u', '-a', \@extraGrubInstallArgs, \@prevExtraGrubInstallArgs)->get_symmetric_difference());
|
2015-07-05 11:54:36 -07:00
|
|
|
|
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;
|
2016-08-16 04:51:58 -07:00
|
|
|
|
if (($ENV{'NIXOS_INSTALL_GRUB'} // "") eq "1") {
|
|
|
|
|
warn "NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER";
|
|
|
|
|
$ENV{'NIXOS_INSTALL_BOOTLOADER'} = "1";
|
|
|
|
|
}
|
2020-04-23 13:44:21 -07:00
|
|
|
|
my $requireNewInstall = $devicesDiffer || $extraGrubInstallArgsDiffer || $nameDiffer || $versionDiffer || $efiDiffer || $efiMountPointDiffer || (($ENV{'NIXOS_INSTALL_BOOTLOADER'} // "") eq "1");
|
2015-01-14 01:30:57 -08:00
|
|
|
|
|
2015-07-05 18:34:45 -07:00
|
|
|
|
# install a symlink so that grub can detect the boot drive
|
2020-07-02 13:18:49 -07:00
|
|
|
|
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: $!";
|
2015-01-14 01:30:57 -08:00
|
|
|
|
|
|
|
|
|
# install non-EFI GRUB
|
|
|
|
|
if (($requireNewInstall != 0) && ($efiTarget eq "no" || $efiTarget eq "both")) {
|
|
|
|
|
foreach my $dev (@deviceTargets) {
|
2012-07-25 07:47:32 -07:00
|
|
|
|
next if $dev eq "nodev";
|
|
|
|
|
print STDERR "installing the GRUB $grubVersion boot loader on $dev...\n";
|
2020-04-23 13:44:21 -07:00
|
|
|
|
my @command = ("$grub/sbin/grub-install", "--recheck", "--root-directory=$tmpDir", Cwd::abs_path($dev), @extraGrubInstallArgs);
|
2016-10-08 20:59:42 -07:00
|
|
|
|
if ($forceInstall eq "true") {
|
|
|
|
|
push @command, "--force";
|
|
|
|
|
}
|
|
|
|
|
if ($grubTarget ne "") {
|
|
|
|
|
push @command, "--target=$grubTarget";
|
2015-01-14 01:30:57 -08:00
|
|
|
|
}
|
2020-07-02 13:18:49 -07:00
|
|
|
|
(system @command) == 0 or die "$0: installation of GRUB on $dev failed: $!\n";
|
2012-07-25 07:47:32 -07:00
|
|
|
|
}
|
2015-01-14 01:30:57 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# install EFI GRUB
|
|
|
|
|
if (($requireNewInstall != 0) && ($efiTarget eq "only" || $efiTarget eq "both")) {
|
|
|
|
|
print STDERR "installing the GRUB $grubVersion EFI boot loader into $efiSysMountPoint...\n";
|
2020-04-23 13:44:21 -07:00
|
|
|
|
my @command = ("$grubEfi/sbin/grub-install", "--recheck", "--target=$grubTargetEfi", "--boot-directory=$bootPath", "--efi-directory=$efiSysMountPoint", @extraGrubInstallArgs);
|
2016-10-08 20:59:42 -07:00
|
|
|
|
if ($forceInstall eq "true") {
|
|
|
|
|
push @command, "--force";
|
|
|
|
|
}
|
2015-01-14 01:30:57 -08:00
|
|
|
|
if ($canTouchEfiVariables eq "true") {
|
2016-09-13 10:46:53 -07:00
|
|
|
|
push @command, "--bootloader-id=$bootloaderId";
|
2015-01-14 01:30:57 -08:00
|
|
|
|
} else {
|
2016-09-13 10:46:53 -07:00
|
|
|
|
push @command, "--no-nvram";
|
|
|
|
|
push @command, "--removable" if $efiInstallAsRemovable eq "true";
|
2015-01-14 01:30:57 -08:00
|
|
|
|
}
|
2016-09-13 10:46:53 -07:00
|
|
|
|
|
2020-07-02 13:18:49 -07:00
|
|
|
|
(system @command) == 0 or die "$0: installation of GRUB EFI into $efiSysMountPoint failed: $!\n";
|
2015-01-14 01:30:57 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# update GRUB state file
|
|
|
|
|
if ($requireNewInstall != 0) {
|
2020-04-23 15:20:56 -07:00
|
|
|
|
# Temp file for atomic rename.
|
|
|
|
|
my $stateFile = "$bootPath/grub/state";
|
|
|
|
|
my $stateFileTmp = $stateFile . ".tmp";
|
|
|
|
|
|
|
|
|
|
open FILE, ">$stateFileTmp" or die "cannot create $stateFileTmp: $!\n";
|
2015-07-05 09:54:35 -07:00
|
|
|
|
print FILE get("fullName"), "\n" or die;
|
2015-01-14 01:30:57 -08:00
|
|
|
|
print FILE get("fullVersion"), "\n" or die;
|
|
|
|
|
print FILE $efiTarget, "\n" or die;
|
2016-07-05 14:40:35 -07:00
|
|
|
|
print FILE join( ",", @deviceTargets ), "\n" or die;
|
2015-01-14 01:30:57 -08:00
|
|
|
|
print FILE $efiSysMountPoint, "\n" or die;
|
2020-04-23 13:44:21 -07:00
|
|
|
|
my %jsonState = (
|
|
|
|
|
extraGrubInstallArgs => \@extraGrubInstallArgs
|
|
|
|
|
);
|
|
|
|
|
my $jsonStateLine = encode_json(\%jsonState);
|
|
|
|
|
print FILE $jsonStateLine, "\n" or die;
|
2015-01-14 01:30:57 -08:00
|
|
|
|
close FILE or die;
|
2020-04-23 15:20:56 -07:00
|
|
|
|
|
|
|
|
|
# Atomically switch to the new state file
|
2020-07-02 13:18:49 -07:00
|
|
|
|
rename $stateFileTmp, $stateFile or die "cannot rename $stateFileTmp to $stateFile: $!\n";
|
2012-07-25 07:47:32 -07:00
|
|
|
|
}
|