
use strict;
use IO::Socket;
use POSIX qw(strftime WNOHANG);
use Time::HiRes qw(gettimeofday tv_interval) ;

use constant PORT => 12000;

my $quit = 0;

# signal handler for child die events
$SIG{CHLD} = sub { while ( waitpid(-1,WNOHANG)>0 ) { } };

# signal handler for interrupt key and TERM signal
$SIG{INT} = sub { $quit++ };

my $listen_socket = IO::Socket::INET->new
					(
					LocalPort => PORT,
                                        Listen    => 20,
                                        Proto     => 'tcp',
                                        Reuse     => 1,
                                        Timeout   => 60*60,
                                        );
					 
die "Can't create a listening socket: $@" unless $listen_socket;
warn "PBS shell command server ready.  Waiting for connections...\n\n";   

my $connection_index = 0 ;
while (!$quit) 
	{
	next unless my $connection = $listen_socket->accept;
	$connection_index++ ;
	
	defined (my $child = fork()) or die "Can't fork: $!";
	
	if ($child == 0) 
		{
		# in child process
		
		my $now_string = strftime "%a %b %e %H:%M:%S %Y", gmtime ;
		print "[$$] Connection $connection_index $now_string.\n" ;
		
		$SIG{CHLD} = undef ; # arrrrrrrgggggg! without this line $? was always -1

		$listen_socket->close;
		
		interact($connection);
		
		exit 0;
		}
	
	$connection->close;
	}

#move all this a to a pbs module Builder::ShellCommand::Server or something like it.
# look into refactoring this one and ForkedNodeBuilder.pm

sub interact 
{
my $sock = shift;

my $build_output ;
my $node_name ;
my $build_time ; #last node build time

while(defined (my $command_and_args = <$sock>))
	{
	$command_and_args =~ s/\n|\r//g ;
	
	my ($command, @args) = split /__PBS_FORKED_BUILDER__/, $command_and_args ;
	
	for ($command)
		{
		/^STOP_PROCESS$/ and do
			{
			print("[$$] Stop.\n") ;
			close($sock) ;
			exit ;
			} ;
			
		/^GET_PROCESS_ID$/ and do
			{
			print $sock "$$\n__PBS_FORKED_BUILDER__\n" ;
			last ;
			} ;
			
		/^NODE_NAME$/ and do
			{
			$node_name = $args[0] ;
			last ;
			} ;
			
		/^RUN_COMMANDS$/ and do
			{
			($build_output, $build_time) = Runcommands($sock, $node_name, @args) ;
			last ;
			} ;
			
		/^GET_LOG$/ and do
			{
			print $sock "'$node_name': No log implemented in light weight build!\n" . "__PBS_FORKED_BUILDER___\n" ;
			last ;
			} ;
			
		/^GET_OUTPUT$/ and do
			{
			SendFile($sock, $build_output, @args) ;
			last ;
			} ;
			
		print $sock "0__PBS_FORKED_BUILDER__ [$$] Unrecognized command '$command_and_args'__PBS_FORKED_BUILDER__\n" ;
		}
	}
}

sub Runcommands
{
my ($sock, $node_name, @commands) = @_ ;
my $t0 = [gettimeofday] ;

#~ my $redirection_file =  $shell->GetInfo() . '_node_' . $node_name ;
my $redirection_file =  $node_name ;

$redirection_file =~ s/\s/_/g ;
$redirection_file =~ s/[[\]]/_/g ;
$redirection_file =~ s/\//_/g ;

#~ my $pbs_build_buffers_directory = $node->{__PBS_CONFIG}{BUILD_DIRECTORY} . "/PBS_BUILD_BUFFERS/";
my $pbs_build_buffers_directory = "./TEST_PBS_BUILD_BUFFERS/";

unless(-e $pbs_build_buffers_directory)
	{
	use File::Path ;
	mkpath($pbs_build_buffers_directory) ;
	}
	
$redirection_file = $pbs_build_buffers_directory . $redirection_file ;

open(OLDOUT, ">&STDOUT") ;
open(OLDERR, ">&STDERR") ;

#all output gore to files that might be kept if KEEP_PBS_BUILD_BUFFERS is set
open STDOUT, '>', $redirection_file or die "Can't redirect STDOUT to '$redirection_file': $!";
STDOUT->autoflush(1) ;

open STDERR, '>&' . fileno(STDOUT) or die "Can't redirect STDERR to '$redirection_file': $!";
STDERR->autoflush(1) ;

my ($build_result, $build_message) = (1, "build OK") ;

my $columns = length("Node '$node_name':") ;
my $separator = '#' . ('-' x ($columns - 1)) . "\n"  ;

print STDOUT $separator . "Node '$node_name':\n" . $separator ;

for my $command (@commands)
	{
	
	print STDOUT "Remote shell command: '$command'\n" ; # if not silent
	
	my $output = `$command` ;

	if($?)
		{
		print OLDOUT "[$$] Error: '$command' => $?\n" ;
		
		print STDOUT "Error: $! $?\n" ;
		print STDOUT $output ;
		
		($build_result, $build_message) = (0, "Error running command '$command' [$! $?]") ;
		last ;
		}
	else
		{
		print OLDOUT "[$$] Done: '$command'\n" ;
		print STDOUT $output ; # if not silent
		}
	
	#~ if(system($command))
		#~ {
		#~ print OLDOUT "[$$] Error: '$command'\n" ;
		#~ print STDOUT "Error: $! $?\n" ;
		
		#~ ($build_result, $build_message) = (0, "Error running command '$command' [$!]") ;
		#~ last ;
		#~ }
	#~ else
		#~ {
		#~ print OLDOUT "[$$] Done: '$command'\n" ;
		#~ }
	}

#stop redirecting to a file
close(STDOUT) ;
close(STDERR) ;
open(STDOUT, ">&OLDOUT") ;
open(STDERR, ">&OLDERR") ;

close(OLDOUT) ;
close(OLDERR) ;

#let other side know if something went wrong
print $sock "${build_result}__PBS_FORKED_BUILDER__${build_message}\n" ;

return($redirection_file, tv_interval ($t0, [gettimeofday])) ;
}

sub SendFile
{
my $channel     = shift ;
my $file        = shift ;
my $remove_file = shift or 'keep' ;

print "[$$] SendFile $file [$remove_file] ***\n" ;

open FILE, '<', $file or die "[$$] Can't open '$file': $!" ;
while(<FILE>)
	{
	print $channel $_ ;
	}
	
close(FILE) ;	

print $channel "__PBS_FORKED_BUILDER___\n" ;

unlink($file) if $remove_file;
}
