3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-12-26 12:42:49 +01:00
pbot/applets/pbot-vm/guest/bin/guest-server
Pragmatic Software 17b69f04ff
pbot-vm: massive refactor
* replace heartbeat with health-check
  * instead of steady stream of newlines every 5s, now awaits input
    and responds with `vmstat` output

* more reliably use host/config/vm-exec.json to get libvirt domain name
  for snapshot-revert, server address, serial ports, vagrant setting, etc

* use iptables/nftables to disable networking
  * added guest/bin/disable-network-[iptables,nftables]
  * added guest/bin/enable-network-[iptables,nftables]

* replace ugly ___OUTPUT___ texts in sh, bash, ksh, zsh languages

* documentation updates and tweaks
2024-04-10 01:21:18 -07:00

173 lines
3.5 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-2024 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/ttyS2',
HEALTH => '/dev/ttyS3',
VPORT => $ENV{PBOTVM_VPORT} // 5555,
};
use lib MOD_DIR;
use lib MOD_DIR . "Languages";
use Guest;
use File::Basename;
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 $!;
my $buffer = '';
while (1) {
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_healthcheck() {
open(my $health_in, '<', HEALTH) or die $!;
open(my $health_out, '>', HEALTH) or die $!;
print "Healthcheck listening on PID $$...\n";
while (1) {
my $input = <$health_in>;
my $vmstat = `vmstat`;
print $health_out "$vmstat\n";
print $health_out ":END\n";
}
print "Healthcheck 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();
my $pid = fork // die "Fork failed: $!";
if ($pid == 0) {
do_healthcheck();
} else {
do_server();
}
print "PBot VM Guest server shutdown.\n";
}
main();