Fix: whiptail-TUI reagiert nicht auf Tastatureingaben bei curl | bash

sudo, aus einer nicht-interaktiven gepipten Shell heraus aufgerufen, gibt die
Terminal-Vordergrundgruppe nicht zuverlässig an den Kindprozess weiter.
setup.sh lief bisher komplett (inkl. aller whiptail-Dialoge) innerhalb von
sudo, wodurch das Menü zwar sichtbar war, aber keine Tasten mehr ankamen.

setup.sh fragt jetzt alle interaktiven Eingaben (Stage-Auswahl, Zielbenutzer,
Ollama-Pfad) zuerst als normaler Nutzer ab und eskaliert erst danach per
exec sudo, wobei die bereits getroffenen Entscheidungen als Flags/Env-Variablen
durchgereicht werden. install.sh eskaliert dadurch nicht mehr selbst.
Zusätzlich wird die fehlerhafte "-c /dev/tty"-Interaktivitätsprüfung
(prüfte nur Existenz der Gerätedatei, nicht ob ein Terminal verbunden ist)
durch has_tty() ersetzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017WMUd2fGsPCtTCVQkSS5TA
This commit is contained in:
2026-09-06 20:31:54 +02:00
co-authored by Claude Sonnet 5
parent 116a33a876
commit 5a8dd73d32
4 changed files with 68 additions and 37 deletions
+7 -5
View File
@@ -23,29 +23,31 @@ curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/
### 2. Manuelles Klonen & Starten ### 2. Manuelles Klonen & Starten
Alternativ kann das Repository manuell geklont und ausgeführt werden: Alternativ kann das Repository manuell geklont und ausgeführt werden:
```bash ```bash
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && sudo ./setup.sh git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && ./setup.sh
``` ```
Oder direkt vollautomatisch alle Stages (`00` bis `10`) ausführen: Oder direkt vollautomatisch alle Stages (`00` bis `10`) ausführen:
```bash ```bash
git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && sudo ./setup.sh --all git clone https://gitea.creative-dragonslayer.de/Scripts/Setup.git && cd Setup && ./setup.sh --all
``` ```
> `setup.sh` fragt zuerst alle nötigen Eingaben (Modulauswahl, Zielbenutzer, ...) direkt am Terminal ab und eskaliert erst danach selbstständig per `sudo`. Ein manuelles `sudo` davor ist nicht nötig, funktioniert aber ebenfalls.
### 3. Interaktiver TUI-Modus ### 3. Interaktiver TUI-Modus
Starte das grafische Terminal-Menü (`whiptail`) zur flexiblen Auswahl einzelner Module: Starte das grafische Terminal-Menü (`whiptail`) zur flexiblen Auswahl einzelner Module:
```bash ```bash
sudo ./setup.sh ./setup.sh
``` ```
### 4. Vollautomatischer CLI-Modus ### 4. Vollautomatischer CLI-Modus
Führe alle Phasen (`00` bis `10`) ohne Interaktion aus: Führe alle Phasen (`00` bis `10`) ohne Interaktion aus:
```bash ```bash
sudo ./setup.sh --all ./setup.sh --all
``` ```
### 5. Gezielte Modulauswahl ### 5. Gezielte Modulauswahl
Installiere nur bestimmte Phasen (z. B. nur Paketlisten und Schriften): Installiere nur bestimmte Phasen (z. B. nur Paketlisten und Schriften):
```bash ```bash
sudo ./setup.sh --stages 04,05 ./setup.sh --stages 04,05
``` ```
### 6. Simulationslauf (Dry-Run) ### 6. Simulationslauf (Dry-Run)
+6 -19
View File
@@ -110,25 +110,12 @@ main() {
log_success "Repository cloned successfully. Starting setup orchestrator..." log_success "Repository cloned successfully. Starting setup orchestrator..."
# Check if dry-run flag is requested # setup.sh itself elevates to root via sudo once it needs it - after all
local is_dry_run=0 # interactive TUI/prompts have already run as the current user. Escalating
for arg in "$@"; do # here instead would wrap the whiptail dialogs in sudo, which (when sudo is
if [[ "$arg" == "-d" || "$arg" == "--dry-run" ]]; then # invoked from this non-interactive piped shell) leaves them unable to
is_dry_run=1 # receive keystrokes. See setup.sh for details.
break local -a setup_cmd=(./setup.sh "$@")
fi
done
# Prepare command to execute setup.sh
local -a setup_cmd=()
if [[ "$(id -u)" -ne 0 ]] && [[ "$is_dry_run" -eq 0 ]]; then
if ! command -v sudo >/dev/null 2>&1; then
log_error "sudo is required to run setup.sh. Please install sudo or run as root."
exit 1
fi
setup_cmd+=(sudo)
fi
setup_cmd+=(./setup.sh "$@")
# Reconnect stdin/stdout to /dev/tty before executing setup.sh if piped # Reconnect stdin/stdout to /dev/tty before executing setup.sh if piped
# (e.g. curl ... | bash), otherwise the interactive TUI receives no keystrokes # (e.g. curl ... | bash), otherwise the interactive TUI receives no keystrokes
+4 -4
View File
@@ -158,14 +158,14 @@ prompt_target_user() {
local chosen="" local chosen=""
local is_interactive=0 local is_interactive=0
if [[ -t 0 || -c /dev/tty ]] && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then if { [[ -t 0 ]] || has_tty; } && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
is_interactive=1 is_interactive=1
fi fi
if [[ "$is_interactive" -eq 1 ]]; then if [[ "$is_interactive" -eq 1 ]]; then
local tty_in="/dev/tty" local tty_in="/dev/tty"
local tty_out="/dev/tty" local tty_out="/dev/tty"
if [[ ! -c /dev/tty ]]; then if ! has_tty; then
tty_in="/dev/stdin" tty_in="/dev/stdin"
tty_out="/dev/stderr" tty_out="/dev/stderr"
fi fi
@@ -243,14 +243,14 @@ prompt_ollama_models_path() {
local default_path="${OLLAMA_MODELS_DEFAULT:-/mnt/Data/Software/ollama/models}" local default_path="${OLLAMA_MODELS_DEFAULT:-/mnt/Data/Software/ollama/models}"
local chosen="" local chosen=""
local is_interactive=0 local is_interactive=0
if [[ -t 0 || -c /dev/tty ]] && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then if { [[ -t 0 ]] || has_tty; } && [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && "${CI:-}" != "1" && "${AUTO_CONFIRM:-}" != "1" ]]; then
is_interactive=1 is_interactive=1
fi fi
if [[ "$is_interactive" -eq 1 ]]; then if [[ "$is_interactive" -eq 1 ]]; then
local tty_in="/dev/tty" local tty_in="/dev/tty"
local tty_out="/dev/tty" local tty_out="/dev/tty"
if [[ ! -c /dev/tty ]]; then if ! has_tty; then
tty_in="/dev/stdin" tty_in="/dev/stdin"
tty_out="/dev/stderr" tty_out="/dev/stderr"
fi fi
+51 -9
View File
@@ -37,10 +37,14 @@ ensure_term
# Show help text # Show help text
show_help() { show_help() {
cat <<EOF cat <<EOF
Usage: sudo ./setup.sh [OPTIONS] Usage: ./setup.sh [OPTIONS]
Debian Unstable Setup & Desktop Customization Framework Debian Unstable Setup & Desktop Customization Framework
Note: setup.sh elevates itself via sudo once needed, after any interactive
prompts have been answered. Run it as your normal desktop user - no need to
prefix it with 'sudo' yourself (though doing so still works).
Options: Options:
-a, --all Run all setup stages sequentially (00 through 10) -a, --all Run all setup stages sequentially (00 through 10)
-s, --stages <list> Comma-separated list of stages to run (e.g. --stages 01,02,04) -s, --stages <list> Comma-separated list of stages to run (e.g. --stages 01,02,04)
@@ -62,10 +66,10 @@ Available Stages:
10: Hyprland Desktop (LinuxBeginnings Auto-Installer) 10: Hyprland Desktop (LinuxBeginnings Auto-Installer)
Examples: Examples:
sudo ./setup.sh # Interactive TUI mode ./setup.sh # Interactive TUI mode
sudo ./setup.sh --all # Full automated install ./setup.sh --all # Full automated install
sudo ./setup.sh --stages 04,05 # Install only packages and fonts ./setup.sh --stages 04,05 # Install only packages and fonts
sudo ./setup.sh --stages 01 --dry-run # Dry-run stage 01 ./setup.sh --stages 01 --dry-run # Dry-run stage 01
EOF EOF
} }
@@ -121,10 +125,9 @@ if [[ -n "$CLI_USER" ]]; then
export TARGET_USER="$CLI_USER" export TARGET_USER="$CLI_USER"
fi fi
# Ensure root privileges unless dry-run # NOTE: Root privileges are intentionally NOT required here. All interactive
if [[ "$FLAG_DRY_RUN" -eq 0 ]]; then # prompts (whiptail TUI, target user, Ollama path) must run BEFORE escalating
require_root # to sudo - see the privilege escalation block below for why.
fi
# Determine stages to run # Determine stages to run
SELECTED_STAGES=() SELECTED_STAGES=()
@@ -181,6 +184,45 @@ if [[ -z "${TARGET_USER:-}" ]]; then
export TARGET_USER export TARGET_USER
fi fi
# Pre-resolve the Ollama models path here (while still unprivileged and
# directly attached to the terminal) if stage 06 will run, so stage
# 06-services.sh never has to prompt again after privilege escalation.
if [[ "$FLAG_DRY_RUN" -eq 0 ]] && [[ -z "${OLLAMA_MODELS:-}" ]]; then
for st in "${SELECTED_STAGES[@]}"; do
if [[ "$st" == "06" ]]; then
OLLAMA_MODELS="$(prompt_ollama_models_path)"
export OLLAMA_MODELS
break
fi
done
fi
# Escalate to root via sudo now that every interactive prompt (whiptail TUI,
# target user, Ollama path) has already been answered while directly attached
# to the terminal. sudo run from a non-interactive shell (e.g. 'curl | bash')
# does not reliably hand the controlling terminal's foreground process group
# to the command it execs, which leaves whiptail/read unable to receive any
# keystrokes. Running all interactive dialogs before this point, and passing
# the already-resolved choices through as flags/env vars, avoids ever needing
# terminal input again after the sudo boundary.
if [[ "$FLAG_DRY_RUN" -eq 0 ]] && [[ "$(id -u)" -ne 0 ]]; then
if ! command_exists sudo; then
log_error "sudo is required to run setup.sh. Please install sudo or run as root."
exit 1
fi
log_info "Elevating privileges via sudo to continue installation..."
joined_stages="$(IFS=,; echo "${SELECTED_STAGES[*]}")"
sudo_env_args=("TARGET_USER=$TARGET_USER")
if [[ -n "${OLLAMA_MODELS:-}" ]]; then
sudo_env_args+=("OLLAMA_MODELS=$OLLAMA_MODELS")
fi
exec sudo "${sudo_env_args[@]}" bash "$SCRIPT_DIR/setup.sh" --stages "$joined_stages" --user "$TARGET_USER"
fi
if [[ "$FLAG_DRY_RUN" -eq 0 ]]; then
require_root
fi
# Map stage identifier to script file # Map stage identifier to script file
get_stage_script() { get_stage_script() {
local stage_id="$1" local stage_id="$1"