diff --git a/README.md b/README.md index 899e9db..252e853 100644 --- a/README.md +++ b/README.md @@ -23,29 +23,31 @@ curl -fsSL https://gitea.creative-dragonslayer.de/Scripts/Setup/raw/branch/main/ ### 2. Manuelles Klonen & Starten Alternativ kann das Repository manuell geklont und ausgeführt werden: ```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: ```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 Starte das grafische Terminal-Menü (`whiptail`) zur flexiblen Auswahl einzelner Module: ```bash -sudo ./setup.sh +./setup.sh ``` ### 4. Vollautomatischer CLI-Modus Führe alle Phasen (`00` bis `10`) ohne Interaktion aus: ```bash -sudo ./setup.sh --all +./setup.sh --all ``` ### 5. Gezielte Modulauswahl Installiere nur bestimmte Phasen (z. B. nur Paketlisten und Schriften): ```bash -sudo ./setup.sh --stages 04,05 +./setup.sh --stages 04,05 ``` ### 6. Simulationslauf (Dry-Run) diff --git a/install.sh b/install.sh index 58ec6c7..ee9c41c 100755 --- a/install.sh +++ b/install.sh @@ -110,25 +110,12 @@ main() { log_success "Repository cloned successfully. Starting setup orchestrator..." - # Check if dry-run flag is requested - local is_dry_run=0 - for arg in "$@"; do - if [[ "$arg" == "-d" || "$arg" == "--dry-run" ]]; then - is_dry_run=1 - break - 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 "$@") + # setup.sh itself elevates to root via sudo once it needs it - after all + # interactive TUI/prompts have already run as the current user. Escalating + # here instead would wrap the whiptail dialogs in sudo, which (when sudo is + # invoked from this non-interactive piped shell) leaves them unable to + # receive keystrokes. See setup.sh for details. + local -a setup_cmd=(./setup.sh "$@") # Reconnect stdin/stdout to /dev/tty before executing setup.sh if piped # (e.g. curl ... | bash), otherwise the interactive TUI receives no keystrokes diff --git a/lib/utils.sh b/lib/utils.sh index d98628d..feceab0 100755 --- a/lib/utils.sh +++ b/lib/utils.sh @@ -158,14 +158,14 @@ prompt_target_user() { local chosen="" 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 fi if [[ "$is_interactive" -eq 1 ]]; then local tty_in="/dev/tty" local tty_out="/dev/tty" - if [[ ! -c /dev/tty ]]; then + if ! has_tty; then tty_in="/dev/stdin" tty_out="/dev/stderr" fi @@ -243,14 +243,14 @@ prompt_ollama_models_path() { local default_path="${OLLAMA_MODELS_DEFAULT:-/mnt/Data/Software/ollama/models}" local chosen="" 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 fi if [[ "$is_interactive" -eq 1 ]]; then local tty_in="/dev/tty" local tty_out="/dev/tty" - if [[ ! -c /dev/tty ]]; then + if ! has_tty; then tty_in="/dev/stdin" tty_out="/dev/stderr" fi diff --git a/setup.sh b/setup.sh index 1ca0e89..f62fbec 100755 --- a/setup.sh +++ b/setup.sh @@ -37,10 +37,14 @@ ensure_term # Show help text show_help() { cat < 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) Examples: - sudo ./setup.sh # Interactive TUI mode - sudo ./setup.sh --all # Full automated install - sudo ./setup.sh --stages 04,05 # Install only packages and fonts - sudo ./setup.sh --stages 01 --dry-run # Dry-run stage 01 + ./setup.sh # Interactive TUI mode + ./setup.sh --all # Full automated install + ./setup.sh --stages 04,05 # Install only packages and fonts + ./setup.sh --stages 01 --dry-run # Dry-run stage 01 EOF } @@ -121,10 +125,9 @@ if [[ -n "$CLI_USER" ]]; then export TARGET_USER="$CLI_USER" fi -# Ensure root privileges unless dry-run -if [[ "$FLAG_DRY_RUN" -eq 0 ]]; then - require_root -fi +# NOTE: Root privileges are intentionally NOT required here. All interactive +# prompts (whiptail TUI, target user, Ollama path) must run BEFORE escalating +# to sudo - see the privilege escalation block below for why. # Determine stages to run SELECTED_STAGES=() @@ -181,6 +184,45 @@ if [[ -z "${TARGET_USER:-}" ]]; then export TARGET_USER 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 get_stage_script() { local stage_id="$1"