package silo; use common qw(:common :file); use partition_table qw(:types); use log; use fsedit; use detect_devices; use partition_table_raw; use run_program; use modules; sub mkinitrd($$$) { my ($prefix, $kernelVersion, $initrdImage) = @_; $::testing and return; modules::load('loop'); run_program::rooted($prefix, "mkinitrd", "-f", $initrdImage, "--ifneeded", $kernelVersion) or unlink("$prefix/$initrdImage"); -e "$prefix/$initrdImage" or die "mkinitrd failed"; } sub mkbootdisk($$$;$) { my ($prefix, $kernelVersion, $dev, $append) = @_; modules::load('loop'); my @l = qw(mkbootdisk --noprompt); push @l, "--appendargs", $append if $append; run_program::rooted($prefix, @l, "--device", "/dev/$dev", $kernelVersion) or die "mkbootdisk failed"; } sub read($$) { my ($prefix, $file) = @_; my $global = 1; my ($e, $v, $f); my %b; foreach (cat_("$prefix$file")) { ($_, $v) = /^\s*(.*?)\s*(?:=\s*(.*?))?\s*$/; if (/^(image|other)$/) { $b{entries}{$v} = $e = { type => $_ }; $global = 0; } elsif ($global) { $b{$_} = $v || 1; } else { $e->{$_} = $v || 1; } } $b{timeout} = $b{timeout} / 10 if $b{timeout}; $b{message} = cat_("$prefix$b{message}") if $b{message}; \%b; } sub add_entry($$$) { my ($entries, $image, $v) = @_; my (%usedold, $freeold); do { $usedold{$1 || 0} = 1 if $_->{label} =~ /^old ([^_]*)_/x } foreach (values %$entries); foreach (0..scalar keys %usedold) { exists $usedold{$_} or $freeold = $_ || '', last } do { $_->{label} = "old${freeold}_$_->{label}" if $_->{label} eq $v->{label} } foreach (values %$entries); $entries->{$image} = $v; } sub add_kernel($$$$$) { my ($prefix, $silo, $kernelVersion, $specific, $v) = @_; my $ext = $specific && "-$specific"; my ($vmlinuz, $image, $initrdImage) = ("vmlinuz-$kernelVersion$specific", "/boot/vmlinuz$ext", "/boot/initrd$ext.img"); if (-e "$prefix/boot/$vmlinuz") { { my $f = "initrd-$kernelVersion$specific.img"; eval { mkinitrd($prefix, "$kernelVersion$specific", "/boot/$f") }; undef $initrdImage if $@; symlinkf $f, "$prefix$initrdImage" if $initrdImage; } add2hash($v, { type => 'image', label => 'linux', initrd => $initrdImage, append => $silo->{perImageAppend}, }); symlinkf "$vmlinuz", "$prefix/$image" if $specific; add_entry($silo->{entries}, $image, $v); 1; } else { log::l("unable to find kernel image $prefix/boot/$vmlinuz"); 0; } } sub suggest($$$$$) { my ($prefix, $silo, $hds, $fstab, $kernelVersion) = @_; my $root = fsedit::get_root($fstab)->{device}; my $partition = ($root =~ /\D*(\d*)/)[0]; add2hash_($silo, { default => "linux", timeout => 5, }); if (!$silo->{message} || $silo->{message} eq "1") { $silo->{message} = join('', cat_("$prefix/boot/message")); if (!$silo->{message}) { my $msg_en = __("Welcome to SILO the operating system chooser! To list the possible choices, press . To load one of them, write its name and press or wait %d seconds for default boot. "); my $msg = translate($msg_en); $msg = $msg_en if int(grep { $_ & 0x80 } unpack "c*", $msg) / length($msg) > 0.2; $silo->{message} = sprintf $msg, $silo->{timeout}; } } my $isSecure = -e "$prefix/boot/vmlinuz-${kernelVersion}secure"; my $isSMP = detect_devices::hasSMP(); if ($isSMP && !-e "$prefix/boot/vmlinuz-${kernelVersion}smp") { log::l("SMP machine, but no SMP kernel found") unless $isSecure; $isSMP = 0; } add_kernel($prefix, $silo, $kernelVersion, $isSecure ? 'secure' : 'smp', { label => 'linux', partition => $partition, root => "/dev/$root", }) if $isSecure || $isSMP; add_kernel($prefix, $silo, $kernelVersion, '', { label => $isSecure || $isSMP ? 'linux-up' : 'linux', partition => $partition, root => "/dev/$root", }); } sub install($$) { my ($prefix, $silo) = @_; if ($silo->{message}) { local *F; open F, ">$prefix/boot/message" and print F $silo->{message} or $silo->{message} = 0; } { local *F; local $\ = "\n"; my $f = "$prefix/etc/silo.conf"; open F, ">$f" or die "cannot create silo config file: $f"; log::l("writing silo config to $f"); $silo->{$_} and print F "$_=$silo->{$_}" foreach qw(partition root default append); $silo->{$_} and print F $_ foreach qw(restricted); print F "timeout=", round(10 * $silo->{timeout}) if $silo->{timeout}; print F "message=/boot/message" if $silo->{message}; while (my ($v, $e) = each %{$silo->{entries}}) { print F "$e->{type}=$v"; print F "\tlabel=$e->{label}"; if ($e->{type} eq "image") { print F "\tpartition=$e->{partition}" if $e->{partition}; print F "\troot=$e->{root}" if $e->{root}; print F "\tinitrd=$e->{initrd}" if $e->{initrd}; print F "\tappend=\"$1\"" if $e->{append} =~ /^\s*"?(.*?)"?\s*$/; print F "\tread-write" if $e->{'read-write'}; print F "\tread-only" if !$e->{'read-write'}; } else { die "Other SILO entries still not supported at the moment"; } } } log::l("Installing boot loader..."); $::testing and return; run_program::rooted($prefix, "silo", "2>", "/tmp/.error") or die "silo failed"; unlink "$prefix/tmp/.error"; } 1; #