#! /usr/bin/env perl -w

# simply takes a server name, and rsync's it to $LOCAL_ROOT_DIR/servername/backup
# Can handle two scenarios:
#     rsync over ssh
#     mount remote drive and rsync contents
# These are controled via the $connectionType. $connectionType of smb mounts the drives
# though of course it could be any type of mount. It assumes the mount parameters are
# stored in fstab with the "noauto" flag set.

# returns   0 on success
#           1 if rsync command could not be executed
#           2 if there was a signal that killed rsync
#           3 if drive could not be mounted (smb connection type)
#           4 if there was an error returned by rsync

use warnings;
use strict;

use Getopt::Long;

my $LOCAL_ROOT_DIR = '/home/backups';
my $LOCAL_MOUNT_POINT = '/media';
my $SUMMARIZE_RSYNC_LOG = '/etc/rsbackup/scripts/summarizeRsyncLog';
my $START_TIME = &dateTimeStamp('u');
my $SENDLOGS = '/etc/rsbackup/scripts/sendLogs';

my $TESTING = 0;

my $connectionType = 'smb';
my $source;
my @additionalParameters;
my @sourceDirectories;
my $logDir='/home/logs/local_backups';
my $mailTo = '';
my $mailLog = 0;

# simple sub that retrieves the date time stamp.
# First parameter is the type of data you want to receive, either 'u' for a simple unix number, 't' for yyyymmddhhmmss, and anything else for yyyymmdd
# second parameter is optional. If passed, this is assumed to be the unix time you want to use. If not passed in, will take current time from computer
sub dateTimeStamp {
   $format = lc shift; # what format do they want
   $theTime = shift; # optional timestamp to process
   $theTime = time unless $theTime; # they did not pass in a timestamp, so get the one from the system
   if ($format eq 'u') { # only unix time, just return time
      return $theTime;
   }
   # all other requires we parse out the time into a gregorian calendar
   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($theTime);
   if ($format eq 't') { # they want the time included
      return sprintf "%4d%02d%02d%02d%02d%02d",$year+1900,$mon+1,$mday,$hour,$min,$sec;
   } else { # don't want time included, so simply get YYYYMMDD (the default)
      return sprintf "%4d%02d%02d",$year+1900,$mon+1,$mday;
   }
}

sub time2HHMMSS {
   my $seconds = shift;
   return sprintf( "%02d:%02d:%02d", $seconds / 3600, ($seconds / 60) % 60, $seconds % 60 );
}


sub checkMount {
   my $mountPoint = shift;
   open MTAB, "</etc/mtab" or die "Could not open /etc/mtab\n";
   @mounts = grep { /$mountPoint/ } <MTAB>;
   close MTAB;
   return scalar @mounts;
}

sub mountDrive {
   my $server = shift; # we assume the drive is listed in fstab
   unless ( &checkMount( "$LOCAL_MOUNT_POINT/$source" ) ) {
      `mount $LOCAL_MOUNT_POINT/$source`;
      sleep(10);
   }
   return &checkMount( "$LOCAL_MOUNT_POINT/$source" );
}

sub umountDrive {
   my $server = shift;;
   if ( &checkMount( "$LOCAL_MOUNT_POINT/$source" ) ) {
      `umount $LOCAL_MOUNT_POINT/$source`;
      sleep(10);
   }
   return ! &checkMount( "$LOCAL_MOUNT_POINT/$source" );
}

sub compressFile {
   my $GZIP = '/bin/gzip';
   my $COMPRESSED_FILE_EXTENSION = 'gz';
   my $filename = shift;
   my $resultFile = $filename . '.' . $COMPRESSED_FILE_EXTENSION;
   if ($GZIP) {
      if ($TEST_CONDITIONS) {
         print qq( $GZIP $filename );
         print "\n";
      } else {
         if ( -e $resultFile ) {
            print "Removing $resultFile as it already exists. DATA LOST\n";
            unlink $resultFile;
         }
         qx( $GZIP $filename );
      }
   } else {
      use IO::Zlib;
      if ($TEST_CONDITIONS) {
         print "Compressing using library\n";
      } else {
         # now, we compress the log file for mailing
         open logFile,"<$filename" or die "could not open $filename for reading: $!";
         # open the same file, with a .gz suffix (just like the gzip command)
         $fh = IO::Zlib->new("$resultFile", "wb9") or die "could not create archive $resultFile: $!";
         # log files can be quite large, so lets process one line at a time.
         while ($line = <logFile>) { # read a line
               print $fh $line; # write it to the compressed image
         }
         close logFile;
         # Add some stats at the end of the compressed file (I don't do this for the main log file)
         print $fh 'Started: ' . &dateTimeStamp('t',$startTime) . "\nEnded: " .  &dateTimeStamp('t',$endTime) . "\nDuration: " . ($endTime - $startTime) . " seconds)\n";
         $fh->close;
      }
   }
   return $resultFile;
}


GetOptions(
            'connection=s' => \$connectionType,
            'root_target=s' => \$LOCAL_ROOT_DIR,
            'server=s' => \$source,
            'parameters=s' => \@additionalParameters,
            'directories=s' => \@sourceDirectories,
            'testing:i' => \$TESTING,
            'mailto:s' => \$mailTo,
            'maillog!' => \$mailLog,  # flag, either 0 or 1
            'logdir=s' => \$logDir
           );

if ( $TESTING > 3 ) {
  print "connection $connectionType\n";
  print "server $source\n";
  print "parameters " . join( "\n\t", @additionalParameters ) . "\n";
  print "directories " . join ("\n\t", @sourceDirectories ) . "\n";
  print "testing $TESTING\n";
  print "mailto $mailTo\n";
  print "mailLog $mailLog\n";
  print "logdir $logDir\n";
  print "target $LOCAL_ROOT_DIR\n";
  die;
}                                                                                                    

# this will allow comma separated values in the directories list, ie --directories='/home,/etc,/root'
@sourceDirectories = split( ',', join( ',', @sourceDirectories ) );

die "Usage: $0 parameters\nRequired Parameters\n==============\n--server=servername\n--directories=RemoteDirectoriesList\n--local=LocalDirectory\nOptional Parameters\n==============\n--connection=[smb|rsync]\n--parameters=additionalparameters\n--testing=[1|2]\n--initialize\n"
   unless $source;

print "Beginning backup of $source at " . `date`;
my $parameters = '-av --delete --stats ' . join( ' ', @additionalParameters ) ;
my $sourceDirectories;

$parameters .= ' --dry-run ' if $TESTING;
$localDirectory = "$LOCAL_ROOT_DIR/$source/backup";
`mkdir -p $localDirectory` unless -d $localDirectory;

if ( lc $connectionType eq 'smb' ) {
   # mount drives, die if not successful
   unless ( &mountDrive( $source ) ) {
      print STDERR "Could not mount $source for backup";
      exit 3;
   }
   # set source to the just the mount point, unless @sourceDirectories contains additional information.
   $sourceDirectories = @sourceDirectories ? join( ' ', ( map { "$LOCAL_MOUNT_POINT\/$source\/$_" } @sourceDirectories ) ) : "$LOCAL_MOUNT_POINT\/$source\/*";
} elsif ( lc $connectionType eq 'rsync' ) {
   $sourceDirectories = join( ' ', ( map { "$source:$_" } @sourceDirectories ) );
}

# make sure the log directory exists
`mkdir -p $logDir` unless -d $logDir;

$logDir .= '/' . &dateTimeStamp('ymd',$START_TIME) . ".$source.backup.log";
# NOTE: in the following, we are sending STDERR and STDOUT to $logdir
my $command .= "rsync $parameters $sourceDirectories $localDirectory &> $logDir";
if ( $TESTING > 1 ) {
   print "$command\n";
} else {
   $returnCode = system( $command );
}

&umountDrive( $source );

die if $TESTING > 1;

if ($returnCode == -1) {
   print STDERR "failed to execute command: $!\n\t$command\n";
   exit 1;
} elsif ($returnCode & 127) {
   printf STDERR "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without';
   exit 4;
} else {
   $returnCode >>= 8; # the actual return code is shifted 8 bits
}

my $summary = qx/$SUMMARIZE_RSYNC_LOG $returnCode '$logDir'/;

$logDir = &compressFile( $logDir );

my $END_TIME = &dateTimeStamp( 'u' );
$summary =  '   Begin    ' . &dateTimeStamp( 't', $START_TIME ) . ' [' . $START_TIME . "]\n" .
            '   Complete ' . &dateTimeStamp( 't', $END_TIME ) . ' [' . $END_TIME . "]\n" .
            '   Duration ' . &time2HHMMSS($END_TIME-$START_TIME) . ' [' . ($END_TIME-$START_TIME) . " seconds]\n" .
            $summary;

# if $mailTo is defined, send an e-mail to the person with the summary
# if $mailLog is true, send a compressed copy of the mailLog also
if ( $mailTo ) {
   my $TEMP_FILE = "/tmp/$START_TIME\-$source.tmp";
   open TEMP,">$TEMP_FILE" or die "Could not open temp file [$TEMP_FILE] for writing: $!";
   print TEMP $summary;
   close TEMP;
   my $subject = "Backup for $source, " . &dateTimeStamp( $START_TIME );
   qx( $SENDLOGS --to='$mailTo' -s '$subject' -a '$logDir' --content='$TEMP_FILE' --testing=1 );
   unlink $TEMP_FILE;
}

print "$summary\n";

`/etc/rsbackup/scripts/getSize`;

1;