package bootloader; # $Id$
use diagnostics;
use strict;
#-######################################################################################
#- misc imports
#-######################################################################################
use common;
use fs::type;
use fs::get;
use fs::loopback;
use fs::proc_partitions;
use log;
use any;
use devices;
use detect_devices;
use partition_table::raw;
use run_program;
use modules;
#-#####################################################################################
#- Functions
#-#####################################################################################
my $vmlinuz_regexp = 'vmlinuz|win4lin';
my $decompose_vmlinuz_name = qr/((?:$vmlinuz_regexp).*?)-(\d+\.\d+.*)/;
sub expand_vmlinuz_symlink {
my ($vmlinuz) = @_;
my $f = $::prefix . ($vmlinuz =~ m!^/! ? $vmlinuz : "/boot/$vmlinuz");
-l $f ? readlink($f) : $vmlinuz;
}
sub installed_vmlinuz_raw() { grep { /^($vmlinuz_regexp)/ } all("$::prefix/boot") }
sub installed_vmlinuz() { grep { ! -l "$::prefix/boot/$_" } installed_vmlinuz_raw() }
sub vmlinuz2version {
my ($vmlinuz) = @_;
expand_vmlinuz_symlink($vmlinuz) =~ /$decompose_vmlinuz_name/ && $2;
}
sub vmlinuz2basename {
my ($vmlinuz) = @_;
expand_vmlinuz_symlink($vmlinuz) =~ /$decompose_vmlinuz_name/ && $1;
}
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"
'initrd' . ($basename ? "-$basename" : '');
}
sub kernel_str2vmlinuz_long {
my ($kernel) = @_;
$kernel->{basename} . '-' . $kernel->{version};
}
sub kernel_str2initrd_long {
my ($kernel) = @_;
basename2initrd_basename($kernel->{basename}) . '-' . $kernel->{version} . '.img';
}
sub kernel_str2vmlinuz_short {
my ($kernel) = @_;
if ($kernel->{use_long_name}) {
kernel_str2vmlinuz_long($kernel);
} else {
my $ext = $kernel->{ext} ? "-$kernel->{ext}" : '';
$kernel->{basename} . $ext;
}
}
sub kernel_str2initrd_short {
my ($kernel) = @_;
if ($kernel->{use_long_name}) {
kernel_str2initrd_long($kernel);
} else {
my $ext = $kernel->{ext} ? "-$kernel->{ext}" : '';
basename2initrd_basename($kernel->{basename}) . $ext . '.img';
}
}
sub vmlinuz2kernel_str {
my ($vmlinuz) = @_;
my ($basename, $version) = expand_vmlinuz_symlink($vmlinuz) =~ /$decompose_vmlinuz_name/ or return;
{
basename => $basename,
version => $version,
$version =~ /(.*mdk)-?(.*)/ ? (ext => $2, version_no_ext => $1) : (version_no_ext => $version),
};
}
sub kernel_str2label {
my ($kernel, $o_use_long_name) = @_;
my $base = $kernel->{basename} eq 'vmlinuz' ? 'linux' : $kernel->{basename};
$o_use_long_name || $kernel->{use_long_name} ?
sanitize_ver($base, $kernel) :
$kernel->{ext} ? "$base-$kernel->{ext}" : $base;
}
sub get {
my ($vmlinuz, $bootloader) = @_;
$_->{kernel_or_dev} && $_->{kernel_or_dev} eq $vmlinuz and return $_ foreach @{$bootloader->{entries}};
undef;
}
sub get_label {
my ($label, $bootloader) = @_;
$_->{label} && lc(make_label_lilo_compatible($_->{label})) eq lc(make_label_lilo_compatible($label)) and return $_ foreach @{$bootloader->{entries}};
undef;
}
sub mkinitrd {
my ($kernel_version, $entry) = @_;
my $initrd = $entry->{initrd};
$::testing || -e "$::prefix/$initrd" and return 1;
my $loop_boot = fs::loopback::prepare_boot();
modules::load('loop');
my @options = (
"-v", "-f", $initrd, "--ifneeded", $kernel_version,
if_($entry->{initrd_options}, split(' ', $entry->{initrd_options})),
);
if (!run_program::rooted($::prefix, 'mkinitrd', @options)) {
unlink("$::prefix/$initrd");
die "mkinitrd failed:\n(mkinitrd @options))";
}
add_boot_splash($entry->{initrd}, $entry->{vga});
fs::loopback::save_boot($loop_boot);
-e "$::prefix/$initrd";
}
sub remove_boot_splash {
my ($initrd) = @_;
run_program::rooted($::prefix, '/usr/share/bootsplash/scripts/remove-boot-splash', $initrd);
}
sub add_boot_splash {
my ($initrd, $vga) = @_;
$vga or return;
require Xconfig::resolution_and_depth;
if (my $res = Xconfig::resolution_and_depth::from_bios($vga)) {
run_program::rooted($::prefix, '/usr/share/bootsplash/scripts/make-boot-splash', $initrd, $res->{X});
} else {
log::l("unknown vga bios mode $vga");
}
}
sub read {
my ($all_hds) = @_;
my $fstab = [ fs::get::fstab($all_hds) ];
foreach my $main_method (main_method_choices()) {
my $f = $bootloader::{"read_$main_method"} or die "unknown bootloader method $main_method (read)";
my $bootloader = $f->($fstab);
cleanup_entries($bootloader);
my @devs = $bootloader->{boot};
if ($bootloader->{'raid-extra-boot'} =~ /mbr/ &&
(my $md = fs::get::device2part($bootloader->{boot}, $all_hds->{raids}))) {
@devs = map { $_->{rootDevice} } @{$md->{disks}};
} elsif ($bootloader->{'raid-extra-boot'} =~ m!/dev/!) {
@devs = split(',', $bootloader->{'raid-extra-boot'});
}
my ($type) = map {
if (m!/fd\d+$!) {
warn "not checking the method on floppy, assuming $main_method is right\n";
$main_method;
} elsif ($main_method eq 'yaboot') {
#- not checking on ppc, there's only yaboot anyway :)
$main_method;
} elsif ($main_method eq 'cromwell') {
#- XBox
$main_method;
} elsif (my $type = partition_table::raw::typeOfMBR($_)) {
warn "typeOfMBR $type on $_ for method $main_method\n" if $ENV{DEBUG};
$type;
} else { () }
} @devs;
if ($type eq $main_method) {
my @prefered_entries = map { get_label($_, $bootloader) } $bootloader->{default}, 'linux';
if (my $default = find { $_ && $_->{type} eq 'image' } (@prefered_entries, @{$bootloader->{entries}})) {
$bootloader->{default_vga} = $default->{vga};
$bootloader->{perImageAppend} ||= $default->{append};
log::l("perImageAppend is now $bootloader->{perImageAppend}");
}
return $bootloader;
}
}
}
sub read_grub {
my ($fstab) = @_;
my $grub2dev = read_grub_device_map();
my $bootloader = read_grub_menu_lst($fstab, $grub2dev) or return;
read_grub_install_sh($bootloader, $grub2dev);
$bootloader;
}
sub read_grub_install_sh {
my ($bootloader, $grub2dev) = @_;
#- matches either:
#- setup (hd0)
#- install (hd0,0)/boot/grub/stage1 d (hd0) (hd0,0)/boot/grub/stage2 p (hd0,0)/boot/grub/menu.lst
if (cat_("$::prefix/boot/grub/install.sh") =~ /^(?:setup.*|install\s.*\sd)\s+(\(.*?\))/m) {
$bootloader->{boot} = grub2dev($1, $grub2dev);
}
}
sub read_grub_menu_lst {
my ($fstab, $grub2dev) = @_;
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)) {
chomp;
s/^\s*//; s/\s*$//;
next if /^#/ || /^$/;
my ($keyword, $v) = split('[ \t=]+', $_, 2) or
warn qq(unknown line in /boot/grub/menu.lst: "$_"\n), next;
if ($keyword eq 'title') {
push @{$b{entries}}, $e = { label => $v };
$global = 0;
} elsif ($global) {
$b{$keyword} = $v eq '' ? 1 : grub2file($v, $grub2dev, $fstab);
} else {
if ($keyword eq 'kernel') {
$e->{type} = 'image';
(my $kernel, $e->{append}) = split(' ', $v, 2);
$e->{root} = $1 if $e->{append} =~ s/root=(\S*)\s*//;
$e->{kernel_or_dev} = grub2file($kernel, $grub2dev, $fstab);
} elsif ($keyword eq 'root') {
$e->{type} = 'other';
if ($v !~ /,/) {
$e->{unsafe} = 1;
}
$e->{kernel_or_dev} = grub2dev($v, $grub2dev);
$e->{append} = "";
} elsif ($keyword eq 'initrd') {
$e->{initrd} = grub2file($v, $grub2dev, $fstab);
} elsif ($keyword eq 'map') {
$e->{mapdrive}{$2} = $1 if $v =~ m/\((.*)\) \((.*)\)/;
} elsif ($keyword eq 'module') {
push @{$e->{modules}}, $v;
} else {
$e->{$keyword} = $v eq '' ? 1 : $v;
}
}
}
|