#!/usr/bin/perl

use strict;
use Sys::Hostname;

# The only documentation for this.
sub usage {
	my $id = '$Id: smart_notify_mail.pl,v 1.2 2006/03/28 14:43:46 bc Exp $';
	print "NSCA Mail Notifer $id\n";
	print "Nagios/NSCA Copyright (c) 2000-2006 Ethan Galstad (www.nagios.org)\n";
	print "Nagios Redundant Distributed Monitoring Idea by B Campbell, 2006\n";
	print "License: GPL 2\n";
	print "\n";
	print "Usage: $0 [-m mail] -s Subject [-S mysite] [-H myhost] [-t timeout] [-c config_file] [-P pluginoutput] <recipients>\n";
	print "\n";
	print "Options:\n";
	print " [myhost]       = The host name or IP address of this host.\n";
	print " [mysite]       = The local site name.\n";
	print " [timeout]      = Timeout for the mail program to complete.\n";
	print " [config_file]  = Name of (smart_send_nsca) config file to use.\n";
	print " [pluginoutput] = Plugin output, including path tracing, from Nagios.\n";
	print " [mail]         = Path to the mail program. Some defaults are tried if absent.\n";
	print " [Subject-line] = Passed straight onto the mail program\n";
	print " [recipients]   = Passed straight onto the mail program\n";
	print "\n";
	print "Note:\n";
	print "This utility is used to determine whether a notification should be actually\n";
	print "sent.  It does this by looking at the path tracing information within the\n";
	print "pluginoutput variable.  If no other sites or known hosts are mentioned\n"; 
	print "in the path tracing information, then the assumption is that this host\n";
	print "is the first monitoring host, and thus the appropriate host to generate\n";
	print "the notification.\n";
	print "If a notification is to be sent, then the mail program specified (or default)\n";
	print "is executed with the subject line and mail recipients, and passed whatever is\n";
	print "sent to the STDIN of this script.  If a notification is not to be sent, then\n";
	print "this program will not execute the mail program, and will swallow anything\n";
	print "supplied via STDIN.\n";
	exit(1);
}

my $configfile = undef;
my $timeout = 10;
my $use_config = 0;
my $found = 0;
my $subject = undef;
my $mail = undef;
my $pluginoutput = undef;
my $mysite = undef;
my $myhost = undef;

my $curloop = 0;
my $rcpt_start = 0;
while( $curloop < scalar @ARGV && ! $rcpt_start ){
	if( $ARGV[$curloop] =~ /^\-(\S)$/ ){
		my $what = $1;
		my $nextloop = $curloop+1;
		if( ! defined( $ARGV[$nextloop] ) ){
			$ARGV[$nextloop] = "";
		}

		if( $what eq "t" && $ARGV[$nextloop] =~ /^\s*\d+\s*$/ ){
			$timeout = $ARGV[$nextloop];
		}elsif( $what eq "H" ){
			$myhost = $ARGV[$nextloop];
		}elsif( $what eq "S" ){
			$mysite = $ARGV[$nextloop];
		}elsif( $what eq "m" ){
			$mail = $ARGV[$nextloop];
		}elsif( $what eq "s" ){
			$subject = $ARGV[$nextloop];
			$found++;
		}elsif( $what eq "c" ){
			$configfile = $ARGV[$nextloop];
			$use_config++;
		}elsif( $what eq "P" ){
			$pluginoutput = $ARGV[$nextloop];
		}elsif( $what eq "h" ){
			&usage();
			exit(2);
		}
		$curloop++;
	}else{
		$rcpt_start = $curloop;
	}
	$curloop++;
}

if( ! $found ){
	&usage();
}

# The recipients is everything from rcpt_start to the end.
my $maxargv = scalar @ARGV - 1;
my $recipients = undef;
if( $maxargv >= $rcpt_start ){
	$recipients = join( ',', @ARGV[$rcpt_start..$maxargv] );
	$recipients =~ s/,,/,/g;
}


# If we didn't have a configuration file supplied, see if there is known
# one that we can use.  We only need one if we don't know our local site.
if( ! $use_config && ! defined( $mysite ) ){
	foreach my $tconfig( "/etc/nsca/smart_send_nsca.cfg", "/etc/nsca/send_nsca.cfg" ){
		next if( $use_config );
		next unless( -f $tconfig && -r $tconfig );
		$use_config++;
		$configfile = $tconfig;
	}
}

# If we don't know our local hostname, we need to grab this
if( ! defined( $myhost ) ){
	$myhost = hostname();
}elsif( $myhost =~ /^\s*$/ ){
	$myhost = hostname();
}

# Parse the path information.
my %seenhosts = ();
my %seensites = ();
my @seenpath = ();
my $pathentries = 0;
if( defined( $pluginoutput ) ){
	if( $pluginoutput =~ /\(([^\)]+)\)(\s*|\s*\|.*)?$/ ){
		my $pathinfo = $1;
		my @tsplit = split( /,/, $pathinfo );
		foreach my $item( @tsplit ){
			next unless( $item =~ /^\s*(\S+)\s*$/ );
			my $itemtext = $1;
			if( $itemtext =~ /^H(\S+)$/ ){
				$pathentries++;
				$seenhosts{"$1"} = 1;
				push @seenpath, $itemtext;
			}elsif( $itemtext =~ /^S(\S+)$/ ){
				$pathentries++;
				$seensites{"$1"} = 1;
				push @seenpath, $itemtext;
			}
		}
	}
}

# Quick abort.  If we know our site name, we can exit if it appears in
# the path.
if( defined( $mysite ) ){
	if( defined( $seensites{"$mysite"} ) ){
		&swallow_stdin_and_exit();

		# Backup exit.
		exit(2);
	}
}

# Quick abort.  If we know our hostname, we can exist if it appears in the
# path (which should not happen, as our hostname is put on when it is
# sent outwards).
if( defined( $myhost ) ){
	if( defined( $seenhosts{"$myhost"} ) ){
		&swallow_stdin_and_exit();
		exit(2);
	}
}

# Preparing to scan the configuration file.  We only need to do this
# if there is path information present, and we have a configuration
# file.
if( defined( $configfile ) && $pathentries > 0 ){
	if( -e $configfile && -r $configfile && ! -d $configfile && $configfile !~ /^\s*$/ ){
		# File exists.  Can we open it?
		my $stillgoing = 1;
		my $shouldswallow = 0;
		if( open( INFILE, $configfile ) ){
			while( my $line = <INFILE> ){
				next unless( $stillgoing );
				next unless( $line =~ /^(host|site)_(\S+)=(\S+.*)$/ );
				my $type = $1;
				my $name = $2;
				my $value = $3;
				if( $type eq "site" ){
					# Quick abort: If this site has been 
					# seen in the path, then we need to 
					# exit.
					if( defined( $seensites{"$name"} ) ){
						# It has been seen.  Therefore, there
						# is no point in continuing.
						$stillgoing = 0;
						$shouldswallow = 1;
						next;
					}

					# See if this site has any of the 
					# hostnames mentioned in the path.
					my @tsplit = split( /,/, $value );
					foreach my $host ( @tsplit ){
						next unless( $host =~ /^\s*(\S+)\s*$/ );
						my $thost = $1;
						if( $thost eq $myhost && ! defined( $mysite ) ){
							$mysite = $name;
						}
						next unless( defined( $seenhosts{"$thost"} ) );
						$stillgoing = 0;
						$shouldswallow = 1;
					}
				}elsif( $type eq "host" ){
					# If this host has been seen, then
					# we should exit.
					if( defined( $seenhosts{"$name"} ) ){
						$stillgoing = 0;
						$shouldswallow = 1;
						next;
					}
				}
			}
			close( INFILE );

			# Should we swallow STDIN because path information
			# said we should?
			if( $shouldswallow ){
				# Yes, we should.
				&swallow_stdin_and_exit();
			}
		}
	}
}

# It is time to execute the mail program.  Find it if possible.
if( ! defined( $mail ) ){
	foreach my $possible( "/bin/mail", "/usr/bin/mail", "/usr/lib/sendmail", "/usr/sbin/sendmail", "/usr/bin/sendmail" ){
		next if( defined( $mail ) );
		next unless( -x $possible && ! -d $possible );
		$mail = $possible;
	}
}

if( defined( $mail ) && defined( $recipients ) ){
	if( -x $mail && ! -d $mail ){
		# Open it with whatever we had for subject and recipients.
		if( ! defined( $subject ) ){
			$subject = "Default subject from $0";
		}

		if( open( MAIL, "| $mail -s \"$subject\" $recipients" ) ){
			while( my $line = <STDIN> ){
				chomp $line;
				print MAIL "$line\n";
			}
			close( MAIL );
		}else{
			print STDERR "Unable to execute mail program ($mail)\n";
			exit(2);
		}
	}else{
		print STDERR "Supplied or found mail program is not executable ($mail)\n";
		exit(2);
	}
}elsif( ! defined( $recipients ) ){
	print STDERR "No recipients specified\n";
	exit(2);
}elsif( ! defined( $mail ) ){
	print STDERR "No mail program found\n";
	exit(2);
}else{
	# This bit probably won't fire.
	&swallow_stdin_and_exit();
}

# Backup exit.
exit( 3 );

sub swallow_stdin_and_exit {
	print STDERR "A known host has already seen this.  No mail will be sent.\n";
	while( my $line = <STDIN> ){
		next;
	}
	exit( 0 );
}
