#!/usr/bin/env perl
#
# BEGIN COPYRIGHT BLOCK
# Copyright (C) 2013 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
# See LICENSE for details. 
# END COPYRIGHT BLOCK
#

use lib qw(/usr/lib/dirsrv/perl);
use DSUtil;

DSUtil::libpath_add("/usr/lib");
DSUtil::libpath_add("/usr/lib");
$ENV{'PATH'} = "/usr/lib/dirsrv/slapd-$servid:/usr/bin:/usr/bin:/";
$ENV{'SHLIB_PATH'} = "$ENV{'LD_LIBRARY_PATH'}";
my $custom_dbdir = 0;
my $i = 0;

sub usage
{
  print "Usage: verify-db.pl [-Z serverID] [ -a <fullpath_to_db_dir> ] [-h]\n";
}

# getDbDir checks up to 4 levels of db dirs
# e.g., <server_inst_dir>/db/<backend_instance_dir>/<subdir>
sub getDbDir
{
  (my $here) = @_;
  my @dbdirs = ();

  opendir(DIR0, $here) or die "can't opendir $here : $!";
  while (defined(my $file0 = readdir(DIR0)))
  {
    if ( "$file0" eq "\." || "$file0" eq "\.\." ) 
    {
      ;
    }
    elsif ( "$file0" eq "DBVERSION" )
    {
      $#dbdirs++;
      $dbdirs[$#dbdirs] = $here;
    }
    elsif ( -d $here . "/" . $file0 )
    {
      opendir(DIR1, $here . "/" . $file0) or die "can't opendir $file0 : $!";
      while (defined(my $file1 = readdir(DIR1)))
      {
        if ( "$file1" eq "\." || "$file1" eq "\.\." ) 
        {
          ;
        }
        elsif ( "$file1" eq "DBVERSION" )
        {
          $#dbdirs++;
          $dbdirs[$#dbdirs] = $here . "/" . $file0;
        }
        elsif ( -d $here . "/" . $file0 . "/" . $file1 )
        {
          opendir(DIR2, $here . "/" . $file0 . "/" . $file1) or die "can't opendir $file1 : $!";
          while (defined(my $file2 = readdir(DIR2)))
          {
            if ( "$file2" eq "\." || "$file2" eq "\.\." ) 
            {
              ;
            }
            elsif ("$file2" eq "DBVERSION")
            {
              $#dbdirs++;
              $dbdirs[$#dbdirs] = $here . "/" . $file0 . "/" . $file1;
            }
            elsif ( -d $here . "/" . $file0 . "/" . $file1 . "/" . $file2 )
            {
              opendir(DIR3, $here . "/" . $file0 . "/" . $file1 . "/" . $file2) or die "can't opendir $file1 : $!";
              while (defined(my $file3 = readdir(DIR3)))
              {
                if ( "$file3" eq "\." || "$file3" eq "\.\." ) 
                {
                  ;
                }
                elsif ("$file3" eq "DBVERSION")
                {
                  $#dbdirs++;
                  $dbdirs[$#dbdirs] = $here . "/" . $file0 . "/" . $file1 . "/" . $file2;
                }
              }
              closedir(DIR3);
            }
          }
          closedir(DIR2);
        }
      }
      closedir(DIR1);
    }
  }
  closedir(DIR0);

  return \@dbdirs;
}

sub getLastLogfile
{
  (my $here) = @_;
  my $logfile = "";

  opendir(DIR, $here) or die "can't opendir $here : $!";
  while (defined($file = readdir(DIR)))
  {
    if ($file =~ /log./)
    {
      $logfile = $file;
    }
  }
  closedir(DIR);

  return \$logfile;
}

$NULL = "/dev/null";

while ($i <= $#ARGV) {
  if ( "$ARGV[$i]" eq "-a" ) {  # path to search the db files
    $i++; $startpoint = $ARGV[$i];
  } elsif ( "$ARGV[$i]" eq "-Z" ) {  # server instance identifier
    $i++; $servid = $ARGV[$i];
  } elsif ("$ARGV[$i]" eq "-h") { # help
    &usage; exit(0);
  } else {
    print "ERROR - Unknown option: $ARGV[$i]\n";
    &usage; exit(1);
  }
  $i++;
}

($servid, $notused_configdir) = DSUtil::get_server_id($servid, "/etc/dirsrv");

print("*****************************************************************\n");
print("verify-db: This tool should only be run if recovery start fails\n" .
      "and the server is down.  If you run this tool while the server is\n" .
      "running, you may get false reports of corrupted files or other\n" .
      "false errors.\n");
print("*****************************************************************\n");

if ( "$startpoint" eq "" ) {
  $startpoint = "/var/lib/dirsrv/slapd-$servid/db";
} else {
  $custom_dbdir = 1;
}
# get dirs having DBVERSION
my $dbdirs = getDbDir($startpoint);

# Check transaction logs by db_printlog
for (my $i = 0; "$$dbdirs[$i]" ne ""; $i++)
{
  my $logfile = getLastLogfile($$dbdirs[$i]);

  if ( "$$logfile" ne "" )
  {
    # run db_printlog -h <dbdir> for each <dbdir>
    print "Verify log files in $$dbdirs[$i] ... ";
    open(PRINTLOG, "db_printlog -h $$dbdirs[$i] 2>&1 1> $NULL |");
    sleep 1;
    my $haserr = 0;
    while ($l = <PRINTLOG>)
    {
      if ("$l" ne "")
      {
        if ($haserr == 0)
        {
          print "\n";
        }
        print "LOG ERROR: $l";
        $haserr++;
      }
    }
    close(PRINTLOG);
    if ($haserr == 0 && $? == 0)
    {
      print "Good\n";
    }
    else
    {
      print "Log file(s) in $$dbdirs[$i] could be corrupted.\n";
      print "Please delete a log file $$logfile, and try restarting the server.\n";
    }
  }
}

# Check db files by db_verify
print "Verify db files ... ";
if ($custom_dbdir){
    open(DBVERIFY, "/usr/bin/dbverify -Z $servid -a $startpoint 2>&1 1> $NULL |");
} else {
    open(DBVERIFY, "/usr/bin/dbverify -Z $servid 2>&1 1> $NULL |");
}
sleep 1;
my $bad_index = 0;
my $bad_id2entry = 0;
my $isfirst = 1;
while ($l = <DBVERIFY>)
{
    if ($isfirst)
    {
        print "\n";
        $isfirst = 0;
    }
    if ("$l" =~ /verify failed/)
    {
        if ("$l" =~ /id2entry.db/)
        {
            $bad_id2entry++;
        }
        else
        {
            $bad_index++;
        }
    }
    print "$l";
}
close(DBVERIFY);

if ($bad_id2entry > 0)
{
    print "\nFound the db was corrupted\n";
    print "Please restore your backup and recover the database.\n";
    exit(1);
}
elsif ($bad_index > 0)
{
    print "\nFound the index file(s) was corrupted\n";
    print "Please run db2index on the corrupted index\n";
    exit(1);
}
else
{
    print "Good\n";
    exit(0);
}
