1 # Copyright (C) 1996, 1998 David Muir Sharnoff
 
   7 @EXPORT = qw(lock unlock lock_rename);
 
  10 use POSIX qw(EAGAIN EACCES EWOULDBLOCK ENOENT EEXIST O_EXCL O_CREAT O_RDWR); 
 
  11 use Fcntl qw(LOCK_SH LOCK_EX LOCK_NB LOCK_UN);
 
  14 use vars qw($VERSION $debug $av0debug);
 
  25 my %locks;              # did we create the file?
 
  33         my ($pkg, $file, $shared, $nonblocking) = @_;
 
  34         &lock($file, $shared, $nonblocking) or return undef;
 
  35         return bless \$file, $pkg;
 
  46         my ($file, $shared, $nonblocking) = @_;
 
  51         my $previous = exists $locks{$file};
 
  53         # the file may be springing in and out of existance...
 
  57                         unless (sysopen($f, $file, O_RDWR)) {
 
  58                                 redo OPEN if $! == ENOENT;
 
  59                                 croak "open $file: $!";
 
  62                         unless (sysopen($f, $file, O_CREAT|O_EXCL|O_RDWR)) {
 
  63                                 redo OPEN if $! == EEXIST;
 
  64                                 croak "open >$file: $!";
 
  66                         print STDERR " {$$ " if $debug; # }
 
  71         $locks{$file} = $created || $locks{$file} || 0;
 
  72         $shared{$file} = $shared;
 
  75         $lockHandle{$file} = $f;
 
  79         $flags = $shared ? LOCK_SH : LOCK_EX;
 
  83         local($0) = "$0 - locking $file" if $av0debug && ! $nonblocking;
 
  84         my $r = flock($f, $flags);
 
  86         print STDERR " ($$ " if $debug and $r;
 
  89                 # let's check to make sure the file wasn't
 
  92                 my $ifile = (stat($file))[1];
 
  94                 eval { $ihandle = (stat($f))[1] };
 
  97                 return 1 if defined $ifile 
 
  99                         and $ifile == $ihandle;
 
 104                 return File::Flock::lock($file);
 
 111                 or ($! == EWOULDBLOCK))) 
 
 114                         delete $locks{$file};
 
 115                         delete $lockHandle{$file};
 
 116                         delete $shared{$file};
 
 120                         # oops, a bad thing just happened.  
 
 121                         # We don't want to block, but we made the file.
 
 122                         &background_remove($f, $file);
 
 127         croak "flock $f $flags: $!";
 
 131 # get a lock on a file and remove it if it's empty.  This is to
 
 132 # remove files that were created just so that they could be locked.
 
 134 # To do this without blocking, defer any files that are locked to the
 
 137 sub background_remove
 
 141         if (flock($f, LOCK_EX|LOCK_NB)) {
 
 148                         unless exists $rm{$file};
 
 157         if (ref $file eq 'File::Flock') {
 
 158                 bless $file, 'UNIVERSAL'; # avoid destructor later
 
 162         croak "no lock on $file" unless exists $locks{$file};
 
 163         my $created = $locks{$file};
 
 168         if ($created && defined($size) && $size == 0) {
 
 169                 if ($shared{$file}) {
 
 171                                 &background_remove($lockHandle{$file}, $file);
 
 174                         print STDERR " $$} " if $debug;
 
 176                                 or croak "unlink $file: $!";
 
 179         delete $locks{$file};
 
 182         my $f = $lockHandle{$file};
 
 184         delete $lockHandle{$file};
 
 186         return 0 unless defined $f;
 
 188         print STDERR " $$) " if $debug;
 
 189         $unlocked or flock($f, LOCK_UN)
 
 190                 or croak "flock $file UN: $!";
 
 198         my ($oldfile, $newfile) = @_;
 
 200         if (exists $locks{$newfile}) {
 
 203         delete $locks{$newfile};
 
 204         delete $shared{$newfile};
 
 205         delete $pid{$newfile};
 
 206         delete $lockHandle{$newfile};
 
 207         delete $rm{$newfile};
 
 209         $locks{$newfile}        = $locks{$oldfile}      if exists $locks{$oldfile};
 
 210         $shared{$newfile}       = $shared{$oldfile}     if exists $shared{$oldfile};
 
 211         $pid{$newfile}          = $pid{$oldfile}        if exists $pid{$oldfile};
 
 212         $lockHandle{$newfile}   = $lockHandle{$oldfile} if exists $lockHandle{$oldfile};
 
 213         $rm{$newfile}           = $rm{$oldfile}         if exists $rm{$oldfile};
 
 215         delete $locks{$oldfile};
 
 216         delete $shared{$oldfile};
 
 217         delete $pid{$oldfile};
 
 218         delete $lockHandle{$oldfile};
 
 219         delete $rm{$oldfile};
 
 223 # Unlock any files that are still locked and remove any files
 
 224 # that were created just so that they could be locked.
 
 228         for $f (keys %locks) {
 
 234         for my $file (keys %rm) {
 
 235                 my $f = new IO::File;
 
 236                 if (sysopen($f, $file, O_RDWR)) {
 
 237                         if (flock($f, LOCK_EX|LOCK_NB)) {
 
 249                 croak "cannot fork" unless defined $ppid;
 
 252                 $0 = "$b0: waiting for child ($ppid) to fork()";
 
 255                         croak "cannot fork" unless defined $pid;
 
 257                                 for my $file (keys %bgrm) {
 
 258                                         my $f = new IO::File;
 
 259                                         if (sysopen($f, $file, O_RDWR)) {
 
 260                                                 if (flock($f, LOCK_EX)) {
 
 268                                 print STDERR " $pppid] $pppid)" if $debug;
 
 270                         kill(9, $$); # exit w/o END or anything else
 
 273                 kill(9, $$); # exit w/o END or anything else
 
 283  File::Flock - file locking with flock
 
 291  lock($filename, 'shared');
 
 293  lock($filename, undef, 'nonblocking');
 
 295  lock($filename, 'shared', 'nonblocking');
 
 299  my $lock = new File::Flock '/somefile';
 
 301  lock_rename($oldfilename, $newfilename)
 
 305 Lock files using the flock() call.  If the file to be locked does not
 
 306 exist, then the file is created.  If the file was created then it will
 
 307 be removed when it is unlocked assuming it's still an empty file.
 
 309 Locks can be created by new'ing a B<File::Flock> object.  Such locks
 
 310 are automatically removed when the object goes out of scope.  The
 
 311 B<unlock()> method may also be used.
 
 313 B<lock_rename()> is used to tell File::Flock when a file has been
 
 314 renamed (and thus the internal locking data that is stored based
 
 315 on the filename should be moved to a new name).  B<unlock()> the
 
 316 new name rather than the original name.
 
 320 File::Flock may be used/modified/distibuted on the same terms
 
 325 David Muir Sharnoff <muir@idiom.org>