]> git.donarmstrong.com Git - bin.git/blob - postfix_grep
add postfix grep
[bin.git] / postfix_grep
1 #!/usr/bin/perl
2 # postfix_grep greps postfix logs and returns all lines matching pattern and queueid from matching lines
3 # and is released under the terms of the GNU GPL version 3, or any
4 # later version, at your option. See the file README and COPYING for
5 # more information.
6 # Copyright 2013 by Don Armstrong <don@donarmstrong.com>.
7
8
9 use warnings;
10 use strict;
11
12 use Getopt::Long;
13 use Pod::Usage;
14
15 =head1 NAME
16
17 postfix_grep - greps postfix logs and returns all lines matching pattern and queueid from matching lines
18
19 =head1 SYNOPSIS
20
21 postfix_grep [options] [regex] [mailfile]
22
23  Options:
24   --regex, -e regular expression to match
25   --debug, -d debugging level (Default 0)
26   --help, -h display this help
27   --man, -m display manual
28
29 =head1 OPTIONS
30
31 =over
32
33 =item B<--regex, -e>
34
35 Regular expression to match. May be specified multiple times, in which
36 case a message must match all of them.
37
38 If given, all remaining options are considered to be mail files.
39
40 =item B<--debug, -d>
41
42 Debug verbosity. (Default 0)
43
44 =item B<--help, -h>
45
46 Display brief usage information.
47
48 =item B<--man, -m>
49
50 Display this manual.
51
52 =back
53
54 =head1 EXAMPLES
55
56 postfix_grep
57
58 =cut
59
60
61 use vars qw($DEBUG);
62
63 my %options = (debug           => 0,
64                help            => 0,
65                man             => 0,
66                );
67
68 GetOptions(\%options,
69            'regex|e=s@',
70            'debug|d+','help|h|?','man|m');
71
72 pod2usage() if $options{help};
73 pod2usage({verbose=>2}) if $options{man};
74
75 use IO::Uncompress::Gunzip;
76
77 $DEBUG = $options{debug};
78
79 if (not exists $options{regex}) {
80     $options{regex} = shift @ARGV if @ARGV;
81 }
82 $options{regex} = ref($options{regex})?$options{regex}:[$options{regex}];
83
84 my @USAGE_ERRORS;
85 if (not $options{regex}) {
86      push @USAGE_ERRORS,"You must supply a regex using -e or the command line";
87 }
88
89 pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
90
91 my %regexes;
92 for my $regex (@{$options{regex}}) {
93     $regexes{$regex} = qr/\Q$regex\E/;
94 }
95
96
97 my @line_buffer;
98 my $buffer_size=10000;
99
100 if (not @ARGV) {
101     # use undef as a special stdin holder
102     push @ARGV,undef;
103 }
104
105 my %postfix_ids;
106 for my $file (@ARGV) {
107     my $fh = IO::Uncompress::Gunzip->new(defined $file ? $file:\*STDIN,
108                                          MultiStream => 1,
109                                          Transparent => 1,)
110         or die "IO::Uncompress::Gunzip failed: $IO::Uncompress::Gunzip::GunzipError";
111     while (<$fh>) {
112         chomp;
113         my $line = $_;
114         my $keep = 1;
115         for my $regex (keys %regexes) {
116             if ($line !~ $regexes{$regex}) {
117                 $keep = 0;
118             }
119         }
120         if ($keep) {
121             my $queue_id = parse_queue_id($line);
122             $postfix_ids{$queue_id} = 1 if defined $queue_id;
123         }
124         output_and_update_buffer(\%regexes,\%postfix_ids,\@line_buffer,$buffer_size,$line);
125     }
126     while (@line_buffer) {
127         output_if_match(\%regexes,\%postfix_ids,\@line_buffer);
128     }
129     use Data::Dumper;
130     print Dumper(\%postfix_ids);
131     %postfix_ids = ();
132 }
133
134 sub parse_queue_id {
135     my ($line) = @_;
136 #     if ($line =~
137 #         /^(?<date>.+?) # date
138 #          \s(?<hostname>\S+)# hostname
139 #          \spostfix\/(?<process_name>[^[]+) #process
140 #          \[(?<pid>[^\]]+)\]:\s #pid
141 #          (?<queue_id>[A-F0-9]+)\:\s
142 #          (?<rest>.+)
143 #         /x) {
144 #         return $+{queue_id}
145 #     }
146     if ($line =~
147         /^(.+?) # date
148          \s(\S+)# hostname
149          \spostfix\/([^[]+) #process
150          \[([^\]]+)\]:\s #pid
151          ([A-F0-9]+)\:\s # queue_id
152          (.+) # rest
153         /x) {
154         return $5;
155     }
156     return undef;
157     # Apr 28 07:18:43 golf postfix/cleanup[27095]: 94E4F27038:
158 }
159
160 sub output_and_update_buffer{
161     my ($regexes,$postfix_ids,$lb,$lb_max_size,$line) = @_;
162     if (@{$lb} > $lb_max_size) {
163         output_if_match($regexes,$postfix_ids,$lb);
164     }
165     push @{$lb},$line;
166 }
167
168 sub output_if_match {
169     my ($regexes,$postfix_ids,$lb) = @_;
170
171     my $line = shift @{$lb};
172     return unless defined $line;
173     my $queue_id = parse_queue_id($line);
174     if (defined $queue_id and $postfix_ids->{$queue_id}) {
175     } else {
176         for my $regex (keys %{$regexes}) {
177             if ($line !~ $regexes->{$regex}) {
178                 return;
179             }
180         }
181     }
182     print $line."\n";
183 }
184
185 __END__