#!/usr/bin/perl


###################################################
# This script reads build requests from a file
# called arc-queue.  It should be run as a cron
# job, so that the queue is processed regularly.
# It uses flock to lock the queue file while it is
# reading and writing so that it doesn't collide
# with the script (arc-qadd) that appends to the queue.  It 
# will keep running as long as there are builds in
# the queue -- it rechecks the queue after completing
# all of the jobs from the previous read.  It will 
# run only $maxSimCmds at a time.  If there are more commands
# in the queue, it blocks until running builds complete.
# Note that it will exit when no more jobs are in the
# queue, even when the builds are still running.
# -- Dan Marcus 2.28.03
###################################################
use FindBin qw($Bin);
use lib "$Bin";

my $ARC_TOOLS = "/home/vmuser/arc-queue";
use strict;


#name of build command
my @currentCommands;
my $maxSimCmds = 1;

#if there's another qrunner executing, then exit
my $cmd = "ps -ef | grep '/usr/bin/perl.*arc-qrunner' | wc -l";
my $procs = `$cmd`;
chop $procs;
$procs =~ s/^\s+//;
if  ($procs > 3){ #with the sh and grep created by the system command, we'll have at least 3
	exit(0);		
}

#open log file --  standard out is directed to it!
my $log = "$ARC_TOOLS/logs/arc-queue.log";
open LOGOUT, ">>$log" or fail("The log file ($log) could not be opened",1);
$| = 1;  #keeps things from being buffered.

#open error log file --  standard error is directed to it!
my $error = "$ARC_TOOLS/logs/arc-queue.error";
open LOGERR, ">>$error" or fail("The error file ($error) could not be opened",1);
$| = 1;  #keeps things from being buffered.

print LOGOUT "\n\n------------------------------------------------\n";
print LOGOUT scalar localtime;
print LOGOUT "\n";

print LOGERR "\n\n------------------------------------------------\n";
print LOGERR scalar localtime;
print LOGERR "\n";

my @cmds = readQueue();
while (scalar(@cmds) > 0){
    foreach my $cmd (@cmds){
	    startCommand($cmd);
    }
    @cmds = readQueue();
}

close(LOGOUT);
close(LOGERR);

sub startCommand(){
    my $command = shift @_;
    #block until number of build processes <= $maxSimCmds
    my $ccount = currentCmdCount();
    while ($ccount >= $maxSimCmds){
	sleep(5);
	$ccount = currentCmdCount();
    }
    print LOGOUT "Moving on ($ccount): $command\n";
    #add this command to the list of running commands
    push @currentCommands, $command;

    print LOGOUT "".(scalar localtime)." -- Starting command: $command\n";
    my $runCmd = "$command &";
    system $runCmd;
    sleep(1); #give the process time to start before checking build count
}

sub currentCmdCount(){

    my @removeMe;
    for (my $i=0; $i<scalar(@currentCommands); $i++){
	#get a truncated version of the current command
	my $testCommand = substr @currentCommands[$i], 0, 55;
	my $procs = `ps -ef | grep '$testCommand'`;
	my @actCmds =  grep !/grep/, (split /\n/, $procs);
	if (scalar (@actCmds) == 0){
	    push @removeMe, $i;
	}
    }
    if (scalar(@removeMe) != 0){
	foreach my $i (@removeMe){
	    splice @currentCommands, $i, 1;
	}
    }

    return scalar(@currentCommands);
}

sub readQueue(){
#exclusive lock arc-queue
    my $queueFile = "$ARC_TOOLS/arc-queue";
    open QUEUE, "$queueFile" or fail("FAILURE: Coulnd't open queue file.\n");
    
    lockIt();

#put all of the elements in the file queue into an array.
    my @list;
    while (<QUEUE>){
	chop;
	my $item = $_;
	push @list, $item;
    }

#clear the file queue
    truncate QUEUE, 0;

#unlock arc-queue
    unlock();

    open QUEUE, ">$queueFile" or fail("FAILURE: Coulnd't open queue file again.\n");
    close QUEUE;

    print LOGOUT "Queue size: ".(scalar(@list))."\n";
    return @list;
}

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

sub lockIt() {
    flock QUEUE, $LOCK_EX;
    #seek QUEUE, 0, 2;
}

sub unlock(){
    flock QUEUE, $LOCK_UN;
}

sub fail(){
    my $failMessage = shift @_;
    print LOGERR $failMessage;
    die;
}
