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

My question is this:

I'm running a program out of cron that writes to creates a log file. I need to open it so that I can write direct STDOUT and STDERR to it so that I can capture all the output from the program as it is running. The first thing that happens is that the old log file is removed so I only get data from the current cycle in the log. The problem is that the cron is set to execute every 15 minutes but if an earlier copy of the process is still executing and writing to that log file (and this is a possibility), the new copy will delete that log file. I need to open the log in such a way that the first process is the only process using the file until it is completely done.

Here is an example of the code, probably not the cleanest code, but what will happen is that if I go to a command prompt while the program is sleeping, I can delete the log file before I can close it.

use strict; use Time::localtime; use Fcntl; my $dir="/path/to/bin/perl"; my @perl=(); # Main Program Block MAIN: { sysopen (STDOUT, "$dir/TEST.NEW", O_RDWR | O_CREAT ) or die "Cannot op +en TEST.LOG: $!"; open (STDERR, ">>&STDOUT") or die "Cannot dupe STDOUT to STDERR: $!"; flock(STDOUT, 2) or die "Cannot lock filename: $!"; opendir (DIR, "$dir") or die "Cannot open $dir: $!"; @perl=grep { /\w\.pl/ } readdir DIR; print "@perl\n"; foreach (<@perl>) {print "\$_ is $_\n";} sleep (60); close (STDOUT); close (STDERR); }

Replies are listed 'Best First'.
Re: File Locking
by perlplexer (Hermit) on Apr 23, 2003 at 15:21 UTC
    A couple of things:

    1. Use flock() constants defined in Fcntl.pm.
    use Fcntl ':flock'; ... flock $fh, LOCK_EX or exit $status; ...
    2. You're redirecting STDOUT and STDERR and then using die(), which will clobber your TEST.NEW if flock() fails. Either don't use die() and just exit() or implement a logging mechanism of some sort so that you're not writing to STDOUT/STDERR.

    3. If the file is locked, do you want to exit immediately or do you want the program to block and wait until it's unlocked?
    # will block until file is unlocked flock $fh, LOCK_EX or exit $status; # will exit immediately if the file is locked flock $fh, LOCK_EX | LOCK_NB or exit $status;
    --perlplexer
      How do I go about check to see if there is already a file lock? I want the program to exit if it cannot get a lock on the file.
        Do you have to delete the file from some other script? Or would it be more reasonable to simply truncate() TEST.NEW from your main script after it aquires the lock?
        open STDOUT, ">>$dir/TEST.NEW" or exit 1; open STDERR, ">>&STDOUT" or exit 2; flock STDOUT, LOCK_EX | LOCK_NB or exit 3; truncate STDOUT, 0; # delete everything from the file # The remainder of the program will be writing to an # empty file. close STDERR; close STDOUT;
        There is a way to check if the file is locked from another script but you can't safely delete it because as soon as you close the file, so that you can delete it, it will be unlocked and there is a slight chance that some other process will write to it in the interim.
        To prevent this from happening, both processes either need to use truncate(), in which case the file doesn't get deleted but you still accomplish the same thing - no data in the file; Or, as the second option, both processes need to use a second file that they would lock whenever they need to update/delete TEST.NEW.

        --perlplexer
Re: File Locking
by zby (Vicar) on Apr 23, 2003 at 15:15 UTC
    If I did understood you well you remove the file from the command line while it is locked by the Perl script - and you are supprised that you can do that. It depends on the system you are using, but on some (if not on most of them) the flock call is a C library call - and thus it is just a advisory lock, not a mandatory one. So with shell you can allways go around it.
      OK, what if I have do an unlink on the file from another Perl program instead?
        Then that "another Perl program" first needs to check whether the file is locked or not, and unlink() or truncate() (if appropriate) it only if it's not locked.

        --perlplexer