From 3cfe86512d86198613adb5cf18e7b880ec64e32f Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 2 Mar 2026 10:05:55 +0100 Subject: [PATCH] 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. --- misc/build.func | 22 ++++++++++++++++------ misc/error_handler.func | 10 +++++++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index 4233d831c..d35d00317 100644 --- a/misc/build.func +++ b/misc/build.func @@ -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)" &1 | tee "$_LXC_CAPTURE_LOG" + exec {_RECOVERY_TTY}/dev/null || _RECOVERY_TTY="" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)" &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/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)" &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)" &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 diff --git a/misc/error_handler.func b/misc/error_handler.func index cb89239ac..9a2709342 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -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/null || true + local _read_fd="${_RECOVERY_TTY:-}" + local _read_src="/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