From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 7DEC51FF15E for ; Wed, 21 Jan 2026 12:23:58 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id A2D1223A1D; Wed, 21 Jan 2026 12:24:14 +0100 (CET) From: Filip Schauer To: pve-devel@lists.proxmox.com Date: Wed, 21 Jan 2026 12:23:31 +0100 Message-ID: <20260121112335.84473-1-f.schauer@proxmox.com> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1768994563016 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.050 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URI_HEX 0.1 URI hostname has long hexadecimal sequence Subject: [pve-devel] [PATCH container] add container console scrollback buffer X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" 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 --- 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 pve-container@.service pve-container-debug@.service \ 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 [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 pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel