mirror of
https://github.com/pragma-/pbot.git
synced 2024-11-22 20:09:43 +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:
|
||||
|
||||
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
|
||||
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
|
||||
and paths where appropriate (some locations may not yet be documented).
|
||||
1) copy compiler_vm_server.pl and compiler_watchdog.pl to the virtual machine.
|
||||
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,
|
||||
you will need to first start up the compiler_vm_server.pl script, and then
|
||||
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
|
||||
'loadvm 1' in order to reset everything within the machine for a clean and working
|
||||
environment for subsequent compiles.
|
||||
Now the virtual machine state 1 is saved in a state where it is listening for incoming code. You can
|
||||
go ahead and quit qemu without shutting down the guest operating sytem.
|
||||
|
||||
Starting the virtual machine for PBot:
|
||||
|
||||
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:
|
||||
|
||||
(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_server.pl. This file can be run be run from the
|
||||
client machine or anywhere.
|
||||
|
@ -9,6 +9,7 @@ use Text::Balanced qw(extract_codeblock extract_delimited);
|
||||
use IO::Socket;
|
||||
use LWP::UserAgent;
|
||||
|
||||
my $USE_LOCAL = defined $ENV{'CC_LOCAL'};
|
||||
my $MAX_UNDO_HISTORY = 100;
|
||||
|
||||
my $output = "";
|
||||
@ -28,19 +29,6 @@ my %preludes = (
|
||||
'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 {
|
||||
my $code = join '', @_;
|
||||
my $result;
|
||||
@ -76,20 +64,28 @@ sub paste_codepad {
|
||||
}
|
||||
|
||||
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 $sock "$code\n";
|
||||
print $sock "compile:end\n";
|
||||
print $compiler "compile:$lang:$args:$input\n";
|
||||
print $compiler "$code\n";
|
||||
print $compiler "compile:end\n";
|
||||
|
||||
my $result = "";
|
||||
my $got_result = 0;
|
||||
|
||||
while(my $line = <$sock>) {
|
||||
while(my $line = <$compiler_output>) {
|
||||
$line =~ s/[\r\n]+$//;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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";
|
||||
|
||||
$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/ Line \d+ ://g;
|
||||
@ -653,12 +651,16 @@ $output =~ s/\/tmp\/.*\.o://g;
|
||||
$output =~ s/collect2: ld returned \d+ exit status//g;
|
||||
$output =~ s/\(\.text\+[^)]+\)://g;
|
||||
$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 $right_quote = chr(226) . chr(128) . chr(153);
|
||||
$output =~ s/$left_quote/'/g;
|
||||
$output =~ s/$right_quote/'/g;
|
||||
|
||||
$output =~ s/[\r\n]+/ /g;
|
||||
$output =~ s/\s+/ /g;
|
||||
|
||||
$output = $nooutput if $output =~ m/^\s+$/;
|
||||
|
||||
unless($got_run) {
|
||||
@ -668,5 +670,3 @@ unless($got_run) {
|
||||
}
|
||||
|
||||
print "$nick: $output\n";
|
||||
|
||||
#reset_vm;
|
||||
|
@ -3,27 +3,36 @@
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
my $USE_LOCAL = defined $ENV{'CC_LOCAL'};
|
||||
|
||||
my %languages = (
|
||||
'C' => {
|
||||
'cmdline' => 'gcc $args $file -o prog',
|
||||
'cmdline' => 'gcc $args $file -o prog -ggdb',
|
||||
'args' => '-Wextra -Wall -Wno-unused -std=gnu89',
|
||||
'file' => 'prog.c',
|
||||
},
|
||||
'C++' => {
|
||||
'cmdline' => 'g++ $args $file -o prog',
|
||||
'cmdline' => 'g++ $args $file -o prog -ggdb',
|
||||
'args' => '',
|
||||
'file' => 'prog.cpp',
|
||||
},
|
||||
'C99' => {
|
||||
'cmdline' => 'gcc $args $file -o prog',
|
||||
'args' => '-Wextra -Wall -Wno-unused -pedantic -std=c99',
|
||||
'cmdline' => 'gcc $args $file -o prog -ggdb',
|
||||
'args' => '-Wextra -Wall -Wno-unused -pedantic -std=c99 -lm',
|
||||
'file' => 'prog.c',
|
||||
},
|
||||
);
|
||||
|
||||
sub runserver {
|
||||
open(my $input, '<', "/dev/ttyS0") or die $!;
|
||||
open(my $output, '>', "/dev/ttyS0") or die $!;
|
||||
my ($input, $output);
|
||||
|
||||
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 $code;
|
||||
@ -49,8 +58,12 @@ sub runserver {
|
||||
print $output "result:$result\n";
|
||||
print $output "result:end\n";
|
||||
|
||||
if(not defined $USE_LOCAL or $USE_LOCAL == 0) {
|
||||
print "input: ";
|
||||
next;
|
||||
} else {
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if($line =~ m/^compile:\s*(.*)/) {
|
||||
@ -107,14 +120,18 @@ sub interpret {
|
||||
|
||||
print "Executing [$cmdline]\n";
|
||||
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) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
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) {
|
||||
$result =~ s/^\s+//;
|
||||
$result =~ s/\s+$//;
|
||||
@ -123,11 +140,11 @@ sub interpret {
|
||||
|
||||
($ret, $result) = execute(5, "./compiler_watchdog.pl");
|
||||
|
||||
print "Executed prog; got result: ($ret) [$result]\n";
|
||||
|
||||
$result =~ s/^\s+//;
|
||||
$result =~ s/\s+$//;
|
||||
|
||||
# print "Executed prog; got result: ($ret) [$result]\n";
|
||||
|
||||
if(not length $result) {
|
||||
$result = "Success (no output).\n" if $ret == 0;
|
||||
$result = "Success (exit code $ret).\n" if $ret != 0;
|
||||
|
@ -4,6 +4,7 @@ use warnings;
|
||||
use strict;
|
||||
|
||||
use POSIX ":sys_wait_h";
|
||||
use IPC::Open2;
|
||||
|
||||
my @signame;
|
||||
$signame[0] = 'SIGZERO';
|
||||
@ -76,6 +77,39 @@ $signame[66] = 'SIGCLD';
|
||||
$signame[67] = 'SIGPOLL';
|
||||
$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 {
|
||||
my $child;
|
||||
while (($child=waitpid(-1,WNOHANG))>0) {
|
||||
@ -92,6 +126,7 @@ sub reaper {
|
||||
|
||||
if($wifsignaled == 1) {
|
||||
print "\nProgram received signal $wtermsig ($signame[$wtermsig])\n";
|
||||
debug_program if $wtermsig != 0;
|
||||
exit;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user