#!/usr/local/bin/perl -w
package daemonpl;

my $localhostname = shift
or die "You must input the hostname of the machine to be run on";
##################################
#The user to the common database
my $dbuser = 'compute_user';
my $dbpwd = 'FeindexAw3';
my $dbserver = 'mysql.research.pdx.edu';
###################################
my $db = 'compute_procs';
my $jobtable = 'remote_jobs';
my $machinetable = 'remote_machines';
my $home = '/home/lukacm/PRE_Daemon';
my $datadir = '/vol/apps/temp/executioner';
my $logdir = '/home/lukacm/PRE_Daemon';
###################################
#Limit the time files can stay in the storage
#Maximum 365 days
my $cronlim = '6';
my $clients = 1;
###################################
# Number of child processes (request handlers) to keep going
my $totalChildren = 1;
# Number of requests each child handles before dying
my $logFile = "";
my $pidFile = "";
my %children;
my $children = 0;
###################################

use strict;
use POSIX;
use POSIX qw(:sys_wait_h);
use POSIX qw(setsid);
use warnings;
use diagnostics;
use IO::Socket;
use DBI;



my ($socket, $buff, $lockf);
my $mode = -1;
my $port = shift || 8080;
my $proto = getprotobyname('tcp');
my ($startyear, $startday) = `date +%G%j` =~ m/(\d\d\d\d)(\d\d\d)$/;

#write the pid file
my $ppid = getppid();
$pidFile = $logdir.'/daemon.'.$ppid.'.pid';
sysopen ($lockf, $pidFile,  O_CREAT|O_RDWR)
    or die "$0 is already running";
 

&daemonize;

# create a socket, make it reusable
$socket = new IO::Socket::INET(
		LocalHost => $localhostname,
		LocalPort => $port,
		Proto     => $proto,
		Listen    => SOMAXCONN,
		Reuse     => 1);
$socket or die "no socket :$!";

# Log the URL used to access our daemon
logMessage ("master is ", $localhostname);
&spawnClients;
&watchStatus;

sub daemonize {   
	my $pid = fork;   defined ($pid) or die "Cannot start daemon: $!";
	# If we're the shell-called process,
	# let the user know the daemon is now running.
	print "Parent daemon running.\n" if $pid;
	# If we're the shell-called process, exit back.
	exit if $pid;

	# Now we're a daemonized parent process!
	#go to the root
	chdir '/'  or die "Can't chdir to /: $!";
	umask 0;

	#create the logfile
	my $pidd = getppid();
	$logFile = $logdir.'/.daemon-log.'.$pidd.'.log';
	open(LOGFILE, ">$logFile");	# Open for output

	# Detach from the shell entirely by setting our own
	# session and making our own process group
	# as well as closing any standard open filehandles.
	POSIX::setsid();
	#close (STDOUT); close (STDIN); close (STDERR);

	#turn off the streams
	open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
#	open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
#	open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!";


	# Set up signals we want to catch. Let's log
	# warnings, fatal errors, and catch hangups
	# and dying children

	$SIG{__WARN__} = sub {
		&logMessage ("NOTE! " . join(" ", @_));
	};

	$SIG{__DIE__} = sub {
		&logMessage ("FATAL! " . join(" ", @_));
		exit;
	};

	$SIG{HUP} = $SIG{INT} = $SIG{TERM} = sub {
		# Any sort of death trigger results in death of all
		my $sig = shift;
		$SIG{$sig} = 'IGNORE';
		kill 'INT' => keys %children;
		die "killed by $sig\n";
		exit;
	};

	# We'll handle our child reaper in a separate sub
	$SIG{CHLD} = \&REAPER;
}


sub REAPER {

      my $stiff;
      while (($stiff = waitpid(-1, &WNOHANG)) > 0) {
        warn ("child $stiff terminated -- status $?");
        $children--;
        delete $children{$stiff};
      }

      $SIG{CHLD} = \&REAPER;
}
sub spawnClients {
	for (1..$totalChildren) {
        	&newClient();
	}
}

sub watchStatus {
	while ( 1 ) {
        	sleep;
		for (my $i = $children; $i < $totalChildren; $i++ ) {
			&newClient();
        	}
		# the cleaning procedure is activated once every 24 hours to remove 
		# files older than $cronlim days
      	};
	close(LOGFILE);
	`rm $logFile`;

}

sub logMessage {
	my $input = shift;
	print LOGFILE "Message: ".$input."\n";
}

sub newClient{
	# Daemonize away from the parent process.
	my $pid;

	my ($client, $writebuffer, $transfersize, $filename, $command, $remote_machine_id, $state, $datdir,$dbh,$prints,$line,$br);
	print "Server starting\n";
	#The main server loop keeping it alive
	while ($client = $socket->accept()) {

		my $sigset = POSIX::SigSet->new(SIGINT);
		sigprocmask(SIG_BLOCK, $sigset) or die "Can't block SIGINT for fork: $!";
		die "Cannot fork child: $!\n" unless defined ($pid = fork);
		if ($pid) {
			$children{$pid} = 1;
			$children++;
			print "forked new child, we now have $children children";
			return;
		}

		## parent goes to continue block
		exit if $pid;
	 	print "Recieving connection on $port \n";
	
		#close the server socket - not needed anymore
		close($socket);

		setsid or die "Can't start a new session: $!";
		binmode $client;

		#the child pid is reading the socket
		while (1){
			$buff = <$client>;
			#	print ("'TOP Mode': ".$mode."\n");
			#	print ("'TOP Buff': ".$buff."\n");
			if($buff =~ m/^'end'/g){
				close($client);
				#print "Closing client \n";
				undef($client);
				$clients--;
				exit;
			}
			if ($mode == -1){
				#print "Buff '".$buff."'\n";
				#$buff = chop $buff;
				if ($buff =~ /^filename:/m){
					$filename = substr($buff, index($buff, " ", 1)+1);
					chomp($filename);
					print "Received: '".$filename."'\n";
				} elsif ($buff =~ /^sendfile (\d*)/m){
					$mode = 1;
					#print "Mode: ".$mode."\n";
					#print "Size: ".$buff."\n";
					$transfersize = $1;
       				} elsif ($buff=~/^retreive (\w*)/m){
					$mode = 2;
					print "Mode: ".$mode."\n";
					$filename = substr($buff, index($buff," ",1)+1);
					$datdir = $datadir."/".$filename;
					if ($datdir =~ /(.+)\n$/m){
						print "Gotcha\n";
						$datdir = $1;
					}
       				} elsif ($buff=~/^mkdir (\S*)/m){
					my $dr = $1;
					chomp $dr;
					umask 0;
					$datdir = $datadir."/".$dr;
					print $datadir."\n";
					if (! -e $datdir){
						`mkdir $datdir`;
						`chmod 777 $datdir`;
						print "Data dir created: ".$datdir."\n";
					}	
       				} elsif ($buff=~/^get '(.+)'/m){
					print ("Getting\n");
					$filename = $1;
					$mode = 3;
					$datdir = $datadir."/".$filename;	
					print "Mode: ".$mode."\n";
	       			} elsif ($buff=~/^list '(.+)'/m){
					print 'Detected Tity';
					my $location = $datadir.'/'.$1;	
					my $tity = `ls $location\n`;
					my @files = split(/\n/, $tity);
					foreach (@files){
						print $client $_." ";
					}
					print $client "\n";
					print "Titty: ".$tity."\n";
					close($client);
					exit;
				} elsif ($buff=~/^rawexec '(.+)':'(.+)'/m){
					# this will execute in any data directory containing some data and command files
					my $ddlocation = $2;	 
					my $comand = $1;	 
					my ($id,$appname, $dlocation,$clocation, $submitdate, $alivetime, $remote_machines_id);
					print "recevied: ".$buff."   ".$comand."   ".$ddlocation."\n";
					my $datafolder = $ddlocation;
			
					$ddlocation = $datadir.'/'.$ddlocation;
					print "Params: ".$comand."   ".$ddlocation."\n";
					`chmod -R  +rwxrw $ddlocation`;
					`chmod -R 755 $ddlocation`;
					chdir $ddlocation;

					$dbh = DBI->connect("DBI:mysql:$db:$dbserver","$dbuser","$dbpwd");
					my $query =  "SELECT * FROM $jobtable WHERE dlocation='$datafolder'";
					my $table_data = $dbh->prepare($query);
					$table_data->execute;
					$table_data->bind_columns(\$id, \$appname, \$dlocation, \$clocation, \$command, \$submitdate, \$alivetime, \$state, \$remote_machines_id);
					$table_data->fetch();

					my $dtime = `date`;
					chomp($dtime);
					my $ipl = `hostname`;
					if ($ipl =~ /(\w*)\./s){
						$ipl = $1;
					}

					$query =  "SELECT * FROM $machinetable WHERE machinename='$ipl'";
					$table_data = $dbh->prepare($query);
					$table_data->execute;
					$table_data->bind_columns(\$id);
					$table_data->fetch();

					print "Machine Id: ".$id."\n";
					if (length($dlocation) > 1){
						$query =  "UPDATE $jobtable SET submitdate='$dtime' WHERE dlocation='$datafolder'";
						$table_data = $dbh->prepare($query);
						$table_data->execute;
					} else {
						#execute the command
						#print "Current location is: ".`pwd`."\n";
						print "Executing: ".$comand."\n";
						$query="INSERT INTO $jobtable SET appname='fastexec', dlocation='$datafolder', clocation='$datafolder', command='$comand', submitdate='$dtime', alivetime='0:0:0', state=1, remote_machines_id=$id";
						$table_data = $dbh->prepare($query);
						$table_data->execute;
						print "Query: ".$query."\n";
					}

					print "running\n";
					my $out = `$comand`;
					print "done\n";
					$query="UPDATE $jobtable SET alivetime='0:0:0', state=-1 WHERE dlocation='$datafolder'";
					$table_data = $dbh->prepare($query);
					$table_data->execute;
					print "Query: ".$query."\n";
					
					# my $currdate = `date +%G%j`;
					# my($lastyear, $lastday) = $currdate =~ m/(\d\d\d\d)(\d\d\d)$/;
					dbh->disconnect();
					close($client);
					exit;
		       		} elsif ($buff=~/^execute '(.+)'/m){
					my $parameters = $1;	 
					my ($dlocation,$date,$query,$table_data);
					print "recevied: ".$buff."   ".$parameters."\n";
					$dbh = DBI->connect("DBI:mysql:$db:$dbserver","$dbuser","$dbpwd");
					if ($parameters =~/(\w+) (\w+) (\d+) (\d+:\d+:\d+) (\w+) (\d+)/m){
						#print "Processing date\n";	
						$query =  "SELECT dlocation FROM $jobtable WHERE submitdate='$parameters'";
						$table_data = $dbh->prepare($query);
						$table_data->execute;
						# assign fields to variables
						$table_data->bind_columns(\$dlocation);
						# output name list to the browser
						$table_data->fetch();
						$date = $parameters;
						#print "Check: ".$date."   ".$dlocation."\n";
					} elsif($parameters =~/(.+)/m) {
						#print "Processing Location\n";
						#get the data of this job (backward compatibility)
						$query =  "SELECT submitdate FROM $jobtable WHERE dlocation='$parameters'";
						#print $query."\n";
						$table_data = $dbh->prepare($query);
						$table_data->execute;
						# assign fields to variables
						$table_data->bind_columns(\$date);
						# output name list to the browser
						$table_data->fetch();
						$dlocation = $parameters;
						#print "Check: ".$date."   ".$dlocation."\n";
					}
					print "executing!!!!!\n";
					$dlocation = $datadir.'/'.$dlocation;
					#print "Params: ".$date."   ".$dlocation."\n";
					`chmod -R  +rwx-s $dlocation`;
					`chmod -R 755 $dlocation`;
					chdir $dlocation;

					#get the name of the job
					$query =  "SELECT command FROM $jobtable WHERE submitdate='$date'";
					$table_data = $dbh->prepare($query);
					$table_data->execute;
					# assign fields to variables
					$table_data->bind_columns(\$command);
					# output name list to the browser
					$table_data->fetch();

					#update the state to running
					$query =  "update $jobtable SET state='1' where submitdate='$date'";
					$table_data = $dbh->prepare($query);
					$table_data->execute;

					# get the machine id
					$query =  "SELECT remote_machines_id FROM $jobtable WHERE submitdate='$date'";
					$table_data = $dbh->prepare($query);
					$table_data->execute;
					# assign fields to variables
					$table_data->bind_columns(\$remote_machine_id);
					$table_data->fetch();
		
					# get the state
					$query =  "SELECT state FROM $machinetable WHERE id='$remote_machine_id'";
					$table_data = $dbh->prepare($query);
					$table_data->execute;
					# assign fields to variables
					$table_data->bind_columns(\$state);
					$table_data->fetch();
		
					$state = $state +1;
					$query =  "update remote_machines SET state='$state' where id='$remote_machine_id'";
					$table_data = $dbh->prepare($query);
					$table_data->execute;
		
					#execute the command
					chdir $dlocation;
					#print "Current location is: ".`pwd`."\n";
					#print "Executing: ".$command."\n";
					my $out = `$command`;
			
		
					# set application as terminated
					$state = $state-1;
					#put the state of the machine back one level bellow
					$query =  "update remote_machines SET state='$state' where id='$remote_machine_id'";
					$table_data = $dbh->prepare($query);
					$table_data->execute;
			
					#update the application state to stoped
					$query =  "update $jobtable SET state='-1' where submitdate='$date'";
					$table_data = $dbh->prepare($query);
					$table_data->execute;
			
					$table_data->finish();
		
					# disconnect from database
					$dbh->disconnect;
					close($client);
					exit;
		       		} elsif($buff eq "quit"){
					#print "QUIT: ".$mode."\n";
					close($client);
					exit;
				} elsif ($buff=~/^clean '(.+)'/m){
					print "$home/clean.pl --clean \n";
					my $out = `$home/clean.pl --clean`;
					exit;
				} elsif($buff=~/hardclean/m){
					print "$home/clean.pl --hardclean \n";
					my $out = `$home/clean.pl --hardclean`;
					exit;
				} elsif($buff=~/(remove) ([A-Z,a-z,0-9,_,+]*)/m){
					print "$home/clean.pl --remove  $2\n";
					my $out = `$home/clean.pl --remove $2`;
					exit;
				} elsif (length($buff) <= 1){
					exit;
				}
			} elsif($mode == 1){
				if($buff =~ /\*\*\*END\*\*\*/m){
					$mode = -1; 
					my $finalname = $datdir."/".$filename;
					print "Closing: '".$finalname."'\n";
					my $filn = ">".$finalname;
					#print data to output file
					open(OUTPUT, $filn);
					binmode OUTPUT;
					print OUTPUT $writebuffer;
					close(OUTPUT);
					close(RESULT);
					print "Buffer length: ".length($writebuffer)."\n";
					close($client);
					#$writebuffer ="";
					exit;
       				}

				$writebuffer.= $buff;
	 		} 
	       		if($mode == 2){
				#go the output directory
				print "Active Mode 2:'\n";
				chdir $datdir;
				my $o = `ls`;
				#print $o."\n";
				#Send all files back to he client
				my @lines = split /^/,$o;
				foreach (@lines) {
					$filename = $_;
					chomp $filename;
					if (length ($filename) > 1 && !(-d $filename)){
						print "Opening file: ".$filename."\n";
						$transfersize = -s $filename;
						open (RESULT, "<$filename");
						binmode RESULT;
						#print "Opening: '".$filename."',  '".$transfersize."'\n";
						$prints = "FileSending: ".$filename." ".$transfersize;
						print $client $prints."\n";
						$br = '';
						my $i = 0;
						while ((my $n = sysread (RESULT, $line, 2048)) != 0) 
						{
							$br.= $line;
							#print "$i .\n";
							$i++;
							print $client $line;
							sleep(0.5);
						}
						close(RESULT);
						print $client "***END***\n";
					}
				}	
				print $client "***DONE***\n";
		 		$mode = -1;	
				close($client);
				exit;
	 		} elsif($mode == 3){
		                #go the output directory
				print "Active Mode 3:'\n";
		                #Send this file back to he client
				#verify the input file name
		                #chomp $filename;
				my $filen;
		                if (length ($filename) > 1 && !(-d $filename)){
					print "Opening file: ".$filename."\n";
					$transfersize = -s $datdir;
					if ($filename =~ /(.+)\/(.+)$/m){
						$filen = $2;
					} else {
						$filen = $filename;
					}
	       				open (RESULT, "<$datdir");
					binmode RESULT;
					#print "Opening: '".$filename."',  '".$transfersize."'\n";
					$prints = "FileSending: ".$filen." ".$transfersize;
					print $client $prints."\n";
	                        	$br = '';
		                        my $j = 0;
		                        while ((my $m = sysread (RESULT, $line, 2048)) != 0)
					{
						#print "Sending Line: $line \n";
	       		                        $br.= $line;
						#print "$j .\n";
						print $client $line;
						$j++;
						#print '.';
						sleep(0.5);
					}
					#print $line."\n";
					close(RESULT);
					#print $client "***END***\n";
                        	}
				#print $client "***DONE***\n";
		                $mode = -1;
				close($client);
				exit;
			} #else {print "Mode: ".$mode."  undefined\n";}
		} continue {
			#close($client);
			#exit;
		}
	}
unlink '/var/lock/mydaemon0';
}
