# ============================================================================ # 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 des disques # SÉLECTIONNÉS dans le GUI (l'utilisateur choisit le # disque cible — les autres, ex. disques de jeux, ne # sont pas touchés s'il ne les sélectionne pas). # --initlabel : réécrit une étiquette de disque neuve. # --disklabel : type de table CHOISI SELON LE FIRMWARE par le %pre # plus bas (ne surtout PAS coder en dur) : # • UEFI → gpt : l'ESP peut être créée → grub-install # EFI fonctionne. Forcer « msdos » en UEFI empêchait # l'ESP (→ install NON bootable) et réécrivait la # table pour rien : c'est LE bug corrigé ici. # • BIOS → msdos : core.img se loge dans l'espace # post-MBR (pas de partition BIOS Boot EF02 requise, # qu'Anaconda ne crée pas avec « bootloader --disabled »). # L'utilisateur garde la main dans le GUI (il peut repasser en manuel), mais le # DÉFAUT est un disque net → réinstall fiable à chaque fois. zerombr # Table de partitions injectée par le %pre (GPT en UEFI, MBR en BIOS). %include /tmp/fws-clearpart.ks # --- 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). # ============================================================================ # %pre — TABLE DE PARTITIONS SELON LE FIRMWARE # Génère /tmp/fws-clearpart.ks (inclus dans la section commandes ci-dessus) : # GPT en UEFI (→ ESP créable → grub-install EFI fonctionne), MBR/msdos en BIOS. # Les scripts %pre s'exécutent AVANT l'application des commandes du kickstart, # donc le %include voit toujours le fichier généré ici. # ============================================================================ %pre --log=/tmp/fws-pre.log if [ -d /sys/firmware/efi ]; then disklabel=gpt else disklabel=msdos fi echo "clearpart --all --initlabel --disklabel=$disklabel" > /tmp/fws-clearpart.ks %end # ============================================================================ # %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 » 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 # Choix « Dual-boot Windows gaming » (spoke fws_windows) → relu par le %post # chrooté (Secure Boot) ET par fws-windows-deploy (%post --nochroot, plus bas). if [ -f /tmp/fws-windows ]; then cp -f /tmp/fws-windows "$SYSROOT/tmp/fws-windows" 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//boot//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 </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 < 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 # --- 7) Secure Boot (uniquement si « Dual-boot Windows gaming » demandé) ------ # Vanguard exige Secure Boot. On PRÉPARE la chaîne pendant l'install : sbctl # create-keys + signature des 3 chemins réels (\EFI\FWS, \EFI\BOOT\BOOTX64, noyaux). # L'enrôlement des clés + l'activation de SB au firmware restent MANUELS (Setup # Mode requis, non automatisable) — instructions au 1er boot. Idempotent. # On NE source PAS /tmp/fws-windows (un chemin d'ISO piégé pourrait injecter du # code) : simple test littéral de la ligne « enabled=1 ». if grep -qx 'enabled=1' /tmp/fws-windows 2>/dev/null \ && [ -x /usr/local/bin/fws-secureboot-setup ]; then /usr/local/bin/fws-secureboot-setup \ || echo "[FWS] Secure Boot : préparation partielle (à finaliser au 1er boot)." fi %end # ============================================================================ # %post --nochroot — DÉPLOIEMENT WINDOWS (option A) si « Dual-boot Windows » # demandé. Tourne dans le LIVE (cible sous /mnt/sysroot). NON bloquant : un # échec de déploiement N'EMPÊCHE PAS FWS de booter (garde-fous fail-closed + # statut DEPLOY_FAILED, aucune entrée UEFI Windows piégeuse). Le carve du disque # Windows vit ICI (le %pre tourne avant le GUI → ne voit pas /tmp/fws-windows). # Voir fws-windows-deploy + docs/windows-spoke-plan.md. # ============================================================================ %post --nochroot --log=/tmp/fws-windows-deploy.log SYSROOT=/mnt/sysroot [ -d "$SYSROOT" ] || SYSROOT=/mnt/sysimage if [ -f /tmp/fws-windows ] && [ -x /usr/local/bin/fws-windows-deploy ]; then /usr/local/bin/fws-windows-deploy --state /tmp/fws-windows --sysroot "$SYSROOT" \ || echo "[FWS] Déploiement Windows échoué (voir le log) — FWS reste bootable." fi %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