<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Inotify &#8211; Johnny Morano&#039;s Tech Articles</title>
	<atom:link href="https://jmorano.moretrix.com/tag/inotify/feed/" rel="self" type="application/rss+xml" />
	<link>https://jmorano.moretrix.com</link>
	<description>Ramblings of an old-fashioned space cowboy</description>
	<lastBuildDate>Thu, 08 Nov 2012 08:03:16 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>

<image>
	<url>https://jmorano.moretrix.com/wp-content/uploads/2022/04/cropped-jmorano_emblem-32x32.png</url>
	<title>Inotify &#8211; Johnny Morano&#039;s Tech Articles</title>
	<link>https://jmorano.moretrix.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Recursive Inotify Daemon</title>
		<link>https://jmorano.moretrix.com/2012/10/recursive-inotify-daemon/</link>
					<comments>https://jmorano.moretrix.com/2012/10/recursive-inotify-daemon/#comments</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Tue, 02 Oct 2012 10:55:01 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[Daemon]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[Inotify]]></category>
		<guid isPermaLink="false">http://jmorano.moretrix.com/?p=905</guid>

					<description><![CDATA[There aren&#8217;t many Inotify daemons available that can work recursive and offer a descent flexibility regarding Inotify signals.&#8230;]]></description>
										<content:encoded><![CDATA[<p>There aren&#8217;t many <a href="http://en.wikipedia.org/wiki/Inotify" target="_blank">Inotify</a> daemons available that can work recursive and offer a descent flexibility regarding Inotify signals. For basic rsync operations, <a href="http://code.google.com/p/lsyncd/" title="lsyncd website" target="_blank">lsyncd</a> isn&#8217;t that bad but it isn&#8217;t that flexible for system administration. The fact that it is written and configured in Lua, makes it actually really complex (considering that you don&#8217;t know to develop in Lua). Most system administrators do know or at least understand Perl.</p>
<p><a href="http://search.cpan.org/~mlehmann/Linux-Inotify2-1.22/Inotify2.pm" title="Linux::Inotify2" target="_blank">Linux::Inotify2</a> is a CPAN module which allows to catch Linux Inotify signals (duh) and react upon them, in realtime. Combined with the <a href="http://search.cpan.org/~mlehmann/AnyEvent-7.02/lib/AnyEvent.pm" title="AnyEvent" target="_blank">AnyEvent</a> CPAN module, you can use a plethora of event loop libraries available in your system, to have an amazingly fast inotify daemon.</p>
<p>The first thing we&#8217;ll discuss, is how to turn a regular Perl script into a standalone daemon process.</p>
<pre class="brush:perl">
sub daemonize {
# Inspired by http://stackoverflow.com/questions/766397/how-can-i-run-a-perl-script-as-a-system-daemon-in-linux
    POSIX::setsid or die "setsid: $!";
    my $pid = fork ();
    if ($pid < 0) {
        die "fork: $!";
    } 
    elsif ($pid) {
        exit 0;
    }

    chdir "/";
    umask 0;
    foreach (0 .. (POSIX::sysconf (&#038;POSIX::_SC_OPEN_MAX) || 1024)) { 
        POSIX::close $_ 
    }

    open (STDIN, "</dev/null");
    # open (STDOUT, ">/dev/null");
    open (STDOUT, ">>/tmp/log.txt");
    open (STDERR, ">&STDOUT");

    # Save PID to disk
    open my $pid_file, '>', '/var/run/script.pid'
        or die "Could not open PID file: $!\n";
    print { $pid_file } "$$";
    close ($pid_file);
} 
</pre>
<p>The process will get forked and the default IO handlers will be redirected. A PID file will also be created, so that the process can killed easily afterwards.<br />
For example:</p>
<pre class="brush:bash">
kill $(cat /var/run/script.pid)
</pre>
<p>Next we will create a inotify signal handler and search for directories to watch for changes. In our example, we will watch every subdirectory of the &#8216;/data/vault&#8217; directory (the parent directory itself is not included in this example):</p>
<pre class="brush:perl">
# Create Inotify object
my $inotify = Linux::Inotify2->new()
    or die "Failed to created inotify object: $!\n";

# Search for directories to watch
find({ wanted => sub { -d $_ 
                       && create_watcher($inotify, $File::Find::name) }  
     }
    , '/data/vault');
</pre>
<p>The &#8216;find&#8217; subroutine comes from the <a href="http://perldoc.perl.org/File/Find.html" title="File::Find" target="_blank">File::Find</a> module, which is a standard Perl module.<br />
For every directory found, the &#8216;create_watcher&#8217; subroutine will be called, with the full path of the found directory.</p>
<pre class="brush:perl">
sub create_watcher {
    my ($inotify, $dir) = @_;
    my $watcher = $inotify->watch($dir, IN_CREATE | IN_CLOSE_WRITE | IN_MOVE | IN_DELETE, sub {
            my $e = shift;
            my $filename  = $e->fullname;
            
            if(-d $filename && $e->IN_CREATE) {
                create_watcher($inotify, $filename);
                return
            }
            elsif(-f $filename){
                if($e->IN_CLOSE_WRITE){
                    print "IN_CLOSE_WRITE $filename\n"
                }
                elsif($e->IN_MOVED_FROM){
                    print "IN_MOVE_FROM $filename\n"
                }
                elsif($e->IN_MOVED_TO){
                    print "IN_MOVED_TO $filename\n"
                }
                elsif($e->IN_DELETE){
                    print "IN_DELETE $filename\n"
                }
            }
    });
    print "Watching $dir\n";
    $W{$dir} = $watcher;
}
</pre>
<p>The watcher code will watch for the following inotify signals:</p>
<ul>
<li>IN_CREATE</li>
<li>IN_CLOSE_WRITE</li>
<li>IN_MOVE</li>
<li>IN_DELETE</li>
</ul>
<p>If a new directory is created within &#8216;/data/vault&#8217;, the &#8216;create_watcher&#8217; subroutine will be called again for this new directory. For files, the code will check what inotify signal was spawned and act accordingly.</p>
<p>The last to do is to initialize the event loop, using AnyEvent:</p>
<pre class="brush:perl">
my $cv = AnyEvent->condvar;
# Create event loop poller
my $poller = AnyEvent->io(
        fh   => $inotify->fileno,
        poll => 'r',
        cb   => sub { $inotify->poll }
);

# Receive event signals (inotify signals)
$cv->recv;
</pre>
<p>The above example shows how the inotify object ($inotify) is used in the event listener.</p>
<p>These examples explain how to create a simple inotify daemon, which can recursively go through directories and then watch these directories for certain inotify signals.</p>
<p>The full script:</p>
<pre class="brush:perl">
#!/usr/bin/env perl 
use strict;
use warnings;
use utf8;

use AnyEvent;
use Linux::Inotify2;
use File::Find;
use POSIX;

my $PID_FILE = "/var/run/$0.pid";

# Fork this process, to run as a daemon
daemonize();

# enable autoflush to have faster logging
$|++;

# Catch kill signals
local $SIG{TERM} = sub {
    if(-f $PID_FILE){
        unlink($PID_FILE)
    }

    print("$0 daemon killed.");
    exit 0;
};
local $SIG{INT} = $SIG{TERM};

my $cv = AnyEvent->condvar;
# watcher container hash
my %W;

# Create Inotify object
my $inotify = Linux::Inotify2->new()
    or die "Failed to created inotify object: $!\n";

# Search for directories to watch
find({ wanted => sub { -d $_ 
                       && create_watcher($inotify, $File::Find::name) }  
     }
    , '/data/vault');


# Create event loop poller
my $poller = AnyEvent->io(
        fh   => $inotify->fileno,
        poll => 'r',
        cb   => sub { $inotify->poll }
);

# Receive event signals (inotify signals)
$cv->recv;

#
# Subroutines
#
sub create_watcher {
    my ($inotify, $dir) = @_;
    my $watcher = $inotify->watch($dir, IN_CREATE | IN_CLOSE_WRITE | IN_MOVE | IN_DELETE, sub {
            my $e = shift;
            my $filename  = $e->fullname;
            
            if(-d $filename && $e->IN_CREATE) {
                create_watcher($inotify, $filename);
                return
            }
            elsif(-f $filename){
                if($e->IN_CLOSE_WRITE){
                    print "IN_CLOSE_WRITE $filename\n"
                }
                elsif($e->IN_MOVED_FROM){
                    print "IN_MOVE_FROM $filename\n"
                }
                elsif($e->IN_MOVED_TO){
                    print "IN_MOVED_TO $filename\n"
                }
                elsif($e->IN_DELETE){
                    print "IN_DELETE $filename\n"
                }
            }
    });
    print "Watching $dir\n";
    $W{$dir} = $watcher;
}

sub daemonize {
# Inspired by http://stackoverflow.com/questions/766397/how-can-i-run-a-perl-script-as-a-system-daemon-in-linux
    POSIX::setsid or die "setsid: $!";
    my $pid = fork ();
    if ($pid < 0) {
        die "fork: $!";
    } 
    elsif ($pid) {
        exit 0;
    }

    chdir "/";
    umask 0;
    foreach (0 .. (POSIX::sysconf (&#038;POSIX::_SC_OPEN_MAX) || 1024)) { 
        POSIX::close $_ 
    }

    open (STDIN, "</dev/null");
    # open (STDOUT, ">/dev/null");
    open (STDOUT, ">>/tmp/log.txt");
    open (STDERR, ">&STDOUT");

    # Save PID to disk
    open my $pid_file, '>', $PID_FILE
        or die "Could not open PID file: $!\n";
    print { $pid_file } "$$";
    close ($pid_file);
} 
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2012/10/recursive-inotify-daemon/feed/</wfw:commentRss>
			<slash:comments>14</slash:comments>
		
		
			</item>
	</channel>
</rss>
