#!/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::Spec::Functions;
use POSIX qw(strftime);
use File::Temp qw/ :POSIX /;
use lib '/usr/local/lib/mysql-zrm';
use ZRM::Common;

my $MYSQL_BIN_PATH;
my $MYSQLBINLOG="mysqlbinlog";

my $parsedString="";

#usage strings 
my $USAGE_PARSE_BINLOGS_STRING=
		"\t\t[--source-directory <directory name>]\n".  
		"\t\t[--bin-logs <\"/fullpath/name1\ /fullpath/name2\ ...\">]\n".
		"\t\t[--mysql-binpath <mysql binaries directory>]\n". 
		"\t\t[--parse-binlogs-plugin <plugin>]\n".
		"\t\t[--parse-binlogs-plugin-options <\"option1 option2...\">]\n";

my @PARSEOPT=qw/
		source-directory=s
		bin-logs=s
		mysql-binpath=s
		parse-binlogs-plugin=s
		parse-binlogs-plugin-options=s
	       /;

#displays $pstring
sub displayParsedString()
{
	if( $parsedString ){
		my $str = $parsedString;
		my $r = &execPlugin( "parse-binlogs-plugin", $str );
		if( $r == 0 ){
			print "$parsedString\n";
		}else{
			if( $verbose ){
				&printLog( "Will not display line $parsedString\n" );
			}
		}
		$parsedString = "";
	}
}

# Takes a string of the form that mysqlbin spits out and
# returns a properly formated timestamp
# $_[0] the string to format.
sub getTimeString()
{
        my $x = $_[0];
        my @a = split( / /, $x );
        my $l = @a;
        my $date = $a[0];
        my $time = $a[$l-1];
        my $y = substr( $date, 0, 2 );
        my $m = substr( $date, 2, 2 );
        $m -= 1;
        my $d = substr( $date, 4, 2 );
        my @t = split( /:/, $time );
        return strftime( "%y-%m-%d %H:%M:%S", $t[2], $t[1], $t[0], $d, $m, $y );
}


# $_[0] name of bin log file
# $_[1] one line in the bin log file
# the $pstring = ""; statements are there to ensure that people reading 
# the code understand what statements are being ignored
sub processOneLine()
{
	my $file = $_[0];
	my $line = $_[1];
	chomp( $line );
	my $orgLine = $line;
	my $pstring="";
	if( $line=~/^# at/ ){
		my $x = $';
		$x=&removeBlanks($x);
		&displayParsedString();
		$pstring = "$file | $x | ";	
	}elsif( $line=~/^#\d/ ){
		$line = substr( $line, 1, -1 );
		my @sec = split( /server/, $line );
		$sec[0] = &removeBlanks( $sec[0] );
		my $timestamp = &getTimeString( $sec[0] );
		my @sec1 = split( /\t/, $orgLine );
		my $la = @sec1;
		$pstring = "$timestamp | ";
		if( $la gt 1 ){
			$pstring .= "$sec1[1] | ";
		}else{
			$pstring .= " | ";
		}
	}elsif( $line=~/^#/ ){
		#ignores all other comments;
		$pstring = "";
	}elsif( $line=~/^\/\*\!\d*\s*SET / ){
		#ignores all lines starting with /*!<version> SET 
		#other statements starting with /*! is printed
		#$pstring = "";
	}elsif( $line=~/^SET / ){
		#ignores all of the SET directives
		$pstring = "";
	}elsif( $line=~/ROLLBACK \/\* added by mysqlbinlog \*\/\;/ ){
		#ignores all the ROLLBACK directive lines
		$pstring = "";
	}else{
		$pstring = $pstring."$line ";
	}
	if( $pstring ){
		$parsedString .= $pstring;
	}
}

#Adds the mysql related parameters from the command line to the mysql command
#$_[0] specifies the mysql command
sub addBinPath()
{
	my $comm = $_[0];
        if( $MYSQL_BIN_PATH ){
        	$comm = catfile( "\"".$MYSQL_BIN_PATH."\"", $comm );
        }
        return $comm;
}

#$_[0] bin log file to parse
sub parseOneBinLog()
{
        my $binfile = $_[0];
        if( ! -f $binfile ){
                &printError( "Cannot find bin log file $_[0] \n" );
                return;
        }
        my $x = &addBinPath($MYSQLBINLOG);
        $x = $x." ".$binfile;
        if( $verbose ){
                &printLog( "Parsing binlog $binfile using command $x\n" );
        }

        unless( open( TMPFILE, "$x|" ) ){
                &printError( "mysqlbinlog failed for file $binfile\n" );
                return;
        }

        while( <TMPFILE> ){
                &processOneLine( $binfile, $_ );
        }
        close TMPFILE;
}

#$_[0] list of bin log files to be parsed
sub parseBinLogFiles()
{
	my @a = glob $_[0];
	print "------------------------------------------------------------\n";
	print "Log filename | Log Position | Timestamp | Event Type | Event\n";
	print "------------------------------------------------------------\n";
	foreach( @a ){
		&parseOneBinLog( $_ );
	}	
	&displayParsedString();
	print "------------------------------------------------------------\n";
}

sub doParseBinLogs()
{
	my $r;
	if( $inputs{"source-directory"} ) {
		if( $inputs{"bin-logs"} ){
			&usage( "Both --source-directory and --bin-logs cannot be specified at the same time. Please supply only one of these.\n" );
		}
		if( !-d $inputs{"source-directory"} ) {
			&printAndDie( "Cannot find source directory ".$inputs{"source-directory"}."\n" );
		}
		my $filename = catfile( $inputs{"source-directory"}, $INDEX_FILENAME );
		$r = &parseIndexFile( $filename );
		if( $r == 0 ){
			&printAndDie("cannot open index file $filename $!\n");
		}
		if( defined $indexdata{"compress"} || defined $indexdata{"encrypt"} ){
			$r = 1;
			$r = &uncompressBackup( $inputs{"source-directory"} );
			if( $r == 0 ){
				&printAndDie( "Unable to uncompress backup\n" );
			}
		}
	}else{
		if( !$inputs{"bin-logs"} ){
			&usage( "For parse-binlogs action specify either --source-directory or --bin-logs\n" );
		}
	}
	if( $inputs{"source-directory"} && $indexdata{"backup-level"} == 0 ){
		&printAndDie( $inputs{"source-directory"}." contains a full backup and there are no binary logs in this directory.\n" );
	}

        if( $inputs{"mysql-binpath"} ){
                if( ! -d $inputs{"mysql-binpath"} ){
                        &printAndDie( "mysql-binpath not found ".$inputs{"mysql-binpath"}."\n" );
                }
                $MYSQL_BIN_PATH=$inputs{"mysql-binpath"};
        }

	my $res="";
	if( $inputs{"bin-logs"} ){
		$res = $inputs{"bin-logs"};
	}else{
		my $t = $inputs{"source-directory"};
		if( $t=~/\s/ ){
                	$t = "\"$t\"";
        	}
		$res = catfile( $t, $indexdata{"incremental"} );
	}
	&parseBinLogFiles( $res );
	if( defined $indexdata{"compress"} ){
		&removeUncompressedBackup( $inputs{"source-directory"}, \%indexdata );
	}
}

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

sub main()
{
        &setupDefaults();
        &initCommon(@PARSEOPT);
	&createConfigFile();
        &doParseBinLogs();
        &my_exit();
}

&main();

