Commit | Line | Data |
---|---|---|
b2f0d2f3 TH |
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 |