diff -r 000000000000 -r f5f2a126ebf8 check_exim.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/check_exim.pl Wed Dec 19 21:24:08 2007 +0000 @@ -0,0 +1,246 @@ +#! /usr/bin/perl +# $Id$ +# $URL$ + +use strict; +use warnings; +use File::Basename; +use Getopt::Long; +use Fatal qw(:void open flock seek truncate); +use Fcntl qw(:flock); +use if $ENV{DEBUG} => "Smart::Comments"; +use Storable; + +my $ME = basename $0; +my $STATE_DIR = "/var/lib/nagios3/$ME"; +my $STATE_FILE = "$STATE_DIR/state"; +my $CACHE_FILE = "$STATE_DIR/exim"; +our $VERSION = "1.0"; + +delete @ENV{ grep /PATH/, keys %ENV }; +$ENV{PATH} = "/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin"; + +my %TRANSPORT = (lmtp => "local", + pipe => "local", + appendfile => "local", + autoreply => "local", + smtp => "remote", +); + +my %opt = (init => undef, + warn => undef, + crit => undef, + update => 1, +); + +my %exim; +my %state = (time => 0, + inode => { mainlog => 0 }, + offset => { mainlog => 0 }, + counter => { transport => {}, + protocol => {}, + in => { local => 0, remote => 0 }, + out => { local => 0, remote => 0 }, + }, +); + +sub init(); + +MAIN: { + + GetOptions("init=s" => \$opt{init}, + "warn=s" => \$opt{warn}, # rate mails/h + "crit=s" => \$opt{crit}, # rate mails/h + "update!" => \$opt{update}, + ) or die "Bad Usage\n"; + + $opt{init} and do { init(); exit }; + + # read the exim (cache) file and the status + die "$0: $CACHE_FILE not found ($!). Try $0 --help\n" + if not -r $CACHE_FILE; + die "$0: $STATE_FILE not found ($!). Try $0 --help\n" + if not -r $STATE_FILE; + %exim = %{ $_ = retrieve($CACHE_FILE) }; + %state = %{ $_ = retrieve($STATE_FILE) }; + + ### %exim + ### %state + + { + my @files; + + # it's important to have the real mainlog last! + if ($state{inode}{mainlog} == 0) { + ### never seen an inode + push @files, [ $exim{mainlog} => 0 ]; + } + elsif ($state{inode}{mainlog} != (stat $exim{mainlog})[1]) { + ### inum changed + if ((my $moved_mainlog) + = grep { (stat)[1] == $state{inode}{mainlog} } + glob(dirname($exim{mainlog}) . "/*")) + { + ### found old one: $moved_mainlog + push @files, [ $moved_mainlog => $state{offset}{mainlog} ]; + } + push @files, [ $exim{mainlog} => 0 ]; + } + else { + push @files, [ $exim{mainlog} => $state{offset}{mainlog} ]; + } + + my $now = time(); + my $dt = $now - $state{time}; + my %out = (local => 0, remote => 0); + my %in = (local => 0, remote => 0); + my (%transport, %protocol); + FILE: foreach (@files) { + my ($file, $offset) = @$_; + open(my $fh, $file) or do { + warn "open $file: $!\n"; + next FILE; + }; + seek($fh, $offset, 0) or do { + warn "seek $file to offset $offset: $!\n"; + next FILE; + }; + while (<$fh>) { + if (/^\S+ \S+ (?:\S+ )?\S+ ?> .* T=(\S+)/) { + $transport{$1}++; + $out{ $exim{transport}{$1} }++; + next; + } + if (/^\S+ \S+ (?:\S+ )?\S+ <= .* P=(\S+)/) { + $protocol{$1}++; + $in{ $1 =~ /smtp/ ? "remote" : "local" }++; + next; + } + } + + # will then hold the values from the *last*, that's why + # above I said that the order matters + $state{offset}{mainlog} = tell($fh); + $state{inode}{mainlog} = (stat $fh)[1]; + } + + # foreach (keys %transport) { + # print "$_: $transport{$_}\n"; + # } + # print "\n"; + + # save status - status contains absolute counters + $state{time} = $now; + $state{counter}{in}{remote} += $in{remote}; + $state{counter}{in}{local} += $in{local}; + $state{counter}{out}{remote} += $out{remote}; + $state{counter}{out}{local} += $out{local}; + + foreach (keys %transport) { + $state{counter}{transport}{$_} = 0 + if not defined $state{counter}{transport}{$_}; + $state{counter}{transport}{$_} += $transport{$_}; + } + + foreach (keys %protocol) { + $state{counter}{protocol}{$_} = 0 + if not defined $state{counter}{protocol}{$_}; + $state{counter}{protocol}{$_} += $protocol{$_}; + } + + store \%state, $STATE_FILE if $opt{update}; + + # start processing + my $in_tot = $in{local} + $in{remote}; + my $out_tot = $out{local} + $out{remote}; + + # mails / minute + my $rate_in_remote = $in{remote} / $dt * 60; + my $rate_out_remote = $out{remote} / $dt * 60; + my $rate_in_local = $in{local} / $dt * 60; + my $rate_out_local = $out{local} / $dt * 60; + my $rate_in = $in_tot / $dt * 60; + my $rate_out = $out_tot / $dt * 60; + + my $perfdata = "in_local=$state{counter}{in}{local}" + . " in_remote=$state{counter}{in}{remote}" + . " out_local=$state{counter}{out}{local}" + . " out_remote=$state{counter}{out}{remote}"; + + foreach (keys %{ $state{counter}{transport} }) { + $perfdata .= " $_=$state{counter}{transport}{$_}"; + } + + foreach (keys %{ $state{counter}{protocol} }) { + $perfdata .= " $_=$state{counter}{protocol}{$_}"; + } + + printf "OK i:%.1f(%.1f/%.1f) o:%.1f(%.1f/%.1f)|$perfdata\n", $rate_in, + $rate_in_local, $rate_in_remote, $rate_out, $rate_out_local, + $rate_out_remote; + + } + + exit 0; + +} + +sub init() { + + # noch sind wir root (hoffentlich) + -d $STATE_DIR or do { + require File::Path; + import File::Path; + umask(022); + mkpath($STATE_DIR, 0, 0777); + }; + + my $uid = (getpwnam $opt{init})[2]; + chown($uid, -1, $STATE_DIR) + or die "Can't chown $STATE_DIR to $opt{init} ($uid): $!\n"; + + umask 077; + $> = $uid; # EUID + + # exim (logfile names) acquired from exim + my (%exim, @exim); + @exim = `exim -bP all` or `exim4 -bP all`; + + my ($log) = (split " ", (grep /^\s*log_file_path\s*=/, @exim)[0])[2]; + + %exim = (mainlog => sprintf($log, "main"), + rejectlog => sprintf($log, "reject"), + paniclog => sprintf($log, "paniclog"), + ); + + { + local $/ = ""; + @exim = `exim -bP transports` or `exim4 -bP transports`; + foreach (@exim) { + my ($transport, $driver) = /^(\S+) transport:.*^driver = (\S+)/ms; + + # map the transport names to their driver and this to "local/remote" + $exim{transport}{$transport} = $TRANSPORT{$driver}; + } + + } + + store \%exim, $CACHE_FILE; + store \%state, $STATE_FILE; + +} + +__END__ + +my $FILE = "/var/log/exim4/mainlog"; +my $STATUS = "/var/run/nagios3/$ME/%s"; + +sub unkn(@) { print "UNKNOWN @_\n"; exit 3 } +sub crit(@) { print "CRIT @_\n"; exit 2 } +sub warn(@) { print "WARN @_\n"; exit 1 } +sub ok(@) { print "OK @_\n"; exit 0 } + +open(my $log, $FILE) or critical("file $FILE not found"); + +__END__ +# vim:sts=4 sw=4 aw ai sm: