#!perl use 5.010_001; use strict; use warnings; use utf8; use Const::Fast; use File::Find; use File::stat; use Linux::Inotify2; our $VERSION = '0.07'; const our $SELF => 'inotifymask'; # Mask to remove the bits of stat->mode that we don't care about const our $STAT_MASK => oct '07777'; # Check argument count after processing options if ( @ARGV < 2 ) { printf {*STDERR} "%s: Need a mask and at least one path\n", $SELF; exit 2; } # Shift off mask as octal, remaining arguments are paths my $mask = oct shift; my %paths = map { $_ => undef } @ARGV; # Expand paths list to include all subdirectories my $wanted = sub { for ($File::Find::name) { return if !-d; $paths{$_} = undef; } }; find( $wanted, keys %paths ); # Process creation and move-in events my $cb = sub { my ($event) = @_; my $name = $event->fullname; # File must exist -e $name or return; # Must not be a directory not -d $name or return; # Get stats or give up my $stat = stat $name or return; # Get the mode we want for the path, masking off irrelevant path type bits my $mode = $stat->mode & $STAT_MASK; # # Check that at least one bit of the mask coincides with one bit of the # present mode, i.e. that we'll be changing anything; this is important # because we might otherwise get misleading log entries, and maybe even an # infinite loop...! # $mode & $mask or return; # Using the masked mode, change the permissions and log to stderr for ( $mode & ~$mask ) { chmod $_, $name or return; printf {*STDERR} "masked %s %04o\n", $name, $_; } }; # Create object and set up watches with callback, start polling my $in = Linux::Inotify2->new; for ( keys %paths ) { $in->watch( $_, IN_ATTRIB | IN_CREATE | IN_MOVED_TO, $cb ); } while (1) { $in->poll; }