#!/home/p/perl5 -w

use FindBin qw($Bin);

use strict;
use lib ("$ENV{HOME}/cvs/triskman/pm", "$Bin/../pm",
	 "/home/t/triskman/share/lib", "/home/p/triskman/share/lib");

use Data::Dumper;
use POSIX;
use Arguments;

my $processfile = "/tmp/process.in";
my $donefiles   = "";
my $rootlogdir  = "/home/nk00637/tmp7/logs";
my $daemon      = 0;
my $LDLP="/home/p/triskman/sparc-sunos5/lib";
my $MACS = "/tmp/usable.lst";

my $parse_ref
  = [
     [ "in" => \$processfile,
       {USAGE => 'input file for list of cmd files'} ],
     [ "done" => \$donefiles,
       {USAGE => 'outfile file for list of completed cmd files'} ],
     [ "logdir" => \$rootlogdir,
       {USAGE => 'log dir'} ],
     [ "dmn|d|daemon" => \$daemon,
       { TYPE => argTypeSwitch(), USAGE => "run as daemon" } ],
     [ "machine|m" => \$MACS,
       { USAGE => "machine list" } ],
    ];
Arguments::parse(@ARGV, $parse_ref);

unless ($donefiles) {
    $donefiles = "$processfile.done";
}

system("mkdir -p $rootlogdir") unless -d $rootlogdir;

if ($daemon) {
    daemonify();
    open (LOG, "> $rootlogdir/daemon.log") || die "can't open log in ($rootlogdir)\n";
} else {
    *LOG=*STDOUT;
}

my %machines;
my %log_files   = ();

update_mac_list(\%machines);

while (1) {
    my @files = get_file_list($processfile, $donefiles);
    exit 0 if $#files < 0;

    foreach my $file (@files) {
	print LOG "Processing file : $file\n";
	# Process file
	# 1. read commands in the file
	my @commands = get_list($file);
	my $i = 0;
	foreach my $cmd (@commands) {
	    # 2.1 wait till machines are free
	    my $mac = get_free_mac(\%machines, \%log_files);
	    $i++;
	    print LOG "doing command ($mac): $cmd\n";
	    my ($err, $std) = log_files($rootlogdir, $file, $mac, $i);
	    # 2.2 run the command on free machine
	    system "echo 'CMD : $cmd' > $std";
#	    system "rsh $mac 'echo Begin ...; ; export DA=/home/t; export SYBASE=/home/t/sybase; export LD_LIBRARY_PATH=$LDLP; $cmd; echo End ...;'>>$std 2>$err &";
	    system "ssh -q $mac 'echo Begin ...; date ; export AUTOSERV=TDA; export DA=/home/p; export SYBASE=/home/p/sybase; export LD_LIBRARY_PATH=$LDLP; . /home/p/tkautosys/env/auto.profile; $cmd; echo EXITSTAT = \$\?; date ; echo End ...; '>>$std 2>$err &";
	    $log_files{$mac} = $std;
	    unless ($i % 60) {
	        print LOG "sleeping for 20 secs (30 jobs already running)\n";
		sleep 20;
	    }	
	}
	# done
	system "printf '$file\n' >> $donefiles";
    }
}

close LOG if $daemon;

exit 0;

sub getmaclist {
    return map { chomp; ($_, 1) } grep { s/\#.*//; !/^\s*$/ } `cat $MACS`;
}

sub update_mac_list {
    my ($mlist) = @_;
    my %nlist = getmaclist();

    my $updated = 0;

    foreach my $mac (keys %$mlist) {
	unless (exists $nlist{$mac}) {
	    delete $mlist->{$mac};
	    $updated = 1;
	}
    }

    foreach my $mac (keys %nlist) {
	unless (exists $mlist->{$mac}) {
	    $mlist->{$mac} = 1;
	    $updated = 1;
	}
    }

    if ($updated) {
	print LOG "USING UPDATED MAC LIST : ", join ", ", keys %$mlist;
	print LOG "\n";
    }
}

sub get_free_mac {
    my ($mac_list, $logs) = @_;

    update_mac_list($mac_list);

    my $macname = (grep { $mac_list->{$_} } keys %$mac_list)[0];
    unless ($macname) {
	my @macs = ();
	while (!scalar(@macs)) {
	    while (my ($mac, $log) = each %$logs) {
		update_mac_list($mac_list);
		my $last = `tail -1 $log`;
		if ($last && $last =~ /End/) {
		    push @macs, $mac if exists $mac_list->{$mac};
		    delete $logs->{$mac};
		}
	    }

	    unless (scalar(@macs)) {
		print LOG "Machines not available ... sleeping for 30 secs\n";
		sleep 30;
	    }
	}
	foreach my $mac (@macs) {
	    $macname = $mac;
	    $mac_list->{$macname} = 1;
	}
    }

    while (1) {
	    my @rsh = `ps -ef | grep ssh | grep Beg`;
	    if (scalar(@rsh) <= ($ENV{MAX_PORT} || 300)) {
		    last;
	    } else {
		    print LOG "300 ports in use ...\n";
		    sleep 10;
	    }
    }

    $mac_list->{$macname} = 0;
    return $macname;
}

sub get_file_list {
    my ($in, $out) = @_;
    my %in  = map { $_, 1} get_list($in);
    my %out = map { $_, 1} get_list($out);

    return (grep { !exists $out{$_} } keys %in);
}

sub get_list {
    my $file = shift;

    my @data = -f $file ? grep { !/^\s*$/ && !/^\#/ } `cat $file` : ();
    chomp @data;
    return @data;
}

sub log_files {
    my ($root, $dir, $mac, $index) = @_;

    my $base = `basename $dir`;
    chomp $base;
    my $log = "$root/$base";
    system "mkdir -p $log" unless -d $log;
    $log .= "/out.$index.$mac";
    return ("$log.err", "$log.std");
}

sub daemonify {
    my $pid;
    if ($pid = fork) {
	# parent: exit
	exit 0;
    }
    # If we are here, it must be child or we had an error in fork
    die "Can't fork new process: $!" if $pid < 0;

    # Let's make the child the session leader, so to detach
    # from the control tty
    my $sess_id = POSIX::setsid();
#    log_msg($Info, "Started daemon, session id: $sess_id .");
    return $sess_id;
}
