require "flush.pl";

$loopingerrors=0;

if(!(defined($ARGV[0]))) {
    print STDERR "Usage: job_end.pl job_number\n";
    exit 1;
}

$JOB_NUMBER   = $ARGV[0];

#
# Go to the job's directory, get job type, and title of solver
#

chdir("$ENV{SERVER_JOBS}/job.$JOB_NUMBER") or die("Could not cd to $ENV{SERVER_JOBS}/job.$JOB_NUMBER\n");

open(CAT, "job.type") or &handler("No job type file found for job $JOB_NUMBER.\n");
chomp($TOKEN = <CAT>);
close CAT;

if ($TOKEN =~ /^ADMIN:.*$/) {
  $SOLVERS = $ENV{SERVER_ADMIN_SOLVERS};
} else {
  $SOLVERS = $ENV{SERVER_SOLVERS};
} 

if (-f "$SOLVERS/$TOKEN/TITLE") {
    open(CAT, "$SOLVERS/$TOKEN/TITLE") or &handler("Could not open file TITLE in $SOLVERS/$TOKEN.\n");
    chomp($TITLE = <CAT>); 
    close CAT;
} else {
    $TITLE = $TOKEN; 
}


RESULTS: 

#
# Add END_TIME to job results.
#

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);  
$year += 1900;
$mon  += 1;
if($mon < 10) {
    $mon = "0".$mon;
}
if($mday < 10) {
    $mday = "0".$mday;
}
if($min < 10) {
    $min = "0".$min;
}
if($sec < 10) {
    $sec = "0".$sec;
}
$endDate = "$mon/$mday/$year";
$endTime = "$hour:$min:$sec"; 
$END_TIME = "$endDate $endTime";
$length = length($END_TIME);
for ($i=0;$i<(20-$length);$i++) {
  $END_TIME .= " ";
}
$size = (-s "$ENV{SERVER_LIB}/txt/announcement.txt") + (-s "$ENV{SERVER_LIB}/txt/disclaimer.txt") + 1024;
open(HANDLE, "+<job.results") or &handler("job_end.pl: Could not open job.results for job $JOB_NUMBER");
seek(HANDLE,0,0);
read(HANDLE,$buffer,$size);
$buffer =~ s/<END_TIME_GOES_HERE>/$END_TIME/;
seek(HANDLE,0,0);
print HANDLE $buffer;
close(HANDLE);

&email;
&forward;
&log;


#
# Signal cleaner to clean
#

open(CAT, ">>job.out") or die "job_end.pl: Could not write to job.out via system call for job $JOB_NUMBER";
print CAT "<END_STANDARD_OUT>";
close CAT;

open(TOUCH, ">DONE") or &handler("job_end.pl: Could not touch DONE file for job $JOB_NUMBER");
close TOUCH;


#
# The End
#


sub handler {
  if($loopingerrors==1) {
      print STDERR "Caught in a loop of errors.\n";
      exit 1;
  }                           
  $loopingerrors=1;
  if(($STATUS & 0xff)==0) {
      $STATUS >>=8;
  }
  open(HANDLE, ">>job.results") or die "job_end.pl: Could not open job.results file for job $JOB_NUMBER";
  if(defined($_[0])) {
      print HANDLE "ERROR $STATUS:\n$_[0]\n";
  } else {
      print HANDLE "ERROR $STATUS:  Your problem either exceeded the time limit of $MAX seconds, \nor the solving station killed your process.\n"; 
  }
  print HANDLE "        The software administrator has been notified.\n\n";
  
  close(HANDLE);
  open(CAT, ">>job.out") or die "job_end.pl: Could not write to job.out via system call for job $JOB_NUMBER";
  print CAT "<END_STANDARD_OUT>";
  close CAT;
  if (-s "$SOLVERS/$TOKEN/CONTACT") {
    chop($CONTACT = `cat $SOLVERS/$TOKEN/CONTACT`);
    open(HANDLE, "|Mail -s '$TOKEN' $CONTACT");
    print HANDLE "Notice for $TOKEN job #$JOB_NUMBER:\n\n";
    if(defined($_[0])) {
	print HANDLE "\n$_[0]\n";
    } else {			
	print HANDLE "ERROR: Your solver timed out after $MAX seconds.\n";
    }
    print HANDLE "       Here is the standard out/error from your code:\n\n";
    print HANDLE "------------------------------------------------------------\n\n";
    $error = `cat job.out`;
    print HANDLE $error;
    close (HANDLE);
  }
  goto RESULTS;
}

sub commsfail {
    &handler("Communications failed with remote solver.  Data lost.");
}
 
sub fsleep {
  select(undef, undef, undef, $_[0]);
}


sub email {
#
# Send results to email addresses
#

    open(CAT, "job.interface") or &handler("Could not open interface file for job $JOB_NUMBER.\n");
    chomp($interface = <CAT>);
    close CAT;

    if ($interface eq "mail") 
    {
	open(CAT, "job.address") or &handler("Could not open address file for job $JOB_NUMBER.\n");
	chomp($address = <CAT>);
	close CAT;

	open(CAT, "job.results") or &handler("Could not open job.results file for reading for job $JOB_NUMBER.\n");

	open(MAIL, "|sendmail -oi -t") or &handler("job_end.pl: Denied access to sendmail program\n");
	print MAIL <<END_SOLVER_RESULTS;
To: $address 
From: $ENV{SERVER_NAME} Server <$ENV{SERVER_EMAIL}>
Subject: Job #$JOB_NUMBER
END_SOLVER_RESULTS
    while(<CAT>) {
        print MAIL;
    }                           
     close CAT;
     close(MAIL);
    }			       
}

sub forward {
#
# Forward results if job.forward is present
#

    if (-s "job.forward") {
        open(RES, "job.results") or &handler("Could not open job.results file for reading for job $JOB_NUMBER.\n");
        if (!$address) {
            open(CAT, "job.address") or &handler("Could not open address file for job $JOB_NUMBER.\n");
            chomp($address = <CAT>);
            close CAT;
        }
        open(CAT, "job.forward") or &handler("Could not read from job.forward for job $JOB_NUMBER");
        chomp($forward_address = <CAT>);
        close CAT;

        open(MAIL, "|sendmail -oi -t") or &handler("job_end.pl: Denied access to sendmail program\n");
        print MAIL <<END_SOLVER_RESULTS;
To: $forward_address 
From: $ENV{SERVER_NAME} Server <$ENV{SERVER_EMAIL}>
Subject: Job #$JOB_NUMBER

END_SOLVER_RESULTS

    while(<RES>) {        
        print MAIL;
    } 
    close RES;
    close(MAIL);
    }
}


sub log {

#
# These parameters are meant to make flock calls more readable.
#

    $LOCK_SH = 1;
    $LOCK_EX = 2;
    $LOCK_NB = 4;
    $LOCK_UN = 8;

    $warnings = " ";
    open(CAT, "job.start") or &handler("Could not read from job.start file for job $JOB_NUMBER.\n");
    chomp($start = <CAT>);	
    close CAT;
				
    $start =~ /(.+) (.+)/;
    $startDate = $1;
    $startTime = $2;
    
#
# note on perl flock:  this may fail if the database
# is being accessed across an nfs
#

    system("touch $ENV{SERVER_VAR}/databases/master") and &handler("job_end.pl: Could not write to master database for job $JOB_NUMBER"); # make sure it exists
    chomp($address = `cat job.address`) if (!$address);
    open(MASTER, "+<$ENV{SERVER_VAR}/databases/master") or &handler("job_end.pl: Could not write to master database for job $JOB_NUMBER");
#
#get an exclusive lock on the database
#wait if necessary
#
    $locked = flock MASTER, $LOCK_EX | $LOCK_NB;
    $wait = 0;
    while(!($locked))
    {
	$wait++;
	if ($wait > 120)
	{
	    # break out of loop
	    # and warn in database
	    $warnings = "Flock:(";
	    last;
	}
	sleep(2);
	$warnings = "Slept";
	$locked = flock MASTER, $LOCK_EX | $LOCK_NB;    
    }

#
# Write to 1K job record in master file
#

    $sought = 200*($JOB_NUMBER-1);
    seek(MASTER, 0, 2);
    $curpos = tell MASTER;
    while(($curpos) < ($sought)) {
	$empty = "";
	print MASTER pack("A10 A10 A8 A10 A8 A16 A30 A50 A50 A7 A", 
			  $empty,$empty,$empty,$empty,$empty,$empty, 
			  $empty,$empty,$empty,$empty,"\n");
	seek(MASTER, 0, 2);
	$curpos = tell MASTER;
	$warnings = $warnings."e"; 
    }
#
# Get the ends of addresses that are too long, since the domains
# are of greater interest than address beginnings with no domains.
#
    $address = substr($address, -50, 50);
    $forward_address = substr($forward_address, -50, 50);
    seek(MASTER, $sought, 0);
    print MASTER pack("A10 A10 A8 A10 A8 A16 A30 A50 A50 A7 A", 
		      $JOB_NUMBER, 
		      $startDate, $startTime, $endDate, $endTime, 
		      $interface, $TOKEN, $address, $forward_address, $warnings, "\n");
    &flush(MASTER);
    flock MASTER, $LOCK_UN;
    close(MASTER);
}				










