#!/usr/bin/perl

use strict;
use warnings;
use POSIX qw(setsid);
use LWP::Simple qw($ua get getstore);

$SIG{TERM} = $SIG{INT} = sub { finalize("Process was terminated\n") };

# CONS
my $ds_root = "/opt/DS";
my $ds_bin = "$ds_root/bin/DS";
my $ds_cache = "$ds_root/var/cache";
my $master_ip_file = "$ds_root/var/s_master";
my $max_tries = 3;
my $continue = $ARGV[0] || '';
my $progname = (split(/\//,$0))[-1];
my $pid_name = "$ds_root/var/$progname";
my $cwd = "/tmp/ds-upgrade-$$";

# Tune up user agent
$ua->timeout(10);
$ua->agent('ds-vmx upgrade script');

sub pid_read
{
	my $pid = 0;
	if(open PID, "$pid_name") {
		$pid = <PID>;
		close PID;
		chomp $pid
	} 
	$pid;
}

sub pid_write
{
	if(open PID, ">$pid_name") {
		print PID "$$\n";
		close PID;
	} else {
	    warn "Failed to write pid: $!\n";
	}
}

sub pid_remove
{ 
	unlink $pid_name if pid_read == $$;
}

sub pid_concurrent_check 
{ 
	my $old_pid = pid_read;
	if($old_pid > 1) {
		open PS, "ps -p $old_pid -o comm=|";
		my $proc = <PS>;
		close PS;
		chomp $proc;
		if ($proc eq $progname) {
    			exit 1;
		} 
	}
	pid_write;
}

sub prepare
{	
	# check for another instance of check_upgrade
	pid_concurrent_check;
	
	print "=" x 50 . "\nDS Updater starts at ".`date`;
	mkdir $cwd or die "Failed to mkdir: $!\n";
	system("cp -f $0 $cwd/$progname");
	setsid;
	exec "$cwd/$progname", "-c";
}

sub get_master_ip
{
	my $master;
	my ($ip, $port);
	
	open(MASTER, $master_ip_file) or die "Failed to open master ip file: $!\n";
	$master = <MASTER>;
	close MASTER;
	$ip = $1 if $master =~ /^(.+):\d+$/;
	die "Invalid master IP format: $master\n" unless $ip;
	$ip;
}

sub get_installed_version
{
	my ($os, $ver, $url) = ('', '');
	
	open(VER, "$ds_bin --version 2>/dev/null |") or die "Cannot read DS version: $!\n";
	while(my $line = <VER>) {
		chomp $line;
		$os  = $1 if $line =~ /^Platform: (.+)$/i;
		$ver = $1 if $line =~ /^Version: (.+)$/i;
		$url = $1 if $line =~ /^URL: (.+)$/i;
	}
	close VER;
	die "Cannot get software version\n" if not $os or not $ver;
	($os, $ver);
}

sub get_version
{	
	my $master_ip = shift;
	
	my $vinfo = get "http://$master_ip/va-stage/ds/current";
	die "Cannot fetch version for upgrade from $master_ip\n" unless defined $vinfo;
	$vinfo;
}

sub upgrade
{	
        my $master_ip	       = get_master_ip;
        my ($own_os, $own_ver) = get_installed_version;
        my $version_str        = get_version($master_ip);
        my $url		       = "";
        my $err		       = "";
	my $tries 	       = 0;
	my $res		       = 0;
	my ($os, $ver, $file)  = split(/:/, $version_str);
	
	print "Installed version: $own_ver/$own_os\nUpgrade version: $ver/$os\n";
	die "Invalid upgrade info on $master_ip: $version_str\n"
		unless defined $os or defined $ver or defined $file;
	die "Software upgrade for different OS ($os)\n" if $os ne $own_os;
	if($ver eq $own_ver) {
		print "Nothing to do\n";
		return;
	}
	
	# Download package
	$url = "http://$master_ip/va-stage/ds/$file";
	print "Downloading package $url\n";
	while($tries < $max_tries) {
		$res = getstore($url, "$cwd/$file");
		last if $res == 200 or $res == 404;
		sleep 2;
		$tries++;
	}
	die "Cannot download package: $res\n" unless $res == 200;
	
	eval {
		# Uninstall current package
		system("rpm -e ds-vmx");
		print "Remove package: return status ".($?>>8)."\n";
		
		# Install downloaded package
		system("rpm -i $cwd/$file");
		die "Error installing package: rpm exited with status ".($?>>8)."\n" if $?;
	
		# start DS
		print "Starting Display server\n";
		system("initctl start ds_patrol") == 0 or die "Cannot start ds_patrol\n";
		
		# Wait a little and verify if DS is running
		sleep 10;
		die "DS stopped after 10 sec wait" if `ps -eo pid,cmd|grep "\\.\\.\\/bin\\/DS"|grep -v grep`!~/DS/;
	};
	if($@) {
		$err = $@;
		# Rollback changes
		warn "Error on installation stage. Rollback changes\n";
		if(opendir(CACHE, $ds_cache)) {
			foreach(sort {$b cmp $a} readdir(CACHE)) {
				next unless /^ds-vmx-.+\.rpm$/;
				system("rpm -e ds-vmx; rpm -i $ds_cache/$_; initctl start ds_patrol");
				if($? == 0) {
					sleep 5; # Avoids infinite loop in case of corrupt rpm file
					last;
				}
			}
			closedir(CACHE);
		}
		die $err;
	}
	else {
		# Upgrade was successful. Store newly installed rpm to cache
		system("rm -r $ds_cache/*");
		system("mv -f $cwd/$file $ds_cache");
	}
}

sub finalize
{
	my $err = shift;
	
	system("rm -rf $cwd");
	pid_remove;
	if($err) {
		warn "Upgrade failed: $err\n";
		exit 1;
        } else {
		print "Upgrade procedure finished successfully\n\n";
		exit 0;
        }
}

sub main
{
	eval {
		prepare unless $continue eq '-c';
		upgrade;
	};
	finalize $@;
}

main;
