Refactoring virtual machine (2/3)

This commit is contained in:
Pragmatic Software 2022-01-29 12:22:48 -08:00
parent 07dff29d4a
commit f460088331
16 changed files with 36 additions and 1084 deletions

View File

@ -151,6 +151,9 @@ sub run_server {
my $result = interpret(%$compile_in);
$GID = 0;
$UID = 0;
my $compile_out = { result => $result };
my $json = encode_json($compile_out);
@ -158,9 +161,6 @@ sub run_server {
print $output "result:$json\n";
print $output "result:end\n";
$( = 0;
$< = 0;
if ($compile_in->{'persist-key'}) {
system("id");
system("cp -R -p \"/home/$USERNAME/$compile_in->{'persist-key'}\" \"/root/factdata/$compile_in->{'persist-key'}\"");

View File

@ -87,10 +87,11 @@ __attribute__ (( constructor )) static void printf_binary_register(void)
register_printf_specifier('b', printf_binary_handler, printf_binary_arginfo);
}
void gdb() { __asm__(""); }
#define dump(...) gdb("print " #__VA_ARGS__)
#define print(...) gdb("print " #__VA_ARGS__)
#define ptype(...) gdb("ptype " #__VA_ARGS__)
#define trace(...) gdb("break " #__VA_ARGS__)
#define watch(...) gdb("watch " #__VA_ARGS__)
void gdb(char *cmd) { __asm__(""); }
#define dump(...) gdb("print " #__VA_ARGS__)
#define print(...) gdb("print " #__VA_ARGS__)
#define ptype(...) gdb("ptype " #__VA_ARGS__)
#define whatis(...) gdb("whatis " #__VA_ARGS__)
#define trace(...) gdb("break " #__VA_ARGS__)
#define watch(...) gdb("watch " #__VA_ARGS__)
#define print_last_statement(...) gdb("print_last_statement " #__VA_ARGS__)

View File

@ -20,6 +20,10 @@ use JSON::XS;
use FindBin qw($RealBin);
use lib "$RealBin/../lib";
use constant {
SERIAL_PORT => 5555,
};
my $json = join ' ', @ARGV;
my $args = eval { decode_json $json };
@ -34,13 +38,14 @@ if ($@) {
}
if (not exists $args->{code}) {
die "Missing `code` field. Usage: $0 {'code':'<code>'}\n";
die "Usage: $0 <code>\n";
}
# set any missing fields to default values
$args->{nick} //= 'vm';
$args->{channel} //= 'vm';
$args->{lang} //= 'c11';
$args->{nick} //= 'vm';
$args->{channel} //= 'vm';
$args->{lang} //= 'c11';
$args->{'vm-port'} //= $ENV{PBOT_VM_PORT} // SERIAL_PORT;
# override vm-port with environment variable
if ($ENV{PBOT_VM_PORT}) {

View File

@ -18,26 +18,25 @@ use IPC::Shareable;
use Time::HiRes qw/gettimeofday/;
use Encode;
my $SERVER_PORT = 9000;
my $SERIAL_PORT = 5555;
my $HEARTBEAT_PORT = 5556;
my $DOMAIN_NAME = 'pbot-vm';
my $COMPILE_TIMEOUT = 15;
use constant {
SERVER_PORT => 9000,
SERIAL_PORT => 5555,
HEARTBEAT_PORT => 5556,
DOMAIN_NAME => 'pbot-vm',
COMPILE_TIMEOUT => 10,
};
sub vm_stop {
system("virsh shutdown $DOMAIN_NAME");
system('virsh shutdown ' . DOMAIN_NAME);
}
sub vm_start {
system("virsh start $DOMAIN_NAME");
system('virsh start ' . DOMAIN_NAME);
}
sub vm_reset {
return if $ENV{NORESET};
#system("virsh detach-disk $DOMAIN_NAME vdb");
system("virsh snapshot-revert $DOMAIN_NAME 1");
#system("virsh attach-disk $DOMAIN_NAME --source /var/lib/libvirt/images/factdata.qcow2 --target vdb");
system('virsh snapshot-revert '.DOMAIN_NAME.' 1');
print "Reset vm\n";
}
@ -64,7 +63,7 @@ sub execute {
}
local $SIG{ALRM} = sub { kill 9, $pid; die "Timed-out: $result\n"; };
alarm($COMPILE_TIMEOUT);
alarm(COMPILE_TIMEOUT);
print "Reading result...\n";
while (my $line = <$fh>) {
@ -105,7 +104,7 @@ sub connect_to_heartbeat {
$heartbeat = IO::Socket::INET->new (
PeerAddr => '127.0.0.1',
PeerPort => $HEARTBEAT_PORT,
PeerPort => HEARTBEAT_PORT,
Proto => 'tcp',
Type => SOCK_STREAM,
);
@ -183,10 +182,10 @@ sub vm_server {
# server
if (not defined $server) {
print "Starting compiler server on port $SERVER_PORT\n";
$server = server_listen($SERVER_PORT);
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";
print "Compiler server already listening on port " . SERVER_PORT . "\n";
}
print "parent: running: $running\n";

View File

@ -23,8 +23,6 @@ use Encode;
use FindBin qw($RealBin);
my $EXECUTE_PORT = '3333';
sub new {
my ($class, %conf) = @_;
my $self = bless {}, $class;
@ -35,10 +33,10 @@ sub new {
$self->{lang} = $conf{lang};
$self->{code} = $conf{code};
$self->{max_history} = $conf{max_history} // 10000;
$self->{arguments} = $conf{arguments};
$self->{arguments} = $conf{arguments} // '';
$self->{factoid} = $conf{factoid};
$self->{'persist-key'} = $conf{'persist-key'};
$self->{'vm-port'} = $conf{'vm-port'} // $EXECUTE_PORT;
$self->{'vm-port'} = $conf{'vm-port'};
$self->{default_options} = '';
$self->{cmdline} = 'echo Hello, world!';

View File

@ -1,182 +0,0 @@
<domain type='kvm' id='2'>
<name>compiler</name>
<uuid>a0955f41-1fb8-48b5-93ea-76319ad07c2f</uuid>
<memory unit='KiB'>1048576</memory>
<currentMemory unit='KiB'>1048576</currentMemory>
<vcpu placement='static'>4</vcpu>
<iothreads>1</iothreads>
<resource>
<partition>/machine</partition>
</resource>
<os>
<type arch='x86_64' machine='pc-i440fx-cosmic'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
<hyperv>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
</hyperv>
<kvm>
<hidden state='on'/>
</kvm>
<vmport state='off'/>
</features>
<cpu mode='host-passthrough' check='full'>
<topology sockets='1' cores='2' threads='2'/>
<cache mode='passthrough'/>
</cpu>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
</clock>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<pm>
<suspend-to-mem enabled='no'/>
<suspend-to-disk enabled='no'/>
</pm>
<devices>
<emulator>/usr/bin/kvm-spice</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/var/lib/libvirt/images/compiler.qcow2'/>
<backingStore/>
<target dev='vda' bus='virtio'/>
<alias name='virtio-disk0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</disk>
<disk type='file' device='cdrom'>
<driver name='qemu'/>
<target dev='hda' bus='ide'/>
<readonly/>
<alias name='ide0-0-0'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<controller type='usb' index='0' model='ich9-ehci1'>
<alias name='usb'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x7'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci1'>
<alias name='usb'/>
<master startport='0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0' multifunction='on'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci2'>
<alias name='usb'/>
<master startport='2'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x1'/>
</controller>
<controller type='usb' index='0' model='ich9-uhci3'>
<alias name='usb'/>
<master startport='4'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x2'/>
</controller>
<controller type='ide' index='0'>
<alias name='ide'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
</controller>
<controller type='virtio-serial' index='0'>
<alias name='virtio-serial0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
</controller>
<controller type='pci' index='0' model='pci-root'>
<alias name='pci.0'/>
</controller>
<interface type='network'>
<mac address='52:54:00:f3:c6:38'/>
<source network='default' bridge='virbr0'/>
<target dev='vnet0'/>
<model type='virtio'/>
<alias name='net0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
<serial type='tcp'>
<source mode='bind' host='127.0.0.1' service='3333' tls='no'/>
<protocol type='telnet'/>
<target type='isa-serial' port='0'>
<model name='isa-serial'/>
</target>
<alias name='serial0'/>
</serial>
<serial type='tcp'>
<source mode='bind' host='127.0.0.1' service='3336' tls='no'/>
<protocol type='telnet'/>
<target type='isa-serial' port='1'>
<model name='isa-serial'/>
</target>
<alias name='serial1'/>
</serial>
<console type='tcp'>
<source mode='bind' host='127.0.0.1' service='3333' tls='no'/>
<protocol type='telnet'/>
<target type='serial' port='0'/>
<alias name='serial0'/>
</console>
<channel type='unix'>
<source mode='bind' path='/var/lib/libvirt/qemu/channel/target/domain-2-compiler/org.qemu.guest_agent.0'/>
<target type='virtio' name='org.qemu.guest_agent.0' state='connected'/>
<alias name='channel0'/>
<address type='virtio-serial' controller='0' bus='0' port='1'/>
</channel>
<channel type='spicevmc'>
<target type='virtio' name='com.redhat.spice.0' state='disconnected'/>
<alias name='channel1'/>
<address type='virtio-serial' controller='0' bus='0' port='2'/>
</channel>
<input type='tablet' bus='usb'>
<alias name='input0'/>
<address type='usb' bus='0' port='1'/>
</input>
<input type='mouse' bus='ps2'>
<alias name='input1'/>
</input>
<input type='keyboard' bus='ps2'>
<alias name='input2'/>
</input>
<graphics type='spice' port='5900' autoport='yes' listen='127.0.0.1'>
<listen type='address' address='127.0.0.1'/>
<image compression='off'/>
</graphics>
<sound model='ich6'>
<alias name='sound0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</sound>
<video>
<model type='cirrus' vram='16384' heads='1' primary='yes'/>
<alias name='video0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
</video>
<redirdev bus='usb' type='spicevmc'>
<alias name='redir0'/>
<address type='usb' bus='0' port='2'/>
</redirdev>
<redirdev bus='usb' type='spicevmc'>
<alias name='redir1'/>
<address type='usb' bus='0' port='3'/>
</redirdev>
<memballoon model='virtio'>
<alias name='balloon0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/>
</memballoon>
<rng model='virtio'>
<backend model='random'>/dev/urandom</backend>
<alias name='rng0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>
</rng>
</devices>
<seclabel type='dynamic' model='apparmor' relabel='yes'>
<label>libvirt-a0955f41-1fb8-48b5-93ea-76319ad07c2f</label>
<imagelabel>libvirt-a0955f41-1fb8-48b5-93ea-76319ad07c2f</imagelabel>
</seclabel>
<seclabel type='dynamic' model='dac' relabel='yes'>
<label>+64055:+130</label>
<imagelabel>+64055:+130</imagelabel>
</seclabel>
</domain>

View File

@ -1,304 +0,0 @@
#!/usr/bin/perl
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
use warnings;
use strict;
use IO::Socket;
use Net::hostent;
use IPC::Shareable;
use Time::HiRes qw/gettimeofday/;
my $SERVER_PORT = 9000;
my $MONITOR_PORT = 3335;
my $SERIAL_PORT = 3333;
my $HEARTBEAT_PORT = 3336;
my $COMPILE_TIMEOUT = 10;
my $NOGRAPHIC = 1;
sub server_listen {
my $port = shift @_;
my $server = IO::Socket::INET->new(
Proto => 'tcp',
LocalPort => $port,
Listen => SOMAXCONN,
Reuse => 1);
die "can't setup server: $!" unless $server;
print "[Server $0 accepting clients]\n";
return $server;
}
sub vm_stop {
my $pid = shift @_;
return if not defined $pid;
print "killing vm $pid\n";
kill 'INT', $pid;
waitpid($pid, 0);
}
sub vm_start {
my $pid = fork;
if(not defined $pid) {
die "fork failed: $!";
}
if($pid == 0) {
my $command = "qemu-system-x86_64 -M pc -net none -hda compiler-snap.qcow2 -m 512 -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 -enable-kvm -loadvm 1" . ($NOGRAPHIC ? " -nographic" : "");
my @command_list = split / /, $command;
exec(@command_list);
} else {
return $pid;
}
}
sub vm_reset {
use IO::Socket;
print "Resetting vm\n";
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;
}
print $sock "loadvm 1\n";
close $sock;
print "Reset vm\n";
}
sub execute {
my ($cmdline) = @_;
print "execute($cmdline)\n";
my ($ret, $result);
my $child = fork;
if($child == 0) {
($ret, $result) = eval {
my $result = '';
my $pid = open(my $fh, '-|', "$cmdline 2>&1");
local $SIG{ALRM} = sub { print "Time out\n"; kill 9, $pid; print "sent KILL to $pid\n"; die "Timed-out: $result\n"; };
alarm($COMPILE_TIMEOUT);
while(my $line = <$fh>) {
$result .= $line;
}
close $fh;
my $ret = $? >> 8;
alarm 0;
#print "[$ret, $result]\n";
return ($ret, $result);
};
alarm 0;
if($@ =~ /Timed-out: (.*)/) {
return (-13, "[Timed-out] $1");
}
return ($ret, $result);
} else {
waitpid($child, 0);
my $result = $? >> 8;
print "child exited, parent continuing [result = $result]\n";
return (undef, $result);
}
}
sub compiler_server {
my ($server, $heartbeat_pid, $heartbeat_monitor);
my $heartbeat;
my $running;
tie $heartbeat, 'IPC::Shareable', 'dat1', { create => 1 };
tie $running, 'IPC::Shareable', 'dat2', { create => 1 };
my $last_wait = 0;
while(1) {
$running = 1;
$heartbeat = 0;
my $vm_pid = vm_start;
print "vm started pid: $vm_pid\n";
$heartbeat_pid = fork;
die "Fork failed: $!" if not defined $heartbeat_pid;
if($heartbeat_pid == 0) {
tie $heartbeat, 'IPC::Shareable', 'dat1', { create => 1 };
tie $running, 'IPC::Shareable', 'dat2', { create => 1 };
$heartbeat_monitor = undef;
my $attempts = 0;
while((not $heartbeat_monitor) and $attempts < 5) {
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";
++$attempts;
sleep 2;
} else {
print " success!\n";
}
}
if ($attempts >= 5) {
print "heart not beating... restarting\n";
$heartbeat = -1;
sleep 5;
next;
}
print "child: running: $running\n";
while($running and <$heartbeat_monitor>) {
$heartbeat = 1;
#print "child: got heartbeat\n";
}
print "child no longer running\n";
exit;
} else {
while ($heartbeat <= 0) {
if ($heartbeat == -1) {
print "heartbeat died\n";
last;
}
print "sleeping for heartbeat...\n";
sleep 1;
}
if ($heartbeat == -1) {
print "fucking dead, restarting\n";
waitpid $heartbeat_pid, 0;
vm_stop $vm_pid;
next;
}
print "K, got heartbeat, here we go...\n";
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";
}
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 $channel;
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" if gettimeofday - $last_wait > 60;
$last_wait = gettimeofday;
last;
}
print "Attempting compile...\n";
alarm 0;
my ($ret, $result) = execute("./compiler_vm_client.pl \Q$lang\E \Q$nick\E \Q$channel\E \Q$code\E");
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;
$channel = $2;
$lang = $3;
$code = "";
next;
}
$code .= $line . "\n";
}
alarm 0;
};
alarm 0;
close $client;
#next unless ($timed_out or $killed);
#next unless $timed_out;
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;

View File

@ -1,280 +0,0 @@
#!/usr/bin/perl
use warnings;
use strict;
use IO::Socket;
use Net::hostent;
#use IPC::Shareable;
use Win32::MMF::Shareable;
my $fh = select STDOUT;
$| = 1;
select $fh;
my $SERVER_PORT = 9000;
my $MONITOR_PORT = 3335;
my $SERIAL_PORT = 3333;
my $HEARTBEAT_PORT = 3336;
my $COMPILE_TIMEOUT = 10;
my $NOGRAPHIC = 0;
sub server_listen {
my $port = shift @_;
my $server = IO::Socket::INET->new(
Proto => 'tcp',
LocalPort => $port,
Listen => SOMAXCONN,
Reuse => 1);
die "can't setup server: $!" unless $server;
print "[Server $0 accepting clients]\n";
return $server;
}
sub vm_stop {
my $pid = shift @_;
return if not defined $pid;
kill 9, $pid;
waitpid($pid, 0);
}
sub vm_start {
my $pid = fork;
if(not defined $pid) {
die "fork failed: $!";
}
if($pid == 0) {
print "\nStarting qemu\n";
my $command = "/cygdrive/e/Downloads/qemu-1.5.0-win32-sdl.tar/qemu-1.5.0-win32-sdl/qemu-system-x86_64.exe -net none -hda c-snap.img -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" . ($NOGRAPHIC ? " -nographic" : "");
my @command_list = split / /, $command;
exec(@command_list);
} else {
return $pid;
}
}
sub vm_reset {
use IO::Socket;
print "Resetting vm\n";
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;
}
print $sock "loadvm 1\n";
close $sock;
print "Reset vm\n";
}
sub execute {
my ($cmdline) = @_;
print "execute($cmdline)\n";
my ($ret, $result);
my $child = fork;
if($child == 0) {
($ret, $result) = eval {
my $result = '';
my $pid = open(my $fh, '-|', "$cmdline 2>&1");
local $SIG{ALRM} = sub { print "Time out\n"; kill 9, $pid; die "Timed-out: $result\n"; };
alarm($COMPILE_TIMEOUT);
while(my $line = <$fh>) {
$result .= $line;
}
close $fh;
my $ret = $? >> 8;
alarm 0;
#print "[$ret, $result]\n";
return ($ret, $result);
};
alarm 0;
if($@ =~ /Timed-out: (.*)/) {
return (-13, "[Timed-out] $1");
}
return ($ret, $result);
} else {
waitpid($child, 0);
my $result = $? >> 8;
print "child exited, parent continuing [result = $result]\n";
return (undef, $result);
}
}
sub compiler_server {
my ($server, $heartbeat_pid, $heartbeat_monitor);
while(1) {
my $vm_pid = vm_start;
print "vm started pid: $vm_pid\n";
$heartbeat_pid = fork;
die "Fork failed: $!" if not defined $heartbeat_pid;
if($heartbeat_pid == 0) {
tie my $heartbeat, 'Win32::MMF::Shareable', 'dat1';
tie my $running, 'Win32::MMF::Shareable', 'dat2';
print "in child: running: " . (defined $running ? $running : "undefined"). "\n";
while(not defined $running) {
print "Child waiting for running status\n";
sleep 1;
}
$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";
}
}
print "child: running: $running\n";
while($running and <$heartbeat_monitor>) {
$heartbeat = 1;
print ".";
}
$heartbeat_monitor->shutdown(2);
$heartbeat = 0;
print "child no longer running\n";
exit;
} else {
tie my $heartbeat, 'Win32::MMF::Shareable', 'dat1';
tie my $running, 'Win32::MMF::Shareable', 'dat2';
$running = 1;
$heartbeat = 0;
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";
}
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 $channel;
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;
}
print "Attempting compile...\n";
alarm 0;
my ($ret, $result) = execute("./compiler_vm_client.pl \Q$nick\E \Q$channel\E -lang=\Q$lang\E \Q$code\E");
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;
$channel = $2;
$lang = $3;
$code = "";
next;
}
$code .= $line . "\n";
}
alarm 0;
};
alarm 0;
close $client;
next unless ($timed_out);
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;

View File

@ -1,274 +0,0 @@
#!/usr/bin/perl
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
use warnings;
use strict;
use IO::Select;
use IO::Socket;
use Net::hostent;
use Win32::MMF;
my $fh = select STDOUT;
$| = 1;
select $fh;
my $VBOX = '/cygdrive/e/VirtualBox/VBoxManage';
my $SERVER_PORT = 9000;
my $SERIAL_PORT = 3333;
my $HEARTBEAT_PORT = 3336;
my $COMPILE_TIMEOUT = 5;
my $NOGRAPHIC = 0;
$SIG{INT} = sub { vm_stop(); exit 1; };
sub server_listen {
my $port = shift @_;
my $server = IO::Socket::INET->new(
Proto => 'tcp',
LocalPort => $port,
Listen => SOMAXCONN,
Reuse => 1);
die "can't setup server: $!" unless $server;
print "[Server $0 accepting clients]\n";
return $server;
}
sub vm_stop {
system("$VBOX controlvm compiler poweroff");
sleep 2;
}
sub vm_start {
print "\nStarting vbox\n";
system("$VBOX snapshot compiler restore compiler");
sleep 2;
system("$VBOX startvm compiler" . ($NOGRAPHIC ? " --type headless" : ""));
}
sub execute {
my ($cmdline) = @_;
print "execute($cmdline)\n";
my ($ret, $result);
my $child = fork;
if($child == 0) {
($ret, $result) = eval {
my $result = '';
my $pid = open(my $fh, '-|', "$cmdline 2>&1");
local $SIG{ALRM} = sub { print "Time out\n"; kill 'INT', $pid; die "Timed-out: $result\n"; };
alarm($COMPILE_TIMEOUT);
while(my $line = <$fh>) {
$result .= $line;
}
close $fh;
my $ret = $? >> 8;
alarm 0;
return ($ret, $result);
};
alarm 0;
if($@ =~ /Timed-out: (.*)/) {
return (-13, "[Timed-out] $1");
}
return ($ret, $result);
} else {
waitpid($child, 0);
my $result = $? >> 8;
print "child exited, parent continuing [result = $result]\n";
return (undef, $result);
}
}
sub compiler_server {
my ($server, $heartbeat_pid, $heartbeat_monitor);
while(1) {
vm_start;
print "vm started\n";
$heartbeat_pid = fork;
die "Fork failed: $!" if not defined $heartbeat_pid;
if($heartbeat_pid == 0) {
my $ns = Win32::MMF->new();
while(not $ns->findvar('running')) {
print "Child waiting for running status\n";
sleep 1;
}
$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";
}
}
my $select = IO::Select->new();
$select->add($heartbeat_monitor);
while($ns->getvar('running')) {
my @ready = $select->can_read(1);
foreach my $fh (@ready) {
my $ret = sysread($fh, my $buf, 32);
if(not defined $ret) {
print "Heartbeat read error: $!\n";
$ns->setvar('running', 0);
}
if($ret == 0) {
print "Heartbeat disconnected.\n";
$ns->setvar('running', 0);
}
$ns->setvar('heartbeat', 1);
print ".";
}
}
$heartbeat_monitor->shutdown(3);
$ns->deletevar('heartbeat');
$ns->deletevar('running');
print "child no longer running\n";
exit;
} else {
print "Heartbeat pid: $heartbeat_pid\n";
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";
}
my $ns = Win32::MMF->new();
$ns->setvar('running', 1);
$ns->setvar('heartbeat', 0);
while ($ns->getvar('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 $channel;
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(not $ns->getvar('heartbeat')) {
print "No heartbeat yet, ignoring compile attempt.\n";
print $client "$nick: Recovering from previous snippet, please wait.\n";
last;
}
print "Attempting compile...\n";
alarm 0;
my ($ret, $result) = execute("./compiler_vm_client.pl \Q$nick\E \Q$channel\E -lang=\Q$lang\E \Q$code\E");
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
$client->shutdown(3);
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";
$client->shutdown(3);
$ret = -14 if $killed;
# child exit
print "child exit\n";
exit $ret;
}
if($line =~ /compile:([^:]+):([^:]+):(.*)$/) {
$nick = $1;
$channel = $2;
$lang = $3;
$code = "";
next;
}
$code .= $line . "\n";
}
alarm 0;
};
alarm 0;
$client->shutdown(3);
next unless ($timed_out);
$server->shutdown(3);
undef $server;
print "stopping vm\n";
$ns->setvar('running', 0);
vm_stop;
last;
}
print "Compiler server no longer running, restarting...\n";
}
print "Waiting for heartbeat $heartbeat_pid to die\n";
waitpid($heartbeat_pid, 0);
print "Heartbeat dead.\n";
}
}
compiler_server;

View File

@ -1 +0,0 @@
telnet localhost 4445

View File

@ -1,4 +0,0 @@
modprobe nbd max_part=16
qemu-nbd -c /dev/nbd0 compiler-savedvm-edit.qcow2
partprobe /dev/nbd0
mount /dev/nbd0p1 edit

View File

@ -1 +0,0 @@
qemu-system-x86_64 -M pc -hda /home/compiler/compiler-vm-image -m 128 -monitor "tcp:127.0.0.1:4445,server,nowait" -serial "tcp:127.0.0.1:4444,server,nowait" -boot c -loadvm 2

View File

@ -1 +0,0 @@
qemu-system-x86_64 -M pc -hda /home/compiler/compiler-vm-image -m 128 -monitor "tcp:127.0.0.1:4445,server,nowait" -serial "tcp:127.0.0.1:4444,server,nowait" -net nic,vlan=0 -net user,vlan=0,hostname=compiler -boot c -loadvm 2

View File

@ -1 +0,0 @@
telnet localhost 4444

View File

@ -1,3 +0,0 @@
umount edit
qemu-nbd -d /dev/nbd0
killall qemu-nbd