| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package CPAN::Testers::Data::Uploads; |
|
2
|
|
|
|
|
|
|
|
|
3
|
13
|
|
|
13
|
|
349357
|
use strict; |
|
|
13
|
|
|
|
|
32
|
|
|
|
13
|
|
|
|
|
502
|
|
|
4
|
13
|
|
|
13
|
|
73
|
use warnings; |
|
|
13
|
|
|
|
|
22
|
|
|
|
13
|
|
|
|
|
466
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
13
|
|
|
13
|
|
71
|
use vars qw($VERSION); |
|
|
13
|
|
|
|
|
79
|
|
|
|
13
|
|
|
|
|
1112
|
|
|
7
|
|
|
|
|
|
|
$VERSION = '0.21'; |
|
8
|
|
|
|
|
|
|
$|++; |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#---------------------------------------------------------------------------- |
|
11
|
|
|
|
|
|
|
# Library Modules |
|
12
|
|
|
|
|
|
|
|
|
13
|
13
|
|
|
13
|
|
86
|
use base qw(Class::Accessor::Fast); |
|
|
13
|
|
|
|
|
21
|
|
|
|
13
|
|
|
|
|
26381
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
13
|
|
|
13
|
|
93816
|
use CPAN::DistnameInfo; |
|
|
13
|
|
|
|
|
22877
|
|
|
|
13
|
|
|
|
|
1563
|
|
|
16
|
13
|
|
|
13
|
|
14804
|
use CPAN::Testers::Common::DBUtils; |
|
|
13
|
|
|
|
|
450963
|
|
|
|
13
|
|
|
|
|
139
|
|
|
17
|
13
|
|
|
13
|
|
19424
|
use CPAN::Testers::Common::Article; |
|
|
13
|
|
|
|
|
306428
|
|
|
|
13
|
|
|
|
|
1409
|
|
|
18
|
13
|
|
|
13
|
|
18879
|
use Config::IniFiles; |
|
|
13
|
|
|
|
|
655879
|
|
|
|
13
|
|
|
|
|
566
|
|
|
19
|
13
|
|
|
13
|
|
162
|
use DBI; |
|
|
13
|
|
|
|
|
24
|
|
|
|
13
|
|
|
|
|
559
|
|
|
20
|
13
|
|
|
13
|
|
197
|
use File::Basename; |
|
|
13
|
|
|
|
|
28
|
|
|
|
13
|
|
|
|
|
1370
|
|
|
21
|
13
|
|
|
13
|
|
17563
|
use File::Find::Rule; |
|
|
13
|
|
|
|
|
151558
|
|
|
|
13
|
|
|
|
|
143
|
|
|
22
|
13
|
|
|
13
|
|
802
|
use File::Path; |
|
|
13
|
|
|
|
|
26
|
|
|
|
13
|
|
|
|
|
945
|
|
|
23
|
13
|
|
|
13
|
|
20398
|
use File::Slurp; |
|
|
13
|
|
|
|
|
257862
|
|
|
|
13
|
|
|
|
|
1287
|
|
|
24
|
13
|
|
|
13
|
|
29005
|
use Getopt::Long; |
|
|
13
|
|
|
|
|
209731
|
|
|
|
13
|
|
|
|
|
89
|
|
|
25
|
13
|
|
|
13
|
|
15495
|
use IO::AtomicFile; |
|
|
13
|
|
|
|
|
43334
|
|
|
|
13
|
|
|
|
|
794
|
|
|
26
|
13
|
|
|
13
|
|
254
|
use IO::File; |
|
|
13
|
|
|
|
|
28
|
|
|
|
13
|
|
|
|
|
2175
|
|
|
27
|
13
|
|
|
13
|
|
22193
|
use Net::NNTP; |
|
|
13
|
|
|
|
|
599718
|
|
|
|
13
|
|
|
|
|
1314
|
|
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
#---------------------------------------------------------------------------- |
|
30
|
|
|
|
|
|
|
# Variables |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
my (%backups); |
|
33
|
13
|
|
|
13
|
|
180
|
use constant LASTMAIL => '_lastmail'; |
|
|
13
|
|
|
|
|
31
|
|
|
|
13
|
|
|
|
|
1081
|
|
|
34
|
13
|
|
|
13
|
|
79
|
use constant LOGFILE => '_uploads.log'; |
|
|
13
|
|
|
|
|
27
|
|
|
|
13
|
|
|
|
|
649
|
|
|
35
|
13
|
|
|
13
|
|
71
|
use constant JOURNAL => '_journal.sql'; |
|
|
13
|
|
|
|
|
27
|
|
|
|
13
|
|
|
|
|
62738
|
|
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
my %phrasebook = ( |
|
38
|
|
|
|
|
|
|
'FindAuthor' => 'SELECT * FROM ixlatest WHERE author=?', |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
'FindDistVersion' => 'SELECT type FROM uploads WHERE author=? AND dist=? AND version=?', |
|
41
|
|
|
|
|
|
|
'InsertDistVersion' => 'INSERT INTO uploads (type,author,dist,version,filename,released) VALUES (?,?,?,?,?,?)', |
|
42
|
|
|
|
|
|
|
'UpdateDistVersion' => 'UPDATE uploads SET type=? WHERE author=? AND dist=? AND version=?', |
|
43
|
|
|
|
|
|
|
'FindDistTypes' => 'SELECT * FROM uploads WHERE type=?', |
|
44
|
|
|
|
|
|
|
'DeleteAll' => 'DELETE FROM uploads', |
|
45
|
|
|
|
|
|
|
'SelectAll' => 'SELECT * FROM uploads', |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
'DeleteAllIndex' => 'DELETE FROM ixlatest', |
|
48
|
|
|
|
|
|
|
'DeleteIndex' => 'DELETE FROM ixlatest WHERE dist=? AND author=?', |
|
49
|
|
|
|
|
|
|
'FindIndex' => 'SELECT * FROM ixlatest WHERE dist=? AND author=?', |
|
50
|
|
|
|
|
|
|
'InsertIndex' => 'INSERT INTO ixlatest (oncpan,author,version,released,dist) VALUES (?,?,?,?,?)', |
|
51
|
|
|
|
|
|
|
'AmendIndex' => 'UPDATE ixlatest SET oncpan=? WHERE author=? AND version=? AND dist=?', |
|
52
|
|
|
|
|
|
|
'UpdateIndex' => 'UPDATE ixlatest SET oncpan=?,version=?,released=? WHERE dist=? AND author=?', |
|
53
|
|
|
|
|
|
|
'BuildAuthorIndex' => 'SELECT x.author,x.version,x.released,x.dist,x.type FROM (SELECT dist, MAX(released) AS mv FROM uploads WHERE author=? GROUP BY dist) AS y INNER JOIN uploads AS x ON x.dist=y.dist AND x.released=y.mv ORDER BY released', |
|
54
|
|
|
|
|
|
|
'GetAllAuthors' => 'SELECT distinct(author) FROM uploads', |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
'InsertRequest' => 'INSERT INTO page_requests (type,name,weight) VALUES (?,?,5)', |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
'ParseFailed' => 'REPLACE INTO uploads_failed (source,type,dist,version,file,pause,created) VALUES (?,?,?,?,?,?,?)', |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# SQLite backup |
|
61
|
|
|
|
|
|
|
'CreateTable' => 'CREATE TABLE uploads (type text, author text, dist text, version text, filename text, released int)', |
|
62
|
|
|
|
|
|
|
); |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
my $extn = qr/\.(tar\.(gz|bz2)|tgz|zip)$/; |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
my %oncpan = ( |
|
67
|
|
|
|
|
|
|
'backpan' => 2, |
|
68
|
|
|
|
|
|
|
'cpan' => 1, |
|
69
|
|
|
|
|
|
|
'upload' => 1 |
|
70
|
|
|
|
|
|
|
); |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
#---------------------------------------------------------------------------- |
|
73
|
|
|
|
|
|
|
# The Application Programming Interface |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub new { |
|
76
|
14
|
|
|
14
|
1
|
18477
|
my $class = shift; |
|
77
|
|
|
|
|
|
|
|
|
78
|
14
|
|
|
|
|
36
|
my $self = {}; |
|
79
|
14
|
|
|
|
|
42
|
bless $self, $class; |
|
80
|
|
|
|
|
|
|
|
|
81
|
14
|
|
|
|
|
242
|
$self->_init_options(@_); |
|
82
|
9
|
|
|
|
|
81
|
return $self; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub DESTROY { |
|
86
|
14
|
|
|
14
|
|
41475
|
my $self = shift; |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors( |
|
90
|
|
|
|
|
|
|
qw( uploads backpan cpan logfile logclean lastfile journal |
|
91
|
|
|
|
|
|
|
mgenerate mupdate mbackup mreindex )); |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub process { |
|
94
|
5
|
|
|
5
|
1
|
1155838
|
my $self = shift; |
|
95
|
5
|
100
|
|
|
|
42
|
$self->generate() if($self->mgenerate); |
|
96
|
5
|
100
|
|
|
|
83
|
$self->reindex() if($self->mreindex); |
|
97
|
5
|
100
|
|
|
|
239
|
$self->update() if($self->mupdate); |
|
98
|
5
|
100
|
|
|
|
426
|
$self->backup() if($self->mbackup); |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub generate { |
|
102
|
1
|
|
|
1
|
1
|
15
|
my $self = shift; |
|
103
|
1
|
|
|
|
|
6
|
my $db = $self->uploads; |
|
104
|
|
|
|
|
|
|
|
|
105
|
1
|
|
|
|
|
14
|
$self->_log("Restarting uploads database"); |
|
106
|
1
|
|
|
|
|
84
|
$db->do_query($phrasebook{'DeleteAll'}); |
|
107
|
|
|
|
|
|
|
|
|
108
|
1
|
|
|
|
|
37212
|
$self->_log("Creating BACKPAN entries"); |
|
109
|
1
|
|
|
|
|
105
|
my @files = File::Find::Rule->file()->name($extn)->in($self->backpan); |
|
110
|
1
|
|
|
|
|
9939
|
$self->_parse_archive('backpan',$_) for(@files); |
|
111
|
|
|
|
|
|
|
|
|
112
|
1
|
|
|
|
|
11
|
$self->_log("Creating CPAN entries"); |
|
113
|
1
|
|
|
|
|
94
|
@files = File::Find::Rule->file()->name($extn)->in($self->cpan); |
|
114
|
1
|
|
|
|
|
7407
|
$self->_parse_archive('cpan',$_) for(@files); |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub reindex { |
|
118
|
3
|
|
|
3
|
1
|
48
|
my $self = shift; |
|
119
|
3
|
|
|
|
|
18
|
my $db = $self->uploads; |
|
120
|
|
|
|
|
|
|
|
|
121
|
3
|
|
|
|
|
29
|
$self->_log("Reindexing by author"); |
|
122
|
|
|
|
|
|
|
|
|
123
|
3
|
|
|
|
|
184
|
my $next = $db->iterator('hash',$phrasebook{'GetAllAuthors'}); |
|
124
|
3
|
|
|
|
|
838
|
while(my $author = $next->()) { |
|
125
|
45
|
|
|
|
|
3530384
|
$self->_log(".. author = $author->{author}"); |
|
126
|
45
|
|
|
|
|
14364
|
my @rows = $db->get_query('hash',$phrasebook{'BuildAuthorIndex'},$author->{author}); |
|
127
|
45
|
|
|
|
|
23934
|
for my $row (@rows) { |
|
128
|
51
|
|
|
|
|
246765
|
$self->_log(".... dist = $row->{dist}, latest = $row->{version}"); |
|
129
|
51
|
|
|
|
|
3098
|
$db->do_query($phrasebook{'DeleteIndex'},$row->{dist},$row->{author}); |
|
130
|
51
|
|
|
|
|
4441745
|
$db->do_query($phrasebook{'InsertIndex'},$oncpan{$row->{type}},$row->{author},$row->{version},$row->{released},$row->{dist}); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
3
|
|
|
|
|
168907
|
$self->_log("Reindexing authors done"); |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub update { |
|
138
|
1
|
|
|
1
|
1
|
16
|
my $self = shift; |
|
139
|
1
|
|
|
|
|
5
|
my $db = $self->uploads; |
|
140
|
|
|
|
|
|
|
|
|
141
|
1
|
|
|
|
|
10
|
$self->_open_journal(); |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# get list of db known CPAN distributions |
|
144
|
1
|
|
|
|
|
277
|
my @rows = $db->get_query('hash',$phrasebook{'FindDistTypes'},'cpan'); |
|
145
|
1
|
|
|
|
|
1502
|
my %cpan = map {$_->{filename} => $_} @rows; |
|
|
63
|
|
|
|
|
172
|
|
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# get currently mirrored CPAN entries |
|
148
|
1
|
|
|
|
|
11
|
$self->_log("Updating CPAN entries"); |
|
149
|
1
|
|
|
|
|
108
|
my @files = File::Find::Rule->file()->name($extn)->in($self->cpan); |
|
150
|
1
|
|
|
|
|
7219
|
for(@files) { |
|
151
|
63
|
50
|
|
|
|
147
|
if(my $file = $self->_parse_archive('cpan',$_,1)) { |
|
152
|
63
|
|
|
|
|
195
|
delete $cpan{$file}; |
|
153
|
|
|
|
|
|
|
} else { |
|
154
|
|
|
|
|
|
|
#$self->_log(".. cannot parse: $_"); |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# demote any distributions no longer on CPAN |
|
159
|
1
|
|
|
|
|
7
|
$self->_log("Updating BACKPAN entries"); |
|
160
|
1
|
|
|
|
|
65
|
for my $file (keys %cpan) { |
|
161
|
|
|
|
|
|
|
#$self->_log("backpan => $cpan{$file}->{dist} => $cpan{$file}->{version} => $cpan{$file}->{author} => $cpan{$file}->{released}"); |
|
162
|
0
|
|
|
|
|
0
|
$self->_write_journal('UpdateDistVersion','backpan',$cpan{$file}->{author},$cpan{$file}->{dist},$cpan{$file}->{version}); |
|
163
|
0
|
|
|
|
|
0
|
$db->do_query($phrasebook{'AmendIndex'},2,$cpan{$file}->{author},$cpan{$file}->{version},$cpan{$file}->{dist}); |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# read NNTP |
|
167
|
1
|
|
|
|
|
4
|
$self->_log("Updating NNTP entries"); |
|
168
|
1
|
|
|
|
|
36
|
my ($nntp,$num,$first,$last) = $self->_nntp_connect(); |
|
169
|
1
|
|
|
|
|
4
|
my $lastid = $self->_lastid(); |
|
170
|
1
|
50
|
|
|
|
5
|
return if($last <= $lastid); |
|
171
|
|
|
|
|
|
|
|
|
172
|
1
|
|
|
|
|
6
|
$self->_log(".. from $lastid to $last"); |
|
173
|
1
|
|
|
|
|
30
|
for(my $id = $lastid+1; $id <= $last; $id++) { |
|
174
|
|
|
|
|
|
|
#$self->_log("NNTP ID = $id"); |
|
175
|
72870
|
50
|
|
|
|
256457
|
my $article = join "", @{$nntp->article($id) || []}; |
|
|
72870
|
|
|
|
|
366559
|
|
|
176
|
72870
|
100
|
|
|
|
4414645
|
next unless($article); |
|
177
|
4
|
|
|
|
|
57
|
my $object = CPAN::Testers::Common::Article->new($article); |
|
178
|
4
|
50
|
|
|
|
9021
|
next unless($object); |
|
179
|
4
|
|
|
|
|
28
|
$self->_log("... [$id] subject=".($object->subject())); |
|
180
|
|
|
|
|
|
|
|
|
181
|
4
|
|
|
|
|
189
|
my ($name,$version,$cpanid,$date,$filename); |
|
182
|
4
|
50
|
|
|
|
21
|
if($object->parse_upload()) { |
|
183
|
4
|
|
|
|
|
771
|
$name = $object->distribution; |
|
184
|
4
|
|
|
|
|
26
|
$version = $object->version; |
|
185
|
4
|
|
|
|
|
23
|
$cpanid = $object->author; |
|
186
|
4
|
|
|
|
|
26
|
$date = $object->epoch; |
|
187
|
4
|
|
|
|
|
22
|
$filename = $object->filename; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
#$self->_log("... name=$name"); |
|
191
|
|
|
|
|
|
|
#$self->_log("... version=$version"); |
|
192
|
|
|
|
|
|
|
#$self->_log("... cpanid=$cpanid"); |
|
193
|
|
|
|
|
|
|
#$self->_log("... date=$date"); |
|
194
|
|
|
|
|
|
|
|
|
195
|
4
|
50
|
33
|
|
|
65
|
next unless($name && $version && $cpanid && $date); |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
196
|
|
|
|
|
|
|
#$self->_log("upload => $name => $version => $cpanid => $date"); |
|
197
|
|
|
|
|
|
|
|
|
198
|
4
|
|
|
|
|
21
|
$self->_update_index($cpanid,$version,$date,$name,1); |
|
199
|
4
|
|
|
|
|
168173
|
my @rows = $db->get_query('array',$phrasebook{'FindDistVersion'},$cpanid,$name,$version); |
|
200
|
4
|
100
|
|
|
|
1857
|
next if(@rows); |
|
201
|
3
|
|
|
|
|
33
|
$self->_write_journal('InsertDistVersion','upload',$cpanid,$name,$version,$filename,$date); |
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
|
|
204
|
1
|
|
|
|
|
11
|
$self->_lastid($last); |
|
205
|
1
|
|
|
|
|
6
|
$self->_close_journal(); |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub backup { |
|
209
|
1
|
|
|
1
|
1
|
9
|
my $self = shift; |
|
210
|
1
|
|
|
|
|
5
|
my $db = $self->uploads; |
|
211
|
|
|
|
|
|
|
|
|
212
|
1
|
50
|
|
|
|
10
|
if(my @journals = $self->_find_journals()) { |
|
213
|
1
|
|
|
|
|
5
|
for my $driver (keys %backups) { |
|
214
|
1
|
50
|
33
|
|
|
20
|
if($driver =~ /(CSV|SQLite)/i && !$backups{$driver}{'exists'}) { |
|
215
|
1
|
|
|
|
|
9
|
$backups{$driver}{db}->do_query($phrasebook{'CreateTable'}); |
|
216
|
1
|
|
|
|
|
85826
|
$backups{$driver}{'exists'} = 1; |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
|
|
220
|
1
|
|
|
|
|
6
|
for my $journal (@journals) { |
|
221
|
1
|
50
|
|
|
|
13
|
next if($journal =~ /TMP$/); # don't process active journals |
|
222
|
1
|
|
|
|
|
12
|
$self->_log("Processing journal $journal"); |
|
223
|
1
|
|
|
|
|
68
|
my $lines = $self->_read_journal($journal); |
|
224
|
1
|
|
|
|
|
5
|
for my $line (@$lines) { |
|
225
|
3
|
|
|
|
|
55011
|
my ($phrase,@args) = @$line; |
|
226
|
3
|
|
|
|
|
14
|
for my $driver (keys %backups) { |
|
227
|
3
|
|
|
|
|
29
|
$backups{$driver}{db}->do_query($phrasebook{$phrase},@args); |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
1
|
|
|
|
|
18056
|
$self->_done_journal($journal); |
|
232
|
|
|
|
|
|
|
} |
|
233
|
1
|
|
|
|
|
58
|
$self->_log("Processed journals"); |
|
234
|
|
|
|
|
|
|
} else { |
|
235
|
0
|
|
|
|
|
0
|
for my $driver (keys %backups) { |
|
236
|
0
|
0
|
|
|
|
0
|
if($backups{$driver}{'exists'}) { |
|
|
|
0
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
0
|
$backups{$driver}{db}->do_query($phrasebook{'DeleteAll'}); |
|
238
|
|
|
|
|
|
|
} elsif($driver =~ /(CSV|SQLite)/i) { |
|
239
|
0
|
|
|
|
|
0
|
$backups{$driver}{db}->do_query($phrasebook{'CreateTable'}); |
|
240
|
0
|
|
|
|
|
0
|
$backups{$driver}{'exists'} = 1; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
|
|
244
|
0
|
|
|
|
|
0
|
$self->_log("Backup via DBD drivers"); |
|
245
|
|
|
|
|
|
|
|
|
246
|
0
|
|
|
|
|
0
|
my $rows = $db->iterator('array',$phrasebook{'SelectAll'}); |
|
247
|
0
|
|
|
|
|
0
|
while(my $row = $rows->()) { |
|
248
|
0
|
|
|
|
|
0
|
for my $driver (keys %backups) { |
|
249
|
0
|
|
|
|
|
0
|
$backups{$driver}{db}->do_query($phrasebook{'InsertDistVersion'},@$row); |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
} |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# handle the CSV exception |
|
255
|
1
|
50
|
|
|
|
123
|
if($backups{CSV}) { |
|
256
|
0
|
|
|
|
|
0
|
$self->_log("Backup to CSV file"); |
|
257
|
0
|
|
|
|
|
0
|
$backups{CSV}{db} = undef; # close db handle |
|
258
|
0
|
0
|
|
|
|
0
|
my $fh1 = IO::File->new('uploads','r') or die "Cannot read temporary database file 'uploads'\n"; |
|
259
|
0
|
0
|
|
|
|
0
|
my $fh2 = IO::File->new($backups{CSV}{dbfile},'w+') or die "Cannot write to CSV database file $backups{CSV}{dbfile}\n"; |
|
260
|
0
|
|
|
|
|
0
|
while(<$fh1>) { print $fh2 $_ } |
|
|
0
|
|
|
|
|
0
|
|
|
261
|
0
|
|
|
|
|
0
|
$fh1->close; |
|
262
|
0
|
|
|
|
|
0
|
$fh2->close; |
|
263
|
0
|
|
|
|
|
0
|
unlink('uploads'); |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
} |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub help { |
|
268
|
5
|
|
|
5
|
1
|
13
|
my ($self,$full,$mess) = @_; |
|
269
|
|
|
|
|
|
|
|
|
270
|
5
|
100
|
|
|
|
535
|
print "\n$mess\n\n" if($mess); |
|
271
|
|
|
|
|
|
|
|
|
272
|
5
|
100
|
|
|
|
15
|
if($full) { |
|
273
|
4
|
|
|
|
|
116
|
print <
|
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
Usage: $0 --config= [-g] [-r] [-u] [-b] [-h] [-v] |
|
276
|
|
|
|
|
|
|
[--logfile=] [--logclean] |
|
277
|
|
|
|
|
|
|
[--lastmail=] [--journal=] |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
--config= database configuration file |
|
280
|
|
|
|
|
|
|
-g generate new database |
|
281
|
|
|
|
|
|
|
-r reindex database (*) |
|
282
|
|
|
|
|
|
|
-u update existing database |
|
283
|
|
|
|
|
|
|
-b backup database to portable files |
|
284
|
|
|
|
|
|
|
-h this help screen |
|
285
|
|
|
|
|
|
|
-v program version |
|
286
|
|
|
|
|
|
|
--logfile= trace log file |
|
287
|
|
|
|
|
|
|
--logclean overwrite exisiting log file |
|
288
|
|
|
|
|
|
|
--lastmail= last id file |
|
289
|
|
|
|
|
|
|
--journal= SQL journal file path |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Notes: |
|
292
|
|
|
|
|
|
|
* A generate request automatically includes a reindex. |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
HERE |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
|
|
298
|
5
|
|
|
|
|
98
|
print "$0 v$VERSION\n\n"; |
|
299
|
5
|
|
|
|
|
21
|
exit(0); |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
#---------------------------------------------------------------------------- |
|
303
|
|
|
|
|
|
|
# Private Methods |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub _parse_archive { |
|
306
|
189
|
|
|
189
|
|
7118
|
my ($self,$type,$file,$update) = @_; |
|
307
|
189
|
|
|
|
|
1328
|
my $db = $self->uploads; |
|
308
|
189
|
|
|
|
|
2612
|
my $dist = CPAN::DistnameInfo->new($file); |
|
309
|
|
|
|
|
|
|
|
|
310
|
189
|
|
|
|
|
28896
|
my $name = $dist->dist; # "CPAN-DistnameInfo" |
|
311
|
189
|
|
|
|
|
1600
|
my $version = $dist->version; # "0.02" |
|
312
|
189
|
|
|
|
|
1338
|
my $cpanid = $dist->cpanid; # "GBARR" |
|
313
|
189
|
|
|
|
|
1565
|
my $filename = $dist->filename; # "CPAN-DistnameInfo-0.02.tar.gz" |
|
314
|
189
|
|
|
|
|
13560
|
my $date = (stat($file))[9]; |
|
315
|
|
|
|
|
|
|
|
|
316
|
189
|
50
|
33
|
|
|
4067
|
unless($name && defined $version && $cpanid && $date) { |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
317
|
|
|
|
|
|
|
#$self->_log("PARSE: FAIL file=$file, $type => $name => $version => $cpanid => $date => $filename"); |
|
318
|
0
|
|
|
|
|
0
|
$file =~ s!/opt/projects/CPAN/!!; |
|
319
|
0
|
|
|
|
|
0
|
$db->do_query($phrasebook{'ParseFailed'},$file,$type,$name,$version,$filename,$cpanid,$date); |
|
320
|
0
|
|
|
|
|
0
|
return; |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
#$self->_log("$type => $name => $version => $cpanid => $date"); |
|
323
|
|
|
|
|
|
|
|
|
324
|
189
|
|
|
|
|
1678
|
my @rows = $db->get_query('array',$phrasebook{'FindDistVersion'},$cpanid,$name,$version); |
|
325
|
189
|
100
|
|
|
|
99087
|
if(@rows) { |
|
326
|
126
|
100
|
|
|
|
949
|
if($type ne $rows[0]->[0]) { |
|
327
|
63
|
|
|
|
|
399
|
$db->do_query($phrasebook{'UpdateDistVersion'},$type,$cpanid,$name,$version); |
|
328
|
63
|
50
|
33
|
|
|
2362521
|
$self->_update_index($cpanid,$version,$date,$name,$oncpan{$type}) |
|
329
|
|
|
|
|
|
|
if($update && $type ne 'backpan'); |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
} else { |
|
332
|
63
|
|
|
|
|
609
|
$db->do_query($phrasebook{'InsertDistVersion'},$type,$cpanid,$name,$version,$filename,$date); |
|
333
|
63
|
50
|
|
|
|
1478064
|
$self->_update_index($cpanid,$version,$date,$name,$oncpan{$type}) if($update); |
|
334
|
|
|
|
|
|
|
} |
|
335
|
|
|
|
|
|
|
|
|
336
|
189
|
|
|
|
|
3073
|
return $filename; |
|
337
|
|
|
|
|
|
|
} |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
sub _update_index { |
|
340
|
4
|
|
|
4
|
|
10
|
my ($self,$author,$version,$date,$name,$oncpan) = @_; |
|
341
|
4
|
|
|
|
|
17
|
my $db = $self->uploads; |
|
342
|
|
|
|
|
|
|
|
|
343
|
4
|
|
|
|
|
43
|
my @index = $db->get_query('hash',$phrasebook{'FindIndex'},$name,$author); |
|
344
|
4
|
100
|
|
|
|
1529
|
if(@index) { |
|
345
|
3
|
100
|
|
|
|
15
|
if($date > $index[0]->{released}) { |
|
346
|
2
|
|
|
|
|
12
|
$db->do_query($phrasebook{'UpdateIndex'},$oncpan,$version,$date,$name,$author); |
|
347
|
2
|
|
|
|
|
256638
|
$self->_log("... index update [$author,$version,$date,$name,$oncpan]"); |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
} else { |
|
350
|
1
|
|
|
|
|
15
|
$db->do_query($phrasebook{'InsertIndex'},$oncpan,$author,$version,$date,$name); |
|
351
|
1
|
|
|
|
|
63576
|
$self->_log("... index insert [$author,$version,$date,$name,$oncpan]"); |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# add to page_requests table to update letter index pages and individual pages |
|
355
|
4
|
|
|
|
|
164
|
$db->do_query($phrasebook{'InsertRequest'},'ixauth',substr($author,0,1)); |
|
356
|
4
|
|
|
|
|
329138
|
$db->do_query($phrasebook{'InsertRequest'},'ixdist',substr($name,0,1)); |
|
357
|
4
|
|
|
|
|
276010
|
$db->do_query($phrasebook{'InsertRequest'},'author',$author); |
|
358
|
4
|
|
|
|
|
170305
|
$db->do_query($phrasebook{'InsertRequest'},'distro',$name); |
|
359
|
|
|
|
|
|
|
} |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
sub _nntp_connect { |
|
362
|
|
|
|
|
|
|
# connect to NNTP server |
|
363
|
1
|
50
|
|
1
|
|
13
|
my $nntp = Net::NNTP->new("nntp.perl.org") or die "Cannot connect to nntp.perl.org"; |
|
364
|
1
|
|
|
|
|
30
|
my ($num,$first,$last) = $nntp->group("perl.cpan.uploads"); |
|
365
|
|
|
|
|
|
|
|
|
366
|
1
|
|
|
|
|
89
|
return ($nntp,$num,$first,$last); |
|
367
|
|
|
|
|
|
|
} |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub _lastid { |
|
370
|
6
|
|
|
6
|
|
1729
|
my ($self,$id) = @_; |
|
371
|
6
|
|
|
|
|
32
|
my $f = $self->lastfile; |
|
372
|
|
|
|
|
|
|
|
|
373
|
6
|
100
|
|
|
|
171
|
unless( -f $f) { |
|
374
|
2
|
|
|
|
|
244
|
mkpath(dirname($f)); |
|
375
|
2
|
|
|
|
|
18
|
overwrite_file( $f, 0 ); |
|
376
|
2
|
|
50
|
|
|
612
|
$id ||= 0; |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
|
|
379
|
6
|
100
|
|
|
|
21
|
if($id) { overwrite_file( $f, $id ); } |
|
|
2
|
|
|
|
|
32
|
|
|
380
|
4
|
|
|
|
|
20
|
else { $id = read_file($f); } |
|
381
|
|
|
|
|
|
|
|
|
382
|
6
|
|
|
|
|
878
|
return $id; |
|
383
|
|
|
|
|
|
|
} |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# generate atomic journal file name |
|
386
|
|
|
|
|
|
|
sub _open_journal { |
|
387
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
|
388
|
1
|
|
|
|
|
43
|
my @now = localtime(time); |
|
389
|
1
|
|
|
|
|
6
|
my $file = sprintf "%s.%04d%02d%02d%02d%02d%02d", $self->journal, $now[5]+1900,$now[4]+1,$now[3],$now[2],$now[1],$now[0]; |
|
390
|
1
|
50
|
|
|
|
23
|
$self->{current} = IO::AtomicFile->new($file,'w+') or die "Cannot write to journal file [$file]: $!\n"; |
|
391
|
|
|
|
|
|
|
} |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
sub _write_journal { |
|
394
|
3
|
|
|
3
|
|
59
|
my ($self,$phrase,@args) = @_; |
|
395
|
3
|
|
|
|
|
15
|
my $fh = $self->{current}; |
|
396
|
|
|
|
|
|
|
|
|
397
|
3
|
|
|
|
|
148
|
print $fh "$phrase," . join(',',@args) . "\n"; |
|
398
|
|
|
|
|
|
|
|
|
399
|
3
|
|
|
|
|
24
|
my $db = $self->uploads; |
|
400
|
3
|
|
|
|
|
51
|
$db->do_query($phrasebook{$phrase},@args); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub _close_journal { |
|
404
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
|
405
|
1
|
|
|
|
|
9
|
$self->{current}->close; |
|
406
|
|
|
|
|
|
|
} |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
sub _find_journals { |
|
409
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
|
410
|
1
|
|
|
|
|
12
|
my @files = glob($self->journal . '.*'); |
|
411
|
1
|
|
|
|
|
145
|
return @files; |
|
412
|
|
|
|
|
|
|
} |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub _read_journal { |
|
415
|
1
|
|
|
1
|
|
3
|
my ($self,$journal) = @_; |
|
416
|
1
|
|
|
|
|
183
|
my @lines; |
|
417
|
|
|
|
|
|
|
|
|
418
|
1
|
50
|
|
|
|
9
|
my $fh = IO::File->new($journal,'r') or die "Cannot read journal file [$journal]: $!\n"; |
|
419
|
1
|
|
|
|
|
762
|
while(<$fh>) { |
|
420
|
3
|
|
|
|
|
22
|
my @fields = split(/,/); |
|
421
|
3
|
|
|
|
|
18
|
push @lines, \@fields; |
|
422
|
|
|
|
|
|
|
} |
|
423
|
1
|
|
|
|
|
5
|
$fh->close; |
|
424
|
1
|
|
|
|
|
23
|
return \@lines; |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
sub _done_journal { |
|
428
|
1
|
|
|
1
|
|
5
|
my ($self,$journal) = @_; |
|
429
|
1
|
|
|
|
|
5
|
my $cmd = "mv $journal logs"; |
|
430
|
1
|
|
|
|
|
17720
|
system($cmd); |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
sub _init_options { |
|
434
|
14
|
|
|
14
|
|
31
|
my $self = shift; |
|
435
|
14
|
|
|
|
|
574
|
my %hash = @_; |
|
436
|
14
|
|
|
|
|
188
|
my %options; |
|
437
|
|
|
|
|
|
|
|
|
438
|
14
|
|
|
|
|
307
|
GetOptions( \%options, |
|
439
|
|
|
|
|
|
|
'config=s', |
|
440
|
|
|
|
|
|
|
'generate|g', |
|
441
|
|
|
|
|
|
|
'update|u', |
|
442
|
|
|
|
|
|
|
'reindex|r', |
|
443
|
|
|
|
|
|
|
'backup|b', |
|
444
|
|
|
|
|
|
|
'journal|j=s', |
|
445
|
|
|
|
|
|
|
'logfile|l=s', |
|
446
|
|
|
|
|
|
|
'logclean=s', |
|
447
|
|
|
|
|
|
|
'lastfile=s', |
|
448
|
|
|
|
|
|
|
'help|h', |
|
449
|
|
|
|
|
|
|
'version|v' |
|
450
|
|
|
|
|
|
|
); |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# default to API settings if no command line option |
|
453
|
14
|
|
|
|
|
10817
|
for(qw(config generate update reindex fast backup help version)) { |
|
454
|
112
|
100
|
33
|
|
|
447
|
$options{$_} ||= $hash{$_} if(defined $hash{$_}); |
|
455
|
|
|
|
|
|
|
} |
|
456
|
|
|
|
|
|
|
|
|
457
|
14
|
100
|
|
|
|
79
|
$self->help(1) if($options{help}); |
|
458
|
13
|
100
|
|
|
|
52
|
$self->help(0) if($options{version}); |
|
459
|
|
|
|
|
|
|
|
|
460
|
12
|
100
|
100
|
|
|
110
|
$self->help(1,"Must specify at least one option from 'generate' (-g), 'reindex' (-r),\n'update' (-u) and/or 'backup' (-b)") |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
461
|
|
|
|
|
|
|
unless($options{generate} || $options{update} || $options{backup} || $options{reindex}); |
|
462
|
11
|
100
|
|
|
|
42
|
$self->help(1,"Must specific the configuration file") unless( $options{config}); |
|
463
|
10
|
100
|
|
|
|
280
|
$self->help(1,"Configuration file [$options{config}] not found") unless(-f $options{config}); |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
# load configuration |
|
466
|
9
|
|
|
|
|
131
|
my $cfg = Config::IniFiles->new( -file => $options{config} ); |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# configure sources |
|
469
|
9
|
100
|
|
|
|
35662
|
if($options{generate}) { |
|
470
|
1
|
|
|
|
|
8
|
my $dir = $cfg->val('MASTER','BACKPAN'); |
|
471
|
1
|
50
|
|
|
|
27
|
$self->help(1,"No source location for 'BACKPAN' in config file") if(! $dir); |
|
472
|
1
|
50
|
|
|
|
43
|
$self->help(1,"Cannot find source location for 'BACKPAN': [$dir]") if(!-d $dir); |
|
473
|
1
|
|
|
|
|
7
|
$self->backpan($dir); |
|
474
|
1
|
|
|
|
|
28
|
$self->mgenerate(1); |
|
475
|
1
|
|
|
|
|
9
|
$self->mreindex(1); |
|
476
|
|
|
|
|
|
|
} |
|
477
|
9
|
100
|
100
|
|
|
91
|
if($options{generate} || $options{update}) { |
|
478
|
7
|
|
|
|
|
34
|
my $dir = $cfg->val('MASTER','CPAN'); |
|
479
|
7
|
50
|
|
|
|
165
|
$self->help(1,"No source location for 'CPAN' in config file") if(! $dir); |
|
480
|
7
|
50
|
|
|
|
195
|
$self->help(1,"Cannot find source location for 'CPAN': [$dir]") if(!-d $dir); |
|
481
|
7
|
|
|
|
|
40
|
$self->cpan($dir); |
|
482
|
|
|
|
|
|
|
} |
|
483
|
9
|
100
|
|
|
|
128
|
if($options{reindex}) { |
|
484
|
1
|
|
|
|
|
7
|
$self->mreindex(1); |
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
|
|
487
|
9
|
100
|
|
|
|
76
|
$self->mupdate(1) if($options{update}); |
|
488
|
9
|
|
50
|
|
|
131
|
$self->logfile( $hash{logfile} || $options{logfile} || $cfg->val('MASTER','logfile' ) || LOGFILE ); |
|
489
|
9
|
|
100
|
|
|
389
|
$self->logclean( $hash{logclean} || $options{logclean} || $cfg->val('MASTER','logclean' ) || 0 ); |
|
490
|
9
|
|
50
|
|
|
344
|
$self->lastfile( $hash{lastfile} || $options{lastfile} || $cfg->val('MASTER','lastfile' ) || LASTMAIL ); |
|
491
|
9
|
|
100
|
|
|
314
|
$self->journal( $hash{journal} || $options{journal} || $cfg->val('MASTER','journal' ) || JOURNAL ); |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# configure upload DB |
|
494
|
9
|
50
|
|
|
|
278
|
$self->help(1,"No configuration for UPLOADS database") unless($cfg->SectionExists('UPLOADS')); |
|
495
|
9
|
|
100
|
|
|
211
|
my %opts = map {$_ => ($cfg->val('UPLOADS',$_) || undef)} qw(driver database dbfile dbhost dbport dbuser dbpass); |
|
|
63
|
|
|
|
|
1246
|
|
|
496
|
9
|
|
|
|
|
342
|
my $db = CPAN::Testers::Common::DBUtils->new(%opts); |
|
497
|
9
|
50
|
|
|
|
308
|
$self->help(1,"Cannot configure UPLOADS database") unless($db); |
|
498
|
9
|
|
|
|
|
132
|
$self->uploads($db); |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
# configure backup DBs |
|
501
|
9
|
100
|
|
|
|
250
|
if($options{backup}) { |
|
502
|
1
|
50
|
|
|
|
5
|
$self->help(1,"No configuration for BACKUPS with backup option") unless($cfg->SectionExists('BACKUPS')); |
|
503
|
|
|
|
|
|
|
|
|
504
|
1
|
|
|
|
|
34
|
my %available_drivers = map { $_ => 1 } DBI->available_drivers; |
|
|
7
|
|
|
|
|
635
|
|
|
505
|
1
|
|
|
|
|
7
|
my @drivers = $cfg->val('BACKUPS','drivers'); |
|
506
|
1
|
|
|
|
|
28
|
for my $driver (@drivers) { |
|
507
|
2
|
100
|
|
|
|
8
|
unless($available_drivers{$driver}) { |
|
508
|
1
|
|
|
|
|
169
|
warn "No DBI support for '$driver', ignoring\n"; |
|
509
|
1
|
|
|
|
|
6
|
next; |
|
510
|
|
|
|
|
|
|
} |
|
511
|
|
|
|
|
|
|
|
|
512
|
1
|
50
|
|
|
|
5
|
$self->help(1,"No configuration for backup option '$driver'") unless($cfg->SectionExists($driver)); |
|
513
|
|
|
|
|
|
|
|
|
514
|
1
|
|
100
|
|
|
27
|
my %opt = map {$_ => ($cfg->val($driver,$_) || undef)} qw(driver database dbfile dbhost dbport dbuser dbpass); |
|
|
7
|
|
|
|
|
146
|
|
|
515
|
1
|
50
|
|
|
|
68
|
$backups{$driver}{'exists'} = $driver =~ /SQLite/i ? -f $opt{database} : 1; |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# CSV is a bit of an oddity! |
|
518
|
1
|
50
|
|
|
|
6
|
if($driver =~ /CSV/i) { |
|
519
|
0
|
|
|
|
|
0
|
$backups{$driver}{'exists'} = 0; |
|
520
|
0
|
|
|
|
|
0
|
$backups{$driver}{'dbfile'} = $opt{dbfile}; |
|
521
|
0
|
|
|
|
|
0
|
$opt{dbfile} = 'uploads'; |
|
522
|
0
|
|
|
|
|
0
|
unlink($opt{dbfile}); |
|
523
|
|
|
|
|
|
|
} |
|
524
|
|
|
|
|
|
|
|
|
525
|
1
|
|
|
|
|
11
|
$backups{$driver}{db} = CPAN::Testers::Common::DBUtils->new(%opt); |
|
526
|
1
|
50
|
|
|
|
26
|
$self->help(1,"Cannot configure BACKUPS database for '$driver'") unless($backups{$driver}{db}); |
|
527
|
|
|
|
|
|
|
} |
|
528
|
|
|
|
|
|
|
|
|
529
|
1
|
50
|
|
|
|
12
|
$self->mbackup(1) if(keys %backups); |
|
530
|
|
|
|
|
|
|
} |
|
531
|
|
|
|
|
|
|
} |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
sub _log { |
|
534
|
122
|
|
|
122
|
|
4355
|
my $self = shift; |
|
535
|
122
|
50
|
|
|
|
660
|
my $log = $self->logfile or return; |
|
536
|
122
|
100
|
|
|
|
4499
|
mkpath(dirname($log)) unless(-f $log); |
|
537
|
|
|
|
|
|
|
|
|
538
|
122
|
100
|
|
|
|
652
|
my $mode = $self->logclean ? 'w+' : 'a+'; |
|
539
|
122
|
|
|
|
|
1341
|
$self->logclean(0); |
|
540
|
|
|
|
|
|
|
|
|
541
|
122
|
|
|
|
|
4491
|
my @dt = localtime(time); |
|
542
|
122
|
|
|
|
|
1325
|
my $dt = sprintf "%04d/%02d/%02d %02d:%02d:%02d", $dt[5]+1900,$dt[4]+1,$dt[3],$dt[2],$dt[1],$dt[0]; |
|
543
|
|
|
|
|
|
|
|
|
544
|
122
|
50
|
|
|
|
1517
|
my $fh = IO::File->new($log,$mode) or die "Cannot write to log file [$log]: $!\n"; |
|
545
|
122
|
|
|
|
|
20222
|
print $fh "$dt ", @_, "\n"; |
|
546
|
122
|
|
|
|
|
721
|
$fh->close; |
|
547
|
|
|
|
|
|
|
} |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
q!Will code for a damn fine Balti!; |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
__END__ |