line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl -w |
2
|
|
|
|
|
|
|
#!/usr/local/bin/perl -w |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package BrLock; |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 NAME |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
BrLock - Distributed Lock with minimal messages exchanges |
9
|
|
|
|
|
|
|
over a reliable network. |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 SYNOPSIS |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
BrLock->new('cfg_file', # configuration file, (see DESCRIPTION). |
14
|
|
|
|
|
|
|
'127.0.0.1', # this node's ip. |
15
|
|
|
|
|
|
|
3001); # port to be bound to this node. |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
BrLock->br_lock(); |
18
|
|
|
|
|
|
|
# enter critical region |
19
|
|
|
|
|
|
|
BrLock->br_unlock(); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 DESCRIPTION |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
BrLock features a distributed lock, using the algorithm |
24
|
|
|
|
|
|
|
Carvalho and Roucariol, On mutual exclusion in computer networks, |
25
|
|
|
|
|
|
|
ACM Communications, Feb83. |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
The algorithm features minimal messages for acquiring the next lock, |
28
|
|
|
|
|
|
|
but with the trade-off of network being reliable enough to ensure that |
29
|
|
|
|
|
|
|
all nodes are alive. In fact, one node won't be able to acquire the |
30
|
|
|
|
|
|
|
next lock if it can't communicate to all other nodes (unless the node |
31
|
|
|
|
|
|
|
which was the last one to acquire the lock). |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
If this is a hard constraint for you, you may want to use IPC::Lock. |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
The list containing all nodes that may apply for this lock must be |
36
|
|
|
|
|
|
|
described in the configuration file passed as parameters to the |
37
|
|
|
|
|
|
|
environment builder (see SYNOPSIS). The file must be as this: |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
0 0 |
40
|
|
|
|
|
|
|
0.0.0.0 0 |
41
|
|
|
|
|
|
|
x.x.x.x port |
42
|
|
|
|
|
|
|
y.y.y.y port |
43
|
|
|
|
|
|
|
... |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
The first line must have two zeros, and the second must have the |
46
|
|
|
|
|
|
|
0.0.0.0 ip and the 0 port (deprecated parameters, see TODO). The |
47
|
|
|
|
|
|
|
next lines must have a node ip and a node port. All nodes must use |
48
|
|
|
|
|
|
|
the same configuration file, so a node will read its own |
49
|
|
|
|
|
|
|
parameters. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
A valid cfg_file for BrLock->new('cfg_file', '127.0.0.1', 3001), |
52
|
|
|
|
|
|
|
for an environment with 3 nodes, is: |
53
|
|
|
|
|
|
|
0 0 |
54
|
|
|
|
|
|
|
0.0.0.0 0 |
55
|
|
|
|
|
|
|
127.0.0.1 3002 |
56
|
|
|
|
|
|
|
127.0.0.1 3001 |
57
|
|
|
|
|
|
|
127.0.0.1 3003 |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Note the networking setup will be made by this module. So, after |
60
|
|
|
|
|
|
|
calling Br->new, the node will be listening at 127.0.0.1:3001 in |
61
|
|
|
|
|
|
|
the above example. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 TODO |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=over |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=item * Accept entire configuration as parameters thus not requiring |
69
|
|
|
|
|
|
|
a configuration file. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=item * Switch to PerlOO, so multiple locks can be used at once. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=back |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=head1 AUTHORS |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Ribamar Santarosa |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Tarcisio Genaro |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=head1 SEE ALSO |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=cut |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
90
|
1
|
|
|
1
|
|
26290
|
use IO::Socket; |
|
1
|
|
|
|
|
30531
|
|
|
1
|
|
|
|
|
4
|
|
91
|
1
|
|
|
1
|
|
1664
|
use Switch; |
|
1
|
|
|
|
|
57313
|
|
|
1
|
|
|
|
|
7
|
|
92
|
1
|
|
|
1
|
|
205368
|
use threads; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
use threads::shared; |
94
|
|
|
|
|
|
|
use XML::Parser; |
95
|
|
|
|
|
|
|
use BrLock::BrXML; # shipped toghether with this. |
96
|
|
|
|
|
|
|
use BrLock::SomePerlFunc; # shipped toghether with this. |
97
|
|
|
|
|
|
|
use warnings; |
98
|
|
|
|
|
|
|
use diagnostics; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
use strict; |
101
|
|
|
|
|
|
|
use base 'Exporter'; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
our $VERSION = 0.1_00; |
104
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
105
|
|
|
|
|
|
|
our @EXPORT = qw(br_lock br_unlock br_free); |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
### |
109
|
|
|
|
|
|
|
# General use constants. |
110
|
|
|
|
|
|
|
### |
111
|
|
|
|
|
|
|
use constant DEBUG => 0; |
112
|
|
|
|
|
|
|
use constant TRUE => 1; |
113
|
|
|
|
|
|
|
use constant FALSE => 0; |
114
|
|
|
|
|
|
|
# max acceptance size for udp messages. It's not specified anywhere: |
115
|
|
|
|
|
|
|
use constant UDP_MSG_SIZE => 1024; |
116
|
|
|
|
|
|
|
# max random number for unknown tests (see file mutex.txt): |
117
|
|
|
|
|
|
|
use constant TEST_RANDOM_NUMBER => 100000; |
118
|
|
|
|
|
|
|
# if we are not informed what are our ip number: |
119
|
|
|
|
|
|
|
use constant OUR_IP => "127.0.0.1"; |
120
|
|
|
|
|
|
|
#use constant OUR_IP => "143.106.73.160"; |
121
|
|
|
|
|
|
|
# if we are not informed what are our ip port: |
122
|
|
|
|
|
|
|
use constant OUR_PORT => 3002; |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
#### |
126
|
|
|
|
|
|
|
# Global variables |
127
|
|
|
|
|
|
|
### |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
our $debug; # to print debug info. |
130
|
|
|
|
|
|
|
our $our_port; |
131
|
|
|
|
|
|
|
our $our_ip; |
132
|
|
|
|
|
|
|
# file where to read config options. |
133
|
|
|
|
|
|
|
our $config_file; |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# variables to be filled with configuration data. |
136
|
|
|
|
|
|
|
our ( $s, # max waiting time for a requisition. |
137
|
|
|
|
|
|
|
$t, # max resource use time. |
138
|
|
|
|
|
|
|
$our_id, # our site ID. |
139
|
|
|
|
|
|
|
%resource_info, # resource is any site. see the definition of |
140
|
|
|
|
|
|
|
# this hash in the info below. |
141
|
|
|
|
|
|
|
):shared; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# The list of sites is defined as a list of Hashes |
144
|
|
|
|
|
|
|
# ( Site => $u , Port => $v , SiteId => $cur_siteid, |
145
|
|
|
|
|
|
|
# AuthBy => TRUE or FALSE, RepDeferred => TRUE or FALSE ) |
146
|
|
|
|
|
|
|
# -- FALSE default for both; $cur_siteid -> current line number |
147
|
|
|
|
|
|
|
# in the config_file (see below) the first line in the file has line |
148
|
|
|
|
|
|
|
# number = -1. |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
our @sites:shared = (); # list of sites in the kidding(see info below). |
151
|
|
|
|
|
|
|
our $osn:shared = 0; # timestamp for our messages. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
### |
154
|
|
|
|
|
|
|
# Global protocol variables (we'll go to hell for using globals). Note |
155
|
|
|
|
|
|
|
# this doesn't mean the procotol won't use the other globals. In an |
156
|
|
|
|
|
|
|
# implemented class, these are private values. |
157
|
|
|
|
|
|
|
### |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
our @baffer:shared = (); # buffer of received messages. |
160
|
|
|
|
|
|
|
our $n_auth:shared = 0; # how many sites gave us auth (to optmized wait). |
161
|
|
|
|
|
|
|
our $hsn:shared = 0; # max known timestamp. |
162
|
|
|
|
|
|
|
our $inside:shared = FALSE; # are we using the resource? |
163
|
|
|
|
|
|
|
our $waiting:shared = FALSE; # are we waiting for the resource? |
164
|
|
|
|
|
|
|
our $br_end; # when set threads finish. |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
### |
167
|
|
|
|
|
|
|
# Protocol constants. |
168
|
|
|
|
|
|
|
### |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
use constant BR_REP => 0; # reply message. |
171
|
|
|
|
|
|
|
use constant BR_REQ => 1; # request message. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# parse_cfgfile(f): |
175
|
|
|
|
|
|
|
# parse the configuration file f and set the globals: |
176
|
|
|
|
|
|
|
# $s, $t, @sites. |
177
|
|
|
|
|
|
|
# The function assumes being ran only once. Undocumented |
178
|
|
|
|
|
|
|
# behaviour if ran more than once. |
179
|
|
|
|
|
|
|
# |
180
|
|
|
|
|
|
|
# Parameters: the config file name. |
181
|
|
|
|
|
|
|
# |
182
|
|
|
|
|
|
|
# Returns: |
183
|
|
|
|
|
|
|
# 0 -> success |
184
|
|
|
|
|
|
|
# a string cotaining a message error (TODO not that good). |
185
|
|
|
|
|
|
|
# |
186
|
|
|
|
|
|
|
# TODO: not really a general function. this function is |
187
|
|
|
|
|
|
|
# really a br_function as it set up br_ data structures. |
188
|
|
|
|
|
|
|
# |
189
|
|
|
|
|
|
|
sub parse_cfgfile { |
190
|
|
|
|
|
|
|
my $file = $_[0]; |
191
|
|
|
|
|
|
|
#TODO: untested change |
192
|
|
|
|
|
|
|
my $line; |
193
|
|
|
|
|
|
|
my $F; |
194
|
|
|
|
|
|
|
return "File $file not found.\n" unless open $F, $file; |
195
|
|
|
|
|
|
|
return "Nothing in file $file.\n" |
196
|
|
|
|
|
|
|
unless (defined $F and $line = <$F>); |
197
|
|
|
|
|
|
|
# first line: $s $t |
198
|
|
|
|
|
|
|
return "Can't parse first line of the config file $file.\n" |
199
|
|
|
|
|
|
|
unless ($line =~ m/([^ ]+)[ ]+([^ ]+)[ ]*$/gi); |
200
|
|
|
|
|
|
|
$s = $1; |
201
|
|
|
|
|
|
|
chomp ($t = $2); |
202
|
|
|
|
|
|
|
# the rest of the file: folks in the kidding. |
203
|
|
|
|
|
|
|
my $cur_siteid = 0; |
204
|
|
|
|
|
|
|
while($line = <$F>){ |
205
|
|
|
|
|
|
|
if($line =~ m/([^ ]+)[ ]+([^ ]+)[ ]*$/gi){ |
206
|
|
|
|
|
|
|
my ($u, $v); |
207
|
|
|
|
|
|
|
$u = $1; |
208
|
|
|
|
|
|
|
chomp ($v = $2); |
209
|
|
|
|
|
|
|
if ( ($v eq $our_port) and ($u eq $our_ip) ){ |
210
|
|
|
|
|
|
|
# we've found our identification. |
211
|
|
|
|
|
|
|
$our_id = $cur_siteid; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
else{ |
214
|
|
|
|
|
|
|
my $ha = &share({}); |
215
|
|
|
|
|
|
|
$ha->{Site} = $u; |
216
|
|
|
|
|
|
|
$ha->{Port} = $v; |
217
|
|
|
|
|
|
|
$ha->{SiteId} = $cur_siteid; |
218
|
|
|
|
|
|
|
$ha->{AuthBy} = FALSE; |
219
|
|
|
|
|
|
|
$ha->{RepDeferred} = FALSE; |
220
|
|
|
|
|
|
|
push @sites, $ha ; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
$cur_siteid++; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
else { |
225
|
|
|
|
|
|
|
return "Can't parse config file $file.\n"; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
close $F; |
229
|
|
|
|
|
|
|
# transferring resource information from @sites into its hash: |
230
|
|
|
|
|
|
|
my $ri = shift @sites; |
231
|
|
|
|
|
|
|
share(%resource_info); |
232
|
|
|
|
|
|
|
%resource_info = ( Site => $ri->{Site}, |
233
|
|
|
|
|
|
|
Port => $ri->{Port}, |
234
|
|
|
|
|
|
|
SiteId => $ri->{SiteId} |
235
|
|
|
|
|
|
|
); |
236
|
|
|
|
|
|
|
return 0; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
### |
242
|
|
|
|
|
|
|
# Protocol functions. The names of these functions start |
243
|
|
|
|
|
|
|
# with "br_", which recalls "Brazilian", which in turn recalls |
244
|
|
|
|
|
|
|
# other things. |
245
|
|
|
|
|
|
|
### |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
#sub br_send(msg_type, receiver, osn): |
248
|
|
|
|
|
|
|
# Send the message $msg_type (which must be BR_REP or BR_REQ), |
249
|
|
|
|
|
|
|
# to $receiver, saying that our timestamp is $osn. |
250
|
|
|
|
|
|
|
# |
251
|
|
|
|
|
|
|
# $receiver must be an element of the list @sites (a hash as defined |
252
|
|
|
|
|
|
|
# in the definition of the @sites array). |
253
|
|
|
|
|
|
|
# |
254
|
|
|
|
|
|
|
# If everything was OK (well, we can't know if the package was |
255
|
|
|
|
|
|
|
# received, we assume as OK if we can send it), returns 0. Else, we |
256
|
|
|
|
|
|
|
# return 1. |
257
|
|
|
|
|
|
|
# |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
sub br_send { |
260
|
|
|
|
|
|
|
my ($msg_type, $receiver, $osn) = @_; |
261
|
|
|
|
|
|
|
# Prepare the XML message (Just remeber: No XML parsing here!). |
262
|
|
|
|
|
|
|
$msg_type = "REP" if ($msg_type eq BR_REP); |
263
|
|
|
|
|
|
|
$msg_type = "REQ" if ($msg_type eq BR_REQ); |
264
|
|
|
|
|
|
|
if ($msg_type ne "REQ" and $msg_type ne "REP"){ |
265
|
|
|
|
|
|
|
# ops: bad argument passed... |
266
|
|
|
|
|
|
|
print "br_send(): \$msg_type must be either BR_REP ". |
267
|
|
|
|
|
|
|
"or BR_REQ.\n" if $debug; |
268
|
|
|
|
|
|
|
return 1; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
my $xml_str = xmlmessage_brpack ($msg_type, $our_id, $osn, |
271
|
|
|
|
|
|
|
choose_integer(TEST_RANDOM_NUMBER) ); |
272
|
|
|
|
|
|
|
# Send a TCP pkg w/ the XML message to $receiver's host:port. |
273
|
|
|
|
|
|
|
return 0 if send_tcp_string ($xml_str, |
274
|
|
|
|
|
|
|
$receiver->{Site}, $receiver->{Port}); |
275
|
|
|
|
|
|
|
return 1; # problems in send_tcp_string... |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# br_xml_to_brdata(xml_str): |
279
|
|
|
|
|
|
|
# converts the xml_str string returning a list ($msg, $j, $k) --- |
280
|
|
|
|
|
|
|
# $msg being one of (BR_REP, BR_REQ). This list is ready to be used |
281
|
|
|
|
|
|
|
# as parameter list to br_functions such br_receiving or br_send. |
282
|
|
|
|
|
|
|
# |
283
|
|
|
|
|
|
|
# Uses Globals: |
284
|
|
|
|
|
|
|
# @sites (read only). |
285
|
|
|
|
|
|
|
# |
286
|
|
|
|
|
|
|
# TODO: verify return values. (undef, again?) / sanity tests. |
287
|
|
|
|
|
|
|
# |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub br_xml_to_brdata { |
290
|
|
|
|
|
|
|
my $xml_str = $_[0]; |
291
|
|
|
|
|
|
|
my ($type, $site_id, $site_sequence, $random) = |
292
|
|
|
|
|
|
|
xmlparse_brmsg ($xml_str); |
293
|
|
|
|
|
|
|
my ($msg, $j, $k) = 0; |
294
|
|
|
|
|
|
|
# setting $msg... |
295
|
|
|
|
|
|
|
$msg = BR_REP if ($type eq "REP"); |
296
|
|
|
|
|
|
|
$msg = BR_REQ if ($type eq "REQ"); |
297
|
|
|
|
|
|
|
if ($type ne "REQ" and $type ne "REP"){ |
298
|
|
|
|
|
|
|
# ops: xmlparse_brmsg went wrong... |
299
|
|
|
|
|
|
|
print "br_xml_to_brdata(): Message must be either \"REQ\"". |
300
|
|
|
|
|
|
|
" or \"REP\".\n" if $debug; |
301
|
|
|
|
|
|
|
return undef; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
# searching for a site $j with $site_id in @sites... |
304
|
|
|
|
|
|
|
foreach my $dummy_var ( @sites ){ |
305
|
|
|
|
|
|
|
if ( $dummy_var->{SiteId} eq $site_id){ |
306
|
|
|
|
|
|
|
$j = $dummy_var; |
307
|
|
|
|
|
|
|
last; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
if ( $j->{SiteId} ne $site_id){ |
311
|
|
|
|
|
|
|
# ops: fail, can't find this site_id in @sites; |
312
|
|
|
|
|
|
|
print "Can't find site_id = [$site_id] in \@sites.\n" if $debug; |
313
|
|
|
|
|
|
|
return undef; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
# setting $msg... |
316
|
|
|
|
|
|
|
$k = $site_sequence; |
317
|
|
|
|
|
|
|
# returning... |
318
|
|
|
|
|
|
|
return ($msg, $j, $k); |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# sub inside(): |
322
|
|
|
|
|
|
|
# returns TRUE if this host is in the moment in the critical region |
323
|
|
|
|
|
|
|
# or FALSE if not. |
324
|
|
|
|
|
|
|
sub inside { |
325
|
|
|
|
|
|
|
return $inside; |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# sub waiting(): |
329
|
|
|
|
|
|
|
# returns TRUE if this host is in the moment waiting to enter |
330
|
|
|
|
|
|
|
# the critical region; FALSE if not. |
331
|
|
|
|
|
|
|
sub waiting { |
332
|
|
|
|
|
|
|
return $waiting; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# br_n_auth(): |
337
|
|
|
|
|
|
|
# returns the number of sites we already got authorization. |
338
|
|
|
|
|
|
|
sub br_n_auth { |
339
|
|
|
|
|
|
|
my $vr = 0; |
340
|
|
|
|
|
|
|
lock @sites; |
341
|
|
|
|
|
|
|
foreach my $j (@sites) { |
342
|
|
|
|
|
|
|
if ($j->{AuthBy}){ |
343
|
|
|
|
|
|
|
$vr++; |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
return $vr; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
# sub br_wanna_resource(): |
350
|
|
|
|
|
|
|
# when we find ourselves wondering how the life would be if we had |
351
|
|
|
|
|
|
|
# the resource, we start this function (probably as a new thread). |
352
|
|
|
|
|
|
|
# Note only one instance of this function must be running at any |
353
|
|
|
|
|
|
|
# time, or strange things may happen. I don't know if the |
354
|
|
|
|
|
|
|
# responsability of checking that falls under the implementation of |
355
|
|
|
|
|
|
|
# this function; never count on it. |
356
|
|
|
|
|
|
|
# |
357
|
|
|
|
|
|
|
# No parameters. No return codes. |
358
|
|
|
|
|
|
|
# |
359
|
|
|
|
|
|
|
# TODO: NOTE: this function was split into br_lock() and br_unlock(). |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
sub br_lock{ |
363
|
|
|
|
|
|
|
$waiting = TRUE; |
364
|
|
|
|
|
|
|
$osn = $hsn + 1; |
365
|
|
|
|
|
|
|
foreach my $j (@sites) { |
366
|
|
|
|
|
|
|
if (not $j->{AuthBy}){ |
367
|
|
|
|
|
|
|
br_send (BR_REQ, $j , $osn); |
368
|
|
|
|
|
|
|
print "br_wanna:send(REQ, $j->{Site}:$j->{Port}, $osn)\n" |
369
|
|
|
|
|
|
|
if $debug; |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
my $na = br_n_auth(); |
373
|
|
|
|
|
|
|
# waiting for all sites to give us auth. |
374
|
|
|
|
|
|
|
while ($na < @sites ){ |
375
|
|
|
|
|
|
|
$na = br_n_auth(); |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
$inside = TRUE; |
378
|
|
|
|
|
|
|
$waiting = FALSE; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
sub br_unlock{ |
382
|
|
|
|
|
|
|
$inside = FALSE; |
383
|
|
|
|
|
|
|
foreach my $j (@sites) { |
384
|
|
|
|
|
|
|
if ($j->{RepDeferred}){ |
385
|
|
|
|
|
|
|
$n_auth-- if $j->{AuthBy}; |
386
|
|
|
|
|
|
|
$j->{RepDeferred} = ($j->{AuthBy} = FALSE); |
387
|
|
|
|
|
|
|
br_send (BR_REP, $j , $osn); |
388
|
|
|
|
|
|
|
print "br_wanna:send(REP, $j->{Site}:$j->{Port}, $osn)\n" |
389
|
|
|
|
|
|
|
if $debug; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
else{ |
392
|
|
|
|
|
|
|
print "br_wanna:undeferred $j->{Site}:$j->{Port}.\n" |
393
|
|
|
|
|
|
|
if $debug; |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
#sub br_receiving(msg, j, k): |
400
|
|
|
|
|
|
|
# When $j has sent us a message $msg (which must be BR_REP or BR_REQ), |
401
|
|
|
|
|
|
|
# with timestamp $k, this function must be called to process it. Do |
402
|
|
|
|
|
|
|
# not multithread it; instead, use a buffer to handle the messages |
403
|
|
|
|
|
|
|
# received (the algorithm presumes it process the messages in a fifo). |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
sub br_receiving { |
406
|
|
|
|
|
|
|
my ($msg, $j, $k) = @_; |
407
|
|
|
|
|
|
|
#TODO: untested change |
408
|
|
|
|
|
|
|
my $priority; |
409
|
|
|
|
|
|
|
#debug if we are receiving correctly the parameters. After |
410
|
|
|
|
|
|
|
#tested, we must remove up to the return and let things to happen! |
411
|
|
|
|
|
|
|
print "br_rec:****($msg, $j->{Site}:$j->{Port}, $k)\n" if $debug; |
412
|
|
|
|
|
|
|
$hsn = ( $k > $hsn ? $k : $hsn ) + 1; |
413
|
|
|
|
|
|
|
print "br_rec:k=$k, osn=$osn, hsn=$hsn, n=$n_auth\n" if $debug; |
414
|
|
|
|
|
|
|
switch ($msg){ |
415
|
|
|
|
|
|
|
case BR_REQ { |
416
|
|
|
|
|
|
|
$priority = |
417
|
|
|
|
|
|
|
(($k > $osn) or |
418
|
|
|
|
|
|
|
( ($k==$osn) and ($our_id < $j->{SiteId}) ) ); |
419
|
|
|
|
|
|
|
# if we feel we are better than the guy sending message, we |
420
|
|
|
|
|
|
|
# kick out him. |
421
|
|
|
|
|
|
|
if ( $inside or ($waiting and $priority) ){ |
422
|
|
|
|
|
|
|
print "br_rec:inside (k=$k)\n" if $debug and $inside; |
423
|
|
|
|
|
|
|
print "br_rec:priority and waiting(k=$k)\n" |
424
|
|
|
|
|
|
|
if $debug and $priority and $waiting; |
425
|
|
|
|
|
|
|
print "br_rec: deferred $j->{Site}:$j->{Port})\n" |
426
|
|
|
|
|
|
|
if $debug; |
427
|
|
|
|
|
|
|
$j->{RepDeferred} = TRUE; |
428
|
|
|
|
|
|
|
#TODO: realy return? |
429
|
|
|
|
|
|
|
return; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
# We lose the authorization from the guy and gently give |
432
|
|
|
|
|
|
|
# him the BR_REP beucase we don't have enough priority. |
433
|
|
|
|
|
|
|
#TODO: if ( (not ($inside or $waiting)) or: |
434
|
|
|
|
|
|
|
# this $inside seems to be a tautology. |
435
|
|
|
|
|
|
|
if ( (not ($inside or $waiting)) or |
436
|
|
|
|
|
|
|
( ($waiting) and |
437
|
|
|
|
|
|
|
(not $priority) and |
438
|
|
|
|
|
|
|
(not $j->{AuthBy}) |
439
|
|
|
|
|
|
|
) |
440
|
|
|
|
|
|
|
) { |
441
|
|
|
|
|
|
|
print "br_rec:(not inside||wait) (k=$k)\n" if $debug |
442
|
|
|
|
|
|
|
and (not ($inside or $waiting)); |
443
|
|
|
|
|
|
|
print "br_rec: not \$j->{AuthBy} (k=$k)\n" if $debug |
444
|
|
|
|
|
|
|
and ($inside or $waiting); |
445
|
|
|
|
|
|
|
print "br_rec:send(REP, $j->{Site}:$j->{Port})\n" |
446
|
|
|
|
|
|
|
if $debug; |
447
|
|
|
|
|
|
|
$n_auth-- if $j->{AuthBy}; |
448
|
|
|
|
|
|
|
$j->{AuthBy} = FALSE; |
449
|
|
|
|
|
|
|
br_send (BR_REP, $j , $osn); |
450
|
|
|
|
|
|
|
# Shouldn't we ask again REQ once we're waiting? |
451
|
|
|
|
|
|
|
# Nope: if we're waiting and we haven't get auth |
452
|
|
|
|
|
|
|
# yet, we're in his RepDeferred list and the guy |
453
|
|
|
|
|
|
|
# will somehow send us the auth in the future. |
454
|
|
|
|
|
|
|
return; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
# We lose the authorization from the guy and gently give |
457
|
|
|
|
|
|
|
# him the BR_REP because he has greatest priority, but we |
458
|
|
|
|
|
|
|
# ask him to give us BR_REP as soon as possible, in order |
459
|
|
|
|
|
|
|
# of us to enter his RepDeferred list. |
460
|
|
|
|
|
|
|
if ( ($waiting) and |
461
|
|
|
|
|
|
|
(not $priority) and |
462
|
|
|
|
|
|
|
($j->{AuthBy}) |
463
|
|
|
|
|
|
|
) { |
464
|
|
|
|
|
|
|
print "br_rec: \$j->{AuthBy} (k=$k)\n" if $debug ; |
465
|
|
|
|
|
|
|
print "br_rec:send(REP, $j->{Site}:$j->{Port},$osn)\n" |
466
|
|
|
|
|
|
|
if $debug; |
467
|
|
|
|
|
|
|
print "br_rec:send(REQ, $j->{Site}:$j->{Port},$osn)\n" |
468
|
|
|
|
|
|
|
if $debug; |
469
|
|
|
|
|
|
|
$n_auth-- if $j->{AuthBy}; |
470
|
|
|
|
|
|
|
$j->{AuthBy} = FALSE; |
471
|
|
|
|
|
|
|
br_send (BR_REP, $j , $osn); |
472
|
|
|
|
|
|
|
br_send (BR_REQ, $j , $osn); |
473
|
|
|
|
|
|
|
return; |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
case BR_REP { |
477
|
|
|
|
|
|
|
# huuuuhhuuu... one more auth... |
478
|
|
|
|
|
|
|
$n_auth++ if not $j->{AuthBy}; |
479
|
|
|
|
|
|
|
print "br_rec: REP ($j->{Site}:$j->{Port}, $k)\n" |
480
|
|
|
|
|
|
|
if $debug ; |
481
|
|
|
|
|
|
|
$j->{AuthBy} = TRUE; |
482
|
|
|
|
|
|
|
return; |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
} |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
# sub br_handle_received(baffer): |
488
|
|
|
|
|
|
|
# this function calls br_receiving() for all elements in the global |
489
|
|
|
|
|
|
|
# buffer @baffer, respecting the order (@baffer is a buffer of raw |
490
|
|
|
|
|
|
|
# XML messages). However, this function doesn't stop if the buffer |
491
|
|
|
|
|
|
|
# is empty: this function will run forever, waiting new messages |
492
|
|
|
|
|
|
|
# in the buffer and calling br_receiving() for these new messages. |
493
|
|
|
|
|
|
|
# |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# TODO: threads should never run forever, but test if an attribute |
496
|
|
|
|
|
|
|
# saying that the application is over is set. |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
sub br_handle_received { |
499
|
|
|
|
|
|
|
# do: |
500
|
|
|
|
|
|
|
# shift the first element from buffer (loop/next if empty). |
501
|
|
|
|
|
|
|
# parse it. |
502
|
|
|
|
|
|
|
# pass it to br_receiving. |
503
|
|
|
|
|
|
|
# loop. |
504
|
|
|
|
|
|
|
$| = 1; |
505
|
|
|
|
|
|
|
while (not $br_end) { |
506
|
|
|
|
|
|
|
my $xml_str = shift @baffer; |
507
|
|
|
|
|
|
|
#print "($xml_str)\n" if $xml_str; |
508
|
|
|
|
|
|
|
br_receiving (br_xml_to_brdata($xml_str)) if $xml_str; |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# br_listen(): |
513
|
|
|
|
|
|
|
# thread that accepts incomming connections, and bufferizes them |
514
|
|
|
|
|
|
|
# into @baffer. |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
# we're a "server", running 'till the end of the times, waiting for |
517
|
|
|
|
|
|
|
# xml messages. |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# TODO: threads should never run forever, but test if an attribute |
520
|
|
|
|
|
|
|
# saying that the application is over is set. |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
sub br_listen{ |
523
|
|
|
|
|
|
|
while (not $br_end) { |
524
|
|
|
|
|
|
|
my ($sock) = @_; |
525
|
|
|
|
|
|
|
# printf "welcome br_listen.\n"; |
526
|
|
|
|
|
|
|
my $new_connect = $sock->accept(); |
527
|
|
|
|
|
|
|
# printf "newly connected.\n"; |
528
|
|
|
|
|
|
|
my $rec_msg = ""; |
529
|
|
|
|
|
|
|
while(<$new_connect>){ |
530
|
|
|
|
|
|
|
$rec_msg .= $_; |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
push @baffer, $rec_msg if $rec_msg; |
533
|
|
|
|
|
|
|
$rec_msg = FALSE; |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
# printf "connect finished.\n"; |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
### |
540
|
|
|
|
|
|
|
# New. |
541
|
|
|
|
|
|
|
### |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
#### |
546
|
|
|
|
|
|
|
# Global variables |
547
|
|
|
|
|
|
|
### |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
sub new{ |
550
|
|
|
|
|
|
|
# OO stuff. |
551
|
|
|
|
|
|
|
my $this = shift; |
552
|
|
|
|
|
|
|
my $class = ref($this) || $this; |
553
|
|
|
|
|
|
|
my $self = {}; |
554
|
|
|
|
|
|
|
bless $self, $class; |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
# config. vars. |
557
|
|
|
|
|
|
|
$config_file = $_[0]; |
558
|
|
|
|
|
|
|
$our_port = $_[1]; |
559
|
|
|
|
|
|
|
$our_ip = $_[2]; |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
# config. vars. |
562
|
|
|
|
|
|
|
$s = 0; |
563
|
|
|
|
|
|
|
$t = 0; |
564
|
|
|
|
|
|
|
$our_id = 0; |
565
|
|
|
|
|
|
|
%resource_info = (); |
566
|
|
|
|
|
|
|
@sites = (); |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
# protocol vars. |
569
|
|
|
|
|
|
|
$osn = 0; |
570
|
|
|
|
|
|
|
@baffer = (); |
571
|
|
|
|
|
|
|
$n_auth = 0; |
572
|
|
|
|
|
|
|
$hsn = 0; |
573
|
|
|
|
|
|
|
$inside = FALSE; |
574
|
|
|
|
|
|
|
$waiting = FALSE; |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
$br_end = 0; |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
$BrXML::brxml_debug = $debug = DEBUG; |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
parse_cfgfile($config_file); |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
my $sock = new IO::Socket::INET ( |
583
|
|
|
|
|
|
|
LocalPort => $our_port, |
584
|
|
|
|
|
|
|
Proto => 'tcp', |
585
|
|
|
|
|
|
|
Listen => 1, |
586
|
|
|
|
|
|
|
Reuse => 1, |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
); |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
# start the thread for handling rec messages. |
592
|
|
|
|
|
|
|
# " pop @baffer, $xml_msg " |
593
|
|
|
|
|
|
|
threads->new(\&br_handle_received); |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
# start the thread to accept connections, to receive |
596
|
|
|
|
|
|
|
# messages "push @baffer, $rec_msg " |
597
|
|
|
|
|
|
|
threads->new(\&br_listen, $sock); |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
sub br_free{ |
601
|
|
|
|
|
|
|
# TODO: find some way to stop threads. |
602
|
|
|
|
|
|
|
# print "br_free(): about to set \$br_end.\n" if $debug; |
603
|
|
|
|
|
|
|
# $br_end = 1; |
604
|
|
|
|
|
|
|
# print "br_free(): \$br_end set.\n" if $debug; |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
1; |
608
|
|
|
|
|
|
|
|