From fdb99417bb13ca0418a5ec8d9e240e29565225c4 Mon Sep 17 00:00:00 2001 From: Marius Gavrilescu Date: Thu, 22 Jan 2015 23:50:50 +0200 Subject: [PATCH] Add virtual machine support --- Makefile.PL | 2 + lib/Gruntmaster/Daemon.pm | 4 +- lib/Gruntmaster/Daemon/Format.pm | 64 ++++++++++++++------ lib/Gruntmaster/Daemon/Runner/Interactive.pm | 11 +++- makevm | 23 +++++++ runvm | 15 +++++ vm.conf | 13 ++++ 7 files changed, 111 insertions(+), 21 deletions(-) create mode 100755 makevm create mode 100755 runvm create mode 100644 vm.conf diff --git a/Makefile.PL b/Makefile.PL index 7f7b2cd..f7d5e99 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -33,12 +33,14 @@ WriteMakefile( sigtrap 0 BSD::Resource 0 + Expect::Simple 0 File::Slurp 0 Gruntmaster::Data 0 IPC::Signal 0 JSON 0 List::MoreUtils 0 Log::Log4perl 0 + String::ShellQuote 0 Try::Tiny 0/ }, META_MERGE => { diff --git a/lib/Gruntmaster/Daemon.pm b/lib/Gruntmaster/Daemon.pm index a84fc57..1d1ca01 100644 --- a/lib/Gruntmaster/Daemon.pm +++ b/lib/Gruntmaster/Daemon.pm @@ -7,7 +7,7 @@ use warnings; our $VERSION = '5999.000_004'; use Gruntmaster::Daemon::Constants qw/ERR/; -use Gruntmaster::Daemon::Format qw/prepare_files/; +use Gruntmaster::Daemon::Format qw/prepare_files stopvms/; use File::Basename qw/fileparse/; use File::Temp qw/tempdir/; @@ -127,6 +127,7 @@ sub process{ $meta->{result} = -1; $meta->{result_text} = $_; }; + stopvms; get_logger->info("Job result: " . $meta->{result_text}); return unless $jobr; @@ -169,6 +170,7 @@ sub got_job{ } sub run{ + warn "No GRUNTMASTER_VM environment variable. Running without a VM is a security risk.\n" unless $ENV{GRUNTMASTER_VM}; require Gruntmaster::Data; $db = Gruntmaster::Data->connect('dbi:Pg:'); Log::Log4perl->init('/etc/gruntmasterd/gruntmasterd-log.conf'); diff --git a/lib/Gruntmaster/Daemon/Format.pm b/lib/Gruntmaster/Daemon/Format.pm index 93035dd..5f503a1 100644 --- a/lib/Gruntmaster/Daemon/Format.pm +++ b/lib/Gruntmaster/Daemon/Format.pm @@ -6,35 +6,62 @@ use warnings; use parent qw/Exporter/; no if $] > 5.017011, warnings => 'experimental::smartmatch'; -use POSIX qw//; +use Expect::Simple; use File::Basename qw/fileparse/; use File::Slurp qw/read_file write_file/; -use Gruntmaster::Daemon::Constants qw/TLE OLE DIED NZX/; -use Time::HiRes qw/alarm/; use List::MoreUtils qw/natatime/; use Log::Log4perl qw/get_logger/; -use IPC::Signal qw/sig_name sig_num/; +use POSIX qw/mkfifo/; +use String::ShellQuote qw/shell_quote/; use Try::Tiny; our $VERSION = "5999.000_004"; -our @EXPORT_OK = qw/prepare_files/; +our @EXPORT_OK = qw/prepare_files stopvms/; ################################################## +our (%vm); + +sub runvm { + my ($name) = @_; + return unless $ENV{GRUNTMASTER_VM}; + mkfifo "$name.in", 0600; + mkfifo "$name.out", 0600; + get_logger->trace("Starting VM $name"); + $vm{$name} = Expect::Simple->new({ + Cmd => "$ENV{GRUNTMASTER_VM} $name", + Prompt => '# ', + DisconnectCmd => 'exit', + RawPty => 1, + Timeout => 10, + }); +} + +sub stopvms { %vm = () } + sub execlist { - my (@args) = @_; - my $ret = fork // die 'Cannot fork'; - if ($ret) { - waitpid $ret, 0; - die "gruntmaster-exec died\n" if -z 'exec-result'; - my ($excode, $exmsg) = read_file 'exec-result'; - unlink 'exec-result'; - chomp ($excode, $exmsg); - die [$excode, $exmsg] if $excode > 0; + my ($vm, @args) = @_; + my $er = "exec-result-$vm"; + if ($vm{$vm}) { + my $cmd = ">$er " . shell_quote 'gruntmaster-exec', @args; + get_logger->trace("Running in VM $vm: $cmd"); + $vm{$vm}->send($cmd); } else { - open STDOUT, '>exec-result'; - exec 'gruntmaster-exec', @args; + my $ret = fork // die 'Cannot fork'; + if ($ret) { + waitpid $ret, 0; + } else { + open STDOUT, ">$er"; + exec 'gruntmaster-exec', @args; + } } + + die "gruntmaster-exec died\n" if -z $er; + my ($excode, $exmsg) = read_file $er; + unlink $er; + chomp ($excode, $exmsg); + get_logger->trace("Exec result: $excode $exmsg"); + die [$excode, $exmsg] if $excode > 0; } sub command_and_args{ @@ -66,7 +93,7 @@ sub mkrun{ while (my ($fd, $file) = $it->()) { push @args, "--fd=$fd $file"; } - execlist @args, command_and_args($format, $basename); + execlist $basename, @args, command_and_args($format, $basename); } } @@ -75,7 +102,7 @@ sub prepare{ get_logger->trace("Preparing file $name..."); try { - execlist '--fd=1 >>errors', '--fd=2 >>errors', 'gruntmaster-compile', $format, $name; + execlist prog => '--fd=1 >>errors', '--fd=2 >>errors', 'gruntmaster-compile', $format, $name; } catch { die "Compile error\n" } finally { @@ -87,6 +114,7 @@ sub prepare{ sub prepare_files{ my $meta = shift; + runvm $_ for keys %{$meta->{files}}; for my $file (values %{$meta->{files}}) { my ($format, $name, $content) = @{$file}{qw/format name content/}; diff --git a/lib/Gruntmaster/Daemon/Runner/Interactive.pm b/lib/Gruntmaster/Daemon/Runner/Interactive.pm index 9118e15..1825fcd 100644 --- a/lib/Gruntmaster/Daemon/Runner/Interactive.pm +++ b/lib/Gruntmaster/Daemon/Runner/Interactive.pm @@ -21,10 +21,16 @@ sub run{ mkfifo 'fifo1', 0600 or die $! unless -e 'fifo1'; mkfifo 'fifo2', 0600 or die $! unless -e 'fifo2'; + if ($test == 1 && $ENV{GRUNTMASTER_VM}) { + exec 'cat ver.in' if fork; + exec 'cat prog.in' if fork; + } + my $ret = fork // get_logger->logdie("Fork failed: $!"); if ($ret) { try { - $meta->{files}{prog}{run}->($meta->{files}{prog}{name}, fds => [qw/0 fifo1 1 >fifo2/], map {defined $meta->{$_} ? ($_ => $meta->{$_}) : () } qw/timeout mlimit/); + my @fds = $ENV{GRUNTMASTER_VM} ? qw,0 /dev/ttyS1 1 >/dev/ttyS1, : qw/0 fifo1 1 >fifo2/; + $meta->{files}{prog}{run}->($meta->{files}{prog}{name}, fds => \@fds, map {defined $meta->{$_} ? ($_ => $meta->{$_}) : () } qw/timeout mlimit/); } catch { die $_ } finally { @@ -33,7 +39,8 @@ sub run{ die [WA, "Wrong Answer"] if $?; } else { try { - $meta->{files}{ver}{run}->($meta->{files}{ver}{name}, fds => [qw/1 >fifo1 0 fifo2 4 >result/], args => [$test], map {defined $meta->{$_} ? ($_ => $meta->{$_}) : () } qw/timeout mlimit/); + my @fds = $ENV{GRUNTMASTER_VM} ? qw,1 >/dev/ttyS1 0 /dev/ttyS1, : qw/1 >fifo1 0 fifo2/; + $meta->{files}{ver}{run}->($meta->{files}{ver}{name}, fds => [@fds, qw,4 >result,], args => [$test], map {defined $meta->{$_} ? ($_ => $meta->{$_}) : () } qw/timeout mlimit/); } catch { exit 1; }; diff --git a/makevm b/makevm new file mode 100755 index 0000000..433370f --- /dev/null +++ b/makevm @@ -0,0 +1,23 @@ +#!/bin/bash +[ `id -u` -eq 0 ] || echo 'This script must be run as root' && exit 1 +rm -rf vm/ vm.squashfs +multistrap -f vm.conf +ln -s vm/usr/bin/mawk vm/bin/awk +echo 'virtfs /mnt 9p trans=virtio,auto 0 0' > vm/etc/fstab +echo '/sbin/poweroff -f' > vm/.bash_logout +cat > vm/.profile < vm/etc/initramfs-tools/modules <