#!/usr/bin/perl
#
# Node Assassin - Uninstaller
# Digimer; digimer@alteeve.com
# Jul. 11, 2010
# Version: 1.1.5
#
# Bugs;
# - None known, many expected
# 

# Play safe!
use strict;
use warnings;
use IO::Handle;
use File::Copy;
require "./installer.lib";

my $conf={
	'system'	=>	{
		conf_file	=>	"./etc/fence_na/fence_na.conf",
		install_log	=>	"./install.log",
		'log'		=>	"./uninstall.log",
		debug		=>	0,
		quiet		=>	0,
		yes		=>	0,
		'no'		=>	0,
		os_name		=>	"",
		os_ver		=>	"",
	},
	install		=>	{
		centos		=>	{
			directory	=>	{
				prefix		=>	"",
				agent		=>	"/sbin/",
				config		=>	"/etc/fence_na/",
				library		=>	"/etc/fence_na/",
				naos		=>	"/etc/fence_na/",
				build		=>	"/etc/fence_na/",
				arduino		=>	"/etc/fence_na/",
				INSTALL		=>	"/etc/fence_na/",
				CHANGES		=>	"/etc/fence_na/",
				README		=>	"/etc/fence_na/",
				fence_na_pod	=>	"/etc/fence_na/",
				man		=>	"/usr/share/man/man8/",
				cluster_ng	=>	"/usr/share/system-config-cluster/misc/",
			},
			files		=>	{
				agent		=>	"fence_na",
				config		=>	"fence_na.conf",
				library		=>	"fence_na.lib",
				naos		=>	"naos-1.1.4.4.c",
				build		=>	"na_build_v1.1.4.1.tar.gz",
				arduino		=>	"arduino-0018.tar.gz",
				INSTALL		=>	"INSTALL",
				CHANGES		=>	"CHANGES",
				README		=>	"README",
				fence_na_pod	=>	"fence_na.pod",
				man		=>	"fence_na.8.gz",
				cluster_ng	=>	"cluster.ng",
			},
		},
		fedora		=>	{
			directory	=>	{
				prefix		=>	"",
				agent		=>	"/usr/sbin/",
# 				stonith		=>	"/usr/lib64/stonith/plugins/external/",
				config		=>	"/etc/fence_na/",
				library		=>	"/etc/fence_na/",
				naos		=>	"/etc/fence_na/",
				build		=>	"/etc/fence_na/",
				arduino		=>	"/etc/fence_na/",
				INSTALL		=>	"/etc/fence_na/",
				CHANGES		=>	"/etc/fence_na/",
				README		=>	"/etc/fence_na/",
				fence_na_pod	=>	"/etc/fence_na/",
				man		=>	"/usr/share/man/man8/",
				cluster_rng	=>	"/usr/share/cluster/",
			},
			files		=>	{
				agent		=>	"fence_na",
# 				stonith		=>	"node_assassin",
				config		=>	"fence_na.conf",
				library		=>	"fence_na.lib",
				naos		=>	"naos-1.1.4.4.c",
				build		=>	"na_build_v1.1.4.1.tar.gz",
				arduino		=>	"arduino-0018.tar.gz",
				INSTALL		=>	"INSTALL",
				CHANGES		=>	"CHANGES",
				README		=>	"README",
				fence_na_pod	=>	"fence_na.pod",
				man		=>	"fence_na.8.gz",
				cluster_rng	=>	"cluster.rng",
			},
		},
	},
};

# Make sure I am running as root.
# print "Real UID: [$<], Effective UID: [$>]\n";
die "The Node Assassin uninstaller must be run as root. Exiting.\n" if (($< != 0) && ($> != 0));

# Log file for output.
my $log=IO::Handle->new();
open ($log, ">$conf->{'system'}{'log'}") or die "Failed to open: [$conf->{'system'}{'log'}] for writing; Error: $!\n";

# This method can't pass in the '$log' handle, obviously, as it does not yet
# exist.
read_conf($conf, $log);

# Set STDOUT and $log to hot (unbuffered) output.
if (1)
{
	select $log;
	$|=1;
	select STDOUT;
	$|=1;
}

# If this gets set in the next two function, the agent will exit.
my $bad=0;

# Read in arguments from the command line.
($bad)=read_cla($conf, $log, $bad);

# Detect the operating system.
detect_os($conf, $log);

record($conf, $log, __LINE__, "Ready to start the uninstall of Node Assassin:\n");

if ( $conf->{'system'}{os_name} eq "centos" )
{
	uninstall_centos($conf, $log);
}
elsif ( $conf->{'system'}{os_name} eq "fedora" )
{
	uninstall_fedora($conf, $log);
}

record($conf, $log, __LINE__, "\nThe uninstall completed!\n\n", 1);

exit(0);


###############################################################################
# Public Functions.                                                           #
###############################################################################

# Uninstall Node Assassin support from Fedora.
sub uninstall_fedora
{
	my ($conf, $log)=@_;
	
	my $os=$conf->{'system'}{os_name};
	foreach my $key (sort {$b cmp $a} keys %{$conf->{install}{$os}{files}})
	{
		next if $key eq "cluster_rng";
		my $dst=$conf->{install}{$os}{directory}{prefix}.$conf->{install}{$os}{directory}{$key}.$conf->{install}{$os}{files}{$key};
		remove_file($conf, $log, $dst);
	}
	unmerge_cluster_rng($conf, $log);
	
	# Before I open the log file for writting, first read it in and look for a list
	# of directories to try to remove.
	my @remove_dirs;
	record($conf, $log, __LINE__, "Will read the install log to see what directories I should remove.\n");
	if (-f $conf->{'system'}{install_log})
	{
		record($conf, $log, __LINE__, " - Found: [$conf->{'system'}{install_log}], reading it in.\n");
		my $read=IO::Handle->new();
		open ($read, "<$conf->{'system'}{install_log}") or record($conf, $log, __LINE__, "Failed to read: [$conf->{'system'}{install_log}], error: $!\n", 2);
		while (<$read>)
		{
			chomp;
			my $line=$_;
			if ($line=~/^ - Created: \[(.*?)\]/)
			{
				my $dir=$1;
				my $found=0;
				foreach my $qdir (@remove_dirs)
				{
					$found=1 if $qdir eq $dir;
					last if $found;
				}
				push @remove_dirs, $dir if not $found;
				record($conf, $log, __LINE__, " - Queueing : [$dir] for removal.\n") if not $found;
			}
		}
	}

	# Remove the directories now.
	foreach my $dir (sort {$b cmp $a} @remove_dirs)
	{
		remove_directory($conf, $log, $dir);
	}
}

# Uninstall Node Assassin support from CentOS.
sub uninstall_centos
{
	my ($conf, $log)=@_;
	
	my $os=$conf->{'system'}{os_name};
	foreach my $key (sort {$b cmp $a} keys %{$conf->{install}{$os}{files}})
	{
		next if $key eq "cluster_ng";
		my $dst=$conf->{install}{$os}{directory}{prefix}.$conf->{install}{$os}{directory}{$key}.$conf->{install}{$os}{files}{$key};
		remove_file($conf, $log, $dst);
	}
	unmerge_cluster_ng($conf, $log);
	
	# Before I open the log file for writting, first read it in and look for a list
	# of directories to try to remove.
	my @remove_dirs;
	record($conf, $log, __LINE__, "Will read the install log to see what directories I should remove.\n");
	if (-f $conf->{'system'}{install_log})
	{
		record($conf, $log, __LINE__, " - Found: [$conf->{'system'}{install_log}], reading it in.\n");
		my $read=IO::Handle->new();
		open ($read, "<$conf->{'system'}{install_log}") or record($conf, $log, __LINE__, "Failed to read: [$conf->{'system'}{install_log}], error: $!\n", 2);
		while (<$read>)
		{
			chomp;
			my $line=$_;
			if ($line=~/^ - Created: \[(.*?)\]/)
			{
				my $dir=$1;
				push @remove_dirs, $dir;
				record($conf, $log, __LINE__, " - Queueing : [$dir] for removal.\n");
			}
		}
	}

	# Remove the directories now.
	foreach my $dir (sort {$b cmp $a} @remove_dirs)
	{
		remove_directory($conf, $log, $dir);
	}
}

# Remove (empty) directories.
sub remove_directory
{
	my ($conf, $log, $dir)=@_;
	
	print "Removing: [$dir]\n";
	# Remove the directory, but don't die if it fails.
	rmdir($dir) or record($conf, $log, __LINE__, "Failed to remove the directory: [$dir], error: $!\n", 1);
	
	return;
}

# Remove files.
sub remove_file
{
	my ($conf, $log, $file)=@_;
	
	record($conf, $log, __LINE__, "Checking if: [$file] exists; ");
	if (-f $file)
	{
		record($conf, $log, __LINE__, "it does, removing it... ");
		unlink($file) or record($conf, $log, __LINE__, "Failed to remove: [$file], error: $!\n", 2);
		record($conf, $log, __LINE__, "done.\n");
	}
	else
	{
		record($conf, $log, __LINE__, "it doesn't, nothing to remove.\n");
	}
	
	return;
}

# Remove the Node Assassin support from the cluster.ng file.
sub unmerge_cluster_ng
{
	my ($conf, $log)=@_;
	
	# backup the current 'cluster.ng' then, if found, replace it with the
	# original backup made when Node Assassin was installed.
	my $os=$conf->{'system'}{os_name};
	my $full_file=$conf->{install}{$os}{directory}{prefix}.$conf->{install}{$os}{directory}{cluster_ng}.$conf->{install}{$os}{files}{cluster_ng};
	my $dir=$conf->{install}{$os}{directory}{prefix}.$conf->{install}{$os}{directory}{cluster_ng};
	my $file=$conf->{install}{$os}{files}{cluster_ng};
	my $nbk_file="$file.post-node_assassin";
	my $obk_file="${dir}${file}.pre-node_assassin";
	
	# Before anything, make a backup of the source file.
	record($conf, $log, __LINE__, "Backing up the current 'cluster.ng' file.\n", 1);
	record($conf, $log, __LINE__, "WARNING! Please check the restored 'cluster.ng' file if you've made changes since installing Node Assassin!\n", 1);
	copy_file($conf, $log, $full_file, $dir, $nbk_file);
	
	# Copy the backup file that was created at install time back.
	if ( -e $obk_file )
	{
		copy_file($conf, $log, $obk_file, $dir, $file);
		record($conf, $log, __LINE__, "You may safely delete\n- [$obk_file]\n- [$nbk_file]\nWhen you are confident that the current 'cluster.ng' is ok.\n", 1);
	}
	else
	{
		record($conf, $log, __LINE__, "FAILED! The original backup file: [$obk_file] was not found!\n", 1);
		record($conf, $log, __LINE__, "Please manually remove the Node Assassin (fence_na) entry.\n", 1);
	}
	
	return;
}

# Remove the Node Assassin support from the cluster.rng (Cluster 3) file.
sub unmerge_cluster_rng
{
	my ($conf, $log)=@_;
	
	# backup the current 'cluster.rng' then, if found, replace it with the
	# original backup made when Node Assassin was installed.
	my $os=$conf->{'system'}{os_name};
	my $full_file=$conf->{install}{$os}{directory}{prefix}.$conf->{install}{$os}{directory}{cluster_rng}.$conf->{install}{$os}{files}{cluster_rng};
	my $dir=$conf->{install}{$os}{directory}{prefix}.$conf->{install}{$os}{directory}{cluster_rng};
	my $file=$conf->{install}{$os}{files}{cluster_rng};
	my $nbk_file="$file.post-node_assassin";
	my $obk_file="${dir}${file}.pre-node_assassin";
	
	# Before anything, make a backup of the source file.
	record($conf, $log, __LINE__, "Backing up the current 'cluster.rng' file.\n", 1);
	record($conf, $log, __LINE__, "WARNING! Please check the restored 'cluster.rng' file if you've made changes since installing Node Assassin!\n", 1);
	copy_file($conf, $log, $full_file, $dir, $nbk_file);
	
	# Copy the backup file that was created at install time back.
	if ( -e $obk_file )
	{
		copy_file($conf, $log, $obk_file, $dir, $file);
		record($conf, $log, __LINE__, "You may safely delete\n- [$obk_file]\n- [$nbk_file]\nWhen you are confident that the current 'cluster.rng' is ok.\n", 1);
	}
	else
	{
		record($conf, $log, __LINE__, "FAILED! The original backup file: [$obk_file] was not found!\n", 1);
		record($conf, $log, __LINE__, "Please manually remove the Node Assassin (fence_na) entry.\n", 1);
	}
	
	return;
}

# This returns the 'help' message.
sub help
{
	my ($conf, $log)=@_;
	
	# Point the user at the man page.
	print "\nPlease read the UNINSTALL file for info on installing the Node Assassin software.\n\n";
	
	do_exit($conf, $log, 0);
}

# Read in command line arguments
sub read_cla
{
	my ($conf, $log, $bad)=@_;
	
	# Loop through the passed arguments, if any.
	record($conf, $log, __LINE__, "Got args:\n") if $conf->{'system'}{debug};
	my $set_next="";
	my $os=$conf->{'system'}{os_name};
	foreach my $arg (@ARGV)
	{
		record($conf, $log, __LINE__, "[$arg]\n") if $conf->{'system'}{debug};
		
		# If 'set_next' has a value, push this argument into the 'conf'
		# hash.
		if ($set_next)
		{
			# It's set, use it's contents as the hash key.
			$conf->{install}{$os}{$set_next}=$arg;
			record($conf, $log, __LINE__, "Setting: 'install::$set_next': [$conf->{install}{$os}{$set_next}]\n") if $conf->{'system'}{debug};
			
			# Clear it now for the next go-round.
			$set_next="";
			next;
		}
		if ($arg=~/-h/)
		{
			# Print the help message and then exit.
			help($conf, $log);
		}
		elsif ($arg=~/-q/)
		{
			# Suppress all non-critical messages from STDOUT.
			$conf->{'system'}{quiet}=1;
		}
		elsif ($arg=~/-y/)
		{
			# Assume "yes" to all questions.
			$conf->{'system'}{yes}=1;
		}
		elsif ($arg=~/-n/)
		{
			# Assume "no" to all questions.
			$conf->{'system'}{'no'}=1;
		}
		elsif ($arg=~/-directory=(.*?)/)
		{
			$conf->{install}{$os}{directory}=$1;
		}
		elsif ($arg=~/^-/)
		{
			$arg=~s/^-//;
			
			if ($arg eq "d")
			{
				# This is the IP address or hostname of the
				# Node Assassin to call.
				$set_next="directory";
				record($conf, $log, __LINE__, "Next argument will be stored in: [install::$set_next]\n") if $conf->{'system'}{debug};
			}
		}
		else
		{
			### MADI: I might want to pick up arguments via multiple lines.
			# Bad argument.
			record($conf, $log, __LINE__, "\nERROR: Argument: [$arg] is not valid!\n");
			record($conf, $log, __LINE__, "ERROR: Please run './fence_na --help' to see a list of valid arguments.\n\n");
			$bad=1;
		}
	}
}
