]> jfr.im git - munin-plugins.git/commitdiff
add mailstats - stock version
authorJohn Runyon <redacted>
Thu, 28 Sep 2023 21:52:48 +0000 (16:52 -0500)
committerJohn Runyon <redacted>
Thu, 28 Sep 2023 21:52:48 +0000 (16:52 -0500)
postfix_mailstats [new file with mode: 0755]

diff --git a/postfix_mailstats b/postfix_mailstats
new file mode 100755 (executable)
index 0000000..518c9c9
--- /dev/null
@@ -0,0 +1,207 @@
+#!/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