#!@@PERL@@ -w # -*- perl -*- # vim:syntax=perl use strict; use warnings; =head1 NAME exim_mailstats - Plugin to monitor the number of mails received and delivered by exim. =head1 APPLICABLE SYSTEMS Exim version 3 or 4. =head1 CONFIGURATION Usually no configuration is needed for this plugin. [exim*] env.logdir /path/to/exim-logs/ env.logname mainlog env.exim /usr/sbin/exim The default value for the C variable is determined by running C. C is the default name given to exims main log, but it has been known to be called "main.log" in some versions of Red Hat/Fedora for example. NOTE: If you need to set logname you must also set logdir as there is no automatic way to determine the path then. The default value of the C variable is C, but if there is a executable called C this is used instead. =head1 INTERPRETATION Need some input from an exim postmaster here. =head1 MAGIC MARKERS #%# family=auto #%# capabilities=autoconf =head1 BUGS None known =head1 AUTHOR Copyright (C) 2000-2009 Torstein Svendsen, Henrik Grindal Bakken, Jimmy Olsen, Nicolai Langfeldt and others. Torstein Svendsen recalls originally writing this with Henrik Grindal Bakken to create MRTG graphs of exims mailqueues for Linpros client RunBox. Thus this code predates Munin itself. The first traces of the plugin in CVS seems to be in 2002. Further messing by Jimmy Olsen the same year. Bugfixing and cleanup by Nicolai Langfeldt 2008. =head1 LICENSE GPLv2 =cut # In most installations the "use lib" can be removed as the Munin # modules are installed in perls module path. use lib $ENV{'MUNIN_LIBDIR'}; use Munin::Plugin; ########## sub get_exim_logfile { my ($spec, $type, $time) = @_; chomp($spec); $time ||= time(); my $logfile = $spec; $logfile =~ s/^log_file_path = //; $logfile =~ s/\%s/$type/; if ($logfile =~ /\%D/) { my @t = localtime($time); my $ts = sprintf("%04d%02d%02d", $t[5] + 1900, $t[4] + 1, $t[3]); $logfile =~ s/\%D/$ts/g; } my @lfiles = split(/\s?:\s?/, $logfile); foreach (@lfiles) { return $_ unless /^syslog/; } return; } my ($pos, $received, $completed, $rejected); sub parseEximfile { my ($fname, $start) = @_; my ($LOGFILE, $rotated) = tail_open($fname, $start); if ($rotated || $received eq 'U') { # Reset everything if the log has been rotated or we've just initialised $pos = $received = $completed = $rejected = 0; } while (<$LOGFILE>) { chomp; if (/ <= /) { $received++; } elsif (m/ Completed$/) { $completed++; } elsif (/ rejected /) { $rejected++; } } return tail_close($LOGFILE); } my $EXIM = "/usr/sbin/exim"; $EXIM = "/usr/sbin/exim4" if (-x "/usr/sbin/exim4"); # a Debianism $EXIM = $ENV{'exim'} if defined $ENV{'exim'}; my $LOGDIR = $ENV{'logdir'} || undef; my $LOGNAME = $ENV{'logname'} || ''; my $logfile; if ($ARGV[0] and $ARGV[0] eq "autoconf") { if (defined($LOGDIR)) { if (! -d $LOGDIR) { print "no (logdir '$LOGDIR' does not exist)\n"; exit 0; } $logfile = $LOGDIR . '/' . ($LOGNAME || 'mainlog'); } else { my $logfilespec = `$EXIM -bP log_file_path 2>/dev/null`; if ($? == 0) { $logfile = get_exim_logfile($logfilespec, 'main'); if (!defined($logfile)) { print "no (not able to parse output of '$EXIM -bP log_file_path' = '$logfilespec')\n"; exit 0; } } elsif ($? == 127) { print "no (exim not found)\n"; exit 0; } else { print "no ('$EXIM -bP log_file_path' returned an error)\n"; exit 0; } } if ($logfile) { if (-r "$logfile") { print "yes\n"; } else { print "no (logfile '$logfile' not readable)\n"; } } exit 0; } my $logfilespec; if (defined($LOGDIR)) { $logfilespec = ''; $logfile = $LOGDIR . '/' . ($LOGNAME || 'mainlog'); } else { $logfilespec = `$EXIM -bP log_file_path 2>/dev/null`; $logfile = get_exim_logfile($logfilespec, 'main'); } die "Logfile '$logfile' is not readable\n" unless -r $logfile; if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_title Exim mail throughput\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel mails/\${graph_period}\n"; print "graph_scale no\n"; print "graph_category exim\n"; print "received.label received\n"; print "received.type DERIVE\n"; print "received.min 0\n"; print "received.draw AREA\n"; print "completed.label completed\n"; print "completed.type DERIVE\n"; print "completed.min 0\n"; print "rejected.label rejected\n"; print "rejected.type DERIVE\n"; print "rejected.min 0\n"; exit 0; } ($pos, $received, $completed, $rejected) = restore_state(); if (! defined $pos) { # No state file present. Avoid startup spike: Do not read log # file up to now, but remember how large it is now, and next # time read from there. $pos = (stat $logfile)[7]; # File size $received = $completed = $rejected = 'U'; } else { $pos = parseEximfile($logfile, $pos); } print "received.value $received\n"; print "completed.value $completed\n"; print "rejected.value $rejected\n"; save_state($pos, $received, $completed, $rejected);