#!/bin/bash export TERM="${TERM:-xterm}" set -Eeuo pipefail # ───────────────────────────────────────────────────────────── # PinCabOS - go-pincabos.sh # Orchestrateur de stages - Isolation TTY & Sécurisation LightDM # Correction : Autologin TTY automatique & Suppression erreurs wall # Branding: Karots Sugarpie # ───────────────────────────────────────────────────────────── ORANGE=$'\033[38;5;208m' PURPLE=$'\033[38;5;93m' RED=$'\033[1;31m' CYAN=$'\033[38;5;51m' GREEN=$'\033[1;32m' YELLOW=$'\033[1;33m' WHITE=$'\033[1;37m' GRAY=$'\033[38;5;245m' RESET=$'\033[0m' INSTALL_DIR="/opt/pincabos/install" LOG_DIR="/opt/pincabos/logs" STATE_DIR="/opt/pincabos/state" FLAG_DIR="${STATE_DIR}/go-pincabos-flags" STATE_FILE="${STATE_DIR}/go-pincabos.stage" LOG="${LOG_DIR}/go-pincabos-$(date +%Y%m%d-%H%M%S).log" PCO_RESUME_MODE="${1:-}" SCRIPT_00="${INSTALL_DIR}/00-install-admin.sh" SCRIPT_01="${INSTALL_DIR}/01-install-system.sh" SCRIPT_02="${INSTALL_DIR}/02-install-engine.sh" SCRIPT_03="${INSTALL_DIR}/03-install-check.sh" SCRIPT_99="${INSTALL_DIR}/99-clean-depot.sh" mkdir -p "$LOG_DIR" "$STATE_DIR" "$FLAG_DIR" exec > >(tee -a "$LOG") 2>&1 # ── Logo ASCII + bandeau standard PinCabOS ────────────────────────────────── pincabos_install_header() { local script_name script_name="$(basename "$0")" echo perl -CS -Mutf8 -e ' binmode(STDOUT, ":utf8"); my $orange = "\033[38;5;208m"; my $purple = "\033[38;5;93m"; my $reset = "\033[0m"; my @logo = ( "██████╗ ██╗███╗ ██╗ ██████╗ █████╗ ██████╗ ██████╗ ███████╗", "██╔══██╗██║████╗ ██║██╔════╝██╔══██╗██╔══██╗██╔═══██╗██╔════╝", "██████╔╝██║██╔██╗ ██║██║ ███████║██████╔╝██║ ██║███████╗", "██╔═══╝ ██║██║╚██╗██║██║ ██╔══██║██╔══██╗██║ ██║╚════██║", "██║ ██║██║ ╚████║╚██████╗██║ ██║██████╔╝╚██████╔╝███████║", "╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚══════╝" ); for my $line (@logo) { $line =~ s/(█+)/$orange$1$reset/g; $line =~ s/([╔╗╚╝═║]+)/$purple$1$reset/g; print "$line\n"; } ' echo printf "${ORANGE} Ultimate VPinball Linux Cabinet System${RESET}\n" printf "${CYAN} VPX • VPinFE • DOF • WebApp${RESET}\n" echo printf "${CYAN}────────────────────────────────────────────────────────────────${RESET}\n" printf "${GRAY} Script :${RESET} ${WHITE}%s${RESET}\n" "$script_name" printf "${GRAY} By :${RESET} ${WHITE}Karots Sugarpie${RESET}\n" printf "${GRAY} Hostname :${RESET} %s\n" "$(hostname)" printf "${GRAY} User :${RESET} %s\n" "$(whoami)" printf "${CYAN}────────────────────────────────────────────────────────────────${RESET}\n" echo } pincabos_install_header # ── Helpers standard pco_* ────────────────────────────────────────────────── PCO_CURRENT_STEP="" PCO_STEPS=() PCO_STATUS=() PCO_FINAL_SUMMARY_DONE=0 pco_line() { printf "${CYAN}────────────────────────────────────────────────────────────────${RESET}\n" } pco_split_step() { local raw="${1:-}" PCO_STEP_NUM="**" PCO_STEP_TITLE="$raw" if [[ "$raw" =~ ^([0-9]+)\)\ (.*)$ ]]; then PCO_STEP_NUM="${BASH_REMATCH[1]}" PCO_STEP_TITLE="${BASH_REMATCH[2]}" fi } pco_title() { local raw="$1" pco_split_step "$raw" printf "\n${CYAN}─[%s]─►${ORANGE} %s ${CYAN}◄────${RESET}\n\n" "$PCO_STEP_NUM" "$PCO_STEP_TITLE" } pco_step() { local step="$1" PCO_CURRENT_STEP="$step" PCO_STEPS+=("$step") PCO_STATUS+=("RUNNING") pco_title "$step" } pco_check() { local step="$1" local i pco_split_step "$step" for i in "${!PCO_STEPS[@]}"; do if [ "${PCO_STEPS[$i]}" = "$step" ]; then PCO_STATUS[$i]="OK" fi done printf "${GREEN}─[%s]─► %s ◄──── Check [√]${RESET}\n" "$PCO_STEP_NUM" "$PCO_STEP_TITLE" } pco_fail_current() { local i if [ -n "${PCO_CURRENT_STEP:-}" ]; then for i in "${!PCO_STEPS[@]}"; do if [ "${PCO_STEPS[$i]}" = "$PCO_CURRENT_STEP" ]; then PCO_STATUS[$i]="FAIL" fi done fi } pco_final_summary() { local exit_code="${1:-0}" local i local failed=0 if [ "${PCO_FINAL_SUMMARY_DONE:-0}" -eq 1 ]; then return 0 fi PCO_FINAL_SUMMARY_DONE=1 echo pco_line printf "${ORANGE}Résumé complet des checks PinCabOS - %s${RESET}\n" "$(basename "$0")" pco_line for i in "${!PCO_STEPS[@]}"; do pco_split_step "${PCO_STEPS[$i]}" case "${PCO_STATUS[$i]}" in OK) printf "${GREEN}─[%s]─► %s ◄──── Check [√]${RESET}\n" "$PCO_STEP_NUM" "$PCO_STEP_TITLE" ;; FAIL|RUNNING) failed=1 printf "${RED}─[%s]─► %s ◄──NOGOOD [X]${RESET}\n" "$PCO_STEP_NUM" "$PCO_STEP_TITLE" ;; *) failed=1 printf "${RED}─[%s]─► %s ◄──NOGOOD [X]${RESET}\n" "$PCO_STEP_NUM" "$PCO_STEP_TITLE" ;; esac done pco_line if [ "$failed" -eq 0 ] && [ "$exit_code" -eq 0 ]; then printf "${GREEN}Tous les checks sont réussis. PinCabOS GO orchestration OK.${RESET}\n" else printf "${RED}Un ou plusieurs checks ont échoué. Code retour: %s${RESET}\n" "$exit_code" fi pco_line echo } pco_on_error() { local exit_code="$?" pco_fail_current pco_final_summary "$exit_code" exit "$exit_code" } trap pco_on_error ERR trap 'pco_final_summary "$?"' EXIT get_stage() { if [ -f "$STATE_FILE" ]; then cat "$STATE_FILE" | tr -d ' \n\r' else echo "START" fi } set_stage() { echo "$1" > "$STATE_FILE" } mark_done() { touch "${FLAG_DIR}/done-${1}.flag" } # Assure que la console TTY1 se log automatiquement sans demander de mot de passe au reboot ensure_tty_autologin() { echo "Configuration de l'autoresume automatique sur le TTY1..." mkdir -p /etc/systemd/system/getty@tty1.service.d # Pendant l'installation, TTY1 doit lancer la reprise PinCabOS en foreground. # Si aucun stage actif existe, le wrapper ouvre un shell root normal. if [ -x /usr/local/sbin/pincabos-autoresume-console.sh ]; then cat << 'GETTYEOF' > /etc/systemd/system/getty@tty1.service.d/override.conf [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear --login-program /usr/local/sbin/pincabos-autoresume-console.sh %I $TERM GETTYEOF else cat << 'GETTYEOF' > /etc/systemd/system/getty@tty1.service.d/override.conf [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear %I $TERM GETTYEOF fi systemctl daemon-reload # IMPORTANT: # Ne pas redémarrer getty@tty1 ici. # Si ce script tourne déjà depuis le TTY1 via --login-program, # un restart du getty tue la reprise en cours et crée un loop autoresume. echo "Autologin/autoresume TTY configuré. Pas de restart getty pendant l'installation." } # Supprime l'autologin de la console à la toute fin disable_tty_autologin() { echo "Restauration de la sécurité de la console (Suppression de l'autologin TTY)..." rm -f /etc/systemd/system/getty@tty1.service.d/override.conf systemctl daemon-reload } pco_restore_default_plymouth_theme() { echo "Restauration du thème Plymouth PinCabOS par défaut..." local theme="/usr/share/plymouth/themes/pincabos/pincabos.plymouth" if [ -s "$theme" ]; then update-alternatives --install /usr/share/plymouth/themes/default.plymouth default.plymouth "$theme" 900 update-alternatives --set default.plymouth "$theme" if command -v plymouth-set-default-theme >/dev/null 2>&1; then plymouth-set-default-theme pincabos || true fi update-initramfs -u || true if command -v update-grub >/dev/null 2>&1; then update-grub || true fi readlink -f /usr/share/plymouth/themes/default.plymouth || true echo "Thème Plymouth final restauré: pincabos" else echo "WARN: thème Plymouth PinCabOS absent, restauration ignorée: $theme" fi } # Verrouille LightDM pour empêcher son exécution pendant l'installation en TTY force_console_boot() { touch "${FLAG_DIR}/force-console-next-boot.flag" echo "Application du verrou strict sur LightDM (Mode TTY Forcé)..." systemctl stop lightdm 2>/dev/null || true systemctl disable lightdm 2>/dev/null || true systemctl mask lightdm systemctl set-default multi-user.target # On force l'autologin de la console à chaque fois qu'on prépare un boot console ensure_tty_autologin } # Libère : Redonne le contrôle à LightDM uniquement au REBOOT_FINAL force_graphical_boot() { echo "Désactivation du verrou et activation définitive de l'interface graphique..." systemctl unmask lightdm systemctl enable lightdm systemctl set-default graphical.target # À la fin seulement: remettre le thème Plymouth PinCabOS par défaut. pco_restore_default_plymouth_theme # On retire l'autologin automatique de la console TTY pour la production finale disable_tty_autologin } # Commande de reboot nettoyée des erreurs 'wall' reboot_now() { echo -e "${YELLOW}Reboot requis: $1${RESET}" echo "Redémarrage demandé. Ce bloc ne doit jamais retourner dans la boucle d'installation." echo "Si le reboot ne part pas immédiatement, fallback shutdown/reboot sera tenté." sync || true sleep 2 systemctl reboot --no-wall 2>/dev/null || true # Donner une chance à systemd de prendre le contrôle. for i in 10 9 8 7 6 5 4 3 2 1; do echo "Attente reboot systemd... $i" sleep 1 done echo "WARN: systemctl reboot est revenu sans redémarrer. Fallback shutdown -r now." shutdown -r now 2>/dev/null || true for i in 5 4 3 2 1; do echo "Attente reboot shutdown... $i" sleep 1 done echo "WARN: shutdown est revenu sans redémarrer. Fallback reboot -f." reboot -f 2>/dev/null || true # Protection anti-loop absolue: # si aucun reboot ne fonctionne, on sort du script au lieu de continuer le while true. echo "NOGOOD: commande reboot retournée. Arrêt sécurité anti-loop." echo "Stage courant conservé dans: $STATE_FILE" echo "Relancer manuellement après correction avec: /opt/pincabos/install/go-pincabos.sh --resume" exit 0 } run_installer_script() { local step_num="$1" local step_name="$2" local script_path="$3" local rc=0 local script_base="" local latest_log="" script_base="$(basename "$script_path")" pco_title "Étape ${step_num}: ${step_name}" echo printf "${CYAN}────────────────────────────────────────────────────────────────${RESET}\n" printf "${ORANGE}[GO-RUN] Stage courant: %s${RESET}\n" "$(get_stage)" printf "${ORANGE}[GO-RUN] Script: %s${RESET}\n" "$script_path" printf "${ORANGE}[GO-RUN] Logs : %s${RESET}\n" "$LOG_DIR" printf "${CYAN}────────────────────────────────────────────────────────────────${RESET}\n" echo if [ ! -s "$script_path" ]; then printf "${RED}[GO-FAIL] Script introuvable ou vide: %s${RESET}\n" "$script_path" exit 1 fi chmod +x "$script_path" set +e bash "$script_path" rc="$?" set -e if [ "$rc" -ne 0 ]; then echo printf "${RED}────────────────────────────────────────────────────────────────${RESET}\n" printf "${RED}[GO-FAIL] %s a échoué avec code retour: %s${RESET}\n" "$script_base" "$rc" printf "${RED}[GO-FAIL] Stage conservé: %s${RESET}\n" "$(get_stage)" printf "${RED}────────────────────────────────────────────────────────────────${RESET}\n" echo echo "=== [GO-FAIL] Derniers logs disponibles ===" ls -1t "$LOG_DIR" 2>/dev/null | head -n 20 || true echo case "$script_base" in 02-install-engine.sh) latest_log="$(ls -1t "$LOG_DIR"/02-install-engine-*.log 2>/dev/null | head -n 1)" ;; 03-install-check.sh) latest_log="$(ls -1t "$LOG_DIR"/03-install-check-*.log 2>/dev/null | head -n 1)" ;; 00-install-admin.sh) latest_log="$(ls -1t "$LOG_DIR"/00-install-admin-*.log 2>/dev/null | head -n 1)" ;; 01-install-system.sh) latest_log="$(ls -1t "$LOG_DIR"/01-install-system-*.log 2>/dev/null | head -n 1)" ;; 99-clean-depot.sh) latest_log="$(ls -1t "$LOG_DIR"/99-clean-depot-*.log 2>/dev/null | head -n 1)" ;; *) latest_log="" ;; esac if [ -n "${latest_log:-}" ] && [ -s "$latest_log" ]; then echo printf "${YELLOW}=== [GO-FAIL] Tail du log: %s ===${RESET}\n" "$latest_log" tail -n 220 "$latest_log" || true echo else echo "WARN: aucun log spécifique trouvé pour $script_base" fi echo printf "${YELLOW}Le script arrête ici pour éviter la loop.${RESET}\n" printf "${YELLOW}Donne-moi le premier NOGOOD/ERREUR/FAILED/Traceback visible ci-dessus.${RESET}\n" echo exit "$rc" fi pco_check "Script ${step_num} - ${step_name}" } pco_main_orchestration() { while true; do STAGE=$(get_stage) case "$STAGE" in START) set_stage "RUN_00" ;; RUN_00) run_installer_script "1" "Configuration Admin - 00-install-admin.sh" "$SCRIPT_00" mark_done "RUN_00" set_stage "RUN_01" ;; RUN_01) run_installer_script "2" "Installation Système - 01-install-system.sh" "$SCRIPT_01" mark_done "RUN_01" # Nettoyage pré-ISO sans reboot. # Objectif: réduire la taille de l'image figée avant le gel ISO. run_installer_script "3" "Pré-cleanup ISO - 99-clean-depot.sh sans reboot" "$SCRIPT_99" mark_done "PRE_ISO_CLEANUP" # Pause pour l'ISO (demande d'appui sur une touche) pco_step "4) PAUSE MAINTENANCE - CAPTURE ISO POSSIBLE" echo "Le système est prêt pour le gel de l'image." read -n 1 -s -r -p "Appuyez sur n'importe quelle touche pour continuer..." || true echo pco_check "4) PAUSE MAINTENANCE - CAPTURE ISO POSSIBLE" set_stage "REBOOT_AFTER_01" force_console_boot reboot_now "Fin du stage 01. Passage sécurisé en TTY." ;; REBOOT_AFTER_01) # Après le reboot post-01, on passe directement à l'engine. # 99-clean-depot.sh est exécuté une seule fois dans 03-install-check.sh. set_stage "RUN_02" ;; RUN_02) force_console_boot run_installer_script "5" "Exécution 02-install-engine.sh" "$SCRIPT_02" mark_done "RUN_02" set_stage "REBOOT_AFTER_02" reboot_now "Fin du stage 02. Reboot intermédiaire." ;; REBOOT_AFTER_02) set_stage "RUN_03" ;; RUN_03) run_installer_script "6" "Vérification Finale - 03-install-check.sh" "$SCRIPT_03" mark_done "RUN_03" set_stage "FINAL_REBOOT" ;; FINAL_REBOOT) pco_step "7) PinCabOS GO - Fin de l'installation" # L'installation est terminée, on remet l'interface graphique par défaut force_graphical_boot pco_check "7) PinCabOS GO - Fin de l'installation" rm -f "$STATE_FILE" reboot_now "Démarrage de la borne en mode graphique utilisateur (LightDM)." exit 0 ;; *) echo "Stage inconnu: $STAGE" exit 1 ;; esac done } pco_main_orchestration