Fix read failures in install scripts and recovery menu

Two critical bugs fixed:

1. Install scripts (80+) using 'read' for interactive prompts all fail because
   lxc-attach stdin was redirected from /dev/null. Change to /dev/tty so install
   scripts like immich, elementsynapse, etc. can prompt the user interactively.

2. Recovery menu read gets 'Input/output error' from /dev/tty after the
   lxc-attach|tee pipeline corrupts the terminal state. Pre-open a separate
   file descriptor to /dev/tty BEFORE the pipeline starts. This fd survives
   any tty corruption and is used as fallback for the recovery menu read.
   Fixes the 'command not found' issue where user input falls through to the
   parent shell.

Both build.func (main install + APT retry) and error_handler.func (fallback
cleanup prompt) are updated with the same pattern.
This commit is contained in:
CanbiZ (MickLesk)
2026-03-02 10:05:55 +01:00
parent 47e3e415b9
commit 3cfe86512d
2 changed files with 25 additions and 7 deletions

View File

@@ -4100,11 +4100,12 @@ EOF'
# - install script crashes before logging starts
# - $STD/silent() not used for some commands
# PIPESTATUS[0] gets the real exit code from lxc-attach (not from tee).
# Note: stdin is redirected from /dev/null so the pipeline does not consume
# the host's stdin/tty. This keeps /dev/tty usable for the recovery menu
# after SIGINT or failure (prevents "read: read error: Input/output error").
# stdin comes from /dev/tty so install scripts can use interactive `read`.
# We pre-open a separate fd to /dev/tty BEFORE the pipeline so the recovery
# menu can still read even if lxc-attach|tee corrupts the tty state.
local _LXC_CAPTURE_LOG="/tmp/.install-capture-${SESSION_ID}.log"
lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)" </dev/null 2>&1 | tee "$_LXC_CAPTURE_LOG"
exec {_RECOVERY_TTY}</dev/tty 2>/dev/null || _RECOVERY_TTY=""
lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)" </dev/tty 2>&1 | tee "$_LXC_CAPTURE_LOG"
local lxc_exit=${PIPESTATUS[0]}
# Restore terminal state after pipeline (tee/lxc-attach may leave it broken)
@@ -4396,7 +4397,14 @@ EOF'
# Ensure terminal is sane before reading input (lxc-attach|tee may corrupt tty state)
stty sane 2>/dev/null || true
if read -t 60 -r response </dev/tty; then
# Use the pre-opened tty fd (survives tty corruption from lxc-attach|tee)
local _read_fd="${_RECOVERY_TTY:-}"
local _read_src="</dev/tty"
if [[ -n "$_read_fd" ]] && { true <&"$_read_fd"; } 2>/dev/null; then
_read_src="<&$_read_fd"
fi
if eval "read -t 60 -r response $_read_src"; then
case "${response:-1}" in
1)
# Remove container
@@ -4484,7 +4492,7 @@ EOF'
set +Eeuo pipefail
trap - ERR
local _LXC_CAPTURE_LOG="/tmp/.install-capture-${SESSION_ID}.log"
lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)" </dev/null 2>&1 | tee "$_LXC_CAPTURE_LOG"
lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)" </dev/tty 2>&1 | tee "$_LXC_CAPTURE_LOG"
local apt_retry_exit=${PIPESTATUS[0]}
stty sane 2>/dev/null || true
set -Eeuo pipefail
@@ -4593,6 +4601,8 @@ EOF'
# Restore default job-control signal handling before exit
trap - TSTP TTIN TTOU
# Close pre-opened tty fd
[[ -n "${_RECOVERY_TTY:-}" ]] && exec {_RECOVERY_TTY}<&- 2>/dev/null || true
exit $install_exit_code
fi

View File

@@ -286,7 +286,15 @@ error_handler() {
echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
fi
if read -t 60 -r response </dev/tty; then
# Use the pre-opened tty fd if available (survives tty corruption from lxc-attach|tee)
stty sane 2>/dev/null || true
local _read_fd="${_RECOVERY_TTY:-}"
local _read_src="</dev/tty"
if [[ -n "$_read_fd" ]] && { true <&"$_read_fd"; } 2>/dev/null; then
_read_src="<&$_read_fd"
fi
if eval "read -t 60 -r response $_read_src"; then
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
echo ""
if declare -f msg_info >/dev/null 2>&1; then