line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# See copyright, etc in below POD section. |
2
|
|
|
|
|
|
|
###################################################################### |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
=head1 NAME |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
IPC::Locker - Distributed lock handler |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 SYNOPSIS |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
use IPC::Locker; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $lock = IPC::Locker->lock(lock=>'one_per_machine', |
13
|
|
|
|
|
|
|
host=>'example.std.com', |
14
|
|
|
|
|
|
|
port=>223); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
if ($lock->lock()) { something; } |
17
|
|
|
|
|
|
|
if ($lock->locked()) { something; } |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
$lock->unlock(); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 DESCRIPTION |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
L will query a remote lockerd server to obtain a lock around a |
24
|
|
|
|
|
|
|
critical section. When the critical section completes, the lock may be |
25
|
|
|
|
|
|
|
returned. |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
This is useful for distributed utilities which run on many machines, and |
28
|
|
|
|
|
|
|
cannot use file locks or other such mechanisms due to NFS or lack of common |
29
|
|
|
|
|
|
|
file systems. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
Multiple locks may be requested, in which case the first lock to be free |
32
|
|
|
|
|
|
|
will be used. Lock requests are serviced in a first-in-first-out order, |
33
|
|
|
|
|
|
|
and the locker can optionally free locks for any processes that cease to |
34
|
|
|
|
|
|
|
exist. |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=over 4 |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=item new ([parameter=>value ...]); |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Create a lock structure. |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=item lock ([parameter=>value ...]); |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Try to obtain the lock, return the lock object if successful, else undef. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=item locked () |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
Return true if the lock has been obtained. |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=item lock_name () |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Return the name of the lock. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=item unlock () |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
Remove the given lock. This will be called automatically when the object |
57
|
|
|
|
|
|
|
is destroyed. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=item ping () |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
A simplified version of ping_status; polls the server to see if it is up. |
62
|
|
|
|
|
|
|
Returns true if up, otherwise undef. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=item ping_status () |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Polls the server to see if it is up. Returns hash reference with {ok} |
67
|
|
|
|
|
|
|
indicating if up, and {status} with status information. If called without |
68
|
|
|
|
|
|
|
an object, defaults to call new() with connect_tries=>1, under the |
69
|
|
|
|
|
|
|
assumption that a quick go/nogo response is desired. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=item break_lock () |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Remove current locker for the given lock. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item owner ([parameter=>value ...]); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Returns a string of who has the lock or undef if not currently locked. |
78
|
|
|
|
|
|
|
Note that this information is not atomic, and may change asynchronously; do |
79
|
|
|
|
|
|
|
not use this to tell if the lock will be available, to do that, try to |
80
|
|
|
|
|
|
|
obtain the lock and then release it if you got it. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=back |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=head1 PARAMETERS |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=over 4 |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=item block |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Boolean flag, true indicates wait for the lock when calling lock() and die |
91
|
|
|
|
|
|
|
if an error occurs. False indicates to just return false. Defaults to |
92
|
|
|
|
|
|
|
true. |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=item connect_tries |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
If none of the lockerd hosts are available or other network errors are |
97
|
|
|
|
|
|
|
encountered, perform this number of retries, with a random connect_delay to |
98
|
|
|
|
|
|
|
connect_delay*2 interval between them before signalling an error. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=item connect_delay |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
The minimum seconds to wait between each of the connect_tries, and |
103
|
|
|
|
|
|
|
one-half of the maximum random wait. Defaults to 30 seconds. |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=item destroy_unlock |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
Boolean flag, true indicates destruction of the lock variable should unlock |
108
|
|
|
|
|
|
|
the lock, only if the current process id matches the pid passed to the |
109
|
|
|
|
|
|
|
constructor. Set to false if destruction should not close the lock, such |
110
|
|
|
|
|
|
|
as when other children destroying the lock variable should not unlock the |
111
|
|
|
|
|
|
|
lock. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=item family |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
The family of transport to use, either INET or UNIX. Defaults to INET. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=item host |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
The name of the host containing the lock server. It may also be an array |
120
|
|
|
|
|
|
|
of hostnames, where if the first one is down, subsequent ones will be |
121
|
|
|
|
|
|
|
tried. Defaults to value of IPCLOCKER_HOST or localhost. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=item port |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
The port number (INET) or name (UNIX) of the lock server. Defaults to |
126
|
|
|
|
|
|
|
IPCLOCKER_PORT environment variable, else 'lockerd' looked up via |
127
|
|
|
|
|
|
|
/etc/services, else 1751. |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=item lock |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
The name of the lock. This may also be a reference to an array of lock names, |
132
|
|
|
|
|
|
|
and the first free lock will be returned. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=item lock_list |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Return a list of lock and lock owner pairs. (You can assign this to a hash |
137
|
|
|
|
|
|
|
for easier parsing.) |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=item pid |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
The process ID that owns the lock, defaults to the current process id. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=item print_broke |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
A function to print a message when the lock is broken. The only argument |
146
|
|
|
|
|
|
|
is self. Defaults to print a message if verbose is set. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=item print_down |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
A function to print a message when the lock server is unavailable. The |
151
|
|
|
|
|
|
|
first argument is self. Defaults to a croak message. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=item print_obtained |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
A function to print a message when the lock is obtained after a delay. The |
156
|
|
|
|
|
|
|
only argument is self. Defaults to print a message if verbose is set. |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=item print_retry |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
A function to print a message when the lock server is unavailable, and is |
161
|
|
|
|
|
|
|
about to be retried. The first argument is self. Defaults to a print |
162
|
|
|
|
|
|
|
message. |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=item print_waiting |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
A function to print a message when the lock is busy and needs to be waited |
167
|
|
|
|
|
|
|
for. The first argument is self, second the name of the lock. Defaults to |
168
|
|
|
|
|
|
|
print a message if verbose is set. |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=item timeout |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
The maximum time in seconds that the lock may be held before being forced |
173
|
|
|
|
|
|
|
open, passed to the server when the lock is created. Thus if the requester |
174
|
|
|
|
|
|
|
dies, the lock will be released after that amount of time. Zero disables |
175
|
|
|
|
|
|
|
the timeout. Defaults to 30 minutes. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=item user |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Name to request the lock under, defaults to host_pid_user |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=item autounlock |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
True to cause the server to automatically timeout a lock if the locking |
184
|
|
|
|
|
|
|
process has died. For the process to be detected, it must be on the same |
185
|
|
|
|
|
|
|
host as either the locker client (the host making the lock call), or the |
186
|
|
|
|
|
|
|
locker server. Defaults false. |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=item verbose |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
True to print messages when waiting for locks. Defaults false. |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=back |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head1 ENVIRONMENT |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=over 4 |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=item IPCLOCKER_HOST |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Hostname of L server, or colon separated list including backup |
201
|
|
|
|
|
|
|
servers. Defaults to localhost. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=item IPCLOCKER_PORT |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
The port number (INET) or name (UNIX) of the lock server. Defaults to |
206
|
|
|
|
|
|
|
'lockerd' looked up via /etc/services, else 1751. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=back |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=head1 DISTRIBUTION |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
The latest version is available from CPAN and from L. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Copyright 1999-2022 by Wilson Snyder. This package is free software; you |
215
|
|
|
|
|
|
|
can redistribute it and/or modify it under the terms of either the GNU |
216
|
|
|
|
|
|
|
Lesser General Public License Version 3 or the Perl Artistic License Version 2.0. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=head1 AUTHORS |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
Wilson Snyder |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=head1 SEE ALSO |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
L, L |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
L, L, L, L |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=cut |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
###################################################################### |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
package IPC::Locker; |
233
|
|
|
|
|
|
|
require 5.004; |
234
|
|
|
|
|
|
|
require Exporter; |
235
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
236
|
|
|
|
|
|
|
|
237
|
3
|
|
|
3
|
|
79995
|
use Socket; |
|
3
|
|
|
|
|
14
|
|
|
3
|
|
|
|
|
1088
|
|
238
|
3
|
|
|
3
|
|
1282
|
use Time::HiRes qw(gettimeofday tv_interval); |
|
3
|
|
|
|
|
3406
|
|
|
3
|
|
|
|
|
8
|
|
239
|
3
|
|
|
3
|
|
466
|
use IO::Socket; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
14
|
|
240
|
|
|
|
|
|
|
|
241
|
3
|
|
|
3
|
|
2547
|
use IPC::PidStat; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
88
|
|
242
|
3
|
|
|
3
|
|
14
|
use strict; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
65
|
|
243
|
3
|
|
|
3
|
|
21
|
use vars qw($VERSION $Debug $Default_Port $Default_Family $Default_UNIX_port $Default_PidStat_Port); |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
170
|
|
244
|
3
|
|
|
3
|
|
14
|
use Carp; |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
7504
|
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
###################################################################### |
247
|
|
|
|
|
|
|
#### Configuration Section |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# Other configurable settings. |
250
|
|
|
|
|
|
|
$Debug = 0; |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
$VERSION = '1.502'; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
###################################################################### |
255
|
|
|
|
|
|
|
#### Useful Globals |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
$Default_Port = ($ENV{IPCLOCKER_PORT}||'lockerd'); # Number (1751) or name to lookup in /etc/services |
258
|
|
|
|
|
|
|
$Default_Port = 1751 if ($Default_Port !~ /^\d+$/ && !getservbyname ($Default_Port,"")); |
259
|
|
|
|
|
|
|
$Default_PidStat_Port = 'pidstatd'; # Number (1752) or name to lookup in /etc/services |
260
|
|
|
|
|
|
|
$Default_PidStat_Port = 1752 if !getservbyname ($Default_PidStat_Port,""); |
261
|
|
|
|
|
|
|
$Default_Family = 'INET'; |
262
|
|
|
|
|
|
|
$Default_UNIX_port = '/var/locks/lockerd'; |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
###################################################################### |
265
|
|
|
|
|
|
|
#### Creator |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub new { |
268
|
6
|
50
|
|
6
|
1
|
1005725
|
@_ >= 1 or croak 'usage: IPC::Locker->new ({options})'; |
269
|
6
|
|
|
|
|
17
|
my $proto = shift; |
270
|
6
|
|
33
|
|
|
48
|
my $class = ref($proto) || $proto; |
271
|
6
|
|
|
|
|
15
|
my $hostname = hostfqdn(); |
272
|
|
|
|
|
|
|
my $self = { |
273
|
|
|
|
|
|
|
#Documented |
274
|
|
|
|
|
|
|
host=>($ENV{IPCLOCKER_HOST}||'localhost'), |
275
|
|
|
|
|
|
|
port=>$Default_Port, |
276
|
|
|
|
|
|
|
lock=>['lock'], |
277
|
|
|
|
|
|
|
timeout=>60*10, block=>1, |
278
|
|
|
|
|
|
|
pid=>$$, |
279
|
|
|
|
|
|
|
#user=> # below |
280
|
|
|
|
|
|
|
hostname=>$hostname, |
281
|
|
|
|
|
|
|
autounlock=>0, |
282
|
|
|
|
|
|
|
destroy_unlock=>1, |
283
|
|
|
|
|
|
|
verbose=>$Debug, |
284
|
|
|
|
|
|
|
connect_tries=>3, |
285
|
|
|
|
|
|
|
connect_delay=>30, |
286
|
0
|
0
|
|
0
|
|
0
|
print_broke=>sub {my $self=shift; print "Broke lock from $_[0] at ".(scalar(localtime))."\n" if $self->{verbose};}, |
|
0
|
|
|
|
|
0
|
|
287
|
0
|
0
|
|
0
|
|
0
|
print_obtained=>sub {my $self=shift; print "Obtained lock at ".(scalar(localtime))."\n" if $self->{verbose};}, |
|
0
|
|
|
|
|
0
|
|
288
|
0
|
0
|
|
0
|
|
0
|
print_waiting=>sub {my $self=shift; print "Waiting for lock from $_[0] at ".(scalar(localtime))."\n" if $self->{verbose};}, |
|
0
|
|
|
|
|
0
|
|
289
|
0
|
0
|
|
0
|
|
0
|
print_retry=>sub {my ($self,$sleep)=@_; print "Unable to connect to server, retrying connection in ${sleep} sec at ".(scalar(localtime))."\n" if $self->{verbose};}, |
|
0
|
|
|
|
|
0
|
|
290
|
6
|
|
50
|
|
|
240
|
print_down=>undef, |
291
|
|
|
|
|
|
|
family=>$Default_Family, |
292
|
|
|
|
|
|
|
#Internal |
293
|
|
|
|
|
|
|
locked=>0, |
294
|
|
|
|
|
|
|
@_,}; |
295
|
6
|
|
50
|
|
|
37
|
$self->{user} ||= hostfqdn() . "_".$self->{pid}."_" . ($ENV{USER} || ""); |
|
|
|
66
|
|
|
|
|
296
|
6
|
|
|
|
|
24
|
foreach (_array_or_one($self->{lock})) { |
297
|
6
|
50
|
|
|
|
33
|
($_ !~ /\s/) or carp "%Error: Lock names cannot contain whitespace: $_\n"; |
298
|
|
|
|
|
|
|
} |
299
|
6
|
|
|
|
|
13
|
bless $self, $class; |
300
|
6
|
50
|
|
|
|
16
|
_timelog("Locker->new ",$self->lock_name_list,"\n") if $Debug; |
301
|
6
|
|
|
|
|
18
|
return $self; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
###################################################################### |
305
|
|
|
|
|
|
|
#### Static Accessors |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub hostfqdn { |
308
|
11
|
|
|
11
|
0
|
37
|
return IPC::PidStat::hostfqdn(); |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
###################################################################### |
312
|
|
|
|
|
|
|
#### Accessors |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub locked () { |
315
|
21
|
50
|
33
|
21
|
1
|
31
|
my $self = shift; ($self && ref($self)) or croak 'usage: $self->locked()'; |
|
21
|
|
|
|
|
71
|
|
316
|
21
|
100
|
|
|
|
43
|
return $self if $self->{locked}; |
317
|
12
|
|
|
|
|
29
|
return undef; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub ping { |
321
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
322
|
1
|
|
|
|
|
3
|
my $res = $self->ping_status(@_); |
323
|
1
|
50
|
|
|
|
4
|
if ($res->{ok}) { |
324
|
1
|
|
|
|
|
4
|
return $self; |
325
|
|
|
|
|
|
|
} else { |
326
|
0
|
|
|
|
|
0
|
return undef; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
sub ping_status { |
331
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
332
|
|
|
|
|
|
|
# Return OK and status message, for nagios like checks |
333
|
1
|
50
|
|
|
|
3
|
$self = $self->new(connect_tries=>1, @_) if (!ref($self)); |
334
|
1
|
|
|
|
|
2
|
my $ok = 0; |
335
|
1
|
|
|
|
|
4
|
my $start_time = [gettimeofday()]; |
336
|
1
|
|
|
|
|
2
|
eval { |
337
|
1
|
|
|
|
|
3
|
$self->_request(""); |
338
|
1
|
|
|
|
|
2
|
$ok = 1; |
339
|
|
|
|
|
|
|
}; |
340
|
1
|
|
|
|
|
23
|
my $elapsed = tv_interval ( $start_time, [gettimeofday]); |
341
|
|
|
|
|
|
|
|
342
|
1
|
50
|
|
|
|
19
|
if (!$ok) { |
343
|
0
|
|
|
|
|
0
|
return ({ok=>undef,status=>"No response from lockerd on $self->{host}:$self->{port}"}); |
344
|
|
|
|
|
|
|
} else { |
345
|
1
|
|
|
|
|
19
|
return ({ok=>1,status=>sprintf("%1.3f second response on $self->{host}:$self->{port}", $elapsed)}); |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
###################################################################### |
350
|
|
|
|
|
|
|
#### Constructor |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub lock { |
353
|
8
|
|
|
8
|
1
|
957
|
my $self = shift; |
354
|
8
|
100
|
|
|
|
24
|
$self = $self->new(@_) if (!ref($self)); |
355
|
8
|
100
|
|
|
|
14
|
if (!$self->locked) { |
356
|
6
|
|
|
|
|
20
|
$self->_request("LOCK"); |
357
|
6
|
50
|
|
|
|
17
|
croak $self->{error} if $self->{error}; |
358
|
|
|
|
|
|
|
} |
359
|
8
|
100
|
|
|
|
39
|
return ($self) if $self->{locked}; |
360
|
2
|
|
|
|
|
15
|
return undef; |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
###################################################################### |
364
|
|
|
|
|
|
|
#### Destructor/Unlock |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
sub DESTROY () { |
367
|
6
|
50
|
33
|
6
|
|
601
|
my $self = shift; ($self && ref($self)) or croak 'usage: $self->DESTROY()'; |
|
6
|
|
|
|
|
27
|
|
368
|
6
|
50
|
33
|
|
|
32
|
if ($self->{destroy_unlock} && $self->{pid} && $self->{pid}==$$) { |
|
|
|
33
|
|
|
|
|
369
|
6
|
|
|
|
|
12
|
$self->unlock(); |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
sub unlock { |
374
|
8
|
50
|
33
|
8
|
1
|
8
|
my $self = shift; ($self && ref($self)) or croak 'usage: $self->unlock()'; |
|
8
|
|
|
|
|
21
|
|
375
|
8
|
100
|
|
|
|
14
|
if ($self->locked) { |
376
|
4
|
|
|
|
|
8
|
$self->_request("UNLOCK"); |
377
|
4
|
50
|
|
|
|
10
|
croak $self->{error} if $self->{error}; |
378
|
|
|
|
|
|
|
} |
379
|
8
|
|
|
|
|
198
|
return ($self); |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub break_lock { |
383
|
0
|
0
|
|
0
|
1
|
0
|
my $self = shift; ($self) or croak 'usage: $self->break_lock()'; |
|
0
|
|
|
|
|
0
|
|
384
|
0
|
0
|
|
|
|
0
|
$self = $self->new(@_) if (!ref($self)); |
385
|
0
|
|
|
|
|
0
|
$self->_request("BREAK_LOCK"); |
386
|
0
|
0
|
|
|
|
0
|
croak $self->{error} if $self->{error}; |
387
|
0
|
|
|
|
|
0
|
return ($self); |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub dead_pid { |
391
|
0
|
0
|
|
0
|
0
|
0
|
my $self = shift; (ref $self) or croak 'usage: $self->dead_pid()'; |
|
0
|
|
|
|
|
0
|
|
392
|
0
|
|
|
|
|
0
|
my %args = (host => hostfqdn(), |
393
|
|
|
|
|
|
|
pid => -1, |
394
|
|
|
|
|
|
|
@_); |
395
|
|
|
|
|
|
|
# Used internally to indicate a pid is gone. |
396
|
0
|
|
|
|
|
0
|
$self->_request("DEAD_PID $args{host} $args{pid}"); |
397
|
0
|
0
|
|
|
|
0
|
croak $self->{error} if $self->{error}; |
398
|
0
|
|
|
|
|
0
|
return ($self); |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
###################################################################### |
402
|
|
|
|
|
|
|
#### User utilities: owner |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
sub owner { |
405
|
1
|
50
|
|
1
|
1
|
2
|
my $self = shift; ($self) or croak 'usage: $self->status()'; |
|
1
|
|
|
|
|
3
|
|
406
|
1
|
50
|
|
|
|
2
|
$self = $self->new(@_) if (!ref($self)); |
407
|
1
|
|
|
|
|
3
|
$self->_request ("STATUS"); |
408
|
1
|
50
|
|
|
|
3
|
croak $self->{error} if $self->{error}; |
409
|
1
|
50
|
0
|
|
|
3
|
_timelog("Locker->owner = ",($self->{owner}||''),"\n") if $Debug; |
410
|
1
|
|
|
|
|
4
|
return $self->{owner}; |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
sub lock_name { |
414
|
2
|
50
|
|
2
|
1
|
248
|
my $self = shift; ($self) or croak 'usage: $self->lock_name()'; |
|
2
|
|
|
|
|
5
|
|
415
|
2
|
50
|
33
|
|
|
5
|
if (ref $self->{lock} |
416
|
0
|
|
|
|
|
0
|
&& $#{$self->{lock}}<1) { |
417
|
0
|
|
|
|
|
0
|
return $self->{lock}[0]; |
418
|
|
|
|
|
|
|
} else { |
419
|
2
|
|
|
|
|
9
|
return $self->{lock}; |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub lock_list { |
424
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
425
|
1
|
50
|
|
|
|
3
|
$self = $self->new(@_) if (!ref($self)); |
426
|
1
|
|
|
|
|
3
|
$self->_request("LOCK_LIST"); |
427
|
1
|
50
|
|
|
|
3
|
croak $self->{error} if $self->{error}; |
428
|
1
|
|
|
|
|
2
|
return @{$self->{lock_list}}; |
|
1
|
|
|
|
|
9
|
|
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
###################################################################### |
432
|
|
|
|
|
|
|
###################################################################### |
433
|
|
|
|
|
|
|
#### Guts: Sending and receiving messages |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
sub _request { |
436
|
13
|
|
|
13
|
|
14
|
my $self = shift; |
437
|
13
|
|
|
|
|
46
|
my $cmd = shift; |
438
|
|
|
|
|
|
|
|
439
|
13
|
|
|
|
|
29
|
my @hostlist = ('localhost'); |
440
|
13
|
50
|
|
|
|
26
|
if ($self->{family} eq 'INET') { |
441
|
13
|
|
|
|
|
17
|
@hostlist = ($self->{host}); |
442
|
13
|
50
|
|
|
|
44
|
@hostlist = split (':', $self->{host}) if (!ref($self->{host})); |
443
|
13
|
50
|
|
|
|
21
|
@hostlist = @{$self->{host}} if (ref($self->{host}) eq "ARRAY"); |
|
0
|
|
|
|
|
0
|
|
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
13
|
|
|
|
|
12
|
my $ok; |
447
|
|
|
|
|
|
|
try: |
448
|
13
|
|
50
|
|
|
35
|
for (my $tries = 0; $tries < ($self->{connect_tries}||1); $tries++) { |
449
|
13
|
50
|
|
|
|
17
|
if ($tries > 0) { |
450
|
0
|
|
|
|
|
0
|
my $sleep = $self->{connect_delay} + int(rand($self->{connect_delay})); |
451
|
0
|
0
|
|
|
|
0
|
_timelog("Locker->connect_delay $sleep sec\n") if $Debug; |
452
|
0
|
|
|
|
|
0
|
&{$self->{print_retry}} ($self, $sleep); |
|
0
|
|
|
|
|
0
|
|
453
|
0
|
|
|
|
|
0
|
sleep($sleep); |
454
|
|
|
|
|
|
|
} |
455
|
13
|
|
|
|
|
15
|
foreach my $host (@hostlist) { |
456
|
13
|
|
|
|
|
31
|
$ok = $self->_request_attempt($cmd,$host); |
457
|
13
|
50
|
|
|
|
24
|
if ($ok) { |
458
|
13
|
50
|
|
|
|
21
|
if ($host ne $hostlist[0]) { |
459
|
|
|
|
|
|
|
# Reorganize host list so whoever responded is first |
460
|
|
|
|
|
|
|
# This is so if we grab a lock we'll try to return it to the same host |
461
|
0
|
|
|
|
|
0
|
$self->{host} = [$host, grep( ($_ ne $host), @hostlist)]; |
462
|
|
|
|
|
|
|
} |
463
|
13
|
|
|
|
|
21
|
last try; |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
|
468
|
13
|
50
|
|
|
|
17
|
if (!$ok) { |
469
|
0
|
0
|
|
|
|
0
|
if (defined $self->{print_down}) { |
470
|
0
|
|
|
|
|
0
|
&{$self->{print_down}} ($self); |
|
0
|
|
|
|
|
0
|
|
471
|
0
|
|
|
|
|
0
|
return; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
croak "%Error: Can't locate lock server on " |
474
|
0
|
0
|
|
|
|
0
|
. (($self->{family} eq 'INET') ? (join " or ", @hostlist) : "UNIX port") |
475
|
|
|
|
|
|
|
." $self->{port}\n" |
476
|
|
|
|
|
|
|
. "\tYou probably need to run lockerd\n$self->_request(): Stopped"; |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
13
|
50
|
|
|
|
25
|
_timelog("Locker->DONE\n") if $Debug; |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub _request_attempt { |
483
|
13
|
|
|
13
|
|
14
|
my $self = shift; |
484
|
13
|
|
|
|
|
12
|
my $cmd = shift; |
485
|
13
|
|
|
|
|
27
|
my $host = shift; |
486
|
|
|
|
|
|
|
# Return true if request was successful |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# IO::Socket::INET nastily undef's $@. Since this may get called |
489
|
|
|
|
|
|
|
# in a destructor due to an error, that looses the error message. |
490
|
|
|
|
|
|
|
# Workaround: save the error and restore at the end. |
491
|
13
|
|
|
|
|
12
|
my $preerror = $@; |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
retry: |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# If adding new features, only send the new feature to the server |
496
|
|
|
|
|
|
|
# if the feature is on. This allows for newer clients that don't |
497
|
|
|
|
|
|
|
# need to the new feature to still talk to older servers. |
498
|
|
|
|
|
|
|
my $req = ("user $self->{user}\n" |
499
|
13
|
|
|
|
|
24
|
."locks ".join(' ',@{_array_or_one($self->{lock})})."\n"); |
|
13
|
|
|
|
|
18
|
|
500
|
|
|
|
|
|
|
$req.= ("block ".($self->{block}||0)."\n" |
501
|
13
|
100
|
100
|
|
|
52
|
."timeout ".($self->{timeout}||0)."\n") if $cmd ne 'UNLOCK'; |
|
|
|
50
|
|
|
|
|
502
|
|
|
|
|
|
|
$req.= ("autounlock ".($self->{autounlock}||0)."\n" |
503
|
|
|
|
|
|
|
."pid ".($self->{pid}||$$)."\n" |
504
|
|
|
|
|
|
|
."hostname ".($self->{hostname})."\n" |
505
|
13
|
100
|
50
|
|
|
47
|
) if $self->{autounlock} && $cmd ne 'UNLOCK'; |
|
|
|
33
|
|
|
|
|
|
|
|
100
|
|
|
|
|
506
|
13
|
|
|
|
|
23
|
$req.= ("$cmd\n" |
507
|
|
|
|
|
|
|
."\n" # End of group. Some day we may not always send EOF immediately |
508
|
|
|
|
|
|
|
."EOF\n"); |
509
|
13
|
50
|
|
|
|
17
|
_timelog("Locker->REQ\nR ",join("\nR ",split(/\n/,$req)),"\n") if $Debug; |
510
|
|
|
|
|
|
|
|
511
|
13
|
|
|
|
|
12
|
my $fh; |
512
|
13
|
50
|
|
|
|
22
|
if ($self->{family} eq 'INET') { |
|
|
0
|
|
|
|
|
|
513
|
13
|
50
|
|
|
|
15
|
_timelog("Locker->Trying host $host $self->{port}\n") if $Debug; |
514
|
|
|
|
|
|
|
$fh = IO::Socket::INET->new( Proto => _tcp_proto(), |
515
|
|
|
|
|
|
|
PeerAddr => $host, |
516
|
13
|
|
|
|
|
39
|
PeerPort => $self->{port}, ); |
517
|
|
|
|
|
|
|
} elsif ($self->{family} eq 'UNIX') { |
518
|
0
|
0
|
|
|
|
0
|
_timelog("Locker->Trying UNIX socket\n") if $Debug; |
519
|
0
|
|
|
|
|
0
|
$fh = IO::Socket::UNIX->new( Peer => $self->{port}, ); |
520
|
|
|
|
|
|
|
} else { |
521
|
0
|
|
|
|
|
0
|
croak "IPC::Locker->_request(): No or wrong transport specified."; |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
|
524
|
13
|
50
|
|
|
|
4366
|
return undef if !$fh; |
525
|
|
|
|
|
|
|
|
526
|
13
|
|
|
|
|
37
|
$self->{lock_list} = []; |
527
|
|
|
|
|
|
|
|
528
|
13
|
|
|
|
|
328
|
print $fh "$req\n"; |
529
|
13
|
|
|
|
|
9277
|
while (defined (my $line = <$fh>)) { |
530
|
52
|
|
|
|
|
69
|
chomp $line; |
531
|
52
|
100
|
|
|
|
544
|
next if $line =~ /^\s*$/; |
532
|
28
|
|
|
|
|
87
|
my @args = split /\s+/, $line; |
533
|
28
|
|
|
|
|
39
|
my $cmd = shift @args; |
534
|
28
|
50
|
|
|
|
41
|
_timelog("RESP $line\n") if $Debug; |
535
|
28
|
100
|
|
|
|
40
|
$self->{locked} = $args[0] if ($cmd eq "locked"); |
536
|
28
|
100
|
|
|
|
42
|
$self->{owner} = $args[0] if ($cmd eq "owner"); |
537
|
28
|
50
|
|
|
|
33
|
$self->{error} = $args[0] if ($cmd eq "error"); |
538
|
28
|
100
|
|
|
|
32
|
if ($cmd eq "lockname") { # LOCK request's reply |
539
|
5
|
|
|
|
|
10
|
$self->{lock} = [$args[0]]; |
540
|
5
|
50
|
|
|
|
5
|
$self->{lock} = $self->{lock}[0] if ($#{$self->{lock}}<1); # Back compatible |
|
5
|
|
|
|
|
15
|
|
541
|
|
|
|
|
|
|
} |
542
|
28
|
100
|
66
|
|
|
60
|
if ($cmd eq 'lock' && @args == 2) { # LOCK_LIST request's reply |
543
|
1
|
|
|
|
|
3
|
push @{$self->{lock_list}}, @args; |
|
1
|
|
|
|
|
3
|
|
544
|
|
|
|
|
|
|
} |
545
|
28
|
50
|
|
|
|
30
|
if ($cmd eq "autounlock_check") { |
546
|
|
|
|
|
|
|
# See if we can break the lock because the lock holder ran on this same machine. |
547
|
0
|
|
|
|
|
0
|
my ($lname,$lhost,$lpid,$supports_dead) = @args; |
548
|
0
|
0
|
|
|
|
0
|
if ($self->{hostname} eq $lhost) { |
549
|
0
|
0
|
|
|
|
0
|
if (IPC::PidStat::local_pid_doesnt_exist($lpid)) { |
550
|
0
|
0
|
|
|
|
0
|
_timelog("Autounlock_LOCAL $lname $lhost $lpid $supports_dead\n") if $Debug; |
551
|
0
|
0
|
|
|
|
0
|
if ($supports_dead) { # 1.480 server and newer |
552
|
0
|
|
|
|
|
0
|
$self->dead_pid(host=>$lhost, pid=>$lpid); |
553
|
|
|
|
|
|
|
} else { # This has a potential race case, which may kill the wrong lock |
554
|
0
|
|
|
|
|
0
|
$self->break_lock(lock=>$self->{lock}); |
555
|
|
|
|
|
|
|
} |
556
|
0
|
|
|
|
|
0
|
$fh->close(); |
557
|
0
|
|
|
|
|
0
|
goto retry; |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
} |
561
|
28
|
50
|
|
|
|
35
|
&{$self->{print_obtained}} ($self,@args) if ($cmd eq "print_obtained"); |
|
0
|
|
|
|
|
0
|
|
562
|
28
|
50
|
|
|
|
32
|
&{$self->{print_waiting}} ($self,@args) if ($cmd eq "print_waiting"); |
|
0
|
|
|
|
|
0
|
|
563
|
28
|
50
|
|
|
|
33
|
&{$self->{print_broke}} ($self,@args) if ($cmd eq "print_broke"); |
|
0
|
|
|
|
|
0
|
|
564
|
28
|
0
|
33
|
|
|
79
|
print "$1\n" if ($line =~ /^ECHO\s+(.*)$/ && $self->{verbose}); #debugging |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
# Note above break_lock also has prologue close |
567
|
13
|
|
|
|
|
68
|
$fh->close(); |
568
|
|
|
|
|
|
|
|
569
|
13
|
|
66
|
|
|
645
|
$@ = $preerror || $@; # User's error is more important than any we make |
570
|
13
|
|
|
|
|
56
|
return 1; |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
###################################################################### |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
our $_Tcp_Proto; |
576
|
|
|
|
|
|
|
sub _tcp_proto { |
577
|
|
|
|
|
|
|
# We don't want creating a socket to have to keep reading /etc/services |
578
|
|
|
|
|
|
|
# One would have thought IO::Socket etc kept this for us... |
579
|
13
|
100
|
|
13
|
|
19
|
if (!defined $_Tcp_Proto) { |
580
|
1
|
50
|
|
|
|
109
|
$_Tcp_Proto = getprotobyname("tcp") |
581
|
|
|
|
|
|
|
or die "Could not determine the protocol number for tcp"; |
582
|
|
|
|
|
|
|
} |
583
|
13
|
|
|
|
|
152
|
return $_Tcp_Proto; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
sub _array_or_one { |
587
|
19
|
100
|
|
19
|
|
52
|
return [$_[0]] if !ref $_[0]; |
588
|
10
|
|
|
|
|
24
|
return $_[0]; |
589
|
|
|
|
|
|
|
} |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
sub colon_joined_list { |
592
|
0
|
|
|
0
|
0
|
|
my $item = shift; |
593
|
0
|
0
|
|
|
|
|
return $item if !ref $item; |
594
|
0
|
|
|
|
|
|
return (join ":",@{$item}); |
|
0
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
} |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
sub lock_name_list { |
598
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
599
|
0
|
|
|
|
|
|
return colon_joined_list($self->{lock}); |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
###################################################################### |
603
|
|
|
|
|
|
|
#### Logging |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
sub _timelog { |
606
|
0
|
|
|
0
|
|
|
my $msg = join('',@_); |
607
|
0
|
|
|
|
|
|
my ($time, $time_usec) = Time::HiRes::gettimeofday(); |
608
|
0
|
|
|
|
|
|
my ($sec,$min,$hour,$mday,$mon) = localtime($time); |
609
|
0
|
|
|
|
|
|
printf +("[%02d/%02d %02d:%02d:%02d.%06d] %s", |
610
|
|
|
|
|
|
|
$mon+1, $mday, $hour, $min, $sec, $time_usec, $msg); |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
sub _timelog_split { |
614
|
0
|
|
|
0
|
|
|
my $first = shift; |
615
|
0
|
|
|
|
|
|
my $prefix = shift; |
616
|
0
|
|
|
|
|
|
my $text = shift; |
617
|
0
|
|
|
|
|
|
my $msg = $first . join("\n$prefix", split(/\n+/, "\n$text")) . "\n"; |
618
|
0
|
|
|
|
|
|
_timelog($msg) |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
###################################################################### |
622
|
|
|
|
|
|
|
#### Package return |
623
|
|
|
|
|
|
|
1; |