#!/usr/bin/perl
#
# @@@ START COPYRIGHT @@@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#
# @@@ END COPYRIGHT @@@
#

use strict;

my %volume_info = ();   # Store all the results
my $volcheck;           # Volume to check
my $cluster;            # Are we on a cluster?
my $pdshres;            # Keep track of $SQ_PDSH output

my $g_dotse = 1;        # Do we think mirroring is on for TSE
my $g_doase = 1;        # Do we think mirroring is on for ASE


#
# Display the contents of the volume_info hash
#
sub print_results()
{
    print "\nRESULTS:\n\n" if (!$cluster);

    for my $k1 (sort(keys(%volume_info)))
    {
        print "$k1\n";
        print "-------\n";
        print "PRIM files: $volume_info{$k1}{'prim'}{numfiles}\n";
        print "PRIM diff errs: $volume_info{$k1}{'prim'}{err}\n";
        print "PRIM problem files: $volume_info{$k1}{'prim'}{problemfiles}\n";
        print "MIR files: $volume_info{$k1}{'mir'}{numfiles}\n";
        print "MIR diff errs: $volume_info{$k1}{'mir'}{err}\n";
        print "MIR problem files: $volume_info{$k1}{'mir'}{problemfiles}\n\n";
    }
}


#
# Process the contents of either the sq_tse.create or sq_ase.create
#
sub process_vol($)
{
    my $tsevols = shift(@_);
    my @problemfiles = ();
    my $i = 0;
    my $errs = 0;
    my $numfiles = 0;
    my @tseitems;
    my @tsedirp;
    my @tsedirm;
    my $tsedirps;
    my $tsedirms;
    my $tseprimfiles;
    my $tsemirfiles;
    my $cmd;
    my $vol;
    my @volfields;
    my $tsefile;
    my $blink = 0; # Just for fun

    my @items = split(/\n/, $tsevols);

    foreach (@items)
    {
        if (($i % 2) != 0) { $i++; next; }

        # Logic of this block may have to change if, in the future,
        # the order of the volumes in the sq_*se.create scripts changes
        @tsedirp = split(/\s+/, $items[$i]);
        $tsedirps = $tsedirp[-1];
        $tsedirps = $tsedirps . "/";
        @tsedirm = split(/\s+/, $items[$i+1]);
        $tsedirms = $tsedirm[-1];
        @volfields = split(/\//, $tsedirms);
        $tsedirms = $tsedirms . "/";

        $tseprimfiles = `ls $tsedirps`;
        $tsemirfiles = `ls $tsedirms`;
        chomp($tseprimfiles);
        chomp($tsemirfiles);

        $vol = $volfields[-1];
        substr($vol, 0, 1) = "";
        print "Diffing $vol files: " if (!$cluster);

        # Diff PRIM to MIR
        for $tsefile (split(/\n/, $tseprimfiles))
        {
            next if ($tsefile =~ /lost\+found/);

            print $tsefile if (!$cluster);
            $cmd = "diff $tsedirps" . "$tsefile " . "$tsedirms" . "$tsefile 2> /dev/null";
            `$cmd`;

            if (!$cluster)
            {
                while ($blink < length($tsefile))
                {
                    print "\b";
                    $blink++;
                }
            }

            if ($? != 0)
            {
                push(@problemfiles, $tsefile);
                $errs++;
            }

            $numfiles++;
            $blink = 0;
        }

        $volume_info{$vol}{'prim'}{err} = $errs;
        $volume_info{$vol}{'prim'}{numfiles} = $numfiles;
        $volume_info{$vol}{'prim'}{problemfiles} = join("\n\t\t   ", @problemfiles);

        $errs = 0;
        $numfiles = 0;
        @problemfiles = ();

        # Diff MIR files to PRIM files, to catch any extra files in the MIR directory
        for $tsefile (split(/\n/, $tsemirfiles))
        {
            next if ($tsefile =~ /lost\+found/);

            print $tsefile if (!$cluster);
            $cmd = "diff $tsedirps" . "$tsefile " . "$tsedirms" . "$tsefile 2> /dev/null";
            `$cmd`;

            if (!$cluster)
            {
                while ($blink < length($tsefile))
                {
                    print "\b";
                    $blink++;
                }
            }

            if ($? != 0) 
            {
                push(@problemfiles, $tsefile);
                $errs++; 
            }

            $numfiles++;
            $blink = 0;
        }

        $volume_info{$vol}{'mir'}{err} = $errs;
        $volume_info{$vol}{'mir'}{numfiles} = $numfiles;
        $volume_info{$vol}{'mir'}{problemfiles} = join("\n\t\t   ", @problemfiles);

        print "\n";
        $errs = 0;
        $numfiles = 0;
        $i++;
        @problemfiles = ();
    }
}


#
# On a cluster, use $SQ_PDSH to call the correct storage nodes for
# each SE volume.
#
sub do_pdsh($)
{
    my $sevols = shift(@_);
    my ($vol, @pdshcmd, @fields, $cmd);

    foreach $vol (split(/\n/, $sevols))
    {
        next if ($vol =~ /databasem/);

        @pdshcmd = split(/\s+/, $vol);
        @fields = split(/\//, $vol);
        print "Diffing " . substr($fields[-1], 1) . " (on $pdshcmd[2])...";
        $cmd = $ENV{'SQ_PDSH'} . " -w $pdshcmd[2] \'dbcheck $fields[-1] --cluster\'";
        $pdshres .= `$cmd`;
        chop($pdshres);
        print "done\n";
    }
}


#
# Show usage information.
#
sub show_help()
{
    print <<END

Usage: dbcheck [--help] <volname>

 Diff files between the primary and mirror volumes, to verify consistency.
 Note that on a cluster, dbcheck will utilize pdsh to call the correct
 storage nodes for the volumes.

 Options:
   --help         Display this usage information.
   <volname>     Optional, can be used to specify a specific SE volume
                  to check.  If <volname> isn't specified, all volumes
                  are checked.  The leading '$' is optional, and it is
                  case-insensitive.

END
}


#
# This utility is only useful if user has mirroring turned on
#
sub mirroring_not_on()
{
    my $cmd;

    $cmd = "grep DEVICE-M $ENV{'TRAF_HOME'}/sql/scripts/gomon.cold | egrep \"DATA|SYSTEM\"";
    `$cmd`;
    $g_dotse = 0 if ($? != 0);

    $cmd = "grep DEVICE-M $ENV{'TRAF_HOME'}/sql/scripts/gomon.cold | egrep \"AUDIT|TLOG\"";
    `$cmd`;
    $g_doase = 0 if ($? != 0);

    if ($g_dotse == 1 || $g_doase == 1) { return 0; }
    else { return 1; }
}


#
# Call process_vol with the list of necessary TSE/ASE volumes to diff
#
sub main()
{
    my ($asevols, $tsevols, $cmd);

    print STDERR "You should not be trying to use the script, it is broken\n";
    exit;

    if (mirroring_not_on())
    {
        print "Error: databasem directory not detected in TSE/ASE creation scripts.\nIs mirroring enabled?\n";
        exit 1;
    }

    if ($#ARGV >= 0)
    {
        if ($ARGV[0] =~ /help/ || $ARGV[0] eq "-h")
        {
            show_help();
            exit (0);
        }

        $volcheck = $ARGV[0];
        $volcheck = uc($volcheck);
        $cluster = 1 if ($ARGV[1] eq "--cluster");
    }

    # Remove leading dollar sign
    if ( $volcheck =~ /\$/) 
    {
        substr($volcheck, 0, 1) = "";
    }

    if ($volcheck)
    {
        $cmd = "cat $ENV{'TRAF_HOME'}/sql/scripts/sq_tse.create | grep mkdir | egrep \'$volcheck\\b\'";
        $tsevols = `$cmd`;
        chomp($tsevols);
        $cmd = "cat $ENV{'TRAF_HOME'}/sql/scripts/sq_ase.create | grep mkdir | egrep \'$volcheck\\b\'";
        $asevols = `$cmd`;
        chomp($asevols);
    }
    else
    {
        $tsevols = `cat $ENV{'TRAF_HOME'}/sql/scripts/sq_tse.create | grep mkdir`;
        $asevols = `cat $ENV{'TRAF_HOME'}/sql/scripts/sq_ase.create | grep mkdir`;
    }

    # Handle cluster case - we will use $SQ_PDSH to run dbcheck on each node,
    # checking the volumes that are stored on that node
    if ($tsevols =~ /pdsh/ || $asevols =~ /pdsh/)
    {
        $tsevols =~ s/\'//g;
        $asevols =~ s/\'//g;

        if (!$cluster)
        {
            print "\nPlease be patient, this may take awhile...\n\n";

            do_pdsh($tsevols) if ($tsevols && $g_dotse);
            do_pdsh($asevols) if ($asevols && $g_doase);

            $pdshres =~ s/n[0-9]+: //g;
            print "\nRESULTS:\n";
            print "$pdshres\n";

            exit (0);
        }
    }

    # Volume specified on command line not found in sq_*se.create
    if ($volcheck && ($tsevols eq "" && $asevols eq ""))
    {
        print "You specified volume \$$volcheck, but this volume wasn't found in the SE creation scripts...\n";
        exit (1);
    }

    print "\nPlease be patient, this may take awhile...\n\n" if (!$cluster);

    process_vol($tsevols) if ($tsevols);
    process_vol($asevols) if ($asevols);

    print_results();
}


main();
