Send a scrollback buffer of the previous 8192 bytes to new clients connecting to a container console.
This is achieved with a basic Perl re-implementation of the dtach master with a scrollback patch applied. [0] This dtach-scrollback-master is started alongside the container at startup. When dtach attaches to this master it receives the scrollback buffer. This improves the user experience for containers and is especially useful for many application containers that print important diagnostic messages at startup, that would otherwise be easily missed by users opening the container console slightly too late. This change is backwards-compatible: upgrading does not break consoles for already running containers, since we fall back to the previous dtach behaviour when dtach-scrollback-master is not running. As an alternative to implementing our own dtach-scrollback-master, we could instead apply the scrollback patch [0] directly to dtach. [0] https://367015.bugs.gentoo.org/attachment.cgi?id=272965 Signed-off-by: Filip Schauer <[email protected]> --- src/Makefile | 2 + src/PVE/LXC.pm | 5 ++ src/dtach-scrollback-master | 99 +++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100755 src/dtach-scrollback-master diff --git a/src/Makefile b/src/Makefile index 2baa782..07a6802 100644 --- a/src/Makefile +++ b/src/Makefile @@ -45,6 +45,8 @@ install: pct lxc-pve.conf pct.1 pct.conf.5 pct.bash-completion pct.zsh-completio pve-userns.seccomp [email protected] [email protected] \ lxc-pve-prestart-hook lxc-pve-autodev-hook lxc-pve-poststop-hook lxcnetaddbr PVE_GENERATING_DOCS=1 perl -I. -T -e "use PVE::CLI::pct; PVE::CLI::pct->verify_api();" + install -d $(BINDIR) + install -m 0755 dtach-scrollback-master $(BINDIR) install -d $(SBINDIR) install -m 0755 pct $(SBINDIR) install -d $(LXC_SCRIPT_DIR) diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm index c19c772..bab27b8 100644 --- a/src/PVE/LXC.pm +++ b/src/PVE/LXC.pm @@ -3100,6 +3100,11 @@ sub vm_start { eval { run_command($cmd); + my $concmd = PVE::LXC::get_console_command($vmid, $conf, -1); + run_command([ + 'dtach-scrollback-master', "/var/run/dtach/vzctlconsole$vmid", @$concmd, + ]); + monitor_start($monitor_socket, $vmid) if defined($monitor_socket); # if debug is requested, print the log it also when the start succeeded diff --git a/src/dtach-scrollback-master b/src/dtach-scrollback-master new file mode 100755 index 0000000..8d348e2 --- /dev/null +++ b/src/dtach-scrollback-master @@ -0,0 +1,99 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use IO::Select; +use IO::Socket::UNIX; +use POSIX qw(EAGAIN WNOHANG setsid); + +use PVE::PTY; + +use constant { + MSG_PUSH => 0, + MSG_WINCH => 3, + MSG_REDRAW => 4, + PACKET_SIZE => 10, + SCROLLSIZE => 8192, + BUFSIZE => 4096, +}; + +my $socketpath = shift or die "Usage: $0 <socket> <cmd> [args]\n"; +unlink($socketpath); + +# Daemonize +my $pid = fork() // die "fork failed: $!\n"; +POSIX::_exit(0) if $pid; +POSIX::setsid(); +my $pid2 = fork() // die "fork failed: $!\n"; +POSIX::_exit(0) if $pid2; +open STDIN, '<', '/dev/null'; +open STDOUT, '>', '/dev/null'; +open STDERR, '>', '/dev/null'; + +$SIG{PIPE} = 'IGNORE'; # Prevent crash on client disconnect +$SIG{INT} = $SIG{TERM} = sub { unlink $socketpath; exit(0); }; + +my $server = IO::Socket::UNIX->new(Local => $socketpath, Listen => 128, Blocking => 0) + or die "Cannot create socket - $IO::Socket::errstr\n"; +my $pty = PVE::PTY->new(); +my $pty_fh = $pty->master(); +my $select = IO::Select->new($server, $pty_fh); +my $scrollback = ''; +my %clientbufs; + +# Spawn Child +my $cpid = fork() // die "fork failed: $!"; +if ($cpid == 0) { + close $server; + $pty->make_controlling_terminal(); + exec(@ARGV) or die; +} + +while (waitpid($cpid, WNOHANG) <= 0) { + for my $fh ($select->can_read(1)) { + if ($fh == $server) { + # Accept new client + my $client = $server->accept(); + $client->blocking(0); + $select->add($client); + syswrite($client, $scrollback); + $clientbufs{$client} = ''; + } elsif ($fh == $pty_fh) { + # PTY Output + sysread($fh, my $output, BUFSIZE) or last; + $scrollback = substr($scrollback . $output, -SCROLLSIZE); + + for my $client (grep { $_ != $server && $_ != $pty_fh } $select->handles()) { + disconnect_client($client) if !defined(syswrite($client, $output)) && $! != EAGAIN; + } + } else { + # Client Input + sysread($fh, $clientbufs{$fh}, 500, length($clientbufs{$fh})) or do { + disconnect_client($fh); + next; + }; + + while (length($clientbufs{$fh}) >= PACKET_SIZE) { + my $packet = substr($clientbufs{$fh}, 0, PACKET_SIZE, ''); + my ($type, $len, $data) = unpack('CCa8', $packet); + if ($type == MSG_PUSH) { + syswrite($pty_fh, substr($data, 0, $len)); + } elsif ($type == MSG_WINCH || $type == MSG_REDRAW) { + my ($rows, $cols) = unpack('S2', $data); + $pty->set_size($cols, $rows) if $rows > 0 && $cols > 0; + kill('WINCH', $pty->get_foreground_pid()); + } + } + } + } +} + +unlink $socketpath; + +sub disconnect_client { + my $fh = shift; + $select->remove($fh); + delete $clientbufs{$fh}; + close $fh; +} -- 2.47.3 _______________________________________________ pve-devel mailing list [email protected] https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
