Files
FWS-ISO/configs/releng/airootfs/usr/share/anaconda/interactive-defaults.ks
nocode b7bf497cd6 feat(releng): enable systemd-timesyncd ntp synchronization
Add explicit activation of systemd-timesyncd service to ensure proper clock synchronization on installed systems. This prevents clock drift issues that can affect TLS and pacman operations, and meets user expectations for accurate system time.

The service is already present in the image but inherited symlinks from the live environment may be ordinary files that systemd ignores, so explicit enablement ensures correct behavior on all installations.
2026-07-07 13:57:04 +02:00

611 lines
34 KiB
Plaintext

# ============================================================================
# FWS — interactive-defaults.ks
#
# Kickstart de base, lu AUTOMATIQUEMENT par Anaconda quand l'install est
# lancée en mode interactif (chemin fixe codé en dur dans Anaconda :
# /usr/share/anaconda/interactive-defaults.ks). Aucun inst.ks= n'est requis.
#
# RÔLE : finaliser une install « copie du live » (surtout pas Fedora).
# ----------------------------------------------------------------------------
# Anaconda n'a AUCUN backend pacman ; son payload par défaut (DNF) poserait
# des RPM Fedora. Ici le payload est LiveOS, imposé par « anaconda --liveinst »
# (cf. bloc « Source d'installation » ci-dessous) : il recopie le squashfs du
# live (= FWS) sur le disque, puis on finalise nous-mêmes (grub, mkinitcpio,
# bureau) en %post. C'est le modèle « Sabayon » : le système installé EST une
# copie du live.
#
# L'utilisateur garde la main dans le GUI sur tout ce qui N'EST PAS fixé ici :
# partitionnement, fuseau, clavier, langue, création d'utilisateur, mot de
# passe root, etc.
# ============================================================================
# --- Source d'installation : le payload LiveOS (surtout pas Fedora/DNF) ------
# AUCUNE directive de payload ici, et c'est VOULU. liveinst lance
# « anaconda --liveinst » et anaconda.py teste opts.liveinst AVANT le type du
# payload DBus → il crée TOUJOURS un payload LiveOS et IGNORE tout payload du
# kickstart (un « liveimg » ici serait du code mort — vérifié dans
# pyanaconda/anaconda.py, propriété payload, et reproduit hors VM).
#
# Le payload LiveOS détecte l'image live via /dev/mapper/live-base ou
# /run/rootfsbase (chemins Fedora/dracut, absents d'archiso → c'était le crash
# « SourceSetupError: No Live OS image found! » au démarrage). Le .xinitrc du
# live bind-monte donc /run/archiso/airootfs (squashfs FWS, ro) sur
# /run/rootfsbase AVANT de lancer liveinst : Anaconda monte le loop device et
# rsync son contenu vers la cible = le système installé EST une copie du live,
# puis on finalise en %post (grub, mkinitcpio, bureau, matériel).
# DNF/libdnf5 n'est jamais importé : le module DNF n'est chargé QUE si le
# payload actif est DNF (import lazy dans modules/payloads/payload/factory.py).
# --- Disque : repartir d'un disque PROPRE, même s'il a déjà servi ------------
# Sans ça, une réinstallation sur un disque déjà partitionné (typiquement une
# install FWS précédente : partitions + VG LVM « anaconda_archiso ») pose deux
# problèmes : le partitionnement auto n'a plus d'espace libre, et blivet
# rescanne/auto-active un LVM existant au boot du live → l'installateur se
# comporte mal voire plante au 2e passage (alors qu'un disque vierge passe).
# zerombr : réinitialise toute table de partitions invalide
# (répond « oui » au lieu de bloquer sur un disque
# non reconnu).
# clearpart --all : supprime TOUTES les partitions de TOUS les disques.
# --initlabel : réécrit une étiquette de disque neuve.
# --disklabel=msdos : force une table MBR (et non GPT). CRUCIAL en BIOS :
# en BIOS+GPT, GRUB i386-pc exige une partition « BIOS
# Boot » (~1 Mo, type EF02) pour embarquer core.img,
# qu'Anaconda NE crée PAS avec « bootloader --disabled
# » → « this GPT partition label contains no BIOS Boot
# Partition; embedding won't be possible » →
# grub-install échoue. En MBR, core.img se loge dans
# l'espace post-MBR, aucune partition dédiée requise.
# ⚠ Ce choix vise le BIOS (cas VMware actuel). Pour l'UEFI il faudra du GPT
# (+ ESP) — à rendre dépendant du firmware (%pre) le jour où on cible l'UEFI.
# L'utilisateur garde la main dans le GUI (il peut repasser en manuel), mais le
# DÉFAUT est désormais un disque net → réinstall fiable à chaque fois.
# ⚠ VM mono-disque : « --all » efface le seul disque, c'est voulu. Sur une
# machine multi-disques il faudrait cibler avec « --drives=sda ».
zerombr
clearpart --all --initlabel --disklabel=msdos
# --- Bootloader : géré à la main en %post ------------------------------------
# Anaconda lancerait « grub2-install » / « grub2-mkconfig » (binaires Fedora,
# préfixe grub2-) qui n'existent pas dans un rootfs Arch (lequel fournit
# grub-install / grub-mkconfig). On désactive donc l'étape native d'Anaconda.
# L'ESP (UEFI) ou la partition bios_boot (BIOS/GPT) reste créée par le
# partitionnement (auto ou manuel) car la plateforme l'impose.
bootloader --disabled
# Pas de section %packages : le payload LiveOS n'installe aucun paquet
# (il copie une image). Toute installation de paquets se fait en %post.
# --- Récapitulatif avant installation : PAGE GUI dédiée (pas de %pre-install) -
# L'écran « RÉCAPITULATIF AVANT INSTALLATION » est une standalone spoke
# Anaconda (overlay airootfs : pyanaconda/ui/gui/spokes/fws_recap.py,
# postForHub=SummaryHub, priority -10) intercalée ENTRE le hub et l'écran de
# progression : l'utilisateur clique « Commencer l'installation », voit le
# récap (bureau choisi, hostname, disques, GPU, réseau — AUCUNE écriture
# disque à ce stade), et l'installation ne démarre qu'à son clic « Lancer
# l'installation ». L'ancien récap zenity en %pre-install a été abandonné :
# le module scripts tourne avec LC_ALL=C et un env X incertain (zenity 4
# refuse les libellés UTF-8 en locale C → rc=255 pris pour « Annuler » →
# poweroff en plein install au 1er test réel).
# ============================================================================
# %post --nochroot — s'exécute dans le LIVE, la cible montée sous /mnt/sysroot
# But : déposer dans la cible ce dont le %post chrooté aura besoin (DNS pour
# pacman, choix de bureau écrit par l'addon de sélection).
# ============================================================================
%post --nochroot --log=/tmp/fws-post-nochroot.log
set -x
# Selon la version d'Anaconda la cible est sous /mnt/sysroot (récent) ou
# /mnt/sysimage (ancien).
SYSROOT=/mnt/sysroot
[ -d "$SYSROOT" ] || SYSROOT=/mnt/sysimage
# DNS pour que « pacman -S <bureau> » fonctionne dans le chroot.
cp -f /etc/resolv.conf "$SYSROOT/etc/resolv.conf" 2>/dev/null || true
# --- Hostname : valeur saisie dans le champ « Nom de machine » du spoke User --
# Anaconda ne capte PAS le hostname du spoke Réseau (→ /etc/hostname vide). On a
# donc ajouté un champ Hostname au spoke Utilisateur (cf. patch du paquet
# anaconda) : user.py écrit la valeur dans /tmp/fws-hostname (live). On la
# nettoie (caractères de hostname valides) et on l'écrit dans la cible ; défaut
# « fws » si vide/absent → plus jamais de hostname vide ni « archiso ».
HN=""
[ -f /tmp/fws-hostname ] && HN="$(tr -cd 'a-zA-Z0-9._-' < /tmp/fws-hostname)"
[ -n "$HN" ] || HN="fws"
echo "$HN" > "$SYSROOT/etc/hostname"
# Choix du bureau (écrit par l'addon « Choix du bureau », à venir ; valeur lue
# par le %post chrooté ci-dessous). « cli » par défaut si absent.
if [ -f /tmp/fws-desktop ]; then
cp -f /tmp/fws-desktop "$SYSROOT/tmp/fws-desktop" 2>/dev/null || true
fi
# --- Noyau : archiso RETIRE /boot/vmlinuz-linux du squashfs ------------------
# mkarchiso boote le noyau depuis l'ISO et l'EXCLUT de l'airootfs squashfs (gain
# de place). Conséquence : le payload LiveOS recopie un rootfs SANS noyau →
# « mkinitcpio: /boot/vmlinuz-linux must be readable » puis aucun OS à booter
# (« Operating System not found »). On récupère donc le noyau depuis le média
# booté (/run/archiso/bootmnt/<install_dir>/boot/<arch>/vmlinuz-linux) et on le
# dépose dans la cible. Les modules (/usr/lib/modules) sont, eux, dans le
# squashfs → déjà recopiés. Avec le noyau présent, le %post chrooté peut bâtir
# l'initramfs (mkinitcpio) et GRUB trouve de quoi booter.
SRC_KERN="$(find /run/archiso/bootmnt -name 'vmlinuz-linux' 2>/dev/null | head -1)"
if [ -n "$SRC_KERN" ]; then
cp -f "$SRC_KERN" "$SYSROOT/boot/vmlinuz-linux"
else
echo "[FWS] AVERTISSEMENT : noyau vmlinuz-linux introuvable sur le média"
fi
%end
# ============================================================================
# %post (chroot) — DANS le rootfs FWS recopié. pacman, grub-install,
# mkinitcpio existent ici : ce sont les outils Arch de l'image copiée.
# Log persistant : /var/log/fws-post.log sur le système installé.
# ============================================================================
%post --log=/var/log/fws-post.log
set -x
# --- 1) Purge des résidus « live » (modèle Sabayon = live copié tel quel) ----
# Ce qui est propre au live casserait ou pollue le système installé.
rm -f /etc/mkinitcpio.conf.d/archiso.conf
rm -f /etc/systemd/system/getty@tty1.service.d/autologin.conf
for svc in choose-mirror.service pacman-init.service \
livecd-talk.service livecd-alsa-unmuter.service \
etc-pacman.d-gnupg.mount; do
rm -f "/etc/systemd/system/$svc"
rm -f /etc/systemd/system/*.target.wants/"$svc"
done
# cloud-init (présent dans l'airootfs archiso) : inutile sur un poste installé.
rm -f /etc/systemd/system/cloud-init.target.wants/* 2>/dev/null || true
# Hooks initramfs archiso, s'ils ont été recopiés en tant que paquet.
if pacman -Q mkinitcpio-archiso &>/dev/null; then
pacman -Rns --noconfirm mkinitcpio-archiso || true
fi
# --- 2) initramfs propre (preset Arch standard, sans archiso) ----------------
# Le live fournit un preset 'archiso' (/etc/mkinitcpio.d/linux.preset qui pointe
# vers conf.d/archiso.conf + hooks archiso) → inadapté à un disque normal. On
# pose un preset Arch standard (config par défaut /etc/mkinitcpio.conf).
cat > /etc/mkinitcpio.d/linux.preset <<'PRESET'
ALL_kver="/boot/vmlinuz-linux"
PRESETS=('default' 'fallback')
default_image="/boot/initramfs-linux.img"
fallback_image="/boot/initramfs-linux-fallback.img"
fallback_options="-S autodetect"
PRESET
# Le partitionnement auto d'Anaconda met « / » sur LVM (/dev/mapper/...-root).
# Sans le hook 'lvm2' dans l'initramfs, le VG n'est pas activé au boot → la
# racine est introuvable → kernel panic. On l'insère avant 'filesystems' s'il
# manque (inoffensif si la racine n'est finalement pas sur LVM).
if ! grep -qE '^HOOKS=.*\blvm2\b' /etc/mkinitcpio.conf; then
sed -i -E 's/^(HOOKS=\(.*)\bfilesystems\b/\1lvm2 filesystems/' /etc/mkinitcpio.conf
fi
mkinitcpio -P || echo "[FWS] AVERTISSEMENT : mkinitcpio a échoué"
# --- 3) GRUB installé à la main (cf. bootloader --disabled) ------------------
install_grub() {
if [ -d /sys/firmware/efi ]; then
# UEFI. efibootmgr crée l'entrée NVRAM « FWS », mais il peut MANQUER de
# l'airootfs et son install dépend du réseau → on ne BLOQUE pas dessus.
pacman -Q efibootmgr &>/dev/null || pacman -Sy --noconfirm efibootmgr || true
# (a) Entrée NVRAM dédiée (best effort, si efibootmgr dispo) :
grub-install --target=x86_64-efi --efi-directory=/boot/efi \
--bootloader-id=FWS --recheck || true
# (b) Chemin de SECOURS EFI/BOOT/BOOTX64.EFI (--removable) : boote SANS
# aucune entrée NVRAM. INDISPENSABLE en VM / sans réseau / firmware
# qui « oublie » l'entrée — c'est lui qui garantit le 1er boot UEFI.
grub-install --target=x86_64-efi --efi-directory=/boot/efi \
--bootloader-id=FWS --removable --recheck
else
# BIOS : installer GRUB dans le MBR du DISQUE physique (PAS une partition)
# portant « / », même quand « / » est sur du LVM (chaîne
# LV → PV partition → disque). « lsblk -s » descend vers le physique ; on
# prend la ligne de type « disk ». ATTENTION : « lsblk -s » dessine un
# ARBRE → les caractères de branche (└─) se collent au chemin (on
# obtenait disk='-/dev/sda' → « grub-install: cannot find a GRUB drive
# for `-/dev/sda' »). On EXTRAIT donc le chemin propre avec grep.
local rootsrc disk
rootsrc="$(findmnt -no SOURCE / 2>/dev/null)"
disk="$(lsblk -spno NAME,TYPE "$rootsrc" 2>/dev/null \
| awk '$2=="disk"{print $1; exit}' | grep -oE '/dev/[a-z0-9]+')"
[ -n "$disk" ] || { echo "[FWS] Disque BIOS introuvable pour '$rootsrc'"; return 1; }
grub-install --target=i386-pc --recheck "$disk"
fi
}
if install_grub; then
grub-mkconfig -o /boot/grub/grub.cfg || echo "[FWS] grub-mkconfig a échoué"
else
echo "[FWS] AVERTISSEMENT : grub-install a échoué — bootloader à refaire à la main"
fi
# --- 3bis) Clavier (X11) + LOCALE --------------------------------------------
# CLAVIER : Anaconda passe par localed et n'écrit pas fiablement la disposition X
# sur la CIBLE → SDDM / sessions X en QWERTY US. On CALCULE la disposition CHOISIE
# (kickstart généré, sinon dérivée de la locale via langtable) en XL/XV, réutilisés
# plus bas (00-keyboard.conf pour SDDM/X ET la config Hyprland qui a son propre
# kb_layout). LOCALE : le locale choisi (ex. fr_CH.UTF-8) doit être GÉNÉRÉ —
# Anaconda vient de Fedora (locales pré-générées) et ne lance pas locale-gen →
# sans ça LANG casse et tout retombe en C.
XL=""; XV=""
XKB="$(sed -n "s/.*--xlayouts=//p" /root/anaconda-ks.cfg 2>/dev/null | head -1)"
XKB="${XKB#[\'\"]}"; XKB="${XKB%%[\'\"]*}"; XKB="${XKB%%,*}" # 1re dispo, ex. « ch (fr) »
if [ -z "$XKB" ]; then
_loc="$(. /etc/locale.conf 2>/dev/null; printf '%s' "${LANG%%.*}")" # fr_CH
_lng="${_loc%%_*}"; _terr="${_loc#*_}"; [ "$_terr" = "$_loc" ] && _terr=""
[ -n "$_terr" ] && XKB="$(python3 -c "import langtable; k=langtable.list_keyboards(languageId='$_lng', territoryId='$_terr'); print(k[0] if k else '')" 2>/dev/null)"
fi
if [ -n "$XKB" ]; then
XL="${XKB%%(*}"; XL="${XL%% *}" # ch
case "$XKB" in *'('*')') XV="${XKB#*(}"; XV="${XV%)}" ;; *) XV="" ;; esac # fr
install -d /etc/X11/xorg.conf.d
cat > /etc/X11/xorg.conf.d/00-keyboard.conf <<KBEOF
Section "InputClass"
Identifier "system-keyboard"
MatchIsKeyboard "on"
Option "XkbLayout" "$XL"
Option "XkbVariant" "$XV"
EndSection
KBEOF
fi
FWSLANG="$(. /etc/locale.conf 2>/dev/null; printf '%s' "${LANG:-}")"
if [ -n "$FWSLANG" ]; then
_cs="${FWSLANG##*.}"; [ "$_cs" = "$FWSLANG" ] && _cs="UTF-8"
grep -qE "^[[:space:]]*${FWSLANG}[[:space:]]+${_cs}\b" /etc/locale.gen 2>/dev/null \
|| printf '%s %s\n' "$FWSLANG" "$_cs" >> /etc/locale.gen
locale-gen || echo "[FWS] locale-gen a échoué pour $FWSLANG"
fi
# --- 4) Bureau choisi : pacman -S du DE (non bloquant si pas de réseau) -------
DESK="cli"
[ -f /tmp/fws-desktop ] && DESK="$(tr -d '[:space:]' < /tmp/fws-desktop)"
DM=""
case "$DESK" in
kde) PKGS="plasma-meta sddm konsole dolphin"; DM="sddm" ;;
gnome) PKGS="gnome gdm"; DM="gdm" ;;
# noto-fonts : KDE l'a via plasma-meta et GNOME a cantarell, mais XFCE et
# Hyprland ne garantissent AUCUNE police TTF → greeter SDDM/apps illisibles.
# Hyprland : la stack des raccourcis FWS (mêmes touches que la config
# i3/sway maison) — dolphin (Super+E), hyprlock (Super+L), grim+slurp+
# wl-clipboard (captures), playerctl (touches multimédia) — plus la
# plomberie de bureau : mako+libnotify (notifications), polkit-gnome
# (agent d'authentification GUI), network-manager-applet (réseau dans le
# tray), xdg-desktop-portal-hyprland (partage d'écran/portails), gvfs
# (montage USB via udisks2), ttf-nerd-fonts-symbols (icônes waybar).
# Son et Bluetooth GARANTIS avec le bureau : la stack PipeWire + le
# mixeur (pavucontrol) + bluez/blueman partent dans la MÊME transaction
# pacman que le DE — si le bureau s'installe, ses gestionnaires aussi
# (fws-setup-hardware les re-propose ensuite en --needed : sans effet
# s'ils sont déjà là, filet de secours sinon).
xfce) PKGS="xfce4 xfce4-goodies sddm noto-fonts pipewire pipewire-pulse wireplumber pavucontrol"; DM="sddm" ;;
hyprland) PKGS="hyprland kitty wofi waybar sddm noto-fonts dolphin kio-extras udisks2 hyprlock grim slurp wl-clipboard playerctl mako libnotify polkit-gnome network-manager-applet xdg-desktop-portal-hyprland ttf-nerd-fonts-symbols pipewire pipewire-alsa pipewire-pulse wireplumber pavucontrol bluez bluez-utils blueman"; DM="sddm" ;;
# i3 : session X11 → COMPATIBLE VR (SteamVR ne marche pas sous Wayland).
# xorg-server est requis (SDDM ne le tire pas) ; maim+xclip = captures X11,
# dunst = notifications X11 (équivalent de mako), i3lock = verrouillage.
# fws-vr-setup (Super+Shift+V ou menu) installe les drivers du casque.
i3) PKGS="i3-wm i3lock xorg-server polybar picom rofi kitty dolphin kio-extras udisks2 sddm noto-fonts ttf-nerd-fonts-symbols maim xclip dunst libnotify polkit-gnome network-manager-applet playerctl brightnessctl pipewire pipewire-alsa pipewire-pulse wireplumber pavucontrol bluez bluez-utils blueman"; DM="sddm" ;;
cli|*) PKGS="" ;;
esac
# Navigateur de base ajouté DÈS QU'un bureau est choisi (jamais en CLI).
# + python-gobject : fenêtre de bienvenue fws-hello (GTK3 vient avec Firefox)
# + pacman-contrib : checkupdates/paccache (indicateur de MàJ + fws-update)
[ -n "$PKGS" ] && PKGS="$PKGS firefox python-gobject pacman-contrib"
if [ -n "$PKGS" ]; then
pacman-key --init &>/dev/null || true
pacman-key --populate archlinux &>/dev/null || true
# Script de RATTRAPAGE généré avec les valeurs de CE choix : si l'install
# du bureau échoue (réseau/miroirs), l'utilisateur la rejoue en une
# commande depuis le système installé. --needed → inoffensif si déjà fait.
cat > /usr/local/bin/fws-desktop-retry <<RETRYEOF
#!/bin/bash
# Généré par l'installateur FWS — (ré)installe le bureau « $DESK ».
set -u
[ "\$(id -u)" -eq 0 ] || exec sudo "\$0" "\$@"
echo "==> Installation du bureau $DESK…"
pacman -Syy --noconfirm --needed $PKGS || { echo "Échec (réseau ?) — réessayez."; exit 1; }
[ -n "$DM" ] && systemctl enable "$DM"
systemctl set-default graphical.target
rm -f /var/lib/fws/desktop-failed
[ -f /etc/motd ] && grep -q fws-desktop-retry /etc/motd && rm -f /etc/motd
echo "==> Bureau installé ✔ — redémarrez (reboot) pour arriver sur l'écran de connexion."
RETRYEOF
chmod 755 /usr/local/bin/fws-desktop-retry
# Le réseau peut mettre quelques secondes à monter dans l'environnement
# d'installation : on attend la résolution DNS avant le premier essai,
# puis on tente pacman jusqu'à 3 fois (miroir capricieux ≠ échec définitif).
for _i in 1 2 3 4 5 6 7 8 9 10; do
getent hosts geo.mirror.pkgbuild.com >/dev/null 2>&1 && break
sleep 3
done
_DE_OK=""
for _try in 1 2 3; do
pacman -Sy --noconfirm --needed $PKGS && { _DE_OK=1; break; }
echo "[FWS] installation du bureau : tentative $_try échouée — nouvel essai dans 15 s…"
sleep 15
done
if [ -n "$_DE_OK" ]; then
[ -n "$DM" ] && systemctl enable "$DM" || true
systemctl set-default graphical.target || true
# SDDM (KDE / XFCE / Hyprland) : pavé numérique + écran de connexion FWS.
# Le thème « fws » (livré par l'airootfs → recopié par le payload dans
# /usr/share/sddm/themes/fws) est en pur QtQuick/Controls Basic :
# qt6-declarative, dépendance de sddm, suffit → même écran soigné pour
# les 3 DE SANS paquet de thème supplémentaire (Breeze n'existe qu'avec
# plasma). Accent adapté au bureau via theme.conf.user (mécanisme de
# surcharge standard de SDDM, lu par-dessus theme.conf du thème).
if [ "$DM" = "sddm" ]; then
install -d /etc/sddm.conf.d
cat > /etc/sddm.conf.d/10-fws.conf <<'SDDMEOF'
[General]
Numlock=on
[Theme]
Current=fws
SDDMEOF
case "$DESK" in
xfce) _ACCENT="#16a085" ;; # sarcelle (assorti à XFCE)
hyprland) _ACCENT="#89b4fa" ;; # bleu Catppuccin (assorti à Hyprland)
*) _ACCENT="#3daee9" ;; # bleu Breeze (KDE, défaut)
esac
if [ -d /usr/share/sddm/themes/fws ]; then
printf '[General]\naccent=%s\n' "$_ACCENT" \
> /usr/share/sddm/themes/fws/theme.conf.user
else
echo "[FWS] thème SDDM fws introuvable — thème par défaut conservé"
sed -i '/^Current=fws$/d' /etc/sddm.conf.d/10-fws.conf
fi
fi
# GDM (GNOME) : « la même chose » autant que possible — le greeter de
# GDM EST GNOME Shell, il ne se thème pas comme SDDM. On passe par
# dconf (base système « gdm ») : bannière d'accueil FWS, horloge avec
# date complète, pavé numérique allumé — même confort qu'avec SDDM.
if [ "$DM" = "gdm" ]; then
install -d /etc/dconf/profile /etc/dconf/db/gdm.d
cat > /etc/dconf/profile/gdm <<'GDMEOF'
user-db:user
system-db:gdm
file-db:/usr/share/gdm/greeter-dconf-defaults
GDMEOF
cat > /etc/dconf/db/gdm.d/10-fws <<'GDMEOF'
[org/gnome/login-screen]
banner-message-enable=true
banner-message-text='Bienvenue sur FWS'
[org/gnome/desktop/interface]
clock-show-date=true
clock-show-weekday=true
[org/gnome/desktop/peripherals/keyboard]
numlock-state=true
remember-numlock-state=false
GDMEOF
dconf update || echo "[FWS] dconf update a échoué (bannière GDM à refaire)"
fi
# Hyprland en VM : sur GPU virtuel (VMware/VirtualBox) le compositeur
# crashe au lancement → SDDM « remet au login ». On le lance via un
# wrapper qui force le rendu LOGICIEL UNIQUEMENT en VM (systemd-detect-virt)
# ; sur un vrai GPU le rendu matériel est conservé.
if [ "$DESK" = "hyprland" ]; then
cat > /usr/local/bin/fws-hyprland <<'HYEOF'
#!/bin/sh
if systemd-detect-virt --quiet 2>/dev/null; then
# GPU virtuel (VMware vmwgfx…) : on FORCE le chemin d'affichage 100% logiciel.
# La PIÈCE MAÎTRESSE est MESA_LOADER_DRIVER_OVERRIDE=kms_swrast : elle force
# Mesa à allouer le GBM/EGL via kms_swrast (rendu logiciel dans les « dumb
# buffers » KMS) AU LIEU du pilote vmwgfx (qui ne fournit pas le GBM dont
# aquamarine a besoin → « CBackend::create() failed »). + llvmpipe pour le GL.
export MESA_LOADER_DRIVER_OVERRIDE=kms_swrast
export GALLIUM_DRIVER=llvmpipe LIBGL_ALWAYS_SOFTWARE=1
export WLR_RENDERER_ALLOW_SOFTWARE=1 WLR_NO_HARDWARE_CURSORS=1
export AQ_NO_ATOMIC=1
fi
# Hyprland ≥ 0.53 : start-hyprland est le lanceur officiel (prépare la
# session/env systemd) — sans lui, bandeau « started without start-hyprland ».
if command -v start-hyprland >/dev/null 2>&1; then
exec start-hyprland "$@"
fi
exec Hyprland "$@"
HYEOF
chmod 755 /usr/local/bin/fws-hyprland
if [ -f /usr/share/wayland-sessions/hyprland.desktop ]; then
sed -i 's|^Exec=.*|Exec=/usr/local/bin/fws-hyprland|' \
/usr/share/wayland-sessions/hyprland.desktop
fi
# Bluetooth prêt au 1er démarrage (bluez vient d'être installé
# avec le bureau ; fws-setup-hardware le refait — idempotent).
systemctl enable bluetooth.service 2>/dev/null || true
fi
if [ "$DESK" = "i3" ]; then
systemctl enable bluetooth.service 2>/dev/null || true
# Icône du lanceur « Installation VR » (casque) visible dans rofi.
gtk-update-icon-cache -f /usr/share/icons/hicolor 2>/dev/null || true
fi
else
echo "[FWS] Bureau '$DESK' NON installé après 3 tentatives (réseau/miroirs ?)."
echo "[FWS] Rattrapage sur le système installé : sudo fws-desktop-retry"
mkdir -p /var/lib/fws
echo "$DESK" > /var/lib/fws/desktop-failed
# Le premier login (console) explique quoi faire — le motd est retiré
# par fws-desktop-retry une fois le bureau posé.
cat > /etc/motd <<'MOTD'
⚠ FWS : l'installation du bureau a échoué (réseau indisponible pendant
l'installation). Le système fonctionne, mais en console.
Une fois le réseau branché, lancez : sudo fws-desktop-retry
puis redémarrez : le bureau choisi sera installé et activé.
MOTD
fi
fi
# --- 4quater) ADN FWS : détection matériel + drivers + stack « Windows » ------
# Script dédié (NON bloquant ; réseau requis) : drivers GPU détectés via lspci +
# outils invité VM, son/micro/casque (PipeWire), Bluetooth, Podman, et — si un
# bureau est choisi — Steam, Wine, VR (Monado/SteamVR), winboat-bin (apps
# Windows). C'est le cœur « remplacement de Windows » de FWS.
if [ -x /usr/local/bin/fws-setup-hardware ]; then
/usr/local/bin/fws-setup-hardware "$DESK" || true
fi
# --- Config Hyprland (si choisi) : clavier + apps + waybar -------------------
# Hyprland (Wayland) NE lit PAS /etc/X11/xorg.conf.d/00-keyboard.conf : sa
# disposition est dans SA config (input { kb_layout }). Sans config il démarre nu
# (QWERTY US, aucune touche utile). On déploie une config FWS (template +
# clavier XL/XV injecté) dans /etc/skel ET le home de chaque utilisateur déjà
# créé par Anaconda (le %post tourne APRÈS la création de l'utilisateur).
if [ "$DESK" = "hyprland" ] && [ -f /usr/local/share/fws/hyprland.conf.tmpl ]; then
sed -e "s/__KB_LAYOUT__/${XL:-us}/" -e "s/__KB_VARIANT__/${XV}/" \
/usr/local/share/fws/hyprland.conf.tmpl > /tmp/fws-hyprland.conf
install -Dm644 /tmp/fws-hyprland.conf /etc/skel/.config/hypr/hyprland.conf
# Waybar FWS : barre assortie + bouton « ⌨ Raccourcis » (fws-keys,
# aussi sur SUPER+K) qui liste les binds RÉELS chargés par Hyprland
# (hyprctl binds -j) — les descriptions viennent des bindd de la config.
install -Dm644 /usr/local/share/fws/waybar-config.json /etc/skel/.config/waybar/config
install -Dm644 /usr/local/share/fws/waybar-style.css /etc/skel/.config/waybar/style.css
# wofi : style du menu d'applications (Super+R) — le panneau « Raccourcis »
# a son propre style (wofi-keys.css), passé en --style par fws-keys.
install -Dm644 /usr/local/share/fws/wofi-config /etc/skel/.config/wofi/config
install -Dm644 /usr/local/share/fws/wofi-style.css /etc/skel/.config/wofi/style.css
while IFS=: read -r _u _x _uid _g _gec _home _sh; do
[ "$_uid" -ge 1000 ] && [ "$_uid" -le 60000 ] && [ -d "$_home" ] || continue
install -Dm644 /tmp/fws-hyprland.conf "$_home/.config/hypr/hyprland.conf"
install -Dm644 /usr/local/share/fws/waybar-config.json "$_home/.config/waybar/config"
install -Dm644 /usr/local/share/fws/waybar-style.css "$_home/.config/waybar/style.css"
install -Dm644 /usr/local/share/fws/wofi-config "$_home/.config/wofi/config"
install -Dm644 /usr/local/share/fws/wofi-style.css "$_home/.config/wofi/style.css"
chown "$_u": "$_home/.config" 2>/dev/null || true
chown -R "$_u": "$_home/.config/hypr" "$_home/.config/waybar" \
"$_home/.config/wofi" 2>/dev/null || true
done < /etc/passwd
rm -f /tmp/fws-hyprland.conf
fi
# --- Config i3 (si choisi) : raccourcis FWS + barre + autostart -------------
# Pas d'injection clavier ici : les sessions X11 lisent la disposition dans
# /etc/X11/xorg.conf.d/00-keyboard.conf, écrite à l'étape 3bis.
if [ "$DESK" = "i3" ] && [ -f /usr/local/share/fws/i3-config ]; then
install -Dm644 /usr/local/share/fws/i3-config /etc/skel/.config/i3/config
# Polybar (clone de la waybar) + picom (coins arrondis) : même rendu
# visuel que la session Hyprland FWS.
install -Dm644 /usr/local/share/fws/polybar-config.ini /etc/skel/.config/polybar/config.ini
install -Dm644 /usr/local/share/fws/picom.conf /etc/skel/.config/picom.conf
# rofi : menu d'applications au look FWS (équivalent du wofi d'Hyprland).
install -Dm644 /usr/local/share/fws/rofi-config.rasi /etc/skel/.config/rofi/config.rasi
while IFS=: read -r _u _x _uid _g _gec _home _sh; do
[ "$_uid" -ge 1000 ] && [ "$_uid" -le 60000 ] && [ -d "$_home" ] || continue
install -Dm644 /usr/local/share/fws/i3-config "$_home/.config/i3/config"
install -Dm644 /usr/local/share/fws/polybar-config.ini "$_home/.config/polybar/config.ini"
install -Dm644 /usr/local/share/fws/picom.conf "$_home/.config/picom.conf"
install -Dm644 /usr/local/share/fws/rofi-config.rasi "$_home/.config/rofi/config.rasi"
chown "$_u": "$_home/.config" 2>/dev/null || true
chown -R "$_u": "$_home/.config/i3" "$_home/.config/polybar" \
"$_home/.config/rofi" "$_home/.config/picom.conf" 2>/dev/null || true
done < /etc/passwd
fi
# --- 4bis) Sudo : l'utilisateur créé doit pouvoir administrer -----------------
# Arch laisse « %wheel » COMMENTÉ dans sudoers → même un membre de wheel ne peut
# pas sudo. On active le groupe wheel (drop-in) ET on y ajoute tous les
# utilisateurs HUMAINS (UID 1000-60000) créés par Anaconda → ils sont sudoers.
install -d -m 750 /etc/sudoers.d
printf '%%wheel ALL=(ALL:ALL) ALL\n' > /etc/sudoers.d/10-fws-wheel
chmod 440 /etc/sudoers.d/10-fws-wheel
while IFS=: read -r _u _x _uid _rest; do
if [ "$_uid" -ge 1000 ] && [ "$_uid" -le 60000 ]; then
usermod -aG wheel "$_u" 2>/dev/null || true
fi
done < /etc/passwd
# --- 4ter) Branding FWS sur le système installé -------------------------------
# Le hook os-release du build n'est PAS sur la cible et « pacman -S » a pu
# remettre l'os-release d'Arch → d'où « Arch Linux » au menu GRUB, le logo Arch
# sur l'écran de connexion (GDM lit os-release LOGO) et le nom « Arch ». On FORCE
# donc l'os-release FWS (+ lsb-release + GRUB_DISTRIBUTOR), on RÉGÉNÈRE le menu
# GRUB (10_linux lit le NAME d'os-release), et on retire le motd « live ».
if [ -f /usr/local/share/fws/os-release ]; then
cp -f /usr/local/share/fws/os-release /usr/lib/os-release
ln -sf ../usr/lib/os-release /etc/os-release
fi
if [ -f /etc/lsb-release ]; then
sed -i 's/^DISTRIB_ID=.*/DISTRIB_ID=FWS/; s/^DISTRIB_DESCRIPTION=.*/DISTRIB_DESCRIPTION="FWS Linux"/' \
/etc/lsb-release
fi
if grep -q '^GRUB_DISTRIBUTOR=' /etc/default/grub 2>/dev/null; then
sed -i 's/^GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR="FWS"/' /etc/default/grub
else
echo 'GRUB_DISTRIBUTOR="FWS"' >> /etc/default/grub
fi
grub-mkconfig -o /boot/grub/grub.cfg 2>/dev/null || true
# motd « live » (texte inadapté + codes ANSI bruts affichés crus par GDM) —
# SAUF si c'est notre message « fws-desktop-retry » (échec d'install du
# bureau), qui doit survivre jusqu'au rattrapage.
grep -q fws-desktop-retry /etc/motd 2>/dev/null || rm -f /etc/motd
# --- 4quinquies) Pare-feu par défaut -------------------------------------------
# firewalld est dans l'IMAGE (packages.x86_64) → recopié par le payload,
# activation possible HORS LIGNE. Zone par défaut « public » : entrées
# bloquées, sortant libre — le comportement attendu d'un poste de travail.
# GUI incluse dans le paquet (firewall-config) ; KDE a en plus plasma-firewall.
systemctl enable firewalld.service 2>/dev/null || true
# --- 4quinquies-bis) Horloge juste : synchronisation NTP -----------------------
# Sans NTP l'horloge dérive (TLS/pacman finissent par râler, et un utilisateur
# venant de Windows s'attend à une heure exacte). systemd-timesyncd est dans
# l'image ; on l'ACTIVE explicitement — les symlinks d'activation hérités du
# live étaient des FICHIERS ordinaires (artefact de checkout) que systemd
# ignore (« not a symlink »), corrigés côté live mais on assure ici quoi
# qu'il arrive sur le système installé.
systemctl enable systemd-timesyncd.service 2>/dev/null || true
# NB Récupération : l'entrée GRUB « Réparer FWS » (générée par
# /etc/grub.d/42_fws-recovery lors des grub-mkconfig ci-dessus) boote
# systemd.unit=fws-recovery.target → fws-recovery-boot.service prend tty1 et
# lance l'application de réparation. RIEN à activer : le service n'a pas de
# [Install] (son Conflicts=getty@tty1 ne doit jamais entrer dans un boot
# normal), la target le tire par Requires. Dans le bureau, le lanceur
# « Récupération FWS » (fws-recovery.desktop, polkit) fait le même travail.
# --- 5) Sécurité : verrouiller root s'il est resté SANS mot de passe ----------
# L'airootfs live a souvent un root sans mot de passe ; si l'utilisateur n'en a
# pas défini dans le GUI, on verrouille plutôt que de laisser le compte ouvert.
if passwd -S root 2>/dev/null | grep -qE '\bNP\b'; then
passwd -l root || true
fi
# --- 6) DNS du système installé : NetworkManager + systemd-resolved ----------
# Le live utilise systemd-resolved (/etc/resolv.conf → stub 127.0.0.53). L'ANCIEN
# « rm /etc/resolv.conf » comptait sur NM pour recréer le fichier — MAIS avec
# systemd-resolved actif, NM ne réécrit PAS resolv.conf (il pousse les DNS vers
# resolved). Sans le lien, la résolution est MORTE → « Limited Connectivity » /
# Firefox « Server Not Found » (le réseau marche, le DNS non). Fix robuste :
# - resolv.conf → stub systemd-resolved,
# - service systemd-resolved activé,
# - on FORCE NM à utiliser resolved (dns=systemd-resolved) → il lui fournit les
# DNS DHCP, la résolution marche dès le 1er boot.
rm -f /tmp/fws-desktop 2>/dev/null || true
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
systemctl enable systemd-resolved.service 2>/dev/null || true
install -d /etc/NetworkManager/conf.d
printf '[main]\ndns=systemd-resolved\n' > /etc/NetworkManager/conf.d/dns.conf
%end
# ============================================================================
# %post --nochroot (FINAL) — exfiltre le log du %post chrooté vers le LIVE.
# À ce stade la cible est ENCORE montée sous /mnt/sysroot (l'unmount n'a pas
# encore eu lieu). On copie /var/log/fws-post.log (résultat de grub-install /
# mkinitcpio) vers le live /tmp pour que le harnais de debug (.xinitrc)
# l'affiche SANS avoir à remonter le disque démonté — c'est ce log qui dit si
# le disque va booter.
# ============================================================================
%post --nochroot
SYSROOT=/mnt/sysroot
[ -d "$SYSROOT" ] || SYSROOT=/mnt/sysimage
cp -f "$SYSROOT/var/log/fws-post.log" /tmp/fws-post-chroot.log 2>/dev/null || true
%end