I need to watch multiple files in Perl, and I am using Linux::Inotify2. However I am encountering an issue in that the first file being watched needs to be modified and hit, then the second, then the first etc etc
For example if the second file is changed before the first, it will not trigger out, or if the first is triggered twice in a row without the second being triggered in between.
This is the section of code I am using which is having this issue.
my $inotify = new Linux::Inotify2;
my $inotify2 = new Linux::Inotify2;
$inotify->watch ("/tmp/rules.txt", IN_MODIFY);
$inotify2->watch ("/tmp/csvrules.out", IN_MODIFY);
while () {
my @events = $inotify->read;
unless (@events > 0){
print "read error: $!";
last ;
}
foreach $mask (@events) {
printf "mask\t%d\n", $mask;
open (WWWRULES, "/tmp/rules.txt");
my @lines = <WWWRULES>;
foreach $line (@lines) {
@things = split(/,/, $line);
addrule(@things[0], @things[1], @things[2], @things[3], trim(@things[4]));
print "PRINTING: @things[0], @things[1], @things[2], @things[3], @things[4]";
close (WWWRULES);
open (WWWRULES, ">/tmp/rules.txt");
close (WWWRULES);
}
}
my @events2 = $inotify2->read;
unless (@events2 > 0){
print "read error: $!";
last ;
}
foreach $mask (@events) {
printf "mask\t%d\n", $mask;
open (SNORTRULES, "/tmp/csvrules.out");
my @lines2 = <SNORTRULES>;
foreach $line2 (@lines2) {
@things2 = split(/,/, $line2);
addrule("INPUT", @things2[0], @things2[1], @things2[2], trim(@things2[3]));
print "PRINTING: INPUT, @things2[0], @things2[1], @things2[2], @things2[3]";
close (SNORTRULES);
open (SNORTRULES, ">/tmp/csvrules.out");
close (SNORTRULES);
}
}
}
Ideally I would like to be watching 3 files but as I cannot get 2 working it seems a little pointless at this stage.
Thanks for any help!
A single inotify object can handle any number of watches. That's one of the advantages of inotify over the older and now obsolete dnotify. So you should be saying:
my $inotify = Linux::Inotify2->new;
$inotify->watch("/tmp/rules.txt", IN_MODIFY);
$inotify->watch("/tmp/csvrules.out", IN_MODIFY);
Then you can see which watch was triggered by checking the fullname
property of the event object:
while () {
my @events = $inotify->read;
unless (@events > 0){
print "read error: $!";
last ;
}
foreach my $event (@events) {
print $event->fullname . " was modified\n" if $event->IN_MODIFY;
}
}
The big problem is that your code is modifying the same files that you're watching for modifications. When /tmp/rules.txt
is modified, you open it, read it, and then truncate it, which triggers another modification notice, starting the whole process over again. In general, this is hard to solve without race conditions, but in your case, you should be able to just check for an empty file (next if -z $event->fullname
).