line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Mail::Log::Parse::Postfix; |
4
|
|
|
|
|
|
|
{ |
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Mail::Log::Parse::Postfix - Parse and return info in Postfix maillogs |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 SYNOPSIS |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
use Mail::Log::Parse::Postfix; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
(See L for more info.) |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 DESCRIPTION |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
This is a subclass of L, which handles parsing for |
18
|
|
|
|
|
|
|
Postfix mail logs. |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 USAGE |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=cut |
23
|
|
|
|
|
|
|
|
24
|
4
|
|
|
4
|
|
58170
|
use strict; |
|
4
|
|
|
|
|
15
|
|
|
4
|
|
|
|
|
105
|
|
25
|
4
|
|
|
4
|
|
21
|
use warnings; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
115
|
|
26
|
4
|
|
|
4
|
|
19
|
use Scalar::Util qw(refaddr); |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
279
|
|
27
|
4
|
|
|
4
|
|
818
|
use Time::Local; |
|
4
|
|
|
|
|
1749
|
|
|
4
|
|
|
|
|
278
|
|
28
|
4
|
|
|
4
|
|
1227
|
use Mail::Log::Parse 1.0400; |
|
4
|
|
|
|
|
84
|
|
|
4
|
|
|
|
|
159
|
|
29
|
4
|
|
|
4
|
|
22
|
use Mail::Log::Exceptions; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
105
|
|
30
|
4
|
|
|
4
|
|
18
|
use base qw(Mail::Log::Parse Exporter); |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
303
|
|
31
|
|
|
|
|
|
|
|
32
|
4
|
|
|
4
|
|
4109
|
use Memoize; |
|
4
|
|
|
|
|
11047
|
|
|
4
|
|
|
|
|
255
|
|
33
|
|
|
|
|
|
|
memoize('timelocal'); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
BEGIN { |
36
|
4
|
|
|
4
|
|
23
|
use Exporter (); |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
80
|
|
37
|
4
|
|
|
4
|
|
16
|
use vars qw($VERSION); |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
146
|
|
38
|
4
|
|
|
4
|
|
1282
|
$VERSION = '1.0501'; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# A constant, to convert month names to month numbers. |
42
|
|
|
|
|
|
|
my %MONTH_NUMBER = ( Jan => 0 |
43
|
|
|
|
|
|
|
,Feb => 1 |
44
|
|
|
|
|
|
|
,Mar => 2 |
45
|
|
|
|
|
|
|
,Apr => 3 |
46
|
|
|
|
|
|
|
,May => 4 |
47
|
|
|
|
|
|
|
,Jun => 5 |
48
|
|
|
|
|
|
|
,Jul => 6 |
49
|
|
|
|
|
|
|
,Aug => 7 |
50
|
|
|
|
|
|
|
,Sep => 8 |
51
|
|
|
|
|
|
|
,Oct => 9 |
52
|
|
|
|
|
|
|
,Nov => 10 |
53
|
|
|
|
|
|
|
,Dec => 11 |
54
|
|
|
|
|
|
|
); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# We are going to assume we are only run once a day. (Actually, since we only |
57
|
|
|
|
|
|
|
# ever use the _year_...) |
58
|
|
|
|
|
|
|
my @CURR_DATE = localtime; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# |
61
|
|
|
|
|
|
|
# Define class variables. Note that they are hashes... |
62
|
|
|
|
|
|
|
# |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
my %log_info; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# |
67
|
|
|
|
|
|
|
# DESTROY class variables. |
68
|
|
|
|
|
|
|
# |
69
|
|
|
|
|
|
|
### IF NOT DONE THERE IS A MEMORY LEAK. ### |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub DESTROY { |
72
|
7
|
|
|
7
|
|
6963
|
my ($self) = @_; |
73
|
|
|
|
|
|
|
|
74
|
7
|
|
|
|
|
20
|
delete $log_info{$$self}; |
75
|
|
|
|
|
|
|
|
76
|
7
|
|
|
|
|
38
|
$self->SUPER::DESTROY(); |
77
|
|
|
|
|
|
|
|
78
|
7
|
|
|
|
|
109
|
return; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub new { |
82
|
7
|
|
|
7
|
1
|
156360
|
my ($class, $parameters_ref) = @_; |
83
|
|
|
|
|
|
|
|
84
|
7
|
|
|
|
|
49
|
my $self = $class->SUPER::new($parameters_ref); |
85
|
|
|
|
|
|
|
|
86
|
5
|
100
|
|
|
|
26
|
if (defined($parameters_ref->{year})) { |
87
|
1
|
|
|
|
|
4
|
$self->set_year($parameters_ref->{year}); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
5
|
|
|
|
|
16
|
return $self |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=head2 set_year |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
Sets the year, for the log timestamps. If not set, the log is assumed to |
96
|
|
|
|
|
|
|
be for the current year. (Can also be passed in C, with the key 'year'.) |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=cut |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub set_year { |
101
|
5
|
|
|
5
|
1
|
4721
|
my ($self, $year) = @_; |
102
|
5
|
|
|
|
|
24
|
$log_info{refaddr $self}->{year} = $year; |
103
|
5
|
|
|
|
|
30
|
$self->_clear_buffer(); |
104
|
|
|
|
|
|
|
return |
105
|
5
|
|
|
|
|
17
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=head2 next |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Returns a hash of the next line of postfix log data. |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Hash keys are: |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
delay_before_queue, delay_connect_setup, delay_in_queue, |
114
|
|
|
|
|
|
|
delay_message_transmission, from, host, id, msgid, pid, program, |
115
|
|
|
|
|
|
|
relay, size, status, text, timestamp, to, delay, connect, |
116
|
|
|
|
|
|
|
disconnect, previous_host, previous_host_name, previous_host_ip |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
All keys are guaranteed to be present. 'program', 'pid', 'host', 'timestamp', |
119
|
|
|
|
|
|
|
'id' and 'text' are guaranteed to have a value. 'connect' and 'disconnect' are |
120
|
|
|
|
|
|
|
boolean: true if the line is the relevant type of line, false otherwise. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
The 'text' key will have all of the log text B the standard Postfix |
123
|
|
|
|
|
|
|
header. (All of which is in the other keys that are required to have a value.) |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=cut |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub _parse_next_line { |
128
|
|
|
|
|
|
|
# my ($self) = @_; # Saves a couple of microseconds per call not to use $self. |
129
|
|
|
|
|
|
|
# Given the _extreme_ amounts this method is called, |
130
|
|
|
|
|
|
|
# I thought it worth the trade-off. $_[0] == $self |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# The hash we will return. |
133
|
2408
|
|
|
2408
|
|
5652
|
my %line_info = ( program => '' ); |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# Some temp variables. |
136
|
2408
|
|
|
|
|
2762
|
my $line; |
137
|
|
|
|
|
|
|
my @line_data; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# In a mixed-log enviornment, we can't count on any particular line being |
140
|
|
|
|
|
|
|
# something we can parse. Keep going until we can. |
141
|
2408
|
|
|
|
|
5713
|
while ( $line_info{program} !~ m/postfix/ ) { |
142
|
|
|
|
|
|
|
# Read the line. |
143
|
2408
|
100
|
|
|
|
6213
|
$line = $_[0]->_get_data_line() or return undef; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# Start parsing. |
146
|
2404
|
|
|
|
|
72786
|
@line_data = split ' ', $line, 7; |
147
|
|
|
|
|
|
|
|
148
|
4
|
|
|
4
|
|
27
|
no warnings qw(uninitialized); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
3583
|
|
149
|
|
|
|
|
|
|
# Program name and pid. |
150
|
2404
|
|
|
|
|
18594
|
($line_info{program}, $line_info{pid}) = $line_data[4] =~ m/([^[]+)\[(\d+)\]/; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# First few fields are the date. Convert back to Unix format... |
154
|
|
|
|
|
|
|
{ # We don't need all these temp variables hanging around. |
155
|
2404
|
|
|
|
|
2728
|
my ($log_hour, $log_minutes, $log_seconds) = split /:/, $line_data[2]; |
|
2404
|
|
|
|
|
6504
|
|
156
|
2404
|
100
|
|
|
|
3299
|
if (!defined($log_info{${$_[0]}}->{year}) ) { |
|
2404
|
|
|
|
|
6622
|
|
157
|
129
|
|
|
|
|
2462
|
$line_info{timestamp} = timelocal($log_seconds, $log_minutes, $log_hour, $line_data[1], $MONTH_NUMBER{$line_data[0]}, $CURR_DATE[5]); |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
else { |
160
|
2275
|
|
|
|
|
4067
|
$line_info{timestamp} = timelocal($log_seconds, $log_minutes, $log_hour, $line_data[1], $MONTH_NUMBER{$line_data[0]}, $log_info{${$_[0]}}->{year}); |
|
2275
|
|
|
|
|
41998
|
|
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# Machine Hostname |
165
|
2404
|
|
|
|
|
59462
|
$line_info{host} = $line_data[3]; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# Connection ID |
168
|
2404
|
100
|
|
|
|
8264
|
if ( $line_data[5] =~ m/([^:]+):/ ) { |
169
|
1997
|
|
|
|
|
4983
|
$line_info{id} = $1; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
else { |
172
|
407
|
|
|
|
|
666
|
$line_info{id} = undef; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# The full rest is given as text. |
176
|
2404
|
100
|
|
|
|
4249
|
if (defined($line_info{id})) { |
177
|
1997
|
|
|
|
|
3300
|
$line_info{text} = $line_data[6]; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
else { |
180
|
407
|
|
|
|
|
1126
|
$line_info{text} = join ' ', @line_data[5..$#line_data]; |
181
|
|
|
|
|
|
|
} |
182
|
2404
|
|
|
|
|
4301
|
chomp $line_info{text}; |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# Stage two of parsing. |
185
|
|
|
|
|
|
|
# (These may or may not return any info...) |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# To address |
188
|
2404
|
|
|
|
|
7417
|
@{$line_info{to}} = $line_info{text} =~ m/\bto=([^,]+),/g; |
|
2404
|
|
|
|
|
5665
|
|
189
|
|
|
|
|
|
|
|
190
|
2404
|
100
|
|
|
|
5241
|
if ( defined($line_info{to}[0]) ) { |
191
|
|
|
|
|
|
|
# Relay |
192
|
1158
|
|
|
|
|
5837
|
($line_info{relay}) = $line_info{text} =~ m/\brelay=([^,]+),/; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Delays |
195
|
|
|
|
|
|
|
($line_info{delay_before_queue}, $line_info{delay_in_queue}, $line_info{delay_connect_setup}, $line_info{delay_message_transmission} ) |
196
|
1158
|
|
|
|
|
7596
|
= $line_info{text} =~ m{\bdelays=([^/]+)/([^/]+)/([^/]+)/([^,]+),}; |
197
|
1158
|
|
|
|
|
5041
|
($line_info{delay}) = $line_info{text} =~ m/\bdelay=([\d.]+),/; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# Status |
200
|
1158
|
|
|
|
|
4766
|
($line_info{status}) = $line_info{text} =~ m/\bstatus=(.+)\Z/; |
201
|
|
|
|
|
|
|
|
202
|
1158
|
|
|
|
|
4896
|
@line_info{'from', 'size', 'msgid', 'connect', 'disconnect', 'previous_host' |
203
|
|
|
|
|
|
|
, 'previous_host_name', 'previous_host_ip' } = undef; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
else { |
206
|
|
|
|
|
|
|
# From address |
207
|
1246
|
|
|
|
|
3900
|
($line_info{from}) = $line_info{text} =~ m/\bfrom=([^,]+),/; |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# Size |
210
|
1246
|
|
|
|
|
2856
|
($line_info{size}) = $line_info{text} =~ m/\bsize=([^,]+),/; |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# Message ID |
213
|
1246
|
|
|
|
|
2766
|
($line_info{msgid}) = $line_info{text} =~ m/\bmessage-id=(.+)$/; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# Connect (Boolean) |
216
|
1246
|
|
|
|
|
3295
|
$line_info{connect} = $line_info{text} =~ m/\bconnect from/; |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Disconnect (Boolean) |
219
|
1246
|
|
|
|
|
2788
|
$line_info{disconnect} = $line_info{text} =~ m/\bdisconnect from/; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# Remote host info. (Only if above.) |
222
|
1246
|
100
|
66
|
|
|
3917
|
if ( $line_info{connect} || $line_info{disconnect} ) { |
223
|
399
|
|
|
|
|
1599
|
($line_info{previous_host}) = $line_info{text} =~ m/connect from (\S+)/; |
224
|
|
|
|
|
|
|
($line_info{previous_host_name}, $line_info{previous_host_ip}) |
225
|
399
|
|
|
|
|
2078
|
= $line_info{previous_host} =~ m/([^[]+)\[([^\]]+)\]/; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
else { |
228
|
847
|
|
|
|
|
1915
|
@line_info{'previous_host', 'previous_host_name', 'previous_host_ip'} = undef; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
1246
|
|
|
|
|
4417
|
@line_info{'relay', 'status', 'delay_before_queue', 'delay_in_queue' |
232
|
|
|
|
|
|
|
, 'delay_connect_setup', 'delay_message_transmission', 'delay'} |
233
|
|
|
|
|
|
|
= undef; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
# Return the data. |
237
|
2404
|
|
|
|
|
10493
|
return \%line_info; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head1 BUGS |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
None known at the moment. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=head1 REQUIRES |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
L, L, L, L, |
247
|
|
|
|
|
|
|
L |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=head1 AUTHOR |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
Daniel T. Staal |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
DStaal@usa.net |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=head1 SEE ALSO |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
L, for the main documentation on this module set. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=head1 HISTORY |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
April 17, 2009 (1.5.1) - No longer uses C<_set_current_position_as_next_line>, |
262
|
|
|
|
|
|
|
instead lets Mail::Log::Parse manage automatically. (Requires 1.4.0.) |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
April 9, 2009 (1.5.0) - Now reads the connecting host from the 'connect' and |
265
|
|
|
|
|
|
|
'disconnect' lines in the log. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
Feb 27, 2009 (1.4.12) - Quieted an occasional error, if the log line doesn't |
268
|
|
|
|
|
|
|
have the standard Postfix format. |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
Dec 23, 2008 (1.4.11) - Further speedups. Now requires Mail::Log::Parse of at |
271
|
|
|
|
|
|
|
least version 1.3.0. |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Dec 09, 2008 (1.4.10) - Profiled code, did some speedups. Added dependency on |
274
|
|
|
|
|
|
|
Memoize: For large logs this is a massive speedup. For extremely sparse logs |
275
|
|
|
|
|
|
|
it may not be, but sparse logs are likely to be small. |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
Nov 28, 2008 - Switched 'total_delay' to slightly more universal 'delay'. |
278
|
|
|
|
|
|
|
Sped up some regexes. |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
Nov 11, 2008 - Switched to using the bufferable C<_parse_next_line> instead of |
281
|
|
|
|
|
|
|
the unbuffered C. |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
Nov 6, 2008 - Added C and alternate year handling, in case we aren't |
284
|
|
|
|
|
|
|
dealing with this year's logs. (From the todo list.) |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
Oct 24, 2008 - Added 'connect' and 'disconnect' members to the return hash. |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
Oct 6, 2008 - Initial version. |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=head1 COPYRIGHT and LICENSE |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
Copyright (c) 2008 Daniel T. Staal. All rights reserved. |
293
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or |
294
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
This copyright will expire in 30 years, or 5 years after the author's |
297
|
|
|
|
|
|
|
death, whichever is longer. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=cut |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# End module package. |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
1; |