From 2708b364161c07063f1984b4bb591c67cc917cf8 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:24:28 +0200 Subject: [PATCH] Fix container locale, TERM and tty sizing Add a forced container env (_ct_env) exporting LC_ALL/LANG=C.UTF-8 and DEBIAN_FRONTEND to ensure commands (apt, sed, locale-gen, getent, tz setup) run reliably in fresh templates that lack generated locales. Use the env when running pct exec for locale setup, timezone symlink, DNS checks, and apt installs. Improve terminal handling: set TERM=xterm-256color only for SSH sessions by appending a guarded block to /root/.bashrc, disable /etc/profile.d/vte.{sh,csh} by renaming them to avoid CSI 6n prompt garbage on LXC/noVNC consoles, and add /etc/profile.d/00-lxc-term-size.sh to force a sane stty rows/cols when the pseudo-tty reports 0x0. These changes prevent locale-related errors and stray CSI responses in LXC containers. --- misc/build.func | 17 +++++++++++------ misc/install.func | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/misc/build.func b/misc/build.func index 6d7bc9ff..fcfc8675 100644 --- a/misc/build.func +++ b/misc/build.func @@ -4602,15 +4602,20 @@ EOF sleep 3 LANG=${LANG:-en_US.UTF-8} + # Force C locale inside the container until proper locales are generated. + # Otherwise perl/apt spam "Setting locale failed" because pct exec inherits + # the host's LANG (e.g. en_US.UTF-8) which isn't generated in fresh templates. + local _ct_env='export LC_ALL=C.UTF-8 LANG=C.UTF-8 DEBIAN_FRONTEND=noninteractive;' + # Devuan templates don't include locales package by default - install it first if [ "$var_os" == "devuan" ]; then - pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y locales >/dev/null" || true + pct exec "$CTID" -- bash -c "${_ct_env} apt-get update >/dev/null && apt-get install -y locales >/dev/null" || true fi # Only configure locale if locale.gen exists (some minimal templates don't have it) if pct exec "$CTID" -- test -f /etc/locale.gen 2>/dev/null; then - pct exec "$CTID" -- bash -c "sed -i \"/$LANG/ s/^# //\" /etc/locale.gen" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + pct exec "$CTID" -- bash -c "${_ct_env} sed -i \"/$LANG/ s/^# //\" /etc/locale.gen" + pct exec "$CTID" -- bash -c "${_ct_env} locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ echo LANG=\$locale_line >/etc/default/locale && \ locale-gen >/dev/null && \ export LANG=\$locale_line" @@ -4624,18 +4629,18 @@ EOF if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then # Set timezone using symlink (Debian 13+ compatible) # Create /etc/timezone for backwards compatibility with older scripts - pct exec "$CTID" -- bash -c "tz='$tz'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" + pct exec "$CTID" -- bash -c "${_ct_env} tz='$tz'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" else msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi # Detect broken DNS resolver (e.g. Tailscale MagicDNS) and inject public DNS - if ! pct exec "$CTID" -- bash -c "getent hosts deb.debian.org >/dev/null 2>&1 && getent hosts archive.ubuntu.com >/dev/null 2>&1"; then + if ! pct exec "$CTID" -- bash -c "${_ct_env} getent hosts deb.debian.org >/dev/null 2>&1 && getent hosts archive.ubuntu.com >/dev/null 2>&1"; then msg_warn "APT repository DNS resolution failed in container, injecting public DNS servers" pct exec "$CTID" -- bash -c "echo -e 'nameserver 8.8.8.8\nnameserver 1.1.1.1' >/etc/resolv.conf" fi - pct exec "$CTID" -- bash -c "apt-get update >/dev/null 2>&1 && apt-get install -y sudo curl mc gnupg2 jq >/dev/null 2>&1" || { + pct exec "$CTID" -- bash -c "${_ct_env} apt-get update >/dev/null 2>&1 && apt-get install -y sudo curl mc gnupg2 jq >/dev/null 2>&1" || { local failed_mirror failed_mirror=$(pct exec "$CTID" -- bash -c "grep -m1 -oP '(?<=URIs: https?://)[^/]+' /etc/apt/sources.list.d/debian.sources 2>/dev/null || grep -m1 -oP '(?<=deb https?://)[^/]+' /etc/apt/sources.list 2>/dev/null" 2>/dev/null || echo "unknown") msg_warn "apt-get update failed (${failed_mirror}), trying alternate mirrors..." diff --git a/misc/install.func b/misc/install.func index 591ed04f..b5fdb4fa 100644 --- a/misc/install.func +++ b/misc/install.func @@ -1077,8 +1077,18 @@ EOF # Configures Message of the Day and SSH settings # ------------------------------------------------------------------------------ motd_ssh() { - # Set terminal to 256-color mode - grep -qxF "export TERM='xterm-256color'" /root/.bashrc 2>/dev/null || echo "export TERM='xterm-256color'" >>/root/.bashrc + # Set 256-color mode only for SSH sessions; LXC console gets TERM from agetty. + # Forcing TERM=xterm-256color on the LXC noVNC console can break readline's + # window-size detection and cause CSI 6n response leaks ("R;80R" garbage). + if ! grep -q '__cs_term_setup' /root/.bashrc 2>/dev/null; then + cat >>/root/.bashrc <<'EOF' + +# __cs_term_setup: community-scripts terminal init +if [ -n "${SSH_CONNECTION:-}${SSH_TTY:-}" ]; then + export TERM='xterm-256color' +fi +EOF + fi # Get OS information local os_name="$OS_TYPE" @@ -1206,11 +1216,28 @@ EOF systemctl enable console-getty.service &>/dev/null || true fi - # Fedora/RHEL: /etc/profile.d/vte.sh emits cursor-position queries (CSI 6n) - # whose responses leak into the shell input buffer, causing - # "R;80R: command not found" garbage on the first prompt. Disable it. - [[ -f /etc/profile.d/vte.sh ]] && chmod -x /etc/profile.d/vte.sh 2>/dev/null || true - [[ -f /etc/profile.d/vte.csh ]] && chmod -x /etc/profile.d/vte.csh 2>/dev/null || true + # Fedora/RHEL: /etc/profile.d/vte.sh sets a PROMPT_COMMAND that, combined + # with broken TIOCGWINSZ in LXC noVNC, causes CSI 6n responses to leak as + # "R;80R" garbage at the shell prompt. Disable by renaming (chmod -x is + # ineffective; /etc/profile sources by readability, not executable bit). + for _f in /etc/profile.d/vte.sh /etc/profile.d/vte.csh; do + [[ -f "$_f" ]] && mv "$_f" "${_f}.disabled" 2>/dev/null || true + done + + # LXC pseudo-tty often reports winsize 0x0; bash readline then falls back + # to CSI 6n cursor-position queries whose responses leak as input. Force + # a sane default size at every shell start until a real size is detected. + cat >/etc/profile.d/00-lxc-term-size.sh <<'EOF' +# community-scripts: ensure sane terminal size in LXC console +if [ -t 0 ] && [ -t 1 ]; then + _sz=$(stty size 2>/dev/null) + if [ -z "$_sz" ] || [ "${_sz% *}" = "0" ]; then + stty rows 24 cols 80 2>/dev/null + fi + unset _sz +fi +EOF + chmod 644 /etc/profile.d/00-lxc-term-size.sh systemctl daemon-reload # Restart only what's currently active to avoid spawning duplicate gettys