line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package cPanel::StateFile; |
2
|
|
|
|
|
|
|
{ |
3
|
|
|
|
|
|
|
$cPanel::StateFile::VERSION = '0.606'; |
4
|
|
|
|
|
|
|
} |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
# cpanel - cPanel/StateFile.pm Copyright(c) 2014 cPanel, Inc. |
7
|
|
|
|
|
|
|
# All rights Reserved. |
8
|
|
|
|
|
|
|
# copyright@cpanel.net http://cpanel.net |
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
# Redistribution and use in source and binary forms, with or without |
11
|
|
|
|
|
|
|
# modification, are permitted provided that the following conditions are met: |
12
|
|
|
|
|
|
|
# * Redistributions of source code must retain the above copyright |
13
|
|
|
|
|
|
|
# notice, this list of conditions and the following disclaimer. |
14
|
|
|
|
|
|
|
# * Redistributions in binary form must reproduce the above copyright |
15
|
|
|
|
|
|
|
# notice, this list of conditions and the following disclaimer in the |
16
|
|
|
|
|
|
|
# documentation and/or other materials provided with the distribution. |
17
|
|
|
|
|
|
|
# * Neither the name of the owner nor the names of its contributors may |
18
|
|
|
|
|
|
|
# be used to endorse or promote products derived from this software |
19
|
|
|
|
|
|
|
# without specific prior written permission. |
20
|
|
|
|
|
|
|
# |
21
|
|
|
|
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
22
|
|
|
|
|
|
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
23
|
|
|
|
|
|
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
24
|
|
|
|
|
|
|
# DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY |
25
|
|
|
|
|
|
|
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
26
|
|
|
|
|
|
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
27
|
|
|
|
|
|
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
28
|
|
|
|
|
|
|
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
29
|
|
|
|
|
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30
|
|
|
|
|
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31
|
|
|
|
|
|
|
|
32
|
40
|
|
|
40
|
|
193686
|
use strict; |
|
40
|
|
|
|
|
399
|
|
|
40
|
|
|
|
|
1466
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
#use warnings; |
35
|
|
|
|
|
|
|
|
36
|
40
|
|
|
40
|
|
294
|
use Fcntl (); |
|
40
|
|
|
|
|
73
|
|
|
40
|
|
|
|
|
601
|
|
37
|
40
|
|
|
40
|
|
215
|
use File::Path (); |
|
40
|
|
|
|
|
73
|
|
|
40
|
|
|
|
|
794
|
|
38
|
40
|
|
|
40
|
|
226
|
use Scalar::Util (); |
|
40
|
|
|
|
|
85
|
|
|
40
|
|
|
|
|
159953
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
my $the_logger; |
41
|
|
|
|
|
|
|
my $the_locker; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# Simplifies having both classes in the module use the same package. |
44
|
|
|
|
|
|
|
my $pkg = __PACKAGE__; |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
47
|
|
|
|
|
|
|
# Policy code: The following allows the calling code to supply two objects that |
48
|
|
|
|
|
|
|
# specify the behavior for logging and file locking. This approach was applied |
49
|
|
|
|
|
|
|
# to allow these policies to be changed without requiring the overhead of the |
50
|
|
|
|
|
|
|
# default implementations. |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# The default logging code is simple enough that I am including it inline |
53
|
|
|
|
|
|
|
# instead of making a separate class file. All methods are forwarded to the |
54
|
|
|
|
|
|
|
# CORE C and C functions. |
55
|
|
|
|
|
|
|
{ |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
package DefaultLogger; |
58
|
|
|
|
|
|
|
{ |
59
|
|
|
|
|
|
|
$DefaultLogger::VERSION = '0.606'; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub new { |
63
|
23
|
|
|
23
|
|
71
|
my ($class) = @_; |
64
|
23
|
|
|
|
|
81
|
return bless {}, $class; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub throw { |
68
|
52
|
|
|
52
|
|
67
|
my $self = shift; |
69
|
52
|
|
|
|
|
676
|
die @_; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
sub warn { |
73
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
74
|
1
|
|
|
|
|
7
|
CORE::warn @_; |
75
|
1
|
|
|
|
|
6
|
return; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub info { |
79
|
1
|
|
|
1
|
|
1
|
my $self = shift; |
80
|
1
|
|
|
|
|
5
|
CORE::warn @_; |
81
|
1
|
|
|
|
|
6
|
return; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub notify { |
85
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
86
|
0
|
|
|
|
|
0
|
return; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub _throw { |
91
|
36
|
|
|
36
|
|
68
|
my $self = shift; |
92
|
36
|
|
|
|
|
97
|
_get_logger()->throw(@_); |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub _warn { |
96
|
16
|
|
|
16
|
|
27
|
my $self = shift; |
97
|
16
|
|
|
|
|
31
|
_get_logger()->warn(@_); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _notify { |
101
|
8
|
|
|
8
|
|
15
|
my $self = shift; |
102
|
8
|
|
|
|
|
20
|
_get_logger()->notify(@_); |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# We never use _info, so remove it |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub _get_logger { |
108
|
150
|
100
|
|
150
|
|
452
|
unless ( defined $the_logger ) { |
109
|
23
|
|
|
|
|
170
|
$the_logger = DefaultLogger->new(); |
110
|
|
|
|
|
|
|
} |
111
|
150
|
|
|
|
|
753
|
return $the_logger; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub _get_locker { |
115
|
61
|
100
|
|
61
|
|
224
|
unless ( defined $the_locker ) { |
116
|
29
|
|
|
34
|
|
2266
|
eval 'use cPanel::StateFile::FileLocker;'; ## no critic (ProhibitStringyEval) |
|
34
|
|
|
|
|
29926
|
|
|
33
|
|
|
|
|
212
|
|
|
33
|
|
|
|
|
694
|
|
117
|
29
|
50
|
|
|
|
164
|
$pkg->_throw(@_) if $@; |
118
|
29
|
|
|
|
|
201
|
$the_locker = cPanel::StateFile::FileLocker->new( { logger => _get_logger() } ); |
119
|
|
|
|
|
|
|
} |
120
|
61
|
|
|
|
|
341
|
return $the_locker; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
my $are_policies_set = 0; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# |
126
|
|
|
|
|
|
|
# This method allows changing the policies for logging and locking. |
127
|
|
|
|
|
|
|
sub import { |
128
|
17
|
|
|
17
|
|
3497
|
my $class = shift; |
129
|
17
|
100
|
|
|
|
95
|
die 'Not an even number of arguments to the $pkg module' if @_ % 2; |
130
|
16
|
50
|
|
|
|
105
|
die 'Policies already set elsewhere' if $are_policies_set; |
131
|
16
|
100
|
|
|
|
2106
|
return 1 unless @_; # Don't set the policies flag. |
132
|
|
|
|
|
|
|
|
133
|
12
|
|
|
|
|
45
|
while (@_) { |
134
|
12
|
|
|
|
|
40
|
my ( $policy, $object ) = splice( @_, 0, 2 ); |
135
|
12
|
50
|
|
|
|
45
|
next unless defined $object; |
136
|
12
|
100
|
|
|
|
42
|
if ( '-logger' eq $policy ) { |
|
|
100
|
|
|
|
|
|
137
|
9
|
100
|
|
|
|
34
|
unless ( ref $object ) { |
138
|
5
|
|
|
1
|
|
1297
|
eval "use $object;"; ## no critic (ProhibitStringyEval) |
|
1
|
|
|
|
|
599
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
139
|
5
|
100
|
|
|
|
31
|
die $@ if $@; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# convert module into an object. |
142
|
4
|
|
|
|
|
17
|
$object = $object->new; |
143
|
|
|
|
|
|
|
} |
144
|
8
|
100
|
|
|
|
62
|
die 'Supplied logger object does not support the correct interface.' |
145
|
|
|
|
|
|
|
unless _valid_logger($object); |
146
|
7
|
|
|
|
|
29
|
$the_logger = $object; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
elsif ( '-filelock' eq $policy ) { |
149
|
2
|
100
|
|
|
|
10
|
unless ( ref $object ) { |
150
|
1
|
|
|
|
|
91
|
eval "use $object;"; ## no critic (ProhibitStringyEval) |
151
|
1
|
50
|
|
|
|
10
|
die $@ if $@; |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# convert module into an object. |
154
|
0
|
|
|
|
|
0
|
$object = $object->new; |
155
|
|
|
|
|
|
|
} |
156
|
1
|
50
|
|
|
|
7
|
die 'Supplied filelock object does not support the correct interface.' |
157
|
|
|
|
|
|
|
unless _valid_file_locker($object); |
158
|
0
|
|
|
|
|
0
|
$the_locker = $object; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
else { |
161
|
1
|
|
|
|
|
10
|
die "Unrecognized policy '$policy'"; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
} |
164
|
7
|
|
|
|
|
14
|
$are_policies_set = 1; |
165
|
7
|
|
|
|
|
2173
|
return 1; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
{ |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# |
171
|
|
|
|
|
|
|
# Nested class to handle locking cleanly. |
172
|
|
|
|
|
|
|
# |
173
|
|
|
|
|
|
|
# Locks the file on creation, throwing on failure. Unlocks the file when |
174
|
|
|
|
|
|
|
# the object is destroyed. |
175
|
|
|
|
|
|
|
{ |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
package cPanel::StateFile::Guard; |
178
|
|
|
|
|
|
|
{ |
179
|
|
|
|
|
|
|
$cPanel::StateFile::Guard::VERSION = '0.606'; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub new { |
183
|
464
|
|
|
464
|
|
961
|
my ( $class, $args_ref ) = @_; |
184
|
464
|
50
|
|
|
|
1368
|
$pkg->throw('Args parameter must be a hash reference.') unless 'HASH' eq ref $args_ref; |
185
|
464
|
50
|
|
|
|
1101
|
$pkg->throw('No StateFile.') unless exists $args_ref->{state}; |
186
|
|
|
|
|
|
|
|
187
|
464
|
|
|
|
|
1693
|
my $self = bless { state_file => $args_ref->{state} }, $class; |
188
|
|
|
|
|
|
|
|
189
|
464
|
|
|
|
|
1117
|
$self->_lock(); |
190
|
|
|
|
|
|
|
|
191
|
464
|
|
|
|
|
873
|
return $self; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub DESTROY { |
195
|
464
|
|
|
464
|
|
1947
|
my ($self) = @_; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# make certain that an exception here cannot escape and won't effect |
198
|
|
|
|
|
|
|
# exceptions currently in progress. |
199
|
464
|
|
|
|
|
658
|
local $@; |
200
|
464
|
|
|
|
|
943
|
eval { $self->_unlock(); }; |
|
464
|
|
|
|
|
1027
|
|
201
|
464
|
|
|
|
|
5203
|
return; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub _lock { |
205
|
484
|
|
|
484
|
|
771
|
my $self = shift; |
206
|
484
|
|
|
|
|
1036
|
my $state_file = $self->{state_file}; |
207
|
|
|
|
|
|
|
|
208
|
484
|
|
|
|
|
1028
|
my $filename = $state_file->{file_name}; |
209
|
484
|
|
|
|
|
2494
|
$self->{lock_file} = $state_file->{locker}->file_lock($filename); |
210
|
484
|
50
|
|
|
|
1805
|
$state_file->throw("Unable to acquire file lock for '$filename'.") unless $self->{lock_file}; |
211
|
484
|
|
|
|
|
811
|
return; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
sub _unlock { |
215
|
489
|
|
|
489
|
|
677
|
my $self = shift; |
216
|
489
|
|
|
|
|
732
|
my $state_file = $self->{state_file}; |
217
|
489
|
100
|
|
|
|
1098
|
return unless $self->{lock_file}; |
218
|
|
|
|
|
|
|
|
219
|
484
|
100
|
|
|
|
1188
|
if ( $state_file->{file_handle} ) { |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# TODO probably need to check for failure, but then what do I do? |
222
|
244
|
|
|
|
|
322
|
eval { |
223
|
244
|
|
|
0
|
|
3889
|
local $SIG{'ALRM'} = sub { die "flock 8 timeout\n"; }; |
|
0
|
|
|
|
|
0
|
|
224
|
244
|
|
|
|
|
1322
|
my $orig_alarm = alarm $state_file->{flock_timeout}; |
225
|
244
|
|
|
|
|
1518
|
flock $state_file->{file_handle}, 8; |
226
|
244
|
|
|
|
|
3181
|
alarm $orig_alarm; |
227
|
|
|
|
|
|
|
}; |
228
|
244
|
|
|
|
|
3470
|
close $state_file->{file_handle}; |
229
|
244
|
|
|
|
|
449
|
$state_file->{file_handle} = undef; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# Update size and timestamp after close. |
232
|
244
|
|
|
|
|
4726
|
@{$state_file}{qw(file_size file_mtime)} = ( stat( $state_file->{file_name} ) )[ 7, 9 ]; |
|
244
|
|
|
|
|
686
|
|
233
|
|
|
|
|
|
|
} |
234
|
484
|
|
|
|
|
2227
|
$state_file->{locker}->file_unlock( $self->{lock_file} ); |
235
|
484
|
|
|
|
|
933
|
$self->{lock_file} = undef; |
236
|
484
|
|
|
|
|
898
|
return; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub call_unlocked { |
240
|
28
|
|
|
28
|
|
1674
|
my ( $self, $code ) = @_; |
241
|
28
|
|
|
|
|
66
|
my $state_file = $self->{state_file}; |
242
|
28
|
100
|
|
|
|
99
|
$state_file->throw('Cannot nest call_unlocked calls.') unless defined $self->{lock_file}; |
243
|
27
|
100
|
|
|
|
118
|
$state_file->throw('Missing coderef to call_unlocked.') unless 'CODE' eq ref $code; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# unlock for the duration of the code execution |
246
|
25
|
|
|
|
|
80
|
$self->_unlock(); |
247
|
25
|
|
|
|
|
44
|
eval { $code->(); }; |
|
25
|
|
|
|
|
76
|
|
248
|
20
|
|
|
|
|
1407
|
my $ex = $@; |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# relock even if exception. |
251
|
20
|
|
|
|
|
544
|
$self->_lock(); |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# probably should resync if necessary. |
254
|
20
|
|
|
|
|
528
|
$state_file->_resynch($self); |
255
|
|
|
|
|
|
|
|
256
|
20
|
100
|
|
|
|
91
|
$pkg->_throw($ex) if $ex; |
257
|
|
|
|
|
|
|
|
258
|
19
|
|
|
|
|
94
|
return; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub _open { |
262
|
205
|
|
|
205
|
|
559
|
my ( $self, $mode ) = @_; |
263
|
205
|
|
|
|
|
328
|
my $state_file = $self->{state_file}; |
264
|
205
|
50
|
|
|
|
556
|
$state_file->throw('Cannot open state file inside a call_unlocked call.') unless defined $self->{lock_file}; |
265
|
|
|
|
|
|
|
|
266
|
205
|
50
|
|
|
|
9060
|
open my $fh, $mode, $state_file->{file_name} |
267
|
|
|
|
|
|
|
or $state_file->throw("Unable to open state file '$state_file->{file_name}': $!"); |
268
|
|
|
|
|
|
|
eval { |
269
|
205
|
|
|
0
|
|
3544
|
local $SIG{'ALRM'} = sub { die "flock 2 timeout\n"; }; |
|
0
|
|
|
|
|
0
|
|
270
|
205
|
|
|
|
|
1099
|
my $orig_alarm = alarm $state_file->{flock_timeout}; |
271
|
205
|
|
|
|
|
1239
|
flock $fh, 2; |
272
|
205
|
|
|
|
|
761
|
alarm $orig_alarm; |
273
|
205
|
|
|
|
|
2516
|
1; |
274
|
205
|
50
|
|
|
|
320
|
} or do { |
275
|
0
|
|
|
|
|
0
|
close($fh); |
276
|
0
|
0
|
|
|
|
0
|
if ( $@ eq "flock 2 timeout\n" ) { |
277
|
0
|
|
|
|
|
0
|
$state_file->throw('Guard timed out trying to open state file.'); |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
else { |
280
|
0
|
|
|
|
|
0
|
$state_file->throw($@); |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
}; |
283
|
205
|
|
|
|
|
629
|
$state_file->{file_handle} = $fh; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
sub update_file { |
287
|
226
|
|
|
226
|
|
1058
|
my ($self) = @_; |
288
|
226
|
|
|
|
|
406
|
my $state_file = $self->{state_file}; |
289
|
226
|
100
|
|
|
|
641
|
$state_file->throw('Cannot update_file inside a call_unlocked call.') unless defined $self->{lock_file}; |
290
|
|
|
|
|
|
|
|
291
|
225
|
50
|
|
|
|
530
|
if ( !$state_file->{file_handle} ) { |
292
|
225
|
100
|
|
|
|
3409
|
if ( -e $state_file->{file_name} ) { |
293
|
186
|
|
|
|
|
535
|
$self->_open('+<'); |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
else { |
296
|
39
|
50
|
|
|
|
2591
|
sysopen( my $fh, $state_file->{file_name}, &Fcntl::O_CREAT | &Fcntl::O_EXCL | &Fcntl::O_RDWR ) |
297
|
|
|
|
|
|
|
or $state_file->throw("Cannot create state file '$state_file->{file_name}': $!"); |
298
|
39
|
|
|
|
|
127
|
$state_file->{file_handle} = $fh; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
} |
301
|
225
|
|
|
|
|
1448
|
seek( $state_file->{file_handle}, 0, 0 ); |
302
|
225
|
50
|
|
|
|
9802
|
truncate( $state_file->{file_handle}, 0 ) |
303
|
|
|
|
|
|
|
or $state_file->throw("Unable to truncate the state file: $!"); |
304
|
|
|
|
|
|
|
|
305
|
225
|
|
|
|
|
1721
|
$state_file->{data_object}->save_to_cache( $state_file->{file_handle} ); |
306
|
225
|
|
|
|
|
30738
|
$state_file->{file_mtime} = ( stat( $state_file->{file_handle} ) )[9]; |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# Make certain we are at end of file. |
309
|
225
|
50
|
|
|
|
9954589
|
seek( $state_file->{file_handle}, 0, 2 ) |
310
|
|
|
|
|
|
|
or $state_file->throw("Unable to go to end of file: $!"); |
311
|
225
|
|
|
|
|
644
|
$state_file->{file_size} = tell( $state_file->{file_handle} ); |
312
|
225
|
|
|
|
|
616
|
return; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
# Back to StateFile |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
sub new { |
320
|
62
|
|
|
62
|
1
|
5254
|
my ( $class, $args_ref ) = @_; |
321
|
62
|
|
|
|
|
257
|
my $self = bless {}, $class; |
322
|
62
|
100
|
100
|
|
|
361
|
if ( exists $args_ref->{logger} && _valid_logger( $args_ref->{logger} ) ) { |
323
|
1
|
|
|
|
|
4
|
$self->{logger} = $args_ref->{logger}; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
else { |
326
|
61
|
|
|
|
|
391
|
$self->{logger} = $pkg->_get_logger(); |
327
|
61
|
100
|
|
|
|
366
|
if ( exists $args_ref->{logger} ) { |
328
|
1
|
|
|
|
|
4
|
$self->throw('Supplied logger does not support required methods.'); |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
} |
331
|
61
|
50
|
66
|
|
|
287
|
if ( exists $args_ref->{locker} && _valid_file_locker( $args_ref->{locker} ) ) { |
332
|
0
|
|
|
|
|
0
|
$self->{locker} = $args_ref->{locker}; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
else { |
335
|
61
|
|
|
|
|
310
|
$self->{locker} = $pkg->_get_locker(); |
336
|
61
|
100
|
|
|
|
259
|
if ( exists $args_ref->{locker} ) { |
337
|
1
|
|
|
|
|
4
|
$self->throw('Supplied locker does not support required methods.'); |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
} |
340
|
60
|
50
|
0
|
|
|
202
|
$args_ref->{state_file} ||= $args_ref->{cache_file} if exists $args_ref->{cache_file}; |
341
|
60
|
100
|
|
|
|
252
|
$self->throw('No state filename supplied.') unless exists $args_ref->{state_file}; |
342
|
57
|
100
|
|
|
|
252
|
$self->throw('No data object supplied.') unless exists $args_ref->{data_obj}; |
343
|
56
|
|
|
|
|
223
|
my $data_obj = $args_ref->{data_obj}; |
344
|
|
|
|
|
|
|
$self->throw('Data object does not have required interface.') |
345
|
56
|
|
|
|
|
606
|
unless eval { $data_obj->can('load_from_cache') } |
346
|
56
|
100
|
66
|
|
|
108
|
and eval { $data_obj->can('save_to_cache') }; |
|
54
|
|
|
|
|
416
|
|
347
|
|
|
|
|
|
|
|
348
|
54
|
|
|
|
|
561
|
my ( $dirname, $file ) = ( $args_ref->{state_file} =~ m{^(.*)/([^/]*)$}g ); |
349
|
54
|
|
|
|
|
223
|
$dirname =~ s{[^/]+/\.\./}{/}g; # resolve parent references |
350
|
54
|
|
|
|
|
119
|
$dirname =~ s{[^/]+/\.\.$}{}; |
351
|
54
|
|
|
|
|
104
|
$dirname =~ s{/\./}{/}g; # resolve self references |
352
|
54
|
|
|
|
|
108
|
$dirname =~ s{/\.$}{}; |
353
|
54
|
100
|
|
|
|
1390
|
if ( !-d $dirname ) { |
354
|
11
|
50
|
|
|
|
28569
|
File::Path::mkpath($dirname) |
355
|
|
|
|
|
|
|
or $self->throw("Unable to create Cache directory ('$dirname')."); |
356
|
|
|
|
|
|
|
} |
357
|
54
|
|
|
|
|
244
|
$self->{file_name} = "$dirname/$file"; |
358
|
|
|
|
|
|
|
|
359
|
54
|
|
|
|
|
122
|
$self->{data_object} = $data_obj; |
360
|
54
|
|
|
|
|
129
|
$self->{file_mtime} = -1; |
361
|
54
|
|
|
|
|
121
|
$self->{file_size} = -1; |
362
|
54
|
|
|
|
|
128
|
$self->{file_handle} = undef; |
363
|
54
|
|
50
|
|
|
484
|
$self->{flock_timeout} = $args_ref->{timeout} || 60; |
364
|
54
|
|
|
|
|
300
|
Scalar::Util::weaken( $self->{data_object} ); |
365
|
|
|
|
|
|
|
|
366
|
54
|
|
|
|
|
216
|
$self->synch(); |
367
|
|
|
|
|
|
|
|
368
|
46
|
|
|
|
|
276
|
return $self; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# |
372
|
|
|
|
|
|
|
# Return true if the supplied logger object implements the correct |
373
|
|
|
|
|
|
|
# interface, false otherwise. |
374
|
|
|
|
|
|
|
sub _valid_logger { |
375
|
10
|
|
|
10
|
|
22
|
my ($logger) = @_; |
376
|
|
|
|
|
|
|
|
377
|
10
|
|
|
|
|
29
|
foreach my $method (qw/throw warn info notify/) { |
378
|
34
|
100
|
|
|
|
41
|
return unless eval { $logger->can($method) }; |
|
34
|
|
|
|
|
215
|
|
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
8
|
|
|
|
|
40
|
return 1; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# |
385
|
|
|
|
|
|
|
# Return true if the supplied file locker object implements the correct |
386
|
|
|
|
|
|
|
# interface, false otherwise. |
387
|
|
|
|
|
|
|
sub _valid_file_locker { |
388
|
2
|
|
|
2
|
|
6
|
my ($locker) = @_; |
389
|
|
|
|
|
|
|
|
390
|
2
|
|
|
|
|
6
|
foreach my $method (qw/file_lock file_unlock/) { |
391
|
2
|
50
|
|
|
|
5
|
return unless eval { $locker->can($method) }; |
|
2
|
|
|
|
|
33
|
|
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
0
|
|
|
|
|
0
|
return 1; |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
sub synch { |
398
|
464
|
|
|
464
|
1
|
3724
|
my ($self) = @_; |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
# need to set the lock asap to avoid any concurrency problem |
401
|
464
|
|
|
|
|
4497
|
my $guard = cPanel::StateFile::Guard->new( { state => $self } ); |
402
|
|
|
|
|
|
|
|
403
|
464
|
100
|
100
|
|
|
11557
|
if ( !-e $self->{file_name} or -z _ ) { |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# File doesn't exist or is empty, initialize it. |
406
|
40
|
|
|
|
|
178
|
$guard->update_file(); |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
else { |
409
|
424
|
|
|
|
|
1282
|
$self->_resynch($guard); |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# if not assigned anywhere, let the guard die. |
413
|
456
|
100
|
|
|
|
1541
|
return unless defined wantarray; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# Otherwise return it. |
416
|
187
|
|
|
|
|
593
|
return $guard; |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
sub _resynch { |
420
|
444
|
|
|
444
|
|
717
|
my ( $self, $guard ) = @_; |
421
|
|
|
|
|
|
|
|
422
|
444
|
|
|
|
|
6564
|
my ( $mtime, $size ) = ( stat( $self->{file_name} ) )[ 9, 7 ]; |
423
|
444
|
100
|
100
|
|
|
3070
|
if ( $self->{file_mtime} < $mtime || $self->{file_size} != $size ) { |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
# File is newer or a different size |
426
|
19
|
|
33
|
|
|
67
|
$guard ||= cPanel::StateFile::Guard->new( { state => $self } ); |
427
|
19
|
|
|
|
|
76
|
$guard->_open('+<'); |
428
|
19
|
|
|
|
|
121
|
$self->{data_object}->load_from_cache( $self->{file_handle} ); |
429
|
11
|
|
|
|
|
141
|
( $self->{file_mtime}, $self->{file_size} ) = ( stat( $self->{file_handle} ) )[ 9, 7 ]; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
436
|
|
|
|
|
822
|
return $guard; |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
|
435
|
22
|
|
|
22
|
1
|
172
|
sub get_logger { return $_[0]->{logger}; } |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
sub throw { |
438
|
28
|
|
|
28
|
1
|
41
|
my $self = shift; |
439
|
28
|
|
|
|
|
96
|
return $self->{logger}->throw(@_); |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub warn { |
443
|
2
|
|
|
2
|
1
|
1751
|
my $self = shift; |
444
|
2
|
|
|
|
|
16
|
return $self->{logger}->warn(@_); |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
sub info { |
448
|
3
|
|
|
3
|
1
|
367
|
my $self = shift; |
449
|
3
|
|
|
|
|
18
|
return $self->{logger}->info(@_); |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
1; |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
__END__ |