File Coverage

blib/lib/Proc/Pidfile.pm
Criterion Covered Total %
statement 103 117 88.0
branch 32 48 66.6
condition 7 15 46.6
subroutine 17 17 100.0
pod 0 2 0.0
total 159 199 79.9


line stmt bran cond sub pod time code
1             package Proc::Pidfile;
2             $Proc::Pidfile::VERSION = '1.08_05'; # TRIAL
3              
4 4     4   269572 $Proc::Pidfile::VERSION = '1.0805';use 5.006;
  4         20  
5 4     4   20 use strict;
  4         8  
  4         124  
6 4     4   24 use warnings;
  4         8  
  4         124  
7              
8 4     4   20 use Fcntl qw/ :flock /;
  4         8  
  4         600  
9 4     4   28 use File::Basename qw/ basename /;
  4         8  
  4         260  
10 4     4   24 use Carp qw/ carp croak /;
  4         8  
  4         236  
11 4     4   2144 use Time::HiRes qw/ usleep /;
  4         5396  
  4         20  
12 4     4   736 use File::Spec::Functions qw/ catfile tmpdir /;
  4         8  
  4         2864  
13              
14             sub new
15             {
16 22     22 0 683577 my $class = shift;
17 22         143 my %args = @_;
18 22         88 my $self = bless \%args, $class;
19              
20 22 100       139 $self->{retries} = 2 unless defined($self->{retries});
21              
22 22 100       75 unless ( $self->{pidfile} ) {
23 17         1333 my $basename = basename( $0 );
24 17         156 my $dir = tmpdir();
25              
26 17 50       762 croak "Can't write to $dir\n" unless -w $dir;
27              
28 17         168 my $pidfile = catfile($dir, "$basename.pid");
29              
30             # untaint the path, since it includes externally generated info
31             # TODO: should we be a bit more pedantic on "valid path"?
32 17 50       153 $pidfile = $1 if ($pidfile =~ /^\s*(.*)\s*/);
33              
34 17         117 $self->_verbose( "pidfile: $pidfile\n" );
35 17         58 $self->{pidfile} = $pidfile;
36             }
37 22         125 $self->_create_pidfile();
38 20         108 return $self;
39             }
40              
41             sub DESTROY
42             {
43 22     22   676219 my $self = shift;
44              
45 22         131 $self->_destroy_pidfile();
46             }
47              
48             sub pidfile
49             {
50 12     12 0 48 my $self = shift;
51 12         92 return $self->{pidfile};
52             }
53              
54             sub _verbose
55             {
56 167     167   305 my $self = shift;
57 167 50       440 return unless $self->{verbose};
58 0         0 print STDERR @_;
59             }
60              
61             sub _get_pid
62             {
63 22     22   39 my $self = shift;
64 22         49 my $pidfile = $self->{pidfile};
65 22         91 $self->_verbose( "get pid from $pidfile\n" );
66 22 50       810 open( PID, $pidfile ) or croak "can't read pid file $pidfile\n";
67 22 50       295 flock( PID, LOCK_SH ) or croak "can't lock pid file $pidfile\n";
68 22         518 my $pid = ;
69 22 50 33     392 if (defined($pid) && $pid =~ /([0-9]+)/) {
70 22         112 $pid = $1;
71             }
72             else {
73 0         0 croak "can't get pid from pidfile $pidfile\n";
74             }
75 22         59 chomp( $pid );
76 22         152 flock( PID, LOCK_UN );
77 22         211 close( PID );
78 22         134 $self->_verbose( "pid = $pid\n" );
79 22         88 return $pid;
80             }
81              
82             sub _is_running
83             {
84 3     3   16 my $pid = shift;
85              
86 3 50       49 if ($^O eq 'riscos') {
87 0         0 require Proc::ProcessTable;
88              
89 0         0 my $table = Proc::ProcessTable->new()->table;
90 0         0 my %processes = map { $_->pid => $_ } @$table;
  0         0  
91 0         0 return exists $processes{$pid};
92             }
93             else {
94 4   66 4   1912 return kill(0, $pid) || $!{'EPERM'};
  4         5340  
  4         36  
  3         198  
95             }
96             }
97              
98             sub _create_pidfile
99             {
100 22     22   54 my $self = shift;
101 22         136 my $pidfile = $self->{pidfile};
102 22         102 my $attempt = 1;
103              
104 22         378 while ( -e $pidfile ) {
105 3         128 $self->_verbose( "pidfile $pidfile exists\n" );
106 3         41 my $pid = $self->_get_pid();
107 3         72 $self->_verbose( "pid in pidfile $pidfile = $pid\n" );
108 3 100       69 if ( _is_running( $pid ) ) {
109            
110             # this might be a race condition, or parallel smoke testers,
111             # so we'll back off a random amount of time and try again
112 2 50       11 if ($attempt <= $self->{retries}) {
113 0         0 ++$attempt;
114             # TODO: let's try this. Guessing we don't have to
115             # bother with increasing backoff times
116 0         0 my $backoff = 100 + rand(300);
117 0         0 $self->_verbose("backing off for $backoff microseconds before trying again");
118 0         0 usleep(100 + rand(300));
119 0         0 next;
120             }
121              
122 2 100       7 if ( $self->{silent} ) {
123 1         38 exit;
124             }
125             else {
126 1         603 croak "$0 already running: $pid ($pidfile)\n";
127             }
128             }
129             else {
130 1         31 $self->_verbose( "$pid has died - replacing pidfile\n" );
131 1 50       71 open( PID, ">$pidfile" ) or croak "Can't write to $pidfile\n";
132 1         26 print PID "$$\n";
133 1         106 close( PID );
134 1         6 last;
135             }
136             }
137              
138 20 100       190 if (not -e $pidfile) {
139 19         119 $self->_verbose( "no pidfile $pidfile\n" );
140 19 50       1245 open( PID, ">$pidfile" ) or croak "Can't write to $pidfile: $!\n";
141 19 50       230 flock( PID, LOCK_EX ) or croak "Can't lock pid file $pidfile\n";
142 19 50       262 print PID "$$\n" or croak "Can't write to pid file $pidfile\n";
143 19         542 flock( PID, LOCK_UN );
144 19 50       200 close( PID ) or croak "Can't close pid file $pidfile: $!\n";
145 19         113 $self->_verbose( "pidfile $pidfile created\n" );
146             }
147              
148 20         66 $self->{created} = 1;
149             }
150              
151             sub _destroy_pidfile
152             {
153 22     22   41 my $self = shift;
154              
155 22 100       93 return unless $self->{created};
156 20         43 my $pidfile = $self->{pidfile};
157 20         135 $self->_verbose( "destroy $pidfile\n" );
158 20 100 66     496 if ( $pidfile and -e $pidfile ) {
159 19         84 my $pid = $self->_get_pid();
160 19         83 $self->_verbose( "pid in $pidfile = $pid\n" );
161 19 100 33     196 if ( $pid == $$ ) {
    50          
162 16         82 $self->_verbose( "remove pidfile: $pidfile\n" );
163 16 50 33     1884 unlink( $pidfile ) if $pidfile and -e $pidfile;
164             }
165             elsif ($^O ne 'MSWin32' && $^O ne 'riscos') {
166 3         58 $self->_verbose( "$pidfile not my pidfile - maybe my parent's?\n" );
167 3         81 my $ppid = getppid();
168 3         70 $self->_verbose( "parent pid = $ppid\n" );
169 3 50       600 if ( $ppid != $pid ) {
170 0         0 carp "pid $pid in $pidfile is not mine ($$) - I am $0 - or my parents ($ppid)\n";
171             }
172             }
173             else {
174 0         0 $self->_verbose( "$pidfile not my pidfile - can't check if it's my parent's on this OS\n" );
175             }
176             }
177             else {
178 1         404 carp "pidfile $pidfile doesn't exist\n";
179             }
180             }
181              
182             #------------------------------------------------------------------------------
183             #
184             # Start of POD
185             #
186             #------------------------------------------------------------------------------
187              
188             =head1 NAME
189              
190             Proc::Pidfile - a simple OO Perl module for maintaining a process id file for
191             the curent process
192              
193             =head1 SYNOPSIS
194              
195             my $pp = Proc::Pidfile->new( pidfile => "/path/to/your/pidfile" );
196             # if the pidfile already exists, die here
197             $pidfile = $pp->pidfile();
198             undef $pp;
199             # unlink $pidfile here
200              
201             my $pp = Proc::Pidfile->new();
202             # creates pidfile in default location
203             my $pidfile = $pp->pidfile();
204             # tells you where this pidfile is ...
205              
206             my $pp = Proc::Pidfile->new( silent => 1 );
207             # if the pidfile already exists, exit silently here
208             ...
209             undef $pp;
210              
211             =head1 DESCRIPTION
212              
213             Proc::Pidfile is a very simple OO interface which manages a pidfile for the
214             current process.
215             You can pass the path to a pidfile to use as an argument to the constructor,
216             or you can let Proc::Pidfile choose one
217             ("/$tmpdir/$basename", where C<$tmpdir> is from C).
218              
219             Pidfiles created by Proc::Pidfile are automatically removed on destruction of
220             the object. At destruction, the module checks the process id in the pidfile
221             against its own, and against its parents (in case it is a spawned child of the
222             process that originally created the Proc::Pidfile object), and barfs if it
223             doesn't match either.
224              
225             If you pass a "silent" parameter to the constructor, then it will still check
226             for the existence of a pidfile, but will exit silently if one is found. This is
227             useful for, for example, cron jobs, where you don't want to create a new
228             process if one is already running, but you don't necessarily want to be
229             informed of this by cron.
230              
231             =head2 Retries
232              
233             If another instance of your script is already running,
234             we'll retry a couple of times,
235             with a random number of microseconds between each attempt.
236              
237             You can specify the number of retries, for example if you
238             want to try more times for some reason:
239              
240             $pidfile = $pp->pidfile(retries => 4);
241              
242             By default this is set to 2,
243             which means if the first attempt to set up a pidfile fails,
244             it will try 2 more times, so three attempts in total.
245              
246             Setting retries to 0 (zero) will disable this feature.
247              
248              
249             =head1 SEE ALSO
250              
251             L - provides a similar interface.
252              
253             L - provides effectively the same functionality,
254             but via class methods. Hasn't been updated since 2011,
255             and has quite a few CPAN Testers fails.
256              
257             L - provides a simple interface, but has some restrictions,
258             and its documentation even recommends you consider a different module,
259             as it has a race condition.
260              
261             L - very simple interface, and uses a different mechanism:
262             it tries to lock the script file which used the module.
263             The trouble with that is that you might be running someone else's script,
264             and thus can't lock it.
265              
266             L - another one with a simple default interface,
267             but can be configured to retry. Based on locking, rather than a pid file.
268             Doesn't work on Windows.
269              
270             L - Linux-specific solution.
271              
272             =head1 REPOSITORY
273              
274             L
275              
276             =head1 AUTHOR
277              
278             Ave Wrigley Eawrigley@cpan.orgE
279              
280             Now maintained by Neil Bowers Eneilb@cpan.orgE
281              
282             =head1 COPYRIGHT
283              
284             Copyright (c) 2003 Ave Wrigley. All rights reserved. This program is free
285             software; you can redistribute it and/or modify it under the same terms as Perl
286             itself.
287              
288             =cut
289              
290             #------------------------------------------------------------------------------
291             #
292             # End of POD
293             #
294             #------------------------------------------------------------------------------
295              
296              
297             #------------------------------------------------------------------------------
298             #
299             # True ...
300             #
301             #------------------------------------------------------------------------------
302              
303             1;
304