line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package File::Pid::Quick; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
7589
|
use 5.006; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
43
|
|
4
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
31
|
|
5
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
56
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
File::Pid::Quick - Quick PID file implementation |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 SYNOPSIS |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use File::Pid::Quick; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
use File::Pid::Quick qw( /var/run/myjob.pid ); |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use File::Pid::Quick qw( /var/run/myjob.pid verbose ); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
use File::Pid::Quick qw( /var/run/myjob.pid timeout 120 ); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
File::Pid::Quick->recheck; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
File::Pid::Quick->check('/var/run/myjob.pid'); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=cut |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our $VERSION = '1.02'; |
28
|
|
|
|
|
|
|
|
29
|
1
|
|
|
1
|
|
6
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
81
|
|
30
|
1
|
|
|
1
|
|
11
|
use Fcntl qw( :flock ); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
139
|
|
31
|
1
|
|
|
1
|
|
5
|
use File::Basename qw( basename ); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
120
|
|
32
|
1
|
|
|
1
|
|
1358
|
use File::Spec::Functions qw( tmpdir catfile ); |
|
1
|
|
|
|
|
936
|
|
|
1
|
|
|
|
|
1767
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 DESCRIPTION |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
This module associates a PID file with your script for the purpose of |
37
|
|
|
|
|
|
|
keeping more than one copy from running (concurrency prevention). It |
38
|
|
|
|
|
|
|
creates the PID file, checks for its existence when the script is run, |
39
|
|
|
|
|
|
|
terminates the script if there is already an instance running, and |
40
|
|
|
|
|
|
|
removes the PID file when the script finishes. |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
This module's objective is to provide a completely simplified interface |
43
|
|
|
|
|
|
|
that makes adding PID-file-based concurrency prevention to your script |
44
|
|
|
|
|
|
|
as quick and simple as possible; hence File::Pid::Quick. For a more |
45
|
|
|
|
|
|
|
nuanced implementation of PID files, please see File::Pid. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
The absolute simplest way to use this module is: |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
use File::Pid::Quick; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
A default PID file will be used, located in C<< File::Spec->tmpdir >> and |
52
|
|
|
|
|
|
|
named C<< File::Basename::basename($0) . '.pid' >>; for example, if |
53
|
|
|
|
|
|
|
C<$0> is F<~/bin/run>, the PID file will be F. The PID file |
54
|
|
|
|
|
|
|
will be checked and/or generated immediately on use of the module. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
Alternately, an import list may be provided to the module. It can contain |
57
|
|
|
|
|
|
|
three kinds of things: |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
use File::Pid::Quick qw( verbose ); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
If the string 'verbose' is passed in the import list, the module will do |
62
|
|
|
|
|
|
|
more reporting on its activities than otherwise. It will use warn() for |
63
|
|
|
|
|
|
|
its verbose output. |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
use File::Pid::Quick qw( timeout 60 ); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
If the string 'timeout' is passed in the import list, the next item in |
68
|
|
|
|
|
|
|
the import list will be interpreted as a timeout after which, instead of |
69
|
|
|
|
|
|
|
terminating itself because another instance was found, the script should |
70
|
|
|
|
|
|
|
send a SIGTERM to the other instance and go ahead itself. The timeout |
71
|
|
|
|
|
|
|
must be a positive integer. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
use File::Pid::Quick qw( manual ); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
If the string 'manual' is passed in the import list, the normal behavior |
76
|
|
|
|
|
|
|
of generating a default PID file will be suppressed. This is essentially |
77
|
|
|
|
|
|
|
for cases where you want to control exactly when the PID file check is |
78
|
|
|
|
|
|
|
performed by using File::Pid::Quick->check(), below. The check will still |
79
|
|
|
|
|
|
|
be performed immediately if a filename is also provided in the import list. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
use File::Pid::Quick qw( /var/run/myscript.pid ); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Any other string passed in the import list is interpreted as a filename |
84
|
|
|
|
|
|
|
to be used instead of the default for the PID file. If more than one such |
85
|
|
|
|
|
|
|
string is found, this is an error. |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Any combination of the above import list options may be used. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=cut |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
our @pid_files_created; |
92
|
|
|
|
|
|
|
our $verbose; |
93
|
|
|
|
|
|
|
our $timeout; |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub import($;@) { |
96
|
1
|
|
|
1
|
|
11
|
my $package = shift; |
97
|
1
|
|
|
|
|
2
|
my $filename; |
98
|
|
|
|
|
|
|
my $manual; |
99
|
1
|
|
|
|
|
7
|
while(scalar @_) { |
100
|
0
|
|
|
|
|
0
|
my $item = shift; |
101
|
0
|
0
|
|
|
|
0
|
if($item eq 'verbose') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
102
|
0
|
|
|
|
|
0
|
$verbose = 1; |
103
|
|
|
|
|
|
|
} elsif($item eq 'manual') { |
104
|
0
|
|
|
|
|
0
|
$manual = 1; |
105
|
|
|
|
|
|
|
} elsif($item eq 'timeout') { |
106
|
0
|
|
|
|
|
0
|
$timeout = shift; |
107
|
0
|
0
|
0
|
|
|
0
|
unless(defined $timeout and $timeout =~ /^\d+$/ and int($timeout) eq $timeout and $timeout > 0) { |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
108
|
0
|
0
|
|
|
|
0
|
carp 'Invalid timeout ' . (defined $timeout ? '"' . $timeout . '"' : '(undefined)'); |
109
|
0
|
|
|
|
|
0
|
exit 1; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
} else { |
112
|
0
|
0
|
|
|
|
0
|
if(defined $filename) { |
113
|
0
|
|
|
|
|
0
|
carp 'Invalid option "' . $item . '" (filename ' . $filename . ' already set)'; |
114
|
0
|
|
|
|
|
0
|
exit 1; |
115
|
|
|
|
|
|
|
} |
116
|
0
|
|
|
|
|
0
|
$filename = $item; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
} |
119
|
1
|
50
|
33
|
|
|
14
|
__PACKAGE__->check($filename, $timeout, 1) |
|
|
|
33
|
|
|
|
|
120
|
|
|
|
|
|
|
unless $^C or ($manual and not defined $filename); |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
END { |
124
|
1
|
|
|
1
|
|
169
|
foreach my $pid_file_created (@pid_files_created) { |
125
|
|
|
|
|
|
|
next |
126
|
2
|
50
|
|
|
|
78
|
unless open my $pid_in, '<', $pid_file_created; |
127
|
2
|
|
|
|
|
25
|
my $pid = <$pid_in>; |
128
|
2
|
|
|
|
|
4
|
chomp $pid; |
129
|
2
|
|
|
|
|
16
|
$pid =~ s/\s.*//o; |
130
|
2
|
50
|
|
|
|
13
|
if($pid == $$) { |
131
|
2
|
50
|
|
|
|
11
|
if($^O =~ /^MSWin/) { |
132
|
0
|
|
|
|
|
0
|
close $pid_in; |
133
|
0
|
|
|
|
|
0
|
undef $pid_in; |
134
|
|
|
|
|
|
|
} |
135
|
2
|
50
|
|
|
|
131
|
if(unlink $pid_file_created) { |
136
|
2
|
50
|
|
|
|
7
|
warn "Deleted $pid_file_created for PID $$\n" |
137
|
|
|
|
|
|
|
if $verbose; |
138
|
|
|
|
|
|
|
} else { |
139
|
0
|
|
|
|
|
0
|
warn "Could not delete $pid_file_created for PID $$\n"; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} else { |
142
|
0
|
0
|
|
|
|
0
|
warn "$pid_file_created had PID $pid, not $$, leaving in place\n" |
143
|
|
|
|
|
|
|
if $verbose; |
144
|
|
|
|
|
|
|
} |
145
|
2
|
50
|
|
|
|
103
|
close $pid_in |
146
|
|
|
|
|
|
|
if defined $pid_in; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head2 check |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
File::Pid::Quick->check('/var/run/myjob.pid', 60); |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
File::Pid::Quick->check(undef, undef, 1); |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Performs a check of the specified PID file, including generating it |
157
|
|
|
|
|
|
|
if necessary, finding whether another instance is actually running, |
158
|
|
|
|
|
|
|
and terminating the current process if necesasry. |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
All arguments are optional. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
The first argument, $pid_file, is the filename to check; an undefined |
163
|
|
|
|
|
|
|
value results in the default (described above) being used. |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
The second argument, $use_timeout, is a PID file timeout. If an |
166
|
|
|
|
|
|
|
already-running script instance started more than this many seconds |
167
|
|
|
|
|
|
|
ago, don't terminate the current instance; instead, terminate the |
168
|
|
|
|
|
|
|
already-running instance (by sending a SIGTERM) and proceed. If |
169
|
|
|
|
|
|
|
defined, this must be a non-negative integer. An undefined value |
170
|
|
|
|
|
|
|
results in the timeout value set by this module's import list being |
171
|
|
|
|
|
|
|
used, if any; a value of 0 causes no timeout to be applied, overriding |
172
|
|
|
|
|
|
|
the value set by the import list if necessary. |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
The third argument, $warn_and_exit, controls how the script terminates. |
175
|
|
|
|
|
|
|
If it is false, die()/croak() is used. If it is true, warn()/carp() is |
176
|
|
|
|
|
|
|
used to issue the appropriate message and exit(1) is used to terminate. |
177
|
|
|
|
|
|
|
This allows the module to terminate the script from inside an eval(); |
178
|
|
|
|
|
|
|
PID file checks performed based on the module's import list use this |
179
|
|
|
|
|
|
|
option. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub check($;$$$) { |
184
|
2
|
|
|
2
|
1
|
292
|
my $package = shift; |
185
|
2
|
|
|
|
|
4
|
my $pid_file = shift; |
186
|
2
|
|
|
|
|
4
|
my $use_timeout = shift; |
187
|
2
|
|
|
|
|
4
|
my $warn_and_exit = shift; |
188
|
2
|
100
|
|
|
|
13
|
$pid_file = catfile(tmpdir, basename($0) . '.pid') |
189
|
|
|
|
|
|
|
unless defined $pid_file; |
190
|
2
|
50
|
|
|
|
226
|
$use_timeout = $timeout |
191
|
|
|
|
|
|
|
unless defined $use_timeout; |
192
|
2
|
0
|
0
|
|
|
7
|
if(defined $use_timeout and ($use_timeout =~ /\D/ or int($use_timeout) ne $use_timeout or $use_timeout < 0)) { |
|
|
|
33
|
|
|
|
|
193
|
0
|
0
|
|
|
|
0
|
if($warn_and_exit) { |
194
|
0
|
|
|
|
|
0
|
carp 'Invalid timeout "' . $use_timeout . '"'; |
195
|
0
|
|
|
|
|
0
|
exit 1; |
196
|
|
|
|
|
|
|
} else { |
197
|
0
|
|
|
|
|
0
|
croak 'Invalid timeout "' . $use_timeout . '"'; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
} |
200
|
2
|
50
|
|
|
|
234
|
if(open my $pid_in, '<', $pid_file) { |
201
|
0
|
|
|
|
|
0
|
flock $pid_in, LOCK_SH; |
202
|
0
|
|
|
|
|
0
|
my $pid_data = <$pid_in>; |
203
|
0
|
|
|
|
|
0
|
chomp $pid_data; |
204
|
0
|
|
|
|
|
0
|
my $pid; |
205
|
|
|
|
|
|
|
my $ptime; |
206
|
0
|
0
|
|
|
|
0
|
if($pid_data =~ /(\d+)\s+(\d+)/o) { |
207
|
0
|
|
|
|
|
0
|
$pid = $1; |
208
|
0
|
|
|
|
|
0
|
$ptime = $2; |
209
|
|
|
|
|
|
|
} else { |
210
|
0
|
|
|
|
|
0
|
$pid = $pid_data; |
211
|
|
|
|
|
|
|
} |
212
|
0
|
0
|
0
|
|
|
0
|
if($pid != $$ and kill 0, $pid) { |
213
|
0
|
|
|
|
|
0
|
my $name = basename($0); |
214
|
0
|
0
|
0
|
|
|
0
|
if($timeout and $ptime < time - $timeout) { |
215
|
0
|
|
|
|
|
0
|
my $elapsed = time - $ptime; |
216
|
0
|
0
|
|
|
|
0
|
warn "Timing out current $name on $timeout sec vs. $elapsed sec, sending SIGTERM and rewriting $pid_file\n" |
217
|
|
|
|
|
|
|
if $verbose; |
218
|
0
|
|
|
|
|
0
|
kill 'TERM', $pid; |
219
|
|
|
|
|
|
|
} else { |
220
|
0
|
0
|
|
|
|
0
|
if($warn_and_exit) { |
221
|
0
|
|
|
|
|
0
|
warn "Running $name found via $pid_file, process $pid, exiting\n"; |
222
|
0
|
|
|
|
|
0
|
exit 1; |
223
|
|
|
|
|
|
|
} else { |
224
|
0
|
|
|
|
|
0
|
die "Running $name found via $pid_file, process $pid, exiting\n"; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
} |
228
|
0
|
|
|
|
|
0
|
close $pid_in; |
229
|
|
|
|
|
|
|
} |
230
|
2
|
50
|
|
|
|
9
|
unless(grep { $_ eq $pid_file } @pid_files_created) { |
|
1
|
|
|
|
|
7
|
|
231
|
2
|
|
|
|
|
13
|
my $pid_out; |
232
|
2
|
50
|
|
|
|
239
|
unless(open $pid_out, '>', $pid_file) { |
233
|
0
|
0
|
|
|
|
0
|
if($warn_and_exit) { |
234
|
0
|
|
|
|
|
0
|
warn "Cannot write $pid_file: $!\n"; |
235
|
0
|
|
|
|
|
0
|
exit 1; |
236
|
|
|
|
|
|
|
} else { |
237
|
0
|
|
|
|
|
0
|
die "Cannot write $pid_file: $!\n"; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
} |
240
|
2
|
|
|
|
|
30
|
flock $pid_out, LOCK_EX; |
241
|
2
|
|
|
|
|
57
|
print $pid_out $$, ' ', time, "\n"; |
242
|
2
|
|
|
|
|
110
|
close $pid_out; |
243
|
2
|
|
|
|
|
5
|
push @pid_files_created, $pid_file; |
244
|
2
|
50
|
|
|
|
65
|
warn "Created $pid_file for PID $$\n" |
245
|
|
|
|
|
|
|
if $verbose; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=head2 recheck |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
File::Pid::Quick->recheck; |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
File::Pid::Quick->recheck(300); |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
File::Pid::Quick->recheck(undef, 1); |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
Used to reverify that the running process is the owner of the |
258
|
|
|
|
|
|
|
appropriate PID file. Checks all PID files which were created by |
259
|
|
|
|
|
|
|
the current process. |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
All arguments are optional. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
The first argument, $timeout, is a timeout value which will be |
264
|
|
|
|
|
|
|
applied to PID file checks in exactly the same manner as describe |
265
|
|
|
|
|
|
|
for check() above. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
The second argument, $warn_and_exit, works identically to the |
268
|
|
|
|
|
|
|
$warn_and_exit argument described for check() above. |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=cut |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub recheck($;$$) { |
273
|
0
|
|
|
0
|
1
|
|
my $package = shift; |
274
|
0
|
|
|
|
|
|
my $timeout = shift; |
275
|
0
|
|
|
|
|
|
my $warn_and_exit = shift; |
276
|
0
|
0
|
|
|
|
|
warn "no PID files created\n" |
277
|
|
|
|
|
|
|
unless scalar @pid_files_created; |
278
|
0
|
|
|
|
|
|
foreach my $pid_file_created (@pid_files_created) { |
279
|
0
|
|
|
|
|
|
$package->check($pid_file_created, $timeout, $warn_and_exit); |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
1; |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
__END__ |