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