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 <TAB>.

To load one of them, write its name and press <ENTER> 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; #
