#! /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, "; 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 = ) { # 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;