File Coverage

blib/lib/DB_File/Lock.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #
2             # DB_File::Lock
3             #
4             # by David Harris
5             #
6             # Copyright (c) 1999-2000 David R. Harris. All rights reserved.
7             # This program is free software; you can redistribute it and/or modify it
8             # under the same terms as Perl itself.
9             #
10              
11             package DB_File::Lock;
12              
13             require 5.004;
14              
15 1     1   1139 use strict;
  1         2  
  1         47  
16 1     1   6 use vars qw($VERSION @ISA $locks);
  1         2  
  1         103  
17              
18             @ISA = qw(DB_File);
19             $VERSION = '0.05';
20              
21 1     1   1710 use DB_File ();
  0            
  0            
22             use Fcntl qw(:flock O_RDWR O_RDONLY O_WRONLY O_CREAT);
23             use Carp qw(croak carp);
24             use Symbol ();
25              
26             # import function can't be inherited, so this magic required
27             sub import
28             {
29             my $ourname = shift;
30             my @imports = @_; # dynamic scoped var, still in scope after package call in eval
31             my $module = caller;
32             my $calling = $ISA[0];
33             eval " package $module; import $calling, \@imports; ";
34             }
35              
36             sub _lock_and_tie
37             {
38             my $package = shift;
39              
40             ## Grab the type of tie
41              
42             my $tie_type = pop @_;
43              
44             ## There are two ways of passing data defined by DB_File
45              
46             my $lock_data;
47             my @dbfile_data;
48              
49             if ( @_ == 5 ) {
50             $lock_data = pop @_;
51             @dbfile_data = @_;
52             } elsif ( @_ == 2 ) {
53             $lock_data = pop @_;
54             @dbfile_data = @{$_[0]};
55             } else {
56             croak "invalid number of arguments";
57             }
58              
59             ## Decipher the lock_data
60              
61             my $mode;
62             my $nonblocking = 0;
63             my $lockfile_name = $dbfile_data[0] . ".lock";
64             my $lockfile_mode;
65              
66             if ( lc($lock_data) eq "read" ) {
67             $mode = "read";
68             } elsif ( lc($lock_data) eq "write" ) {
69             $mode = "write";
70             } elsif ( ref($lock_data) eq "HASH" ) {
71             $mode = lc $lock_data->{mode};
72             croak "invalid mode ($mode)" if ( $mode ne "read" and $mode ne "write" );
73             $nonblocking = $lock_data->{nonblocking};
74             $lockfile_name = $lock_data->{lockfile_name} if ( defined $lock_data->{lockfile_name} );
75             $lockfile_mode = $lock_data->{lockfile_mode};
76             } else {
77             croak "invalid lock_data ($lock_data)";
78             }
79              
80             ## Warn about opening a lockfile for writing when only locking for reading
81              
82             # NOTE: This warning disabled for RECNO because RECNO seems to require O_RDWR
83             # even when opening only for reading.
84              
85             carp "opening with write access when locking only for reading (use O_RDONLY to fix)"
86             if (
87             ( $dbfile_data[1] && O_RDWR or $dbfile_data[1] && O_WRONLY ) # any kind of write access
88             and $mode eq "read" # and opening for reading
89             and $tie_type ne "TIEARRAY" # and not RECNO
90             );
91              
92             ## Determine the mode of the lockfile, if not given
93              
94             # THEORY: if someone can read or write the database file, we must allow
95             # them to read and write the lockfile.
96              
97             if ( not defined $lockfile_mode ) {
98             $lockfile_mode = 0600; # we must be allowed to read/write lockfile
99             $lockfile_mode |= 0060 if ( $dbfile_data[2] & 0060 );
100             $lockfile_mode |= 0006 if ( $dbfile_data[2] & 0006 );
101             }
102              
103             ## Open the lockfile, lock it, and open the database
104              
105             my $lockfile_fh = Symbol::gensym();
106             my $saved_umask = umask(0000) if ( umask() & $lockfile_mode );
107             my $open_ok = sysopen($lockfile_fh, $lockfile_name, O_RDWR|O_CREAT,
108             $lockfile_mode);
109             umask($saved_umask) if ( defined $saved_umask );
110             $open_ok or croak "could not open lockfile ($lockfile_name)";
111              
112             my $flock_flags = ($mode eq "write" ? LOCK_EX : LOCK_SH) | ($nonblocking ? LOCK_NB : 0);
113             if ( not flock $lockfile_fh, $flock_flags ) {
114             close $lockfile_fh;
115             return undef if ( $nonblocking );
116             croak "could not flock lockfile";
117             }
118              
119             my $self = $tie_type eq "TIEHASH"
120             ? $package->SUPER::TIEHASH(@_)
121             : $package->SUPER::TIEARRAY(@_);
122             if ( not $self ) {
123             close $lockfile_fh;
124             return $self;
125             }
126              
127             ## Store the info for the DESTROY function
128              
129             my $id = "" . $self;
130             $id =~ s/^[^=]+=//; # remove the package name in case re-blessing occurs
131             $locks->{$id} = $lockfile_fh;
132              
133             ## Return the object
134              
135             return $self;
136             }
137              
138             sub TIEHASH
139             {
140             return _lock_and_tie(@_, 'TIEHASH');
141             }
142              
143             sub TIEARRAY
144             {
145             return _lock_and_tie(@_, 'TIEARRAY');
146             }
147              
148             sub DESTROY
149             {
150             my $self = shift;
151              
152             my $id = "" . $self;
153             $id =~ s/^[^=]+=//;
154             my $lockfile_fh = $locks->{$id};
155             delete $locks->{$id};
156              
157             $self->SUPER::DESTROY(@_);
158              
159             # un-flock not needed, as we close here
160             close $lockfile_fh;
161             }
162              
163              
164              
165              
166              
167             1;
168             __END__