3
0
mirror of https://github.com/pragma-/pbot.git synced 2025-04-01 13:27:10 +02:00

Add heartbeat logic to compiler server to detect when vm is booted (prevents unnecessary vm restarts)

This commit is contained in:
Pragmatic Software 2014-02-23 00:56:51 +00:00
parent 38fb275234
commit 7388bf9cfc
3 changed files with 198 additions and 121 deletions

View File

@ -13,8 +13,8 @@ use warnings;
# These are set automatically by the build/commit script # These are set automatically by the build/commit script
use constant { use constant {
BUILD_NAME => "PBot", BUILD_NAME => "PBot",
BUILD_REVISION => 482, BUILD_REVISION => 483,
BUILD_DATE => "2014-02-21", BUILD_DATE => "2014-02-22",
}; };
1; 1;

View File

@ -5,8 +5,12 @@ use strict;
use IO::Socket; use IO::Socket;
use Net::hostent; 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 { sub server_listen {
my $port = shift @_; my $port = shift @_;
@ -17,7 +21,7 @@ sub server_listen {
Listen => SOMAXCONN, Listen => SOMAXCONN,
Reuse => 1); Reuse => 1);
die "can't setup server" unless $server; die "can't setup server: $!" unless $server;
print "[Server $0 accepting clients]\n"; print "[Server $0 accepting clients]\n";
@ -39,7 +43,7 @@ sub vm_start {
} }
if($pid == 0) { 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; my @command_list = split / /, $command;
exec(@command_list); exec(@command_list);
} else { } else {
@ -51,7 +55,7 @@ sub vm_reset {
use IO::Socket; use IO::Socket;
print "Resetting vm\n"; 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) { if(not defined $sock) {
print "[vm_reset] Unable to connect to monitor: $!\n"; print "[vm_reset] Unable to connect to monitor: $!\n";
return; return;
@ -59,7 +63,7 @@ sub vm_reset {
print $sock "loadvm 1\n"; print $sock "loadvm 1\n";
close $sock; close $sock;
print "Resetted vm\n"; print "Reset vm\n";
} }
sub execute { sub execute {
@ -107,95 +111,153 @@ sub execute {
} }
sub compiler_server { sub compiler_server {
my $vm_pid = vm_start; my ($server, $heartbeat_pid, $heartbeat_monitor);
print "vm started pid: $vm_pid\n";
my $server = server_listen($PORT); my $heartbeat;
my $running;
while (my $client = $server->accept()) { tie $heartbeat, 'IPC::Shareable', 'dat1', { create => 1 };
$client->autoflush(1); tie $running, 'IPC::Shareable', 'dat2', { create => 1 };
my $hostinfo = gethostbyaddr($client->peeraddr);
print '-' x 20, "\n";
printf "[Connect from %s]\n", $client->peerhost;
my $timed_out = 0;
my $killed = 0;
eval { while(1) {
my $lang; $running = 1;
my $nick; $heartbeat = 0;
my $code = "";
local $SIG{ALRM} = sub { die 'Timed-out'; }; my $vm_pid = vm_start;
alarm 5; print "vm started pid: $vm_pid\n";
while (my $line = <$client>) { $heartbeat_pid = fork;
$line =~ s/[\r\n]+$//; die "Fork failed: $!" if not defined $heartbeat_pid;
next if $line =~ m/^\s*$/;
alarm 5;
print "got: [$line]\n";
if($line =~ m/^compile:end$/) { if($heartbeat_pid == 0) {
$code = quotemeta($code); tie $heartbeat, 'IPC::Shareable', 'dat1', { create => 1 };
print "Attemping compile...\n"; tie $running, 'IPC::Shareable', 'dat2', { create => 1 };
alarm 0;
my $tnick = quotemeta($nick);
my $tlang = quotemeta($lang);
my ($ret, $result) = execute("./compiler_vm_client.pl $tnick -lang=$tlang $code"); $heartbeat_monitor = undef;
while(not $heartbeat_monitor) {
if(not defined $ret) { print "Connecting to heartbeat ...";
#print "parent continued\n"; $heartbeat_monitor = IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => $HEARTBEAT_PORT, Proto => 'tcp', Type => SOCK_STREAM);
print "parent continued [$result]\n"; if(not $heartbeat_monitor) {
$timed_out = 1 if $result == 243; # -13 == 243 print " failed.\n";
$killed = 1 if $result == 242; # -14 = 242 sleep 2;
last; } else {
} print " success!\n";
$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;
} }
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 "parent: running: $running\n";
print "stopping vm $vm_pid\n"; while ($running and my $client = $server->accept()) {
vm_stop $vm_pid; $client->autoflush(1);
$vm_pid = vm_start; my $hostinfo = gethostbyaddr($client->peeraddr);
print "new vm pid: $vm_pid\n"; 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);
} }
} }

View File

@ -29,11 +29,12 @@ my %languages = (
); );
sub runserver { sub runserver {
my ($input, $output); my ($input, $output, $heartbeat);
if(not defined $USE_LOCAL or $USE_LOCAL == 0) { if(not defined $USE_LOCAL or $USE_LOCAL == 0) {
open($input, '<', "/dev/ttyS0") or die $!; open($input, '<', "/dev/ttyS0") or die $!;
open($output, '>', "/dev/ttyS0") or die $!; open($output, '>', "/dev/ttyS0") or die $!;
open($heartbeat, '>', "/dev/ttyS1") or die $!;
} else { } else {
open($input, '<', "/dev/stdin") or die $!; open($input, '<', "/dev/stdin") or die $!;
open($output, '>', "/dev/stdout") or die $!; open($output, '>', "/dev/stdout") or die $!;
@ -47,54 +48,65 @@ sub runserver {
print "Waiting for input...\n"; print "Waiting for input...\n";
while(my $line = <$input>) { my $pid = fork;
chomp $line; 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/) { print "Got [$line]\n";
next if not defined $lang or not defined $code;
print "Attempting compile [$lang] ...\n"; if($line =~ m/^compile:\s*end/) {
next if not defined $lang or not defined $code;
my $result = interpret($lang, $code, $user_args, $user_input, $date); print "Attempting compile [$lang] ...\n";
print "Done compiling; result: [$result]\n"; my $result = interpret($lang, $code, $user_args, $user_input, $date);
print $output "result:$result\n";
print $output "result:end\n";
system("rm prog"); print "Done compiling; result: [$result]\n";
print $output "result:$result\n";
print $output "result:end\n";
if(not defined $USE_LOCAL or $USE_LOCAL == 0) { #system("rm prog");
print "input: ";
next; if(not defined $USE_LOCAL or $USE_LOCAL == 0) {
} else { print "input: ";
exit; 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";
} }
} else {
if($line =~ m/^compile:\s*(.*)/) { while(1) {
my $options = $1; print $heartbeat "\n";
$user_args = undef; sleep 1;
$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";
} }
close $input; close $input;
close $output; close $output;
close $heartbeat;
} }
sub interpret { sub interpret {
@ -108,6 +120,8 @@ sub interpret {
return "No support for language '$lang' at this time.\n"; return "No support for language '$lang' at this time.\n";
} }
system("chmod -R 755 /home/compiler");
open(my $fh, '>', $languages{$lang}{'file'}) or die $!; open(my $fh, '>', $languages{$lang}{'file'}) or die $!;
print $fh $code . "\n"; print $fh $code . "\n";
close $fh; close $fh;
@ -149,6 +163,7 @@ sub interpret {
$output = "[$result]\n"; $output = "[$result]\n";
} }
print "Executing gdb\n";
my $user_input_quoted = quotemeta $user_input; 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'"); ($ret, $result) = execute(60, "bash -c 'date -s \@$date; ulimit -t 1; compiler_watchdog.pl $user_input_quoted > .output'");