# This is a function wrapper for SSH that provides reasonable timeouts (in
# case the server is busy, hung, or dead).  To use, put this file (called
# "Ssh.pm") in your perl library path or cricket/lib directory, then edit
# cricket/lib/func.pm and add "use Ssh.pm;" under "use Common::Log;" and edit
# cricket/collector and uncomment the "use func;" line.
#
# There are a couple of configuration variables in this module:
#  $TIMEOUT - the number of seconds to wait for the SSH to complete
#  @SSH - list with full path to SSH and args to pass

package Ssh;

require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(ssh);
use strict;

my $TIMEOUT = 5;
my @SSH = ("/usr/bin/ssh", "-x", "-a");

# Setup cache
use vars qw(%servers);
%servers = ();

# Called by collector
sub ssh
{
	my ($host, $cmd, $line) = @_;

	if (! defined ($servers{$host}{$cmd})) {
		# No data in the cache for this host so fetch it
		do_ssh ($host, $cmd);
	}
	if (! defined ($servers{$host}{$cmd}[$line])) {
		# No data for this line, return "U"nkown
		$servers{$host}{$cmd}[$line] = "U";
	}
	return $servers{$host}{$cmd}[$line];
}

# Function to actually fetch data
sub do_ssh
{
	my ($host, $cmd) = @_;

	# Fork off the child process and call ssh
	local (*SUB);
	die "Can't fork: $!\n" unless defined (my $pid = open (SUB, "-|"));
	if (! $pid) {
		open (STDIN, "</dev/null") or die "dup(/dev/nul): $!\n";
		exec @SSH, $host, $cmd;
		die "exec: $!\n";
	}

	# Fetch lines until either end-of-file or timeout
	local $SIG{ALRM} = sub { 1 };
	eval {
		local $SIG{ALRM} = sub { die "timeout\n" };
		alarm ($TIMEOUT);

		while (<SUB>) {
			# Just take the first column of data
			chomp;
			my @l = split;
			push @{$servers{$host}{$cmd}}, $l[0];
		}
	};
	alarm (0);

	# Make sure the child process is gone
	kill (9, $pid);
	waitpid ($pid, 0);
	close (SUB);
}

1;
