File Coverage

blib/lib/Lazy/Lockfile.pm
Criterion Covered Total %
statement 81 100 81.0
branch 20 40 50.0
condition 11 27 40.7
subroutine 10 11 90.9
pod 4 4 100.0
total 126 182 69.2


line stmt bran cond sub pod time code
1             package Lazy::Lockfile;
2              
3 1     1   13337 use strict;
  1         3  
  1         26  
4 1     1   4 use Fcntl qw/ :DEFAULT :flock /;
  1         2  
  1         263  
5 1     1   397 use POSIX qw/ :errno_h /;
  1         4916  
  1         4  
6 1     1   1355 use File::Basename;
  1         2  
  1         64  
7              
8 1     1   5 use vars qw( $VERSION );
  1         2  
  1         234  
9             ( $VERSION ) = '1.23';
10              
11             =head1 NAME
12              
13             Lazy::Lockfile - File based locking for the lazy.
14              
15             =head1 SYNOPSIS
16              
17             use Lazy::Lockfile;
18              
19             my $lockfile = Lazy::Lockfile->new() || die "Couldn't get lock!";
20             ...
21             # Lock is released when $lockfile goes out of scope or your program exits.
22              
23             =head1 DESCRIPTION
24              
25             Lazy::Lockfile is a module designed for simple locking through the use of
26             lockfiles, requiring very little effort on the part of the developer. Once the
27             object is instanced, the lock will be held as long as object exists. When the
28             object is destroyed, the lock is released.
29              
30             Locks are based around the existence of a named file, not around the use of
31             L (though flock is used to synchronize access to the lock file).
32             Lazy::Lockfile is (usually) smart enough to detect stale lockfiles from PIDs no
33             longer running by placing the PID of the process holding the lock inside the
34             lockfile.
35              
36             =head1 NOTES
37              
38             Lazy::Lockfile is not safe for use on NFS volumes.
39              
40             Lazy::Lockfile is not tested to interact correctly with other file locking
41             systems when used on the same lockfile.
42              
43             Lazy::Lockfile uses kill (with signal zero) to determine if the lockfile is
44             stale. This works on most systems running as most users but there are likely
45             instances where this will fail. If this applies to your system, you can use the
46             L option to disable the check.
47              
48             If Lazy::Lockfile encounters a malformed lockfile (empty, containing other
49             text, etc), it will treat it as a corrupt file and overwrite it, assuming the
50             lock. The author believes this behavior should be changed (and malformed files
51             should be left untouched), but has kept this behavior for backwards
52             compatibility.
53              
54             =head1 USAGE
55              
56             All of the magic in Lazy::Lockfile is done through the constructor and
57             destructor.
58              
59             =head1 METHODS
60              
61             =head2 new
62              
63             Constructor for Lazy::Lockfile.
64              
65             =head3 Parameters
66              
67             Accepts a single optional parameter, a hashref containing the following
68             options:
69              
70             =head4 location
71              
72             Specifies the full path to the location of the lockfile. Defaults to:
73              
74             '/tmp/' . (fileparse($0))[0] . '.pid'
75              
76             i.e., the name of the program being run, with a ".pid" extension, in /tmp/.
77              
78             =head4 no_pid
79              
80             If true, instead of writing the PID file, a value of "0" is written instead.
81             When read by another instance of Lazy::Lockfile attempting to acquire the lock,
82             no PID check will be performed and the lock will be assumed to be active as
83             long as the file exists. Defaults to false.
84              
85             =head4 delete_on_destroy
86              
87             If true, sets the "delete on destroy" flag. This flag defaults to true, which
88             causes the lockfile to be removed when the object is destroyed. Generally,
89             this is the desired behavior. When set to false, this flag prevents the
90             lockfile from being removed automatically when the object is destroyed. See
91             also C.
92              
93             =head3 Compatibility
94              
95             For compatibility with older versions of Lazy::Lockfile (pre-1.0), a single
96             optional parameter is accepted, the path to the lockfile. This parameter
97             functions the same as the 'location' parameter described above.
98              
99             As stated above, malformed lockfiles will be overwritten, though this may be
100             subject to change in a future version.
101              
102             =head3 Return value
103              
104             If the lock can not be obtained, undef is returned (and $! will contain useful
105             information). Otherwise, the lock is exclusive to this process, as long as the
106             object exists.
107              
108             =head3 Example
109              
110             my $lockfile = Lazy::Lockfile->new( { location => "/var/lock", no_pid => 1 } )
111             || die "Couldn't get lock!";
112              
113             =cut
114              
115             sub new {
116 3     3 1 417 my ( $class, $params ) = @_;
117 3         6 my $self = {};
118 3         6 my $lockfile_location;
119              
120             # Yargh, backwards compatibility ahoy!
121 3 50       7 if ( ref $params ne 'HASH' ) {
122 3         6 $lockfile_location = $params;
123 3         5 $params = {};
124             } else {
125 0 0       0 if ( !defined $params ) {
126 0         0 $params = {};
127             }
128 0         0 $lockfile_location = $params->{'location'};
129             }
130              
131 3 50 33     10 if ( ( !defined $lockfile_location ) || ( $lockfile_location eq '' ) ) {
132 3         58 $lockfile_location = '/tmp/' . (fileparse($0))[0] . '.pid';
133             }
134              
135 3         14 my $lock_tries = 0;
136 3         6 my ( $lock, $file_pid );
137              
138             # If we return here, sysopen will set $! for us.
139 3 50       309 sysopen( $lock, $lockfile_location, O_RDWR | O_CREAT | O_NOFOLLOW, 0644 ) or return;
140 3         13 while ( $lock_tries++ < 5 ) {
141 3 50       15 if ( flock( $lock, LOCK_NB | LOCK_EX ) ) {
142 3         6 last;
143             }
144 0         0 sleep( 1 );
145             }
146 3 50       8 if ( $lock_tries > 5 ) {
147 0         0 close( $lock );
148 0         0 $! = EWOULDBLOCK;
149 0         0 return;
150             }
151 3         10 seek( $lock, 0, 0 );
152 3         37 $file_pid = <$lock>;
153              
154 3 100       9 if ( defined $file_pid ) {
155 1         5 ( $file_pid ) = $file_pid =~ /^(\d+)/;
156             }
157             # Would it be better to detect the broken file and return a different error?
158             # if ( ( !defined $file_pid ) && ( $file_pid eq '' ) )
159             # flock( $lock, LOCK_UN );
160             # close( $lock );
161             # $! = EFTYPE;
162             # return;
163             # }
164 3 50 66     32 if (
      33        
      66        
165             ( ( defined $file_pid ) && ( $file_pid ne '' ) )
166             &&
167 1     1   384 ( ( $file_pid == 0 ) || ( kill( 0, $file_pid ) || $!{EPERM} ) )
  1         1008  
  1         398  
168             ) {
169 1         4 flock( $lock, LOCK_UN );
170 1         6 close( $lock );
171 1         3 $! = EEXIST;
172 1         6 return;
173             }
174              
175 2         4 my $lockfile_contents;
176 2 50       6 if ( $params->{'no_pid'} ) {
177 0         0 $lockfile_contents = "0\n";
178             } else {
179 2         11 $lockfile_contents = "$$\n";
180             }
181              
182 2         6 seek( $lock, 0, 0 );
183 2         50 truncate( $lock, length( $lockfile_contents ) );
184 2         31 my $r = syswrite( $lock, $lockfile_contents );
185              
186             # If there's a filesystem problem (full? corrupt?) and we can't write
187             # a PID, bail and error.
188 2 50 33     19 if ( !defined $r || $r != length( $lockfile_contents ) ) {
189 0         0 unlink( $lockfile_location );
190 0         0 flock( $lock, LOCK_UN );
191 0         0 close( $lock );
192 0         0 return;
193             }
194              
195 2         7 flock( $lock, LOCK_UN );
196 2         12 close( $lock );
197 2         7 bless $self, $class;
198 2         10 $self->{'lockfile_location'} = $lockfile_location;
199              
200 2 50       6 if ( defined $params->{'delete_on_destroy'} ) {
201 0 0       0 $self->{'delete_on_destroy'} = $params->{'delete_on_destroy'} ? 1 : 0;
202             } else {
203 2         4 $self->{'delete_on_destroy'} = 1;
204             }
205              
206 2         11 return $self;
207             }
208              
209             =head2 name
210              
211             Returns the file name of the lockfile.
212              
213             =cut
214              
215             sub name {
216 0     0 1 0 my ( $self ) = @_;
217 0         0 return $self->{'lockfile_location'};
218             }
219              
220             =head2 delete_on_destroy
221              
222             Gets or sets the "delete on destroy" flag.
223              
224             If called without a parameter (or with undef), delete_on_destroy will return
225             the current state of the "delete on destroy" flag. If called with a parameter,
226             this flag will be set.
227              
228             =cut
229              
230             sub delete_on_destroy {
231 1     1 1 3 my ( $self, $new_setting ) = @_;
232              
233 1 50       4 if ( !defined $new_setting ) {
234 0         0 return $self->{'delete_on_destroy'};
235             } else {
236 1 50       3 $self->{'delete_on_destroy'} = $new_setting ? 1 : 0;
237 1         2 return;
238             }
239             }
240              
241             =head2 unlock
242              
243             Explicitly removes the lockfile, just as if the object were destroyed. Once
244             this has been called, delete_on_destroy will be set to false, since the lock
245             has already been deleted. Once this method is called, there is not much use
246             left for the object, so the user may as well delete it now.
247              
248             unlock should be used when the lockfile needs to be removed deterministically
249             while the program is running. If you simply remove all references to the
250             Lazy::Lockfile object, the lock will be freed when garbage collection is run,
251             which is not guaranteed to happen until the program exits (though it will
252             likely happen immediately).
253              
254             Returns a true value if the lockfile was found and removed, false otherwise.
255              
256             =cut
257              
258             sub unlock {
259 1     1 1 363 my ( $self ) = @_;
260 1         4 my $retval = $self->DESTROY;
261 1         4 $self->delete_on_destroy(0);
262 1         4 return $retval;
263             }
264              
265             # Make sure the lockfile contains our pid before we delete it...
266             # do we need this?
267             sub DESTROY {
268 3     3   298 my ( $self ) = @_;
269 3         5 my $retval = 0;
270 3 100 33     16 if ( ( $self ) && ( $self->{'lockfile_location'} ) && ( $self->{'delete_on_destroy'} ) ) {
      33        
271 2         4 my ( $lock, $file_pid );
272 2         4 my $lock_tries = 0;
273 2 50       49 open( $lock, '<', $self->{'lockfile_location'} ) || return 0;
274 2         8 while ( $lock_tries++ < 5 ) {
275 2 50       9 if ( flock( $lock, LOCK_NB | LOCK_EX ) ) {
276 2         4 last;
277             }
278 0         0 sleep( 1 );
279             }
280 2 50       10 if ( $lock_tries > 5 ) { close( $lock ); return 0; }
  0         0  
  0         0  
281 2         11 seek( $lock, 0, 0 );
282 2         16 $file_pid = <$lock>;
283 2 50       8 chomp( $file_pid ) if defined $file_pid;
284 2 50 33     18 if ( ( defined $file_pid ) && ( ( $file_pid == 0 ) || ( $file_pid == $$ ) ) ) {
      33        
285 2         58 $retval = unlink $self->{'lockfile_location'};
286             }
287 2         65 close( $lock );
288             }
289 3         33 return $retval;
290             }
291              
292             =head1 CHANGES
293              
294             =head2 2017-05-29, 1.23 - jeagle
295              
296             Detect and bail out on errors when writing to the lockfile (RT#121894).
297              
298             Thanks MRDVT.
299              
300             =head2 2014-10-30, 1.22 - jeagle
301              
302             Add missing dependency.
303              
304             =head2 2014-09-14, 1.21 - jeagle
305              
306             Re-package to make it easier to convert to RPM, etc.
307              
308             =head2 2012-04-01, 1.20 - jeagle
309              
310             Updated documentation, thanks Alister W.
311              
312             =head2 2011-01-05, 1.19 - jeagle
313              
314             Change to unit tests to appease cpantesters.
315              
316             =head2 2011-01-04, 1.18 - jeagle
317              
318             Implement suggestion by srezic to check PIDs belonging to other users
319             (RT#69185).
320              
321             Clean up documentation.
322              
323             =head2 2010-06-22, 1.17 - jeagle
324              
325             Update L to return a useful status.
326              
327             =head2 2010-06-22, 1.16 - jeagle
328              
329             Version bumps for migration to CPAN.
330              
331             =head2 2009-12-03, 1.14 - jeagle
332              
333             Fix a bug causing lockfiles with no_pid to not be deleted on destroy/unlink.
334              
335             =head2 2009-12-03, 1.13 - jeagle
336              
337             Add the unlock method, to allow for deterministic lockfile removal at runtime.
338              
339             =head2 2009-11-30, 1.12 - jeagle
340              
341             Update documentation to clarify delete_on_destroy parameter default setting.
342              
343             =head2 2009-07-06, 1.11 - jeagle
344              
345             Fix error thrown when running with taint checking enabled.
346              
347             =head2 2009-07-06, 1.10 - jeagle
348              
349             Fix a bug with lockfile location being overwritten with the default.
350              
351             =head2 2009-07-06, 1.9 - jeagle
352              
353             Add new parameter, no_pid, which disabled active lockfile checks.
354              
355             Allow constructor to accept multiple parameters via hashref.
356              
357             =head2 2009-06-10, 0.4 - jeagle
358              
359             Introduce the delete_on_destroy flag.
360              
361             =head2 2009-06-03, 0.3 - jeagle
362              
363             Open pid file with O_NOFOLLOW, to avoid symlink attacks.
364              
365             Change default pid file location from /var/tmp to /tmp.
366              
367             Correct dates in CHANGES section.
368              
369             Add useful error indicators, documentation on error detection.
370              
371             =head2 2009-04-27, 0.2 - jeagle
372              
373             Fix a bug with unspecified lockfile paths trying to create impossible file
374             names.
375              
376             =head2 2009-04-06, v0.1 - jeagle
377              
378             Initial release.
379              
380             =cut
381              
382             1;