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