Tinkster has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

Trying to add "watches" event-triggered. The idea is to have the files that eventually end up in the in-directories automatically processed, and have the results put into the out ones of the same 0-9{9} subdirs.

When I add a directory structure under the main dir in question, /home/tink/files/, the watch creation always fails; can someone think of a reason WHY?

The sub-directories are always 9-digit names, and always get created via

mkdir -p /home/tink/files/123456789/{in,out}

The script is clumsy, and fraught w/ debug code atm.

#!/usr/bin/perl -w use Linux::Inotify2; use File::Find (); use strict; use vars qw/*name *dir *prune/; *name = *File::Find::name; *dir = *File::Find::dir; *prune = *File::Find::prune; sub wanted; my @results = '/home/tink/files/'; File::Find::find({wanted => \&wanted}, '/home/tink/files/'); print "@results\n"; sub wanted { my ($dev,$ino,$mode,$nlink,$uid,$gid); (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) && -d _ && ( /^in\z/s || /^out\z/s ) && push(@results, $name); } our $inotify = new Linux::Inotify2 or die "unable to create new inotify object: $!"; my @result; foreach my $jail (@results){ $inotify->watch($jail, IN_CLOSE_WRITE|IN_CREATE|IN_DELETE) or die "w +atch creation failed" ; } while () { my @events = $inotify->read; unless (@events > 0) { print "read error: $!"; last ; } foreach (@events) { printf "mask\t%d\t\%s\t%s\n", $_->mask, $_->name, $_->fullname ; if ( $_->fullname =~ m@(/home/tink/files/[0-9]{9})@) { sleep 5; my $myin = "'".$1."/in'"; my $myout = "'".$1."/out'"; printf "%s\t%s\n", $myin ,$myout; $inotify->watch($myin, IN_CLOSE_WRITE|IN_CREATE|IN_DELETE) or d +ie "watch creation failed" ; $inotify->watch($myout, IN_CLOSE_WRITE|IN_CREATE|IN_DELETE) or +die "watch creation failed" ; } } }

Cheers, Tink

Replies are listed 'Best First'.
Re: Linux::Inotify2, adding sub-dirs to watches event-triggered...
by Marshall (Canon) on Nov 19, 2010 at 10:41 UTC
    I hadn't played with inotify2 before and I remembered that I have a temporary student account on another machine (I do most of my work with Windows) so I ran your code. I was actually surprised that it basically worked because you have some really strange things in your code.

    When you ask a question like: "the watch creation always fails; can someone think of a reason WHY?", explain how it fails! What kind of symptom and output do you get? Also, reduce the code to what is necessary to show the problem - take out the stuff you don't need. Probably the reason that this question has been hanging out for some hours with no replies is that it is too much hassle to mess with! I got interested because I wanted to install a local perl lib directory on Linux without root permission and also see how well inotify2 really worked (and it works pretty well albeit with some "hick-ups"). Answering your question wound up being a side-effect of doing something that I wanted to do anyway.

    So, I guess you mean that "watch creation fails" means that the watches aren't installed rather than getting an error message of some sort? There is a problem with the $_->fullname method. It returns something like: fullname:/home/student/tmp2604/ntest//2299 and therefore your regex fails to match when the 2299 directory is installed "on the fly" - therefore the watches for the new in or out directories are never installed. So I just simplified that code along the way. The below works on my testing machine.

    I can tell that you are new to Perl. A few pointers:
    - you don't need to do stuff like: *name = *File::Find::name; and I took it out.
    - I was surprised that this extraneous "_" in what should have been an "if" statement didn't cause problems, but I took that out also.
    - something like: my $myin   = "'".$1."/in'"; is pretty darn hard to understand and there is no need for it,  my $myin = "$1/in"; would have sufficed.
    - you don't need to pre-declare a sub name like with: sub wanted; - also not needed and I took that out also.
    - the /s option on a regex means that "." in like ".+" is allowed to match newline \n, which it normally doesn't. Not needed so I took that out.
    - Perl has a shortcut for [0-9]{9} like \d{9} I didn't want to mess with having exactly 9 digits in my test code, so I just used /^\d+$/ which means the match encompasses things that contain 1 or more digits and only digits.
    - printf isn't used that often in Perl. printf "in:%s\tout:%s\n",  $myin ,$myout; can be:
       print "in:$myin\tout:$myout\n";
    - don't use an "our" variable when a "my" variable will suffice. my $inotify = new Linux::Inotify2... not our $inotify = new Linux::Inotify2

    Anyway, you've got more code to write in order to get to were you want to go. But adding more watches based upon a new directory showing up in a watched directory does work.

    #!/usr/bin/perl -w use strict; use lib '/home/student/tmp2604/lib'; use Linux::Inotify2; use File::Find; my @results = "/home/student/tmp2604/ntest/"; File::Find::find({wanted => \&wanted}, '/home/student/tmp2604/ntest/') +; print "@results\n"; sub wanted { if (lstat($_) && -d && (/^in\z/ || /^out\z/) ) { push(@results, $File::Find::name); } } my $inotify = new Linux::Inotify2 or die "unable to create new inotify object: $!"; foreach my $jail (@results){ $inotify->watch($jail, IN_CLOSE_WRITE|IN_CREATE|IN_DELETE) or die "watch creation failed for $jail" ; } while () { my @events = $inotify->read; unless (@events > 0) { print "read error: $!"; last ; } foreach (@events) { printf "mask: %s name: %s fullname:%s\n", $_->mask, $_->name, $_-> +fullname ; my $name = $_->name; if ( $name =~ m/^\d+$/ ) { sleep 5; my $myin = "/home/student/tmp2604/ntest/$name/in"; my $myout = "/home/student/tmp2604/ntest/$name/out"; printf "in:%s\tout:%s\n", $myin ,$myout; $inotify->watch($myin, IN_CLOSE_WRITE|IN_CREATE|IN_DELETE) or die "watch creation failed for $myin" ; $inotify->watch($myout, IN_CLOSE_WRITE|IN_CREATE|IN_DELETE) or die "watch creation failed for $myout" ; } } }
      Good Explanation. I haven't tried both the code but it educates me lot.
      Thanks Marshall
        Thanks for the compliment! I am happy to hear that somebody is learning something from this - I actually did too!

        I don't know that you will ever need to use Linux::Inotify2 or not. But, you may need to install some other module on your Linux system without root permission. Here's how I did it... Keep in mind that I am using a student account which has the lowest permission levels on the planet!

        I started with looking at the tutorials: don't have permission?. The first step is to make the directory that your "private Perl library" will reside in. I just used the suggested name of "lib".

        Now things diverge a bit from the tutorial.. I just typed "cpan" at the command prompt. That was already a command and I didn't have to do anything special. This will create a .cpan directory and start asking a bunch of questions (in UNIX a file name that beings with "." is a "hidden file" - so you will have to use "ls -al" or some such to see it in a directory listing).

        When this dialog gets to a question about PREFIX, that's where you type in "PREFIX=~/lib LIB=~/lib", re:the tutorial. I think all of the other defaults were ok on my test machine except for the question about mirrors, enter one or more mirror sites specified by number - otherwise just hit enter.

        I didn't see this in the tut, probably there but I missed it. However, I found that I needed to set the PERL5LIB environment variable to get the 'install Linux::Inotify2' command to work from the cpan application. The "make" needs to find common/sense.pm

        This student account uses the bash shell, so I modified the file .bash_profile by adding:
        PERL5LIB=$HOME/lib:$HOME/lib/common
        export PERL5LIB
        at the end of it.
        This results in:
        [tmp2604@mymachine~]$ env | grep -i Perl
        PERL5LIB=/home/student/tmp2604/lib:/home/student/tmp2604/lib/common

        I don't know if adding /common to the path was necessary or not - at the time I didn't worry about it and actually I'm still not worried about it - searching will stop once "make" finds what it needs.

        At the end of the day, installing this special module wasn't THAT hard. I had to mess around a bit, but it works.

      Hi,

      Thanks for the feedback! Some of the weirdness you pointed out came from running find2perl ... sorry if the tool produces "weird code". But you're right in saying that I'm new to perl; I know enough to be dangerous, never use it enough to become proficient.

      Anyhow - thank you very much for your input - I'll tackle it from there.

      Cheers,
      Tink