#!/usr/bin/perl
#
# Copyright (c) 2006 Zmanda Inc.  All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# Contact information: Zmanda Inc, 505 N Mathlida Ave, Suite 120
# Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
#
#

use strict;
use warnings;
use File::Find;
use File::Spec::Functions;
use File::Temp qw/ :POSIX /;
use POSIX qw(setsid _exit);
use lib '/usr/local/lib/mysql-zrm';
use ZRM::Common;


my $MD5SUM="md5";
my $CHECKSUM_FILE="zrm_checksum";
my $CHECKSUM_PENDING=".checksum_pending";

#usage strings 
my $USAGE_VERIFY_BACKUP_STRING=  
		"\t\t[--source-directory <directory name>]\n";  

my @VERIFYOPT=qw/source-directory=s
                 create-checksum/;

my %mdcheck;
my $checkCompressedFile = ""; 


#$_[0] specifies the directory whoes file's check sum needs to be verified
sub verifyCheckSum()
{
	my $dir = $_[0];
        find( {wanted => sub
                {
			if( $_ ne "index" && $_ ne $checkCompressedFile && 
				$_ ne $EXTRACTED_FILENAME && $_ ne $CHECKSUM_FILE ){
				my $file = $File::Find::fullname;
				if( -f $file ){
					my $x = $MD5SUM." -bq "."\"$file\"";
        				$x = &execCmdAndGetOutput($x);
					if( !defined $x ){
						&printError( "Could not get md5 checksum\n" );
					}else{
						my @a = split( / /, $x );
						&verifyCMForFile( $file, $a[0],
								 	$dir );
					}
				}
			}
                }, follow => 1}, $dir);
	for(keys%indexdata){
		&checkIfFile( $_ );
	}
}

#$_[0] file
#$_[1] current checksum
#$_[2] source-directory
sub verifyCMForFile()
{
	my $sep = "/";
	if( $^O eq "MSWin32" ) {
		$sep = "\\";
	}
	my $dir = $_[2];
        my $newSum=$_[1];
	my $x = $_[0];
	my $file = $x;
        my $p = $dir.$sep;
	$x=~/$p/;
	$x = catfile($indexdata{"backup-directory"}, $' );
	my $oldSum=$indexdata{$x};
	if(defined $oldSum && $newSum eq $oldSum){
		if( $verbose ){
			&printLog( "checksum for file $file is correct\n" );
		}
	}else{
		&printError( "checksum for file $file does not match\n" );
	}
	delete $indexdata{$x};
}

#Checks if $_[0] is a file and if so prints an error
sub checkIfFile()
{
	my $file = $_[0];
	$file=~/$indexdata{"backup-directory"}/;
	$file=$';
	if( $file ){
		&printError( "File $_[0] not found\n" );
	}
}

#$_[0] directory name 
#Calculates the md5 checksum of each;
sub calculateMD5Sum()
{
        my $dir = $_[0];
        find( {wanted => sub
                {
                        if( $_ ne "index" && $_ ne $CHECKSUM_PENDING ){
                                my $file = $File::Find::fullname;
                                if( -f $file ){
                                        my $x = $MD5SUM." -b "."\"$file\"";
                                        $x = &execCmdAndGetOutput($x);
                                        if( !defined $x ){
                                                &printError( "Could not get md5 checksum\n" );
                                        }else{
                                                my @a = split( / /, $x );
                                                $mdcheck{$file}=$a[0];
                                        }
                                }
                        }
                },
                follow => 1}, $_[0]);
}

sub doPostBackupPlugin()
{
        if( $verbose ){
                &printLog( "Executing post-backup-plugin\n" );
        }
        my @params;
        push @params, "--backup-directory";
        push @params, $inputs{"source-directory"};
        push @params, "--checksum-finished";
        my $r = &execPlugin( "post-backup-plugin", @params );
        return $r;
}

sub doStart()
{
        open STDIN, ">>/dev/null";
        open STDOUT, ">>/dev/null";
        open STDERR, ">>/dev/null";
        my $pid = fork();
        if(!defined $pid){
                &printAndDie( "Cannot fork monitor" );
        }
        if( ! $pid ){
                setsid();
		&main( "" );
        }
        _exit(0);
}

sub doCreateChecksum()
{
	my $dir = $inputs{"source-directory"};
	my $fsnap = catfile( $dir, $ZRM_MOUNT_DETAILS );
	if( -f $fsnap ){
		&printError( "$dir contains snapshots as backup. Checksum is not supported on this type of backups\n" );
		return;
	}
	my $fn = catfile( $dir, $CHECKSUM_PENDING );
	my $nc = catfile( $dir, ".nochecksum" );
	if( -f $nc ){
		open TP, ">$fn";
		close TP;
		unlink( $nc );
	}

	if( ! -f $fn ){
		return;
	}

	my $filename = catfile( $dir, $CHECKSUM_FILE );
	if( -f $filename ){
		&printError( "Checksum file already exists\n" );
		return;
	}
	&calculateMD5Sum( $dir );
	unless( open( FH, ">$filename" ) ){
		&printError( "Unable to open checksum file\n" );
		return;
	}
	print FH "backup-directory=".$inputs{"source-directory"}."\n";
	if( %mdcheck ){
                for(keys%mdcheck){
                        print FH "$_=$mdcheck{$_}\n";
                }
        }
	close( FH );
	unlink( $fn );
	&doPostBackupPlugin();
}

sub doVerifyBackup()
{
	if( !$inputs{"source-directory"} ) {
		if( !$lastBackupDir ){
			&printWarning( "Nothing to verify\n" );
			return;
		}
		$inputs{"source-directory"}=$lastBackupDir;
	}
	if( !-d $inputs{"source-directory"} ) {
		&printAndDie( "Cannot find source directory ".$inputs{"source-directory"}."\n" );
	}

	if( defined $inputs{"create-checksum"} ){
		&doCreateChecksum();
		return;
	}

	my $dir = $inputs{"source-directory"};

	my $fsnap = catfile( $dir, $ZRM_MOUNT_DETAILS );
	if( -f $fsnap ){
		&printError( "$dir contains snapshots as backup. Verification cannot be done this type of backups\n" );
		return;
	}

	my $nc = catfile( $dir, ".nochecksum" );
	if( -f $nc ){
		&printError( "Checksum information not yet created. If verification functionality is needed, please run the following command and then run verify.\nmysql-zrm --action verify-backup --create-checksum --source-directory $dir\n" );
		return;
	}

	my $fn = catfile( $dir, $CHECKSUM_PENDING );
	if( -f $fn ){
		&printError( "Checksum creation in progress. Please run the verify after the checksum creation is done\n" );
		return;
	}

	my $cmFile = catdir( $dir, $CHECKSUM_FILE );
	my $fname;
	if( -f $cmFile ){
		$fname = $cmFile;
		$checkCompressedFile = "";
		my $ext = catfile( $dir, $EXTRACTED_FILENAME );
		if( -f $ext ){
			&printAndDie( "This backup directory seems to contain extracted data. Please run 'mysql-zrm --action extract-backup --cleanup --source-directory $dir' before trying to verify the backup\n" );
		}
	}else{
		$fname = catfile( $dir, $INDEX_FILENAME );
		$checkCompressedFile = $COMPRESS_FILENAME;
	}

        my $r = &parseIndexFile( $fname );
	if( $r == 0 ){
		print "ERROR: Looks like backup data for $dir is not available\n";
		return;
	}
	if( defined $indexdata{"compress"} || defined $indexdata{"encrypt"} ){
		$r = 1;
		$r = &uncompressBackup( $inputs{"source-directory"} , 1 );
		if( $r == 0 ){
			&printAndDie( "Unable to uncompress backup\n" );
		}
	}
	&verifyCheckSum( $dir );
	if( $zrm_error == 0 ){
		&printLog( "Verification successful\n" );
	}else{
		&printError( "Errors found during verification\n" );
	}
	if( defined $indexdata{"compress"} || defined $indexdata{"encrypt"} ){
		&removeUncompressedBackup( $inputs{"source-directory"}, \%indexdata );
	}
}

#Sets up defaults for backup
sub setupDefaults()
{
        $action = "verify-backup";
        $USAGE_STRING = $USAGE_VERIFY_BACKUP_STRING.$USAGE_COMMON_OPTIONS_STRING;
}

sub main()
{
	if( ! defined $_[0] && 
	    defined $ARGV[0] && $ARGV[0] eq "--create-checksum" ){
		&doStart();
		&my_exit();
	}
        &setupDefaults();
        &initCommon(@VERIFYOPT);
	&createConfigFile();
        &doVerifyBackup();
        &my_exit();
}

&main();


