This appendix lists the code for the three monitoring scripts mentioned previously.
Example F-1. Testing an SMTP transaction against the localhost.
#!/usr/bin/perl -w
#
# Run a complete test SMTP transaction against the localhost, using the
# special domain smtp-test.org.
#
# This will not be run if the system is closed due to load, but will otherwise
# allow us to know if the system is broken
#
# Steve
# --
#
use strict;
use warnings;
use IO::Socket;
#
# Interactive run? If so we'll show output.
#
my $verbose = 0;
$verbose = 1
if ( ( defined( $ENV{ 'TERM' } ) )
&& ( length( $ENV{ 'TERM' } ) ) );
#
# Exit if we're too loaded.
#
if ( -e "/tmp/loaded" )
{
$verbose && print "Machine loaded: skipping test\n";
exit;
}
#
# Get the socket.
#
eval {
local $SIG{ ALRM } = sub {die "alarm\n"}; # NB: \n required
alarm 60;
my $socket = IO::Socket::INET->new(
PeerAddr => "localhost",
PeerPort => 25,
Proto => "tcp",
Type => SOCK_STREAM
) or die "Couldn't connect to localhost:25 $@\n";
#
# wait for greeting.
#
my $greet = <$socket> || "";
chomp($greet) if ( defined($greet) );
$verbose && print $greet . "\n";
#
# send: helo
#
print $socket "helo my.smtp.test\n";
$verbose && print "helo my.smtp.test\n";
#
# get response
#
my $answer = <$socket> || "";
$verbose && print $answer;
#
# send: mail from
#
print $socket "mail from: <steve\@smtp-test.org>\n";
$verbose && print "mail from: <steve\@smtp-test.org>\n";
#
# get response
#
$answer = <$socket> || "";
$verbose && print $answer;
#
# send: rcpt to
#
print $socket "rcpt to: <steve\@smtp-test.org>\n";
$verbose && print "rcpt to: <steve\@smtp-test.org>\n";
#
# get response
#
$answer = <$socket> || "";
$verbose && print $answer;
#
# send: quit
#
print $socket "quit\n";
$verbose && print "quit\n";
#
# get response
#
$answer = <$socket> || "";
$verbose && print $answer;
close($socket);
};
if ($@)
{
if ( $@ eq "alarm\n" )
{
$verbose && print "Restarting due to timeout\n";
system("/etc/init.d/qpsmtpd restart >/dev/null");
}
else
{
$verbose && print "Restarting due to error: $@\n";
system("/etc/init.d/qpsmtpd restart >/dev/null");
}
}
Example F-2. Throttling back when under a high load.
#!/usr/bin/perl -w
#
# This script is designed to disable incoming connections when
# the load is "too high".
#
# Steve
# --
#
use strict;
use warnings;
use Sys::CpuLoad;
#
# When loaded we'll add a new iptables rule to deny new connections.
#
my $busy = "/sbin/iptables -I INPUT -p tcp -i eth0 -m state --state NEW --dport 25 -j REJECT";
#
# When not loaded we'll re-run our main firewall, at /firewall, which
# will re-allow incoming connections.
#
my $ok = "/firewall";
#
# Maximum load we operate with.
#
my $max = 5;
#
# The temporary file
#
my $file = "/tmp/loaded";
#
# Get the uptime
#
my @loads = Sys::CpuLoad::load();
my $load = int( $loads[0] );
#
# The machine is currently loaded
#
if ( -e $file )
{
#
# If the load is now dropped then we're fine.
#
if ( $load <= 1 )
{
#
# Remove marker
#
unlink($file);
system( $ok );
print "Load dropped to $load - restoring service.\n"
if ( -e "/etc/noisy" );
}
}
else
{
#
# File doesn't exist
#
if ( $load >= $max )
{
#
# But the load is high.
#
open( FILE, ">", $file );
print FILE "now";
close(FILE);
#
# Stop the new connections
#
system( $busy );
print "Load risen to $load preventing new connections\n"
if ( -e "/etc/noisy" );
}
}
Example F-3. Avoiding orphaned qpsmtpd processes.
#!/bin/sh
#
# Ensure we don't have qpsmtpd processes stuck in a "timedout" state.
#
# This used to happen prior to 1.4x.x but the tests have been left in
# place just in case.
#
#
#
# If we're loaded then we'll not care about them.
#
if [ -e /tmp/loaded ]; then
exit
fi
#
# Restart if we see them.
#
if ( ps -ef | grep forkserver | grep -i "connection timed out" 2>/dev/null >/dev/null) ; then
/etc/init.d/qpsmtpd restart
fi