From: "Thomas Ellmenreich" <t.ellmenreich@proxmox.com>
To: "Proxmox VE development discussion" <pve-devel@lists.proxmox.com>
Cc: "pve-devel" <pve-devel-bounces@lists.proxmox.com>
Subject: Re: [pve-devel] [PATCH container] add container console scrollback buffer
Date: Fri, 25 Sep 2026 13:00:23 +0200 [thread overview]
Message-ID: <DLOCG07XBVQX.3CQ73KN912Y4C@proxmox.com> (raw)
In-Reply-To: <20260121112335.84473-1-f.schauer@proxmox.com>
Thanks for this patch! In my opinion, this would be a great addition.
I installed the patch on one of my test nodes and it worked wonderfully.
However, looking at the approach, I'm not convinced that having a separate
process for each container just for the scrollback buffer is ideal, so I would
definitely make it optional. This is especially the case since the new process
is added regardless of whether the terminal is ever used. That said, I'm not
quite sure how to improve it.
I assume that applying the patch [0] to dtach itself would not solve the
problem of the extra process, as dtach would just become that extra process.
Overall, I think the approach is reasonable. I considered having one 'master
master' process (xD) for all containers instead of a new process for each
container. However, that would introduce a lot of programming complexity.
When I first started using it, I thought the buffer was a bit small.
Implementing it as you have would allow us to easily make the buffer
configurable. I can see larger buffer sizes being useful in some cases.
As an aside: I found it especially nice that some context is now retained
when refreshing the page.
Tested-by: Thomas Ellmenreich <t.ellmenreich@proxmox.com>
[0] https://367015.bugs.gentoo.org/attachment.cgi?id=272965
On Wed Jan 21, 2026 at 12:23 PM CET, Filip Schauer wrote:
> 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 <f.schauer@proxmox.com>
> ---
> 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 <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;
> +}
prev parent reply other threads:[~2026-09-25 11:00 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-21 11:23 [pve-devel] [PATCH container] add container console scrollback buffer Filip Schauer
2026-09-25 11:00 ` Thomas Ellmenreich [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DLOCG07XBVQX.3CQ73KN912Y4C@proxmox.com \
--to=t.ellmenreich@proxmox.com \
--cc=pve-devel-bounces@lists.proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox