diff options
Diffstat (limited to 'perl-install/bootloader.pm')
-rw-r--r-- | perl-install/bootloader.pm | 1450 |
1 files changed, 1146 insertions, 304 deletions
diff --git a/perl-install/bootloader.pm b/perl-install/bootloader.pm index 10f8062da..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,22 +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}); @@ -130,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) = @_; @@ -161,11 +259,25 @@ sub add_boot_splash { sub update_splash { my ($bootloader) = @_; + my %real_initrd_entries; foreach (@{$bootloader->{entries}}) { - 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) ]; @@ -173,7 +285,7 @@ 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}; @@ -184,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($_)) { @@ -198,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) = @_; @@ -228,10 +444,22 @@ sub read_grub { $bootloader; } -# adapts device.map (aka $grub2dev) when for example hda is now sda -# nb: -# - $boot_part comes from /boot/grub/install.sh "root (hd...)" line -# - $grub2dev is /boot/grub/device.map + +=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) = @_; @@ -258,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; @@ -323,6 +557,24 @@ sub _parse_grub_menu_lst() { %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) = @_; @@ -335,9 +587,16 @@ sub read_grub_menu_lst { #- sanitize foreach my $e (@{$b{entries}}) { if (member($e->{type}, 'other', 'grub_configfile')) { - $e->{kernel_or_dev} = grub2dev($e->{rootnoverify} || $e->{grub_root}, $grub2dev); + eval { $e->{kernel_or_dev} = grub2dev($e->{rootnoverify} || $e->{grub_root}, $grub2dev) }; + $e->{keep_verbatim} = 1 unless $e->{kernel_or_dev}; } elsif ($e->{initrd}) { - $e->{initrd} = grub2file($e->{initrd}, $grub2dev, $fstab, $e); + 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/) { @@ -350,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) { @@ -371,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] }); @@ -511,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"); @@ -519,16 +858,41 @@ sub suggest_onmbr { ($onmbr, $unsafe); } -# list of places where we can install the bootloader + +=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}}, # MBR + 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)), # filesystems except those who do not leave space for our bootloaders + (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(), ); } @@ -560,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; @@ -631,18 +995,6 @@ 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) = @_; @@ -703,18 +1055,14 @@ 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"; @@ -725,19 +1073,35 @@ sub add_kernel { $v->{initrd} = mkinitrd($kernel_str->{version}, $bootloader, $v, "/boot/$initrd_long"); } + 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); + _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) = @_; @@ -853,7 +1217,12 @@ sub set_append_netprofile { $e->{append} = pack_append($simple, $dict); } -# used when a bootloader $entry has been modified (eg: $entry->{vga}) +=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; @@ -871,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}}) { @@ -899,6 +1260,9 @@ sub get_kernel_labels { $labels{$_->{ext}} = 1; } } + + $kernels_str[0]{ext} = ''; + @kernels_str; } @@ -906,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) = @_; @@ -927,27 +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; } -# for lilo +=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! @@ -957,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) { @@ -975,44 +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 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} } @{$all_hds->{hds}}; + $mbr = find { !$_->{is_removable} } allowed_boot_disks($all_hds); } my ($onmbr, $unsafe) = $bootloader->{crushMbr} ? (1, 0) : suggest_onmbr($mbr); - 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, - } : + add2hash_($bootloader, { bootUnsafe => $unsafe, entries => [], timeout => $onmbr && 10, nowarn => 1, - if_(arch() !~ /ia64/, 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); @@ -1025,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') { @@ -1039,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 { @@ -1078,9 +1409,8 @@ 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; } @@ -1088,14 +1418,31 @@ sub suggest { $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); + } } } } @@ -1116,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 { @@ -1128,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) ]; @@ -1156,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)); @@ -1183,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 { @@ -1314,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 { @@ -1373,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(.*)/) { @@ -1390,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}; @@ -1424,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); @@ -1444,7 +1771,7 @@ 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; } @@ -1494,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}); } @@ -1577,8 +1904,14 @@ sub write_grub_device_map { (map_index { "(hd$::i) /dev/$_->{device}\n" } @$sorted_hds)); } -# parses things like "(hd0,4)/boot/vmlinuz" -# returns: ("hd0", 4, "boot/vmlinuz") +=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; @@ -1586,8 +1919,14 @@ sub parse_grub_file { ($hd, $part, $rel_file); } -# takes things like "(hd0,4)/boot/vmlinuz" -# returns: ("/dev/sda5", "boot/vmlinuz") +=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; @@ -1596,16 +1935,34 @@ sub grub2dev_and_file { my $device = '/dev/' . ($part eq '' ? $grub2dev->{$hd} : devices::prefix_for_dev($grub2dev->{$hd}) . $part); $device, $rel_file; } -# takes things like "(hd0,4)/boot/vmlinuz" -# returns: "/dev/sda5" + +=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)); } -# replaces -# - "/vmlinuz" with "/boot/vmlinuz" when "root" or "rootnoverify" is set for the entry -# - "(hdX,Y)" in "(hdX,Y)/boot/vmlinuz..." by appropriate path if possible/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) = @_; @@ -1655,6 +2012,170 @@ 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, $o_backup_extension) = @_; @@ -1686,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'; @@ -1697,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 { @@ -1726,7 +2258,18 @@ sub write_grub { 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) { @@ -1735,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)) { @@ -1750,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"; } @@ -1760,7 +2309,7 @@ sub write_grub { my $f = "$::prefix/boot/grub/menu.lst"; log::l("writing grub config to $f"); renamef($f, $f . ($o_backup_extension || '.old')); - output($f, map { "$_\n" } @conf); + output_with_perm($f, 0600, map { "$_\n" } @conf); } { my $f = "$::prefix/boot/grub/install.sh"; @@ -1818,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) = @_; @@ -1850,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 @@ -1857,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) = @_; @@ -1865,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 %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 'grub' || $main_method eq 'lilo') { + 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) = @_; @@ -1907,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}; @@ -2003,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; |