#!/usr/bin/env perl use strict; use warnings; use autodie; use FindBin; use lib "$FindBin::Bin/lib"; use LP_EnsureArch; LP_EnsureArch::ensure_support('inotify'); use File::Temp; use File::Slurp; use Test::More; use Test::Deep; use Test::FailWarnings -allow_deps => 1; use Test::SharedFork; use Socket; use Linux::Perl::inotify; for my $generic_yn ( 0, 1 ) { if ( my $pid = fork ) { waitpid $pid, 0; die if $?; } else { eval { my $class = 'Linux::Perl::inotify'; if (!$generic_yn) { require Linux::Perl::ArchLoader; $class = Linux::Perl::ArchLoader::get_arch_module($class); }; _do_tests($class); }; die if $@; exit; } } sub _do_tests { my ($class) = @_; note "Using class: $class (PID $$)"; cmp_bag( [ keys %{ $class->EVENT_NUMBER() } ], [ 'ACCESS', 'MODIFY', 'ATTRIB', 'CLOSE_WRITE', 'CLOSE_NOWRITE', 'OPEN', 'MOVED_FROM', 'MOVED_TO', 'CREATE', 'DELETE', 'DELETE_SELF', 'MOVE_SELF', 'UNMOUNT', 'Q_OVERFLOW', 'IGNORED', 'ISDIR', 'CLOSE', 'MOVE', ], 'all expected EVENT_NUMBER() members', ) or diag explain $class->EVENT_NUMBER(); my $dir = File::Temp::tempdir( CLEANUP => 1 ); my $inotify = eval { $class->new( flags => [ 'NONBLOCK' ] ); }; if (!$inotify) { my $err = $@ or die "no inotify but no error?"; if ($err->get('error') == Errno::EMFILE()) { diag "inotify init failed: $@"; for my $in_stat ( qw( user_instances user_watches queued_events ) ) { my $node = "/proc/sys/fs/inotify/max_$in_stat"; my $val = File::Slurp::read_file($node); diag "$node: $val"; } #diag "inotify instances for UID $>:"; #diag q<> . `for foo in /proc/*/fd/*; do readlink -f \$foo; done | grep ':inotify' | sort | uniq -c | sort -nr | awk '{print; s+=\$1} END {print s}'`; #diag q<> . `ps aux`; return; } local $@ = $err; die; } like( $inotify->fileno(), qr<\A[1-9][0-9]*\z>, 'fileno()' ); my $wd = $inotify->add( path => $dir, events => [ 'ONLYDIR', 'DONT_FOLLOW', 'ALL_EVENTS' ] ); $inotify->read(); ok( $!{'EAGAIN'}, 'EAGAIN when a non-blocking inotify does empty read()' ); do { open my $wfh, '>', "$dir/thefile" }; chmod 0765, $dir; # a quasi-nonsensical mode rename "$dir/thefile" => "$dir/thefile2"; unlink "$dir/thefile2"; $inotify->remove($wd); # This will NOT be picked up by the inotify because # of the remove() just above. do { open my $wfh, '>', "$dir/thefile" }; my @events = $inotify->read(); cmp_bag( \@events, [ { wd => $wd, cookie => 0, mask => $inotify->EVENT_NUMBER()->{'CREATE'}, name => 'thefile', }, { wd => $wd, cookie => 0, mask => $inotify->EVENT_NUMBER()->{'OPEN'}, name => 'thefile', }, { wd => $wd, cookie => 0, mask => $inotify->EVENT_NUMBER()->{'ATTRIB'} | $inotify->EVENT_NUMBER()->{'ISDIR'}, name => q<>, }, { wd => $wd, cookie => ignore(), mask => $inotify->EVENT_NUMBER()->{'MOVED_FROM'}, name => 'thefile', }, { wd => $wd, cookie => ignore(), mask => $inotify->EVENT_NUMBER()->{'MOVED_TO'}, name => 'thefile2', }, { wd => $wd, cookie => 0, mask => $inotify->EVENT_NUMBER()->{'DELETE'}, name => 'thefile2', }, { wd => $wd, cookie => 0, mask => $inotify->EVENT_NUMBER()->{'IGNORED'}, name => q<>, }, ], 'create chmod, unlink, rmdir events', ) or diag explain \@events; my @move_evts = grep { $_->{'mask'} & $inotify->EVENT_NUMBER()->{'MOVE'} } @events; is( $move_evts[0]{'cookie'}, $move_evts[1]{'cookie'}, 'move cookie values match', ); return; } done_testing();