--- /dev/null
+#!/usr/bin/perl -w
+# -*- perl -*-
+
+=head1 NAME
+
+postfix_mailstats - Plugin to monitor the number of mails delivered and
+rejected by postfix
+
+=head1 CONFIGURATION
+
+Configuration parameters for /etc/munin/postfix_mailstats,
+if you need to override the defaults below:
+
+ [postfix_mailstats]
+ env.logdir - Which logfile to use
+ env.logfile - What file to read in logdir
+
+=head2 DEFAULT CONFIGURATION
+
+ [postfix_mailstats]
+ env.logdir /var/log
+ env.logfile mail.log
+
+=head1 AUTHOR
+
+Records show that the plugin was contributed by Nicolai Langfeldt in
+2003. Nicolai can't find anything in his email about this and expects
+the plugin is based on the corresponding exim plugin - to which it now
+bears no resemblence.
+
+=head1 LICENSE
+
+GPLv2
+
+=head1 MAGIC MARKERS
+
+=begin comment
+
+These magic markers are used by munin-node-configure when installing
+munin-node.
+
+=end comment
+
+ #%# family=manual
+ #%# capabilities=autoconf
+
+=head1 RANDOM COMMENTS
+
+Would be cool if someone ported this to Munin::Plugin for both state
+file and log tailing.
+
+=cut
+
+use strict;
+
+use Munin::Plugin;
+
+my $statefile = $ENV{'MUNIN_PLUGSTATE'} . "/munin-plugin-postfix_mailstats.state";
+my $pos;
+my $delivered = 0;
+my %rejects = ();
+my $LOGDIR = $ENV{'logdir'} || '/var/log';
+my $LOGFILE = $ENV{'logfile'} || 'mail.log';
+
+my $logfile = "$LOGDIR/$LOGFILE";
+
+if ( $ARGV[0] and $ARGV[0] eq "autoconf" )
+{
+ if (-d $LOGDIR)
+ {
+ if (-f $logfile)
+ {
+ if (-r $logfile)
+ {
+ print "yes\n";
+ exit 0;
+ }
+ else
+ {
+ print "no (logfile '$logfile' not readable)\n";
+ }
+ }
+ else
+ {
+ print "no (logfile '$logfile' not found)\n";
+ }
+ }
+ else
+ {
+ print "no (could not find logdir '$LOGDIR')\n";
+ }
+
+ exit 0;
+}
+
+
+if ( -f $statefile)
+{
+ open (IN, '<', $statefile) or die "Unable to open state-file: $!\n";
+ if (<IN> =~ /^(\d+):(\d+)/)
+ {
+ ($pos, $delivered) = ($1, $2);
+ }
+ while (<IN>)
+ {
+ if (/^([0-9a-z.\-]+):(\d+)$/)
+ {
+ $rejects{$1} = $2;
+ }
+ }
+ close IN;
+}
+
+if (! -f $logfile)
+{
+ print "delivered.value U\n";
+ foreach my $reason (sort keys %rejects)
+ {
+ my $fieldname = clean_fieldname("r$reason");
+ print "$fieldname.value U\n";
+ }
+ exit 0;
+}
+
+
+my $startsize = (stat $logfile)[7];
+
+if (!defined $pos)
+{
+ # Initial run.
+ $pos = $startsize;
+}
+
+parseLogfile($logfile, $pos, $startsize);
+$pos = $startsize;
+
+if ( $ARGV[0] and $ARGV[0] eq "config" )
+{
+ print "graph_title Postfix message throughput\n";
+ print "graph_args --base 1000 -l 0\n";
+ print "graph_vlabel mails / \${graph_period}\n";
+ print "graph_scale no\n";
+ print "graph_total Total\n";
+ print "graph_category postfix\n";
+ print "graph_period minute\n";
+ print "delivered.label delivered\n";
+ print "delivered.type DERIVE\n";
+ print "delivered.draw AREA\n";
+ print "delivered.min 0\n";
+ foreach my $reason (sort keys %rejects)
+ {
+ my $fieldname = clean_fieldname("r$reason");
+ print "$fieldname.label reject $reason\n";
+ print "$fieldname.type DERIVE\n";
+ print "$fieldname.draw STACK\n";
+ print "$fieldname.min 0\n";
+ }
+ exit 0;
+}
+
+print "delivered.value $delivered\n";
+foreach my $reason (sort keys %rejects)
+{
+ my $fieldname = clean_fieldname("r$reason");
+ print "$fieldname.value ", $rejects{$reason}, "\n";
+}
+
+if(-l $statefile) {
+ die("$statefile is a symbolic link, refusing to touch it.");
+}
+open (OUT, '>', $statefile) or die "Unable to open statefile: $!\n";
+print OUT "$pos:$delivered\n";
+foreach my $i (sort keys %rejects)
+{
+ print OUT "$i:", $rejects{$i}, "\n";
+}
+close OUT;
+
+sub parseLogfile
+{
+ my ($fname, $start, $stop) = @_;
+ open (LOGFILE, $fname)
+ or die "Unable to open logfile $fname for reading: $!\n";
+ seek (LOGFILE, $start, 0)
+ or die "Unable to seek to $start in $fname: $!\n";
+
+ while (tell (LOGFILE) < $stop)
+ {
+ my $line = <LOGFILE>;
+ chomp ($line);
+
+ if ($line =~ / to=.*, status=sent /)
+ {
+ $delivered++;
+ }
+ elsif ($line =~ /postfix\/smtpd.*proxy-reject: \S+ (\S+)/ ||
+ $line =~ /postfix\/smtpd.*reject: \S+ \S+ \S+ (\S+)/ ||
+ $line =~ /postfix\/cleanup.* reject: (\S+)/ ||
+ $line =~ /postfix\/cleanup.* milter-reject: \S+ \S+ \S+ (\S+)/)
+ {
+ $rejects{$1}++;
+ }
+ }
+ close(LOGFILE) or warn "Error closing $fname: $!\n";
+}
+
+# vim:syntax=perl