#!/bin/bash # ============================================================================= # build-pagan-complete.sh - Kompletny builder Pagan Linux # ============================================================================= # Pełna budowa: LFS Ch. 5, 6, 7, 8 + BLFS + Kernel + systemd + GRUB # + środowisko graficzne (domyślnie XFCE4) + Calamares (instalator) # + PAG/pagsync/pagbuild + fish (powłoka użytkowników i roota) # + bootowalny LIVE ISO (BIOS + UEFI) + obraz IMG. # # JAK URUCHOMIĆ (jeden skrypt robi wszystko – od pobrania po ISO): # sudo ./build-pagan.sh # buduj XFCE4 + Calamares do ISO # sudo ./build-pagan.sh --de deepin --both # Deepin + IMG + ISO # sudo ./build-pagan.sh --no-calamares # ISO bez instalatora # sudo ./build-pagan.sh --rebuild --repo https://repo.paganlinux.eu/stable/ # # Konfiguracja: skopiuj build.conf.example → build.conf (lub /etc/pagan-build.conf) # cp build.conf.example build.conf # Pełna pomoc: ./build-pagan.sh --help # ============================================================================= set -euo pipefail # ========================= KONFIGURACJA ========================= # Absolutna ścieżka do katalogu skryptów – działa nawet po `cd /` w main() SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Odcisk pliku skryptu z chwili startu. # # PO CO: bash czyta skrypt INKREMENTALNIE (śledzi offset w bajtach), więc jeśli # plik zmieni się W TRAKCIE działania – np. edytor z auto-zapisem przy zmianie # fokusu (klik na terminal!) – bash wznawia czytanie pod ZŁYM offsetem # „wykonuje” fragment przypadkowej linii, podając bzdurny numer: # ./build-pagan.sh: line 8458: $'z\303\263w': nie znaleziono polecenia # [ERROR] Linia 8458: "zów" # (tu 'zów' to urwany ogon słowa „Wskazówka”/„obrazów” z zupełnie innej linii). # Ten odcisk pozwala odróżnić taką sytuację od prawdziwego błędu skryptu. _PAGAN_SELF_SIG="$(stat -c '%s-%Y' "${BASH_SOURCE[0]}" 2>/dev/null || echo '')" _pagan_self_changed() { [[ -n "$_PAGAN_SELF_SIG" ]] || return 1 local _now _now="$(stat -c '%s-%Y' "${BASH_SOURCE[0]}" 2>/dev/null || echo '')" [[ -n "$_now" && "$_now" != "$_PAGAN_SELF_SIG" ]] } # ========================= PARSOWANIE ARGUMENTÓW (CLI) ========================= # Precedencja ustawień: CLI > zmienne środowiskowe > plik konfiguracyjny > domyślne. # Parsowanie na POCZĄTKU skryptu (przed nadaniem domyślnych wartości), inaczej # flaga z CLI nie miałaby wpływu na build. _usage_builder() { cat <<'EOF' Użycie: sudo ./build-pagan.sh [OPCJE] OPCJE: --config FILE Wczytaj plik konfiguracyjny (domyślnie: ./build.conf, /etc/pagan-build.conf) --root DIR Katalog rootfs budowy (PAGAN_ROOT) --de NAZWA Środowisko graficzne: xfce|deepin|budgie|pagande|none (domyślnie: xfce) – none = BAZA bez GUI (instalator z netinstallu / --auto) Uwaga: kde i gnome są tymczasowo wyłączone. --kernel WERSJA Wersja jądra Linux (np. 6.18.43) --calamares Włącz Calamares jako instalator (domyślne) --no-calamares Pomiń Calamares (ISO bez instalatora systemu) --no-fish Nie ustawiaj fish jako powłoki użytkowników (zostaje bash) --img Tylko obraz IMG --iso Tylko obraz ISO (domyślne) --both IMG + ISO --no-nvidia Pomiń budowę sterowników NVIDIA --no-rolling-kernel Nie pakuj jądra jako pakiet rolling (linux) do PAG --repo URL Domyślne repozytorium PAG (trafi do /etc/pag/repos.conf) --size MB Rozmiar obrazu IMG w MB --jobs N Liczba równoległych zadań (make/ninja). Domyślnie ograniczona pamięcią (1 zadanie ≈ 2 GB), by nie zamrozić komputera. Alias środowiskowy: PAGAN_JOBS=N --force-pkg=NAZWA Wymuś przebudowę pakietu mimo zapisu resume --repo-pkgs="P1 P2" Doinstaluj pakiety z repo PAG do rootfs przed utworzeniem obrazu (np. --repo-pkgs="xfce4 xfce4-panel thunar lightdm"; wielokrotne użycie dopisuje pakiety) --rebuild Czyść i buduj od zera (alias: --clean, --from-scratch) --serial-console Loguj jądro/systemd ZAINSTALOWANEGO systemu także na ttyS0 (testy w QEMU/KVM: -serial file:log → pełny log jako tekst). Alias środowiskowy: PAGAN_SERIAL_CONSOLE=1 --keep-sources Z --rebuild: zachowaj $PAGAN_ROOT/sources (pobrane archiwa) i nie kasuj ich – oszczędza ponowne pobieranie -h, --help Ta pomoc EOF exit "${1:-0}" } parse_args_early() { while [[ $# -gt 0 ]]; do case "$1" in --config) export PAGAN_CONFIG_FILE="$2"; shift 2 ;; --root) export PAGAN_ROOT="$2"; shift 2 ;; --de) export PAGAN_DE="$2"; shift 2 ;; --kernel) export KERNEL_VERSION="$2"; shift 2 ;; --img) export BUILD_IMG=1 BUILD_ISO=0 ARTIFACT_MODE=img; shift ;; --iso) export BUILD_IMG=0 BUILD_ISO=1 ARTIFACT_MODE=iso; shift ;; --both) export BUILD_IMG=1 BUILD_ISO=1 ARTIFACT_MODE=both; shift ;; --no-nvidia) export SKIP_NVIDIA=1; shift ;; --no-rolling-kernel) export ROLLING_KERNEL=0; shift ;; --serial-console) export PAGAN_SERIAL_CONSOLE=1; shift ;; --calamares) export PAGAN_CALAMARES=1; shift ;; --no-calamares) export PAGAN_CALAMARES=0; shift ;; --no-fish) export PAGAN_FISH=0; shift ;; --repo) export PAGAN_REPO_URL="$2"; shift 2 ;; --size) export IMAGE_SIZE="$2"; shift 2 ;; --jobs) export PAGAN_JOBS="$2"; shift 2 ;; --jobs=*) export PAGAN_JOBS="${1#*=}"; shift ;; --force-pkg=*) export FORCE_PKGS="${1#*=},${FORCE_PKGS:-}"; shift ;; --repo-pkgs=*) export REPO_PKGS="${REPO_PKGS:+$REPO_PKGS }${1#*=}"; shift ;; --rebuild|--clean|--from-scratch) export REBUILD_FROM_SCRATCH=1; shift ;; --keep-sources) export REBUILD_KEEP_SOURCES=1; shift ;; -h|--help) _usage_builder ;; *) echo "Nieznany argument: $1" >&2; _usage_builder 2 ;; esac done } parse_args_early "$@" # ========================= PLIK KONFIGURACYJNY ========================= # Wczytuje ./build.conf lub /etc/pagan-build.conf. Zmienne już ustawione przez # CLI/środowisko mają pierwszeństwo i NIE zostają nadpisane przez plik. _load_config_file() { local _cf="${PAGAN_CONFIG_FILE:-}" if [[ -z "$_cf" ]]; then for _c in "$SCRIPT_DIR/build.conf" "/etc/pagan-build.conf"; do [[ -f "$_c" ]] && { _cf="$_c"; break; } done fi [[ -n "$_cf" && -f "$_cf" ]] || return 0 local _vars=(PAGAN_ROOT PAGAN_VERSION PAGAN_DE PAGAN_RELEASE LFS_VERSION KERNEL_VERSION MAKEFLAGS OUTPUT_DIR ARTIFACT_MODE REBUILD_FROM_SCRATCH IMAGE_SIZE PAGAN_REPO_URL PAGAN_LOG_LEVEL SKIP_NVIDIA BUILD_IMG BUILD_ISO ROLLING_KERNEL REPO_PKGS PAGAN_CALAMARES PAGAN_FISH PAGAN_JOBS) local _v declare -A _pre=() for _v in "${_vars[@]}"; do _pre[$_v]="${!_v:-__UNSET__}" done # shellcheck disable=SC1090 source "$_cf" 2>/dev/null || true # Przywróć wartości z CLI/env (mają pierwszeństwo nad plikiem) for _v in "${_vars[@]}"; do if [[ "${_pre[$_v]:-__UNSET__}" != "__UNSET__" ]]; then printf -v "$_v" '%s' "${_pre[$_v]}" fi done echo "[config] Wczytano konfigurację: $_cf" } _load_config_file PAGAN_ROOT="${PAGAN_ROOT:-/mnt/pagan}" PAGAN_VERSION="${PAGAN_VERSION:-0.1.2 Mokosz}" PAGAN_DE="${PAGAN_DE:-xfce}" # wersja podstawowa: xfce | deepin | budgie | pagande | none (none = baza/CLI; kde/gnome wyłączone) PAGAN_RELEASE="2026.09" # Tapeta wydania; można nadpisać np. PAGAN_WALLPAPER=paganlinux-marzanna-wallpaper.png. PAGAN_WALLPAPER="${PAGAN_WALLPAPER:-paganlinux-mokosz-autumn-wallpaper.png}" LFS_VERSION="13.1" # Instalator systemu: Calamares (domyślnie włączony). 0 = pomiń instalator. PAGAN_CALAMARES="${PAGAN_CALAMARES:-1}" # Powłoka: fish zamiast bash dla użytkowników i roota (domyślnie). 0 = zostaw bash. PAGAN_FISH="${PAGAN_FISH:-1}" # Katalog patchy LFS (RC: 13.1-rc2; po wydaniu stabilnym zmień na 13.1) LFS_PATCH_DIR="${LFS_PATCH_DIR:-13.1-rc2}" # Kernel (można zmienić: KERNEL_VERSION=6.18.x dla starszego LTS – URL skaluje się sam) KERNEL_VERSION="${KERNEL_VERSION:-7.2.5}" KERNEL_URL="https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_VERSION%%.*}.x/linux-${KERNEL_VERSION}.tar.xz" # Rolling kernel (Arch-like): pakuj jądro jako pakiet .pag (linux) i zainstaluj przez PAG, # żeby `pag upgrade` / `pagan-drivers kernel` mogły je aktualizować bez nowego ISO. # Ustaw 0 (--no-rolling-kernel), jeśli kernel ma żyć tylko w ISO. ROLLING_KERNEL="${ROLLING_KERNEL:-1}" # Domyślne repozytorium PAG – trafi do /etc/pag/repos.conf w budowanym systemie PAGAN_REPO_URL="${PAGAN_REPO_URL:-https://repo.paganlinux.eu/stable/}" # Szyfrowany DNS (DoT) przez systemd-resolved w zainstalowanym systemie. # PAGAN_DNS="" wyłącza (zostawia DNS z DHCP). DNSSEC=allow-downgrade: # walidacja gdy serwer wspiera, bez łamania sieci lokalnych. PAGAN_DNS="${PAGAN_DNS:-1.1.1.1}" # PAGAN_SERIAL_CONSOLE=1 → jądro i systemd ZAINSTALOWANEGO systemu logują także # na konsolę szeregową (ttyS0). W QEMU/KVM `-serial file:log` daje wtedy PEŁNY # log startu jako tekst – nie trzeba zrzutów ekranu, a przyczynę widać od razu. # # Domyślnie 0 (wyłączone): z „console=ttyS0” jako OSTATNIĄ konsolą /dev/console # wskazuje port szeregowy, więc na komputerze bez takiego portu komunikaty # systemd nie pojawiłyby się na ekranie. Włączać tylko do testów/VM. PAGAN_SERIAL_CONSOLE="${PAGAN_SERIAL_CONSOLE:-0}" PAGAN_DNS_TLS="${PAGAN_DNS_TLS:-yes}" # Hardware / równoległość. # PAGAN_JOBS = liczba równoległych zadań (make/ninja/mksquashfs). Domyślnie # ograniczamy ją PAMIĘCIĄ (1 zadanie ≈ 2 GB), bo Qt6/KF6 (ciężkie C++ + PCH) przy # -j$(nproc) potrafią wyczerpać RAM → swap → komputer zamarza. Nadpisz przez # --jobs N, PAGAN_JOBS=N w środowisku lub w build.conf. PAGAN_JOBS="${PAGAN_JOBS:-}" if [[ -z "$PAGAN_JOBS" ]]; then _ncpu="$(nproc 2>/dev/null || echo 1)" _memgb=$(( $(awk '/^MemTotal:/{print $2}' /proc/meminfo 2>/dev/null || echo 0) / 1048576 )) if [[ "$_memgb" -gt 0 ]]; then PAGAN_JOBS=$(( _memgb / 2 )) if [[ "$PAGAN_JOBS" -lt 1 ]]; then PAGAN_JOBS=1; fi if [[ "$PAGAN_JOBS" -gt "$_ncpu" ]]; then PAGAN_JOBS="$_ncpu"; fi else PAGAN_JOBS="$_ncpu" fi fi MAKEFLAGS="-j$PAGAN_JOBS" export PAGAN_JOBS MAKEFLAGS LFS_TGT="$(uname -m)-pagan-linux-gnu" # Ścieżki SRC_DIR="$PAGAN_ROOT/sources" TOOLS_DIR="$PAGAN_ROOT/tools" CROSS_BIN="$TOOLS_DIR/bin" OUTPUT_DIR="${OUTPUT_DIR:-$SCRIPT_DIR/ISO}" mkdir -p "$OUTPUT_DIR" # Artefakt końcowy: img | iso | both | ask ARTIFACT_MODE="${ARTIFACT_MODE:-iso}" # Wymuś pełne czyszczenie i budowę od zera: 1|true|yes REBUILD_FROM_SCRATCH="${REBUILD_FROM_SCRATCH:-0}" # Pomiń budowę sterowników NVIDIA (open-gpu-kernel-modules): 1|true|yes SKIP_NVIDIA="${SKIP_NVIDIA:-0}" # Rozmiar obrazu IMG (MB) IMAGE_SIZE="${IMAGE_SIZE:-16384}" # 16GB # ========================= SYSTEM LOGÓW ========================= # Poziomy: 0=DEBUG 1=INFO 2=WARN 3=ERROR : "${PAGAN_LOG_LEVEL:=0}" LOG_FILE="/var/log/pagan-build.log" SUMMARY_FILE="/var/log/pagan-build.summary.log" mkdir -p "$(dirname "$LOG_FILE")" # Inicjalizuj plik logu cat > "$LOG_FILE" < "$SUMMARY_FILE" # ========================= KOLORY (tylko konsola) ========================= RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' BLUE='\033[0;34m'; MAGENTA='\033[0;35m'; CYAN='\033[0;36m' BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m' # ========================= INTERFEJS UŻYTKOWNIKA ========================= source "$SCRIPT_DIR/ui.sh" ui_init # ========================= STATYSTYKI ========================= PKG_BUILT=0; PKG_FAILED=0; PKG_SKIPPED=0 RESUME_FILE="${RESUME_FILE:-/var/log/pagan-build.done}" # Rejestry zasobów IMG – pozwalają trapowi EXIT (patrz _show_summary) posprzątać # po błędzie: bind-mounty i loop device nie mogą zostać zajęte po przerwaniu. _PAGAN_IMG_LOOPDEV="" _PAGAN_IMG_MNT="" # ========================= SYSTEM RESUME ========================= # Sprawdza czy pakiet był już zbudowany _is_built() { local pkg="$1" # --force-pkg nadpisuje resume. Drugi warunek: dopasowanie po nazwie-bazie # (część przed pierwszym '-'), np. --force-pkg=gcc wymusza także gcc-pass1/pass2. # To celowe (przebudowa wszystkich etapów danego pakietu); NIE rozciąga się na # inne nazwy – np. 'gcc14' nie pasuje do 'gcc', bo jego baza to 'gcc14'. [[ -n "${FORCE_PKGS:-}" ]] && [[ ",${FORCE_PKGS}," == *",${pkg},"* || ",${FORCE_PKGS}," == *",${pkg%%-*},"* ]] && return 1 [[ -f "$RESUME_FILE" ]] && grep -qxF "$pkg" "$RESUME_FILE" 2>/dev/null } # Oznacza pakiet jako zbudowany _mark_built() { local pkg="$1" mkdir -p "$(dirname "$RESUME_FILE")" echo "$pkg" >> "$RESUME_FILE" } _is_true() { # Akceptuj też warianty mieszane (True/Yes/On) – normalizacja do małych liter. local v="${1:-}" case "${v,,}" in 1|true|yes|on|y) return 0 ;; *) return 1 ;; esac } # Czy adres (hex, np. z readelf/objdump) to dokładnie 0x9000 – link_addr jądra # GRUB-a i386-pc? # # Oba narzędzia piszą ten sam adres RÓŻNYMI formatami: # readelf -SW → „00009000” (zeruje pole Addr do szerokości ELF-a: 8 dla i386-pc) # objdump -f → „0x00009000” (z prefiksem 0x) # Dlatego najpierw zdejmujemy opcjonalny prefiks, a potem porównujemy LICZBOWO. # # DLACZEGO NIE PORÓWNANIE TEKSTOWE: `[[ "$a" == "9000" ]]` dawało FAŁSZYWY # ALARM („kernel.img is miscompiled”) dla całkowicie poprawnego pliku (bo # „00009000” != „9000”), a permissywny glob `!= *9000` przepuszczał realnie # zepsute 0x19000. _addr_is_0x9000() { local a="${1:-}" a="${a#0[xX]}" [[ "$a" =~ ^[0-9a-fA-F]+$ ]] || return 1 (( 16#$a == 0x9000 )) } clean_build_state() { step "Tryb: zbuduj od nowa" if [[ -z "$PAGAN_ROOT" || "$PAGAN_ROOT" == "/" ]]; then error "Niebezpieczny PAGAN_ROOT='$PAGAN_ROOT' - odmawiam czyszczenia" fi if declare -F umount_virtual_fs >/dev/null; then umount_virtual_fs >/dev/null 2>&1 || true fi local _keep="" # Odzysk po przerwanym --rebuild --keep-sources: sources odłożone do # $PAGAN_ROOT.sources.keep, a rootfs jeszcze nie odtworzony. if [[ ! -d "$PAGAN_ROOT/sources" && -d "$PAGAN_ROOT.sources.keep" ]]; then warn "Odzyskuję wcześniej odłożone sources/ (przerwany --rebuild --keep-sources)" mkdir -p "$PAGAN_ROOT" mv "$PAGAN_ROOT.sources.keep" "$PAGAN_ROOT/sources" fi if _is_true "${REBUILD_KEEP_SOURCES:-}" && [[ -d "$PAGAN_ROOT/sources" ]]; then # sources/ (pobrane archiwa, zwykle kilka GB) wynosimy w całości poza # rootfs (rename na tym samym FS = natychmiastowe), żeby --rebuild ich # nie skasował – po odtworzeniu rootfs wracają na miejsce. _keep="$PAGAN_ROOT.sources.keep" warn "Zachowuję sources/ (--keep-sources): przenoszę do $_keep" rm -rf "$_keep" mv "$PAGAN_ROOT/sources" "$_keep" || \ error "Nie udało się odłożyć sources/ ($_keep) – przerwij i zrób to ręcznie" fi warn "Czyszczenie katalogu builda: $PAGAN_ROOT" rm -rf --one-file-system "$PAGAN_ROOT" rm -f "$RESUME_FILE" mkdir -p "$PAGAN_ROOT" "$OUTPUT_DIR" if [[ -n "$_keep" && -d "$_keep" ]]; then mv "$_keep" "$PAGAN_ROOT/sources" ok "Przywrócono sources/ do $PAGAN_ROOT/sources" fi ok "Wyczyszczono rootfs i stan resume; start od zera" } # Wykrywa nieaktualny zapis resume. Sytuacja: ktoś ręcznie skasował $PAGAN_ROOT, # a wpisy w $RESUME_FILE (/var/log/pagan-build.done, POZA rootfs) pozostały – # przez co build "myśli", że pakiety są zbudowane, choć rootfs jest pusty. _detect_stale_resume() { [[ -s "$RESUME_FILE" ]] || return 0 local n n=$(wc -l < "$RESUME_FILE" 2>/dev/null || echo 0) if [[ ! -d "$PAGAN_ROOT/usr" ]] || [[ ! -d "$PAGAN_ROOT/etc" ]]; then warn "Wykryto nieaktualny zapis resume ($n pakietów) – $PAGAN_ROOT jest pusty/nieistniejący" warn "Resetuję resume (build od początku; usuń ręcznie $RESUME_FILE, aby pominąć to ostrzeżenie)" rm -f "$RESUME_FILE" return 0 fi # Samo-naprawa ABI libjpeg: host (Arch) i binarki linkowane host-first używają # libjpeg.so.8. Stary build targetu dawał .62, a libtiff/tumbler linkowały # wlasnie z .62. Jeśli w rootfs jest jeszcze .62 (stary ABI) albo brak .8, # przebudowujemy libjpeg (receptura ma -DWITH_JPEG8=1) + zależne pakiety. if [[ -d "$PAGAN_ROOT/usr/lib" ]] \ && { [[ -e "$PAGAN_ROOT/usr/lib/libjpeg.so.62" ]] || [[ ! -e "$PAGAN_ROOT/usr/lib/libjpeg.so.8" ]]; } \ && grep -qxF 'libjpeg-turbo-3.1.3' "$RESUME_FILE" 2>/dev/null; then warn "libjpeg: wykryto stary ABI .62 w targetcie – wymuszam przebudowę libjpeg/libtiff/tumbler (na .8)" sed -i -E '/^(libjpeg-turbo-3\.1\.3|libtiff-4\.7\.1|tumbler-4\.20\.2)$/d' "$RESUME_FILE" 2>/dev/null || true fi # KF6 CoreAddons: metatypes instalowały się do zagnieżdżonego rootfsa # (absolutny KDE_INSTALL_QTMETATYPESDIR + DESTDIR), a eksport celuje w # $PAGAN_ROOT/usr/metatypes/… → brak pliku = kpmcore się nie skonfiguruje. if [[ -e "$PAGAN_ROOT/usr/lib/cmake/KF6CoreAddons/KF6CoreAddonsTargets.cmake" ]] \ && [[ ! -e "$PAGAN_ROOT/usr/metatypes/qt6kf6coreaddons_metatypes.json" ]]; then warn "KF6 CoreAddons: brak pliku metatypes w targetcie – wymuszam przebudowę kcoreaddons" sed -i '/^kcoreaddons-6\.28\.0$/d' "$RESUME_FILE" 2>/dev/null || true fi # KF6: stare configi (z pełnego KF6) eksportują targety QML, których libki # usuwamy (budujemy KF6 bez QML) → find_package(KF6...) pada. Wymuś # przebudowę danych modułów, żeby ich configi były spójne z libkami. local _kf6_pairs=( "kcoreaddons:KF6CoreAddons" "kconfig:KF6Config" "ki18n:KF6I18n" "kwidgetsaddons:KF6WidgetsAddons" ) local _pp _cm _tp for _pp in "${_kf6_pairs[@]}"; do _cm="${_pp##*:}" _tp="$PAGAN_ROOT/usr/lib/cmake/$_cm/${_cm}Targets.cmake" [[ -f "$_tp" ]] || continue grep -q "Qml" "$_tp" 2>/dev/null || continue [[ -e "$PAGAN_ROOT/usr/lib/lib${_cm}Qml.so.6" ]] && continue warn "KF6 $_cm: stary config eksportuje QML bez libki – wymuszam przebudowę ${_pp%%:*}-6.28.0" sed -i "/^${_pp%%:*}-6\.28\.0$/d" "$RESUME_FILE" 2>/dev/null || true done } # ========================= FUNKCJE LOGOWANIA ========================= # _log_raw – zapisuje do pliku (bez kolorów) i na konsolę _log_raw() { local color="$1" lvl="$2" msg="$3" local ts; ts=$(date '+%H:%M:%S') echo -e "${color}[${ts}] [${lvl}]${NC} $msg" echo "[${ts}] [${lvl}] $msg" >> "$LOG_FILE" } dbg() { [[ $PAGAN_LOG_LEVEL -le 0 ]] && _log_raw "${DIM}" "DEBUG" "$*" || true } info() { [[ $PAGAN_LOG_LEVEL -le 1 ]] && _log_raw "${BLUE}" "INFO " "$*" || true } warn() { [[ $PAGAN_LOG_LEVEL -le 2 ]] && _log_raw "${YELLOW}" "WARN " "$*" >&2 || true } # Brak KRYTYCZNY – nie wystarczy „uwaga”: obraz z takim brakiem nie nadaje się do # wydania. Tak liczony problem podnosi VERIFY_CRITICAL, a main() PRZERYWA przed # create_image/create_iso (obejście: PAGAN_ALLOW_BROKEN_ISO=1). # # DLACZEGO OSOBNA FUNKCJA: wszystkie braki były wcześniej zwykłymi ostrzeżeniami, # więc ISO wyszło bez modułów NVIDIA (DESTDIR zamiast INSTALL_MOD_PATH) i z # niepełnym initramfs – nikt tego nie zatrzymał. Kryterium: „czy z takiego obrazu # da się zainstalować i wystartować system”. crit() { warn "$*" # `fails` jest lokalne w verify_rootfs – dynamiczny zasięg basha sprawia, że # trafiamy w tę samą zmienną; poza tą funkcją zmiennej nie ma, więc `-v` chroni # przed błędem pod `set -u`. [[ -v fails ]] && ((fails++)) || true [[ -v VERIFY_CRITICAL ]] && ((VERIFY_CRITICAL++)) || true return 0 } error() { _log_raw "${RED}" "ERROR" "$*" >&2 _dump_stack _log_raw "${RED}" "FATAL" "Budowa przerwana. Log: $LOG_FILE" >&2 exit 1 } ok() { _log_raw "${GREEN}" "OK " "$*" ((PKG_BUILT++)) || true echo "✓ $*" >> "$SUMMARY_FILE" } step() { local title="$*" echo "" echo -e "${MAGENTA}${BOLD}═══ ${title} ═══${NC}" echo "━━━ $(date '+%H:%M:%S') ━━━ ${title} ━━━" >> "$LOG_FILE" dbg "START: $title" } _dump_stack() { local frame=0 echo " Stack trace:" >> "$LOG_FILE" while caller $frame 2>/dev/null; do ((frame++)) done >> "$LOG_FILE" } # Trap błędów – łapie SIGERR _on_error() { local lineno="$1" cmd="$2" echo -e "${RED}[ERROR]${NC} Linia ${lineno}: \"${cmd}\"" | tee -a "$LOG_FILE" >&2 echo "[$(date '+%H:%M:%S')] [ERROR] Linia ${lineno}: ${cmd}" >> "$LOG_FILE" # Kluczowa diagnoza: jeśli SAM PLIK skryptu zmienił się w trakcie builda, # to komunikaty o „nieznalezionym poleceniu”/bzdurnych numerach linii są # artefaktem desynchronizacji odczytu, a nie błędem w skrypcie. if _pagan_self_changed; then { echo -e "${YELLOW}[UWAGA]${NC} build-pagan.sh ZMIENIŁ SIĘ w trakcie działania (rozmiar/mtime)." echo " Bash czyta skrypt z offsetem – po zmianie pliku wykonuje przypadkowe" echo " fragmenty linii i podaje bzdurne numery. Zamknij edytor (Zed zapisuje" echo " plik przy zmianie fokusu – klik na terminal!) i uruchom build ponownie." echo " To NIE jest błąd skryptu – nie zgłaszaj tego jako błąd w kodzie." } >&2 fi _dump_stack } trap '_on_error "${LINENO}" "${BASH_COMMAND}"' ERR # Podsumowanie na wyjściu _show_summary() { # Best-effort cleanup: avoid stale mounts after cancel/error. if declare -F umount_virtual_fs >/dev/null; then umount_virtual_fs >/dev/null 2>&1 || true fi # Zasoby IMG (bind-mounty + loop) – gdy create_image padnie w połowie. if declare -F _cleanup_image_mounts >/dev/null; then _cleanup_image_mounts fi ui_summary } trap _show_summary EXIT # ========================= KONFIGURACJA ========================= # ========================= BANNER ========================= show_banner() { cat << 'EOF' ╔═══════════════════════════════════════════════════════════════════╗ ║ ║ ║ ██████ █████ █████ █████ ███ ██ ║ ║ ██ ██ ██ ██ ██ ██ ██ ████ ██ ║ ║ ██████ ███████ ██████ ███████ ██ ██ ██ ║ ║ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ║ ║ ██ ██ ██ █████ ██ ██ ██ ████ ║ ║ ║ ║ ╔══════════════════════════════════════════════════════════╗ ║ EOF # Wiersze ze zmiennymi – padding liczony w locie, żeby ramka # zawsze się domykała (niezależnie od długości wersji/architektury). local pad entry for entry in \ " Pagan Linux Builder v${PAGAN_VERSION}" \ " LFS ${LFS_VERSION} + BLFS + ${PAGAN_DE^^}" \ " Architektura: $(uname -m)" \ " Kernel: ${KERNEL_VERSION} $(_is_true "${ROLLING_KERNEL:-1}" && printf '(rolling/PAG)' || printf '(tylko w ISO)')" \ " Data: $(date +'%Y-%m-%d %H:%M:%S')"; do # Gdy wpis jest dłuższy niż ramka (58), nie licz ujemnej szerokości # (printf '%*s' z ujemną szerokością rozjeżdża ramkę). if (( ${#entry} > 58 )); then pad="" else pad=$(printf '%*s' "$((58 - ${#entry}))" "") fi printf '║ ║%s%s║ ║\n' "$entry" "$pad" done cat << 'EOF' ║ ╚══════════════════════════════════════════════════════════╝ ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════╝ EOF } # ========================= SPRAWDZANIE ZALEŻNOŚCI ========================= # Zwraca nazwę pakietu dla danego polecenia ($1) i menedżera pakietów ($2: apt|dnf|pacman). # WAŻNE: nazwy pakietów są RÓŻNE między dystrybucjami (np. Arch: glib-mkenums jest # w glib2, a nie w „libglib2.0-dev”; xorriso to „libisoburn”; pkg-config to „pkgconf”). # Błędna nazwa = pacman/apt przerywa CAŁĄ transakcję i nic nie instaluje. _pm_pkg() { local cmd="$1" pm="${2:-apt}" case "$cmd" in mkfs.fat) echo dosfstools ;; mkfs.ext4) echo e2fsprogs ;; mksquashfs) echo squashfs-tools ;; mcopy) echo mtools ;; glib-mkenums) case "$pm" in dnf) echo glib2-devel ;; pacman) echo glib2 ;; # Arch: binarka + nagłówki w jednym pakiecie glib2 *) echo libglib2.0-dev ;; esac ;; g-ir-scanner) case "$pm" in dnf) echo gobject-introspection-devel ;; *) echo gobject-introspection ;; esac ;; intltool-update) echo intltool ;; losetup|blkid|wipefs) echo util-linux ;; parted) echo parted ;; cpio) echo cpio ;; rg) echo ripgrep ;; zstd) echo zstd ;; unzip) echo unzip ;; gpg) case "$pm" in dnf) echo gnupg2 ;; *) echo gnupg ;; esac ;; pkg-config) case "$pm" in pacman) echo pkgconf ;; # Arch nie ma pakietu „pkg-config” dnf) echo pkgconf-pkg-config ;; *) echo pkg-config ;; esac ;; xorriso) case "$pm" in pacman) echo libisoburn ;; # Arch: /usr/bin/xorriso jest w libisoburn *) echo xorriso ;; esac ;; find) echo findutils ;; diff|cmp) echo diffutils ;; strings) echo binutils ;; gcc) echo gcc ;; g++) # g++ to zwykły pakiet tylko w Debianie; Arch/Fedora dają go z gcc / gcc-c++ case "$pm" in pacman) echo gcc ;; dnf) echo gcc-c++ ;; *) echo g++ ;; esac ;; iceauth) case "$pm" in pacman) echo xorg-iceauth ;; *) echo iceauth ;; esac ;; *) echo "$cmd" ;; esac } # ========================= PLIKI STARTOWE ISO (SYSLINUX) ========================= # Do bootowalnego (hybrydowego) ISO potrzeba na HOŚCIE plików syslinux: # isolinux.bin – El Torito boot BIOS (menu startowe ISO), # ldlinux.c32 – moduł wymagany przez isolinux.bin, # isohdpfx.bin – 432-bajtowy MBR isohybrid: bez niego sektor 0 ISO to zera, # brak tablicy partycji → balenaEtcher/Rufus odrzucają obraz # („Missing partition table") i pendrive nie bootuje. # Syslinux bywa rozłożony różnie w zależności od dystrybucji: # Arch/EndeavourOS: /usr/lib/syslinux/bios/* # Debian/Ubuntu: /usr/lib/ISOLINUX/*, /usr/lib/syslinux/mbr/* # Fedora/openSUSE: /usr/share/syslinux/* # Skrypt instaluje pakiet automatycznie (jak pozostałe zależności), a gdy to # niemożliwe – przerywa z czytelnym komunikatem zamiast po cichu wygenerować # ISO niebootowalne z USB. SYSLINUX_DIRS=( /usr/lib/syslinux/bios /usr/lib/syslinux /usr/lib/syslinux/modules/bios /usr/share/syslinux /usr/lib/ISOLINUX /usr/lib/syslinux/mbr ) # $1 = nazwa pliku (isolinux.bin|ldlinux.c32|isohdpfx.bin) → echo pełnej ścieżki _find_syslinux_file() { local f="$1" d for d in "${SYSLINUX_DIRS[@]}"; do if [[ -f "$d/$f" ]]; then echo "$d/$f" return 0 fi done return 1 } # Instaluje syslinux na hoście przez menedżer pakietów (jeśli się da). _install_syslinux_host() { # Menedżer pakietów hosta uruchamiamy bez LD_LIBRARY_PATH/LIBRARY_PATH # buildera – inaczej binarka hosta załaduje biblioteki z $PAGAN_ROOT # i wywali się na niezgodności SONAME („libcurl.so.4: no version # information available (required by libalpm.so.16)”). if command -v pacman &>/dev/null; then env -u LD_LIBRARY_PATH -u LIBRARY_PATH pacman -Sy --noconfirm --needed syslinux elif command -v apt-get &>/dev/null; then env -u LD_LIBRARY_PATH -u LIBRARY_PATH apt-get update env -u LD_LIBRARY_PATH -u LIBRARY_PATH apt-get install -y isolinux syslinux-common syslinux-utils elif command -v dnf &>/dev/null; then env -u LD_LIBRARY_PATH -u LIBRARY_PATH dnf install -y syslinux else return 1 fi } check_dependencies() { step "Sprawdzanie zależności systemowych" local deps=( "gcc" "g++" "make" "bison" "flex" "texinfo" "bc" "wget" "curl" "tar" "xz" "bzip2" "gzip" "zstd" "unzip" "patch" "gawk" "sed" "grep" "find" "strings" "file" "diff" "cmp" "cpio" "python3" "perl" "m4" "cmake" "ninja" "meson" "gperf" "git" "rsync" "gpg" "pkg-config" "xorriso" "mkfs.fat" "mkfs.ext4" "mcopy" "parted" "mksquashfs" "losetup" "g-ir-scanner" "intltool-update" "itstool" "iceauth" "rg" ) # Narzędzia glib (glib-mkenums, glib-genmarshal) CELOWO nie są tu # sprawdzane: nie potrzebujemy ich z hosta. glib-mkenums powstaje dopiero # przy budowie glib2 w targetcie i builder podstawia je sobie sam # (_provide_glib_build_tools tworzy wrapper w $TOOLS_DIR/bin i to on jest # pierwszy w PATH przy buildach). Wymaganie ich od hosta było fałszywym # alarmem, który wołał `pacman -Sy` – czyli CZĘŚCIOWĄ aktualizację # systemu, po której host potrafi się rozjechać (klasyk: libalpm.so.16 # vs libcurl „no version information available”). _provide_glib_build_tools local missing=() for dep in "${deps[@]}"; do # Zadowalamy się też narzędziem z TARGETU lub wrapperem buildera – # przy wznowionym buildzie rootfs już je ma. if command -v "$dep" &>/dev/null \ || [[ -x "$TOOLS_DIR/bin/$dep" ]] \ || [[ -x "$PAGAN_ROOT/usr/bin/$dep" ]] \ || [[ -x "$PAGAN_ROOT/usr/sbin/$dep" ]]; then continue fi # texinfo nie ma binarki „texinfo” (w Arch/Fedora jest makeinfo/texi2any) case "$dep" in texinfo) command -v makeinfo &>/dev/null || command -v texi2any &>/dev/null || missing+=("$dep") ;; *) missing+=("$dep") ;; esac done if [[ ${#missing[@]} -gt 0 ]]; then warn "Brakujące pakiety: ${missing[*]}" info "Instalowanie brakujących pakietów..." # Uwaga: menedżer pakietów hosta uruchamiamy w OCZYSZCZONYM środowisku # (env -u LD_LIBRARY_PATH -u LIBRARY_PATH). Builder eksportuje # LD_LIBRARY_PATH na $PAGAN_ROOT/usr/lib, a wtedy binarka hosta # (pacman) potrafi załadować biblioteki z TARGETU i wywalić się na # niezgodności SONAME (np. „libcurl.so.4: no version information # available (required by libalpm.so.16)”). if command -v apt-get &>/dev/null; then local apt_pkgs=() for dep in "${missing[@]}"; do apt_pkgs+=("$(_pm_pkg "$dep" apt)"); done env -u LD_LIBRARY_PATH -u LIBRARY_PATH apt-get update env -u LD_LIBRARY_PATH -u LIBRARY_PATH apt-get install -y "${apt_pkgs[@]}" \ libncurses-dev libssl-dev libreadline-dev \ libxml2-dev libxslt1-dev libglib2.0-dev \ libgtk-3-dev libpango1.0-dev libcairo2-dev \ libx11-dev libxext-dev libxft-dev \ libfreetype6-dev libfontconfig1-dev \ libjpeg-dev libpng-dev libtiff-dev \ libgdk-pixbuf2.0-dev libnotify-dev \ libdbus-1-dev libsystemd-dev libudev-dev libmnl-dev libevdev-dev \ libelf-dev libffi-dev libunistring-dev libacl1-dev libattr1-dev \ libpam0g-dev libgirepository1.0-dev gobject-introspection \ libqt5svg5-dev qtbase5-dev qtdeclarative5-dev qttools5-dev \ shared-mime-info intltool xsltproc gperf valac gcc-multilib \ libc6-dev-i386 python3-pip python3-venv xorg-util-macros elif command -v dnf &>/dev/null; then local dnf_pkgs=() for dep in "${missing[@]}"; do dnf_pkgs+=("$(_pm_pkg "$dep" dnf)"); done env -u LD_LIBRARY_PATH -u LIBRARY_PATH dnf groupinstall -y "Development Tools" "Development Libraries" env -u LD_LIBRARY_PATH -u LIBRARY_PATH dnf install -y "${dnf_pkgs[@]}" \ ncurses-devel openssl-devel readline-devel \ libxml2-devel libxslt-devel glib2-devel \ gtk3-devel pango-devel cairo-devel \ libX11-devel libXext-devel libXft-devel \ freetype-devel fontconfig-devel \ libjpeg-devel libpng-devel libtiff-devel \ gdk-pixbuf2-devel libnotify-devel \ dbus-devel systemd-devel libudev-devel libmnl-devel libevdev-devel \ elfutils-libelf-devel libffi-devel libacl-devel libattr-devel \ pam-devel gobject-introspection-devel qt5-qtbase-devel \ qt5-qtdeclarative-devel qt5-qtsvg-devel shared-mime-info intltool \ libxslt-devel gperf vala gcc-c++ multilib-rpm-config \ python3-devel python3-pip xorg-x11-util-macros elif command -v pacman &>/dev/null; then local arch_pkgs=() for dep in "${missing[@]}"; do arch_pkgs+=("$(_pm_pkg "$dep" pacman)"); done # Uwaga (Arch): NIE MA pakietów „glib2-devel”/„libglib2.0-dev” – nagłówki # i narzędzia (glib-mkenums) są w glib2. Błędna nazwa = pacman przerywa # całą transakcję („pakiet nie został znaleziony”) i nic nie instaluje. # systemd CELOWO pominięty: na Arch/EndeavourOS próba aktualizacji # systemd (np. 260→261) koliduje z pakietami typu systemd-resolvconf / # systemd-sysvcompat (wymagają dokładnej wersji systemd=…), a dev-nagłówki # libsystemd nie są potrzebne na hoście – systemd buduje się w chrocie. if ! env -u LD_LIBRARY_PATH -u LIBRARY_PATH \ pacman -Sy --noconfirm --needed base-devel "${arch_pkgs[@]}" \ ncurses openssl readline libxml2 libxslt glib2 \ gtk3 pango cairo libx11 libxext libxft xtrans xorg-util-macros intltool \ freetype2 fontconfig libjpeg-turbo libpng libtiff \ gdk-pixbuf2 libnotify dbus \ elfutils libffi python python-pip wayland-protocols; then # Konflikt na liście dev-libs (np. AUR trzyma starszą wersję czegoś) # nie może zabić builda – doinstaluj przynajmniej same narzędzia. warn "Pełna lista dev-libs nie przeszła przez pacman – próbuję same narzędzia..." env -u LD_LIBRARY_PATH -u LIBRARY_PATH \ pacman -Sy --noconfirm --needed "${arch_pkgs[@]}" || \ error "Nie udało się zainstalować narzędzi builda przez pacman: ${arch_pkgs[*]}" fi else error "Nieznany menedżer pakietów. Zainstaluj ręcznie: ${missing[*]}" fi fi # Hostowy python3 musi faktycznie DZIAŁAĆ – glibc (i inne pakiety) używa go # do generowania nagłówków (gen-as-const). Najczęstsza przyczyna awarii: # „partial upgrade” na Arch – python zaktualizowany pod nowsze glibc, a libm # została stara → ImportError: „version `GLIBC_x.y' not found”. To NIE jest # błąd builda – trzeba najpierw naprawić system (sudo pacman -Syu). if ! python3 -c 'import math, sys' >/dev/null 2>&1; then error "Hostowy python3 jest uszkodzony (python3 -c 'import math' nie działa). Wykonaj PEŁNĄ aktualizację systemu (np. sudo pacman -Syu), potem uruchom build ponownie." fi # Pliki startowe ISO (isolinux + isohdpfx MBR) to NIE polecenia – command -v # ich nie złapie, więc sprawdzamy pliki wprost i instalujemy pakiet sami. if ! _find_syslinux_file isolinux.bin >/dev/null || ! _find_syslinux_file ldlinux.c32 >/dev/null || ! _find_syslinux_file isohdpfx.bin >/dev/null; then warn "Brak plików syslinux na hoście (isolinux.bin/ldlinux.c32/isohdpfx.bin) – instaluję..." _install_syslinux_host || \ error "Nieznany menedżer pakietów – zainstaluj syslinux ręcznie (potrzebne: isolinux.bin, ldlinux.c32, isohdpfx.bin)" if ! _find_syslinux_file isohdpfx.bin >/dev/null; then error "Auto-instalacja syslinux nie powiodła się. Bez isohdpfx.bin ISO nie będzie hybrydowe (balenaEtcher: 'Missing partition table'). Zainstaluj ręcznie: Arch/EndeavourOS 'sudo pacman -S syslinux' | Debian/Ubuntu 'sudo apt install isolinux syslinux-common syslinux-utils' | Fedora 'sudo dnf install syslinux'" fi fi # docbook-xsl (style XSL manpages) – xfce4-terminal 1.1.5+ generuje strony man # przez xsltproc z martwego URL http://docbook.sourceforge.net/… # („cannot parse …/manpages/docbook.xsl” → ninja: build stopped). Pakiet rejestruje # w /etc/xml/catalog rewriteURI dla tego legacy URL → xsltproc bierze style lokalnie. # To pakiet DANYCH (bez binarki) – command -v go nie złapie, sprawdzamy pliki. if ! find /usr/share/xml/docbook -maxdepth 1 -name 'xsl-stylesheets*' 2>/dev/null | grep -q .; then warn "Brak docbook-xsl (strony man XFCE: xfce4-terminal/…) – instaluję..." if command -v pacman &>/dev/null; then env -u LD_LIBRARY_PATH -u LIBRARY_PATH pacman -Sy --noconfirm --needed docbook-xsl elif command -v apt-get &>/dev/null; then env -u LD_LIBRARY_PATH -u LIBRARY_PATH apt-get update env -u LD_LIBRARY_PATH -u LIBRARY_PATH apt-get install -y docbook-xsl elif command -v dnf &>/dev/null; then env -u LD_LIBRARY_PATH -u LIBRARY_PATH dnf install -y docbook-xsl else error "Nieznany menedżer pakietów – zainstaluj ręcznie docbook-xsl (potrzebny do stron man xfce4-terminal)" fi if ! find /usr/share/xml/docbook -maxdepth 1 -name 'xsl-stylesheets*' 2>/dev/null | grep -q .; then error "Instalacja docbook-xsl nie powiodła się – zainstaluj ręcznie (Arch/EndeavourOS: 'sudo pacman -S docbook-xsl')" fi fi # jinja2 (host, python3) – wymagany przez meson systemd (generowanie plików). # systemd buduje się NA HOŚCIE hostowym mesonem/pythonem, więc moduł musi być # w hostowym python3 (install_pag instaluje go dopiero później – do targetu). if ! python3 -c 'import jinja2' >/dev/null 2>&1; then warn "Brak hostowego modułu jinja2 (wymagany przez systemd) – instaluję..." if command -v pacman &>/dev/null; then env -u LD_LIBRARY_PATH -u LIBRARY_PATH pacman -Sy --noconfirm --needed python-jinja elif command -v apt-get &>/dev/null; then apt-get update && apt-get install -y python3-jinja2 elif command -v dnf &>/dev/null; then dnf install -y python3-jinja2 else python3 -m pip install --break-system-packages jinja2 2>/dev/null || \ python3 -m pip install jinja2 fi if ! python3 -c 'import jinja2' >/dev/null 2>&1; then error "Nie udało się zainstalować jinja2 na hoście – wymagane do builda systemd (Arch: 'sudo pacman -S python-jinja', Debian/Ubuntu: 'sudo apt install python3-jinja2')" fi ok "jinja2 (host) zainstalowany" fi ok "Wszystkie zależności spełnione" } # ========================= INICJALIZACJA ========================= # ========================= USRMERGE (scalone /usr) ========================= # systemd (261) WYMAGA layoutu z połączonym /usr: /bin, /sbin, /lib muszą być # symlinkami do odpowiedników w /usr. Wcześniej były to zwykłe katalogi, więc: # - /bin/bash nie istniał (bash instaluje się do /usr/bin) → chroot /bin/bash # i ldd (skrypt z shebang #!/bin/bash) padały w sanity check, # - /bin/sh (tworzony przez build bash jako `ln -sf bash /bin/sh`) wisiał, # - systemd odmówiłby startu ("system is not with a merged /usr"). # Funkcja przenosi zawartość istniejących katalogów do /usr (jeśli coś tam jest) # i zamienia je w symlinki. Bezpieczna do wielokrotnego wywołania (resume). _usrmerge_rootfs() { # Bezpieczeństwo: nigdy nie ruszaj / przy PAGAN_ROOT="/" lub pustym if [[ -z "$PAGAN_ROOT" || "$PAGAN_ROOT" == "/" ]]; then warn "Usrmerge pominięty – niebezpieczny PAGAN_ROOT='${PAGAN_ROOT:-}'" return 0 fi # Uwaga: systemd budowany z -Dsplit-usr=false tłumaczy ścieżki /sbin/foo → # /usr/bin/foo, więc WSZYSTKO (łącznie z /usr/sbin) musi trafić do /usr/bin. # bin → usr/bin if [[ ! -L "$PAGAN_ROOT/bin" && -d "$PAGAN_ROOT/bin" ]]; then mkdir -p "$PAGAN_ROOT/usr/bin" cp -a "$PAGAN_ROOT/bin/." "$PAGAN_ROOT/usr/bin/" 2>/dev/null || true rm -rf "$PAGAN_ROOT/bin" ln -s usr/bin "$PAGAN_ROOT/bin" fi # sbin → usr/bin (NIE usr/sbin! inaczej agetty/modprobe są nieosiągalne dla systemd) if [[ ! -L "$PAGAN_ROOT/sbin" && -d "$PAGAN_ROOT/sbin" ]]; then # SysVinit-owe binarki (halt/shutdown/poweroff/reboot) przegrywają z systemd rm -f "$PAGAN_ROOT/sbin/halt" "$PAGAN_ROOT/sbin/shutdown" \ "$PAGAN_ROOT/sbin/poweroff" "$PAGAN_ROOT/sbin/reboot" "$PAGAN_ROOT/sbin/init" mkdir -p "$PAGAN_ROOT/usr/bin" cp -a "$PAGAN_ROOT/sbin/." "$PAGAN_ROOT/usr/bin/" 2>/dev/null || true rm -rf "$PAGAN_ROOT/sbin" ln -s usr/bin "$PAGAN_ROOT/sbin" fi # usr/sbin → usr/bin (realny katalog scal; puste → od razu symlink, żeby # pakiety z --sbindir=/usr/sbin lądowały w /usr/bin) if [[ ! -L "$PAGAN_ROOT/usr/sbin" ]]; then if [[ -d "$PAGAN_ROOT/usr/sbin" ]]; then rm -f "$PAGAN_ROOT/usr/sbin/halt" "$PAGAN_ROOT/usr/sbin/shutdown" \ "$PAGAN_ROOT/usr/sbin/poweroff" "$PAGAN_ROOT/usr/sbin/reboot" "$PAGAN_ROOT/usr/sbin/init" mkdir -p "$PAGAN_ROOT/usr/bin" cp -a "$PAGAN_ROOT/usr/sbin/." "$PAGAN_ROOT/usr/bin/" 2>/dev/null || true rm -rf "$PAGAN_ROOT/usr/sbin" else mkdir -p "$PAGAN_ROOT/usr/bin" fi ln -s bin "$PAGAN_ROOT/usr/sbin" fi # lib → usr/lib if [[ ! -L "$PAGAN_ROOT/lib" && -d "$PAGAN_ROOT/lib" ]]; then mkdir -p "$PAGAN_ROOT/usr/lib" cp -a "$PAGAN_ROOT/lib/." "$PAGAN_ROOT/usr/lib/" 2>/dev/null || true rm -rf "$PAGAN_ROOT/lib" ln -s usr/lib "$PAGAN_ROOT/lib" fi # lib64 → usr/lib (x86_64 ABI) if [[ ! -L "$PAGAN_ROOT/lib64" && -d "$PAGAN_ROOT/lib64" ]]; then mkdir -p "$PAGAN_ROOT/usr/lib" cp -a "$PAGAN_ROOT/lib64/." "$PAGAN_ROOT/usr/lib/" 2>/dev/null || true rm -rf "$PAGAN_ROOT/lib64" ln -s usr/lib "$PAGAN_ROOT/lib64" fi # usr/lib64 → usr/lib: ld.so NIE szuka /usr/lib64 (poza cache); bez scalenia # binarki nie znajdą np. libjpeg.so.8 (libtiff!). if [[ ! -L "$PAGAN_ROOT/usr/lib64" ]]; then if [[ -d "$PAGAN_ROOT/usr/lib64" ]]; then mkdir -p "$PAGAN_ROOT/usr/lib" # dev-symlink libjpeg.so z /usr/lib (→ .8) jest właściwszy niż stary z lib64 (→ .62) rm -f "$PAGAN_ROOT/usr/lib64/libjpeg.so" cp -a "$PAGAN_ROOT/usr/lib64/." "$PAGAN_ROOT/usr/lib/" 2>/dev/null || true rm -rf "$PAGAN_ROOT/usr/lib64" else mkdir -p "$PAGAN_ROOT/usr/lib" fi ln -s lib "$PAGAN_ROOT/usr/lib64" fi # /var/run i /var/lock muszą być symlinkami do tmpfs /run (inaczej systemd # taint var-run-bad, a na RO squashfs zapis do /var/run w ogóle nie działa) if [[ ! -L "$PAGAN_ROOT/var/run" ]]; then rm -rf "$PAGAN_ROOT/var/run" ln -s ../run "$PAGAN_ROOT/var/run" fi if [[ ! -L "$PAGAN_ROOT/var/lock" ]]; then rm -rf "$PAGAN_ROOT/var/lock" ln -s ../run/lock "$PAGAN_ROOT/var/lock" fi info " ✓ Usrmerge: /bin /sbin /lib /lib64 /usr/sbin /usr/lib64 /var/run /var/lock → /usr" } # Wrapper `ninja` w $TOOLS_DIR/bin (pierwszy w PATH): dokłada -j/-l z $PAGAN_JOBS, # jeśli użytkownik nie podał -j. Ninja domyślnie bierze WSZYSTKIE rdzenie, co przy # Qt6/KF6 (ciężkie C++ + PCH) zamraża komputer. Współdzielone przez init_build # (pełny build) i fix-calamares.sh (naprawa pojedynczego pakietu). _install_ninja_wrapper() { mkdir -p "$TOOLS_DIR/bin" cat > "$TOOLS_DIR/bin/ninja" <<'NINJA_WRAPPER' #!/bin/sh # Pagan: ogranicz równoległość ninja do $PAGAN_JOBS, o ile użytkownik nie podał -j. real="" for c in /usr/bin/ninja /bin/ninja /usr/local/bin/ninja; do if [ -x "$c" ]; then real="$c"; break; fi done if [ -z "$real" ]; then echo "ninja(wrapper): brak prawdziwego ninja" >&2; exit 127; fi has_j=0; has_l=0 for a in "$@"; do case "$a" in -j|-j[0-9]*|--jobs|--jobs=*) has_j=1 ;; -l|-l[0-9]*|--load-average*) has_l=1 ;; esac done if [ "$has_j" = 0 ] && [ -n "${PAGAN_JOBS:-}" ]; then if [ "$has_l" = 0 ]; then exec "$real" -j "$PAGAN_JOBS" -l "$PAGAN_JOBS" "$@" fi exec "$real" -j "$PAGAN_JOBS" "$@" fi exec "$real" "$@" NINJA_WRAPPER chmod 755 "$TOOLS_DIR/bin/ninja" info " ✓ Wrapper ninja: limit zadań = ${PAGAN_JOBS:-auto} (rdzeni: $(nproc 2>/dev/null || echo '?'))" } init_build() { step "Inicjalizacja środowiska budowy" cd / mkdir -p "$PAGAN_ROOT" "$SRC_DIR" "$TOOLS_DIR" "$OUTPUT_DIR" # Katalogi systemowe for dir in usr bin sbin lib lib64 etc var boot home root \ opt tmp run mnt media srv; do mkdir -p "$PAGAN_ROOT/$dir" done # Złączone /usr – wymagane przez systemd; naprawia /bin/bash, /bin/sh, ldd _usrmerge_rootfs # Wrapper ninja: ogranicza równoległość (chroni przed zamrożeniem komputera). _install_ninja_wrapper # Katalogi PAG mkdir -p "$PAGAN_ROOT/var/lib/pag"{,/packages,/snapshots} mkdir -p "$PAGAN_ROOT/var/cache/pag" # Deploymenty mkdir -p "$PAGAN_ROOT/.deployments" ok "Środowisko zainicjowane w: $PAGAN_ROOT" } # ========================= POBIERANIE PAKIETÓW ========================= _spinner() { local pid=$1 delay=0.1 spinstr='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' text="${2:-}" local i=0 while kill -0 "$pid" 2>/dev/null; do i=$(( (i+1) % ${#spinstr} )) printf '\r %s %s' "${spinstr:$i:1}" "$text" sleep "$delay" done printf '\r ✓ %s\n' "$text" } _archive_is_valid() { local file="$1" [[ -s "$file" ]] || return 1 case "$file" in *.tar.xz) xz -t "$file" >/dev/null 2>&1 ;; *.tar.gz|*.tgz) gzip -t "$file" >/dev/null 2>&1 ;; *.tar.bz2|*.tbz2) bzip2 -t "$file" >/dev/null 2>&1 ;; *.tar.zst) if command -v zstd >/dev/null 2>&1; then zstd -t "$file" >/dev/null 2>&1 else unzstd -t "$file" >/dev/null 2>&1 fi ;; *.tar) tar -tf "$file" >/dev/null 2>&1 ;; *.zip) unzip -tqq "$file" >/dev/null 2>&1 ;; *) ! file "$file" 2>/dev/null | grep -qE "HTML document|XML document|ASCII text|empty" ;; esac } # Weryfikuje sumę SHA256 pliku. Zwraca 0 gdy plik pasuje do oczekiwanej sumy # (lub gdy suma jest "SKIP"/pusta). Zwraca 1 przy niezgodności. # # Uwaga: mechanizm weryfikacji jest gotowy – w recepturze wystarczy wpisać # realną sumę zamiast "SKIP" (4. pole: nazwa:wersja:url:SHA256:komendy). # Póki wpisy mają "SKIP", archiwa NIE są weryfikowane kryptograficznie (tylko # test poprawności archiwum). Aby wymusić podawanie sum (tryb CI/safety): # PAGAN_STRICT_SHA=1 → każdy pakiet bez sumy SHA256 kończy się błędem. _sha_ok() { local file="$1" expected="${2:-SKIP}" if [[ -z "$expected" || "$expected" == "SKIP" ]]; then if _is_true "${PAGAN_STRICT_SHA:-0}"; then warn " ✗ Brak sumy SHA256 (SKIP) dla $(basename "$file") – PAGAN_STRICT_SHA=1" return 1 fi return 0 fi command -v sha256sum >/dev/null 2>&1 || return 0 local got got=$(sha256sum "$file" 2>/dev/null | awk '{print $1}') if [[ "$got" == "$expected" ]]; then return 0 fi warn " ✗ SHA256 niezgodność dla $(basename "$file"):" warn " oczekiwano: $expected" warn " otrzymano: ${got:-brak}" return 1 } download_pkg() { local url="$1" dest="$2" sha="${3:-SKIP}" force="${4:-0}" mkdir -p "$(dirname "$dest")" if [[ "$force" != "1" && -f "$dest" ]]; then if _archive_is_valid "$dest" && _sha_ok "$dest" "$sha"; then info " ⏭ $(basename "$dest") – już pobrany (ok)" return 0 fi warn "Uszkodzone/niezgodne archiwum: $(basename "$dest") – pobieram ponownie" rm -f "$dest" fi # Lista adresów do próby (główny + mirror) local urls=("$url") case "$url" in *ftp.gnu.org/gnu/*) urls+=("https://mirror.kumi.systems/gnu/${url#https://ftp.gnu.org/gnu/}") urls+=("https://mirrors.ocf.berkeley.edu/gnu/${url#https://ftp.gnu.org/gnu/}") urls+=("https://ftp.nluug.nl/pub/gnu/${url#https://ftp.gnu.org/gnu/}") ;; *ftpmirror.gnu.org/*) # ftpmirror.gnu.org to usługa przekierowań (bywa 502) – zapas: wprost z ftp.gnu.org urls+=("https://ftp.gnu.org/gnu/${url#https://ftpmirror.gnu.org/}") ;; *cdn.kernel.org/*|*www.kernel.org/*) urls+=("https://mirrors.edge.kernel.org/${url#https://cdn.kernel.org/}") urls+=("https://mirrors.edge.kernel.org/${url#https://www.kernel.org/}") ;; *download.savannah.gnu.org*) # download.savannah.gnu.org bywa 502 (awarie usługi) – pełne mirror-y Savannah: urls+=("$(echo "$url" | sed 's|https://download.savannah.gnu.org|https://www.mirrorservice.org/sites/download.savannah.gnu.org|')") urls+=("$(echo "$url" | sed 's|https://download.savannah.gnu.org|https://mirror.rabisu.com/mirrors/savannah|')") ;; *libbsd.freedesktop.org*) # libbsd.freedesktop.org bywa martwy/wisnący (timeout bez odpowiedzi). # Debian mirror trzyma ten sam upstream orig tarball (libbsd_.orig.tar.xz, # ten sam katalog libbsd-/ w środku). urls+=("$(echo "$url" | sed 's|https://libbsd.freedesktop.org/releases/libbsd-\(.*\)\.tar\.xz|https://deb.debian.org/debian/pool/main/libb/libbsd/libbsd_\1.orig.tar.xz|')") ;; *dbus.freedesktop.org*) # dbus.freedesktop.org bywa martwy (jak libbsd) – Debian mirror: dbus_.orig.tar.xz urls+=("$(echo "$url" | sed 's|https://dbus.freedesktop.org/releases/dbus/dbus-\(.*\)\.tar\.xz|https://deb.debian.org/debian/pool/main/d/dbus/dbus_\1.orig.tar.xz|')") ;; *github.com*releases*download*.tar.xz) # GitHub: próbuj .tar.gz zamiast .tar.xz (nie zawsze jest .tar.xz) urls+=("${url%.tar.xz}.tar.gz") ;; *github.com*releases*download*.tar.bz2) urls+=("${url%.tar.bz2}.tar.gz") ;; *prdownloads.sourceforge.net*|*downloads.sourceforge.net*|*download.sourceforge.net*) urls+=("${url}?download") ;; *sourceforge.net/projects/*) urls+=("$(echo "$url" | sed 's|https://sourceforge.net/projects/|https://downloads.sourceforge.net/|')?download") ;; *samba.org/ftp/rsync/src/*|*samba.org/pub/rsync/src/*|*rsync.samba.org/*) # rsync.samba.org bywa wolny / niedostępny (awarie, geoblokady). Debian # mirror trzyma ten sam upstream tarball: rsync_.orig.tar.gz # (w środku ten sam katalog rsync-/). Wzorzec jak libbsd/dbus wyżej. # Kolejność: alternatywny host samba → CDN deb.debian.org → mirror PL. urls+=("$(echo "$url" | sed 's|https://www.samba.org/|https://download.samba.org/|')") urls+=("$(echo "$url" | sed 's|.*/rsync-\([0-9.]*\)\.tar\.gz|https://deb.debian.org/debian/pool/main/r/rsync/rsync_\1.orig.tar.gz|')") urls+=("$(echo "$url" | sed 's|.*/rsync-\([0-9.]*\)\.tar\.gz|https://ftp.pl.debian.org/debian/pool/main/r/rsync/rsync_\1.orig.tar.gz|')") ;; *download.kde.org/stable/plasma/*polkit-kde-agent-*) # Repo invent nazywa się polkit-kde-agent-1 (z -1); bez sufiksu → ślepy # adres (GitLab redirect na logowanie). Specjalny mapping do v. urls+=("$(echo "$url" | sed -E 's@https://download\.kde\.org/stable/plasma/([0-9.]+)/polkit-kde-agent-([0-9.]+)\.tar\.xz@https://invent.kde.org/plasma/polkit-kde-agent-1/-/archive/v\2/polkit-kde-agent-1-v\2.tar.bz2@')") ;; *download.kde.org/stable/frameworks/*|*download.kde.org/stable/plasma/*|*download.kde.org/stable/phonon/*) # download.kde.org bywa w awarii (całe stable/ potrafi zwracać 404 – np. # 2026-08-24 frameworks/6.28.0 mimo że pliki wcześniej się pobierały). # Zapas: invent.kde.org (GitLab) archiwa .tar.bz2. Wzorzec: # .../stable///-.tar.xz # -> invent.kde.org///-/archive/v/-v.tar.bz2 urls+=("$(echo "$url" | sed -E 's@https://download\.kde\.org/stable/(frameworks|plasma|phonon)/([0-9.]+)/([^/]+)-([0-9.]+)\.tar\.xz@https://invent.kde.org/\1/\3/-/archive/v\4/\3-v\4.tar.bz2@')") ;; *download.kde.org/stable/release-service/*) # Gear (release-service): grupa w invent zależy od pakietu local _kg=utilities case "$url" in *kpmcore-*) _kg=partitionmanager ;; esac local _kp _kpn _kver _kp=$(basename "$url" .tar.xz); _kpn="${_kp%-*}"; _kver="${_kp##*-}" urls+=("https://invent.kde.org/$_kg/$_kpn/-/archive/v$_kver/$_kpn-v$_kver.tar.bz2") ;; esac local ok=0 for u in "${urls[@]}"; do if [[ "$u" != "$url" ]]; then info " ↻ Próbuję mirror: $u" fi rm -f "$dest" # Mirror o innym rozszerzeniu archiwum (np. .tar.bz2 z invent.kde.org zamiast # .tar.xz): pobieramy do tymczasowej nazwy z rozszerzeniem mirrora, walidujemy # wg niego, potem mv do $dest (tar -xf auto-detektuje kompresję po bajtach). local _u_ext="${u##*.}"; _u_ext="${_u_ext%%\?*}" local _orig_ext="${url##*.}"; _orig_ext="${_orig_ext%%\?*}" local _dl="$dest" [[ "$u" != "$url" && "$_u_ext" != "$_orig_ext" ]] && _dl="${dest%.*}.${_u_ext}" rm -f "$_dl" # Timeoity: martwy serwer (np. libbsd/dbus.freedesktop.org) nie może zawiesić builda. # Retry-all-errors: xorg.freedesktop.org bywa flaky (przerywane połączenia) – retry to pokonuje. if wget -q --timeout=30 --tries=3 --show-progress -O "$_dl" "$u" 2>/dev/null || \ curl -f -# --connect-timeout 20 --max-time 600 --retry 8 --retry-all-errors --retry-delay 3 -L -o "$_dl" "$u" 2>/dev/null; then # Sprawdź czy archiwum jest poprawne (nie ucięte/nie HTML) i zgadza # się suma SHA256 (jeśli podana). if _archive_is_valid "$_dl" && _sha_ok "$_dl" "$sha"; then if [[ "$_dl" != "$dest" ]]; then mv -f "$_dl" "$dest" fi ok=1 break fi warn "Pobrano uszkodzony/błędny plik z $u – próbuję kolejne źródło" rm -f "$_dl" continue # wyraźnie przechodzimy do kolejnego mirrora (u) fi done if [[ $ok -eq 0 ]]; then rm -f "$dest" error "Nie udało się pobrać: $url" fi info " ✓ $(basename "$dest")" } # ========================= BUDOWA PAKIETU LFS ========================= # Hostowe buildy autotools/meson wołają z PATH skryptowe narzędzia glib # (np. glib-mkenums w libxklavier). Host bywa bez nich (zależy od dystrybucji). # Wystawiamy je w $TOOLS_DIR/bin (pierwszy w PATH), ale TYLKO skrypty z shebangiem # – binarki targetu linkują libglib i nie uruchomią się na hoście. _provide_glib_build_tools() { local t _src mkdir -p "$TOOLS_DIR/bin" for t in glib-mkenums glib-genmarshal; do _src="$PAGAN_ROOT/usr/bin/$t" [[ -x "$_src" ]] || continue if command -v "$t" >/dev/null 2>&1; then continue; fi [[ "$(head -c2 "$_src" 2>/dev/null)" == "#!" ]] || continue printf '#!/bin/sh\nexec "%s" "$@"\n' "$_src" > "$TOOLS_DIR/bin/$t" chmod +x "$TOOLS_DIR/bin/$t" done } # ========================= MANIFEST BAZY Z OBRAZU (ROLLING/ADOPT) ========================= # Buildery zapisują tu KAŻDY pakiet wbudowany ze źródeł (nazwa + wersja), a na # końcu builda generate_base_manifest() zamienia to na /usr/share/pag/base.json. # # Po co: pliki zbudowane w ISO nie są znane pagowi (nie ma ich w installed.json), # więc `pag upgrade` ich nie widzi. Manifest mówi pagowi, CO stanowi bazę obrazu # i w jakiej wersji, dzięki czemu `pag base adopt` może dograć je z repo (albo # przynajmniej powiedzieć, które zostają w wersji z obrazu) – patrz komendy # `pag base` w projekcie pag. _base_manifest_file() { printf '%s\n' "$PAGAN_ROOT/usr/share/pag/base-packages.txt" } _base_manifest_add() { local _n="$1" _v="$2" [[ -n "$_n" ]] || return 0 # Pakiety wyłącznie budowlane (toolchain/Ch.5) – nie istnieją w systemie # docelowym jako pakiety, więc nie trafiają do manifestu bazy. case "$_n" in *-pass1|*-pass2|*-tc) return 0 ;; esac [[ -d "$PAGAN_ROOT/usr" ]] || return 0 local _f _f=$(_base_manifest_file) mkdir -p "$(dirname "$_f")" 2>/dev/null || return 0 printf '%s %s\n' "$_n" "${_v:-?}" >> "$_f" 2>/dev/null || true } # Zamienia base-packages.txt na base.json (czytany przez `pag base` w systemie). # Deduplikacja po nazwie – wygrywa OSTATNI wpis (np. przebudowa gtk w install_xfce # nadpisuje wersję z build_blfs). generate_base_manifest() { local _txt _json _txt=$(_base_manifest_file) _json="$PAGAN_ROOT/usr/share/pag/base.json" if [[ ! -s "$_txt" ]]; then warn "Manifest bazy: brak $_txt – 'pag base adopt' nie będzie miał czego zaadoptować" return 0 fi mkdir -p "$(dirname "$_json")" python3 - "$_txt" "$_json" "$PAGAN_VERSION" "$PAGAN_DE" "$KERNEL_VERSION" <<'PYEOF' || warn "Manifest bazy: generowanie base.json nie powiodło się" import json, sys src, dst, iso_version, de, kernel = sys.argv[1:6] pairs = {} with open(src, encoding="utf-8", errors="replace") as fh: for line in fh: line = line.strip() if not line: continue name, _, ver = line.partition(" ") if name: pairs[name] = ver or "?" with open(dst, "w", encoding="utf-8") as fh: json.dump({ "iso_version": iso_version, "de": de, "kernel": kernel, "packages": [{"name": n, "version": v} for n, v in sorted(pairs.items())], }, fh, indent=2, ensure_ascii=False) print(f" manifest bazy: {len(pairs)} pakietow => {dst}") PYEOF } # Scala „zagnieżdżony rootfs” ($PAGAN_ROOT/mnt/pagan) z powrotem do rootfsu. # # SKĄD SIĘ BIERZE: budujemy na hoście z PKG_CONFIG_SYSROOT_DIR="$PAGAN_ROOT", więc # pkg-config zwraca ścieżki instalacyjne JUŻ z sysrootem — np. NetworkManager # dostaje `systemdsystemunitdir=/mnt/pagan/usr/lib/systemd/system` # i `dbus_conf_dir=/mnt/pagan/usr/share/dbus-1/system.d`, a pipewire # `systemd_user_unit_dir=/mnt/pagan/usr/lib/systemd/user`. Meson dokłada do takiej # ścieżki jeszcze DESTDIR=$PAGAN_ROOT → plik ląduje w /mnt/pagan/mnt/pagan/usr/... # (podwójny prefiks), czyli POZA systemem. Meson nie odcina sysrootu, bo nie jest # mu znany (nie używamy cross-file z [properties] sys_root). # # Realne skutki uboczne (zaobserwowane): # * NetworkManager: brak NetworkManager.service → w LIVE nikt nie podnosi # interfejsu (brak DHCP — w tym w QEMU) i brak polityki # org.freedesktop.NetworkManager.conf → NM nie zajmie nazwy na D-Bus, # * bluez: brak bluetooth.service, # * pipewire/wireplumber: brak jednostek user (brak dźwięku w sesji), # * systemd, librsvg: analogicznie (część ratują funkcje _ensure_*). # # DLACZEGO SCALAMY, A NIE KASUJEMY: kasowanie $PAGAN_ROOT/mnt (tak było wcześniej # przy librsvg) gubiło legalne pliki zainstalowane wcześniej przez INNE pakiety. # Scalanie jest idempotentne: pliki z zagnieżdżonego rootfsu pochodzą z właśnie # zakończonej instalacji, więc słusznie nadpisują ewentualne starsze kopie. _merge_nested_rootfs() { local _root="${PAGAN_ROOT%/}" [[ -n "$_root" && "$_root" != "/" ]] || return 0 local _nested="$_root/${_root#/}" [[ -d "$_nested" ]] || return 0 local _n _n=$(find "$_nested" 2>/dev/null | wc -l) cp -a "$_nested/." "$_root/" 2>/dev/null || true rm -rf "$_nested" # Katalog pośredni ("$PAGAN_ROOT" obcięty z podwójnego prefiksu) ma zostać # pusty – rmdir nie ruszy go, gdyby było tam coś jeszcze. rmdir "$(dirname "$_nested")" 2>/dev/null || true warn " Scalono zagnieżdżony rootfs: $_nested → $_root (${_n} wpisów, błąd DESTDIR+sysroot)" } build_lfs_pkg() { local pkg_name="$1" local pkg_version="$2" local pkg_url="$3" local pkg_sha="$4" local pkg_config="$5" # Manifest bazy: rejestrujemy też pakiety POMINIĘTE przez resume (wersja # i nazwa są znane z receptury), bo w rootfsie i tak są – a pag musi # wiedzieć, że pochodzą z obrazu, nie z repo. _base_manifest_add "$pkg_name" "$pkg_version" # Prawdziwa nazwa pliku z URL – niezależna od pkg_name local archive archive=$(basename "$pkg_url" | sed 's/?.*//') local archive_path="$SRC_DIR/$archive" local pkg_dir="$SRC_DIR/build-${pkg_name}" ((UI_PKG_N++)) || true # Resume – pomiń jeśli pakiet już zbudowany if _is_built "${pkg_name}-${pkg_version}"; then ui_skip "${pkg_name}-${pkg_version}" ((PKG_SKIPPED++)) || true return 0 fi info " Budowanie: ${pkg_name}-${pkg_version}" download_pkg "$pkg_url" "$archive_path" "$pkg_sha" rm -rf "$pkg_dir" mkdir -p "$pkg_dir" # Sprawdź czy archiwum ma katalog nadrzędny (np. gcc-14.2.0/) czy jest płaskie (tzdata) local -a _strip=() local _tar_first _tar_second tar tf "$archive_path" > /tmp/_tar_first.tmp 2>/dev/null || true _tar_first=$(sed -n '1p' /tmp/_tar_first.tmp 2>/dev/null) _tar_second=$(sed -n '2p' /tmp/_tar_first.tmp 2>/dev/null) rm -f /tmp/_tar_first.tmp # Niektóre tarballe (np. openssh) listują katalog nadrzędny BEZ końcowego '/', # więc jeśli 1. wpis nie ma '/', ale 2. już ma → to katalog nadrzędny. if [[ "$_tar_first" == *"/"* ]]; then _strip=(--strip-components=1) elif [[ -n "$_tar_second" && "$_tar_second" == *"/"* ]]; then _strip=(--strip-components=1) fi case "$archive" in *.tar.xz) tar -xf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.tar.gz) tar -xf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.tar.zst) unzstd -d "$archive_path" -o "$SRC_DIR/${archive%.zst}" && \ tar -xf "$SRC_DIR/${archive%.zst}" -C "$pkg_dir" "${_strip[@]}" ;; *.tar.bz2|*.tbz2) tar -xjf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.tgz) tar -xzf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.tar) tar -xf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.zip) unzip -q "$archive_path" -d "$pkg_dir" ;; # Pliki bez archiwum (np. .pem, .crt) – kopiuj bezpośrednio *) cp "$archive_path" "$pkg_dir/$(basename "$archive")" ;; esac || { warn "Rozpakowanie $pkg_name nieudane – wymuszam ponowne pobranie i retry" rm -f "$archive_path" "$SRC_DIR/${archive%.zst}" download_pkg "$pkg_url" "$archive_path" "$pkg_sha" 1 case "$archive" in *.tar.xz) tar -xf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.tar.gz) tar -xf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.tar.zst) unzstd -d "$archive_path" -o "$SRC_DIR/${archive%.zst}" && tar -xf "$SRC_DIR/${archive%.zst}" -C "$pkg_dir" "${_strip[@]}" ;; *.tar.bz2|*.tbz2) tar -xjf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.tgz) tar -xzf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.tar) tar -xf "$archive_path" -C "$pkg_dir" "${_strip[@]}" ;; *.zip) unzip -q "$archive_path" -d "$pkg_dir" ;; *) cp "$archive_path" "$pkg_dir/$(basename "$archive")" ;; esac || error "Rozpakowanie $pkg_name nieudane" } pushd "$pkg_dir" > /dev/null local pkg_log="$UI_LOG_DIR/${pkg_name}-${pkg_version}.log" # Udostępnij hostowym buildom skryptowe narzędzia glib z targetu # (np. glib-mkenums dla libxklavier) – zanim ruszy configure/make. _provide_glib_build_tools if [[ -n "$pkg_config" && "$pkg_config" != "SKIP" ]]; then ui_build_eval "${pkg_name}-${pkg_version}" "$pkg_log" "$pkg_config" \ || error "Configure failed for $pkg_name (log: $pkg_log)" fi # Pomijaj auto make/install jeśli pkg_config już robi wszystko sam local _skip_auto_make=0 case "$pkg_config" in *"cp -"*) _skip_auto_make=1 ;; *"cp "*) _skip_auto_make=1 ;; *"make install"*) _skip_auto_make=1 ;; *"DESTDIR="*) _skip_auto_make=1 ;; *"ninja"*) _skip_auto_make=1 ;; *"pip3"*) _skip_auto_make=1 ;; *"pip install"*) _skip_auto_make=1 ;; *"cmake --install"*) _skip_auto_make=1 ;; *"./bootstrap"*) _skip_auto_make=1 ;; *"install -m"*) _skip_auto_make=1 ;; *"osinfo-db-import"*) _skip_auto_make=1 ;; *"PREFIX="*) _skip_auto_make=1 ;; *"ZONEINFO="*) _skip_auto_make=1 ;; make*) _skip_auto_make=1 ;; esac if [[ $_skip_auto_make -eq 0 ]]; then info " Make: $pkg_name" ui_build_eval "${pkg_name}-${pkg_version} (make)" "$pkg_log" "make $MAKEFLAGS && make install" \ || error "Make/Install failed for $pkg_name (log: $pkg_log)" fi popd > /dev/null rm -rf "$pkg_dir" # Zagnieżdżony rootfs (błąd DESTDIR+sysroot, patrz _merge_nested_rootfs) – # scalaj NATYCHMIAST po instalacji pakietu, a nie dopiero w finalize_build. # Wcześniej robił to tylko finalize, więc każdy `rm -rf "$PAGAN_ROOT/mnt"` # w środku builda (np. po librsvg) kasował pliki już zainstalowanych pakietów: # tak zginęły NetworkManager.service, bluetooth.service i jednostki user # pipewire/wireplumber. Scalanie tutaj sprawia też, że poprawki .la/.pc # poniżej obejmują odzyskane pliki. _merge_nested_rootfs # Remove .la files (libtool archives) – they have hardcoded paths, break cross-building find "$PAGAN_ROOT/usr/lib" -name "*.la" -delete 2>/dev/null || true # Fix .pc files: rewrite prefix paths so pkg-config finds target without SYSROOT. # UWAGA: omijamy SYMLINKI (foo.pc → foo-.pc). [[ -f ]] łapie też symlink, # a sed -i na symlinku ZASTĘPUJE go zwykłym plikiem → przy reinstalacji meson # pada: „Destination '.../foo.pc' already exists and is not a symlink”. for pc in "$PAGAN_ROOT/usr/lib/pkgconfig/"*.pc "$PAGAN_ROOT/usr/share/pkgconfig/"*.pc; do if [[ -f "$pc" && ! -L "$pc" ]]; then sed -i 's|^prefix=/usr$|prefix='"$PAGAN_ROOT"'/usr|' "$pc" # Remove bare -I${includedir} (without subdir) to avoid exposing target libc headers sed -i '/^Cflags:/ { s/ -I\${includedir} / /g; s/^-I\${includedir} //; s/ -I\${includedir}$// }' "$pc" fi done 2>/dev/null || true _mark_built "${pkg_name}-${pkg_version}" ok " ✅ Zbudowano: $pkg_name-$pkg_version" # xkeyboard-config: dane lądują w /usr/share/xkeyboard-config-2/, a Xorg czyta # je z /usr/share/X11/xkb (symlink). Meson potrafi nie odtworzyć symlinka po # przerwanej instalacji – domykamy go tutaj, żeby sanity/live nie zgłaszał # „Brak danych xkeyboard-config (/usr/share/X11/xkb)”. if [[ "$pkg_name" == "xkeyboard-config" && -d "$PAGAN_ROOT/usr/share/xkeyboard-config-2" ]]; then if [[ ! -L "$PAGAN_ROOT/usr/share/X11/xkb" ]]; then rm -rf "$PAGAN_ROOT/usr/share/X11/xkb" mkdir -p "$PAGAN_ROOT/usr/share/X11" ln -s ../xkeyboard-config-2 "$PAGAN_ROOT/usr/share/X11/xkb" info " ✓ xkeyboard-config: /usr/share/X11/xkb → xkeyboard-config-2" fi fi } # ========================= ABI SANITY (TARGET ONLY) ========================= _max_ver_from_strings() { local file="$1" prefix="$2" strings -a "$file" 2>/dev/null \ | rg -o "${prefix}_[0-9]+\.[0-9]+" 2>/dev/null \ | sed "s/^${prefix}_//" \ | sort -V \ | tail -n 1 || true } _ver_gt() { # true jeśli $1 > $2 [[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n 1)" == "$1" && "$1" != "$2" ]] } check_target_abi() { step "ABI sanity check (target LFS libs)" local target_libc="$PAGAN_ROOT/usr/lib/libc.so.6" if [[ ! -f "$target_libc" ]]; then warn "Brak $target_libc - pomijam ABI check" return 0 fi local target_glibc target_glibc=$(_max_ver_from_strings "$target_libc" "GLIBC") if [[ -z "$target_glibc" ]]; then warn "Nie udało się odczytać wersji GLIBC z targetu - pomijam ABI check" return 0 fi info "Target GLIBC (z $target_libc): $target_glibc" local bad=0 while IFS= read -r so; do local req req=$(_max_ver_from_strings "$so" "GLIBC") [[ -z "$req" ]] && continue if _ver_gt "$req" "$target_glibc"; then warn "ABI mismatch (target): $(basename "$so") wymaga GLIBC_$req > GLIBC_$target_glibc" ((bad++)) || true fi done < <(find "$PAGAN_ROOT/usr/lib" -maxdepth 1 -type f -name '*.so*' 2>/dev/null) if [[ $bad -gt 0 ]]; then error "Wykryto $bad bibliotek(i) w targetowym $PAGAN_ROOT/usr/lib wymagających nowszego GLIBC niż LFS target. To nie jest host-check, tylko check targetu. Wyczyść $PAGAN_ROOT i zbuduj od nowa." fi ok "ABI sanity check targetu: OK" } # ========================= LFS PATCH CHECK (BEST-EFFORT) ========================= check_lfs_patches() { step "Sprawdzanie dostępnych patchy LFS (best-effort)" local patch_index_url="https://www.linuxfromscratch.org/patches/lfs/${LFS_PATCH_DIR:-13.1-rc2}/" local html if ! html=$(curl -fsSL --max-time 20 "$patch_index_url" 2>/dev/null); then warn "Nie udało się pobrać indeksu patchy LFS: $patch_index_url" warn "Pomijam check patchy (build będzie kontynuowany)." return 0 fi local tracked=( "bzip2-1.0.8" "coreutils-9.11" "expect-5.45.4" "glibc-2.44" "glibc-fhs" "kbd-2.10.0" "Python-3.14.7" "sysvinit-3.14" "tar-1.35" ) local found=0 for pkg in "${tracked[@]}"; do if echo "$html" | rg -qi "${pkg}.*\.patch"; then warn "LFS patch dostępny dla: $pkg" ((found++)) || true fi done if [[ $found -eq 0 ]]; then ok "Nie wykryto oczywistych patchy dla śledzonych pakietów LFS ${LFS_VERSION}" else warn "Wykryto $found potencjalnych patchy LFS. Rozważ integrację przed release build." fi } report_artifacts() { step "LOKALIZACJE ARTEFAKTÓW" local img_file="$OUTPUT_DIR/Pagan-Linux-${PAGAN_VERSION}-${PAGAN_DE}.img" local iso_file="$OUTPUT_DIR/Pagan-Linux-${PAGAN_VERSION}-${PAGAN_DE}.iso" local printed=0 if [[ -f "$img_file" ]]; then info "IMG: $img_file ($(du -h "$img_file" 2>/dev/null | cut -f1))" echo "IMG: $img_file" >> "$SUMMARY_FILE" printed=1 fi if [[ -f "$iso_file" ]]; then info "ISO: $iso_file ($(du -h "$iso_file" 2>/dev/null | cut -f1))" echo "ISO: $iso_file" >> "$SUMMARY_FILE" printed=1 fi if [[ $printed -eq 0 ]]; then warn "Nie znaleziono artefaktów IMG/ISO w $OUTPUT_DIR" fi } # ========================= BUDOWA TOOLCHAIN (Ch. 5) ========================= build_toolchain() { step "ROZDZIAŁ 5: BUDOWA TOOLCHAIN" ui_set_total 7 export LC_ALL=POSIX export LFS="$PAGAN_ROOT" export LFS_TGT="$LFS_TGT" # 5.2 - Linux headers (host make) build_lfs_pkg "linux-headers-tc" "$KERNEL_VERSION" \ "$KERNEL_URL" \ "SKIP" \ "make mrproper && make headers && find usr/include -type f ! -name '*.h' -delete && cp -rv usr/include $PAGAN_ROOT/usr" # 5.3 - GMP (statycznie – PRZED binutils, czysty host PATH!) # GCC 16 (C23) zabija stare testy GMP. -std=gnu89 dla configure (testy), # ale build potrzebuje C99 → make CFLAGS='-O2' (nadpisuje Makefile). build_lfs_pkg "gmp-tc" "6.3.0" \ "https://ftp.gnu.org/gnu/gmp/gmp-6.3.0.tar.xz" \ "SKIP" \ "CFLAGS='-std=gnu89 -O2' ./configure --prefix=$TOOLS_DIR --enable-cxx --disable-shared --enable-static && make CFLAGS='-O2' $MAKEFLAGS && make install" # 5.4 - MPFR (host PATH) build_lfs_pkg "mpfr-tc" "4.2.2" \ "https://ftp.gnu.org/gnu/mpfr/mpfr-4.2.2.tar.xz" \ "SKIP" \ "CFLAGS='-std=gnu89 -O2' ./configure --prefix=$TOOLS_DIR --disable-shared --enable-static --with-gmp=$TOOLS_DIR && make CFLAGS='-O2' $MAKEFLAGS && make install" # 5.5 - MPC (host PATH) build_lfs_pkg "mpc-tc" "1.4.1" \ "https://ftp.gnu.org/gnu/mpc/mpc-1.4.1.tar.xz" \ "SKIP" \ "CFLAGS='-std=gnu89 -O2' ./configure --prefix=$TOOLS_DIR --disable-shared --enable-static --with-gmp=$TOOLS_DIR --with-mpfr=$TOOLS_DIR && make CFLAGS='-O2' $MAKEFLAGS && make install" # 5.6 - Binutils pass 1 (cross-tools – od teraz PATH=$TOOLS_DIR/bin:$PATH) export PATH="$TOOLS_DIR/bin:$PATH" build_lfs_pkg "binutils-pass1" "2.47" \ "https://ftp.gnu.org/gnu/binutils/binutils-2.47.tar.xz" \ "SKIP" \ "mkdir -v build && cd build && ../configure --prefix=$TOOLS_DIR --with-sysroot=$PAGAN_ROOT --target=$LFS_TGT --disable-nls --enable-gprofng=no --disable-werror --disable-dependency-tracking && make $MAKEFLAGS && make install" # 5.7 - GCC pass 1 (cross-compiler) # GCC 16.1.1 host kompiluje GCC 16.2.0 źródła jako C++. safe-ctype.h # definiuje #define isgraph(c) do_not_use_isgraph..., a GCC16 # robi using ::isgraph; → makro się rozwija → łamie parser locale_facets.h. # Fix: w system.h dodajemy #include przed safe-ctype.h i # #undef makr po nim. build_lfs_pkg "gcc-pass1" "16.2.0" \ "https://ftp.gnu.org/gnu/gcc/gcc-16.2.0/gcc-16.2.0.tar.xz" \ "SKIP" \ 'case $(uname -m) in x86_64) sed -e "/m64=/s/lib64/lib/" -i.orig gcc/config/i386/t-linux64 ;; esac; sed -i "s/__cplusplus != 201103/__cplusplus < 201103/g; s/__cplusplus > 201103/__cplusplus < 201103/g" libcody/configure 2>/dev/null || true; sed -i "/^#include \"safe-ctype.h\"/i #ifdef __cplusplus\n#include \n#endif" gcc/system.h 2>/dev/null || true; sed -i "/^#include \"safe-ctype.h\"/a #ifdef __cplusplus\n#undef isalpha\n#undef isalnum\n#undef iscntrl\n#undef isdigit\n#undef isgraph\n#undef islower\n#undef isprint\n#undef ispunct\n#undef isspace\n#undef isupper\n#undef isxdigit\n#undef toupper\n#undef tolower\n#endif" gcc/system.h 2>/dev/null || true; sed -i "1i #include " gcc/config/i386/i386.h 2>/dev/null || true; mkdir -v build && cd build && ../configure --target='"$LFS_TGT"' --prefix='"$TOOLS_DIR"' --with-gmp='"$TOOLS_DIR"' --with-mpfr='"$TOOLS_DIR"' --with-mpc='"$TOOLS_DIR"' --with-glibc-version=2.44 --with-sysroot='"$PAGAN_ROOT"' --with-newlib --without-headers --enable-default-pie --enable-default-ssp --disable-nls --disable-shared --disable-multilib --disable-threads --disable-libatomic --disable-libgomp --disable-libquadmath --disable-libssp --disable-libvtv --disable-libstdcxx --disable-split-stack --enable-languages=c,c++ --enable-werror=no CXXFLAGS="-std=gnu++20 -O2 -Wno-error=return-type -fno-char8_t -Wno-deprecated-enum-enum-conversion" && make '"$MAKEFLAGS"' && make install && cd .. && ln -sf gcc '"$CROSS_BIN"'/cc' # 5.8 - Glibc (cross-kompilowana) build_lfs_pkg "glibc-tc" "2.44" \ "https://ftp.gnu.org/gnu/glibc/glibc-2.44.tar.xz" \ "SKIP" \ "rm -rf $PAGAN_ROOT/usr/include/gnu $PAGAN_ROOT/usr/include/features.h $PAGAN_ROOT/usr/include/stdc-predef.h 2>/dev/null; mkdir -v build && cd build && ../configure --prefix=/usr --host=$LFS_TGT --build=\$(../scripts/config.guess) --enable-kernel=4.14 --with-headers=$PAGAN_ROOT/usr/include libc_cv_slibdir=/usr/lib --disable-werror CFLAGS=\"-O2 -Wno-error=maybe-uninitialized\" && make $MAKEFLAGS && make DESTDIR=$PAGAN_ROOT install" ok "Toolchain zbudowany!" } # ========================= BUDOWA SYSTEMU KOŃCOWEGO (Ch. 6) ========================= build_final_system() { step "ROZDZIAŁ 6-8: PEŁNY SYSTEM KOŃCOWY LFS 13.1" # Używamy NARZĘDZI HOSTA podczas budowy (nie chroot). $PAGAN_ROOT/usr/bin # celowo pomijamy: targetowe binarki (groff 1.23.0, msgfmt) nie działają na # hoście (szukają fontów/bibliotek w /usr lub nie mają LD_LIBRARY_PATH). # /usr/bin/core_perl+/vendor_perl: sudo resetuje PATH i gubi je (pod2man!). export PATH="$TOOLS_DIR/bin:/usr/bin/core_perl:/usr/bin/vendor_perl:$PATH" export LC_ALL=POSIX # Globalna kompatybilność z GCC 16 (Arch) – wycisza nowe warningi, # które stają się błędami przez -Werror w wielu projektach. # -std=gnu17: stare pakiety (gnulib: m4/coreutils/grep) nie rozumieją C23 ([[nodiscard]]) export CFLAGS="-std=gnu17 -O2 -Wno-error=implicit-function-declaration -Wno-error=int-conversion -Wno-error=return-type -Wno-error=maybe-uninitialized -Wno-error=discarded-qualifiers -Wno-error=unterminated-string-initialization -Wno-error=incompatible-pointer-types -Wno-error=null-dereference -Wno-error=override-init" export CXXFLAGS="-O2 -Wno-error=return-type -Wno-error=maybe-uninitialized" # Coreutils (i inne GNU) odmawiają configure jako root bez tej zmiennej export FORCE_UNSAFE_CONFIGURE=1 # Targetowe metadane mają pierwszeństwo podczas budowy systemu PaganOS. export PKG_CONFIG_DIR="" export PKG_CONFIG_PATH="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/lib/x86_64-linux-gnu/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig" export PKG_CONFIG_LIBDIR="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/lib/x86_64-linux-gnu/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig" export PKG_CONFIG_SYSROOT_DIR="$PAGAN_ROOT" # Linkuj z targetowymi libami (glibc 2.44, zgodne z hostem) # UWAGA: brak globalnego CPPFLAGS=-I$PAGAN_ROOT/usr/include — glibc budując sam siebie # nie może mieć -I na zainstalowane headery glibc (guard _LIBC w gnu/stubs-64.h) export LDFLAGS="-L$PAGAN_ROOT/usr/lib -L$PAGAN_ROOT/lib64 -Wl,-rpath-link,$PAGAN_ROOT/usr/lib -Wl,-rpath-link,$PAGAN_ROOT/lib64" # fish 4.x – domyślna powłoka użytkowników (zamiast bash). To przepisanie na # Rusta, więc build ze źródeł wymagałby Rust >= 1.85 + cargo + sieci do # crates.io. Zamiast tego bierzemy OFICJALNĄ, statyczną (musl) jednoplikową # binarkę z wydania fish – jest samodzielna (funkcje/completions wbudowane # w binarkę), bez zależności i bez toolchainu Rusta. Analogicznie do # prebuilt Firefox/NVIDIA już używanych w tym buildzie. local _fish_url _fish_sha case "$(uname -m)" in aarch64|arm64) _fish_url="https://github.com/fish-shell/fish-shell/releases/download/4.9.3/fish-4.9.3-linux-aarch64.tar.xz" _fish_sha="57349f0111728c64b703a84e611aa97ff4d1fa8466385d64e67bd5bc3ffa49fc" ;; *) _fish_url="https://github.com/fish-shell/fish-shell/releases/download/4.9.3/fish-4.9.3-linux-x86_64.tar.xz" _fish_sha="8f643d10ad1abb0ef7072764c01ceb5cf61b2d373fda400cf60f0bad69b7e095" ;; esac local packages=( # ======== ADMINISTRACJA I KONFIGURACJA ======== "man-pages:6.18:https://www.kernel.org/pub/linux/docs/man-pages/man-pages-6.18.tar.xz:SKIP:make -R prefix=/usr install" "iana-etc:20260805:https://github.com/Mic92/iana-etc/releases/download/20260805/iana-etc-20260805.tar.gz:SKIP:cp services protocols \$PAGAN_ROOT/etc" "tzdata:2026c:https://www.iana.org/time-zones/repository/releases/tzdata2026c.tar.gz:SKIP:mkdir -p \$PAGAN_ROOT/usr/share/zoneinfo/posix \$PAGAN_ROOT/usr/share/zoneinfo/right && for tz in etcetera southamerica northamerica europe africa antarctica asia australasia backward; do zic -L /dev/null -d \$PAGAN_ROOT/usr/share/zoneinfo \$tz && zic -L /dev/null -d \$PAGAN_ROOT/usr/share/zoneinfo/posix \$tz && zic -L leapseconds -d \$PAGAN_ROOT/usr/share/zoneinfo/right \$tz; done && cp -f zone.tab zone1970.tab iso3166.tab leapseconds leap-seconds.list \$PAGAN_ROOT/usr/share/zoneinfo/ && zic -d \$PAGAN_ROOT/usr/share/zoneinfo -p Europe/Warsaw" # ======== BIBLIOTEKI BAZOWE ======== "glibc:2.44:https://ftp.gnu.org/gnu/glibc/glibc-2.44.tar.xz:SKIP:mkdir bld && cd bld && ../configure --prefix=/usr --enable-kernel=4.14 --enable-stack-protector=strong libc_cv_slibdir=/usr/lib --disable-werror CFLAGS=\"-O2 -Wno-error=maybe-uninitialized\" && make && make install DESTDIR=\$PAGAN_ROOT" "zlib:1.3.2:https://zlib.net/fossils/zlib-1.3.2.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "bzip2:1.0.8:https://www.sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz:SKIP:make -f Makefile-libbz2_so && make install PREFIX=\$PAGAN_ROOT/usr" "xz:5.8.3:https://github.com/tukaani-project/xz/releases/download/v5.8.3/xz-5.8.3.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "zstd:1.5.7:https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz:SKIP:make prefix=/usr && make prefix=/usr DESTDIR=\$PAGAN_ROOT install" "lz4:1.10.0:https://github.com/lz4/lz4/releases/download/v1.10.0/lz4-1.10.0.tar.gz:SKIP:make && make PREFIX=/usr DESTDIR=\$PAGAN_ROOT install" "readline:8.3:https://ftp.gnu.org/gnu/readline/readline-8.3.tar.gz:SKIP:./configure --prefix=/usr --disable-static --with-curses && make SHLIB_LIBS=-lncurses && make install DESTDIR=\$PAGAN_ROOT" "gmp:6.3.0:https://ftp.gnu.org/gnu/gmp/gmp-6.3.0.tar.xz:SKIP:./configure --prefix=/usr --enable-cxx --disable-static CFLAGS=\"-O2 -std=gnu89\" && make CFLAGS=\"-O2\" && make install DESTDIR=\$PAGAN_ROOT" "mpfr:4.2.2:https://ftp.gnu.org/gnu/mpfr/mpfr-4.2.2.tar.xz:SKIP:./configure --prefix=/usr --disable-static --enable-thread-safe && make && make install DESTDIR=\$PAGAN_ROOT" "mpc:1.4.1:https://ftp.gnu.org/gnu/mpc/mpc-1.4.1.tar.xz:SKIP:./configure --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "isl:0.27:https://libisl.sourceforge.io/isl-0.27.tar.bz2:SKIP:./configure --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "attr:2.6.0:https://download.savannah.gnu.org/releases/attr/attr-2.6.0.tar.gz:SKIP:./configure --prefix=/usr --sysconfdir=/etc --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "acl:2.4.0:https://download.savannah.gnu.org/releases/acl/acl-2.4.0.tar.xz:SKIP:./configure --prefix=/usr --sysconfdir=/etc --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "libcap:2.78:https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2/libcap-2.78.tar.xz:SKIP:make prefix=/usr lib=lib && make prefix=/usr lib=lib DESTDIR=\$PAGAN_ROOT install" "libxcrypt:4.5.2:https://github.com/besser82/libxcrypt/releases/download/v4.5.2/libxcrypt-4.5.2.tar.xz:SKIP:./configure --prefix=/usr --enable-hashes=strong,glibc --disable-static CFLAGS=\"-O2 -Wno-error=discarded-qualifiers -Wno-error=unterminated-string-initialization\" && make && make install DESTDIR=\$PAGAN_ROOT" "pcre2:10.47:https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.47/pcre2-10.47.tar.bz2:SKIP:./configure --prefix=/usr --enable-unicode --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "elfutils:0.195:https://sourceware.org/ftp/elfutils/0.195/elfutils-0.195.tar.bz2:SKIP:./configure --prefix=/usr --disable-debuginfod --disable-libdebuginfod --disable-werror && make && make -C libelf install DESTDIR=\$PAGAN_ROOT" "libffi:3.8.0:https://github.com/libffi/libffi/releases/download/v3.8.0/libffi-3.8.0.tar.gz:SKIP:./configure --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" # ======== KOMPILATOR GCC (z C i C++) ======== "gcc:16.2.0:https://ftp.gnu.org/gnu/gcc/gcc-16.2.0/gcc-16.2.0.tar.xz:SKIP:sed -i 's/__cplusplus != 201103/__cplusplus < 201103/g; s/__cplusplus > 201103/__cplusplus < 201103/g' libcody/configure 2>/dev/null || true; sed -i '/^#include \"safe-ctype.h\"/i #ifdef __cplusplus\n#include \n#endif' gcc/system.h 2>/dev/null || true; case \$(uname -m) in x86_64) sed -e '/m64=/s/lib64/lib/' -i.orig gcc/config/i386/t-linux64 ;; esac; sed -i '1i #include ' gcc/config/i386/i386.h 2>/dev/null || true; mkdir -v build && cd build && ../configure --prefix=/usr --enable-languages=c,c++ --disable-multilib --disable-bootstrap --with-system-zlib --enable-default-pie --enable-default-ssp --disable-libsanitizer --disable-split-stack --enable-werror=no CXXFLAGS=\"-O2 -Wno-error=return-type -fno-char8_t -Wno-deprecated-enum-enum-conversion\" && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT && ln -sf gcc \$PAGAN_ROOT/usr/bin/cc" # ======== NARZĘDZIA SYSTEMOWE ======== "file:5.48:https://astron.com/pub/file/file-5.48.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "m4:1.4.21:https://ftp.gnu.org/gnu/m4/m4-1.4.21.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "bc:7.0.3:https://github.com/gavinhoward/bc/releases/download/7.0.3/bc-7.0.3.tar.xz:SKIP:CC=gcc CFLAGS=\"-std=c99 -O2\" ./configure --prefix=/usr -G -O3 && make && make install DESTDIR=\$PAGAN_ROOT" "flex:2.6.4:https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz:SKIP:./configure --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT && ln -sf flex \$PAGAN_ROOT/usr/bin/lex" "bison:3.8.2:https://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "tcl:8.6.18:https://downloads.sourceforge.net/tcl/tcl8.6.18-src.tar.gz:SKIP:cd unix && ./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" # tk – opcjonalne wsparcie Tk (python buduje się z --with-tcltk). # Wersja „8.6.18-xft”: wymusza przebudowę w resume (stary wpis tk-8.6.18 # był zbudowany BEZ Xft). `make install` (bez DESTDIR) instaluje Tk na # HOSTIE: Python 3.14 wykrywa tcl/tk przez pkg-config (tcl.pc/tk.pc) # i buduje moduł _tkinter; drugie `make install DESTDIR` dostarcza Tk do # live (libtk8.6.so, skrypty tk8.6/, tk.h, tkConfig.sh). Wymaga nagłówków # X11 na hoście (/usr/include/X11). # --enable-xft (jak BLFS): BEZ tego Tk używa X core fontów – brak polskich # glifów (ą/ę/ł/ż) i wolne renderowanie (live ma libXft + fontconfig + DejaVu). "tk:8.6.18-xft:https://downloads.sourceforge.net/tcl/tk8.6.18-src.tar.gz:SKIP:test -f /usr/include/X11/Xlib.h || { echo 'Brak nagłówków X11 (Xlib.h) na hoście – zainstaluj libx11/xorgproto-dev'; exit 1; }; cd unix && ./configure --prefix=/usr --enable-xft && make && make install && make install DESTDIR=\$PAGAN_ROOT" "expect:5.45.4:https://prdownloads.sourceforge.net/expect/expect5.45.4.tar.gz:SKIP:cp -f pty_termios.c pty_.c 2>/dev/null || true; ./configure --prefix=/usr --with-tcl=/usr/lib --enable-shared && sed -e 's/^#undef HAVE_TERMIOS/#define HAVE_TERMIOS 1/' -e 's/^#undef HAVE_UNISTD_H/#define HAVE_UNISTD_H 1/' -e 's/^#undef HAVE_SYS_FCNTL_H/#define HAVE_SYS_FCNTL_H 1/' -e 's/^#undef HAVE_SYS_SELECT_H/#define HAVE_SYS_SELECT_H 1/' -e 's/^#undef HAVE_SYS_TIME_H/#define HAVE_SYS_TIME_H 1/' -e 's/^#undef HAVE_SYS_TYPES_H/#define HAVE_SYS_TYPES_H 1/' -e 's/^#undef HAVE_SYS_STAT_H/#define HAVE_SYS_STAT_H 1/' -e 's/^#undef HAVE_STDLIB_H/#define HAVE_STDLIB_H 1/' -e 's/^#undef HAVE_STRING_H/#define HAVE_STRING_H 1/' -e 's/^#undef HAVE_MEMORY_H/#define HAVE_MEMORY_H 1/' -e 's/^#undef HAVE_INTTYPES_H/#define HAVE_INTTYPES_H 1/' -e 's/^#undef HAVE_LIMITS_H/#define HAVE_LIMITS_H 1/' -e 's/^#undef HAVE_TCGETS_OR_TCGETA_IN_TERMIOS_H/#define HAVE_TCGETS_OR_TCGETA_IN_TERMIOS_H 1/' -e 's/^#undef HAVE_TIOCGWINSZ_IN_TERMIOS_H/#define HAVE_TIOCGWINSZ_IN_TERMIOS_H 1/' -e 's/^#undef HAVE_SYSMACROS_H/#define HAVE_SYSMACROS_H 1/' -e 's/^#undef HAVE_STDARG_H/#define HAVE_STDARG_H 1/' -e 's/^#undef TIME_WITH_SYS_TIME/#define TIME_WITH_SYS_TIME 1/' expect_cf.h.in > expect_cf.h && sed -i '\$a#define NO_UNION_WAIT 1\n#define SETPGRP_VOID 1\n#define POSIX 1\n#define RETSIGTYPE void\n#define HAVE_OPENPTY 1\n#define HAVE_TCSETATTR 1\n#define HAVE_SYS_WAIT_H 1\n#define HAVE_SYSCONF 1\n#define HAVE_STRFTIME 1\n#define HAVE_MEMMOVE 1\n#define HAVE_MEMCPY 1\n#define HAVE_TIMEZONE 1\n#define HAVE_SIGLONGJMP 1\n#define HAVE_STRCHR 1\n#define HAVE_LONG_FILE_NAMES 1\n#define HAVE_TIME_H 1\n#define REARM_SIG 1\n#define WNOHANG_REQUIRES_POSIX_SOURCE 1' expect_cf.h && make && make install DESTDIR=\$PAGAN_ROOT" "dejagnu:1.6.3:https://ftp.gnu.org/gnu/dejagnu/dejagnu-1.6.3.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "autoconf:2.73:https://ftp.gnu.org/gnu/autoconf/autoconf-2.73.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "automake:1.18.1:https://ftp.gnu.org/gnu/automake/automake-1.18.1.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "libtool:2.6.2:https://ftp.gnu.org/gnu/libtool/libtool-2.6.2.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "gdbm:1.26:https://ftp.gnu.org/gnu/gdbm/gdbm-1.26.tar.gz:SKIP:./configure --prefix=/usr --disable-static --enable-libgdbm-compat && make && make install DESTDIR=\$PAGAN_ROOT" "gperf:3.3:https://ftp.gnu.org/gnu/gperf/gperf-3.3.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "expat:2.8.3:https://github.com/libexpat/libexpat/releases/download/R_2_8_3/expat-2.8.3.tar.xz:SKIP:./configure --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "gettext:1.0:https://ftp.gnu.org/gnu/gettext/gettext-1.0.tar.xz:SKIP:./configure --prefix=/usr --disable-static --disable-acl && make && make install DESTDIR=\$PAGAN_ROOT" "check:0.15.2:https://github.com/libcheck/check/releases/download/0.15.2/check-0.15.2.tar.gz:SKIP:./configure --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "groff:1.24.1:https://ftp.gnu.org/gnu/groff/groff-1.24.1.tar.gz:SKIP:./configure --prefix=/usr --with-gs=missing --with-urw-fonts-dir=/nonexistent && make && make install DESTDIR=\$PAGAN_ROOT" "texinfo:7.3:https://ftp.gnu.org/gnu/texinfo/texinfo-7.3.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "intltool:0.51.0:https://deb.debian.org/debian/pool/main/i/intltool/intltool_0.51.0.orig.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" # ======== NARZĘDZIA GNU CORE ======== "ncurses:6.6:https://invisible-mirror.net/archives/ncurses/ncurses-6.6.tar.gz:SKIP:./configure --prefix=/usr --with-shared --without-debug --without-normal --enable-widec --with-versioned-syms && make && make install DESTDIR=\$PAGAN_ROOT && for lib in ncurses form panel menu; do ln -sfv lib\${lib}w.so \$PAGAN_ROOT/usr/lib/lib\${lib}.so 2>/dev/null; done && ln -sfv libncursesw.so \$PAGAN_ROOT/usr/lib/libtinfo.so 2>/dev/null" "dialog:1.3-20240619:https://invisible-island.net/archives/dialog/dialog-1.3-20240619.tgz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "bash:5.3:https://ftp.gnu.org/gnu/bash/bash-5.3.tar.gz:SKIP:./configure --prefix=/usr --without-bash-malloc --with-installed-readline && make && make install DESTDIR=\$PAGAN_ROOT && ln -sf bash \$PAGAN_ROOT/bin/sh" "coreutils:9.11:https://ftp.gnu.org/gnu/coreutils/coreutils-9.11.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "diffutils:3.12:https://ftp.gnu.org/gnu/diffutils/diffutils-3.12.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "findutils:4.11.0:https://ftp.gnu.org/gnu/findutils/findutils-4.11.0.tar.xz:SKIP:./configure --prefix=/usr --localstatedir=/var/lib/locate && make && make install DESTDIR=\$PAGAN_ROOT" "gawk:5.4.1:https://ftp.gnu.org/gnu/gawk/gawk-5.4.1.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "grep:3.12:https://ftp.gnu.org/gnu/grep/grep-3.12.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "gzip:1.14:https://ftp.gnu.org/gnu/gzip/gzip-1.14.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "make:4.4.1:https://ftp.gnu.org/gnu/make/make-4.4.1.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "patch:2.8:https://ftp.gnu.org/gnu/patch/patch-2.8.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "sed:4.10:https://ftp.gnu.org/gnu/sed/sed-4.10.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "tar:1.35:https://ftp.gnu.org/gnu/tar/tar-1.35.tar.xz:SKIP:./configure --prefix=/usr --without-posix-acls && make && make install DESTDIR=\$PAGAN_ROOT" "libpipeline:1.5.8:https://download.savannah.gnu.org/releases/libpipeline/libpipeline-1.5.8.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "man-db:2.13.1:https://download.savannah.gnu.org/releases/man-db/man-db-2.13.1.tar.xz:SKIP:./configure --prefix=/usr --sysconfdir=/etc --disable-setuid && make && make install DESTDIR=\$PAGAN_ROOT" "vim:9.2.0954:https://github.com/vim/vim/archive/v9.2.0954/vim-9.2.0954.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT && ln -sf vim \$PAGAN_ROOT/usr/bin/vi" # busybox (statyczny) – potrzebny do INITRAMFS: samowystarczalny /bin/sh # i narzędzia (mount/switch_root/losetup/...). Bez niego initramfs kopiował # binarki HOSTA bez /bin/sh i bibliotek → kernel: "Failed to execute /init". # Instalujemy TYLKO binarkę (bez symlinków appletów – nie nadpisujemy coreutils). "busybox:1.37.0:https://busybox.net/downloads/busybox-1.37.0.tar.bz2:SKIP:make defconfig && sed -i -e 's/^# CONFIG_STATIC is not set/CONFIG_STATIC=y/' -e 's/^CONFIG_TC=y/# CONFIG_TC is not set/' .config && make -j\$PAGAN_JOBS && install -m755 busybox \$PAGAN_ROOT/usr/bin/busybox" # ======== BEZPIECZEŃSTWO ======== "openssl:4.0.1:https://github.com/openssl/openssl/releases/download/openssl-4.0.1/openssl-4.0.1.tar.gz:SKIP:./Configure --prefix=/usr --openssldir=/etc/ssl --libdir=lib shared zlib-dynamic && make && make install DESTDIR=\$PAGAN_ROOT" "libbsd:0.12.2:https://libbsd.freedesktop.org/releases/libbsd-0.12.2.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "shadow:4.20.2:https://github.com/shadow-maint/shadow/releases/download/4.20.2/shadow-4.20.2.tar.xz:SKIP:./configure --prefix=/usr --sysconfdir=/etc --with-group-name-max-length=32 && make && make install DESTDIR=\$PAGAN_ROOT" "linux-pam:1.7.2:https://github.com/linux-pam/linux-pam/releases/download/v1.7.2/Linux-PAM-1.7.2.tar.xz:SKIP:meson setup build --prefix=/usr --sysconfdir=/etc -Ddocs=disabled -Daudit=disabled -Deconf=disabled -Dlogind=disabled -Dselinux=disabled -Dnis=disabled -Dopenssl=disabled -Dexamples=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "sudo:1.9.17p2:https://www.sudo.ws/dist/sudo-1.9.17p2.tar.gz:SKIP:sed -i 's/current_name->d.iPAddress->length/ASN1_STRING_length(current_name->d.iPAddress)/g' lib/iolog/hostcheck.c && ./configure --prefix=/usr --sysconfdir=/etc --with-secure-path && make && make install DESTDIR=\$PAGAN_ROOT" # ======== SIEC ======== # libtirpc – Xorg 21.x linkuje się z nim (RPC/Xtrans). Bez builda w # rootfs configure łapał .pc z HOSTA i w live /usr/bin/X padał: # „libtirpc.so.3: cannot open shared object file”. # Źródło wg BLFS (SourceForge). Jeśli gcc16 będzie marudzić, dojdzie patch. "libtirpc:1.3.7:https://downloads.sourceforge.net/libtirpc/libtirpc-1.3.7.tar.bz2:SKIP:./configure --prefix=/usr --sysconfdir=/etc --disable-static --disable-gssapi && make && make install DESTDIR=\$PAGAN_ROOT" "curl:8.18.0:https://curl.se/download/curl-8.18.0.tar.xz:SKIP:./configure --prefix=/usr --with-openssl --disable-ldap --disable-ldaps --without-libidn2 --without-brotli --without-nghttp2 --without-librtmp --without-libpsl CPPFLAGS=\"-DOPENSSL_NO_UI_CONSOLE\" --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "inetutils:2.8:https://ftp.gnu.org/gnu/inetutils/inetutils-2.8.tar.gz:SKIP:./configure --prefix=/usr --disable-logger --disable-whois --disable-servers && make && make install DESTDIR=\$PAGAN_ROOT" "iproute2:7.1.0:https://www.kernel.org/pub/linux/utils/net/iproute2/iproute2-7.1.0.tar.xz:SKIP:sed -i /ARPD/d Makefile && make && make DESTDIR=\$PAGAN_ROOT SBINDIR=/usr/sbin install" "dhcpcd:10.0.8:https://github.com/NetworkConfiguration/dhcpcd/releases/download/v10.0.8/dhcpcd-10.0.8.tar.xz:SKIP:./configure --prefix=/usr --sysconfdir=/etc && make && make install DESTDIR=\$PAGAN_ROOT" "libnl:3.12.0:https://github.com/thom311/libnl/releases/download/libnl3_12_0/libnl-3.12.0.tar.gz:SKIP:./configure --prefix=/usr --sysconfdir=/etc --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" # libmnl + libnftnl – zależności iptables-nft (nowoczesny firewall/nftables) "libmnl:1.0.5:https://www.netfilter.org/projects/libmnl/files/libmnl-1.0.5.tar.bz2:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "libnftnl:1.3.1:https://www.netfilter.org/projects/libnftnl/files/libnftnl-1.3.1.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "iptables:1.8.12:https://www.netfilter.org/projects/iptables/files/iptables-1.8.12.tar.xz:SKIP:./configure --prefix=/usr --sysconfdir=/etc && make && make install DESTDIR=\$PAGAN_ROOT" # ======== SYSTEM I USŁUGI ======== "util-linux:2.42.2:https://www.kernel.org/pub/linux/utils/util-linux/v2.42/util-linux-2.42.2.tar.xz:SKIP:./configure --bindir=/usr/bin --libdir=/usr/lib --sbindir=/usr/sbin --disable-chfn-chsh --disable-login --disable-nologin --disable-su --disable-runuser --disable-pylibmount --disable-liblastlog2 --without-python --without-systemd && make && make install DESTDIR=\$PAGAN_ROOT" "e2fsprogs:1.47.4:https://downloads.sourceforge.net/project/e2fsprogs/e2fsprogs/v1.47.4/e2fsprogs-1.47.4.tar.gz:SKIP:mkdir bld && cd bld && ../configure --prefix=/usr --sysconfdir=/etc --enable-elf-shlibs --disable-fuse2fs && make && make install DESTDIR=\$PAGAN_ROOT" # Narzędzia systemów plików dla instalatora (Calamares): # • dosfstools (mkfs.vfat) – BEZ niego instalacja EFI pada po wyborze # dysku („mkfs.vfat: command not found” → set -e ubija instalator). # • xfsprogs / btrfs-progs – gdy użytkownik wybierze xfs/btrfs w instalatorze. "dosfstools:4.2:https://github.com/dosfstools/dosfstools/releases/download/v4.2/dosfstools-4.2.tar.gz:SKIP:./configure --prefix=/usr --enable-compat-symlinks && make && make install DESTDIR=\$PAGAN_ROOT" "xfsprogs:7.1.1:https://www.kernel.org/pub/linux/utils/fs/xfs/xfsprogs/xfsprogs-7.1.1.tar.xz:SKIP:./configure --prefix=/usr --disable-static --disable-scrub && make && make install DESTDIR=\$PAGAN_ROOT" # btrfs-progs: --disable-libudev, bo libudev (z systemd/udev) buduje się w tej # liście PÓŹNIEJ niż btrfs-progs, a configure bez tej opcji pada na # „Package 'libudev' not found". libudev służy tu tylko do wykrywania # urządzeń MULTIPATH – zbędne w live/instalatorze. "btrfs-progs:7.1:https://www.kernel.org/pub/linux/kernel/people/kdave/btrfs-progs/btrfs-progs-v7.1.tar.xz:SKIP:./configure --prefix=/usr --disable-documentation --disable-python --disable-libudev && make && make install DESTDIR=\$PAGAN_ROOT" # ======== SZYFROWANIE (LUKS) – cryptsetup + zależności ======== # Kolejność ma znaczenie: libdevmapper (lvm2) → json-c → popt → argon2 → cryptsetup. # lvm2: libdevmapper wymagany przez cryptsetup; daje też LVM w instalatorze (auto+LVM). "lvm2:2.03.42:https://sourceware.org/pub/lvm2/releases/LVM2.2.03.42.tgz:SKIP:./configure --prefix=/usr --disable-selinux --disable-systemd-journal && make && make install DESTDIR=\$PAGAN_ROOT" "json-c:0.19:https://github.com/json-c/json-c/releases/download/json-c-0.19-20260627/json-c-0.19.tar.gz:SKIP:mkdir bld && cd bld && cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_STATIC_LIBS=OFF -DCMAKE_BUILD_TYPE=Release && make && make install DESTDIR=\$PAGAN_ROOT" "popt:1.19:https://github.com/rpm-software-management/popt/archive/refs/tags/popt-1.19-release.tar.gz:SKIP:./autogen.sh && ./configure --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "argon2:20190702:https://github.com/P-H-C/phc-winner-argon2/archive/refs/tags/20190702.tar.gz:SKIP:make && make install PREFIX=/usr DESTDIR=\$PAGAN_ROOT" # cryptsetup 2.8 – backend openssl (openssl jest budowany wcześniej), # bez tokena ssh i bez bindingu Pythona; man wymaga asciidoctor → off. "cryptsetup:2.8.7:https://www.kernel.org/pub/linux/utils/cryptsetup/v2.8/cryptsetup-2.8.7.tar.xz:SKIP:./configure --prefix=/usr --with-crypto_backend=openssl --disable-ssh-token --disable-asciidoc && make && make install DESTDIR=\$PAGAN_ROOT" "procps-ng:4.0.7:https://sourceforge.net/projects/procps-ng/files/Production/procps-ng-4.0.7.tar.xz:SKIP:./configure --prefix=/usr --disable-static --disable-kill --without-ncurses && make && make install DESTDIR=\$PAGAN_ROOT" "psmisc:23.7:https://sourceforge.net/projects/psmisc/files/psmisc/psmisc-23.7.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "kmod:34.2:https://www.kernel.org/pub/linux/utils/kernel/kmod/kmod-34.2.tar.xz:SKIP:./configure --prefix=/usr --sysconfdir=/etc --disable-manpages --with-openssl --with-xz --with-zstd --with-zlib && make && make install DESTDIR=\$PAGAN_ROOT" "kbd:2.10.0:https://www.kernel.org/pub/linux/utils/kbd/kbd-2.10.0.tar.xz:SKIP:./configure --prefix=/usr --disable-vlock && make && make install DESTDIR=\$PAGAN_ROOT" "less:704:https://www.greenwoodsoftware.com/less/less-704.tar.gz:SKIP:./configure --prefix=/usr --sysconfdir=/etc && make && make install DESTDIR=\$PAGAN_ROOT" "sysklogd:2.7.2:https://github.com/troglobit/sysklogd/releases/download/v2.7.2/sysklogd-2.7.2.tar.gz:SKIP:./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var && make && make install DESTDIR=\$PAGAN_ROOT" "dbus:1.16.2:https://dbus.freedesktop.org/releases/dbus/dbus-1.16.2.tar.xz:SKIP:CC=gcc CXX=g++ cmake -B build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_SYSCONFDIR=/etc -DCMAKE_INSTALL_LOCALSTATEDIR=/var -DDBUS_BUILD_TESTS=OFF -DDBUS_SYSTEMD_USER_SERVICE_DIR=/usr/lib/systemd/user && make -C build && make -C build DESTDIR=\$PAGAN_ROOT install" # wpa_supplicant po dbus (defconfig ma CONFIG_CTRL_IFACE_DBUS_NEW=y – potrzebuje dbus/dbus.h) i po libnl # BINDIR=/usr/sbin: domyślnie wpa_supplicant instaluje się do /usr/local/sbin # (Makefile: export BINDIR ?= /usr/local/sbin), a NetworkManager szuka # helpera w standardowych ścieżkach systemowych. Dodatkowo `make install` # NIE instaluje plików D-Bus (fi.w1.wpa_supplicant1) ani jednostki # systemd – dokłada je _ensure_wifi_supplicant(). "wpa_supplicant:2.11:https://w1.fi/releases/wpa_supplicant-2.11.tar.gz:SKIP:cd wpa_supplicant && cp defconfig .config && sed -i 's/^CONFIG_SMARTCARD=y/#CONFIG_SMARTCARD=y/' .config && sed -i -E 's/([A-Za-z_][A-Za-z0-9_]*)->d.(ia5|dNSName|iPAddress|uniformResourceIdentifier|rfc822Name)->data/ASN1_STRING_get0_data(\1->d.\2)/g; s/([A-Za-z_][A-Za-z0-9_]*)->d.(ia5|dNSName|iPAddress|uniformResourceIdentifier|rfc822Name)->length/ASN1_STRING_length(\1->d.\2)/g; s/cn->data/ASN1_STRING_get0_data(cn)/g; s/cn->length/ASN1_STRING_length(cn)/g' ../src/crypto/tls_openssl.c && make BINDIR=/usr/sbin && make DESTDIR=\$PAGAN_ROOT BINDIR=/usr/sbin install" # systemd: konflikt net/if.h (glibc) z linux/if.h (kernel UAPI). Build kompiluje # NA HOŚCIE z nagłówkami TARGETA (-I$PAGAN_ROOT/usr/include), które przesłaniają # vendorowane UAPI systemd (src/include/uapi) → wchodzi do TU # PRZED jakimkolwiek headerem glibc, libc-compat.h nie widzi __GLIBC__ i kernel # definiuje pełne enumy IFF_*/struct ifreq → potem glibc → # „redeclaration of enumerator”. Kernelowy linux/libc-compat.h wyłącza własne # definicje TYLKO gdy glibc wszedł pierwszy (warunek _NET_IF_H && __USE_MISC). # Dlatego wymuszamy glibcowy na starcie KAŻDEJ TU przez -include: # wtedy kernel (defs=0) nie dubluje enuma IFF_*/struct ifreq i build przechodzi # jak na dystrybucjach. UWAGA: ten -include ładuje features.h PRZED -include # config.h (który niesie _GNU_SOURCE), więc dorzucamy -D_GNU_SOURCE – inaczej # features.h blokuje __USE_GNU/__USE_LARGEFILE64 → „O_LARGEFILE not defined” # (-Werror=undef) i „implicit declaration of gettid”. (Pomysł -D__GLIBC__ NIE # działa – libc-compat.h i tak ustawia __UAPI_DEF_IF_*=1 bez _NET_IF_H.) "systemd:261.2:https://github.com/systemd/systemd/archive/v261.2/systemd-261.2.tar.gz:SKIP:sed -i 's|#include |&\n#ifndef __key_serial_t_defined\ntypedef int key_serial_t;\n#define __key_serial_t_defined\n#endif|' src/include/override/sys/keyctl.h && sed -i '/^#include $/d' src/shared/firewall-util.c && sed -i 's|^#include $|&\n#include \n#include |' src/shared/firewall-util.c && cp -f src/include/uapi/linux/liveupdate.h \$PAGAN_ROOT/usr/include/linux/liveupdate.h && sed -i -e 's/ifr->ifr_data = &ecmd;/ifr->ifr_data = (void*) \&ecmd;/' -e 's/ifr->ifr_data = settings;/ifr->ifr_data = (void*) settings;/' -e 's/ifr\.ifr_data = settings;/ifr.ifr_data = (void*) settings;/' src/shared/ethtool-util.c && sed -i '/#include /a #include ' src/shared/resize-fs.c && sed -i '/^#endif/i #define MQUEUE_MAGIC 0x19800202' \$PAGAN_ROOT/usr/include/linux/magic.h && CFLAGS=\"\$CFLAGS -D_GNU_SOURCE -include \$PAGAN_ROOT/usr/include/net/if.h -Dopen_tree_attr=open_tree_attr_shim -Dquotactl_fd=quotactl_fd_shim -DTASK_COMM_LEN=16 -Wno-redundant-decls -Wno-error=redundant-decls -Wno-error\" CC=gcc CXX=g++ meson setup build --prefix=/usr --sysconfdir=/etc --localstatedir=/var --buildtype=release -Ddefault-dnssec=no -Dfirstboot=false -Dinstall-tests=false -Dldconfig=false -Dsysusers=false -Drpmmacrosdir=no -Dhomed=disabled -Dman=disabled -Dmode=release -Dpamconfdir=no -Ddev-kvm-mode=0660 -Dnobody-group=nogroup -Dsysupdate=disabled -Dukify=disabled -Dwerror=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # GRUB i386-pc: WYMUSZAMY `-Wl,-Ttext` jako opcję adresu bazowego jądra. # GRUB sam wykrywa ją w configure.ac (AX_CHECK_LINK_FLAG na # "-Wl,--image-base,0x400000"): gdy linker przyjmie tę opcję, wybiera # --image-base, w przeciwnym razie -Ttext. Problem: dzisiejszy GNU ld # PRZYJMUJE --image-base także dla celów ELF (to opcja PE/EFI, ld tylko # ostrzega), więc probe „się udaje” i kernel.exec jest linkowany z # --image-base,0x9000. Wtedy .text NIE trafia na 0x9000, tylko na # 0x9000 + rozmiar nagłówka ELF (0x74) = 0x9074, bo jądro i386-pc musi # być jednym segmentem RWX (-Wl,-N/OMAGIC) i nagłówki siedzą w nim. # Skutek: grub-mkimage/grub-install: „kernel.img is miscompiled: its # start address is not 0x9000” → instalacja na BIOS nie działa. # Odtworzone i zmierzone lokalnie (binutils hosta, GRUB 2.14): # --image-base,0x9000 -> .text 0x9074 (zepsute) # -Ttext,0x9000 -> .text 0x9000 (dobre) # Dlatego podajemy wynik probe-a z góry (zmienna cache AX_CHECK_LINK_FLAG) # TYLKO dla builda pc – build EFI zostaje przy --image-base, bo tam (PE) # to jest właściwa opcja. # UWAGA: -Wl,-z,noseparate-code nie ma tu znaczenia (testowane: sam nie # naprawia, a z -Ttext jest zbędny) – zostawiony, bo cały pozostały # build (moduły, grub-mkimage) jest z nim przetestowany. "grub:2.14:https://ftp.gnu.org/gnu/grub/grub-2.14.tar.xz:SKIP:mkdir -p build-pc build-efi && cd build-pc && ../configure --prefix=/usr --sysconfdir=/etc --disable-werror --with-platform=pc TARGET_CFLAGS=\"-Os -fno-pie -fno-PIE\" TARGET_LDFLAGS=\"-no-pie -Wl,-z,noseparate-code\" ax_cv_check_ldflags___Wl___image_base_0x400000=no && touch grub-core/extra_deps.lst && make && make install DESTDIR=\$PAGAN_ROOT && cd ../build-efi && ../configure --prefix=/usr --sysconfdir=/etc --disable-werror --with-platform=efi TARGET_CFLAGS=\"-Os -fno-pie -fno-PIE\" TARGET_LDFLAGS=\"-no-pie -Wl,-z,noseparate-code\" && touch grub-core/extra_deps.lst && make && make install DESTDIR=\$PAGAN_ROOT" # efivar + efibootmgr: moduł "bootloader" Calamaresa woła efibootmgr # w ścieżce UEFI (efi_boot_next) BEZ zabezpieczenia — gdy binarki nie ma, # subprocess.check_output rzuca FileNotFoundError, a Calamares pokazuje # „Bad main script file … for python job bootloader raised an exception”. # efibootmgr wymaga libefivar (popt jest już wyżej w liście). # ENABLE_DOCS=0: strony man efivara generuje MANDOC (docs/Makefile), # którego nie ma w systemie budującym — bez tej flagi `make all` pada na # „mandoc: command not found”. Biblioteki i narzędzia budują się bez docs. # (efibootmgr nie ma tego problemu: %.8 powstaje z %.8.in przez sed.) "efivar:39:https://github.com/rhboot/efivar/archive/refs/tags/39.tar.gz:SKIP:unset MAKEFLAGS && make ERRORS='' ENABLE_DOCS=0 all && make DESTDIR=\$PAGAN_ROOT/ LIBDIR=/usr/lib/ PCDIR=/usr/lib/pkgconfig INCLUDEDIR=/usr/include/ BINDIR=/usr/bin/ ENABLE_DOCS=0 install -j1 V=1" "efibootmgr:18:https://github.com/rhboot/efibootmgr/archive/edc8b9b6ec1c7751ccb9a483405c99141ba237fc.tar.gz:e951ce9e0534c63bb71ba8b2a3830d4402e51440cb4d524d18c1ef40ae5ee218:make EXTRA_CFLAGS=\"-Os\" EFIDIR=\"/boot/EFI\" && install -D -m0755 src/efibootmgr \$PAGAN_ROOT/usr/bin/efibootmgr && install -D -m0644 src/efibootmgr.8 \$PAGAN_ROOT/usr/share/man/man8/efibootmgr.8" "sysvinit:3.14:https://github.com/slicer69/sysvinit/releases/download/3.14/sysvinit-3.14.tar.xz:SKIP:curl -fsSL -o /tmp/sysvinit.patch 'https'\$'\72''//www.linuxfromscratch.org/patches/lfs/13.1-rc2/sysvinit-3.14-consolidated-1.patch' 2>/dev/null || true; patch -Np1 < /tmp/sysvinit.patch 2>/dev/null || true; rm -f /tmp/sysvinit.patch; make && make install DESTDIR=\$PAGAN_ROOT && rm -f \$PAGAN_ROOT/sbin/init" # ======== PERL I PYTHON ======== "sqlite:3530400:https://sqlite.org/2026/sqlite-autoconf-3530400.tar.gz:SKIP:CC=gcc ./configure --prefix=/usr --disable-static --enable-fts5 && make && make install DESTDIR=\$PAGAN_ROOT" "perl:5.44.0:https://www.cpan.org/src/5.0/perl-5.44.0.tar.xz:SKIP:sh Configure -des -Dcc=gcc -Dprefix=/usr -Duseshrplib && make && make install DESTDIR=\$PAGAN_ROOT" "mpdecimal:4.0.1:https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-4.0.1.tar.gz:SKIP:./configure --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" # python – wymagany przez cały system (pag, pagsync/pagbuild, narzędzia GUI). # OpenSSL w rootfs to 4.x (libssl.so.4): build musi używać NAGŁÓWKÓW targetu # (CPPFLAGS=-I$PAGAN_ROOT/usr/include), bo inaczej _ssl.c kompiluje się przeciw # hostowi (openssl 3.x z TLSv1_method) i linkuje do libssl.so.4 bez tej funkcji # → „undefined symbol: TLSv1_method” i make usuwa moduł. Dodatkowo: # • sed usuwa legacy wywołania TLSv1/1_1/1_2_method dla openssl >= 4.0 # (patch LFS openssl_4-1 nie pokrywa tej strażnika w 3.14.7); # • LDFLAGS z -Wl,-rpath do rootfs – test importu modułów w trakcie make # (RUNSHARED nadpisuje LD_LIBRARY_PATH) musi znaleźć libssl.so.4. "python:3.14.7:https://www.python.org/ftp/python/3.14.7/Python-3.14.7.tar.xz:SKIP:curl -fsSL -o /tmp/python-openssl.patch 'https'\$'\72''//www.linuxfromscratch.org/patches/lfs/13.1-rc2/Python-3.14.7-openssl_4-1.patch' 2>/dev/null || true; patch -Np1 < /tmp/python-openssl.patch 2>/dev/null || true; rm -f /tmp/python-openssl.patch; sed -i 's/!defined(OPENSSL_NO_TLS1_METHOD))/!defined(OPENSSL_NO_TLS1_METHOD) \&\& OPENSSL_VERSION_NUMBER < 0x40000000L)/; s/!defined(OPENSSL_NO_TLS1_1_METHOD))/!defined(OPENSSL_NO_TLS1_1_METHOD) \&\& OPENSSL_VERSION_NUMBER < 0x40000000L)/; s/!defined(OPENSSL_NO_TLS1_2_METHOD))/!defined(OPENSSL_NO_TLS1_2_METHOD) \&\& OPENSSL_VERSION_NUMBER < 0x40000000L)/' Modules/_ssl.c && sed -i 's|^@MODULE__CRYPT_TRUE@_crypt|#_crypt|' Modules/Setup.stdlib.in && ac_cv_lib_crypt_crypt_r=no ac_cv_func_crypt_r=no TCLTK_CFLAGS=\"-I/usr/include\" TCLTK_LIBS=\"-L/usr/lib -ltk8.6 -ltcl8.6\" CPPFLAGS=\"-I\$PAGAN_ROOT/usr/include\" ./configure --prefix=/usr --enable-shared && CPPFLAGS=\"-I\$PAGAN_ROOT/usr/include\" make LDFLAGS=\"\$LDFLAGS -Wl,-rpath,\$PAGAN_ROOT/usr/lib\" && CPPFLAGS=\"-I\$PAGAN_ROOT/usr/include\" make install DESTDIR=\$PAGAN_ROOT" "ninja:1.13.2:https://github.com/ninja-build/ninja/archive/v1.13.2/ninja-1.13.2.tar.gz:SKIP:python3 configure.py --bootstrap && install -m755 ninja \$PAGAN_ROOT/usr/bin/" "meson:1.12.0:https://github.com/mesonbuild/meson/releases/download/1.12.0/meson-1.12.0.tar.gz:SKIP:pip3 wheel -w dist --no-build-isolation --no-deps \$PWD && pip3 install --no-index --find-links dist --root=\$PAGAN_ROOT meson" "pkgconf:3.0.5:https://distfiles.ariadne.space/pkgconf/pkgconf-3.0.5.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "cmake:4.2.3:https://cmake.org/files/v4.2/cmake-4.2.3.tar.gz:SKIP:sed -i 's/j < psig->length/j < ASN1_STRING_length(psig)/; s/psig->data\\[j\\]/ASN1_STRING_get0_data(psig)[j]/; s/num->type == V_ASN1_NEG_INTEGER/ASN1_STRING_get0_data(num)[0] >= 0x80/; s/j < num->length/j < ASN1_STRING_length(num)/; s/num->data\\[j\\]/ASN1_STRING_get0_data(num)[j]/' Utilities/cmcurl/lib/vtls/openssl.c && cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF && cmake --build build $MAKEFLAGS && DESTDIR=\$PAGAN_ROOT cmake --install build" # ======== NARZĘDZIA DEWELOPERSKIE ======== "git:2.53.0:https://www.kernel.org/pub/software/scm/git/git-2.53.0.tar.xz:SKIP:sed -i -E 's/([A-Za-z_][A-Za-z0-9_]*)->d\.(ia5|dNSName|iPAddress|uniformResourceIdentifier|rfc822Name)->data/ASN1_STRING_get0_data(\1->d.\2)/g; s/([A-Za-z_][A-Za-z0-9_]*)->d\.(ia5|dNSName|iPAddress|uniformResourceIdentifier|rfc822Name)->length/ASN1_STRING_length(\1->d.\2)/g' imap-send.c && ./configure --prefix=/usr --with-openssl --with-curl --with-expat && make && make install DESTDIR=\$PAGAN_ROOT" # gnupg 2.5 linkuje się przeciw libassuan z HOSTA (target nie buduje libassuan). # Archowy/upstreamowy assuan.h 3.0 bywa uszkodzony: zamiast # „#define ASSUAN_NO_NPTH_SYSTEM_HOOKS_ANY_MORE 1” zawiera literówkę „#defined” # (invalid preprocessing directive). Samouzdrawianie: poprawiamy ten JEDEN token # w /usr/include (idempotentne; no-op gdy pliku/znaku brak). "gnupg:2.5.17:https://www.gnupg.org/ftp/gcrypt/gnupg/gnupg-2.5.17.tar.bz2:SKIP:sed -i 's/#defined ASSUAN_NO_NPTH_SYSTEM_HOOKS_ANY_MORE/#define ASSUAN_NO_NPTH_SYSTEM_HOOKS_ANY_MORE/' /usr/include/assuan.h 2>/dev/null || true; ./configure --prefix=/usr --sysconfdir=/etc && make && make install DESTDIR=\$PAGAN_ROOT" "openssh:10.2p1:https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-10.2p1.tar.gz:SKIP:sed -i 's/octet->data/ASN1_STRING_get0_data(octet)/; s/octet->length/ASN1_STRING_length(octet)/' ssh-pkcs11.c && CPPFLAGS=\"-I\$PAGAN_ROOT/usr/include\" LDFLAGS=\"\$LDFLAGS -Wl,-rpath,\$PAGAN_ROOT/usr/lib\" ./configure --prefix=/usr --sysconfdir=/etc/ssh --with-md5-passwords && make && make install DESTDIR=\$PAGAN_ROOT" "rsync:3.4.1:https://www.samba.org/ftp/rsync/src/rsync-3.4.1.tar.gz:SKIP:rm -f config.cache && LDFLAGS=\"\$LDFLAGS -Wl,-rpath,\$PAGAN_ROOT/usr/lib\" ./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "htop:3.3.0:https://github.com/htop-dev/htop/releases/download/3.3.0/htop-3.3.0.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "nano:8.7.1:https://www.nano-editor.org/dist/v8/nano-8.7.1.tar.xz:SKIP:./configure --prefix=/usr --enable-utf8 && make && make install DESTDIR=\$PAGAN_ROOT" # fish 4.x – domyślna powłoka użytkowników (zamiast bash). Statyczna # binarka z wydania 4.9.3 została przygotowana wyżej (_fish_url/_fish_sha). # Usuwamy resztki instalacji 3.7.1 (share/fish), żeby nie mieszały się z # wbudowanymi w binarkę funkcjami/completions. "fish:4.9.3:${_fish_url}:${_fish_sha}:rm -rf \$PAGAN_ROOT/usr/share/fish && cp -f fish \$PAGAN_ROOT/usr/bin/fish && chmod 755 \$PAGAN_ROOT/usr/bin/fish" # ======== DODATKOWE NARZĘDZIA ======== "ca-certificates:2024:https://curl.se/ca/cacert-2024-07-02.pem:SKIP:mkdir -p \$PAGAN_ROOT/etc/ssl/certs && cp cacert-2024-07-02.pem \$PAGAN_ROOT/etc/ssl/certs/ca-certificates.crt" "icu:75.1:https://github.com/unicode-org/icu/releases/download/release-75-1/icu4c-75_1-src.tgz:SKIP:( export CFLAGS=\"-fno-stack-protector -std=gnu17 -O2 -Wno-error=override-init\"; export CXXFLAGS=\"-fno-stack-protector -O2\"; S=\$'\72'; cd source && ./configure --prefix=/usr && sed -i \"s#\\\$(LIBRARY_PATH_PREFIX)\\\$(LIBDIR)\${S}\\\$(top_builddir)/stubdata\${S}\\\$(top_builddir)/tools/ctestfw#\$PWD/lib\${S}\$PWD/stubdata\${S}\$PWD/tools/ctestfw#g; s#\\\$(top_builddir)/stubdata\${S}\\\$(top_builddir)/tools/ctestfw\${S}\\\$(LIBRARY_PATH_PREFIX)\\\$(LIBDIR)#\$PWD/stubdata\${S}\$PWD/tools/ctestfw\${S}\$PWD/lib#g\" icudefs.mk && make && make install DESTDIR=\$PAGAN_ROOT && ln -sf libicudata.so.75.1 \$PAGAN_ROOT/usr/lib/libicudata.so.75 && ln -sf libicudata.so.75.1 \$PAGAN_ROOT/usr/lib/libicudata.so )" "shared-mime-info:2.4:https://gitlab.freedesktop.org/xdg/shared-mime-info/-/archive/2.4/shared-mime-info-2.4.tar.bz2:SKIP:PKG_CONFIG_SYSROOT_DIR= PKG_CONFIG_PATH=/usr/lib/pkgconfig meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # ======== FONTY I FIRMWARE ======== "dejavu-fonts:2.37:https://github.com/dejavu-fonts/dejavu-fonts/releases/download/version_2_37/dejavu-fonts-ttf-2.37.tar.bz2:SKIP:mkdir -p \$PAGAN_ROOT/usr/share/fonts/dejavu && install -m644 ttf/*.ttf \$PAGAN_ROOT/usr/share/fonts/dejavu/" "linux-firmware:20260810:https://cdn.kernel.org/pub/linux/kernel/firmware/linux-firmware-20260810.tar.xz:SKIP:mkdir -p \$PAGAN_ROOT/usr/lib/firmware && cp -a . \$PAGAN_ROOT/usr/lib/firmware/" ) ui_set_total "${#packages[@]}" # tk (nowy pakiet, wymagany przez tkinter instalatora GUI) wymusza przebudowę # Pythona: resume-build zostawiłby stary Python zbudowany BEZ Tk (brak _tkinter) # → instalator GUI nie wystartuje mimo obecności pakietu tk. # Uwaga: klucz resume to „tk-8.6.18-xft” (nowa wersja z Xft) – po udanej # przebudowie warunek przestaje działać i Python NIE jest przebudowywany co build. if ! _is_built "tk-8.6.18-xft" && _is_built "python-3.14.7"; then sed -i '/^python-3\.14\.7$/d' "$RESUME_FILE" 2>/dev/null || true warn "tk-8.6.18-xft jest nowy → usuwam python-3.14.7 z resume (przebudowa Pythona z tkinter)" fi # ncurses MUSI mieć wersjonowane symbole (@NCURSES6_TINFO_...): readline i inne # pakiety buildowane PRZED ncurses (kolejność listy) linkowały się z HOSTOWYM # ncurses (wersjonowanym) → bez wersjonowania w targetcie link pada: # „undefined reference to tgetent@NCURSES6_TINFO_5.0.19991023”. Wymuś przebudowę. if [[ -f "$PAGAN_ROOT/usr/lib/libncursesw.so.6" ]] && \ ! nm -D "$PAGAN_ROOT/usr/lib/libncursesw.so.6" 2>/dev/null | grep -q 'NCURSES6_TINFO'; then warn "ncurses w targetcie bez wersjonowanych symboli – usuwam z resume (przebudowa)" sed -i '/^ncurses-6.6$/d' "$RESUME_FILE" 2>/dev/null || true fi # GRUB i386-pc: kernel.exec MUSI mieć start 0x9000, inaczej grub-mkimage pada # z „kernel.img is miscompiled: its start address is not 0x9000” i instalacja # na BIOS nie działa. Prawdziwą przyczynę (GRUB wybiera -Wl,--image-base # zamiast -Wl,-Ttext, bo nowy ld przyjmuje --image-base także dla ELF) # i naprawę opisano przy wpisie `grub:` na liście pakietów. # Ten check jest samonaprawczy: gdy w rootfsie został stary/zepsuty kernel.exec # z wcześniejszego przebiegu, zdejmujemy grub z resume i przebudowujemy go. if command -v objdump >/dev/null 2>&1 && \ [[ -f "$PAGAN_ROOT/usr/lib/grub/i386-pc/kernel.exec" ]]; then _gaddr=$(objdump -f "$PAGAN_ROOT/usr/lib/grub/i386-pc/kernel.exec" 2>/dev/null \ | awk '/start address/{print $NF}') if [[ -n "$_gaddr" ]] && ! _addr_is_0x9000 "$_gaddr"; then warn "GRUB i386-pc: kernel.exec start=$_gaddr (ma być 0x…9000) – usuwam z resume (przebudowa)" sed -i '/^grub-2\.14$/d' "$RESUME_FILE" 2>/dev/null || true fi fi for pkg_info in "${packages[@]}"; do name="${pkg_info%%:*}"; rest="${pkg_info#*:}" ver="${rest%%:*}"; rest="${rest#*:}" config="${rest##*:}"; rest_no_cfg="${rest%:*}" sha="${rest_no_cfg##*:}" url="${rest_no_cfg%:*}" build_lfs_pkg "$name" "$ver" "$url" "$sha" "$config" done # TWARDA weryfikacja po przebudowie: zły kernel.img = dysk nie wstanie. # Najpierw próbujemy importu spójnego gruba z hosta; dopiero gdy to # niemożliwe – zatrzymujemy build z błędem (zamiast pakować złe ISO). # # WAŻNE, KTÓRY PLIK SPRAWDZAĆ: dla i386-pc `grub-mkimage` czyta plik # **kernel.img** (util/mkimage.c: `kernel_path = grub_util_get_path(dir, # "kernel.img")` bezwarunkowo), a test „is miscompiled: its start address” # z util/grub-mkimagexx.c porównuje adres sekcji .text właśnie z link_addr # (0x9000) tego pliku. `kernel.exec` to plik tylko pomocniczy # (symbole/debug_info) – sam jego zły adres niczego nie psuje i jest # NORMALNY po imporcie gruba z hosta, bo paczki dystrybucyjne nie zawierają # kernel.exec wcale. Dlatego sprawdzamy kernel.img, a .exec tylko # raportujemy jako informację (wcześniej fałszywy alarm z .exec wymuszał # niepotrzebny import). if command -v readelf >/dev/null 2>&1 && \ [[ -f "$PAGAN_ROOT/usr/lib/grub/i386-pc/kernel.img" ]]; then _gaddr=$(readelf -SW "$PAGAN_ROOT/usr/lib/grub/i386-pc/kernel.img" 2>/dev/null \ | sed -n 's/^ *\[ *[0-9]*\] *\.text *[A-Z]* *\([0-9a-fA-F]*\).*/\1/p' | head -1) if [[ -n "$_gaddr" ]] && ! _addr_is_0x9000 "$_gaddr"; then warn "GRUB i386-pc: kernel.img .text na 0x$_gaddr (ma być 0x9000) – grub-install padnie" if ! import_host_grub; then warn "Brak gruba na hoście do importu – NIE przerywam builda: instalator użyje gruba z repo (netinstall → grub → grub-install). Uwaga: source-grub w ISO jest zepsuty – instalacja BIOS wymaga dostępu do repo." fi else info " ✓ GRUB i386-pc: kernel.img .text=0x${_gaddr:-?} (OK)" # Diagnostyka: rozbieżność .exec/.img oznacza mieszankę buildów # (kernel.img z hosta + kernel.exec z builda). Dla i386-pc to # nieszkodliwe, ale warto wiedzieć, co jest w rootfsie. if command -v objdump >/dev/null 2>&1 && \ [[ -f "$PAGAN_ROOT/usr/lib/grub/i386-pc/kernel.exec" ]]; then _gexec=$(objdump -f "$PAGAN_ROOT/usr/lib/grub/i386-pc/kernel.exec" 2>/dev/null \ | awk '/start address/{print $NF}') if [[ -n "$_gexec" ]] && ! _addr_is_0x9000 "$_gexec"; then info " · grub: kernel.exec start=$_gexec (nieużywany przez grub-mkimage dla i386-pc – ignoruję)" fi fi fi fi rebuild_dbus_with_systemd _configure_basic_system ok "System końcowy zbudowany!" } # Jednostki D-Busa w kanonicznej, działającej postaci (idempotentne). # # DLACZEGO OSOBNA FUNKCJA: rebuild_dbus_with_systemd() kończy się wcześnie, # gdy dbus-daemon ma już --systemd-activation – a wtedy stare, wadliwe symlinki # z /etc zostają. Ta funkcja jest wołana też z finalize_build i fix-calamares.sh, # więc naprawia istniejący rootfs bez pełnej przebudowy. # # BŁĄD, KTÓRY NAPRAWIA (cykl zależności, systemd usuwa zadanie i D-Bus nie wstaje): # sockets.target: Found ordering cycle: dbus.service/start after # basic.target/start after sockets.target/start - after dbus.service # Job dbus.service/start deleted to break ordering cycle starting with sockets.target/start # Przyczyna: dbus.service (DefaultDependencies=yes ⇒ After=basic.target) był # dowiązany w sockets.target.wants, a basic.target ma After=… sockets.target. # Skutek: brak D-Busa ⇒ brak polkitd (Calamares nie widzi dysków), brak # NetworkManager i brak lightdm. _ensure_dbus_units() { local _us="$PAGAN_ROOT/usr/lib/systemd/system" [[ -f "$_us/dbus.socket" || -f "$_us/dbus.service" ]] || return 0 cat > "$_us/dbus.socket" <<'DBUSEOF' [Unit] Description=D-Bus System Message Bus Socket [Socket] ListenStream=/run/dbus/system_bus_socket SocketMode=0666 [Install] WantedBy=sockets.target DBUSEOF cat > "$_us/dbus.service" <<'DBUSEOF' [Unit] Description=D-Bus System Message Bus Documentation=man:dbus-daemon(1) Requires=dbus.socket After=dbus.socket [Service] Type=notify NotifyAccess=main ExecStart=/usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only ExecReload=/usr/bin/dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig ExecStartPre=/usr/bin/dbus-uuidgen --ensure RuntimeDirectory=dbus RuntimeDirectoryMode=0755 Restart=on-failure RestartSec=1s OOMScoreAdjust=-900 [Install] WantedBy=multi-user.target Also=dbus.socket DBUSEOF # Gniazdo nasłuchuje wcześnie (aktywacja socketowa), usługa startuje # w multi-user.target. Usługa w sockets.target.wants = cykl (patrz wyżej). rm -f "$PAGAN_ROOT/etc/systemd/system/sockets.target.wants/dbus.service" mkdir -p "$PAGAN_ROOT/etc/systemd/system/sockets.target.wants" \ "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants" ln -sfn /usr/lib/systemd/system/dbus.socket \ "$PAGAN_ROOT/etc/systemd/system/sockets.target.wants/dbus.socket" ln -sfn /usr/lib/systemd/system/dbus.service \ "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants/dbus.service" ok " dbus: jednostki bez cyklu zależności (socket → sockets.target, usługa → multi-user.target)" } # WiFi: wpa_supplicant gotowy dla NetworkManagera. # # Trzy niezależne braki blokowały WiFi (także na USB): # 1. binarka lądowała w /usr/local/sbin, bo Makefile ma # „export BINDIR ?= /usr/local/sbin” – NM szuka helpera w ścieżkach # systemowych, # 2. brak /usr/share/dbus-1/system-services/fi.w1.wpa_supplicant1.service → # D-Bus nie może aktywować demona, a libnm komunikuje się z nim po D-Bus, # 3. brak polityki /etc/dbus-1/system.d/wpa_supplicant.conf i jednostki systemd. # # `make install` wpa_supplicanta instaluje WYŁĄCZNIE binarki – punkty 2 i 3 # trzeba dołożyć. Idempotentne; wołane z finalize_build i fix-calamares.sh. _ensure_wifi_supplicant() { # 1. Sprowadź binarkę do kanonicznego /usr/sbin/wpa_supplicant. # # Zależnie od wersji i opcji build wpa_supplicant ląduje w /usr/local/sbin # (Makefile ma „export BINDIR ?= /usr/local/sbin”) ALBO w /usr/bin. # Pliki aktywacji D-Bus i jednostka systemd poniżej wskazują # /usr/sbin/wpa_supplicant – jeśli tam nic nie ma, NetworkManager nie ma # czym zarządzać kartą (także na USB), a `systemctl start wpa_supplicant` # kończy się błędem 203/EXEC. Dlatego przenosimy albo dowiązujemy. local _b _src mkdir -p "$PAGAN_ROOT/usr/sbin" for _b in wpa_supplicant wpa_cli wpa_passphrase; do [[ -e "$PAGAN_ROOT/usr/sbin/$_b" ]] && continue for _src in "$PAGAN_ROOT/usr/local/sbin/$_b" "$PAGAN_ROOT/usr/bin/$_b" \ "$PAGAN_ROOT/sbin/$_b" "$PAGAN_ROOT/bin/$_b"; do [[ -x "$_src" ]] || continue if [[ "$_src" == "$PAGAN_ROOT/usr/bin/$_b" ]]; then # /usr/bin jest w PATH tak samo jak /usr/sbin – dowiązanie # wystarcza i nie psuje innych instalacji. ln -sfn "/usr/bin/$_b" "$PAGAN_ROOT/usr/sbin/$_b" else mv -f "$_src" "$PAGAN_ROOT/usr/sbin/$_b" fi break done done if [[ ! -x "$PAGAN_ROOT/usr/sbin/wpa_supplicant" ]]; then warn " WiFi: brak binarki wpa_supplicant w rootfsie – pomijam konfigurację D-Bus" return 0 fi chmod 0755 "$PAGAN_ROOT/usr/sbin/wpa_supplicant" 2>/dev/null || true [[ -e "$PAGAN_ROOT/usr/sbin/wpa_cli" ]] && chmod 0755 "$PAGAN_ROOT/usr/sbin/wpa_cli" 2>/dev/null || true # 2. Aktywacja przez D-Bus (bez tego NM nie wystartuje wpa_supplicanta). mkdir -p "$PAGAN_ROOT/usr/share/dbus-1/system-services" \ "$PAGAN_ROOT/etc/dbus-1/system.d" \ "$PAGAN_ROOT/usr/lib/systemd/system" \ "$PAGAN_ROOT/etc/wpa_supplicant" cat > "$PAGAN_ROOT/usr/share/dbus-1/system-services/fi.w1.wpa_supplicant1.service" <<'WPAEOF' [D-BUS Service] Name=fi.w1.wpa_supplicant1 Exec=/usr/sbin/wpa_supplicant -u User=root WPAEOF cat > "$PAGAN_ROOT/etc/dbus-1/system.d/wpa_supplicant.conf" <<'WPAEOF' WPAEOF # 3. Jednostka systemd (dodatkowa droga startu, gdyby D-Bus nie aktywował). cat > "$PAGAN_ROOT/usr/lib/systemd/system/wpa_supplicant.service" <<'WPAEOF' [Unit] Description=WPA supplicant Before=network.target After=dbus.service Wants=network.target [Service] Type=dbus BusName=fi.w1.wpa_supplicant1 ExecStart=/usr/sbin/wpa_supplicant -u [Install] WantedBy=multi-user.target Alias=dbus-fi.w1.wpa_supplicant1.service WPAEOF # /etc/wpa_supplicant/wpa_supplicant.conf nie jest wymagany (NM steruje przez # D-Bus), ale wiele narzędzi go oczekuje. if [[ ! -f "$PAGAN_ROOT/etc/wpa_supplicant/wpa_supplicant.conf" ]]; then printf '# Zarządzane przez NetworkManager (config_methods przez D-Bus)\n' \ > "$PAGAN_ROOT/etc/wpa_supplicant/wpa_supplicant.conf" fi ok " wpa_supplicant: /usr/sbin + aktywacja D-Bus dla NetworkManagera" } # Netinstall: backend `pag` w module packages Calamaresa. # # Moduł `netinstall` sam nic nie instaluje – zbiera wybór użytkownika do # globalstorage (packageOperations) i przekazuje go modułowi `packages`. # `packages/main.py` obsługuje backendy apk/apt/dnf/pacman/…, ale nie zna # naszego `pag`. Dopisujemy więc klasę backendu na SAMYM KOŃCU pliku # (po utworzeniu listy backend_managers) i rejestrujemy ją w tej liście. # # Dodatkowo pilnujemy /etc/resolv.conf w systemie docelowym: w rootfsie leży # on po NetworkManagerze z hosta budującego (np. nameserver 192.168.73.85), # więc w chroocie `pag sync` nie mógłby rozwiązać DNS. _install_pag_package_backend() { local _m="$PAGAN_ROOT/usr/lib/calamares/modules/packages/main.py" [[ -f "$_m" ]] || return 0 grep -q 'PAGAN: backend pag' "$_m" 2>/dev/null && return 0 cat >> "$_m" <<'PAGEOF' # PAGAN: backend pag (PaganLinux) – dopisane przez build-pagan.sh class PMPag(PackageManager): backend = "pag" @property def name(self): return "pag" def _ensure_target_dns(self): # W chroocie /etc/resolv.conf bywa nieużywalny (stary wpis z buildera # albo symlink do /run/systemd/resolve, którego w chroocie nie ma), # a bez DNS `pag sync` nie pobierze indeksu. target = libcalamares.globalstorage.value("rootMountPoint") if not target: return dst = os.path.join(target, "etc/resolv.conf") try: if os.path.exists(dst) and os.path.getsize(dst) > 0: with open(dst, "r", errors="ignore") as handle: body = handle.read() if "nameserver" in body: return except OSError: pass for src in ("/run/NetworkManager/resolv.conf", "/etc/resolv.conf", "/run/systemd/resolve/stub-resolv.conf"): try: if os.path.exists(src) and os.path.getsize(src) > 0: shutil.copyfile(os.path.realpath(src), dst) return except OSError: continue try: with open(dst, "w") as handle: handle.write("nameserver 1.1.1.1\nnameserver 8.8.8.8\n") except OSError: pass def update_db(self): self._ensure_target_dns() try: check_target_env_call(["pag", "sync"]) except subprocess.CalledProcessError as exc: libcalamares.utils.warning( "pag sync nie powiódł się ({}) – próbuję instalować mimo to".format(exc.returncode)) def update_system(self): self._ensure_target_dns() check_target_env_call(["pag", "upgrade", "-y"]) def install(self, pkgs, from_local=False): self._ensure_target_dns() check_target_env_call(["pag", "install", "-y"] + pkgs) def remove(self, pkgs): check_target_env_call(["pag", "remove", "-y"] + pkgs) backend_managers.append(("pag", PMPag)) PAGEOF # Wymagane importy: base main.py importuje abc/subprocess/libcalamares, ale # nie os/shutil (używa ich nasz backend). Dodajemy przed „import subprocess”. if ! grep -qE '^import os$' "$_m"; then sed -i '0,/^import subprocess$/s//import os\nimport shutil\nimport subprocess/' "$_m" elif ! grep -qE '^import shutil$' "$_m"; then sed -i '0,/^import os$/s//import os\nimport shutil/' "$_m" fi if python3 -c "import ast,sys; ast.parse(open(sys.argv[1]).read())" "$_m" 2>/dev/null; then ok " Calamares: moduł packages – dodany backend pag (netinstall)" else error "Calamares packages/main.py: patch backendu pag jest niepoprawny składniowo" fi } # Systemd nie ma w rootfsie SWOJEJ polityki D-Bus. # # Bez plików /usr/share/dbus-1/system.d/org.freedesktop.*.conf obowiązuje # domyślna polityka z /usr/share/dbus-1/system.conf: # # ← nikt nie może zająć nazwy # # więc PID1 NIE MOŻE zarejestrować org.freedesktop.systemd1, a logind # org.freedesktop.login1. Skutki (dokładnie widać je w logu startu): # systemd[1]: Got disconnect on API bus. # dbus-daemon: Failed to activate service 'org.freedesktop.systemd1': timed out # pam_systemd: Varlink call io.systemd.Login.CreateSession failed # systemd-logind: Failed to start user service …: Transport endpoint is not connected # → sesje nie powstają → lightdm/XFCE nie startują → system „wisi”. # # `src/core/meson.build` robi install_data('org.freedesktop.systemd1.conf'), # ale w tym buildzie pliki nie trafiły do rootfsu (meson policzył dbuspolicydir # z pkg-config dbus-1). Wstawiamy je wprost ze źródeł systemd — deterministycznie. _ensure_systemd_dbus_policy() { local _pol="$PAGAN_ROOT/usr/share/dbus-1/system.d" local _svc="$PAGAN_ROOT/usr/share/dbus-1/system-services" local _ses="$PAGAN_ROOT/usr/share/dbus-1/services" [[ -d "$PAGAN_ROOT/usr/lib/systemd" ]] || return 0 # Już jest? (idempotencja – nie nadpisujemy, gdy systemd zainstalował sam.) # Sprawdzamy też symlink aktywacji systemd --user z magistrali SESJI – on też # ginął do zagnieżdżonego rootfsu (patrz _merge_nested_rootfs), a bez niego # XFCE przy starcie woła `dbus-update-activation-environment --systemd` i loguje: # „error sending to systemd: … The name org.freedesktop.systemd1 was not # provided by any .service files”. if [[ -f "$_pol/org.freedesktop.systemd1.conf" && -f "$_svc/org.freedesktop.login1.service" \ && -e "$_ses/org.freedesktop.systemd1.service" ]]; then return 0 fi mkdir -p "$_pol" "$_svc" # Źródło: rozpakowane drzewo systemd (SRC_DIR/systemd-*) albo tarball. local _src="" _tb="" _d for _d in "$SRC_DIR"/systemd-* "$SRC_DIR"/build-systemd; do [[ -d "$_d/src/core" ]] && { _src="$_d"; break; } done if [[ -z "$_src" ]]; then for _tb in "$SRC_DIR"/systemd-*.tar.gz "$SRC_DIR"/systemd-*.tar.xz; do [[ -f "$_tb" ]] && break _tb="" done [[ -n "$_tb" ]] || { warn " dbus: brak źródeł systemd – nie mogę dołożyć polityki D-Bus (PID1 nie zajmie nazwy!)"; return 0; } _src="$(mktemp -d)" tar -xf "$_tb" -C "$_src" --strip-components=1 \ --wildcards '*/src/*/org.freedesktop.*.conf' '*/src/*/org.freedesktop.*.service' 2>/dev/null || true if [[ ! -d "$_src/src/core" ]]; then warn " dbus: nie udało się rozpakować polityki D-Bus z $_tb" rm -rf "$_src" return 0 fi fi local _n_conf=0 _n_svc=0 _f for _f in "$_src"/src/*/org.freedesktop.*.conf; do [[ -f "$_f" ]] || continue cp -f "$_f" "$_pol/" && _n_conf=$((_n_conf + 1)) done for _f in "$_src"/src/*/org.freedesktop.*.service; do [[ -f "$_f" ]] || continue cp -f "$_f" "$_svc/" && _n_svc=$((_n_svc + 1)) done # Sprzątanie tylko własnego katalogu tymczasowego. [[ "$_src" == /tmp/* ]] && rm -rf "$_src" # Aktywacja systemd --user z magistrali SESJI (plik instalowany przez systemd # jako symlink do ../system-services/…). Bez niego ostrzeżenie z xinitrc XFCE # (patrz komentarz na początku funkcji). if [[ -f "$_svc/org.freedesktop.systemd1.service" ]]; then mkdir -p "$_ses" ln -sfn ../system-services/org.freedesktop.systemd1.service \ "$_ses/org.freedesktop.systemd1.service" fi if [[ -f "$_pol/org.freedesktop.systemd1.conf" ]]; then ok " dbus: polityka systemd ($_n_conf plików .conf, $_n_svc .service) – PID1 może zająć org.freedesktop.systemd1" else warn " dbus: nie udało się dołożyć org.freedesktop.systemd1.conf – systemd nie zarejestruje się na magistrali" fi } # Udev: baza hwdb (/etc/udev/hwdb.bin). # # Bez niej generator systemd-imds-generator (systemd 261) kończy się błędem: # systemd-imds-generator: Failed to open hwdb: No such file or directory # '/usr/lib/systemd/system-generators/systemd-imds-generator' failed with exit status 1 # hwdb poprawia też rozpoznawanie urządzeń przez udev (klawiatury, tablety, # kamery), więc generujemy ją przy każdym buildzie/respinie. _ensure_hwdb() { [[ -x "$PAGAN_ROOT/usr/bin/systemd-hwdb" ]] || return 0 # Brak źródeł = nie ma z czego zbudować bazy (kompilatory hwdb są w /usr/lib). if [[ ! -d "$PAGAN_ROOT/usr/lib/udev/hwdb.d" && ! -d "$PAGAN_ROOT/etc/udev/hwdb.d" ]]; then return 0 fi mkdir -p "$PAGAN_ROOT/etc/udev" # Najpierw chroot (identyczne środowisko jak w systemie), potem --root. if chroot "$PAGAN_ROOT" /usr/bin/systemd-hwdb update >/dev/null 2>&1 || \ "$PAGAN_ROOT/usr/bin/systemd-hwdb" --root="$PAGAN_ROOT" update >/dev/null 2>&1; then if [[ -s "$PAGAN_ROOT/etc/udev/hwdb.bin" ]]; then ok " hwdb: /etc/udev/hwdb.bin wygenerowane ($(stat -c %s "$PAGAN_ROOT/etc/udev/hwdb.bin") B)" return 0 fi fi warn " hwdb: nie udało się wygenerować /etc/udev/hwdb.bin (generator imds będzie zgłaszał błąd)" } # dbus w tablicy build_final_system buduje się PRZED systemd (brak libsystemd # w pkg-config rootfs) → ENABLE_SYSTEMD=AUTO wyłącza wsparcie systemd i cmake # nie instaluje dbus.socket/dbus.service. To zmuszało nas do niestandardowego # wywołania dbus-daemon (bez aktywacji socketowej), które powodowało wyścigi # i zerwane połączenia („Failed to parse bus message", logind ENOTCONN). # Tu przebudowujemy dbus PO systemd z ENABLE_SYSTEMD=ON → standardowe jednostki # aktywacji socketowej (dbus.socket + dbus.service z cmake). rebuild_dbus_with_systemd() { step "DBUS: PRZEBUDOWA Z WSPARCIEM SYSTEMD (aktywacja socketowa)" # Idempotencja (resume): jeśli binarka wspiera systemd ORAZ jednostki są # poprawne, nie rób nic. (Uwaga: dbus-daemon --help NIE pokazuje tej opcji, # więc sprawdzamy przez strings.) if [[ -f "$PAGAN_ROOT/usr/bin/dbus-daemon" ]] && \ strings "$PAGAN_ROOT/usr/bin/dbus-daemon" | grep -q -- "--systemd-activation" && \ grep -q '^ListenStream=/run/dbus/system_bus_socket$' "$PAGAN_ROOT/usr/lib/systemd/system/dbus.socket" 2>/dev/null; then ok "dbus: aktywacja socketowa już aktywna" return 0 fi # Wymuś przebudowę mimo wpisu resume z pierwszej (niesystemdowej) budowy. sed -i '/^dbus-1.16.2$/d' "$RESUME_FILE" 2>/dev/null || true build_lfs_pkg "dbus" "1.16.2" \ "https://dbus.freedesktop.org/releases/dbus/dbus-1.16.2.tar.xz" \ "SKIP" \ "CC=gcc CXX=g++ cmake -B build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_SYSCONFDIR=/etc -DCMAKE_INSTALL_LOCALSTATEDIR=/var -DDBUS_BUILD_TESTS=OFF -DENABLE_SYSTEMD=ON -DDBUS_SYSTEMD_USER_SERVICE_DIR=/usr/lib/systemd/user && make -C build && make -C build DESTDIR=\$PAGAN_ROOT install" # cmake z dbus 1.16.2 generuje jednostki z PUSTYMI @EXPANDED_BINDIR@/ # @DBUS_SYSTEM_SOCKET@ (bug: te zmienne są zdefiniowane tylko w meson.build; # patrz bus/dbus.service.in i bus/CMakeLists.txt) → nadpisujemy poprawnymi. # System bus MUSI działać jako root (bez User=) – inaczej nie utworzy # /run/dbus i nie przeczyta /etc/dbus-1/system.conf. # Kanoniczne jednostki + symlinki (bez cyklu zależności) ustawia # _ensure_dbus_units() – jedno źródło prawdy, używane też przez finalize_build # i fix-calamares.sh. _ensure_dbus_units if [[ -f "$PAGAN_ROOT/usr/lib/systemd/system/dbus.socket" ]]; then ok "dbus: jednostki systemd (dbus.socket + dbus.service) naprawione" else warn "dbus: brak dbus.socket po przebudowie – sprawdź log budowy dbus" fi } _configure_basic_system() { # /etc/passwd cat > "$PAGAN_ROOT/etc/passwd" <<'EOF' root:x:0:0:root:/root:/bin/bash nobody:x:65534:65534:nobody:/:/bin/false systemd-timesync:x:103:103:systemd Time Synchronization:/:/usr/sbin/nologin systemd-network:x:104:104:systemd Network Management:/:/usr/sbin/nologin systemd-resolve:x:105:105:systemd Resolver:/:/usr/sbin/nologin systemd-oom:x:106:106:systemd Userspace OOM Killer:/:/usr/sbin/nologin systemd-coredump:x:107:107:systemd Core Dumper:/:/usr/sbin/nologin systemd-journal-remote:x:108:108:systemd Journal Remote:/:/usr/sbin/nologin polkitd:x:110:110:polkit PolicyKit daemon:/:/usr/sbin/nologin man:x:12:12:Manuals Reader:/var/cache/man:/usr/sbin/nologin messagebus:x:81:81:system message bus:/:/usr/sbin/nologin EOF # /etc/group cat > "$PAGAN_ROOT/etc/group" <<'EOF' root:x:0: bin:x:1: sys:x:2: kmem:x:3: tty:x:4: tape:x:5: daemon:x:6: floppy:x:7: disk:x:8: lp:x:9: dialout:x:10: audio:x:11: video:x:12: utmp:x:13: usb:x:14: cdrom:x:15: mail:x:34: nogroup:x:65534: wheel:x:97: plugdev:x:98: input:x:99: render:x:100: kvm:x:101: systemd-journal:x:102: systemd-timesync:x:103: systemd-network:x:104: systemd-resolve:x:105: systemd-oom:x:106: systemd-coredump:x:107: systemd-journal-remote:x:108: polkitd:x:110: man:x:12: clock:x:35: sgx:x:974: messagebus:x:81: # Grupy wymagane przez Calamares (users.defaultGroups/sudoersGroup/autologinGroup) # oraz standardowe grupy sprzętowe (BLFS). optical:x:21: scanner:x:22: storage:x:23: power:x:24: users:x:25: autologin:x:26: EOF # /etc/fstab cat > "$PAGAN_ROOT/etc/fstab" <<'EOF' # /etc/fstab /dev/sda1 / ext4 defaults 0 1 proc /proc proc nosuid,noexec,nodev 0 0 sysfs /sys sysfs nosuid,noexec,nodev 0 0 devtmpfs /dev devtmpfs mode=0755,nosuid 0 0 tmpfs /tmp tmpfs defaults 0 0 devpts /dev/pts devpts gid=5,mode=620 0 0 shm /dev/shm tmpfs defaults 0 0 EOF # /etc/systemd/system.conf cat > "$PAGAN_ROOT/etc/systemd/system.conf" <<'EOF' [Manager] DefaultTimeoutStopSec=10s DefaultTimeoutStartSec=10s DefaultStartLimitIntervalSec=10s DefaultStartLimitBurst=5 EOF # /etc/systemd/logind.conf cat > "$PAGAN_ROOT/etc/systemd/logind.conf" <<'EOF' [Login] HandlePowerKey=poweroff HandleSuspendKey=suspend HandleHibernateKey=hibernate HandleLidSwitch=suspend HandleLidSwitchExternalPower=suspend HandleLidSwitchDocked=ignore PowerKeyIgnoreInhibited=no SuspendKeyIgnoreInhibited=no HibernateKeyIgnoreInhibited=no LidSwitchIgnoreInhibited=yes HoldoffTimeoutSec=30s IdleAction=ignore IdleActionSec=30min RuntimeDirectorySize=10% InhibitDelayMaxSec=5 KillUserProcesses=no KillExcludeUsers=root RemoveIPC=yes EOF # dbus: aktywacja socketowa. Gniazdo (dbus.socket) nasłuchuje wcześnie # (sockets.target), a usługa startuje w multi-user.target – NIE w # sockets.target.wants, bo to zamyka cykl zależności: # dbus.service (After=basic.target) → basic.target (After=sockets.target) # → sockets.target (pulls dbus.service) → cykl → zadanie usuwane # i D-Bus nie wstaje (brak polkitd/NetworkManager/lightdm). _ensure_dbus_units # systemd przejmuje kontrolę nad shutdown/halt/reboot/poweroff/init. # (sysvinit przez /sbin → usr/bin nadpisałby symlinki systemd realnymi binarkami) for _ctl in halt poweroff reboot shutdown; do ln -sf systemctl "$PAGAN_ROOT/usr/bin/$_ctl" done ln -sf ../lib/systemd/systemd "$PAGAN_ROOT/usr/bin/init" # Locale UTF-8: Qt (Calamares/KF6) wymaga UTF-8 – bez wygenerowanych locale # Qt ostrzega: "Qt depends on a UTF-8 locale, but has failed to switch". # UWAGA: /usr/lib/locale musi istnieć, inaczej localedef pada z ENOENT. mkdir -p "$PAGAN_ROOT/usr/lib/locale" for _loc in en_US.UTF-8 pl_PL.UTF-8; do chroot "$PAGAN_ROOT" /usr/bin/localedef -i "${_loc%.*}" -f UTF-8 "$_loc" || true done # /etc/hostname echo "pagan" > "$PAGAN_ROOT/etc/hostname" # /etc/locale.conf cat > "$PAGAN_ROOT/etc/locale.conf" <<'EOF' LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 EOF # /etc/vconsole.conf cat > "$PAGAN_ROOT/etc/vconsole.conf" <<'EOF' KEYMAP=pl FONT=Lat2-Terminus16 EOF # Symlink init systemd ln -sf /usr/lib/systemd/systemd "$PAGAN_ROOT/sbin/init" 2>/dev/null || true # Enable podstawowych usług (best-effort, może nie działać przy DESTDIR) chroot "$PAGAN_ROOT" systemctl enable systemd-logind 2>/dev/null || true chroot "$PAGAN_ROOT" systemctl enable systemd-timesyncd 2>/dev/null || true } # ========================= BUDOWA BLFS (PEŁNE BIBLIOTEKI) ========================= build_blfs() { step "BLFS: BIBLIOTEKI I ZALEŻNOŚCI" # Sprawdzamy WYŁĄCZNIE biblioteki targetu LFS w $PAGAN_ROOT/usr/lib # zanim zaczniemy BLFS, żeby nie kontynuować z uszkodzonym ABI. check_target_abi export PATH="$TOOLS_DIR/bin:/usr/bin/core_perl:/usr/bin/vendor_perl:$PATH" export PKG_CONFIG_DIR="" export PKG_CONFIG_PATH="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/lib/x86_64-linux-gnu/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig" export PKG_CONFIG_SYSROOT_DIR="$PAGAN_ROOT" export PKG_CONFIG_LIBDIR="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/lib/x86_64-linux-gnu/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig" export CPPFLAGS="-I$PAGAN_ROOT/usr/include" export LDFLAGS="-L$PAGAN_ROOT/usr/lib -L$PAGAN_ROOT/lib64 -Wl,-rpath-link,$PAGAN_ROOT/usr/lib -Wl,-rpath-link,$PAGAN_ROOT/lib64" # Wykryj architekturę dla cross-file (działa na x86_64, aarch64, armv7l...) local ARCH ARCH=$(uname -m) case "$ARCH" in x86_64|amd64) ARCH=x86_64 ;; aarch64|arm64) ARCH=aarch64 ;; armv7l|armv7*) ARCH=arm ;; esac # Meson cross-file + stub .pc files # Wrapper kompilatora: dodaje -isystem $PAGAN_ROOT/usr/include. Inaczej cross-gcc # rozwiązuje do WEWNĘTRZNEGO limits.h gcc (jego include_next nie sięga # $sysroot/usr/include) i giną makra glibc: LINE_MAX, _POSIX_HOST_NAME_MAX itd. # (env CFLAGS ani c_args z cross-filesa NIE docierają do kompilatora cross – meson # traktuje je per-machine; wrapper to omija.) # rm -f przed zapisem: /tmp bywa sticky, a obcy plik (inny właściciel) zablokuje cat. rm -f /tmp/pagan-cross-gcc /tmp/pagan-cross-g++ /tmp/pagan-cross.ini cat > /tmp/pagan-cross-gcc < itd. # bierzemy z libstdc++ TARGETA (finalny gcc). Wymagana pełna ścieżka -isystem: # katalog multilib (bits/c++config.h) + backward + główny + /usr/include # (sam -isystem /usr/include NIE wystarczy – iostream leży w c++//). _gxx_ver_dir=$(ls -d "$PAGAN_ROOT"/usr/include/c++/*/ 2>/dev/null | head -1) _gxx_ver_dir=${_gxx_ver_dir%/} _gxx_multi="" for _d in "$_gxx_ver_dir"/*/; do [[ -f "${_d}bits/c++config.h" ]] && { _gxx_multi="${_d%/}"; break; } done cat > /tmp/pagan-cross-g++ < /tmp/pagan-cross.ini < rozwiązuje się do # WEWNĘTRZNEGO limits.h gcc (include_next nie sięga katalogów -I) i giną makra # _POSIX_* (glib: „_POSIX_HOST_NAME_MAX undeclared” w ghostutils.c). c_args = ['-isystem', '$PAGAN_ROOT/usr/include'] CROSS_EOF mkdir -p "$PAGAN_ROOT/usr/lib/pkgconfig" "$PAGAN_ROOT/usr/include/sysprof-6" cat > "$PAGAN_ROOT/usr/include/sysprof-6/sysprof-capture.h" <<'SYSPROFH' #ifndef SYSPROF_CAPTURE_H #define SYSPROF_CAPTURE_H #include typedef struct _SysprofCaptureWriter SysprofCaptureWriter; typedef struct _SysprofCaptureReader SysprofCaptureReader; #endif SYSPROFH for stub in iconv sysprof-capture-4; do [[ -f "$PAGAN_ROOT/usr/lib/pkgconfig/$stub.pc" ]] || \ cat > "$PAGAN_ROOT/usr/lib/pkgconfig/$stub.pc" < "$PAGAN_ROOT/usr/lib/pkgconfig/duktape.pc" <<'DUKTAPE' prefix=/usr exec_prefix=${prefix} libdir=${prefix}/lib includedir=${prefix}/include Name: duktape Description: duktape embedded JS engine Version: 2.7.0 Libs: -L${libdir} -lduktape Cflags: -I${includedir} DUKTAPE info " ✓ Meson cross-file + stubs" _pipewire_flag_if_supported() { local opt="$1" val="$2" local opt_file="" if [[ -f meson.options ]]; then opt_file="meson.options" elif [[ -f meson_options.txt ]]; then opt_file="meson_options.txt" fi [[ -z "$opt_file" ]] && return 0 if rg -q "option\\(['\"]${opt}['\"]" "$opt_file"; then printf ' -D%s=%s' "$opt" "$val" fi } _configure_pipewire() { local cross_arg="$1" local flags="" flags+=$(_pipewire_flag_if_supported "man" "disabled") flags+=$(_pipewire_flag_if_supported "session-managers" "[]") flags+=$(_pipewire_flag_if_supported "examples" "disabled") flags+=$(_pipewire_flag_if_supported "tools" "disabled") flags+=$(_pipewire_flag_if_supported "tests" "disabled") sed -i '/spa-acp-tool/,+5d; /test-timer/,+5d; /test-hw-params/,+5d' spa/plugins/alsa/meson.build || true eval "meson setup build $cross_arg --prefix=/usr${flags}" } local xc="--cross-file=/tmp/pagan-cross.ini" # CFLAGS dla CROSS GCC (nie host GCC). UWAGA: -isystem $PAGAN_ROOT/usr/include jest # tu KLUCZOWE: cross-gcc rozwiązuje do WEWNĘTRZNEGO limits.h gcc (jego # include_next nie sięga katalogów -I), przez co giną makra _POSIX_* z glibc # (np. „_POSIX_HOST_NAME_MAX undeclared” w glib). -isystem kieruje łańcuch do # glibcowego limits.h targetu. Meson w cross-buildu bierze c_args z env CFLAGS # (nadpisuje c_args z cross-filesa) – dlatego dodajemy tu, a nie w pagan-cross.ini. export CFLAGS="-O2 -Wno-error=implicit-function-declaration -Wno-error=int-conversion -Wno-error=return-type -Wno-error=maybe-uninitialized -Wno-error=discarded-qualifiers -Wno-error=incompatible-pointer-types -isystem $PAGAN_ROOT/usr/include" # C++ dla pakietów AUTOTOOLS: env CXXFLAGS (pakiety meson dostają ścieżki przez # wrapper pagan-cross-g++). Ścieżki libstdc++ TARGETA wyliczone w bloku wrappera. if [[ -n "$_gxx_ver_dir" && -n "$_gxx_multi" ]]; then export CXXFLAGS="-O2 -Wno-error=return-type -Wno-error=maybe-uninitialized -Wno-error=deprecated-declarations -isystem $_gxx_multi -isystem $_gxx_ver_dir/backward -isystem $_gxx_ver_dir -isystem $PAGAN_ROOT/usr/include" else export CXXFLAGS="-O2 -Wno-error=return-type -Wno-error=maybe-uninitialized -Wno-error=deprecated-declarations" fi export FORCE_UNSAFE_CONFIGURE=1 local libs=( # ======== FUNDAMENTY (glib2/libxml2/atk – muszą być pierwsze!) ======== "glib:2.86.4:https://download.gnome.org/sources/glib/2.86/glib-2.86.4.tar.xz:SKIP:sed -i 's/return _POSIX_HOST_NAME_MAX;/return 255; \/\/ fallback (cross-gcc nie widzi makra _POSIX_HOST_NAME_MAX)/' glib/ghostutils.c && rm -rf build && meson setup build $xc --prefix=/usr -Dselinux=disabled -Dman-pages=disabled -Dlibmount=enabled -Dtests=false -Dintrospection=disabled -Dsysprof=disabled && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # polkit wymaga gio/glib (target) – musi iść PO glib. Cross-build z $xc; # LIBRARY_PATH = target żeby cc.find_library('pam') znalazł libpam z linux-pam. # SONAME jest konieczny: bez niego linker wpisuje absolutne /mnt/pagan/usr/lib/libduktape.so do polkitd. "duktape:2.7.0:https://duktape.org/duktape-2.7.0.tar.xz:SKIP:cd src && gcc -O2 -fPIC -shared -Wl,-soname,libduktape.so -o libduktape.so duktape.c && install -m 755 libduktape.so \$PAGAN_ROOT/usr/lib/libduktape.so && install -m 644 duktape.h \$PAGAN_ROOT/usr/include/duktape.h && install -m 644 duk_config.h \$PAGAN_ROOT/usr/include/duk_config.h" "polkit:127:https://github.com/polkit-org/polkit/archive/127/polkit-127.tar.gz:SKIP:( export LIBRARY_PATH=\"\$PAGAN_ROOT/usr/lib\"; meson setup build $xc --prefix=/usr -Dintrospection=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install )" "libxml2:2.15.1:https://download.gnome.org/sources/libxml2/2.15/libxml2-2.15.1.tar.xz:SKIP:./configure --host=$LFS_TGT --prefix=/usr --without-python --disable-static && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" "atk:2.58.3:https://download.gnome.org/sources/at-spi2-core/2.58/at-spi2-core-2.58.3.tar.xz:SKIP:meson setup build $xc --prefix=/usr -Dintrospection=disabled -Ddocs=false -Dx11=disabled -Ddbus_broker=disabled && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "alsa-lib:1.2.15.3:https://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.15.3.tar.bz2:SKIP:./configure --host=$LFS_TGT --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "libogg:1.3.6:https://downloads.xiph.org/releases/ogg/libogg-1.3.6.tar.xz:SKIP:./configure --host=$LFS_TGT --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "libvorbis:1.3.7:https://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz:SKIP:./configure --host=$LFS_TGT --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "flac:1.5.0:https://github.com/xiph/flac/releases/download/1.5.0/flac-1.5.0.tar.xz:SKIP:./configure --host=$LFS_TGT --prefix=/usr --disable-static --disable-cpplibs && make && make install DESTDIR=\$PAGAN_ROOT" "opus:1.6.1:https://downloads.xiph.org/releases/opus/opus-1.6.1.tar.gz:SKIP:./configure --host=$LFS_TGT --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "pipewire:1.6.0:https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/1.6.0/pipewire-1.6.0.tar.bz2:SKIP:_configure_pipewire \"$xc\" && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install && perl \"\$SCRIPT_DIR/patches/spa-json-core-cxx.pl\" \"\$PAGAN_ROOT/usr/include/spa-0.2/spa/utils/json-core.h\"" # wireplumber potrzebuje Lua (5.4) do modułów. Domyślnie meson ściąga ją # jako subproject-wrap (lua.wrap), ale Pythona mesona nie weryfikuje # certyfikatów w tym środowisku ([SSL: CERTIFICATE_VERIFY_FAILED]). # Podkładamy więc źródło Lua + patch z wrapdb do subprojects/packagecache # przez wget (on działa) i blokujemy sieć mesona (--wrap-mode=nodownload). # Meson i tak zweryfikuje oba pliki swoimi hashami z lua.wrap. "wireplumber:0.5.13:https://gitlab.freedesktop.org/pipewire/wireplumber/-/archive/0.5.13/wireplumber-0.5.13.tar.bz2:SKIP:mkdir -p subprojects/packagecache && wget -q -O subprojects/packagecache/lua-5.4.4.tar.gz 'https'\$'\72''//www.lua.org/ftp/lua-5.4.4.tar.gz' && wget -q -O subprojects/packagecache/lua_5.4.4-1_patch.zip 'https'\$'\72''//wrapdb.mesonbuild.com/v2/lua_5.4.4-1/get_patch' && meson setup build $xc --prefix=/usr --wrap-mode=nodownload && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "nasm:3.01:https://www.nasm.us/pub/nasm/releasebuilds/3.01/nasm-3.01.tar.xz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "sdl2:2.30.7:https://www.libsdl.org/release/SDL2-2.30.7.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" # ======== GRAFIKA 2D/3D ======== "libpng:1.6.55:https://downloads.sourceforge.net/libpng/libpng-1.6.55.tar.xz:SKIP:./configure --host=$LFS_TGT --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" # WITH_JPEG8=1 → SONAME libjpeg.so.8, zgodny z hostem (Arch) i z binarkami # linkowanymi host-first. Bez tego target buduje .62, a część binarek # (gdk-pixbuf/gd/openraw…) zapisuje NEEDED .8 → runtime „cannot open # shared object file: libjpeg.so.8”. Usuwamy też stare .62 z poprzedniej # budowy, żeby nie został mieszany ABI. "libjpeg-turbo:3.1.3:https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.3/libjpeg-turbo-3.1.3.tar.gz:SKIP:rm -f \$PAGAN_ROOT/usr/lib/libjpeg.so \$PAGAN_ROOT/usr/lib/libjpeg.so.62 \$PAGAN_ROOT/usr/lib/libjpeg.so.62.4.0 || true; cmake -B build -DCMAKE_INSTALL_PREFIX=/usr -DWITH_JPEG8=1 -G Ninja && DESTDIR=\$PAGAN_ROOT ninja -C build install" "libtiff:4.7.1:https://download.osgeo.org/libtiff/tiff-4.7.1.tar.gz:SKIP:./configure --host=$LFS_TGT --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "freetype:2.14.1:https://downloads.sourceforge.net/freetype/freetype-2.14.1.tar.xz:SKIP:./configure --host=$LFS_TGT --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "fontconfig:2.17.1:https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/2.17.1/fontconfig-2.17.1.tar.xz:SKIP:./configure --host=$LFS_TGT --prefix=/usr --sysconfdir=/etc --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "harfbuzz:12.3.2:https://github.com/harfbuzz/harfbuzz/releases/download/12.3.2/harfbuzz-12.3.2.tar.xz:SKIP:meson setup build --prefix=/usr -Dintrospection=disabled -Ddocs=disabled && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "fribidi:1.0.16:https://github.com/fribidi/fribidi/releases/download/v1.0.16/fribidi-1.0.16.tar.xz:SKIP:meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # pixman – TWARDA zależność cairo (dependency('pixman-1')). Bez systemowego # pixmana cairo cofa się do subproject-wrappa (pixman.wrap) i pada na # weryfikacji certyfikatów Pythona mesona. Budowane przed cairo. "pixman:0.43.4:https://www.cairographics.org/releases/pixman-0.43.4.tar.gz:SKIP:meson setup build --prefix=/usr -Dtests=disabled -Ddemos=disabled -Dgtk=disabled && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "cairo:1.18.4:https://www.cairographics.org/releases/cairo-1.18.4.tar.xz:SKIP:meson setup build --prefix=/usr -Dxlib=auto -Dxcb=auto && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "pango:1.57.0:https://download.gnome.org/sources/pango/1.57/pango-1.57.0.tar.xz:SKIP:meson setup build --prefix=/usr -Dgtk_doc=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "gdk-pixbuf:2.44.5:https://download.gnome.org/sources/gdk-pixbuf/2.44/gdk-pixbuf-2.44.5.tar.xz:SKIP:meson setup build --prefix=/usr -Dman=false -Dglycin=disabled -Dintrospection=disabled && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "libepoxy:1.5.10:https://github.com/anholt/libepoxy/archive/refs/tags/1.5.10.tar.gz:SKIP:meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "libdrm:2.4.131:https://dri.freedesktop.org/libdrm/libdrm-2.4.131.tar.xz:SKIP:meson setup build --prefix=/usr -Dudev=true && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # ======== WAYLAND (przed mesa/gtk! mesa wymaga wayland-*-pkgconfig, # a X11/xcb w tej fazie NIE istnieje – dlatego mesa/gtk bez platformy x11) ======== "wayland:1.24.0:https://gitlab.freedesktop.org/wayland/wayland/-/releases/1.24.0/downloads/wayland-1.24.0.tar.xz:SKIP:meson setup build --prefix=/usr -Ddocumentation=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "wayland-protocols:1.47:https://gitlab.freedesktop.org/wayland/wayland-protocols/-/releases/1.47/downloads/wayland-protocols-1.47.tar.xz:SKIP:meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "libxkbcommon:1.13.1:https://github.com/lfs-book/libxkbcommon/archive/v1.13.1/libxkbcommon-1.13.1.tar.gz:SKIP:meson setup build --prefix=/usr -Denable-x11=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "mesa:25.3.5:https://mesa.freedesktop.org/archive/mesa-25.3.5.tar.xz:SKIP:( export CFLAGS=\"-DHAVE_THRD_CREATE \${CFLAGS}\"; meson setup build --prefix=/usr -Dgallium-drivers=softpipe -Dvulkan-drivers= -Dplatforms=wayland -Dglx=disabled -Dllvm=disabled && sed -i 's/^timespec_sub_saturate/__attribute__((used)) timespec_sub_saturate/' src/util/timespec.h && sed -i 's|#include \"loader_wayland_helper.h\"|#include \"util/timespec.h\"\\n#include \"loader_wayland_helper.h\"|' src/loader/loader_wayland_helper.c && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install )" # ======== GTK I POZOSTAŁE (wayland; X11 buduje się dopiero w fazie DE) ======== "gtk:3.24.51:https://download.gnome.org/sources/gtk/3.24/gtk-3.24.51.tar.xz:SKIP:meson setup build --prefix=/usr -Dx11_backend=false -Dwayland_backend=true -Dintrospection=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # libinput wymaga libevdev (nie było go w targetcie) "libevdev:1.13.3:https://www.freedesktop.org/software/libevdev/libevdev-1.13.3.tar.xz:SKIP:meson setup build --prefix=/usr -Ddocumentation=disabled && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" "mtdev:1.1.6:https://bitmath.org/code/mtdev/mtdev-1.1.6.tar.gz:SKIP:./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" "libinput:1.31.0:https://gitlab.freedesktop.org/libinput/libinput/-/archive/1.31.0/libinput-1.31.0.tar.gz:SKIP:meson setup build --prefix=/usr -Ddocumentation=false -Dtests=false -Dlibwacom=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # ======== SIEC ======== "libndp:1.9:http://libndp.org/files/libndp-1.9.tar.gz:SKIP:./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" # NetworkManager: `systemdsystemunitdir` i `dbus_conf_dir` podajemy JAWNIE. # Domyślnie NM bierze je z pkg-config, a pkg-config przy # PKG_CONFIG_SYSROOT_DIR zwraca ścieżki JUŻ z sysrootem (/mnt/pagan/usr/...). # Meson dokłada wtedy jeszcze DESTDIR → jednostki i polityka D-Bus lądują # w /mnt/pagan/mnt/pagan/usr/... i w live nie ma NetworkManager.service # (brak DHCP, brak sieci) ani org.freedesktop.NetworkManager.conf # (NM nie zajmie nazwy na D-Bus). Uwaga: dbussystemservicedir NIE jest # opcją mesonową NM, dlatego plik aktywacji dispatchera i tak dokłada # _ensure_network_manager (oraz _merge_nested_rootfs po każdym pakiecie). "networkmanager:1.56.0:https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/releases/1.56.0/downloads/NetworkManager-1.56.0.tar.xz:SKIP:meson setup build --prefix=/usr --sysconfdir=/etc --localstatedir=/var -Dsystemdsystemunitdir=/usr/lib/systemd/system -Ddbus_conf_dir=/usr/share/dbus-1/system.d -Dwrap_mode=nodownload -Dovs=false -Dteamdctl=false -Diwd=false -Dofono=false -Dpolkit=false -Dselinux=false -Dlibaudit=no -Dcrypto=null -Dmodem_manager=false -Dlibpsl=false -Dconcheck=false -Dnm_cloud_setup=false -Dnbft=false -Dnmcli=true -Dnmtui=false -Dppp=false -Dwext=false -Dintrospection=false -Ddocs=false -Dtests=no -Dvapi=false -Dqt=false && ninja -C build && DESTDIR=$PAGAN_ROOT ninja -C build install" "iw:6.9:https://www.kernel.org/pub/software/network/iw/iw-6.9.tar.xz:SKIP:make && make install DESTDIR=\$PAGAN_ROOT" "libical:3.0.20:https://github.com/libical/libical/releases/download/v3.0.20/libical-3.0.20.tar.gz:SKIP:( export ICU_ROOT=\"\$PAGAN_ROOT/usr\"; cmake -B build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DICAL_BUILD_DOCS=OFF -DLIBICAL_BUILD_TESTING=OFF -DICAL_GLIB=OFF -DSHARED_ONLY=ON && cmake --build build && DESTDIR=\$PAGAN_ROOT cmake --install build )" "bluez:5.79:https://www.kernel.org/pub/linux/bluetooth/bluez-5.79.tar.xz:SKIP:./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --enable-library --disable-manpages && make && make install DESTDIR=\$PAGAN_ROOT" "libarchive:3.8.5:https://github.com/libarchive/libarchive/releases/download/v3.8.5/libarchive-3.8.5.tar.xz:SKIP:./configure --prefix=/usr --disable-static && make && make install DESTDIR=\$PAGAN_ROOT" "libnspr:4.38.2:https://archive.mozilla.org/pub/nspr/releases/v4.38.2/src/nspr-4.38.2.tar.gz:SKIP:cd nspr && ./configure --prefix=/usr --disable-static --enable-64bit && make && make install DESTDIR=$PAGAN_ROOT" ) # duktape: nowsza receptura instaluje też duk_config.h (wymagany przez # u konsumentów, np. polkit). Stare przebiegi mają wpis w resume bez duk_config.h # → wymuś przebudowę, żeby poprawka faktycznie dotarła do targeta. if [[ -f "$PAGAN_ROOT/usr/include/duktape.h" && ! -f "$PAGAN_ROOT/usr/include/duk_config.h" ]]; then warn "duktape w targetcie bez duk_config.h – usuwam z resume (przebudowa)" sed -i '/^duktape-2\.7\.0$/d' "$RESUME_FILE" 2>/dev/null || true fi # Poprzednia wersja była zbudowana bez SONAME i powodowała absolutny DT_NEEDED. if [[ -f "$PAGAN_ROOT/usr/lib/libduktape.so" ]] && \ ! readelf -d "$PAGAN_ROOT/usr/lib/libduktape.so" 2>/dev/null | grep -q 'SONAME.*libduktape\.so'; then warn "duktape bez SONAME – usuwam z resume (przebudowa, inaczej polkitd dostaje ścieżkę builda)" sed -i '/^duktape-2\.7\.0$/d' "$RESUME_FILE" 2>/dev/null || true fi # Napraw także stare polkitd już zapisane w rootfsie. Bez tego resume pominie # pakiet mimo poprawionego duktape i wadliwy absolutny DT_NEEDED pozostanie. if [[ -x "$PAGAN_ROOT/usr/lib/polkit-1/polkitd" ]] && \ readelf -d "$PAGAN_ROOT/usr/lib/polkit-1/polkitd" 2>/dev/null | grep -q '/mnt/pagan/'; then warn "polkitd zawiera ścieżkę /mnt/pagan – wymuszam przebudowę polkit" sed -i '/^polkit-127$/d' "$RESUME_FILE" 2>/dev/null || true fi # Pakiety, którym błąd DESTDIR+sysroot zabrał pliki do zagnieżdżonego rootfsu # ($PAGAN_ROOT/mnt/pagan, zob. _merge_nested_rootfs) – a kasowanie $PAGAN_ROOT/mnt # w środku builda (przy librsvg) gubiło je bezpowrotnie. Poprawka # (_merge_nested_rootfs po KAŻDYM pakiecie) ratuje tylko nowo budowane pakiety, # a resume by je pominął – dlatego przy braku pliku zdejmujemy pakiet z resume # i przebudowujemy go. Warunki są celowo zawężone (sentinel binarki + brak pliku), # żeby nie wymuszać przebudowy na zdrowym rootfsie. # NetworkManager nie potrzebuje tu przebudowy: jego brakujące pliki pisze # _ensure_network_manager (wierne kopie plików z NetworkManager-1.56.0). if [[ -f "$PAGAN_ROOT/usr/bin/bluetoothctl" && \ ! -f "$PAGAN_ROOT/usr/lib/systemd/system/bluetooth.service" ]]; then warn "bluez bez bluetooth.service (błąd DESTDIR+sysroot) – usuwam z resume (przebudowa)" sed -i '/^bluez-5\.79$/d' "$RESUME_FILE" 2>/dev/null || true fi if [[ -e "$PAGAN_ROOT/usr/lib/libpipewire-0.3.so.0" && \ ! -f "$PAGAN_ROOT/usr/lib/systemd/user/pipewire.service" ]]; then warn "pipewire bez jednostek user (błąd DESTDIR+sysroot) – usuwam z resume (przebudowa)" sed -i '/^pipewire-1\.6\.0$/d' "$RESUME_FILE" 2>/dev/null || true fi if [[ -x "$PAGAN_ROOT/usr/bin/wireplumber" && \ ! -f "$PAGAN_ROOT/usr/lib/systemd/user/wireplumber.service" ]]; then warn "wireplumber bez jednostek user (błąd DESTDIR+sysroot) – usuwam z resume (przebudowa)" sed -i '/^wireplumber-0\.5\.13$/d' "$RESUME_FILE" 2>/dev/null || true fi # wayland/wayland-protocols (i inni) generują .pc z pkgdatadir=\${pc_sysrootdir}\${datarootdir}/… # – przy wgryzionym prefix=$PAGAN_ROOT/usr do .pc ORAZ PKG_CONFIG_SYSROOT_DIR=$PAGAN_ROOT # daje to podwójną ścieżkę (/mnt/pagan/mnt/pagan/...), jak przy libxkbcommon. .pc są # już absolutne (prefix=$PAGAN_ROOT/usr), więc \${pc_sysrootdir} jest zbędny → usuń. _heal_pc_sysrootdir() { local _pc while IFS= read -r _pc; do sed -i 's/\${pc_sysrootdir}//g' "$_pc" done < <(grep -rl '\${pc_sysrootdir}' \ "$PAGAN_ROOT/usr/lib/pkgconfig" "$PAGAN_ROOT/usr/share/pkgconfig" 2>/dev/null) } _heal_pc_sysrootdir ui_set_total "${#libs[@]}" for pkg_info in "${libs[@]}"; do name="${pkg_info%%:*}"; rest="${pkg_info#*:}" ver="${rest%%:*}"; rest="${rest#*:}" config="${rest##*:}"; rest_no_cfg="${rest%:*}" sha="${rest_no_cfg##*:}" url="${rest_no_cfg%:*}" build_lfs_pkg "$name" "$ver" "$url" "$sha" "$config" # świeżo zainstalowane .pc też mogą nieść \${pc_sysrootdir} (wayland itd.) _heal_pc_sysrootdir done # Nie pozwól wygenerować obrazu z nieprzenośnym DT_NEEDED. Taki błąd # objawia się dopiero po instalacji jako kod 127 polkitd. if [[ -x "$PAGAN_ROOT/usr/lib/polkit-1/polkitd" ]] && \ readelf -d "$PAGAN_ROOT/usr/lib/polkit-1/polkitd" 2>/dev/null | grep -q '/mnt/pagan/'; then error "polkitd ma absolutną zależność do /mnt/pagan – przebudowa polkit/duktape nie naprawiła linkowania" fi ok "BLFS – kompletny zestaw bibliotek!" } # ========================= BUDOWA KERNELA ========================= build_kernel() { step "BUDOWA KERNELA ${KERNEL_VERSION}" export PATH="$TOOLS_DIR/bin:$PATH" unset PKG_CONFIG_PATH PKG_CONFIG_SYSROOT_DIR PKG_CONFIG_LIBDIR unset CFLAGS CXXFLAGS CPPFLAGS local kernel_src="$SRC_DIR/linux-${KERNEL_VERSION}" if [[ ! -d "$kernel_src" ]]; then download_pkg "$KERNEL_URL" "$SRC_DIR/linux-${KERNEL_VERSION}.tar.xz" "SKIP" tar -xf "$SRC_DIR/linux-${KERNEL_VERSION}.tar.xz" -C "$SRC_DIR" fi pushd "$kernel_src" > /dev/null info " ⚙ Konfiguracja jądra (defconfig + tweaks Pagan)..." # Generuj pelna konfiguracje domyslna dla x86_64 (zawsze aktualna dla wersji kernela) make defconfig || error "Kernel defconfig failed" # === PaganOS overrides === ./scripts/config --set-str CONFIG_LOCALVERSION "-pagan" ./scripts/config --disable CONFIG_LOCALVERSION_AUTO ./scripts/config --disable CONFIG_WERROR # Preemption i scheduling (dla desktop/pulpitu) ./scripts/config --enable CONFIG_PREEMPT ./scripts/config --disable CONFIG_PREEMPT_VOLUNTARY ./scripts/config --set-val CONFIG_HZ 1000 ./scripts/config --enable CONFIG_SCHED_HRTICK # Moduly ./scripts/config --enable CONFIG_MODULES ./scripts/config --enable CONFIG_MODULE_UNLOAD ./scripts/config --enable CONFIG_MODULE_FORCE_UNLOAD ./scripts/config --enable CONFIG_MODVERSIONS ./scripts/config --disable CONFIG_MODULE_SIG # EFI (obowiazkowe dla nowoczesnego sprzetu) ./scripts/config --enable CONFIG_EFI ./scripts/config --enable CONFIG_EFI_STUB # === Pamięć masowa === # NVMe (typowy dysk realnego sprzętu i nowych VM) – bez tego instalator # pokazuje pustą listę dysków (lsblk nie widzi /dev/nvme*) na NVMe. ./scripts/config --enable CONFIG_BLK_DEV_NVME ./scripts/config --disable CONFIG_NVME_FC ./scripts/config --disable CONFIG_NVME_TCP # exFAT: domyślny FS partycji danych Ventoy – bez tego initramfs nie odczyta # ISO z pendrive Ventoy (mount exfat kończy się błędem). # (Samo CONFIG_EXFAT_FS włączamy niżej, razem z IOCHARSET=utf8.) ./scripts/config --disable CONFIG_EXFAT_DONT_MOUNT_VOLMGS ./scripts/config --enable CONFIG_EXFAT_DEFAULT_IOCHARSET_NLS # KVM / wirtualizacja ./scripts/config --enable CONFIG_VIRTUALIZATION ./scripts/config --enable CONFIG_KVM ./scripts/config --enable CONFIG_KVM_INTEL ./scripts/config --enable CONFIG_KVM_AMD # WiFi (jako moduly) ./scripts/config --enable CONFIG_WIRELESS ./scripts/config --module CONFIG_CFG80211 ./scripts/config --module CONFIG_MAC80211 ./scripts/config --module CONFIG_IWLWIFI ./scripts/config --module CONFIG_IWLMVM ./scripts/config --module CONFIG_ATH9K ./scripts/config --module CONFIG_ATH10K ./scripts/config --module CONFIG_RTW88 ./scripts/config --module CONFIG_BRCMFMAC # USB Wi-Fi (karty na USB – bez tych modułów instalator/live nie widzi # zewnętrznych kart, bo nie powstaje w ogóle interfejs wlan*): # • ath9k_htc – Atheros AR9271/AR7010 (np. TP-Link TL-WN722N v1) # • rtl8187 / rtl8xxxu / rtl8192cu – Realtek USB (8187L, 8188EU, 8192CU, 8723AU) # • mt7601u / mt76x0u / mt76x2u – MediaTek MT7601U/MT7610U/MT7612U (tanie karty) # • rt2x00 – Ralink/MediaTek RT2500USB/RT2800USB/RT3070 (USB) # • mwifiex_usb + brcmfmac (USB) – Marvell/Broadcom ./scripts/config --module CONFIG_ATH9K_HTC ./scripts/config --module CONFIG_RTL8187 ./scripts/config --module CONFIG_RTL8XXXU ./scripts/config --module CONFIG_RTLWIFI ./scripts/config --module CONFIG_RTL8192CU ./scripts/config --module CONFIG_MT7601U ./scripts/config --module CONFIG_MT76x0U ./scripts/config --module CONFIG_MT76x2U ./scripts/config --module CONFIG_RT2X00 ./scripts/config --module CONFIG_RT2500USB ./scripts/config --module CONFIG_RT73USB ./scripts/config --module CONFIG_RT2800USB # Uwaga: RT2800USB_RT33XX/35XX/53XX to BOOL (pod-opcje rt2800usb), nie tristate – # wartość 'm' jest nieprawidłowa (warning kconfig „symbol value 'm' invalid”). ./scripts/config --enable CONFIG_RT2800USB_RT33XX ./scripts/config --enable CONFIG_RT2800USB_RT35XX ./scripts/config --enable CONFIG_RT2800USB_RT53XX ./scripts/config --module CONFIG_MWIFIEX ./scripts/config --module CONFIG_MWIFIEX_USB ./scripts/config --enable CONFIG_BRCMFMAC_USB # === Live ISO (squashfs + overlay + ISO9660 + loop) – WYMAGANE w jądrze # (nie jako moduł), żeby initramfs mógł zamontować system live bez ładowania # modułów. ./scripts/config --enable CONFIG_BLK_DEV_LOOP ./scripts/config --enable CONFIG_SQUASHFS ./scripts/config --enable CONFIG_SQUASHFS_XZ ./scripts/config --enable CONFIG_SQUASHFS_ZSTD ./scripts/config --enable CONFIG_OVERLAY_FS ./scripts/config --enable CONFIG_ISO9660_FS ./scripts/config --enable CONFIG_JOLIET ./scripts/config --enable CONFIG_ZISOFS ./scripts/config --enable CONFIG_UDF_FS ./scripts/config --enable CONFIG_EFI_FS ./scripts/config --enable CONFIG_EFI_PARTITION ./scripts/config --enable CONFIG_NLS_UTF8 ./scripts/config --enable CONFIG_NLS_CODEPAGE_437 # exFAT – partycja danych Ventoy (domyślnie exFAT). Bez tego initramfs nie # zamontuje nośnika i nie znajdzie ISO („nie znaleziono nośnika LIVE”). ./scripts/config --enable CONFIG_EXFAT_FS ./scripts/config --set-str CONFIG_EXFAT_DEFAULT_IOCHARSET utf8 # UAS – część zewnętrznych USB3/SATA w obudowach zgłasza się tylko przez UAS. ./scripts/config --enable CONFIG_USB_UAS ./scripts/config --enable CONFIG_DEVTMPFS ./scripts/config --enable CONFIG_DEVTMPFS_MOUNT ./scripts/config --enable CONFIG_TMPFS # XATTR/ACL – wymagane dla live ISO i tmpfs: # • CONFIG_SQUASHFS_XATTR: bez niego kernel loguje # "SQUASHFS error: Xattrs in filesystem, these will be ignored" # (mksquashfs domyślnie zapisuje xatrry do obrazu) # • CONFIG_TMPFS_POSIX_ACL: bez niego systemd-tmpfiles nie może ustawić # ACL na /var/log/journal (overlay/tmpfs) → "Failed to parse ACL ... Invalid argument" ./scripts/config --enable CONFIG_SQUASHFS_XATTR ./scripts/config --enable CONFIG_TMPFS_POSIX_ACL # === Dyski dla QEMU/KVM, sprzętu fizycznego i instalatora === # Wszystko jest wbudowane w kernel (nie jako moduł), ponieważ live ISO # musi wykryć dysk przed uruchomieniem Calamares. # Podstawa SCSI i narzędzia działające na urządzeniach SG. ./scripts/config --enable CONFIG_SCSI ./scripts/config --enable CONFIG_BLK_DEV_SD ./scripts/config --enable CONFIG_CHR_DEV_SG # Kontrolery SATA/IDE – QEMU z if=ide, płyty główne i większość komputerów. ./scripts/config --enable CONFIG_ATA ./scripts/config --enable CONFIG_SATA_AHCI ./scripts/config --enable CONFIG_ATA_PIIX # NVMe – współczesne dyski SSD. ./scripts/config --enable CONFIG_NVME_CORE ./scripts/config --enable CONFIG_BLK_DEV_NVME # VirtIO – QEMU/KVM/Proxmox. CONFIG_VIRTIO_BLK tworzy /dev/vda. ./scripts/config --enable CONFIG_VIRTIO ./scripts/config --enable CONFIG_VIRTIO_PCI ./scripts/config --enable CONFIG_VIRTIO_BLK ./scripts/config --enable CONFIG_VIRTIO_NET ./scripts/config --enable CONFIG_VIRTIO_CONSOLE # Systemy plików używane przez instalator, EFI i zamontowane partycje. ./scripts/config --enable CONFIG_EXT4_FS ./scripts/config --enable CONFIG_FAT_FS ./scripts/config --enable CONFIG_VFAT_FS ./scripts/config --enable CONFIG_ISO9660_FS ./scripts/config --enable CONFIG_SQUASHFS # === DRM: maszyny wirtualne + fallback ekranu === # Bez tych opcji Xorg/modesetting nie ma czym sterować ekranu: # • QEMU/KVM: domyślna karta "std" (bochs-drm), QXL (Spice), Cirrus; # bez CONFIG_DRM_VIRTIO_GPU Xorg loguje "virtio_gpu: driver missing". # • CONFIG_DRM_SIMPLEDRM: fallback z framebuffera EFI – ekran działa nawet # gdy brak natywnego sterownika GPU (typowy „pada Xorg" na nieznanym sprzęcie). # • VirtualBox (vboxvideo) i VMware (vmwgfx). # • amdgpu: najpopularniejsze GPU desktopu (firmware jest w linux-firmware). ./scripts/config --enable CONFIG_DRM_VIRTIO_GPU ./scripts/config --module CONFIG_DRM_BOCHS ./scripts/config --module CONFIG_DRM_QXL ./scripts/config --module CONFIG_DRM_CIRRUS_QEMU ./scripts/config --module CONFIG_DRM_VMWGFX ./scripts/config --module CONFIG_DRM_VBOXVIDEO ./scripts/config --enable CONFIG_DRM_SIMPLEDRM # SYSFB_SIMPLEFB: zamienia framebuffer EFI (GOP) na platformowe # "simple-framebuffer" – bez tego simpledrm nie dostaje urządzenia. ./scripts/config --enable CONFIG_SYSFB_SIMPLEFB # amdgpu jako moduł (firmware jest w linux-firmware); FBDEV-EMULATION – żeby # xf86-video-fbdev też miał czym sterować (/dev/fb0); wymaga CONFIG_FB. ./scripts/config --module CONFIG_DRM_AMDGPU ./scripts/config --enable CONFIG_FB ./scripts/config --enable CONFIG_DRM_FBDEV_EMULATION # === Utwardzanie jądra (security) === # KASLR (RANDOMIZE_BASE) już jest z defconfig; dokładamy resztę: # • STACKPROTECTOR_STRONG/FORTIFY_SOURCE/HARDENED_USERCOPY – ochrona # przed przepełnieniami bufora w jądrze/usermode. # • SLAB_FREELIST_* + SHUFFLE_PAGE_ALLOCATOR + INIT_ON_* – utrudniają # exploity wykorzystujące niezerowaną pamięć/kinetykę alokatora. # • SECURITY_YAMA – kernel.yama.ptrace_scope (ogranicza ptrace). # • SECURITY_DMESG_RESTRICT – kernel.dmesg_restrict. ./scripts/config --enable CONFIG_STACKPROTECTOR_STRONG ./scripts/config --enable CONFIG_FORTIFY_SOURCE ./scripts/config --enable CONFIG_HARDENED_USERCOPY ./scripts/config --enable CONFIG_SLAB_FREELIST_RANDOM ./scripts/config --enable CONFIG_SLAB_FREELIST_HARDENED ./scripts/config --enable CONFIG_SHUFFLE_PAGE_ALLOCATOR ./scripts/config --enable CONFIG_INIT_ON_ALLOC_DEFAULT_ON ./scripts/config --enable CONFIG_INIT_ON_FREE_DEFAULT_ON ./scripts/config --enable CONFIG_STRICT_KERNEL_RWX ./scripts/config --enable CONFIG_STRICT_MODULE_RWX ./scripts/config --enable CONFIG_SECURITY_YAMA ./scripts/config --enable CONFIG_SECURITY_DMESG_RESTRICT # Uzupelnij wszystkie brakujace opcje domyslynymi # === Systemy plików dla instalatora i systemu docelowego === # Calamares oferuje ext4/xfs/btrfs – bez CONFIG_BTRFS_FS/CONFIG_XFS_FS # mount -t btrfs/xfs pada („unknown filesystem type”) i instalacja wysypuje # się przy wyborze tych FS. =y (wbudowane) – root na btrfs/xfs musi wstać # zanim initramfs załaduje moduły. ./scripts/config --enable CONFIG_BTRFS_FS ./scripts/config --enable CONFIG_XFS_FS # === LUKS / device-mapper (szyfrowanie dysku przez Calamares) === # =y (wbudowane), bo initramfs musi odblokować root ZANIM załaduje moduły. ./scripts/config --enable CONFIG_BLK_DEV_DM ./scripts/config --enable CONFIG_DM_CRYPT # Kryptografia używana przez LUKS2 (aes-xts-plain64 + sha256/sha512) ./scripts/config --enable CONFIG_CRYPTO_XTS ./scripts/config --enable CONFIG_CRYPTO_AES ./scripts/config --enable CONFIG_CRYPTO_SHA256 ./scripts/config --enable CONFIG_CRYPTO_SHA512 # Uzupelnij wszystkie brakujace opcje domyslynymi info " ⚙ olddefconfig (uzupelnianie configu)..." make olddefconfig || error "Kernel olddefconfig failed" info " Kompilacja jądra..." make $MAKEFLAGS bzImage || error "Kernel build failed" make $MAKEFLAGS modules || error "Kernel modules build failed" info " Instalacja jądra..." make INSTALL_MOD_PATH="$PAGAN_ROOT" modules_install || error "Modules install failed" cp arch/x86/boot/bzImage "$PAGAN_ROOT/boot/vmlinuz-${KERNEL_VERSION}-pagan" cp System.map "$PAGAN_ROOT/boot/System.map-${KERNEL_VERSION}-pagan" cp .config "$PAGAN_ROOT/boot/config-${KERNEL_VERSION}-pagan" # Symlink ln -sf "vmlinuz-${KERNEL_VERSION}-pagan" "$PAGAN_ROOT/boot/vmlinuz-linux" ln -sf "System.map-${KERNEL_VERSION}-pagan" "$PAGAN_ROOT/boot/System.map" popd > /dev/null ok "Kernel ${KERNEL_VERSION} zbudowany!" } # ========================= WIRELESS-REGDB (regulatory.db dla WiFi) ========================= # cfg80211 z CONFIG_CFG80211_REQUIRE_SIGNED_REGDB=y (domyślne od 5.19) WYMAGA # podpisanego regulatory.db w /usr/lib/firmware. Bez niego kernel loguje: # "Direct firmware load for regulatory.db failed with error -2" # "cfg80211: failed to load regulatory.db" # a WiFi nie ma prawidłowego domyślnego kraju → problemy z kanałami 5 GHz. install_wireless_regdb() { step "WIRELESS-REGDB (regulatory.db)" local fw_dir="$PAGAN_ROOT/usr/lib/firmware" mkdir -p "$fw_dir" # Już jest? (np. ręcznie dołożone) – nic nie rób if [[ -s "$fw_dir/regulatory.db" && -s "$fw_dir/regulatory.db.p7s" ]]; then ok "regulatory.db + .p7s już obecne" return 0 fi # 1) Najszybsze: z hosta buildera local src for src in /usr/lib/firmware/regulatory.db /lib/firmware/regulatory.db; do if [[ -s "$src" && -s "${src}.p7s" ]]; then cp -a "$src" "${src}.p7s" "$fw_dir/" 2>/dev/null && { ok "regulatory.db + .p7s (z hosta: $src)" return 0 } fi done # 2) Pobierz wireless-regdb z kernel.org (host-independently) local _ver="${WIRELESS_REGDB_VERSION:-2026.05.30}" local _url="https://www.kernel.org/pub/software/network/wireless-regdb/wireless-regdb-${_ver}.tar.xz" local _arch="$SRC_DIR/wireless-regdb-${_ver}.tar.xz" local _dir="$SRC_DIR/wireless-regdb-${_ver}" download_pkg "$_url" "$_arch" "SKIP" rm -rf "$_dir" && mkdir -p "$_dir" tar -xf "$_arch" -C "$_dir" --strip-components=1 2>/dev/null || { warn "Nie udało się rozpakować wireless-regdb – WiFi bez regulatory.db" return 0 } if [[ -s "$_dir/regulatory.db" ]]; then install -Dm644 "$_dir/regulatory.db" "$fw_dir/regulatory.db" install -Dm644 "$_dir/regulatory.db.p7s" "$fw_dir/regulatory.db.p7s" 2>/dev/null || true ok "regulatory.db + .p7s zainstalowane (wireless-regdb-${_ver})" return 0 fi warn "Nie udało się zdobyć regulatory.db – WiFi może nie działać (cfg80211)" } # ========================= PAGAN-WIFI (konsolowy asystent Wi-Fi, PL/EN) ========================= # Pomocnik dla LIVE/instalatora i systemu bez DE: wykrywa kartę Wi-Fi i łączy # z siecią WPA/WPA2. Woli NetworkManager (nmcli – używa go też instalator GUI), # a gdy go nie ma – wpa_supplicant + dhcpcd/udhcpc. Dwujęzyczny (PL/EN wg $LANG). install_pagan_wifi() { step "PAGAN-WIFI (konsolowy asystent Wi-Fi)" cat > "$PAGAN_ROOT/usr/bin/pagan-wifi" <<'WIFIEOF' #!/bin/bash # pagan-wifi – połącz z siecią Wi-Fi (WPA/WPA2) – komunikaty PL/EN wg $LANG # Użycie: sudo pagan-wifi [SSID] [hasło] (puste = zapyta) set -u is_pl() { [[ "${LANG:-C}" == pl* ]]; } msg() { if is_pl; then echo "$1"; else echo "$2"; fi; } IFACE=$(ip -o link 2>/dev/null | sed -n 's/^[0-9]*: \(w[^:@]*\):.*/\1/p' | head -n1) if [[ -z "$IFACE" ]]; then msg "[!] Nie znaleziono żadnego interfejsu Wi-Fi (wlan*/wlp*)." \ "[!] No Wi-Fi interface found (wlan*/wlp*)." msg " Sprawdź: lsusb | grep -i -E 'wireless|realtek|mediatek|atheros'" \ " Check: lsusb | grep -i -E 'wireless|realtek|mediatek|atheros'" msg " oraz: dmesg | grep -i firmware (brak firmware = karta niewidoczna)" \ " and: dmesg | grep -i firmware (missing firmware = card invisible)" exit 1 fi msg "[*] Znaleziono interfejs: $IFACE" "[*] Interface found: $IFACE" SSID="${1:-}"; PASS="${2:-}" if [[ -z "$SSID" ]]; then read -r -p "$(msg 'SSID: ' 'SSID: ')" SSID fi if [[ -z "${2+x}" && -z "$PASS" ]]; then read -r -s -p "$(msg 'Hasło (puste = sieć otwarta): ' 'Password (empty = open network): ')" PASS echo "" fi [[ -n "$SSID" ]] || { msg "[!] Brak SSID." "[!] No SSID."; exit 1; } ip link set "$IFACE" up 2>/dev/null # 1) NetworkManager (jak instalator GUI) if command -v nmcli >/dev/null 2>&1; then if [[ -n "$PASS" ]]; then nmcli dev wifi connect "$SSID" password "$PASS" else nmcli dev wifi connect "$SSID" fi if [[ $? -eq 0 ]]; then msg "[+] Połączono przez NetworkManager." "[+] Connected via NetworkManager." exit 0 fi msg "[.] nmcli nie połączył – próbuję wpa_supplicant..." "[.] nmcli failed – trying wpa_supplicant..." fi # 2) wpa_supplicant (+ dhcp) command -v wpa_supplicant >/dev/null 2>&1 || { msg "[!] Brak wpa_supplicant – doinstaluj: pag install wpa_supplicant iw" \ "[!] wpa_supplicant missing – install: pag install wpa_supplicant iw" exit 1 } _CFG=/tmp/pagan-wpa.conf rm -f "$_CFG" if [[ -n "$PASS" ]]; then wpa_passphrase "$SSID" "$PASS" > "$_CFG" else printf 'network={\n ssid="%s"\n key_mgmt=NONE\n}\n' "$SSID" > "$_CFG" fi pkill -f "wpa_supplicant -B -i $IFACE" 2>/dev/null wpa_supplicant -B -i "$IFACE" -c "$_CFG" 2>/dev/null sleep 2 if command -v dhcpcd >/dev/null 2>&1; then dhcpcd "$IFACE" elif command -v udhcpc >/dev/null 2>&1; then udhcpc -i "$IFACE" -q elif command -v systemctl >/dev/null 2>&1; then systemctl restart systemd-networkd 2>/dev/null fi msg "[+] Gotowe. Test: ping -c2 1.1.1.1" "[+] Done. Test: ping -c2 1.1.1.1" WIFIEOF chmod 755 "$PAGAN_ROOT/usr/bin/pagan-wifi" ok "pagan-wifi zainstalowany (/usr/bin/pagan-wifi, PL/EN)" } # ========================= STEROWNIKI NVIDIA (open-gpu-kernel-modules) ========================= # Buduje moduły NVIDIA z oficjalnego open-source repo NVIDIA # (https://github.com/NVIDIA/open-gpu-kernel-modules) przeciw zbudowanemu kernelowi. install_nvidia_drivers() { step "INSTALACJA STEROWNIKÓW NVIDIA (open-gpu-kernel-modules)" local nv_ver="${NVIDIA_VERSION:-main}" local nv_url="https://github.com/NVIDIA/open-gpu-kernel-modules/archive/refs/heads/${nv_ver}.tar.gz" local nv_arch="$SRC_DIR/nvidia-open-gpu-kernel-modules-${nv_ver}.tar.gz" local nv_dir="$SRC_DIR/build-nvidia-open" # Kernel źródła muszą być zbudowane (po build_kernel) if [[ ! -f "$SRC_DIR/linux-${KERNEL_VERSION}/.config" ]]; then warn "Brak zbudowanego kernela – pomijam NVIDIA (zbuduj kernel najpierw)" return 0 fi download_pkg "$nv_url" "$nv_arch" "SKIP" || return 0 rm -rf "$nv_dir" mkdir -p "$nv_dir" tar -xf "$nv_arch" -C "$nv_dir" --strip-components=1 2>/dev/null || { warn "Rozpakowanie NVIDIA nieudane"; return 0; } pushd "$nv_dir" > /dev/null info " Budowa modułów NVIDIA (to potrwa)..." # Czyścimy flagi z build_blfs (LDFLAGS -Wl,-rpath-link,$PAGAN_ROOT/usr/lib trafia # w Kbuild do ld WPROST -> "ld: unrecognized option '-Wl,-rpath-link,...'") if ( unset LDFLAGS CFLAGS CXXFLAGS CPPFLAGS; \ make -j"$PAGAN_JOBS" modules \ SYSSRC="$SRC_DIR/linux-${KERNEL_VERSION}" \ SYSOUT="$SRC_DIR/linux-${KERNEL_VERSION}" ) 2>/tmp/nvidia-build.log; then info " Instalacja modułów NVIDIA do systemu..." # UWAGA na DESTDIR vs INSTALL_MOD_PATH: to modules_install z Kbuild, a on # używa INSTALL_MOD_PATH (DESTDIR jest ignorowany!). Wcześniej przekazywaliśmy # DESTDIR="$PAGAN_ROOT", więc moduły NVIDIA lądowały w /usr/lib/modules/... # HOSTA (i tam je widzieliśmy: /usr/lib/modules/7.2.5-pagan/), a w ISO nie # było ICH WCALE – na realnym PC z NVIDIA nie dało się uruchomić Xorga # akcelerowanego (brak nvidia.ko). DEPMOD=true, żeby nie ruszać modułów # hosta; depmod w rootfsie robi hook pakietu kernel / pagan-initramfs. ( unset LDFLAGS CFLAGS CXXFLAGS CPPFLAGS; \ make modules_install \ SYSSRC="$SRC_DIR/linux-${KERNEL_VERSION}" \ SYSOUT="$SRC_DIR/linux-${KERNEL_VERSION}" \ INSTALL_MOD_PATH="$PAGAN_ROOT/usr" \ INSTALL_MOD_STRIP=1 \ DEPMOD=true ) 2>/tmp/nvidia-install.log || \ warn "NVIDIA modules_install nieudane" if [[ -n "$(find "$PAGAN_ROOT/usr/lib/modules/${KERNEL_VERSION}-pagan" -name 'nvidia.ko*' 2>/dev/null | head -n1)" ]]; then ok "NVIDIA open-gpu-kernel-modules zbudowane i zainstalowane (usr/lib/modules/${KERNEL_VERSION}-pagan)" # Same moduły w obrazie to za mało – nikt ich nie załaduje: # * kernel NIE ma nouveau (CONFIG_DRM_NOUVEAU is not set), # * w ISO nie ma sterownika userspace (DDX „nvidia”), który normalnie # robi modprobe, # * bez reguł udev nie powstają węzły /dev/nvidia*, /dev/nvidia-uvm… # Dokładamy więc: reguły udev (ten sam plik co pakiet `nvidia` z repo – # jedna definicja, bez konfliktów przy późniejszej instalacji) oraz regułę # AUTOLOADU dla kart NVIDIA (PCI vendor 10de, klasa VGA/3D). # Efekt: karta dostaje KMS przez nvidia-drm i pulpit działa na modesetting; # akceleracja GL dochodzi po zainstalowaniu pakietu `nvidia` z repo # (jego 20-nvidia.conf przełącza Xorg na DDX nvidia). local _nvrules="$SCRIPT_DIR/recipes/gui/nvidia/nvidia.rules" install -dm0755 "$PAGAN_ROOT/usr/lib/udev/rules.d" if [[ -f "$_nvrules" ]]; then install -Dm0644 "$_nvrules" "$PAGAN_ROOT/usr/lib/udev/rules.d/60-nvidia.rules" else warn " brak $_nvrules – węzły /dev/nvidia* mogą się nie utworzyć" fi cat > "$PAGAN_ROOT/usr/lib/udev/rules.d/99-pagan-nvidia-autoload.rules" <<'NVEOF' # PaganLinux: automatyczne załadowanie sterownika NVIDIA (open kernel modules) # na kartach NVIDIA. Powód: kernel nie ma nouveau, a w obrazie (ISO/instalacja) # nie ma DDX „nvidia”, który normalnie robi modprobe – bez tej reguły moduły # nvidia.ko/nvidia-drm.ko leżą w /usr/lib/modules i nikt ich nie używa. # modeset=1 → nvidia-drm daje KMS (Xorg/modesetting działa bez userspace). # WYŁĄCZENIE: usuń ten plik (lub `pag remove nvidia` po instalacji userspace). ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", RUN+="/usr/sbin/modprobe nvidia-drm modeset=1" ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", RUN+="/usr/sbin/modprobe nvidia-drm modeset=1" NVEOF info " NVIDIA: reguły udev (/dev/nvidia*) + autoload dla kart 10de" else warn "NVIDIA: brak nvidia.ko w rootfsie po instalacji – sprawdź /tmp/nvidia-install.log" fi else warn "NVIDIA modules build nieudane – pomijam (log: /tmp/nvidia-build.log)" fi popd > /dev/null } # ========================= INSTALACJA XFCE ========================= # Minimalny stos X dla instalatora (tryb BAZA – live bez środowiska graficznego). # Calamares/GUI startujemy przez startx (profile.d/pagan-live.sh → .xinitrc), # więc live musi mieć Xorg + xinit. # Lista pakietów = sprawdzona warstwa X11 (te same wersje co stos Qt6/KF6). install_x_installer() { step "INSTALACJA X11 (DLA INSTALATORA GUI – TRYB BAZA)" # Środowisko builda jak przy budowie Qt6/KF6: hostowe narzędzia pierwsze, ale # pkg-config/LD_LIBRARY_PATH muszą widzieć libki staged w $PAGAN_ROOT. export PATH="$TOOLS_DIR/bin:$PATH" export PKG_CONFIG_PATH="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig:$PAGAN_ROOT/usr/lib/x86_64-linux-gnu/pkgconfig:$PAGAN_ROOT/usr/lib64/pkgconfig" local _old_ld="${LD_LIBRARY_PATH:-}" export LD_LIBRARY_PATH="/usr/lib:$PAGAN_ROOT/usr/lib${_old_ld:+:$_old_ld}" local xorg_url="https://xorg.freedesktop.org/archive/individual" local x_autotools="./configure --prefix=/usr --disable-static && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" build_lfs_pkg "xorgproto" "2025.1" \ "$xorg_url/proto/xorgproto-2025.1.tar.gz" "SKIP" \ "meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" build_lfs_pkg "libXau" "1.0.12" "$xorg_url/lib/libXau-1.0.12.tar.gz" "SKIP" "$x_autotools" build_lfs_pkg "libXdmcp" "1.1.5" "$xorg_url/lib/libXdmcp-1.1.5.tar.gz" "SKIP" "$x_autotools" build_lfs_pkg "libxcb" "1.17.0" "$xorg_url/lib/libxcb-1.17.0.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "libX11" "1.8.13" "$xorg_url/lib/libX11-1.8.13.tar.xz" "SKIP" "$x_autotools" # libXext / libXrender / libXss – Tk (python/tkinter) linkuje je, gdy configure # wykryje X na HOŚCIE builda. Bez nich w rootfs import tkinter w chrocie pada: # „ImportError: libXss.so.1: cannot open shared object file”. Budujemy je do # rootfs PRZED Tk, żeby runtime (chroot) miał komplet zależności libtk. build_lfs_pkg "libXext" "1.3.6" "$xorg_url/lib/libXext-1.3.6.tar.xz" "SKIP" "$x_autotools" # libXrender/libXss – runtime'owe zależności Tk (python/tkinter). Build ze źródeł # potyka się o GCC 16 (stary X.org: Glyph.c – „expected ';' before 'elt'”, błąd # istnieje też z -std=gnu17). Tk i tak linkował się przeciw HOSTOWYM libkom, # a host ma ten sam glibc (2.44/x86_64) – więc kopiujemy działające biblioteki # z hosta do rootfs (SONAME libXrender.so.1 / libXss.so.1 wystarcza runtime'owo). for _xlib in libXrender.so.1 libXss.so.1; do if [[ ! -e "$PAGAN_ROOT/usr/lib/$_xlib" && -e "/usr/lib/$_xlib" ]]; then cp -a /usr/lib/${_xlib}* "$PAGAN_ROOT/usr/lib/" 2>/dev/null || true [[ -e "$PAGAN_ROOT/usr/lib/$_xlib" ]] && info " ✓ $_xlib (skopiowane z hosta)" fi if [[ ! -e "$PAGAN_ROOT/usr/lib/$_xlib" ]]; then warn " ⚠ brak $_xlib w rootfs i na hoście – tkinter może nie zadziałać" fi done # libXft + PRZEBUDOWA Tk z obsługą Xft – bez tego Tk (python/tkinter) używa # X core fontów i nie ma polskich znaków (ą/ę/ł/ż): fonty DejaVu (TTF) działają # tylko przez Xft/fontconfig. Bazowy Tk budował się przed X11 (configure: xft=no); # tu mamy już libX11, więc dorabiamy libXft i przebudowujemy Tk (klucz -xft2 = nowy wpis resume). build_lfs_pkg "libXft" "2.3.9" "$xorg_url/lib/libXft-2.3.9.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "tk" "8.6.18-xft2" "https://downloads.sourceforge.net/tcl/tk8.6.18-src.tar.gz" "SKIP" \ "cd unix && ./configure --prefix=/usr --enable-xft && make && make install DESTDIR=\$PAGAN_ROOT" if [[ -x "$PAGAN_ROOT/usr/bin/python3" ]]; then if chroot "$PAGAN_ROOT" /usr/bin/python3 -c 'import tkinter' >/dev/null 2>&1; then info " ✓ tkinter import OK (Tk z Xft)" else warn " ⚠ tkinter nie importuje się w chrocie – sprawdź ldd $PAGAN_ROOT/usr/lib/libtk8.6.so (brak libek X w rootfs?)" fi fi build_lfs_pkg "libxkbfile" "1.1.3" "$xorg_url/lib/libxkbfile-1.1.3.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "xkbcomp" "1.4.7" "$xorg_url/app/xkbcomp-1.4.7.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "xauth" "1.1.3" "$xorg_url/app/xauth-1.1.3.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "xinit" "1.4.2" "$xorg_url/app/xinit-1.4.2.tar.xz" "SKIP" \ "./configure --prefix=/usr --with-xkbdefaultdir=/usr/share/X11/xkb && make && make install DESTDIR=\$PAGAN_ROOT" # xkeyboard-config: instalacja chce /usr/share/X11/xkb jako SYMLINK do # xkeyboard-config-2/xkb – po przerwanej przebudowie zostaje zwykły katalog # → „Destination '.../X11/xkb' already exists and is not a symlink”. # Analogicznie usuwamy realny plik pkgconfig/xkeyboard-config.pc – mezony robi # z niego symlink do xkeyboard-config-2.pc (stary „Fix .pc” potrafił go zniszczyć). rm -rf "$PAGAN_ROOT/usr/share/X11/xkb" rm -f "$PAGAN_ROOT/usr/share/pkgconfig/xkeyboard-config.pc" build_lfs_pkg "xkeyboard-config" "2.48" \ "$xorg_url/data/xkeyboard-config/xkeyboard-config-2.48.tar.xz" "SKIP" \ "meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" build_lfs_pkg "xorg-server" "21.1.24" \ "$xorg_url/xserver/xorg-server-21.1.24.tar.xz" "SKIP" \ "./configure --prefix=/usr --enable-glamor --enable-dri3 --enable-xcsecurity && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" build_lfs_pkg "xf86-input-libinput" "1.5.0" \ "$xorg_url/driver/xf86-input-libinput-1.5.0.tar.xz" "SKIP" \ "./configure --prefix=/usr --with-sdkdir=/usr/include/xorg && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" ok "X11 dla instalatora GUI zainstalowane (xorg-server + xinit)" } # Cache motywów ikon dla wszystkich motywów z index.theme w rootfs. GTK na świeżo # zamontowanym squashfs (overlay) potrafi nie znaleźć ikon bez icon-theme.cache. _refresh_icon_caches() { local _t _dir _rc for _t in "$PAGAN_ROOT"/usr/share/icons/*/; do [ -d "$_t" ] || continue [ -f "$_t/index.theme" ] || continue _dir="$(basename "$_t")" local _err _rc=0 _err=$(chroot "$PAGAN_ROOT" env -u LD_LIBRARY_PATH /usr/bin/gtk-update-icon-cache -f -q \ "/usr/share/icons/$_dir" 2>&1) || _rc=1 if [[ $_rc -eq 0 ]]; then info " ✓ icon-theme.cache: $_dir" else warn " ⚠ gtk-update-icon-cache nie zadziałało dla $_dir – ${_err%%$'\n'*}" fi done } # Włącza usługi systemowe potrzebne pulpitowi. Symlinki piszemy wprost – # `systemctl enable` w chroot bez PID 1 potrafi nie zadziałać (ten sam wzorzec, # co przy polkit/NetworkManagerze). Idempotentne. _ensure_desktop_units() { local _us="$PAGAN_ROOT/usr/lib/systemd/system" local _w="$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants" local _u mkdir -p "$_w" # upower.service → xfce4-power-manager: stan baterii/zasilania (bez niego # loguje „up_client_get_devices failed: The name # org.freedesktop.UPower was not provided by any .service # files”). UWAGA: to NIE usuwa ostrzeżenia Calamaresa # „Could not inhibit sleep” – ono dotyczy starszego # org.freedesktop.PowerManagement.Inhibit, które zapewnia # menedżer zasilania SESJI (xfce4-power-manager). # udisks2.service → montowanie nośników (backend menedżera nośników) for _u in upower.service udisks2.service; do [[ -f "$_us/$_u" ]] || continue ln -sfn "/usr/lib/systemd/system/$_u" "$_w/$_u" done } # Dodatki pulpitu z repo PAG + naprawa bibliotek z rozjechanym ABI. # # To nie są zależności BUDOWY, tylko braki W OBRAZIE, które wyszły dopiero # w sesji live (log serial z QEMU). Każdy wpis ma konkretny dowód z tego logu: # # * xorg-xrdb – /etc/xdg/xfce4/xinitrc:80 „xrdb: command not found” # (XFCE ładuje Xresources; bez xrdb część ustawień X nie działa) # * upower – xfce4-power-manager: „up_client_get_devices failed: # The name org.freedesktop.UPower was not provided by any # .service files” (stan baterii/zasilania) # * libnotify – dostarcza `notify-send` (skrypty, instalator) # * xfce4-notifyd – bez niego libnotify kończy „Failed to connect to proxy” # i żadne powiadomienie nie dociera do użytkownika # * udisks2 – warstwa montowania nośników (backend menedżera nośników) # # Dodatkowo NAPRAWA ABI: w rootfsie potrafią zostać biblioteki z wcześniejszych # przebiegów (resume), zbudowane w innym środowisku niż reszta obrazu: # * libgsf-1.so.114 → brak xmlNanoHTTPClose → „Failed to load plugin # tumbler-odf-thumbnailer.so” (libxml2 2.15 nie eksportuje już nanohttp) # * libavcodec.so.63 → brak rsvg_handle_render_document → „Failed to load plugin # tumbler-ffmpeg-thumbnailer.so” (librsvg 2.40.21 nie ma API 2.46+) # Wersje z repo PAG są spójne z tym obrazem (zweryfikowane `nm -D`), więc # wymuszamy reinstalację (`pag install -f`). Warunek zostaje na stałe – gdyby # rozjazd wrócił, build naprawi się sam. install_desktop_extras() { step "DODATKI PULPITU + NAPRAWA ABI (repo PAG)" if [[ ! -x "$PAGAN_ROOT/usr/bin/pag" ]]; then warn " brak /usr/bin/pag w rootfs – pomijam dodatki pulpitu" return 0 fi local _p _ok=0 _fail=0 local -a _want=() # Ogólne (każde DE na X11): [[ -x "$PAGAN_ROOT/usr/bin/xrdb" ]] || _want+=(xorg-xrdb) [[ -x "$PAGAN_ROOT/usr/bin/upower" ]] || _want+=(upower) [[ -x "$PAGAN_ROOT/usr/bin/notify-send" ]] || _want+=(libnotify) [[ -x "$PAGAN_ROOT/usr/bin/udisksctl" ]] || _want+=(udisks2) # Demon powiadomień tylko dla XFCE – Deepin/Budgie mają własne, a obecność # xfce4-notifyd (autostart/aktywacja D-Bus) mogłaby im podebrać nazwę # org.freedesktop.Notifications na magistrali. # UWAGA: pakiet z repo instaluje demona w libexecdir – /usr/lib/xfce4/notifyd/ # xfce4-notifyd – a w /usr/bin jest tylko xfce4-notifyd-config. Sprawdzamy oba # miejsca (wcześniej sentinel na /usr/bin/xfce4-notifyd dawał fałszywy alarm # „brak powiadomień” mimo poprawnie zainstalowanego pakietu). if [[ "${PAGAN_DE,,}" == "xfce" && \ ! -x "$PAGAN_ROOT/usr/bin/xfce4-notifyd" && \ ! -x "$PAGAN_ROOT/usr/lib/xfce4/notifyd/xfce4-notifyd" ]]; then _want+=(xfce4-notifyd) fi for _p in "${_want[@]:-}"; do [[ -n "$_p" ]] || continue if _pag_install "$_p"; then ok " ✓ z repo: $_p"; ((_ok++)) || true else warn " ✗ nie udało się doinstalować: $_p (sprawdź: pag search $_p)" ((_fail++)) || true fi done if [[ ${#_want[@]} -eq 0 ]]; then info " wszystkie dodatki pulpitu już są w rootfs" fi # Naprawa rozjechanego ABI (szczegóły w komentarzu nad funkcją). local _abi=0 if _lib_missing_symbol "/usr/lib/libgsf-1.so.114*" "xmlNanoHTTPClose"; then warn " libgsf: rozjazd ABI (brak xmlNanoHTTPClose) – reinstalacja z repo" _pag_install libgsf -f && ((_abi++)) || true fi if _lib_missing_symbol "/usr/lib/libavcodec.so.63*" "rsvg_handle_render_document"; then warn " libavcodec: rozjazd ABI (brak rsvg_handle_render_document) – reinstalacja z repo" _pag_install ffmpeg -f && ((_abi++)) || true fi if [[ $_abi -gt 0 ]]; then ok " naprawiono biblioteki z rozjechanym ABI: $_abi (tumbler: ODF + ffmpeg)" fi _ensure_desktop_units if [[ $_fail -eq 0 ]]; then ok " dodatki pulpitu gotowe (doinstalowano: $_ok)" fi } # Uzupełnienie TOOLCHAINU DOCELOWEGO o binutils. # # DLACZEGO: ISO buduje gcc (LFS rozdz. 6), ale binutils wchodzi tylko jako „pass1” # do /tools – w /usr/bin NIE MA `ld`, `as`, `objcopy`, `nm`, `readelf`. Efekt: # na zainstalowanym systemie nie da się nic skompilować, a tego wymaga DKMS # (pakiet `nvidia-dkms` buduje moduły NVIDIA u użytkownika) i każda budowa ze # źródeł – `pag install nvidia-dkms` kończyłby się błędem linkowania (brak ld). # # Repo PAG ma binutils 2.47 (w środku ld/as/objcopy/nm/readelf; wymaga maks. # GLIBC_2.38, więc działa na glibc 2.44 z tego obrazu – sprawdzone objdump -T). # DOCELOWO: zbudować binutils natywnie w obrazie (jak gcc) i wyłączyć ten krok. install_target_toolchain_extras() { step "TOOLCHAIN DOCELOWY (binutils)" if [[ -x "$PAGAN_ROOT/usr/bin/ld" ]]; then info " binutils już w obrazie (ld obecny)" return 0 fi if _pag_install binutils; then ok " ✓ binutils z repo (ld/as/objcopy) – DKMS i budowa ze źródeł zadziałają" else warn " ✗ binutils nie doinstalowane – DKMS na docelowym systemie nie zadziała (brak linkera ld)" fi } install_xfce() { step "INSTALACJA XFCE" export PATH="$TOOLS_DIR/bin:$PATH" export PKG_CONFIG_PATH="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig" # Target libs first for rpath-link: host pangoft2 is ABI-newer than target # pango, causing symbol mismatch when host pangoft2 resolves against target pango. export LDFLAGS="-L$PAGAN_ROOT/usr/lib -L/usr/lib -Wl,-rpath-link,$PAGAN_ROOT/usr/lib -Wl,-rpath-link,/usr/lib" # Hostowe binarki budowane w trakcie XFCE (g-ir-scanner/introspection itp.) linkują # się z libkami ROOTFS (np. libxklavier.so.16) i MUSZĄ je znaleźć w runtime – bez # tego pada: „error while loading shared libraries: libxklavier.so.16”. Kolejność # HOST-PIERWSZY (jak przy budowie Qt6/KF6): hostowe libc/libm wygrywają, brakujące targetowe # libki dobierane są z rootfs. local _old_ld="${LD_LIBRARY_PATH:-}" export LD_LIBRARY_PATH="/usr/lib:$PAGAN_ROOT/usr/lib${_old_ld:+:$_old_ld}" # Xorg # UWAGA: receptura MUSI mieć pełny „make install DESTDIR=…”, inaczej # build_lfs_pkg sam odtwarza „make && make install” na HOŚCIE (prefix=/usr) # i nadpisuje pliki systemu builda zamiast rootfs. build_lfs_pkg "xorg-server" "21.1.13" \ "https://www.x.org/releases/individual/xserver/xorg-server-21.1.13.tar.xz" \ "SKIP" \ "./configure --prefix=/usr --enable-glamor --enable-dri3 --enable-xcsecurity --disable-systemd-logind && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # Klawiatura X11: xkbcomp + xkeyboard-config (+ setxkbmap). Bez nich X serwer # pada przy starcie: „/usr/bin/xkbcomp: No such file … Failed to activate # virtual core keyboard” (Fatal server error). local xorg_url="https://xorg.freedesktop.org/archive/individual" build_lfs_pkg "xkbcomp" "1.4.7" \ "$xorg_url/app/xkbcomp-1.4.7.tar.xz" "SKIP" \ "./configure --prefix=/usr && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" build_lfs_pkg "setxkbmap" "1.3.4" \ "$xorg_url/app/setxkbmap-1.3.4.tar.xz" "SKIP" \ "./configure --prefix=/usr && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # xkeyboard-config: po przerwanej przebudowie /usr/share/X11/xkb bywa zwykłym # katalogiem, a meson-install chce tam SYMLINK → czyścimy przed instalacją. # (także realny pkgconfig/xkeyboard-config.pc – ma być symlinkiem). rm -rf "$PAGAN_ROOT/usr/share/X11/xkb" rm -f "$PAGAN_ROOT/usr/share/pkgconfig/xkeyboard-config.pc" build_lfs_pkg "xkeyboard-config" "2.48" \ "$xorg_url/data/xkeyboard-config/xkeyboard-config-2.48.tar.xz" "SKIP" \ "meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # startx/xinit + xauth – bez nich profile.d/pagan-live.sh nie ma czym # podnieść X w live („startx” nie istnieje) i live spada do TTY. build_lfs_pkg "xauth" "1.1.3" \ "$xorg_url/app/xauth-1.1.3.tar.xz" "SKIP" \ "./configure --prefix=/usr && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" build_lfs_pkg "xinit" "1.4.2" \ "$xorg_url/app/xinit-1.4.2.tar.xz" "SKIP" \ "./configure --prefix=/usr --with-xkbdefaultdir=/usr/share/X11/xkb && make && make install DESTDIR=\$PAGAN_ROOT" # iceauth – wymagany przez ICE w sesji X11 (xfce4-session/xfwm4 bez niego: # „/usr/bin/iceauth: No such file” → „Authentication Rejected” i brak sesji). build_lfs_pkg "iceauth" "1.0.11" \ "$xorg_url/app/iceauth-1.0.11.tar.xz" "SKIP" \ "./configure --prefix=/usr && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # Xorg drivers # xf86-input-libinput instaluje libinput-properties.h do $(sdkdir). Domyślnie # sdkdir bierze z xorg-server.pc, a nasza pętla .pc ma prefix="$PAGAN_ROOT/usr", # więc sdkdir byłby już z rootfs → DESTDIR doklejałby go DRUGI raz # (/mnt/pagan/mnt/pagan/...). Wymuszamy sdkdir bezwzględnie na /usr/include/xorg # (nagłówka wymaga xfce4-settings). build_lfs_pkg "xf86-input-libinput" "1.4.0" \ "https://www.x.org/releases/individual/driver/xf86-input-libinput-1.4.0.tar.xz" \ "SKIP" \ "./configure --prefix=/usr --with-sdkdir=/usr/include/xorg && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" build_lfs_pkg "xf86-video-fbdev" "0.5.1" \ "https://www.x.org/releases/individual/driver/xf86-video-fbdev-0.5.1.tar.xz" \ "SKIP" \ "./configure --prefix=/usr && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # Cairo z BLFS budowany z -Dxlib=auto → bez X11 w rootfs nie powstał # cairo-xlib.h, a GDK-X11 (GTK poniżej) go wymaga. X11 już jest w rootfs, # więc przebudowujemy cairo z xlib=enabled (usuwamy marker resume). if [[ -f "$RESUME_FILE" ]]; then sed -i '/^cairo-1\.18\.4$/d' "$RESUME_FILE" 2>/dev/null || true fi build_lfs_pkg "cairo" "1.18.4" \ "https://www.cairographics.org/releases/cairo-1.18.4.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr -Dxlib=enabled -Dxcb=auto && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" info " ✓ Cairo przebudowany z backendem Xlib (cairo-xlib.h dostępny)" # GTK w BLFS budowany jest z -Dx11_backend=false → w rootfs NIE MA gdk/gdkx.h, # a libwnck i reszta XFCE (DE X11) tego wymagają. Teraz X11 jest już w rootfs # (xorg-server powyżej), więc przebudowujemy GTK z backendem X11 (usuwamy marker # resume, żeby build_lfs_pkg nie pominął przebudowy). if [[ -f "$RESUME_FILE" ]]; then sed -i '/^gtk-3\.24\.51$/d' "$RESUME_FILE" 2>/dev/null || true fi build_lfs_pkg "gtk" "3.24.51" \ "https://download.gnome.org/sources/gtk/3.24/gtk-3.24.51.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr -Dx11_backend=true -Dwayland_backend=true -Dintrospection=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" info " ✓ GTK3 przebudowany z backendem X11 (gdk/gdkx.h dostępny)" # SVG dla ikon: Adwaita (domyślny motyw GTK/XFCE) jest SVG-owy, a gdk-pixbuf # sam SVG nie dekoduje → xfce4-session ginie „Wnck:ERROR: default_icon_at_size: # assertion (base)”. Budujemy libcroco (zależność) + librsvg 2.40.21 (ostatnia # wersja C/autotools, bez toolchainu Rusta). build_lfs_pkg "libcroco" "0.6.13" \ "https://download.gnome.org/sources/libcroco/0.6/libcroco-0.6.13.tar.xz" \ "SKIP" \ "./configure --prefix=/usr --disable-static && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" build_lfs_pkg "librsvg" "2.40.21" \ "https://download.gnome.org/sources/librsvg/2.40/librsvg-2.40.21.tar.xz" \ "SKIP" \ "sed -i '/#include /i #include ' rsvg-private.h && sed -i 's/rsvg_xml_noerror (void \\*data, xmlErrorPtr error)/rsvg_xml_noerror (void *data, const xmlError *error)/' rsvg-css.c && ./configure --prefix=/usr --disable-introspection --disable-gtk-doc && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # Loader gdk-pixbuf z librsvg potrafi wylądować w zduplikowanej ścieżce # (DESTDIR + prefix z .pc rootfs: /mnt/pagan/mnt/pagan/...) – przenosimy go. # Uwaga: sprawdzamy NAJPIERW właściwy katalog docelowy (przy resume/buildzie # sprzed poprawek loader mógł być już przeniesiony), a dopiero potem szukamy # zabłąkanego pliku gdziekolwiek w rootfs (nie tylko w $PAGAN_ROOT/mnt). local _stray_svg _gl_dir _have_svg=0 for _gl_dir in "$PAGAN_ROOT"/usr/lib/gdk-pixbuf-2.0/*/loaders; do [[ -f "$_gl_dir/libpixbufloader-svg.so" ]] && _have_svg=1 done if [[ $_have_svg -eq 0 ]]; then _stray_svg=$(find "$PAGAN_ROOT" -name 'libpixbufloader-svg.so' 2>/dev/null | head -n1) if [[ -n "$_stray_svg" ]]; then for _gl_dir in "$PAGAN_ROOT"/usr/lib/gdk-pixbuf-2.0/*/loaders; do [[ -d "$_gl_dir" ]] && cp -a "$_stray_svg" "$_gl_dir/libpixbufloader-svg.so" done _have_svg=1 fi fi if [[ $_have_svg -eq 1 ]]; then info " ✓ librsvg: loader SVG w gdk-pixbuf (libpixbufloader-svg.so)" else warn " ⚠ librsvg: brak loadera SVG w gdk-pixbuf (SVG/wektory nie będą renderowane)" fi # UWAGA: NIE kasujemy tu $PAGAN_ROOT/mnt. Wcześniej było tu `rm -rf` # „na śmieci po librsvg”, ale w tym momencie w zagnieżdżonym rootfsie leżą # już pliki WIELU innych pakietów (m.in. NetworkManager, bluez, pipewire) – # kasowanie zabierało je bezpowrotnie i to właśnie dlatego live nie miał # sieci. Scalanie (idempotentne) robi teraz _merge_nested_rootfs. _merge_nested_rootfs # === Ikony pulpitu: komplet loaderów gdk-pixbuf + cache ===================== # Objaw: xfce4-session ginie z SIGABRT – „Wnck:ERROR: …default_icon_at_size: # assertion failed (base)”, a panel/xfwm4 ostrzegają „Could not find the icon # '…'. The 'hicolor' theme was not found either”. Sedno problemu: w wariancie # XFCE rootfs NIE MA ŻADNEGO motywu ikon – brak hicolor-icon-theme (index.theme # dla /usr/share/icons/{hicolor,locolor}) i brak Adwaita (ikony stockowe GTK). # GTK bez index.theme nie traktuje katalogów /usr/share/icons jako motywu (pliki # ikon XFCE fizycznie tam są, ale są „niewidoczne”), więc ikony panelu/xfwm4 się # nie ładują, a libwnck dostaje NULL i robi twardy assert → SIGABRT sesji. # hicolor-icon-theme – index.theme + katalogi kontekstów; dopiero z nim GTK # widzi hicolor (i locolor) jako motyw zapasowy i znajduje ikony org.xfce.*. build_lfs_pkg "hicolor-icon-theme" "0.18" \ "https://icon-theme.freedesktop.org/releases/hicolor-icon-theme-0.18.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # Adwaita – pełny motyw ikon (stock GTK/panel/xfwm4/libwnck). Uwaga: 47.0 # buduje się mesonem i instaluje z DESTDIR (do rootfs, NIE na hosta). build_lfs_pkg "adwaita-icon-theme" "47.0" \ "https://download.gnome.org/sources/adwaita-icon-theme/47/adwaita-icon-theme-47.0.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # gdk-pixbuf: loadery png/jpeg są (od 2.42) WBUDOWANE w libgdk_pixbuf # („Builtin loaders: png jpeg”) – NIE istnieją jako libpixbufloader-png.so. # Heurystyka „brak .so = brak loadera” zawsze przebudowywała gdk-pixbuf bez # żadnego efektu; sprawdzamy realną dostępność: moduł .so LUB symbol # fill_vtable w bibliotece rootfs. local _gp_lib _gp_ok=1 _m _gp_lib="$(ls "$PAGAN_ROOT"/usr/lib/libgdk_pixbuf-2.0.so.0 2>/dev/null | head -n1)" for _m in png jpeg; do if ! ls "$PAGAN_ROOT"/usr/lib/gdk-pixbuf-2.0/*/loaders/libpixbufloader-$_m.so >/dev/null 2>&1; then if [[ -z "$_gp_lib" ]] || ! strings "$_gp_lib" 2>/dev/null | grep -q "_gdk_pixbuf__${_m}_fill_vtable"; then _gp_ok=0 break fi fi done if [[ $_gp_ok -eq 0 ]]; then info " Przebudowa gdk-pixbuf (brak loadera png/jpeg – ani moduł .so, ani wbudowanie)..." sed -i '/^gdk-pixbuf-2\.44\.5$/d' "$RESUME_FILE" 2>/dev/null || true build_lfs_pkg "gdk-pixbuf" "2.44.5" \ "https://download.gnome.org/sources/gdk-pixbuf/2.44/gdk-pixbuf-2.44.5.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr -Dman=false -Dglycin=disabled -Dintrospection=disabled && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" info " ✓ gdk-pixbuf przebudowany (png/jpeg loadery)" fi # Obcy loader SVG bywa skopiowany pod złą nazwą (libpixbufloader_svg.so), a # gdk-pixbuf-query-loaders wykrywa moduły tylko jako libpixbufloader-*.so → # poprawiamy nazwę (lub usuwamy duplikat) i generujemy loaders.cache. local _gl for _gl in "$PAGAN_ROOT"/usr/lib/gdk-pixbuf-2.0/*/loaders; do [ -d "$_gl" ] || continue if [ -e "$_gl/libpixbufloader_svg.so" ]; then if [ ! -e "$_gl/libpixbufloader-svg.so" ]; then mv "$_gl/libpixbufloader_svg.so" "$_gl/libpixbufloader-svg.so" info " ✓ loader SVG: poprawiona nazwa modułu (myślnik)" else rm -f "$_gl/libpixbufloader_svg.so" fi fi done chroot "$PAGAN_ROOT" env -u LD_LIBRARY_PATH /usr/bin/gdk-pixbuf-query-loaders --update-cache 2>/dev/null || \ warn "⚠ gdk-pixbuf-query-loaders --update-cache nie zadziałało (sprawdź loaders.cache)" # Pierwszy przebieg cache motywów (hicolor/Adwaita zainstalowane powyżej); # drugi przebieg wykonujemy na końcu install_xfce (po instalacji pakietów XFCE), # gdy w hicolor są już ikony konkretnych aplikacji. _refresh_icon_caches # Xfce dependencies (not in BLFS) # libwnck: test executables fail to link when host pangoft2 is newer than # target pango. Patch meson.build to skip test_progs (no --Dtests option). build_lfs_pkg "libwnck" "43.0" \ "https://download.gnome.org/sources/libwnck/43/libwnck-43.0.tar.xz" \ "SKIP" \ "sed -i 's/progs + test_progs/[]/' libwnck/meson.build && meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" build_lfs_pkg "startup-notification" "0.12" \ "https://www.freedesktop.org/software/startup-notification/releases/startup-notification-0.12.tar.gz" \ "SKIP" \ "./configure --prefix=/usr && make && make install DESTDIR=\$PAGAN_ROOT" # libxklavier – wymagane przez xfce4-settings (keyboard-settings) i lightdm. # Budujemy PRZED rdzeniem XFCE: xfce4-settings kompiluje się z tym nagłówkiem. build_lfs_pkg "libxklavier" "5.4" \ "https://people.freedesktop.org/~svu/libxklavier-5.4.tar.bz2" \ "SKIP" \ "./configure --prefix=/usr --disable-static && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # Katalog nagłówków libxklavier w rootfs bywa libxklavier-1.0 albo inny, a .pc # potrafi wskazać nieistniejącą ścieżkę (-I/usr/include/libxklavier-1.0) → # „fatal error: libxklavier/xklavier.h”. Faktyczną ścieżkę wykrywamy dynamicznie # i wstrzykujemy ROZWINIĘTĄ przez CPPFLAGS (bez $ w stringu receptury – escaping # łamie się w ewaluatorze poleceń). Używają jej: xfce4-settings, lightdm, greeter. local _xk_inc="$PAGAN_ROOT/usr/include" local _xk_h _xk_h=$(find "$PAGAN_ROOT/usr/include" -maxdepth 3 \ -path '*/libxklavier/xklavier.h' 2>/dev/null | head -n1) if [[ -n "$_xk_h" ]]; then _xk_inc="${_xk_h%/libxklavier/xklavier.h}" info " libxklavier.h w: $_xk_inc" else warn " ⚠ nie znaleziono libxklavier/xklavier.h w rootfs (fallback: $_xk_inc)" fi # Xfce core – seria 4.20 (najnowsza; point-releasy potwierdzone na # archive.xfce.org 2026-09). Uwaga: część pakietów 4.20 przeszła na Meson, # dlatego receptura sama wybiera: configure → autotools, inaczej meson. # libxfce4windowing to nowa zależność 4.20 (xfwm4/xfce4-session) – budowana # pierwsza. # xfconf/exo/garcon/xfwm4/xfce4-appfinder: 4.20.0 = najnowsze w serii. local xfce_packages=( "libxfce4windowing:4.20.7:https://archive.xfce.org/src/xfce/libxfce4windowing/4.20/libxfce4windowing-4.20.7.tar.bz2" "libxfce4util:4.20.1:https://archive.xfce.org/src/xfce/libxfce4util/4.20/libxfce4util-4.20.1.tar.bz2" "xfconf:4.20.0:https://archive.xfce.org/src/xfce/xfconf/4.20/xfconf-4.20.0.tar.bz2" "libxfce4ui:4.20.2:https://archive.xfce.org/src/xfce/libxfce4ui/4.20/libxfce4ui-4.20.2.tar.bz2" "exo:4.20.0:https://archive.xfce.org/src/xfce/exo/4.20/exo-4.20.0.tar.bz2" "garcon:4.20.0:https://archive.xfce.org/src/xfce/garcon/4.20/garcon-4.20.0.tar.bz2" "xfce4-panel:4.20.8:https://archive.xfce.org/src/xfce/xfce4-panel/4.20/xfce4-panel-4.20.8.tar.bz2" "thunar:4.20.9:https://archive.xfce.org/src/xfce/thunar/4.20/thunar-4.20.9.tar.bz2" "xfce4-settings:4.20.5:https://archive.xfce.org/src/xfce/xfce4-settings/4.20/xfce4-settings-4.20.5.tar.bz2" "xfce4-session:4.20.4:https://archive.xfce.org/src/xfce/xfce4-session/4.20/xfce4-session-4.20.4.tar.bz2" "xfdesktop:4.20.2:https://archive.xfce.org/src/xfce/xfdesktop/4.20/xfdesktop-4.20.2.tar.bz2" "xfwm4:4.20.0:https://archive.xfce.org/src/xfce/xfwm4/4.20/xfwm4-4.20.0.tar.bz2" "xfce4-appfinder:4.20.0:https://archive.xfce.org/src/xfce/xfce4-appfinder/4.20/xfce4-appfinder-4.20.0.tar.bz2" "xfce4-terminal:1.1.5:https://archive.xfce.org/src/apps/xfce4-terminal/1.1/xfce4-terminal-1.1.5.tar.xz" "tumbler:4.20.2:https://archive.xfce.org/src/xfce/tumbler/4.20/tumbler-4.20.2.tar.bz2" "mousepad:0.6.5:https://archive.xfce.org/src/apps/mousepad/0.6/mousepad-0.6.5.tar.xz" "ristretto:0.13.4:https://archive.xfce.org/src/apps/ristretto/0.13/ristretto-0.13.4.tar.xz" ) local xfce_recipe for pkg_info in "${xfce_packages[@]}"; do IFS=':' read -r name ver url <<< "$pkg_info" if [[ "$name" == "xfce4-settings" ]]; then # keyboard-settings kompiluje #include , a # mouse-settings (SDK Xorga: /usr/include/xorg) # – .pc potrafi dać nieistniejące ścieżki, więc dokładamy CPPFLAGS # z faktycznymi katalogami rootfs (jak lightdm). xfce_recipe="if [ -x ./configure ]; then CPPFLAGS='-I$_xk_inc -I$PAGAN_ROOT/usr/include/xorg' ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --disable-introspection && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT; else meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install; fi" else xfce_recipe="if [ -x ./configure ]; then ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --disable-introspection && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT; else meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install; fi" fi build_lfs_pkg "$name" "$ver" "$url" "SKIP" "$xfce_recipe" done # xfce4-power-manager (potrzebuje upower+libnotify — build best-effort bez nich) build_lfs_pkg "xfce4-power-manager" "4.20.1" \ "https://archive.xfce.org/src/xfce/xfce4-power-manager/4.20/xfce4-power-manager-4.20.1.tar.bz2" \ "SKIP" \ "if [ -x ./configure ]; then ./configure --prefix=/usr --sysconfdir=/etc --disable-introspection --enable-polkit --disable-upower --disable-notifications 2>/dev/null && make \$MAKEFLAGS || true; make install DESTDIR=\$PAGAN_ROOT 2>/dev/null || true; else meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install; fi" # ── Menedżer schowka w panelu (xfce4-clipman-plugin) ─────────────────────── # Historia schowka dostępna wprost z panelu – obok tacki systemowej (układ # panelu: _xfce_default_panel_layout). 1.7.0 buduje się wyłącznie mesonem; # zależności (libxfce4ui/libxfce4panel/gtk3/libX11) są już w rootfs. # Wyłączamy opcje opcjonalne: Wayland (live to X11), libXtst (wymaga libXi, # którego wariant XFCE nie buduje) i qrencode (kody QR dla wpisu schowka). # Best-effort: nieudana budowa wtyczki nie może przerwać całego builda – # panel zostanie wtedy bez menedżera schowka (wtyczka to dodatek, nie # warunek startu sesji). if ( build_lfs_pkg "xfce4-clipman-plugin" "1.7.0" \ "https://archive.xfce.org/src/panel-plugins/xfce4-clipman-plugin/1.7/xfce4-clipman-plugin-1.7.0.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr -Dx11=enabled -Dwayland=disabled -Dxtst=disabled -Dqrencode=disabled -Dtests=false && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" ) \ && [[ -e "$PAGAN_ROOT/usr/lib/xfce4/panel/plugins/libclipman.so" ]]; then info " ✓ clipman: wtyczka panelu gotowa (historia schowka)" else warn " ✗ clipman: brak wtyczki w rootfs – panel zostanie bez menedżera schowka" fi # ── nm-applet (network-manager-applet) – ikona sieci w tacce systemowej ── # NetworkManager jest w rootfs od build_blfs; sam applet bierzemy z repo PAG: # ze źródeł ciągnąłby libnma → gcr-4 → gtk4 oraz libsecret, a wariant XFCE # nie ma gtk4. Best-effort (jak fallback Calamaresa): bez sieci/repo build # leci dalej, tylko bez ikony sieci w panelu. Ikona pokazuje się w tacce # systemowej (plugin „systray”), która jest w domyślnym układzie panelu. if [[ -x "$PAGAN_ROOT/usr/bin/nm-applet" ]]; then info " ✓ nm-applet już w rootfs (ikona sieci w tacce systemowej)" elif _pag_install network-manager-applet && [[ -x "$PAGAN_ROOT/usr/bin/nm-applet" ]]; then info " ✓ nm-applet z repo PAG (ikona sieci w tacce systemowej)" else warn " ✗ nm-applet: brak w repo PAG – ikona sieci w panelu się nie pojawi (doinstaluj: pag install network-manager-applet)" fi # LightDM – jawne CPPFLAGS do nagłówków libxklavier (ten sam mechanizm co dla # xfce4-settings; _xk_inc wykryte po budowie libxklavier, przed rdzeniem XFCE). build_lfs_pkg "lightdm" "1.32.0" \ "https://github.com/canonical/lightdm/releases/download/1.32.0/lightdm-1.32.0.tar.xz" \ "SKIP" \ "CPPFLAGS='-I$_xk_inc' ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --disable-gtk-doc && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # xfce4-dev-tools: make greetera (i innych pakietów Xfce budowanych z tagu git) # woła xdt-csource (generowanie nagłówków z .ui/.css). Narzędzie buildowe – # instalujemy do $TOOLS_DIR (host, pierwszy w PATH), nie do rootfs. build_lfs_pkg "xfce4-dev-tools" "4.20.0" \ "https://archive.xfce.org/src/xfce/xfce4-dev-tools/4.20/xfce4-dev-tools-4.20.0.tar.bz2" \ "SKIP" \ "if [ -x ./configure ]; then ./configure --prefix=$TOOLS_DIR && make \$MAKEFLAGS && make install; else meson setup build --prefix=$TOOLS_DIR && ninja -C build && ninja -C build install; fi" build_lfs_pkg "lightdm-gtk-greeter" "2.0.9" \ "https://github.com/Xubuntu/lightdm-gtk-greeter/archive/refs/tags/lightdm-gtk-greeter-2.0.9.tar.gz" \ "SKIP" \ "autoreconf -fiv && CPPFLAGS='-I$_xk_inc' ./configure --prefix=/usr --sysconfdir=/etc && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # === Motyw graficzny (ładny wygląd XFCE) ================================= # Papirus (ikony) + Nordic (motyw GTK + xfwm4). Nordic bierzemy jako GOTOWE, # PREBUILT archiwum z GitHub releases – zero kompilacji SCSS. Poprzednia droga # (sassc + arc-theme) była zawodna: release sassc 3.6.2 nie ma żadnych assetów. # Fallback: Adwaita-dark (tworzony niżej), a _configure_xfce wybiera motyw dostępny. if [[ "${PAGAN_XFCE_THEME:-1}" != "0" ]]; then ( set +e # Ikony Papirus: archiwum z master zawiera gotowe Papirus/ i Papirus-Dark/. build_lfs_pkg "papirus-icon-theme" "master" \ "https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/archive/refs/heads/master.tar.gz" \ "SKIP" \ "mkdir -p \$PAGAN_ROOT/usr/share/icons && cp -a Papirus Papirus-Dark \$PAGAN_ROOT/usr/share/icons/ && for _d in Papirus Papirus-Dark; do chroot \$PAGAN_ROOT env -u LD_LIBRARY_PATH /usr/bin/gtk-update-icon-cache -f -q /usr/share/icons/\$_d 2>/dev/null || true; done" # Nordic-darker: gotowy tarball (gtk-2/3/4 + xfwm4 + index.theme). build_lfs_pkg "nordic-darker-theme" "2.2.0" \ "https://github.com/EliverLara/Nordic/releases/download/v2.2.0/Nordic-darker.tar.xz" \ "SKIP" \ "mkdir -p \$PAGAN_ROOT/usr/share/themes/Nordic-darker && cp -a . \$PAGAN_ROOT/usr/share/themes/Nordic-darker/" # Nordic: wariant jaśniejszy (alternatywa w ustawieniach XFCE). build_lfs_pkg "nordic-theme" "2.2.0" \ "https://github.com/EliverLara/Nordic/releases/download/v2.2.0/Nordic.tar.xz" \ "SKIP" \ "mkdir -p \$PAGAN_ROOT/usr/share/themes/Nordic && cp -a . \$PAGAN_ROOT/usr/share/themes/Nordic/" ) || warn "Motyw: część pobrań nie powiodła się – zostaje fallback (Papirus-Dark/Adwaita-dark)" fi # Firefox ESR (prebuilt, ~90 MB). URL mozilli NIE MA nazwy pliku (redirect # ?product=…), więc pobieramy ręcznie do stałej nazwy. Ucięty/niekompletny # plik (np. po przerwaniu pobierania) kasujemy i pobieramy ponownie. if _is_built "firefox-esr"; then ui_skip "firefox-esr" ((PKG_SKIPPED++)) || true else ((UI_PKG_N++)) || true local _ff_arc="$SRC_DIR/firefox-esr.tar.xz" # Usuń niekompletne archiwum zanim zdecydujemy o pobraniu. if [[ -s "$_ff_arc" ]] && ! tar -tJf "$_ff_arc" >/dev/null 2>&1; then warn "Firefox ESR: niekompletne archiwum – usuwam i pobieram ponownie" rm -f "$_ff_arc" fi if [[ ! -s "$_ff_arc" ]]; then info " Pobieranie Firefox ESR (prebuilt, ~90 MB)..." curl -fL --connect-timeout 20 --max-time 900 --retry 3 --retry-delay 3 \ -o "$_ff_arc" "https://download.mozilla.org/?product=firefox-esr-latest-ssl&os=linux64&lang=pl" \ || { rm -f "$_ff_arc"; error "Nie udało się pobrać Firefox ESR (download.mozilla.org)"; } fi if ! tar -tJf "$_ff_arc" >/dev/null 2>&1; then rm -f "$_ff_arc" error "Firefox ESR: uszkodzone archiwum $_ff_arc" fi info " Rozpakowywanie Firefox ESR do rootfs..." rm -rf "$PAGAN_ROOT/usr/lib/firefox" tar -xf "$_ff_arc" -C "$PAGAN_ROOT/usr/lib" || error "Firefox ESR: rozpakowanie nieudane" local _ff_dir _ff_dir=$(find "$PAGAN_ROOT/usr/lib" -maxdepth 1 -type d -name 'firefox*' | head -n1) if [[ -n "$_ff_dir" && "$_ff_dir" != "$PAGAN_ROOT/usr/lib/firefox" ]]; then mv "$_ff_dir" "$PAGAN_ROOT/usr/lib/firefox" fi if [[ ! -x "$PAGAN_ROOT/usr/lib/firefox/firefox" ]]; then error "Firefox ESR: brak binarki firefox po rozpakowaniu" fi ln -sf /usr/lib/firefox/firefox "$PAGAN_ROOT/usr/bin/" _mark_built "firefox-esr" ok "Firefox ESR zainstalowany" fi export LD_LIBRARY_PATH="$_old_ld" # Stary sddm (relikt wcześniejszych buildów w rootfs po resume) nie może # startować w live XFCE – wyłączamy (czysty build XFCE go nie ma). chroot "$PAGAN_ROOT" env -u LD_LIBRARY_PATH systemctl disable sddm 2>/dev/null || true # Odśwież cache motywów PONOWNIE – dopiero teraz wszystkie pakiety XFCE # zainstalowały swoje ikony do hicolor (panel, xfwm4, xfdesktop, thunar…). _refresh_icon_caches _configure_xfce ok "Xfce zainstalowane!" } _configure_xfce() { # LightDM mkdir -p "$PAGAN_ROOT/etc/lightdm" cat > "$PAGAN_ROOT/etc/lightdm/lightdm.conf" <<'EOF' [LightDM] greeter-session=lightdm-gtk-greeter user-session=xfce minimum-vt=7 xserver-command=X -background none -verbose [Seat:*] greeter-session=lightdm-gtk-greeter user-session=xfce EOF # .xinitrc mkdir -p "$PAGAN_ROOT/etc/skel" cat > "$PAGAN_ROOT/etc/skel/.xinitrc" <<'EOF' #!/bin/sh exec startxfce4 EOF chmod +x "$PAGAN_ROOT/etc/skel/.xinitrc" # ── Motyw: spójny ciemny wygląd (Adwaita-dark + Papirus-Dark) ── # GTK3 z LFS/BLFS instaluje Adwaitę, ale BEZ katalogu /usr/share/themes/ # Adwaita-dark. Tworzymy wariant ciemny importujący zasób GTK – dzięki # temu nazwa "Adwaita-dark" działa w xfsettingsdialog i w settings.ini. mkdir -p "$PAGAN_ROOT/usr/share/themes/Adwaita-dark/gtk-3.0" cat > "$PAGAN_ROOT/usr/share/themes/Adwaita-dark/index.theme" <<'EOF' [Desktop Entry] Type=X-GNOME-Metatheme Name=Adwaita-dark Comment=Adwaita dark variant GtkTheme=Adwaita-dark MetacityTheme=Adwaita-dark EOF cat > "$PAGAN_ROOT/usr/share/themes/Adwaita-dark/gtk-3.0/gtk.css" <<'EOF' @import url("resource:///org/gtk/libgtk/theme/Adwaita/gtk-dark.css"); EOF # Motyw: preferuj Nordic-darker (pełny motyw GTK + xfwm4), potem Nordic, # potem (legacy) Arc-Dark, w ostateczności Adwaita-dark. local _gtk_theme=Adwaita-dark _wm_theme=Default _icon_theme=Papirus-Dark if [[ -d "$PAGAN_ROOT/usr/share/themes/Nordic-darker" ]]; then _gtk_theme="Nordic-darker"; _wm_theme="Nordic-darker" elif [[ -d "$PAGAN_ROOT/usr/share/themes/Nordic" ]]; then _gtk_theme="Nordic"; _wm_theme="Nordic" elif [[ -d "$PAGAN_ROOT/usr/share/themes/Arc-Dark" ]]; then _gtk_theme="Arc-Dark"; _wm_theme="Arc-Dark" fi [[ -d "$PAGAN_ROOT/usr/share/icons/Papirus-Dark" ]] || _icon_theme="Adwaita" # Systemowe domyślne xfconf (/etc/xdg) – działają też dla kont bez # własnego ~/.config/xfce4 (nowi użytkownicy z Calamares). mkdir -p "$PAGAN_ROOT/etc/xdg/xfce4/xfconf/xfce-perchannel-xml" cat > "$PAGAN_ROOT/etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml" < EOF cat > "$PAGAN_ROOT/etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfwm4.xml" < EOF # Domyślny układ panelu: tacka systemowa (ikona nm-applet = NetworkManager) # + menedżer schowka (clipman) – patrz _xfce_default_panel_layout. _xfce_default_panel_layout # GTK3 – ten sam motyw dla aplikacji spoza xfconf. finalize_build # dopisuje ten plik tylko gdy nie istnieje, więc nasz wybór się utrzyma. mkdir -p "$PAGAN_ROOT/etc/gtk-3.0" if [[ ! -f "$PAGAN_ROOT/etc/gtk-3.0/settings.ini" ]]; then cat > "$PAGAN_ROOT/etc/gtk-3.0/settings.ini" </dev/null || true echo "pagan:changeme" | chroot "$PAGAN_ROOT" chpasswd 2>/dev/null || true; chroot "$PAGAN_ROOT" passwd -e pagan 2>/dev/null || true } # ── Domyślny układ panelu XFCE ──────────────────────────────────────── # /etc/xdg/xfce4/panel/default.xml to szablon, który xfconf kopiuje do # ~/.config/xfce4/panel/xfce4-panel.xml przy pierwszym uruchomieniu panelu na # koncie – dotyczy to LIVE oraz każdego nowego użytkownika (Calamares/skel). # # Układ = domyślny układ Xfce 4.20 + dwie zmiany: # • plugin-6 systray – tacka systemowa, w której pokazuje się ikona nm-applet # (NetworkManager) oraz inne statusnotifiery, # • plugin-19 xfce4-clipman-plugin – menedżer schowka (klik → wybór wpisu), # wstawiony do panelu-1 zaraz za tacką. # # configver czytamy z pliku instalowanego przez xfce4-panel (fallback 2 = seria # 4.20): dzięki temu zmiana serii Xfce nie wymusza migracji naszego układu. _xfce_default_panel_layout() { local _f="$PAGAN_ROOT/etc/xdg/xfce4/panel/default.xml" _ver if [[ ! -x "$PAGAN_ROOT/usr/bin/xfce4-panel" ]]; then info " Pomijam układ panelu (brak xfce4-panel w rootfs)" return 0 fi _ver=$(sed -n 's/.*name="configver" type="int" value="\([0-9]\{1,\}\)".*/\1/p' "$_f" 2>/dev/null | head -n1 || true) [[ -n "$_ver" ]] || _ver=2 mkdir -p "$PAGAN_ROOT/etc/xdg/xfce4/panel" cat > "$_f" < EOF info " ✓ panel: tacka systemowa (nm-applet/NetworkManager) + menedżer schowka (clipman)" } # Best-effort: instalacja pakietu z repo przez pag w chrocie targeta. # PAG_INSECURE=1: w środowisku builda pomijamy weryfikację GPG repo (własne repo, # fail-closed TOFU blokowałby instalację bez trusted.json). Zwraca 0 gdy OK. _pag_install() { local _pkg="$1" shift || true # dodatkowe flagi pag, np. -f (wymuś reinstalację z repo) [[ -x "$PAGAN_ROOT/usr/bin/pag" ]] || return 1 local _had_r=0 [[ -e "$PAGAN_ROOT/etc/resolv.conf" ]] && _had_r=1 cp -L /etc/resolv.conf "$PAGAN_ROOT/etc/resolv.conf" 2>/dev/null || true # "$@" to flagi pag (np. -f/--force → cmd_install(upgrade=True)), dzięki którym # można wymusić reinstalację pakietu już obecnego w rootfsie – patrz # install_desktop_extras (naprawa bibliotek z rozjechanym ABI). chroot "$PAGAN_ROOT" env PAG_INSECURE=1 /usr/bin/pag install -y "$@" "$_pkg" >/dev/null 2>&1 local _rc=$? [[ $_had_r -eq 0 ]] && rm -f "$PAGAN_ROOT/etc/resolv.conf" return $_rc } # true, gdy w bibliotece $1 (wzorzec glob, ścieżka względem $PAGAN_ROOT) jest # NIEROZWIĄZANE odwołanie do symbolu $2. # # Tani detektor rozjazdu ABI: symbol jest wymagany przez kod, ale nie eksportuje # go żadna biblioteka w obrazie → dlopen() pada i np. plugin tumblera się nie # ładuje („undefined symbol”). Takie biblioteki w rootfsie pochodzą z wcześniejszych # przebiegów (resume) i są zbudowane w innym środowisku niż reszta obrazu – # wersja z repo PAG bywa spójna (weryfikowaliśmy to `nm -D`). _lib_missing_symbol() { local _glob="$1" _sym="$2" _lib command -v nm >/dev/null 2>&1 || return 1 _lib=$(ls -1 $PAGAN_ROOT$_glob 2>/dev/null | head -n1) [[ -n "$_lib" ]] || return 1 nm -D --undefined-only "$_lib" 2>/dev/null | awk '{print $NF}' | grep -qx "$_sym" } # Instalacja pakietów z repo PAG wprost do rootfs (--repo-pkgs). Najpierw `pag # update` (raz), potem `pag install -y` dla każdego pakietu. Raportuje każdy # pakiet osobno; błędne nazwy nie przerywają reszty. _pag_install_repo() { [[ -n "${REPO_PKGS:-}" ]] || return 0 [[ -x "$PAGAN_ROOT/usr/bin/pag" ]] || { warn "pag nieobecny w rootfs – pomijam instalację z repo"; return 1; } [[ -n "${PAGAN_REPO_URL:-}" ]] || { warn "Brak PAGAN_REPO_URL – pomijam instalację z repo"; return 1; } step "INSTALACJA PAKIETÓW Z REPO PAG (--repo-pkgs): ${REPO_PKGS}" local _had_r=0 _synced=0 _rc=0 _pkg [[ -e "$PAGAN_ROOT/etc/resolv.conf" ]] && _had_r=1 cp -L /etc/resolv.conf "$PAGAN_ROOT/etc/resolv.conf" 2>/dev/null || true if chroot "$PAGAN_ROOT" /usr/bin/env PAG_INSECURE=1 /usr/bin/pag update >/dev/null 2>&1; then _synced=1 else warn " ✗ pag update nieudane (repo: ${PAGAN_REPO_URL}) – sprawdź sieć/nazwę repo" fi for _pkg in $REPO_PKGS; do if [[ $_synced -eq 0 ]]; then warn " ✗ pomijam: $_pkg (brak zsynchronizowanego repo)" _rc=1 continue fi if chroot "$PAGAN_ROOT" /usr/bin/env PAG_INSECURE=1 /usr/bin/pag install -y "$_pkg" >/dev/null 2>&1; then info " ✓ z repo: $_pkg" else warn " ✗ pag install $_pkg nieudany (repo: ${PAGAN_REPO_URL}; spróbuj: pag search $_pkg)" _rc=1 fi done [[ $_had_r -eq 0 ]] && rm -f "$PAGAN_ROOT/etc/resolv.conf" return $_rc } # ============================ INSTALACJA PAGANDE (FORK LABWC + STACK) ============================ # PaganDE: wlroots-pagan (fork wlroots, fundament; źródło: paganDE/compositor/wlroots) + # labwc-pagan (nasz fork kompozytora; źródło: paganDE/compositor) + # lekki stack Catppuccin Mocha (waybar, rofi, foot, mako, kanshi, swaybg, # swaylock, thunar). Meta-pakiet `pagande` (recipes/de/pagande) niesie sesję # wayland i domyślne konfiguracje; zależności budowane z receptur przez # pagbuild i instalowane w chroot przez `pag install`. install_pagande() { step "INSTALACJA PAGANDE (labwc-pagan + Catppuccin Mocha)" [[ -x "$PAGAN_ROOT/usr/bin/pag" ]] || { warn "pag nie zainstalowany – pomijam PaganDE"; return 0; } command -v pagbuild >/dev/null 2>&1 || { warn "pagbuild niedostępny na hoście – pomijam PaganDE"; return 0; } local de_src="$SCRIPT_DIR/recipes" [[ -d "$de_src" ]] || { warn "brak recipes/ (katalog: $de_src) – pomijam PaganDE"; return 0; } local out_dir="$PAGAN_ROOT/tmp/pagande-pkgs" rm -rf "$out_dir" mkdir -p "$out_dir" # Kolejność zależności: baza (wlroots-pagan itd.) → nasz fork labwc → meta-pakiet local -a pkgs=(libdisplay-info libliftoff hwdata seatd wlroots-pagan labwc-pagan pagande) local p recipe f sub for p in "${pkgs[@]}"; do recipe="" # pagande mieszka w de/, pozostałe w gui/ (część bazowa w utils/) for sub in de gui utils; do recipe="$de_src/$sub/$p/PAGBUILD.yaml" [[ -f "$recipe" ]] && break recipe="" done [[ -n "$recipe" ]] || { warn "brak receptury: $p – pomijam"; continue; } info " pagbuild $p ..." if ! pagbuild "$recipe" --rootfs "$PAGAN_ROOT" --out "$out_dir"; then warn " ✗ pagbuild $p nie powiódł się" fi done # Instalacja pakietów .pag w chroot (kolejność alfabetyczna = zależności najpierw) for f in "$out_dir"/*.pag; do [[ -e "$f" ]] || continue info " pag install $(basename "$f")" chroot "$PAGAN_ROOT" env PAG_YES=1 /usr/bin/pag install "/tmp/pagande-pkgs/$(basename "$f")" \ || warn " ✗ pag install $(basename "$f") nie powiódł się" done ok "PaganDE zainstalowane! (labwc-pagan + stack Catppuccin Mocha)" } # ========================= INSTALACJA DEEPIN ========================= install_deepin() { step "INSTALACJA DEEPIN" export PATH="$TOOLS_DIR/bin:$PATH" export PKG_CONFIG_PATH="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig" # Xorg build_lfs_pkg "xorg-server" "21.1.13" \ "https://www.x.org/releases/individual/xserver/xorg-server-21.1.13.tar.xz" \ "SKIP" \ "./configure --prefix=/usr --enable-glamor --enable-dri3 --enable-xcsecurity && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # Qt6 build_lfs_pkg "qtbase" "6.7.2" \ "https://download.qt.io/official_releases/qt/6.7/6.7.2/submodules/qtbase-everywhere-src-6.7.2.tar.xz" \ "SKIP" \ "./configure -prefix /usr -opensource -confirm-license -release -shared -nomake tests -nomake examples" # Deepin local deepin_packages=( "dde-qt-dbus-factory:https://github.com/linuxdeepin/dde-qt-dbus-factory/releases/latest/download/dde-qt-dbus-factory.tar.xz" "dtkcore:https://github.com/linuxdeepin/dtkcore/releases/latest/download/dtkcore.tar.xz" "dtkgui:https://github.com/linuxdeepin/dtkgui/releases/latest/download/dtkgui.tar.xz" "dtkwidget:https://github.com/linuxdeepin/dtkwidget/releases/latest/download/dtkwidget.tar.xz" "dde-dock:https://github.com/linuxdeepin/dde-dock/releases/latest/download/dde-dock.tar.xz" "dde-launcher:https://github.com/linuxdeepin/dde-launcher/releases/latest/download/dde-launcher.tar.xz" "dde-control-center:https://github.com/linuxdeepin/dde-control-center/releases/latest/download/dde-control-center.tar.xz" "dde-session-shell:https://github.com/linuxdeepin/dde-session-shell/releases/latest/download/dde-session-shell.tar.xz" "deepin-terminal:https://github.com/linuxdeepin/deepin-terminal/releases/latest/download/deepin-terminal.tar.xz" "deepin-file-manager:https://github.com/linuxdeepin/deepin-file-manager/releases/latest/download/deepin-file-manager.tar.xz" ) for pkg_info in "${deepin_packages[@]}"; do IFS=':' read -r name url <<< "$pkg_info" build_lfs_pkg "$name" "latest" "$url" "SKIP" \ "cmake -B build -DCMAKE_INSTALL_PREFIX=/usr && cmake --build build && cmake --install build" done _configure_deepin ok "Deepin zainstalowane!" } _configure_deepin() { mkdir -p "$PAGAN_ROOT/etc/lightdm" cat > "$PAGAN_ROOT/etc/lightdm/lightdm.conf" <<'EOF' [LightDM] greeter-session=lightdm-deepin-greeter user-session=deepin minimum-vt=7 [Seat:*] greeter-session=lightdm-deepin-greeter user-session=deepin EOF chroot "$PAGAN_ROOT" useradd -m -G wheel,audio,video,cdrom,plugdev -s /bin/bash pagan 2>/dev/null || true echo "pagan:changeme" | chroot "$PAGAN_ROOT" chpasswd 2>/dev/null || true; chroot "$PAGAN_ROOT" passwd -e pagan 2>/dev/null || true } # ========================= INSTALACJA BUDGIE ========================= install_budgie() { step "INSTALACJA BUDGIE DESKTOP" export PATH="$TOOLS_DIR/bin:$PATH" export PKG_CONFIG_PATH="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig" # Xorg base build_lfs_pkg "xorg-server" "21.1.13" \ "https://www.x.org/releases/individual/xserver/xorg-server-21.1.13.tar.xz" \ "SKIP" \ "./configure --prefix=/usr --enable-glamor --enable-dri3 --enable-xcsecurity && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" build_lfs_pkg "xf86-input-libinput" "1.4.0" \ "https://www.x.org/releases/individual/driver/xf86-input-libinput-1.4.0.tar.xz" \ "SKIP" "./configure --prefix=/usr --with-sdkdir=/usr/include/xorg && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" # GTK+3 build_lfs_pkg "gtk3" "3.24.43" \ "https://download.gnome.org/sources/gtk+/3.24/gtk+-3.24.43.tar.xz" \ "SKIP" \ "./configure --prefix=/usr --enable-x11-backend --enable-wayland-backend" # GNOME stack dependencies for Budgie build_lfs_pkg "gnome-menus" "3.36.0" \ "https://download.gnome.org/sources/gnome-menus/3.36/gnome-menus-3.36.0.tar.xz" \ "SKIP" "./configure --prefix=/usr" build_lfs_pkg "gnome-settings-daemon" "47.2" \ "https://download.gnome.org/sources/gnome-settings-daemon/47/gnome-settings-daemon-47.2.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr -Dsystemd=false && ninja -C build && DESTDIR=$PAGAN_ROOT ninja -C build install" build_lfs_pkg "gnome-control-center" "47.3" \ "https://download.gnome.org/sources/gnome-control-center/47/gnome-control-center-47.3.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr && ninja -C build && DESTDIR=$PAGAN_ROOT ninja -C build install" build_lfs_pkg "gnome-session" "47.1" \ "https://download.gnome.org/sources/gnome-session/47/gnome-session-47.1.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr -Dsystemd=false && ninja -C build && DESTDIR=$PAGAN_ROOT ninja -C build install" build_lfs_pkg "mutter" "47.5" \ "https://download.gnome.org/sources/mutter/47/mutter-47.5.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr -Dtests=false && ninja -C build && DESTDIR=$PAGAN_ROOT ninja -C build install" # Budgie desktop build_lfs_pkg "budgie-desktop" "10.9.2" \ "https://github.com/BuddiesOfBudgie/budgie-desktop/releases/download/v10.9.2/budgie-desktop-10.9.2.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr -Dwith-polkit=disabled && ninja -C build && DESTDIR=$PAGAN_ROOT ninja -C build install" # LightDM + slick-greeter build_lfs_pkg "lightdm" "1.32.0" \ "https://github.com/canonical/lightdm/releases/download/1.32.0/lightdm-1.32.0.tar.xz" \ "SKIP" \ "./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" build_lfs_pkg "slick-greeter" "2.0.8" \ "https://github.com/linuxmint/slick-greeter/archive/refs/tags/2.0.8.tar.gz" \ "SKIP" \ "meson setup build --prefix=/usr && ninja -C build && DESTDIR=$PAGAN_ROOT ninja -C build install" # Aplikacje _install_firefox_binary _install_terminal_apps _configure_budgie ok "Budgie zainstalowane!" } _configure_budgie() { mkdir -p "$PAGAN_ROOT/etc/lightdm" cat > "$PAGAN_ROOT/etc/lightdm/lightdm.conf" <<'EOF' [LightDM] greeter-session=slick-greeter user-session=budgie-desktop minimum-vt=7 [Seat:*] greeter-session=slick-greeter user-session=budgie-desktop EOF mkdir -p "$PAGAN_ROOT/etc/skel" cat > "$PAGAN_ROOT/etc/skel/.xinitrc" <<'EOF' #!/bin/sh exec budgie-desktop EOF chmod +x "$PAGAN_ROOT/etc/skel/.xinitrc" chroot "$PAGAN_ROOT" useradd -m -G wheel,audio,video,cdrom,plugdev -s /bin/bash pagan 2>/dev/null || true echo "pagan:changeme" | chroot "$PAGAN_ROOT" chpasswd 2>/dev/null || true; chroot "$PAGAN_ROOT" passwd -e pagan 2>/dev/null || true } # ========================= WSPÓLNE APLIKACJE ========================= _install_firefox_binary() { local ff_ver="${FIREFOX_VERSION:-140.0.2}" local ff_url="https://ftp.mozilla.org/pub/firefox/releases/${ff_ver}/linux-$(uname -m)/en-US/firefox-${ff_ver}.tar.xz" local ff_arch="$SRC_DIR/firefox-${ff_ver}.tar.xz" info " Instalowanie Firefox ${ff_ver}..." wget -q --timeout=120 -O "$ff_arch" "$ff_url" 2>/dev/null \ || { warn "Firefox ${ff_ver}: pobieranie nieudane – pomijam"; return 0; } mkdir -p "$PAGAN_ROOT/usr/lib/firefox" tar -xf "$ff_arch" -C "$PAGAN_ROOT/usr/lib/firefox" --strip-components=1 2>/dev/null \ || { warn "Firefox: rozpakowanie nieudane – pomijam"; return 0; } ln -sf /usr/lib/firefox/firefox "$PAGAN_ROOT/usr/bin/firefox" 2>/dev/null || true ok "Firefox ${ff_ver} zainstalowany" } _install_terminal_apps() { build_lfs_pkg "xfce4-terminal" "1.1.5" \ "https://archive.xfce.org/src/apps/xfce4-terminal/1.1/xfce4-terminal-1.1.5.tar.xz" \ "SKIP" \ "if [ -x ./configure ]; then ./configure --prefix=/usr && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT; else meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install; fi" \ 2>/dev/null || true build_lfs_pkg "nano" "8.1" \ "https://www.nano-editor.org/dist/v8/nano-8.1.tar.xz" \ "SKIP" "./configure --prefix=/usr --enable-utf8" 2>/dev/null || true } # ========================= OSINFO-DB (PAGAN ROLLING) ========================= install_osinfo() { step "INSTALACJA OSINFO-DB (Pagan Linux OS database)" build_lfs_pkg "osinfo-db-tools" "1.12.0" \ "https://releases.pagure.org/libosinfo/osinfo-db-tools-1.12.0.tar.xz" \ "SKIP" \ "meson setup build --prefix=/usr && DESTDIR=$PAGAN_ROOT ninja -C build install" build_lfs_pkg "osinfo-db" "20250606" \ "https://releases.pagure.org/libosinfo/osinfo-db-20250606.tar.xz" \ "SKIP" \ "osinfo-db-import --root $PAGAN_ROOT --system $SRC_DIR/osinfo-db-20250606.tar.xz 2>/dev/null || true" # Pagan Linux Rolling XML mkdir -p "$PAGAN_ROOT/usr/share/osinfo/os/pagan" cat > "$PAGAN_ROOT/usr/share/osinfo/os/pagan/pagan-rolling.xml" <<'OSINFO' pagan-rolling Pagan Linux Rolling Pagan Linux Community linux paganlinux.eu rolling 200000000 1073741824 8589934592 1000000000 2147483648 21474836480 PaganLinux boot/vmlinuz-linux boot/initramfs.img OSINFO ok "osinfo-db + Pagan Rolling XML ✓" } # ========================= ZALEŻNOŚCI CALAMARES (Qt6 + KF6) ========================= # Calamares 3.3 + KPMcore wymagają Qt6 i KF6. Przy DE=XFCE tego stosu nie ma, # więc budujemy go tutaj. Receptury/wersje sprawdzone (ta sama warstwa Qt6/KF6), # bez Plasma/SDDM i z mniejszym podzbiorem modułów Qt6. # Wywoływane w podpowłoce przez install_calamares – awaria nie ubija builda. install_calamares_deps() { step "ZALEŻNOŚCI CALAMARES (Qt6 + KF6)" export PATH="$TOOLS_DIR/bin:$PATH" export PKG_CONFIG_PATH="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig:$PAGAN_ROOT/usr/lib/x86_64-linux-gnu/pkgconfig:$PAGAN_ROOT/usr/lib64/pkgconfig" export CMAKE_PREFIX_PATH="$PAGAN_ROOT/usr:/usr" local _old_ld="${LD_LIBRARY_PATH:-}" export LD_LIBRARY_PATH="/usr/lib:$PAGAN_ROOT/usr/lib${_old_ld:+:$_old_ld}" local _old_xdg="${XDG_DATA_DIRS:-}" export XDG_DATA_DIRS="$PAGAN_ROOT/usr/share${_old_xdg:+:$_old_xdg}" # ── Warstwa X11/XCB wymagana przez platformę xcb Qt6 (XFCE jej nie buduje – # Xorg w install_xfce linkuje hostowe libki). Wersje sprawdzone; # pomijamy Xorg/narzędzia, które XFCE już ma (inna wersja = clobber). ── local xorg_url="https://xorg.freedesktop.org/archive/individual" local x_autotools="./configure --prefix=/usr --disable-static && make \$MAKEFLAGS && make install DESTDIR=\$PAGAN_ROOT" build_lfs_pkg "xorgproto" "2025.1" \ "$xorg_url/proto/xorgproto-2025.1.tar.gz" "SKIP" \ "meson setup build --prefix=/usr && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" build_lfs_pkg "libXau" "1.0.12" "$xorg_url/lib/libXau-1.0.12.tar.gz" "SKIP" "$x_autotools" build_lfs_pkg "libXdmcp" "1.1.5" "$xorg_url/lib/libXdmcp-1.1.5.tar.gz" "SKIP" "$x_autotools" build_lfs_pkg "libxcb" "1.17.0" "$xorg_url/lib/libxcb-1.17.0.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "xcb-util" "0.4.1" "$xorg_url/xcb/xcb-util-0.4.1.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "xcb-util-image" "0.4.1" "$xorg_url/xcb/xcb-util-image-0.4.1.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "xcb-util-keysyms" "0.4.1" "$xorg_url/xcb/xcb-util-keysyms-0.4.1.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "xcb-util-renderutil" "0.3.10" "$xorg_url/xcb/xcb-util-renderutil-0.3.10.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "xcb-util-wm" "0.4.2" "$xorg_url/xcb/xcb-util-wm-0.4.2.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "xcb-util-cursor" "0.1.6" "$xorg_url/xcb/xcb-util-cursor-0.1.6.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "libX11" "1.8.13" "$xorg_url/lib/libX11-1.8.13.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "libXext" "1.3.6" "$xorg_url/lib/libXext-1.3.6.tar.xz" "SKIP" "$x_autotools" build_lfs_pkg "libxkbfile" "1.1.3" "$xorg_url/lib/libxkbfile-1.1.3.tar.xz" "SKIP" "$x_autotools" # libxkbcommon-x11: build_blfs zbudował libxkbcommon z -Denable-x11=false, # a platforma xcb Qt6 wymaga xkbcommon-x11. Wersja "-x11" wymusza przebudowę. build_lfs_pkg "libxkbcommon" "1.13.1-x11" \ "https://github.com/lfs-book/libxkbcommon/archive/v1.13.1/libxkbcommon-1.13.1.tar.gz" \ "SKIP" \ "meson setup build --prefix=/usr -Denable-x11=true && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # ── Perl target dla kdoctools (meinproc6/URI::Escape) – jak przy budowie KF6. ── local _old_perl5lib="${PERL5LIB:-}" local _pver="" _parch="" _d for _d in "$PAGAN_ROOT"/usr/lib/perl5/5.*; do [[ -d "$_d" ]] && _pver="$(basename "$_d")"; done if [[ -n "$_pver" && -x "$PAGAN_ROOT/usr/bin/perl" ]]; then for _d in "$PAGAN_ROOT/usr/lib/perl5/$_pver"/*/; do [[ -d "$_d/CORE" ]] && _parch="$(basename "$_d")" && break; done export LD_LIBRARY_PATH="$PAGAN_ROOT/usr/lib/perl5/$_pver/$_parch/CORE:$LD_LIBRARY_PATH" export PERL5LIB="$PAGAN_ROOT/usr/lib/perl5/$_pver:$PAGAN_ROOT/usr/lib/perl5/site_perl/$_pver:$PAGAN_ROOT/usr/lib/perl5/$_pver/$_parch:$PAGAN_ROOT/usr/lib/perl5/site_perl/$_pver/$_parch" local _site="$PAGAN_ROOT/usr/lib/perl5/site_perl/$_pver" if [[ -f /usr/share/perl5/vendor_perl/URI.pm && ! -e "$_site/URI.pm" ]]; then install -Dm644 /usr/share/perl5/vendor_perl/URI.pm "$_site/URI.pm" cp -a /usr/share/perl5/vendor_perl/URI "$_site/" fi fi find "$PAGAN_ROOT/usr/bin" -maxdepth 1 -type f -exec sed -i 's|^#!/usr/bin/perl|#!'"$PAGAN_ROOT"'/usr/bin/perl|' {} + 2>/dev/null || true # XML::Parser (XS) dla intltool-merge (kdoctools/KF6). # NIE budujemy w chrocie: target nie ma natywnego as/ld (binutils powstaje # tylko jako cross do $TOOLS_DIR), więc kompilacja XS pada. Targetowy perl # 5.44 uruchamia się jednak na hoście (ta sama glibc 2.44), a hostowy gcc ma # as/ld. Trzeba tylko przepisać ścieżki w Config.pm rootfsu # (/usr/lib/perl5 → $PAGAN_ROOT/usr/lib/perl5), bo inaczej MakeMaker nie # znajdzie CORE („Failed to opendir '/usr/lib/perl5/.../CORE'”). Config w 5.44 # jest read-only (nie da się nadpisać %Config w locie), dlatego cieniujemy # Config.pm/Config_heavy.pl kopią z przepisanymi ścieżkami (katalog o nazwie # = archname, żeby MakeMaker nie ostrzegał o niezgodności arch). local _xp_perl_lib="$PAGAN_ROOT/usr/lib/perl5/$_pver" if [[ -n "$_pver" && -n "$_parch" && -x "$PAGAN_ROOT/usr/bin/perl" ]] \ && ! LD_LIBRARY_PATH="$_xp_perl_lib/$_parch/CORE:/usr/lib:$PAGAN_ROOT/usr/lib" \ PERL5LIB="$_xp_perl_lib/$_parch:$_xp_perl_lib:$_xp_perl_lib/site_perl/$_parch:$PAGAN_ROOT/usr/lib/perl5/site_perl/$_pver" \ "$PAGAN_ROOT/usr/bin/perl" -MXML::Parser -e 'exit 0' >/dev/null 2>&1; then local _xp_ver="2.44" local _xp_arch="$SRC_DIR/XML-Parser-${_xp_ver}.tar.gz" if download_pkg "https://cpan.metacpan.org/authors/id/T/TO/TODDR/XML-Parser-${_xp_ver}.tar.gz" "$_xp_arch" "SKIP"; then local _xp_build="$SRC_DIR/build-xml-parser" local _xp_cfg="$SRC_DIR/xml-parser-config/$_parch" local _xp_log="/var/log/pagan-pkgs/XML-Parser-${_xp_ver}.log" rm -rf "$_xp_build" "$SRC_DIR/xml-parser-config" mkdir -p "$_xp_build" "$_xp_cfg" "$(dirname "$_xp_log")" tar -xzf "$_xp_arch" -C "$_xp_build" --strip-components=1 cp "$_xp_perl_lib/$_parch/Config.pm" "$_xp_perl_lib/$_parch/Config_heavy.pl" "$_xp_cfg/" 2>/dev/null || true sed -i -e "s|'/usr/|'$PAGAN_ROOT/usr/|g" "$_xp_cfg/Config.pm" "$_xp_cfg/Config_heavy.pl" 2>/dev/null || true ( cd "$_xp_build" || exit 1 export LD_LIBRARY_PATH="$_xp_perl_lib/$_parch/CORE:/usr/lib:$PAGAN_ROOT/usr/lib" export PERL5LIB="$_xp_cfg:$_xp_perl_lib/$_parch:$_xp_perl_lib:$_xp_perl_lib/site_perl/$_parch:$PAGAN_ROOT/usr/lib/perl5/site_perl/$_pver" PERL_MM_USE_DEFAULT=1 "$PAGAN_ROOT/usr/bin/perl" Makefile.PL && make && make install ) >"$_xp_log" 2>&1 || { warn " ⚠ XML::Parser build nieudany (log: $_xp_log) – kdoctools (KF6) może paść" tail -n 15 "$_xp_log" 2>/dev/null | sed 's/^/ /' >&2 || true } rm -rf "$_xp_build" "$SRC_DIR/xml-parser-config" fi fi # ── Qt6 (TYLKO to, co wymaga Calamares/KPMcore) ── # Calamares 3.3: Qt6 Core/Concurrent/DBus/Gui/LinguistTools/Network/Svg/Widgets. # KPMcore 24.12: Qt6 Core/DBus/Gui/Widgets. # QML/Qt Quick (qtdeclarative + qtshadertools) NIE jest potrzebne: Calamares # budujemy z -DWITH_QML=OFF, a KF6 bez modułów QML. To wycina największy # i najbardziej pamięciożerny moduł Qt (kilkaset MB źródeł, ~3700 zadań). local qt_ver="6.11.2" local qt_url="https://download.qt.io/official_releases/qt/${qt_ver%.*}/$qt_ver/submodules" # Build/target-first LD_LIBRARY_PATH dla narzędzi z drzewa build (moc/rcc/qmlcachegen): # host może mieć INNĄ wersję Qt niż budowana, a przy host-first ld.so brał # hostowe libQt6* → mieszane ABI i qmlcachegen dostawał SIGSEGV. LD_LIBRARY_PATH # ma priorytet nad RUNPATH, więc prependujemy: # $PWD/build/lib – libki właśnie budowanego modułu (np. libQt6Qml) # $PAGAN_ROOT/usr/lib – zainstalowany Qt6 (np. libQt6Core) # Wersje glibc/libstdc++ w targetcie = host (gcc 16.2.x), więc bezpiecznie. local _qt_env="export LD_LIBRARY_PATH=\"\$PWD/build/lib:$PAGAN_ROOT/usr/lib:\${LD_LIBRARY_PATH:-}\";" local qt_cfg="$_qt_env cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DQT_BUILD_TESTS=OFF -DQT_BUILD_EXAMPLES=OFF && ninja -C build && DESTDIR=$PAGAN_ROOT ninja -C build install" local qt_modules=(qtbase qtsvg qttools) # Sprzątanie zbędnych libek Qt z poprzednich prób (QML/Quick/shadertools oraz # stara wersja), żeby nie trafiły do ISO. NIE ruszamy bieżącej qtbase/qtsvg/ # qttools (mogą być już w resume i nie zostaną reinstalowane). rm -f "$PAGAN_ROOT"/usr/lib/libQt6*.so.6.11.1 \ "$PAGAN_ROOT"/usr/lib/libQt6Qml*.so* \ "$PAGAN_ROOT"/usr/lib/libQt6Quick*.so* \ "$PAGAN_ROOT"/usr/lib/libQt6ShaderTools*.so* 2>/dev/null || true local qm _cfg for qm in "${qt_modules[@]}"; do _cfg="$qt_cfg" if [[ "$qm" == "qtbase" ]]; then # OpenSSL 4: ASN1_STRING jest nieprzezroczysty, a plugin TLS nie linkuje # -lcrypto (Qt ładuje OpenSSL przez dlopen i wrappers q_*). # UWAGA: Qt 6.11.2 ma to już naprawione upstream (QT_OPENSSL4_CONST, # q_ASN1_STRING_*), więc ten sed jest zwykle no-opem – zostawiony jako # zabezpieczenie dla starszych 6.11.x. Ważne: zamiana musi używać q_* # (bezpośrednie ASN1_STRING_* daje „undefined reference” przy linkowaniu). # Rzuty obok muszą być const (get0_data zwraca const unsigned char*). _cfg="sed -i -e 's|auth_key->keyid->data|q_ASN1_STRING_get0_data(auth_key->keyid)|' -e 's|auth_key->keyid->length|q_ASN1_STRING_length(auth_key->keyid)|' -e 's|genName->d\\.iPAddress->data|q_ASN1_STRING_get0_data(genName->d.iPAddress)|' -e 's|serialNumber->length|q_ASN1_STRING_length(serialNumber)|g' -e 's|serialNumber->data\\[a\\]|q_ASN1_STRING_get0_data(serialNumber)[a]|' -e 's|reinterpret_cast, a GCC 16 już go nie dołącza transitively → dodajemy # globalnie -include cstdint. build_lfs_pkg "yaml-cpp" "0.8.0" \ "https://github.com/jbeder/yaml-cpp/archive/refs/tags/0.8.0.tar.gz" "SKIP" \ "cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_CXX_FLAGS=\"-include cstdint\" -DYAML_CPP_BUILD_TESTS=OFF -DYAML_BUILD_SHARED_LIBS=ON && ninja -C build && DESTDIR=\$PAGAN_ROOT ninja -C build install" # libpwquality POMIJAMY: wymaga cracklib (+ słownika), a Calamares ma je jako # zależność OPCJONALNĄ (moduł „users”: find_package(LibPWQuality) bez REQUIRED). # Bez niego instalator działa – tracimy tylko dodatkową ocenę siły hasła. # polkit-qt-1 (Qt6): WYMAGANE przez KPMcore (PolkitQt6-1) i Calamares. # -DQT_MAJOR_VERSION=6 jest konieczne (domyślnie projekt buduje Qt5!). # Korzysta z polkit-gobject-1/polkit-agent-1 (zbudowane w build_blfs). build_lfs_pkg "polkit-qt-1" "0.201.1" \ "https://download.kde.org/stable/polkit-qt-1/polkit-qt-1-0.201.1.tar.xz" "SKIP" \ "$_qt_env cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DQT_MAJOR_VERSION=6 -DBUILD_EXAMPLES=OFF -DBUILD_TEST=OFF && DESTDIR=\$PAGAN_ROOT ninja -C build install" export LD_LIBRARY_PATH="$_old_ld" export XDG_DATA_DIRS="$_old_xdg" export PERL5LIB="$_old_perl5lib" } # ========================= CZYSZCZENIE STALEGO Qt6 QML ========================= # W rootfsie zostawały libki Qt6 QML/Quick z wcześniejszych prób (wersja 6.11.1) # oraz stare KF6 z QML. Mieszanie ich z Qt6Core 6.11.2 dawało przy starcie: # "symbol lookup error: /usr/lib/libQt6Qml.so.6: undefined symbol # ...Qt_6_11_1, version Qt_6_Private_API" # QML nie jest nam potrzebny (Calamares budujemy z WITH_QML=OFF), więc usuwamy cały # ten stos (libki + .prl/.a + cmake + dane) i wymuszamy przebudowę calamaresa, # jeśli zainstalowany libcalamaresui linkuje QML. _purge_stale_qt() { local r="$PAGAN_ROOT/usr" if [[ -e "$r/lib/libQt6Qml.so.6" || -e "$r/lib/libQt6Core.so.6.11.1" ]]; then info " Usuwam stary stos Qt6 QML/Quick (6.11.1) z targetu – nie budujemy QML" fi rm -f "$r"/lib/libQt6Qml* "$r"/lib/libQt6Quick* "$r"/lib/libQt6Labs* \ "$r"/lib/libQt6ShaderTools* "$r"/lib/libQt6*.so.6.11.1 \ "$r"/lib/libKF6*Qml* 2>/dev/null || true rm -rf "$r"/lib/cmake/Qt6Qml* "$r"/lib/cmake/Qt6Quick* "$r"/lib/cmake/Qt6Labs* \ "$r"/lib/cmake/Qt6ShaderTools* 2>/dev/null || true rm -rf "$r"/lib/qt6/qml "$r"/lib/qml "$r"/qml 2>/dev/null || true rm -rf "$r"/include/QtQml* "$r"/include/QtQuick* "$r"/include/QtLabs* \ "$r"/include/QtShaderTools* 2>/dev/null || true rm -f "$r"/bin/qml* "$r"/libexec/qml* 2>/dev/null || true # dangling symlinki (libQt6Qml.so.6 → usunięta .6.11.1 itp.) find "$r"/lib -maxdepth 1 -xtype l \( -name 'libQt6*' -o -name 'libKF6*' \) -delete 2>/dev/null || true # Stary calamares linkujący QML MUSI się przebudować, inaczej po usunięciu # libQt6Qml dostanie "cannot open shared object file". if objdump -p "$r/lib/libcalamaresui.so.3.3.14" 2>/dev/null | grep -q 'NEEDED.*libQt6Qml'; then warn " Calamares: zainstalowany libcalamaresui linkuje QML – wymuszam przebudowę (WITH_QML=OFF)" sed -i '/^calamares-3\.3\.14$/d' "$RESUME_FILE" 2>/dev/null || true fi } # Naprawa stalych configów Qt6 (typowy efekt rollbacku `paga`): libki są już # nowej wersji (np. 6.11.2), a configi CMake wciąż wskazują starą (6.11.1) → # configure pada: „Qt6CoreTargets.cmake ... references the file # /usr/lib/libQt6Core.so.6.11.1 but this file does not exist”. Przepisujemy # wersję w configach na tę, której libki faktycznie istnieją. _fix_stale_qt_cmake() { local libdir="$PAGAN_ROOT/usr/lib" cfgdir="$PAGAN_ROOT/usr/lib/cmake" [[ -d "$cfgdir" ]] || return 0 local cur cur=$(ls "$libdir"/libQt6Core.so.6.* 2>/dev/null \ | sed -n 's/.*\.so\.\(6\.[0-9][0-9.]*\)$/\1/p' | sort -V | tail -1) [[ -n "$cur" ]] || return 0 local v vre for v in $(grep -rhoE 'libQt6[A-Za-z0-9]*\.so\.6\.[0-9]+(\.[0-9]+)*' "$cfgdir" 2>/dev/null \ | sed -E 's/.*\.so\.(6\.[0-9]+(\.[0-9]+)*)/\1/' | sort -u); do [[ "$v" == "$cur" ]] && continue [[ -e "$libdir/libQt6Core.so.$v" ]] && continue vre=$(printf '%s' "$v" | sed 's/\./\\./g') warn "Qt6: configi CMake wskazują $v bez libek – przepisuję na $cur" find "$cfgdir" -type f -exec sed -i "s/$vre/$cur/g" {} + 2>/dev/null || true done } # Pisze branding Calamares (branding.desc + obrazy). Wydzielone, żeby korzystał # z tego zarówno pełny build, jak i fix-calamares.sh. # UWAGA: `slideshow` jest WYMAGANY; przy WITH_QML=OFF musi być LISTĄ obrazków # (scalar = QML → błąd), a wskazane pliki muszą istnieć. _write_calamares_branding() { local _cdir="$PAGAN_ROOT/usr/share/calamares/branding/paganlinux" mkdir -p "$_cdir" cat > "$_cdir/branding.desc" <<'CALEOF' --- componentName: paganlinux # Okno i panele (widget = klasyczne widgety; bez QML) windowExpanding: normal windowPlacement: center sidebar: widget,left navigation: widget,top welcomeStyleCalamares: true strings: productName: "PaganLinux" shortProductName: "PaganLinux" version: "1.0" shortVersion: "1.0" versionedName: "PaganLinux" shortVersionedName: "PaganLinux" bootloaderEntryName: "PaganLinux" productUrl: "https://paganlinux.eu" supportUrl: "https://paganlinux.eu/support" images: productLogo: "logo.png" productIcon: "icon.png" productWelcome: "welcome.png" # Slideshow bez QML: lista obrazków wyświetlanych w trakcie instalacji. slideshow: - "welcome.png" # Klucze stylu są WALIDOWANE case-sensitively (Branding::validateStyleEntries); # nieznane lądują w logu jako warning i są ignorowane. Dlatego dokładnie: # SidebarBackground / SidebarBackgroundCurrent / SidebarText / SidebarTextCurrent. style: SidebarBackground: "#2e3440" SidebarBackgroundCurrent: "#434c5e" SidebarText: "#d8dee9" SidebarTextCurrent: "#88c0d0" CALEOF # Motyw Calamares – Calamares ładuje ten plik automatycznie # (Branding::stylesheet() czyta /stylesheet.qss). Paleta Nord, # spójna z motywem XFCE Nordic. cat > "$_cdir/stylesheet.qss" <<'QSSEOF' /* Pagan Linux – motyw Calamares (Nord) */ QWidget { font-size: 10pt; color: #d8dee9; } #mainApp { background: #2e3440; } #sidebarApp { background: #2e3440; } QPushButton { background: #5e81ac; color: #eceff4; border: 0; border-radius: 6px; padding: 8px 20px; min-height: 26px; font-weight: 600; } QPushButton:hover { background: #81a1c1; } QPushButton:pressed { background: #4c566a; } QPushButton:disabled { background: #434c5e; color: #7b8394; } QPushButton#quitButton { background: transparent; color: #d8dee9; font-weight: 500; } QPushButton#quitButton:hover { background: #434c5e; } QProgressBar { border: 1px solid #4c566a; border-radius: 6px; background: #3b4252; text-align: center; color: #eceff4; } QProgressBar::chunk { background: #88c0d0; border-radius: 5px; } QLineEdit, QComboBox, QSpinBox, QTextEdit { background: #3b4252; color: #eceff4; border: 1px solid #4c566a; border-radius: 6px; padding: 5px; } QLineEdit:focus, QComboBox:focus, QSpinBox:focus { border: 1px solid #88c0d0; } QListView, QTreeView, QTableView { background: #3b4252; color: #eceff4; border: 1px solid #4c566a; border-radius: 6px; } QCheckBox, QRadioButton { color: #d8dee9; } QScrollArea { border: 0; } QSSEOF # Obrazy brandingu z zasobów branding/ – bez nich Calamares zgłasza brak # logo/ikony/welcome (a slideshow musi wskazywać istniejący plik). local _bdir="$SCRIPT_DIR/branding" local _wallpaper="$_bdir/$PAGAN_WALLPAPER" if [[ -f "$_bdir/pagan-loki-pingwin.png" ]]; then install -m644 "$_bdir/pagan-loki-pingwin.png" "$_cdir/logo.png" install -m644 "$_bdir/pagan-loki-pingwin.png" "$_cdir/icon.png" fi if [[ -f "$_wallpaper" ]]; then install -m644 "$_wallpaper" "$_cdir/welcome.png" fi # Fallback: slideshow/welcome musi istnieć. if [[ ! -f "$_cdir/welcome.png" && -f "$_cdir/logo.png" ]]; then install -m644 "$_cdir/logo.png" "$_cdir/welcome.png" fi } # Moduły Calamares wymagane przez naszą sekwencję z /etc/calamares/settings.conf. # Moduły Pythonowe (mount, unpackfs, fstab, bootloader, services-systemd, …) # powstają TYLKO przy -DWITH_PYTHON=ON + -DWITH_PYBIND11=ON. Bez któregokolwiek # Calamares kończy się „Initialization Failed” i nie zainstaluje systemu – # a system MUSI wstać po instalacji, więc brak modułu = twardy błąd builda. _CALAMARES_REQUIRED_MODULES=( welcome summary partition mount unpackfs machineid fstab locale keyboard localecfg users displaymanager networkcfg hwclock services-systemd grubcfg bootloader umount finished # netinstall (wybór pakietów) i packages (backend `pag`) – netinstall sam nic # nie instaluje, więc brak któregokolwiek z nich cicho psuje instalację # dodatkowego oprogramowania. netinstall packages # packagechooser (wybór dodatków po instalacji, m.in. aktualizacja z repo) # + contextualprocess, który tę komendę wykonuje w systemie docelowym. packagechooser contextualprocess ) # 0 = komplet wymaganych modułów jest zainstalowany w rootfsie. _calamares_modules_ok() { local _mdir="$PAGAN_ROOT/usr/lib/calamares/modules" _m [[ -d "$_mdir" ]] || return 1 for _m in "${_CALAMARES_REQUIRED_MODULES[@]}"; do [[ -e "$_mdir/$_m" ]] || return 1 done return 0 } # Wypisuje brakujące moduły (diagnostyka). _calamares_missing_modules() { local _mdir="$PAGAN_ROOT/usr/lib/calamares/modules" _m for _m in "${_CALAMARES_REQUIRED_MODULES[@]}"; do [[ -e "$_mdir/$_m" ]] || printf '%s ' "$_m" done } # ── KPMcore / wtyczki Qt ───────────────────────────────────────────── # Targetowy Qt6 ma QT_INSTALL_PLUGINS=/plugins (tu: /usr/plugins), ale # część pakietów (KF6, kpmcore) instaluje wtyczki do /qt6/plugins. # QCoreApplication::libraryPaths() NIE zawiera tego drugiego katalogu, więc takie # wtyczki nie ładują się (KPMcore → qFatal „Failed to initialize KPMcore backend”). # Bezwzględna ścieżka katalogu wtyczek Qt w BUDOWANYM rootfsie. _qt_runtime_plugin_dir() { local p="" if [[ -x "$PAGAN_ROOT/usr/bin/qmake6" ]]; then p=$(env LD_LIBRARY_PATH="$PAGAN_ROOT/usr/lib:/usr/lib" \ "$PAGAN_ROOT/usr/bin/qmake6" -query QT_INSTALL_PLUGINS 2>/dev/null) || p="" fi [[ -n "$p" ]] || p="$PAGAN_ROOT/usr/plugins" # qmake może zwrócić ścieżkę w przestrzeni docelowej (/usr/...) – przenieś ją # do budowanego rootfsu, jeśli nie zaczyna się od PAGAN_ROOT. if [[ "$p" != "$PAGAN_ROOT"* ]]; then p="$PAGAN_ROOT$p" fi printf '%s\n' "$p" } # Udostępnia wtyczki z /qt6/plugins w katalogu runtime Qt. # KOPIUJE pliki (nie symlinkuje): bezwzględne symlinki wskazywałyby na ścieżki # z build-prefixem ($PAGAN_ROOT/...) i byłyby zepsute po instalacji na /. _sync_qt_plugin_dirs() { local qtp src top name dst f qtp="$(_qt_runtime_plugin_dir)" [[ -d "$qtp" ]] || return 0 # Kolejność ma znaczenie: najpierw katalog używany przez nasz Qt6 (właściwa # wersja wtyczek), potem legacy /plugins (np. stara kpmcore). for src in "$PAGAN_ROOT/usr/lib/qt6/plugins" "$PAGAN_ROOT/usr/lib/plugins"; do [[ -d "$src" ]] || continue for top in "$src"/*; do [[ -d "$top" ]] || continue name=$(basename "$top") dst="$qtp/$name" # Zepsuty symlink z wcześniejszych wersji – usuń. if [[ -L "$dst" ]]; then rm -f "$dst" fi if [[ ! -e "$dst" ]]; then cp -a "$top" "$dst" elif [[ -d "$dst" ]]; then for f in "$top"/*; do [[ -e "$f" ]] || continue [[ -e "$dst/$(basename "$f")" ]] || cp -a "$f" "$dst/" done fi done done } # Czy backend KPMcore jest widoczny dla runtime Qt? _kpmcore_backend_ok() { local qtp qtp="$(_qt_runtime_plugin_dir)" [[ -e "$qtp/kpmcore/pmsfdiskbackendplugin.so" ]] } # KPMcore w dwóch wersjach (różne soname) = Calamares linkuje jedną, a wtyczkę # backendu bierze z drugiej → backend się nie inicjalizuje. Zostaw jedną wersję. _purge_duplicate_kpmcore() { local n n=$(ls "$PAGAN_ROOT"/usr/lib/libkpmcore.so.[0-9]* 2>/dev/null | wc -l) (( n > 1 )) || return 0 warn " KPMcore: wykryto $n plików biblioteki (więcej niż jedna wersja) – czyszczę" rm -f "$PAGAN_ROOT"/usr/lib/libkpmcore.so* rm -rf "$PAGAN_ROOT"/usr/lib/plugins/kpmcore \ "$PAGAN_ROOT"/usr/lib/qt6/plugins/kpmcore \ "$PAGAN_ROOT"/usr/plugins/kpmcore \ "$PAGAN_ROOT"/usr/lib/cmake/KPMcore \ "$PAGAN_ROOT"/usr/include/kpmcore rm -f "$PAGAN_ROOT"/usr/lib/libexec/kpmcore_externalcommand sed -i -E '/^kpmcore-/d' "$RESUME_FILE" 2>/dev/null || true sed -i '/^calamares-3\.3\.14$/d' "$RESUME_FILE" 2>/dev/null || true } # libpwquality w tym systemie jest MARTWE: linkuje libcrack.so.2 (cracklib), # którego w targecie nie ma. Calamares wykrywa je jednak przez find_package # i podlinkowuje moduł „users” → przy starcie „Module users loading FAILED” # (QPluginLoader nie może załadować .so). PAM go nie używa, więc usuwamy. _purge_broken_pwquality() { rm -f "$PAGAN_ROOT"/usr/lib/libpwquality.so* \ "$PAGAN_ROOT"/usr/lib/libpwquality.a \ "$PAGAN_ROOT"/usr/lib/security/pam_pwquality.so \ "$PAGAN_ROOT"/usr/lib/security/pam_pwquality.a \ "$PAGAN_ROOT"/usr/lib/pkgconfig/pwquality.pc \ "$PAGAN_ROOT"/usr/lib/pkgconfig/libpwquality.pc \ "$PAGAN_ROOT"/usr/include/pwquality.h } # os-prober: moduł `partition` Calamaresa woła `os-prober` BEZWARUNKOWO # (PartUtils::runOsprober), a bez binarki loguje „os-prober cannot start.”. # Wchodzi do rootfsu live; unpackfs skopiuje go też do zainstalowanego systemu, # gdzie użyje go grub-mkconfig (wykrywanie innych systemów w menu GRUB). install_os_prober() { build_lfs_pkg "os-prober" "1.85" \ "https://deb.debian.org/debian/pool/main/o/os-prober/os-prober_1.85.tar.xz" \ "f06094db841a214f975e79d66e2a79ea8c2b39b0f762e9d384b2feffd4ae7447" \ "sed -i -e 's|/lib/ld*\.so*|/lib*/ld*.so*|g' os-probes/mounted/common/90linux-distro && rm -f Makefile && make newns && install -Dm755 linux-boot-prober \$PAGAN_ROOT/usr/bin/linux-boot-prober && install -Dm755 os-prober \$PAGAN_ROOT/usr/bin/os-prober && install -Dm755 newns \$PAGAN_ROOT/usr/lib/os-prober/newns && install -Dm755 common.sh \$PAGAN_ROOT/usr/share/os-prober/common.sh && for dir in os-probes os-probes/mounted os-probes/init linux-boot-probes linux-boot-probes/mounted; do install -dm755 \$PAGAN_ROOT/usr/lib/\$dir; install -m755 -t \$PAGAN_ROOT/usr/lib/\$dir \$dir/common/*; if [ -d \"\$dir/x86\" ]; then cp -r \"\$dir/x86/.\" \$PAGAN_ROOT/usr/lib/\$dir/; fi; done && install -Dm755 os-probes/mounted/powerpc/20macosx \$PAGAN_ROOT/usr/lib/os-probes/mounted/20macosx && install -dm755 \$PAGAN_ROOT/var/lib/os-prober" } # /etc/group: (1) usuwa Wiodące białe znaki (psuły nazwy grup: zapisane jako # "\twheel" nie są widoczne jako `wheel`, więc `useradd -G wheel` zawodzi), # (2) dopisuje grupy wymagane przez Calamaresa (`users.defaultGroups`: optical, # storage, scanner, power) oraz `autologin` (autologinGroup) i `users`. # Idempotentne – bezpieczne do wołania przy każdym respinie. _ensure_system_groups() { local gf="$PAGAN_ROOT/etc/group" [[ -f "$gf" ]] || return 0 sed -i 's/^[[:space:]]\+//' "$gf" local item gid g for item in optical:21 scanner:22 storage:23 power:24 users:25 autologin:26; do g="${item%%:*}"; gid="${item##*:}" if ! awk -F: -v G="$g" '{n=$1; gsub(/^[ \t]+|[ \t]+$/,"",n); if(n==G) f=1} END{exit !f}' "$gf"; then printf '%s:x:%s:\n' "$g" "$gid" >> "$gf" fi done } # Awaryjne dopełnienie plików polkita. Polkit instaluje część plików pod # ścieżkami ABSOLUTNYMI, przez co meson dokłada i sysroot, i DESTDIR → lądują # w zagnieżdżonym rootfsie ($PAGAN_ROOT/mnt/pagan/...). Gdy zginą, polkitd # nie wystartuje, a wtedy KPMcore (ExternalCommand → D-Bus helper → polkit) # nie wykona `lsblk` i Calamares zgłasza „There are no partitions to install on”. # Idempotentne: pisze tylko brakujące pliki. _ensure_polkit_units() { [[ -x "$PAGAN_ROOT/usr/lib/polkit-1/polkitd" ]] || return 0 local _dbusdir="$PAGAN_ROOT/usr/share/dbus-1/system-services" local _unitdir="$PAGAN_ROOT/usr/lib/systemd/system" mkdir -p "$_dbusdir" "$_unitdir" "$PAGAN_ROOT/usr/lib/sysusers.d" \ "$PAGAN_ROOT/usr/lib/tmpfiles.d" "$PAGAN_ROOT/var/lib/polkit-1" # Zawsze zapisuj kanoniczne wersje. Sam warunek „jeśli brak” pozostawiał # stary, niekompatybilny unit po wcześniejszym buildzie/resume. cat > "$_dbusdir/org.freedesktop.PolicyKit1.service" <<'PKEOF' [D-BUS Service] Name=org.freedesktop.PolicyKit1 Exec=/usr/lib/polkit-1/polkitd --no-debug User=root SystemdService=polkit.service PKEOF cat > "$_unitdir/polkit.service" <<'PKEOF' [Unit] Description=Authorization Manager Documentation=man:polkit(8) After=dbus.service [Service] Type=simple BusName=org.freedesktop.PolicyKit1 ExecStart=/usr/lib/polkit-1/polkitd --no-debug --log-level=notice User=polkitd Restart=on-failure [Install] WantedBy=multi-user.target PKEOF # Nie polegaj na `systemctl enable` w chroot (może nie działać bez PID 1). # Symlink trafia bezpośrednio do gotowego systemu i uruchamia polkit przy boot. mkdir -p "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants" ln -sfn /usr/lib/systemd/system/polkit.service \ "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants/polkit.service" if [[ ! -f "$_unitdir/polkit-agent-helper@.service" ]]; then cat > "$_unitdir/polkit-agent-helper@.service" <<'PKEOF' [Unit] Description=Authorization Manager Agent Helper Documentation=man:polkit(8) [Service] Type=oneshot DeviceAllow=/dev/null rw DevicePolicy=strict ExecStart=/usr/lib/polkit-1/polkit-agent-helper-1 --socket-activated SuccessExitStatus=2 StandardInput=socket StandardOutput=socket LimitMEMLOCK=0 LockPersonality=yes MemoryDenyWriteExecute=yes NoNewPrivileges=yes PrivateDevices=yes PrivateNetwork=yes PrivateTmp=yes ProtectControlGroups=yes ProtectHome=yes ProtectKernelModules=yes ProtectKernelLogs=yes ProtectKernelTunables=yes ProtectSystem=strict ProtectClock=yes ProtectHostname=yes RemoveIPC=yes RestrictAddressFamilies=AF_UNIX RestrictNamespaces=yes RestrictRealtime=yes RestrictSUIDSGID=yes SystemCallArchitectures=native SystemCallFilter=@system-service UMask=0077 PKEOF fi if [[ ! -f "$_unitdir/polkit-agent-helper.socket" ]]; then cat > "$_unitdir/polkit-agent-helper.socket" <<'PKEOF' [Unit] Description=Authorization Manager Agent Helper Documentation=man:polkit(8) [Socket] Accept=yes RemoveOnStop=yes ListenStream=/run/polkit/agent-helper.socket [Install] WantedBy=sockets.target PKEOF fi [[ -f "$PAGAN_ROOT/usr/lib/sysusers.d/polkit.conf" ]] || \ printf 'u polkitd 110 "User for polkitd"\n' > "$PAGAN_ROOT/usr/lib/sysusers.d/polkit.conf" [[ -f "$PAGAN_ROOT/usr/lib/tmpfiles.d/polkit-tmpfiles.conf" ]] || \ printf 'd /etc/polkit-1/rules.d 0750 root polkitd - -\n' \ > "$PAGAN_ROOT/usr/lib/tmpfiles.d/polkit-tmpfiles.conf" # D-Bus odrzuca żądanie polkita błędem Spawn.PermissionsInvalid, jeśli # setuid helper nie ma dokładnie właściciela root:root i trybu 4755. # Nie polegamy na uprawnieniach zachowanych przez instalator/DESTDIR — # ustawiamy je jawnie przed utworzeniem squashfs. local _polkit_helper="$PAGAN_ROOT/usr/lib/polkit-1/polkit-agent-helper-1" if [[ -e "$_polkit_helper" ]]; then chown root:root "$_polkit_helper" chmod 4755 "$_polkit_helper" else warn " Brak polkit-agent-helper-1 – KPMcore nie będzie mógł autoryzować operacji dyskowych" fi # KPMcore uruchamia własny uprzywilejowany helper przez systemowy D-Bus. # Bez setuid D-Bus odrzuca start komunikatem Spawn.PermissionsInvalid, # a Calamares widzi wtedy 0 urządzeń i pokazuje „There are no partitions”. local _kpm_helper="$PAGAN_ROOT/usr/lib/libexec/kpmcore_externalcommand" if [[ -e "$_kpm_helper" ]]; then chown root:root "$_kpm_helper" chmod 4755 "$_kpm_helper" else warn " Brak kpmcore_externalcommand – moduł partycjonowania nie zadziała" fi # To jest helper samego D-Busa, używany do aktywacji usług systemowych. # Jeśli ma 0755 zamiast 4755, dbus-daemon zwraca: # Spawn.PermissionsInvalid: The permission of the setuid helper is not correct. local _dbus_helper="$PAGAN_ROOT/usr/libexec/dbus-daemon-launch-helper" if [[ -e "$_dbus_helper" ]]; then chown root:root "$_dbus_helper" chmod 4755 "$_dbus_helper" else warn " Brak dbus-daemon-launch-helper – aktywacja usług D-Bus może nie działać" fi } # NetworkManager: jednostki systemd + polityka/aktywacja D-Bus + config + WŁĄCZENIE w live. # # DLACZEGO TO ISTNIEJE: NetworkManager instaluje jednostki i politykę D-Bus do # katalogów wyliczonych przez pkg-config (systemdsystemunitdir, dbus_conf_dir, # dbussystemservicedir). Przy budowie na hoście z PKG_CONFIG_SYSROOT_DIR te # ścieżki są JUŻ z sysrootem, a meson dokłada jeszcze DESTDIR → pliki lądują # w $PAGAN_ROOT/mnt/pagan/... i w systemie ich NIE MA (patrz _merge_nested_rootfs). # # Objawy (dokładnie to widzieliśmy w live i w QEMU): # * /usr/sbin/NetworkManager istnieje, ale nie ma NetworkManager.service → # w live NIKT nie podnosi interfejsu: brak DHCP (`ip a` ma tylko lo, # `nmcli device status` nie działa), mimo że QEMU sieć przekazuje poprawnie # (`-netdev user` + kernel z CONFIG_VIRTIO_NET=y dają gościowi 10.0.2.15), # * brak org.freedesktop.NetworkManager.conf → z # /usr/share/dbus-1/system.conf blokuje NM nazwę na magistrali, # * brak /etc/NetworkManager/NetworkManager.conf (receptura zapisuje go dopiero # na etapie package, którego ten build nie uruchamia). # # Idempotentna: pisze TYLKO brakujące pliki, więc nie nadpisuje nowszych # z przebudowy NM; wołana z finalize_build – naprawia też rootfs zbudowany przed # tą poprawką, bez pełnego przebudowania ISO. _ensure_network_manager() { [[ -x "$PAGAN_ROOT/usr/sbin/NetworkManager" ]] || return 0 local _us="$PAGAN_ROOT/usr/lib/systemd/system" local _pol="$PAGAN_ROOT/usr/share/dbus-1/system.d" local _svc="$PAGAN_ROOT/usr/share/dbus-1/system-services" local _etc="$PAGAN_ROOT/etc/NetworkManager" mkdir -p "$_us" "$_pol" "$_svc" "$_etc/conf.d" "$_etc/system-connections" "$_etc/VPN" local _fixed=0 # 1. Jednostka główna. Treść wierna NetworkManager-1.56.0/data/NetworkManager.service.in, # z podstawionymi ścieżkami tego buildu (sbindir=/usr/sbin). # `Before=network.target` bez @DISTRO_NETWORK_SERVICE@ – ifcfg-rh jest wyłączony. if [[ ! -f "$_us/NetworkManager.service" ]]; then cat > "$_us/NetworkManager.service" <<'NMEOF' [Unit] Description=Network Manager Documentation=man:NetworkManager(8) Wants=network.target After=network-pre.target dbus.service Before=network.target BindsTo=dbus.service [Service] Type=dbus BusName=org.freedesktop.NetworkManager ExecReload=/usr/bin/busctl call org.freedesktop.NetworkManager /org/freedesktop/NetworkManager org.freedesktop.NetworkManager Reload u 0 ExecStart=/usr/sbin/NetworkManager --no-daemon Restart=on-failure # NM nie chce, żeby systemd zabijał jego procesy potomne KillMode=process # Przy dużej liczbie interfejsów start potrafi się wydłużyć. TimeoutStartSec=600 CapabilityBoundingSet=CAP_NET_ADMIN CAP_DAC_OVERRIDE CAP_NET_RAW CAP_BPF CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_MODULE CAP_AUDIT_WRITE CAP_KILL CAP_SYS_CHROOT ProtectSystem=true ProtectHome=read-only LimitNOFILE=65536 [Install] WantedBy=multi-user.target Also=NetworkManager-dispatcher.service NMEOF _fixed=$((_fixed + 1)) fi # 2. Dispatcher (NM startuje go po D-Bus, stąd Type=dbus + alias dbus-org.*). if [[ ! -f "$_us/NetworkManager-dispatcher.service" ]]; then cat > "$_us/NetworkManager-dispatcher.service" <<'NMEOF' [Unit] Description=Network Manager Script Dispatcher Service Documentation=man:NetworkManager-dispatcher.service(8) [Service] Type=dbus BusName=org.freedesktop.nm_dispatcher ExecStart=/usr/libexec/nm-dispatcher NotifyAccess=main KillMode=process [Install] Alias=dbus-org.freedesktop.nm-dispatcher.service NMEOF _fixed=$((_fixed + 1)) fi # 3. Aktywacja D-Bus dispatchera (NM nie ma własnego pliku aktywacji – # korzysta z Type=dbus i BindsTo=dbus.service). if [[ ! -f "$_svc/org.freedesktop.nm_dispatcher.service" ]]; then cat > "$_svc/org.freedesktop.nm_dispatcher.service" <<'NMEOF' [D-BUS Service] Name=org.freedesktop.nm_dispatcher Exec=/usr/libexec/nm-dispatcher User=root SystemdService=dbus-org.freedesktop.nm-dispatcher.service NMEOF _fixed=$((_fixed + 1)) fi # 4. Polityka D-Bus – BEZ NIEJ NM nie zajmie nazwy org.freedesktop.NetworkManager # (system.conf ma dla kontekstu domyślnego). # Treść 1:1 z NetworkManager-1.56.0/src/core/org.freedesktop.NetworkManager.conf. if [[ ! -f "$_pol/org.freedesktop.NetworkManager.conf" ]]; then cat > "$_pol/org.freedesktop.NetworkManager.conf" <<'NMEOF' 1024 2048 NMEOF _fixed=$((_fixed + 1)) fi # 5. Polityka D-Bus dispatchera (NetworkManager-1.56.0/src/nm-dispatcher/nm-dispatcher.conf). if [[ ! -f "$_pol/nm-dispatcher.conf" ]]; then cat > "$_pol/nm-dispatcher.conf" <<'NMEOF' NMEOF _fixed=$((_fixed + 1)) fi # 6. Minimalna konfiguracja. Bez tego pliku NM działa na wbudowanych # domyślnych – wolimy mieć jawnie to, co deklaruje receptura. if [[ ! -f "$_etc/NetworkManager.conf" ]]; then cat > "$_etc/NetworkManager.conf" <<'NMEOF' [main] plugins=keyfile NMEOF _fixed=$((_fixed + 1)) fi # 7. DNS: wymuś plugin `default` (NM sam zapisuje /etc/resolv.conf). # Powód: w obrazie JEST plik aktywacji D-Bus systemd-resolved # (/usr/share/dbus-1/system-services/org.freedesktop.resolve1.service), # więc NM może przełączyć się na plugin systemd-resolved, którego # w tym systemie nikt nie uruchamia – wtedy DNS nie działa. if [[ ! -f "$_etc/conf.d/dns.conf" ]]; then cat > "$_etc/conf.d/dns.conf" <<'NMEOF' [main] dns=default NMEOF _fixed=$((_fixed + 1)) fi # 8. WŁĄCZENIE W LIVE. Calamares włącza NM dopiero na instalowanym systemie # (services-systemd.conf), więc bez tego dowiązania live nie ma jak # podnieść sieci. Nie polegamy na `systemctl enable` (w chroot może nie # działać bez PID 1) – piszemy symlinki wprost. mkdir -p "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants" ln -sfn /usr/lib/systemd/system/NetworkManager.service \ "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants/NetworkManager.service" # Alias dispatchera (jego jednostka nie ma WantedBy, tylko Alias=) ln -sfn /usr/lib/systemd/system/NetworkManager-dispatcher.service \ "$PAGAN_ROOT/etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service" # NetworkManager-wait-online.service celowo nieaktywny (tak jak w recepturze: # post-install robi `systemctl disable NetworkManager-wait-online`) – # blokowałby start na czekaniu na sieć online. rm -f "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants/NetworkManager-wait-online.service" 2>/dev/null || true if [[ $_fixed -gt 0 ]]; then ok " NetworkManager: dopisano $_fixed brakujących plików (jednostki/D-Bus/config)" fi ok " NetworkManager: włączony w live + polityka D-Bus (network.target)" } # Kompiluje bazę stref czasowych (tzdata) za pomocą zic. # # DLACZEGO: stara receptura tylko KOPIOWAŁA pliki źródłowe do # /usr/share/zoneinfo, więc w systemie nie było ŻADNEJ skompilowanej strefy # (np. Europe/Warsaw), tylko surowe pliki „europe”, „asia”, „africa”… # Moduł locale Calamaresa (SetTimezoneJob) sprawdza # /usr/share/zoneinfo// i przerywa instalację błędem: # „Cannot access selected timezone path. Bad path: /tmp/calamares-root/usr/share/zoneinfo/Europe/Warsaw”. # # Idempotentna: gdy strefy są już skompilowane (i nie ma surowych plików), # kończy natychmiast. # Katalog tymczasowy `mktemp -d` ma uprawnienia 0700 – po `mv` korzeń # /usr/share/zoneinfo dziedziczył je i TAK TRAFIAŁ DO ISO (drwx------). # Procesy nie-root nie mogły wtedy czytać stref (glibc przy TZ=…, panel XFCE, # systemd-timedated), a Calamares raportował „Bad path …/zoneinfo//”. _fix_zoneinfo_perms() { local _zi="${1:-$PAGAN_ROOT/usr/share/zoneinfo}" [[ -d "$_zi" ]] || return 0 chmod 0755 "$_zi" 2>/dev/null || true # a+rX: czytanie dla wszystkich, wykonanie tylko tam, gdzie już było # (katalogi) – czyli bez rozdawania +x plikom danych. chmod -R a+rX "$_zi" 2>/dev/null || true } _install_tzdata() { local _zi="$PAGAN_ROOT/usr/share/zoneinfo" # Kanoniczny test: skompilowana strefa istnieje, a surowe pliki zniknęły. if [[ -f "$_zi/Europe/Warsaw" && ! -e "$_zi/europe" ]]; then # Strefy już są – ale napraw uprawnienia (stare ISO miały 0700). _fix_zoneinfo_perms "$_zi" return 0 fi # zic: najpierw z targetu (właściwa wersja), potem z hosta (pliki TZif są # niezależne od architektury). local _zic="" _c for _c in "$PAGAN_ROOT/usr/bin/zic" "$PAGAN_ROOT/usr/sbin/zic" "$PAGAN_ROOT/bin/zic"; do [[ -x "$_c" ]] && { _zic="$_c"; break; } done if [[ -z "$_zic" ]]; then _zic="$(command -v zic 2>/dev/null || true)" fi if [[ -z "$_zic" ]]; then warn "tzdata: brak zic – strefy czasowe pozostaną nieskompilowane (Calamares padnie na wyborze strefy)" return 0 fi # Źródło danych: rozpakowany tarball albo surowe pliki skopiowane przez # wcześniejszą, wadliwą receptę. local _src="" _d for _d in "$SRC_DIR"/tzdata-*; do [[ -f "$_d/europe" ]] && { _src="$_d"; break; } done local _tmp_src="" if [[ -z "$_src" && -f "$_zi/europe" ]]; then _tmp_src="$(mktemp -d)" local _f for _f in etcetera southamerica northamerica europe africa antarctica \ asia australasia backward leapseconds leap-seconds.list \ zone.tab zone1970.tab iso3166.tab; do [[ -f "$_zi/$_f" ]] && cp -f "$_zi/$_f" "$_tmp_src/" done _src="$_tmp_src" fi if [[ -z "$_src" ]]; then warn "tzdata: brak danych źródłowych (SRC_DIR/tzdata-* ani surowe pliki w zoneinfo) – pomijam" return 0 fi info " tzdata: kompilacja stref czasowych ($_zic)" local _out _out="$(mktemp -d)" mkdir -p "$_out/posix" "$_out/right" # -L /dev/null = strefy bez sekund przestępnych (domyślne), # /right = z sekundami przestępnymi (TZ=…right), # ( cd … && … ) bo zic czyta pliki wejściowe z katalogu bieżącego. local _tz _ok=1 for _tz in etcetera southamerica northamerica europe africa antarctica asia australasia backward; do ( cd "$_src" && "$_zic" -L /dev/null -d "$_out" "$_tz" ) || _ok=0 ( cd "$_src" && "$_zic" -L /dev/null -d "$_out/posix" "$_tz" ) || _ok=0 ( cd "$_src" && "$_zic" -L leapseconds -d "$_out/right" "$_tz" ) || _ok=0 done if [[ "$_ok" != 1 || ! -f "$_out/Europe/Warsaw" ]]; then warn "tzdata: kompilacja stref nie powiodła się – zostawiam poprzednie dane" rm -rf "$_out" "$_tmp_src" return 0 fi # Pliki pomocnicze i posixrules (TZ=EST5EDT itp.). local _t for _t in zone.tab zone1970.tab iso3166.tab leapseconds leap-seconds.list; do [[ -f "$_src/$_t" ]] && cp -f "$_src/$_t" "$_out/" done ( cd "$_src" && "$_zic" -d "$_out" -p Europe/Warsaw ) || true # Podmiana atomowa: najpierw znikają surowe pliki źródłowe. rm -rf "$_zi" mv "$_out" "$_zi" # $_out pochodzi z mktemp -d (0700!) – bez tego poprawka nie ma sensu. _fix_zoneinfo_perms "$_zi" rm -rf "$_tmp_src" # Domyślna strefa systemu live (Calamares i tak ustawia ją w docelowym # systemie według wyboru użytkownika). if [[ ! -e "$PAGAN_ROOT/etc/localtime" ]]; then ln -sf /usr/share/zoneinfo/Europe/Warsaw "$PAGAN_ROOT/etc/localtime" fi ok " tzdata: strefy skompilowane ($(find "$_zi" -type f | wc -l) plików, Europe/Warsaw ✓)" } # Zapisuje konfigurację Calamaresa: settings.conf + pliki modułów. Wydzielone, # żeby korzystał z tego zarówno pełny build, jak i fix-calamares.sh (respin ISO # nie może zostawić starego settings.conf). # # UWAGA: `modules-search: [ local ]` jest OBOWIĄZKOWE – Calamares buduje z niego # listę ścieżek (Settings::interpretModulesSearch); bez tego klucza lista jest # pusta i KAŻDY moduł kończy się „not found in module search paths”. _write_calamares_config() { mkdir -p "$PAGAN_ROOT/etc/calamares/modules" cat > "$PAGAN_ROOT/etc/calamares/settings.conf" <<'CALEOF' --- # Pagan Linux – Calamares: instalacja z live (squashfs przez moduł unpackfs). modules-search: [ local ] sequence: - show: [welcome, locale, keyboard, partition, users, netinstall, packagechooser, summary] - exec: [partition, mount, unpackfs, machineid, fstab, locale, keyboard, localecfg, users, networkcfg, packages, contextualprocess, displaymanager, hwclock, services-systemd, grubcfg, bootloader, umount] - show: [finished] branding: paganlinux CALEOF # Źródło systemu dla Calamaresa: drzewo squashfs wystawione przez initramfs # pod /live/root. NIE używamy /run/live/root – systemd montuje tmpfs na /run # i zasłoniłby ten katalog. sourcefs "file" = kopiowanie drzewa 1:1. # exclude: ikona instalatora ma być TYLKO na pulpicie live (root), nie w # zainstalowanym systemie. cat > "$PAGAN_ROOT/etc/calamares/modules/unpackfs.conf" <<'CALEOF' --- unpack: - source: "/live/root" sourcefs: "file" destination: "" exclude: - "/root/Desktop/pagan-calamares.desktop" - "/root/Desktop/pagan-wifi.desktop" CALEOF cat > "$PAGAN_ROOT/etc/calamares/modules/partition.conf" <<'CALEOF' --- # Przy automatycznym "Wymaż dysk" używamy GPT na BIOS i UEFI. # Na BIOS+GPT Calamares wymaga ręcznego 8 MiB bios_grub z flagą BiosGrub; # nie dodajemy go do layoutu, bo 3.3.14 nie ustawia tej flagi z partitionLayout. defaultPartitionTableType: gpt defaultFileSystemType: "ext4" # f2fs USUNIĘTE: kernel nie ma CONFIG_F2FS_FS → instalacja na f2fs nie wstałaby. availableFileSystemTypes: ["ext4","xfs","btrfs"] # Jawny układ automatycznej instalacji. Na UEFI Calamares doda ESP # automatycznie przed tym wpisem; BIOS+GPT wymaga ręcznego bios_grub. partitionLayout: - name: "root" size: 100% filesystem: "ext4" mountPoint: "/" # WYMAGANE przez schemat modułu (partition.schema.yaml: required: [userSwapChoices]). userSwapChoices: [ none, file ] # `enableLuks` NIE istnieje w Calamares 3.3 (był ignorowany). Szyfrowanie w UI # włącza dopiero to: enableLuksAutomatedPartitioning: true luksGeneration: luks2 requiredStorage: 8.0 CALEOF # users.conf – zgodnie ze schematem wymagane są defaultGroups, autologinGroup # i sudoersGroup. Bez sudoersGroup nowy użytkownik NIE dostaje sudo. # Grupy (optical/storage/scanner/power/autologin) tworzy builder w /etc/group. cat > "$PAGAN_ROOT/etc/calamares/modules/users.conf" <<'CALEOF' --- defaultGroups: [lp, video, audio, optical, storage, scanner, power, wheel] autologinGroup: autologin sudoersGroup: wheel setRootPassword: true doAutologin: false user: shell: /usr/bin/fish CALEOF # Moduł włącza menedżera logowania na ZAINSTALOWANYM systemie (lightdm) – # bez tego system wstaje do trybu tekstowego (brak GUI po instalacji). cat > "$PAGAN_ROOT/etc/calamares/modules/displaymanager.conf" <<'CALEOF' --- displaymanagers: [lightdm] basicSetup: false CALEOF # UWAGA: nazwa pliku = nazwa modułu, a KLUCZ to `units` (nie `services`!). # services-systemd/main.py czyta cfg.get("units", []) – ze starą nazwą klucza # lightdm/NetworkManager NIE zostałyby włączone na zainstalowanym systemie # (brak GUI po instalacji). cat > "$PAGAN_ROOT/etc/calamares/modules/services-systemd.conf" <<'CALEOF' --- units: - name: NetworkManager action: enable - name: lightdm action: enable - name: pipewire action: enable - name: sshd action: disable CALEOF # ── Netinstall (dobór pakietów z repo PAG) + moduł packages ── # # `netinstall` sam NIE instaluje – zapisuje wybór do globalstorage, a moduł # `packages` (backend `pag`, dodany przez _install_pag_package_backend) woła # `pag install -y ` w systemie docelowym. Dlatego: # • netinstall jest w `show`, packages w `exec` PO unpackfs, # • networkcfg idzie PRZED packages (system docelowy ma konfigurację sieci), # • grupy mają critical: false → błąd pojedynczego pakietu nie przerywa # instalacji (moduł packages dostaje je jako „try_install”). cat > "$PAGAN_ROOT/etc/calamares/modules/packages.conf" <<'CALEOF' --- backend: pag # Bez sieci pomiń krok pakietów zamiast przerywać instalację. skip_if_no_internet: true # Odśwież indeks repo w systemie docelowym ("pag sync"). update_db: true # Pełny upgrade systemu podczas instalacji byłby zbyt wolny – pakiety i tak # pochodzą z tego samego repozytorium co obraz. update_system: false CALEOF cat > "$PAGAN_ROOT/etc/calamares/modules/netinstall.conf" <<'CALEOF' --- # Grupy pakietów trzymamy w tym samym pliku (groupsUrl: local). groupsUrl: local required: false label: sidebar: "Pakiet dodatkowe" sidebar[pl]: "Pakiety dodatkowe" title: "Wybierz dodatkowe oprogramowanie" title[pl]: "Wybierz dodatkowe oprogramowanie" groups: - name: "Przeglądarka" description: "Firefox ESR z repozytorium PaganOS" selected: false critical: false packages: - firefox - name: "Biuro" description: "LibreOffice z czytnikiem PDF (polskie tłumaczenie jest w pakiecie)" selected: false critical: false packages: - libreoffice - name: "Multimedia" description: "Kodeki, odtwarzacz i edycja grafiki" selected: false critical: false packages: - vlc - gimp - ffmpeg - name: "Narzędzia" description: "Podstawowe narzędzia użytkownika" selected: false critical: false packages: - htop - nano - git - name: "Polskie czcionki" description: "Dodatkowe kroje z pełną polską diakrytyką" selected: false critical: false packages: - dejavu-ttf - noto-fonts - noto-fonts-emoji # ── Grafika ────────────────────────────────────────────────────────── # mesa w obrazie ISO jest zbudowana pod instalator (softpipe, bez GLX/X11) # – repo ma pełne mesa (gallium all + Vulkan). Bez tej grupy desktop # zostaje na renderowaniu programowym. - name: "Akceleracja 3D (Mesa + Vulkan)" description: "Sprzętowe OpenGL/GLX i Vulkan – zalecane na każdym desktopie (mesa z repo ma pełne sterowniki)" selected: false critical: false packages: - mesa - libglvnd - vulkan-icd-loader - name: "Sterowniki AMD (amdgpu)" description: "Dla kart AMD Radeon: xf86-video-amdgpu + firmware amdgpu" selected: false critical: false packages: - xorg-xf86-video-amdgpu - amdgpu-firmware - name: "Sterowniki Intel" description: "Dla zintegrowanych kart Intel: xf86-video-intel + intel-media-driver (VA-API)" selected: false critical: false packages: - xorg-xf86-video-intel - intel-media-driver - name: "Sterowniki NVIDIA (DKMS)" description: "Dla kart NVIDIA: gotowe moduły nvidia-kernel (do jądra z repo) + nvidia-dkms. Instaluje pakiet kernel i kompiluje moduły – trwa dłużej" selected: false critical: false packages: - nvidia-kernel - nvidia-dkms # ── Sprzęt dodatkowy ───────────────────────────────────────────────── - name: "Drukarki i skanery" description: "CUPS, sterowniki drukarek i skanerów oraz narzędzie konfiguracji" selected: false critical: false packages: - cups - cups-filters - ghostscript - gutenprint - sane - system-config-printer - name: "Wirtualizacja" description: "QEMU i libvirt – maszyny wirtualne" selected: false critical: false packages: - qemu - libvirt CALEOF # ── Dodatki po instalacji: packagechooser + contextualprocess ── # # `packagechooser` to JEDEN ekran wyboru (0 lub 1 pozycja). Zapisuje wybór # do globalstorage pod kluczem packagechooser_ (Config.cpp: # "packagechooser_" + key.id(); bez własnego instances = packagechooser_packagechooser), # a `contextualprocess` (moduł C++, bez Pythona) wykonuje komendę przypisaną # do tej wartości – już w systemie docelowym (dontChroot: false). # # Dlaczego nie `update_system: true` w packages.conf: moduł packages robi # upgrade PRZED instalacją wybranych pakietów, a my chcemy PO nich – i z # własnym DNS w chrocie. Dodatkowo `pag upgrade` (inaczej niż `pag update`) # przebudowuje initramfs i woła grub-update, a bootloaderem zajmuje się # późniejszy moduł Calamaresa (grubcfg/bootloader) – dlatego w tym kroku # aktualizujemy tylko PAKIETY. cat > "$PAGAN_ROOT/etc/calamares/modules/packagechooser.conf" <<'CALEOF' --- # 0 lub 1 pozycja: "" albo id wybranego elementu. mode: optional # "legacy" = moduł tylko zapisuje wybór do globalstorage; wykonanie → contextualprocess. method: legacy labels: step: "Dodatki" step[pl]: "Dodatki" items: - id: "" name: "Nic dodatkowego (najszybciej)" name[pl]: "Nic dodatkowego (najszybciej)" description: "System zainstaluje się dokładnie w wersji z obrazu. Pakiety można zaktualizować później: sudo pag upgrade" description[pl]: "System zainstaluje się dokładnie w wersji z obrazu. Pakiety można zaktualizować później: sudo pag upgrade" screenshot: ":/images/no-selection.png" - id: "upgrade" name: "Zaktualizuj pakiety z repozytorium" name[pl]: "Zaktualizuj pakiety z repozytorium" description: "Po instalacji pobiera indeks repo i aktualizuje pakiety z repozytorium PAG (m.in. jądro z obrazu). Wymaga internetu i trwa dłużej" description[pl]: "Po instalacji pobiera indeks repo i aktualizuje pakiety z repozytorium PAG (m.in. jądro z obrazu). Wymaga internetu i trwa dłużej" screenshot: "logo.png" CALEOF cat > "$PAGAN_ROOT/etc/calamares/modules/contextualprocess.conf" <<'CALEOF' --- # Uruchamiane w chrocie systemu docelowego. dontChroot: false # Limit: pełna aktualizacja na wolnym łączu potrafi potrwać. timeout: 5400 # Klucz = wartość wybrana w packagechooser ("" = nic nie wybrano). packagechooser_packagechooser: # Lista = osobne polecenia. Prefiks "-" ignoruje kod wyjścia – brak sieci # albo brak aktualizacji nie może wywalić całej instalacji. "upgrade": - "echo '[pagan] Aktualizacja pakietów z repo (pag update)...'" - test -s /etc/resolv.conf || printf 'nameserver 1.1.1.1\nnameserver 8.8.8.8\n' > /etc/resolv.conf - "-pag sync" - "-pag update -y" "": /bin/true "*": /bin/true CALEOF # Sprzątanie: dawna nazwa pliku (moduł `services` nie istnieje w 3.3.14). rm -f "$PAGAN_ROOT/etc/calamares/modules/services.conf" # grubcfg: preferGrubEfi NIE jest kluczem tego modułu (nie ma go w źródłach # Calamares 3.3.14). Poprawne klucze: overwrite/kernel_params/defaults. # Konsola szeregowa (PAGAN_SERIAL_CONSOLE=1): przy testach w QEMU/KVM # `-serial file:log` daje pełny log startu ZAINSTALOWANEGO systemu jako tekst # (bez tego stage-2 w VM nie zapisuje nic – nie ma gdzie zobaczyć, dlaczego # system nie wstaje). console=tty0 PRZED ttyS0 zostawia jądro na ekranie; # ttyS0 jako ostatnia przejmuje /dev/console, żeby logi systemd trafiły na serial. local _kernel_params='[ "quiet", "splash" ]' if _is_true "$PAGAN_SERIAL_CONSOLE"; then _kernel_params='[ "quiet", "splash", "console=tty0", "console=ttyS0", "systemd.journald.forward_to_console=yes" ]' fi cat > "$PAGAN_ROOT/etc/calamares/modules/grubcfg.conf" < "$PAGAN_ROOT/etc/calamares/modules/bootloader.conf" <<'CALEOF' --- # Bootloader: GRUB (BIOS i UEFI). efiBootLoader: "grub" # Wykrywanie jąder (systemd-boot/refind; dla GRUB-a nieszkodliwe). kernelSearchPath: "/usr/lib/modules" kernelPattern: "^vmlinuz.*" loaderEntries: - "timeout 5" - "console-mode keep" kernelParams: [ "quiet" ] # Nazwy programów GRUB-a użytych przez moduł. grubInstall: "grub-install" grubMkconfig: "grub-mkconfig" grubCfg: "/boot/grub/grub.cfg" grubProbe: "grub-probe" # Wymagane: modyfikowanie wpisów firmware (efi_boot_next). efiBootMgr: "efibootmgr" # Kopiuj GRUB-a na ścieżkę fallback (EFI/BOOT/BOOTX64.EFI). installEFIFallback: true CALEOF # networkcfg nie ma opcji konfiguracji – kopiuje konfigurację sieci z live # do docelowego systemu (writeNetworkManager nie istnieje w 3.3.14). cat > "$PAGAN_ROOT/etc/calamares/modules/networkcfg.conf" <<'CALEOF' --- CALEOF # Pusty URL = przycisk ukryty (bool `false` byłby potraktowany jako adres). # # requirements: BEZ tej sekcji Calamares loguje „no valid requirements map # found in welcome module configuration” i nie sprawdza NIC (domyślnie # requiredStorage/requiredRam = -1). Dajemy konkretne progi pod desktop XFCE. # # „internet” jest w `check`, ale NIE w `required`: sprawdzenie ustawia # globalstorage["hasInternet"], którego używa moduł packages/netinstall # (skip_if_no_internet, pobranie indeksu repo), a brak sieci NIE blokuje # instalacji – użytkownik może ją zrobić offline. # Świadomie NIE sprawdzamy „power” (VM bez baterii). cat > "$PAGAN_ROOT/etc/calamares/modules/welcome.conf" <<'CALEOF' --- showDonateUrl: "" showSupportUrl: true showKnownIssuesUrl: false showReleaseNotesUrl: false requirements: requiredStorage: 8.0 requiredRam: 1.0 check: - storage - ram - root - internet required: - ram CALEOF # restartNowEnabled jest deprecated – używamy restartNowMode. cat > "$PAGAN_ROOT/etc/calamares/modules/finished.conf" <<'CALEOF' --- restartNowMode: user-checked restartNowCommand: "systemctl reboot" notifyOnFinished: false CALEOF } # ========================= CALAMARES INSTALLER ========================= install_calamares() { step "INSTALACJA CALAMARES (Instalator systemu)" export PATH="$TOOLS_DIR/bin:$PATH" export PKG_CONFIG_PATH="$PAGAN_ROOT/usr/lib/pkgconfig:$PAGAN_ROOT/usr/share/pkgconfig" # Pozbądź się zaległego Qt6 QML/Quick (6.11.1) i starych KF6-QML z targetu # (mieszanie z Qt6Core 6.11.2 = "symbol lookup error: libQt6Qml.so.6"). _purge_stale_qt _fix_stale_qt_cmake # KPMcore w dwóch wersjach = backend KPMcore się nie załaduje. _purge_duplicate_kpmcore # Binarka jest, ale brakuje modułów z settings.conf (np. stary build # z -DWITH_PYTHON=OFF) → wymuś przebudowę, inaczej „Initialization Failed”. if [[ -x "$PAGAN_ROOT/usr/bin/calamares" ]] && ! _calamares_modules_ok; then warn " Calamares: brak modułów ($(_calamares_missing_modules)) – wymuszam przebudowę" sed -i '/^calamares-3\.3\.14$/d' "$RESUME_FILE" 2>/dev/null || true fi # KPMcore/Calamares szukają ECM (share/ECM/cmake) i przez FindKF6.cmake # komponentów KF6 – wszystko w ROOTFSIE. Bez CMAKE_PREFIX_PATH cmake widzi # tylko hosta (który nie ma ECM) → „Could not find ECM”. Target-first. export CMAKE_PREFIX_PATH="$PAGAN_ROOT/usr:/usr" # Jawne wskazanie ECM – kpmcore/calamares używają go do FindKF6.cmake. [[ -f "$PAGAN_ROOT/usr/share/ECM/cmake/ECMConfig.cmake" ]] && \ export ECM_DIR="$PAGAN_ROOT/usr/share/ECM/cmake" export LD_LIBRARY_PATH="$PAGAN_ROOT/usr/lib:/usr/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" export XDG_DATA_DIRS="$PAGAN_ROOT/usr/share${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}" # Calamares 3.3 wymaga Qt6 + KF6 oraz kpmcore (moduł partycjonowania). # Przy DE=XFCE stosu Qt6/KF6 nie ma – budujemy go (install_calamares_deps). # Gdy się nie powiedzie, fallback: repo PAG (`pag install calamares`). local _have_qt6=0 _have_kf6=0 _have_extra=1 _calamares_from_repo=0 [[ -e "$PAGAN_ROOT/usr/lib/libQt6Core.so.6" ]] && _have_qt6=1 [[ -d "$PAGAN_ROOT/usr/lib/kf6" || -e "$PAGAN_ROOT/usr/lib/libKF6CoreAddons.so.6" ]] && _have_kf6=1 # Sprawdzamy też yaml-cpp i polkit-qt-1: bez nich kpmcore/calamares się nie # skonfigurują, a sam warunek Qt6/KF6 po restarcie pominąłby ich budowę. [[ -e "$PAGAN_ROOT/usr/lib/libyaml-cpp.so.0.8" || -e "$PAGAN_ROOT/usr/lib/libyaml-cpp.so" ]] || _have_extra=0 [[ -e "$PAGAN_ROOT/usr/lib/cmake/PolkitQt6-1/PolkitQt6-1Config.cmake" ]] || _have_extra=0 # KF6 CoreAddons metatypes (bez tego kpmcore nie skonfiguruje się – patrz # KDE_INSTALL_QTMETATYPESDIR w install_calamares_deps). [[ -e "$PAGAN_ROOT/usr/metatypes/qt6kf6coreaddons_metatypes.json" ]] || _have_extra=0 # Stare configi KF6 eksportujące QML bez libki → trzeba przebudować KF6 (bez QML), # inaczej find_package(KF6Config/KF6I18n) rzuca twardy błąd. if grep -q "Qml" "$PAGAN_ROOT/usr/lib/cmake/KF6Config/KF6ConfigTargets.cmake" 2>/dev/null; then _have_extra=0; fi if grep -q "Qml" "$PAGAN_ROOT/usr/lib/cmake/KF6I18n/KF6I18nTargets.cmake" 2>/dev/null; then _have_extra=0; fi if [[ $_have_qt6 -ne 1 || $_have_kf6 -ne 1 || $_have_extra -ne 1 ]]; then info " Brak Qt6/KF6 w rootfs – buduję stos zależności Calamares (Qt6 + KF6)..." if ( set +e; install_calamares_deps ); then ok " ✓ stos Qt6 + KF6 zbudowany" else warn " ✗ Budowa Qt6/KF6 nie powiodła się – próbuję repo PAG..." if _pag_install calamares; then ok " ✓ calamares z repo PAG (zależności Qt6/KF6/kpmcore)" _calamares_from_repo=1 elif [[ -x "$PAGAN_ROOT/usr/bin/calamares" ]]; then info " ✓ calamares już obecny w rootfs" _calamares_from_repo=1 else error "Brak Calamares: Qt6/KF6 nie zbudowane i brak w repo PAG (pomiń świadomie: --no-calamares)" fi fi fi if [[ "$_calamares_from_repo" != "1" ]]; then # os-prober – moduł partition woła go bezwarunkowo. if ! ( set +e; install_os_prober ); then warn " ✗ os-prober: budowa nieudana (Calamares zaloguje 'os-prober cannot start.')" fi # Martwe pwquality (brak libcrack) – usuń, żeby moduł „users” się go nie # podlinkował i nie failował przy starcie Calamaresa. _purge_broken_pwquality # KPMcore + Calamares ze źródeł. Best-effort: awaria → repo PAG. if ! ( set +e # KPMcore – biblioteka partycjonowania build_lfs_pkg "kpmcore" "26.04.3" \ "https://download.kde.org/stable/release-service/26.04.3/src/kpmcore-26.04.3.tar.xz" \ "SKIP" \ "cmake -B build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -G Ninja && DESTDIR=\$PAGAN_ROOT ninja -C build install" # Calamares – MUSI mieć moduły Pythonowe (mount/unpackfs/fstab/ # bootloader/services-systemd/…), inaczej brakuje modułów z settings.conf # („Initialization Failed”). Wymuszamy targetowy Python 3.14. # Bez QML (nie budujemy qtdeclarative); bez schemat-testów. build_lfs_pkg "calamares" "3.3.14" \ "https://github.com/calamares/calamares/releases/download/v3.3.14/calamares-3.3.14.tar.gz" \ "SKIP" \ "cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DWITH_QT6=ON -DWITH_QML=OFF -DWITH_PYTHON=ON -DWITH_PYBIND11=ON -DCMAKE_DISABLE_FIND_PACKAGE_LibPWQuality=TRUE -DPython_EXECUTABLE=\$PAGAN_ROOT/usr/bin/python3 -DPython_ROOT_DIR=\$PAGAN_ROOT/usr -DBUILD_SCHEMA_TESTING=OFF -DBUILD_TESTING=OFF -DBUILD_CRASH_REPORTING=OFF -DINSTALL_CONFIG=ON && DESTDIR=\$PAGAN_ROOT ninja -C build install" ); then warn " ✗ Budowa kpmcore/calamares nie powiodła się – próbuję repo PAG..." _pag_install calamares || warn " ✗ Nie udało się także z repo PAG – brak instalatora" fi fi # Konfiguracja mkdir -p "$PAGAN_ROOT/etc/calamares" mkdir -p "$PAGAN_ROOT/usr/share/calamares/branding/paganlinux" # Wtyczki z /qt6/plugins (kpmcore, kf6) muszą być widoczne w katalogu # runtime Qt (/plugins) – inaczej backend KPMcore nie wstanie. _sync_qt_plugin_dirs _write_calamares_config _write_calamares_branding # Twarda kontrola: instalator MUSI istnieć. Gdy Calamares jest włączony # (domyślnie), brak instalatora = przerwanie builda (nie robimy ISO bez niego). # Świadome pominięcie: --no-calamares (wtedy ta funkcja w ogóle nie jest wołana). if [[ ! -x "$PAGAN_ROOT/usr/bin/calamares" ]]; then error "Calamares nie powstał – przerywam build (świadomie pominiesz instalator: --no-calamares)" fi # Twarda kontrola: instalator MUSI mieć komplet modułów używanych przez # settings.conf. Bez modułów Pythonowych Calamares odpali się, pokaże # „Initialization Failed” i NIE zainstaluje systemu. if ! _calamares_modules_ok; then error "Calamares: brakujące moduły [$(_calamares_missing_modules)] – zbuduj z -DWITH_PYTHON=ON -DWITH_PYBIND11=ON (WITH_QML=OFF) i bez -DWITH_PYTHON=OFF" fi # settings.conf MUSI mieć modules-search – inaczej zero modułów w search paths. if ! grep -q '^modules-search:' "$PAGAN_ROOT/etc/calamares/settings.conf" 2>/dev/null; then error "settings.conf bez 'modules-search' – Calamares nie znajdzie żadnego modułu" fi # Backend KPMcore musi być widoczny dla runtime Qt (inaczej qFatal przy partycjonowaniu). if ! _kpmcore_backend_ok; then error "Backend KPMcore niewidoczny w '$(_qt_runtime_plugin_dir)' – sprawdź _sync_qt_plugin_dirs" fi ok "Calamares Installer + Pagan branding ✓" } # ========================= PAG / PAGSYNC / PAGBUILD ========================= install_pag() { step "INSTALACJA PAG + PAGSYNC + PAGBUILD" # --- Python: pag używa wyłącznie stdlib; pagsync/pagbuild potrzebują PyYAML. # pip/setuptools/wheel/flit-core/packaging/markupsafe/jinja2 + lxml – # instalowane pipem (zgodnie z LFS 13.1). --- local _pmods=(pyyaml setuptools wheel flit-core packaging markupsafe jinja2 lxml) # Sprawdź WSZYSTKIE moduły – sam yaml nie wystarczy (resume-build ze starym # pyyaml pomijałby instalację nowych modułów). local _need_pip=0 _m _imp for _m in "${_pmods[@]}"; do _imp="${_m//-/_}" [[ "$_m" == "pyyaml" ]] && _imp="yaml" if ! chroot "$PAGAN_ROOT" /usr/bin/python3 -c "import $_imp" >/dev/null 2>&1; then _need_pip=1 break fi done if [[ $_need_pip -eq 0 ]]; then info " ✓ Moduły Pythona dostępne (${_pmods[*]})" else info " ⬇ Instalowanie modułów Pythona (pip) w systemie docelowym..." # DNS dla chroot (hostowy resolv.conf; usuwany po instalacji – NM/systemd # wygenerują własny na starcie, nie chcemy wpisywać IP hosta w obraz). local _had_resolv=0 [[ -e "$PAGAN_ROOT/etc/resolv.conf" ]] && _had_resolv=1 cp -L /etc/resolv.conf "$PAGAN_ROOT/etc/resolv.conf" 2>/dev/null || true # Bootstrap pip: make install Pythona NIE instaluje pip3 (LFS robi to przez # `python3 -m ensurepip`). Bez tego pip3 nie istnieje i instalacja pada. # Fallback: gdy Python zbudowany bez ensurepip – pobierz oficjalny get-pip.py. if ! chroot "$PAGAN_ROOT" /usr/bin/python3 -m ensurepip --upgrade >/dev/null 2>&1; then warn " ensurepip niedostępny – próbuję get-pip.py" if curl -fsSL https://bootstrap.pypa.io/get-pip.py -o "$PAGAN_ROOT/tmp/get-pip.py" 2>/dev/null; then chroot "$PAGAN_ROOT" /usr/bin/python3 /tmp/get-pip.py --break-system-packages >/dev/null 2>&1 || \ warn " get-pip.py nie powiódł się – pip3 może być niedostępny" fi fi chroot "$PAGAN_ROOT" /usr/bin/pip3 install "${_pmods[@]}" --no-cache-dir --break-system-packages >/dev/null 2>&1 || \ chroot "$PAGAN_ROOT" /usr/bin/pip3 install "${_pmods[@]}" --no-cache-dir >/dev/null 2>&1 || \ warn " ⚠ Nie udało się zainstalować modułów Pythona (${_pmods[*]}) – pag działa (stdlib), pagsync/pagbuild mogą wymagać pyyaml" if [[ $_had_resolv -eq 0 ]]; then rm -f "$PAGAN_ROOT/etc/resolv.conf" fi fi # ---- pag - natywny menedżer pakietów (pełna wersja v3, stdlib-only) ---- # Źródło: $SCRIPT_DIR/pag (dev z repo) albo pełna wersja z hosta # (/usr/local/bin/pag – instalowana np. przez install-pag-fix.sh). local _pag_src="" _p if [[ -f "$SCRIPT_DIR/pag" ]]; then _pag_src="$SCRIPT_DIR/pag" elif command -v pag >/dev/null 2>&1 \ && grep -q "PAG - Pagan Linux Package Manager v3" "$(command -v pag)" 2>/dev/null; then _pag_src="$(command -v pag)" else for _p in /usr/local/bin/pag /usr/bin/pag; do if [[ -f "$_p" ]] && grep -q "PAG - Pagan Linux Package Manager v3" "$_p" 2>/dev/null; then _pag_src="$_p"; break fi done fi if [[ -n "$_pag_src" ]]; then install -Dm755 "$_pag_src" "$PAGAN_ROOT/usr/bin/pag" info " ✓ pag → /usr/bin/pag (ze: $_pag_src)" else warn "Brak źródła pag (szukałem: $SCRIPT_DIR/pag, pag w PATH, /usr/local/bin/pag) – menedżer pakietów nie zostanie zainstalowany" fi # Tłumaczenia paga (pag czyta PAG_LANG_DIR → /etc/pag/lang → /usr/share/pag/lang) if [[ -d "$SCRIPT_DIR/pag-lang" ]]; then mkdir -p "$PAGAN_ROOT/usr/share/pag/lang" cp -a "$SCRIPT_DIR/pag-lang/." "$PAGAN_ROOT/usr/share/pag/lang/" info " ✓ pag: tłumaczenia → /usr/share/pag/lang" fi # Uwaga: pagsync i pagbuild to narzędzia BUILDERA (repo + budowa pakietów) – # celowo NIE trafiają do systemu live; są w repozytorium projektu. # --- Konfiguracja PAG: repozytorium + katalogi stanu + keyring --- mkdir -p "$PAGAN_ROOT/etc/pag/gpg" mkdir -p "$PAGAN_ROOT/var/lib/pag" "$PAGAN_ROOT/var/cache/pag" mkdir -p "$PAGAN_ROOT/var/cache/pagbuild/"{sources,output} mkdir -p "$PAGAN_ROOT/.pag_staging" "$PAGAN_ROOT/.deployments" # repos.conf – jedno repozytorium na linię (format PAG) cat > "$PAGAN_ROOT/etc/pag/repos.conf" </dev/null || \ echo '/usr/bin/fish' >> "$PAGAN_ROOT/etc/shells" # Domyślna powłoka dla NOWYCH kont (useradd / Calamares), użytkownika live i roota. chroot "$PAGAN_ROOT" useradd -D -s /usr/bin/fish 2>/dev/null || true if chroot "$PAGAN_ROOT" id pagan >/dev/null 2>&1; then chroot "$PAGAN_ROOT" usermod -s /usr/bin/fish pagan 2>/dev/null || true fi chroot "$PAGAN_ROOT" usermod -s /usr/bin/fish root 2>/dev/null || true # Globalna konfiguracja fish (/etc/fish/config.fish). fish NIE czyta # /etc/profile.d, więc autostart sesji live na tty1 (root) jest tutaj. mkdir -p "$PAGAN_ROOT/etc/fish" cat > "$PAGAN_ROOT/etc/fish/config.fish" <<'FISHEOF' # Pagan Linux – globalna konfiguracja fish (/etc/fish/config.fish) fish_add_path /usr/local/sbin /usr/local/bin /usr/sbin /sbin # Autostart sesji live na tty1 – odpowiednik /etc/profile.d/pagan-live.sh dla # basha (fish go nie czyta). Tylko powłoka loginowa, tylko tty1, raz. if status is-login; and status is-interactive if test (command tty 2>/dev/null) = /dev/tty1 if command -q startx if not set -q DISPLAY; and not set -q WAYLAND_DISPLAY clear echo "PaganLinux – uruchamiam sesję live (startx)..." startx set -l xrc $status clear if test $xrc -ne 0 echo "Sesja X zakończyła się z kodem $xrc – logi (ostatnie linie):" set -l xlog (ls -t /root/.local/share/xorg/Xorg.0.log /var/log/Xorg.0.log 2>/dev/null | head -n1) if test -n "$xlog" tail -n 20 "$xlog" end end printf "\nUruchomić ponownie? [y/N] " read -l -n 1 _ans if string match -qi y -- "$_ans" reboot end end end end end FISHEOF # Konfiguracja dla nowych użytkowników (/etc/skel) – bez Oh My Fish. mkdir -p "$PAGAN_ROOT/etc/skel/.config/fish" cat > "$PAGAN_ROOT/etc/skel/.config/fish/config.fish" <<'FISHEOF' # Pagan Linux – domyślna konfiguracja fish if status is-interactive set -g fish_greeting "Witaj w Pagan Linux" fish_add_path /usr/local/bin /usr/local/sbin abbr -a ll 'ls -lah' abbr -a update 'sudo pag update && sudo pag upgrade' abbr -a install 'sudo pag install' abbr -a search 'pag search' end FISHEOF info " ✓ fish: /etc/shells + powłoka roota i użytkowników + /etc/fish/config.fish" } # ========================= INITRAMFS (ROLLING) ========================= # pagan-initramfs – przebudowa initramfs na żywym systemie po aktualizacji jądra. # Priorytet: mkinitramfs (NuTyX) → dracut → mkinitcpio → własny busybox+cpio. install_initramfs_tool() { step "INSTALACJA NARZĘDZIA INITRAMFS (ROLLING)" cat > "$PAGAN_ROOT/usr/bin/pagan-initramfs" <<'EOF' #!/bin/bash # pagan-initramfs – przebudowa initramfs Pagan Linux (rolling: po aktualizacji jądra) # Kolejność: mkinitramfs (NuTyX) → dracut → mkinitcpio → własny busybox+cpio. [ "$(id -u)" = "0" ] || { echo "Uruchom jako root"; exit 1; } kver() { ls -1 /usr/lib/modules 2>/dev/null | head -n1; } if command -v mkinitramfs >/dev/null 2>&1 && [ -n "$(kver)" ]; then echo "mkinitramfs $(kver)..." mkinitramfs "$(kver)" && { echo "✅ Initramfs zbudowany (mkinitramfs)"; exit 0; } fi if command -v dracut >/dev/null 2>&1; then echo "dracut..." dracut --force /boot/initramfs.img && { echo "✅ Initramfs zbudowany (dracut)"; exit 0; } fi if command -v mkinitcpio >/dev/null 2>&1; then echo "mkinitcpio..." mkinitcpio -g /boot/initramfs.img && { echo "✅ Initramfs zbudowany (mkinitcpio)"; exit 0; } fi # --- Fallback: własny initramfs (busybox + cpio) --- KV="$(kver)" [ -n "$KV" ] || { echo "❌ Brak modułów w /usr/lib/modules"; exit 1; } echo "Fallback: busybox+cpio (initramfs dla $KV)..." TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT mkdir -p "$TMP"/{bin,dev,proc,sys,run,new_root,etc,lib,lib64,usr,sbin} cat > "$TMP/init" <<'INITEOF' #!/bin/sh # Pagan Linux initramfs (minimal, dysk + deploymenty) – fallback pagan-initramfs mount -t proc proc /proc 2>/dev/null mount -t sysfs sysfs /sys 2>/dev/null mount -t devtmpfs dev /dev 2>/dev/null || mount -t tmpfs dev /dev 2>/dev/null mkdir -p /run /new_root /etc /bin /sbin /usr/bin /usr/sbin ROOT_DEV="" CRYPT_DEV_ARG="" for arg in $(cat /proc/cmdline 2>/dev/null); do case "$arg" in root=*) ROOT_DEV="${arg#root=}" ;; cryptdevice=*) CRYPT_DEV_ARG="${arg#cryptdevice=}" ;; esac done [ -n "$ROOT_DEV" ] || ROOT_DEV="/dev/sda1" # ---- LUKS: odblokowanie roota (cryptdevice= lub wprost partycja LUKS) ---- unlock_luks() { local dev="$1" name="$2" pass [ -n "$name" ] || name="cryptroot" mkdir -p /dev/mapper mknod /dev/mapper/control c 10 236 2>/dev/null modprobe dm-crypt 2>/dev/null || true if ! command -v cryptsetup >/dev/null 2>&1; then echo "[pagan-init] BŁĄD: initramfs nie ma cryptsetup – nie mogę odblokować $dev" return 1 fi stty -echo 2>/dev/null printf "Hasło (LUKS %s): " "$dev" read -r pass stty echo 2>/dev/null echo if ! printf '%s\n' "$pass" | cryptsetup open "$dev" "$name" 2>/dev/null; then pass="" echo "[pagan-init] BŁĄD: nie udało się odblokować $dev (złe hasło?)" return 1 fi pass="" echo "[pagan-init] Odblokowano: /dev/mapper/$name" return 0 } if [ -n "$CRYPT_DEV_ARG" ]; then crypt_src="${CRYPT_DEV_ARG%%:*}"; crypt_name="${CRYPT_DEV_ARG#*:}"; crypt_name="${crypt_name%%:*}" crypt_dev="" case "$crypt_src" in UUID=*) want="${crypt_src#UUID=}" for d in /dev/sd*[0-9] /dev/hd*[0-9] /dev/vd*[0-9] /dev/nvme*[0-9]n[0-9]p[0-9]* /dev/mmcblk*p[0-9]*; do [ -b "$d" ] || continue [ "$(blkid -s UUID -o value "$d" 2>/dev/null)" = "$want" ] && { crypt_dev="$d"; break; } done ;; /dev/*) crypt_dev="$crypt_src" ;; esac if [ -n "$crypt_dev" ] && [ -b "$crypt_dev" ]; then if unlock_luks "$crypt_dev" "$crypt_name"; then ROOT_DEV="/dev/mapper/$crypt_name" else echo "[pagan-init] Powłoka ratunkowa:"; exec /bin/sh fi else echo "[pagan-init] BŁĄD: cryptdevice nie znaleziono ($crypt_src)"; exec /bin/sh fi fi i=0 while [ ! -e "$ROOT_DEV" ] && [ "$i" -lt 10 ]; do sleep 1; i=$((i+1)); done # Fallback: root= wskazuje wprost na partycję LUKS if blkid -t TYPE=crypto_LUKS "$ROOT_DEV" >/dev/null 2>&1; then echo "[pagan-init] Wykryto partycję LUKS na root ($ROOT_DEV)" if unlock_luks "$ROOT_DEV" cryptroot; then ROOT_DEV="/dev/mapper/cryptroot" else echo "[pagan-init] Powłoka ratunkowa:"; exec /bin/sh fi fi mount -t auto -o rw "$ROOT_DEV" /new_root 2>/dev/null || { echo "[pagan-init] BŁĄD: nie można zamontować roota ($ROOT_DEV)"; exec /bin/sh; } # Deploymenty PAG (immutable OS) ACTIVE="/new_root/.deployments/active" DEP="" if [ -L "$ACTIVE" ]; then DEP=$(readlink "$ACTIVE") [ -n "$DEP" ] && DEP="/new_root/.deployments/$DEP" elif [ -d "/new_root/.deployments" ]; then DEP=$(ls -1 /new_root/.deployments/ 2>/dev/null | grep -v active | sort -r | head -1) [ -n "$DEP" ] && DEP="/new_root/.deployments/$DEP" fi [ -n "$DEP" ] && [ -d "$DEP" ] && NEWROOT="$DEP" || NEWROOT="/new_root" mount --move /proc "$NEWROOT/proc" 2>/dev/null mount --move /sys "$NEWROOT/sys" 2>/dev/null mount --move /dev "$NEWROOT/dev" 2>/dev/null exec switch_root "$NEWROOT" /sbin/init INITEOF chmod +x "$TMP/init" BB="" for p in /usr/bin/busybox /bin/busybox; do [ -x "$p" ] && BB="$p" && break; done if [ -n "$BB" ]; then cp "$BB" "$TMP/bin/busybox" chmod 755 "$TMP/bin/busybox" for c in sh mount umount ls cat echo sleep readlink mkdir switch_root cp rm ln grep poweroff reboot \ blkid mknod stty vi; do ln -sf busybox "$TMP/bin/$c" done else for t in bash mount umount ls cat echo sleep readlink mkdir switch_root cp rm ln grep blkid; do s=$(command -v "$t" 2>/dev/null) && [ -n "$s" ] && [ -f "$s" ] && cp "$s" "$TMP/bin/" done fi # cryptsetup + biblioteki (obsługa LUKS) – jeśli system go ma CS="" for p in /usr/sbin/cryptsetup /usr/bin/cryptsetup /sbin/cryptsetup; do [ -x "$p" ] && CS="$p" && break; done if [ -n "$CS" ]; then mkdir -p "$TMP/usr/sbin" cp "$CS" "$TMP/usr/sbin/cryptsetup" chmod 755 "$TMP/usr/sbin/cryptsetup" for lib in $(ldd "$CS" 2>/dev/null | awk '/=>/ {print $3} /^[[:space:]]*\// {print $1}'); do [ -f "$lib" ] || continue mkdir -p "$TMP/${lib%/*}" cp "$lib" "$TMP$lib" done loader=$(ldd "$CS" 2>/dev/null | awk '/ld-linux/ {print $1}') if [ -n "$loader" ] && [ -f "$loader" ]; then mkdir -p "$TMP/${loader%/*}" cp "$loader" "$TMP$loader" fi echo "cryptsetup → initramfs (LUKS)" else echo "Brak cryptsetup – initramfs nie obsłuży LUKS (pag install cryptsetup)" fi [ -f /etc/pagan-release ] && cp /etc/pagan-release "$TMP/etc/" cd "$TMP" find . | cpio -oH newc 2>/dev/null | gzip > /boot/initramfs.img echo "✅ Initramfs zbudowany (fallback busybox+cpio)" exit 0 EOF chmod +x "$PAGAN_ROOT/usr/bin/pagan-initramfs" ok "Narzędzie initramfs (rolling) zainstalowane!" } # ========================= MENEDŻER STEROWNIKÓW (PAG – ROLLING) ========================= # Wszystkie sterowniki i jądro to pakiety z repo → aktualizacja bez nowego ISO # (model Arch, nie point-release jak Debian). install_driver_manager() { step "INSTALACJA MENEDŻERA STEROWNIKÓW (PAG/ROLLING)" # pciutils (lspci) – pagan-drivers rozpoznaje GPU właśnie przez `lspci -nn`. # Bez tego `pagan-drivers auto/detect` nie ma czym wykryć karty (a grupy # „Sterowniki …” w instalatorze opierają się na tej samej wiedzy użytkownika). build_lfs_pkg "pciutils" "3.15.0" \ "https://www.kernel.org/pub/software/utils/pciutils/pciutils-3.15.0.tar.xz" \ "SKIP" \ "make PREFIX=/usr SBINDIR=/usr/bin SHAREDIR=/usr/share/misc MANDIR=/usr/share/man SHARED=yes ZLIB=no LIBKMOD=no && make PREFIX=\$PAGAN_ROOT/usr SBINDIR=\$PAGAN_ROOT/usr/bin SHAREDIR=\$PAGAN_ROOT/usr/share/misc MANDIR=\$PAGAN_ROOT/usr/share/man SHARED=yes ZLIB=no LIBKMOD=no install install-lib && chmod 755 \$PAGAN_ROOT/usr/lib/libpci.so.3.15.0 2>/dev/null || true" cat > "$PAGAN_ROOT/usr/bin/pagan-drivers" <<'EOF' #!/bin/bash # pagan-drivers – menedżer sterowników i jądra Pagan Linux (rolling przez PAG). # Sterowniki/jądro to pakiety z repo → pagan-drivers update / kernel aktualizują # system BEZ czekania na nowe ISO. # # Użycie: # pagan-drivers detect – wykryj sprzęt (lspci) # pagan-drivers status – wersja jądra + zainstalowane sterowniki # pagan-drivers auto – wykryj GPU i zainstaluj właściwy sterownik # pagan-drivers nvidia – NVIDIA (userland + moduły jądra) [pag install nvidia-dkms nvidia-kernel] # pagan-drivers amd – AMDGPU (xf86-video-amdgpu + firmware) # pagan-drivers intel – Intel (xf86-video-intel + media driver) # pagan-drivers kernel – zaktualizuj jądro z repo (kernel) + initramfs + GRUB # pagan-drivers initramfs – przebudowa initramfs # pagan-drivers update – pełna aktualizacja systemu (pag upgrade) GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m' PAG="pag" [ "$(id -u)" = "0" ] || { echo -e "${RED}Uruchom jako root (sudo)${NC}"; exit 1; } have() { command -v "$1" >/dev/null 2>&1; } gpu_vendor() { have lspci || return 0 lspci -nn 2>/dev/null | grep -iE "vga|3d|display" | grep -oiE "nvidia|amd|ati|intel" | head -n1 | tr 'A-Z' 'a-z' } install_pkgs() { echo -e "${CYAN} $PAG update && $PAG install $*${NC}" PAG_YES=1 "$PAG" update >/dev/null 2>&1 || true PAG_YES=1 "$PAG" install "$@" || { echo -e "${RED}Instalacja nieudana (sprawdź repo: $PAG repo-list)${NC}"; return 1; } } detect() { if ! have lspci; then echo -e "${YELLOW}Brak narzędzia lspci (pciutils) – doinstaluj: sudo pag install pciutils${NC}" return 1 fi echo "Wykrywanie sprzętu..." echo -e "\n${YELLOW}GPU:${NC}" lspci | grep -E "VGA|3D|Display" || echo " Brak" echo -e "\n${YELLOW}Sieć:${NC}" lspci | grep -E "Ethernet|Network|Wi-Fi" || echo " Brak" echo -e "\n${YELLOW}Audio:${NC}" lspci | grep -E "Audio|Sound" || echo " Brak" } status() { echo -e "${YELLOW}Jądro:${NC} $(uname -r 2>/dev/null)" echo -e "${YELLOW}GPU:${NC} $(lspci -nn 2>/dev/null | grep -iE 'vga|3d|display' | head -n1 || echo 'brak')" echo -e "${YELLOW}Pakiety:${NC}" "$PAG" list 2>/dev/null | grep -iE "^(kernel|linux|nvidia|mesa|xorg-xf86-video|xorg-server)" || true } nvidia() { echo "⬇ NVIDIA (userland + moduły jądra) z repo..." # Pakiet z receptury nazywa się nvidia-dkms (dawne „nvidia” nie istnieje w repo). # nvidia-kernel ciągnie pakiet `kernel` (jądro rolling z repo) jako zależność. install_pkgs nvidia-dkms nvidia-kernel || return 1 local kv kver kv=$(uname -r 2>/dev/null) if have modinfo; then kver=$(modinfo -F vermagic nvidia 2>/dev/null | awk '{print $1}') if [ -n "$kver" ] && [ "$kver" != "$kv" ]; then echo -e "${YELLOW}⚠ Moduły nvidia zbudowane pod $kver, działasz na $kv.${NC}" echo -e "${YELLOW} Poczekaj na nvidia-kernel dla $kv lub zaktualizuj jądro: pagan-drivers kernel${NC}" else echo -e "${GREEN}✅ NVIDIA gotowe (moduły pasują do jądra)${NC}" fi fi } amd() { echo "⬇ AMDGPU (xf86-video-amdgpu + firmware) z repo..." install_pkgs xorg-xf86-video-amdgpu amdgpu-firmware || return 1 echo -e "${GREEN}✅ AMD gotowe${NC}" } intel() { echo "⬇ Intel (xf86-video-intel + media driver) z repo..." install_pkgs xorg-xf86-video-intel intel-media-driver || return 1 echo -e "${GREEN}✅ Intel gotowe${NC}" } kernel() { echo "Aktualizacja jądra (kernel z repo) + initramfs + GRUB..." # W repo jądro to pakiet `kernel` (kernel-lts / kernel-mainline = alternatywy); # nagłówki/źródła DKMS są w tym samym pakiecie (/usr/src/kernel). install_pkgs kernel || return 1 echo -e "${GREEN}✅ Jądro zaktualizowane (initramfs + GRUB przebudowane hookiem)${NC}" } initramfs() { echo "Przebudowa initramfs..." if have pagan-initramfs; then pagan-initramfs; else "$PAG" initramfs-update; fi } update() { echo "Pełna aktualizacja systemu (rolling)..." PAG_YES=1 "$PAG" update || true PAG_YES=1 "$PAG" upgrade || return 1 echo -e "${GREEN}✅ System zaktualizowany${NC}" } auto() { detect local v v=$(gpu_vendor) echo -e "\n${CYAN}Wykryto GPU: ${v:-nieznany}${NC}" case "$v" in nvidia) nvidia ;; amd|ati) amd ;; intel) intel ;; *) echo -e "${YELLOW}Brak rozpoznanego GPU – domyślne (mesa) już działa.${NC}" ;; esac } case "${1:-help}" in detect) detect ;; status) status ;; nvidia) nvidia ;; amd) amd ;; intel) intel ;; kernel) kernel ;; initramfs) initramfs ;; update) update ;; auto) auto ;; *) echo "Użycie: pagan-drivers {detect|status|nvidia|amd|intel|kernel|initramfs|update|auto}" ;; esac EOF chmod +x "$PAGAN_ROOT/usr/bin/pagan-drivers" ok "Menedżer sterowników (PAG/rolling) zainstalowany!" } # ========================= PAKIET KERNELA JAKO ROLLING (.pag) ========================= # Po build_kernel pakujemy jądro jako pakiet `kernel` (metadata.json + data.tar.xz # + hooks/post-install) i instalujemy przez PAG. Dzięki temu pag "widzi" jądro # w bazie: `pag upgrade` / `pagan-drivers kernel` aktualizują je z repo bez nowego ISO. # # NAZWA: `kernel` – DOKŁADNIE tak, jak jądro w repozytorium PAG # (recipes/utils/kernel). Wcześniej pakiet nazywał się `linux`, przez co # `pag upgrade` NIGDY nie ruszył jądra (repo nie ma pakietu `linux`), a # `nvidia-kernel` (depends: kernel) nie widział zainstalowanego jądra. # Dla kompatybilności starych skryptów i przyzwyczajeń pakiet deklaruje # `provides: ["linux"]` – `pag install linux` nadal działa (rozwiąże się na kernel). install_rolling_kernel() { step "PAKIET KERNELA JAKO ROLLING (kernel-${KERNEL_VERSION})" if ! _is_true "${ROLLING_KERNEL:-1}"; then info "Pomijam pakiet rolling kernela (ROLLING_KERNEL=0)" return 0 fi [[ -x "$PAGAN_ROOT/usr/bin/pag" ]] || { warn "pag nie zainstalowany – pomijam rolling kernel"; return 0; } local kver="${KERNEL_VERSION}-pagan" local pkgdir="$PAGAN_ROOT/tmp/kernel-pkg" local tmp="$PAGAN_ROOT/tmp/kernel-pag" local pag_file="$PAGAN_ROOT/tmp/kernel-${KERNEL_VERSION}-1.pag" rm -rf "$pkgdir" "$tmp" mkdir -p "$pkgdir/boot" "$pkgdir/usr/lib/modules" "$tmp/hooks" # boot (rozwiń symlinki) cp -L "$PAGAN_ROOT/boot/vmlinuz-${kver}" "$pkgdir/boot/vmlinuz-${kver}" 2>/dev/null \ || { warn "brak vmlinuz-${kver} – pomijam rolling kernel"; return 0; } ln -sf "vmlinuz-${kver}" "$pkgdir/boot/vmlinuz-linux" cp -L "$PAGAN_ROOT/boot/System.map-${kver}" "$pkgdir/boot/System.map-${kver}" 2>/dev/null || true ln -sf "System.map-${kver}" "$pkgdir/boot/System.map" cp -L "$PAGAN_ROOT/boot/config-${kver}" "$pkgdir/boot/config-${kver}" 2>/dev/null || true # moduły (usrmerge: /lib → usr/lib) local modsrc="" if [[ -d "$PAGAN_ROOT/usr/lib/modules/${kver}" ]]; then modsrc="$PAGAN_ROOT/usr/lib/modules/${kver}" elif [[ -d "$PAGAN_ROOT/lib/modules/${kver}" ]]; then modsrc="$PAGAN_ROOT/lib/modules/${kver}"; fi if [[ -n "$modsrc" ]]; then mkdir -p "$pkgdir/usr/lib/modules" # cp -a (bez -L): build/source to absolutne symlinki – ich NIE wolno # rozwijać, bo cp -aL skopiowałby całe drzewo źródłowe kernela do pakietu cp -a "$modsrc" "$pkgdir/usr/lib/modules/" # absolutne symlinki build/source są blokowane przez pag (security); # rm -rf, bo po błędnym cp -aL mogły zostać jako katalogi rm -rf "$pkgdir/usr/lib/modules/${kver}"/{build,source} fi # hook post-install: przebudowa initramfs + GRUB po instalacji jądra cat > "$tmp/hooks/post-install" <<'HOOK' #!/bin/bash # Czysty locale – unikaj "cannot change locale (C.UTF-8)" przy hookach export LC_ALL=C LANG=C export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin KV="$(uname -r 2>/dev/null)" if [ -d "/usr/src/linux-headers-${KV}" ] && [ -d "/usr/lib/modules/${KV}" ]; then ln -sfn "/usr/src/linux-headers-${KV}" "/usr/lib/modules/${KV}/build" ln -sfn "/usr/src/linux-headers-${KV}" "/usr/lib/modules/${KV}/source" fi if command -v pagan-initramfs >/dev/null 2>&1; then pagan-initramfs; fi # pag grub-update NIE może działać podczas pag install (lock jest trzymany) if command -v pag >/dev/null 2>&1 && [ ! -f /var/lib/pag/pag.lock ]; then pag grub-update; fi HOOK chmod +x "$tmp/hooks/post-install" # data.tar.xz (ścieżki względne – bezpieczne dla pag) tar -cJf "$tmp/data.tar.xz" -C "$pkgdir" . # metadata.json (format PAG) local size size=$(du -sb "$pkgdir" 2>/dev/null | cut -f1) cat > "$tmp/metadata.json" </dev/null || true mknod -m 666 "$PAGAN_ROOT/dev/zero" c 1 5 2>/dev/null || true mknod -m 666 "$PAGAN_ROOT/dev/tty" c 5 0 2>/dev/null || true mknod -m 644 "$PAGAN_ROOT/dev/random" c 1 8 2>/dev/null || true mknod -m 644 "$PAGAN_ROOT/dev/urandom" c 1 9 2>/dev/null || true fi if chroot "$PAGAN_ROOT" /usr/bin/env PAG_YES=1 /usr/bin/pag install "/tmp/kernel-${KERNEL_VERSION}-1.pag"; then ok "Kernel spakowany i zarejestrowany w PAG jako kernel (rolling: pagan-drivers kernel / pag upgrade)" else warn "pag install kernela nieudany – przywracam kernel z build_kernel" cp -a "$pkgdir/boot/." "$PAGAN_ROOT/boot/" 2>/dev/null || true [[ -d "$pkgdir/usr/lib/modules/${kver}" ]] \ && cp -a "$pkgdir/usr/lib/modules/${kver}" "$PAGAN_ROOT/usr/lib/modules/" 2>/dev/null || true fi rm -rf "$pkgdir" "$tmp" "$pag_file" } # GRUB a nazwy initramfs – KLUCZOWE dla rozruchu ZAINSTALOWANEGO systemu. # # Calamares (moduł `bootloader`) regeneruje /boot/grub/grub.cfg poleceniem # `grub-mkconfig`, które uruchamia /etc/grub.d/10_linux. Ten skrypt wylicza # wersję jądra z NAZWY pliku i szuka initrd WYŁĄCZNIE pod nazwami z wersją: # initrd.img-, initrd-.img, initramfs-.img, … # gdzie = `basename vmlinuz- | sed 's,^[^0-9]*-,,g'`, # czyli dla /boot/vmlinuz-7.2.5-pagan → 7.2.5-pagan. # # Nasz initramfs nazywa się /boot/initramfs.img, więc NIE pasuje do żadnego # wzorca → grub-mkconfig generuje menuentry BEZ linii `initrd`. Skutek zależy # tylko od tego, co jest wkompilowane na sztywno w jądro: przy sterownikach # modułowych kończy się to „VFS: Cannot open root device” / paniką jądra. # # Dodatkowo /boot/vmlinuz-linux jest symlinkiem, a 10_linux go NIE odrzuca # (grub_file_is_not_garbage sprawdza tylko *.dpkg-*, *.rpmnew, README*, *.sig), # więc powstaje DRUGI wpis menu z wersją `linux`. On też musi mieć initrd, bo # właśnie on bywa wpisem domyślnym (kolejność z sortowania wersji). # # Rozwiązanie: dla każdego /boot/vmlinuz* tworzymy alias # /boot/initramfs-.img → initramfs.img. Symlink, a nie kopia, żeby # przebudowa initramfs (`pagan-initramfs`) automatycznie go odświeżała; # unpackfs kopiuje drzewo rsynciem -aHAXSr, więc symlinki przechodzą do # zainstalowanego systemu bez zmian. # # Idempotentne; wołane z build_initramfs, finalize_build i fix-calamares.sh. _ensure_grub_boot_artifacts() { local _bd="$PAGAN_ROOT/boot" _k _b _v _n=0 # /etc/default/grub: bez tego pliku grub-mkconfig działa „na domyślnych” # i nie da się ustawić GRUB_DISTRIBUTOR ani parametrów jądra poza # Calamaresem (np. przy `pag grub-update` po aktualizacji jądra). # Moduł grubcfg Calamaresa i tak go potem scala z grubcfg.conf. if [[ -x "$PAGAN_ROOT/usr/bin/grub-mkconfig" && ! -f "$PAGAN_ROOT/etc/default/grub" ]]; then mkdir -p "$PAGAN_ROOT/etc/default" cat > "$PAGAN_ROOT/etc/default/grub" <<'GRUBDEF' GRUB_DEFAULT=0 GRUB_TIMEOUT=5 GRUB_DISTRIBUTOR="PaganLinux" GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" GRUB_CMDLINE_LINUX="" GRUB_TERMINAL_OUTPUT="console" GRUB_DISABLE_SUBMENU=true GRUB_DISABLE_RECOVERY=true GRUB_DISABLE_LINUX_UUID=false GRUBDEF ok " grub: /etc/default/grub utworzony (wcześniej go brakowało)" fi [[ -f "$_bd/initramfs.img" ]] || return 0 for _k in "$_bd"/vmlinuz*; do [[ -e "$_k" ]] || continue _b="$(basename "$_k")" # Dokładnie jak /etc/grub.d/10_linux: s,^[^0-9]*-,,g _v="$(printf '%s' "$_b" | sed -e 's,^[^0-9]*-,,g')" [[ -n "$_v" && "$_v" != "$_b" ]] || continue if [[ ! -e "$_bd/initramfs-$_v.img" ]]; then ln -sfn initramfs.img "$_bd/initramfs-$_v.img" _n=$((_n + 1)) fi # „initrd.img-” – 10_linux sprawdza ten wzorzec jako pierwszy, # a używają go też inne narzędzia. if [[ ! -e "$_bd/initrd.img-$_v" ]]; then ln -sfn initramfs.img "$_bd/initrd.img-$_v" _n=$((_n + 1)) fi done if ((_n > 0)); then ok " grub: aliasy initramfs-.img ($_n) – grub-mkconfig doda linię initrd" fi } # ========================= KONFIGURACJA GRUB ========================= configure_grub() { step "KONFIGURACJA GRUB" mkdir -p "$PAGAN_ROOT/boot/grub" cat > "$PAGAN_ROOT/boot/grub/grub.cfg" <<'EOF' # GRUB dla Pagan Linux set default=0 set timeout=5 set root=(hd0,gpt1) # Splash PaganLinux (jeśli zainstalowany) insmod png if background_image /boot/grub/splash.png; then set color_normal=white/black set color_highlight=green/black fi menuentry "Pagan Linux" { linux /boot/vmlinuz-linux root=/dev/sda1 rw quiet splash initrd /boot/initramfs.img } menuentry "Pagan Linux (single)" { linux /boot/vmlinuz-linux root=/dev/sda1 rw single initrd /boot/initramfs.img } menuentry "Pagan Linux (fallback)" { linux /boot/vmlinuz-linux root=/dev/sda1 rw noacpi initrd /boot/initramfs.img } EOF ok "GRUB skonfigurowany" } # ========================= BRANDING (tapeta, neofetch, GRUB splash) ========================= install_branding() { step "INSTALACJA BRANDINGU (tapeta, neofetch, GRUB splash)" local bdir="$SCRIPT_DIR/branding" [[ -d "$bdir" ]] || { warn "brak katalogu branding/ – pomijam branding"; return 0; } # --- Tapeta (XFCE i pozostałe motywy) --- if [[ -f "$bdir/$PAGAN_WALLPAPER" ]]; then mkdir -p "$PAGAN_ROOT/usr/share/wallpapers/paganlinux" install -m644 "$bdir/$PAGAN_WALLPAPER" "$PAGAN_ROOT/usr/share/wallpapers/paganlinux/$PAGAN_WALLPAPER" cat > "$PAGAN_ROOT/usr/share/wallpapers/paganlinux/metadata.json" < "$initramfs_dir/init" <<'EOF' #!/bin/sh # Pagan Linux initramfs init echo "[pagan-init] Uruchamianie initramfs..." mount -t proc proc /proc 2>/dev/null mount -t sysfs sysfs /sys 2>/dev/null mount -t devtmpfs dev /dev 2>/dev/null || mount -t tmpfs dev /dev 2>/dev/null mkdir -p /run /new_root /etc /bin /sbin /usr/bin /usr/sbin /dev/disk/by-label # --- parsowanie cmdline --- LIVE=0 ROOT_DEV="" CRYPT_DEV_ARG="" FINDISO="" # Ventoy / grub loopback: findiso=ścieżka.iso (lub iso-scan/filename=) IMG_DEV="" # jawnie wskazany nośnik live (img_dev=/dev/sdX) for arg in $(cat /proc/cmdline 2>/dev/null); do case "$arg" in pagan.live|live) LIVE=1 ;; root=*) ROOT_DEV="${arg#root=}" ;; cryptdevice=*) CRYPT_DEV_ARG="${arg#cryptdevice=}" ;; findiso=*) FINDISO="${arg#findiso=}" ;; iso-scan/filename=*) FINDISO="${arg#iso-scan/filename=}" ;; img_dev=*) IMG_DEV="${arg#img_dev=}" ;; esac done # ================= TRYB LIVE ================= if [ "$LIVE" = "1" ]; then echo "[pagan-init] Tryb LIVE (pagan.live)" LIVE_DEV="" # nośnik do zamontowania (partycja / CD / loop) LIVE_ISO="" # ścieżka ISO na nośniku (Ventoy / grub loopback: findiso=…) SFS="live/filesystem.squashfs" # Montowanie nośnika z próbą kilku systemów plików. `mount -t auto` bywa # zawodne – zwłaszcza na exFAT (partycja danych Ventoy), dlatego próbujemy # też jawnie. Bez tego boot z Ventoy kończy się „nie można zamontować”. live_mount_ro() { # $1 = urządzenie, $2 = punkt montowania # Nie używaj mount -t auto dla napędu CD: kernel próbuje wtedy kolejno # ext4/FAT/UDF itd. i zaśmieca log błędami superbloku. Nie skanujemy też # loop* jako nośników — są to pętle utworzone dla squashfs/ISO. case "$1" in /dev/sr*|/dev/cdrom) for _t in iso9660 udf; do mount -t "$_t" -o ro "$1" "$2" 2>/dev/null && return 0 done ;; *) mount -t auto -o ro "$1" "$2" 2>/dev/null && return 0 for _t in exfat vfat ntfs3 ntfs; do mount -t "$_t" -o ro "$1" "$2" 2>/dev/null && return 0 done ;; esac return 1 } # Próba montowania kandydata; po sukcesie ustawia LIVE_DEV (i LIVE_ISO). try_medium() { # $1 = urządzenie blokowe [ -b "$1" ] || return 1 live_mount_ro "$1" /run/mnt || return 1 if [ -f "/run/mnt/$SFS" ]; then LIVE_DEV="$1"; LIVE_ISO="" umount /run/mnt 2>/dev/null return 0 fi if [ -n "$FINDISO" ] && [ -f "/run/mnt/$FINDISO" ]; then LIVE_DEV="$1"; LIVE_ISO="$FINDISO" umount /run/mnt 2>/dev/null return 0 fi umount /run/mnt 2>/dev/null return 1 } # Szukanie ISO w TOP-LEVEL katalogu zamontowanego nośnika (Ventoy bez # parametru findiso): weryfikuje przez losetup, czy ISO zawiera squashfs. find_iso_on_dev() { [ -b "$1" ] || return 1 _dev="$1" live_mount_ro "$_dev" /run/mnt || return 1 # ISO w katalogu głównym lub podkatalogach (do 4 poziomów) – Ventoy lubi # /ISO/…; każdy kandydat jest weryfikowany przez losetup (czy zawiera # live/filesystem.squashfs), więc obce ISO są pomijane. _iso_candidates="" if command -v find >/dev/null 2>&1; then _iso_candidates=$(find /run/mnt -maxdepth 4 -type f -name '*.iso' 2>/dev/null) fi [ -n "$_iso_candidates" ] || _iso_candidates=$(printf '%s\n' /run/mnt/*.iso /run/mnt/*/*.iso /run/mnt/*/*/*.iso) for f in $_iso_candidates; do [ -f "$f" ] || continue LOK="" for L in /dev/loop0 /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4; do [ -e "$L" ] || mknod "$L" b 7 "${L#/dev/loop}" 2>/dev/null if losetup "$L" "$f" 2>/dev/null; then LOK="$L"; break; fi done [ -n "$LOK" ] || continue mkdir -p /run/mnt2 if (mount -t iso9660 -o ro "$LOK" /run/mnt2 2>/dev/null || \ mount -t auto -o ro "$LOK" /run/mnt2 2>/dev/null) && \ [ -f "/run/mnt2/$SFS" ]; then LIVE_DEV="$_dev"; LIVE_ISO="${f#/run/mnt/}" umount /run/mnt2 2>/dev/null losetup -d "$LOK" 2>/dev/null umount /run/mnt 2>/dev/null return 0 fi umount /run/mnt2 2>/dev/null losetup -d "$LOK" 2>/dev/null done umount /run/mnt 2>/dev/null return 1 } mkdir -p /run/mnt # Odśwież /dev (mdev/udev, gdyby były w initramfs) – USB potrafi pojawić się # z opóźnieniem; pętla skanuje przez ~30 s (5 s dłużej niż stary limit). command -v mdev >/dev/null 2>&1 && mdev -s 2>/dev/null command -v udevadm >/dev/null 2>&1 && udevadm trigger 2>/dev/null i=0 while [ -z "$LIVE_DEV" ] && [ "$i" -lt 30 ]; do # dysk USB może się pojawić chwilę po starcie – odświeżaj listing /dev command -v mdev >/dev/null 2>&1 && mdev -s 2>/dev/null # 1) po etykiecie woluminu for l in PAGANLIVE PAGAN_LIVE PAGANLIVE_EFI; do if [ -e "/dev/disk/by-label/$l" ]; then LIVE_DEV="/dev/disk/by-label/$l"; LIVE_ISO=""; break fi done # 2) jawnie wskazany nośnik (img_dev=) if [ -z "$LIVE_DEV" ] && [ -n "$IMG_DEV" ]; then try_medium "$IMG_DEV" fi # 3) skan: partycje I całe dyski (USB bez tablicy), loop (Ventoy/grub # loopback), napędy CD i mapowania device-mapper. if [ -z "$LIVE_DEV" ]; then for d in /dev/sd* /dev/hd* /dev/vd* /dev/xvd* /dev/nvme*n* \ /dev/mmcblk* /dev/sr* /dev/mapper/*; do try_medium "$d" && break done fi # 4) Ventoy/grub loopback bez findiso: szukaj pliku *.iso w katalogu # głównym partycji danych (zwykle exFAT) i podepnij go przez loop. if [ -z "$LIVE_DEV" ]; then for d in /dev/sd* /dev/hd* /dev/vd* /dev/xvd* /dev/nvme*n* /dev/mmcblk*; do find_iso_on_dev "$d" && break done fi [ -z "$LIVE_DEV" ] && { sleep 1; i=$((i+1)); } done if [ -z "$LIVE_DEV" ]; then echo "[pagan-init] BŁĄD: nie znaleziono nośnika LIVE ($SFS)" echo "[pagan-init] cmdline: $(cat /proc/cmdline 2>/dev/null)" echo "[pagan-init] /dev: $(ls /dev 2>/dev/null | tr '\n' ' ')" echo "[pagan-init] /proc/filesystems: $(cat /proc/filesystems 2>/dev/null | tr '\n' ' ')" echo "[pagan-init] blkid: $(blkid 2>/dev/null | tr '\n' ' ')" echo "[pagan-init] Wskazówka: Ventoy używa exFAT (kernel musi mieć CONFIG_EXFAT_FS);" echo "[pagan-init] ISO powinno być w katalogu głównym partycji danych." echo "[pagan-init] Wchodzę do powłoki ratunkowej:" exec /bin/sh fi echo "[pagan-init] Nośnik live: $LIVE_DEV${LIVE_ISO:+ (ISO: $LIVE_ISO)}" mkdir -p /run/live/media /run/live/iso /run/live/root /run/live/overlay mount -t auto -o ro "$LIVE_DEV" /run/live/media 2>/dev/null || { echo "[pagan-init] BŁĄD: nie można zamontować nośnika $LIVE_DEV"; exec /bin/sh; } SFS_BASE="/run/live/media" if [ -n "$LIVE_ISO" ]; then # ISO leży na partycji (Ventoy / grub loopback) – podepnij przez loop. LDEV="" for L in /dev/loop0 /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4 /dev/loop5; do [ -e "$L" ] || mknod "$L" b 7 "${L#/dev/loop}" 2>/dev/null if losetup "$L" "/run/live/media/$LIVE_ISO" 2>/dev/null; then LDEV="$L"; break fi done [ -n "$LDEV" ] || { echo "[pagan-init] BŁĄD: losetup ISO nieudany ($LIVE_ISO)"; exec /bin/sh; } if mount -t iso9660 -o ro "$LDEV" /run/live/iso 2>/dev/null || \ mount -t auto -o ro "$LDEV" /run/live/iso 2>/dev/null; then SFS_BASE="/run/live/iso" echo "[pagan-init] ISO zamontowane: $LIVE_ISO (loop $LDEV)" else echo "[pagan-init] BŁĄD: nie można zamontować ISO ($LIVE_ISO)"; exec /bin/sh fi fi mount -t squashfs -o ro,loop "$SFS_BASE/$SFS" /run/live/root 2>/dev/null || { echo "[pagan-init] BŁĄD: nie można zamontować $SFS_BASE/$SFS"; exec /bin/sh; } # Nadpisywalny root przez overlay (tmpfs + squashfs) mount -t tmpfs tmpfs /run/live/overlay 2>/dev/null || true mkdir -p /run/live/overlay/upper /run/live/overlay/work if ! mount -t overlay overlay \ -o "lowerdir=/run/live/root,upperdir=/run/live/overlay/upper,workdir=/run/live/overlay/work" \ /new_root 2>/dev/null; then echo "[pagan-init] Overlay niedostępny – montuję root readonly" mount --move /run/live/root /new_root 2>/dev/null fi # Zachowaj źródło squashfs dla Calamaresa (moduł unpackfs). # # UWAGA: NIE wystarczy /run/live/root. Przy overlay ten katalog jest # używany jako lowerdir, więc `mount --move` nie zadziała (błąd był # ukrywany przez 2>/dev/null i Calamares zgłaszał dopiero # „source filesystem /run/live/root does not exist”). Dodatkowo systemd # montuje przy starcie świeży tmpfs na /run i zasłoniłby zamontowane tam # katalogi. Dlatego źródło wystawiamy pod /live/root (poza /run). mkdir -p /new_root/live/root /new_root/live/source # Bind działa także dla mountpointu użytego jako lowerdir overlay. Sprawdzamy # jednak ZAWARTOŚĆ: przy awarii overlay /run/live/root jest już przeniesione, # więc bind "się udaje", ale daje pusty katalog. mount --bind /run/live/root /new_root/live/root 2>/dev/null || true if [ ! -d /new_root/live/root/usr ] && [ ! -d /new_root/live/root/etc ]; then umount /new_root/live/root 2>/dev/null || true # Overlay niedostępny (root = squashfs przeniesiony na /new_root) – # montujemy squashfs jeszcze raz, bezpośrednio pod /live/root. mount -t squashfs -o ro,loop "$SFS_BASE/$SFS" /new_root/live/root 2>/dev/null || true fi # Katalog zawierający live/filesystem.squashfs (medium lub podpięte ISO) # – przydatny, gdy unpackfs używa sourcefs: "squashfs". mount --bind "$SFS_BASE" /new_root/live/source 2>/dev/null || true # /run/live/* zachowujemy dla zgodności z układem live-boot. Najpierw # montujemy tam własny tmpfs, żeby systemd nie położył drugiego i nie # zasłonił bindów. mkdir -p /new_root/run mount -t tmpfs tmpfs /new_root/run 2>/dev/null || true mkdir -p /new_root/run/live/root /new_root/run/live/media /new_root/run/live/overlay mount --bind /run/live/root /new_root/run/live/root 2>/dev/null || true mount --move /run/live/media /new_root/run/live/media 2>/dev/null || true mount --move /run/live/overlay /new_root/run/live/overlay 2>/dev/null || true # Diagnostyka: bez tego Calamares pokazuje tylko „does not exist”, # nie mówiąc, że źródło nie zostało w ogóle podmontowane. if [ ! -d /new_root/live/root/usr ] && [ ! -d /new_root/live/root/etc ]; then echo "[pagan-init] UWAGA: /live/root nie zawiera drzewa systemu –" echo "[pagan-init] unpackfs nie skopiuje systemu na dysk" else echo "[pagan-init] Źródło dla instalatora: /live/root (squashfs)" fi mount --move /proc /new_root/proc 2>/dev/null mount --move /sys /new_root/sys 2>/dev/null mount --move /dev /new_root/dev 2>/dev/null exec switch_root /new_root /sbin/init fi # ================= TRYB DYSK ================= echo "[pagan-init] Tryb dyskowy" # ---- LUKS: odblokowanie roota (cryptdevice= z GRUB/systemd-boot) ---- # Format: cryptdevice=UUID=:[:opcje] # (taki sam jak w mkinitcpio/Arch – systemd-boot już go zapisuje w loader.conf). unlock_luks() { # $1 = urządzenie (partycja LUKS), $2 = nazwa mappera local dev="$1" name="$2" pass [ -n "$name" ] || name="cryptroot" mkdir -p /dev/mapper mknod /dev/mapper/control c 10 236 2>/dev/null modprobe dm-crypt 2>/dev/null || true if ! command -v cryptsetup >/dev/null 2>&1; then echo "[pagan-init] BŁĄD: initramfs nie zawiera cryptsetup – nie mogę odblokować $dev" return 1 fi stty -echo 2>/dev/null printf "Hasło (LUKS %s): " "$dev" read -r pass stty echo 2>/dev/null echo if ! printf '%s\n' "$pass" | cryptsetup open "$dev" "$name" 2>/dev/null; then pass="" echo "[pagan-init] BŁĄD: nie udało się odblokować $dev (złe hasło?)" return 1 fi pass="" echo "[pagan-init] Odblokowano: /dev/mapper/$name" return 0 } if [ -n "$CRYPT_DEV_ARG" ]; then crypt_src="${CRYPT_DEV_ARG%%:*}" crypt_name="${CRYPT_DEV_ARG#*:}"; crypt_name="${crypt_name%%:*}" crypt_dev="" case "$crypt_src" in UUID=*) want="${crypt_src#UUID=}" for d in /dev/sd*[0-9] /dev/hd*[0-9] /dev/vd*[0-9] /dev/xvd*[0-9] \ /dev/nvme*[0-9]n[0-9]p[0-9]* /dev/mmcblk*p[0-9]*; do [ -b "$d" ] || continue [ "$(blkid -s UUID -o value "$d" 2>/dev/null)" = "$want" ] && { crypt_dev="$d"; break; } done ;; /dev/*) crypt_dev="$crypt_src" ;; esac if [ -n "$crypt_dev" ] && [ -b "$crypt_dev" ]; then if unlock_luks "$crypt_dev" "$crypt_name"; then ROOT_DEV="/dev/mapper/$crypt_name" else echo "[pagan-init] Powłoka ratunkowa:" exec /bin/sh fi else echo "[pagan-init] BŁĄD: cryptdevice nie znaleziono ($crypt_src)" echo "[pagan-init] Powłoka ratunkowa:" exec /bin/sh fi fi # root= z GRUB-a może być: UUID=... / PARTUUID=... / LABEL=... / PARTLABEL=... # albo /dev/... . Initramfs NIE ma udev (brak /dev/disk/by-*), więc bez tej # konwersji `mount UUID=...` padał i wyskakiwała powłoka ratunkowa (bash). # Rozwiązujemy przez blkid (busybox) z fallbackiem probe-mount. resolve_root() { case "$ROOT_DEV" in /dev/*) return 0 ;; UUID=*|PARTUUID=*|LABEL=*|PARTLABEL=*) local want="${ROOT_DEV#*=}" d tag case "$ROOT_DEV" in UUID=*) tag=UUID;; PARTUUID=*) tag=PARTUUID;; LABEL=*) tag=LABEL;; *) tag=PARTLABEL;; esac for d in /dev/sd*[0-9] /dev/hd*[0-9] /dev/vd*[0-9] /dev/xvd*[0-9] \ /dev/nvme*[0-9]n[0-9]p[0-9]* /dev/mmcblk*p[0-9]*; do [ -b "$d" ] || continue [ "$(blkid -s "$tag" -o value "$d" 2>/dev/null)" = "$want" ] && { ROOT_DEV="$d"; return 0; } done ;; esac # Fallback (brak blkid w busybox / nieczytelny FS): probe-mount każdej # partycji i szukaj markera systemu (/etc/pagan-release lub /bin/bash). local d for d in /dev/sd*[0-9] /dev/hd*[0-9] /dev/vd*[0-9] /dev/xvd*[0-9] \ /dev/nvme*[0-9]n[0-9]p[0-9]* /dev/mmcblk*p[0-9]*; do [ -b "$d" ] || continue mkdir -p /run/probe if mount -t auto -o ro "$d" /run/probe 2>/dev/null; then if [ -f /run/probe/etc/pagan-release ] || [ -x /run/probe/bin/bash ]; then ROOT_DEV="$d"; umount /run/probe 2>/dev/null; return 0 fi umount /run/probe 2>/dev/null fi done return 1 } if ! resolve_root; then echo "[pagan-init] BŁĄD: nie znaleziono urządzenia root ($ROOT_DEV)" echo "[pagan-init] Powłoka ratunkowa:" exec /bin/sh fi [ -n "$ROOT_DEV" ] || ROOT_DEV="/dev/sda1" i=0 while [ ! -b "$ROOT_DEV" ] && [ "$i" -lt 10 ]; do sleep 1; i=$((i+1)); done # Fallback: root= wskazuje wprost na partycję LUKS (bez cryptdevice=). # blkid czyta typ z superbloku LUKS, więc rozpoznamy ją bez montowania. if blkid -t TYPE=crypto_LUKS "$ROOT_DEV" >/dev/null 2>&1; then echo "[pagan-init] Wykryto partycję LUKS na root ($ROOT_DEV)" if unlock_luks "$ROOT_DEV" cryptroot; then ROOT_DEV="/dev/mapper/cryptroot" else echo "[pagan-init] Powłoka ratunkowa:" exec /bin/sh fi fi mount -t auto -o rw "$ROOT_DEV" /new_root 2>/dev/null || { echo "[pagan-init] BŁĄD: nie można zamontować roota ($ROOT_DEV)"; exec /bin/sh; } # Obsługa deploymentów PAG (immutable OS) ACTIVE_LINK="/new_root/.deployments/active" DEPLOYMENT="" if [ -L "$ACTIVE_LINK" ]; then DEPLOYMENT=$(readlink "$ACTIVE_LINK") [ -n "$DEPLOYMENT" ] && DEPLOYMENT="/new_root/.deployments/$DEPLOYMENT" elif [ -d "/new_root/.deployments" ]; then DEPLOYMENT=$(ls -1 /new_root/.deployments/ 2>/dev/null | grep -v active | sort -r | head -1) [ -n "$DEPLOYMENT" ] && DEPLOYMENT="/new_root/.deployments/$DEPLOYMENT" fi if [ -n "$DEPLOYMENT" ] && [ -d "$DEPLOYMENT" ]; then echo "[pagan-init] Boot z deploymentu: $DEPLOYMENT" mount --move /proc "$DEPLOYMENT/proc" 2>/dev/null mount --move /sys "$DEPLOYMENT/sys" 2>/dev/null mount --move /dev "$DEPLOYMENT/dev" 2>/dev/null exec switch_root "$DEPLOYMENT" /sbin/init else echo "[pagan-init] Brak deploymentu, boot z /new_root" mount --move /proc /new_root/proc 2>/dev/null mount --move /sys /new_root/sys 2>/dev/null mount --move /dev /new_root/dev 2>/dev/null exec switch_root /new_root /sbin/init fi EOF chmod +x "$initramfs_dir/init" # Busybox lub bash local busybox="" for p in "$PAGAN_ROOT/usr/bin/busybox" "$PAGAN_ROOT/bin/busybox" "/usr/bin/busybox"; do [ -f "$p" ] && busybox="$p" && break done if [ -n "$busybox" ]; then cp "$busybox" "$initramfs_dir/bin/busybox" chmod 755 "$initramfs_dir/bin/busybox" for cmd in sh mount umount ls cat echo sleep readlink mkdir switch_root \ cp rm ln blkid losetup modprobe grep dmesg lsblk poweroff reboot \ mknod stty find sync vi; do ln -sf busybox "$initramfs_dir/bin/$cmd" done else # Kopiuj bash i ldd for tool in bash mount umount ls cat echo sleep readlink mkdir switch_root cp rm ln; do src=$(which "$tool" 2>/dev/null) [ -n "$src" ] && cp "$src" "$initramfs_dir/bin/" done fi # ── cryptsetup + biblioteki (obsługa LUKS w initramfs) ── # Bez tego initramfs nie odblokuje zaszyfrowanego roota. Kopiujemy binarkę # i wszystkie zależności dynamiczne (ldd) + interpreter, żeby zadziałało # na czystym initramfs bez udev. local _cs="" for _p in "$PAGAN_ROOT/usr/sbin/cryptsetup" "$PAGAN_ROOT/usr/bin/cryptsetup" "$PAGAN_ROOT/sbin/cryptsetup"; do [[ -x "$_p" ]] && _cs="$_p" && break done if [[ -n "$_cs" ]]; then mkdir -p "$initramfs_dir/usr/sbin" cp "$_cs" "$initramfs_dir/usr/sbin/cryptsetup" chmod 755 "$initramfs_dir/usr/sbin/cryptsetup" local _lib for _lib in $(ldd "$_cs" 2>/dev/null | awk '/=>/ {print $3} /^[[:space:]]*\// {print $1}'); do [[ -f "$_lib" ]] || continue mkdir -p "$initramfs_dir/${_lib%/*}" cp "$_lib" "$initramfs_dir${_lib}" done local _loader _loader=$(ldd "$_cs" 2>/dev/null | awk '/ld-linux/ {print $1}') if [[ -n "$_loader" && -f "$_loader" ]]; then mkdir -p "$initramfs_dir/${_loader%/*}" cp "$_loader" "$initramfs_dir${_loader}" fi info " ✓ cryptsetup + biblioteki → initramfs (LUKS)" else warn "Brak cryptsetup w rootfs – initramfs bez obsługi LUKS!" fi # Kopiuj /etc/pagan-release [ -f "$PAGAN_ROOT/etc/pagan-release" ] && cp "$PAGAN_ROOT/etc/pagan-release" "$initramfs_dir/etc/" # Pakowanie pushd "$initramfs_dir" > /dev/null find . | cpio -oH newc | gzip > "$PAGAN_ROOT/boot/initramfs.img" popd > /dev/null rm -rf "$initramfs_dir" # Świeży obraz = trzeba odświeżyć aliasy initramfs-.img, po które # grub-mkconfig (10_linux) szuka initrd. Bez nich wpis w menu GRUB jest bez # linii `initrd` i zainstalowany system nie ma initramfs. _ensure_grub_boot_artifacts ok "Initramfs zbudowany!" } # ========================= TWORZENIE IMG ========================= # Sprząta zasoby IMG (bind-mounty + loop device) także przy błędzie/przerwaniu. # Wołane z trapa EXIT (_show_summary), więc korzysta z globalnych rejestrów. _cleanup_image_mounts() { local mnt="${_PAGAN_IMG_MNT:-}" if [[ -n "$mnt" && -d "$mnt" ]]; then umount "$mnt/dev" 2>/dev/null || true umount "$mnt/proc" 2>/dev/null || true umount "$mnt/sys" 2>/dev/null || true umount "$mnt/boot/efi" 2>/dev/null || true umount "$mnt" 2>/dev/null || true fi if [[ -n "${_PAGAN_IMG_LOOPDEV:-}" ]]; then losetup -d "$_PAGAN_IMG_LOOPDEV" 2>/dev/null || true fi _PAGAN_IMG_LOOPDEV="" _PAGAN_IMG_MNT="" } create_image() { step "TWORZENIE OBRAZU IMG (BIOS + UEFI)" mkdir -p "$OUTPUT_DIR" local img_file="$OUTPUT_DIR/Pagan-Linux-${PAGAN_VERSION}-${PAGAN_DE}.img" local img_esp_size=512 # MB – partycja EFI (FAT32) info "Tworzenie IMG (${IMAGE_SIZE}MB)..." dd if=/dev/zero of="$img_file" bs=1M count="$IMAGE_SIZE" status=progress 2>/dev/null parted -s "$img_file" mklabel gpt # Układ: [ESP fat32] [root ext4] parted -s "$img_file" mkpart "EFI" fat32 1MiB "${img_esp_size}MiB" parted -s "$img_file" set 1 esp on parted -s "$img_file" mkpart "root" ext4 "${img_esp_size}MiB" 100% parted -s "$img_file" set 2 boot on local loopdev loopdev=$(losetup -f) losetup -P "$loopdev" "$img_file" # Zarejestruj loop device – przy błędzie zdejmie go trap EXIT. _PAGAN_IMG_LOOPDEV="$loopdev" mkfs.vfat -F 32 -n PAGAN_ESP "${loopdev}p1" >/dev/null 2>&1 mkfs.ext4 -F -L PAGAN_ROOT "${loopdev}p2" >/dev/null 2>&1 local mount_point="/mnt/pagan-img" mkdir -p "$mount_point" _PAGAN_IMG_MNT="$mount_point" mount "${loopdev}p2" "$mount_point" mkdir -p "$mount_point/boot/efi" mount "${loopdev}p1" "$mount_point/boot/efi" info "Kopiowanie systemu..." rsync -aAX --delete \ --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","*.img","*.iso","/sources/*","/tools/*","/var/cache/*"} \ "$PAGAN_ROOT/" "$mount_point/" 2>/dev/null # Wirtualne FS dla chroot mount --bind /dev "$mount_point/dev" mount --bind /proc "$mount_point/proc" mount --bind /sys "$mount_point/sys" # GRUB – BIOS (MBR) i UEFI chroot "$mount_point" grub-install --target=i386-pc "$loopdev" 2>/dev/null || \ warn "grub-install i386-pc nieudany – IMG bez boot BIOS" chroot "$mount_point" grub-install --target=x86_64-efi \ --efi-directory=/boot/efi --bootloader-id=PAGAN 2>/dev/null || \ warn "grub-install x86_64-efi nieudany – IMG bez boot UEFI" # Root na partycji 2 w tym układzie (p2 = ext4). Aktualizacja grub.cfg i fstab. sed -i 's|root=/dev/sda1|root=/dev/sda2|g' "$mount_point/boot/grub/grub.cfg" 2>/dev/null || true sed -i 's|set root=(hd0,gpt1)|set root=(hd0,gpt2)|' "$mount_point/boot/grub/grub.cfg" 2>/dev/null || true cat > "$mount_point/etc/fstab" <<'EOF' # /etc/fstab (obraz IMG) /dev/sda2 / ext4 defaults 0 1 /dev/sda1 /boot/efi vfat umask=0077 0 1 proc /proc proc nosuid,noexec,nodev 0 0 sysfs /sys sysfs nosuid,noexec,nodev 0 0 devtmpfs /dev devtmpfs mode=0755,nosuid 0 0 tmpfs /tmp tmpfs defaults 0 0 devpts /dev/pts devpts gid=5,mode=620 0 0 shm /dev/shm tmpfs defaults 0 0 EOF _cleanup_image_mounts ok "IMG: $img_file ($(du -h "$img_file" 2>/dev/null | cut -f1))" } # ========================= TWORZENIE ISO ========================= # Sprawdza, czy obraz ISO jest hybrydowy (isohybrid): sektor 0 musi zawierać MBR # z sygnaturą 0x55AA (offset 510) i aktywny wpis tablicy partycji (offset 446). # Tylko takie ISO da się zapisać na pendrive (balenaEtcher/Rufus/dd) w sposób # bootowalny – czysty ISO9660 (sektor 0 = zera) kończy się błędem # „Missing partition table" w balenaEtcher. _iso_is_hybrid() { local file="$1" boot_flag sig [[ -s "$file" ]] || return 1 sig=$(dd if="$file" bs=1 skip=510 count=2 2>/dev/null | od -An -tx1 | tr -d ' \n') [[ "$sig" == "55aa" ]] || return 1 boot_flag=$(dd if="$file" bs=1 skip=446 count=1 2>/dev/null | od -An -tx1 | tr -d ' \n') [[ "$boot_flag" != "00" && -n "$boot_flag" ]] } create_iso() { step "TWORZENIE OBRAZU ISO (LIVE + INSTALATOR)" mkdir -p "$OUTPUT_DIR" local iso_file="$OUTPUT_DIR/Pagan-Linux-${PAGAN_VERSION}-${PAGAN_DE}.iso" local iso_root="/tmp/pagan-iso-$$" rm -rf "$iso_root" mkdir -p "$iso_root"/{boot,isolinux,live,tools} # --- Binaria ISOLINUX z hosta (boot BIOS) + MBR dla isohybrid --- # check_dependencies zadbał o instalację syslinux na hoście – tutaj tylko # lokalizujemy pliki przez wspólny helper i kopiujemy do drzewa ISO. # (Twardy błąd na wypadek, gdyby pliki zniknęły między fazami builda.) local isolinux_bin ldlinux mbr isolinux_bin=$(_find_syslinux_file isolinux.bin) || true ldlinux=$(_find_syslinux_file ldlinux.c32) || true mbr=$(_find_syslinux_file isohdpfx.bin) || true if [[ -n "$isolinux_bin" && ! -f "$iso_root/isolinux/isolinux.bin" ]]; then cp "$isolinux_bin" "$iso_root/isolinux/" fi if [[ -n "$ldlinux" && ! -f "$iso_root/isolinux/ldlinux.c32" ]]; then cp "$ldlinux" "$iso_root/isolinux/" fi if [[ -z "$isolinux_bin" || -z "$ldlinux" || -z "$mbr" ]]; then error "Brak plików syslinux na hoście mimo auto-instalacji (potrzebne: isolinux.bin, ldlinux.c32, isohdpfx.bin). Zainstaluj ręcznie: Arch/EndeavourOS 'sudo pacman -S syslinux' | Debian/Ubuntu 'sudo apt install isolinux syslinux-common syslinux-utils' | Fedora 'sudo dnf install syslinux'" fi # --- Kernel + initramfs (podążaj za symlinkami vmlinuz-linux) --- cp -L "$PAGAN_ROOT/boot/vmlinuz-linux" "$iso_root/boot/vmlinuz-linux" 2>/dev/null || \ cp -L "$PAGAN_ROOT"/boot/vmlinuz-* "$iso_root/boot/vmlinuz-linux" 2>/dev/null || true cp -L "$PAGAN_ROOT/boot/initramfs.img" "$iso_root/boot/initramfs.img" 2>/dev/null || true # --- System live (squashfs) – bez toolchaina/sources/cache (zbędna waga) --- info "Tworzenie squashfs (to potrwa)..." # UWAGA: mksquashfs 4.7 NIE akceptuje wzorców z wiodącym "/" razem # z -wildcards (FATAL ERROR). Wzorce są względne do źródła → bez "/". # -no-progress MUSI być przed -e (wszystko po -e to wzorce wykluczeń). # -no-xattrs: xatrry (file capabilities itp.) i tak nie działają w live # na starych jądrach bez CONFIG_SQUASHFS_XATTR („Xattrs in filesystem, # these will be ignored") – pomijamy je, żeby obraz był neutralny. # Stderr zostaje widoczne – dzięki temu realne błędy widać w logu. # UWAGA: NIE wykluczamy "root/*"! Wzorzec bez kropki łapie /root/Desktop # (ikonę instalatora na pulpicie live) i wycinał ją z ISO. W /root poza tym # są tylko wpisy z kropką (.config, .xinitrc), których ten wzorzec i tak nie # łapie – więc usunięcie "root/*" niczego nie zaśmieca, a przywraca ikonę. # .pag_staging/.deployments to katalogi robocze buildera (same kopie # zapasowe paga – u nas 475 MB!), które nie mają po co trafiać do live ani # do systemu instalowanego przez unpackfs. mksquashfs "$PAGAN_ROOT" "$iso_root/live/filesystem.squashfs" \ -comp xz -b 1M -noappend -no-progress -no-xattrs -processors "$PAGAN_JOBS" \ -wildcards -e "*.img" "*.iso" "*.log" \ "dev/*" "proc/*" "sys/*" "tmp/*" "run/*" "mnt/*" "media/*" \ "sources/*" "tools/*" "var/cache/*" "usr/src/*" \ ".pag_staging/*" ".deployments/*" if [[ ! -s "$iso_root/live/filesystem.squashfs" ]]; then error "mksquashfs nie wygenerował filesystem.squashfs" fi # --- Menu BIOS (isolinux) – tryb LIVE --- cat > "$iso_root/isolinux/isolinux.cfg" <<'EOF' DEFAULT pagan TIMEOUT 100 PROMPT 0 LABEL pagan MENU LABEL ^Pagan Linux Live KERNEL /boot/vmlinuz-linux APPEND initrd=/boot/initramfs.img pagan.live rw quiet splash LABEL pagan-fallback MENU LABEL Pagan Linux (tryb awaryjny) KERNEL /boot/vmlinuz-linux APPEND initrd=/boot/initramfs.img pagan.live rw noacpi nomodeset LABEL pagan-debug MENU LABEL Pagan Linux (debug – powłoka) KERNEL /boot/vmlinuz-linux APPEND initrd=/boot/initramfs.img pagan.live break EOF # --- Menu EFI (GRUB) + obraz efi dla eltorito --- local efi_ok=0 if [[ -x "$PAGAN_ROOT/usr/bin/grub-mkstandalone" ]]; then mkdir -p "$iso_root/boot/grub" "$iso_root/EFI/BOOT" # Splash GRUB (branding) – tło menu startowego ISO if [[ -f "$PAGAN_ROOT/boot/grub/splash.png" ]]; then cp "$PAGAN_ROOT/boot/grub/splash.png" "$iso_root/boot/grub/splash.png" fi cat > "$iso_root/boot/grub/grub.cfg" <<'GRUBEOF' set timeout=10 set default=0 insmod all_video insmod gfxterm insmod png insmod part_gpt insmod iso9660 if background_image /boot/grub/splash.png; then set color_normal=light-gray/black set color_highlight=white/black else set color_normal=cyan/blue set color_highlight=white/blue fi menuentry "Pagan Linux (LIVE)" { linux /boot/vmlinuz-linux pagan.live rw quiet splash initrd /boot/initramfs.img } menuentry "Pagan Linux (verbose)" { linux /boot/vmlinuz-linux pagan.live rw initrd /boot/initramfs.img } menuentry "Pagan Linux (tryb awaryjny)" { linux /boot/vmlinuz-linux pagan.live rw noacpi nomodeset initrd /boot/initramfs.img } menuentry "Pagan Linux (debug – konsola szeregowa)" { linux /boot/vmlinuz-linux pagan.live rw console=ttyS0 systemd.journald.forward_to_console=yes initrd /boot/initramfs.img } GRUBEOF # Osadzony config = PEŁNE menu (self-contained). WAŻNE: bez `configfile` # i bez zmiany $prefix – GRUB leniwie ładuje moduły (configfile.mod itd.) # z $prefix, a w ISO /boot/grub NIE MA modułów → "file # '/boot/grub/x86_64-efi/configfile.mod' not found" → grub>. # Moduły ładujemy jawnie z memdisk (startowy prefix=(memdisk)/boot/grub). cat > "$iso_root/EFI/BOOT/grub-embed.cfg" <<'EMBED' set timeout=10 set default=0 insmod all_video insmod gfxterm insmod png insmod search insmod iso9660 insmod part_gpt insmod linux search --no-floppy --set=root --label PAGANLIVE if background_image ($root)/boot/grub/splash.png; then set color_normal=light-gray/black set color_highlight=white/black else set color_normal=cyan/blue set color_highlight=white/blue fi menuentry "Pagan Linux (LIVE)" { linux ($root)/boot/vmlinuz-linux pagan.live rw quiet splash initrd ($root)/boot/initramfs.img } menuentry "Pagan Linux (verbose)" { linux ($root)/boot/vmlinuz-linux pagan.live rw initrd ($root)/boot/initramfs.img } menuentry "Pagan Linux (tryb awaryjny)" { linux ($root)/boot/vmlinuz-linux pagan.live rw noacpi nomodeset initrd ($root)/boot/initramfs.img } menuentry "Pagan Linux (debug – konsola szeregowa)" { linux ($root)/boot/vmlinuz-linux pagan.live rw console=ttyS0 systemd.journald.forward_to_console=yes initrd ($root)/boot/initramfs.img } EMBED # grub-mkstandalone to binarka docelowa → uruchamiamy przez chroot # (targetowe biblioteki), z tymczasowo podpiętymi /proc /sys /dev. cp "$iso_root/EFI/BOOT/grub-embed.cfg" "$PAGAN_ROOT/tmp/grub-embed.cfg" 2>/dev/null || true mount_virtual_fs # grub-mkstandalone/mkimage (binarki targetowe) wymagają libdevmapper # (lvm2), którego nie ma w rootfs → loader w chrocie pada natychmiast. # Podepnij tymczasowo z hosta (host i target: glibc 2.44); usuwamy po kroku. local _dl_tmp="" if [[ ! -e "$PAGAN_ROOT/usr/lib/libdevmapper.so.1.02" && -e "/usr/lib/libdevmapper.so.1.02" ]]; then cp -L /usr/lib/libdevmapper.so.1.02 "$PAGAN_ROOT/usr/lib/libdevmapper.so.1.02" && _dl_tmp=1 fi if chroot "$PAGAN_ROOT" /usr/bin/grub-mkstandalone \ --format=x86_64-efi --output=/tmp/grubx64.efi \ --locales="" --themes="" --fonts="" \ "boot/grub/grub.cfg=/tmp/grub-embed.cfg" \ && [[ -s "$PAGAN_ROOT/tmp/grubx64.efi" ]]; then cp "$PAGAN_ROOT/tmp/grubx64.efi" "$iso_root/EFI/BOOT/BOOTX64.EFI" efi_ok=1 info " ✓ BOOTX64.EFI wygenerowany (GRUB EFI)" else warn "grub-mkstandalone nie udał się – ISO bez boot EFI" fi [[ -n "$_dl_tmp" ]] && rm -f "$PAGAN_ROOT/usr/lib/libdevmapper.so.1.02" umount_virtual_fs rm -f "$PAGAN_ROOT/tmp/grub-embed.cfg" "$PAGAN_ROOT/tmp/grubx64.efi" if [[ $efi_ok -eq 1 ]]; then # Zapakuj EFI/BOOT do obrazu FAT dla eltorito rm -rf "/tmp/pagan-efi-$$" mkdir -p "/tmp/pagan-efi-$$/EFI" cp -a "$iso_root/EFI/BOOT" "/tmp/pagan-efi-$$/EFI/" # Label ≤ 11 znaków (limit FAT). PAGANLIVE_EFI (13) wywalało mkfs.vfat; # odrębna etykieta niż ISO (PAGANLIVE) – grub szuka --label PAGANLIVE truncate -s 8M "$iso_root/boot/efiboot.img" || true if ! mkfs.vfat -n PAGANEFI "$iso_root/boot/efiboot.img" >/dev/null 2>&1; then warn "mkfs.vfat nie sformatował efiboot.img – ISO bez boot EFI" efi_ok=0 elif ! mcopy -s -i "$iso_root/boot/efiboot.img" \ "/tmp/pagan-efi-$$/EFI" ::/ >/dev/null 2>&1; then mcopy -s -i "$iso_root/boot/efiboot.img" \ "/tmp/pagan-efi-$$/EFI" :: >/dev/null 2>&1 || { warn "Nie udało się zapisać efiboot.img (brak mtools?) – ISO tylko BIOS" efi_ok=0 } fi rm -rf "/tmp/pagan-efi-$$" fi else warn "Brak grub-mkstandalone w systemie docelowym – ISO bez boot EFI" fi # --- Skrót do instalatora (Calamares) + narzędzia PAG na nośniku --- cat > "$iso_root/install-pagan.sh" <<'EOF' #!/bin/bash # Uruchomienie instalatora systemu (Calamares) z sesji graficznej live. # Calamares jest też dostępny z ikony „Zainstaluj Pagan Linux” na pulpicie. if [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] && command -v calamares >/dev/null 2>&1; then exec calamares "$@" fi echo "Instalator Calamares wymaga sesji graficznej (uruchom go z pulpitu live)." echo "Ręcznie: sudo calamares" EOF chmod +x "$iso_root/install-pagan.sh" # Dodatkowe narzędzia (wygodne do pobrania z nośnika) – tylko to, co użyteczne # w systemie live; pagsync/pagbuild to narzędzia buildera (są w repo projektu) for t in pag; do [[ -f "$SCRIPT_DIR/$t" ]] && cp "$SCRIPT_DIR/$t" "$iso_root/tools/$t" 2>/dev/null || true done chmod +x "$iso_root"/tools/* 2>/dev/null || true cat > "$iso_root/tools/README.txt" <<'EOF' Pagan Linux – narzędzia na nośniku live ======================================= install-pagan.sh – uruchamia instalator systemu (Calamares, GUI) dostępny też z ikony „Zainstaluj Pagan Linux” na pulpicie pagan – menedżer pakietów PAG (pełna wersja) W samym systemie live narzędzia są już zainstalowane w /usr/bin. EOF # --- Generuj ISO (BIOS isolinux + EFI eltorito + hybrid MBR) --- if ! command -v xorriso &>/dev/null; then error "Brak xorriso (zainstaluj pakiet xorriso)" fi local xorriso_args=( xorriso -as mkisofs -o "$iso_file" -V PAGANLIVE ) # Boot BIOS tylko gdy isolinux.bin faktycznie jest – inaczej xorriso # kończy się FAILURE ("Cannot find in ISO image: bin_path=...") if [[ -f "$iso_root/isolinux/isolinux.bin" ]]; then xorriso_args+=( -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table ) fi if [[ $efi_ok -eq 1 ]]; then xorriso_args+=( -eltorito-alt-boot -e boot/efiboot.img -no-emul-boot -isohybrid-gpt-basdat ) fi [[ -n "$mbr" ]] && xorriso_args+=( -isohybrid-mbr "$mbr" ) xorriso_args+=( "$iso_root" ) "${xorriso_args[@]}" >/dev/null 2>&1 || { # Ponów z widocznym błędem (bez -no-progress na xorriso) "${xorriso_args[@]}" || error "xorriso nie wygenerował ISO" } # Weryfikacja hybrydy: sektor 0 musi mieć MBR z sygnaturą 0x55AA i tablicą # partycji – inaczej balenaEtcher/Rufus odrzucą ISO („Missing partition table"). # (Sprawdzenie bez fdisk: sygnatura MBR na offset 510 + niezerowy bajt flagi # rozruchu w pierwszym wpisie tablicy partycji na offset 446.) if ! _iso_is_hybrid "$iso_file"; then error "Wygenerowane ISO NIE jest hybrydowe (brak MBR/tablicy partycji) – balenaEtcher odrzuci je jako 'Missing partition table'. Sprawdź, czy xorriso dostał poprawny isohdpfx.bin." fi rm -rf "$iso_root" ok "ISO: $iso_file ($(du -h "$iso_file" 2>/dev/null | cut -f1))" } # ========================= FINALIZACJA ========================= # ===================== IMPORT BRAKUJĄCYCH LIB Z HOSTA ===================== # Buildery linkowały pakiety przeciw zależnościom HOSTA (krb5, KF6 Frameworks, # gpg-stack, ICU 78…), których nie ma w targetcie → setki brakujących libek # (np. libgssapi_krb5.so.2 dla Qt6Network, libKF6I18n.so.6, libaudit.so.1...). # Host i target mają tę samą glibc (2.44) → libki z hosta są ABI-kompatybilne. # Iteracyjnie uzupełniamy braki (kopiujemy realny plik + symlink soname). # Uwaga: to pragmatyczny krok (libki "niezarządzane"); docelowo pakiety powinny # być zbudowane w targetcie (IMPORT_HOST_LIBS=0 wyłącza). _import_host_libs() { step "IMPORT BRAKUJĄCYCH BIBLIOTEK Z HOSTA (krb5/KF6/gpg/icu…)" _is_true "${IMPORT_HOST_LIBS:-1}" || { info "Import pominięty (IMPORT_HOST_LIBS=0)"; return 0; } # Całość w PODPOWŁOCE z set +e: wewnętrzne błędy skanu/pętli (set -e/pipefail # potrafiły zabić cały build w rundzie 2) nie mogą przerwać budowy. ( set +e local round=0 imported=0 lib src real # indeks wszystkich plików *.so* w targetcie (szybkie wyszukiwanie) local all_libs all_libs=$(cd "$PAGAN_ROOT" && find . -name "*.so*" -printf "%f\n" 2>/dev/null | sort -u) while (( round < 6 )); do imported=0 # skan: wszystkie NEEDED w binarkach/libkach targeta while IFS= read -r lib; do [[ -n "$lib" ]] || continue # już jest w /usr/lib? albo gdziekolwiek w targetcie? [[ -e "$PAGAN_ROOT/usr/lib/$lib" ]] && continue echo "$all_libs" | grep -qxF "$lib" && continue # szukaj na hoście src="" for d in /usr/lib /usr/lib64 /usr/lib/x86_64-linux-gnu /lib64 /usr/local/lib; do [[ -e "$d/$lib" ]] && { src="$d/$lib"; break; } done [[ -n "$src" ]] || continue real=$(readlink -f "$src") [[ -f "$real" ]] || continue cp -a "$real" "$PAGAN_ROOT/usr/lib/$(basename "$real")" 2>/dev/null || continue # symlink soname (jeśli nazwa się różni od realnego pliku) if [[ "$(basename "$real")" != "$lib" ]]; then ln -sf "$(basename "$real")" "$PAGAN_ROOT/usr/lib/$lib" 2>/dev/null || true fi imported=1 info " ✓ $lib" done < <( cd "$PAGAN_ROOT" && { find usr/bin usr/sbin usr/libexec -type f 2>/dev/null; find usr/lib usr/lib64 -name "*.so*" -type f 2>/dev/null; } \ | while read -r f; do readelf -d "$f" 2>/dev/null | grep NEEDED | sed 's/.*\[\(.*\)\]/\1/' || true; done \ | sort -u ) (( imported == 0 )) && break (( round++ )) done ) || true # Gwarancja obecności kluczowych narzędzi X11/SDDM (xkbcomp, xauth, setxkbmap, xinit) for _xbin in xkbcomp xauth setxkbmap xinit; do if [[ ! -x "$PAGAN_ROOT/usr/bin/$_xbin" && -x "/usr/bin/$_xbin" ]]; then cp -a "/usr/bin/$_xbin" "$PAGAN_ROOT/usr/bin/$_xbin" 2>/dev/null || true chmod +x "$PAGAN_ROOT/usr/bin/$_xbin" 2>/dev/null || true info " ✓ $PAGAN_ROOT/usr/bin/$_xbin (z hosta)" fi done ok "Import libek z hosta zakończony" } configure_pam() { step "KONFIGURACJA LINUX-PAM" mkdir -p "$PAGAN_ROOT/etc/pam.d" cat > "$PAGAN_ROOT/etc/pam.d/other" <<'EOF' #%PAM-1.0 auth required pam_deny.so auth required pam_warn.so account required pam_deny.so account required pam_warn.so password required pam_deny.so password required pam_warn.so session required pam_deny.so session required pam_warn.so EOF cat > "$PAGAN_ROOT/etc/pam.d/system-auth" <<'EOF' #%PAM-1.0 # pam_faillock: blokada po 4 nieudanych logowaniach (patrz /etc/security/faillock.conf) auth required pam_env.so auth required pam_faillock.so preauth auth sufficient pam_unix.so nullok try_first_pass auth [default=die] pam_faillock.so authfail auth sufficient pam_faillock.so authsucc auth required pam_deny.so EOF cat > "$PAGAN_ROOT/etc/pam.d/system-account" <<'EOF' #%PAM-1.0 account required pam_unix.so account required pam_faillock.so EOF mkdir -p "$PAGAN_ROOT/etc/security" cat > "$PAGAN_ROOT/etc/security/faillock.conf" <<'EOF' # Blokada konta po nieudanych logowaniach (pam_faillock) deny = 4 unlock_time = 600 EOF # pam_faillock wymaga katalogu licznika w tmpfs (/var/run/faillock = /run/faillock). # Bez niego faza "account" odrzuca KAŻDĄ weryfikację konta → sudo/login: # "account validation failure, is your account locked?". /run jest tmpfs, # więc sam mkdir nie wystarczy – dostarczamy wpis tmpfiles.d, który odtworzy # katalog przy każdym starcie (systemd-tmpfiles-setup). mkdir -p "$PAGAN_ROOT/run/faillock" chmod 0700 "$PAGAN_ROOT/run/faillock" mkdir -p "$PAGAN_ROOT/usr/lib/tmpfiles.d" cat > "$PAGAN_ROOT/usr/lib/tmpfiles.d/pam.conf" <<'EOF' # Katalog licznika pam_faillock – musi istnieć po starcie (/run to tmpfs). d /run/faillock 0700 root root - EOF cat > "$PAGAN_ROOT/etc/pam.d/system-password" <<'EOF' #%PAM-1.0 password required pam_unix.so sha512 shadow nullok try_first_pass EOF cat > "$PAGAN_ROOT/etc/pam.d/system-session" <<'EOF' #%PAM-1.0 session required pam_limits.so session required pam_unix.so session optional pam_systemd.so EOF cat > "$PAGAN_ROOT/etc/pam.d/system-login" <<'EOF' #%PAM-1.0 auth include system-auth account include system-account password include system-password session include system-session EOF cat > "$PAGAN_ROOT/etc/pam.d/system-local-login" <<'EOF' #%PAM-1.0 auth include system-login account include system-login password include system-login session include system-login EOF # Nadpisanie domyślnego niespójnego /etc/pam.d/login (np. z shadow), które odwołuje się do SELinux/console cat > "$PAGAN_ROOT/etc/pam.d/login" <<'EOF' #%PAM-1.0 auth required pam_securetty.so auth requisite pam_nologin.so auth include system-local-login account include system-local-login password include system-local-login session include system-local-login session optional pam_loginuid.so EOF # /etc/pam.d/sudo – MUSI zawierać system-account/system-session. Starszy # wariant z "account include system-auth" daje PUSTY stos account (system-auth # ma tylko sekcję auth) → PAM zwraca błąd i sudo pada komunikatem # "account validation failure, is your account locked?". Wymuszamy poprawny plik. cat > "$PAGAN_ROOT/etc/pam.d/sudo" <<'EOF' #%PAM-1.0 auth include system-auth account include system-account session required pam_env.so session include system-session EOF chmod 644 "$PAGAN_ROOT/etc/pam.d/sudo" # Upewnij sie, ze uzytkownik pagan nie ma wygaszonego hasla (wazne dla autologin) chroot "$PAGAN_ROOT" chage -E -1 -m 0 -M 99999 pagan 2>/dev/null || true echo "pagan:changeme" | chroot "$PAGAN_ROOT" chpasswd 2>/dev/null || true ok "Konfiguracja PAM utworzona" } # ========================= LIVE XFCE: PULPIT + IKONA INSTALATORA + TAPETA ========================= # Gdy build ma pełne DE (domyślnie XFCE), live wita pełny pulpit XFCE (root) z: # • ikoną „Zainstaluj Pagan Linux” na pulpicie (uruchamia Calamares), # • tapetą PaganLinux (pliki z install_branding), ustawioną w xfconf dla każdego # użytkownika (root / skel / pagan), ze stylem zoom. _setup_xfce_live_desktop() { # ── ikona (wpis menu + pixmap) ── local _logo="" _cand for _cand in "$SCRIPT_DIR/branding/pagan-loki-pingwin.png" \ "$SCRIPT_DIR/paganlinux-loki-logo.svg"; do [[ -f "$_cand" ]] && { _logo="$_cand"; break; } done if [[ -n "$_logo" ]]; then install -Dm644 "$_logo" "$PAGAN_ROOT/usr/share/pixmaps/pagan-calamares.png" fi mkdir -p "$PAGAN_ROOT/usr/share/applications" cat > "$PAGAN_ROOT/usr/share/applications/pagan-calamares.desktop" <<'EOF' [Desktop Entry] Type=Application Version=1.0 Name=Install Pagan Linux Name[pl]=Zainstaluj Pagan Linux Name[de]=Pagan Linux installieren Comment=Install PaganLinux to the disk Comment[pl]=Zainstaluj PaganLinux na dysku Comment[de]=PaganLinux auf der Festplatte installieren Exec=/usr/bin/calamares Icon=/usr/share/pixmaps/pagan-calamares.png Terminal=false Categories=System;Settings;Utility; Keywords=installer;system;pagan;paganlinux; StartupNotify=true EOF # ── Ikona instalatora TYLKO na pulpicie live (root). NIE kopiujemy jej do # /etc/skel ani /home/pagan – na zainstalowanym systemie jest zbędna. if [[ -d "$PAGAN_ROOT/root" ]]; then mkdir -p "$PAGAN_ROOT/root/Desktop" cp -a "$PAGAN_ROOT/usr/share/applications/pagan-calamares.desktop" \ "$PAGAN_ROOT/root/Desktop/pagan-calamares.desktop" chmod 755 "$PAGAN_ROOT/root/Desktop/pagan-calamares.desktop" fi # ── Wybór sieci Wi-Fi w sesji live (PRZED instalatorem) ── # # Calamares 3.3 NIE ma już strony konfiguracji sieci: opcję # showNetworkConfiguration usunięto w 3.3 (sprawdzone w źródle 3.3.14 – brak # takiego klucza i brak wywołań nm-connection-editor), a moduł # interactiveterminal wymaga KDE Konsole/KParts, którego nie mamy. # Standardowe rozwiązanie dystrybucji: połączyć się z siecią w live przed # uruchomieniem instalatora. Stąd pomocnik pagan-wifi (nmcli + xfce4-terminal). if [[ -f "$SCRIPT_DIR/branding/pagan-wifi" ]]; then install -Dm755 "$SCRIPT_DIR/branding/pagan-wifi" "$PAGAN_ROOT/usr/bin/pagan-wifi" cat > "$PAGAN_ROOT/usr/share/applications/pagan-wifi.desktop" <<'EOF' [Desktop Entry] Type=Application Version=1.0 Name=Connect to Wi-Fi Name[pl]=Połącz z siecią Wi-Fi Comment=Choose a wireless network Comment[pl]=Wybierz sieć bezprzewodową Exec=/usr/bin/pagan-wifi Icon=network-wireless Terminal=true Categories=Network;Settings;Utility; Keywords=wifi;wireless;network;siec;pagan; StartupNotify=true EOF if [[ -d "$PAGAN_ROOT/root/Desktop" ]]; then cp -a "$PAGAN_ROOT/usr/share/applications/pagan-wifi.desktop" \ "$PAGAN_ROOT/root/Desktop/pagan-wifi.desktop" chmod 755 "$PAGAN_ROOT/root/Desktop/pagan-wifi.desktop" fi ok " live: pomocnik Wi-Fi (pagan-wifi) + ikona na pulpicie" else warn " live: brak branding/pagan-wifi – nie ma wyboru sieci przed instalatorem" fi # ── tapeta (root / skel / pagan), workspaces 0-3 ── # Ustawiamy też jawnie /desktop-icons (style=2 → ikony plików/launcherów), # żeby ikona instalatora na pulpicie była widoczna niezależnie od domyślnych # xfdesktop. Domyślny ICON_STYLE_DEFAULT to FILES, ale wolimy nie polegać na # kompilacyjnych domyślach (ENABLE_DESKTOP_ICONS/ENABLE_FILE_ICONS). local _home _cfg _w for _home in /root /etc/skel /home/pagan; do [[ -d "$PAGAN_ROOT$_home" ]] || continue _cfg="$PAGAN_ROOT$_home/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-desktop.xml" mkdir -p "$(dirname "$_cfg")" { echo '' echo '' echo ' ' echo ' ' echo ' ' for _w in 0 1 2 3; do echo " " echo ' ' echo ' ' echo ' ' echo ' ' done echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo ' ' echo '' } > "$_cfg" done info " ✓ XFCE live: ikona Calamares na pulpicie (root) + ikony plików + tapeta PaganLinux" } # Xorg budowany NA HOŚCIE potrafi linkować biblioteki HOSTA (np. libtirpc), # których w rootfs nie ma -> live: „/usr/bin/X: error while loading shared # libraries”. Samouzdrawianie: ldd w chroot -> „not found” -> kopia z hosta # (host == target: glibc 2.44, ABI zgodne – ten sam wzorzec co import libek). import_xorg_runtime_libs() { # Binarki X (jeśli są w rootfs) + narzędzia gruba (są zawsze) – ldd w chroot # pokaże brakujące libki (libtirpc, libdevmapper, ...) i importujemy je z hosta. local _xbin=() local _x # Xorg + menedżer logowania + rdzeń XFCE + Calamares. Gdy tym binarkom # brakuje bibliotek w rootfs, „zainstalowany” system wstaje do TTY bez GUI # (np. lightdm: „lib...: cannot open shared object file”). for _x in /usr/bin/X /usr/bin/Xorg /usr/bin/xinit /usr/bin/Xvfb \ /usr/sbin/lightdm /usr/bin/lightdm /usr/bin/lightdm-gtk-greeter \ /usr/bin/startxfce4 /usr/bin/xfce4-session /usr/bin/xfwm4 \ /usr/bin/xfce4-panel /usr/bin/xfdesktop /usr/bin/thunar \ /usr/bin/calamares; do [[ -e "$PAGAN_ROOT$_x" ]] && _xbin+=("$_x") done # grub-install/mkimage (repo-grub linkuje libdevmapper) – rootfs go nie ma # (source-lvm2 nie położył .so), a bez niego grub-install pada przy instalacji. local _g for _g in /usr/bin/grub-install /usr/bin/grub-mkimage /usr/bin/grub-bios-setup \ /usr/bin/grub-mkconfig /usr/bin/grub-mkstandalone; do [[ -e "$PAGAN_ROOT$_g" ]] && _xbin+=("$_g") done [[ ${#_xbin[@]} -eq 0 ]] && return 0 local _miss _name _src _real _cand _i for _i in 1 2 3; do # || true: ldd zwraca niezerowo, gdy czegoś brakuje; przy set -e + pipefail # niezerowy pipeline w $(...) ubiłby CAŁY build (tuż przed ISO). _miss=$(chroot "$PAGAN_ROOT" /bin/sh -c "ldd ${_xbin[*]} 2>/dev/null" 2>/dev/null \ | awk '/not found/{print $1}' | sort -u || true) [[ -z "$_miss" ]] && break for _name in $_miss; do _src="" for _cand in "/usr/lib/$_name" "/usr/lib64/$_name" "/lib/$_name"; do [[ -e "$_cand" ]] && { _src="$_cand"; break; } done if [[ -z "$_src" ]]; then _src=$(find /usr/lib /usr/lib64 /lib -maxdepth 3 -name "$_name" 2>/dev/null | head -1 || true) fi if [[ -z "$_src" || ! -e "$_src" ]]; then warn " Xorg: brak '$_name' także na hoście (raportuj)" continue fi _real=$(readlink -f "$_src") install -Dm755 "$_real" "$PAGAN_ROOT/usr/lib/$(basename "$_real")" ln -sf "$(basename "$_real")" "$PAGAN_ROOT/usr/lib/$_name" info " Xorg: import libki z hosta: $_name <- $(basename "$_real")" done # skopiowana libka też może mieć braki – pętla to domknie done _miss=$(chroot "$PAGAN_ROOT" /bin/sh -c "ldd ${_xbin[*]} 2>/dev/null" 2>/dev/null \ | awk '/not found/{print $1}' | sort -u || true) if [[ -n "$_miss" ]]; then warn " Xorg: nadal brak w rootfs: $_miss" else info " ✓ Xorg: komplet zależności (ldd OK)" fi } # Stopgap: gdy build gruba daje zepsuty kernel.img i386-pc („miscompiled: start # != 0x9000”), importujemy SPÓJNY zestaw gruba z HOSTA (Arch: moduły i386-pc + # x86_64-efi + narzędzia + potrzebne libki). Host == target (glibc 2.44, x86_64) # – ten sam wzorzec co import libek Xorga. # Uwaga: główną przyczyną był brak -Os w TARGET_CFLAGS (GRUB dodaje -Os tylko # gdy TARGET_CFLAGS jest puste) – receptura przekazuje teraz jawnie -Os. import_host_grub() { step "IMPORT GRUB-A Z HOSTA (kernel.img i386-pc z builda zepsuty)" # UWAGA: importujemy moduły/narzędzia gruba z HOSTA. Zadziała, gdy wersja # gruba na hoście zgadza się z targetem (2.14, ten sam builder). Przy innej # wersji moduły mogą nie pasować do grub-install/mkimage w targetcie. [[ -d /usr/lib/grub/i386-pc ]] || { warn "Brak /usr/lib/grub/i386-pc na hoście – nie importuję"; return 1; } # Kontrola zgodności wersji: import mieszający moduły/narzędzia z RÓŻNYCH # wersji gruba daje niespójny bootloader. Host i target to zwykle ten sam # builder (2.14) – jeśli nie, ostrzegamy zamiast cicho psuć obraz. # Porównujemy tylko wersję upstream (x.y[.z]) – ignorujemy prefiks ścieżki # wywołania, epoch (2:) i pkgrel (-1), które różnią się między dystrybucjami # (np. host "… (GRUB) 2:2.14-1" vs target "… (GRUB) 2.14"). local _tver _hver _hver=$(grub-install --version 2>/dev/null | head -n1 \ | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 || true) _tver=$(chroot "$PAGAN_ROOT" /usr/bin/grub-install --version 2>/dev/null | head -n1 \ | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 || true) [[ -n "$_tver" ]] || _tver=$(chroot "$PAGAN_ROOT" /usr/sbin/grub-install --version 2>/dev/null | head -n1 \ | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 || true) if [[ -n "$_tver" && -n "$_hver" && "$_tver" != "$_hver" ]]; then warn " grub: wersja hosta (GRUB $_hver) != targetu (GRUB $_tver) – import może dać niespójny bootloader" else info " ✓ grub: wersja host/target zgodna (GRUB ${_tver:-?})" fi mkdir -p "$PAGAN_ROOT/usr/lib/grub" cp -a /usr/lib/grub/. "$PAGAN_ROOT/usr/lib/grub/" local _f _d for _f in /usr/bin/grub-* /usr/sbin/grub-*; do [[ -e "$_f" ]] || continue _d="$PAGAN_ROOT$(dirname "$_f")" mkdir -p "$_d" cp -a "$_f" "$PAGAN_ROOT$_f" done info " ✓ moduły + narzędzia gruba skopiowane z hosta" # grub-install/mkimage z hosta może potrzebować libek, których nie ma w # rootfs (np. libdevmapper) – import jak przy Xorgu (pętla domyka zależności). local _gbins=(/usr/bin/grub-install /usr/bin/grub-mkimage \ /usr/bin/grub-bios-setup /usr/bin/grub-mkconfig \ /usr/bin/grub-mkstandalone) local _miss _name _src _real _cand _i for _i in 1 2 3; do # || true: ldd niezerowo przy brakach; set -e + pipefail w $(...) ubiłby build. _miss=$(chroot "$PAGAN_ROOT" /bin/sh -c "ldd ${_gbins[*]} 2>/dev/null" 2>/dev/null \ | awk '/not found/{print $1}' | sort -u || true) [[ -z "$_miss" ]] && break for _name in $_miss; do _src="" for _cand in "/usr/lib/$_name" "/usr/lib64/$_name" "/lib/$_name"; do [[ -e "$_cand" ]] && { _src="$_cand"; break; } done [[ -z "$_src" ]] && _src=$(find /usr/lib /lib /usr/lib64 -maxdepth 3 -name "$_name" 2>/dev/null | head -1 || true) if [[ -z "$_src" || ! -e "$_src" ]]; then warn " grub: brak libki '$_name' także na hoście" continue fi _real=$(readlink -f "$_src") install -Dm755 "$_real" "$PAGAN_ROOT/usr/lib/$(basename "$_real")" ln -sf "$(basename "$_real")" "$PAGAN_ROOT/usr/lib/$_name" info " grub: import libki z hosta: $_name" done done info " ✓ GRUB z hosta gotowy (wersja hosta, spójna: moduły + narzędzia)" } # Release notes: pagan-news / pagan-update + pliki changelogu. # System jest ROLLING (repo PAG): nowa wersja ISO nie wymaga reinstalacji # (aktualizacja to `pag update && pag upgrade`); wpisy changelogu to pliki # /usr/share/pagan/news/.md. Wydzielone, żeby odświeżał to także # fix-calamares.sh (respin ISO bez pełnego rebuildu). _write_pagan_news() { mkdir -p "$PAGAN_ROOT/usr/share/pagan/news" "$PAGAN_ROOT/var/lib/pag" cat > "$PAGAN_ROOT/usr/bin/pagan-news" <<'NEWSEOF' #!/bin/bash # pagan-news – pokazuje wpisy zmian (release notes) nowsze niż ostatnio pokazane. # Nowy wpis: dodaj plik /usr/share/pagan/news/.md (np. 0.2.0.md). [ "$(id -u)" = "0" ] || { echo "Uruchom jako root: sudo pagan-news"; exit 1; } NEWS_DIR="/usr/share/pagan/news" SEEN_FILE="/var/lib/pag/news.seen" [ -d "$NEWS_DIR" ] || { echo "Brak katalogu z newsami: $NEWS_DIR"; exit 0; } seen="" [ -f "$SEEN_FILE" ] && seen="$(cat "$SEEN_FILE" 2>/dev/null)" state=0 [ -z "$seen" ] && state=1 newest="$seen" while read -r v; do [ -n "$v" ] || continue if [ "$state" = "0" ]; then if [ "$v" = "$seen" ]; then state=1; fi continue fi newest="$v" echo "" echo "==================================================" cat "$NEWS_DIR/$v.md" 2>/dev/null || true echo "" done < <(for f in "$NEWS_DIR"/*.md; do [ -f "$f" ] || continue; basename "$f" .md; done | sort -V) if [ -n "$newest" ]; then echo "$newest" > "$SEEN_FILE" 2>/dev/null || true fi exit 0 NEWSEOF chmod 755 "$PAGAN_ROOT/usr/bin/pagan-news" cat > "$PAGAN_ROOT/usr/bin/pagan-update" <<'UPDEOF' #!/bin/bash # pagan-update – pełna aktualizacja systemu z repo PAG + pokazanie nowości. # Użycie: pagan-update [--check] (--check = tylko pokaż newsy, bez aktualizacji) set -u [ "$(id -u)" = "0" ] || { echo "Uruchom jako root: sudo pagan-update"; exit 1; } if [ "${1:-}" = "--check" ]; then exec /usr/bin/pagan-news fi echo "==> pag update (synchronizacja repo)..." /usr/bin/pag update || { echo "pag update nieudane (sieć/repo?)"; exit 1; } echo "==> pag upgrade (aktualizacja pakietów)..." /usr/bin/pag upgrade rc=$? /usr/bin/pagan-news exit "$rc" UPDEOF chmod 755 "$PAGAN_ROOT/usr/bin/pagan-update" # Wpis startowy 0.1.0. # Jak dodać wpis dla kolejnej wersji: utwórz /usr/share/pagan/news/.md, # opisz zmiany – użytkownicy zobaczą go po `sudo pagan-update` (albo `sudo pagan-news`). # (Ta instrukcja jest CELOWO komentarzem w builderze – nie trafia do treści newsa.) cat > "$PAGAN_ROOT/usr/share/pagan/news/0.1.0.md" <<'NEWSMD' # PaganLinux 0.1.0 Pierwsze wydanie systemu: * środowisko **XFCE 4.20** (budowane ze źródeł, tapeta Pagan + motyw Nordic-darker + ikony Papirus-Dark) * instalator systemu **Calamares** (GUI) * menedżer pakietów **pag** + repo https://repo.paganlinux.eu/stable * powłoka **fish** dla użytkowników (root na live pozostaje przy bashu) * live ISO (BIOS + UEFI), squashfs + overlay, kernel z obsługą WiFi USB NEWSMD ok "Release notes: pagan-news + pagan-update zainstalowane" } finalize_build() { step "FINALIZACJA BUDOWY" configure_pam # Zagnieżdżony rootfs z błędnego DESTDIR+sysroot (np. gdy sdkdir z # xorg-server.pc wskazywał już $PAGAN_ROOT/usr → /mnt/pagan/mnt/pagan). # NIE kasujemy – SCALAMY z rootfsem, bo kasowanie gubi legalne pliki # (NetworkManager.service, polkit.service, org.freedesktop.PolicyKit1.service # itd.). Bez polkitd helper KPMcore nie autoryzuje `lsblk` → Calamares # pokazuje „There are no partitions to install on”. # Normalnie jest to już puste (scalanie po każdym pakiecie) – zostaje jako # bezpiecznik dla przebiegów sprzed tej poprawki. _merge_nested_rootfs # Dopełnij pliki polkita (gdyby scalanie czegoś nie objęło). _ensure_system_groups _ensure_polkit_units # dbus: jednostki bez cyklu zależności (inaczej D-Bus nie wstaje → brak # polkitd, NetworkManager i lightdm). Naprawia też stary rootfs. _ensure_dbus_units # D-Bus: polityka systemd (/usr/share/dbus-1/system.d/org.freedesktop.*.conf). # Bez niej obowiązuje z /usr/share/dbus-1/system.conf i PID1 # NIE MOŻE zająć nazwy org.freedesktop.systemd1 (zweryfikowane eksperymentalnie # na dbus-daemon 1.16.2). Skutki: „systemctl” nie działa, aktywacja kończy się # „Failed to activate service 'org.freedesktop.systemd1': timed out”, logind nie # tworzy sesji (ENOTCONN) i nie startuje lightdm. _ensure_systemd_dbus_policy # WiFi: wpa_supplicant w /usr/sbin + aktywacja D-Bus (inaczej NM nie ma # czym zarządzać kartą WLAN – dotyczy też kart na USB). _ensure_wifi_supplicant # NetworkManager: jednostki + polityka D-Bus + włączenie w live. Bez tego # live nie podnosi interfejsu (brak DHCP – także w QEMU), mimo że sprzęt # i sterowniki są w porządku. _ensure_network_manager # Usługi pulpitu (upower/udisks2) – włączenie jest tanie i idempotentne, # a musi być w rootfsie, żeby trafiło też do systemu instalowanego (Calamares # kopiuje rootfs). Wołane także z install_desktop_extras. _ensure_desktop_units # Netinstall: backend `pag` dla modułu packages Calamaresa. _install_pag_package_backend # Udev/hwdb: bez /etc/udev/hwdb.bin generator imds loguje błąd przy starcie. _ensure_hwdb # Strefy czasowe: bez skompilowanych danych Calamares przerywa instalację # na module locale (SetTimezoneJob → „Bad path: …/zoneinfo/Europe/Warsaw”). _install_tzdata # GRUB: aliasy initramfs-.img + /etc/default/grub. Bez nich # grub-mkconfig (10_linux) nie znajdzie initrd i wpis w menu GRUB nie ma # linii `initrd` – zainstalowany system startuje bez initramfs. _ensure_grub_boot_artifacts # CZYSTY OBRAZ: w ISO ŻADEN display manager nie może być włączony (relikty # po wcześniejszych buildach w rootfs po resume: sddm, gdm itd. potrafią # kraść vt w live). Live startuje przez getty tty1 → startx. DM na # zainstalowanym systemie włącza DOPIERO instalator – moduł `displaymanager` # Calamaresa (lightdm). Binarki i konfiguracje zostają – usuwamy tylko stan # „enabled”. local _dm_unit for _dm_unit in sddm gdm lightdm lxdm slim; do chroot "$PAGAN_ROOT" env -u LD_LIBRARY_PATH \ systemctl disable "$_dm_unit" 2>/dev/null || true done rm -f "$PAGAN_ROOT/etc/systemd/system/display-manager.service" 2>/dev/null || true # --- Konfiguracja live: autologin root na tty1 + sesja XFCE (Calamares) --- # Autologin na tty1 (root) dla automatycznego startu sesji live mkdir -p "$PAGAN_ROOT/etc/systemd/system/getty@tty1.service.d" cat > "$PAGAN_ROOT/etc/systemd/system/getty@tty1.service.d/autologin.conf" <<'EOF' [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear %I $TERM EOF # Fallback .xinitrc (gdy brak startxfce4): startx → Calamares. # Polski układ klawiszy – DISPLAY jest już ustawione. mkdir -p "$PAGAN_ROOT/root" cat > "$PAGAN_ROOT/root/.xinitrc" <<'EOF' #!/bin/sh # Minimalna sesja X live: instalator Calamares (bez DE). setxkbmap -layout pl 2>/dev/null || true /usr/bin/calamares > /tmp/calamares.log 2>&1 _rc=$? if [ $_rc -ne 0 ] && [ -s /tmp/calamares.log ]; then echo "--- calamares (stdout+stderr, kod $_rc): ---" >&2 tail -n 60 /tmp/calamares.log >&2 fi exit $_rc EOF chmod +x "$PAGAN_ROOT/root/.xinitrc" # DE z pełnym pulpitem (domyślnie XFCE): live wita XFCE z ikoną instalatora # i tapetą zamiast „gołego” okna instalatora (patrz wyżej – tryb minimalny). local _live_de="${PAGAN_DE,,}" if [[ "$_live_de" == "xfce" && -x "$PAGAN_ROOT/usr/bin/startxfce4" ]]; then cat > "$PAGAN_ROOT/root/.xinitrc" <<'EOF' #!/bin/sh # Live XFCE (root). Wyjście sesji + diagnostyka trafiają do /tmp/xfce-session.log. # Dodatkowo w MASZYNIE WIRTUALNEJ (QEMU/KVM – systemd-detect-virt) lustrujemy je # na /dev/ttyS0 → qemu_boot.log. Na prawdziwym PC sink = /dev/null (żaden port # szeregowy nie jest potrzebny ani nie blokuje startu). setxkbmap -layout pl 2>/dev/null || true SINK=/dev/null if [ -c /dev/ttyS0 ] && command -v systemd-detect-virt >/dev/null 2>&1; then case "$(systemd-detect-virt 2>/dev/null)" in ""|none) ;; # fizyczny PC – bez lustra na serial *) SINK=/dev/ttyS0 ;; # VM (kvm/qemu/…) – log na serial esac fi { echo "===== [live-xfce] start $(date) =====" # efivarfs: bez zamontowanych zmiennych firmware efibootmgr (wołany przez # moduł bootloader) kończy się błędem. W maszynie UEFI montujemy jawnie. if [ -d /sys/firmware/efi ] && [ ! -d /sys/firmware/efi/efivars ]; then mount -t efivarfs efivarfs /sys/firmware/efi/efivars 2>/dev/null || true fi # D-Bus wymaga setuid helpera polkita jako root:root 4755. Squashfs/overlay # może zachować plik, ale zgubić bit setuid, więc odtwarzamy go w live. if [ -e /usr/lib/polkit-1/polkit-agent-helper-1 ]; then chown root:root /usr/lib/polkit-1/polkit-agent-helper-1 2>/dev/null || true chmod 4755 /usr/lib/polkit-1/polkit-agent-helper-1 2>/dev/null || true fi if [ -e /usr/lib/libexec/kpmcore_externalcommand ]; then chown root:root /usr/lib/libexec/kpmcore_externalcommand 2>/dev/null || true chmod 4755 /usr/lib/libexec/kpmcore_externalcommand 2>/dev/null || true fi if [ -e /usr/libexec/dbus-daemon-launch-helper ]; then chown root:root /usr/libexec/dbus-daemon-launch-helper 2>/dev/null || true chmod 4755 /usr/libexec/dbus-daemon-launch-helper 2>/dev/null || true fi stat -c '[live-xfce] polkit-helper: %A %a %U:%G %n' \ /usr/lib/polkit-1/polkit-agent-helper-1 2>/dev/null || true stat -c '[live-xfce] kpmcore-helper: %A %a %U:%G %n' \ /usr/lib/libexec/kpmcore_externalcommand 2>/dev/null || true stat -c '[live-xfce] dbus-helper: %A %a %U:%G %n' \ /usr/libexec/dbus-daemon-launch-helper 2>/dev/null || true # KPMcore (moduł partition Calamaresa) wykonuje `lsblk` przez swój D-Bus helper, # a ten autoryzuje się przez polkit → w live MUSZĄ działać systemowy D-Bus # i polkitd. `systemctl` roota działa także bez magistrali (private socket), # więc wymuszamy ich start zanim ruszy pulpit/instalator. systemctl start dbus.socket dbus.service polkit.service 2>/dev/null || true sleep 1 echo "[live-xfce] dbus-daemon: $(pgrep -c dbus-daemon 2>/dev/null || echo 0) proc; polkitd: $(pgrep -c polkitd 2>/dev/null || echo 0) proc" # Sieć: ważne przy instalacji z netinstall (moduł packages pobiera pakiety) # oraz dla WiFi – użytkownik może je ustawić w live przed instalatorem. echo "[live-xfce] sieć: $(nmcli -t -f DEVICE,TYPE,STATE device 2>/dev/null | tr '\n' ' ')" if ! pgrep -x polkitd >/dev/null 2>&1; then echo "----- polkit.service nie wstał – diagnostyka -----" systemctl status polkit.service --no-pager -l 2>&1 | head -20 systemctl status dbus.socket dbus.service --no-pager -l 2>&1 | head -20 journalctl -b -u polkit.service -u dbus.service --no-pager 2>/dev/null | tail -30 fi if command -v dbus-run-session >/dev/null 2>&1; then dbus-run-session -- startxfce4 else startxfce4 fi _rc=$? echo "===== [live-xfce] sesja zakonczona rc=$_rc =====" echo "----- Xorg.0.log (ogon) -----" tail -n 40 /root/.local/share/xorg/Xorg.0.log 2>/dev/null || true tail -n 40 /var/log/Xorg.0.log 2>/dev/null || true echo "----- xsession-errors (ogon) -----" tail -n 60 /root/.xsession-errors 2>/dev/null || true echo "----- journalctl (xfce/err/dbus) -----" journalctl -b --no-pager 2>/dev/null | grep -iE 'xfce|xfdesktop|xfwm|xfsettings|panel|dbus|error|fail|fatal' | tail -n 60 || true echo "===== [live-xfce] koniec =====" exit "$_rc" } 2>&1 | tee /tmp/xfce-session.log > "$SINK" EOF chmod +x "$PAGAN_ROOT/root/.xinitrc" _setup_xfce_live_desktop fi mkdir -p "$PAGAN_ROOT/etc/profile.d" cat > "$PAGAN_ROOT/etc/profile.d/pagan-live.sh" <<'EOF' # Automatyczny start sesji live na tty1 (autologin root). # Live wita pulpit XFCE (instalator Calamares uruchamiasz ikoną na pulpicie). # Gdy XFCE niedostępny, .xinitrc sam odpala Calamares. if [ "$(tty 2>/dev/null)" = "/dev/tty1" ] && command -v startx >/dev/null 2>&1; then if [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ]; then return 0 2>/dev/null || true fi clear echo "PaganLinux – uruchamiam sesję live (startx)..." startx _xrc=$? clear if [ "$_xrc" -ne 0 ]; then echo "Sesja X zakończyła się z kodem $_xrc – logi (ostatnie linie):" _xlog="$(ls -t /root/.local/share/xorg/Xorg.0.log /var/log/Xorg.0.log 2>/dev/null | head -n1)" [ -n "$_xlog" ] && tail -n 20 "$_xlog" [ -s /tmp/calamares.log ] && { echo "--- calamares ---"; tail -n 30 /tmp/calamares.log; } fi echo "" echo "Uruchomić ponownie? [y/N] " read -r _ans case "${_ans:-n}" in y|Y) reboot ;; esac fi EOF chmod +x "$PAGAN_ROOT/etc/profile.d/pagan-live.sh" # /etc/profile – bash login shell ładuje TYLKO /etc/profile; bez niego # skrypty /etc/profile.d/*.sh (m.in. pagan-live.sh) NIGDY się nie # wykonują i live wita gołym bashem zamiast pulpitem live (GUI). # (Standardowy wzorzec LFS/BLFS "Configuring the Bash Shell Startup Files") if [[ ! -s "$PAGAN_ROOT/etc/profile" ]]; then cat > "$PAGAN_ROOT/etc/profile" <<'EOF' # /etc/profile – systemowe zmienne środowiskowe i programy startowe # (wczytywane przez każdy bash login shell). export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin export HISTCONTROL=ignoredups export HISTSIZE=1000 export HISTFILESIZE=2000 if [ "$PS1" ]; then # interaktywny shell umask 022 fi # Ładuj skrypty z /etc/profile.d (kolejność alfabetyczna) for script in /etc/profile.d/*.sh ; do if [ -r "$script" ]; then . "$script" fi done unset script EOF info " ✓ /etc/profile (ładuje /etc/profile.d/*.sh)" fi # /etc/bashrc – dla interaktywnych shelli NIE-loginowych if [[ ! -s "$PAGAN_ROOT/etc/bashrc" ]]; then cat > "$PAGAN_ROOT/etc/bashrc" <<'EOF' # /etc/bashrc – interaktywne shelle nie-loginowe (aliasy/funkcje) if [ "$PS1" ]; then umask 022 fi EOF fi # DNS dla live: bez /etc/resolv.conf curl nie rozwiąże nazw i netinstall # pada („repo unreachable”) nawet z działającą siecią. # # PISZEMY ZAWSZE, nie tylko gdy pliku brak: _pag_install/install_calamares # kopiują HOSTOWY /etc/resolv.conf do rootfsu na czas instalacji z repo i nie # zawsze go potem czyszczą, więc do ISO trafiał adres DNS z sieci budującej # (np. 192.168.x.x) – bezużyteczny poza tą siecią i wyciek prywatnego adresu # do wydania. NetworkManager nadpisze ten plik dzierżawą DHCP po połączeniu, # a poniższe wpisy to tylko sensowny awaryjny fallback. cat > "$PAGAN_ROOT/etc/resolv.conf" <<'EOF' # Nadpisywany przez NetworkManager po uzyskaniu adresu (DHCP). # Poniżej tylko awaryjny fallback – gdyby NM nie wystartował. nameserver 1.1.1.1 nameserver 8.8.8.8 EOF info " ✓ /etc/resolv.conf: neutralny fallback (DNS hosta nie trafia do ISO)" # Szyfrowany DNS (DoT) – systemd-resolved w zainstalowanym systemie użyje # tego pliku (instalator włącza resolved + stub resolv.conf). DoT = 853/tls, # wiec nawet na otwartej sieci nikt nie podgląda zapytań DNS. if [[ -n "$PAGAN_DNS" ]]; then cat > "$PAGAN_ROOT/etc/systemd/resolved.conf" < "$PAGAN_ROOT/etc/sysctl.d/99-pagan-hardening.conf" <<'EOF' # Pagan Linux – utwardzanie jądra (sysctl) # Ukrywanie informacji kernel.kptr_restrict=2 kernel.dmesg_restrict=1 kernel.unprivileged_bpf_disabled=1 kernel.yama.ptrace_scope=1 # Sieć: antyspoofing, brak przekierowań, syn-cookies net.ipv4.conf.all.rp_filter=1 net.ipv4.conf.default.rp_filter=1 net.ipv4.conf.all.accept_redirects=0 net.ipv4.conf.default.accept_redirects=0 net.ipv6.conf.all.accept_redirects=0 net.ipv6.conf.default.accept_redirects=0 net.ipv4.icmp_echo_ignore_broadcasts=1 net.ipv4.tcp_syncookies=1 # Ochrona plików (hardlink/symlink) fs.protected_hardlinks=1 fs.protected_symlinks=1 EOF info " ✓ /etc/sysctl.d/99-pagan-hardening.conf" # ── SSH: domyślnie WYŁĄCZONY + utwardzony ── chroot "$PAGAN_ROOT" systemctl disable sshd 2>/dev/null || true mkdir -p "$PAGAN_ROOT/etc/ssh/sshd_config.d" cat > "$PAGAN_ROOT/etc/ssh/sshd_config.d/99-pagan.conf" <<'EOF' # Pagan Linux – utwardzanie SSH (usługa domyślnie wyłączona) PermitRootLogin no MaxAuthTries 4 LoginGraceTime 30 X11Forwarding no AllowTcpForwarding no EOF chmod 600 "$PAGAN_ROOT/etc/ssh/sshd_config.d/99-pagan.conf" 2>/dev/null || true info " ✓ sshd utwardzony + wyłączony (włącz: systemctl enable sshd)" # Domyślny start do trybu tekstowego (multi-user.target) zamiast odpalania menedżera logowania chroot "$PAGAN_ROOT" systemctl set-default multi-user.target 2>/dev/null || true chroot "$PAGAN_ROOT" systemctl disable sddm 2>/dev/null || true # Uprawnienia sudo dla grupy wheel mkdir -p "$PAGAN_ROOT/etc/sudoers.d" echo "%wheel ALL=(ALL:ALL) ALL" > "$PAGAN_ROOT/etc/sudoers.d/wheel" chmod 440 "$PAGAN_ROOT/etc/sudoers.d/wheel" 2>/dev/null || true # Przywróć shebang perl do #!/usr/bin/perl (docelowy system w chrocie ma tam swój perl) find "$PAGAN_ROOT/usr/bin" -maxdepth 1 -type f -exec sed -i 's|^#!'"$PAGAN_ROOT"'/usr/bin/perl|#!/usr/bin/perl|' {} + 2>/dev/null || true # /etc/pagan-release cat > "$PAGAN_ROOT/etc/pagan-release" < "$PAGAN_ROOT/etc/issue" < "$PAGAN_ROOT/etc/os-release" </dev/null || warn "ldconfig -r nie powiodło się (niekrytyczne)" # XKB: dane xkeyboard-config leżą w /usr/share/xkeyboard-config-2, a Xorg czyta # /usr/share/X11/xkb (symlink). Pre-cleanup przy buildzie potrafi go usunąć, # a przy skip-resume nikt go nie odtwarza → domykamy ZAWSZE na końcu buildu. if [[ -d "$PAGAN_ROOT/usr/share/xkeyboard-config-2" && ! -L "$PAGAN_ROOT/usr/share/X11/xkb" ]]; then rm -rf "$PAGAN_ROOT/usr/share/X11/xkb" mkdir -p "$PAGAN_ROOT/usr/share/X11" ln -s ../xkeyboard-config-2 "$PAGAN_ROOT/usr/share/X11/xkb" info " ✓ XKB: /usr/share/X11/xkb → xkeyboard-config-2" fi _write_pagan_news # GSettings: bez gschemas.compiled każde g_settings… pada (at-spi robi TRAP: # „Cannot get the default GSettingsSchemaSource”). Kompilujemy schematy. if [[ -d "$PAGAN_ROOT/usr/share/glib-2.0/schemas" && -x "$PAGAN_ROOT/usr/bin/glib-compile-schemas" ]]; then chroot "$PAGAN_ROOT" env -u LD_LIBRARY_PATH /usr/bin/glib-compile-schemas \ /usr/share/glib-2.0/schemas >/dev/null 2>&1 || true info " ✓ GSettings: gschemas.compiled wygenerowane" fi # at-spi (a11y) startuje z autostartu XFCE, a at-spi2-core w tym buildzie nie # dostarcza schemy org.a11y.Bus → launcher wpada w TRAP przy starcie sesji. # Na 0.1 wyłączamy autostart (usługa pozostaje aktywowalna na żądanie). if [[ -f "$PAGAN_ROOT/etc/xdg/autostart/at-spi-dbus-bus.desktop" ]]; then mv -f "$PAGAN_ROOT/etc/xdg/autostart/at-spi-dbus-bus.desktop" \ "$PAGAN_ROOT/etc/xdg/autostart/at-spi-dbus-bus.desktop.disabled" 2>/dev/null || true info " ✓ at-spi autostart wyłączony (brak org.a11y.Bus schema)" fi # GTK: bez gsettings-desktop-schemas GTK potrafi nie ustawić motywu ikon i # spaść do (pustego) hicolor → „Could not load a pixbuf from icon theme” + # twardy assert libwnck w xfce4-session. Wymuszamy Adwaitę w /etc/gtk-3.0 # i dajemy fallback PNG w hicolor dla ikony okien (niezależny od loadera SVG). mkdir -p "$PAGAN_ROOT/etc/gtk-3.0" if [[ ! -f "$PAGAN_ROOT/etc/gtk-3.0/settings.ini" ]]; then cat > "$PAGAN_ROOT/etc/gtk-3.0/settings.ini" <<'GTKINI' [Settings] gtk-icon-theme-name=Adwaita gtk-theme-name=Adwaita gtk-font-name=Sans 10 GTKINI fi mkdir -p "$PAGAN_ROOT/usr/share/icons/hicolor/16x16/apps" \ "$PAGAN_ROOT/usr/share/icons/hicolor/scalable/apps" _ai16="$PAGAN_ROOT/usr/share/icons/Adwaita/16x16/mimetypes/application-x-executable.png" _aisvg="$PAGAN_ROOT/usr/share/icons/Adwaita/scalable/mimetypes/application-x-executable.svg" [[ -f "$_ai16" ]] && cp -a "$_ai16" "$PAGAN_ROOT/usr/share/icons/hicolor/16x16/apps/application-x-executable.png" [[ -f "$_aisvg" ]] && cp -a "$_aisvg" "$PAGAN_ROOT/usr/share/icons/hicolor/scalable/apps/application-x-executable.svg" chroot "$PAGAN_ROOT" env -u LD_LIBRARY_PATH /usr/bin/gtk-update-icon-cache -f -q \ /usr/share/icons/hicolor 2>/dev/null || true chroot "$PAGAN_ROOT" env -u LD_LIBRARY_PATH /usr/bin/gtk-update-icon-cache -f -q \ /usr/share/icons/Adwaita 2>/dev/null || true info " ✓ GTK: motyw ikon Adwaita wymuszony + fallback PNG w hicolor" # X + narzędzia gruba muszą mieć komplet libek w rootfs – inaczej GUI live # nie wstanie („libtirpc.so.3: …”), a grub-install pada: # „libdevmapper.so.1.02: cannot open shared object file”. Import z hosta # (host == target glibc 2.44); funkcja pomija nieobecne binarki. import_xorg_runtime_libs || warn " Xorg: import libek nieudany – kontynuuję" # Manifest bazy z obrazu (/usr/share/pag/base.json) – dla `pag base`: # informuje, które pakiety wbudował builder (nazwa + wersja), żeby # `pag base adopt` mógł dograć je z repo i włączyć je do rollingu. generate_base_manifest ok "Finalizacja zakończona!" } # ========================= WERYFIKACJA SYSTEMU ========================= verify_rootfs() { step "WERYFIKACJA SYSTEMU – sanity check po buildzie" local fails=0 VERIFY_CRITICAL=0 # globalny licznik braków KRYTYCZNYCH (patrz `crit`) # ── BOOTOWALNOŚĆ (krytyczne) ──────────────────────────────────────────── # To, bez czego system NIE WSTANIE. Sprawdzane jako pierwsze i twardo, bo # właśnie tu obraz potrafił wyjść niekompletny (brak NVIDIA, niepełny # initramfs) i mimo to został wydany. local _kimg _init _mdep _grubimg _gtext _kimg=$(ls -1 "$PAGAN_ROOT/boot/"vmlinuz* 2>/dev/null | grep -v 'vmlinuz-linux$' | head -n1 || true) if [[ -n "$_kimg" ]]; then ok "Boot: jądro $(basename "$_kimg")" else crit "Boot: brak /boot/vmlinuz* – GRUB (10_linux szuka 'vmlinuz*') nie znajdzie jądra" fi # initramfs: pusty/uszkodzony = rootfs się nie zamontuje (brak squashfs/ext4/nvme). _init=$(ls -1 "$PAGAN_ROOT/boot/"initramfs* "$PAGAN_ROOT/boot/"initrd* 2>/dev/null | head -n1 || true) if [[ -s "$_init" ]]; then ok "Boot: initramfs $(basename "$_init") ($(du -hL "$_init" 2>/dev/null | cut -f1 || echo '?'))" else crit "Boot: brak lub pusty /boot/initramfs* – system nie wystartuje (rootfs się nie zamontuje)" fi # modules.dep: bez depmod żaden moduł nie zostanie znaleziony (m.in. squashfs, # sterowniki dysku i nvidia-drm). _mdep=$(ls -1 "$PAGAN_ROOT/usr/lib/modules/"*/modules.dep 2>/dev/null | head -n1 || true) if [[ -s "$_mdep" ]]; then ok "Boot: $(basename "$(dirname "$_mdep")")/modules.dep obecne (depmod wykonany)" else crit "Boot: brak /usr/lib/modules/*/modules.dep – moduły się nie załadują (brak depmod)" fi # GRUB: instalator (Calamares) i `pag grub-update` wołają grub-install/mkconfig. if [[ -x "$PAGAN_ROOT/usr/bin/grub-install" || -x "$PAGAN_ROOT/usr/sbin/grub-install" ]]; then ok "Boot: grub-install obecny" else crit "Boot: brak grub-install – instalacja bootloadera padnie" fi # GRUB i386-pc: kernel.img musi mieć .text na 0x9000, inaczej grub-mkimage kończy # „kernel.img is miscompiled: its start address is not 0x9000” i instalacja na # BIOS (MBR) NIE działa. UWAGA: w GRUB 2.14 kernel.img JEST plikiem ELF (dlatego # czyta go readelf), a `kernel.exec` NIE jest używany przez grub-mkimage – jego # adres 0x9074 (typowy po imporcie z dystrybucji) niczego nie psuje, więc go nie # sprawdzamy. Test funkcjonalny: grub-mkimage -d /usr/lib/grub/i386-pc. # # PORÓWNANIE LICZBOWE (_addr_is_0x9000), nie tekstowe: readelf zeruje pole # Addr do szerokości ELF-a, więc dla i386-pc wychodzi „00009000”. Wcześniejsze # `== "9000"` dawało fałszywy „miscompiled” i wstrzymywało wydanie ISO mimo # sprawnego GRUB-a (a wcześniejsza kontrola w fazie builda, glob `*9000`, # raportowała wtedy OK – stąd sprzeczność w logach). _grubimg="$PAGAN_ROOT/usr/lib/grub/i386-pc/kernel.img" if [[ -f "$_grubimg" ]] && command -v readelf >/dev/null 2>&1; then _gtext=$(readelf -SW "$_grubimg" 2>/dev/null \ | sed -n 's/^ *\[ *[0-9]*\] *\.text *[A-Z]* *\([0-9a-fA-F]*\).*/\1/p' | head -n1 || true) if _addr_is_0x9000 "$_gtext"; then ok "Boot: GRUB i386-pc kernel.img .text=0x9000 (instalacja na BIOS OK)" else crit "Boot: GRUB i386-pc kernel.img .text=0x${_gtext:-?} (ma być 0x9000) – instalacja na BIOS padnie (grub-mkimage: miscompiled)" fi fi # Strefy czasowe: Calamares (SetTimezoneJob) wymaga SKOMPILOWANYCH danych # w /usr/share/zoneinfo//, inaczej instalacja kończy się # błędem „Bad path” jeszcze przed zapisem systemu. if [[ -f "$PAGAN_ROOT/usr/share/zoneinfo/Europe/Warsaw" ]]; then ok "Strefy czasowe: dane zoneinfo skompilowane" else warn "Brak /usr/share/zoneinfo/Europe/Warsaw – instalator padnie na wyborze strefy czasowej" ((fails++)) || true fi # Moduły Pythona Calamaresa czytają klucze konfiguracji TWARDO # (configuration["…"]), a Calamares nie scala plików conf — bierze pierwszy # istniejący. Moduł bootloader bez klucza grubInstall/efiBootMgr rzuca # KeyError, co Calamares raportuje jako „Bad main script file … raised an # exception”. Sprawdzamy więc kompletność tego pliku. local _bl_conf="$PAGAN_ROOT/etc/calamares/modules/bootloader.conf" if [[ -f "$_bl_conf" ]]; then local _missing="" _key for _key in efiBootLoader grubInstall grubMkconfig grubCfg grubProbe efiBootMgr; do grep -qE "^[[:space:]]*${_key}:" "$_bl_conf" || _missing+=" $_key" done if [[ -n "$_missing" ]]; then warn "bootloader.conf: brak kluczy:$_missing – moduł bootloader padnie (KeyError)" ((fails++)) || true else ok "Calamares bootloader.conf: wszystkie wymagane klucze obecne" fi else warn "Brak /etc/calamares/modules/bootloader.conf – moduł bootloader padnie" ((fails++)) || true fi # efibootmgr jest potrzebny przy instalacji na UEFI (efi_boot_next). if [[ -x "$PAGAN_ROOT/usr/bin/efibootmgr" ]]; then ok "efibootmgr obecny (instalacja UEFI)" else warn "Brak efibootmgr – instalacja na UEFI padnie na module bootloader" ((fails++)) || true fi # WiFi: bez wpa_supplicant w /usr/sbin + aktywacji D-Bus NetworkManager nie # obsłuży żadnej karty WLAN (także USB). if [[ -x "$PAGAN_ROOT/usr/sbin/wpa_supplicant" ]]; then if [[ -f "$PAGAN_ROOT/usr/share/dbus-1/system-services/fi.w1.wpa_supplicant1.service" ]]; then ok "WiFi: wpa_supplicant + aktywacja D-Bus obecne" else warn "WiFi: brak aktywacji D-Bus dla wpa_supplicant – NetworkManager nie połączy WLAN" ((fails++)) || true fi if [[ -x "$PAGAN_ROOT/usr/local/sbin/wpa_supplicant" ]]; then warn "WiFi: kopia wpa_supplicant w /usr/local/sbin – NM szuka w /usr/sbin" fi else warn "WiFi: brak /usr/sbin/wpa_supplicant – sieć bezprzewodowa nie zadziała" ((fails++)) || true fi # Netinstall: moduł packages musi znać backend `pag`. if [[ -f "$PAGAN_ROOT/usr/lib/calamares/modules/packages/main.py" ]]; then if grep -q 'PAGAN: backend pag' "$PAGAN_ROOT/usr/lib/calamares/modules/packages/main.py"; then ok "Calamares: backend pag dla netinstall obecny" else warn "Calamares: brak backendu pag – netinstall nie zainstaluje pakietów" ((fails++)) || true fi fi # D-Bus: wykryj stary, wadliwy układ (usługa w sockets.target.wants), który # tworzy cykl zależności i TWARDO blokuje start systemu. if [[ -e "$PAGAN_ROOT/etc/systemd/system/sockets.target.wants/dbus.service" ]]; then crit "dbus: dbus.service w sockets.target.wants → cykl zależności (system nie wstanie)" else if [[ -e "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants/dbus.service" ]] || \ [[ -e "$PAGAN_ROOT/etc/systemd/system/sockets.target.wants/dbus.socket" ]]; then ok "dbus: układ jednostek bez cyklu zależności" else crit "dbus: brak obu dowiązań (multi-user.target.wants/dbus.service i sockets.target.wants/dbus.socket) – bez D-Busa nie wstanie polkitd ani sieć" fi fi # NetworkManager: bez jednostki systemd w live NIKT nie podniesie interfejsu # (brak DHCP), a bez polityki D-Bus NM nie zajmie nazwy na magistrali. # To najczęstsza przyczyna „QEMU nie przekazuje sieci” – a QEMU przekazuje. if [[ -x "$PAGAN_ROOT/usr/sbin/NetworkManager" ]]; then local _nm_ok=1 if [[ ! -f "$PAGAN_ROOT/usr/lib/systemd/system/NetworkManager.service" ]]; then warn "NetworkManager: brak jednostki /usr/lib/systemd/system/NetworkManager.service – live bez sieci (brak DHCP)" _nm_ok=0 fi if [[ ! -f "$PAGAN_ROOT/usr/share/dbus-1/system.d/org.freedesktop.NetworkManager.conf" ]]; then warn "NetworkManager: brak polityki D-Bus – NM nie zajmie nazwy org.freedesktop.NetworkManager" _nm_ok=0 fi if [[ ! -e "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants/NetworkManager.service" ]]; then warn "NetworkManager: nie włączony w multi-user.target.wants – live wystartuje bez sieci" _nm_ok=0 fi if [[ $_nm_ok -eq 1 ]]; then ok "NetworkManager: jednostka + polityka D-Bus + włączony w live" else crit "NetworkManager: niekompletny (jednostka/polityka/włączenie) – live bez sieci. QEMU sieć PRZEKAZUJE (-netdev user), więc winny jest obraz, a nie maszyna wirtualna" fi else crit "Brak /usr/sbin/NetworkManager – system bez menedżera sieci (tylko DHCP na piechotę)" fi # Dodatki pulpitu – każdy wpis naprawia KONKRETNY błąd z sesji live # (log serial qemu_install.log). Brak = sesja działa, ale okrojona. local _dt_ok=1 if [[ ! -x "$PAGAN_ROOT/usr/bin/xrdb" ]]; then warn "xrdb: brak /usr/bin/xrdb – xinitrc XFCE zgłosi „xrdb: command not found” (Xresources nie wczytane)" _dt_ok=0 fi if [[ ! -x "$PAGAN_ROOT/usr/bin/upower" ]] || \ [[ ! -e "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants/upower.service" ]]; then warn "upower: brak binarki lub usługa niewłączona – xfce4-power-manager nie pokaże stanu baterii (i będzie spam w logu)" _dt_ok=0 fi if [[ ! -x "$PAGAN_ROOT/usr/bin/xfce4-notifyd" && \ ! -x "$PAGAN_ROOT/usr/lib/xfce4/notifyd/xfce4-notifyd" ]] || \ [[ ! -x "$PAGAN_ROOT/usr/bin/notify-send" ]]; then warn "powiadomienia: brak demona xfce4-notifyd (bin/ lub lib/xfce4/notifyd/) albo notify-send – libnotify zgłosi „Failed to connect to proxy”" _dt_ok=0 fi if [[ ! -x "$PAGAN_ROOT/usr/bin/udisksctl" ]] || \ [[ ! -e "$PAGAN_ROOT/etc/systemd/system/multi-user.target.wants/udisks2.service" ]]; then warn "udisks2: brak binarki lub usługa niewłączona – montowanie nośników nie zadziała" _dt_ok=0 fi if [[ $_dt_ok -eq 1 ]]; then ok "Dodatki pulpitu: xrdb + upower + powiadomienia + udisks2 obecne i włączone" else ((fails++)) || true fi # Tumbler: pluginy nie mogą mieć nierozwiązanych symboli (rozjazd ABI # z wcześniejszych przebiegów/resume). Repo PAG ma spójne wersje libgsf/ffmpeg. local _tb_bad="" if _lib_missing_symbol "/usr/lib/libgsf-1.so.114*" "xmlNanoHTTPClose"; then _tb_bad+=" odf(libgsf)" fi if _lib_missing_symbol "/usr/lib/libavcodec.so.63*" "rsvg_handle_render_document"; then _tb_bad+=" ffmpeg(libavcodec)" fi if [[ -n "$_tb_bad" ]]; then warn "tumbler: rozjechane ABI – te pluginy się nie wczytają:$_tb_bad (napraw: pag install -f libgsf ffmpeg)" ((fails++)) || true else ok "tumbler: biblioteki pluginów bez rozjechanego ABI" fi # NVIDIA: moduły muszą być W OBRAZIE – kernel nie ma nouveau, więc bez nich # karta NVIDIA w live nie ma żadnego sterownika. Wcześniej build je gubił # (DESTDIR zamiast INSTALL_MOD_PATH → lądowały na HOŚCIE, nie w rootfsie). if _is_true "${SKIP_NVIDIA:-0}"; then info "NVIDIA: pominięte w tym buildzie (SKIP_NVIDIA=1/--no-nvidia)" else local _nvko _nvko=$(find "$PAGAN_ROOT/usr/lib/modules" -name 'nvidia.ko*' 2>/dev/null | head -n1) if [[ -n "$_nvko" ]]; then ok "NVIDIA: moduły w obrazie (${_nvko#"$PAGAN_ROOT"})" else crit "NVIDIA: brak nvidia.ko w obrazie – karta NVIDIA w live nie dostanie żadnego sterownika (kernel bez nouveau). Zbuduj moduły i wydaj ISO ponownie" fi fi # Toolchain docelowy: bez `ld` (binutils) nie da się skompilować NICZEGO na # zainstalowanym systemie – czyli DKMS/nvidia-dkms też nie zadziała. if [[ -x "$PAGAN_ROOT/usr/bin/ld" ]]; then ok "Toolchain docelowy: binutils obecne (ld/as/objcopy) – DKMS zadziała" else crit "Toolchain docelowy: brak /usr/bin/ld (binutils) – na zainstalowanym systemie nie zbudujesz NICZEGO (DKMS/nvidia-dkms też nie)" fi # Udev: baza hwdb. if [[ -s "$PAGAN_ROOT/etc/udev/hwdb.bin" ]]; then ok "udev: /etc/udev/hwdb.bin obecne" else warn "udev: brak /etc/udev/hwdb.bin – generator systemd-imds zgłosi błąd przy starcie" fi # Edytor tekstu w systemie live – bez niego nie da się poprawić żadnego # pliku .conf z powłoki ratunkowej/analitycznej. nano jest domyślny, # vim/vi jako alternatywa. if [[ -x "$PAGAN_ROOT/usr/bin/nano" || -x "$PAGAN_ROOT/bin/nano" ]]; then ok "Edytor tekstu: nano obecny" elif [[ -x "$PAGAN_ROOT/usr/bin/vi" || -x "$PAGAN_ROOT/bin/vi" ]]; then ok "Edytor tekstu: vi/vim obecny (brak nano)" else warn "Brak edytora tekstu (nano/vi) – nie poprawisz konfiguracji z live" ((fails++)) || true fi # Podstawowe binarki local check for check in bash sh ls cat grep sed awk make gcc python3 perl; do if [[ ! -x "$PAGAN_ROOT/usr/bin/$check" ]] && [[ ! -x "$PAGAN_ROOT/bin/$check" ]]; then warn "Brak binarki: $check" ((fails++)) || true fi done # Python + sqlite3 (wymagane przez pag) if [[ -x "$PAGAN_ROOT/usr/bin/python3" ]]; then if chroot "$PAGAN_ROOT" /usr/bin/python3 -c "import sqlite3; import zlib; import tarfile" >/dev/null 2>&1; then ok "python3 + sqlite3 działa" else warn "python3 nie ma sqlite3/zlib/tarfile – pag nie będzie działać!" ((fails++)) || true fi else warn "Brak python3 – pag nie będzie działać!" ((fails++)) || true fi # pag – pełna wersja v3 (nie stub) if [[ -f "$PAGAN_ROOT/usr/bin/pag" ]]; then if grep -q "PAG - Pagan Linux Package Manager v3" "$PAGAN_ROOT/usr/bin/pag" 2>/dev/null; then ok "pag – pełna wersja v3" else warn "pag to stub (niepełna wersja) – sprawdź install_pag" ((fails++)) || true fi else warn "Brak /usr/bin/pag" ((fails++)) || true fi # Manifest bazy z obrazu (/usr/share/pag/base.json) – bez niego `pag base` # nie wie, które pakiety obrazu da się zaadoptować z repo (rolling). if [[ -s "$PAGAN_ROOT/usr/share/pag/base.json" ]]; then local _bn _bn=$(python3 -c "import json,sys; print(len(json.load(open(sys.argv[1])).get('packages', [])))" \ "$PAGAN_ROOT/usr/share/pag/base.json" 2>/dev/null || echo 0) if [[ "${_bn:-0}" -gt 0 ]]; then ok "Manifest bazy (pag base): ${_bn} pakietów" else warn "Manifest bazy jest pusty – 'pag base adopt' nie zaadoptuje niczego" ((fails++)) || true fi else warn "Brak /usr/share/pag/base.json – 'pag base adopt' (rolling) nie zadziała" ((fails++)) || true fi # Calamares – instalator systemu (domyślnie; dla XFCE z repo PAG) if [[ -x "$PAGAN_ROOT/usr/bin/calamares" ]]; then ok "calamares – instalator obecny" else warn "Brak /usr/bin/calamares – z live nie zainstalujesz systemu (sprawdź install_calamares)" ((fails++)) || true fi # fish – domyślna powłoka użytkowników (wyłącza ją --no-fish) if [[ "${PAGAN_FISH:-1}" == "1" ]]; then if [[ -x "$PAGAN_ROOT/usr/bin/fish" ]]; then ok "fish – powłoka obecna (domyślna dla użytkowników)" else warn "Brak /usr/bin/fish – zostaje bash (sprawdź build_final_system)" ((fails++)) || true fi fi # pagsync – tylko po stronie buildera (repo/receptury); w live system nie trafia # (usunięty z install_pag – narzędzie deweloperskie) # Konfiguracja PAG: repozytorium + keyring if [[ -s "$PAGAN_ROOT/etc/pag/repos.conf" ]]; then local _repo_url _repo_url=$(grep -v '^#' "$PAGAN_ROOT/etc/pag/repos.conf" | grep -v '^$' | head -1) ok "PAG repos.conf: ${_repo_url:-?}" else warn "Brak /etc/pag/repos.conf" ((fails++)) || true fi if [[ -d "$PAGAN_ROOT/etc/pag/gpg" ]]; then ok "PAG keyring: /etc/pag/gpg" else warn "Brak /etc/pag/gpg" ((fails++)) || true fi # Kernel: wsparcie dla live ISO (squashfs + overlay + iso9660 wbudowane) local kconf="$PAGAN_ROOT/boot/config-${KERNEL_VERSION}-pagan" if [[ -f "$kconf" ]]; then local missing_k=() for opt in CONFIG_SQUASHFS CONFIG_SQUASHFS_XATTR CONFIG_OVERLAY_FS \ CONFIG_ISO9660_FS CONFIG_BLK_DEV_LOOP CONFIG_TMPFS_POSIX_ACL; do grep -q "^${opt}=y" "$kconf" || missing_k+=("$opt") done if [[ ${#missing_k[@]} -eq 0 ]]; then ok "Kernel: live-boot (squashfs/overlay/iso9660) wbudowane" else warn "Kernel: brak wbudowanych: ${missing_k[*]} – live ISO może nie wstać" ((fails++)) || true fi else warn "Brak configu kernela: $kconf" fi # --- X11/Xorg: bez tych elementów Xorg pada (brak ekranu logowania) --- # W trybie none/base/cli Xorg nie jest instalowany – checki X11 pomijamy. if [[ "${PAGAN_DE,,}" != "none" && "${PAGAN_DE,,}" != "base" && "${PAGAN_DE,,}" != "cli" ]]; then # xkbcomp – bez niego: "XKB: Failed to compile keymap" → fatal server error. if [[ -x "$PAGAN_ROOT/usr/bin/xkbcomp" ]]; then ok "xkbcomp obecny" else warn "Brak /usr/bin/xkbcomp – Xorg nie wystartuje (Failed to compile keymap)" ((fails++)) || true fi # xkeyboard-config (dane XKB) – bez nich XKB nie ma czym kompilować mapy if [[ -d "$PAGAN_ROOT/usr/share/X11/xkb/rules" && -d "$PAGAN_ROOT/usr/share/X11/xkb/symbols" ]]; then ok "xkeyboard-config (dane XKB) obecne" else warn "Brak danych xkeyboard-config (/usr/share/X11/xkb) – XKB nie zadziała" ((fails++)) || true fi # Pozostałe narzędzia do startu sesji graficznej (live: startx, system: lightdm) for _xbin in Xorg xauth setxkbmap xinit; do if [[ -x "$PAGAN_ROOT/usr/bin/$_xbin" ]]; then ok "$_xbin obecny" else warn "Brak /usr/bin/$_xbin – sesja graficzna (startx/lightdm) nie wystartuje poprawnie" ((fails++)) || true fi done # Sterowniki Xorg: libinput (input) + modesetting (ekran przez DRM/KMS) if [[ -f "$PAGAN_ROOT/usr/lib/xorg/modules/drivers/modesetting_drv.so" ]]; then ok "Xorg modesetting driver obecny" else warn "Brak modesetting_drv.so – Xorg nie ma sterownika ekranu (tylko fbdev/vesa)" ((fails++)) || true fi if [[ -f "$PAGAN_ROOT/usr/lib/xorg/modules/input/libinput_drv.so" ]]; then ok "Xorg libinput driver obecny" else warn "Brak libinput_drv.so – klawiatura/mysz nie będą działać w Xorg" ((fails++)) || true fi # Menedżer logowania + sesja XFCE – bez nich „zainstalowany” system wstaje # do trybu tekstowego zamiast do okna logowania (objaw: brak GUI po instalacji). for _sesbin in lightdm startxfce4 xfce4-session; do if [[ -x "$PAGAN_ROOT/usr/bin/$_sesbin" ]]; then ok "$_sesbin obecny" else warn "Brak /usr/bin/$_sesbin – logowanie/sesja XFCE nie wystartuje" ((fails++)) || true fi done fi # X11 checks (pomijane dla --de none/base/cli) # regulatory.db – bez niego cfg80211 loguje błąd i WiFi nie ma kraju domyślnego if [[ -s "$PAGAN_ROOT/usr/lib/firmware/regulatory.db" ]]; then ok "regulatory.db (WiFi) obecny" else warn "Brak regulatory.db – WiFi: cfg80211 failed to load regulatory.db" ((fails++)) || true fi # /etc/profile – bez niego profile.d (m.in. pagan-live.sh) nigdy się # nie wykona i live kończy w gołym bashu zamiast pulpitem live (GUI). if [[ -s "$PAGAN_ROOT/etc/profile" ]] && \ grep -q 'profile.d/\*.sh' "$PAGAN_ROOT/etc/profile" 2>/dev/null; then ok "/etc/profile ładuje /etc/profile.d/*.sh" else warn "Brak /etc/profile (lub bez pętli profile.d) – live wita gołym bashem, sesja live nie startuje" ((fails++)) || true fi if [[ -f "$PAGAN_ROOT/etc/profile.d/pagan-live.sh" ]]; then ok "pagan-live.sh w profile.d" else warn "Brak /etc/profile.d/pagan-live.sh – sesja live nie wystartuje" ((fails++)) || true fi # dbus.socket – aktywacja socketowa; cmake z dbus 1.16.2 generuje jednostki # z pustymi @EXPANDED_BINDIR@/@DBUS_SYSTEM_SOCKET@ (bug) → sprawdzamy poprawność if [[ -f "$PAGAN_ROOT/usr/lib/systemd/system/dbus.socket" ]] && \ grep -q '^ListenStream=/run/dbus/system_bus_socket$' "$PAGAN_ROOT/usr/lib/systemd/system/dbus.socket" && \ grep -q '^ExecStart=/usr/bin/dbus-daemon' "$PAGAN_ROOT/usr/lib/systemd/system/dbus.service"; then ok "dbus.socket (aktywacja socketowa) poprawny" else warn "dbus: brak poprawnych jednostek dbus.socket/dbus.service – dbus nie wystartuje (logind padnie)" ((fails++)) || true fi # Polityka D-Bus systemd. /usr/share/dbus-1/system.conf ma w polityce # domyślnej , więc bez tych plików PID1 nie zajmie nazwy # org.freedesktop.systemd1, a logind org.freedesktop.login1. if [[ -f "$PAGAN_ROOT/usr/share/dbus-1/system.d/org.freedesktop.systemd1.conf" ]]; then ok "dbus: polityka systemd (org.freedesktop.systemd1.conf) obecna" elif [[ -f "$PAGAN_ROOT/usr/lib/systemd/systemd" ]]; then warn "dbus: BRAK org.freedesktop.systemd1.conf – PID1 nie zajmie nazwy (systemctl/logind padną)" ((fails++)) || true fi if [[ -f "$PAGAN_ROOT/usr/share/dbus-1/system-services/org.freedesktop.login1.service" ]]; then ok "dbus: aktywacja org.freedesktop.login1 dostępna" fi # Strefy czasowe: katalog musi być czytelny dla nie-roota. mktemp -d dawał # 0700 i taki korzeń trafiał do ISO (objaw: „Bad path …/zoneinfo/Europe/Warsaw"). if [[ -f "$PAGAN_ROOT/usr/share/zoneinfo/Europe/Warsaw" ]]; then _zi_mode=$(stat -c '%a' "$PAGAN_ROOT/usr/share/zoneinfo" 2>/dev/null || echo '') # 10# wymusza podstawę 10 (bash inaczej czytałby np. 0755 jako ósemkowe). if [[ -n "$_zi_mode" && $(( 10#$_zi_mode % 10 )) -ge 4 ]]; then ok "zoneinfo: /usr/share/zoneinfo czytelne dla wszystkich (mode $_zi_mode)" else warn "zoneinfo: uprawnienia /usr/share/zoneinfo = ${_zi_mode:-brak} (powinno być 755) – nie-root nie odczyta stref" ((fails++)) || true fi fi # GRUB + initramfs: grub-mkconfig (/etc/grub.d/10_linux) szuka initrd # wyłącznie pod nazwą Z WERSJĄ JĄDRA (`initramfs-.img`), gdzie # = nazwa kernela bez wiodących znaków nie-cyfrowych. Nasz obraz to # /boot/initramfs.img, więc bez aliasu wpis w menu GRUB nie ma linii # `initrd` i zainstalowany system startuje bez initramfs. if [[ -f "$PAGAN_ROOT/boot/initramfs.img" ]]; then local _zk _zb _zv _zbad="" for _zk in "$PAGAN_ROOT"/boot/vmlinuz*; do [[ -e "$_zk" ]] || continue _zb="$(basename "$_zk")" _zv="$(printf '%s' "$_zb" | sed -e 's,^[^0-9]*-,,g')" [[ -n "$_zv" && "$_zv" != "$_zb" ]] || continue [[ -e "$PAGAN_ROOT/boot/initramfs-$_zv.img" ]] || _zbad+=" $_zb" done if [[ -z "$_zbad" ]]; then ok "GRUB: każdy /boot/vmlinuz* ma initramfs-.img (menuentry z initrd)" else warn "GRUB: brak initramfs-.img dla:$_zbad – grub-mkconfig pominie linię initrd (system może nie wstać)" ((fails++)) || true fi fi # Initramfs zawiera skrypt init (obsługa live + dysk) if [[ -f "$PAGAN_ROOT/boot/initramfs.img" ]]; then if gzip -dc "$PAGAN_ROOT/boot/initramfs.img" 2>/dev/null | cpio -it 2>/dev/null | grep -qx "init"; then ok "Initramfs: zawiera /init" else warn "Initramfs: brak /init" ((fails++)) || true fi else warn "Brak /boot/initramfs.img" ((fails++)) || true fi # glibc if [[ -f "$PAGAN_ROOT/usr/lib/libc.so.6" ]]; then local glibc_ver glibc_ver=$(chroot "$PAGAN_ROOT" /usr/bin/ldd --version 2>/dev/null | head -1 | grep -o '[0-9.]*' || true) ok "glibc: ${glibc_ver:-?}" else warn "Brak libc.so.6!" ((fails++)) || true fi # Chroot test if chroot "$PAGAN_ROOT" /bin/bash -c "echo OK" >/dev/null 2>&1; then ok "chroot + bash działa" else local _chroot_err _chroot_err=$(chroot "$PAGAN_ROOT" /bin/bash -c "echo OK" 2>&1 >/dev/null || true) warn "chroot /bin/bash NIE działa! (${_chroot_err:-brak komunikatu błędu})" ((fails++)) || true fi if [[ $fails -gt 0 ]]; then warn "Weryfikacja: $fails problemów znalezionych (patrz wyżej)" else ok "Weryfikacja: wszystkie sanity checki przeszły ✅" fi return $fails } # ========================= WIRTUALNE SYSTEMY PLIKÓW ========================= mount_virtual_fs() { info "Montowanie wirtualnych systemów plików w $PAGAN_ROOT..." # --rbind + --make-rslave: kopia /dev (razem z /dev/pts) wewnątrz rootfs # jest SLAVE – odmontowanie jej NIE zdejmie devpts z HOSTA. Wcześniejszy # `mount --bind /dev/pts` + `umount -l` odmontowywał hostowy /dev/pts # (po budowie nowe ptysy padały: sudo, nowe terminale). mount --rbind /dev "$PAGAN_ROOT/dev" 2>/dev/null && mount --make-rslave "$PAGAN_ROOT/dev" 2>/dev/null || true mount -t proc proc "$PAGAN_ROOT/proc" 2>/dev/null || true mount -t sysfs sysfs "$PAGAN_ROOT/sys" 2>/dev/null || true mount -t tmpfs tmpfs "$PAGAN_ROOT/run" 2>/dev/null || true } umount_virtual_fs() { info "Odmontowywanie wirtualnych systemów plików..." # -R zdejmuje całe drzewo (w tym /dev/pts wewnątrz rbinda /dev). # Bezpieczne: to slave-kopie, host zostaje nietknięty. umount -R "$PAGAN_ROOT/dev" 2>/dev/null || true umount "$PAGAN_ROOT/proc" 2>/dev/null || true umount "$PAGAN_ROOT/sys" 2>/dev/null || true umount -R "$PAGAN_ROOT/run" 2>/dev/null || true } # ========================= PURGE Qt/KDE (tryb none/base/cli) ========================= # `--de none` na resume-buildzie ZOSTAWIA pakiety z poprzednich prób (Plasma/KDE) – # przez co „baza” waży tyle co pełne DE. Ta funkcja czyści resztki Plasma/SDDM/appek # KDE. UWAGA: gdy Calamares jest włączony (domyślnie), Qt6/KF6 są POTRZEBNE # instalatorowi (KPMcore) – wtedy NIE usuwamy Qt6/KF6 ani markera resume, tylko resztki. purge_kde() { step "PURGE Qt/KDE (tryb none/base/cli)" local root="$PAGAN_ROOT" local _keep_qt=0 _is_true "${PAGAN_CALAMARES:-1}" && _keep_qt=1 # Nic do roboty, gdy brak śladów Qt6/KDE if [[ ! -d "$root/usr/lib/qt6" && ! -d "$root/usr/lib/kf6" && \ ! -e "$root/usr/bin/sddm" && ! -e "$root/usr/lib/libQt6Core.so.6" ]]; then info "Brak pakietów Qt/KDE – nic do usunięcia" return 0 fi if [[ $_keep_qt -eq 1 ]]; then info "Usuwam Plasma/SDDM/appki KDE z $root (Qt6/KF6 zostają – potrzebne Calamares)" else info "Usuwam Qt6/KF6/Plasma/SDDM/Calamares z $root ..." fi # ── Biblioteki plasma/phonon (zawsze – Plasma nie jest już wspierana) ── find "$root/usr/lib" -maxdepth 1 \( -name 'libplasma*' -o -name 'libphonon*' \) \ -delete 2>/dev/null || true # ── Qt6/KF6: usuwamy TYLKO gdy Calamares wyłączony (inaczej potrzebne) ── if [[ $_keep_qt -eq 0 ]]; then rm -rf "$root/usr/lib/qt6" "$root/usr/include/qt6" "$root/usr/share/qt6" \ "$root/usr/lib/kf6" "$root/usr/include/kf6" "$root/usr/share/kf6" \ "$root/usr/lib/cmake/Qt6" "$root/usr/lib/cmake/KF6" \ "$root/usr/lib64/qt6" "$root/usr/lib64/kf6" 2>/dev/null || true find "$root/usr/lib" -maxdepth 1 \( -name 'libQt6*' -o -name 'libKF6*' \) \ -delete 2>/dev/null || true rm -rf "$root/usr/libexec"/kf6 2>/dev/null || true fi # ── Dane / konfiguracja Plasma/SDDM/KDE (zawsze) ── rm -rf "$root/usr/share/plasma" "$root/usr/share/plasma-workspace" \ "$root/usr/share/breeze" "$root/usr/share/breeze-icons" \ "$root/usr/share/sddm" "$root/usr/share/konsole" "$root/usr/share/dolphin" \ "$root/usr/share/kde" "$root/usr/share/kde-settings" \ "$root/usr/share/kdevappwizard" 2>/dev/null || true rm -rf "$root/etc/sddm.conf.d" "$root/etc/pam.d/sddm" \ "$root/etc/pam.d/sddm-autologin" "$root/etc/pam.d/sddm-greeter" 2>/dev/null || true rm -rf "$root/usr/lib/systemd/system/sddm.service" 2>/dev/null || true # ── KF6-owe dane – tylko gdy Qt6/KF6 usuwamy ── if [[ $_keep_qt -eq 0 ]]; then rm -rf "$root/usr/share/kio" "$root/usr/share/knotifications5" \ "$root/usr/share/kservices5" "$root/usr/share/kxmlgui5" \ "$root/usr/share/kauth" "$root/usr/share/kpackage" \ "$root/usr/share/kfontinst" 2>/dev/null || true fi # ── Binarki KDE/Plasma ── local b for b in sddm plasmashell kwin_wayland kwin_x11 konsole dolphin systemsettings \ kactivitymanagerd kscreenlocker powerdevil ksmserver kstart krunner \ kglobalaccel6 kaccess klipper kmenuedit khotkeys kpackagetool6 \ kreadconfig6 kwriteconfig6 kuser kcmshell6; do rm -f "$root/usr/bin/$b" 2>/dev/null || true done rm -f "$root/usr/bin"/plasma-* "$root/usr/bin"/kde-* "$root/usr/bin"/kwin* 2>/dev/null || true # ── libexec KDE (bez kf6 – patrz blok Qt6/KF6 wyżej) ── rm -rf "$root/usr/libexec"/kwin* plasma* 2>/dev/null || true # ── Calamares: usuwamy tylko gdy instalator wyłączony ── if [[ $_keep_qt -eq 0 ]]; then rm -f "$root/usr/bin/calamares" 2>/dev/null || true rm -rf "$root/usr/share/calamares" "$root/etc/calamares" 2>/dev/null || true fi # ── Markery resume: przy włączonym Calamares zachowujemy Qt6/KF6/kpmcore/calamares ── if [[ -f "$RESUME_FILE" ]]; then if [[ $_keep_qt -eq 1 ]]; then sed -i -E '/^(plasma-|plasma-desktop|plasma-workspace|kwin|konsole|dolphin|sddm|sddm-kcm|phonon|breeze|breeze-icons|kactivitymanagerd|kscreenlocker|kde-cli-tools|powerdevil|polkit-kde-agent|systemsettings|kwayland|layer-shell-qt|kdecoration|libkscreen|libksysguard|libplasma|plasma-activities|plasma-integration|plasma-wayland-protocols)/d' \ "$RESUME_FILE" 2>/dev/null || true info "Resume: usunięto wpisy Plasma/KDE (markery Qt6/KF6 zachowane dla Calamares)" else sed -i -E '/^(qt6-|plasma-|plasma-desktop|plasma-workspace|kwin|konsole|dolphin|sddm|sddm-kcm|kpmcore|calamares|phonon|breeze|kactivitymanagerd|kscreenlocker|kde-cli-tools|powerdevil|polkit-kde-agent|systemsettings|kwayland|layer-shell-qt|kdecoration|libkscreen|libksysguard|libplasma|plasma-activities|plasma-integration|plasma-wayland-protocols|extra-cmake-modules|kconfig|kcoreaddons|ki18n|kwidgetsaddons|kjobwidgets|kdbusaddons|kitemviews|kitemmodels|kcodecs|karchive|kcrash|kguiaddons|kwindowsystem|kauth|kcompletion|knotifications|kservice|ktextwidgets|kglobalaccel|kxmlgui|kbookmarks|kiconthemes|kdoctools|kunitconversion|kconfigwidgets|kdesu|solid|sonnet|kidletime|kdnssd|syntax-highlighting|kpackage|kdeclarative|kcmutils|kio|kparts|kpty|kfilemetadata|kirigami|kquickcharts|knotifyconfig|breeze-icons)/d' \ "$RESUME_FILE" 2>/dev/null || true info "Resume: usunięto wpisy pakietów Qt/KDE (przyszły build odtworzy je od nowa)" fi fi # Użytkownik pagan (relikt starszych buildów) – w bazie niepotrzebny if chroot "$root" /usr/bin/getent passwd pagan >/dev/null 2>&1; then chroot "$root" /usr/sbin/userdel -r pagan 2>/dev/null || true info "Usunięto konto 'pagan' (relikt starszego builda)" fi ok "Qt/KDE usunięte – baza bez środowiska (doinstaluj: pag install xfce4 lightdm)" } # ========================= TRIM FIRMWARE (PAGAN_FIRMWARE_MIN=1) ========================= # linux-firmware waży ~1.9 GB, a większość to firmware martwe na x86 (SoC # z telefonów/ARM, egzotyczne karty sieciowe). PAGAN_FIRMWARE_MIN=1 zostawia # tylko typowy sprzęt desktop/laptop: intel (WiFi/BT/GPU), amdgpu, nvidia, # iwlwifi, rtl, atheros, i915, bt* ... Oszczędność ~800-900 MB (przed xz). trim_firmware() { [[ "${PAGAN_FIRMWARE_MIN:-0}" == "1" ]] || return 0 step "TRIM FIRMWARE (PAGAN_FIRMWARE_MIN=1)" local fw="$PAGAN_ROOT/usr/lib/firmware" [[ -d "$fw" ]] || return 0 local d tot=0 # SoC z telefonów/ARM (0% użycia na x86) + egzotyczne NIC/fibre-channel local drop=(qcom tegra exynos mvebu imx amlogic meson rockchip allwinner \ sun s5p rk hisilicon nxp cavium cnm inside-secure ezchip \ netronome mellanox mrvl liquidio netxen qed ql2500 ql2700 \ ql3500 qlcnic qla2xxx ti) for d in "${drop[@]}"; do if [[ -d "$fw/$d" ]]; then sz=$(du -sm "$fw/$d" 2>/dev/null | cut -f1) rm -rf "$fw/$d" 2>/dev/null tot=$((tot + ${sz:-0})) info " ✓ usunięto firmware/$d (~${sz} MB)" fi done ok "Firmware przycięty (usunięto ~$((tot/1024)) GB)" } # ========================= WALIDACJA PAGAN_ROOT ========================= # Chroni przed skasowaniem katalogu systemowego przez --rebuild # (clean_build_state robi rm -rf "$PAGAN_ROOT"). _validate_pagan_root() { local r="${PAGAN_ROOT%/}" [[ -n "$r" ]] || error "PAGAN_ROOT nie może być pusty" [[ "$r" == /* ]] || error "PAGAN_ROOT musi być ścieżką absolutną (jest: '$PAGAN_ROOT')" # /run/media/... to standardowy punkt montowania nośników – dozwolony. case "$r" in /run/media/*) return 0 ;; esac case "$r" in /|/bin|/boot|/dev|/etc|/home|/lib|/lib64|/media|/mnt|/opt|/proc|/root|/run|/sbin|/srv|/sys|/tmp|/usr|/var) error "Odmawiam użycia PAGAN_ROOT='$PAGAN_ROOT' – to katalog systemowy/montowania. Użyj np. /mnt/pagan" ;; /bin/*|/boot/*|/dev/*|/etc/*|/lib/*|/lib64/*|/proc/*|/sbin/*|/sys/*|/usr/*|/var/*|/root/*|/run/*) error "Odmawiam użycia PAGAN_ROOT='$PAGAN_ROOT' – leży w katalogu systemowym. Użyj np. /mnt/pagan" ;; esac } # ========================= GŁÓWNY PROCES ========================= main() { cd / show_banner _validate_pagan_root # Argumenty CLI zostały już sparsowane na początku skryptu (parse_args_early). # Clean environment from any previous SYSROOT leakage unset PKG_CONFIG_SYSROOT_DIR if [[ $EUID -ne 0 ]]; then error "Wymagane uprawnienia root (sudo)" fi if _is_true "${REBUILD_FROM_SCRATCH:-0}"; then clean_build_state fi # Wybór środowiska if [[ -z "${PAGAN_DE:-}" ]]; then echo "" echo "Wybierz środowisko graficzne:" echo " 1) Xfce (wersja podstawowa – lekkie, stabilne; + Calamares)" echo " 2) Deepin (nowoczesne, eleganckie)" echo " 3) Budgie (minimalne, eleganckie)" echo " 4) PaganDE (labwc-pagan + lekki stack Wayland, Catppuccin Mocha)" echo " 5) none (BAZA – bez środowiska; DE/aplikacje doinstalujesz przez pag)" echo "" echo " (KDE Plasma i GNOME są tymczasowo wyłączone.)" echo "" read -p "Wybierz [1-5]: " de_choice case "$de_choice" in 1|"") PAGAN_DE="xfce" ;; 2) PAGAN_DE="deepin" ;; 3) PAGAN_DE="budgie" ;; 4) PAGAN_DE="pagande" ;; 5) PAGAN_DE="none" ;; *) error "Nieprawidłowy wybór" ;; esac fi # Wybór formatu obrazu BUILD_IMG="${BUILD_IMG:-}" BUILD_ISO="${BUILD_ISO:-}" if [[ -z "$BUILD_IMG" ]] && [[ -z "$BUILD_ISO" ]]; then case "${ARTIFACT_MODE,,}" in img|"") BUILD_IMG=1; BUILD_ISO=0 ;; iso) BUILD_IMG=0; BUILD_ISO=1 ;; both) BUILD_IMG=1; BUILD_ISO=1 ;; ask) echo "" echo "Wybierz format obrazu:" echo " 1) IMG (obraz dysku – gotowy system)" echo " 2) ISO (obraz live/instalacyjny)" echo " 3) Oba (IMG + ISO)" echo "" read -p "Wybierz [1-3]: " img_choice case "$img_choice" in 1|"") BUILD_IMG=1; BUILD_ISO=0 ;; 2) BUILD_IMG=0; BUILD_ISO=1 ;; 3) BUILD_IMG=1; BUILD_ISO=1 ;; *) error "Nieprawidłowy wybór" ;; esac ;; *) error "Nieprawidłowe ARTIFACT_MODE: ${ARTIFACT_MODE}. Użyj: img|iso|both|ask" ;; esac fi check_dependencies check_lfs_patches _detect_stale_resume init_build build_toolchain # Ch. 5 mount_virtual_fs # Montuj /dev /proc /sys przed budową systemu # Tryb BAZA (none/base/cli): relikty Qt6/KF6/Plasma/Calamares z poprzedniego # builda (resume) muszą zniknąć NA POCZĄTKU – zanim build_final_system/ # build_blfs cokolwiek zbudują lub ponownie wykorzystają – a nie na końcu, # tuż przed pakowaniem ISO. purge_kde sam sprawdza, czy cokolwiek usunąć. if [[ "${PAGAN_DE,,}" == "none" || "${PAGAN_DE,,}" == "base" || "${PAGAN_DE,,}" == "cli" ]]; then purge_kde fi build_final_system # Ch. 6-8 (LFS + systemd + GRUB) build_blfs # BLFS build_kernel # Kernel (w tym moduły WiFi) install_wireless_regdb # regulatory.db dla cfg80211 (WiFi) install_pagan_wifi # konsolowy asystent Wi-Fi (PL/EN) do rootfs if ! _is_true "${SKIP_NVIDIA:-0}"; then install_nvidia_drivers # NVIDIA open-gpu-kernel-modules (best-effort) else info "Pomijam sterowniki NVIDIA (--no-nvidia)" fi install_pag configure_fish # fish jako domyślna powłoka użytkowników install_driver_manager install_initramfs_tool # pagan-initramfs (przebudowa initramfs po aktualizacji jądra) install_rolling_kernel # kernel jako pakiet rolling (linux) w PAG case "${PAGAN_DE,,}" in xfce) install_xfce ;; deepin) install_deepin ;; budgie) install_budgie ;; pagande|pagan) install_pagande ;; kde|gnome) error "DE '${PAGAN_DE}' jest tymczasowo wyłączone (KDE/GNOME odłożone). Wybierz: xfce|deepin|budgie|pagande|none" ;; none|base|cli) # Tryb BAZA: bez środowiska graficznego – użytkownik wybiera DE/aplikacje # w Calamares / przez `pag` (netinstall z repo). Środowisko w live = instalator. info "PAGAN_DE=$PAGAN_DE – pomijam instalację środowiska graficznego" info "Środowisko/aplikacje można dobrać w instalatorze (netinstall z repo)" # Minimalny X (xorg-server + xinit) – instalator GUI musi wystartować na # pierwszym rozruchu (profile.d → startx), bo live nie ma środowiska graficznego. # (Qt6/KF6/Plasma z resume-builda usunął już purge_kde NA POCZĄTKU main) install_x_installer ;; *) warn "Nieznane DE: $PAGAN_DE – używam XFCE (podstawowe)"; install_xfce ;; esac # Dodatki pulpitu: braki, które wyszły dopiero w sesji live (xrdb, upower, # powiadomienia, udisks2) + naprawa bibliotek z rozjechanym ABI z wcześniejszych # przebiegów (tumbler ODF/ffmpeg). Pakiety pochodzą z repo PAG. # Dla obrazu BAZOWEGO (none/base/cli) pomijamy – tam nie ma pulpitu, a sens # tego obrazu polega na tym, że środowisko dobiera się w Calamares/netinstall. case "${PAGAN_DE,,}" in none|base|cli) info "Obraz bazowy – pomijam dodatki pulpitu (repo PAG)" ;; *) install_desktop_extras ;; esac # Toolchain docelowy (binutils) – niezależnie od DE: bez linkera `ld` # nie zadziała DKMS, czyli m.in. `nvidia-dkms` na zainstalowanym systemie. install_target_toolchain_extras # Opcjonalne odchudzenie linux-firmware (PAGAN_FIRMWARE_MIN=1) trim_firmware # Calamares – instalator systemu (DOMYŚLNIE WŁĄCZONY). Dla wersji podstawowej # (XFCE) stos Qt6/KF6 buduje install_calamares (zależności źródłowe, przy # niepowodzeniu fallback do repo PAG). Wyłączenie: --no-calamares. # (KDE/GNOME są tymczasowo wyłączone.) if _is_true "${PAGAN_CALAMARES:-1}"; then install_calamares else info "Pomijam calamares (--no-calamares) – ISO bez instalatora systemu" fi install_osinfo # Doinstalowanie pakietów z repo PAG do rootfs (przed initramfs/branding, # żeby trafiły do obrazu). Błędne nazwy = warning, reszta builda leci dalej. # UWAGA: auto --repo-pkgs dla XFCE jest WYŁĄCZONE – repo potrafi mieć nowszą # serię XFCE (4.20) niż source-build (4.18); nadpisanie binarek łamie sesję # („libxfce4windowing-0.so.0: cannot open shared object file”). Włącz tylko, # gdy repo będzie zgodne wersyjnie: PAGAN_REPO_XFCE=1 sudo ./build-pagan.sh … if _is_true "${PAGAN_REPO_XFCE:-0}" && [[ "${PAGAN_DE,,}" == "xfce" && -z "${REPO_PKGS:-}" ]]; then REPO_PKGS="xfce4 lightdm htop firefox" info "DE=xfce: repo-pkgs (PAGAN_REPO_XFCE=1): ${REPO_PKGS}" fi if [[ -n "${REPO_PKGS:-}" ]]; then _pag_install_repo || warn "Niektóre pakiety z repo nie zostały zainstalowane (patrz wyżej)" fi configure_grub install_branding # tapeta + neofetch logo + GRUB splash build_initramfs # Pozbądź się zaległego Qt6 QML/Quick z targetu także tutaj (idempotentne) – # gdyby poprzednie kroki zostawiły libki mieszające ABI z Qt6Core. _purge_stale_qt # Best-effort: nieudany krok finalizacji nie może przerwać builda przed ISO # (raportujemy i lecimy dalej – inaczej pod trapem EXIT zostajemy bez obrazu). finalize_build || warn " Finalizacja zgłosiła problem (patrz wyżej) – kontynuuję" # verify_rootfs kończy się liczbą problemów (return $fails). Pod set -e # niezerowy wynik UBIŁBY build tuż przed tworzeniem ISO, więc łapiemy go `||`. # DWA POZIOMY (patrz `crit`): # * VERIFY_CRITICAL > 0 – braki, przez które obraz jest bezużyteczny (brak # jądra/initramfs/modules.dep, zepsuty GRUB i386-pc, brak NVIDIA mimo # włączonego budowania, brak `ld`, brak sieci w live, cykl D-Busa). # PRZERYWAMY – dokładnie tak wyszło już ISO bez modułów NVIDIA. # Świadome wydanie mimo tego: PAGAN_ALLOW_BROKEN_ISO=1. # * pozostałe uwagi – raportujemy i tworzymy obraz (kosmetyka). verify_rootfs || { if [[ ${VERIFY_CRITICAL:-0} -gt 0 ]] && ! _is_true "${PAGAN_ALLOW_BROKEN_ISO:-0}"; then umount_virtual_fs >/dev/null 2>&1 || true error "Weryfikacja: ${VERIFY_CRITICAL} braków KRYTYCZNYCH – NIE wydaję obrazu (byłby bezużyteczny). Napraw powyższe i uruchom build ponownie, albo wymuś wydanie: PAGAN_ALLOW_BROKEN_ISO=1" fi warn " Weryfikacja: są uwagi (patrz wyżej) – kontynuuję tworzenie obrazu" } umount_virtual_fs # Odmontuj przed tworzeniem obrazów if [[ "${BUILD_IMG:-1}" -eq 1 ]]; then create_image fi if [[ "${BUILD_ISO:-0}" -eq 1 ]]; then create_iso fi report_artifacts # Podsumowanie step "✅ BUDOWA ZAKOŃCZONA!" banner_final } # ========================= BANNER KOŃCOWY (dynamiczny) ========================= # Ramka dopasowuje się do najdłuższej linii – długie ścieżki # (OUTPUT_DIR/ISO/LOG_FILE) nie łamią ramki, a w treści nie ma emoji # (4-bajtowe znaki potrafiły wywalać 'syntax error' na starszych bashach). banner_final() { # Jeśli plik zmienił się w trakcie builda, wynikom nie można ufać – nawet # gdy wszystko „przeszło” (bash mógł wykonać przypadkowe fragmenty linii). if _pagan_self_changed; then warn " build-pagan.sh zmienił się w trakcie działania – wyniki mogą być niepewne!" warn " (edytor z auto-zapisem? Zamknij go i powtórz build, zanim zaufasz obrazowi.)" fi local -a L=( "" " PAGAN LINUX ${PAGAN_VERSION} z ${PAGAN_DE^^} ZBUDOWANY!" "" " System: ${PAGAN_ROOT}" " Środowisko: ${PAGAN_DE^^}" " Kernel: ${KERNEL_VERSION}" " Menedżer: PAG + Calamares + pagan-drivers" "" ) [[ -f "$OUTPUT_DIR/Pagan-Linux-${PAGAN_VERSION}-${PAGAN_DE}.img" ]] \ && L+=(" IMG: $OUTPUT_DIR/Pagan-Linux-${PAGAN_VERSION}-${PAGAN_DE}.img") [[ -f "$OUTPUT_DIR/Pagan-Linux-${PAGAN_VERSION}-${PAGAN_DE}.iso" ]] \ && L+=(" ISO: $OUTPUT_DIR/Pagan-Linux-${PAGAN_VERSION}-${PAGAN_DE}.iso") L+=( "" " Log: ${LOG_FILE}" "" " Następne kroki:" " - ISO: wypal na USB (dd/balenaEtcher) i uruchom LIVE" " - W live: uruchom 'calamares' (lub ikonę na pulpicie), aby zainstalować system" " - Pakiety: pag install (repo: ${PAGAN_REPO_URL})" " - Sterowniki: pagan-drivers auto" " - Rolling: pagan-drivers update (kernel+sterowniki, bez nowego ISO)" "" ) local w=0 i len for i in "${L[@]}"; do len=${#i} (( len > w )) && w=$len done local bar pad bar=$(printf '%*s' "$((w + 2))" "") bar=${bar// /═} echo -e "\n${GREEN}${BOLD}╔${bar}╗${NC}" for i in "${L[@]}"; do pad=$(printf '%*s' "$((w - ${#i}))" "") echo -e "${GREEN}${BOLD}║${i}${pad}║${NC}" done echo -e "${GREEN}${BOLD}╚${bar}╝${NC}" echo "" } # ========================= URUCHOMIENIE ========================= if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi