<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">package File::ChangeNotify::Watcher::Inotify;
{
  $File::ChangeNotify::Watcher::Inotify::VERSION = '0.22';
}

use strict;
use warnings;
use namespace::autoclean;

use File::Find ();
use Linux::Inotify2 1.2;

use Moose;

extends 'File::ChangeNotify::Watcher';

has is_blocking =&gt; (
    is      =&gt; 'ro',
    isa     =&gt; 'Bool',
    default =&gt; 1,
);

has _inotify =&gt; (
    is       =&gt; 'ro',
    isa      =&gt; 'Linux::Inotify2',
    default  =&gt; sub { Linux::Inotify2-&gt;new() },
    init_arg =&gt; undef,
);

has _mask =&gt; (
    is      =&gt; 'ro',
    isa     =&gt; 'Int',
    lazy    =&gt; 1,
    builder =&gt; '_build_mask',
);

sub sees_all_events {1}

sub BUILD {
    my $self = shift;

    $self-&gt;_inotify()-&gt;blocking( $self-&gt;is_blocking() );

    # If this is done via a lazy_build then the call to
    # -&gt;_watch_directory ends up causing endless recursion when it
    # calls -&gt;_inotify itself.
    $self-&gt;_watch_directory($_) for @{ $self-&gt;directories() };

    return $self;
}

sub wait_for_events {
    my $self = shift;

    $self-&gt;_inotify()-&gt;blocking(1);

    while (1) {
        my @events = $self-&gt;_interesting_events();
        return @events if @events;
    }
}

override new_events =&gt; sub {
    my $self = shift;

    $self-&gt;_inotify()-&gt;blocking(0);

    super();
};

sub _interesting_events {
    my $self = shift;

    my $filter = $self-&gt;filter();

    my @interesting;

    # This is a blocking read, so it will not return until
    # something happens. The restarter will end up calling -&gt;watch
    # again after handling the changes.
    for my $event ( $self-&gt;_inotify()-&gt;read() ) {
        # An excluded path will show up here if ...
        #
        # Something created a new directory and that directory needs to be
        # excluded or when the exclusion excludes a file, not a dir.
        next if $self-&gt;_path_is_excluded( $event-&gt;fullname() );

        if ( $event-&gt;IN_CREATE() &amp;&amp; $event-&gt;IN_ISDIR() ) {
            $self-&gt;_watch_directory( $event-&gt;fullname() );
            push @interesting, $event;
            push @interesting,
                $self-&gt;_fake_events_for_new_dir( $event-&gt;fullname() );
        }
        elsif ( $event-&gt;IN_DELETE_SELF() ) {
            $self-&gt;_remove_directory( $event-&gt;fullname() );
        }

        # We just want to check the _file_ name
        elsif ( $event-&gt;name() =~ /$filter/ ) {
            push @interesting, $event;
        }
    }

    return
        map { $_-&gt;can('path') ? $_ : $self-&gt;_convert_event($_) } @interesting;
}

sub _build_mask {
    my $self = shift;

    my $mask
        = IN_MODIFY | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF;
    $mask |= IN_DONT_FOLLOW unless $self-&gt;follow_symlinks();

    return $mask;
}

sub _watch_directory {
    my $self = shift;
    my $dir  = shift;

    # A directory could be created &amp; then deleted before we get a
    # chance to act on it.
    return unless -d $dir;

    File::Find::find(
        {
            wanted =&gt; sub {
                my $path = $File::Find::name;

                if ( $self-&gt;_path_is_excluded($path) ) {
                    $File::Find::prune = 1;
                    return;
                }

                $self-&gt;_add_watch_if_dir($path);
            },
            follow_fast =&gt; ( $self-&gt;follow_symlinks() ? 1 : 0 ),
            no_chdir    =&gt; 1,
            follow_skip =&gt; 2,
        },
        $dir
    );
}

sub _add_watch_if_dir {
    my $self = shift;
    my $path = shift;

    return if -l $path &amp;&amp; !$self-&gt;follow_symlinks();

    return unless -d $path;
    return if $self-&gt;_path_is_excluded($path);

    $self-&gt;_inotify()-&gt;watch( $path, $self-&gt;_mask() );
}

sub _fake_events_for_new_dir {
    my $self = shift;
    my $dir  = shift;

    return unless -d $dir;

    my @events;
    File::Find::find(
        {
            wanted =&gt; sub {
                my $path = $File::Find::name;

                return if $path eq $dir;
                if ( $self-&gt;_path_is_excluded($path) ) {
                    $File::Find::prune = 1;
                    return;
                }

                push @events, $self-&gt;event_class()-&gt;new(
                    path =&gt; $path,
                    type =&gt; 'create',
                );
            },
            follow_fast =&gt; ( $self-&gt;follow_symlinks() ? 1 : 0 ),
            no_chdir =&gt; 1
        },
        $dir
    );

    return @events;
}

sub _convert_event {
    my $self  = shift;
    my $event = shift;

    return $self-&gt;event_class()-&gt;new(
        path =&gt; $event-&gt;fullname(),
        type =&gt; (
              $event-&gt;IN_CREATE() ? 'create'
            : $event-&gt;IN_MODIFY() ? 'modify'
            : $event-&gt;IN_DELETE() ? 'delete'
            : 'unknown'
        ),
    );
}

__PACKAGE__-&gt;meta()-&gt;make_immutable();

1;

# ABSTRACT: Inotify-based watcher subclass



=pod

=head1 NAME

File::ChangeNotify::Watcher::Inotify - Inotify-based watcher subclass

=head1 VERSION

version 0.22

=head1 DESCRIPTION

This class implements watching by using the L&lt;Linux::Inotify2&gt;
module. This only works on Linux 2.6.13 or newer.

This watcher is much more efficient and accurate than the
C&lt;File::ChangeNotify::Watcher::Default&gt; class.

=head1 AUTHOR

Dave Rolsky &lt;autarch@urth.org&gt;

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2012 by Dave Rolsky.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)

=cut


__END__

</pre></body></html>