mirror of
https://github.com/pragma-/pbot.git
synced 2024-12-02 00:49:26 +01:00
17b69f04ff
* 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
173 lines
3.5 KiB
Perl
Executable File
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();
|