#!/usr/bin/perl -w

use Bootloader::Tools;
use Getopt::Long;
use Pod::Usage;
use File::Temp;

=head1 NAME

multi-update - update the other system in the split-brain scenario

=head1 SYNOPSIS

multi-update [options]

valid options are --help, --verbose, --force, --non-interactive

=head1 DESCRIPTION

B<multi-update> will let you update the other system in the multi-update scenario
and set it to be booted after reboot

=head1 PARAMETER

=over 8

=item B<--help>

Print a brief help message and exits.

=item B<--verbose>

Print more detailed messages

=item B<--force>

Update the other system even if current system is up-to-date; in this case, it brings both systems to sync

=item B<--non-interactive>

Update in non-interactive mode

=back

=cut


#internal variables storing system info
$im1_root = $im2_root = "";
$im1_grub_boot = $im2_grub_boot = "";
$disk_device = "";
$im1_root_part = $im2_root_part = "";
$im1_part_nr = $im2_part_nr = "";

#options
my %oper;
my ($opt_verbose, $opt_help, $opt_force, $opt_non_interactive)
    = (0,0,0,0);
my $system_updated = 0;

#FIXME this is copy-paste from multi-update-setup
sub debug {
    $opt_verbose || return;
    my $msg = shift;
    print "$msg\n";
}

#FIXME this is copy-paste from multi-update-setup
sub initialize {
    print "Reading system information...";
    debug ("");
    my $mp = Bootloader::Tools::ReadMountPoints();
    $im1_root = $mp->{"/"};
    if ($im1_root =~ m/^(.*)-part([0-9]+)$/) {
	$disk_device = "$1";
#FIXME variables before to be backported to multi-update-setup
	$im1_part_nr = $2;
	$im2_part_nr = $im1_part_nr + 1;
	$im2_part_nr = $im1_part_nr - 1 if ($im1_part_nr == 2);
	$im2_root = "$disk_device-part$im2_part_nr";
	$im1_root_part = "-part$im1_part_nr";
	$im2_root_part = "-part$im2_part_nr";
    }
    elsif ($im1_root =~ m/^(.*[0-9]+)p([0-9]+)$/) {
	$disk_device = "$1";
#FIXME variables before to be backported to multi-update-setup
	$im1_part_nr = $2;
	$im2_part_nr = $im1_part_nr + 1;
	$im2_part_nr = $im1_part_nr - 1 if ($im1_part_nr == 1);
	$im2_root = $disk_device . "p" . $im2_part_nr;
	if ($disk_device =~ m/^.*\/([^\/]+)$/)
	{
	    $im1_root_part = "$1$im1_part_nr";
	    $im2_root_part = "$1$im2_part_nr";
	}
	else
	{
	    die "\nCannot initialize the disk information.\n";
	}
    }
    elsif ($im1_root =~ m/^([^0-9]+)([0-9]+)$/) {
	$disk_device = "$1";
#FIXME variables before to be backported to multi-update-setup
	$im1_part_nr = $2;
	$im2_part_nr = $im1_part_nr + 1;
	$im2_part_nr = $im1_part_nr - 1 if ($im1_part_nr == 2);
	$im2_root = "$disk_device$im2_part_nr";
	if ($disk_device =~ m/^.*\/([^\/]+)$/)
	{
	    $im1_root_part = "$1$im1_part_nr";
	    $im2_root_part = "$1$im2_part_nr";
	}
	else
	{
	    die "\nCannot initialize the disk information.\n";
	}
    }
    debug ("This root partition: $im1_root");
    debug ("Another system partition: $im2_root");
    debug ("The disk: $disk_device");
    debug ("Partition to fstab: $im1_root_part $im2_root_part");
    $im2_root ne "" || die "\nInitializatoin failed.\n";
# FIXME: assumes partnr 1 + 2
    $im1_grub_boot = "(hd0,0)";
    $im2_grub_boot = "(hd0,1)";
    print "Done.\n";
}

#FIXME this is copy-paste from multi-update-setup
sub mount {
    print "Mounting target system...";
    debug ("");
    my $target = shift;
    my $root = shift;
    if ($root) {
	my $cmd = "mount $im2_root $target";
	debug ("Running command: $cmd");
	system ($cmd) == 0 || die "Cannot mount $im2_root.\n";
    }
    my $cmd = "mount --bind /sys $target/sys; mount --bind /dev $target/dev; mount --bind /proc $target/proc";
    debug ("Running command: $cmd");
    system ($cmd) == 0 || die "\nCannot mount target system.\n";
    print "Done.\n";
}

#FIXME this is copy-paste from multi-update-setup
sub clean {
    print "Cleaning up...";
    debug ("");
    my $target = shift;
    my $cmd = "umount $target/sys 2>/dev/null; umount $target/proc 2>/dev/null; umount $target/dev 2>/dev/null; umount $target 2>/dev/null; rmdir $target 2>/dev/null;";
    debug ("Running command: $cmd");
    system ($cmd) == 0 || die "\nClean-up failed\n";
    print "Done.\n";
}

sub update {
    print "Updating target system...";
    debug ("");
    my $target = shift;

    my $do_update = 1;
    # check if current system is up-to-date
    $cmd = "zypper --non-interactive pchk >/dev/null 2>/dev/null";
    if (system ($cmd) == 0)
    {
	debug ("Current system is up-to-date");
	$do_update = 0;
	if ($opt_force) {
	    print "\nBringing the other system in sync with running system...";
	    debug ("");
	    $do_update = 1;
	}
    }

    if ($do_update) {
        my $cmd = "cp /etc/resolv.conf $target/etc/resolv.conf";
        debug ("Running command: $cmd");
        system ($cmd) == 0 || die "\nCannot update target system.\n";

	my $zypper_cmd = "zypper patch";
	if ($opt_non_interactive) {
	    $zypper_cmd = "zypper --non-interactive patch --auto-agree-with-licenses";
	}
	
	my $ret = 103; #ZYPPER_EXIT_INF_RESTART_NEEDED
	while ($ret == 103) {
	    $cmd = "chroot $target bash -c '$zypper_cmd'";
	    debug ("Running command: $cmd");
	    $ret = system ($cmd);
	};
	
        $ret == 0 || $ret >= 100 || die "\nCannot update target system.\n";
	print "Done.\n";
	$system_updated = 1;
    }
    else
    {
	print "Current system is up-to-date.\n";
    }
}

sub set_boot {
    print "Setting up system for boot...";
    debug ("");
    my $cmd = "/usr/sbin/parted -s $disk_device set $im2_part_nr boot on";
    debug ("Running command: $cmd");
    system ($cmd) == 0 || die "\nCannot update target system.\n";
    print "Done.\n";
}

GetOptions (\%oper,
    'help|h'     => \$opt_help,
    'verbose|v' => \$opt_verbose,
    'force|f' => \$opt_force,
    'non-interactive|n' => \$opt_non_interactive)
    or pod2usage(2);
pod2usage(1) if $opt_help;

my $tmpdir = File::Temp::mkdtemp( "/tmp/split-brain-XXXXXX" );

initialize();
mount($tmpdir, 1);
update($tmpdir);
if ($system_updated) {
    set_boot();
}
clean($tmpdir);

exit 0;



