#!/usr/bin/perl -w
# $Id: mailscanner-mrtg,v 1.57 2005/07/07 17:59:41 kevinspicer Exp $
# mailscanner-mrtg - Extensive monitoring for MailScanner machines
# Copyright (C) 2002 Dale Lovelace <dlovelace@hotels.com>
# With various bits shamelessly stolen from others
# Restructuring and Substantial Modifications
# Copyright (C) 2003-5 Kevin Spicer <kevin@kevinspicer.co.uk>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Ok, so this is not very good perl code. I'm not a perl coder!
# Hell, I'm not ANY coder... Please Please make modifications
# and send them back to me!!!
#
# Dale Lovelace
#
# ditto
# Kevin Spicer (some time later)
#
# Please submit all bug reports, patches etc via the sourceforge project
# page at http://sourceforge.net/projects/mailscannermrtg
#
#
# Change this if your MSMRTG Modules are in a different place
use lib "/usr/local/lib/MailScanner-MRTG";
use MSMRTG::Debug;
use MSMRTG::Config;
use MSMRTG::State;
use MSMRTG::Log;
use Getopt::Long;
use POSIX qw(:signal_h setsid);
require MSMRTG::Data;
require MSMRTG::Scale;
use MSMRTG::Alarm;
umask(0022);
# Change these lines if your config files are in a non-standard location!
$configfile = "/usr/local/etc/mailscanner-mrtg/mailscanner-mrtg.conf";
$thresholdfile = "/usr/local/etc/mailscanner-mrtg/mailscanner-mrtg.thresholds";
# Set this to 1 to enable debugging output to stderr
# Or just use the command line option --debug
$debug=0;
$notimeout=0;
unless (GetOptions('debug' => \$debug, 'notimeout' => \$notimeout)) { Log_Syslog("err",1,"ERROR: Could not parse command line"); }
$version="0.11.00";
my($hostname) = `hostname`;
chomp($hostname);
my($cmd) = shift;
if ($debug) { Debug ("Using Stuff!") }
# (re)open stdin from /dev/null - some folks have a broken cron that
# doesn't open stdin, which can casue problems for external commands
# notably sar.
open (STDIN, "/dev/null");
# Get the things we need
GetTimeDate();
ReadConfFile();
ReadThresholdFile();
InitialiseVars();
# Check that the type of data we want exists
if (defined($cmd)) {
# Sanitise the input a little, just in case...
$cmd =~ s/\W//g;
# sendmail is deprecated and has been replaced by mta, for which it is a
# synonym until we remove it.
if ($cmd eq "sendmail") { $cmd = "mta" };
unless(defined($logdata{$cmd})){
print STDERR "\n";
print STDERR "ERROR: Unknown command-line option $cmd\n";
Usage();
}
} else {
Usage();
}
# Now we can start looking for data
ParseStateFile();
# Check to see whether we need to get the data on this run, or just use the
# cached data in the state file. Do this by checking if its more than 4.5
# minutes since the data was last run
if (($timedate{'utime'} - $lastrun[1]) > 270) {
# If this is a data run check whether the version number has been
# changed since the last run - if so then run the scale functions to
# check if any of the data needs rescaling
if ($MSMRTG::Config::version ne $MSMRTG::State::sfversion) {
if ($debug) { Debug ("Checking if data is scaled correctly") }
MSMRTG::Scale::checkScale();
}
if ($debug) { Debug ("Starting Data Run") }
timedDataRun();
# MSMRTG::Data::GetData();
# WriteStateFile();
} else {
if ($debug) { Debug ("Using Cached Data") }
CloseStateFile();
}
# Quickly check if the data is 'u' (undefined), if so change it to zero
# Maybe we'll find a use for u in the future!
if ($logdata{$cmd}[0] eq 'u') { $logdata{$cmd}[0] = 0 }
if ($logdata{$cmd}[1] eq 'u') { $logdata{$cmd}[1] = 0 }
if ($debug) {
# Now we send debug to stderr make sure we also print the output
# to debug
Debug($logdata{$cmd}[0]);
Debug($logdata{$cmd}[1]);
Debug($logdata{'uptime'}[0]);
Debug("MailScanner at $hostname");
Debug("Non-Debug Program Output Begins Here");
}
print $logdata{$cmd}[0] . "\n";
print $logdata{$cmd}[1] . "\n";
print $logdata{'uptime'}[0] . "\n";
print "MailScanner at $hostname\n";
sub Usage {
print "\n";
print "USAGE:\n";
print $0 . " option\n";
print "Where option is one of the following:\n";
print "\n";
print "loadavg : returns the load average over the last five minutes\n";
print "cpu : returns the cpu utilization percentage\n";
print "memory : returns the average amount of ram used during the last five mins\n";
print "iptraffic : returns the amount of ip traffic on all configured interfaces\n";
print "inqueue : returns the number of files in the incoming mail queue\n";
print "outqueue : returns the number of files in the outgoing mail queue\n";
print "quarantine : returns the number of files & messages in quarantine\n";
print "rootusage : returns the percent of disk space available in \\ \n";
print "spoolusage : returns the percent of disk space available in the spool\n";
print "tmpfsusage : returns the percent of disk space available in MS work filesystem.\n";
print "mailscanner : returns the number of copies of mailscanner running\n";
print "mta : returns the number of MTA processes running\n";
print "mail : returns the amount of mail relayed today\n";
print "mailbytes : returns the Mbytes of mail relayed today\n";
print "virus : returns the number of viruses detected today\n";
print "spam : returns the number of mails identified as spam today\n";
print "virusratio : returns the percentage of mail detected as viruses\n";
print "spamratio : returns the percentage of mail identified as spam\n";
print "batchsize : returns the average number of messages per batch\n";
print "speed : returns the average speed of processing\n";
print "\n";
exit(1);
}
sub timedDataRun {
# Adapted from some of the fork & timeout code in MailScanner
# The idea being that it should then work for anyone using MailScanner
# i.e. 100% of our userbase
$Kid= new FileHandle;
$TimedOut = 0;
if ($debug) { Debug("Beginning sub timedDataRun") }
if ($notimeout) { MSMRTG::Data::GetData() }
else {
unless (defined $Config{'dataruntimeout'}){
Log_Syslog("warning",0,"Data Run Timeout not defined in conf file, using 240s");
$Config{'dataruntimeout'}=240;
}
eval {
if ($debug) { Debug("Forking to process Data") }
Log_Syslog("err",1,"Can't fork for data run: $!")
unless defined ($pid = open($Kid, '-|'));
if ($pid) {
# In the parent
local $SIG{ALRM} =
sub { $TimedOut = 1; die "Data Run Timed Out" };
alarm $Config{'dataruntimeout'};
while (<$Kid>) {
# Read the updated logdata from stdin
chomp;
if (/^DATA\t+(\S+)\t+(.*)\s*$/) {
if (defined($logdata{$1})) {
$logdata{$1}=[split /\t/, $2];
$done{$1}++;
}
} elsif (/^POSITION\t+(\S+)\t+(\d+)\s*$/) {
if (defined($logfiles{$1})) { $logfiles{$1}=$2 }
} else { print $_ . "\n" }
}
close $Kid;
$pid = 0;
alarm 0;
# Julians workaround for a Solaris perl Bug
eval {
my $unblockset = POSIX::SigSet->new(SIGALRM);
sigprocmask(SIG_UNBLOCK, $unblockset)
or Log_Syslog("err",1,"Could not unblock alarm: $!");
}
} else {
# In the child
POSIX::setsid();
MSMRTG::Data::GetData();
exit 1;
}
};
alarm 0;
}
if ($debug) { Debug("All together again - Data collection complete") }
if ($@ and $@ !~/Data Run Timed Out/)
{
# Must write the state file anyway - to make sure we don't run
# multiple times
WriteStateFile();
Log_Syslog("err",1,"Data collection run error: $@");
}
# Any failure now must be the alarm
if ($@ or $pid>0) {
# Kill running child processes
my ($i);
kill -15, $pid;
# Wait up to 5 secs
for ($i=0; $i<5; $i++) {
sleep 1;
waitpid($pid, &POSIX::WNOHANG);
($pid=0), last unless kill(0, $pid);
kill -15, $pid;
}
# Insist!
if ($pid) {
kill -9, $pid;
waitpid $pid, 0;
}
}
# regardless of whether we timed out we need to write the state data
UpdateAlarmStatus();
WriteStateFile();
WriteAlarmIncludes();
if ($TimedOut) { Log_Syslog("err",1,"Data collection run timed out, this may well be due to the Quarantine being too full. If that is the case you need to run the clean.quarantine script from cron (change the line near the start of the script to \$disabled=0). If this is the first time you have run MailScanner-MRTG it may be sufficient to temporarily stop the cron job, run the command 'mailscanner-mrtg --notimeout cpu', wait for it to complete (may take a very long time), then reinstate the cron job.") }
return;
}
# Here be the man page
=head1 MailScanner-MRTG
=head1 SYNOPSIS
mailscanner-mrtg I<switches> I<option>
=head1 DESCRIPTION
MailScanner-MRTG provides configuration files, web pages and related perl scripts for mrtg to monitor many aspects of your MailScanner machine.
Most users will interact with MailScanner-MRTG through the web interface by pointing their browser of choice at...
http://<thismachine>/mailscanner-mrtg/
mailscanner-mrtg is the main perl 'worker' script which is called by mrtg to obtain the data for each category. It will not normally be necessary for the user to call this script manually, however this may prove useful for debugging purposes. It should be noted that this script is designed to extract all data once every five minutes - subsequent calls within a five minute window will return cached data.
The behaviour of MailScanner-MRTG can be customised by editing the main configuration file (mailscanner-mrtg.conf) which can be found in the MailScanner configuration directory. Hardcore MRTG fans might also like to play with the MRTG configuration in mailscannner.cfg, which can be found in the mrtg configuration directory.
=head1 ARGUMENTS
I<switches> should be any of the following...
=over 4
=item -d, --debug
Print extensive debugging output to stderr
=item -n, --notimeout
Disable the timeout on data collection (this may be neded on an initial run, or on a manual run when recovering from a problem).
=back
I<option> should be one of the following...
=over 4
=item loadavg
Returns the load average over the last five minutes
=item cpu
Returns the cpu utiliization percentage
=item memory
Returns the average amount of ram is use during the last five minutes
=item iptraffic
Returns the amount of ip traffic on the interfaces listed in the config file
=item inqueue
Returns the number of files in the incoming mail queue
=item outqueue
Returns the number of files in the outgoing mail queue
=item outqueue
Returns the number of files and messages in quarantine
=item rootusage
Returns the percentage of free space in the root filesystem
=item spoolusage
Returns the percent of disk space available in the spool filesystem
=item tmpfsusage
Returns the percentage of free space in the MailScanner work filesystem (useful if this is in tmpfs)
=item mailscanner
Returns the number of copies of mailscanner running
=item mta
Returns the number of MTA processes running
=item mail
Returns the amount of mail relayed today
=item mailbytes
Returns the bytes of mail relayed today
=item virus
Returns the number of viruses detected today
=item spam
Returns the number of mails identified as spam today
=item virusratio
Returns the percentage of mail detected as infected - normally this is over the previous five minute period, unless I<Ratios are Daily> is set to yes in the configuration file. Note that some infected mails trigger more than one check, in which case this figure is likely overstate the infection level (since it is actually number of tests triggered against the number of mails)
=item spamratio
Returns the percentage of mails identified as spam - normally this is over the previous five minute period, unless I<Ratios are Daily> is set to yes in the configuration file.
=item batchsize
Returns the average number of messages per batch processed
=item speed
Returns the average speed of processing (in bytes per second) - you must have I<Log Speed = yes> in MailScanner.conf for this to work.
=back
=head1 AUTHORS
MailScanner-MRTG originally by Dale Lovelace
E<lt>dlovelace@hotels.comE<gt>
Extensively revised and restructured by Kevin Spicer
E<lt>kevin@kevinspicer.co.ukE<gt>
With contributions from many others, please see the changelog.
=head1 BUGS
(aka features!)
spoolusage and tmpfsusage may return zero values if the configured paths do not correspond to a mount point. This is deliberate, since indicating the space available on the underlying filesystem may be misleading.
cpu, memory and iptraffic will return zero values if UCD-SNMP or NET-SNMP are not installed, or are disabled in the configuration file.
Currently blank graphs will still be shown on the web interface.
=head1 SEE ALSO
L<MailScanner> L<mrtg>
=head1 COPYRIGHT
Copyright (C) 2002-3 Dale Lovelace E<lt>dlovelace@hotels.comE<gt>
Copyright (C) 2003-5 Kevin Spicer E<lt>kevin@kevinspicer.co.ukE<gt>
All other contributions copyright of their respective authors (see the CHANGELOG and comments in the code for details).
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
=cut
1;
syntax highlighted by Code2HTML, v. 0.9.1