#!/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 Getopt::Long;
use File::Copy;
use File::Find;
use File::Path;
use File::Spec::Functions;
use File::Basename;
use POSIX qw(strftime);
use Fcntl ':flock';
use File::Temp qw/ :POSIX /;
use lib "/usr/local/lib/mysql-zrm";
use ZRM::Common;
use ZRM::MySQL;
use File::Spec::Functions;

$SIG{'TERM'} = sub { $abort_flag = 1; };
$SIG{'PIPE'} = sub { &printWarning( "Communication pipe broke. Continuing\n" ); };


my $MD5SUM="md5";


my $MAILCMD="";
if ($^O =~ /^solaris/) {
	$MAILCMD="/usr/ucb/mail";
} else {
	$MAILCMD="mail";
}

#Neither mysqlhotcopy not mysqldump will do a --flush-logs
#so as to avoid flushing multiple times.
#Instead flush logs wil lbe called explicitly before data is backed up
my $MYSQLDUMP5="mysqldump --opt --extended-insert --create-options";
my $MYSQLDUMP41="mysqldump --opt --extended-insert";
# Lock tables and default-character-set are not compatible in mysqldump 4.0
my $MYSQLDUMP40="mysqldump --opt --extended-insert --all";

my %SUPPORTED_NONTRANS_ENGINES = ( MyISAM => "1" ,
			  	   MRG_MyISAM => "1",
			  	   MERGE => "1",
				   FEDERATED => "1",
				   CSV => "1",
				   NULL => "1",
				   ARCHIVE => "1" );
my %SUPPORTED_IHB_ENGINES = ( 	MyISAM => "1" ,
			     	MRG_MyISAM => "1",
			     	MERGE => "1",
				FEDERATED => "1",
			   	InnoDB => "1" );
#Setup defaults
my $pid_file = 0;
my $flushLogsTime=0;
my $readLocksTime=0;
my @dirList;
my %checkForDir;
my %lvmInnoDBDirTable;
my %lvmDirTable;
my %snapshotDirMap;
my %snapshotNameMap;
my %fsTypeMap;
my %devMountPnt;
my %relativeCopyDir;
my %mountTable;
my %mountFSType;
my $linksToBeCreated = 1;
my %mdcheck;
my $nextBinLog;
my $index_open = 0;
my $logicalCet = 0;
my $linkFile = "";


my $USAGE_BACKUP_STRING=
               "\t\t[--user\ <user>]\ [--password\ <password>]\n".
                "\t\t[--host\ <hostname>]\ [--port\ <portnumber>]\n".
                "\t\t[--socket\ <name>]\ [--ssl-options\ <\"MySQL\ ssl\ options\">]\n".
                "\t\t[--mysql-binpath\ <mysql\ binaries\ directory>]\n".
                "\t\t[--mysql-binlog-path <mysql binary log directory>]\n".
                "\t\t[--backup-name <name>]\n".
                "\t\t[--mailto <mail address>]\n".
                "\t\t[--mail-policy <always|never|only-on-error>]\n".
                "\t\t[--comment \"This is a comment\"]\n".
                "\t\t[--all-databases]\ [--databases\ <\"name1\ name2\ ...\">]\n".
                "\t\t[--database <name> [--tables <\"name1 name2 ...\">]]\n".
 		"\t\t[--exclude-pattern <pattern>]\n".
 		"\t\t[--backup-level <0|1>]\n".
                "\t\t[--backup-mode <raw|logical>]\n".
		"\t\t[--destination\ <directory\ name>]\n".
                "\t\t[--lvm-snapshot <size> (This option is deprecated. Use snapshot-size)]\n".
                "\t\t[--snapshot-size <size>]\n".
                "\t\t[--snapshot-plugin <plugin>]\n".
                "\t\t[--backup-type <regular|quick>]\n".
                "\t\t[--replication\ |\ --noreplication]\n".
                "\t\t[--default-character-set <char set name>]\n".
                "\t\t[--compress|--no-compress]\n".
                "\t\t[--compress-mysqldump-onthefly|--no-compress-mysqldump-onthefly]\n".
                "\t\t[--compress-plugin <program>]\n".
                "\t\t[--encrypt|--no-encrypt]\n".
                "\t\t[--encrypt-plugin <program> [--decrypt-option <option>]]\n".
                "\t\t[--passfile <path>]\n".
                "\t\t[--copy-plugin <plugin>]\n".
                "\t\t[--ssh-user <user>]\n".
                "\t\t[--socket-remote-port <port>]\n".
                "\t\t[--remote-mysql-binpath <path>]\n".
                "\t\t[--windows-backup-port <port>]\n".
                "\t\t[--snapshot-device <device>]\n".
                "\t\t[--vxfs-binpath <path>]\n".
                "\t\t[--netapp-user <user>]\n".
                "\t\t[--netapp-password <password>]\n".
		"\t\t[--emc-host <emc-host-name>]\n".
		"\t\t[--navicli-binpath <path>]\n".
		"\t\t[--storage-group <storage-group-name>]\n".
                "\t\t[--pre-backup-plugin <plugin>]\n".
                "\t\t[--pre-backup-plugin-options <\"option1 option2 ...\">]\n".
                "\t\t[--post-backup-plugin <plugin>]\n".
                "\t\t[--post-backup-plugin-options <\"option1 option2 ...\">]\n".
                "\t\t[--routines|--no-routines]\n".
                "\t\t[--single-transaction <always|never|only-innodb>]\n".
		"\t\t[--extra-mysqldump-options <\"Other mysqldump options needed\">]\n".
		"\t\t[--ihb-plugin=path to ihb-plugin]\n".
		"\t\t[--ihb-binpath=path to ibbackup & innobackup.pl\n".
		"\t\t[--ihb-sleep=ms]\n".
		"\t\t[--ihb-use-memory=mb]\n".
                "\t\t[--synchronous-checksum\ |\ --no-synchronous-checksum]\n".
                "\t\t[--config-file-list=path to file containing list of configuration files]\n".
                "\t\t[--check-innodb\ |\ --no-check-innodb]\n".
                "\t\t[--compress-data-during-transport|--no-compress-data-during-transport]\n".
                "\t\t[--bluearc-host <bluearc hostname>]\n".
                "\t\t[--bluearc-user <user>]\n".
                "\t\t[--bluearc-password <password>]\n".
                "\t\t[--bluearc-ssc-bin-path <path to bluearc ssc utility>]\n".
		"\t\t[--ssh-identity-file <file>]\n".
                "\t\t[--retention-policy <amount of time to retain backup>]\n";

my @BACKUPOPT = qw/backup-name=s
              	   user=s
              	   password=s
              	   host=s
              	   port=i
              	   socket=s
              	   ssl-options=s
              	   mysql-binpath=s
              	   mysql-binlog-path=s
                   mailto=s
		   mail-policy=s
                   comment=s
                   all-databases
                   databases=s
                   database=s
                   tables=s
		   exclude-pattern=s
                   backup-level=i
                   backup-mode=s
                   destination=s
                   lvm-snapshot=s
		   snapshot-size=s
		   snapshot-plugin=s
                   replication!
                   default-character-set=s
                   compress!
                   compress-mysqldump-onthefly!
                   compress-plugin:s
                   encrypt!
                   encrypt-plugin:s
                   decrypt-option=s
                   passfile=s
                   copy-plugin=s
                   ssh-user=s
                   socket-remote-port=i
                   remote-mysql-binpath=s
		   windows-backup-port=i
		   snapshot-device=s
		   backup-type=s
		   vxfs-binpath=s
		   netapp-user=s
                   netapp-password=s
		   emc-host=s
		   navicli-binpath=s
		   storage-group=s
                   pre-backup-plugin=s
                   pre-backup-plugin-options=s
                   post-backup-plugin=s
                   post-backup-plugin-options=s
                   routines!
                   single-transaction=s
		   extra-mysqldump-options=s
		   synchronous-checksum!
		   config-file-list=s
		   check-innodb!
		   ihb-plugin=s
		   ihb-binpath=s
		   ihb-sleep=i
 		   ihb-use-memory=i
		   compress-data-during-transport!
		   bluearc-host=s
		   bluearc-user=s
                   bluearc-password=s
                   bluearc-ssc-bin-path=s
		   ssh-identity-file=s
                   retention-policy=s/;

#$_[0] file name to mail
#$_[1] Subject of mail
sub sendToEventLog()
{
	
	eval "use Win32::EventLog";

	my %hash;

	if( $_[1] =~ /ERROR during backup/ ){
		$hash{"EventType"} = Win32::EventLog->EVENTLOG_ERROR_TYPE;
	}else{
		$hash{"EventType"} = Win32::EventLog->EVENTLOG_INFORMATION_TYPE;
	}

	if( ! open( TP, $_[0] ) ){
			&printWarning( "Not able to open mail file $_[0]\n" );
			return;
	} 
	my @a = <TP>;
	close TP;
	my $msg = "\n\n";
	$msg .= join( "", @a );

	$hash{"Source"} = "Zmanda Recovery Manager";
	$hash{"EventID"} = 100;
	$hash{"Strings"} = $msg;

	my $handle = Win32::EventLog->new( "Application" );
	if( ! defined $handle ){
			&printWarning( "Not able to create EventLog handle\n" );
			return;
	}
	$handle->Report( \%hash );
	$handle->Close();
}

#mail given file
#$_[0] file name to mail
#$_[1] Subject of mail
sub mailFile()
{
	if( $zrm_error== 0 && $inputs{"mail-policy"} eq "only-on-error" ){
		return;
	}
        if( $inputs{"mailto"} ){
		if( $onWindows ){
			&sendToEventLog( @_ );
		}else{
                	if( $verbose ){
                        	&printLog( "mailing file $_[0]\n" );
                	}
		
                	my $command = $CAT." \"".$_[0]."\"|";
                	my $mcmd = $MAILCMD." -s \"".$_[1]."\" ".$inputs{"mailto"};
                	if( $verbose ){
                        	&printLog( "mail command is ".$command.$mcmd."\n" );
                	}
                	my $r = system( $command.$mcmd );
                	if( $r > 0 ) {
                        	&printError( "mail command failed ".$command.$mcmd."\n" );
                	}
		}
        }
}

# Exit routine that is hooked up to my_exit and is called from my_exit to do any cleanup
sub backupExit()
{
	&printStartPhase( "Cleanup\n" );
        &doRemoveSnapshot();
	&startSlave();
	&closeIndexFile();
        if( $zrm_error > 0 ){
                my $file = tmpnam();
                if( open( TMPH, ">$file" ) ){
                        print TMPH "There were errors during this backup run. Please check $LOGNAME file for details\n";
			if( defined $backupset ){
				print TMPH "backup-set=$backupset\n";
			}
			print TMPH "backup-level=".$inputs{"backup-level"}."\n";
			print TMPH "backup-mode=".$inputs{"backup-mode"}."\n";
			if( $inputs{"host"} ){
				print TMPH "host=".$inputs{"host"}."\n";
			}
			my $x = localtime($start_time);
			print TMPH "backup-date=$x\n";
                        close( TMPH );
			my $sub = "[ZRM for MySQL Report] ERROR during backup";
			if( defined $backupset ){
				$sub .= " of backup-set $backupset";
			}
			&mailFile( $file, $sub );
                        unlink( $file );
                }
        }

	if( $pid_file == 1 && -f "$backupset_dir/.mysql-zrm.pid" ){
                unlink "$backupset_dir/.mysql-zrm.pid";
        }

	&printEndPhase( "Cleanup\n" );

}

#$_[0] directory where to create index file
sub createIndexFile()
{
        my $f = catfile( $_[0], $INDEX_FILENAME );
        open (INDEX, ">$f") || &printAndDie("Cannot create index file. $!\n");
        $index_open = 1;
        return $f;
}

#prints message to the index file
sub printIndex()
{
        &printLog($_[0]);
        print INDEX $_[0];
}

#Closes the index file if open
sub closeIndexFile()
{
        if( $index_open == 1 ){
                my $msg;
                if( $zrm_error == 2 ){
                        $msg="Backup failed\n";
                } elsif( $zrm_error == 1 ){
                        $msg="Backup done but with errors\n";
                }else {
                        $msg="Backup succeeded\n";
                }
                &printIndex( "backup-status=".$msg );
                close( INDEX );
                $index_open = 0;
                &printLog( $msg );
        }
}

#lists all of the databases in the mysql server
sub enumAllDatabases()
{
	my $p = &addMySQLParams($MYSQL);
	my $command = "$p -e \"show databases;\"";
	if( $verbose ) {
		&printLog( "Getting list of Databases\n" );
		&printLog( $command."\n" );
	}
	my $s = &execCmdAndGetOutput($command);
	if( !defined $s ){
		&printAndDie( "Could not enumerate databases\n" );
	}
	my @q = split( "\n", $s );
	my @db;
	my $j = 0;
	for (my $i=1;$i<=$#q;$i++) {
		if( $q[$i] ne "information_schema" ) {
			$db[$j] = $q[$i];
			$j++;
		}
	}
	return @db;
}

#get the engines used by given tables in the given database
#$_[0] specifies the db
#$_[1] if present specifies the table;
sub getEngines()
{
	my $command = &addMySQLParams($MYSQL);
	$command = $command." -e \"show table status from \\`".$_[0]."\\`";
        if( $_[1] ){
                $command = $command." like '".$_[1]."'";
        }
	$command = $command."\"";
	if( $verbose ) {
		&printLog( "Command used for getting engine type ".$command."\n" );
	}
	my $x = &execCmdAndGetOutput( $command );
	if( !defined $x ) {
		&printAndDie( "Could not find storage engine for tables in database $_[0]. Command used is ".$command."\n" );
	}
        my @out = split( "\n", $x );
	#Remove the header
        shift @out;
        chomp( @out );
        my @a;
        push @a, "engine";
        foreach( @out ){
                my @r = split( /\t/, $_ );
		if( $r[1] ){
			#remove white spaces in front
                       	$r[1]=~ s/^\s+//;
			#remove white spaces at the end
                       	$r[1]=~ s/\s+$//;
                       	push @a, $r[1];
		}
        }
	if( $verbose ){
		&printLog( "For database $_[0]\n" );
		if( $_[1] ){
			&printLog( "and for table $_[1]\n" );
		}
		foreach( @a ){
			&printLog( $_."\n" );
		}

	}
        return @a;
}

#Checks if any of the given tables are using a non transaction engine
#$_[0] specifies the database
#$_[1] specifies the list of tables
sub checkIfNonTransTables()
{
	if( ! defined $inputs{"single-transaction"} ){
		$inputs{"single-transaction"} = "always";
	}
	if( $inputs{"single-transaction"} ne "only-innodb" ){
		return 0;
	}
	my %checkHash;
	%checkHash = %SUPPORTED_NONTRANS_ENGINES;
	my @tables = split ( " ", $_[1] );
	foreach( @tables )
	{
		my @q = &getEngines( $_[0], $_ );
		if( !$q[1] ){
			&printAndDie( "Could not find table $_ in database $_[0]\n" );
		}
		if( $q[1] eq "" || $checkHash{$q[1]} ) {
			return 1;
		}
	}
	return 0;
}

#Checks if any of the given databases are using a non transaction engine
#@_ specfies the list of databases
sub checkIfNonTransDbs()
{
	if( ! defined $inputs{"single-transaction"} ){
		$inputs{"single-transaction"} = "always";
	}
	if( $inputs{"single-transaction"} ne "only-innodb" ){
		return 0;
	}
	my $trans = "";
	my $non_trans = "";

	my %checkHash = %SUPPORTED_NONTRANS_ENGINES;
	foreach( @_ ) {
		my @q = &getEngines( $_ );
		my $supported_trans = 0;
                my $supported_non_trans = 0;
		for (my $i=1;$i<=$#q;$i++) {
			if( $q[$i] eq "" || $checkHash{$q[$i]} ){
				return 1;
			}
		}

	}
	return 0;
}

#Checks if any of the given tables are using either InnoDB or BDB engines
#If mysql server is run on windows all tables will be labeled as
# non transactional since all types of databases can be backedup using VSScopy
#$_[0] specifies the database
#$_[1] specifies the list of tables
sub checkIfTrans()
{
	if( $mysql_server_os eq $MYSQL_WINDOWS ){
		return 0;
	}
	my %checkHash;
	if( $have_innodb eq "YES" && defined $inputs{"ihb-plugin"} && $inputs{"ihb-plugin"} ne "" ){
		%checkHash = %SUPPORTED_IHB_ENGINES;
	}else{
		%checkHash = %SUPPORTED_NONTRANS_ENGINES;
	}
	my @tables = split ( " ", $_[1] );
	foreach( @tables )
	{
		my @q = &getEngines( $_[0], $_ );
		if( !$q[1] ){
			&printAndDie( "Could not find table $_ in database $_[0]\n" );
		}
		if( $q[1] eq "" || !$checkHash{$q[1]} ) {
			return 1;
		}
	}
	return 0;
}
#Splits the list of databases into non-transactional and transactional databases
#If mysql server is run on windows all databases will be labeled as
# non transactional since all types of databases can be backedup using VSScopy
#@_ specfies the list of databases
sub splitUp()
{
	my $trans = "";
	my $non_trans = "";

	my %checkHash;
	if( defined $inputs{"ihb-plugin"} && $inputs{"ihb-plugin"} ne "" ){
		%checkHash = %SUPPORTED_IHB_ENGINES;
	}else{
		%checkHash = %SUPPORTED_NONTRANS_ENGINES;
	}
	foreach( @_ ) {
		if( $mysql_server_os eq $MYSQL_WINDOWS ){
			$non_trans = $non_trans.$_." ";
			next;
		}
		my @q = &getEngines( $_ );
		my $supported_trans = 0;
                my $supported_non_trans = 0;
		for (my $i=1;$i<=$#q;$i++) {
			if( $q[$i] eq "" ){
				$supported_non_trans++;
			} elsif( $checkHash{$q[$i]} ) {
				$supported_non_trans++;
			}else{
				$supported_trans++;
			}
		}

		if ( $supported_trans > 0 ) {
			$trans = $trans.$_." ";
		} elsif( $supported_non_trans > 0 ) {
			$non_trans = $non_trans.$_." ";
		} else {
			&printWarning( "Database $_ is empty and hence will not be backedup\n" );
		}
	}
	return($trans, $non_trans);
}

#fire the mysqlhotcopy command
#$_[0] specifies the list to be copied.
sub doMySqlHotCopy()
{
	my $hotcopy_cmd;
	if( $inputs{"copy-plugin"} ){
		$hotcopy_cmd = $inputs{"copy-plugin"};
	}else{
		$hotcopy_cmd = $MYSQLHOTCOPY;
	}
	my $p = &addMySQLParams($hotcopy_cmd);
	$p .= " --quiet ";
	my $command = " ".$_[0]." \"".$inputs{"destination"}."\"";
	$command = $command." > ".$LOGGER. " 2>&1";
	if( $verbose ) {
		&printLog( "Command used for raw backup is ".$p.$command."\n" );
	}
        if( $abort_flag ){
                &abortAndDie( );
        }
	my $ti = time();
	my $r = system($p.$command);
	$readLocksTime = $readLocksTime + (time() - $ti);
        if( $abort_flag ){
                &abortAndDie( );
        }
	if( $r > 0 ) {
		&printCommandOutputToLog( "ERROR", "mysqlhotcopy", $LOGGER );
		&printAndDie("mysqlhotcopy did not succeed. Command used is ".$p.$command." Return value is ".$r."\n");
	}else {
		if( $verbose ){
			&printCommandOutputToLog( "INFO", $MYSQLHOTCOPY, $LOGGER );
		}
	}

}

sub getLinksTobeCreated()
{
	if( !defined $inputs{"snapshot-plugin"} ){
		return;
	}
	my $cmd = $inputs{"snapshot-plugin"};
	$cmd .= " --action supports-regular";
	$cmd .= " 2>$LOGGER";
	if( $verbose ){
		&printLog( "checking if links should be created using command $cmd\n" );
	}
	my $out = &execCmdAndGetOutput( $cmd, "NO ERROR" );
	if( defined $out && $out =~ /^no\n$/ ){
		$linksToBeCreated = 0;
		if( $inputs{"backup-type"} ne "quick" ){
			&printAndDie( $inputs{"snapshot-plugin"}." supports only backup-type quick\n" );
		}
	}	
}

# Finds out if database is on a logical volume.
# Not relevant on windows
# $_[0] specifies the directory;
# Returns volume device, snapshot device name, mnt point, fstype, mnt point for snapshot
sub checkIfDirIsOnLogicalVol()
{
	if( $onWindows ) {
		&printAndDie( "Cannot use --snapshot-size on windows" );
	}
	if( defined $inputs{"snapshot-plugin"} ){
		my $snapshotMntPoint = tmpnam();
		my @suf;
		my $snp = basename( $snapshotMntPoint, @suf );
		$snp = "zrm".$snp;
		my $cmd = $inputs{"snapshot-plugin"};
		$cmd .= " --action get-vm-device-details";
		$cmd .= " --directory $_[0]";
		$cmd .= " --sname $snp";
		if( defined $inputs{"host"} && $inputs{"host"} ne "localhost" ){
			$cmd .= " --host ".$inputs{"host"};
		}

		$cmd .= " 2>$LOGGER";
		if( $verbose ){
			&printLog( "getting device details using command $cmd\n" );
		}
		my $out = &execCmdAndGetOutput( $cmd, "NO ERROR" );
		if( ! defined $out ) {
			&printWarning( "Error getting device details for $_[0]\n" );
			&printCommandOutputToLog( "WARNING", "Getting Device Details", $LOGGER );

		}
		if( !defined $out || $out eq "" ){
			return;
		}else{
			my @ret = split( "\n", $out );
			my %oo;
			foreach( @ret ){
				my @o = split( /=/, $_ );
				$oo{$o[0]}=$o[1];
			}
			my $x = &removeExtraSlashesFromPath( $_[0] );
			my $y = &removeExtraSlashesFromPath( $oo{"relative-copy-dir"} );
			$relativeCopyDir{$x} = $y;
			if( ! ($oo{"snapshot-mount-point"} =~ /^\//) ){
				$oo{"snapshot-mount-point"} = $inputs{"destination"}."/".$MOUNT_POINT."/".$oo{"snapshot-mount-point"};

			}
			return $oo{"device"}, $oo{"snapshot-device"}, $oo{"device-mount-point"}, $oo{"filesystem-type"}, $oo{"snapshot-mount-point"};
		}
	}
}

#Will return the link directory if it is a link
#$_[0] directory path
#$_[1] remaining path
sub getIfLink()
{
	my $t = &removeExtraSlashesFromPath( $_[0] );
        my $r = readlink($t);
        if($r) {
                if( $_[1] ){
                        $r = $r."/".$_[1];
                }
                return $r;
        }
        my @x = split( "/", $t );
        if( @x ){
                my $y = pop @x;
                if( $_[1] ){
                        $y = $y."/".$_[1];
                }
                $r = join( "/", @x );
                &getIfLink($r, $y);
        }
}

#This function will return the relative path from the mount point
#Not relevant on windows
#$_[0] is full path to db
#$_[1] is mnt pnt
sub getRelativeCopyDir()
{
        my $y = &getIfLink( $_[0] );
        if( !$y ) {
                $y = $_[0];
        }
        $y=~/$_[1]/;

        my $z=$';
        if($z){
                return $z;
        }
}

# This mounts a snapshot if not already mounted
# Not relevant on windows
#$_[0] = dbname or filename
#$_[1] = volume device
#$_[2] = snapshot device
#$_[3] = mnt pnt of dir
#$_[4] = fstype
#$_[5] = list of tables to backup or list of files to backup
sub doMount()
{
	my $tmpDir = $snapshotDirMap{"$_[1]"};
	if( !$mountTable{$tmpDir} ){
		#my $snp = $_[2];
		my $snp = $snapshotNameMap{"$_[1]"};
		if( $linksToBeCreated != 0 ){
			my $command = $inputs{"snapshot-plugin"};
			$command .= " --action mount";
			$command .= " --dev $snp";
			$command .= " --directory $tmpDir";
			$command .= " --fstype $_[4]";
			if( defined $inputs{"host"} && $inputs{"host"} ne "localhost" ){
				$command .= " --host ".$inputs{"host"};
			}
			if( $verbose ) {
				&printLog( "Mounting snapshot\n" );
				&printLog( $command."\n" );
			}
			my $r = system( $command );
			if( $r ) {
				&printWarning( "Snapshot failed\n" );
				&printCommandOutputToLog( "WARNING", "mount", $LOGGER );
				return 0;
			}
		}
		$mountTable{$tmpDir} = $snp;
		$mountFSType{$tmpDir} = $_[4];
	}
	return 1;
}

#$_[0] srcDir
#$_[1] srcFile
#$_[2] trgtDir
sub createInternalLinksOnRemote()
{
	if( $linkFile eq "" ){
		$linkFile = tmpnam();
		unless( open( LI, ">$linkFile" ) ){
			$linkFile = "";
			return 1;
		}
	}
	print LI "$_[2]\n";
	print LI "$_[0]/$_[1] $_[2]\n";
	return 0;
}

sub createRemoteLinks()
{
	close LI;
	if( $linksToBeCreated == 0 ){
		return 0;
	}
	my @params;
	push @params, "--create-link";
	push @params, "--source-file";
	push @params, $linkFile;
	push @params, "--destination-host";
	push @params, $inputs{"host"};
	push @params, "--destination-directory";
	push @params, "File";
	my $cmd = $inputs{"copy-plugin"};
	my $r = system( $cmd, @params );
	if( $r != 0 ){
		return 1;
	}
	return 0;
}

#$_[0] srcDir
#$_[1] srcFile
#$_[2] trgtDir
sub createInternalLinks()
{
	if( defined $inputs{"host"} && $inputs{"host"} ne "localhost" ){
		return &createInternalLinksOnRemote( $_[0], $_[1], $_[2] );
	}
	my $srcDir = $_[0];
	my $srcFile = $_[1];
	my $trgtDir = $_[2];
	my $r;

	mkpath( $trgtDir );
	my $cmd = "ln -s $srcDir/$srcFile $trgtDir";
	$r = system( $cmd );

	return $r;
}

sub doCopyFromLinksUsingTar()
{
	my $srcDir = catfile( $inputs{"destination"}, $LINK_POINT );
	my $destDir = catfile( $inputs{"destination"} );
	my $srcFile = ".";
	return &copyUsingTar( $srcDir, $srcFile, $destDir );
}

# This uses LVM snapshot to take backup
# Not relevant on windows
#$_[0] = dbname or filename
#$_[1] = volume device
#$_[2] = snapshot device
#$_[3] = mnt pnt of dir
#$_[4] = fstype
#$_[5] = list of tables to backup or list of files to backup
sub createLinksFromSnapshot()
{
	#Mount the snapshot
        if( $abort_flag ){
                &abortAndDie( );
        }
	my $r = &doMount( @_ );
	if( $r == 0 ){
		return;
	}
	if( $linksToBeCreated == 0 ){
		return 1;
	}
	my $tmpDir = $snapshotDirMap{"$_[1]"};
	my $name = $_[0];
	my $n;
	if( $name=~/^\// ){
		$n = $name;
	}else{
		$n = "\"$datadir/$name\"";
	}
	#my $reldir = &getRelativeCopyDir( $n, $_[3] );
	my $nn = &removeExtraSlashesFromPath( $n );
	my $reldir = $relativeCopyDir{$nn};
	my @fileDet = split /;/, $reldir;
	$reldir = $fileDet[0];
	my $cpdir = $tmpDir;
	if( $reldir ) {
		$cpdir = $cpdir."/".$reldir;
	}
	my $trgtDir;
	my $srcDir;
	my $includeList = "";

	$srcDir = $name;
	$trgtDir = "$inputs{destination}/$LINK_POINT";
	if( $_[5] ){
		if( $n eq $name ){
			$srcDir = $_[5];
			$trgtDir = "$trgtDir/$name";
			#mkpath( $trgtDir );
		}else{
			$includeList = $_[5];
			$cpdir = "$cpdir/$name";
			$trgtDir = "$trgtDir/$name";
			# We need to set permissions & ownership correctly

			#mkpath( $trgtDir, 0, $fileDet[1]&07777 );
			#chown( $fileDet[2], $fileDet[3], $trgtDir );
			$cpdir = dirname( $cpdir );
		}
	}else{
		$cpdir = dirname( $cpdir );
	}

	if( $includeList ne "" ){
		my @list = split /\s/, $includeList;
		foreach( @list ){
			$r = &createInternalLinks( $cpdir, "$_.*", $trgtDir );
			if( $r ){
				last;
			}
		}
	}else{
		$r = &createInternalLinks( $cpdir, $srcDir, $trgtDir );
	}

	if( $r ) {
		&printWarning( "Snapshot failed as copy failed\n" );
		rmtree( $inputs{"destination"}."/".$name, 0, 0 );
		$r = 0;
	}else {
		$r = 1;
	}
        if( $abort_flag ){
                &abortAndDie( );
        }

	return $r;
}

# Unmounts all of the snapshots
sub doUnmount()
{
	for( keys%mountTable ){
		my $tmpDir = $_;
		#umount
		my $umnt = $inputs{"snapshot-plugin"};
		$umnt .= " --action umount --directory $tmpDir";
		if( defined $inputs{"host"} && $inputs{"host"} ne "localhost" ){
			$umnt .= " --host ".$inputs{"host"};
		}
		if( $verbose ) {
			&printLog( "Unmounting\n" );
			&printLog( $umnt."\n" );
		}
		my $r = system($umnt);
		if( $r > 0 ){
			&printWarning( "umount failed. Command used is ".$umnt."\n" );
		}
	}
	%mountTable = ();
	&doRemoveMountDir();
}

#$_[0] volume device
#$_[1] snapshot device
#$_[2] snapshot device mount point
#$_[3] fstype
#$_[4] device mount point
sub insertIntoSnapshotMap()
{
	my $t = $_[0];
	if( !$snapshotDirMap{$t} ){
		$snapshotDirMap{$t} = $_[2];
		$snapshotNameMap{$t} = $_[1];
		$fsTypeMap{$t} = $_[3];
		$devMountPnt{$t} = $_[4];
	}
}

# Checks if the innodb shared date is on lvm.
sub checkIfInnoDBOnLVM()
{
	if( $have_innodb eq "YES" ){
		# If the shared data is on a raw volume we cant use snapshots
		if( $innodb_shared_data_on_fs == 0 ){
			return 0;
		}
		my @a = &checkIfDirIsOnLogicalVol( $innodb_log_dir );
		my $x = @a;
		if( $x > 1 ){
			$lvmInnoDBDirTable{"*innodb_log"} = \@a;
			&insertIntoSnapshotMap( $a[0], $a[1], $a[4], $a[3], $a[2] );
			foreach( @innodb_shared_data_files ){
				my $dir = dirname( $_ );
				my @b = &checkIfDirIsOnLogicalVol( $dir );
				$x = @b;
				if( $x <= 1 ){
					%lvmDirTable = ();
					return 0;
				}
				$lvmInnoDBDirTable{$_}=\@b;
				&insertIntoSnapshotMap( $b[0], $b[1], $b[4], $b[3], $b[2] );
			}
		}else{
			return 0;
		}
		return 1;
	}else{
		return 0;
	}
}

# Check for if any of the given tables are innodb;
# $_[0] db to check
# $_[1] specifies the table to check
# returns 0 if no innodb present
# returns 1 if innodb present
sub checkIfTablesInnoDB()
{
	my @tables = split ( " ", $_[1] );
	foreach( @tables )
	{
		my @q = &getEngines( $_[0], $_ );
		if( !$q[1] ){
			&printAndDie( "Could not find table $_ in database $_[0]\n" );
		}
		if( $q[1] eq "InnoDB" ){
			return 1;
		}
	}
	return 0;
}

# $_[0] db to check
# returns 0 if no innodb present
# returns 1 if innodb present
sub checkIfInnoDBPresent()
{
	# If innodb is not enabled, then always return no innodb.
	if( $have_innodb ne "YES" ){
		return 0;
	}
	if( defined $inputs{"check-innodb"} && $inputs{"check-innodb"} == 1 ){

		if( $inputs{tables} ){
			return &checkIfTablesInnoDB( $_[0], $inputs{tables} );
		}
		my @q = &getEngines( @_ );
		my $x = join( "|", @q );
		if( $x=~/InnoDB/ ){
			return 1;
		}
		return 0;
	}else{
		return 1;
	}
}

# $_[0] name of device
# $_[1] name of snapshot device
# log file name
sub createSnapshotCommand()
{
	my @suf;
	my $log = $snapshotDirMap{"$_[0]"};
	$log .= ".log";
	$log = basename( $log );
	$log = "$TMPDIR/$log";
	my $snp = basename( $_[1], @suf );
	my $command = "system (".$inputs{"snapshot-plugin"};
	$command .= " --action create-snapshot --dev $_[0]";
	$command .= " --size ".$inputs{"snapshot-size"};
	$command .= " --sname $snp";
	$command .= " --directory \"".$snapshotDirMap{"$_[0]"}."\"";
	$command .= " --fstype ".$fsTypeMap{$_[0]};
	$command .= " --device-mount-point \"".$devMountPnt{$_[0]}."\"";
	if( defined $inputs{"host"} && $inputs{"host"} ne "localhost" ){
		$command .= " --host ".$inputs{"host"};
	}
	$command .= " >$log 2>&1 );";
	return $command;
}

#$_[0] trgtDir
#$_[1] type of dir
sub removeOnRemote()
{
	my $destinationDirectory = $_[0];
	my @params;
	push @params, "remove-backup-data";
	push @params, "--backup-dir";
	push @params, $destinationDirectory;
	push @params, "--type-of-dir";
	push @params, $_[1];
	push @params, "--host";
	push @params, $inputs{"host"};
	my $cmd = $inputs{"copy-plugin"};
	my $r = system( $cmd, @params );
	if( $r != 0 ){
		return 1;
	}
	return 0;
}

sub doRemoveInternalLinks()
{
	if( $linksToBeCreated == 0 ){
		return;
	}
	my $dir = $inputs{"destination"};
	if( defined $inputs{"host"} && $inputs{"host"} ne "localhost" ){
		return &removeOnRemote( $dir, "LINKS" );
	}
	$dir = catfile( $dir, $LINK_POINT );
	rmtree(	$dir, 0, 0 );
}

sub doRemoveMountDir()
{
	if( $linksToBeCreated == 0 ){
		return;
	}
	my $dir = $inputs{"destination"};
	if( defined $inputs{"host"} && $inputs{"host"} ne "localhost" ){
		return &removeOnRemote(  $dir, "MOUNTS" );
	}
	$dir = catfile( $dir, $MOUNT_POINT );
	rmtree(	$dir, 0, 0 );
}

# Deletes snapshots from the system
sub doRemoveSnapshot()
{
	my $x = keys%snapshotDirMap;
	if( $x == 0 ){
		return;
	}
	&doRemoveInternalLinks();
	&doUnmount();
	for(keys%snapshotDirMap){
		my $snp = $snapshotNameMap{$_};
		my $rmcommand = $inputs{"snapshot-plugin"};
		$rmcommand .= " --action remove-snapshot";
		if( defined $inputs{"host"} && $inputs{"host"} ne "localhost" ){
			$rmcommand .= " --host ".$inputs{"host"};
		}
		my $dir = $snapshotDirMap{$_};
		$rmcommand .= " --dev \"$snp\" --directory \"$dir\" > $LOGGER 2>&1";
		if( $verbose ){
			&printLog( "$rmcommand\n" );
		}
		my $r = system( $rmcommand );
		if( $r > 0  ){
			&printCommandOutputToLog( "WARNING", "remove-snapshot", $LOGGER );
			&printWarning( "remove snapshot failed. Command used is ".$rmcommand."\n" );
		}
	}
	%snapshotDirMap = ();
}

# Creates the snapshots
sub doSnapshot()
{
	my $cmd = "";
	for(keys%snapshotDirMap){
		$cmd .= &createSnapshotCommand( $_, $snapshotNameMap{$_} );
	}
	my $p = &addMySQLParams($MYSQL);
	#unlink $LOGGER;
	my $command = "";
	$command .= "flush tables with read lock;";
	$command .= " flush logs; show master status;";
	$command .= $cmd;
	$command .= " unlock tables;";
	my $tpFile = tmpnam();
	unless( open( TP_FILE, ">".$tpFile ) ){
		&printAndDie( "Unable to open temporary file $tpFile\n" );
	}
	print TP_FILE $command;
	close TP_FILE;
	$command = "$p < $tpFile";
	if( $verbose ) {
		&printLog( "Locking tables and creating snapshot\n" );
		&printLog( $command."\n" );
	}
	my $ti = time();
	my $out = &execCmdAndGetOutput($command, "no error");
	$readLocksTime = $readLocksTime + (time() - $ti);
	unlink( $tpFile );
	if( !defined $out ){
		&printWarning( "flush tables with read lock and create snapshot failed\n");
	}else{
		&printLog( $out );
        	my @t = split( "\n", $out );
		@t = split( "\t", $t[1] );
		$nextBinLog = $t[0];
	}
	for(keys%snapshotDirMap){
		my $v = basename( $snapshotDirMap{$_} );
		$v = "$TMPDIR/$v.log";
		if( $verbose ){
			&printCommandOutputToLog( "INFO", "Locking tables and creating snapshot", $v );
		}
		unlink( $v );
	}
	if( !defined $out ){
		return "error";
	}else{
		return "";
	}
}

sub createSnapshotDetailsFile()
{
	my $d = keys%mountTable;
	if( $d == 0 ){
		return;
	}
	my $file = catfile( $inputs{"destination"}, $ZRM_MOUNT_DETAILS );
	unless( open( F, ">$file" ) ){
		&printAndDie( "Unable to create snapshot details file $file\n" );
	}
	print F "backup-set=$backupset\n";
	print F "snapshot-plugin=".$inputs{"snapshot-plugin"}."\n";
	if( defined $inputs{"host"}  ){
		print F "host=".$inputs{"host"}."\n";
	}else{
		print F "host=localhost\n";
	}
	for( keys%mountTable ){
		print F "$_=$mountTable{$_};$mountFSType{$_}\n";
	}
	close F;
	%mountTable = ();
	%mountFSType = ();
	%snapshotDirMap = ();
}

#Use LVM based snapshots to backup
#Not relevant on windows
# @_ list of databases to backup
sub doLVM()
{
	if( $onWindows ){
		return @_;
	}
	&getLinksTobeCreated();
	if( $inputs{"backup-type"} eq "regular" && $linksToBeCreated == 0 ){
		&printAndDie( $inputs{"snapshot-plugin"}." snapshot plugin is not compatable with backup-type regular\n" );
	}
	my @db;
	my $insert = 0;
	my @lvmbackup;
	my $innodbOnLVMChecked = 0;
	my $innodbOnLVM = 0;
	if( $verbose ){
		&printLog( "innodb on lvm = $innodbOnLVM\n" );
	}
	foreach( @_ ){
                if( $abort_flag ){
                        &abortAndDie();
                }
		my $cur_db = $_;
		$insert = 0;
		my $innodb;
		if( $innodbOnLVMChecked && $innodbOnLVM ){
			# In this case there is no need to see if the
			# db contains innodb tables or not.
			$innodb = 1;
		}else{
			$innodb = &checkIfInnoDBPresent( $cur_db );
		}
		if( $innodb ){
			# This flag so that we do the shared data on 					# lvm check only once.
			if( ! $innodbOnLVMChecked ){
				$innodbOnLVM = &checkIfInnoDBOnLVM();
				$innodbOnLVMChecked = 1;
			}
			if( !$innodbOnLVM ){
				$insert = 1;
			}
		}
		if( !$insert  ){
			my $d = "\"$datadir/$cur_db\"";
			my @a = &checkIfDirIsOnLogicalVol( $d );
			my $l = @a;
			if( $l > 1 ){
				$lvmDirTable{$cur_db} = \@a;
				&insertIntoSnapshotMap( $a[0], $a[1], $a[4], $a[3], $a[2] );
			}else{
				$insert = 1;
			}
		}
		if( $insert ) {
			push @db, $cur_db;
		} else {
			push @lvmbackup, $cur_db;
		}
		$insert = 0;
	}
	my $len = keys%lvmDirTable;
	if( $len == 0 ){
		push @db, @lvmbackup;
		return @db;
	}
	my $out = &doSnapshot();
	if( $out ){
		push @db, @lvmbackup;
		return @db;
	}

        if( $linksToBeCreated == 0 ){
                # in case ZRM_MOUNT directory has not been created,
                # there is an error since this directory is mandatory
                # to be created in case plugins say it does not
                # support regular snapshots.
                my $mntpnt = catfile( $inputs{"destination"}, $MOUNT_POINT );
                if( ! -d $mntpnt ){
                        &printError( "Creation of snapshots have failed\n" );
                        push @db, @lvmbackup;
                        return @db;

                }
        }
	my $innodb_logs_backup;
	my $innodb_data_backup = "";
	my $q;
	my $success;
	if( $innodbOnLVMChecked && $innodbOnLVM ){
		$q = $lvmInnoDBDirTable{"*innodb_log"};
		$success = &createLinksFromSnapshot( $innodb_log_dir, $$q[0], $$q[1], $$q[2], $$q[3], "ib_logfile*" );
		if( !$success ){
			&printWarning( "Unable to use snapshot \n" );
			push @db, @lvmbackup;
			&doRemoveSnapshot();
			return @db;
		}
		$innodb_logs_backup = "$innodb_log_dir/ib_logfile*";
		delete $lvmInnoDBDirTable{"*innodb_log"};
		for(keys%lvmInnoDBDirTable){
			$q = $lvmInnoDBDirTable{$_};
			my @suf;
			my $basename = basename( $_, @suf );
			my $dirname = dirname( $_ );
			$success = &createLinksFromSnapshot( $dirname, $$q[0], $$q[1], $$q[2], $$q[3], $basename );
			if( !$success ){
				&printError( "Unable to use snapshot for copying inndb shared data $_\n" );
				push @db, @lvmbackup;
				&doRemoveSnapshot();
				return @db;
			}else{
				$innodb_data_backup .= "$_;";
			}
		}
	}
	my $lvm_dbs = "";
	for(keys%lvmDirTable){
		$q = $lvmDirTable{$_};
		if( $inputs{"tables"} ) {
			$success = &createLinksFromSnapshot( $_, $$q[0], $$q[1], $$q[2], $$q[3], $inputs{"tables"} );
		}else{
			$success = &createLinksFromSnapshot( $_, $$q[0], $$q[1], $$q[2], $$q[3] );
		}
		if( !$success ){
			&printError( "Unable to use snapshot for ".$_."\n" );
			push @db, $_;
		}else{
			$lvm_dbs .= "$_ ";
		}
	}
	my $r;
	if( $linkFile ne "" ){
		$r = &createRemoteLinks();
		unlink( $linkFile );
		if( $r ){
			&printError( "Unable to create remote links\n" );
			push @db, @lvmbackup;
			&doRemoveSnapshot();
			return @db;
		}
	}
	if( $inputs{"backup-type"} eq "regular" ){
		$r = &doCopyFromLinksUsingTar();
		if( $r ){
			&printError( "Unable to copy from links\n" );
			push @db, @lvmbackup;
			&doRemoveSnapshot();
			return @db;
		}
		&doRemoveSnapshot();
	}else{
		&createSnapshotDetailsFile();
	}
	if( $innodbOnLVMChecked && $innodbOnLVM ){
		&printIndex( "innodb-data=".$innodb_data_backup."\n" );
		&printIndex( "innodb-logs=".$innodb_logs_backup."\n" );
	}
	if( $inputs{"tables"} ){
		&printIndex( "raw-tables-snapshot=".$inputs{"tables"}."\n" );
	}
	if( $lvm_dbs ne "" ) {
		&printIndex( "raw-databases-snapshot=".$lvm_dbs."\n" );
	}
	return @db;
}

sub doIHBBackup()
{
	my $dbs = $_[0];
	my $tables = "";
	if( defined $_[1] ){
		$tables = $_[1];
	}

	unless( open( FH, ">>$ENV{'ZRM_CONF'}" ) ){
		&printAndDie( "Unable to open conf file\n" );
	}
	print FH "ihb-innodb-data=".$inputs{"ihb-innodb-data"}."\n";
	print FH "ihb-innodb-logs=".$inputs{"ihb-innodb-logs"}."\n";
	print FH "ihb-datadir=".$inputs{"ihb-datadir"}."\n";
	print FH "ihb-innodb-log-file-size=".$inputs{"ihb-innodb-log-file-size"}."\n";
	print FH "ihb-innodb-log-files-in-group=".$inputs{"ihb-innodb-log-files-in-group"}."\n";
	print FH "ihb-databases=$_[0]\n";
	print FH "snapshot-plugin=".$inputs{"ihb-plugin"}."\n";
	if( defined $_[1] ){
		print FH "ihb-tables=$_[1]\n";
	}
	close FH;
	my $command = $inputs{"ihb-plugin"};
	if( defined $inputs{"host"} && $inputs{"host"} ne "localhost" ){
		$command .= " --host=".$inputs{"host"};
	}
	$command .= " --destination-directory=".$inputs{"destination"};
	if( $verbose ) {
		&printLog( "Command used for raw backup is ".$command."\n" );
	}
        if( $abort_flag ){
                &abortAndDie( );
        }
	my $out = &execCmdAndGetOutput( $command );
	#my $r = system($command);
        if( $abort_flag ){
                &abortAndDie( );
        }
	if( ! defined $out ) {
		&printAndDie("innobackup did not succeed. Command used is $command\n");
	}else {
		my @a = split /\n/, $out;
		foreach( @a ){
			if( $_ =~ /read-locks=/ ){
				$readLocksTime += $';
				last;
			}
		}
	}
	my $innoDBData = "";
	my $dir = "";
	foreach $dir ( @innodb_shared_data_files ){
		if( $dir ne "" ){
			$innoDBData .= $dir.";";
		}
	}
	chop( $innoDBData );
	&printIndex( "innodb-data=$innoDBData\n" );
	&printIndex( "innodb-logs=$innodb_log_dir/ib_logfile*\n" );
}

#This does the selection and execution of the raw full backup
sub doRawFullBackup()
{
	my @databases;
	my $trans_db;
	my $nontrans_db;
	if( $inputs{"tables"} ) {
		if( $mysql_server_os eq $MYSQL_WINDOWS ){
			&printStartPhase( "Creating snapshot based backup\n" );
			&doVSSCopy( $inputs{"database"}, $inputs{"tables"} );
			&printEndPhase( "Creating snapshot based backup\n" );
		}else{
			if( defined $inputs{"snapshot-plugin"} ) {
				&printStartPhase( "Creating snapshot based backup\n" );
				push @databases, $inputs{database};
				@databases = &doLVM( @databases );
				my $l = @databases;
				if( $l == 0 ) {
					&printEndPhase( "Creating snapshot based backup\n" );
					return;
				}
				else {
					if( $inputs{"backup-type"} eq "regular" ){
						&printLog( "Unable to use snapshot for tables in ".$inputs{"database"}."\n" );
					}else{
						&printAndDie( "Unable to use snapshot for tables in ".$inputs{"database"}."\n" );
					}
				}
				&printEndPhase( "Creating snapshot based backup\n" );
			}

			my $r = &checkIfTrans( $inputs{"database"}, $inputs{"tables"} );
			if( $r == 0 ){
				&printStartPhase( "Creating raw backup\n" );
				if( $have_innodb eq "YES" && defined $inputs{"ihb-plugin"} && $inputs{"ihb-plugin"} ne "" ){
					&doIHBBackup( $inputs{"database"}, $inputs{"tables"} );
				}else{
					my @tt = split( " ", $inputs{"tables"} );
					my $p = "";
					foreach( @tt ) {
						$p = $p.$inputs{"database"}."./^".$_."\$/"." ";
					}
					&doMySqlHotCopy( $p );
				}
				&printIndex( "raw-tables=".$inputs{"tables"}."\n" );
				&printIndex( "raw-databases=".$inputs{"database"}."\n" );
				&printEndPhase( "Creating raw backup\n" );
			} else {
				&printStartPhase( "Creating logical backup\n" );
				&printWarning( "The table(s) $inputs{tables} of the database $inputs{database} will be backed up in logical mode since some of the tables use a transactional engine.\n" );
				my $ntp = &checkIfNonTransTables( $inputs{"database"}, $inputs{"tables"} );
				doMySqlDump( $inputs{"database"}." ".$inputs{"tables"}, $ntp );
				&printIndex( "logical-tables=".$inputs{"tables"}."\n" );
				&printIndex( "logical-databases=".$inputs{"database"}."\n" );
				&printEndPhase( "Creating logical backup\n" );
			}
		}
	} else {
		#Create the list of databases
		if( $inputs{"databases"} ) {
			@databases = split( " ", $inputs{"databases"} );
		} else {
			@databases = &enumAllDatabases();
		}
		if( defined $inputs{"exclude-pattern"} ){
			@databases = &filterPattern( @databases );
			my $l = @databases;
			if( $l == 0 ){
				&printAndDie( "Nothing to backup after exclude-pattern is applied\n" );
			}
		}
		if( $verbose ){
			&printLog( "backup of the following databases will be done @databases\n" );
		}

		if( $mysql_server_os eq $MYSQL_WINDOWS ){
			&printStartPhase( "Creating snapshot based backup\n" );
                        my $sdbs = join( " ", @databases );
			&doVSSCopy( $sdbs );
			&printEndPhase( "Creating snapshot based backup\n" );
		}else{
			#for all those databases which are on LVM volumes
			#do backup using snapshots.
			if( defined $inputs{"snapshot-plugin"} ) {
				&printStartPhase( "Creating snapshot based backup\n" );
				@databases = &doLVM(@databases);
				&printEndPhase( "Creating snapshot based backup\n" );
				my $remDBs = @databases;
				if( $remDBs == 0 ){
					return;
				}
				if( $remDBs > 0 && $inputs{"backup-type"} eq "quick" ){
					&printAndDie( "Snapshot backup failed\n" );
				}
			}
			#Remaining do mysqlhotcopy or mysqldump
			&printStartPhase( "Find table type\n" );
			($trans_db, $nontrans_db) = &splitUp( @databases );
			&printEndPhase( "Find table type\n" );

			if( $nontrans_db ne "" ){
				&printStartPhase( "Creating raw backup\n" );
				if( $have_innodb eq "YES" && defined $inputs{"ihb-plugin"} && $inputs{"ihb-plugin"} ne "" ){
					&doIHBBackup($nontrans_db);
				}else{
					&doMySqlHotCopy($nontrans_db);
				}
				&printIndex( "raw-databases=".$nontrans_db."\n" );
				&printEndPhase( "Creating raw backup\n" );
			}
			if( $trans_db ne "" ){
				&printStartPhase( "Creating logical backup\n" );
				&printWarning( "The database(s) $trans_db will be backed up in logical mode since they contain tables that use a transactional engine.\n" );
				my @dbs = split " ", $trans_db;
				my $ntp = &checkIfNonTransDbs( @dbs );
				&doMySqlDump("--databases ".$trans_db, $ntp);
				&printIndex( "logical-databases=".$trans_db."\n" );
				&printEndPhase( "Creating logical backup\n" );
			}
		}
	}
}

#Does the execution of the logical backup
sub doLogicalFullBackup()
{
	&printStartPhase( "Creating logical backup\n" );
	my $params;
	my $msg = "";
	my $dbs = "";
	my $index_msg = "";
	my @pdbs;
	if( $inputs{"tables"} ) {
		$params = $inputs{"database"}." ".$inputs{"tables"};
		push @pdbs,  $inputs{"database"};
		push @pdbs, $inputs{"tables"};
		$msg = "Logical backup done for the following table(s) \n";
		$msg = $msg."\t".$inputs{"tables"}."\n";
		$msg = $msg."For the database: ".$inputs{"database"}."\n";
		$index_msg = "logical-tables=".$inputs{"tables"}."\n";
		$index_msg= $index_msg."logical-databases=".$inputs{"database"}."\n";
	} elsif( $inputs{"databases"} ) {
		$params = "--databases ".$inputs{"databases"};
		@pdbs = split " ", $inputs{"databases"};
		$msg = "Logical backup done for the following database(s)\n";
		$msg = $msg."\t".$inputs{"databases"}."\n";
		$index_msg = "logical-databases=".$inputs{"databases"}."\n";
	} else {
		$params = "--all-databases ";
		@pdbs = &enumAllDatabases();
		if( defined $inputs{"exclude-pattern"} ){
			@pdbs = &filterPattern( @pdbs );
			my $l = @pdbs;
			if( $l == 0 ){
				&printAndDie( "Nothing to backup after exclude-pattern is applied\n" );
			}
		}
		if( $verbose ){
			&printLog( "backup of the following databases will be done @pdbs\n" );
		}

		$msg = "Logical backup done for the following database(s)\n";
		foreach( @pdbs ) {
			$dbs = $dbs.$_." ";
		}
		$msg = $msg.$dbs."\n";
		$index_msg = "logical-databases=".$dbs."\n";
	}
	my $ntp = &checkIfNonTrans( @pdbs );
	doMySqlDump($params, $ntp);
	if( $verbose ){
		&printLog( $msg );
	}
	&printIndex( $index_msg );
	&printEndPhase( "Creating logical backup\n" );
}

#@_ list of stuff to check
sub checkIfNonTrans()
{
	if( defined $inputs{"tables"} ){
		return &checkIfNonTransTables( @_ );
	}else{
		return &checkIfNonTransDbs( @_ );
	}
	return 0;
}

#Fires the mysqldump command
#$_[0] lists stuff to be backedup
#$_[1] flag if db using non trans present
sub doMySqlDump()
{
	my $default_char_set = "utf8";
	if( $inputs{"default-character-set"} ){
		$default_char_set = $inputs{"default-character-set"};
	}
	my $MYSQLDUMP;
	my $MASTERDATA="--master-data";
	my $SINGLETRANSACTION="--single-transaction";

        my @a = split /\./, $mysql_version;
        my $len = @a;
        my $major = 0;
        my $minor = 0;
        my $rel = 0;

        # We are interested in only the first 3 numbers in the version
        $major = shift @a;
        if( $len > 1 ){
                $minor = shift @a;
        }
        if( $len > 2 ){
                $rel = shift @a;
        }
        # The last number could have a string at the end.
        # Remove it as we are not interested in that
        $rel=~/^(\d+)/;
        $rel = $1;

        if( $major > 4 ||
            ($major == 4 && $minor > 1) ||
            ($major == 4 && $minor == 1 && $rel > 8 ) ){     # (above 4.1.8)
                $MYSQLDUMP=$MYSQLDUMP5;
                $MYSQLDUMP.=" --default-character-set=$default_char_set";
                if( (($major > 5) ||
                     ($major == 5 && $minor > 0) ||
                     ($major == 5 && $minor >= 0 && $rel > 13)) &&
                     $inputs{"routines"} ){                  # (above 5.0.13)
                        $MYSQLDUMP.=" --routines";
                }
                $MASTERDATA.="=2";
        }elsif( $major < 4 ||
                ($major == 4 && $minor < 1 ) ){              # (4.0.x and below)
                # in version below 4.1.0 we cannot use --create-option and
                # cannot specify a number to --master-data
                # Instead of --create-options we need to use --all
                # --default-character-set also cannot be specifed
                $SINGLETRANSACTION.=" --skip-lock-tables";
                $MYSQLDUMP=$MYSQLDUMP40;
        }elsif( $rel >= 0 && $rel < 2 ){                     # (4.1.0 to 4.1.1)
                # in these version we cannot use --create-option and
                # cannot specify a number to --master-data
                # Instead of --create-options we need to use --all
                $MYSQLDUMP=$MYSQLDUMP41." --all";
                $MYSQLDUMP.=" --default-character-set=$default_char_set";
        }else{                                               # (4.1.2 to 4.1.8)
                # if version is between 4.1.2 & 4.1.8 both inclusive
                # in these version we can use --create-option but
                # cannot specify a number to --master-data
                $MYSQLDUMP=$MYSQLDUMP41." --create-options";
                $MYSQLDUMP.=" --default-character-set=$default_char_set";
        }

	if( $inputs{"replication"} ){
		if( $binLog eq "OFF" ){
			&printAndDie( "Binary logging is off. Logical backup cannot be done\n" );
		}
		$MYSQLDUMP.=" $MASTERDATA";
	}

	if( ! defined $inputs{"single-transaction"} ){
		$inputs{"single-transaction"} = "always";
	}
	if( $inputs{"single-transaction"} eq "always" ){
		$MYSQLDUMP.= " ".$SINGLETRANSACTION;
	}elsif(  $inputs{"single-transaction"} eq "only-innodb" && ! $_[1] ){
		$MYSQLDUMP.= " ".$SINGLETRANSACTION;
	}
	if( defined $inputs{"extra-mysqldump-options"} ){
		$MYSQLDUMP.= " ".$inputs{"extra-mysqldump-options"};
	}
	my $p = &addMySQLParams($MYSQLDUMP);

	my $cmpCmd = "";
	my $bkpFile = $BACKUPSQLFILE;

        if( defined $inputs{"compress-mysqldump-onthefly"} && $inputs{"compress-mysqldump-onthefly"} == 1 ){
                $cmpCmd .= " | ";
                if( $inputs{"compress"} ne "" ){
                        $cmpCmd .= "\"$inputs{compress}\"";
                }else{
                        $cmpCmd .= $GZIP;
                }
		$bkpFile = $COMPRESS_LOGICAL_FILENAME;
		if( $verbose ){
			&printLog( "Compressing backup\n" );
		}
        }
	my $filename = catfile( $inputs{"destination"},  $bkpFile );
	my $command = " ".$_[0].$cmpCmd." > \"".$filename."\"";
	if( $verbose ) {
		&printLog( "Command used for logical backup is ".$p.$command."\n" );
	}
        if( $abort_flag ){
                &abortAndDie( );
        }
	my $ti = time();
	my $r = system($p.$command);
	my $t1 = (time() - $ti);
	$readLocksTime = $readLocksTime + $t1;
	$logicalCet = $t1;
        if( $abort_flag ){
                &abortAndDie( );
        }
	if( $r > 0 ) {
		&printAndDie("mysqldump did not succeed.\n Command used is ".$p.$command);
	}
}

sub checkAndBackupReplicationData()
{
        if( !$inputs{replication} || $inputs{replication} eq "0" ) {
                return 0;
        }

        my $replicationFunction;
	my $module = "ZRM::Replication";
        my $return = eval "use base qw($module)";
        if( ! $@ ){
        	$replicationFunction = \&ZRM::Replication::backupReplicationData;
        }else{
        	&printWarning( "Replication option is not installed\n" );
        }
        if( defined &$replicationFunction ){
		&printStartPhase( "Copying replication data\n" );
                my @ind = &$replicationFunction();
		if( @ind ){
			foreach( @ind ){
				&printIndex( $_ );
			}
		}

		&printEndPhase( "Copying replication data\n" );
        }else{
                &printWarning( "Replication option is not installed\n" );
	}
}


#Returns the list of bin logs in the server
sub getListOfBinLogs()
{
	my $x = &addMySQLParams($MYSQL);
	$x = "$x -e \"show master logs\"";
	if( $verbose ){
		&printLog( "Getting list of binary logs using command ".$x."\n" );
	}
        $x = &execCmdAndGetOutput($x);
	if( $x ) {
        	my @t = split( "\n", $x );
		shift @t;
		my @retArr = ();
		foreach( @t ) {
			my @name = split( "\t", $_ );
			push @retArr, $name[0];
		}
		return @retArr;
	} else {
		&printAndDie( "Could not get list of binary logs\n" );
	}

}

#will return $_[0] if the file pointed to in the index file is not present
#$_[0] first bin log file in the server
sub getFirstBinLogFileToBackup()
{
	if( $lastBackupDir ){
		my $filename = catfile( $lastBackupDir, $INDEX_FILENAME );
		my $r = &parseIndexFile( $filename );
		if( $r == 0 ){
			&printAndDie("cannot open index file $filename $!\n");
		}
		return $indexdata{"next-binlog"};
	}else{
		&printWarning( "No last backup dir\n" );
	}
	return $_[0];
}

#This executes the incremental backup
sub doIncBackup()
{
	&printStartPhase( "Creating incremental backup\n" );
	my @list = getListOfBinLogs();
	my $firstFile = &getFirstBinLogFileToBackup( $list[0] );
	my $ind = 0;
	foreach( @list ){
		if( $firstFile && $firstFile eq $_ ){
			while( $ind > 0 ){
				shift @list;
				$ind--;
			}
			last;
		}
		$ind++;
	}
	foreach( @list ){
		if( $_ ne $nextBinLog ){
			my $src;
			if( $inputs{"mysql-binlog-path"} ){
				$src = $inputs{"mysql-binlog-path"};
			}else{
				$src = $datadir;
			}
			$src = catfile( $src, $_ );
			$ENV{'BIN_LOG_NAME'} = $src;
			print "src = $src\n";
			my $d = $inputs{"destination"};
			my $k;
			if ( $onWindows ) {
				$d = catfile( $d, "ZRM_BINLOGS" );
				mkpath $d, 0755;
				$k = catfile( $d, "$_.zip" );
			} else {
				$k = catfile( $inputs{"destination"}, "$_" );

			}
			print "k = $k\n";
			my $r = &copyOneFileRemote( $src, $d   );
			if( $r == 0 || ! -f $k ){
				&printError( "Could not copy bin log file $k\n" );
			}
		}else{
			last;
		}
	}
	my ($filename, $ext) = split( /\./, $nextBinLog );
	&printIndex( "incremental=".$filename.".[0-9]*\n" );
	&printEndPhase( "Creating incremental backup\n" );
}


#Returns total size of backed up data;
#Also calculates the md5 checksum of each;
sub totalSize()
{
        my $total_size = 0;
	if( $inputs{"backup-type"} eq "quick" ){
		return $total_size;
	}
	my $dir = $_[0];
        find( {wanted => sub
                {
			if( $_ ne "index" ){
                        	$total_size += -s $_;
				if( defined $inputs{"synchronous-checksum"} &&
				    $inputs{"synchronous-checksum"} == 1 ){
					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 );
                                			$mdcheck{$file}=$a[0];
						}
					}
				}
			}
                },
                follow => 1}, $_[0]);
        return $total_size;
}

#Returns the current bin log filename
sub getBinLogFilename()
{
	my $x = &addMySQLParams($MYSQL);
	$x = "$x -e \"show master status\"";
	if( $verbose ){
		&printLog( "Getting master logname using command ".$x."\n" );
	}

        $x = &execCmdAndGetOutput($x);
	if( $x ) {
        	my @t = split( "\n", $x );
		@t = split( "\t", $t[1] );
		return $t[0];
	}
}

#Flush the big log files
#This is run once every backup so that the logs are flushed only once
sub flushLogs()
{
	my $x = &addMySQLParams($MYSQLADMIN);
	my $y = " flush-logs";
	if( $verbose ) {
		&printLog( "Flushing the logs \n" );
		&printLog( $x.$y."\n" );
	}
	my $t = time();
	$x = system( $x.$y );
	$flushLogsTime = $flushLogsTime + (time() - $t);
	if( $x > 0 ) {
		&printAndDie( "Flush of logs failed\n" );
	}
	$nextBinLog = &getBinLogFilename();
}

#Puts the directory in which the previous backup happened
#in the last_backup file in the backset directory
sub registerPrevBackupDirectory()
{
	# If there is an error in backup, leave the last_backup as is
	# so that the next time the backup will point to the correct binary log.
	if( $zrm_error != 0 ){
		return;
	}
	my $file = catfile( $backupset_dir, "last_backup" );
	unless( open( TMPH, ">$file" ) ){
		&printError( "Register backup in ".$backupset."failed\n" );
		return;
	}
	print TMPH $inputs{"destination"}."\n";
	close( TMPH );
}


# $_[0] message to be logged
sub printStartPhase()
{
	&printLog( "PHASE START: $_[0]" );
}

# $_[0] message to be logged
sub printEndPhase()
{
	&printLog( "PHASE END: $_[0]" );
}

#This executes the backup action
sub doBackup()
{
	#Setup destination directory
	if( !$inputs{"destination"} ) {
		my $x = $VARLIB;
		$inputs{"destination"}=catfile( $x, "mysql-zrm" );
		mkpath( $inputs{"destination"}, 0, $def_perm );
	} else {
		if( !-d $inputs{"destination"} ) {
			&printAndDie( "Cannot find destination ".$inputs{"destination"}."\n" );
		}
	}
	if( $verbose ){
		&printLog( "backup set being used is ".$backupset."\n" );
	}
	$inputs{"destination"}=catfile($inputs{"destination"}, $backupset );
	if( $inputs{"backup-name"} ){
		if( !&validateData( $inputs{"backup-name"} ) ){
			&printAndDie( "backup-name not valid ".$inputs{"backup-name"}."\n" );
		}
		$inputs{"destination"}=catfile($inputs{"destination"},
							$inputs{"backup-name"});
	}else{
		$inputs{"destination"}=catfile( $inputs{"destination"}, $date );
		my $x = $inputs{"destination"};
		# Just to ensure that even if 2 backups are kicked off
		# at the same time we are ok.
		if( -e $inputs{"destination"} ){
			$inputs{"destination"} = &getUniqueFileName( $x, "0" );
			&printWarning( "Directory with name ".$x." exists. Creating a unique name for this backup called ".$inputs{"destination"}.".\n" );
		}
	}
	if( -e $inputs{"destination"} ){
		&printError( "Directory ".$inputs{"destination"}." exists\n" );
		&printError( "Directory with backup-name ".$inputs{"backup-name"}." already exists inside backup-set ".$backupset."\n");
 		&printAndDie( "Please provide a unique backup-name.\n" );
	}
	mkpath( $inputs{"destination"}, 0, $def_perm );

	if ( $onWindows && defined $inputs{"encrypt"} )  {
		my $d = $inputs{"destination"};
		my $n =tmpnam();
		my $n1 = tmpnam();
		my $cmd = "cipher /a  /e /i /f $d > $n 2>$n1";
		my $r = system( $cmd );
		if ( $r != 0 ) {
			&printWarning( "Cipher error\n" );
		}
		unlink( $n );
		unlink( $n1 );
	}

	#my $f = catfile( $inputs{"destination"}, $INDEX_FILENAME );
	#open (INDEX, ">$f") || &printAndDie("Cannot create index file. $!\n");
	#if (! flock(INDEX, 2)) {
       		#&printAndDie("Cannot lock index file for writing. $!\n");
	#}
	#$index_open = 1;
	my $f = &createIndexFile( $inputs{"destination"} );
	&printIndex("backup-set=".$backupset."\n");
	&printIndex("backup-date=".$date."\n");
	&printIndex("mysql-server-os=".$mysql_server_os."\n");
	&printIndex( "backup-type=".$inputs{"backup-type"}."\n" );
	if( $inputs{"host"} ){
		&printIndex( "host=".$inputs{"host"}."\n" );
	}else{
		&printIndex( "host="."localhost\n" );
	}
	&printIndex("backup-date-epoch=".$start_time."\n");
	if( $inputs{"retention-policy"} ){
		&getRetentionTimeInSecs($inputs{"retention-policy"});
		&printIndex("retention-policy=".$inputs{"retention-policy"}."\n");
	}
	if( $zrm_version ){
		&printIndex( "mysql-zrm-version=$zrm_version\n" );
	}
	&printIndex("mysql-version=".$mysql_version."\n");
	&printIndex("backup-directory=".$inputs{"destination"}."\n");
	if( $inputs{"comment"} ) {
		&printIndex("comment=".$inputs{"comment"}."\n");
	}

	if( $inputs{"backup-level"} eq "0"  || $inputs{"backup-level"} eq "1") {
		&printIndex( "backup-level=".$inputs{"backup-level"}."\n" );
	}else{
		&printAndDie("Unknown backup level\n");
	}
	if( $inputs{"backup-mode"} eq "raw" ||
	    $inputs{"backup-mode"} eq "logical" ){
		if( $inputs{"backup-level"} eq "0" ){
			&printLog( "backup-mode=".$inputs{"backup-mode"}."\n" );
		}
	}else{
		&printAndDie("Unknown backup mode");
	}
	&printEndPhase( "Initialization\n" );
	&printStartPhase( "Running pre backup plugin\n" );
	my $pre = &doBackupPlugin( "pre-backup-plugin" );
	if( $pre > 0 ){
		&printAndDie( "pre-backup-plugin returned error $pre\n" );
	}
	&printEndPhase( "Running pre backup plugin\n" );
	my $isReplicating = 0;
	my $r = isSlave();
	if( $r ) {
		&printStartPhase( "Stopping slave\n" );
		$r = stopSlave();
		if( $r == 1 ){
			$isReplicating = 1;
		}
		&printEndPhase( "Stopping slave\n" );
	}
	&printStartPhase( "Flushing logs\n" );
	&flushLogs();
	&printEndPhase( "Flushing logs\n" );
	if( $isReplicating == 1 ){
		&checkAndBackupReplicationData();
	}

	if( $inputs{"backup-level"} eq "0" ) {
		if( $inputs{"backup-mode"} eq "logical" ) {
			&doLogicalFullBackup();
		} elsif( $inputs{"backup-mode"} eq "raw" ) {
			&doRawFullBackup();
		} else {
			&printAndDie("Unknown backup mode");
		}
		# Backup Configuration Files (only at level 0)
		if (defined $inputs{"config-file-list"})	{
			&printStartPhase( "Creating configuration file backup\n" );
			&doConfFileBackup();				    
			&printEndPhase( "Creating configuration file backup\n" );
		}
	} elsif( $inputs{"backup-level"} eq "1" ) {
		doIncBackup();
	}
	else {
		&printAndDie("Unknown backup level\n");
	}
	
	if( $isReplicating eq "1" ) {
		&printStartPhase( "Starting slave\n" );
		&startSlave();
		&printEndPhase( "Starting slave\n" );
	}
	&printStartPhase( "Calculating backup size & checksums \n" );
	my $totSize = &totalSize( $inputs{"destination"} );
	my $SUF=" MB";
	$totSize = $totSize/1048576;
	if( $totSize > 1000 ){
        	$totSize = $totSize/1024;
        	$SUF=" GB";
	}
	$totSize = sprintf( "%0.2f", $totSize );
	if( $nextBinLog ){
		&printIndex("next-binlog=".$nextBinLog."\n");
	}
	if( $lastBackupDir ){
		# So that we avoid pointing to the same backup
		if( $lastBackupDir ne $inputs{"destination"} ){
			&printIndex("last-backup=".$lastBackupDir."\n");
		}
	}
	if( %mdcheck ){
		for(keys%mdcheck){
        		&printIndex( "$_=$mdcheck{$_}\n" );
		}
	}
	&printIndex("backup-size=".$totSize.$SUF."\n");
	&printEndPhase( "Calculating backup size & checksums \n" );
	my $cet1;
	if( ! ($onWindows)  && $inputs{"backup-type"} eq "regular" && (defined $inputs{"compress"} || defined $inputs{"encrypt"} ) ){
		my $cet = time();
		&printStartPhase( "Compression/Encryption\n" );
		$r = &doCompressAndEncrypt();
		if( $r == 1 ){
			&removeUncompressedBackup( $inputs{"destination"}, \%inputs );
		}
		$cet1 = time() - $cet;
		$cet1 .= $logicalCet;
		&printEndPhase( "Compression/Encryption\n" );
	}elsif( $onWindows ){
        	if( defined $inputs{"compress"} ){
                	if( $inputs{"compress"} ){
                        	&$indexRoutine( "compress=$inputs{compress}\n" );
                	}else{
                        	&$indexRoutine( "compress=\n" );
                	}
        	}
        	if( $inputs{"encrypt"} ){
                	&$indexRoutine( "encrypt=$inputs{encrypt}\n" );
                	&$indexRoutine( "decrypt-option=".$inputs{"decrypt-option"}."\n");
        	}
	}
	my $time_taken = time() - $start_time;
	my $tt = &getFormatedTime( $readLocksTime );
	&printIndex("read-locks-time=".$tt."\n");
	$tt = &getFormatedTime( $flushLogsTime );
	&printIndex("flush-logs-time=".$tt."\n");
	if( defined $cet1 ){
		$cet1 = &getFormatedTime( $cet1 );
		&printIndex("compress-encrypt-time=$cet1\n");
	}
	$tt = &getFormatedTime( $time_taken );
	&printIndex("backup-time=".$tt."\n");
	&closeIndexFile();
	&registerPrevBackupDirectory();
	&printStartPhase( "Running post backup plugin\n" );
	my $post = &doBackupPlugin( "post-backup-plugin" );
	if( $post > 0 ){
		&printError( "post-backup-plugin returned error $post\n" );
	}
	&printEndPhase( "Running post backup plugin\n" );
	if( $inputs{"mailto"} ){
		if( $zrm_error == 0 && 
		    $inputs{"mail-policy"} eq "only-on-error" ){
			if( $verbose == 1 ){
				&printLog( "Not sending email since mail poilcy is only-on-error and backup is a success\n" );
			}
		}else{
			my $pmsg = "Mailing backup report\n";
			if( $onWindows ){
				$pmsg = "Updating EventLog\n";
			}
			&printStartPhase( $pmsg );
			$f = &prepareReportToMail( $f );
			if( $f ){
				&mailFile( $f, "[ZRM for MySQL Report] backup-set $backupset" );
				unlink( $f );
			}
			&printEndPhase( $pmsg );
		}
	}
	&runAsyncChecksumProcess();
}

sub runAsyncChecksumProcess()
{
	if( defined $inputs{"synchronous-checksum"} && $inputs{"synchronous-checksum"} == 1 ){
		return;
	}

	if( defined $inputs{"synchronous-checksum"} && $inputs{"synchronous-checksum"} == -1 ){
		my $f = catfile( $inputs{"destination"}, ".nochecksum" );
		open( TP, ">$f" );
		close( TP );
	}else{
		my $f = catfile( $inputs{"destination"}, ".checksum_pending" );
		open( TP, ">$f" );
		close( TP );
		system( "/usr/local/bin/mysql-zrm-verify-backup --create-checksum --source-directory $inputs{'destination'} --backup-set $backupset 2>/dev/null" );
	}
}

#$_[0] Index filename
#Returns a file containing the report
sub prepareReportToMail()
{
	&parseIndexFile( $_[0] );
	my $file = tmpnam();
	unless( open( TMPH, ">$file" ) ){
		&printError( "error opening temporary file to create backup report\n" );
		unlink( $file );
		return;
	}
	print TMPH "Backup set=".$indexdata{"backup-set"}."\n";
	my $epoch = $indexdata{"backup-date-epoch"};
	my $str = localtime( $epoch );
	print TMPH "Backup date=".$str."\n";
	print TMPH "Backup level=".$indexdata{"backup-level"}."\n";
	if( $indexdata{"raw-databases-snapshot"} ){
		print TMPH "Raw Databases (Snapshot)=".$indexdata{"raw-databases-snapshot"}."\n";
	}
	if( $indexdata{"raw-tables-snapshot"} ){
		print TMPH "Raw Tables (Snapshot)=".$indexdata{"raw-tables-snapshot"}."\n";
	}
	if( $indexdata{"raw-databases"} ){
		print TMPH "Raw Databases=".$indexdata{"raw-databases"}."\n";
	}
	if( $indexdata{"raw-tables"} ){
		print TMPH "Raw Tables=".$indexdata{"raw-tables"}."\n";
	}
	if( $indexdata{"logical-databases"} ){
		print TMPH "Logical Databases=".$indexdata{"logical-databases"}."\n";
	}
	if( $indexdata{"logical-tables"} ){
		print TMPH "Logical Tables=".$indexdata{"logical-tables"}."\n";
	}
	print TMPH "Backup size=".$indexdata{"backup-size"}."\n";
	print TMPH "Backup time=".$indexdata{"backup-time"}."\n";
	print TMPH "Backup status=".$indexdata{"backup-status"}."\n";

	close TMPH;
	return $file;
}

# exec the preBackup script
#$_[0] is either pre-backup-plugin or post-backup-plugin
sub doBackupPlugin()
{
        if( $verbose ){
                &printLog( "Executing $_[0]\n" );
        }
        my @params;
        if( $inputs{"tables"} ){
                push @params, "--tables";
                push @params, $inputs{"tables"};
                push @params, "--database";
                push @params, $inputs{"database"};
        }elsif( $inputs{"databases"} ){
                push @params, "--databases";
                push @params, $inputs{"databases"};
        }else{
                push @params, "--all-databases";
        }
	push @params, "--backup-directory";
	push @params, $inputs{"destination"};
	if( $_[0] eq "post-backup-plugin" && !$onWindows ){
		if( defined $inputs{"synchronous-checksum"} && $inputs{"synchronous-checksum"} == 1 ){
			push @params, "--checksum-finished";
		}else{
			push @params, "--checksum-pending";
		}
	}
        my $r = &execPlugin( $_[0], @params );
        return $r;
}

#format as HH:MM:SS given time in seconds
#if time is more than 86400 it will be formated as dd:HH:MM:SS
#$_[0] time in seconds
sub getFormatedTime()
{
        my $fmt = "%H:%M:%S";
        if( $_[0] >= 86400 ){
                $fmt = "%d:%H:%M:%S";
        }
        return strftime($fmt, $_[0], 0, 0, 0, 0, 0 );
}

sub setupPidFile()
{
        if( -f "$backupset_dir/.mysql-zrm.pid" ){
            $pid_file = 0;
            &printError( "mysql-zrm appears to be already running for this backupset\n" );
            &printAndDie( "If you are sure mysql-zrm is not running, please remove the file $backupset_dir/.mysql-zrm.pid and restart mysql-zrm\n" );
        }

        unless( open TMP, ">$backupset_dir/.mysql-zrm.pid" ){
                &printAndDie( "Unable to write pid file $backupset_dir/.mysql-zrm.pid \n" );
        }
        my $r = flock(TMP, LOCK_EX|LOCK_NB);
        if( $r == 0 ){
            $pid_file = 0;
            &printError( "mysql-zrm appears to be already running for this backupset\n" );
            &printAndDie( "If you are sure mysql-zrm is not running, please remove the file $backupset_dir/.mysql-zrm.pid and restart mysql-zrm\n" );
        }
	if( defined $ENV{"ZRM_LAUNCHER"} && $ENV{"ZRM_LAUNCHER"} ne "" ){
		print TMP $ENV{"ZRM_LAUNCHER"}."\n";
	}else{
        	print TMP "$$\n";
	}
        flock(TMP, LOCK_UN);
        close TMP;
        $pid_file = 1;
}

#$_[0] database list or database name
#$_[1] if present gives list of tables
sub doVSSCopy()
{
	my $command = "";
	my @dbs;
	if( defined $_[1] ){
		#tables
		$command .= " --database $_[0] $_[1]";
		push @dbs, $_[0];
	}else{
		#dbs
		$command .= " $_[0]";
		@dbs = split /\s/, $_[0];
	}

	$command = " --datadir '$datadir' $command";

	my $backupInnoDBFiles = 0;
	my $cur_db;
	if( $have_innodb eq "YES" ){
		foreach $cur_db ( @dbs ){
			if( $abort_flag ){
                        	&abortAndDie();
                	}
			$backupInnoDBFiles = &checkIfInnoDBPresent( $cur_db );
			if( $backupInnoDBFiles == 1 ){
				last;
			}
		}
	}
	my $innoDBData = "";
	if( $backupInnoDBFiles == 1 ){
		my $dir;
		foreach $dir ( @innodb_shared_data_files ){
			if( $dir ne "" ){
				$innoDBData .= $dir.";";
			}
		}
		chop( $innoDBData );
		$command = " --innodb-logs='$innodb_log_dir' --innodb-data='$innoDBData' $command";
	}

        $command = &backSlashesToForwardSlashes ($command) ;
        my $dest= $inputs{"destination"};
        $dest = "\"$dest\"";
        $command .= " ".$dest;
	my $vsscopy_cmd = $inputs{"copy-plugin"};
	my $p = &addMySQLParams($vsscopy_cmd);
	$command = $p." ".$command;

        if( $onWindows ) {
	        $command = &singleQuoteWithDoubleQuote($command);
        }

        if( $abort_flag ){
                &abortAndDie( );
        }
	if( $verbose ) {
                &printLog( "Command used for raw snapshot backup is ".$command."\n" );
        }
	my $out = &execCmdAndGetOutput( "$PERL_BINARY $command" );
        if( $abort_flag ){
                &abortAndDie( );
        }
	if( defined $out ){
		$readLocksTime = $readLocksTime + $out;
	}else{
		&printAndDie( "Backup from remote mysql server did not succeed. command used is $command.\n" );
	}
	if( $backupInnoDBFiles ){
		&printIndex( "innodb-data=$innoDBData\n" );
                my $logName = catfile( "$innodb_log_dir", "ib_logfile" );
		&printIndex( "innodb-logs=$logName\n" );
	}
	if( defined $_[1] ){
		&printIndex( "raw-tables-snapshot=$_[1]\n" );
		&printIndex( "raw-databases-snapshot=$_[0]\n" );
	}else{
		&printIndex( "raw-databases-snapshot=$_[0]\n" );
	}
}

# Backup Configuration Files
sub doConfFileBackup()	{
	
	if (defined $inputs{"config-file-list"})	{
		my $filename = $inputs{"config-file-list"};

		# Copy the file list itself to the backup directory
		# copyOneFileLocal works on Windows as well
		if (!&copyOneFileLocal($filename, $inputs{"destination"}))	{
			&printError("$filename could not be copied to $inputs{\"destination\"}");
		}
		
		open(CFH, "< $filename") or 
		(&printError("Could not open $inputs{\"config-file-list\"}:$!\n"),
		&printError("Configuration Files Could not be backed up\n"),
		return);

		while (<CFH>)	{

			if (! defined $_)	{
				next;
			}
			chomp($_);
			s/^\s+//g;
			s/\s+$//g;
			if( $_ eq "" ){
				next;
			}

			my $src = "\"" . $_ . "\"";
			my $basedir = dirname($_);
			if ($onWindows)	{
				# If the basedir is C:, 
				# a directory by that name cannot be created.
				# So remove the colon.
				$basedir =~ s/\://g;
			}
			my $destination = catfile($inputs{"destination"}, $basedir);
			mkpath($destination);
			if (!&copyOneFileRemote($src, $destination))	{			
				&printError("Could not backup $_\n");
			}
		}

		close(CFH) or 
		&printWarning("Could not close $inputs{\"config-file-list\"}:$!");

		# Update the index
		&printIndex( "config-file-list=".$inputs{"config-file-list"}."\n");
	}
}

#Sets up defaults for backup
sub setupDefaults()
{
	$inputs{"backup-level"}="0";
        $inputs{"backup-mode"}="raw";
	$exitRoutine = \&backupExit;
	$indexRoutine = \&printIndex;
	$action = "backup";
	$USAGE_STRING = $USAGE_BACKUP_STRING.$USAGE_COMMON_OPTIONS_STRING;
}

sub main()
{
	&setupDefaults();
	&initMySQL(@BACKUPOPT);
	&verifySnapshotParams();
	&setupPidFile();
        &doBackup();
	&my_exit( );
}
&main();
