#!/bin/sh
# Lancé par startx (cf. /root/.zprofile), uniquement dans le live FWS.
if [ ! -d /run/archiso ]; then
exit 0
fi
xsetroot -solid "#1a1a2e"
# Openbox en arrière-plan : gestionnaire de fenêtres pour l'installateur.
openbox &
_OB_PID=$!
# Chantier C : lance l'installateur Anaconda en mode live s'il est présent.
# « liveinst » re-exécute anaconda (ici on est déjà root → pas de pkexec/polkit).
#
# DEBUG (premier run réel) : on CAPTURE stdout+stderr — un plantage d'import
# précoce de Python n'apparaît QUE là, pas dans /tmp/anaconda.log — et, si
# liveinst quitte en erreur, on regroupe tous les logs Anaconda et on les AFFICHE
# (zenity) au lieu de laisser startx rendre la main → .zprofile relancerait X en
# boucle (symptôme « fond bleu qui clignote/reboote »). On NE rend jamais la main
# à startx : on reste sur Openbox pour lire les logs / rebooter proprement.
if command -v liveinst >/dev/null 2>&1; then
# Payload LiveOS : avec --liveinst (passé par liveinst), Anaconda IGNORE le
# payload du kickstart (anaconda.py teste opts.liveinst AVANT le type du
# payload DBus — un « liveimg » dans le ks serait du code mort) et détecte
# l'image live sur des chemins Fedora/dracut : /dev/mapper/live-base,
# /dev/mapper/live-osimg-min ou /run/rootfsbase — tous ABSENTS d'archiso →
# « SourceSetupError: No Live OS image found! » = crash au démarrage
# (reproduit hors VM dans un chroot de l'airootfs). On expose donc le
# squashfs archiso (monté ro sur /run/archiso/airootfs) là où Anaconda le
# cherche : findmnt y résout le loop device → montage → rsync vers la cible.
if [ -d /run/archiso/airootfs ] && [ ! -e /run/rootfsbase ]; then
mkdir -p /run/rootfsbase
mount --bind /run/archiso/airootfs /run/rootfsbase || rmdir /run/rootfsbase
fi
# GARDE-FOU RAM : le live n'a que le zram comme marge — en dessous de
# ~2 Go, Anaconda (GUI Python + squashfs) peut geler la machine en plein
# « Préparation de l'installateur ». On prévient AVANT plutôt que de
# laisser l'utilisateur croire à un bug.
_MEMKB=$(awk '/^MemTotal:/{print $2}' /proc/meminfo 2>/dev/null)
if [ -n "$_MEMKB" ] && [ "$_MEMKB" -lt 2000000 ] && command -v zenity >/dev/null 2>&1; then
zenity --warning --width=460 --title="FWS — mémoire insuffisante ?" \
--text="Cette machine n'a que $(( _MEMKB / 1024 )) Mo de RAM.\n\nL'installateur graphique peut geler en dessous de 2 Go.\nEn VM : augmente la mémoire (4 Go recommandés) puis redémarre.\n\nOK pour tenter quand même." 2>/dev/null
fi
# ÉCRAN D'ATTENTE immédiat : le démarrage d'anaconda peut prendre 2 à
# 4 minutes (observé en VM VMware : un worker udev se bloque au boot et
# n'est tué par udevd qu'après 45 s — udev.event_timeout). Sans feedback,
# l'utilisateur croit à une panne alors que TOUT va bien. Le splash montre
# EN DIRECT ce que fait anaconda : un nourrisseur écrit sur stdin de
# zenity --progress (« # texte » = mise à jour du libellé) le log anaconda
# le plus récemment modifié + son dernier message + un chrono. Si les logs
# ne bougent plus (vrai blocage), le libellé le DIT (au lieu d'une barre
# qui pulse pour rien) et, au-delà de 4 min d'inactivité, on collecte
# journal+dmesg dans /tmp/fws-stall.txt et on l'affiche : l'écran devient
# lui-même le rapport de bug. Le nourrisseur meurt par SIGPIPE quand le
# splash est tué à la sortie de liveinst.
_SPLASH_PID=""
if command -v zenity >/dev/null 2>&1; then
(
_el=0; _quiet=0; _prevstamp=""
while :; do
sleep 3
# GUI d'anaconda à l'écran (marqueur ui.gui) → le splash n'a
# plus de raison d'être et le watchdog DOIT se désarmer : des
# logs silencieux pendant que l'utilisateur remplit les écrans
# sont NORMAUX (bug vécu : le rapport « préparation bloquée »
# s'affichait PAR-DESSUS l'installateur au bout de 4 min).
if grep -q 'ui\.gui' /tmp/anaconda.log 2>/dev/null; then
pkill -f 'zenity --progress' 2>/dev/null
exit 0
fi
# Compteurs en itérations (pas d'heure murale : l'horloge du
# guest SAUTE en VM — resynchro RTC — et gonflerait les âges,
# vu en vrai : « 242 s d'inactivité » 90 s après le boot).
_el=$(( _el + 3 ))
_last=$(ls -t /tmp/storage.log /tmp/anaconda.log /tmp/program.log /tmp/dbus.log 2>/dev/null | head -n1)
if [ -z "$_last" ]; then
printf '# Démarrage d'"'"'Anaconda… (%d min %02d s)\n' $(( _el / 60 )) $(( _el % 60 )) || exit 0
continue
fi
_stamp=$(stat -c %Y "$_last" 2>/dev/null)
if [ "$_stamp" = "$_prevstamp" ]; then
_quiet=$(( _quiet + 3 ))
else
_quiet=0; _prevstamp="$_stamp"
fi
# Rebranding d'affichage : anaconda (installateur Fedora) parle
# de Fedora/Arch dans ses logs — on ne montre pas ça à l'écran.
_msg=$(tail -n1 "$_last" 2>/dev/null | tr -d '\r' \
| sed -E 's/Fedora( Linux)?/FWS/g; s/Arch Linux/FWS/g; s/\/FWS/g' \
| cut -c1-90)
if [ "$_quiet" -ge 240 ] && [ ! -f /tmp/fws-stall.txt ]; then
{
echo "=== FWS : préparation bloquée (aucune activité log depuis ${_quiet}s) ==="
echo "--- dernier log actif : $_last"; tail -n 25 "$_last" 2>/dev/null
echo "--- udev (workers bloqués ?)"; udevadm settle --timeout=0 2>&1
echo "--- journal (fin)"; journalctl -b --no-pager 2>/dev/null | tail -n 60
echo "--- dmesg (fin)"; dmesg 2>/dev/null | tail -n 40
} > /tmp/fws-stall.txt 2>&1
zenity --text-info --title="FWS — préparation bloquée : diagnostic" \
--filename=/tmp/fws-stall.txt --width=1000 --height=650 2>/dev/null &
fi
if [ "$_quiet" -ge 60 ]; then
printf '# ⚠ Aucune activité depuis %d s (%s) — dernier message : %s\n' "$_quiet" "$(basename "$_last")" "$_msg" || exit 0
else
printf '# Préparation (%d min %02d s) — %s : %s\n' $(( _el / 60 )) $(( _el % 60 )) "$(basename "$_last")" "$_msg" || exit 0
fi
done
) | zenity --progress --pulsate --no-cancel \
--title="FWS" \
--text="Préparation de l'installateur FWS…\n\nAnalyse du matériel et des disques en cours.\nEncore une à deux minutes, c'est normal." \
--width=560 2>/dev/null &
_SPLASH_PID=$!
fi
# Hub Résumé SANS le libellé « We won't touch your disks… » : notre glade
# modifié NE PEUT PAS être livré directement à son chemin final —
# mkarchiso pose l'overlay airootfs AVANT pacstrap, et pacman refuse
# alors d'installer anaconda par-dessus (« exists in filesystem »). On le
# livre donc sous /usr/local/share/fws et on ÉCRASE ici, au boot du live
# (l'overlay du live est inscriptible), le fichier du paquet.
if [ -f /usr/local/share/fws/anaconda-summary-hub.glade ]; then
cp -f /usr/local/share/fws/anaconda-summary-hub.glade \
/usr/share/anaconda/ui/hubs/summary.glade 2>/dev/null || true
fi
# BRANDING : sans /.buildstamp, anaconda s'appelle « anaconda bluesky »
# (défauts de pyanaconda/core/product.py) et ses logs parlent de Fedora.
# Les variables d'environnement ANACONDA_* ont priorité → l'en-tête
# devient « FWS 1.0 INSTALLATION » et le log « anaconda … for FWS started ».
# (Les mentions Fedora PROFONDES dans les logs — GeoIP, profils — restent :
# ce sont des chaînes internes de l'installateur, sans effet sur l'install.)
export ANACONDA_PRODUCTNAME="$(. /etc/os-release 2>/dev/null; echo "${NAME:-FWS}")"
export ANACONDA_PRODUCTVERSION="$(. /etc/os-release 2>/dev/null; echo "${VERSION_ID:-1.0}")"
export ANACONDA_ISFINAL=true
LIVEINST_LOG=/tmp/fws-liveinst.log
# Bus D-Bus de SESSION : au démarrage (--liveinst), inhibit_screensaver()
# de pyanaconda se connecte au bus de session. S'il n'y en a AUCUN (notre
# startx root minimal n'en garantit pas), Gio lève une GLib.GError que
# pyanaconda n'attrape PAS (il n'attrape que DBusError) → crash immédiat
# (2e crash reproduit hors VM). dbus-run-session fournit un bus de session
# pour toute la durée de l'installateur ; avec un bus présent, l'absence du
# service ScreenSaver est attrapée (simple warning dans anaconda.log).
if command -v dbus-run-session >/dev/null 2>&1 && [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
dbus-run-session -- liveinst >"$LIVEINST_LOG" 2>&1
else
liveinst >"$LIVEINST_LOG" 2>&1
fi
rc=$?
# liveinst terminé (install finie ou échec) : le splash n'a plus de raison
# d'être — kill de zenity ; le nourrisseur meurt de SIGPIPE à son
# prochain printf. Idem pour un éventuel dialogue de diagnostic de stall.
if [ -n "$_SPLASH_PID" ]; then
pkill -P "$_SPLASH_PID" 2>/dev/null
kill "$_SPLASH_PID" 2>/dev/null
fi
pkill -f "fws-stall.txt" 2>/dev/null
# ATTENTION : liveinst NE propage PAS le code de sortie d'anaconda — il
# enchaîne son propre nettoyage et sort souvent 0 même quand anaconda a
# planté. On ne se fie donc PAS à $rc : en phase debug on regroupe TOUS les
# logs et on les affiche systématiquement (zenity). Le traceback Python d'un
# crash d'import précoce n'est QUE dans $LIVEINST_LOG (stderr capturé).
# Log du %post chrooté (grub-install / mkinitcpio) = CE qui rend le disque
# bootable. Le kickstart en copie une version dans le LIVE
# (/tmp/fws-post-chroot.log, via un %post --nochroot final, cible encore
# montée → fiable). SECOURS si absent : on réactive le LVM (pvscan/vgscan
# rescannent les PV qu'Anaconda a désactivés) et on remonte le disque pour
# le lire.
if [ ! -s /tmp/fws-post-chroot.log ]; then
pvscan --cache >/dev/null 2>&1 || true
vgscan --mknodes >/dev/null 2>&1 || true
vgchange -ay >/dev/null 2>&1 || true
udevadm settle --timeout=10 >/dev/null 2>&1 || true
mkdir -p /run/fws-installed
for _dev in /dev/mapper/*-root /dev/mapper/* /dev/sd*[0-9] /dev/vd*[0-9] /dev/nvme*n*p*; do
[ -b "$_dev" ] || continue
mount -o ro "$_dev" /run/fws-installed 2>/dev/null || continue
if [ -f /run/fws-installed/var/log/fws-post.log ]; then
cp -f /run/fws-installed/var/log/fws-post.log /tmp/fws-post-chroot.log 2>/dev/null
umount /run/fws-installed 2>/dev/null || true
break
fi
umount /run/fws-installed 2>/dev/null || true
done
fi
DBG=/tmp/fws-debug.txt
{
echo "===== liveinst sorti (code $rc) — logs Anaconda ====="
echo
echo "########## sortie liveinst (stdout+stderr) ##########"
cat "$LIVEINST_LOG" 2>/dev/null
echo
echo "########## %post chrooté = grub-install / mkinitcpio ##########"
echo "########## >>> C'EST CE BLOC QUI DIT SI LE DISQUE VA BOOTER <<< ##########"
cat /tmp/fws-post-chroot.log 2>/dev/null || echo "(log introuvable)"
echo
echo "########## %post --nochroot (/tmp/fws-post-nochroot.log) ##########"
cat /tmp/fws-post-nochroot.log 2>/dev/null || echo "(absent)"
echo
echo "########## Récapitulatif (page GUI fws_recap, avant install) ##########"
cat /tmp/fws-hw-summary.txt 2>/dev/null || echo "(absent — page récap pas atteinte)"
for f in /tmp/anaconda.log /tmp/syslog /tmp/program.log \
/tmp/storage.log /tmp/dbus.log /tmp/packaging.log /tmp/X.log; do
echo
echo "########## $f ##########"
tail -n 200 "$f" 2>/dev/null || echo "(absent)"
done
} >"$DBG" 2>&1
# Succès d'install = les DEUX étapes qui rendent le disque bootable ont
# réussi dans le %post : mkinitcpio (« Initcpio image generation successful »)
# ET grub-install (« Installation finished. No error reported. »). Si oui →
# on montre un écran de RÉUSSITE et on éteint (l'utilisateur retire l'ISO puis
# rallume sur FWS). Sinon → on affiche tous les logs de debug (comportement
# debug). Les lignes cosmétiques (load_policy, warnings GTK…) n'influent PAS
# sur cette détection : on ne regarde que le log du %post.
if grep -q 'Initcpio image generation successful' /tmp/fws-post-chroot.log 2>/dev/null \
&& grep -q 'Installation finished. No error reported.' /tmp/fws-post-chroot.log 2>/dev/null; then
if command -v zenity >/dev/null 2>&1; then
zenity --info --width=480 --title="FWS — installation réussie ✅" \
--text="FWS est installé sur le disque.\n\n1. Clique OK pour éteindre la machine.\n2. Retire l'ISO (lecteur CD/DVD).\n3. Rallume → FWS démarre." \
2>/dev/null
fi
systemctl poweroff
else
# Échec (ou install non terminée) : on montre les logs pour diagnostiquer.
if command -v zenity >/dev/null 2>&1; then
zenity --text-info --title="FWS — logs Anaconda (sortie $rc)" \
--filename="$DBG" --width=1100 --height=720 2>/dev/null &
fi
wait "$_OB_PID"
fi
else
echo "[FWS] Anaconda absent de l'ISO — à builder (setup-aur.sh) + ajouter à packages.x86_64." >&2
exec openbox
fi