#!/usr/bin/perl -w

# $Id: check_spamd,v 1.3 2006/09/26 17:09:03 holger Exp $
#
# check SPAMD connections
#
# Copyright (c) 2005, 2006 Holger Weiss <holger@CIS.FU-Berlin.DE>
#
# 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.
#
# ---------------------------------------------------------------
#
# New in 1.3:
# - The "-v" option now works again.
#
# New in 1.2:
# - A proper error message is reported if spamd(1) closes the connection
#   unexpectedly.  This happens, for example, if spamd(1)s "-A" option is
#   used, but the client hasn't been added to the list of authorized hosts.
# - Minor code cleanup.

use strict;
use Socket;
use Getopt::Long;
use lib "/usr/local/nagios/libexec";
use utils qw(%ERRORS $TIMEOUT &print_revision &support);
use vars qw($PROGNAME $PORT $CRIT);
use vars qw($opt_H $opt_V $opt_c $opt_h $opt_p $opt_t $opt_v $opt_w);

sub talk_spamd ($$);
sub die_crit ($);
sub die_warn ($);
sub die_unknown ($);
sub print_usage ();
sub print_help ();
my ($spamd_port, $warning, $critical, $timeout, $response, $start, $duration);

$PROGNAME = "check_spamd";
$PORT = 783;
$CRIT = $TIMEOUT;
$SIG{'ALRM'} = sub { die_unknown("Timeout"); };
$ENV{'PATH'}='';
$ENV{'ENV'}='';

Getopt::Long::Configure("bundling");
if (!GetOptions("V"   => \$opt_V, "version"    => \$opt_V,
                "h"   => \$opt_h, "help"       => \$opt_h,
                "v+"  => \$opt_v, "verbose+"   => \$opt_v,
                "H=s" => \$opt_H, "hostname=s" => \$opt_H,
                "c=i" => \$opt_c, "critical=i" => \$opt_c,
                "p=i" => \$opt_p, "port=i"     => \$opt_p,
                "t=i" => \$opt_t, "timeout=i"  => \$opt_t,
                "w=i" => \$opt_w, "warning=i"  => \$opt_w)) {
	print "SPAMD UNKNOWN - Error processing command line options\n";
	print_usage();
	exit $ERRORS{'UNKNOWN'};
}
if ($opt_V) {
	print_revision($PROGNAME,'$Revision: 1.3 $ ');
	exit $ERRORS{'OK'};
}
if ($opt_h) {
	print_help();
	exit $ERRORS{'OK'};
}
unless ($opt_H) {
	print "SPAMD UNKNOWN - No target host specified\n";
	print_usage();
	exit $ERRORS{'UNKNOWN'};
}
alarm($opt_t ? $opt_t : $TIMEOUT);
$critical = $opt_c ? $opt_c : $CRIT;
$warning = $opt_w ? $opt_w : $critical;
if ($warning > $critical) {
	print "SPAMD UNKNOWN - Warning larger than critical threshold\n";
	print_usage();
	exit $ERRORS{'UNKNOWN'};
}
$spamd_port = $opt_p ? $opt_p : getservbyname("spamd", "tcp");
$spamd_port = $PORT unless defined $spamd_port;
$start = time;
$response = talk_spamd($opt_H, $spamd_port);
$duration = time - $start;
die_crit("$response - $duration sec. response time") if $duration >= $critical;
die_warn("$response - $duration sec. response time") if $duration >= $warning;
$response .= " - $duration sec. response time" if $opt_v;
print "SPAMD OK - Server response: $response\n";
exit $ERRORS{'OK'};

sub talk_spamd ($$) {
	my ($host, $port) = @_;
	my ($buffer, $iaddr, $message, $n, $paddr, $proto, $status, $str);

	$iaddr = inet_aton($host)
	    || die_unknown("Invalid hostname/address: $host");
	$paddr = sockaddr_in($port, $iaddr);
	socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
	    || die_unknown("Socket error: $!");
	connect(SOCK, $paddr) || die_crit("Error connecting to $host: $!");
	defined(send(SOCK, "PING SPAMC/1.2\r\n", 0))
	    || die_unknown("Error sending request to $host: $!");
	$buffer = <SOCK>;
	die_unknown("Connection reset by peer") unless $buffer;
	close SOCK || die_unknown("Cannot close connection: $!");
	$n = ($proto, $status, $message)
	    = ($buffer =~ /^(\S+) +(\S+) +([^\r\n]+)/);
	if ($n != 3) {
		my $error = "Protocol error";

		$error .= " - Server said: $buffer" if $buffer;
		die_crit($error);
	}
	$str = "$proto $status $message";
	die_crit($str) unless $status == 0;
	$str;
}

sub die_unknown ($) {
	printf "SPAMD UNKNOWN - %s\n", shift;
	exit $ERRORS{'UNKNOWN'};
}

sub die_warn ($) {
	printf "SPAMD WARNING - %s\n", shift;
	exit $ERRORS{'WARNING'};
}

sub die_crit ($) {
	printf "SPAMD CRITICAL - %s\n", shift;
	exit $ERRORS{'CRITICAL'};
}

sub print_usage () {
	print "Usage: $PROGNAME -H host [-p port] [-w warn] [-c crit] " .
	      "[-t timeout] [-v]\n";
}

sub print_help () {
	print_revision($PROGNAME, '$Revision: 1.3 $');
	print "Copyright (c) 2005, 2006 Holger Weiss\n\n";
	print "Check SPAMD connections with the specified host.\n\n";
	print_usage();
	print <<EOF;

 -H, --hostname=ADDRESS
    Host name or IP address
 -p, --port=INTEGER
    Port number (default: $PORT)
 -w, --warning=INTEGER
    Response time to result in warning status (seconds)
 -c, --critical=INTEGER
    Response time to result in critical status (seconds)
 -t, --timeout=INTEGER
    Seconds before connection times out (default: $TIMEOUT)
 -v, --verbose
    Show details for command-line debugging (Nagios may truncate output)

EOF
	support();
}
