#!/usr/bin/perl # (defaulting to rsync, since v. 2.1) $version="3.07"; $date="20 July 2021"; # by Nick Manini sub print_help { print STDERR <=8 || print "\t"; `$lsfull > $tmp1`; print "\t"; `$remoterun "$lsfull" > $tmp2`; print ":"; `$remotecp $remotemachinename:$lspath/$dir $rtmp`; $diffLoc = `diff $reference $tmp1`; $diffRem = `diff $rtmp $tmp2`; print " "; $lLoc=length $diffLoc; $lRem=length $diffRem; if($lLoc+$lRem==0){ $_="s"; print "local and remote are UNCHANGED since last sync: doing nothing.\n"; $nequal++; }else{ # differences detected: something is to be done print "since last sync has the following novelties (<=old, >=new):\n"; $sugg=""; if($lLoc>0){ print "------------------- LOCAL changes to ".$dir.":\n".$diffLoc; }else{ print "LOCAL ".$dir." UNCHANGED since last sync\n"; $sugg="r"; } if($lRem>0){ print "------------------- REMOTE changes to ".$dir.":\n".$diffRem; }else{ print "REMOTE ".$dir." UNCHANGED since last sync\n"; $sugg="l"; } if($sugg=~/[lr]/){ $suggestion="(suggest:$sugg)"; }else{ $suggestion=""; } print "What should we do now about directory ".$dir." ?\n options: sync to [lL]ocal / sync to [rR]emote / [s]kip / [q]uit) ".$suggestion; $result="n"; while($result eq "n"){ $_=&readkey; print $_."\n"; my $lc=lc $_; if($lc eq $sugg){ # OK, doing as suggested, no further check $result="y"; }elsif($lc =~ /[lLrR]/){ # valid key, suspicious request $result=&ask("WARNING, it looks like you want to destroy data: Are you sure?"); if($result eq "y"){ print "OK, syncing to $lc at your own command, don't complain later!\n"; }else{ print "Fine, then what now? [valid options: lL / rR / s / q ]\n"; } }elsif($lc =~ /[sSqQ]/){ # valid key, proceed $result="y"; }else{ print "?beg your pardon? [valid options: lL / rR / s / q ]"; } if($result eq "y"){ if(/l/){ # quick (rsync) local sync print "Syncing to local... "; @args=split(/\s+/,"$quickcp $dir/ $remotemachinename:$dir"); system(@args)==0 or &terminate("ERROR rsyncing from local folder: quitting!\n"); &finalchecks; $nsync++; }elsif(/L/){ # full (tgz) Local sync print "Syncing to local:\n now packaging.. "; @args=split(/\s+/,"$dotarcommand $tmp2 $dir"); system(@args)==0 or &terminate("ERROR making tar file: quitting!\n"); print "now remote-copying.. "; @args=split(/\s+/,"$remotecp $tmp2 $remotemachinename:$rtmp"); system(@args)==0 or &ask_if_terminate("ERROR transmitting file to remote!\n"); &checkCopiedFile; @args=split(/\s+/,"$remoterun rm -fr $dir"); system(@args)==0 or &terminate("ERROR remote-removing obsolete folder: quitting!\n"); print "now remote-unpackaging.. "; @args=split(/\s+/,"$remoterun $untarcommand $rtmp"); system(@args)==0 or &terminate("ERROR remote-untarring: quitting!\n"); &checkTotalSize; &finalchecks; $nsync++; }elsif(/r/){ # quick (rsync) remote sync print "Syncing to remote... "; @args=split(/\s+/,"$quickcp $remotemachinename:$dir/ $dir"); system(@args)==0 or &terminate("ERROR rsyncing from remote folder: quitting!\n"); &finalchecks; $nsync++; }elsif(/R/){ # full (tgz) Remote sync print "Syncing to remote:\n now packaging... "; @args=split(/\s+/,"$remoterun $dotarcommand $rtmp $dir"); system(@args)==0 or &terminate("ERROR making remote tar file: quitting!\n"); print "now copying from remote... "; @args=split(/\s+/,"$remotecp $remotemachinename:$rtmp $tmp2"); system(@args)==0 or &ask_if_terminate("ERROR copying the tar file from remote!\n"); &checkCopiedFile; @args=split(/\s+/,"rm -fr $dir"); system(@args)==0 or &terminate("ERROR removing obsolete local dir: quitting!\n"); print "now unpackaging... "; @args=split(/\s+/,"$untarcommand $tmp2"); system(@args)==0 or &terminate("ERROR untarring file: quitting!\n"); &checkTotalSize; &finalchecks; $nsync++; }elsif(/[sS]/){ $nleft++; print "Skipping and leaving directory ".$dir." unsynchronized... "; }elsif(/[qQ]/){ $nleft++; &terminate("quitting here\n"); }else{ print "?beg your pardon? [valid keys are lL / rR / s / q ]"; $result="n"; } } # if($result eq "y" } # while($result eq "n") print "done.\n"; } } # final cleanup &terminate("normal completion\n"); sub ask_if_terminate{ print "\n".$_[0]; print "Should we now Continue or Terminate? "; $_=&readkey; print $_."\n"; $result="y"; if(/t/ || /T/ || /q/ || /Q/){ $nleft++; &terminate("OK, quitting here\n"); }else{ print "OK, continuing at your request, but beware of potential trouble!\n"; } } sub terminate{ `$remoterun rm -f $rtmp`; `rm -f $tmp1 $tmp2 $rtmp`; print "synchronizedisks: verified the equality of ".$nequal." folders\n"; print " synchronized ".$nsync." folders\n"; print " left unsynchronized ".$nleft." folders\n"; die " ".$_[0]; } sub finalchecks{ @args=("$lsfull > $lsdir/$dir"); system(@args)==0 or print "WARNING: error updating the local list\n"; @args=split(/\s+/,"$remoterun $lsfull > $lspath/$dir"); system(@args)==0 or print "WARNING: error updating the remote list\n"; # explicit comparison of the ls of local and remote folders: they must match `$remotecp $remotemachinename:$lspath/$dir $rtmp`; $diffLocRem = `diff $lspath/$dir $rtmp`; $lLocRem=length $diffLocRem; if($lLocRem>0){ print "WARNING WARNING WARNING!!!\nThere remain the following unexpected differences between Local and Remote:\n".$diffLocRem."\nEND OF WARNING\n\n"; } } sub checkCopiedFile{ $_=`md5sum $tmp2`; @l=split(/\s+/); $lcheck=$l[0]; $_=`$remoterun "md5sum $rtmp"`; @l=split(/\s+/); $rcheck=$l[0]; if ($lcheck ne $rcheck){ die "ERROR! non-matching md5sums: $lcheck $rcheck\nStopping here, please check manually the files! $dir!\n"; } } sub checkTotalSize{ $lsize=`du -s0 $dir`; $rsize=`$remoterun "du -s0 $dir"`; $deviation=abs($lsize-$rsize); if($deviation>5){ if ($deviation>0.05*($lsize+$rsize)/2.){ # tolerate 5% size mismatch die "FAILED SIZE TEST! local size=$lsize; remote size=$rsize\nStopping here, please check manually what happened to folder $dir!\nNote that both tar files are kept and have passed the md5sum tests\n"; }else{ print STDERR "\nWARNING, local size=$lsize; remote size=$rsize \n"; } } } sub readkey{ # returns the first keypressed, not waiting for return use Term::ReadKey; # in Debian, provided by libterm-readkey-perl ReadMode raw; # Turn off controls keys until (defined ($key = ReadKey(-1))) { # No key yet } ReadMode restore; # Reset tty mode before exiting # print "Got key $key\n"; return $key; } sub ask{ print $_[0]." (y/n) "; local $ans; while($ans=&readkey){ if($ans=~/[yYnN]/){ print $ans."\n"; return lc $ans; } print "?".$ans."?\t"; } }