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

I have a piece of code this way:
use File::Tail; $SIG{'INT'} = 'INT_handler'; sub INT_handler { open(FILE,"/tmp/filename") || die "Cannot open file:$!"; $cur_filename=<FILE>; close(FILE); print("Don't Interrupt!\n"); chomp($cur_filename); $file=File::Tail->new(name=>$cur_filename, maxinterval=>3, adjusta +fter=>2,tail=>-1); } $cur_filename=$ARGV[0]; $file=File::Tail->new(name=>$cur_filename, maxinterval=>3, adjustafter +=>2,tail=>-1); while (defined($line=$file->read)) { print $line; }
Let me explain my intention. I have a constantly changing log and the log is rotated every hour(so a new log file is created every hour). What i intend to do is have a configuration file which has the current hour's log's filename and every hour I'll change the filename in this config file (externally from a diff prog ) and pass a INTsignal to the above code for it to pick up the newfile name.
But some how this doesnt work. The first time, File::Tail reads the file. But it doesnt read the new one when the INT_handler function handles the signal.
I'm also not sure if what i think is right...looking forward for suggestions.

Replies are listed 'Best First'.
Re: File::Tail issue..
by ikegami (Patriarch) on Mar 20, 2009 at 14:59 UTC

    If read returns with an error when interrupted by the signal, the loop exits so you never read from the new file.

    If read doesn't return when interrupted by the signal, you don't use the new value of $file so you never read from the new file.

    Ideally, the former occurs. Then you could do something like

    use Errno qw( EINTR ); do { while (defined($line=$file->read)) { print $line; } } while $! != EINTR; # Go read from new file.

    I don't have an obvious solution if the read doesn't return. Is this what happens?

      I think the problem is  read doesn't return. Because if it had retured the program would have terminated(becuase it must have exited the loop). But the program never ends. It does what File::Tail should be doing but just doesn't update it with the new file's contents.

        Searching the docs for "timeout" found a solution.

        # Check for new File::Tail object every $timeout seconds. my $timeout = 60; for (;;) { my (undef, undef, @pending) = File::Tail::select(undef, undef, undef, $timeout, $file); for (@pending) { my $line = $_->read(); print $line; } }

        Update: You could even simplify your signal handler (always a good thing) and your code in general as follows:

        use File::Tail; # Check for new File::Tail object every $timeout seconds. my $timeout = 60; my $qfn = $ARGV[0]; my $new = 1; my $changed = 0; my $tail; $SIG{INT} = sub { $changed = 1 }; for (;;) { if ($changed) { $changed = 0; open(my $fh, '<', '/tmp/filename') or die("Cannot open file: $!\n"); chomp( $qfn = <$fh> ); $new = 1; } if ($new) { $new = 0; $tail = File::Tail->new( name => $qfn, maxinterval => 3, adjustafter => 2, tail => -1, ); } my (undef, undef, @pending) = File::Tail::select(undef, undef, undef, $timeout, $tail); for (@pending) { my $line = $_->read(); print $line; } }
Interrupting File::Tail with a signal handler
by ig (Vicar) on Mar 20, 2009 at 20:07 UTC

    I suggest a different approach...

    note: I have not tested this code. The concept has worked elsewhere, but there may be typos or incompatibility with File::Tail.

    #!/usr/bin/perl use strict; use warnings; use File::Tail; my $cur_filename = $ARGV[0]; { my $file = File::Tail->new( name => $cur_filename, maxinterval => 3, adjustafter => 2, tail => -1, ); eval { local $SIG{INT} = sub { die "interrupted\n"; }; while ( defined( my $line = $file->read) ) { print $line; } }; if($@ eq "interrupted\n") { open(FILE,"/tmp/filename") || die "Cannot open file:$!"; $cur_filename=<FILE>; close(FILE); redo; } else { die "$@"; } }

    You may never print the last line of the file if the interrupt arrives after a line has been added and before it has been printed: an interval including a significant number of CPU cycles. This risk can be mitigated somewhat by having the external program redirect log entries to the new file then waiting briefly before interrupting this program.

Re: File::Tail issue..
by targetsmart (Curate) on Mar 20, 2009 at 16:06 UTC
    since you already got some answers, my part of wisdom is this.
    I was once backfired when my program misbehaved because of calling a whole bunch of code inside a signal handler, please look at this post Do's and Dont's inside a signal handler, you will get some idea.

    Vivek
    -- In accordance with the prarabdha of each, the One whose function it is to ordain makes each to act. What will not happen will never happen, whatever effort one may put forth. And what will happen will not fail to happen, however much one may seek to prevent it. This is certain. The part of wisdom therefore is to stay quiet.
Re: File::Tail issue..
by VinsWorldcom (Prior) on Mar 20, 2009 at 15:01 UTC

    I don't think your $file is getting reassigned in the loop which is why you don't get the subsequent file's after the first.

    Try something like (untested):

    use File::Tail; my $cur_filename $SIG{'INT'} = sub { if (-e ($cur_filename = "/tmp/filename")) { $NEWFILE = 1 } else { die "Cannot open file:$!" } }; $cur_filename=$ARGV[0]; while (1) { my $file=File::Tail->new(name => $cur_filename, maxinterval => 3, adjustafter => 2, tail => -1); while (defined($line=$file->read)) { print $line; last if $NEWFILE; } $NEWFILE = 0 }

      I'm pretty sure scoping is not the issue (I thought this at first), because the following code works as expected:

      $SIG{'INT'} = 'Int_Handler'; sub Int_Handler { $file = 2; }; $| = 1; $file = 1; while ($file) { print $file; $file = $file == 1 ? 1 : 0; sleep(1); }

      While the OP should probably adhere to strictures (and is doing a lot of work inside a signal handler), this is not the issue.