line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# -*- perl -*- |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# File::NFSLock - bdpO - NFS compatible (safe) locking utility |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# $Id: NFSLock.pm,v 1.29 2018/11/01 14:00:00 bbb Exp $ |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# Copyright (C) 2002, Paul T Seamons |
8
|
|
|
|
|
|
|
# paul@seamons.com |
9
|
|
|
|
|
|
|
# http://seamons.com/ |
10
|
|
|
|
|
|
|
# |
11
|
|
|
|
|
|
|
# Rob B Brown |
12
|
|
|
|
|
|
|
# bbb@cpan.org |
13
|
|
|
|
|
|
|
# |
14
|
|
|
|
|
|
|
# This package may be distributed under the terms of either the |
15
|
|
|
|
|
|
|
# GNU General Public License |
16
|
|
|
|
|
|
|
# or the |
17
|
|
|
|
|
|
|
# Perl Artistic License |
18
|
|
|
|
|
|
|
# |
19
|
|
|
|
|
|
|
# All rights reserved. |
20
|
|
|
|
|
|
|
# |
21
|
|
|
|
|
|
|
# Please read the perldoc File::NFSLock |
22
|
|
|
|
|
|
|
# |
23
|
|
|
|
|
|
|
################################################################ |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
package File::NFSLock; |
26
|
|
|
|
|
|
|
|
27
|
78
|
|
|
78
|
|
6747738
|
use strict; |
|
78
|
|
|
|
|
1022
|
|
|
78
|
|
|
|
|
2262
|
|
28
|
78
|
|
|
78
|
|
422
|
use warnings; |
|
78
|
|
|
|
|
130
|
|
|
78
|
|
|
|
|
2247
|
|
29
|
|
|
|
|
|
|
|
30
|
78
|
|
|
78
|
|
411
|
use Carp qw(croak confess); |
|
78
|
|
|
|
|
155
|
|
|
78
|
|
|
|
|
4855
|
|
31
|
|
|
|
|
|
|
our $errstr; |
32
|
78
|
|
|
78
|
|
492
|
use base 'Exporter'; |
|
78
|
|
|
|
|
171
|
|
|
78
|
|
|
|
|
14084
|
|
33
|
|
|
|
|
|
|
our @EXPORT_OK = qw(uncache); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
our $VERSION = '1.29'; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
#Get constants, but without the bloat of |
38
|
|
|
|
|
|
|
#use Fcntl qw(LOCK_SH LOCK_EX LOCK_NB); |
39
|
|
|
|
|
|
|
use constant { |
40
|
78
|
|
|
|
|
193793
|
LOCK_SH => 1, |
41
|
|
|
|
|
|
|
LOCK_EX => 2, |
42
|
|
|
|
|
|
|
LOCK_NB => 4, |
43
|
78
|
|
|
78
|
|
570
|
}; |
|
78
|
|
|
|
|
158
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
### Convert lock_type to a number |
46
|
|
|
|
|
|
|
our $TYPES = { |
47
|
|
|
|
|
|
|
BLOCKING => LOCK_EX, |
48
|
|
|
|
|
|
|
BL => LOCK_EX, |
49
|
|
|
|
|
|
|
EXCLUSIVE => LOCK_EX, |
50
|
|
|
|
|
|
|
EX => LOCK_EX, |
51
|
|
|
|
|
|
|
NONBLOCKING => LOCK_EX | LOCK_NB, |
52
|
|
|
|
|
|
|
NB => LOCK_EX | LOCK_NB, |
53
|
|
|
|
|
|
|
SHARED => LOCK_SH, |
54
|
|
|
|
|
|
|
SH => LOCK_SH, |
55
|
|
|
|
|
|
|
}; |
56
|
|
|
|
|
|
|
our $LOCK_EXTENSION = '.NFSLock'; # customizable extension |
57
|
|
|
|
|
|
|
our $HOSTNAME = undef; |
58
|
|
|
|
|
|
|
our $SHARE_BIT = 1; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
###----------------------------------------------------------------### |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $graceful_sig = sub { |
63
|
|
|
|
|
|
|
print STDERR "Received SIG$_[0]\n" if @_; |
64
|
|
|
|
|
|
|
# Perl's exit should safely DESTROY any objects |
65
|
|
|
|
|
|
|
# still "alive" before calling the real _exit(). |
66
|
|
|
|
|
|
|
exit 1; |
67
|
|
|
|
|
|
|
}; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
our @CATCH_SIGS = qw(TERM INT); |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub new { |
72
|
1167
|
|
|
1167
|
0
|
82523906
|
$errstr = undef; |
73
|
|
|
|
|
|
|
|
74
|
1167
|
|
|
|
|
3855
|
my $type = shift; |
75
|
1167
|
|
50
|
|
|
9753
|
my $class = ref($type) || $type || __PACKAGE__; |
76
|
1167
|
|
|
|
|
3211
|
my $self = {}; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
### allow for arguments by hash ref or serially |
79
|
1167
|
100
|
66
|
|
|
7406
|
if( @_ && ref $_[0] ){ |
80
|
1134
|
|
|
|
|
2708
|
$self = shift; |
81
|
|
|
|
|
|
|
}else{ |
82
|
33
|
|
|
|
|
287
|
$self->{file} = shift; |
83
|
33
|
|
|
|
|
252
|
$self->{lock_type} = shift; |
84
|
33
|
|
|
|
|
196
|
$self->{blocking_timeout} = shift; |
85
|
33
|
|
|
|
|
185
|
$self->{stale_lock_timeout} = shift; |
86
|
|
|
|
|
|
|
} |
87
|
1167
|
|
50
|
|
|
3755
|
$self->{file} ||= ""; |
88
|
1167
|
|
50
|
|
|
3723
|
$self->{lock_type} ||= 0; |
89
|
1167
|
|
100
|
|
|
5502
|
$self->{blocking_timeout} ||= 0; |
90
|
1167
|
|
100
|
|
|
6019
|
$self->{stale_lock_timeout} ||= 0; |
91
|
1167
|
|
|
|
|
4745
|
$self->{lock_pid} = $$; |
92
|
1167
|
|
|
|
|
5443
|
$self->{unlocked} = 1; |
93
|
1167
|
|
|
|
|
4398
|
foreach my $signal (@CATCH_SIGS) { |
94
|
2334
|
100
|
66
|
|
|
10171
|
if (!$SIG{$signal} || |
95
|
|
|
|
|
|
|
$SIG{$signal} eq "DEFAULT") { |
96
|
2246
|
|
|
|
|
34310
|
$SIG{$signal} = $graceful_sig; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
### force lock_type to be numerical |
101
|
1167
|
50
|
33
|
|
|
13992
|
if( $self->{lock_type} && |
|
|
|
33
|
|
|
|
|
102
|
|
|
|
|
|
|
$self->{lock_type} !~ /^\d+/ && |
103
|
|
|
|
|
|
|
exists $TYPES->{$self->{lock_type}} ){ |
104
|
0
|
|
|
|
|
0
|
$self->{lock_type} = $TYPES->{$self->{lock_type}}; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
### need the hostname |
108
|
1167
|
100
|
|
|
|
3173
|
if( !$HOSTNAME ){ |
109
|
68
|
|
|
|
|
74569
|
require Sys::Hostname; |
110
|
68
|
|
|
|
|
118772
|
$HOSTNAME = Sys::Hostname::hostname(); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
### quick usage check |
114
|
|
|
|
|
|
|
croak ($errstr = "Usage: my \$f = $class->new('/pathtofile/file',\n" |
115
|
|
|
|
|
|
|
."'BLOCKING|EXCLUSIVE|NONBLOCKING|SHARED', [blocking_timeout, stale_lock_timeout]);\n" |
116
|
|
|
|
|
|
|
."(You passed \"$self->{file}\" and \"$self->{lock_type}\")") |
117
|
1167
|
50
|
|
|
|
4462
|
unless length($self->{file}); |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
croak ($errstr = "Unrecognized lock_type operation setting [$self->{lock_type}]") |
120
|
1167
|
50
|
33
|
|
|
8829
|
unless $self->{lock_type} && $self->{lock_type} =~ /^\d+$/; |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
### Input syntax checking passed, ready to bless |
123
|
1167
|
|
|
|
|
3037
|
bless $self, $class; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
### choose a random filename |
126
|
1167
|
|
|
|
|
3511
|
$self->{rand_file} = rand_file( $self->{file} ); |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
### choose the lock filename |
129
|
1167
|
|
|
|
|
4640
|
$self->{lock_file} = $self->{file} . $LOCK_EXTENSION; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
my $quit_time = $self->{blocking_timeout} && |
132
|
|
|
|
|
|
|
!($self->{lock_type} & LOCK_NB) ? |
133
|
1167
|
100
|
66
|
|
|
4522
|
time() + $self->{blocking_timeout} : 0; |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
### remove an old lockfile if it is older than the stale_timeout |
136
|
1167
|
50
|
100
|
|
|
22996
|
if( -e $self->{lock_file} && |
|
|
|
66
|
|
|
|
|
137
|
|
|
|
|
|
|
$self->{stale_lock_timeout} > 0 && |
138
|
|
|
|
|
|
|
time() - (stat _)[9] > $self->{stale_lock_timeout} ){ |
139
|
0
|
|
|
|
|
0
|
unlink $self->{lock_file}; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
1167
|
|
|
|
|
2909
|
while (1) { |
143
|
|
|
|
|
|
|
### open the temporary file |
144
|
1373
|
50
|
|
|
|
6039
|
$self->create_magic |
145
|
|
|
|
|
|
|
or return undef; |
146
|
|
|
|
|
|
|
|
147
|
1373
|
100
|
|
|
|
5331
|
if ( $self->{lock_type} & LOCK_EX ) { |
|
|
50
|
|
|
|
|
|
148
|
1344
|
100
|
|
|
|
4291
|
last if $self->do_lock; |
149
|
|
|
|
|
|
|
} elsif ( $self->{lock_type} & LOCK_SH ) { |
150
|
29
|
100
|
|
|
|
125
|
last if $self->do_lock_shared; |
151
|
|
|
|
|
|
|
} else { |
152
|
0
|
|
|
|
|
0
|
$errstr = "Unknown lock_type [$self->{lock_type}]"; |
153
|
0
|
|
|
|
|
0
|
return undef; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
### Lock failed! |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
### I know this may be a race condition, but it's okay. It is just a |
159
|
|
|
|
|
|
|
### stab in the dark to possibly find long dead processes. |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
### If lock exists and is readable, see who is mooching on the lock |
162
|
|
|
|
|
|
|
|
163
|
218
|
|
|
|
|
572
|
my $fh; |
164
|
218
|
100
|
100
|
|
|
10711
|
if ( -e $self->{lock_file} && |
165
|
|
|
|
|
|
|
open ($fh,'+<', $self->{lock_file}) ){ |
166
|
|
|
|
|
|
|
|
167
|
166
|
|
|
|
|
736
|
my @mine = (); |
168
|
166
|
|
|
|
|
407
|
my @them = (); |
169
|
166
|
|
|
|
|
385
|
my @dead = (); |
170
|
|
|
|
|
|
|
|
171
|
166
|
|
|
|
|
858
|
my $has_lock_exclusive = !((stat _)[2] & $SHARE_BIT); |
172
|
166
|
|
|
|
|
584
|
my $try_lock_exclusive = !($self->{lock_type} & LOCK_SH); |
173
|
|
|
|
|
|
|
|
174
|
166
|
|
|
|
|
2850
|
while(defined(my $line=<$fh>)){ |
175
|
166
|
50
|
|
|
|
4476
|
if ($line =~ /^\Q$HOSTNAME\E (-?\d+) /) { |
176
|
166
|
|
|
|
|
1373
|
my $pid = $1; |
177
|
166
|
100
|
|
|
|
2770
|
if ($pid == $$) { # This is me. |
|
|
100
|
|
|
|
|
|
178
|
1
|
|
|
|
|
13
|
push @mine, $line; |
179
|
|
|
|
|
|
|
}elsif(kill 0, $pid) { # Still running on this host. |
180
|
163
|
|
|
|
|
2328
|
push @them, $line; |
181
|
|
|
|
|
|
|
}else{ # Finished running on this host. |
182
|
2
|
|
|
|
|
29
|
push @dead, $line; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
} else { # Running on another host, so |
185
|
0
|
|
|
|
|
0
|
push @them, $line; # assume it is still running. |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
### If there was at least one stale lock discovered... |
190
|
166
|
100
|
|
|
|
881
|
if (@dead) { |
191
|
|
|
|
|
|
|
# Lock lock_file to avoid a race condition. |
192
|
2
|
|
|
|
|
18
|
local $LOCK_EXTENSION = ".shared"; |
193
|
|
|
|
|
|
|
my $lock = new File::NFSLock { |
194
|
|
|
|
|
|
|
file => $self->{lock_file}, |
195
|
2
|
|
|
|
|
66
|
lock_type => LOCK_EX, |
196
|
|
|
|
|
|
|
blocking_timeout => 62, |
197
|
|
|
|
|
|
|
stale_lock_timeout => 60, |
198
|
|
|
|
|
|
|
}; |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
### Rescan in case lock contents were modified between time stale lock |
201
|
|
|
|
|
|
|
### was discovered and lockfile lock was acquired. |
202
|
2
|
|
|
|
|
23
|
seek ($fh, 0, 0); |
203
|
2
|
|
|
|
|
14
|
my $content = ''; |
204
|
2
|
|
|
|
|
26
|
while(defined(my $line=<$fh>)){ |
205
|
2
|
50
|
|
|
|
73
|
if ($line =~ /^\Q$HOSTNAME\E (-?\d+) /) { |
206
|
2
|
|
|
|
|
10
|
my $pid = $1; |
207
|
2
|
50
|
|
|
|
45
|
next if (!kill 0, $pid); # Skip dead locks from this host |
208
|
|
|
|
|
|
|
} |
209
|
0
|
|
|
|
|
0
|
$content .= $line; # Save valid locks |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
### Save any valid locks or wipe file. |
213
|
2
|
50
|
|
|
|
12
|
if( length($content) ){ |
214
|
0
|
|
|
|
|
0
|
seek $fh, 0, 0; |
215
|
0
|
|
|
|
|
0
|
print $fh $content; |
216
|
0
|
|
|
|
|
0
|
truncate $fh, length($content); |
217
|
0
|
|
|
|
|
0
|
close $fh; |
218
|
|
|
|
|
|
|
}else{ |
219
|
2
|
|
|
|
|
18
|
close $fh; |
220
|
2
|
|
|
|
|
96
|
unlink $self->{lock_file}; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
### No "dead" or stale locks found. |
224
|
|
|
|
|
|
|
} else { |
225
|
164
|
|
|
|
|
2903
|
close $fh; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
### If attempting to acquire the same type of lock |
229
|
|
|
|
|
|
|
### that it is already locked with, and I've already |
230
|
|
|
|
|
|
|
### locked it myself, then it is safe to lock again. |
231
|
|
|
|
|
|
|
### Just kick out successfully without really locking. |
232
|
|
|
|
|
|
|
### Assumes locks will be released in the reverse |
233
|
|
|
|
|
|
|
### order from how they were established. |
234
|
166
|
100
|
100
|
|
|
1695
|
if ($try_lock_exclusive eq $has_lock_exclusive && @mine){ |
235
|
1
|
|
|
|
|
8
|
return $self; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
### If non-blocking, then kick out now. |
240
|
|
|
|
|
|
|
### ($errstr might already be set to the reason.) |
241
|
217
|
100
|
|
|
|
1056
|
if ($self->{lock_type} & LOCK_NB) { |
242
|
11
|
|
50
|
|
|
139
|
$errstr ||= "NONBLOCKING lock failed!"; |
243
|
11
|
|
|
|
|
85
|
return undef; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
### wait a moment |
247
|
206
|
|
|
|
|
206047494
|
sleep(1); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
### but don't wait past the time out |
250
|
206
|
50
|
66
|
|
|
5596
|
if( $quit_time && (time > $quit_time) ){ |
251
|
0
|
|
|
|
|
0
|
$errstr = "Timed out waiting for blocking lock"; |
252
|
0
|
|
|
|
|
0
|
return undef; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# BLOCKING Lock, So Keep Trying |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
### clear up the NFS cache |
259
|
1155
|
|
|
|
|
5762
|
$self->uncache; |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
### Yes, the lock has been acquired. |
262
|
1155
|
|
|
|
|
4827
|
delete $self->{unlocked}; |
263
|
|
|
|
|
|
|
|
264
|
1155
|
|
|
|
|
4130
|
return $self; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub DESTROY { |
268
|
1167
|
|
|
1167
|
|
36093930
|
shift()->unlock(); |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub unlock ($) { |
272
|
1187
|
|
|
1187
|
1
|
100015469
|
my $self = shift; |
273
|
1187
|
100
|
|
|
|
3656
|
if (!$self->{unlocked}) { |
274
|
1155
|
50
|
|
|
|
19555
|
unlink( $self->{rand_file} ) if -e $self->{rand_file}; |
275
|
1155
|
100
|
|
|
|
4797
|
if( $self->{lock_type} & LOCK_SH ){ |
276
|
33
|
|
|
|
|
355
|
$self->do_unlock_shared; |
277
|
|
|
|
|
|
|
}else{ |
278
|
1122
|
|
|
|
|
3230
|
$self->do_unlock; |
279
|
|
|
|
|
|
|
} |
280
|
1155
|
|
|
|
|
4727
|
$self->{unlocked} = 1; |
281
|
1155
|
|
|
|
|
3383
|
foreach my $signal (@CATCH_SIGS) { |
282
|
2310
|
100
|
66
|
|
|
16101
|
if ($SIG{$signal} && |
283
|
|
|
|
|
|
|
($SIG{$signal} eq $graceful_sig)) { |
284
|
|
|
|
|
|
|
# Revert handler back to how it used to be. |
285
|
|
|
|
|
|
|
# Unfortunately, this will restore the |
286
|
|
|
|
|
|
|
# handler back even if there are other |
287
|
|
|
|
|
|
|
# locks still in tact, but for most cases, |
288
|
|
|
|
|
|
|
# it will still be an improvement. |
289
|
2240
|
|
|
|
|
32439
|
delete $SIG{$signal}; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
} |
293
|
1187
|
|
|
|
|
17449
|
return 1; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
###----------------------------------------------------------------### |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# concepts for these routines were taken from Mail::Box which |
299
|
|
|
|
|
|
|
# took the concepts from Mail::Folder |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub rand_file ($) { |
303
|
2322
|
|
|
2322
|
0
|
4179
|
my $file = shift; |
304
|
2322
|
|
|
|
|
17810
|
"$file.tmp.". time()%10000 .'.'. $$ .'.'. int(rand()*10000); |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub create_magic ($;$) { |
308
|
1401
|
|
|
1401
|
0
|
2965
|
$errstr = undef; |
309
|
1401
|
|
|
|
|
2606
|
my $self = shift; |
310
|
1401
|
|
66
|
|
|
7406
|
my $append_file = shift || $self->{rand_file}; |
311
|
1401
|
|
66
|
|
|
11611
|
$self->{lock_line} ||= "$HOSTNAME $self->{lock_pid} ".time()." ".int(rand()*10000)."\n"; |
312
|
1401
|
50
|
|
|
|
106237
|
open (my $fh,'>>', $append_file) or do { $errstr = "Couldn't open \"$append_file\" [$!]"; return undef; }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
313
|
1401
|
|
|
|
|
12078
|
print $fh $self->{lock_line}; |
314
|
1401
|
|
|
|
|
45362
|
close $fh; |
315
|
1401
|
|
|
|
|
10581
|
return 1; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
sub do_lock { |
319
|
1344
|
|
|
1344
|
0
|
2645
|
$errstr = undef; |
320
|
1344
|
|
|
|
|
2573
|
my $self = shift; |
321
|
1344
|
|
|
|
|
2628
|
my $lock_file = $self->{lock_file}; |
322
|
1344
|
|
|
|
|
2247
|
my $rand_file = $self->{rand_file}; |
323
|
1344
|
|
|
|
|
2125
|
my $chmod = 0600; |
324
|
1344
|
50
|
|
|
|
23139
|
chmod( $chmod, $rand_file) |
325
|
|
|
|
|
|
|
|| die "I need ability to chmod files to adequatetly perform locking"; |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
### try a hard link, if it worked |
328
|
|
|
|
|
|
|
### two files are pointing to $rand_file |
329
|
1344
|
|
66
|
|
|
46148
|
my $success = link( $rand_file, $lock_file ) |
330
|
|
|
|
|
|
|
&& -e $rand_file && (stat _)[3] == 2; |
331
|
1344
|
|
|
|
|
38895
|
unlink $rand_file; |
332
|
|
|
|
|
|
|
|
333
|
1344
|
|
|
|
|
8905
|
return $success; |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub do_lock_shared { |
337
|
29
|
|
|
29
|
0
|
56
|
$errstr = undef; |
338
|
29
|
|
|
|
|
61
|
my $self = shift; |
339
|
29
|
|
|
|
|
66
|
my $lock_file = $self->{lock_file}; |
340
|
29
|
|
|
|
|
78
|
my $rand_file = $self->{rand_file}; |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
### chmod local file to make sure we know before |
343
|
29
|
|
|
|
|
46
|
my $chmod = 0600; |
344
|
29
|
|
|
|
|
54
|
$chmod |= $SHARE_BIT; |
345
|
29
|
50
|
|
|
|
590
|
chmod( $chmod, $rand_file) |
346
|
|
|
|
|
|
|
|| die "I need ability to chmod files to adequatetly perform locking"; |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
### lock the locking process |
349
|
29
|
|
|
|
|
389
|
local $LOCK_EXTENSION = ".shared"; |
350
|
29
|
|
|
|
|
658
|
my $lock = new File::NFSLock { |
351
|
|
|
|
|
|
|
file => $lock_file, |
352
|
|
|
|
|
|
|
lock_type => LOCK_EX, |
353
|
|
|
|
|
|
|
blocking_timeout => 62, |
354
|
|
|
|
|
|
|
stale_lock_timeout => 60, |
355
|
|
|
|
|
|
|
}; |
356
|
|
|
|
|
|
|
# The ".shared" lock will be released as this status |
357
|
|
|
|
|
|
|
# is returned, whether or not the status is successful. |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
### If I didn't have exclusive and the shared bit is not |
360
|
|
|
|
|
|
|
### set, I have failed |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
### Try to create $lock_file from the special |
363
|
|
|
|
|
|
|
### file with the magic $SHARE_BIT set. |
364
|
29
|
|
|
|
|
409
|
my $success = link( $rand_file, $lock_file); |
365
|
29
|
|
|
|
|
1201
|
unlink $rand_file; |
366
|
29
|
100
|
66
|
|
|
1183
|
if ( !$success && |
|
|
100
|
100
|
|
|
|
|
367
|
|
|
|
|
|
|
-e $lock_file && |
368
|
|
|
|
|
|
|
((stat _)[2] & $SHARE_BIT) != $SHARE_BIT ){ |
369
|
|
|
|
|
|
|
|
370
|
2
|
|
|
|
|
15
|
$errstr = 'Exclusive lock exists.'; |
371
|
2
|
|
|
|
|
17
|
return undef; |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
} elsif ( !$success ) { |
374
|
|
|
|
|
|
|
### Shared lock exists, append my lock |
375
|
20
|
|
|
|
|
159
|
$self->create_magic ($self->{lock_file}); |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# Success |
379
|
27
|
|
|
|
|
212
|
return 1; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub do_unlock ($) { |
383
|
1122
|
|
|
1122
|
0
|
42681
|
return unlink shift->{lock_file}; |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
sub do_unlock_shared ($) { |
387
|
33
|
|
|
33
|
0
|
223
|
$errstr = undef; |
388
|
33
|
|
|
|
|
175
|
my $self = shift; |
389
|
33
|
|
|
|
|
189
|
my $lock_file = $self->{lock_file}; |
390
|
33
|
|
|
|
|
201
|
my $lock_line = $self->{lock_line}; |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
### lock the locking process |
393
|
33
|
|
|
|
|
630
|
local $LOCK_EXTENSION = '.shared'; |
394
|
33
|
|
|
|
|
903
|
my $lock = new File::NFSLock ($lock_file,LOCK_EX,62,60); |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
### get the handle on the lock file |
397
|
33
|
|
|
|
|
168
|
my $fh; |
398
|
33
|
50
|
|
|
|
1387
|
if( ! open ($fh,'+<', $lock_file) ){ |
399
|
0
|
0
|
|
|
|
0
|
if( ! -e $lock_file ){ |
400
|
0
|
|
|
|
|
0
|
return 1; |
401
|
|
|
|
|
|
|
}else{ |
402
|
0
|
|
|
|
|
0
|
die "Could not open for writing shared lock file $lock_file ($!)"; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
### read existing file |
407
|
33
|
|
|
|
|
175
|
my $content = ''; |
408
|
33
|
|
|
|
|
823
|
while(defined(my $line=<$fh>)){ |
409
|
251
|
100
|
|
|
|
814
|
next if $line eq $lock_line; |
410
|
218
|
|
|
|
|
931
|
$content .= $line; |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
### other shared locks exist |
414
|
33
|
100
|
|
|
|
296
|
if( length($content) ){ |
415
|
28
|
|
|
|
|
281
|
seek $fh, 0, 0; |
416
|
28
|
|
|
|
|
224
|
print $fh $content; |
417
|
28
|
|
|
|
|
1540
|
truncate $fh, length($content); |
418
|
28
|
|
|
|
|
905
|
close $fh; |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
### only I exist |
421
|
|
|
|
|
|
|
}else{ |
422
|
5
|
|
|
|
|
60
|
close $fh; |
423
|
5
|
|
|
|
|
503
|
unlink $lock_file; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
sub uncache ($;$) { |
429
|
|
|
|
|
|
|
# allow as method call |
430
|
1155
|
|
|
1155
|
1
|
2309
|
my $file = pop; |
431
|
1155
|
50
|
|
|
|
4194
|
ref $file && ($file = $file->{file}); |
432
|
1155
|
|
|
|
|
2550
|
my $rand_file = rand_file( $file ); |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
### hard link to the actual file which will bring it up to date |
435
|
1155
|
|
66
|
|
|
64325
|
return ( link( $file, $rand_file) && unlink($rand_file) ); |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub newpid { |
439
|
12
|
|
|
12
|
1
|
8014559
|
my $self = shift; |
440
|
|
|
|
|
|
|
# Detect if this is the parent or the child |
441
|
12
|
100
|
|
|
|
506
|
if ($self->{lock_pid} == $$) { |
442
|
|
|
|
|
|
|
# This is the parent |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# Must wait for child to call newpid before processing. |
445
|
|
|
|
|
|
|
# A little patience for the child to call newpid |
446
|
4
|
|
|
|
|
37
|
my $patience = time + 10; |
447
|
4
|
|
|
|
|
75
|
while (time < $patience) { |
448
|
46
|
100
|
|
|
|
2482
|
if (rename("$self->{lock_file}.fork",$self->{rand_file})) { |
449
|
|
|
|
|
|
|
# Child finished its newpid call. |
450
|
|
|
|
|
|
|
# Wipe the signal file. |
451
|
4
|
|
|
|
|
255
|
unlink $self->{rand_file}; |
452
|
4
|
|
|
|
|
71
|
last; |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
# Brief pause before checking again |
455
|
|
|
|
|
|
|
# to avoid intensive IO across NFS. |
456
|
42
|
|
|
|
|
4210402
|
select(undef,undef,undef,0.1); |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# Child finished running newpid() and acquired shared lock |
460
|
|
|
|
|
|
|
# So now we're safe to continue without risk of |
461
|
|
|
|
|
|
|
# blowing away the lock prematurely. |
462
|
4
|
100
|
|
|
|
131
|
unless ( $self->{lock_type} & LOCK_SH ) { |
463
|
|
|
|
|
|
|
# If it's not already a SHared lock, then |
464
|
|
|
|
|
|
|
# just switch it from EXclusive to SHared |
465
|
|
|
|
|
|
|
# from this process's point of view. |
466
|
|
|
|
|
|
|
# Then the child will still hold the lock |
467
|
|
|
|
|
|
|
# if the parent releases it first. |
468
|
|
|
|
|
|
|
# (Don't chmod the lock file.) |
469
|
2
|
|
|
|
|
64
|
$self->{lock_type} |= LOCK_SH; |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
} else { |
472
|
|
|
|
|
|
|
# This is the new child |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# Fix lock_pid to the new pid. |
475
|
8
|
|
|
|
|
171
|
$self->{lock_pid} = $$; |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
# We can leave the old lock_line in the lock_file |
478
|
|
|
|
|
|
|
# But we need to add the new lock_line for this pid. |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# Clear lock_line to create a fresh one. |
481
|
8
|
|
|
|
|
434
|
delete $self->{lock_line}; |
482
|
|
|
|
|
|
|
# Append a new lock_line to the lock_file. |
483
|
8
|
|
|
|
|
317
|
$self->create_magic($self->{lock_file}); |
484
|
|
|
|
|
|
|
|
485
|
8
|
100
|
|
|
|
157
|
unless ( $self->{lock_type} & LOCK_SH ) { |
486
|
|
|
|
|
|
|
# If it's not already a SHared lock, then |
487
|
|
|
|
|
|
|
# just switch it from EXclusive to SHared |
488
|
|
|
|
|
|
|
# from this process's point of view. |
489
|
|
|
|
|
|
|
# Then the parent will still hold the lock |
490
|
|
|
|
|
|
|
# if this child releases it first. |
491
|
|
|
|
|
|
|
# (Don't chmod the lock file.) |
492
|
4
|
|
|
|
|
56
|
$self->{lock_type} |= LOCK_SH; |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# Create signal file to notify parent that |
496
|
|
|
|
|
|
|
# the lock_line entry has been delegated. |
497
|
8
|
|
|
|
|
663
|
open (my $fh, '>', "$self->{lock_file}.fork"); |
498
|
8
|
|
|
|
|
222
|
close($fh); |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
sub fork { |
503
|
6
|
|
|
6
|
1
|
1838
|
my $self = shift; |
504
|
|
|
|
|
|
|
# Store fork response. |
505
|
6
|
|
|
|
|
5297
|
my $pid = CORE::fork(); |
506
|
6
|
50
|
33
|
|
|
646
|
if (defined $pid and !$self->{unlocked}) { |
507
|
|
|
|
|
|
|
# Fork worked and we really have a lock to deal with |
508
|
|
|
|
|
|
|
# So upgrade to shared lock across both parent and child |
509
|
6
|
|
|
|
|
192
|
$self->newpid; |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
# Return original fork response |
512
|
6
|
|
|
|
|
187
|
return $pid; |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
1; |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=pod |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=head1 NAME |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
File::NFSLock - perl module to do NFS (or not) locking |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head1 SYNOPSIS |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
use File::NFSLock qw(uncache); |
527
|
|
|
|
|
|
|
use Fcntl qw(LOCK_EX LOCK_NB); |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
my $file = "somefile"; |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
### set up a lock - lasts until object looses scope |
532
|
|
|
|
|
|
|
if (my $lock = new File::NFSLock { |
533
|
|
|
|
|
|
|
file => $file, |
534
|
|
|
|
|
|
|
lock_type => LOCK_EX|LOCK_NB, |
535
|
|
|
|
|
|
|
blocking_timeout => 10, # 10 sec |
536
|
|
|
|
|
|
|
stale_lock_timeout => 30 * 60, # 30 min |
537
|
|
|
|
|
|
|
}) { |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
### OR |
540
|
|
|
|
|
|
|
### my $lock = File::NFSLock->new($file,LOCK_EX|LOCK_NB,10,30*60); |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
### do write protected stuff on $file |
543
|
|
|
|
|
|
|
### at this point $file is uncached from NFS (most recent) |
544
|
|
|
|
|
|
|
open(FILE, "+<$file") || die $!; |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
### or open it any way you like |
547
|
|
|
|
|
|
|
### my $fh = IO::File->open( $file, 'w' ) || die $! |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
### update (uncache across NFS) other files |
550
|
|
|
|
|
|
|
uncache("someotherfile1"); |
551
|
|
|
|
|
|
|
uncache("someotherfile2"); |
552
|
|
|
|
|
|
|
# open(FILE2,"someotherfile1"); |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
### unlock it |
555
|
|
|
|
|
|
|
$lock->unlock(); |
556
|
|
|
|
|
|
|
### OR |
557
|
|
|
|
|
|
|
### undef $lock; |
558
|
|
|
|
|
|
|
### OR let $lock go out of scope |
559
|
|
|
|
|
|
|
}else{ |
560
|
|
|
|
|
|
|
die "I couldn't lock the file [$File::NFSLock::errstr]"; |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=head1 DESCRIPTION |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
Program based of concept of hard linking of files being atomic across |
567
|
|
|
|
|
|
|
NFS. This concept was mentioned in Mail::Box::Locker (which was |
568
|
|
|
|
|
|
|
originally presented in Mail::Folder::Maildir). Some routine flow is |
569
|
|
|
|
|
|
|
taken from there -- particularly the idea of creating a random local |
570
|
|
|
|
|
|
|
file, hard linking a common file to the local file, and then checking |
571
|
|
|
|
|
|
|
the nlink status. Some ideologies were not complete (uncache |
572
|
|
|
|
|
|
|
mechanism, shared locking) and some coding was even incorrect (wrong |
573
|
|
|
|
|
|
|
stat index). File::NFSLock was written to be light, generic, |
574
|
|
|
|
|
|
|
and fast. |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
=head1 USAGE |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
Locking occurs by creating a File::NFSLock object. If the object |
580
|
|
|
|
|
|
|
is created successfully, a lock is currently in place and remains in |
581
|
|
|
|
|
|
|
place until the lock object goes out of scope (or calls the unlock |
582
|
|
|
|
|
|
|
method). |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
A lock object is created by calling the new method and passing two |
585
|
|
|
|
|
|
|
to four parameters in the following manner: |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
my $lock = File::NFSLock->new($file, |
588
|
|
|
|
|
|
|
$lock_type, |
589
|
|
|
|
|
|
|
$blocking_timeout, |
590
|
|
|
|
|
|
|
$stale_lock_timeout, |
591
|
|
|
|
|
|
|
); |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
Additionally, parameters may be passed as a hashref: |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
my $lock = File::NFSLock->new({ |
596
|
|
|
|
|
|
|
file => $file, |
597
|
|
|
|
|
|
|
lock_type => $lock_type, |
598
|
|
|
|
|
|
|
blocking_timeout => $blocking_timeout, |
599
|
|
|
|
|
|
|
stale_lock_timeout => $stale_lock_timeout, |
600
|
|
|
|
|
|
|
}); |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=head1 PARAMETERS |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
=over 4 |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=item Parameter 1: file |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
Filename of the file upon which it is anticipated that a write will |
609
|
|
|
|
|
|
|
happen to. Locking will provide the most recent version (uncached) |
610
|
|
|
|
|
|
|
of this file upon a successful file lock. It is not necessary |
611
|
|
|
|
|
|
|
for this file to exist. |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=item Parameter 2: lock_type |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
Lock type must be one of the following: |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
BLOCKING |
618
|
|
|
|
|
|
|
BL |
619
|
|
|
|
|
|
|
EXCLUSIVE (BLOCKING) |
620
|
|
|
|
|
|
|
EX |
621
|
|
|
|
|
|
|
NONBLOCKING |
622
|
|
|
|
|
|
|
NB |
623
|
|
|
|
|
|
|
SHARED |
624
|
|
|
|
|
|
|
SH |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
Or else one or more of the following joined with '|': |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
Fcntl::LOCK_EX() (BLOCKING) |
629
|
|
|
|
|
|
|
Fcntl::LOCK_NB() (NONBLOCKING) |
630
|
|
|
|
|
|
|
Fcntl::LOCK_SH() (SHARED) |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
Lock type determines whether the lock will be blocking, non blocking, |
633
|
|
|
|
|
|
|
or shared. Blocking locks will wait until other locks are removed |
634
|
|
|
|
|
|
|
before the process continues. Non blocking locks will return undef if |
635
|
|
|
|
|
|
|
another process currently has the lock. Shared will allow other |
636
|
|
|
|
|
|
|
process to do a shared lock at the same time as long as there is not |
637
|
|
|
|
|
|
|
already an exclusive lock obtained. |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=item Parameter 3: blocking_timeout (optional) |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
Timeout is used in conjunction with a blocking timeout. If specified, |
642
|
|
|
|
|
|
|
File::NFSLock will block up to the number of seconds specified in |
643
|
|
|
|
|
|
|
timeout before returning undef (could not get a lock). |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=item Parameter 4: stale_lock_timeout (optional) |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
Timeout is used to see if an existing lock file is older than the stale |
649
|
|
|
|
|
|
|
lock timeout. If do_lock fails to get a lock, the modified time is checked |
650
|
|
|
|
|
|
|
and do_lock is attempted again. If the stale_lock_timeout is set to low, a |
651
|
|
|
|
|
|
|
recursion load could exist so do_lock will only recurse 10 times (this is only |
652
|
|
|
|
|
|
|
a problem if the stale_lock_timeout is set too low -- on the order of one or two |
653
|
|
|
|
|
|
|
seconds). |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=back |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=head1 METHODS |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
After the $lock object is instantiated with new, |
660
|
|
|
|
|
|
|
as outlined above, some methods may be used for |
661
|
|
|
|
|
|
|
additional functionality. |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
=head2 unlock |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
$lock->unlock; |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
This method may be used to explicitly release a lock |
668
|
|
|
|
|
|
|
that is acquired. In most cases, it is not necessary |
669
|
|
|
|
|
|
|
to call unlock directly since it will implicitly be |
670
|
|
|
|
|
|
|
called when the object leaves whatever scope it is in. |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=head2 uncache |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
$lock->uncache; |
675
|
|
|
|
|
|
|
$lock->uncache("otherfile1"); |
676
|
|
|
|
|
|
|
uncache("otherfile2"); |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
This method is used to freshen up the contents of a |
679
|
|
|
|
|
|
|
file across NFS, ignoring what is contained in the |
680
|
|
|
|
|
|
|
NFS client cache. It is always called from within |
681
|
|
|
|
|
|
|
the new constructor on the file that the lock is |
682
|
|
|
|
|
|
|
being attempted. uncache may be used as either an |
683
|
|
|
|
|
|
|
object method or as a stand alone subroutine. |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=head2 fork |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
my $pid = $lock->fork; |
688
|
|
|
|
|
|
|
if (!defined $pid) { |
689
|
|
|
|
|
|
|
# Fork Failed |
690
|
|
|
|
|
|
|
} elsif ($pid) { |
691
|
|
|
|
|
|
|
# Parent ... |
692
|
|
|
|
|
|
|
} else { |
693
|
|
|
|
|
|
|
# Child ... |
694
|
|
|
|
|
|
|
} |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
fork() is a convenience method that acts just like the normal |
697
|
|
|
|
|
|
|
CORE::fork() except it safely ensures the lock is retained |
698
|
|
|
|
|
|
|
within both parent and child processes. WITHOUT this, then when |
699
|
|
|
|
|
|
|
either the parent or child process releases the lock, then the |
700
|
|
|
|
|
|
|
entire lock will be lost, allowing external processes to |
701
|
|
|
|
|
|
|
re-acquire a lock on the same file, even if the other process |
702
|
|
|
|
|
|
|
still has the lock object in scope. This can cause corruption |
703
|
|
|
|
|
|
|
since both processes might think they have exclusive access to |
704
|
|
|
|
|
|
|
the file. |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=head2 newpid |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
my $pid = fork; |
709
|
|
|
|
|
|
|
if (!defined $pid) { |
710
|
|
|
|
|
|
|
# Fork Failed |
711
|
|
|
|
|
|
|
} elsif ($pid) { |
712
|
|
|
|
|
|
|
$lock->newpid; |
713
|
|
|
|
|
|
|
# Parent ... |
714
|
|
|
|
|
|
|
} else { |
715
|
|
|
|
|
|
|
$lock->newpid; |
716
|
|
|
|
|
|
|
# Child ... |
717
|
|
|
|
|
|
|
} |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
The newpid() synopsis shown above is equivalent to the |
720
|
|
|
|
|
|
|
one used for the fork() method, but it's not intended |
721
|
|
|
|
|
|
|
to be called directly. It is called internally by the |
722
|
|
|
|
|
|
|
fork() method. To be safe, it is recommended to use |
723
|
|
|
|
|
|
|
$lock->fork() from now on. |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=head1 FAILURE |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
On failure, a global variable, $File::NFSLock::errstr, should be set and should |
728
|
|
|
|
|
|
|
contain the cause for the failure to get a lock. Useful primarily for debugging. |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=head1 LOCK_EXTENSION |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
By default File::NFSLock will use a lock file extension of ".NFSLock". This is |
733
|
|
|
|
|
|
|
in a global variable $File::NFSLock::LOCK_EXTENSION that may be changed to |
734
|
|
|
|
|
|
|
suit other purposes (such as compatibility in mail systems). |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=head1 REPO |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
The source is now on github: |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
git clone https://github.com/hookbot/File-NFSLock |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=head1 BUGS |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
If you spot anything, please submit a pull request on |
745
|
|
|
|
|
|
|
github and/or submit a ticket with RT: |
746
|
|
|
|
|
|
|
https://rt.cpan.org/Dist/Display.html?Queue=File-NFSLock |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
=head2 FIFO |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
Locks are not necessarily obtained on a first come first serve basis. |
751
|
|
|
|
|
|
|
Not only does this not seem fair to new processes trying to obtain a lock, |
752
|
|
|
|
|
|
|
but it may cause a process starvation condition on heavily locked files. |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=head2 DIRECTORIES |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
Locks cannot be obtained on directory nodes, nor can a directory node be |
757
|
|
|
|
|
|
|
uncached with the uncache routine because hard links do not work with |
758
|
|
|
|
|
|
|
directory nodes. Some other algorithm might be used to uncache a |
759
|
|
|
|
|
|
|
directory, but I am unaware of the best way to do it. The biggest use I |
760
|
|
|
|
|
|
|
can see would be to avoid NFS cache of directory modified and last accessed |
761
|
|
|
|
|
|
|
timestamps. |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
=head1 INSTALL |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
Download and extract tarball before running |
766
|
|
|
|
|
|
|
these commands in its base directory: |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
perl Makefile.PL |
769
|
|
|
|
|
|
|
make |
770
|
|
|
|
|
|
|
make test |
771
|
|
|
|
|
|
|
make install |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
For RPM installation, download tarball before |
774
|
|
|
|
|
|
|
running these commands in your _topdir: |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
rpm -ta SOURCES/File-NFSLock-*.tar.gz |
777
|
|
|
|
|
|
|
rpm -ih RPMS/noarch/perl-File-NFSLock-*.rpm |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
=head1 AUTHORS |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
Paul T Seamons (paul@seamons.com) - Performed majority of the |
782
|
|
|
|
|
|
|
programming with copious amounts of input from Rob Brown. |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
Rob B Brown (bbb@cpan.org) - In addition to helping in the |
785
|
|
|
|
|
|
|
programming, Rob Brown provided most of the core testing to make sure |
786
|
|
|
|
|
|
|
implementation worked properly. He is now the current maintainer. |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
Also Mark Overmeer (mark@overmeer.net) - Author of Mail::Box::Locker, |
789
|
|
|
|
|
|
|
from which some key concepts for File::NFSLock were taken. |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
Also Kevin Johnson (kjj@pobox.com) - Author of Mail::Folder::Maildir, |
792
|
|
|
|
|
|
|
from which Mark Overmeer based Mail::Box::Locker. |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
=head1 COPYRIGHT |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
Copyright (C) 2001 |
797
|
|
|
|
|
|
|
Paul T Seamons |
798
|
|
|
|
|
|
|
paul@seamons.com |
799
|
|
|
|
|
|
|
http://seamons.com/ |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
Copyright (C) 2002-2018, |
802
|
|
|
|
|
|
|
Rob B Brown |
803
|
|
|
|
|
|
|
bbb@cpan.org |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
This package may be distributed under the terms of either the |
806
|
|
|
|
|
|
|
GNU General Public License |
807
|
|
|
|
|
|
|
or the |
808
|
|
|
|
|
|
|
Perl Artistic License |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
All rights reserved. |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
=cut |