3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-11-12 15:09:33 +01:00
pbot/applets/compiler_vm/guest/bin/guest-server
Pragmatic Software 1326b0ac5f compiler_vm: major refactor to support VM sockets (AF_VSOCK)
VM socket communication is superior to VM serial communication in
every way. Unfortunately at this time only Linux supports them.
Fortunately, that's 99% of PBot's userbase.

If you're not using Linux or if you're using an older Linux that
does not support VM sockets, the PBot VM scripts will gracefully
fallback to using the serial connection. You may explicitly
disable VM socket connection attempts by setting PBOTVM_CID=0.
2022-02-12 16:06:04 -08:00

181 lines
3.7 KiB
Perl
Executable File

#!/usr/bin/env perl
# File: guest-server
#
# Purpose: PBot VM Guest server. Runs inside PBot VM Guest and processes
# incoming VM commands from vm-exec.
# SPDX-FileCopyrightText: 2022 Pragmatic Software <pragma78@gmail.com>
# SPDX-License-Identifier: MIT
use 5.020;
use warnings;
use strict;
use feature qw/signatures/;
no warnings qw(experimental::signatures);
use constant {
USERNAME => 'vm',
MOD_DIR => '/usr/local/share/pbot-vm/',
SERIAL => '/dev/ttyS1',
HEARTBEAT => '/dev/ttyS2',
INPUT => '/dev/stdin',
OUTPUT => '/dev/stdout',
VPORT => $ENV{PBOTVM_VPORT} // 5555,
};
use lib MOD_DIR;
use lib MOD_DIR . "Languages";
use Guest;
use File::Basename;
use IPC::Shareable;
my %languages;
sub load_modules() {
my @files = glob MOD_DIR . "Languages/*.pm";
foreach my $mod (@files){
print "Loading module $mod\n";
my $filename = basename($mod);
require $filename;
$filename =~ s/\.pm$//;
$languages{$filename} = 1;
}
}
sub vsock_server() {
print "Starting VSOCK server on PID $$\n";
system("socat VSOCK-LISTEN:".VPORT.",reuseaddr,fork EXEC:accept-vsock-client");
print "VSOCK server shutdown.\n";
exit; # exit child process
}
sub serial_server() {
print "Starting serial server on PID $$\n";
# set serial to 115200 baud instead of 9600
system('stty -F '.SERIAL.' 115200');
open(my $input, '<', SERIAL) or die $!;
open(my $output, '>', SERIAL) or die $!;
tie my $running, 'IPC::Shareable', { key => 'running' };
my $buffer = '';
while ($running) {
my $command = Guest::read_input($input, \$buffer, 'Serial');
if (not defined $command) {
# recoverable error while reading, try again
next;
}
if ($command == 0) {
# serial closed, exit child process
exit;
}
if (not exists $languages{$command->{lang}}) {
$command->{lang} = '_default';
}
my $mod = $command->{lang}->new(%$command);
my $result = Guest::process_command($command, $mod, USERNAME, 'Serial');
if (not defined $result) {
$result = "[Fatal error]";
}
if ($result) {
Guest::send_output($output, $result, 'Serial');
exit; # exit child process
}
}
}
sub do_server() {
my $pid = fork;
if (not defined $pid) {
print STDERR "Could not fork server: $!\n";
die;
}
if ($pid == 0) {
vsock_server();
} else {
serial_server();
}
}
sub do_heartbeat() {
open(my $heartbeat, '>', HEARTBEAT) or die $!;
tie my $running, 'IPC::Shareable', { key => 'running' };
print "Heart beating on PID $$...\n";
while ($running) {
print $heartbeat "\n";
sleep 5;
}
print "Heart beat stopped.\n";
exit; # exit child process
}
sub install_signal_handlers() {
use POSIX qw(:signal_h :errno_h :sys_wait_h);
$SIG{CHLD} = \&REAPER;
sub REAPER {
my $pid = waitpid(-1, &WNOHANG);
if ($pid == -1) {
# no child waiting. Ignore it.
} elsif (WIFEXITED($?)) {
print "Process $pid exited.\n";
}
$SIG{CHLD} = \&REAPER; # in case of unreliable signals
}
}
sub main() {
print "Starting PBot VM Guest server on PID $$\n";
load_modules();
install_signal_handlers();
tie my $running, 'IPC::Shareable', { key => 'running', create => 1, destroy => 1 };
$running = 1;
my $pid = fork // die "Fork failed: $!";
if ($pid == 0) {
do_heartbeat();
} else {
do_server();
}
print "PBot VM Guest server shutdown.\n";
}
main();