Automatic synchronization using recursive inotify change detection

Ryan Babchishin <rbabchishin@win2ix.ca>

Win2ix Systems Inc. http://www.win2ix.ca

 

Below is a simple Perl script that automatically synchronizes two directories recursively. Changes to the source directory are detected using the Linux kernel inotify feature.

The inotify API is a kernel feature that allows you to monitor filesystem events. You can monitor files or folders for things like create, delete, access, move, etc… inotify does not support monitoring directories recursively, so that needs to be handled by the programmer. The script below synchronizes two directories (local or remote via rsync). The source directory is re-synchronized only on startup and when changes are detected by inotify.

To try the script, create source and destination directories, set $syncDir and $syncDest in the script and run it. Then in another application/terminal, try creating, copying, moving, renaming files in the source directory. You should see the script react and all changes replicated in the destination directory as they happen.

 

#!/usr/bin/perl

#
# This script is public domain. May, 2013.
# 
# Ryan Babchishin <rbabchishin@win2ix.ca>
# http://www.win2ix.ca
#

# Load the inotify perl module
use Linux::Inotify2;

my $syncDir = "sync"; 	   # Source directory
my $syncDest = "sync-new"; # Destination directory or user@host:/path (rsync/ssh)

# Create inotify object
my $inotify = Linux::Inotify2->new or die "unable to create new inotify object: $!";

# Get a list of subdirectories to watch for changes
open(FIND, "find $syncDir -type d |");
while(my $dir = ) {
	chomp($dir); # Remove newline
	print "Adding watcher for: $dir\n";
	# Create new inotify watcher for this directory
	$inotify->watch($dir, IN_CLOSE_WRITE | IN_MODIFY | IN_CREATE | IN_DELETE| IN_MOVED_FROM | IN_MOVED_TO);
}

# Sync once on startup
system("rsync $syncDir --delete -av $syncDest");

# Process inotify events
while () {
  # Get a new inotify event
  my @events = $inotify->read;
  unless (@events > 0){
    print "read error: $!";
    last ;
  }

  # Loop for each event encountered
  foreach my $event (@events) {
    	print $event->fullname . " was modified\n" ;

	# If a new directory is created, add a watcher for it
	if (($event->IN_ISDIR) && ($event->IN_CREATE)) {
		print "Is a new directory, adding watcher\n";	
		# Create new inotify watcher for this directory
		$inotify->watch($event->fullname, IN_CLOSE_WRITE | IN_MODIFY | IN_CREATE | IN_DELETE| IN_MOVED_FROM | IN_MOVED_TO);
	}

	# Perform a sync
	system("rsync $syncDir --delete -av $syncDest");
  }
}