Initial check-in.
[monitoring/munin.git] / thh_exim_mailstats
1 #!/usr/bin/perl
2 #
3 # Plugin to monitor the number of mails received and delivered by exim.
4 #
5 # Usage: copy or link into /etc/munin/node.d/
6 #
7 # Parameters:
8 #
9 #       config   (required)
10 #       autoconf (optional - used by munin-config)
11 #
12 # Config variables:
13 #
14 #       logdir       - Override what exim says
15 #       exim         - Where's exim?
16 #
17 # $Log$
18 # Revision 1.7.2.2  2005/03/12 23:07:17  jimmyo
19 # Avoid negative spike in generic/exim_mailstats.
20 #
21 # Revision 1.7.2.1  2005/03/09 19:24:12  jimmyo
22 # Thanks to Stephen Gran, generic/exim_mailstats now graphs rejects (Deb#295799).
23 #
24 # Revision 1.7  2004/12/10 18:51:43  jimmyo
25 # linux/apt* has been forced to LANG=C, to get predictable output.
26 #
27 # Revision 1.6  2004/12/10 10:47:47  jimmyo
28 # Change name from ${scale} to ${graph_period}, to be more consistent.
29 #
30 # Revision 1.5  2004/12/09 22:12:54  jimmyo
31 # Added "graph_period" option, to make "graph_sums" usable.
32 #
33 # Revision 1.4  2004/11/21 00:16:56  jimmyo
34 # Changed a lot of plugins so they use DERIVE instead of COUNTER.
35 #
36 # Revision 1.3  2004/11/10 15:54:49  jimmyo
37 # Applied patch from Torstein T. Svendsen to generic/exim_mailstats, to handle logfiles with timestamps in the name (SF#1055214)
38 #
39 # Revision 1.2  2004/05/20 13:57:12  jimmyo
40 # Set categories to some of the plugins.
41 #
42 # Revision 1.1  2004/01/02 18:50:00  jimmyo
43 # Renamed occurrances of lrrd -> munin
44 #
45 # Revision 1.1.1.1  2004/01/02 15:18:07  jimmyo
46 # Import of LRRD CVS tree after renaming to Munin
47 #
48 # Revision 1.7  2003/11/15 11:10:28  jimmyo
49 # Various fixes
50 #
51 # Revision 1.6  2003/11/07 17:43:16  jimmyo
52 # Cleanups and log entries
53 #
54 #
55 #
56 # Magic markers (optional - used by munin-config and some installation
57 # scripts):
58 #
59 #%# family=auto
60 #%# capabilities=autoconf
61
62
63 sub get_exim_logfile {
64     my ($spec, $type, $time) = @_;
65     chomp($spec);
66     $time = time() unless $time;
67     my $logfile = $spec;
68     $logfile =~ s/^log_file_path = //;
69     $logfile =~ s/\%s/$type/;
70
71     if( $logfile =~ /\%D/ ) {
72         my @t=localtime($time);
73         my $ts = sprintf("%04d%02d%02d",$t[5]+1900, $t[4]+1, $t[3]);
74         $logfile =~ s/\%D/$ts/g;
75     }        
76     my @lfiles = split(/\s?:\s?/, $logfile);
77     foreach (@lfiles) {
78         return $_ unless /^syslog/;
79     }
80 }
81
82
83
84 $statefile = "/var/lib/munin/plugin-state/plugin-exim_mailstats.state";
85 $pos   = undef;
86 $received = 0;
87 $completed = 0;
88 $rejected = 0;
89 $greylisted = 0;
90 $rbl = 0;
91 $spam = 0;
92 $sender = 0;
93 $user = 0;
94 $protocol = 0;
95 $helo = 0;
96 $virus = 0;
97 $fakemx = 0;
98 ($dirname = $0) =~ s/[^\/]+$//;
99 $EXIM = "/usr/sbin/exim";
100 $EXIM = "/usr/sbin/exim4" if (-x "/usr/sbin/exim4"); # a Debianism
101 $EXIM = $ENV{'exim'} if defined  $ENV{'exim'};
102 $EXIM = "/usr/exim/bin/exim"; 
103 $LOGDIR = $ENV{'logdir'} || undef;
104
105 if ( $ARGV[0] and $ARGV[0] eq "autoconf" )
106 {
107     my $logfile;
108     
109     if(defined($LOGDIR)) {
110         if(! -d $LOGDIR) {
111             print "no (logdir does not exist)\n";
112             exit(1);
113         }
114         $logfile = $LOGDIR . '/mainlog';
115     } else {
116         my $logfilespec = `$EXIM -bP log_file_path 2>/dev/null`;
117         if (! $?)
118         {
119             $logfile = get_exim_logfile( $logfilespec, 'main');
120         }
121         elsif ($? eq "127")
122         {
123             print "no (exim not found)\n";
124         }
125         else
126         {
127             print "no\n";
128         }
129     }
130
131     if ($logfile)
132     {
133         if (-r "$logfile")
134         {
135             print "yes\n";
136             exit 0;
137         }
138         else
139         {
140             print "no (logfile not readable)\n";
141         }
142     }
143     exit 1;
144 }
145
146
147 my $logfilespec;
148
149 if(defined($LOGDIR)) {
150     $logfilespec = '';
151     $logfile = $LOGDIR . '/' . 'mainlog';
152 } else {
153     $logfilespec = `$EXIM -bP log_file_path`;
154     $logfile = get_exim_logfile( $logfilespec, 'main');
155 }
156
157 exit 1 unless -r $logfile;
158
159 if( $logfilespec =~ /\%D/ ) {
160     $rotlogfile = get_exim_logfile( $logfilespec, 'main', (time()-(24*60*60)));
161 } else {
162 if (-f "$logfile.0")
163 {
164     $rotlogfile = $logfile . ".0";
165 }
166 elsif (-f "$logfile.1")
167 {
168     $rotlogfile = $logfile . ".1";
169 }
170 elsif (-f "$logfile.01")
171 {
172     $rotlogfile = $logfile . ".01";
173 }
174 else
175 {
176     $rotlogfile = $logfile . ".0";
177 }
178 }
179
180 if ( $ARGV[0] and $ARGV[0] eq "config" )
181 {
182     print "graph_title Exim mail throughput\n";
183     print "graph_args --base 1000 -l 0\n";
184     print "graph_vlabel mails/\${graph_period}\n";
185     print "graph_scale  no\n";
186     print "graph_category Mail\n";
187     print "received.label received\n";
188     print "received.type DERIVE\n";
189     print "received.min 0\n";
190     print "received.draw LINE\n";
191     print "completed.label completed\n";
192     print "completed.type DERIVE\n";
193     print "completed.min 0\n";
194 #    print "completed.draw AREA\n";
195     print "rejected.label rejected (other)\n";
196     print "rejected.type DERIVE\n";
197     print "rejected.min 0\n";
198 #    print "rejected.draw STACK\n";
199     print "rbl.label rejected (RBL)\n";
200     print "rbl.type DERIVE\n";
201     print "rbl.min 0\n";
202 #    print "rbl.draw STACK\n";
203     print "spam.label rejected (spam)\n";
204     print "spam.type DERIVE\n";
205     print "spam.min 0\n";
206 #    print "spam.draw STACK\n";
207     print "greylisted.label greylisted\n";
208     print "greylisted.type DERIVE\n";
209     print "greylisted.min 0\n";
210 #    print "greylisted.draw STACK\n";
211     print "sender.label rejected (sender verify)\n";
212     print "sender.type DERIVE\n";
213     print "sender.min 0\n";
214 #    print "sender.draw STACK\n";
215     print "user.label rejected (user unknown)\n";
216     print "user.type DERIVE\n";
217     print "user.min 0\n";
218 #    print "user.draw STACK\n";
219     print "protocol.label rejected (protocol violation)\n";
220     print "protocol.type DERIVE\n";
221     print "protocol.min 0\n";
222 #    print "protocol.draw STACK\n";
223     print "helo.label rejected (helo/ehlo)\n";
224     print "helo.type DERIVE\n";
225     print "helo.min 0\n";
226 #    print "helo.draw STACK\n";
227     print "virus.label rejected (virus)\n";
228     print "virus.type DERIVE\n";
229     print "virus.min 0\n";
230 #    print "virus.draw STACK\n";
231     print "fakemx.label rejected (fakeMX)\n";
232     print "fakemx.type DERIVE\n";
233     print "fakemx.min 0\n";
234 #    print "fakemx.draw STACK\n";
235     exit 0;
236 }
237
238 if (! -f $logfile and ! -f $rotlogfile)
239 {
240     print "completed.value U\n";
241     print "received.value U\n";
242     print "rejected.value U\n";
243     print "greylisted.value U\n";
244     print "rbl.value U\n";
245     print "spam.value U\n";
246     print "sender.value U\n";
247     print "user.value U\n";
248     print "protocol.value U\n";
249     print "helo.value U\n";
250     print "virus.value U\n";
251     print "fakemx.value U\n";
252     exit 0;
253 }
254
255 if (-f "$statefile")
256 {
257     open (IN, "$statefile") or exit 4;
258         my $in = <IN>;
259     if ($in =~ /^(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+)/)
260     {   
261         ($pos, $received, $completed, $rejected, $greylisted, $rbl, $spam, $sender, $user, $protocol, $helo, $virus, $fakemx) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);
262     }
263     elsif ($in =~ /^(\d+):(\d+):(\d+):(\d+):(\d+):(\d+):(\d+)/)
264     {
265         ($pos, $received, $completed, $rejected, $greylisted, $rbl, $spam) = ($1, $2, $3, $4, $5, $6, $7);
266     }
267     elsif ($in =~ /^(\d+):(\d+):(\d+):(\d+)/)
268     {   
269         ($pos, $received, $completed, $rejected) = ($1, $2, $3, $4);
270     }
271
272     elsif ($in =~ /^(\d+):(\d+):(\d+)/)
273     {
274         ($pos, $received, $completed) = ($1, $2, $3);
275         $rejected = 0;
276     }
277     close IN;
278 }
279
280 $startsize = (stat $logfile)[7];
281
282 if (!defined $pos)
283 {
284     # Initial run.
285     $pos = $startsize;
286 }
287
288 if ($startsize < $pos)
289 {
290     # Log rotated
291     parseEximfile ($rotlogfile, $pos, (stat $rotlogfile)[7]);
292     $pos = 0;
293 }
294
295 parseEximfile ($logfile, $pos, $startsize);
296 $pos = $startsize;
297
298 print "received.value $received\n";
299 print "completed.value $completed\n";
300 print "rejected.value $rejected\n";
301 print "rbl.value $rbl\n";
302 print "spam.value $spam\n";
303 print "greylisted.value $greylisted\n";
304 print "sender.value $sender\n";
305 print "user.value $user\n";
306 print "protocol.value $protocol\n";
307 print "helo.value $helo\n";
308 print "virus.value $virus\n";
309 print "fakemx.value $fakemx\n";
310
311 if(-l $statefile) {
312         die("$statefile is a symbolic link, refusing to touch it.");
313 }                               
314 open (OUT, ">$statefile") or exit 4;
315 print OUT "$pos:$received:$completed:$rejected:$greylisted:$rbl:$spam:$sender:$user:$protocol:$helo:$virus:$fakemx\n";
316 close OUT;
317
318 sub parseEximfile 
319 {    
320     my ($fname, $start, $stop) = @_;
321     open (LOGFILE, $fname) or exit 3;
322     seek (LOGFILE, $start, 0) or exit 2;
323
324     while (tell (LOGFILE) < $stop) 
325     {
326         my $line =<LOGFILE>;
327         chomp ($line);
328
329         if (substr ($line, 37,2 ) eq '<=') 
330         {
331             $received++;
332         } 
333         elsif (substr ($line, 37,9) eq 'Completed')
334         {
335             $completed++;
336         }
337         elsif ($line=~/greylisted\.$/)
338         {   
339             $greylisted++;
340         }
341         elsif ($line=~/in a RBL/)
342         {   
343             $rbl++;
344         }
345         elsif ($line=~/considered spam/)
346         {   
347             $spam++;
348         }
349         elsif ($line=~/sender verify/)
350         {   
351             $sender++;
352         }
353         elsif ($line=~/Unknown user/)
354         {   
355             $user++;
356         }
357         elsif ($line=~/synchronization error/)
358         {   
359             $protocol++;
360         }
361         elsif ($line=~/HELO\/EHLO/)
362         {   
363             $helo++;
364         }
365         elsif ($line=~/contains a virus/)
366         {   
367             $virus++;
368         }
369         elsif ($line=~/fakemx for/)
370         {   
371             $fakemx++;
372         }
373         elsif ($line=~/rejected/)
374         {
375             $rejected++;
376         }
377     }
378     close(LOGFILE);    
379 }
380
381 # vim:syntax=perl
This page took 0.020516 seconds and 3 git commands to generate.