diff --git a/PBot/VERSION.pm b/PBot/VERSION.pm index 0cca2ebb..be9617ae 100644 --- a/PBot/VERSION.pm +++ b/PBot/VERSION.pm @@ -13,8 +13,8 @@ use warnings; # These are set automatically by the build/commit script use constant { BUILD_NAME => "PBot", - BUILD_REVISION => 482, - BUILD_DATE => "2014-02-21", + BUILD_REVISION => 483, + BUILD_DATE => "2014-02-22", }; 1; diff --git a/modules/compiler_vm/compiler_server.pl b/modules/compiler_vm/compiler_server.pl index 80d99f1c..c1d2cc5e 100755 --- a/modules/compiler_vm/compiler_server.pl +++ b/modules/compiler_vm/compiler_server.pl @@ -5,8 +5,12 @@ use strict; use IO::Socket; use Net::hostent; +use IPC::Shareable; -my $PORT = 9000; +my $SERVER_PORT = 9000; +my $MONITOR_PORT = 3335; +my $SERIAL_PORT = 3333; +my $HEARTBEAT_PORT = 3336; sub server_listen { my $port = shift @_; @@ -17,7 +21,7 @@ sub server_listen { Listen => SOMAXCONN, Reuse => 1); - die "can't setup server" unless $server; + die "can't setup server: $!" unless $server; print "[Server $0 accepting clients]\n"; @@ -39,7 +43,7 @@ sub vm_start { } if($pid == 0) { - my $command = 'nice -n -20 qemu-system-x86_64 -M pc -net none -hda /home/compiler/compiler/compiler-savedvm.qcow2 -m 128 -monitor tcp:127.0.0.1:3335,server,nowait -serial tcp:127.0.0.1:3333,server,nowait -boot c -loadvm 1 -enable-kvm -nographic -no-kvm-irqchip'; + my $command = "nice -n -20 qemu-system-x86_64 -M pc -net none -hda /home/compiler/compiler/compiler-savedvm.qcow2 -m 128 -monitor tcp:127.0.0.1:$MONITOR_PORT,server,nowait -serial tcp:127.0.0.1:$SERIAL_PORT,server,nowait -serial tcp:127.0.0.1:$HEARTBEAT_PORT,server -boot c -loadvm 1 -enable-kvm -no-kvm-irqchip -nographic"; my @command_list = split / /, $command; exec(@command_list); } else { @@ -51,7 +55,7 @@ sub vm_reset { use IO::Socket; print "Resetting vm\n"; - my $sock = IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => 4445, Prot => 'tcp'); + my $sock = IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => $MONITOR_PORT, Prot => 'tcp'); if(not defined $sock) { print "[vm_reset] Unable to connect to monitor: $!\n"; return; @@ -59,7 +63,7 @@ sub vm_reset { print $sock "loadvm 1\n"; close $sock; - print "Resetted vm\n"; + print "Reset vm\n"; } sub execute { @@ -107,96 +111,154 @@ sub execute { } sub compiler_server { - my $vm_pid = vm_start; - print "vm started pid: $vm_pid\n"; + my ($server, $heartbeat_pid, $heartbeat_monitor); - my $server = server_listen($PORT); + my $heartbeat; + my $running; - while (my $client = $server->accept()) { - $client->autoflush(1); - my $hostinfo = gethostbyaddr($client->peeraddr); - print '-' x 20, "\n"; - printf "[Connect from %s]\n", $client->peerhost; - my $timed_out = 0; - my $killed = 0; + tie $heartbeat, 'IPC::Shareable', 'dat1', { create => 1 }; + tie $running, 'IPC::Shareable', 'dat2', { create => 1 }; - eval { - my $lang; - my $nick; - my $code = ""; + while(1) { + $running = 1; + $heartbeat = 0; - local $SIG{ALRM} = sub { die 'Timed-out'; }; - alarm 5; + my $vm_pid = vm_start; + print "vm started pid: $vm_pid\n"; - while (my $line = <$client>) { - $line =~ s/[\r\n]+$//; - next if $line =~ m/^\s*$/; - alarm 5; - print "got: [$line]\n"; + $heartbeat_pid = fork; + die "Fork failed: $!" if not defined $heartbeat_pid; - if($line =~ m/^compile:end$/) { - $code = quotemeta($code); - print "Attemping compile...\n"; - alarm 0; - my $tnick = quotemeta($nick); - my $tlang = quotemeta($lang); + if($heartbeat_pid == 0) { + tie $heartbeat, 'IPC::Shareable', 'dat1', { create => 1 }; + tie $running, 'IPC::Shareable', 'dat2', { create => 1 }; - my ($ret, $result) = execute("./compiler_vm_client.pl $tnick -lang=$tlang $code"); - - if(not defined $ret) { - #print "parent continued\n"; - print "parent continued [$result]\n"; - $timed_out = 1 if $result == 243; # -13 == 243 - $killed = 1 if $result == 242; # -14 = 242 - last; - } - - $result =~ s/\s+$//; - print "Ret: $ret; result: [$result]\n"; - - if($result =~ m/\[Killed\]$/) { - print "Processed was killed\n"; - $killed = 1; - } - - if($ret == -13) { - print $client "$nick: "; - } - - print $client $result . "\n"; - close $client; - - $ret = -14 if $killed; - - # child exit - # print "child exit\n"; - exit $ret; + $heartbeat_monitor = undef; + while(not $heartbeat_monitor) { + print "Connecting to heartbeat ..."; + $heartbeat_monitor = IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => $HEARTBEAT_PORT, Proto => 'tcp', Type => SOCK_STREAM); + if(not $heartbeat_monitor) { + print " failed.\n"; + sleep 2; + } else { + print " success!\n"; } - - if($line =~ /compile:([^:]+):(.*)$/) { - $nick = $1; - $lang = $2; - $code = ""; - next; - } - - $code .= $line . "\n"; } - alarm 0; - }; + #print "child: running: $running\n"; - alarm 0; + while($running and <$heartbeat_monitor>) { + $heartbeat = 1; + #print "child: got heartbeat\n"; + } - close $client; + #print "child no longer running\n"; + exit; + } else { + if(not defined $server) { + print "Starting compiler server on port $SERVER_PORT\n"; + $server = server_listen($SERVER_PORT); + } else { + print "Compiler server already listening on port $SERVER_PORT\n"; + } - next unless ($timed_out or $killed); - - print "stopping vm $vm_pid\n"; - vm_stop $vm_pid; - $vm_pid = vm_start; - print "new vm pid: $vm_pid\n"; - } + #print "parent: running: $running\n"; + + while ($running and my $client = $server->accept()) { + $client->autoflush(1); + my $hostinfo = gethostbyaddr($client->peeraddr); + print '-' x 20, "\n"; + printf "[Connect from %s at %s]\n", $client->peerhost, scalar localtime; + my $timed_out = 0; + my $killed = 0; + + eval { + my $lang; + my $nick; + my $code = ""; + + local $SIG{ALRM} = sub { die 'Timed-out'; }; + alarm 5; + + while (my $line = <$client>) { + $line =~ s/[\r\n]+$//; + next if $line =~ m/^\s*$/; + alarm 5; + print "got: [$line]\n"; + + if($line =~ m/^compile:end$/) { + if($heartbeat == 0) { + print "No heartbeat yet, ignoring compile attempt.\n"; + print $client "$nick: Recovering from previous snippet, please wait.\n"; + last; + } + + $code = quotemeta($code); + print "Attempting compile...\n"; + alarm 0; + my $tnick = quotemeta($nick); + my $tlang = quotemeta($lang); + + my ($ret, $result) = execute("./compiler_vm_client.pl $tnick -lang=$tlang $code"); + + if(not defined $ret) { + #print "parent continued\n"; + print "parent continued [$result]\n"; + $timed_out = 1 if $result == 243; # -13 == 243 + $killed = 1 if $result == 242; # -14 = 242 + last; + } + + $result =~ s/\s+$//; + print "Ret: $ret; result: [$result]\n"; + + if($result =~ m/\[Killed\]$/) { + print "Process was killed\n"; + $killed = 1; + } + + if($ret == -13) { + print $client "$nick: "; + } + + print $client $result . "\n"; + close $client; + + $ret = -14 if $killed; + + # child exit + # print "child exit\n"; + exit $ret; + } + + if($line =~ /compile:([^:]+):(.*)$/) { + $nick = $1; + $lang = $2; + $code = ""; + next; + } + + $code .= $line . "\n"; + } + + alarm 0; + }; + + alarm 0; + + close $client; + + next unless ($timed_out or $killed); + + print "stopping vm $vm_pid\n"; + vm_stop $vm_pid; + $running = 0; + last; + } + #print "Compiler server no longer running, restarting...\n"; + } + waitpid($heartbeat_pid, 0); + } } compiler_server; diff --git a/modules/compiler_vm/compiler_vm_server.pl b/modules/compiler_vm/compiler_vm_server.pl index 299a198b..07372ca2 100755 --- a/modules/compiler_vm/compiler_vm_server.pl +++ b/modules/compiler_vm/compiler_vm_server.pl @@ -29,11 +29,12 @@ my %languages = ( ); sub runserver { - my ($input, $output); + my ($input, $output, $heartbeat); if(not defined $USE_LOCAL or $USE_LOCAL == 0) { open($input, '<', "/dev/ttyS0") or die $!; open($output, '>', "/dev/ttyS0") or die $!; + open($heartbeat, '>', "/dev/ttyS1") or die $!; } else { open($input, '<', "/dev/stdin") or die $!; open($output, '>', "/dev/stdout") or die $!; @@ -47,54 +48,65 @@ sub runserver { print "Waiting for input...\n"; - while(my $line = <$input>) { - chomp $line; + my $pid = fork; + die "Fork failed: $!" if not defined $pid; - print "Got [$line]\n"; + if($pid == 0) { + while(my $line = <$input>) { + chomp $line; - if($line =~ m/^compile:\s*end/) { - next if not defined $lang or not defined $code; + print "Got [$line]\n"; - print "Attempting compile [$lang] ...\n"; - - my $result = interpret($lang, $code, $user_args, $user_input, $date); - - print "Done compiling; result: [$result]\n"; - print $output "result:$result\n"; - print $output "result:end\n"; + if($line =~ m/^compile:\s*end/) { + next if not defined $lang or not defined $code; - system("rm prog"); + print "Attempting compile [$lang] ...\n"; - if(not defined $USE_LOCAL or $USE_LOCAL == 0) { - print "input: "; - next; - } else { - exit; + my $result = interpret($lang, $code, $user_args, $user_input, $date); + + print "Done compiling; result: [$result]\n"; + print $output "result:$result\n"; + print $output "result:end\n"; + + #system("rm prog"); + + if(not defined $USE_LOCAL or $USE_LOCAL == 0) { + print "input: "; + next; + } else { + exit; + } } + + if($line =~ m/^compile:\s*(.*)/) { + my $options = $1; + $user_args = undef; + $user_input = undef; + $lang = undef; + + ($lang, $user_args, $user_input, $date) = split /:/, $options; + + $code = ""; + $lang = "C11" if not defined $lang; + $user_args = "" if not defined $user_args; + $user_input = "" if not defined $user_input; + + print "Setting lang [$lang]; [$user_args]; [$user_input]; [$date]\n"; + next; + } + + $code .= $line . "\n"; } - - if($line =~ m/^compile:\s*(.*)/) { - my $options = $1; - $user_args = undef; - $user_input = undef; - $lang = undef; - - ($lang, $user_args, $user_input, $date) = split /:/, $options; - - $code = ""; - $lang = "C11" if not defined $lang; - $user_args = "" if not defined $user_args; - $user_input = "" if not defined $user_input; - - print "Setting lang [$lang]; [$user_args]; [$user_input]; [$date]\n"; - next; + } else { + while(1) { + print $heartbeat "\n"; + sleep 1; } - - $code .= $line . "\n"; } close $input; close $output; + close $heartbeat; } sub interpret { @@ -108,6 +120,8 @@ sub interpret { return "No support for language '$lang' at this time.\n"; } + system("chmod -R 755 /home/compiler"); + open(my $fh, '>', $languages{$lang}{'file'}) or die $!; print $fh $code . "\n"; close $fh; @@ -149,6 +163,7 @@ sub interpret { $output = "[$result]\n"; } + print "Executing gdb\n"; my $user_input_quoted = quotemeta $user_input; ($ret, $result) = execute(60, "bash -c 'date -s \@$date; ulimit -t 1; compiler_watchdog.pl $user_input_quoted > .output'");