#!/usr/bin/perl -w # Synchronize directory between multiple locations using the freshest copy. use strict; use Net::SSH qw(sshopen2); use Net::SCP qw(iscp); use Date::Parse; # Helpful for troubleshooting and calming down your nervous system my $debug = 0; # Directory to synchronize (get from command line) my $dir = shift; # Shortcuts to frequently used directories my %dir_shortcuts = ( 'sim' => '/home/me/data/sim', 'vim' => '/home/me/.vim', ); # Use shortcut if one was specified $dir = $dir_shortcuts{$dir} if ($dir && $dir_shortcuts{$dir}); # Stop if no directory was specified die "No directory specified" unless ($dir && -d $dir); print "Working with $dir\n" if ($debug); # Locations. Make sure one of them is 'localhost'. my %locations = ( 'home' => 'me@homeserver.net', 'work1' => 'office-me@workstation1', 'work2' => 'localhost', ); # Now split the full path into path and relative dir my $path = $dir; $path =~ s#(.*)/.*$#$1#; $dir =~ s#^.*/##; print "Path = $path , dir = $dir\n" if ($debug); # Get directory modification times for all hosts my %times = (); my $diff = 0; # Flag to see if data is in sync or not my $cache = 0; # Cache for previous mtime foreach my $location (keys %locations) { $times{$location} = get_time($locations{$location}); # We don't want to do anything if all mtimes are the same unless ($cache) { $cache = $times{$location} }; $diff++ if ($cache - $times{$location} != 0); print "Modification time for $location is $times{$location}\n" if ($debug); } unless ($diff) { print "All data is in sync\n"; exit(0); } my $source; my $center; # Identify the most recently modified host foreach my $host (sort { $times{$b} <=> $times{$a} } keys %times) { unless ($source) { $source = $locations{$host}; } # I'm just teasing you here ;) unless ($locations{$host} ne 'localhost') { $center = $locations{$host}; } } print "Latest data is at $source\n" if ($debug); print "Center is at $center\n" if ($debug); # Make sure that we have both locations before going on unless ($source && $center) { die "Something went wrong! (Source: $source, Center: $center)\n"; } # Switch to the proper directory chdir($path) or die "Couldn't switch to $path : $!\n"; # Copy files to a center location if they aren't already there if ($source ne 'localhost') { print "Copying files from $source to $center\n" if ($debug); iscp("$source:$path/$dir", ".") or die "Couldn't copy data from $source to $center\n"; } # Copy files from center to all locations except the one we found the most recent copy at foreach my $host (keys %locations) { unless (($locations{$host} eq $center) || ($locations{$host} eq $source)) { print "Syncing from $center to $locations{$host}\n" if ($debug); iscp("$dir","$locations{$host}:$path/") or die "Couldn't copy data from $center to $host\n"; } } # Get the mtime of the directory sub get_time { my $target = shift; my $time; # If target is a remote machine, use SSH unless ($target eq 'localhost') { sshopen2($target, *READER, *WRITER, 'stat', "$path/$dir") or die "ssh: $!"; while (my $line = ) { if ($line =~ m/^Modify/) { chomp($line); $line =~ s/^Modify:\s+//; $time = str2time($line); } } # This is needed to avoid warnings from strict. close(READER); close(WRITER); } # Otherwise stat the local filesystem else { my @stat = stat("$path/$dir"); $time = $stat[9]; } return $time; }