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::Server - Distributed lock handler server |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 SYNOPSIS |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
use IPC::Locker::Server; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
IPC::Locker::Server->new(port=>1234)->start_server; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Or more typically via the command line |
15
|
|
|
|
|
|
|
lockerd |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 DESCRIPTION |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
L provides the server for the IPC::Locker package. |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=over 4 |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=item new ([parameter=>value ...]); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
Creates a server object. |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=item start_server ([parameter=>value ...]); |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Starts the server. Does not return. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=back |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head1 PARAMETERS |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=over 4 |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=item family |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
The family of transport to use, either INET or UNIX. Defaults to INET. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=item port |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
The port number (INET) or name (UNIX) of the lock server. Defaults to |
44
|
|
|
|
|
|
|
'lockerd' looked up via /etc/services, else 1751. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=back |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head1 DISTRIBUTION |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
The latest version is available from CPAN and from L. |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Copyright 1999-2019 by Wilson Snyder. This package is free software; you |
53
|
|
|
|
|
|
|
can redistribute it and/or modify it under the terms of either the GNU |
54
|
|
|
|
|
|
|
Lesser General Public License Version 3 or the Perl Artistic License Version 2.0. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head1 AUTHORS |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Wilson Snyder |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head1 SEE ALSO |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
L, L |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=cut |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
###################################################################### |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
package IPC::Locker::Server; |
69
|
|
|
|
|
|
|
require 5.006; |
70
|
|
|
|
|
|
|
require Exporter; |
71
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
72
|
|
|
|
|
|
|
|
73
|
2
|
|
|
2
|
|
89442
|
use IPC::Locker; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
78
|
|
74
|
2
|
|
|
2
|
|
10
|
use Socket; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
827
|
|
75
|
2
|
|
|
2
|
|
14
|
use IO::Socket; |
|
2
|
|
|
|
|
21
|
|
|
2
|
|
|
|
|
16
|
|
76
|
2
|
|
|
2
|
|
2269
|
use IO::Poll qw(POLLIN POLLOUT POLLERR POLLHUP POLLNVAL); |
|
2
|
|
|
|
|
1432
|
|
|
2
|
|
|
|
|
123
|
|
77
|
2
|
|
|
2
|
|
11
|
use Time::HiRes; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
11
|
|
78
|
|
|
|
|
|
|
|
79
|
2
|
|
|
2
|
|
147
|
use IPC::PidStat; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
31
|
|
80
|
2
|
|
|
2
|
|
7
|
use strict; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
37
|
|
81
|
2
|
|
|
2
|
|
7
|
use vars qw($VERSION $Debug %Locks %Clients $Poll $Interrupts $Hostname $Exister); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
144
|
|
82
|
2
|
|
|
2
|
|
10
|
use Carp; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
8673
|
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
###################################################################### |
85
|
|
|
|
|
|
|
#### Configuration Section |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# Other configurable settings. |
88
|
|
|
|
|
|
|
$Debug = 0; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
$VERSION = '1.500'; |
91
|
|
|
|
|
|
|
$Hostname = IPC::Locker::hostfqdn(); |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
###################################################################### |
94
|
|
|
|
|
|
|
#### Globals |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# All held locks |
97
|
|
|
|
|
|
|
%Locks = (); |
98
|
|
|
|
|
|
|
our $_Client_Num = 0; # Debug use only |
99
|
|
|
|
|
|
|
our $StartTime = time(); |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
our $RecheckLockDelta = 1; # Loop all locks every N seconds |
102
|
|
|
|
|
|
|
our $PollDelta = 1; # Poll every N seconds for activity |
103
|
|
|
|
|
|
|
our $AutoUnlockCheckDelta = 2; # Check every N seconds for pid existance |
104
|
|
|
|
|
|
|
our $AutoUnlockCheckPerSec = 100; # Check at most N existances per second |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
###################################################################### |
107
|
|
|
|
|
|
|
#### Creator |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub new { |
110
|
|
|
|
|
|
|
# Establish the server |
111
|
0
|
0
|
|
0
|
1
|
|
@_ >= 1 or croak 'usage: IPC::Locker::Server->new ({options})'; |
112
|
0
|
|
|
|
|
|
my $proto = shift; |
113
|
0
|
|
0
|
|
|
|
my $class = ref($proto) || $proto; |
114
|
0
|
|
|
|
|
|
my $self = { |
115
|
|
|
|
|
|
|
#Documented |
116
|
|
|
|
|
|
|
port=>$IPC::Locker::Default_Port, |
117
|
|
|
|
|
|
|
family=>$IPC::Locker::Default_Family, |
118
|
|
|
|
|
|
|
host=>'localhost', |
119
|
|
|
|
|
|
|
@_,}; |
120
|
0
|
|
|
|
|
|
bless $self, $class; |
121
|
0
|
|
|
|
|
|
my $param = {@_}; |
122
|
0
|
0
|
0
|
|
|
|
if (defined $param->{family} && $param->{family} eq 'UNIX' |
|
|
|
0
|
|
|
|
|
123
|
|
|
|
|
|
|
&& !exists($param->{port})) { |
124
|
0
|
|
|
|
|
|
$self->{port} = $IPC::Locker::Default_UNIX_port; |
125
|
|
|
|
|
|
|
} |
126
|
0
|
|
|
|
|
|
return $self; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub start_server { |
130
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# Open the socket |
133
|
0
|
0
|
|
|
|
|
_timelog("Listening on $self->{port}\n") if $Debug; |
134
|
0
|
|
|
|
|
|
my $server; |
135
|
0
|
0
|
|
|
|
|
if ($self->{family} eq 'INET') { |
|
|
0
|
|
|
|
|
|
136
|
|
|
|
|
|
|
$server = IO::Socket::INET->new( Proto => 'tcp', |
137
|
|
|
|
|
|
|
LocalAddr => $self->{host}, |
138
|
|
|
|
|
|
|
LocalPort => $self->{port}, |
139
|
0
|
0
|
|
|
|
|
Listen => SOMAXCONN, |
140
|
|
|
|
|
|
|
Reuse => 1) |
141
|
|
|
|
|
|
|
or die "$0: Error, socket: $!"; |
142
|
|
|
|
|
|
|
} elsif ($self->{family} eq 'UNIX') { |
143
|
|
|
|
|
|
|
$server = IO::Socket::UNIX->new(Local => $self->{port}, |
144
|
0
|
0
|
|
|
|
|
Listen => SOMAXCONN, |
145
|
|
|
|
|
|
|
Reuse => 1) |
146
|
|
|
|
|
|
|
or die "$0: Error, socket: $!\n port=$self->{port}="; |
147
|
0
|
|
|
|
|
|
$self->{unix_socket_created}=1; |
148
|
|
|
|
|
|
|
} else { |
149
|
0
|
|
|
|
|
|
die "IPC::Locker::Server: What transport do you want to use?"; |
150
|
|
|
|
|
|
|
} |
151
|
0
|
|
|
|
|
|
$Poll = IO::Poll->new(); |
152
|
0
|
|
|
|
|
|
$Poll->mask($server => (POLLIN | POLLERR | POLLHUP | POLLNVAL)); |
153
|
|
|
|
|
|
|
|
154
|
0
|
|
|
|
|
|
$Exister = IPC::PidStat->new(); |
155
|
0
|
|
|
|
|
|
my $exister_fh = $Exister->fh; # Avoid method calls, to accelerate things |
156
|
0
|
|
|
|
|
|
$Poll->mask($exister_fh => (POLLIN | POLLERR | POLLHUP | POLLNVAL)); |
157
|
|
|
|
|
|
|
|
158
|
0
|
|
|
|
|
|
%Clients = (); |
159
|
|
|
|
|
|
|
#$SIG{ALRM} = \&sig_alarm; |
160
|
0
|
|
|
|
|
|
$SIG{INT}= \&sig_INT; |
161
|
0
|
|
|
|
|
|
$SIG{HUP}= \&sig_INT; |
162
|
|
|
|
|
|
|
|
163
|
0
|
|
|
|
|
|
$! = 0; |
164
|
0
|
|
|
|
|
|
while (!$Interrupts) { |
165
|
0
|
0
|
|
|
|
|
_timelog("Pre-poll $!\n") if $Debug; |
166
|
|
|
|
|
|
|
#use Data::Dumper; Carp::cluck(Dumper(\%Clients, \%Locks)); |
167
|
0
|
|
|
|
|
|
$! = 0; |
168
|
0
|
|
|
|
|
|
my (@r, @w, @e); |
169
|
|
|
|
|
|
|
|
170
|
0
|
0
|
|
|
|
|
my $timeout = ((scalar keys %Locks) ? $PollDelta : 2000); |
171
|
0
|
|
|
|
|
|
my $npolled = $Poll->poll($timeout); |
172
|
0
|
0
|
|
|
|
|
if ($npolled>0) { |
173
|
0
|
|
|
|
|
|
@r = $Poll->handles(POLLIN); |
174
|
0
|
|
|
|
|
|
@e = $Poll->handles(POLLERR | POLLHUP | POLLNVAL); |
175
|
|
|
|
|
|
|
#@w = $Poll->handles(POLLOUT); |
176
|
|
|
|
|
|
|
} |
177
|
0
|
0
|
|
|
|
|
_timelog("Poll $npolled Locks=",(scalar keys %Locks),": $#r $#w $#e $!\n") if $Debug; |
178
|
0
|
|
|
|
|
|
foreach my $fh (@r) { |
179
|
0
|
0
|
|
|
|
|
if ($fh == $server) { |
|
|
0
|
|
|
|
|
|
180
|
|
|
|
|
|
|
# Create a new socket |
181
|
0
|
|
|
|
|
|
my $clientfh = $server->accept; |
182
|
0
|
|
|
|
|
|
$Poll->mask($clientfh => (POLLIN | POLLERR | POLLHUP | POLLNVAL)); |
183
|
|
|
|
|
|
|
# |
184
|
0
|
|
|
|
|
|
my $clientvar = {socket=>$clientfh, |
185
|
|
|
|
|
|
|
input=>'', |
186
|
|
|
|
|
|
|
inputlines=>[], |
187
|
|
|
|
|
|
|
}; |
188
|
0
|
0
|
|
|
|
|
$clientvar->{client_num} = $_Client_Num++ if $Debug; |
189
|
0
|
|
|
|
|
|
$Clients{$clientfh}=$clientvar; |
190
|
0
|
0
|
|
|
|
|
client_send($clientvar,"HELLO\n") if $Debug; |
191
|
|
|
|
|
|
|
} elsif ($fh == $exister_fh) { |
192
|
0
|
|
|
|
|
|
exist_traffic(); |
193
|
|
|
|
|
|
|
} else { |
194
|
0
|
|
|
|
|
|
my $data = ''; |
195
|
|
|
|
|
|
|
# For debug, change the 1000 to 1 below |
196
|
0
|
|
|
|
|
|
my $rc = recv($fh, $data, 1000, 0); |
197
|
0
|
0
|
|
|
|
|
if ($data eq '') { |
198
|
|
|
|
|
|
|
# we have finished with the socket |
199
|
0
|
|
|
|
|
|
delete $Clients{$fh}; |
200
|
0
|
|
|
|
|
|
$Poll->remove($fh); |
201
|
0
|
|
|
|
|
|
$fh->close; |
202
|
|
|
|
|
|
|
} else { |
203
|
0
|
|
|
|
|
|
my $line = $Clients{$fh}->{input}.$data; |
204
|
0
|
|
|
|
|
|
my @lines = split /\n/, $line; |
205
|
0
|
0
|
|
|
|
|
if ($line =~ /\n$/) { |
206
|
0
|
|
|
|
|
|
$Clients{$fh}->{input}=''; |
207
|
0
|
0
|
|
|
|
|
_timelog("Nothing Left\n") if $Debug; |
208
|
|
|
|
|
|
|
} else { |
209
|
0
|
|
|
|
|
|
$Clients{$fh}->{input}=pop @lines; |
210
|
0
|
0
|
|
|
|
|
_timelog("Left: ".$Clients{$fh}->{input}."\n") if $Debug; |
211
|
|
|
|
|
|
|
} |
212
|
0
|
|
|
|
|
|
client_service($Clients{$fh}, \@lines); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
} |
216
|
0
|
|
|
|
|
|
foreach my $fh (@e) { |
217
|
|
|
|
|
|
|
# we have finished with the socket |
218
|
0
|
|
|
|
|
|
delete $Clients{$fh}; |
219
|
0
|
|
|
|
|
|
$Poll->remove($fh); |
220
|
0
|
|
|
|
|
|
$fh->close; |
221
|
|
|
|
|
|
|
} |
222
|
0
|
|
|
|
|
|
$self->recheck_locks(); |
223
|
|
|
|
|
|
|
} |
224
|
0
|
0
|
|
|
|
|
_timelog("Loop end\n") if $Debug; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
###################################################################### |
228
|
|
|
|
|
|
|
###################################################################### |
229
|
|
|
|
|
|
|
#### Client servicing |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub client_service { |
232
|
0
|
|
0
|
0
|
0
|
|
my $clientvar = shift || die; |
233
|
0
|
|
|
|
|
|
my $linesref = shift; |
234
|
|
|
|
|
|
|
# Loop getting commands from a specific client |
235
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: REQS $clientvar->{socket}\n") if $Debug; |
236
|
|
|
|
|
|
|
|
237
|
0
|
0
|
|
|
|
|
if (defined $clientvar->{inputlines}[0]) { |
238
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: handling pre-saved lines\n") if $Debug; |
239
|
0
|
|
|
|
|
|
$linesref = [@{$clientvar->{inputlines}}, @{$linesref}]; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
240
|
0
|
|
|
|
|
|
$clientvar->{inputlines} = []; # Zap, in case we get called recursively |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# We may return before processing all lines, thus the lines are |
244
|
|
|
|
|
|
|
# stored in the client variables |
245
|
0
|
|
|
|
|
|
while (defined (my $line = shift @{$linesref})) { |
|
0
|
|
|
|
|
|
|
246
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: REQ $line\n") if $Debug; |
247
|
0
|
|
|
|
|
|
my ($cmd,@param) = split /\s+/, $line; # We rely on the newline to terminate the split |
248
|
0
|
0
|
|
|
|
|
if ($cmd) { |
249
|
|
|
|
|
|
|
# Variables |
250
|
0
|
0
|
|
|
|
|
if ($cmd eq 'user') { $clientvar->{user} = $param[0]; } |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
251
|
0
|
|
|
|
|
|
elsif ($cmd eq 'locks') { $clientvar->{locks} = [@param]; } |
252
|
0
|
|
|
|
|
|
elsif ($cmd eq 'block') { $clientvar->{block} = $param[0]; } |
253
|
0
|
|
|
|
|
|
elsif ($cmd eq 'timeout') { $clientvar->{timeout} = $param[0]; } |
254
|
0
|
|
|
|
|
|
elsif ($cmd eq 'autounlock') { $clientvar->{autounlock} = $param[0]; } |
255
|
0
|
|
|
|
|
|
elsif ($cmd eq 'hostname') { $clientvar->{hostname} = $param[0]; } |
256
|
0
|
|
|
|
|
|
elsif ($cmd eq 'pid') { $clientvar->{pid} = $param[0]; } |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# Frequent Commands |
259
|
|
|
|
|
|
|
elsif ($cmd eq 'UNLOCK') { |
260
|
0
|
|
|
|
|
|
client_unlock ($clientvar); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
elsif ($cmd eq 'LOCK') { |
263
|
0
|
|
|
|
|
|
my $wait = client_lock ($clientvar); |
264
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: Wait= $wait\n") if $Debug; |
265
|
0
|
0
|
|
|
|
|
last if $wait; |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
elsif ($cmd eq 'EOF') { |
268
|
0
|
|
|
|
|
|
client_close ($clientvar); |
269
|
0
|
|
|
|
|
|
undef $clientvar; |
270
|
0
|
|
|
|
|
|
last; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# Infrequent commands |
274
|
|
|
|
|
|
|
elsif ($cmd eq 'STATUS') { |
275
|
0
|
|
|
|
|
|
client_status ($clientvar); |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
elsif ($cmd eq 'BREAK_LOCK') { |
278
|
0
|
|
|
|
|
|
client_break ($clientvar); |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
elsif ($cmd eq 'DEAD_PID') { |
281
|
0
|
|
|
|
|
|
dead_pid($param[0],$param[1]); |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
elsif ($cmd eq 'LOCK_LIST') { |
284
|
0
|
|
|
|
|
|
client_lock_list ($clientvar); |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
elsif ($cmd eq 'VERSION') { |
287
|
0
|
|
|
|
|
|
client_send ($clientvar, "version $VERSION $StartTime\n\n"); |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
elsif ($cmd eq 'RESTART') { |
290
|
0
|
|
|
|
|
|
die "restart"; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
# Commands |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# Save any non-processed lines (from 'last') for next time |
297
|
0
|
|
|
|
|
|
$clientvar->{inputlines} = $linesref; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
sub client_close { |
301
|
0
|
|
0
|
0
|
0
|
|
my $clientvar = shift || die; |
302
|
0
|
0
|
|
|
|
|
if ($clientvar->{socket}) { |
303
|
0
|
|
|
|
|
|
delete $Clients{$clientvar->{socket}}; |
304
|
0
|
|
|
|
|
|
$Poll->remove($clientvar->{socket}); |
305
|
0
|
|
|
|
|
|
$clientvar->{socket}->close(); |
306
|
|
|
|
|
|
|
} |
307
|
0
|
|
|
|
|
|
$clientvar->{socket} = undef; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
sub client_status { |
311
|
|
|
|
|
|
|
# Send status of lock back to client |
312
|
|
|
|
|
|
|
# Return 1 if success (client didn't hangup) |
313
|
0
|
|
0
|
0
|
0
|
|
my $clientvar = shift || die; |
314
|
0
|
|
|
|
|
|
$clientvar->{locked} = 0; |
315
|
0
|
|
|
|
|
|
$clientvar->{owner} = ""; |
316
|
0
|
|
|
|
|
|
my $send = ""; |
317
|
0
|
|
|
|
|
|
foreach my $lockname (@{$clientvar->{locks}}) { |
|
0
|
|
|
|
|
|
|
318
|
0
|
0
|
|
|
|
|
if (my $locki = locki_find ($lockname)) { |
319
|
0
|
0
|
|
|
|
|
if ($locki->{owner} eq $clientvar->{user}) { # (Re) got lock |
320
|
0
|
|
|
|
|
|
$clientvar->{locked} = 1; |
321
|
0
|
|
|
|
|
|
$clientvar->{locks} = [$locki->{lock}]; |
322
|
0
|
|
|
|
|
|
$clientvar->{owner} = $locki->{owner}; # == Ourself |
323
|
0
|
0
|
|
|
|
|
if ($clientvar->{told_locked}) { |
324
|
0
|
|
|
|
|
|
$clientvar->{told_locked} = 0; |
325
|
0
|
|
|
|
|
|
$send .= "print_obtained\n"; |
326
|
|
|
|
|
|
|
} |
327
|
0
|
|
|
|
|
|
last; |
328
|
|
|
|
|
|
|
} else { |
329
|
|
|
|
|
|
|
# Indicate first owner, for client "waiting" message |
330
|
0
|
0
|
|
|
|
|
$clientvar->{owner} = $locki->{owner} if !$clientvar->{owner}; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
0
|
|
|
|
|
|
$send .= "owner $clientvar->{owner}\n"; |
336
|
0
|
|
|
|
|
|
$send .= "locked $clientvar->{locked}\n"; |
337
|
0
|
0
|
|
|
|
|
$send .= "lockname $clientvar->{locks}[0]\n" if $clientvar->{locked}; |
338
|
0
|
0
|
|
|
|
|
$send .= "error $clientvar->{error}\n" if $clientvar->{error}; |
339
|
0
|
|
|
|
|
|
$send .= "\n\n"; # End of group. Some day we may not always send EOF immediately |
340
|
0
|
|
|
|
|
|
return client_send ($clientvar, $send); |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
sub client_lock_list { |
344
|
0
|
|
0
|
0
|
0
|
|
my $clientvar = shift || die; |
345
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: Locklist!\n") if $Debug; |
346
|
0
|
|
|
|
|
|
while (my ($lockname, $lock) = each %Locks) { |
347
|
0
|
0
|
|
|
|
|
if (!$lock->{locked}) { |
348
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: Note unlocked lock $lockname\n") if $Debug; |
349
|
0
|
|
|
|
|
|
next; |
350
|
|
|
|
|
|
|
} |
351
|
0
|
|
|
|
|
|
client_send ($clientvar, "lock $lockname $lock->{owner}\n"); |
352
|
|
|
|
|
|
|
} |
353
|
0
|
|
|
|
|
|
return client_send ($clientvar, "\n\n"); |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
sub client_lock { |
357
|
|
|
|
|
|
|
# Client wants this lock, return true if delayed transaction |
358
|
0
|
|
0
|
0
|
0
|
|
my $clientvar = shift || die; |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# Fast case, see if there are any non-allocated locks |
361
|
0
|
|
|
|
|
|
foreach my $lockname (@{$clientvar->{locks}}) { |
|
0
|
|
|
|
|
|
|
362
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: check $lockname\n") if $Debug; |
363
|
0
|
|
|
|
|
|
my $locki = locki_find ($lockname); |
364
|
0
|
0
|
0
|
|
|
|
if ($locki && $locki->{owner} ne $clientvar->{user}) { |
365
|
|
|
|
|
|
|
# See if the user's machine can clear it |
366
|
0
|
0
|
0
|
|
|
|
if ($locki->{autounlock} && $clientvar->{autounlock}) { |
367
|
|
|
|
|
|
|
# The 2 is for supports DEAD_PID added in version 1.480 |
368
|
|
|
|
|
|
|
# Older clients will ignore it. |
369
|
0
|
|
|
|
|
|
client_send ($clientvar, "autounlock_check $locki->{lock} $locki->{hostname} $locki->{pid} 2\n"); |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
# Try to have timer/exister clear up existing lock |
372
|
0
|
|
|
|
|
|
locki_recheck($locki,undef); # locki maybe deleted |
373
|
|
|
|
|
|
|
} else { |
374
|
0
|
0
|
|
|
|
|
if (!$clientvar->{locked}) { # Unlikely - some async path established the lock |
375
|
|
|
|
|
|
|
# Know there's a free lock; for speed, munge request to point to only it |
376
|
0
|
|
|
|
|
|
$clientvar->{locks} = [$lockname]; |
377
|
0
|
|
|
|
|
|
last; |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# Create lock requests |
383
|
0
|
|
|
|
|
|
my $first_locki = undef; |
384
|
0
|
|
|
|
|
|
foreach my $lockname (@{$clientvar->{locks}}) { |
|
0
|
|
|
|
|
|
|
385
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: new $lockname\n") if $Debug; |
386
|
|
|
|
|
|
|
# Create new request. If it can be serviced, this will |
387
|
|
|
|
|
|
|
# establish the lock and send status back. |
388
|
0
|
|
|
|
|
|
my $locki = locki_new_request($lockname, $clientvar); |
389
|
0
|
|
0
|
|
|
|
$first_locki ||= $locki; |
390
|
|
|
|
|
|
|
# Done if found free lock |
391
|
0
|
0
|
|
|
|
|
last if $clientvar->{locked}; |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# All locks busy? |
395
|
0
|
0
|
|
|
|
|
if ($clientvar->{locked}) { |
|
|
0
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# Done, and we already sent client_status when the lock was made |
397
|
0
|
|
|
|
|
|
return 0; |
398
|
|
|
|
|
|
|
} elsif (!$clientvar->{block}) { |
399
|
|
|
|
|
|
|
# All busy, and user wants non-blocking, just send status |
400
|
0
|
|
|
|
|
|
client_status($clientvar); |
401
|
0
|
|
|
|
|
|
return 0; |
402
|
|
|
|
|
|
|
} else { |
403
|
|
|
|
|
|
|
# All busy, we need to block the user's request and tell the user |
404
|
0
|
0
|
0
|
|
|
|
if (!$clientvar->{told_locked} && $first_locki) { |
405
|
0
|
|
|
|
|
|
$clientvar->{told_locked} = 1; |
406
|
0
|
|
|
|
|
|
client_send ($clientvar, "print_waiting $first_locki->{owner}\n"); |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
# Either need to wait for timeout, or someone else to return key |
409
|
0
|
|
|
|
|
|
return 1; # Exit loop and check if can lock later |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
sub client_break { |
414
|
0
|
|
0
|
0
|
0
|
|
my $clientvar = shift || die; |
415
|
|
|
|
|
|
|
# The locki may be deleted by this call |
416
|
0
|
|
|
|
|
|
foreach my $lockname (@{$clientvar->{locks}}) { |
|
0
|
|
|
|
|
|
|
417
|
0
|
0
|
|
|
|
|
if (my $locki = locki_find ($lockname)) { |
418
|
0
|
0
|
|
|
|
|
if ($locki->{locked}) { |
419
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: broke lock $locki->{locks} User $clientvar->{user}\n") if $Debug; |
420
|
0
|
|
|
|
|
|
client_send ($clientvar, "print_broke $locki->{owner}\n"); |
421
|
0
|
|
|
|
|
|
locki_unlock ($locki); # locki may be deleted |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
} |
425
|
0
|
|
|
|
|
|
client_status ($clientvar); |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
sub client_unlock { |
429
|
0
|
|
0
|
0
|
0
|
|
my $clientvar = shift || die; |
430
|
|
|
|
|
|
|
# Client request to unlock the given lock |
431
|
|
|
|
|
|
|
# The locki may be deleted by this call |
432
|
0
|
|
|
|
|
|
$clientvar->{locked} = 0; |
433
|
0
|
|
|
|
|
|
foreach my $lockname (@{$clientvar->{locks}}) { |
|
0
|
|
|
|
|
|
|
434
|
0
|
0
|
|
|
|
|
if (my $locki = locki_find ($lockname)) { |
435
|
0
|
0
|
|
|
|
|
if ($locki->{owner} eq $clientvar->{user}) { |
436
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: Unlocked $locki->{lock} User $clientvar->{user}\n") if $Debug; |
437
|
0
|
|
|
|
|
|
locki_unlock ($locki); # locki may be deleted |
438
|
|
|
|
|
|
|
} else { |
439
|
|
|
|
|
|
|
# Doesn't hold lock but might be waiting for it. |
440
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: Waiter count: ".$#{$locki->{waiters}}."\n") if $Debug; |
|
0
|
|
|
|
|
|
|
441
|
0
|
|
|
|
|
|
for (my $n=0; $n <= $#{$locki->{waiters}}; $n++) { |
|
0
|
|
|
|
|
|
|
442
|
0
|
0
|
|
|
|
|
if ($locki->{waiters}[$n]{user} eq $clientvar->{user}) { |
443
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: Dewait $locki->{lock} User $clientvar->{user}\n") if $Debug; |
444
|
0
|
|
|
|
|
|
splice @{$locki->{waiters}}, $n, 1; |
|
0
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
} |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
} |
450
|
0
|
|
|
|
|
|
client_status ($clientvar); |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
sub client_send { |
454
|
|
|
|
|
|
|
# Send a string to the client, return 1 if success |
455
|
0
|
|
0
|
0
|
0
|
|
my $clientvar = shift || die; |
456
|
0
|
|
|
|
|
|
my $msg = shift; |
457
|
|
|
|
|
|
|
|
458
|
0
|
|
|
|
|
|
my $clientfh = $clientvar->{socket}; |
459
|
0
|
0
|
|
|
|
|
return 0 if (!$clientfh); |
460
|
0
|
0
|
|
|
|
|
_timelog_split("c$clientvar->{client_num}: RESP $clientfh", |
461
|
|
|
|
|
|
|
(' 'x24)."c$clientvar->{client_num}: RES ", $msg) if $Debug; |
462
|
|
|
|
|
|
|
|
463
|
0
|
|
|
|
|
|
$SIG{PIPE} = 'IGNORE'; |
464
|
0
|
|
|
|
|
|
my $status = eval { local $^W=0; send $clientfh,$msg,0; }; # Disable warnings |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
465
|
0
|
0
|
|
|
|
|
if (!$status) { |
466
|
0
|
0
|
0
|
|
|
|
warn "client_send hangup $? $! ".($status||"")." $clientfh " if $Debug; |
467
|
0
|
|
|
|
|
|
client_close ($clientvar); |
468
|
0
|
|
|
|
|
|
return 0; |
469
|
|
|
|
|
|
|
} |
470
|
0
|
|
|
|
|
|
return 1; |
471
|
|
|
|
|
|
|
} |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
###################################################################### |
474
|
|
|
|
|
|
|
###################################################################### |
475
|
|
|
|
|
|
|
#### Alarm handler |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
sub sig_INT { |
478
|
0
|
|
|
0
|
0
|
|
$Interrupts++; |
479
|
|
|
|
|
|
|
#$SIG{INT}= \&sig_INT; |
480
|
0
|
|
|
|
|
|
0; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
sub alarm_time { |
484
|
|
|
|
|
|
|
# Compute alarm interval and set |
485
|
0
|
|
|
0
|
0
|
|
die "Dead code\n"; |
486
|
0
|
|
|
|
|
|
my $time = fractime(); |
487
|
0
|
|
|
|
|
|
my $timelimit = undef; |
488
|
0
|
|
|
|
|
|
foreach my $locki (values %Locks) { |
489
|
0
|
0
|
0
|
|
|
|
if ($locki->{locked} && $locki->{timelimit}) { |
490
|
|
|
|
|
|
|
$timelimit = $locki->{timelimit} if |
491
|
|
|
|
|
|
|
(!defined $timelimit |
492
|
0
|
0
|
0
|
|
|
|
|| $locki->{timelimit} <= $timelimit); |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
} |
495
|
0
|
0
|
|
|
|
|
return $timelimit ? ($timelimit - $time + 1) : 0; |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
sub fractime { |
499
|
0
|
|
|
0
|
0
|
|
my ($time, $time_usec) = Time::HiRes::gettimeofday(); |
500
|
0
|
|
|
|
|
|
return $time + $time_usec * 1e-6; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
###################################################################### |
504
|
|
|
|
|
|
|
###################################################################### |
505
|
|
|
|
|
|
|
#### Exist traffic |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
sub exist_traffic { |
508
|
|
|
|
|
|
|
# Handle UDP responses from our $Exister->pid_request calls. |
509
|
0
|
0
|
|
0
|
0
|
|
_timelog("UDP PidStat in...\n") if $Debug; |
510
|
0
|
|
|
|
|
|
my ($pid,$exists,$onhost) = $Exister->recv_stat(); |
511
|
0
|
0
|
0
|
|
|
|
if (defined $pid && defined $exists && !$exists) { |
|
|
|
0
|
|
|
|
|
512
|
|
|
|
|
|
|
# We only care about known-missing processes |
513
|
0
|
0
|
|
|
|
|
_timelog(" UDP PidStat PID $pid no longer with us. RIP.\n") if $Debug; |
514
|
0
|
|
|
|
|
|
dead_pid($onhost,$pid); |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
sub dead_pid { |
519
|
0
|
|
|
0
|
0
|
|
my $host = shift; |
520
|
0
|
|
|
|
|
|
my $pid = shift; |
521
|
|
|
|
|
|
|
# We don't maintain a table sorted by pid, as these messages |
522
|
|
|
|
|
|
|
# are rare, and there can be many locks per pid. |
523
|
0
|
|
|
|
|
|
foreach my $locki (values %Locks) { |
524
|
0
|
0
|
0
|
|
|
|
if ($locki->{locked} && $locki->{autounlock} |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
525
|
|
|
|
|
|
|
&& $locki->{hostname} eq $host |
526
|
|
|
|
|
|
|
&& $locki->{pid} == $pid) { |
527
|
0
|
0
|
|
|
|
|
_timelog("\tUDP RIP Unlock\n") if $Debug; |
528
|
0
|
|
|
|
|
|
locki_unlock($locki); # break the lock, locki may be deleted |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
} |
531
|
0
|
0
|
|
|
|
|
_timelog(" UDP RIP done\n\n") if $Debug; |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
###################################################################### |
535
|
|
|
|
|
|
|
###################################################################### |
536
|
|
|
|
|
|
|
#### Internals |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
sub locki_action { |
539
|
|
|
|
|
|
|
# Give lock to next requestor that accepts it |
540
|
0
|
|
0
|
0
|
0
|
|
my $locki = shift || die; |
541
|
|
|
|
|
|
|
|
542
|
0
|
0
|
|
|
|
|
_timelog("$locki->{lock}: Locki_action:Waiter count: ".$#{$locki->{waiters}}."\n") if $Debug; |
|
0
|
|
|
|
|
|
|
543
|
0
|
0
|
0
|
|
|
|
if (!$locki->{locked} && defined $locki->{waiters}[0]) { |
|
|
0
|
0
|
|
|
|
|
544
|
0
|
|
|
|
|
|
my $clientvar = shift @{$locki->{waiters}}; |
|
0
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
# Give it to a client. If it fails, it will call locki_unlock then locki_action again |
546
|
|
|
|
|
|
|
# so we just return after this. |
547
|
0
|
|
|
|
|
|
locki_lock_to_client($locki,$clientvar); |
548
|
0
|
|
|
|
|
|
return; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
elsif (!$locki->{locked} && !defined $locki->{waiters}[0]) { |
551
|
0
|
|
|
|
|
|
locki_delete ($locki); # locki invalid |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
sub locki_lock_to_client { |
556
|
0
|
|
|
0
|
0
|
|
my $locki = shift; |
557
|
0
|
|
|
|
|
|
my $clientvar = shift; |
558
|
|
|
|
|
|
|
|
559
|
0
|
0
|
|
|
|
|
_timelog("$locki->{lock}: Issuing to $clientvar->{user}\n") if $Debug; |
560
|
0
|
|
|
|
|
|
$locki->{locked} = 1; |
561
|
0
|
|
|
|
|
|
$locki->{owner} = $clientvar->{user}; |
562
|
0
|
0
|
|
|
|
|
if ($clientvar->{timeout}) { |
563
|
0
|
|
|
|
|
|
$locki->{timelimit} = $clientvar->{timeout} + fractime(); |
564
|
|
|
|
|
|
|
} else { |
565
|
0
|
|
|
|
|
|
$locki->{timelimit} = 0; |
566
|
|
|
|
|
|
|
} |
567
|
0
|
|
|
|
|
|
$locki->{autounlock} = $clientvar->{autounlock}; |
568
|
0
|
|
|
|
|
|
$locki->{hostname} = $clientvar->{hostname}; |
569
|
0
|
|
|
|
|
|
$locki->{pid} = $clientvar->{pid}; |
570
|
|
|
|
|
|
|
|
571
|
0
|
0
|
0
|
|
|
|
if ($clientvar->{locked} && $clientvar->{locks}[0] ne $locki->{lock}) { |
572
|
|
|
|
|
|
|
# Client gave a choice of locks, and another one got to |
573
|
|
|
|
|
|
|
# satisify it first |
574
|
0
|
0
|
|
|
|
|
_timelog("$locki->{lock}: Already has different lock\n") if $Debug; |
575
|
0
|
|
|
|
|
|
return locki_unlock ($locki); # locki_unlock may recurse to call locki_lock |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
else { |
578
|
|
|
|
|
|
|
# This is the only call to a client_ routine not in the direct |
579
|
|
|
|
|
|
|
# client call stack. Thus we may need to process more commands |
580
|
|
|
|
|
|
|
# after this call |
581
|
0
|
0
|
|
|
|
|
if (client_status ($clientvar)) { # sets clientvar->{locked} |
582
|
|
|
|
|
|
|
# Worked ok |
583
|
0
|
|
|
|
|
|
client_service($clientvar, []); # If any queued, handle more commands/ EOF |
584
|
0
|
|
|
|
|
|
return; # Don't look for another lock waiter |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
# Else hung up, didn't get the lock, give to next guy |
587
|
0
|
0
|
|
|
|
|
_timelog("$locki->{lock}: Owner hangup $locki->{owner}\n") if $Debug; |
588
|
0
|
|
|
|
|
|
return locki_unlock ($locki); # locki_unlock may recurse to call locki_lock |
589
|
|
|
|
|
|
|
} |
590
|
0
|
|
|
|
|
|
die "%Error: Can't get here - instead we recurse thru unlock\n"; |
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
sub locki_unlock { |
594
|
0
|
|
0
|
0
|
0
|
|
my $locki = shift || die; |
595
|
|
|
|
|
|
|
# Unlock this lock |
596
|
|
|
|
|
|
|
# The locki may be deleted by this call |
597
|
0
|
|
|
|
|
|
$locki->{locked} = 0; |
598
|
0
|
|
|
|
|
|
$locki->{owner} = "unlocked"; |
599
|
0
|
|
|
|
|
|
$locki->{autounlock} = 0; |
600
|
0
|
|
|
|
|
|
$locki->{hostname} = ""; |
601
|
0
|
|
|
|
|
|
$locki->{pid} = 0; |
602
|
|
|
|
|
|
|
# Give it to someone else? |
603
|
|
|
|
|
|
|
# Note the new lock request client may not still be around, if so we |
604
|
|
|
|
|
|
|
# recurse back to this function with waiters one element shorter. |
605
|
0
|
|
|
|
|
|
locki_action ($locki); |
606
|
|
|
|
|
|
|
} |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
sub locki_delete { |
609
|
0
|
|
|
0
|
0
|
|
my $locki = shift; |
610
|
|
|
|
|
|
|
# The locki may be deleted by this call |
611
|
0
|
0
|
|
|
|
|
_timelog("$locki->{lock}: locki_delete\n") if $Debug; |
612
|
0
|
|
|
|
|
|
delete $Locks{$locki->{lock}}; |
613
|
|
|
|
|
|
|
} |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
sub recheck_locks { |
616
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
617
|
|
|
|
|
|
|
# Main loop to see if any locks have changed state |
618
|
0
|
|
|
|
|
|
my $time = fractime(); |
619
|
0
|
0
|
0
|
|
|
|
if (($self->{_recheck_locks_time}||0) < $time) { |
620
|
0
|
|
|
|
|
|
$self->{_recheck_locks_time} = $time + $RecheckLockDelta; |
621
|
0
|
|
|
|
|
|
foreach my $locki (values %Locks) { |
622
|
0
|
|
|
|
|
|
locki_recheck($locki,$time); # locki may be deleted |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
sub locki_recheck { |
628
|
0
|
|
|
0
|
0
|
|
my $locki = shift; |
629
|
0
|
|
0
|
|
|
|
my $time = shift || fractime(); |
630
|
|
|
|
|
|
|
# See if any locks need to change state due to pid disappearance or timeout |
631
|
|
|
|
|
|
|
# The locki may be deleted by this call |
632
|
0
|
0
|
|
|
|
|
if ($locki->{locked}) { |
633
|
0
|
0
|
0
|
|
|
|
if ($locki->{timelimit} && ($locki->{timelimit} <= $time)) { |
|
|
0
|
|
|
|
|
|
634
|
0
|
0
|
|
|
|
|
_timelog("$locki->{lock}: Timeout of $locki->{owner}\n") if $Debug; |
635
|
0
|
|
|
|
|
|
locki_unlock ($locki); # locki may be deleted |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
elsif ($locki->{autounlock}) { # locker said it was OK to break lock if he dies |
638
|
0
|
0
|
0
|
|
|
|
if (($locki->{autounlock_check_time}||0) < $time) { |
639
|
|
|
|
|
|
|
# If there's 1000 locks, we don't want to check them all |
640
|
|
|
|
|
|
|
# in one second, so scale back appropriately. |
641
|
0
|
|
|
|
|
|
my $chkdelta = ($AutoUnlockCheckDelta |
642
|
|
|
|
|
|
|
+ ((scalar keys %Locks)/$AutoUnlockCheckPerSec)); |
643
|
0
|
|
|
|
|
|
$locki->{autounlock_check_time} = $time + $chkdelta; |
644
|
|
|
|
|
|
|
# Only check every 2 secs or so, else we can spend more time |
645
|
|
|
|
|
|
|
# doing the OS calls than it's worth |
646
|
0
|
|
|
|
|
|
my $dead = undef; |
647
|
0
|
0
|
|
|
|
|
if ($locki->{hostname} eq $Hostname) { # lock owner is running on same host |
648
|
0
|
|
|
|
|
|
$dead = IPC::PidStat::local_pid_doesnt_exist($locki->{pid}); |
649
|
0
|
0
|
|
|
|
|
if ($dead) { |
650
|
0
|
0
|
|
|
|
|
_timelog("$locki->{lock}: Autounlock of $locki->{owner}\n") if $Debug; |
651
|
0
|
|
|
|
|
|
locki_unlock($locki); # break the lock, locki may be deleted |
652
|
|
|
|
|
|
|
} |
653
|
|
|
|
|
|
|
} |
654
|
0
|
0
|
|
|
|
|
if (!defined $dead) { |
655
|
|
|
|
|
|
|
# Ask the other host if the PID is gone |
656
|
|
|
|
|
|
|
# Or, we had a permission problem so ask root. |
657
|
0
|
0
|
|
|
|
|
_timelog("$locki->{lock}: UDP pid_request $locki->{hostname} $locki->{pid}\n") if $Debug; |
658
|
|
|
|
|
|
|
$Exister->pid_request(host=>$locki->{hostname}, pid=>$locki->{pid}, |
659
|
0
|
|
|
|
|
|
return_exist=>0, return_doesnt=>1, return_unknown=>1); |
660
|
|
|
|
|
|
|
# This may (or may not) return a UDP message with the status in it. |
661
|
|
|
|
|
|
|
# If so, they will call exist_traffic. |
662
|
|
|
|
|
|
|
} |
663
|
|
|
|
|
|
|
} |
664
|
|
|
|
|
|
|
} |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
sub locki_new_request { |
669
|
0
|
|
0
|
0
|
0
|
|
my $lockname = shift || "lock"; |
670
|
0
|
|
|
|
|
|
my $clientvar = shift; |
671
|
0
|
|
|
|
|
|
my $locki; |
672
|
0
|
0
|
|
|
|
|
if ($locki=locki_find($lockname)) { |
673
|
|
|
|
|
|
|
# Same existing owner wants to grab it under a new connection |
674
|
0
|
0
|
0
|
|
|
|
if ($locki->{locked} && ($locki->{owner} eq $clientvar->{user})) { |
675
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: Renewing connection\n") if $Debug; |
676
|
0
|
|
|
|
|
|
locki_lock_to_client($locki,$clientvar); |
677
|
|
|
|
|
|
|
} else { |
678
|
|
|
|
|
|
|
# Search waiters to see if already on list |
679
|
0
|
|
|
|
|
|
my $found; |
680
|
0
|
|
|
|
|
|
for (my $n=0; $n <= $#{$locki->{waiters}}; $n++) { |
|
0
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
# Note the old client value != new client value, although the user is the same |
682
|
0
|
0
|
|
|
|
|
if ($locki->{waiters}[$n]{user} eq $clientvar->{user}) { |
683
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: Renewing wait list\n") if $Debug; |
684
|
0
|
|
|
|
|
|
$locki->{waiters}[$n] = $clientvar; |
685
|
0
|
|
|
|
|
|
$found = 1; |
686
|
0
|
|
|
|
|
|
last; |
687
|
|
|
|
|
|
|
} |
688
|
|
|
|
|
|
|
} |
689
|
0
|
0
|
|
|
|
|
if (!$found) { |
690
|
0
|
0
|
|
|
|
|
_timelog("c$clientvar->{client_num}: New waiter\n") if $Debug; |
691
|
0
|
|
|
|
|
|
push @{$locki->{waiters}}, $clientvar; |
|
0
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
} |
693
|
|
|
|
|
|
|
# Either way, we don't have the lock, so just hang out |
694
|
|
|
|
|
|
|
} |
695
|
|
|
|
|
|
|
} else { # new |
696
|
0
|
|
|
|
|
|
$locki = { |
697
|
|
|
|
|
|
|
lock=>$lockname, |
698
|
|
|
|
|
|
|
locked=>0, |
699
|
|
|
|
|
|
|
owner=>"unlocked", |
700
|
|
|
|
|
|
|
waiters=>[$clientvar], |
701
|
|
|
|
|
|
|
}; |
702
|
0
|
|
|
|
|
|
$Locks{$lockname} = $locki; |
703
|
0
|
0
|
|
|
|
|
_timelog("$locki->{lock}: New\n") if $Debug; |
704
|
|
|
|
|
|
|
# Process it, which will establish the lock for this client |
705
|
0
|
|
|
|
|
|
locki_action($locki); |
706
|
|
|
|
|
|
|
} |
707
|
0
|
|
|
|
|
|
return $locki; |
708
|
|
|
|
|
|
|
} |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
sub locki_find { |
711
|
0
|
|
0
|
0
|
0
|
|
return $Locks{$_[0] || "lock"}; |
712
|
|
|
|
|
|
|
} |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
sub DESTROY { |
715
|
0
|
|
|
0
|
|
|
my $self = shift; |
716
|
0
|
0
|
|
|
|
|
_timelog("DESTROY\n") if $Debug; |
717
|
0
|
0
|
0
|
|
|
|
if (($self->{family} eq 'UNIX') && $self->{unix_socket_created}){ |
718
|
0
|
|
|
|
|
|
unlink $self->{port}; |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
} |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
###################################################################### |
723
|
|
|
|
|
|
|
#### Logging |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
sub _timelog { |
726
|
0
|
|
|
0
|
|
|
IPC::Locker::_timelog(@_); |
727
|
|
|
|
|
|
|
} |
728
|
|
|
|
|
|
|
sub _timelog_split { |
729
|
0
|
|
|
0
|
|
|
IPC::Locker::_timelog_split(@_); |
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
###################################################################### |
733
|
|
|
|
|
|
|
#### Package return |
734
|
|
|
|
|
|
|
1; |