Real beim Testen entdeckt: nach einem nachweislich korrekten Klick auf
'Jetzt installieren' (Debug-Log erreichte sogar 'grub-installer/bootdev
seen') sprang der Installer immer wieder zurueck auf dieselbe
Partitionierungsseite - ausgeloest durch die wiederholt gestellte
Debconf-Frage 'ubiquity/partman-rebuild-cache'
(/lib/partman/update.d/99signal_ubiquity), die ubi-partman.py's
rebuild_cache() aufruft.
Vergleich mit der Original-Recipe, aus der unsere ESP-Stanza kopiert
wurde (/usr/lib/partman/recipes-amd64-efi/30atomic): dort traegt NUR die
ESP-Stanza die Boot-Kennzeichnung (method{ efi }), die Root-Partition
bekommt kein zusaetzliches $bootable{ }. Unser Rezept setzte
$bootable{ } bisher unbedingt auf Root, unabhaengig von EFI/BIOS -
moeglicherweise die Ursache der wiederholten Neubewertung durch partman.
Root_extra_flags jetzt bedingt: nur im BIOS-Zweig (wo keine ESP existiert,
die diese Rolle uebernehmen koennte) weiterhin $bootable{ } gesetzt.
Noch nicht live verifiziert - naechster Schritt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
455 lines
22 KiB
Bash
455 lines
22 KiB
Bash
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
# Dieses Skript wird von einem Orchestrator-Modul (z.B. 40_backend.sh) per
|
|
# `source` in dessen Shell geladen. Variablen bleiben deshalb bewusst nicht
|
|
# readonly, um Namenskollisionen mit dem ladenden Modul zu vermeiden.
|
|
BACKEND_KEY="mint"
|
|
|
|
BACKEND_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PRESEED_TEMPLATE="${BACKEND_DIR}/preseed.tpl"
|
|
POSTINSTALL_SCRIPT="${BACKEND_DIR}/postinstall.sh"
|
|
|
|
RUNTIME_BLUEPRINT_FILE="/run/tuxflotte/runtime/runtime_blueprint.json"
|
|
SERVER_RESPONSE_FILE="/run/tuxflotte/server/response.json"
|
|
# Von 10_hardware.sh im selben Live-Boot geschrieben (siehe dort) - Quelle
|
|
# fuer den Fingerprint, der jetzt auch auf dem installierten Geraet selbst
|
|
# hinterlegt wird (siehe backend_generate_config()/postinstall.sh).
|
|
HARDWARE_FILE="/run/tuxflotte/hardware/hardware.json"
|
|
|
|
RUNTIME_DIR="/run/tuxflotte/backend"
|
|
CONFIG_FILE="${RUNTIME_DIR}/config"
|
|
|
|
backend_log() {
|
|
printf '[backend:%s] %s\n' "${BACKEND_KEY}" "$*" >&2
|
|
}
|
|
|
|
backend_fatal() {
|
|
printf '[backend:%s] FEHLER: %s\n' "${BACKEND_KEY}" "$*" >&2
|
|
return 1
|
|
}
|
|
|
|
backend_init() {
|
|
# jq/envsubst(gettext-base)/cpio/kexec-tools sind auf dem Live-Medium
|
|
# selbst (anders als im Zielsystem, siehe pkgsel/include in preseed.tpl)
|
|
# nicht vorinstalliert - real gegen das Live-Abbild verifiziert
|
|
# (Phase-1-Spike, zunächst nur für kexec-tools behoben, hier auf alle
|
|
# vier Live-only-Werkzeuge ausgeweitet). base64 kommt aus coreutils und
|
|
# ist auf jedem Debian-Derivat immer vorhanden, deshalb ohne Nachinstal-
|
|
# lationspfad.
|
|
local live_packages_needed=()
|
|
|
|
command -v jq >/dev/null 2>&1 || live_packages_needed+=(jq)
|
|
command -v envsubst >/dev/null 2>&1 || live_packages_needed+=(gettext-base)
|
|
command -v cpio >/dev/null 2>&1 || live_packages_needed+=(cpio)
|
|
command -v kexec >/dev/null 2>&1 || live_packages_needed+=(kexec-tools)
|
|
|
|
if [[ "${#live_packages_needed[@]}" -gt 0 ]]; then
|
|
backend_log "Werkzeuge fehlen auf dem Live-Medium, installiere nach: ${live_packages_needed[*]}"
|
|
|
|
sed -i '/^deb cdrom/d' /etc/apt/sources.list 2>/dev/null || true
|
|
rm -f /etc/apt/sources.list.d/*cdrom* 2>/dev/null || true
|
|
|
|
apt-get update -qq ||
|
|
{ backend_fatal "apt-get update fehlgeschlagen."; return 1; }
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y "${live_packages_needed[@]}" ||
|
|
{ backend_fatal "Installation fehlender Werkzeuge fehlgeschlagen."; return 1; }
|
|
fi
|
|
|
|
command -v base64 >/dev/null 2>&1 ||
|
|
{ backend_fatal "Benötigtes Werkzeug fehlt: base64"; return 1; }
|
|
|
|
[[ -r "${PRESEED_TEMPLATE}" ]] ||
|
|
{ backend_fatal "Preseed-Template nicht gefunden: ${PRESEED_TEMPLATE}"; return 1; }
|
|
|
|
[[ -r "${POSTINSTALL_SCRIPT}" ]] ||
|
|
{ backend_fatal "Postinstall-Skript nicht gefunden: ${POSTINSTALL_SCRIPT}"; return 1; }
|
|
|
|
install -d \
|
|
--mode=0700 \
|
|
--owner=root \
|
|
--group=root \
|
|
"${RUNTIME_DIR}"
|
|
|
|
rm -f -- "${CONFIG_FILE}"
|
|
|
|
backend_log "Initialisiert."
|
|
}
|
|
|
|
backend_validate() {
|
|
[[ -r "${RUNTIME_BLUEPRINT_FILE}" ]] ||
|
|
{ backend_fatal "Runtime Blueprint nicht gefunden: ${RUNTIME_BLUEPRINT_FILE}"; return 1; }
|
|
|
|
jq --exit-status \
|
|
--arg backend_key "${BACKEND_KEY}" \
|
|
'.runtime_blueprint.backend_id == $backend_key' \
|
|
"${RUNTIME_BLUEPRINT_FILE}" >/dev/null ||
|
|
{ backend_fatal "Runtime Blueprint ist nicht für Backend '${BACKEND_KEY}' aufgelöst."; return 1; }
|
|
|
|
jq --exit-status '
|
|
.runtime_blueprint.installation_directives
|
|
| (.disk_encryption | type == "boolean")
|
|
and (.partitioning | type == "object")
|
|
and (.secure_boot_required | type == "boolean")
|
|
' "${RUNTIME_BLUEPRINT_FILE}" >/dev/null ||
|
|
{ backend_fatal "Installationszeitliche Vorgaben fehlen oder sind ungültig."; return 1; }
|
|
|
|
# disk_encryption wird für Mint (noch) nicht unterstützt - kein getesteter
|
|
# LUKS-Preseed-Mechanismus (anders als Fedoras "autopart --encrypted").
|
|
if [[ "$(jq --raw-output '.runtime_blueprint.installation_directives.disk_encryption' "${RUNTIME_BLUEPRINT_FILE}")" == "true" ]]; then
|
|
backend_fatal "disk_encryption=true wird vom Mint-Backend derzeit nicht unterstützt."
|
|
return 1
|
|
fi
|
|
|
|
backend_log "Runtime Blueprint ist gültig für Backend '${BACKEND_KEY}'."
|
|
}
|
|
|
|
|
|
# Baut einen einzelnen partman-auto/expert_recipe-Partitionsblock als
|
|
# EINZEILIGEN String (Felder durch Leerzeichen statt Zeilenumbrueche
|
|
# getrennt) - Debconf-Preseed-Werte mit eingebetteten Zeilenumbruechen
|
|
# brechen leicht lautlos (siehe base64-Kommentar bei success_command weiter
|
|
# unten fuer denselben Fallstrick an anderer Stelle), partmans Parser selbst
|
|
# ist bei Leerzeichen als Trenner tolerant.
|
|
_tuxflotte_partman_stanza() {
|
|
local size_mb="$1"
|
|
local filesystem="$2"
|
|
local mountpoint="$3"
|
|
local extra_flags="${4:-}"
|
|
|
|
printf '%s %s %s %s %s method{ format } format{ } use_filesystem{ } filesystem{ %s } mountpoint{ %s } . ' \
|
|
"${size_mb}" "${size_mb}" "${size_mb}" "${filesystem}" "${extra_flags}" "${filesystem}" "${mountpoint}"
|
|
}
|
|
|
|
# Ermittelt den ersten echten Datentraeger des Zielgeraets fuer die
|
|
# Groessenberechnung bei prozentualer Partitionierung (Fedoras autopart macht
|
|
# implizit dieselbe Ein-Datentraeger-Annahme, siehe fedora/backend.sh).
|
|
# Groessenfilter (>0) und Namensausschluss noetig - reale Systeme koennen
|
|
# nbd-/zram-/loop-Geraete mit type=="disk" aber ohne echte Speicherkapazitaet
|
|
# auflisten, die sonst faelschlich vor dem echten Zieldatentraeger gewaehlt
|
|
# wuerden (real beim Testen entdeckt).
|
|
_tuxflotte_detect_target_disk() {
|
|
lsblk --nodeps --noheadings --bytes --output NAME,TYPE,SIZE --paths |
|
|
awk '$2 == "disk" && $3 > 0 && $1 !~ /(nbd|zram|loop)[0-9]*$/ { print $1; exit }'
|
|
}
|
|
|
|
# Baut den kompletten partman-auto/expert_recipe-Rezeptkoerper aus dem
|
|
# installation_directives.partitioning-Objekt. Prozentangaben werden anhand
|
|
# der realen Zieldatentraegergroesse (erst hier, live auf dem Zielgeraet,
|
|
# bekannt - nicht beim ISO-Bau) in feste MB-Groessen umgerechnet. Bewusst
|
|
# feste Groessen (min=priority=max) statt partmans eigener proportionaler
|
|
# Prioritaets-Verteilung - deterministischer und leichter zu verifizieren.
|
|
_tuxflotte_render_partman_recipe() {
|
|
local partitioning_json="$1"
|
|
local root_filesystem="$2"
|
|
local scheme
|
|
local disk_device
|
|
local disk_size_mb
|
|
local extra_count
|
|
local extra_percent_sum=0
|
|
local root_mb
|
|
local recipe_body=""
|
|
local i
|
|
local mountpoint
|
|
local filesystem
|
|
local percent
|
|
local size_mb
|
|
local esp_mb=0
|
|
local bios_grub_mb=0
|
|
local root_extra_flags
|
|
|
|
scheme="$(jq --raw-output '.scheme // "single"' <<<"${partitioning_json}")"
|
|
|
|
disk_device="$(_tuxflotte_detect_target_disk)"
|
|
[[ -n "${disk_device}" ]] ||
|
|
{ backend_fatal "Zieldatenträger konnte nicht ermittelt werden."; return 1; }
|
|
disk_size_mb="$(( $(blockdev --getsize64 "${disk_device}") / 1024 / 1024 ))"
|
|
|
|
# Die eingebauten partman-Recipes ("atomic" etc.) legen auf UEFI-Systemen
|
|
# automatisch eine EFI-System-Partition an - ein eigenes expert_recipe
|
|
# muss das selbst tun, sonst warnt/verweigert der Installer (real beim
|
|
# Testen entdeckt: "No EFI System Partition was found"). 512 MB vorab
|
|
# reserviert, vor der Prozentaufteilung der restlichen Platte.
|
|
if [[ -d /sys/firmware/efi ]]; then
|
|
# Exakte Stanza-Form aus der eingebauten "atomic"-Recipe uebernommen
|
|
# (/usr/lib/partman/recipes-amd64-efi/30atomic auf dem Live-Medium
|
|
# ausgelesen) - eine erste eigene Vermutung ohne $reusemethod{ } und
|
|
# mit $bootable{ } wurde von partman zwar anstandslos geparst, aber
|
|
# nicht als gueltige EFI-System-Partition erkannt ("No EFI System
|
|
# Partition was found", real beim Testen entdeckt).
|
|
esp_mb=512
|
|
disk_size_mb="$(( disk_size_mb - esp_mb ))"
|
|
recipe_body="${esp_mb} ${esp_mb} ${esp_mb} fat32 \$reusemethod{ } \$primary{ } method{ efi } format{ } . "
|
|
# Root NICHT zusaetzlich $bootable{ } markieren wie im BIOS-Zweig
|
|
# unten (real beim Testen entdeckt, 29.08.2026): auf UEFI traegt
|
|
# bereits die ESP-Stanza oben die eigentliche Boot-Kennzeichnung
|
|
# (method{ efi }) - das im Original-Rezept
|
|
# /usr/lib/partman/recipes-amd64-efi/30atomic (Quelle der
|
|
# ESP-Stanza) uebernommene Wurzel-Partitionsschema markiert die
|
|
# Root-Partition dort ebenfalls NICHT als bootable. Ein
|
|
# zusaetzliches $bootable{ } auf der Root-Partition scheint partman
|
|
# in einen Zustand zu bringen, den es nach dem Commit staendig neu
|
|
# bewerten will: der Installer klickte "Jetzt installieren"
|
|
# nachweislich korrekt (Debug-Log erreichte sogar
|
|
# "grub-installer/bootdev seen"), sprang aber danach immer wieder
|
|
# zurueck auf dieselbe Partitionierungsseite (ubi-partman.py:
|
|
# rebuild_cache(), ausgeloest durch die wiederholt gestellte
|
|
# Debconf-Frage "ubiquity/partman-rebuild-cache" aus
|
|
# /lib/partman/update.d/99signal_ubiquity). Noch nicht abschliessend
|
|
# verifiziert, ob dies die alleinige Ursache ist - siehe
|
|
# ADR-0023-Nachtrag.
|
|
root_extra_flags='$primary{ }'
|
|
else
|
|
# Analoges Pendant fuer reinen BIOS-Betrieb: partman legt den
|
|
# Datentraeger auch ohne EFI offenbar als GPT an (real beim Testen
|
|
# bestaetigt: freier Speicher vor Partition 1 und nach der letzten
|
|
# Partition ist die GPT-Kopfdaten-Signatur, kein MSDOS-Layout). GPT +
|
|
# BIOS-Boot braucht eine kleine unformatierte Boot-Partition fuer den
|
|
# GRUB-Core, sonst schlaegt /usr/lib/partman/check.d/08biosgrub fehl
|
|
# und partman-partitioning/no_bootable_biosgrub sorgt fuer eine
|
|
# Endlosschleife zurueck ins choose_partition-Menue statt
|
|
# abzuschliessen (real beim Testen entdeckt und via 08biosgrub-
|
|
# Quelltext auf dem Live-Medium verifiziert - "true" bei dieser Frage
|
|
# bedeutet dort "Problem besteht weiterhin", nicht "trotzdem
|
|
# fortfahren", anders als bei den meisten uebrigen Boolean-Fragen in
|
|
# diesem Rezept). "$iflabel{ gpt }" macht die Stanza auf einem
|
|
# MSDOS-Datentraeger automatisch wirkungslos.
|
|
bios_grub_mb=1
|
|
disk_size_mb="$(( disk_size_mb - bios_grub_mb ))"
|
|
recipe_body="${bios_grub_mb} ${bios_grub_mb} ${bios_grub_mb} free \$iflabel{ gpt } \$reusemethod{ } method{ biosgrub } . "
|
|
# Im BIOS-Zweig (anders als EFI oben) bleibt $bootable{ } auf der
|
|
# Root-Partition noetig - hier gibt es keine ESP, die diese Rolle
|
|
# uebernimmt.
|
|
root_extra_flags='$primary{ } $bootable{ }'
|
|
fi
|
|
|
|
case "${scheme}" in
|
|
single)
|
|
root_mb="$(( disk_size_mb - 1024 ))"
|
|
[[ "${root_mb}" -ge 2048 ]] ||
|
|
{ backend_fatal "Zieldatenträger ist zu klein (${disk_size_mb} MB)."; return 1; }
|
|
|
|
recipe_body="${recipe_body}$(_tuxflotte_partman_stanza "${root_mb}" "${root_filesystem}" "/" "${root_extra_flags}")"
|
|
;;
|
|
custom)
|
|
extra_count="$(jq '.extra_partitions | length' <<<"${partitioning_json}")"
|
|
[[ "${extra_count}" -gt 0 ]] ||
|
|
{ backend_fatal "scheme=custom ohne extra_partitions angegeben."; return 1; }
|
|
|
|
for ((i = 0; i < extra_count; i++)); do
|
|
mountpoint="$(jq --raw-output ".extra_partitions[${i}].mountpoint" <<<"${partitioning_json}")"
|
|
filesystem="$(jq --raw-output ".extra_partitions[${i}].filesystem" <<<"${partitioning_json}")"
|
|
percent="$(jq --raw-output ".extra_partitions[${i}].percent" <<<"${partitioning_json}")"
|
|
|
|
case "${mountpoint}" in
|
|
/home|/var) ;;
|
|
*) backend_fatal "Nicht unterstützter Einhängepunkt: ${mountpoint}"; return 1 ;;
|
|
esac
|
|
case "${filesystem}" in
|
|
ext4|btrfs) ;;
|
|
*) backend_fatal "Nicht unterstütztes Dateisystem: ${filesystem}"; return 1 ;;
|
|
esac
|
|
|
|
extra_percent_sum="$(( extra_percent_sum + percent ))"
|
|
done
|
|
|
|
[[ "${extra_percent_sum}" -gt 0 && "${extra_percent_sum}" -lt 90 ]] ||
|
|
{ backend_fatal "Summe der Partitions-Prozentangaben ist ungültig: ${extra_percent_sum}"; return 1; }
|
|
|
|
root_mb="$(( disk_size_mb * (100 - extra_percent_sum) / 100 - 1024 ))"
|
|
[[ "${root_mb}" -ge 2048 ]] ||
|
|
{ backend_fatal "Root-Partition wäre bei dieser Aufteilung zu klein."; return 1; }
|
|
|
|
recipe_body="${recipe_body}$(_tuxflotte_partman_stanza "${root_mb}" "${root_filesystem}" "/" "${root_extra_flags}")"
|
|
|
|
for ((i = 0; i < extra_count; i++)); do
|
|
mountpoint="$(jq --raw-output ".extra_partitions[${i}].mountpoint" <<<"${partitioning_json}")"
|
|
filesystem="$(jq --raw-output ".extra_partitions[${i}].filesystem" <<<"${partitioning_json}")"
|
|
percent="$(jq --raw-output ".extra_partitions[${i}].percent" <<<"${partitioning_json}")"
|
|
size_mb="$(( disk_size_mb * percent / 100 ))"
|
|
|
|
recipe_body="${recipe_body}$(_tuxflotte_partman_stanza "${size_mb}" "${filesystem}" "${mountpoint}")"
|
|
done
|
|
;;
|
|
*)
|
|
backend_fatal "Nicht unterstütztes Partitionierungsschema: ${scheme}"
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
printf 'tuxflotte :: %s' "${recipe_body}"
|
|
}
|
|
|
|
backend_generate_config() {
|
|
local hostname
|
|
local device_id
|
|
local partitioning_json
|
|
local root_filesystem
|
|
local secure_boot_required
|
|
local partman_recipe
|
|
local blueprints_json
|
|
local postinstall_rendered
|
|
local postinstall_b64
|
|
|
|
[[ -r "${SERVER_RESPONSE_FILE}" ]] ||
|
|
{ backend_fatal "Serverantwort nicht gefunden: ${SERVER_RESPONSE_FILE}"; return 1; }
|
|
|
|
hostname="$(jq --raw-output '.device.hostname // empty' "${SERVER_RESPONSE_FILE}")"
|
|
[[ -n "${hostname}" ]] ||
|
|
{ backend_fatal "Kein Hostname in der Serverantwort gefunden."; return 1; }
|
|
|
|
device_id="$(jq --raw-output '.device.id // empty' "${SERVER_RESPONSE_FILE}")"
|
|
[[ -n "${device_id}" ]] ||
|
|
{ backend_fatal "Keine Geräte-ID in der Serverantwort gefunden."; return 1; }
|
|
|
|
# Fuer die beidseitige Identifikation (Geraeteliste <-> Geraet selbst,
|
|
# siehe postinstall.sh) - aus der bereits waehrend des Live-Boots
|
|
# berechneten hardware.json, nicht aus der Serverantwort (die kennt nur
|
|
# die zugewiesene device_id, nicht den urspruenglichen Hardware-Hash).
|
|
local device_fingerprint
|
|
[[ -r "${HARDWARE_FILE}" ]] ||
|
|
{ backend_fatal "Hardware-Erfassung nicht gefunden: ${HARDWARE_FILE}"; return 1; }
|
|
device_fingerprint="$(jq --raw-output '.identity.device_fingerprint // empty' "${HARDWARE_FILE}")"
|
|
[[ -n "${device_fingerprint}" ]] ||
|
|
{ backend_fatal "Kein device_fingerprint in ${HARDWARE_FILE} gefunden."; return 1; }
|
|
|
|
partitioning_json="$(jq --compact-output '.runtime_blueprint.installation_directives.partitioning' "${RUNTIME_BLUEPRINT_FILE}")"
|
|
secure_boot_required="$(jq --raw-output '.runtime_blueprint.installation_directives.secure_boot_required' "${RUNTIME_BLUEPRINT_FILE}")"
|
|
|
|
root_filesystem="$(jq --raw-output '.root_filesystem // "ext4"' <<<"${partitioning_json}")"
|
|
case "${root_filesystem}" in
|
|
ext4|btrfs) ;;
|
|
*) backend_fatal "Nicht unterstütztes Root-Dateisystem: ${root_filesystem}"; return 1 ;;
|
|
esac
|
|
|
|
partman_recipe="$(_tuxflotte_render_partman_recipe "${partitioning_json}" "${root_filesystem}")" ||
|
|
return 1
|
|
|
|
if [[ "${secure_boot_required}" == "true" ]]; then
|
|
backend_log "Hinweis: secure_boot_required=true wird derzeit nicht in der Preseed-Konfiguration durchgesetzt (Phase 1)."
|
|
fi
|
|
|
|
blueprints_json="$(jq --compact-output '.runtime_blueprint.blueprints' "${RUNTIME_BLUEPRINT_FILE}")"
|
|
|
|
# Erste Stufe: postinstall.sh-Platzhalter auflösen.
|
|
postinstall_rendered="$(
|
|
TUXFLOTTE_DEVICE_ID="${device_id}" \
|
|
TUXFLOTTE_BLUEPRINTS_JSON="${blueprints_json}" \
|
|
TUXFLOTTE_DEVICE_FINGERPRINT="${device_fingerprint}" \
|
|
envsubst '${TUXFLOTTE_DEVICE_ID} ${TUXFLOTTE_BLUEPRINTS_JSON} ${TUXFLOTTE_DEVICE_FINGERPRINT}' \
|
|
<"${POSTINSTALL_SCRIPT}"
|
|
)"
|
|
|
|
if grep -q '\${TUXFLOTTE_' <<<"${postinstall_rendered}"; then
|
|
backend_fatal "postinstall.sh enthält nach envsubst nicht aufgelöste Platzhalter."
|
|
return 1
|
|
fi
|
|
|
|
# base64-Kodierung: mehrzeilige/zitierte Preseed-Werte brechen unter
|
|
# Debconf lautlos (real erprobt, siehe backends/mint/wlan-test.seed) -
|
|
# als einzeiliger Base64-Blob besteht der success_command-Wert nur noch
|
|
# aus unkritischen Zeichen.
|
|
postinstall_b64="$(printf '%s' "${postinstall_rendered}" | base64 -w0)"
|
|
|
|
# Zweite Stufe: preseed.tpl mit allen Werten inkl. des fertigen Base64-Blobs auflösen.
|
|
TUXFLOTTE_HOSTNAME="${hostname}" \
|
|
TUXFLOTTE_PARTMAN_RECIPE="${partman_recipe}" \
|
|
TUXFLOTTE_POSTINSTALL_B64="${postinstall_b64}" \
|
|
envsubst '${TUXFLOTTE_HOSTNAME} ${TUXFLOTTE_PARTMAN_RECIPE} ${TUXFLOTTE_POSTINSTALL_B64}' \
|
|
<"${PRESEED_TEMPLATE}" >"${CONFIG_FILE}"
|
|
|
|
chmod 0600 "${CONFIG_FILE}"
|
|
|
|
[[ -s "${CONFIG_FILE}" ]] ||
|
|
{ backend_fatal "Erzeugte Konfigurationsdatei ist leer: ${CONFIG_FILE}"; return 1; }
|
|
|
|
if grep -q '\${TUXFLOTTE_' "${CONFIG_FILE}"; then
|
|
backend_fatal "Erzeugte Konfigurationsdatei enthält nicht aufgelöste Platzhalter."
|
|
return 1
|
|
fi
|
|
|
|
backend_log "Konfiguration erzeugt: ${CONFIG_FILE}"
|
|
}
|
|
|
|
backend_launch() {
|
|
local cdrom_vmlinuz="/cdrom/casper/vmlinuz"
|
|
local cdrom_initrd="/cdrom/casper/initrd.lz"
|
|
local extra_initrd_dir="${RUNTIME_DIR}/initrd-extra"
|
|
local extra_cpio="${RUNTIME_DIR}/extra.cpio.gz"
|
|
local custom_initrd="${RUNTIME_DIR}/initrd-custom.lz"
|
|
|
|
[[ -r "${cdrom_vmlinuz}" && -r "${cdrom_initrd}" ]] ||
|
|
{ backend_fatal "Casper-Kernel/-Initrd nicht gefunden unter /cdrom/casper."; return 1; }
|
|
|
|
# Das personalisierte Preseed (CONFIG_FILE, erst live auf diesem Gerät
|
|
# erzeugt - Hostname/Geräte-ID/Partitionierung sind erst hier bekannt,
|
|
# nicht schon beim ISO-Bau) kann nicht per file=/cdrom/... übergeben
|
|
# werden (read-only Medium, Inhalt seit ISO-Bau fixiert) und auch nicht
|
|
# per url= von einem selbst gestarteten lokalen Server - der komplette
|
|
# Prozess- und Netzwerkzustand dieser Sitzung geht beim Kexec-Sprung
|
|
# verloren, ein soeben gestarteter HTTP-Server koennte die neue
|
|
# Boot-Umgebung also nicht mehr bedienen. Stattdessen wird das Preseed in
|
|
# eine zusaetzliche Initrd-Schicht eingebettet: der Kernel unterstuetzt
|
|
# aneinandergehaengte cpio-Archive als initramfs (spaetere Archive
|
|
# ergaenzen fruehere), das uebersteht den Kexec-Uebergang unveraendert.
|
|
# Real gegen QEMU verifiziert (Phase-1-Spike, beide Ansaetze getestet).
|
|
rm -rf "${extra_initrd_dir}"
|
|
install -d --mode=0700 --owner=root --group=root "${extra_initrd_dir}"
|
|
cp "${CONFIG_FILE}" "${extra_initrd_dir}/preseed.cfg"
|
|
|
|
(cd "${extra_initrd_dir}" && find . | cpio -o -H newc 2>/dev/null | gzip) \
|
|
>"${extra_cpio}" ||
|
|
{ backend_fatal "Preseed-Initrd-Schicht konnte nicht gebaut werden."; return 1; }
|
|
|
|
cat "${cdrom_initrd}" "${extra_cpio}" >"${custom_initrd}" ||
|
|
{ backend_fatal "Initrd konnte nicht zusammengesetzt werden."; return 1; }
|
|
|
|
backend_log "Lade Kexec-Ziel fuer automatisierten Ubiquity-Start."
|
|
|
|
# Ubiquitys eigenes "noninteractive"-Frontend (ubiquity/frontend/
|
|
# noninteractive.py, ueber das gleichnamige Boot-Keyword ausgewaehlt)
|
|
# arbeitet zwar rein ueber Debconf ohne je ein Fenster zu zeichnen, aber
|
|
# dessen Seite fuer die gefuehrte Partitionierung (ubi-partman.py) haengt
|
|
# sich bei einem vollstaendig vorbefuellten Rezept in einer echten
|
|
# Endlosschleife auf (staendiges Neuaufbauen des choose_partition-Menues,
|
|
# "partman/confirm" wird nie erreicht - real ueber >800 Wiederholungen
|
|
# ohne Fortschritt bestaetigt, siehe ADR-0023-Nachtrag). Ursache: die
|
|
# PageNoninteractive-Klasse liefert fuer etliche vom GTK-Codepfad
|
|
# benoetigte Rueckfragen (get_autopartition_choice(), get_crypto_keys())
|
|
# nur "pass"/None statt echter Werte.
|
|
#
|
|
# Deshalb bewusst zurueck auf "automatic-ubiquity" (echte GTK-Oberflaeche,
|
|
# PageGtk-Klasse - der von Ubiquity selbst getestete, produktiv genutzte
|
|
# Codepfad, auch fuer die Partitionierung). Der GTK-Assistent fuellt jede
|
|
# Seite aus dem Preseed vor, wartet aber weiterhin auf einen "Weiter"-Klick
|
|
# pro Seite (real verifiziert, siehe ADR-0023-Nachtrag) - dafuer laeuft
|
|
# zusaetzlich autoclicker.sh (live-updates/opt/tuxflotte/scripts/), per
|
|
# systemd-Service und ausgeloest durch das eigene Boot-Keyword
|
|
# "tuxflotte-autoclick" (harmlos bei jedem anderen Boot ohne dieses
|
|
# Keyword). "debug-ubiquity" (setzt debug="-d") macht
|
|
# /var/log/installer/debug ausfuehrlicher, hilfreich bei weiterer
|
|
# Fehlersuche.
|
|
# username=/hostname=mint bewusst ergaenzt (real beim Testen entdeckt,
|
|
# 29.08.2026): ohne diese beiden Parameter faellt casper auf dem
|
|
# kexec-Boot auf einen anderen Live-Account-Zustand zurueck als beim
|
|
# ersten Boot (der die grub.cfg-Vorlage explizit mit "username=mint
|
|
# hostname=mint" startet) - konkret verlangt der Konsolenlogin auf
|
|
# diesem zweiten Boot ein echtes Passwort statt des sonst leeren
|
|
# Live-Session-Passworts. Fuer den eigentlichen Auto-Install-Ablauf
|
|
# (GTK-Assistent unter ubiquity-dm, kein Konsolenlogin noetig)
|
|
# folgenlos, aber inkonsistent gegenueber dem ersten Boot und erschwert
|
|
# die Fehlersuche via Konsole unnoetig - deshalb hier angeglichen.
|
|
kexec -l "${cdrom_vmlinuz}" \
|
|
--initrd="${custom_initrd}" \
|
|
--append="boot=casper automatic-ubiquity tuxflotte-autoclick debug-ubiquity noprompt file=/preseed.cfg debian-installer/language=de keyboard-configuration/layoutcode=de username=mint hostname=mint quiet splash ---" ||
|
|
{ backend_fatal "kexec -l fehlgeschlagen."; return 1; }
|
|
|
|
backend_log "Starte unbeaufsichtigte Installation (kexec -e). Kein Ruecksprung erwartet - ab hier laeuft die eigentliche Installation im neuen Kernel weiter."
|
|
|
|
kexec -e
|
|
}
|
|
|
|
backend_postinstall() {
|
|
backend_log "Provisioning-Agent-Einrichtung erfolgt im ubiquity/success_command der Preseed-Konfiguration (Agent-Abruf, Bootstrap-Registrierung, systemd-Aktivierung)."
|
|
}
|