my $statefile = $ENV{'MUNIN_PLUGSTATE'} . "/munin-plugin-postfix_mailstats.state";
my $pos;
-my $delivered = 0;
+my %descriptions = (
+ messages => 'Number of messages',
+ recipients => 'Number of recipients',
+ local => 'Outgoing local messages',
+ smtp => 'Outgoing SMTP messages',
+ pickup => 'Incoming local messages',
+ smtpd => 'Incoming SMTP messages',
+ bounce => 'Bounces handled',
+);
+my %stats = map { $_ => 0 } keys %descriptions;
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 ( $ARGV[0] and $ARGV[0] eq "autoconf" ) {
+ my $logfile;
+ `which postconf >/dev/null 2>/dev/null`;
+ if (!$?) { # if postconf returns success
+ `journalctl --facility=mail --show-cursor -n 0`;
+ if (!$?) { # if journalctl returns success
+ print "yes\n";
+ } else {
+ print "no (journalctl returned error)\n";
+ }
+ } else {
+ print "no (postfix not found)\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 $statefile) {
+ open (IN, '<', $statefile) or die "Unable to open state-file: $!\n";
+ chomp($pos = <IN>);
+ chomp(my $stat_line = <IN>);
+ for my $stat (split /:/, $stat_line) {
+ my ($k, $v) = split /=/, $stat, 2;
+ $stats{$k} = $v;
+ }
+ 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;
+if (!defined $pos) { # Initial run.
+ my $cursor = `journalctl --facility=mail --show-cursor -n 0 | tail -n 1`;
+ $pos = ($cursor =~ s/^-- cursor: //r);
}
-
-my $startsize = (stat $logfile)[7];
-
-if (!defined $pos)
-{
- # Initial run.
- $pos = $startsize;
+$pos = parseLogfile($pos);
+
+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 "messages.label messages\n";
+ print "messages.type DERIVE\n";
+ print "messages.min 0\n";
+
+ foreach my $key (sort keys %descriptions) {
+ print "$key.label $key\n";
+ print "$key.info $descriptions{$key}\n";
+ print "$key.type DERIVE\n";
+ print "$key.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.min 0\n";
+ }
+ exit 0;
}
-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;
+foreach my $type (sort keys %stats) {
+ print "$type.value " . $stats{$type} . "\n";
}
-
-print "delivered.value $delivered\n";
-foreach my $reason (sort keys %rejects)
-{
- my $fieldname = clean_fieldname("r$reason");
- print "$fieldname.value ", $rejects{$reason}, "\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.");
+ 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";
+print OUT "$pos\n";
+print OUT join(':', map { "$_=$stats{$_}" } keys %stats) . "\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";
+sub parseLogfile {
+ my ($start) = @_;
+ open(my $LOGFILE, '-|', "journalctl --facility=mail --after-cursor='$start' --show-cursor")
+ or die "Unable to open journalctl for reading: $? $!\n";
+
+ while (my $line = <$LOGFILE>) {
+ chomp $line;
+
+ if ($line =~ /qmgr\[[^]]+\]: [0-9A-Za-z]+: from=.*, size=\d+, nrcpt=(\d+)/) {
+ $stats{messages}++;
+ $stats{recipients} += $1;
+ } elsif ($line =~ /local\[[^]]+\]: [0-9A-Za-z]+: to=/) {
+ $stats{local}++;
+ } elsif ($line =~ /smtp\[[^]]+\]: [0-9A-Za-z]+: to=/) {
+ $stats{smtp}++;
+ } elsif ($line =~ /pickup\[[^]]+\]: [0-9A-Za-z]+: uid=/) {
+ $stats{pickup}++;
+ } elsif ($line =~ /smtpd\[[^]]+\]: [0-9A-Za-z]+: client=/) {
+ $stats{smtpd}++;
+ } elsif ($line =~ /bounce\[[^]]+\]: [0-9A-Za-z]+: /) {
+ $stats{bounce}++;
+ } 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}++;
+ } elsif ($line =~ /^-- cursor: (.+)$/) {
+ $start = $1;
+ }
+ }
+ close($LOGFILE);
+ return $start;
}
# vim:syntax=perl