#!/usr/bin/perl -w use strict; #----------------------------------------# # What to do, and when, from arguments # #----------------------------------------# my $what = $ARGV[0] || 'none'; # either 'from' or 'to' my $mach = $ARGV[1] || 'none'; # what machine to get/put info from/to my $often = $ARGV[2] || 'none'; # frequency string ('daily', 'weekly' or 'montly') my $debug = $ARGV[3] || 0; # whether to run in debug mode (normal run + debugging output) #-------------# # Variables # #-------------# my $rsync = "rsync -a -e ssh --delete --delete-excluded"; # base rsync command to use my $user = $ENV{'LOGNAME'}; # username of script user my $home = $ENV{'HOME'}; # your home dir my $logfile = "$home/.LOGs/backup_log"; # file to put a log entry of what we did my $local = 'homer'; # name of local machine my $source = ''; # dir rsync pulls data from my $destdir = ''; # destination dir to put backup my $excluded = ''; # string with info of what files should be excluded my $ssh_str = ''; # string that makes SSH connection my $cp = '/bin/cp -P'; # command to use for copying files # Hash with 'machine name' => 'IP': my %ip = ( 'example' => '1.2.3.4', ); # Hash with 'machine name' => 'username in that machine': my %user = ( 'example' => 'isilanes', ); # Hash with 'machine name' => 'backup dir in that machine': my %dir = ( 'example' => "/backup/isilanes/$local.home", ); my %nmax = ( 'daily' => 7, 'weekly' => 4, 'monthly' => 6 ); #-----------------# # Sanitize vars # #-----------------# my $whats = 'to from'; # available $whats die "Can not execute action '$what'!\n" unless ($whats =~ /\b$what\b/); # check requested $what my $machines = 'example example2 example3'; # available machines die "Machine '$mach' is an incorrect option!\n" unless ($machines =~ /\b$mach\b/); # check requested machine my $oftens = 'daily weekly monthly'; # available frequencies die "Frequency '$often' is an incorrect option!\n" unless ($oftens =~ /\b$often\b/); # check requested frequency #--------------------# # Create variables # #--------------------# if ($what eq 'to') # we are backing up local machine into remote { $source = $home; $destdir = $dir{$mach} or die "Destination machine '$mach' has no defined destination dir!\n"; $excluded = "--exclude-from $home/.LOGs/excludes_backup.dat"; } else # we are backing up remote machine into local { $source = $user{$mach} . '@' . "$ip{$mach}:~/"; $destdir = "/scratch/backup/home.$mach"; $excluded = '--exclude "*.chk" --exclude "*.o?????" --exclude RUNs'; }; #--------------# # Do the job # #--------------# unless ($often eq 'none') { if ($what eq 'to') # put local home in remote machine { my $remote = $user{$mach} . '@' . $ip{$mach}; # user@machine for remote machine $ssh_str = "ssh $remote"; # build SSH string &rotate_backups($often,$mach,$ssh_str); # rotate the remote backup dirs (remote machine is given by $ssh_str) print "$rsync $excluded $source/ $remote:$destdir.current/\n" if $debug; # debugging output system "$rsync $excluded $source/ $remote:$destdir.current/"; # actually perform the BACKUP with RSYNC system "$ssh_str \"touch $destdir.current\""; # timestamp the destination dir &writelog($local,$often,$mach); # write log entry } elsif ($what eq 'from') # put remote home in local scratch { &rotate_backups($often,$mach,'bash -c'); # rotate the local backup dirs corresponding to machine $mach print "$rsync $excluded $source/ $destdir.current/\n" if $debug; # debugging output system "$rsync $excluded $source/ $destdir.current/"; # actually perform the BACKUP with RSYNC system "touch $destdir.current"; # timestamp local dir &writelog($mach,$often,$local); # write log entry }; }; # ----- Subroutines ----------------------------------------------------------- sub writelog { ####################################################### # # # This writes an entry in the logfile, to assess that # # the job was done (and when). # # # ####################################################### my $from = ucfirst($_[0]); my $often = $_[1]; my $to = uc($_[2]); my $date = `date`; open(LOG,">>$logfile"); printf LOG "home@%-10s %-7s backup at %-10s on %1s",$from,$often,$to,$date; close(LOG); }; sub rotate_backups { ################################################### # # # Rotate the backups, to make room for new one. # # # ################################################### my $type = $_[0] or die "rotate_backups: Specify backup frequency!\n"; # the type (frequency) of backups we're rotating my $mach = $_[1] or die "rotate_backups: Specify a machine to backup!\n"; # the machine the backups belong to my $ssh_str = $_[2] or die "rotate_backups: Specify a connection command!\n"; # SSH string to exec (to connect to the remote machine) # If 'bash -c' is used, it means local machine. my $bdir = ''; # base backup dir to rotate if ($ssh_str eq 'bash -c') # then it's local { $bdir = "/scratch/backup/home.$mach"; } else # then it's remote { $bdir = $dir{$mach} or die "rotate_backups: Machine '$mach' has no assigned backup dir!\n"; }; my $nmax = $nmax{$type} || 7; # Max number of backups you want: my $i; # Rotate latest dir present -> .tmp for ($i=$nmax;$i>0;$i--) # count up-down { if (`$ssh_str 'test -d $bdir.$type.$i && echo OK'`) # first match { print "$bdir.$type.$i exists!\n" if $debug; # debugging output print "$ssh_str 'rm -rf $bdir.tmp'\n" if $debug; # debugging output system "$ssh_str 'rm -rf $bdir.tmp'"; # delete .tmp, just in case it exists print "$ssh_str '$cp -alf $bdir.$type.$i $bdir.tmp'\n" if $debug; # debugging output system "$ssh_str '$cp -alf $bdir.$type.$i $bdir.tmp'"; # copy it to .tmp last; # stop after first match }; }; # Maybe there's not even .current (1st run). If not, create it: unless (`$ssh_str 'test -e $bdir.current && echo OK'`) { print "$ssh_str 'mkdir $bdir.current'\n" if $debug; # debugging output system "$ssh_str 'mkdir $bdir.current'" # create if missing }; # Maybe there's not even .1, just .current (2nd run), so .tmp # wasn't created. Create it from .current, unless it exists: unless (`$ssh_str 'test -e $bdir.tmp && echo OK'`) { print "$ssh_str '$cp -alf $bdir.current $bdir.tmp'\n" if $debug; # debugging output system "$ssh_str '$cp -alf $bdir.current $bdir.tmp'"; # copy if need be }; # Rotate .N-1 -> .N, ..., .1 -> .2 print "$ssh_str 'rm -rf $bdir.$type.$nmax'\n" if $debug; # debugging output system "$ssh_str 'rm -rf $bdir.$type.$nmax'"; # so that we never have more than nmax backups for ($i=$nmax-1;$i>0;$i--) # count up-down { my $j = $i+1; &test_mv("$bdir.$type.$i","$bdir.$type.$j",$ssh_str); # mv dir .i to .i+1 }; # Rotate .current -> .1: &test_mv("$bdir.current","$bdir.$type.1",$ssh_str); # Restore last (tmp) backup into .current, and then refresh it: &test_mv("$bdir.tmp","$bdir.current",$ssh_str); if (`$ssh_str 'test -d $bdir.$type.1 && echo OK'`) { print "$ssh_str '$cp -alf $bdir.$type.1/* $bdir.current/'\n" if $debug; # debugging output system "$ssh_str '$cp -alf $bdir.$type.1/* $bdir.current/'"; # copy if need be } else { warn "WARNING: Not even dir '$bdir.$type.1' existed!\n" }; }; sub test_mv { ########################################################## # # # Move dir FROM into dir TO, warning if error happens. # # # ########################################################## my $from = $_[0] or die "Specify a FROM dir!\n"; # directory to copy my $to = $_[1] or die "Specify a TO dir!\n"; # destination directory my $ssh_str = $_[2] or die "Specify a SSH string!\n"; # SSH string to connect to remote machine, or 'bash -c' for local machine) if (`$ssh_str 'test -d $to && echo OK'`) { warn "ERROR: dir '$to' exists!\n" } elsif (not `$ssh_str 'test -d $from && echo OK'`) { warn "WARNING: dir '$from' does not exist.\n" } else { print "$ssh_str 'mv $from $to'\n" if $debug; # debugging output system "$ssh_str 'mv $from $to'"; # actually move }; };