#!/bin/bash # ============================================================================ # FWS — moteur de réparation d'une installation existante depuis le live # (« point de restauration à la Windows »). # # L'interface GRAPHIQUE est fws-recovery-gui (GTK3 python — le live embarque # python-gobject/gtk3 via Anaconda) ; elle pilote CE script en sous-commandes. # Sans argument : assistant CONSOLE (repli sans X, ou usage à la main). # # Sous-commandes (pour la GUI / scripts) : # scan → une ligne « périphérique|nom » par install FWS # repair → monte, répare, démonte ; sortie en continu # action ∈ grub|initramfs|pacman|pacman-update|all # shell → terminal DANS le système (xterm si DISPLAY) # # arch-chroot (arch-install-scripts, présent sur le live) gère les binds. # # Fonctionne depuis le LIVE (autre disque) ET depuis le SYSTÈME INSTALLÉ # lui-même (menu d'applications / entrée GRUB « Réparer FWS ») : si la cible # est la racine en cours d'exécution, on opère directement, sans mount/chroot. # ============================================================================ set -u MNT=/mnt/fws-recovery ROOTDEV="" DO_UPDATE="" SELF="" # cible = système courant ROOTSRC="$(findmnt -no SOURCE / 2>/dev/null)" # sur le live : overlay → jamais un lsblk say() { printf '\n==> %s\n' "$*"; } warn() { printf ' ⚠ %s\n' "$*"; } [ "$(id -u)" -eq 0 ] || { echo "root requis"; exit 1; } cleanup() { [ -n "$MNT" ] && [ "$MNT" != "/" ] && umount -R "$MNT" 2>/dev/null; } # Exécute DANS la cible : chroot si elle est montée, directement si c'est nous. in_target() { if [ -n "$SELF" ]; then "$@"; else arch-chroot "$MNT" "$@"; fi } # ── Moteur ─────────────────────────────────────────────────────────────────── scan_installs() { vgscan --mknodes >/dev/null 2>&1 vgchange -ay >/dev/null 2>&1 udevadm settle --timeout=10 >/dev/null 2>&1 # Le système en cours d'exécution d'abord (app lancée depuis l'OS installé). if [ ! -d /run/archiso ] && grep -qiE '^(ID|NAME)=.*fws' /etc/os-release 2>/dev/null; then printf '%s|%s (ce système)\n' "$ROOTSRC" \ "$(. /etc/os-release 2>/dev/null; echo "${PRETTY_NAME:-FWS}")" fi mkdir -p "$MNT" local dev name while read -r dev; do [ -b "$dev" ] || continue [ "$dev" = "$ROOTSRC" ] && continue # déjà listé ci-dessus mount -o ro "$dev" "$MNT" 2>/dev/null || continue if [ -f "$MNT/etc/os-release" ] && [ -d "$MNT/boot" ] \ && grep -qiE '^(ID|NAME)=.*fws' "$MNT/etc/os-release" 2>/dev/null; then name="$(. "$MNT/etc/os-release" 2>/dev/null; echo "${PRETTY_NAME:-FWS}")" printf '%s|%s\n' "$dev" "$name" fi umount "$MNT" 2>/dev/null done < <(lsblk -rpno NAME,TYPE | awk '$2=="part"||$2=="lvm"{print $1}') } # Monte la cible + /boot + ESP d'après son fstab. # Cas particulier : la cible EST la racine en cours d'exécution (app lancée # depuis l'OS installé, ou entrée GRUB « Réparer FWS ») → tout est déjà là, # on opère en place (MNT="" : les chemins "$MNT/…" pointent sur la vraie racine). mount_target() { ROOTDEV="$1" if [ "$ROOTDEV" = "$ROOTSRC" ] && [ ! -d /run/archiso ]; then SELF=1; MNT="" echo " cible = système en cours d'exécution (pas de montage)" return 0 fi mkdir -p "$MNT" mount "$ROOTDEV" "$MNT" || return 1 local mp src for mp in /boot /boot/efi; do src="$(awk -v mp="$mp" '$2==mp && $1!~/^#/{print $1; exit}' "$MNT/etc/fstab" 2>/dev/null)" [ -n "$src" ] || continue mount "$(findfs "$src" 2>/dev/null || echo "$src")" "$MNT$mp" 2>/dev/null \ && echo " $mp monté" || warn "$mp non monté (on continue)" done return 0 } fix_grub() { say "Réinstallation de GRUB…" if [ -d /sys/firmware/efi ]; then in_target grub-install --target=x86_64-efi \ --efi-directory=/boot/efi --bootloader-id=FWS --recheck || true in_target grub-install --target=x86_64-efi \ --efi-directory=/boot/efi --bootloader-id=FWS --removable --recheck else local disk disk="$(lsblk -spno NAME,TYPE "$ROOTDEV" 2>/dev/null \ | awk '$2=="disk"{print $1; exit}' | grep -oE '/dev/[a-z0-9]+')" [ -n "$disk" ] || { warn "disque BIOS introuvable"; return 1; } in_target grub-install --target=i386-pc --recheck "$disk" fi in_target grub-mkconfig -o /boot/grub/grub.cfg } fix_initramfs() { say "Régénération de l'initramfs (mkinitcpio -P)…" echo " NB : les avertissements « Possibly missing firmware for module … » sont" echo " NORMAUX sur Arch : firmware de matériel exotique (cartes SCSI/fibre…)" echo " non fourni par linux-firmware. Sans effet, sauf si tu AS ce matériel." in_target mkinitcpio -P } fix_pacman() { say "Réparation de pacman…" rm -f "$MNT/var/lib/pacman/db.lck" && echo " verrou db.lck retiré (s'il existait)" in_target pacman-key --init in_target pacman-key --populate archlinux if [ -n "$DO_UPDATE" ]; then say "Mise à jour complète (pacman -Syu)…" [ -z "$SELF" ] && cp -f /etc/resolv.conf "$MNT/etc/resolv.conf" 2>/dev/null in_target pacman -Syu --noconfirm fi } fix_all() { fix_initramfs && fix_grub; } # ── Sous-commandes (pilotées par fws-recovery-gui) ─────────────────────────── case "${1:-}" in scan) scan_installs exit 0 ;; repair) action="${2:?action manquante}"; dev="${3:?périphérique manquant}" trap cleanup EXIT say "Montage de $dev…" mount_target "$dev" || { warn "montage impossible"; exit 1; } rc=0 case "$action" in grub) fix_grub || rc=$? ;; initramfs) fix_initramfs || rc=$? ;; pacman) fix_pacman || rc=$? ;; pacman-update) DO_UPDATE=1; fix_pacman || rc=$? ;; all) fix_all || rc=$? ;; *) warn "action inconnue : $action"; rc=2 ;; esac cleanup; trap - EXIT [ "$rc" -eq 0 ] && say "Terminé ✔" || say "Échec (code $rc)" exit "$rc" ;; shell) dev="${2:?périphérique manquant}" trap cleanup EXIT mount_target "$dev" || { warn "montage impossible"; exit 1; } if [ -z "$SELF" ] && [ -n "${DISPLAY:-}" ] && command -v xterm >/dev/null 2>&1; then xterm -title "FWS — terminal dans le système (chroot)" \ -e arch-chroot "$MNT" /bin/bash else in_target /bin/bash fi cleanup; trap - EXIT exit 0 ;; ""|tui) : ;; # assistant console ci-dessous *) echo "usage : fws-recovery [scan|repair |shell ]"; exit 2 ;; esac # ── Assistant CONSOLE (repli sans X / usage manuel) ────────────────────────── trap cleanup EXIT say "Recherche des installations FWS…" mapfile -t CANDIDATES < <(scan_installs) if [ "${#CANDIDATES[@]}" -eq 0 ]; then warn "Aucune installation FWS trouvée. Disques visibles :" lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS read -rp "Entrée pour ouvrir un shell (exit pour redémarrer). " _ bash; exit 0 fi echo; i=1 for c in "${CANDIDATES[@]}"; do echo " $i) ${c%%|*} — ${c##*|}"; i=$((i+1)); done read -rp "Installation à réparer [1] : " pick; pick="${pick:-1}" dev="${CANDIDATES[$((pick-1))]%%|*}" [ -n "$dev" ] || exit 1 say "Montage de $dev…" mount_target "$dev" || { warn "montage impossible"; exit 1; } while :; do cat <<'MENU' ╔════════════════════════════════════════════════════════╗ ║ FWS — Réparation du système ║ ╠════════════════════════════════════════════════════════╣ ║ 1) Réinstaller GRUB (le système ne démarre plus) ║ ║ 2) Régénérer l'initramfs (mkinitcpio -P) ║ ║ 3) Réparer pacman (verrou, clés, mise à jour) ║ ║ 4) Tout réparer (initramfs + GRUB) ║ ║ 5) Ouvrir un shell DANS le système (chroot) ║ ║ q) Quitter et redémarrer ║ ╚════════════════════════════════════════════════════════╝ MENU read -rp " Choix : " choice case "$choice" in 1) fix_grub ;; 2) fix_initramfs ;; 3) DO_UPDATE="" read -rp " Mise à jour complète aussi (réseau) ? [o/N] " _u case "$_u" in o|O|oui) DO_UPDATE=1 ;; esac fix_pacman ;; 4) fix_all ;; 5) say "Shell chroot — « exit » pour revenir au menu." in_target /bin/bash ;; q|Q) break ;; *) warn "choix inconnu" ;; esac done cleanup; trap - EXIT say "Réparation terminée." read -rp "Redémarrer maintenant ? [O/n] " _r case "$_r" in n|N) bash ;; *) systemctl reboot ;; esac