summaryrefslogtreecommitdiffstats
path: root/perl-install/bootloader.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perl-install/bootloader.pm')
-rw-r--r--perl-install/bootloader.pm1537
1 files changed, 1204 insertions, 333 deletions
diff --git a/perl-install/bootloader.pm b/perl-install/bootloader.pm
index ade21a222..d1d757c3d 100644
--- a/perl-install/bootloader.pm
+++ b/perl-install/bootloader.pm
@@ -1,4 +1,4 @@
-package bootloader; # $Id$
+package bootloader;
use diagnostics;
use strict;
@@ -19,11 +19,73 @@ use partition_table::raw;
use run_program;
use modules;
-#-#####################################################################################
-#- Functions
-#-#####################################################################################
-my $vmlinuz_regexp = 'vmlinuz|win4lin';
+=head1 SYNOPSYS
+
+B<bootloader> enables to configure various boot loaders (LILO, GRUB Legacy, GRUB2, ...)
+
+Example of usage:
+
+ $all_hds = fsedit::get_hds();
+ fs::get_raw_hds('', $all_hds);
+ fs::get_info_from_fstab($all_hds);
+ $fstab = [ fs::get::fstab($all_hds) ];
+ $bootloader = bootloader::read($all_hds);
+ (...)
+ bootloader::action($bootloader, 'write', $all_hds);
+
+A particular bootloader is handled in separate typical functions that can be called by action() by /sbin/bootloader-config:
+
+
+=head1 VFS aka how to support a bootloader
+
+Most of the code is generic.
+
+A particular bootloader implementation/support is done through a couple functions:
+
+=over 4
+
+=item * read_FOOBAR()
+
+read the config file(s) of the FOOBAR bootloader, returning a $bootloader object
+
+=item * install_FOOBAR($bootloader, $all_hds)
+
+usually call write_FOOBAR() & install_raw_FOOBAR() if existing, aka:
+
+=over 4
+
+=item * updating config files
+
+=item * then reinstalling the bootloader
+
+=back
+
+=item * install_raw_FOOBAR()
+
+Optionnal: Low level installation of the stage1 of the bootloader, usually embedding it in MBR or on ESP, ... (eg: running lilo or /boot/grub*/install.sh)
+
+=item * write_FOOBAR($bootloader, $all_hds)
+
+update the config files, ie when a kernel was installed or removed.
+when_config_changed_FOOBAR() is called right after in case something is needed when config is done (eg: lilo needs to be reinstalled)
+
+=item * when_config_changed_FOOBAR()
+
+Run needed action when config has changed or when initrds were regenerated, ...
+Usually nothing for intelligent bootloaders such as grub-legacy or grub2.
+But eg: lilo needs to rewrite its stage1 (thus it needs to rerun lilo).
+
+=back
+
+=head1 Functions
+
+=over
+
+=cut
+
+my $vmlinuz_regexp = 'vmlinu[xz]|win4lin|uImage';
my $decompose_vmlinuz_name = qr/((?:$vmlinuz_regexp).*?)-(\d+\.\d+.*)/;
+my $liblinux_path = '/usr/lib/linux';
sub expand_vmlinuz_symlink {
my ($vmlinuz) = @_;
@@ -43,21 +105,21 @@ sub vmlinuz2kernel_str {
{
basename => $basename,
version => $version,
- $version =~ /(.*)-(\D.*)-(\d+(mdk|mdv|mnb))$/ ? #- eg: 2.6.22.5-server-1mdv
+ $version =~ /([\d.]*)-(\D.*)-((\d+|0\.rc\d+.*)\.mga.*)$/ ? #- eg: 3.0.0-1.mga2
(ext => $2, version_no_ext => "$1-$3") :
- $version =~ /(.*md[kv])-?(.*)/ ? #- (old) eg: 2.6.17-13mdventerprise
+ $version =~ /(.*mga)-?(.*)/ ? #- (old) eg: 2.6.17-13mdventerprise
(ext => $2, version_no_ext => $1) : (version_no_ext => $version),
};
}
sub kernel_str2short_name {
my ($kernel) = @_;
- $kernel->{ext} =~ /^xen/ ? 'xen' : $kernel->{basename};
+ $kernel->{basename};
}
sub basename2initrd_basename {
my ($basename) = @_;
- $basename =~ s!vmlinuz-?!!; #- here we do not use $vmlinuz_regexp since we explictly want to keep all that is not "vmlinuz"
+ $basename =~ s!(vmlinu[zx]|uImage)-?!!; #- here we do not use $vmlinuz_regexp since we explicitly want to keep all that is not "vmlinuz"
'initrd' . ($basename ? "-$basename" : '');
}
sub kernel_str2vmlinuz_long {
@@ -68,6 +130,10 @@ sub kernel_str2initrd_long {
my ($kernel) = @_;
basename2initrd_basename(kernel_str2short_name($kernel)) . '-' . $kernel->{version} . '.img';
}
+sub kernel_str2liblinux_long {
+ my ($kernel) = @_;
+ $liblinux_path . '-' . $kernel->{version};
+}
sub kernel_str2vmlinuz_short {
my ($kernel) = @_;
if ($kernel->{use_long_name}) {
@@ -84,6 +150,9 @@ sub kernel_str2initrd_short {
basename2initrd_basename(kernel_str2short_name($kernel)) . '.img';
}
}
+sub kernel_str2liblinux_short() {
+ $liblinux_path;
+}
sub kernel_str2label {
my ($kernel, $o_use_long_name) = @_;
@@ -91,7 +160,7 @@ sub kernel_str2label {
_sanitize_ver($kernel);
} else {
my $short_name = kernel_str2short_name($kernel);
- $short_name eq 'vmlinuz' ? 'linux' : $short_name;
+ $kernel->{ext} =~ /^xen/ ? 'xen' : ($short_name eq 'vmlinuz' ? 'linux' : $short_name);
}
}
@@ -106,21 +175,44 @@ sub get_label {
undef;
}
+=item mkinitrd($kernel_version, $bootloader, $entry, $initrd)
+
+Regenerates kernel's initrd.
+
+=cut
+
sub mkinitrd {
my ($kernel_version, $bootloader, $entry, $initrd) = @_;
- $::testing || -e "$::prefix/$initrd" and return $initrd;
-
+ my $dir = dirname($initrd);
+ if ($::testing) {
+ log::l("Skipping initrd generation: testing mode");
+ return $initrd;
+ } elsif (-e "$::prefix/$initrd") {
+ log::l("Skipping initrd generation: already exists");
+ return $initrd;
+ } elsif ($initrd =~ /\(hd/) {
+ log::l("Skipping initrd generation: unrecognized partition");
+ return $initrd;
+ } elsif (!-d "$::prefix/$dir") {
+ log::l("Skipping initrd generation: dir doesn't exist (probably !mounted foreign part)");
+ return $initrd;
+ }
+
+ # for /boot on dos partitions when installing on loopback file on dos partition
my $loop_boot = fs::loopback::prepare_boot();
modules::load('loop');
my @options = (
- if_($::isInstall, "-v"), "-f", $initrd, "--ifneeded", $kernel_version,
+ if_($::isInstall, "-v"), "-f", $initrd, $kernel_version,
if_($entry->{initrd_options}, split(' ', $entry->{initrd_options})),
);
- if (!run_program::rooted($::prefix, 'mkinitrd', @options)) {
+
+ my $err;
+ if (!run_program::rooted($::prefix, 'mkinitrd', '2>', \$err, @options)) {
unlink("$::prefix/$initrd");
- die "mkinitrd failed:\n(mkinitrd @options)";
+ log::explanations("mkinitrd failed:\n(mkinitrd @options)\nError: <$err>");
+ die "mkinitrd failed:\n(mkinitrd @options)\nError: $err";
}
add_boot_splash($initrd, $entry->{vga} || $bootloader->{vga});
@@ -129,6 +221,13 @@ sub mkinitrd {
-e "$::prefix/$initrd" && $initrd;
}
+=item rebuild_initrd($kernel_version, $bootloader, $entry, $initrd)
+
+Saves the old initrd then regenerate it.
+If it fails, restore the old initrd.
+
+=cut
+
sub rebuild_initrd {
my ($kernel_version, $bootloader, $entry, $initrd) = @_;
@@ -160,11 +259,25 @@ sub add_boot_splash {
sub update_splash {
my ($bootloader) = @_;
+ my %real_initrd_entries;
foreach (@{$bootloader->{entries}}) {
- bootloader::add_boot_splash($_->{initrd}, $_->{vga} || $bootloader->{vga}) if $_->{initrd};
+ if ($_->{initrd} && $_->{vga}) {
+ my $initrd = expand_symlinks($_->{initrd});
+ $real_initrd_entries{$initrd} = $_;
+ }
+ }
+ foreach (values %real_initrd_entries) {
+ log::l("add boot splash to $_->{initrd}\n");
+ add_boot_splash($_->{initrd}, $_->{vga} || $bootloader->{vga});
}
}
+=item read($all_hds)
+
+Reads bootloader config by calling the proper read_XYZ function.
+
+=cut
+
sub read {
my ($all_hds) = @_;
my $fstab = [ fs::get::fstab($all_hds) ];
@@ -172,8 +285,9 @@ sub read {
my $f = $bootloader::{"read_$main_method"} or die "unknown bootloader method $main_method (read)";
my $bootloader = $f->($fstab);
- cleanup_entries($bootloader);
+ cleanup_entries($bootloader) if $main_method ne 'refind';
+ # handle raid-extra-boot (lilo)
my @devs = $bootloader->{boot};
if ($bootloader->{'raid-extra-boot'} =~ /mbr/ &&
(my $md = fs::get::device2part($bootloader->{boot}, $all_hds->{raids}))) {
@@ -182,11 +296,21 @@ sub read {
@devs = split(',', $bootloader->{'raid-extra-boot'});
}
+ #- if $bootloader->{boot} is undefined, @devs contains a single undef element
my ($type) = map {
- if (m!/fd\d+$!) {
+ if (is_uefi()) {
+ if (-e "$::prefix/boot/refind_linux.conf") {
+ 'refind';
+ } elsif (-f "$::prefix/boot/EFI/EFI/mageia/grub" . uefi_type() . ".efi" ||
+ $main_method eq 'grub2' && $bootloader->{removable} && -f "$::prefix/boot/EFI/EFI/BOOT/BOOT" . uc(uefi_type()) . ".EFI") {
+ 'grub2';
+ } else {
+ ()
+ }
+ } elsif (m!/fd\d+$!) {
warn "not checking the method on floppy, assuming $main_method is right\n";
$main_method;
- } elsif (member($main_method, qw(yaboot cromwell silo))) {
+ } elsif (member($main_method, qw(cromwell uboot))) {
#- not checking, there's only one bootloader anyway :)
$main_method;
} elsif (my $type = partition_table::raw::typeOfMBR($_)) {
@@ -196,20 +320,114 @@ sub read {
} @devs;
if ($type eq $main_method) {
+ return $bootloader if read_($bootloader);
+ }
+ }
+}
+
+sub read_ {
+ my ($bootloader) = @_;
my @prefered_entries = map { get_label($_, $bootloader) } $bootloader->{default}, 'linux';
if (my $default = find { $_ && $_->{type} eq 'image' } (@prefered_entries, @{$bootloader->{entries}})) {
$bootloader->{default_options} = $default;
$bootloader->{perImageAppend} ||= $default->{append};
log::l("perImageAppend is now $bootloader->{perImageAppend}");
+ $bootloader->{default_vga} ||= $default->{vga};
+ log::l("default_vga is now $bootloader->{default_vga}");
} else {
$bootloader->{default_options} = {};
}
return $bootloader;
+}
+
+
+=item is_grub2_already_crypted($password)
+
+Returns whether grub2 password is already encrypted or not
+
+=cut
+
+sub is_grub2_already_crypted {
+ my ($password) = @_;
+ $password =~ /grub.pbkdf2.sha512/;
+}
+
+=item read_grub2 ($o_fstab)
+
+Read back GRUB2 config
+
+=cut
+
+sub read_grub2() {
+ my %bootloader = read_grub2_install_sh() if -e get_grub2_install_sh();
+ return if is_empty_hash_ref(\%bootloader) & !-s "$::prefix/boot/grub2/grub.cfg";
+ my %h = getVarsFromSh("$::prefix/etc/default/grub");
+ $bootloader{timeout} = $h{GRUB_TIMEOUT};
+ # keep suggested perImageAppend and default_vga on first run (during installer) or when migrating from grub-legacy or lilo:
+ my ($vga, $other) = partition { /^vga=/ } split(' ', $h{GRUB_CMDLINE_LINUX_DEFAULT});
+ $bootloader{perImageAppend} ||= join(' ', @$other) if @$other;
+ $bootloader{default_vga} ||= $vga->[0] =~ /vga=(.*)/ && $1 if @$vga;
+ $bootloader{entries} = [];
+ my $entry;
+ my $f = "$::prefix/boot/grub2/grub.cfg";
+ my @menus;
+ foreach (cat_utf8($f)) {
+ next if /^#/;
+ if (/menuentry\s+['"]([^']+)["']/) {
+ $entry = { label => $1, real_label => join('>', @menus, $1) };
+ } elsif (/linux(?:16)?\s+([^-]\S+)\s+(.*)?/ || /module\s+(\S+vmlinu\S+)\s+(.*)?/) {
+ $entry->{type} = 'image';
+ @$entry{qw(kernel_or_dev append)} = ($1, $2);
+ my ($vga, $other) = partition { /^vga=/ } split(' ', $entry->{append});
+ if (@$vga) {
+ $entry->{vga} = $vga->[0] =~ /vga=(.*)/ && $1;
+ $entry->{append} = join(' ', @$other);
+ }
+ } elsif (/initrd(?:16)?\s+(\S+)/ || /module\s+(\S+initrd\S+)\s+(.*)?/) {
+ $entry->{initrd} = $1;
+ } elsif (/submenu\s+['"]([^']+)["']/) {
+ push @menus, $1;
+ } elsif (/^\s*}/) {
+ if ($entry) {
+ push @{$bootloader{entries}}, $entry;
+ undef $entry;
+ } else {
+ pop @menus;
+ }
}
}
+
+ # get default entry:
+ foreach (run_program::rooted_get_stdout($::prefix, qw(grub2-editenv list))) {
+ $bootloader{default} = $1 if /saved_entry=(.*)/;
+ $bootloader{default} =~ s/.*>//; # strip full menu entry path
+ }
+
+ # Get password prior to run update-grub2:
+ $bootloader{password} = { getVarsFromSh(get_grub2_users()) }->{GRUB2_PASSWORD};
+
+ $bootloader{method} = cat_($f) =~ /set theme=.*maggy/ ? 'grub2-graphic' : 'grub2';
+ \%bootloader;
+}
+
+sub read_grub2_install_sh() {
+ my $s = cat_(get_grub2_install_sh());
+ my %h;
+ if ($s =~ m!(/dev/\S+)!m) {
+ $h{boot} = $1;
+ }
+ $h{no_esp_or_mbr} = $s =~ m!--grub-setup=/bin/true!;
+ $h{removable} = $s =~ m!--removable!;
+ %h;
}
+=item read_grub($fstab)
+
+Reads back Grub Legacy config.
+
+=cut
+
sub read_grub {
my ($fstab) = @_;
@@ -226,8 +444,27 @@ sub read_grub {
$bootloader;
}
+
+=item _may_fix_grub2dev($fstab, $grub2dev, $boot_part)
+
+Adapts device.map (aka $grub2dev) when for example hda is now sda.
+nb:
+
+=over 4
+
+=item * $boot_part comes from C</boot/grub/install.sh> C<root (hd...)> line
+
+=item * $grub2dev is C</boot/grub/device.map>
+
+=back
+
+=cut
+
sub _may_fix_grub2dev {
my ($fstab, $grub2dev, $boot_part) = @_;
+
+ $boot_part or log::l("install.sh does not contain 'root (hd...)' line, no way to magically adapt device.map"), return;
+
my $real_boot_part = fs::get::root_($fstab, 'boot') or
log::l("argh... the fstab given is useless, it doesn't contain '/'"), return;
@@ -239,7 +476,7 @@ sub _may_fix_grub2dev {
log::l("WARNING: we have detected that device.map is inconsistent with the system");
- my ($hd_grub, undef, undef) = parse_grub_file($boot_part);
+ my ($hd_grub, undef, undef) = parse_grub_file($boot_part); # extract hdX
if (my $prev_hd_grub = find { $grub2dev->{$_} eq $real_boot_dev } keys %$grub2dev) {
$grub2dev->{$prev_hd_grub} = $grub2dev->{$hd_grub};
log::l("swapping result: $hd_grub/$real_boot_dev and $prev_hd_grub/$grub2dev->{$hd_grub}");
@@ -249,6 +486,12 @@ sub _may_fix_grub2dev {
$grub2dev->{$hd_grub} = $real_boot_dev;
}
+=item read_grub_install_sh() {
+
+Reads "config" from /boot/grub/install.sh (mainly used partitions)
+
+=cut
+
sub read_grub_install_sh() {
my $s = cat_("$::prefix/boot/grub/install.sh");
my %h;
@@ -265,15 +508,14 @@ sub read_grub_install_sh() {
\%h;
}
-sub read_grub_menu_lst {
- my ($fstab, $grub2dev) = @_;
+sub _parse_grub_menu_lst() {
my $global = 1;
my ($e, %b);
my $menu_lst_file = "$::prefix/boot/grub/menu.lst";
-e $menu_lst_file or return;
- foreach (cat_($menu_lst_file)) {
+ foreach (MDK::Common::File::cat_utf8($menu_lst_file)) {
my $verbatim = $_;
chomp;
s/^\s*//; s/\s*$//;
@@ -290,21 +532,17 @@ sub read_grub_menu_lst {
push @{$b{entries}}, $e = { label => $v };
$global = 0;
} elsif ($global) {
- $b{$keyword} = $v eq '' ? 1 : grub2file($v, $grub2dev, $fstab, \%b);
+ $b{$keyword} = $v;
} else {
if ($keyword eq 'kernel') {
$e->{type} = 'image';
$e->{kernel} = $v;
} elsif ($keyword eq 'chainloader') {
$e->{type} = 'other';
- $e->{kernel_or_dev} = grub2dev($e->{rootnoverify} || $e->{grub_root}, $grub2dev);
$e->{append} = "";
} elsif ($keyword eq 'configfile') {
$e->{type} = 'grub_configfile';
- $e->{kernel_or_dev} = grub2dev($e->{rootnoverify} || $e->{grub_root}, $grub2dev);
$e->{configfile} = $v;
- } elsif ($keyword eq 'initrd') {
- $e->{initrd} = grub2file($v, $grub2dev, $fstab, $e);
} elsif ($keyword eq 'map') {
$e->{mapdrive}{$2} = $1 if $v =~ m/\((.*)\) \((.*)\)/;
} elsif ($keyword eq 'module') {
@@ -316,8 +554,51 @@ sub read_grub_menu_lst {
$e and $e->{verbatim} .= $verbatim;
}
+ %b;
+}
+
+
+=item is_already_crypted($password)
+
+Returns whether grub password is already encrypted or not
+
+=cut
+
+sub is_already_crypted {
+ my ($password) = @_;
+ $password =~ /^--md5 (.*)/;
+}
+
+=item read_grub_menu_lst($fstab, $grub2dev)
+
+Read config from /boot/grub/menu.lst
+
+=cut
+
+sub read_grub_menu_lst {
+ my ($fstab, $grub2dev) = @_;
+
+ my %b = _parse_grub_menu_lst();
+
+ foreach my $keyword (grep { $_ ne 'entries' } keys %b) {
+ $b{$keyword} = $b{$keyword} eq '' ? 1 : grub2file($b{$keyword}, $grub2dev, $fstab, \%b);
+ }
+
#- sanitize
foreach my $e (@{$b{entries}}) {
+ if (member($e->{type}, 'other', 'grub_configfile')) {
+ eval { $e->{kernel_or_dev} = grub2dev($e->{rootnoverify} || $e->{grub_root}, $grub2dev) };
+ $e->{keep_verbatim} = 1 unless $e->{kernel_or_dev};
+ } elsif ($e->{initrd}) {
+ my $initrd;
+ eval { $initrd = grub2file($e->{initrd}, $grub2dev, $fstab, $e) };
+ if ($initrd) {
+ $e->{initrd} = $initrd;
+ } else {
+ $e->{keep_verbatim} = 1;
+ }
+ }
+
if ($e->{kernel} =~ /xen/ && @{$e->{modules} || []} == 2 && $e->{modules}[1] =~ /initrd/) {
(my $xen, $e->{xen_append}) = split(' ', $e->{kernel}, 2);
($e->{kernel}, my $initrd) = @{delete $e->{modules}};
@@ -328,8 +609,8 @@ sub read_grub_menu_lst {
(my $kernel, $e->{append}) = split(' ', $v, 2);
$e->{append} = join(' ', grep { !/^BOOT_IMAGE=/ } split(' ', $e->{append}));
$e->{root} = $1 if $e->{append} =~ s/root=(\S*)\s*//;
- $e->{kernel_or_dev} = grub2file($kernel, $grub2dev, $fstab, $e);
- $e->{keep_verbatim} = 1 if dirname($e->{kernel_or_dev}) ne '/boot';
+ eval { $e->{kernel_or_dev} = grub2file($kernel, $grub2dev, $fstab, $e) };
+ $e->{keep_verbatim} = 1 if !$e->{kernel_or_dev} || dirname($e->{kernel_or_dev}) ne '/boot';
}
my ($vga, $other) = partition { /^vga=/ } split(' ', $e->{append});
if (@$vga) {
@@ -349,42 +630,122 @@ sub read_grub_menu_lst {
\%b;
}
-sub yaboot2dev {
- my ($of_path) = @_;
- find { dev2yaboot($_) eq $of_path } map { "/dev/$_->{dev}" } fs::proc_partitions::read_raw();
+sub _parse_extlinux_conf() {
+ my $global = 1;
+ my ($e, %b);
+
+ my $f = "$::prefix/boot/extlinux/extlinux.conf";
+ -e $f or return;
+
+ foreach (cat_utf8($f)) {
+ chomp;
+ s/^\s*//; s/\s*$//;
+ next if /^#/ || /^$/;
+ my ($keyword, $v) = split('[ \t=]+', $_, 2) or
+ warn qq(unknown line in $f: "$_"\n), next;
+
+ $keyword = lc($keyword);
+
+ if ($keyword eq 'label') {
+ push @{$b{entries}}, $e = { label => $v };
+ $global = 0;
+ } elsif ($global) {
+ $b{$keyword} = $v;
+ } else {
+ if ($keyword eq 'kernel') {
+ $e->{type} = 'image';
+ $e->{kernel_or_dev} = $v;
+ } else {
+ $e->{$keyword} = $v;
+ }
+ }
+ }
+
+ %b;
+}
+
+sub read_uboot() {
+ my %b = _parse_extlinux_conf();
+
+ $b{method} = 'uboot';
+ $b{timeout} //= 30;
+
+ my $is_raspberry = cat_("/proc/device-tree/model") =~ /^Raspberry Pi/;
+ if ($is_raspberry) {
+ $b{perImageAppend} //= "8250.nr_uarts=1 console=ttyS0,115200 console=tty1 elevator=deadline cma=256M\@512M";
+ }
+
+ $b{perImageAppend} //= $b{entries}[0]{append};
+
+ \%b;
+}
+
+=item read_refind ($fstab)
+
+Read back rEFInd config + C</boot/refind_linux.conf>
+
+=cut
+
+sub read_refind() {
+ my %bootloader = (entries => []);
+ foreach (cat_utf8("$::prefix/boot/refind_linux.conf")) {
+ next if /^#/;
+ my ($label, $append) = /"(.*)"\s*"(.*)"/;
+ my $root = $1 if $append =~ s/root=(\S*)\s*//;
+ my $vga = $1 if $append =~ s/vga=(\S*)\s*//;
+ if ($label && $root) {
+ push @{$bootloader{entries}}, {
+ type => 'image',
+ kernel_or_dev => '/boot/vmlinuz',
+ label => $label,
+ root => $root,
+ append => $append,
+ vga => $vga
+ };
+ }
+ }
+ $bootloader{method} = 'refind';
+ read_refind_config(\%bootloader);
+ \%bootloader;
}
-# assumes file is in /boot
-# to do: use yaboot2dev for files as well
-#- example of of_path: /pci@f4000000/ata-6@d/disk@0:3,/initrd-2.6.8.1-8mdk.img
-sub yaboot2file {
- my ($of_path) = @_;
-
- if ($of_path =~ /,/) {
- "$::prefix/boot/" . basename($of_path);
+sub read_refind_config {
+ my ($bootloader) = @_;
+
+ #- These are the rEFInd default values.
+ $bootloader->{use_nvram} = 1;
+ $bootloader->{banner_path} = '';
+ $bootloader->{banner_scale} = 'noscale';
+
+ if (-r "$::prefix/boot/EFI/EFI/refind/refind.conf") {
+ read_refind_conf($bootloader, "$::prefix/boot/EFI/EFI/refind/refind.conf");
+ } elsif (-r "$::prefix/boot/EFI/EFI/BOOT/refind.conf") {
+ read_refind_conf($bootloader, "$::prefix/boot/EFI/EFI/BOOT/refind.conf");
} else {
- yaboot2dev($of_path);
+ #- This is the preferred value if rEFInd is not yet installed,
+ $bootloader->{banner_path} = 'refind_banner.png';
}
}
-sub read_silo() {
- my $bootloader = read_lilo_like("/boot/silo.conf", sub {
- my ($f) = @_;
- "/boot$f";
- });
- $bootloader->{method} = 'silo';
- $bootloader;
+sub read_refind_conf {
+ my ($bootloader, $config_file) = @_;
+ foreach (cat_utf8($config_file)) {
+ if ($_ =~ /^\s*use_nvram\s+(false|off|0)/) {
+ $bootloader->{use_nvram} = 0;
+ } elsif ($_ =~ /^\s*banner\s+(\S*)/) {
+ $bootloader->{banner_path} = $1;
+ } elsif ($_ =~ /^\s*banner_scale\s+(\S*)/) {
+ $bootloader->{banner_scale} = $1;
+ }
+ }
}
+
+# FIXME: actually read back previous conf
sub read_cromwell() {
- my %b;
- $b{method} = 'cromwell';
- \%b;
-}
-sub read_yaboot() {
- my $bootloader = read_lilo_like("/etc/yaboot.conf", \&yaboot2file);
- $bootloader->{method} = 'yaboot';
- $bootloader;
+ +{ method => 'cromwell' };
}
+
+
sub read_lilo() {
my $bootloader = read_lilo_like("/etc/lilo.conf", sub { $_[0] });
@@ -489,7 +850,7 @@ sub suggest_onmbr {
if (my $type = partition_table::raw::typeOfMBR($hd->{device})) {
if (member($type, qw(dos dummy empty))) {
$unsafe = 0;
- } elsif (!member($type, qw(lilo grub))) {
+ } elsif (!member($type, qw(lilo grub grub2))) {
$onmbr = 0;
}
log::l("bootloader::suggest_onmbr: type $type, onmbr $onmbr, unsafe $unsafe");
@@ -497,14 +858,41 @@ sub suggest_onmbr {
($onmbr, $unsafe);
}
+
+=item allowed_boot_disks($bootloader, $all_hds)
+
+Returns list of disks where we can install the bootloader when not in UEFI mode
+(accounting for misssing BIOS boot partitions on GPT disks)
+in UEFI mode, grub2 automatically look for the ESP).
+
+=cut
+
+sub allowed_boot_disks {
+ my ($all_hds) = @_;
+ # GPT disks w/o a BIOS boot partition do not have free space for grub2 to embed:
+ grep { $_->{pt_table_type} ne 'gpt' ||
+ any { isBIOS_GRUB($_) } map { partition_table::get_normal_parts($_) } $_;
+ } @{$all_hds->{hds}};
+}
+
+=item allowed_boot_parts($bootloader, $all_hds)
+
+Returns list of places where we can install the bootloader when not in UEFI mode
+(in UEFI mode, grub2 automatically look for the ESP).
+
+=cut
+
sub allowed_boot_parts {
my ($bootloader, $all_hds) = @_;
(
- @{$all_hds->{hds}},
+ allowed_boot_disks($all_hds), # MBR
+
if_($bootloader->{method} =~ /lilo/,
grep { $_->{level} eq '1' } @{$all_hds->{raids}}
),
- (grep { !isFat_or_NTFS($_) } fs::get::fstab($all_hds)),
+ (if_(main_method($bootloader->{method}) ne 'grub2',
+ grep { !isFat_or_NTFS($_) } fs::get::fstab($all_hds)), # filesystems except those who do not leave space for our bootloaders
+ ),
detect_devices::floppies(),
);
}
@@ -536,7 +924,7 @@ sub add_entry {
my $to_add = $v;
my $label = $v->{label};
- for (my $i = 0; $i < 10;) {
+ for (my $i = 0; $i < 100;) {
my $conflicting = get_label($label, $bootloader);
$to_add->{label} = $label;
@@ -607,18 +995,7 @@ sub _do_the_symlink {
or cp_af("$::prefix/boot/$long_name", "$::prefix$link");
}
-sub cmp_kernel_versions {
- my ($va, $vb) = @_;
- my $rel_a = $va =~ s/-(.*)$// && $1;
- my $rel_b = $vb =~ s/-(.*)$// && $1;
- ($va, $vb) = map { [ split /[.-]/ ] } $va, $vb;
- my $r = 0;
- mapn_ {
- $r ||= $_[0] <=> $_[1];
- } $va, $vb;
- $r || $rel_a <=> $rel_b || $rel_a cmp $rel_b;
-}
-
+# for lilo & xen
sub get_mbootpack_filename {
my ($entry) = @_;
my $mbootpack_file = $entry->{initrd};
@@ -626,6 +1003,7 @@ sub get_mbootpack_filename {
$entry->{xen} && $mbootpack_file;
}
+# for lilo & xen
sub build_mbootpack {
my ($entry) = @_;
@@ -677,39 +1055,53 @@ sub add_kernel {
#- perImageAppend contains resume=/dev/xxx which we don't want
@$dict = grep { $_->[0] ne 'resume' } @$dict;
}
- if (-e "$::prefix/sbin/udev" && cmp_kernel_versions($kernel_str->{version_no_ext}, '2.6.8') >= 0) {
- log::l("it is a recent kernel, so we remove any existing devfs= kernel option to enable udev");
- @$dict = grep { $_->[0] ne 'devfs' } @$dict;
- }
$v->{append} = pack_append($simple, $dict);
}
- #- new versions of yaboot do not handle symlinks
- $b_nolink ||= arch() =~ /ppc/;
-
$b_nolink ||= $kernel_str->{use_long_name};
+ #- do not link /boot/vmlinuz to xen
+ $b_nolink ||= $v->{xen};
+
my $vmlinuz_long = kernel_str2vmlinuz_long($kernel_str);
+ my $initrd_long = kernel_str2initrd_long($kernel_str);
$v->{kernel_or_dev} = "/boot/$vmlinuz_long";
-e "$::prefix$v->{kernel_or_dev}" or log::l("unable to find kernel image $::prefix$v->{kernel_or_dev}"), return;
- if (!$b_nolink) {
- $v->{kernel_or_dev} = '/boot/' . kernel_str2vmlinuz_short($kernel_str);
- _do_the_symlink($bootloader, $v->{kernel_or_dev}, $vmlinuz_long);
- }
log::l("adding $v->{kernel_or_dev}");
if (!$b_no_initrd) {
- my $initrd_long = kernel_str2initrd_long($kernel_str);
$v->{initrd} = mkinitrd($kernel_str->{version}, $bootloader, $v, "/boot/$initrd_long");
- if ($v->{initrd} && !$b_nolink) {
+ }
+
+ my $liblinux_long = kernel_str2liblinux_long($kernel_str);
+ if (-d "$::prefix$liblinux_long") {
+ $v->{liblinux} = $v->{fdtdir} = $liblinux_long;
+ }
+
+ if (!$b_nolink) {
+ $v->{kernel_or_dev} = '/boot/' . kernel_str2vmlinuz_short($kernel_str);
+ _do_the_symlink($bootloader, $v->{kernel_or_dev}, $vmlinuz_long);
+
+ if ($v->{initrd}) {
$v->{initrd} = '/boot/' . kernel_str2initrd_short($kernel_str);
- _do_the_symlink($bootloader, $v->{initrd}, $initrd_long);
+ _do_the_symlink($bootloader, $v->{initrd}, $initrd_long);
+ }
+
+ if ($v->{liblinux}) {
+ $v->{liblinux} = kernel_str2liblinux_short();
+ _do_the_symlink($bootloader, $v->{liblinux}, basename($liblinux_long));
}
}
add_entry($bootloader, $v);
}
+=item rebuild_initrds($bootloader)
+
+Rebuilds all initrds
+
+=cut
+
sub rebuild_initrds {
my ($bootloader) = @_;
@@ -723,6 +1115,7 @@ sub rebuild_initrds {
}
}
+# unused (?)
sub duplicate_kernel_entry {
my ($bootloader, $new_label) = @_;
@@ -755,7 +1148,7 @@ sub pack_append {
sub modify_append {
my ($b, $f) = @_;
- my @l = grep { $_->{type} eq 'image' && !($::isStandalone && $_->{label} eq 'failsafe') } @{$b->{entries}};
+ my @l = grep { $_->{type} eq 'image' && !$_->{keep_verbatim} && !($::isStandalone && $_->{label} eq 'failsafe') } @{$b->{entries}};
foreach (\$b->{perImageAppend}, map { \$_->{append} } @l) {
my ($simple, $dict) = unpack_append($$_);
@@ -765,8 +1158,6 @@ sub modify_append {
}
}
-sub append__mem_is_memsize { $_[0] =~ /^\d+[kM]?$/i }
-
sub get_append_simple {
my ($b, $key) = @_;
my ($simple, $_dict) = unpack_append($b->{perImageAppend});
@@ -813,24 +1204,6 @@ sub may_append_with_key {
set_append_with_key($b, $key, $val) if !get_append_with_key($b, $key);
}
-sub get_append_memsize {
- my ($b) = @_;
- my ($_simple, $dict) = unpack_append($b->{perImageAppend});
- my $e = find { $_->[0] eq 'mem' && append__mem_is_memsize($_->[1]) } @$dict;
- $e && $e->[1];
-}
-
-sub set_append_memsize {
- my ($b, $memsize) = @_;
-
- modify_append($b, sub {
- my ($_simple, $dict) = @_;
-
- @$dict = grep { $_->[0] ne 'mem' || !append__mem_is_memsize($_->[1]) } @$dict;
- push @$dict, [ mem => $memsize ] if $memsize;
- });
-}
-
sub get_append_netprofile {
my ($e) = @_;
my ($simple, $dict) = unpack_append($e->{append});
@@ -844,6 +1217,12 @@ sub set_append_netprofile {
$e->{append} = pack_append($simple, $dict);
}
+=item configure_entry($bootloader, $entry)
+
+Used when a bootloader $entry has been modified (eg: $entry->{vga})
+
+=cut
+
sub configure_entry {
my ($bootloader, $entry) = @_;
$entry->{type} eq 'image' or return;
@@ -861,26 +1240,18 @@ sub get_kernels_and_labels_before_kernel_remove {
map { kernel_str2label($_) => $_ } get_kernel_labels(\@kernels);
}
-sub get_kernels_and_labels {
- my ($b_prefer_24) = @_;
- get_kernel_labels([ installed_vmlinuz() ], $b_prefer_24);
+sub get_kernels_and_labels() {
+ get_kernel_labels([ installed_vmlinuz() ]);
}
sub get_kernel_labels {
- my ($kernels, $b_prefer_24) = @_;
+ my ($kernels) = @_;
my @kernels_str =
- sort { cmp_kernel_versions($b->{version_no_ext}, $a->{version_no_ext}) }
+ sort { common::cmp_kernel_versions($b->{version_no_ext}, $a->{version_no_ext}) }
grep { -d "$::prefix/lib/modules/$_->{version}" }
map { vmlinuz2kernel_str($_) } @$kernels;
- if ($b_prefer_24) {
- my ($kernel_24, $other) = partition { $_->{ext} eq '' && $_->{version} =~ /^\Q2.4/ } @kernels_str;
- @kernels_str = (@$kernel_24, @$other);
- }
-
- $kernels_str[0]{ext} = '';
-
my %labels;
foreach (@kernels_str) {
if ($labels{$_->{ext}}) {
@@ -889,6 +1260,9 @@ sub get_kernel_labels {
$labels{$_->{ext}} = 1;
}
}
+
+ $kernels_str[0]{ext} = '';
+
@kernels_str;
}
@@ -896,20 +1270,12 @@ sub short_ext {
my ($kernel_str) = @_;
my $short_ext = {
- 'i586-up-1GB' => 'i586',
- 'i686-up-4GB' => '4GB',
'xen0' => 'xen',
}->{$kernel_str->{ext}};
$short_ext || $kernel_str->{ext};
}
-# deprecated, only for compatibility (nov 2007)
-sub sanitize_ver {
- my ($_name, $kernel_str) = @_;
- _sanitize_ver($kernel_str);
-}
-
sub _sanitize_ver {
my ($kernel_str) = @_;
@@ -917,26 +1283,25 @@ sub _sanitize_ver {
$name = '' if $name eq 'vmlinuz';
my $v = $kernel_str->{version_no_ext};
- if ($v =~ s/-\d+\.mm\././) {
- $name = join(' ', grep { $_ } $name, 'multimedia');
- }
- $v =~ s!md[kv]$!!;
+ $v =~ s!(md[kv]|mnb)$!!;
$v =~ s!-0\.(pre|rc)(\d+)\.!$1$2-!;
my $return = join(' ', grep { $_ } $name, short_ext($kernel_str), $v);
- length($return) < 30 or $return =~ s!secure!sec!;
- length($return) < 30 or $return =~ s!enterprise!ent!;
- length($return) < 30 or $return =~ s!multimedia!mm!;
-
$return;
}
+=item suggest_message_text($bootloader)
+
+Provides a description text for Lilo
+
+=cut
+
sub suggest_message_text {
my ($bootloader) = @_;
- if (!$bootloader->{message} && !$bootloader->{message_text} && arch() !~ /ia64/) {
+ if (!$bootloader->{message} && !$bootloader->{message_text}) {
my $msg_en =
#-PO: these messages will be displayed at boot time in the BIOS, use only ASCII (7bit)
N_("Welcome to the operating system chooser!
@@ -946,7 +1311,7 @@ wait for default boot.
");
my $msg = translate($msg_en);
- #- use the english version if more than 40% of 8bits chars
+ #- use the English version if more than 40% of 8bits chars
#- else, use the translation but force a conversion to ascii
#- to be sure there won't be undisplayable characters
if (int(grep { $_ & 0x80 } unpack "c*", $msg) / length($msg) > 0.4) {
@@ -964,36 +1329,28 @@ sub suggest {
my $root_part = fs::get::root($fstab);
my $root = isLoopback($root_part) ? '/dev/loop7' : fs::wild_device::from_part('', $root_part);
my $boot = fs::get::root($fstab, 'boot')->{device};
- #- PPC xfs module requires enlarged initrd
- my $xfsroot = $root_part->{fs_type} eq 'xfs';
+ my $mbr;
+
+ # If installing onto an USB drive, put the MBR there, else on the first non removable drive
+ if ($root_part->{is_removable}) {
+ $mbr = fs::get::part2hd($root_part, $all_hds);
+ } else {
+ $mbr = find { !$_->{is_removable} } allowed_boot_disks($all_hds);
+ }
- my ($onmbr, $unsafe) = $bootloader->{crushMbr} ? (1, 0) : suggest_onmbr($all_hds->{hds}[0]);
- add2hash_($bootloader, arch() =~ /ppc/ ?
- {
- defaultos => "linux",
- entries => [],
- 'init-message' => "Welcome to Mandriva Linux!",
- delay => 30, #- OpenFirmware delay
- timeout => 50,
- enableofboot => 1,
- enablecdboot => 1,
- if_(detect_devices::get_mac_model() =~ /IBM/,
- boot => "/dev/sda1",
- ),
- xfsroot => $xfsroot,
- } :
+ my ($onmbr, $unsafe) = $bootloader->{crushMbr} ? (1, 0) : suggest_onmbr($mbr);
+ add2hash_($bootloader,
{
bootUnsafe => $unsafe,
entries => [],
timeout => $onmbr && 10,
nowarn => 1,
- if_(arch() !~ /ia64/,
- boot => "/dev/" . ($onmbr ? $all_hds->{hds}[0]{device} : $boot),
+ boot => "/dev/" . ($onmbr ? $mbr->{device} : $boot),
map => "/boot/map",
compact => 1,
+ 'large-memory' => 1,
color => 'black/cyan yellow/cyan',
'menu-scheme' => 'wb:bw:wb:bw'
- ),
});
suggest_message_text($bootloader);
@@ -1006,13 +1363,23 @@ sub suggest {
}
my @kernels = get_kernels_and_labels() or die "no kernel installed";
+ log::l("found kernels: ", join(', ', map { $_->{version} } @kernels));
+
+ my %old_kernels = map { vmlinuz2version($_->{kernel_or_dev}) => 1 } @{$bootloader->{entries}};
+ @kernels = grep { !$old_kernels{$_->{version}} } @kernels;
+
+ #- remove existing failsafe and linux-nonfb, do not care if the previous one was modified by the user?
+ #- but only if we are going to add new ones.
+ @{$bootloader->{entries}} = grep { !member($_->{label}, qw(failsafe linux-nonfb)) } @{$bootloader->{entries}}
+ if @kernels;
foreach my $kernel (@kernels) {
my $e = add_kernel($bootloader, $kernel,
{
root => $root,
- if_($options{vga_fb} && $kernel->{ext} eq '', vga => $options{vga_fb}), #- using framebuffer
- if_($options{vga_fb} && $options{quiet}, append => "splash=silent"),
+ if_($options{vga_fb}, vga => $options{vga_fb}), #- using framebuffer
+ if_($options{vga_fb} && $options{splash}, append => "splash noiswmd audit=0"),
+ if_($options{quiet}, append => "splash quiet noiswmd audit=0"),
});
if ($options{vga_fb} && $e->{label} eq 'linux') {
@@ -1020,33 +1387,16 @@ sub suggest {
}
}
- #- remove existing failsafe, do not care if the previous one was modified by the user?
- @{$bootloader->{entries}} = grep { $_->{label} ne 'failsafe' } @{$bootloader->{entries}};
-
add_kernel($bootloader, $kernels[0],
- { root => $root, label => 'failsafe', append => 'failsafe' });
+ { root => $root, label => 'failsafe', append => 'failsafe noiswmd audit=0' })
+ if @kernels;
- if (arch() =~ /ppc/) {
- #- if we identified a MacOS partition earlier - add it
- if (defined $partition_table::mac::macos_part) {
- add_entry($bootloader,
- {
- type => "macos",
- kernel_or_dev => $partition_table::mac::macos_part
- });
- }
- } elsif (arch() !~ /ia64/) {
#- search for dos (or windows) boot partition. Do not look in extended partitions!
my @windows_boot_parts =
- grep {
- my $handle = any::inspect($_, $::prefix);
- my $dir = $handle && $handle->{dir};
- my @root_files = map { lc($_) } all($dir);
- log::l("found the following files on potential windows partition $_->{device}: " . join(' ', @root_files));
- intersection(\@root_files, [ "windows", "winnt" ]);
- }
- grep { isFat_or_NTFS($_) && member(fs::type::fs_type_from_magic($_), 'vfat', 'ntfs', 'ntfs-3g')
- && fs::type::part2type_name($_) !~ /^Hidden/;
+ grep { $_->{active}
+ && isFat_or_NTFS($_) && member(fs::type::fs_type_from_magic($_), qw(vfat ntfs ntfs-3g))
+ && !$_->{is_removable}
+ && !isRecovery($_);
}
map { @{$_->{primary}{normal}} } @{$all_hds->{hds}};
each_index {
@@ -1059,23 +1409,40 @@ sub suggest {
makeactive => 1,
});
} @windows_boot_parts;
- }
- my @preferred = map { "linux-$_" } 'p3-smp-64GB', 'secure', 'enterprise', 'smp', 'i686-up-4GB';
+ my @preferred = map { "linux-$_" } 'server';
if (my $preferred = find { get_label($_, $bootloader) } @preferred) {
$bootloader->{default} ||= $preferred;
}
$bootloader->{default} ||= "linux";
- $bootloader->{method} ||= first(method_choices($all_hds, 1));
+ $bootloader->{method} ||= first(method_choices($all_hds, 1), # best installed
+ method_choices($all_hds, 0)); # or best if no valid one is installed
+
+ $bootloader->{perImageAppend} = $bootloader->{entries}[0]{append};
+ $bootloader->{default_vga} = $options{vga_fb};
if (main_method($bootloader->{method}) eq 'grub') {
+ my %processed_entries = {};
foreach my $c (find_other_distros_grub_conf($fstab)) {
- add_entry($bootloader, {
- type => 'grub_configfile',
- label => $c->{name},
- kernel_or_dev => "/dev/$c->{bootpart}{device}",
- configfile => $c->{grub_conf},
- });
+ my %h = (
+ type => 'grub_configfile',
+ label => $c->{name},
+ kernel_or_dev => "/dev/$c->{bootpart}{device}",
+ configfile => $c->{grub_conf},
+ );
+ if ($c->{root}) {
+ my $key = "$c->{name} - $c->{linux} - $c->{initrd}";
+ next if $processed_entries{$key};
+ $processed_entries{$key} = 1;
+ add_entry($bootloader, {
+ %h,
+ linux => $c->{linux},
+ initrd => $c->{initrd},
+ root => $c->{root},
+ });
+ } else {
+ add_entry($bootloader, \%h);
+ }
}
}
}
@@ -1096,6 +1463,7 @@ sub config_files() {
lilo => '/etc/lilo.conf',
grub => '/boot/grub/menu.lst',
grub_install => '/boot/grub/install.sh',
+ uboot => '/boot/extlinux/extlinux.conf',
);
map_each {
@@ -1108,26 +1476,53 @@ sub method2text {
my ($method) = @_;
+{
'lilo-menu' => N("LILO with text menu"),
+ 'grub2-graphic' => N("GRUB2 with graphical menu"),
+ 'grub2' => N("GRUB2 with text menu"),
'grub-graphic' => N("GRUB with graphical menu"),
'grub-menu' => N("GRUB with text menu"),
- 'yaboot' => N("Yaboot"),
- 'silo' => N("SILO"),
+ 'refind' => N("rEFInd with graphical menu"),
+ 'uboot' => N("U-Boot/Extlinux with text menu"),
}->{$method};
}
+
+=item method_choices_raw($b_prefix_mounted)
+
+Returns list of bootloaders.
+
+method_choices_raw(1) will return the list of installed boot loaders.
+
+method_choices_raw(0) will return the list of all boot loaders supported by drakboot.
+
+Returns: ("grub2", "grub2-graphic")
+
+=cut
+
sub method_choices_raw {
my ($b_prefix_mounted) = @_;
detect_devices::is_xbox() ? 'cromwell' :
- arch() =~ /ppc/ ? 'yaboot' :
- arch() =~ /ia64/ ? 'lilo' :
- arch() =~ /sparc/ ? 'silo' :
- (
- if_(!$b_prefix_mounted || whereis_binary('grub', $::prefix),
+ arch() =~ /arm/ ? 'uboot' :
+ if_(!$b_prefix_mounted || whereis_binary('grub2-reboot', $::prefix),
+ 'grub2-graphic', 'grub2'),
+ if_(uefi_type() eq kernel_uefi_type() && (!$b_prefix_mounted || whereis_binary('refind-install', $::prefix)), 'refind'),
+ # only grub2 & rEFInd work on UEFI:
+ # lilo & grub-legacy do not suppport new ext4/xfs format and are unmainted so only allow them on upgrade:
+ if_(!is_uefi() && !($::isInstall && !$::o->{isUpgrade} || $::isLiveInstall), (
+ if_(!$b_prefix_mounted || whereis_binary('grub', $::prefix) && -f "$::prefix/boot/grub/install.sh",
'grub-graphic', 'grub-menu'),
- if_(!$b_prefix_mounted || whereis_binary('lilo', $::prefix),
+ if_(!$b_prefix_mounted || whereis_binary('lilo', $::prefix) && -f "$::prefix/etc/lilo.conf",
'lilo-menu'),
- );
+ ));
}
+
+=item method_choices($all_hds, $b_prefix_mounted)
+
+Returns list of supported bootloaders according to what is detected.
+
+Like method_choices_raw(), the $b_prefix_mounted parameter enables to return the list of either installed supported methods or the list of all supported boot loaders.
+
+=cut
+
sub method_choices {
my ($all_hds, $b_prefix_mounted) = @_;
my $fstab = [ fs::get::fstab($all_hds) ];
@@ -1136,11 +1531,20 @@ sub method_choices {
my $have_dmraid = find { fs::type::is_dmraid($_) } @{$all_hds->{hds}};
grep {
- !(/lilo/ && (isLoopback($root_part) || $have_dmraid))
- && !(/grub/ && isRAID($boot_part))
- && !(/grub-graphic/ && cat_("/proc/cmdline") =~ /console=ttyS/);
+ !(/lilo/ && (isLoopback($root_part) || $have_dmraid)) # LILO doesn't work fake raid
+ && (/grub2/ || $boot_part->{fs_type} ne 'btrfs') # Only grub2 works on btrfs
+ && !(/grub2?-graphic/ && cat_("/proc/cmdline") =~ /console=ttyS/); # No Gfx mode on console
} method_choices_raw($b_prefix_mounted);
}
+
+=item main_method_choices($b_prefix_mounted)
+
+Returns list of supported bootloaders, not distinging text/gfx mode.
+
+Like method_choices_raw(), the $b_prefix_mounted parameter enables to return the list of either installed supported methods or the list of all supported boot loaders.
+
+=cut
+
sub main_method_choices {
my ($b_prefix_mounted) = @_;
uniq(map { main_method($_) } method_choices_raw($b_prefix_mounted));
@@ -1150,6 +1554,7 @@ sub configured_main_methods() {
difference2([ main_method_choices(1) ], \@bad_main_methods);
}
+# for lilo
sub keytable {
my ($f) = @_;
$f or return;
@@ -1162,125 +1567,59 @@ sub keytable {
-r "$::prefix/$f" && $f;
}
-
-sub create_link_source() {
- #- we simply do it for all kernels :)
- #- so this can be used in %post of kernel and also of kernel-source
- foreach (all("$::prefix/usr/src")) {
- my ($version) = /^linux-(\d+\.\d+.*)/ or next;
- foreach (glob("$::prefix/lib/modules/$version*")) {
- -d $_ or next;
- log::l("creating symlink $_/build");
- symlink "/usr/src/linux-$version", "$_/build";
- log::l("creating symlink $_/source");
- symlink "/usr/src/linux-$version", "$_/source";
- }
- }
-}
-
-sub dev2yaboot {
- my ($dev) = @_;
-
- devices::make("$::prefix$dev"); #- create it in the chroot
-
- my $of_dev;
- run_program::rooted_or_die($::prefix, "/usr/sbin/ofpath", ">", \$of_dev, $dev);
- chomp($of_dev);
- log::l("OF Device: $of_dev");
- $of_dev;
-}
-
sub check_enough_space() {
my $e = "$::prefix/boot/.enough_space";
output $e, 1; -s $e or die N("not enough room in /boot");
unlink $e;
}
-sub write_yaboot {
+sub install_uboot {
my ($bootloader, $all_hds) = @_;
+ write_uboot($bootloader, $all_hds);
+ when_config_changed_uboot($bootloader);
+}
- my $fstab = [ fs::get::fstab($all_hds) ];
-
- my $file2yaboot = sub {
- my ($part, $file) = fs::get::file2part($fstab, $_[0]);
- dev2yaboot('/dev/' . $part->{device}) . "," . $file;
- };
-
- #- do not write yaboot.conf for old-world macs
- my $mac_type = detect_devices::get_mac_model();
- return if $mac_type =~ /Power Macintosh/;
-
- $bootloader->{prompt} ||= $bootloader->{timeout};
-
- if ($bootloader->{message_text}) {
- eval { output("$::prefix/boot/message", $bootloader->{message_text}) }
- and $bootloader->{message} = '/boot/message';
- }
-
- my @conf;
+sub _write_extlinux_conf {
+ my ($bootloader, $_all_hds) = @_;
if (!get_label($bootloader->{default}, $bootloader)) {
log::l("default bootloader entry $bootloader->{default} is invalid, choosing another one");
$bootloader->{default} = $bootloader->{entries}[0]{label};
}
- push @conf, "# yaboot.conf - generated by DrakX/drakboot";
- push @conf, "# WARNING: do not forget to run ybin after modifying this file\n";
- push @conf, "default=" . make_label_lilo_compatible($bootloader->{default}) if $bootloader->{default};
- push @conf, sprintf('init-message="\n%s\n"', $bootloader->{'init-message'}) if $bootloader->{'init-message'};
- if ($bootloader->{boot}) {
- push @conf, "boot=$bootloader->{boot}";
- push @conf, "ofboot=" . dev2yaboot($bootloader->{boot}) if $mac_type !~ /IBM/;
- } else {
- die "no bootstrap partition defined.";
- }
+ my @conf;
+ push @conf, "# File generated by DrakX/drakboot";
+ push @conf, "default " . $bootloader->{default} if $bootloader->{default};
+ push @conf, "timeout " . $bootloader->{timeout} if $bootloader->{timeout};
+ # needed to show boot menu and enable timeout
+ push @conf, "menu title Boot Menu" if $bootloader->{timeout};
- push @conf, map { "$_=$bootloader->{$_}" } grep { $bootloader->{$_} } (qw(delay timeout), if_($mac_type !~ /IBM/, 'defaultos'));
- push @conf, "install=/usr/lib/yaboot/yaboot";
- if ($mac_type =~ /IBM/) {
- push @conf, 'nonvram';
- } else {
- push @conf, 'magicboot=/usr/lib/yaboot/ofboot';
- push @conf, grep { $bootloader->{$_} } qw(enablecdboot enableofboot);
- }
foreach my $entry (@{$bootloader->{entries}}) {
+ push @conf, "\nlabel $entry->{label}";
+ push @conf, " kernel $entry->{kernel_or_dev}";
+ push @conf, " initrd $entry->{initrd}" if $entry->{initrd};
+ push @conf, " fdtdir $entry->{fdtdir}" if $entry->{fdtdir};
- if ($entry->{type} eq "image") {
- push @conf, "$entry->{type}=" . $file2yaboot->($entry->{kernel_or_dev});
- my @entry_conf;
- push @entry_conf, "label=" . make_label_lilo_compatible($entry->{label});
- push @entry_conf, "root=$entry->{root}";
- push @entry_conf, "initrd=" . $file2yaboot->($entry->{initrd}) if $entry->{initrd};
- #- xfs module on PPC requires larger initrd - say 6MB?
- push @entry_conf, "initrd-size=6144" if $bootloader->{xfsroot};
- push @entry_conf, qq(append=" $entry->{append}") if $entry->{append};
- push @entry_conf, grep { $entry->{$_} } qw(read-write read-only);
- push @conf, map { "\t$_" } @entry_conf;
- } else {
- my $of_dev = dev2yaboot($entry->{kernel_or_dev});
- push @conf, "$entry->{type}=$of_dev";
- }
+ my @append;
+ push @append, "root=" . $entry->{root} if $entry->{root};
+ push @append, $entry->{append} if $entry->{append};
+ push @conf, " append " . join(' ', @append) if @append;
}
- my $f = "$::prefix/etc/yaboot.conf";
- log::l("writing yaboot config to $f");
- renamef($f, "$f.old");
- output($f, map { "$_\n" } @conf);
+
+ my $f = "$::prefix/boot/extlinux/extlinux.conf";
+ log::l("writing extlinux config to $f");
+ renamef($f, $f . '.old');
+ output_with_perm($f, 0600, map { "$_\n" } @conf);
}
-sub install_yaboot {
+sub write_uboot {
my ($bootloader, $all_hds) = @_;
- log::l("Installing boot loader...");
- write_yaboot($bootloader, $all_hds);
- when_config_changed_yaboot($bootloader);
+ _write_extlinux_conf($bootloader, $all_hds);
}
-sub when_config_changed_yaboot {
- my ($bootloader) = @_;
- $::testing and return;
- if (defined $partition_table::mac::new_bootstrap) {
- run_program::run("hformat", $bootloader->{boot}) or die "hformat failed";
- }
- my $error;
- run_program::rooted($::prefix, "/usr/sbin/ybin", "2>", \$error) or die "ybin failed: $error";
+
+sub when_config_changed_uboot {
+ my ($_bootloader) = @_;
+ #- do not do anything
}
sub install_cromwell {
@@ -1293,7 +1632,7 @@ sub write_cromwell {
}
sub when_config_changed_cromwell {
my ($_bootloader) = @_;
- log::l("XBox/Cromwell - nothing to do...");
+ #- do not do anything
}
sub simplify_label {
@@ -1312,7 +1651,7 @@ sub make_label_lilo_compatible {
}
sub write_lilo {
- my ($bootloader, $all_hds) = @_;
+ my ($bootloader, $all_hds, $o_backup_extension) = @_;
$bootloader->{prompt} ||= $bootloader->{timeout};
my $file2fullname = sub {
@@ -1352,9 +1691,17 @@ sub write_lilo {
my @conf;
- #- normalize: RESTRICTED is only valid if PASSWORD is set
- delete $bootloader->{restricted} if !$bootloader->{password};
+ #- normalize: RESTRICTED and MANDATORY are only valid if PASSWORD is set
+ if ($bootloader->{password}) {
+ # lilo defaults to mandatory, use restricted by default to have
+ # the same behaviour as with grub
+ $bootloader->{restricted} = 1;
+ } else {
+ delete $bootloader->{mandatory} if !$bootloader->{password};
+ delete $bootloader->{restricted} if !$bootloader->{password};
+ }
foreach my $entry (@{$bootloader->{entries}}) {
+ delete $entry->{mandatory} if !$entry->{password} && !$bootloader->{password};
delete $entry->{restricted} if !$entry->{password} && !$bootloader->{password};
}
if (get_append_with_key($bootloader, 'console') =~ /ttyS(.*)/) {
@@ -1369,7 +1716,7 @@ sub write_lilo {
push @conf, "# WARNING: do not forget to run lilo after modifying this file\n";
push @conf, "default=" . make_label_lilo_compatible($bootloader->{default}) if $bootloader->{default};
push @conf, map { $_ . '=' . $quotes_if_needed->($bootloader->{$_}) } grep { $bootloader->{$_} } qw(boot root map install serial vga keytable raid-extra-boot menu-scheme vmdefault);
- push @conf, grep { $bootloader->{$_} } qw(linear geometric compact prompt nowarn restricted static-bios-codes);
+ push @conf, grep { $bootloader->{$_} } qw(linear geometric compact prompt mandatory nowarn restricted static-bios-codes large-memory);
push @conf, "append=" . $quotes->($bootloader->{append}) if $bootloader->{append};
push @conf, "password=" . $bootloader->{password} if $bootloader->{password}; #- also done by msec
push @conf, "timeout=" . round(10 * $bootloader->{timeout}) if $bootloader->{timeout};
@@ -1403,6 +1750,7 @@ sub write_lilo {
push @entry_conf, "append=" . $quotes->($append) if $append;
push @entry_conf, "vga=$entry->{vga}" if $entry->{vga};
push @entry_conf, grep { $entry->{$_} } qw(read-write read-only optional);
+ push @entry_conf, "mandatory" if $entry->{lock};
} else {
delete $entry->{unsafe} if $entry->{table}; #- we can't have both
push @entry_conf, map { "$_=$entry->{$_}" } grep { $entry->{$_} } qw(table boot-as);
@@ -1423,14 +1771,14 @@ sub write_lilo {
}
}
push @entry_conf, "password=$entry->{password}" if $entry->{password};
- push @entry_conf, grep { $entry->{$_} } qw(restricted vmwarn vmdisable);
+ push @entry_conf, grep { $entry->{$_} } qw(mandatory vmwarn vmdisable);
push @conf, map { "\t$_" } @entry_conf;
}
my $f = arch() =~ /ia64/ ? "$::prefix/boot/efi/elilo.conf" : "$::prefix/etc/lilo.conf";
log::l("writing lilo config to $f");
- renamef($f, "$f.old");
+ renamef($f, $f . ($o_backup_extension || '.old'));
output_with_perm($f, $bootloader->{password} ? 0600 : 0644, map { "$_\n" } @conf);
}
@@ -1473,7 +1821,7 @@ sub install_raw_lilo {
sub when_config_changed_lilo {
my ($bootloader) = @_;
- if (!$::testing && arch() !~ /ia64/ && $bootloader->{method} =~ /lilo/) {
+ if (!$::testing && $bootloader->{method} =~ /lilo/) {
log::l("Installing boot loader on $bootloader->{boot}...");
install_raw_lilo($bootloader->{force_lilo_answer});
}
@@ -1556,6 +1904,14 @@ sub write_grub_device_map {
(map_index { "(hd$::i) /dev/$_->{device}\n" } @$sorted_hds));
}
+=item parse_grub_file($grub_file)
+
+Parses things like C<(hd0,4)/boot/vmlinuz>
+
+Returns: ("hd0", 4, "boot/vmlinuz")
+
+=cut
+
sub parse_grub_file {
my ($grub_file) = @_;
my ($grub_dev, $rel_file) = $grub_file =~ m!\((.*?)\)/?(.*)! or return;
@@ -1563,20 +1919,50 @@ sub parse_grub_file {
($hd, $part, $rel_file);
}
+=item grub2dev_and_file($grub_file, $grub2dev, $o_block_device)
+
+Takes things like C<(hd0,4)/boot/vmlinuz>
+
+Returns: ("/dev/sda5", "boot/vmlinuz")
+
+=cut
+
sub grub2dev_and_file {
my ($grub_file, $grub2dev, $o_block_device) = @_;
my ($hd, $part, $rel_file) = parse_grub_file($grub_file) or return;
$grub2dev->{$hd} or internal_error("$hd has no mapping in device.map (when translating $grub_file)");
$part = $o_block_device ? '' : defined $part && $part + 1; #- grub wants "(hdX,Y)" where lilo just want "hdY+1"
- my $device = '/dev/' . $grub2dev->{$hd} . $part;
+ my $device = '/dev/' . ($part eq '' ? $grub2dev->{$hd} : devices::prefix_for_dev($grub2dev->{$hd}) . $part);
$device, $rel_file;
}
+
+=item grub2devd($grub_file, $grub2dev, $o_block_device)
+
+Takes things like C<(hd0,4)/boot/vmlinuz>
+
+Returns: "/dev/sda5"
+
+=cut
+
sub grub2dev {
my ($grub_file, $grub2dev, $o_block_device) = @_;
first(grub2dev_and_file($grub_file, $grub2dev, $o_block_device));
}
-# replace dummy "(hdX,Y)" in "(hdX,Y)/boot/vmlinuz..." by appropriate path if needed
+=item grub2file($grub_file, $grub2dev, $fstab, $o_entry)
+
+Replaces
+
+=over 4
+
+=item * C</vmlinuz> with C</boot/vmlinuz> when "root" or "rootnoverify" is set for the entry
+
+=item * C<(hdX,Y)> in C<(hdX,Y)/boot/vmlinuz...> by appropriate path if possible/needed
+
+=back
+
+=cut
+
sub grub2file {
my ($grub_file, $grub2dev, $fstab, $o_entry) = @_;
@@ -1626,8 +2012,172 @@ sub update_copy_in_boot {
}
}
+sub crypt_grub_password {
+ my ($password) = @_;
+ require IPC::Open2;
+ local $ENV{LC_ALL} = 'C';
+ my ($his_out, $his_in);
+ my $cmd = ($::prefix ? "chroot $::prefix " : "") . "/sbin/grub-md5-crypt";
+
+ my $pid = IPC::Open2::open2($his_out, $his_in, $cmd);
+
+ my ($line, $res);
+ while (sysread($his_out, $line, 100)) {
+ if ($line =~ /Password/i) {
+ syswrite($his_in, "$password\n");
+ } else {
+ $res = $line;
+ }
+ }
+ waitpid($pid, 0);
+ my $status = $? >> 8;
+ die "failed to encrypt password (status=$status)" if $status != 0;
+ chomp_($res);
+}
+
+sub get_grub2_first_entry {
+ my ($bootloader) = @_;
+ # set default parameters:
+ my ($entry) = grep { $_->{kernel_or_dev} =~ /vmlin/ } @{$bootloader->{entries}};
+ $entry;
+}
+
+sub get_grub2_append {
+ my ($bootloader) = @_;
+ # get default parameters from first entry:
+ my ($entry) = get_grub2_first_entry($bootloader);
+ my $append = $entry->{append};
+ if (my $vga = $entry->{vga} || $bootloader->{vga}) {
+ $append .= " vga=$vga";
+ }
+ $append =~ s/root=\S+//g;
+ $append =~ s/\bro\b//g;
+ $append =~ s/\s+/ /g;
+ $append;
+}
+
+sub crypt_grub2_password {
+ my ($password) = @_;
+ require IPC::Open2;
+ local $ENV{LC_ALL} = 'C';
+ my ($his_out, $his_in);
+ my $pid = IPC::Open2::open2($his_out, $his_in, "$::prefix/bin/grub2-mkpasswd-pbkdf2");
+
+ my ($line, $res);
+ while (sysread($his_out, $line, 100)) {
+ if ($line =~ /enter.*password:/i) {
+ syswrite($his_in, "$password\n");
+ } else {
+ chomp($line);
+ $res .= $line if $line;
+ }
+ }
+ $res =~ s/^PBKDF2 hash of your password is //;
+ waitpid($pid, 0);
+ my $status = $? >> 8;
+ die "failed to encrypt password (status=$status)" if $status != 0;
+ chomp_($res);
+}
+
+sub write_grub2_sysconfig {
+ my ($bootloader, $_all_hds, $o_backup_extension) = @_;
+
+ # Set password prior to run update-grub2:
+ my $pw_f = get_grub2_users();
+ if ($bootloader->{password}) {
+ if (!is_grub2_already_crypted($bootloader->{password})) {
+ $bootloader->{password} = crypt_grub2_password($bootloader->{password});
+ }
+ output_with_perm($pw_f, 0600, "GRUB2_PASSWORD=$bootloader->{password}");
+ } else {
+ unlink($pw_f);
+ }
+
+ my $f = "$::prefix/etc/default/grub";
+ my %conf = getVarsFromSh($f);
+
+ my $append = $bootloader->{perImageAppend} || get_grub2_append($bootloader);
+ my $vga = $bootloader->{default_vga};
+ $append .= " vga=$vga" if $append !~ /vga=/ && $vga && $vga ne "normal";
+
+ $conf{GRUB_CMDLINE_LINUX_DEFAULT} = $append;
+ $conf{GRUB_GFXPAYLOAD_LINUX} = 'auto' if is_uefi();
+ $conf{GRUB_DISABLE_RECOVERY} = 'false'; # for 'failsafe' entry
+ $conf{GRUB_DEFAULT} //= 'saved'; # for default entry but do not overwrite user choice
+ $conf{GRUB_SAVEDEFAULT} //= 'true'; # for default entry but do not overwrite user choice
+ # special case so that setVarsInSh() doesn't emit the line when timeout is 0
+ $conf{GRUB_TIMEOUT} = $bootloader->{timeout} eq 0 ? "0 " : $bootloader->{timeout};
+ renamef($f, $f . ($o_backup_extension || '.old'));
+ setVarsInSh($f, \%conf);
+}
+
+sub write_grub2_default_entry {
+ my ($bootloader, $_all_hds, $o_backup_extension) = @_;
+
+ my $default = $bootloader->{default};
+ # menu entry must be identified by its full path. eg: "submenu1>submenu2>title":
+ if (my $def = find { $_->{label} eq $bootloader->{default} } @{$bootloader->{entries}}) {
+ $default = $def->{real_label} if $def->{real_label};
+ }
+
+ # set default entry:
+ eval {
+ my $f2 = "$::prefix/boot/grub2/grubenv";
+ cp_af($f2, $f2 . ($o_backup_extension || '.old'));
+ my $error;
+ run_program::rooted($::prefix, 'grub2-set-default', '2>', \$error, $default) or die "grub2-set-default failed: $error";
+ };
+ if (my $err = $@) {
+ log::l("error while running grub2-set-default: $err");
+ }
+}
+
+sub write_grub2 {
+ my ($bootloader, $o_all_hds, $o_backup_extension) = @_;
+ my $error;
+
+ write_grub2_sysconfig($bootloader, $o_all_hds, $o_backup_extension);
+
+ my $f1 = "$::prefix/boot/grub2/grub.cfg";
+ #- we won't just rename as grub2-mkconfig (more likely os-prober) may fail:
+ cp_af($f1, $f1 . '.old') if -e $f1;
+ #- don't use the update-grub2 script here, it hangs when run by service_harddrake
+ #- during boot because it causes systemd to try to start a user session
+ run_program::rooted($::prefix, 'grub2-mkconfig', '2>', \$error, '-o', '/boot/grub2/grub.cfg') or die "grub2-mkconfig failed: $error";
+ log::l("grub2-mkconfig logs: $error");
+
+ write_grub2_default_entry($bootloader, $o_all_hds, $o_backup_extension);
+ check_enough_space();
+}
+
+sub get_grub2_users() {
+ "$::prefix/boot/grub2/user.cfg";
+}
+
+sub get_grub2_install_sh() {
+ "$::prefix/boot/grub2/install.sh";
+}
+
+sub write_grub2_install_sh {
+ my ($bootloader, $o_backup_extension) = @_;
+ my $f = get_grub2_install_sh();
+ my $boot = $bootloader->{boot};
+ my @options;
+ if (is_uefi()) {
+ if ($bootloader->{no_esp_or_mbr}) {
+ push @options, qw(--bootloader-id=tmp --no-nvram);
+ } elsif ($bootloader->{removable}) {
+ push @options, '--removable';
+ }
+ } else {
+ @options = $bootloader->{no_esp_or_mbr} ? ('--grub-setup=/bin/true', $boot) : $boot;
+ }
+ renamef($f, $f . ($o_backup_extension || '.old'));
+ output_with_perm($f, 0755, join(' ', 'grub2-install', @options));
+}
+
sub write_grub {
- my ($bootloader, $all_hds) = @_;
+ my ($bootloader, $all_hds, $o_backup_extension) = @_;
my $fstab = [ fs::get::fstab($all_hds) ];
my @legacy_floppies = detect_devices::floppies();
@@ -1657,7 +2207,7 @@ sub write_grub {
$bootloader->{terminal} ||= "--timeout=" . ($bootloader->{timeout} || 0) . " console serial";
} elsif ($bootloader->{method} eq 'grub-graphic') {
my $bin = '/usr/sbin/grub-gfxmenu';
- if ($bootloader->{gfxmenu} eq '' && -x "$::prefix/usr/sbin/grub-gfxmenu") {
+ if ($bootloader->{gfxmenu} eq '' && -x "$::prefix$bin") {
my $locale = $::o->{locale} || do { require lang; lang::read() };
run_program::rooted($::prefix, $bin, '--lang', $locale->{lang}, '--update-gfxmenu');
$bootloader->{gfxmenu} ||= '/boot/gfxmenu';
@@ -1668,10 +2218,21 @@ sub write_grub {
delete $bootloader->{gfxmenu};
}
+ my $format = sub { map { "$_ $bootloader->{$_}" } @_ };
+
{
my @conf;
- push @conf, map { "$_ $bootloader->{$_}" } grep { $bootloader->{$_} } qw(timeout color password serial shade terminal viewport background foreground);
+ if ($bootloader->{password}) {
+ if (!is_already_crypted($bootloader->{password})) {
+ my $encrypted = crypt_grub_password($bootloader->{password});
+ $bootloader->{password} = "--md5 $encrypted";
+ }
+ }
+
+ push @conf, $format->(grep { defined $bootloader->{$_} } qw(timeout));
+ push @conf, $format->(grep { $bootloader->{$_} } qw(color password serial shade terminal viewport background foreground));
+
push @conf, map { $_ . ' ' . $file2grub->($bootloader->{$_}) } grep { $bootloader->{$_} } qw(gfxmenu);
eval {
@@ -1691,13 +2252,24 @@ sub write_grub {
my $vga = $entry->{vga} || $bootloader->{vga};
push @conf, join(' ', $entry->{xen} ? 'module' : 'kernel',
$file2grub->($entry->{kernel_or_dev}),
- $entry->{xen} ? '' : 'BOOT_IMAGE=' . simplify_label($entry->{label}),
+ $entry->{xen} ? () : 'BOOT_IMAGE=' . simplify_label($entry->{label}),
if_($entry->{root}, $entry->{root} =~ /loop7/ ? "root=707" : "root=$entry->{root}"), #- special to workaround bug in kernel (see #ifdef CONFIG_BLK_DEV_LOOP)
$entry->{append},
if_($entry->{'read-write'}, 'rw'),
if_($vga && $vga ne "normal", "vga=$vga"));
push @conf, "module " . $_ foreach @{$entry->{modules} || []};
- push @conf, join(' ', $entry->{xen} ? 'module' : 'initrd', $file2grub->($entry->{initrd})) if $entry->{initrd};
+ if ($entry->{initrd}) {
+ # split partition from initrd path and place
+ # it to a separate 'root' entry.
+ # Grub2's mkconfig takes initrd entry 'as is',
+ # but grub2 fails to load smth like '(hd0,1)/boot/initrd' taken from grub-legacy
+ my $initrd_path = $file2grub->($entry->{initrd});
+ if ($initrd_path =~ /^(\([^\)]+\))/) {
+ push @conf, "root $1";
+ $initrd_path =~ s/^(\([^\)]+\))//;
+ }
+ push @conf, join(' ', $entry->{xen} ? 'module' : 'initrd', $initrd_path);
+ }
} else {
my $dev = eval { device_string2grub($entry->{kernel_or_dev}, \@legacy_floppies, \@sorted_hds) };
if (!$dev) {
@@ -1706,7 +2278,9 @@ sub write_grub {
}
push @conf, $title;
push @conf, grep { $entry->{$_} } 'lock';
- push @conf, join(' ', $entry->{rootnoverify} ? 'rootnoverify' : 'root', $dev);
+ if ($entry->{type} ne 'grub_configfile' || $entry->{configfile} !~ /grub\.cfg/ || !$entry->{root}) {
+ push @conf, join(' ', $entry->{rootnoverify} ? 'rootnoverify' : 'root', $dev);
+ }
if ($entry->{table}) {
if (my $hd = fs::get::device2part($entry->{table}, \@sorted_hds)) {
@@ -1721,8 +2295,12 @@ sub write_grub {
push @conf, map_each { "map ($::b) ($::a)" } %{$entry->{mapdrive}};
}
push @conf, "makeactive" if $entry->{makeactive};
- if ($entry->{type} eq 'grub_configfile') {
+ # grub.cfg is grub2 config, can't use it as configfile for grub-legacy
+ if ($entry->{type} eq 'grub_configfile' && $entry->{configfile} !~ /grub\.cfg/) {
push @conf, "configfile $entry->{configfile}";
+ } elsif ($entry->{linux}) {
+ push @conf, "root $entry->{root}", "kernel $entry->{linux}";
+ push @conf, "initrd $entry->{initrd}" if $entry->{initrd};
} else {
push @conf, "chainloader +1";
}
@@ -1730,14 +2308,14 @@ sub write_grub {
}
my $f = "$::prefix/boot/grub/menu.lst";
log::l("writing grub config to $f");
- renamef($f, "$f.old");
- output($f, map { "$_\n" } @conf);
+ renamef($f, $f . ($o_backup_extension || '.old'));
+ output_with_perm($f, 0600, map { "$_\n" } @conf);
}
{
my $f = "$::prefix/boot/grub/install.sh";
my $boot_dev = device_string2grub($bootloader->{boot}, \@legacy_floppies, \@sorted_hds);
my $files_dev = device2grub(fs::get::root_($fstab, 'boot'), \@sorted_hds);
- renamef($f, "$f.old");
+ renamef($f, $f . ($o_backup_extension || '.old'));
output_with_perm($f, 0755,
"grub --device-map=/boot/grub/device.map --batch <<EOF
root $files_dev
@@ -1789,6 +2367,26 @@ sub restore_previous_MBR_bootloader {
output($dev, scalar cat_(_dev_to_MBR_backup($dev)));
}
+sub install_grub2 {
+ my ($bootloader, $all_hds) = @_;
+ write_grub2($bootloader, $all_hds);
+ write_grub2_install_sh($bootloader, '.old');
+ install_raw_grub2();
+}
+
+sub install_raw_grub2() {
+ my $error;
+ my $f = '/boot/grub2/install.sh';
+ my ($right_dir, $bad_dir) = ("$::prefix/boot/EFI/EFI/", "$::prefix/boot/EFI/efi/");
+ if (-e $bad_dir && ! -e $right_dir) {
+ renamef($bad_dir, $right_dir);
+ }
+ if (!run_program::rooted($::prefix, "sh", "2>", \$error, $f)) {
+ log::explanations("grub2-install failed:\n(" . cat_($f) . ")\nError: <$error>");
+ die "grub2-install failed: $error";
+ }
+}
+
sub install_grub {
my ($bootloader, $all_hds) = @_;
@@ -1796,7 +2394,7 @@ sub install_grub {
if (!$::testing) {
if ($bootloader->{previous_boot} && $bootloader->{previous_boot} eq $bootloader->{boot}) {
- # nothing to do
+ # nothing to do (already installed in {boot})
} else {
if ($bootloader->{previous_boot}) {
restore_previous_MBR_bootloader(delete $bootloader->{previous_boot});
@@ -1821,6 +2419,11 @@ sub install_raw_grub() {
run_program::rooted($::prefix, "sh", "2>", \$error, '/boot/grub/install.sh') or die "grub failed: $error";
}
+sub when_config_changed_grub2 {
+ my ($_bootloader) = @_;
+ #- do not do anything
+}
+
sub when_config_changed_grub {
my ($_bootloader) = @_;
#- do not do anything
@@ -1828,6 +2431,187 @@ sub when_config_changed_grub {
update_copy_in_boot($_) foreach glob($::prefix . boot_copies_dir() . '/*.link');
}
+sub write_refind {
+ my ($bootloader, $_all_hds, $o_backup_extension) = @_;
+
+ my @config;
+
+ foreach my $entry (@{$bootloader->{entries}}) {
+ if ($entry->{type} eq 'image' && $entry->{kernel_or_dev} eq '/boot/vmlinuz') {
+ my $vga = $entry->{vga} || $bootloader->{vga};
+ my $boot_params = join(' ',
+ "root=$entry->{root}",
+ $entry->{append},
+ if_($entry->{'read-write'}, 'rw'),
+ if_($vga && $vga ne "normal", "vga=$vga")
+ );
+ push @config, '"' . $entry->{label} . '" "' . $boot_params . '"';
+ }
+ }
+ if (@config) {
+ my $f = "$::prefix/boot/refind_linux.conf";
+ log::l("writing rEFInd config to $f");
+ renamef($f, $f . ($o_backup_extension || '.old'));
+ output_with_perm($f, 0600, map { "$_\n" } @config);
+ check_enough_space();
+ } else {
+ log::l("config has no entries - rEFInd config file not written");
+ }
+
+ my $default_kernel = readlink("$::prefix/boot/vmlinuz");
+ if ($default_kernel) {
+ if ($bootloader->{use_nvram}) {
+ write_refind_previous_boot_var($default_kernel);
+ } else {
+ write_refind_previous_boot_file($default_kernel, "$::prefix/boot/EFI/EFI/refind");
+ write_refind_previous_boot_file($default_kernel, "$::prefix/boot/EFI/EFI/BOOT");
+ }
+ }
+}
+
+sub write_refind_previous_boot_var {
+ my ($kernel) = @_;
+ my ($efivars, $already_mounted) = mount_efivars();
+ my $previous_boot = "$efivars/PreviousBoot-36d08fa7-cf0b-42f5-8f14-68df73ed3740";
+ run_program::run('chattr', '-i', $previous_boot) if -e $previous_boot;
+ if (open(my $efivar, '>:raw', $previous_boot)) {
+ require Encode;
+ log::l("writing rEFInd PreviousBoot variable");
+ print $efivar "\x07\x00\x00\x00";
+ print $efivar Encode::encode('UTF16-LE', $kernel);
+ print $efivar "\x00\x00";
+ close($efivar);
+ } else {
+ log::l("failed to write rEFInd PreviousBoot variable: $@ ($!)");
+ }
+ run_program::run('umount', $efivars) if !$already_mounted;
+}
+
+sub write_refind_previous_boot_file {
+ my ($kernel, $base_path) = @_;
+ return if ! -e "$base_path/refind.conf" || ! mkdir_p("$base_path/vars");
+ if (open(my $f, '>:raw', "$base_path/vars/PreviousBoot")) {
+ require Encode;
+ log::l("writing rEFInd $base_path/vars/PreviousBoot file");
+ print $f Encode::encode('UTF16-LE', "Boot boot\\$kernel");
+ print $f "\x00\x00";
+ close($f);
+ } else {
+ log::l("failed to write rEFInd $base_path/vars/PreviousBoot file");
+ }
+}
+
+sub install_refind {
+ my ($bootloader, $all_hds) = @_;
+
+ if ($bootloader->{install_mode} ne 'no_install') {
+ my (@options, $error);
+ if ($bootloader->{install_mode} eq 'as_default') {
+ $bootloader->{esp_device} or die "ESP device is unknown";
+ push @options, '--usedefault';
+ push @options, $bootloader->{esp_device};
+ #- refind_install uses lower case file names. If a default bootloader was previously
+ #- created with an upper case file name, Linux won't overwrite it.
+ my $default_fn = "$::prefix/boot/EFI/EFI/BOOT/BOOT" . uc(uefi_type()) . ".EFI";
+ unlink($default_fn) if -e $default_fn;
+ } elsif ($bootloader->{install_mode} eq 'nvram_only') {
+ push @options, '--nvramonly';
+ }
+ run_program::rooted($::prefix, '/sbin/refind-install', '2>', \$error, @options)
+ or die "refind-install failed: $error";
+ }
+
+ #- This file is not used by rEFInd itself. It just defines the paths to the image
+ #- files used for the standard banner choices.
+ my %h = getVarsFromSh("$::prefix/etc/sysconfig/refind");
+
+ my $banner_source;
+ if ($bootloader->{banner_path} eq 'refind_banner.png') {
+ $banner_source = "$::prefix" . $h{REFIND_BANNER};
+ } elsif ($bootloader->{banner_path} eq 'mageia_theme.png') {
+ $banner_source = "$::prefix" . $h{MAGEIA_THEME};
+ }
+ if (defined $banner_source && ! (-f $banner_source && -r $banner_source)) {
+ log::l("$banner_source does not exist or is not readable");
+ $bootloader->{banner_path} = '';
+ $bootloader->{banner_scale} = 'noscale';
+ undef $banner_source;
+ }
+
+ #- Try both possible locations for the main config file.
+ modify_refind_config($bootloader, $banner_source, "$::prefix/boot/EFI/EFI/refind");
+ modify_refind_config($bootloader, $banner_source, "$::prefix/boot/EFI/EFI/BOOT");
+
+ write_refind($bootloader, $all_hds);
+}
+
+sub modify_refind_config {
+ my ($bootloader, $banner_source, $esp_dir) = @_;
+
+ my $config_file = "$esp_dir/refind.conf";
+ return if ! -f $config_file || ! -w $config_file;
+
+ my $use_nvram = $bootloader->{use_nvram} ? 'true' : 'false';
+
+ my $banner_path = $bootloader->{banner_path};
+ cp_f($banner_source, "$esp_dir/$banner_path") if (defined $banner_source);
+
+ my $banner_scale = $bootloader->{banner_scale};
+
+ my @config;
+
+ my %done;
+ foreach (cat_utf8($config_file)) {
+ if ($_ =~ /^#?use_nvram\s/) {
+ if (! $done{use_nvram}) {
+ push @config, "use_nvram $use_nvram\n" ;
+ $done{use_nvram} = 1;
+ }
+ } elsif ($_ =~ /^#?banner\s/) {
+ if (! $done{banner_path}) {
+ if ($banner_path eq '') {
+ push @config, "#banner my_banner.png\n";
+ } else {
+ push @config, "banner $banner_path\n";
+ }
+ $done{banner_path} = 1;
+ }
+ } elsif ($_ =~ /^#?banner_scale\s/) {
+ if (! $done{banner_scale}) {
+ push @config, "banner_scale $banner_scale\n";
+ $done{banner_scale} = 1;
+ }
+ } else {
+ push @config, $_;
+ }
+ }
+
+ if (@config) {
+ log::l("writing rEFInd config to $config_file");
+ renamef($config_file, $config_file . '.old');
+ output_with_perm($config_file, 0600, @config);
+ } else {
+ log::l("config has no entries - rEFInd config file not written");
+ }
+}
+
+sub when_config_changed_refind {
+ my ($_bootloader) = @_;
+ #- do not do anything
+}
+
+=item action($bootloader, $action, @para)
+
+Calls the C<$action> function with @para parameters:
+
+ $actions->($bootloader, @para)
+
+If needed, the function name will be resolved to call a boot loader specific function (eg: for LILO/GRUB/...)
+
+This Swiss army knife function is heavily used by eg /sbin/bootloader-config
+
+=cut
+
sub action {
my ($bootloader, $action, @para) = @_;
@@ -1836,30 +2620,104 @@ sub action {
$f->($bootloader, @para);
}
+=item install($bootloader, $all_hds)
+
+Writes back the boot loader config. Calls the proper write_XYZ() function.
+
+=cut
+
sub install {
my ($bootloader, $all_hds) = @_;
- if (my $part = fs::get::device2part($bootloader->{boot}, [ fs::get::fstab($all_hds) ])) {
- die N("You can not install the bootloader on a %s partition\n", $part->{fs_type})
- if $part->{fs_type} eq 'xfs';
- }
- $bootloader->{keytable} = keytable($bootloader->{keytable});
+ my ($efivars, $already_mounted) = mount_efivars() if is_uefi();
+
+ $bootloader->{keytable} = keytable($bootloader->{keytable}) if $bootloader->{method} eq 'lilo';
action($bootloader, 'install', $all_hds);
+
+ run_program::run('umount', $efivars) if is_uefi() && !$already_mounted;
+}
+
+sub get_grub2_pkg() {
+ my ($prefix, $pkg);
+ if (is_uefi()) {
+ my %convert = (ia32 => 'i386', aa64 => 'arm64', x64 => 'x86_64');
+ my %pkgs = (ia32 => 'x86-32', aa64 => 'aarch-64', x64 => 'x86-64');
+ $prefix = $convert{uefi_type()} . "-efi";
+ $pkg = "grub2-efi(" . $pkgs{uefi_type()} . ")";
+ } else {
+ $prefix = 'i386-pc';
+ $pkg = 'grub2';
+ }
+ ($prefix, $pkg);
}
sub ensure_pkg_is_installed {
my ($do_pkgs, $bootloader) = @_;
- my $main_method = bootloader::main_method($bootloader->{method});
- if ($main_method eq 'grub' || $main_method eq 'lilo') {
+ my %suppl = (
+ # method => [ 'pkg_name', 'file_to_test' ],
+ 'grub-graphic' => [ qw(mageia-gfxboot-theme /usr/share/gfxboot/themes/Mageia/boot/message) ],
+ 'grub2-graphic' => [ qw(grub2-mageia-theme /boot/grub2/themes/maggy/theme.txt) ],
+ );
+ my $main_method = main_method($bootloader->{method});
+ if ($main_method eq 'grub2') {
+ my ($prefix, $pkg) = get_grub2_pkg();
+ $do_pkgs->ensure_is_installed($pkg, "/usr/lib/grub/$prefix/ext2.mod", 1) or return 0;
+ } elsif ($main_method eq 'refind') {
+ $do_pkgs->ensure_is_installed('refind', '/sbin/refind-install', 1) or return 0;
+ } elsif (member($main_method, qw(grub grub2 lilo))) {
$do_pkgs->ensure_binary_is_installed($main_method, $main_method, 1) or return 0;
- if ($bootloader->{method} eq 'grub-graphic') {
- $do_pkgs->ensure_is_installed('mandriva-gfxboot-theme', '/usr/share/gfxboot/themes/Mandriva/boot/message', 1) or return 0;
- }
+ }
+ # Install gfx theme if needed:
+ if (my $pkg = $suppl{$bootloader->{method}}) {
+ $do_pkgs->ensure_is_installed(@$pkg, 1) or return 0;
}
1;
}
+sub parse_grub2_config {
+ my ($l, $grubcfg, $part) = @_;
+
+ my ($linux, $menuentry, $root, $root_dev, $initrd);
+
+ foreach (cat_($grubcfg)) {
+ chomp;
+ if (/^menuentry\s+['"]([^']+)["']/) {
+ if ($menuentry && $root) {
+ my $parttype = partition_table::raw::typeOfMBR($root_dev);
+ if ((!$parttype || $parttype eq "empty") && $linux) {
+ push @$l, { menuentry => $menuentry, bootpart => $part, root => $root, linux => $linux, initrd => $initrd, grub_conf => $grubcfg };
+ }
+ }
+ $menuentry = $1;
+ $root = $linux = undef;
+ } elsif (/set root='(\([^\)]+\))'/) {
+ $root = $1;
+
+ if ($root =~ /\(([^,]+),msdos(\d+)\)/) {
+ my $dev_title = "/" . $1;
+ my $part_num = $2;
+ my $dec_part_num = $part_num-1;
+ $dev_title =~ s!hd!dev/sd!;
+ $dev_title =~ tr/0123456789/abcdefghi/;
+
+ $root_dev = $part_num ? $dev_title . $part_num : $dev_title;
+ $root =~ s/msdos$part_num/$dec_part_num/;
+ }
+ } elsif (/^\s+linux\s+(.+)/) {
+ $linux = $1;
+ } elsif (/^\s+initrd\s+(.+)/) {
+ $initrd = $1;
+ }
+ }
+}
+
+=item find_other_distros_grub_conf($fstab)
+
+Returns a list of other distros' grub.conf
+
+=cut
+
sub find_other_distros_grub_conf {
my ($fstab) = @_;
@@ -1878,6 +2736,15 @@ sub find_other_distros_grub_conf {
my $f = find { -e "$handle->{dir}$bootdir/$_" } 'grub.conf', 'grub/menu.lst' or next;
push @l, { bootpart => $part, bootdir => $bootdir, grub_conf => "$bootdir/$f" };
}
+ foreach my $bootdir ('', '/boot', '/boot/grub', '/boot/grub2') {
+ my $f = find { -e "$handle->{dir}$bootdir/$_" } 'grub.cfg' or next;
+ my $parttype = partition_table::raw::typeOfMBR($part->{device});
+ if (!$parttype || $parttype eq "empty") {
+ parse_grub2_config(\@l, "$handle->{dir}/$bootdir/$f", $part);
+ } else {
+ push @l, { bootpart => $part, bootdir => $bootdir, grub_conf => "$bootdir/$f" };
+ }
+ }
if (my $f = common::release_file($handle->{dir})) {
my $h = common::parse_release_file($handle->{dir}, $f, $part);
$h->{name} = $h->{release};
@@ -1974,10 +2841,14 @@ sub update_for_renumbered_partitions {
} $main_method ? $main_method : ('lilo', 'grub');
if (intersection(\@needed, [ map { $_->{name} } @changed_configs ])) {
- $in->ask_warn('', N("The bootloader can not be installed correctly. You have to boot rescue and choose \"%s\"",
+ $in->ask_warn('', N("The bootloader cannot be installed correctly. You have to boot rescue and choose \"%s\"",
N("Re-install Boot Loader")));
}
1;
}
+=back
+
+=cut
+
1;