mirror of
https://github.com/pragma-/pbot.git
synced 2024-12-23 11:12:42 +01:00
Refactoring virtual machine (2/3)
This commit is contained in:
parent
07dff29d4a
commit
f460088331
@ -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'}\"");
|
||||
|
@ -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__(""); }
|
||||
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__)
|
||||
|
@ -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->{'vm-port'} //= $ENV{PBOT_VM_PORT} // SERIAL_PORT;
|
||||
|
||||
# override vm-port with environment variable
|
||||
if ($ENV{PBOT_VM_PORT}) {
|
||||
|
@ -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";
|
||||
|
@ -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!';
|
||||
|
@ -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>
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -1 +0,0 @@
|
||||
telnet localhost 4445
|
@ -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
|
Binary file not shown.
@ -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
|
@ -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
|
@ -1 +0,0 @@
|
||||
telnet localhost 4444
|
@ -1,3 +0,0 @@
|
||||
umount edit
|
||||
qemu-nbd -d /dev/nbd0
|
||||
killall qemu-nbd
|
Loading…
Reference in New Issue
Block a user