mirror of
https://github.com/pragma-/pbot.git
synced 2025-10-14 15:07:22 +02:00
applets/pbot-vm: Improve input to VM
- use JSONL encoding for input (use \n instead of 🔚)
- split VM input into 4k byte chunks to prevent truncation due to
4096 byte line limitation
- produce error message instead of timing out when guest fails to
parse input JSON
- minor refactoring of info/debug logging
This commit is contained in:
parent
99c9c3b262
commit
3606aa8cc5
@ -26,10 +26,9 @@ use Data::Dumper;
|
|||||||
|
|
||||||
sub read_input($input, $buffer, $tag) {
|
sub read_input($input, $buffer, $tag) {
|
||||||
my $line;
|
my $line;
|
||||||
my $total_read = 0;
|
|
||||||
|
|
||||||
print STDERR "$tag waiting for input...\n";
|
print STDERR "$tag waiting for input...\n";
|
||||||
my $ret = sysread($input, my $buf, 16384);
|
my $ret = sysread($input, my $buf, 4096);
|
||||||
|
|
||||||
if (not defined $ret) {
|
if (not defined $ret) {
|
||||||
print STDERR "Error reading $tag: $!\n";
|
print STDERR "Error reading $tag: $!\n";
|
||||||
@ -41,19 +40,16 @@ sub read_input($input, $buffer, $tag) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$total_read += $ret;
|
print STDERR "$tag read $ret bytes [$buf]\n";
|
||||||
|
|
||||||
print STDERR "$tag read $ret bytes [$total_read total] [$buf]\n";
|
if ($buf ne "\n") {
|
||||||
|
chomp $buf;
|
||||||
$$buffer .= $buf;
|
$$buffer .= $buf;
|
||||||
|
return undef;
|
||||||
return undef if $$buffer !~ s/\s*:end:\s*$//m;
|
}
|
||||||
|
|
||||||
$line = $$buffer;
|
$line = $$buffer;
|
||||||
chomp $line;
|
|
||||||
|
|
||||||
$$buffer = '';
|
$$buffer = '';
|
||||||
$total_read = 0;
|
|
||||||
|
|
||||||
print STDERR "-" x 40, "\n";
|
print STDERR "-" x 40, "\n";
|
||||||
print STDERR "$tag got [$line]\n";
|
print STDERR "$tag got [$line]\n";
|
||||||
@ -62,7 +58,16 @@ sub read_input($input, $buffer, $tag) {
|
|||||||
|
|
||||||
if ($@) {
|
if ($@) {
|
||||||
print STDERR "Failed to decode JSON: $@\n";
|
print STDERR "Failed to decode JSON: $@\n";
|
||||||
return undef;
|
return {
|
||||||
|
arguments => '',
|
||||||
|
cmdline => 'sh prog.sh',
|
||||||
|
code => "echo 'Failed to decode JSON: $@'",
|
||||||
|
date => 0,
|
||||||
|
execfile => 'prog.sh',
|
||||||
|
input => '',
|
||||||
|
lang => 'sh',
|
||||||
|
sourcefile => 'prog.sh'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
$command->{arguments} //= '';
|
$command->{arguments} //= '';
|
||||||
|
@ -24,8 +24,6 @@ sub split_line($line, %opts) {
|
|||||||
strip_commas => 0,
|
strip_commas => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
print STDERR "split: [$line]\n";
|
|
||||||
|
|
||||||
%opts = (%default_opts, %opts);
|
%opts = (%default_opts, %opts);
|
||||||
|
|
||||||
return () if not length $line;
|
return () if not length $line;
|
||||||
@ -149,8 +147,6 @@ sub split_line($line, %opts) {
|
|||||||
$token .= $ch;
|
$token .= $ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
use Data::Dumper;
|
|
||||||
print STDERR "split: ", Dumper(\@args), "\n";
|
|
||||||
return @args;
|
return @args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,9 @@ use Getopt::Long qw(GetOptionsFromArray :config pass_through no_ignore_case no_a
|
|||||||
use Time::HiRes qw(gettimeofday);
|
use Time::HiRes qw(gettimeofday);
|
||||||
use POSIX;
|
use POSIX;
|
||||||
|
|
||||||
|
use Data::Dumper;
|
||||||
|
$Data::Dumper::Useqq = 1;
|
||||||
|
|
||||||
use FindBin qw($RealBin);
|
use FindBin qw($RealBin);
|
||||||
|
|
||||||
use InteractiveEdit;
|
use InteractiveEdit;
|
||||||
@ -136,8 +139,8 @@ sub preprocess_code($self, %opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unless($self->{got_run} and $self->{copy_code}) {
|
unless($self->{got_run} and $self->{copy_code}) {
|
||||||
$self->debug("---- preprocess\n");
|
$self->info("---- preprocess\n");
|
||||||
$self->debug("$self->{nick} $self->{channel}: [$self->{arguments}] $self->{cmdline_options}\n$self->{code}\n", 0);
|
$self->info("$self->{nick} $self->{channel}: [$self->{arguments}] $self->{cmdline_options}\n$self->{code}\n",0);
|
||||||
}
|
}
|
||||||
|
|
||||||
# replace \n outside of quotes with literal newline
|
# replace \n outside of quotes with literal newline
|
||||||
@ -253,8 +256,8 @@ sub execute {
|
|||||||
$cmdline =~ s/\$options\s+//;
|
$cmdline =~ s/\$options\s+//;
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->debug("---- executing\n");
|
$self->info("---- executing\n");
|
||||||
$self->debug("$cmdline\n$stdin\n$pretty_code\n", 0);
|
$self->info("$cmdline\n$stdin\n$pretty_code\n", 0);
|
||||||
|
|
||||||
my $compile_in = {
|
my $compile_in = {
|
||||||
lang => $self->{lang},
|
lang => $self->{lang},
|
||||||
@ -270,44 +273,45 @@ sub execute {
|
|||||||
$compile_in->{'factoid'} = $self->{'factoid'} if length $self->{'factoid'};
|
$compile_in->{'factoid'} = $self->{'factoid'} if length $self->{'factoid'};
|
||||||
$compile_in->{'persist-key'} = $self->{'persist-key'} if length $self->{'persist-key'};
|
$compile_in->{'persist-key'} = $self->{'persist-key'} if length $self->{'persist-key'};
|
||||||
|
|
||||||
|
$self->debug("compile_in: ", Dumper($compile_in), "\n") if $self->{debug} > 5;
|
||||||
|
|
||||||
my $compile_json = encode_json($compile_in);
|
my $compile_json = encode_json($compile_in);
|
||||||
$compile_json .= "\n:end:\n";
|
my $chunk_size = 4000;
|
||||||
|
|
||||||
my $length = length $compile_json;
|
$self->debug("compile_json: ", Dumper($compile_json), "\n") if $self->{debug} > 5;
|
||||||
my $sent = 0;
|
|
||||||
my $chunk_max = 16384;
|
|
||||||
my $chunk_size = $length < $chunk_max ? $length : $chunk_max;
|
|
||||||
my $chunks_sent = 0;
|
|
||||||
|
|
||||||
# $self->debug("Sending $length bytes [$compile_json] to vm_server\n");
|
my @lines = unpack("(A$chunk_size)*", $compile_json);
|
||||||
|
push @lines, '';
|
||||||
|
|
||||||
$chunk_size -= 1; # account for newline in syswrite
|
$self->debug("Lines:\n" . (Dumper(\@lines))) if $self->{debug} > 1;
|
||||||
|
|
||||||
while ($chunks_sent < $length) {
|
foreach my $line (@lines) {
|
||||||
my $chunk = substr $compile_json, $chunks_sent, $chunk_size;
|
$line .= "\n";
|
||||||
|
my $length = length $line;
|
||||||
|
my $sent = 0;
|
||||||
|
|
||||||
$chunks_sent += length $chunk;
|
$self->debug("writing [$line]\n") if $self->{debug} > 1;
|
||||||
|
|
||||||
my $ret = syswrite($input, $chunk);
|
while ($sent < $length) {
|
||||||
|
my $ret = syswrite($input, $line, $chunk_size + 1, $sent);
|
||||||
|
$self->debug("sent: $sent, length: $length, ret: $ret\n");
|
||||||
|
|
||||||
if (not defined $ret) {
|
if (not defined $ret) {
|
||||||
my $error = $!;
|
$self->info("Error sending: $!\n");
|
||||||
print STDERR "Error sending: $error\n";
|
last;
|
||||||
$self->debug("Error sending: $error\n");
|
}
|
||||||
last;
|
|
||||||
|
if ($ret == 0) {
|
||||||
|
$self->info("Sent 0 bytes. Sleep 1 sec and try again\n");
|
||||||
|
sleep 1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sent += $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($ret == 0) {
|
|
||||||
print STDERR "Sent 0 bytes. Sleep 1 sec and try again\n";
|
|
||||||
$self->debug("Sent 0 bytes. Sleep 1 sec and try again\n");
|
|
||||||
sleep 1;
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sent += $ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
my $result = "";
|
my $result = '';
|
||||||
my $got_result = 0;
|
my $got_result = 0;
|
||||||
|
|
||||||
while (my $line = decode('UTF-8', <$output>, sub { sprintf '\\\\x%02X', shift })) {
|
while (my $line = decode('UTF-8', <$output>, sub { sprintf '\\\\x%02X', shift })) {
|
||||||
@ -337,8 +341,8 @@ sub execute {
|
|||||||
|
|
||||||
sub postprocess_output($self) {
|
sub postprocess_output($self) {
|
||||||
unless($self->{got_run} and $self->{copy_code}) {
|
unless($self->{got_run} and $self->{copy_code}) {
|
||||||
$self->debug("---- post-processing\n");
|
$self->info("---- post-processing\n");
|
||||||
$self->debug("$self->{output}\n", 0);
|
$self->info("$self->{output}\n", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
# backspace
|
# backspace
|
||||||
@ -374,7 +378,6 @@ sub postprocess_output($self) {
|
|||||||
# "\n" => '<nl>',
|
# "\n" => '<nl>',
|
||||||
|
|
||||||
# \t is left alone
|
# \t is left alone
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$self->{output} =~ s/([\e\f])/$escapes{$1}/gs;
|
$self->{output} =~ s/([\e\f])/$escapes{$1}/gs;
|
||||||
@ -397,9 +400,9 @@ sub show_output($self) {
|
|||||||
my $output = $self->{output};
|
my $output = $self->{output};
|
||||||
|
|
||||||
unless ($self->{got_run} and $self->{copy_code}) {
|
unless ($self->{got_run} and $self->{copy_code}) {
|
||||||
$self->debug("---- show output\n");
|
$self->info("---- show output\n");
|
||||||
$self->debug("$output\n", 0);
|
$self->info("$output\n", 0);
|
||||||
$self->debug("=========================\n", 0);
|
$self->info("=========================\n", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exists $self->{options}->{'-paste'} or (defined $self->{got_run} and $self->{got_run} eq 'paste')) {
|
if (exists $self->{options}->{'-paste'} or (defined $self->{got_run} and $self->{got_run} eq 'paste')) {
|
||||||
@ -490,6 +493,11 @@ sub show_output($self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub debug($self, $text, $timestamp = 1) {
|
sub debug($self, $text, $timestamp = 1) {
|
||||||
|
return if not $self->{debug};
|
||||||
|
$self->info($text, $timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub info($self, $text, $timestamp = 1) {
|
||||||
if (not exists $self->{logh}) {
|
if (not exists $self->{logh}) {
|
||||||
open $self->{logh}, '>>:encoding(UTF-8)', "$RealBin/../log.txt" or die "Could not open log file: $!";
|
open $self->{logh}, '>>:encoding(UTF-8)', "$RealBin/../log.txt" or die "Could not open log file: $!";
|
||||||
}
|
}
|
||||||
@ -498,8 +506,10 @@ sub debug($self, $text, $timestamp = 1) {
|
|||||||
my ($sec, $usec) = gettimeofday;
|
my ($sec, $usec) = gettimeofday;
|
||||||
my $time = strftime "%a %b %e %Y %H:%M:%S", localtime $sec;
|
my $time = strftime "%a %b %e %Y %H:%M:%S", localtime $sec;
|
||||||
$time .= sprintf ".%03d", $usec / 1000;
|
$time .= sprintf ".%03d", $usec / 1000;
|
||||||
|
print STDERR "$time :: $text";
|
||||||
print { $self->{logh} } "$time :: $text";
|
print { $self->{logh} } "$time :: $text";
|
||||||
} else {
|
} else {
|
||||||
|
print STDERR $text;
|
||||||
print { $self->{logh} } $text;
|
print { $self->{logh} } $text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,6 @@ sub split_line($line, %opts) {
|
|||||||
strip_commas => 0,
|
strip_commas => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
print STDERR "split: [$line]\n";
|
|
||||||
|
|
||||||
%opts = (%default_opts, %opts);
|
%opts = (%default_opts, %opts);
|
||||||
|
|
||||||
return () if not length $line;
|
return () if not length $line;
|
||||||
@ -149,8 +147,6 @@ sub split_line($line, %opts) {
|
|||||||
$token .= $ch;
|
$token .= $ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
use Data::Dumper;
|
|
||||||
print STDERR "split: ", Dumper(\@args), "\n";
|
|
||||||
return @args;
|
return @args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ use PBot::Imports;
|
|||||||
# These are set by the /misc/update_version script
|
# These are set by the /misc/update_version script
|
||||||
use constant {
|
use constant {
|
||||||
BUILD_NAME => "PBot",
|
BUILD_NAME => "PBot",
|
||||||
BUILD_REVISION => 4897,
|
BUILD_REVISION => 4898,
|
||||||
BUILD_DATE => "2025-09-25",
|
BUILD_DATE => "2025-09-28",
|
||||||
};
|
};
|
||||||
|
|
||||||
sub initialize {}
|
sub initialize {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user