| 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__ |