mirror of
https://github.com/pragma-/pbot.git
synced 2024-11-26 22:09:26 +01:00
compiler_vm: added functionality to be executed without qemu, via cc script; added gdb debugging to watchdog
This commit is contained in:
parent
cd8791a854
commit
0b8216e13c
@ -1,22 +1,49 @@
|
|||||||
Installation:
|
Installation:
|
||||||
|
|
||||||
|
If you want to run the compiler inside a virtual machine for more security,
|
||||||
|
these scripts are designed to work with qemu 0.11.1. This is required in
|
||||||
|
order to use PBot's trigger.
|
||||||
|
|
||||||
|
In addition, you can use the provided 'cc' script to use your local compiler tools
|
||||||
|
without installing qemu. No PBot installation or configuration is required in this case.
|
||||||
|
|
||||||
|
To use the local non-vm 'cc' script, you will need to have gcc, gdb and astyle installed locally.
|
||||||
|
|
||||||
|
Be aware that you need to single-quote or escape the code if you use the local 'cc' within a shell,
|
||||||
|
e.g.: ./cc 'char s[] = "hello, world"; puts(s); if(s[0] == \'h\') puts("true");'
|
||||||
|
|
||||||
|
WARNING: Using the local 'cc' script outside of the virtual machine will not use qemu at all;
|
||||||
|
it will affect local system -- compile "safe" code!
|
||||||
|
|
||||||
|
Virtual machine installation:
|
||||||
|
|
||||||
You will need to download qemu and set up a virtual machine containing a system
|
You will need to download qemu and set up a virtual machine containing a system
|
||||||
with a compiler and, optionally, sensible ulimits/fork-preventation/other security.
|
with a compiler and, optionally, sensible ulimits/fork-preventation/other security.
|
||||||
|
|
||||||
Then you will need to edit some of the following files and replace IP addresses
|
1) copy compiler_vm_server.pl and compiler_watchdog.pl to the virtual machine.
|
||||||
and paths where appropriate (some locations may not yet be documented).
|
2) then start up the compiler_vm_server.pl script inside the virtual machine
|
||||||
|
3) then connect to qemu's monitor and issue the 'savevm 1' command to save the virtual state
|
||||||
|
(After compiles, this state will be loaded via 'loadvm 1' to reset everything within the machine
|
||||||
|
for a clean and working environment for subsequent compiles.)
|
||||||
|
|
||||||
Once the files have been configured and copied to the appropriate locations,
|
Now the virtual machine state 1 is saved in a state where it is listening for incoming code. You can
|
||||||
you will need to first start up the compiler_vm_server.pl script, and then
|
go ahead and quit qemu without shutting down the guest operating sytem.
|
||||||
connect to qemu's monitor and issue the 'savevm 1' command to save the virtual
|
|
||||||
machine in this state. Between compiles, this state will be loaded via
|
Starting the virtual machine for PBot:
|
||||||
'loadvm 1' in order to reset everything within the machine for a clean and working
|
|
||||||
environment for subsequent compiles.
|
Now that the virtual machine is configured and saved, you may launch the local server to listen for
|
||||||
|
code from PBot to send to the virtual machine's server. To do so, run the compiler_server.pl script.
|
||||||
|
|
||||||
Files:
|
Files:
|
||||||
|
|
||||||
(Read within each file for configuration instructions.)
|
(Read within each file for configuration instructions.)
|
||||||
|
|
||||||
|
- cc: Allows you to use the compiler locally with or without qemu installed.
|
||||||
|
Can be used within virtual machine for testing.
|
||||||
|
Must have gcc, gdb and astyle installed locally if not used within virtual machine.
|
||||||
|
WARNING: If not used within virtual machine will not use qemu at all and will
|
||||||
|
affect local system -- compile "safe" code!
|
||||||
|
|
||||||
- compiler_client.pl: Main entry point for compiling snippets. Sends over TCP to
|
- compiler_client.pl: Main entry point for compiling snippets. Sends over TCP to
|
||||||
compiler_server.pl. This file can be run be run from the
|
compiler_server.pl. This file can be run be run from the
|
||||||
client machine or anywhere.
|
client machine or anywhere.
|
||||||
|
@ -9,6 +9,7 @@ use Text::Balanced qw(extract_codeblock extract_delimited);
|
|||||||
use IO::Socket;
|
use IO::Socket;
|
||||||
use LWP::UserAgent;
|
use LWP::UserAgent;
|
||||||
|
|
||||||
|
my $USE_LOCAL = defined $ENV{'CC_LOCAL'};
|
||||||
my $MAX_UNDO_HISTORY = 100;
|
my $MAX_UNDO_HISTORY = 100;
|
||||||
|
|
||||||
my $output = "";
|
my $output = "";
|
||||||
@ -28,19 +29,6 @@ my %preludes = (
|
|||||||
'C++' => "#include <iostream>\n#include <cstdio>\n\nusing namespace std;\n\n",
|
'C++' => "#include <iostream>\n#include <cstdio>\n\nusing namespace std;\n\n",
|
||||||
);
|
);
|
||||||
|
|
||||||
sub reset_vm {
|
|
||||||
my $sock = IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => '4445', Proto => 'tcp', Type => 'SOCK_STREAM');
|
|
||||||
die "Could not create socket: $!" unless $sock;
|
|
||||||
|
|
||||||
$sock->autoflush();
|
|
||||||
print $sock "loadvm 2\r\n";
|
|
||||||
while(my $line = <$sock>) {
|
|
||||||
last if $line =~ /loadvm 2/;
|
|
||||||
}
|
|
||||||
sleep 2;
|
|
||||||
$sock->shutdown(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub pretty {
|
sub pretty {
|
||||||
my $code = join '', @_;
|
my $code = join '', @_;
|
||||||
my $result;
|
my $result;
|
||||||
@ -76,20 +64,28 @@ sub paste_codepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub compile {
|
sub compile {
|
||||||
my ($lang, $code, $args, $input) = @_;
|
my ($lang, $code, $args, $input, $local) = @_;
|
||||||
|
|
||||||
my $sock = IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => '4444', Proto => 'tcp', Type => 'SOCK_STREAM');
|
my ($compiler, $compiler_output, $pid);
|
||||||
|
|
||||||
die "Could not create socket: $!" unless $sock;
|
if(defined $local and $local != 0) {
|
||||||
|
print "Using local compiler instead of virtual machine\n";
|
||||||
|
$pid = open2($compiler_output, $compiler, './compiler_vm_server.pl') || die "repl failed: $@\n";
|
||||||
|
print "Started compiler, pid: $pid\n";
|
||||||
|
} else {
|
||||||
|
$compiler = IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => '4444', Proto => 'tcp', Type => 'SOCK_STREAM');
|
||||||
|
die "Could not create socket: $!" unless $compiler;
|
||||||
|
$compiler_output = $compiler;
|
||||||
|
}
|
||||||
|
|
||||||
print $sock "compile:$lang:$args:$input\n";
|
print $compiler "compile:$lang:$args:$input\n";
|
||||||
print $sock "$code\n";
|
print $compiler "$code\n";
|
||||||
print $sock "compile:end\n";
|
print $compiler "compile:end\n";
|
||||||
|
|
||||||
my $result = "";
|
my $result = "";
|
||||||
my $got_result = 0;
|
my $got_result = 0;
|
||||||
|
|
||||||
while(my $line = <$sock>) {
|
while(my $line = <$compiler_output>) {
|
||||||
$line =~ s/[\r\n]+$//;
|
$line =~ s/[\r\n]+$//;
|
||||||
|
|
||||||
last if $line =~ /^result:end/;
|
last if $line =~ /^result:end/;
|
||||||
@ -106,7 +102,9 @@ sub compile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close $sock;
|
close $compiler;
|
||||||
|
close $output if defined $output;
|
||||||
|
waitpid($pid, 0) if defined $pid;
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -640,7 +638,7 @@ if(defined $got_run and $got_run eq "paste") {
|
|||||||
|
|
||||||
print FILE "$nick: [lang:$lang][args:$args][input:$input]\n$code\n";
|
print FILE "$nick: [lang:$lang][args:$args][input:$input]\n$code\n";
|
||||||
|
|
||||||
$output = compile($lang, $code, $args, $input);
|
$output = compile($lang, pretty($code), $args, $input, $USE_LOCAL);
|
||||||
|
|
||||||
$output =~ s/cc1: warnings being treated as errors//;
|
$output =~ s/cc1: warnings being treated as errors//;
|
||||||
$output =~ s/ Line \d+ ://g;
|
$output =~ s/ Line \d+ ://g;
|
||||||
@ -653,12 +651,16 @@ $output =~ s/\/tmp\/.*\.o://g;
|
|||||||
$output =~ s/collect2: ld returned \d+ exit status//g;
|
$output =~ s/collect2: ld returned \d+ exit status//g;
|
||||||
$output =~ s/\(\.text\+[^)]+\)://g;
|
$output =~ s/\(\.text\+[^)]+\)://g;
|
||||||
$output =~ s/\[ In/[In/;
|
$output =~ s/\[ In/[In/;
|
||||||
|
$output =~ s/warning: Can't read pathname for load map: Input.output error.//g;
|
||||||
|
|
||||||
my $left_quote = chr(226) . chr(128) . chr(152);
|
my $left_quote = chr(226) . chr(128) . chr(152);
|
||||||
my $right_quote = chr(226) . chr(128) . chr(153);
|
my $right_quote = chr(226) . chr(128) . chr(153);
|
||||||
$output =~ s/$left_quote/'/g;
|
$output =~ s/$left_quote/'/g;
|
||||||
$output =~ s/$right_quote/'/g;
|
$output =~ s/$right_quote/'/g;
|
||||||
|
|
||||||
|
$output =~ s/[\r\n]+/ /g;
|
||||||
|
$output =~ s/\s+/ /g;
|
||||||
|
|
||||||
$output = $nooutput if $output =~ m/^\s+$/;
|
$output = $nooutput if $output =~ m/^\s+$/;
|
||||||
|
|
||||||
unless($got_run) {
|
unless($got_run) {
|
||||||
@ -668,5 +670,3 @@ unless($got_run) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print "$nick: $output\n";
|
print "$nick: $output\n";
|
||||||
|
|
||||||
#reset_vm;
|
|
||||||
|
@ -3,27 +3,36 @@
|
|||||||
use warnings;
|
use warnings;
|
||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
|
my $USE_LOCAL = defined $ENV{'CC_LOCAL'};
|
||||||
|
|
||||||
my %languages = (
|
my %languages = (
|
||||||
'C' => {
|
'C' => {
|
||||||
'cmdline' => 'gcc $args $file -o prog',
|
'cmdline' => 'gcc $args $file -o prog -ggdb',
|
||||||
'args' => '-Wextra -Wall -Wno-unused -std=gnu89',
|
'args' => '-Wextra -Wall -Wno-unused -std=gnu89',
|
||||||
'file' => 'prog.c',
|
'file' => 'prog.c',
|
||||||
},
|
},
|
||||||
'C++' => {
|
'C++' => {
|
||||||
'cmdline' => 'g++ $args $file -o prog',
|
'cmdline' => 'g++ $args $file -o prog -ggdb',
|
||||||
'args' => '',
|
'args' => '',
|
||||||
'file' => 'prog.cpp',
|
'file' => 'prog.cpp',
|
||||||
},
|
},
|
||||||
'C99' => {
|
'C99' => {
|
||||||
'cmdline' => 'gcc $args $file -o prog',
|
'cmdline' => 'gcc $args $file -o prog -ggdb',
|
||||||
'args' => '-Wextra -Wall -Wno-unused -pedantic -std=c99',
|
'args' => '-Wextra -Wall -Wno-unused -pedantic -std=c99 -lm',
|
||||||
'file' => 'prog.c',
|
'file' => 'prog.c',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
sub runserver {
|
sub runserver {
|
||||||
open(my $input, '<', "/dev/ttyS0") or die $!;
|
my ($input, $output);
|
||||||
open(my $output, '>', "/dev/ttyS0") or die $!;
|
|
||||||
|
if(not defined $USE_LOCAL or $USE_LOCAL == 0) {
|
||||||
|
open($input, '<', "/dev/ttyS0") or die $!;
|
||||||
|
open($output, '>', "/dev/ttyS0") or die $!;
|
||||||
|
} else {
|
||||||
|
open($input, '<', "/dev/stdin") or die $!;
|
||||||
|
open($output, '>', "/dev/stdout") or die $!;
|
||||||
|
}
|
||||||
|
|
||||||
my $lang;
|
my $lang;
|
||||||
my $code;
|
my $code;
|
||||||
@ -49,8 +58,12 @@ sub runserver {
|
|||||||
print $output "result:$result\n";
|
print $output "result:$result\n";
|
||||||
print $output "result:end\n";
|
print $output "result:end\n";
|
||||||
|
|
||||||
|
if(not defined $USE_LOCAL or $USE_LOCAL == 0) {
|
||||||
print "input: ";
|
print "input: ";
|
||||||
next;
|
next;
|
||||||
|
} else {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($line =~ m/^compile:\s*(.*)/) {
|
if($line =~ m/^compile:\s*(.*)/) {
|
||||||
@ -107,14 +120,18 @@ sub interpret {
|
|||||||
|
|
||||||
print "Executing [$cmdline]\n";
|
print "Executing [$cmdline]\n";
|
||||||
my ($ret, $result) = execute(60, $cmdline);
|
my ($ret, $result) = execute(60, $cmdline);
|
||||||
print "Got result: ($ret) [$result]\n";
|
# print "Got result: ($ret) [$result]\n";
|
||||||
|
|
||||||
|
# if exit code was not 0, then there was a problem compiling, such as an error diagnostic
|
||||||
|
# so return the compiler output
|
||||||
if($ret != 0) {
|
if($ret != 0) {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $output = "";
|
my $output = "";
|
||||||
|
|
||||||
|
# no errors compiling, but if $result contains something, it must be a warning message
|
||||||
|
# so prepend it to the output
|
||||||
if(length $result) {
|
if(length $result) {
|
||||||
$result =~ s/^\s+//;
|
$result =~ s/^\s+//;
|
||||||
$result =~ s/\s+$//;
|
$result =~ s/\s+$//;
|
||||||
@ -123,11 +140,11 @@ sub interpret {
|
|||||||
|
|
||||||
($ret, $result) = execute(5, "./compiler_watchdog.pl");
|
($ret, $result) = execute(5, "./compiler_watchdog.pl");
|
||||||
|
|
||||||
print "Executed prog; got result: ($ret) [$result]\n";
|
|
||||||
|
|
||||||
$result =~ s/^\s+//;
|
$result =~ s/^\s+//;
|
||||||
$result =~ s/\s+$//;
|
$result =~ s/\s+$//;
|
||||||
|
|
||||||
|
# print "Executed prog; got result: ($ret) [$result]\n";
|
||||||
|
|
||||||
if(not length $result) {
|
if(not length $result) {
|
||||||
$result = "Success (no output).\n" if $ret == 0;
|
$result = "Success (no output).\n" if $ret == 0;
|
||||||
$result = "Success (exit code $ret).\n" if $ret != 0;
|
$result = "Success (exit code $ret).\n" if $ret != 0;
|
||||||
|
@ -4,6 +4,7 @@ use warnings;
|
|||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
use POSIX ":sys_wait_h";
|
use POSIX ":sys_wait_h";
|
||||||
|
use IPC::Open2;
|
||||||
|
|
||||||
my @signame;
|
my @signame;
|
||||||
$signame[0] = 'SIGZERO';
|
$signame[0] = 'SIGZERO';
|
||||||
@ -76,6 +77,39 @@ $signame[66] = 'SIGCLD';
|
|||||||
$signame[67] = 'SIGPOLL';
|
$signame[67] = 'SIGPOLL';
|
||||||
$signame[68] = 'SIGUNUSED';
|
$signame[68] = 'SIGUNUSED';
|
||||||
|
|
||||||
|
sub debug_program {
|
||||||
|
my ($input, $output);
|
||||||
|
|
||||||
|
my $pid = open2($output, $input, 'gdb -silent -batch -x debugcommands ./prog ./core 2>/dev/null');
|
||||||
|
|
||||||
|
if(not $pid) {
|
||||||
|
print "Error debugging program.\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $result = "";
|
||||||
|
|
||||||
|
while(my $line = <$output>) {
|
||||||
|
if($line =~ s/^#\d+//) {
|
||||||
|
$line =~ s/\s*0x[0-9a-fA-F]+\s*//;
|
||||||
|
$result .= "$line ";
|
||||||
|
}
|
||||||
|
elsif($line =~ s/^\d+//) {
|
||||||
|
$result .= "statement: $line";
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close $output;
|
||||||
|
close $input;
|
||||||
|
waitpid($pid, 0);
|
||||||
|
|
||||||
|
$result =~ s/^\s+//;
|
||||||
|
$result =~ s/\s+$//;
|
||||||
|
print "$result\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
sub reaper {
|
sub reaper {
|
||||||
my $child;
|
my $child;
|
||||||
while (($child=waitpid(-1,WNOHANG))>0) {
|
while (($child=waitpid(-1,WNOHANG))>0) {
|
||||||
@ -92,6 +126,7 @@ sub reaper {
|
|||||||
|
|
||||||
if($wifsignaled == 1) {
|
if($wifsignaled == 1) {
|
||||||
print "\nProgram received signal $wtermsig ($signame[$wtermsig])\n";
|
print "\nProgram received signal $wtermsig ($signame[$wtermsig])\n";
|
||||||
|
debug_program if $wtermsig != 0;
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user