# ============================================================================ # 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 : empêcher Anaconda d'installer Fedora. # ---------------------------------------------------------------------------- # Sans payload défini, Anaconda utilise DNF → des RPM depuis les dépôts # Fedora = il installe Fedora. Anaconda n'a AUCUN backend pacman. La seule # façon de poser FWS/Arch est le payload « liveimg » : il recopie une image # de système de fichiers (ici 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 : l'image du live (FWS), surtout pas Fedora ------- # Squashfs archiso sur le média booté : # /run/archiso/bootmnt///airootfs.sfs # install_dir=fws, arch=x86_64, airootfs_image_type=squashfs (cf. profiledef.sh). # Anaconda loop-monte ce .sfs et rsync son contenu vers la cible. # # ⚠ À VALIDER sur ISO réelle : certaines versions d'Anaconda attendent une # structure « LiveOS/rootfs.img » à l'intérieur du squashfs (format Fedora # Live) et non un squashfs « plat » comme celui d'archiso. Si l'install # échoue à monter la source, basculer sur un tarball de rootfs dédié : # liveimg --url=file:///run/archiso/bootmnt/fws/x86_64/fws-rootfs.tar.gz # (un build-rootfs.sh produirait alors ce tar via pacstrap). liveimg --url=file:///run/archiso/bootmnt/fws/x86_64/airootfs.sfs # --- 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 liveimg n'installe aucun paquet # (il copie une image). Toute installation de paquets se fait en %post. # ============================================================================ # %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 # --- 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 # --- 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" ;; xfce) PKGS="xfce4 xfce4-goodies sddm"; DM="sddm" ;; hyprland) PKGS="hyprland kitty wofi waybar sddm"; DM="sddm" ;; cli|*) PKGS="" ;; esac # Navigateur de base ajouté DÈS QU'un bureau est choisi (jamais en CLI). [ -n "$PKGS" ] && PKGS="$PKGS firefox" if [ -n "$PKGS" ]; then pacman-key --init &>/dev/null || true pacman-key --populate archlinux &>/dev/null || true if pacman -Sy --noconfirm $PKGS; then [ -n "$DM" ] && systemctl enable "$DM" || true systemctl set-default graphical.target || true # SDDM (KDE / XFCE / Hyprland) : pavé numérique allumé au login. if [ "$DM" = "sddm" ]; then install -d /etc/sddm.conf.d printf '[General]\nNumlock=on\n' > /etc/sddm.conf.d/10-fws.conf fi else echo "[FWS] Bureau '$DESK' non installé (réseau ?) — à refaire après le 1er boot." fi fi # --- Clavier X11 (SDDM + sessions graphiques) -------------------------------- # SDDM et les sessions X/Wayland (kwin lit aussi ce fichier) prennent leur # disposition de /etc/X11/xorg.conf.d/00-keyboard.conf. Anaconda passe par localed # qui ne l'écrit pas fiablement sur la CIBLE → écran SDDM en QWERTY US → le mot de # passe (tapé sur le clavier choisi, ex. suisse QWERTZ) ne correspond pas. On # l'écrit donc nous-mêmes, depuis la disposition X CHOISIE (kickstart généré), # sinon dérivée de la locale via langtable. Cohérent avec le clavier du GUI. 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 < /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). rm -f /etc/motd # --- 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