chore(boot-medium): Phase 4 - alte Ubiquity-Ära-Dateien entfernen, build.sh auf Fedora reduzieren
Entfernt (Historie bleibt ueber Git-Log erhalten, siehe ADR-0025 in platform-docs fuer die vollstaendige Begruendung/den Gesamtrueckblick): - backends/mint/ (Ubiquity-Preseed-Backend, backend_launch()-Pfad nicht mehr referenziert seit Phase 1, postinstall.sh dort bereits migriert) - scripts/lib/initrd.sh, initrd-hooks/ (Casper-Bottom-Hook-Injection, nur fuer das Patchen einer fremden fertigen Mint-ISO noetig) - grub/mint-boot-grub.cfg, grub/mint-isolinux-live.cfg - live-updates/ (Cinnamon-Kiosk, AT-SPI-Autoklicker) scripts/build.sh (das alte Test-Build-Skript) bedient jetzt nur noch das Fedora-Backend (Kickstart/Anaconda, unveraendert, ausserhalb dieses Plans) - Mint-Zweig entfernt (bake_test_preseed(), patch_initrd(), initrd.sh-Source, mint-spezifische GRUB-Pfade). Die unbedingte live-updates/-Kopie in prepare_updates() entfiel ebenfalls - sie wurde fuer Fedora nie inhaltlich gebraucht (Anaconda/Kickstart kennt kein Cinnamon-Kiosk/keinen AT-SPI-Autoklicker), nur bisher blind mitkopiert. README.md um einen Abschnitt zum neuen Boot-Medium-Bauweg ergaenzt (build_boot_medium.sh/build_customer_iso.sh vs. weiterhin Fedora-spezifisches build.sh).
This commit is contained in:
parent
b9ccae6e85
commit
d179f9c2f5
21
README.md
21
README.md
@ -14,3 +14,24 @@ Ziele:
|
|||||||
- lokaler Festplattenstart
|
- lokaler Festplattenstart
|
||||||
|
|
||||||
Die eigentliche Provisionierung erfolgt über den Tuxflotte Provisioning Server.
|
Die eigentliche Provisionierung erfolgt über den Tuxflotte Provisioning Server.
|
||||||
|
|
||||||
|
## Boot-Medium (Mint)
|
||||||
|
|
||||||
|
Das Mint-Boot-Medium ist ein eigenständiges, per Debian `live-build`
|
||||||
|
gebautes Image (`boot-medium/`, siehe ADR-0025) - kein gepatchter
|
||||||
|
Distributions-Installer mehr. Es enthält nur Tuxflottes eigenen,
|
||||||
|
headless-tauglichen Code (Module `scripts/modules/00`-`40`,
|
||||||
|
`backends/mint-image/`) und bootet direkt in einen systemd-Dienst, der
|
||||||
|
`installer.sh` startet.
|
||||||
|
|
||||||
|
- `scripts/build_boot_medium.sh [--dev]` baut das Basis-Image
|
||||||
|
(`boot-medium/live-image-amd64.hybrid.iso`). `--dev` aktiviert einen
|
||||||
|
SSH-Debug-Zugang (`tuxflotte`/`test123`); ohne `--dev` bleibt SSH
|
||||||
|
deaktiviert (Produktiv-Default).
|
||||||
|
- `scripts/build_customer_iso.sh <source.iso> <output.iso> <activation_code> [wifi_ssid] [wifi_psk] [volid]`
|
||||||
|
personalisiert ein bereits gebautes Basis-Image für eine konkrete
|
||||||
|
Kundenorganisation (Aktivierungscode + WLAN-Zugangsdaten).
|
||||||
|
|
||||||
|
Fedora nutzt weiterhin das ältere Kickstart/Anaconda-Muster
|
||||||
|
(`scripts/build.sh <source.iso> fedora`) - noch nicht auf dasselbe
|
||||||
|
Boot-Medium-Muster umgestellt.
|
||||||
|
|||||||
@ -1,454 +0,0 @@
|
|||||||
#!/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)."
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
{
|
|
||||||
echo "tuxflotte: early_command lief, $(date -u)"
|
|
||||||
echo "--- ip link ---"
|
|
||||||
ip link 2>&1
|
|
||||||
echo "--- nmcli device status (vorher) ---"
|
|
||||||
nmcli device status 2>&1
|
|
||||||
echo "--- WLAN verbinden (mit Wiederholung, falls Treiber noch nicht bereit) ---"
|
|
||||||
for i in 1 2 3 4 5 6; do
|
|
||||||
nmcli device wifi rescan 2>&1
|
|
||||||
sleep 2
|
|
||||||
if nmcli device wifi connect stallinux password a987654321 2>&1; then
|
|
||||||
echo "WLAN verbunden nach Versuch $i"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo "Versuch $i fehlgeschlagen, warte..."
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
echo "--- nmcli device status (nachher) ---"
|
|
||||||
nmcli device status 2>&1
|
|
||||||
echo "--- curl anode /health ---"
|
|
||||||
curl -m 10 -sS https://anode.tuxflotte.de/health 2>&1
|
|
||||||
echo
|
|
||||||
echo "--- debconf-set-selections Test (simuliert dynamisch von anode geholten Wert) ---"
|
|
||||||
echo "d-i netcfg/get_hostname string tuxflotte-early-test" | debconf-set-selections
|
|
||||||
echo "debconf-set-selections exit: $?"
|
|
||||||
echo "--- Verifikation per debconf-communicate ---"
|
|
||||||
echo "GET netcfg/get_hostname" | debconf-communicate 2>&1
|
|
||||||
} >> /var/log/tuxflotte-early-command.log 2>&1
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Lesbare Referenzfassung des Agent-Bootstraps, den backend_generate_config()
|
|
||||||
# in backend.sh zur Laufzeit envsubst-auflöst und anschließend base64-kodiert
|
|
||||||
# in preseed.tpls ubiquity/success_command einsetzt (siehe backend.sh). Diese
|
|
||||||
# Datei selbst wird nie direkt ausgeführt - sie existiert, damit der Code
|
|
||||||
# lesbar bleibt statt nur als Base64-Blob im Preseed zu existieren.
|
|
||||||
#
|
|
||||||
# Inhaltlich das Bash-Pendant zu backends/fedora/kickstart.tpl %post: gleiche
|
|
||||||
# curl/jq-Aufrufe, nur eingebettet über ubiquity/success_command (in-target,
|
|
||||||
# chrooted) statt Kickstart %post.
|
|
||||||
tuxflotte_agent_fatal() {
|
|
||||||
echo "tuxflotte: Provisioning-Agent-Einrichtung fehlgeschlagen: $*" >> /var/log/tuxflotte-postinstall.log
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
ANODE_URL="https://anode.tuxflotte.de"
|
|
||||||
AGENT_REPO_RAW="https://git.tuxflotte.de/admin/provisioning-agent/raw/branch/main"
|
|
||||||
|
|
||||||
install -d -m 0700 /etc/tuxflotte ||
|
|
||||||
tuxflotte_agent_fatal "Verzeichnis /etc/tuxflotte konnte nicht angelegt werden."
|
|
||||||
|
|
||||||
# Identifikation soll in beide Richtungen moeglich sein: die Geraeteliste
|
|
||||||
# zeigt den Fingerprint bereits an (siehe geraete_liste.html), aber bislang
|
|
||||||
# gab es auf dem installierten Geraet selbst keine Datei, um ihn mit einem
|
|
||||||
# einfachen "cat" gegenzupruefen - build_device_fingerprint() (10_hardware.sh)
|
|
||||||
# berechnet ihn nur einmalig waehrend des Live-Boots und haelt ihn sonst
|
|
||||||
# nirgends fest. Absichtlich Klartext, kein Secret - reiner Hardware-Hash,
|
|
||||||
# kein chmod 0600 noetig wie bei agent.credentials.
|
|
||||||
echo "${TUXFLOTTE_DEVICE_FINGERPRINT}" > /etc/tuxflotte/device_fingerprint ||
|
|
||||||
tuxflotte_agent_fatal "device_fingerprint konnte nicht abgelegt werden."
|
|
||||||
|
|
||||||
cat > /etc/tuxflotte/runtime_blueprint.json <<'RUNTIME_BLUEPRINT_EOF'
|
|
||||||
${TUXFLOTTE_BLUEPRINTS_JSON}
|
|
||||||
RUNTIME_BLUEPRINT_EOF
|
|
||||||
|
|
||||||
install -d /opt/tuxflotte/agent ||
|
|
||||||
tuxflotte_agent_fatal "Verzeichnis /opt/tuxflotte/agent konnte nicht angelegt werden."
|
|
||||||
|
|
||||||
curl --silent --show-error --fail --location \
|
|
||||||
--output /opt/tuxflotte/agent/agent.py \
|
|
||||||
"${AGENT_REPO_RAW}/agent.py" ||
|
|
||||||
tuxflotte_agent_fatal "agent.py konnte nicht von ${AGENT_REPO_RAW} geladen werden."
|
|
||||||
|
|
||||||
curl --silent --show-error --fail --location \
|
|
||||||
--output /etc/systemd/system/tuxflotte-agent.service \
|
|
||||||
"${AGENT_REPO_RAW}/tuxflotte-agent.service" ||
|
|
||||||
tuxflotte_agent_fatal "tuxflotte-agent.service konnte nicht von ${AGENT_REPO_RAW} geladen werden."
|
|
||||||
|
|
||||||
AGENT_BOOTSTRAP_RESPONSE="$(
|
|
||||||
curl --silent --show-error --fail --location \
|
|
||||||
--header 'Content-Type: application/json' \
|
|
||||||
--data-binary "{\"device_id\": \"${TUXFLOTTE_DEVICE_ID}\"}" \
|
|
||||||
"${ANODE_URL}/api/v1/agent/bootstrap"
|
|
||||||
)" ||
|
|
||||||
tuxflotte_agent_fatal "Bootstrap-Aufruf gegen ${ANODE_URL} ist fehlgeschlagen."
|
|
||||||
|
|
||||||
jq --exit-status '.success == true' <<<"${AGENT_BOOTSTRAP_RESPONSE}" >/dev/null ||
|
|
||||||
tuxflotte_agent_fatal "Server hat den Bootstrap abgelehnt: ${AGENT_BOOTSTRAP_RESPONSE}"
|
|
||||||
|
|
||||||
jq --null-input \
|
|
||||||
--arg device_id "${TUXFLOTTE_DEVICE_ID}" \
|
|
||||||
--argjson response "${AGENT_BOOTSTRAP_RESPONSE}" \
|
|
||||||
'{device_id: $device_id, agent_secret: $response.agent_secret}' \
|
|
||||||
> /etc/tuxflotte/agent.credentials ||
|
|
||||||
tuxflotte_agent_fatal "Credentials-Datei konnte nicht erzeugt werden."
|
|
||||||
chmod 0600 /etc/tuxflotte/agent.credentials
|
|
||||||
|
|
||||||
systemctl enable tuxflotte-agent.service ||
|
|
||||||
tuxflotte_agent_fatal "systemd-Dienst tuxflotte-agent konnte nicht aktiviert werden."
|
|
||||||
|
|
||||||
echo "tuxflotte: Runtime Blueprint unter /etc/tuxflotte/runtime_blueprint.json hinterlegt." >> /var/log/tuxflotte-postinstall.log
|
|
||||||
echo "tuxflotte: Provisioning-Agent installiert, registriert und für den ersten Boot aktiviert." >> /var/log/tuxflotte-postinstall.log
|
|
||||||
@ -1,137 +0,0 @@
|
|||||||
### Tuxflotte Auto-Install Preseed fuer Linux Mint (Ubiquity/Debian-Installer).
|
|
||||||
### Liegt direkt auf dem Medium (file=/cdrom/preseed/tuxflotte.seed), keine
|
|
||||||
### Netz-Zustellung noetig -- analog zu Fedoras inst.ks=cdrom:/ks.cfg.
|
|
||||||
###
|
|
||||||
### d-i preseed/early_command wird bewusst NICHT verwendet -- unter Ubiquity
|
|
||||||
### bestaetigt wirkungslos (Ubiquity nutzt eigene Python-Plugins statt der
|
|
||||||
### klassischen Debian-Installer-Komponenten, an die early_command haengt).
|
|
||||||
|
|
||||||
d-i debian-installer/locale string de_DE.UTF-8
|
|
||||||
d-i keyboard-configuration/xkb-keymap select de
|
|
||||||
d-i keyboard-configuration/layoutcode string de
|
|
||||||
|
|
||||||
d-i netcfg/get_hostname string ${TUXFLOTTE_HOSTNAME}
|
|
||||||
d-i netcfg/get_domain string unassigned-domain
|
|
||||||
|
|
||||||
# Lab-Bootstrap-Zugangsdaten. Ersetzt ein noch fehlendes Secret-Reference-Modell
|
|
||||||
# (siehe 09-data-model-v1.md) und darf nicht als Produktionsmechanismus gelten.
|
|
||||||
d-i passwd/user-fullname string Tuxflotte
|
|
||||||
d-i passwd/username string tuxflotte
|
|
||||||
d-i passwd/user-password password test123
|
|
||||||
d-i passwd/user-password-again password test123
|
|
||||||
d-i user-setup/allow-password-weak boolean true
|
|
||||||
|
|
||||||
d-i clock-setup/utc boolean true
|
|
||||||
d-i time/zone string Europe/Berlin
|
|
||||||
d-i clock-setup/ntp boolean true
|
|
||||||
|
|
||||||
d-i partman-auto/method string regular
|
|
||||||
d-i partman-auto/expert_recipe string ${TUXFLOTTE_PARTMAN_RECIPE}
|
|
||||||
d-i partman-auto/choose_recipe select tuxflotte
|
|
||||||
d-i partman-partitioning/confirm_write_new_label boolean true
|
|
||||||
d-i partman-partitioning/confirm_new_label boolean true
|
|
||||||
# "partman/choose_partition select finish" wurde bewusst entfernt: das ist
|
|
||||||
# die Frage des MANUELLEN/erweiterten Partitionierers ("Menu" -> "Finish
|
|
||||||
# partitioning"), keine des gefuehrten/automatischen Ablaufs. Direktes
|
|
||||||
# Preseeden ohne echten Seitenaufbau (weder im GTK- noch im
|
|
||||||
# noninteractive-Frontend) fuehrt in ubi-partman.py real reproduzierbar zu
|
|
||||||
# einer Endlosschleife im internen "building_cache"-Zustandsautomaten der
|
|
||||||
# choose_partition-Verarbeitung (ueber 100.000 Debconf-Zeilen in wenigen
|
|
||||||
# Sekunden ohne echten Fortschritt, sowohl mit automatic-ubiquity/GTK als
|
|
||||||
# auch mit dem verworfenen noninteractive-Frontend - siehe ADR-0023-Nachtrag).
|
|
||||||
# Ohne diese Zeile uebernehmen partman-auto/method + expert_recipe +
|
|
||||||
# choose_recipe (oben) die gefuehrte Partitionierung auf dem dafuer
|
|
||||||
# vorgesehenen Weg.
|
|
||||||
d-i partman/confirm boolean true
|
|
||||||
d-i partman/confirm_nochanges boolean true
|
|
||||||
d-i partman/confirm_nooverwrite boolean true
|
|
||||||
d-i partman/unmount_active boolean true
|
|
||||||
d-i partman/automount boolean true
|
|
||||||
d-i partman/filter_mounted boolean true
|
|
||||||
d-i partman-partitioning/confirm_resize boolean true
|
|
||||||
d-i partman-ext3/lazy_itable_init boolean true
|
|
||||||
d-i partman/boot_not_first_partition boolean true
|
|
||||||
d-i partman-basicfilesystems/boot_not_first_partition boolean true
|
|
||||||
d-i partman-basicfilesystems/boot_not_ext2 boolean true
|
|
||||||
d-i partman-ext3/boot_not_bootable boolean true
|
|
||||||
d-i partman-ext3/boot_not_ext2_or_ext3 boolean true
|
|
||||||
d-i partman-basicfilesystems/no_mount_point boolean true
|
|
||||||
d-i partman-basicfilesystems/no_swap boolean true
|
|
||||||
d-i partman-basicfilesystems/check_failed boolean true
|
|
||||||
d-i partman-basicfilesystems/swap_check_failed boolean true
|
|
||||||
d-i partman-ext3/bad_alignment boolean true
|
|
||||||
# Dieselbe Polaritaets-Falle wie bei no_bootable_biosgrub weiter unten (siehe
|
|
||||||
# dortiger Kommentar) - "true" hiesse "Problem besteht wirklich, abbrechen".
|
|
||||||
# Fuer UEFI-Zielgeraete legt _tuxflotte_render_partman_recipe() bereits eine
|
|
||||||
# echte ESP an, dieser Fallback sollte also nie greifen.
|
|
||||||
d-i partman-partitioning/no_bootable_efi boolean false
|
|
||||||
# Anders als die meisten uebrigen Boolean-Fragen hier bedeutet "true" bei
|
|
||||||
# no_bootable_biosgrub NICHT "trotzdem fortfahren", sondern "das Problem
|
|
||||||
# besteht wirklich" -> der pruefende Skript (check.d/08biosgrub) bricht dann
|
|
||||||
# mit exit 1 ab, was zur Endlosschleife zurueck ins choose_partition-Menue
|
|
||||||
# fuehrt (real entdeckt und via Quelltext auf dem Live-Medium verifiziert,
|
|
||||||
# siehe ADR-0023-Nachtrag). Der eigentliche Fix ist eine echte BIOS-Boot-
|
|
||||||
# Partition im Rezept (siehe _tuxflotte_render_partman_recipe() in
|
|
||||||
# backend.sh) - "false" hier bleibt nur als defensiver Fallback, falls die
|
|
||||||
# Partition aus irgendeinem Grund nicht als "biosgrub" erkannt wird.
|
|
||||||
d-i partman-partitioning/no_bootable_biosgrub boolean false
|
|
||||||
d-i partman-partitioning/bootable_logical boolean true
|
|
||||||
d-i partman-partitioning/unknown_label boolean true
|
|
||||||
d-i partman-partitioning/unsupported_label boolean true
|
|
||||||
d-i partman-basicmethods/method_only boolean true
|
|
||||||
d-i grub-installer/only_debian boolean true
|
|
||||||
d-i grub-installer/with_other_os boolean true
|
|
||||||
d-i grub-installer/make_active boolean true
|
|
||||||
d-i grub-installer/grub2_instead_of_grub_legacy boolean true
|
|
||||||
d-i grub-installer/grub_not_mature_on_this_platform boolean true
|
|
||||||
d-i grub-installer/multipath boolean true
|
|
||||||
d-i grub-installer/sataraid boolean true
|
|
||||||
# Wichtig: false, nicht true -- "skip" heisst hier woertlich "GRUB-Installation
|
|
||||||
# ueberspringen". true wuerde die Bootloader-Installation aktiv verhindern.
|
|
||||||
d-i grub-installer/skip boolean false
|
|
||||||
d-i partman-auto-lvm/no_boot boolean true
|
|
||||||
d-i partman-target/mount_failed boolean true
|
|
||||||
# partman-efi/no_efi wird ENTGEGEN der urspruenglichen Annahme sehr wohl
|
|
||||||
# gefragt, auch im reinen BIOS/SeaBIOS-Betrieb ohne NVRAM (real via
|
|
||||||
# debug-ubiquity-Log verifiziert, siehe ADR-0023-Nachtrag) -- Ubiquity prueft
|
|
||||||
# offenbar unabhaengig vom aktuellen Boot-Modus, ob eine EFI-System-Partition
|
|
||||||
# existiert. "false" heisst hier "trotzdem fortfahren" (die Alternative
|
|
||||||
# "true" wuerde zurueck ins Partitionierungsmenue springen und den Ablauf
|
|
||||||
# blockieren -- fuer ein bewusstes BIOS/MBR-Setup ohne EFI-Partition ist
|
|
||||||
# false die richtige Antwort).
|
|
||||||
d-i partman-efi/no_efi boolean false
|
|
||||||
# Bewusst weiterhin NICHT preseeded: grub-installer/force-efi-extra-removable
|
|
||||||
# (EFI-spezifisch, im BIOS-Betrieb ohne Wirkung) sowie partman-crypto/*,
|
|
||||||
# partman-lvm/*, partman-jfs/* (Recipe nutzt weder Crypto noch LVM noch JFS,
|
|
||||||
# koennen also nie auftreten; einige dieser Fragen sind bei "true" destruktiv
|
|
||||||
# (z.B. crypto_warn_erase), daher hier absichtlich nicht blind auf true
|
|
||||||
# gesetzt).
|
|
||||||
|
|
||||||
ubiquity ubiquity/summary note
|
|
||||||
ubiquity ubiquity/reboot boolean true
|
|
||||||
ubiquity ubiquity/use_nonfree boolean true
|
|
||||||
# Ohne diese Zeile stuerzt Ubiquity im automatic-ubiquity-GTK-Modus real
|
|
||||||
# reproduzierbar ab (TypeError: Argument 1 does not allow None as a value,
|
|
||||||
# in ubi-prepare.py enable_download_updates() -> label_download_updates.
|
|
||||||
# set_label(), ausgeloest durch einen globalen Online-Status-Callback, der
|
|
||||||
# unabhaengig vom Automatik-Modus feuert, obwohl die zugehoerige Seite dort
|
|
||||||
# nie aufgebaut wird - die Widget-Referenz bleibt None). Die "Waehrend der
|
|
||||||
# Installation aktualisieren"-Option wird dadurch bewusst deaktiviert;
|
|
||||||
# funktional kein Verlust, da der Provisioning Agent das System nach der
|
|
||||||
# Ersteinrichtung ohnehin selbst aktuell haelt.
|
|
||||||
ubiquity ubiquity/download_updates boolean false
|
|
||||||
|
|
||||||
# Pendant zu Fedoras kickstart.tpl %packages (ansible-core, git) - der
|
|
||||||
# Provisioning Agent braucht ansible-pull, das wiederum git zum Klonen des
|
|
||||||
# Ansible-Repos. Ohne diese Zeile fehlen beide auf einer frischen
|
|
||||||
# Mint-Installation, der Agent-Dienst laeuft dann in einer
|
|
||||||
# Restart-Fehlerschleife ("ansible-pull nicht gefunden") - real gegen eine
|
|
||||||
# frische Testinstallation gefunden und verifiziert (2026-08-04).
|
|
||||||
d-i pkgsel/include string ansible-core git
|
|
||||||
|
|
||||||
# success_command laeuft in-target (gechrootet ins Zielsystem) nach der
|
|
||||||
# Paketinstallation, vor dem Reboot - das Pendant zu Kickstarts %post. Der
|
|
||||||
# Payload ist base64-kodiert (backend_generate_config() in backend.sh baut
|
|
||||||
# ihn aus postinstall.sh): mehrzeilige/zitierte Preseed-Werte brechen unter
|
|
||||||
# Debconf lautlos, Base64 umgeht das (real erprobt, siehe wlan-test.seed).
|
|
||||||
ubiquity ubiquity/success_command string in-target bash -c 'echo ${TUXFLOTTE_POSTINSTALL_B64} | base64 -d | bash'
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
loadfont unicode
|
|
||||||
|
|
||||||
set color_normal=white/black
|
|
||||||
set color_highlight=black/light-gray
|
|
||||||
|
|
||||||
set timeout=30
|
|
||||||
|
|
||||||
menuentry "Start Linux Mint 22.3 Cinnamon 64-bit" --class linuxmint {
|
|
||||||
set gfxpayload=keep
|
|
||||||
linux /casper/vmlinuz boot=casper uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint iso-scan/filename=${iso_path} quiet splash --
|
|
||||||
initrd /casper/initrd.lz
|
|
||||||
}
|
|
||||||
menuentry "Start Linux Mint 22.3 Cinnamon 64-bit (compatibility mode)" {
|
|
||||||
linux /casper/vmlinuz boot=casper uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint iso-scan/filename=${iso_path} noapic noacpi nosplash irqpoll nomodeset --
|
|
||||||
initrd /casper/initrd.lz
|
|
||||||
}
|
|
||||||
menuentry "OEM install (for manufacturers)" {
|
|
||||||
set gfxpayload=keep
|
|
||||||
linux /casper/vmlinuz oem-config/enable=true only-ubiquity boot=casper uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint iso-scan/filename=${iso_path} quiet splash --
|
|
||||||
initrd /casper/initrd.lz
|
|
||||||
}
|
|
||||||
menuentry "Tuxflotte Auto-Install (Linux Mint 22.3 Cinnamon)" --class linuxmint {
|
|
||||||
set gfxpayload=keep
|
|
||||||
linux /casper/vmlinuz boot=casper uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint iso-scan/filename=${iso_path} file=/cdrom/preseed/tuxflotte.seed automatic-ubiquity noprompt debian-installer/language=de keyboard-configuration/layoutcode=de quiet splash --
|
|
||||||
initrd /casper/initrd.lz
|
|
||||||
}
|
|
||||||
grub_platform
|
|
||||||
if [ "$grub_platform" = "efi" ]; then
|
|
||||||
menuentry 'Von lokaler Festplatte booten (Standard)' {
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
set default="Von lokaler Festplatte booten (Standard)"
|
|
||||||
menuentry 'UEFI Firmware Settings' {
|
|
||||||
fwsetup
|
|
||||||
}
|
|
||||||
menuentry 'Memory test' {
|
|
||||||
linux /boot/memtest.efi
|
|
||||||
}
|
|
||||||
else
|
|
||||||
set default="0"
|
|
||||||
fi
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
timeout 100
|
|
||||||
|
|
||||||
menu background splash.png
|
|
||||||
menu title Welcome to Linux Mint 22.3 64-bit
|
|
||||||
|
|
||||||
menu color screen 37;40 #80ffffff #00000000 std
|
|
||||||
MENU COLOR border 30;44 #40ffffff #a0000000 std
|
|
||||||
MENU COLOR title 1;36;44 #ffffffff #a0000000 std
|
|
||||||
MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all
|
|
||||||
MENU COLOR unsel 37;44 #50ffffff #a0000000 std
|
|
||||||
MENU COLOR help 37;40 #c0ffffff #a0000000 std
|
|
||||||
MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std
|
|
||||||
MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std
|
|
||||||
MENU COLOR msg07 37;40 #90ffffff #a0000000 std
|
|
||||||
MENU COLOR tabmsg 31;40 #ffDEDEDE #00000000 std
|
|
||||||
MENU WIDTH 78
|
|
||||||
MENU MARGIN 15
|
|
||||||
MENU ROWS 6
|
|
||||||
MENU VSHIFT 10
|
|
||||||
MENU TABMSGROW 12
|
|
||||||
MENU CMDLINEROW 12
|
|
||||||
MENU HELPMSGROW 16
|
|
||||||
MENU HELPMSGENDROW 29
|
|
||||||
|
|
||||||
label tuxflotte
|
|
||||||
menu label Tuxflotte Auto-Install
|
|
||||||
kernel /casper/vmlinuz
|
|
||||||
append boot=casper initrd=/casper/initrd.lz uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint file=/cdrom/preseed/tuxflotte.seed automatic-ubiquity noprompt debian-installer/language=de keyboard-configuration/layoutcode=de quiet splash --
|
|
||||||
|
|
||||||
label live
|
|
||||||
menu label Start Linux Mint
|
|
||||||
kernel /casper/vmlinuz
|
|
||||||
append boot=casper initrd=/casper/initrd.lz uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint quiet splash --
|
|
||||||
|
|
||||||
label compat
|
|
||||||
menu label Start Linux Mint in compatibility mode
|
|
||||||
linux /casper/vmlinuz
|
|
||||||
append boot=casper initrd=/casper/initrd.lz uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint noapic noacpi nosplash irqpoll nomodeset --
|
|
||||||
|
|
||||||
label oem
|
|
||||||
menu label OEM install (for manufacturers)
|
|
||||||
linux /casper/vmlinuz
|
|
||||||
append oem-config/enable=true only-ubiquity boot=casper initrd=/casper/initrd.lz uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint quiet splash --
|
|
||||||
|
|
||||||
label hdt
|
|
||||||
menu label Hardware Detection
|
|
||||||
kernel hdt.c32
|
|
||||||
|
|
||||||
label local
|
|
||||||
menu label Boot from local drive
|
|
||||||
menu default
|
|
||||||
COM32 chain.c32
|
|
||||||
APPEND hd0
|
|
||||||
|
|
||||||
label memtest
|
|
||||||
menu label Memory test
|
|
||||||
linux /boot/memtest.bin
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
{
|
|
||||||
echo "tuxflotte: success_command lief, $(date -u)"
|
|
||||||
echo "--- nmcli device status (vorher) ---"
|
|
||||||
nmcli device status 2>&1
|
|
||||||
echo "--- WLAN verbinden (live, fuer den anode-Test waehrend der Installation) ---"
|
|
||||||
nmcli device wifi rescan 2>&1
|
|
||||||
sleep 3
|
|
||||||
nmcli device wifi connect stallinux password a987654321 2>&1
|
|
||||||
sleep 3
|
|
||||||
echo "--- nmcli device status (nachher) ---"
|
|
||||||
nmcli device status 2>&1
|
|
||||||
echo "--- curl anode /health ---"
|
|
||||||
curl -m 10 -sS https://anode.tuxflotte.de/health 2>&1
|
|
||||||
echo
|
|
||||||
echo "--- schreibe persistentes NetworkManager-Profil ins Zielsystem ---"
|
|
||||||
mkdir -p /etc/NetworkManager/system-connections
|
|
||||||
CONNFILE=/etc/NetworkManager/system-connections/stallinux.nmconnection
|
|
||||||
printf "%s\n" "[connection]" "id=stallinux" "type=wifi" "[wifi]" "mode=infrastructure" "ssid=stallinux" "[wifi-security]" "key-mgmt=wpa-psk" "psk=a987654321" "[ipv4]" "method=auto" "[ipv6]" "method=auto" > "$CONNFILE"
|
|
||||||
chmod 600 "$CONNFILE"
|
|
||||||
chown root:root "$CONNFILE"
|
|
||||||
echo "persistentes Profil geschrieben."
|
|
||||||
} >> /var/log/tuxflotte-postinstall.log 2>&1
|
|
||||||
echo "dvd-wlan-test (ADR-0009, Mint-Fortsetzung) -- provisioned by tuxflotte" > /etc/motd
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
### Test-Preseed fuer ADR-0009 Fortsetzung mit Mint/Ubuntu (31.07.2026-01.08.2026).
|
|
||||||
### Liegt direkt auf dem Medium (file=/cdrom/preseed/tuxflotte.seed), keine
|
|
||||||
### Netz-Zustellung noetig -- analog zu Fedoras inst.ks=cdrom:/ks.cfg.
|
|
||||||
|
|
||||||
# Test 01.08.2026: laeuft VOR allen anderen Fragen (Partitionierung etc.),
|
|
||||||
# nicht gechrootet -- Ziel ist zu pruefen, ob so frueh schon WLAN herstellbar
|
|
||||||
# ist und ob debconf-set-selections tatsaechlich spaetere Fragen beeinflusst.
|
|
||||||
# Base64 von Anfang an (Lehre aus success_command Versuch 3 gestern).
|
|
||||||
d-i preseed/early_command string bash -c 'echo IyEvYmluL2Jhc2gKewogIGVjaG8gInR1eGZsb3R0ZTogZWFybHlfY29tbWFuZCBsaWVmLCAkKGRhdGUgLXUpIgogIGVjaG8gIi0tLSBpcCBsaW5rIC0tLSIKICBpcCBsaW5rIDI+JjEKICBlY2hvICItLS0gbm1jbGkgZGV2aWNlIHN0YXR1cyAodm9yaGVyKSAtLS0iCiAgbm1jbGkgZGV2aWNlIHN0YXR1cyAyPiYxCiAgZWNobyAiLS0tIFdMQU4gdmVyYmluZGVuIChtaXQgV2llZGVyaG9sdW5nLCBmYWxscyBUcmVpYmVyIG5vY2ggbmljaHQgYmVyZWl0KSAtLS0iCiAgZm9yIGkgaW4gMSAyIDMgNCA1IDY7IGRvCiAgICBubWNsaSBkZXZpY2Ugd2lmaSByZXNjYW4gMj4mMQogICAgc2xlZXAgMgogICAgaWYgbm1jbGkgZGV2aWNlIHdpZmkgY29ubmVjdCBzdGFsbGludXggcGFzc3dvcmQgYTk4NzY1NDMyMSAyPiYxOyB0aGVuCiAgICAgIGVjaG8gIldMQU4gdmVyYnVuZGVuIG5hY2ggVmVyc3VjaCAkaSIKICAgICAgYnJlYWsKICAgIGZpCiAgICBlY2hvICJWZXJzdWNoICRpIGZlaGxnZXNjaGxhZ2VuLCB3YXJ0ZS4uLiIKICAgIHNsZWVwIDUKICBkb25lCiAgZWNobyAiLS0tIG5tY2xpIGRldmljZSBzdGF0dXMgKG5hY2hoZXIpIC0tLSIKICBubWNsaSBkZXZpY2Ugc3RhdHVzIDI+JjEKICBlY2hvICItLS0gY3VybCBhbm9kZSAvaGVhbHRoIC0tLSIKICBjdXJsIC1tIDEwIC1zUyBodHRwczovL2Fub2RlLnR1eGZsb3R0ZS5kZS9oZWFsdGggMj4mMQogIGVjaG8KICBlY2hvICItLS0gZGViY29uZi1zZXQtc2VsZWN0aW9ucyBUZXN0IChzaW11bGllcnQgZHluYW1pc2NoIHZvbiBhbm9kZSBnZWhvbHRlbiBXZXJ0KSAtLS0iCiAgZWNobyAiZC1pIG5ldGNmZy9nZXRfaG9zdG5hbWUgc3RyaW5nIHR1eGZsb3R0ZS1lYXJseS10ZXN0IiB8IGRlYmNvbmYtc2V0LXNlbGVjdGlvbnMKICBlY2hvICJkZWJjb25mLXNldC1zZWxlY3Rpb25zIGV4aXQ6ICQ/IgogIGVjaG8gIi0tLSBWZXJpZmlrYXRpb24gcGVyIGRlYmNvbmYtY29tbXVuaWNhdGUgLS0tIgogIGVjaG8gIkdFVCBuZXRjZmcvZ2V0X2hvc3RuYW1lIiB8IGRlYmNvbmYtY29tbXVuaWNhdGUgMj4mMQp9ID4+IC92YXIvbG9nL3R1eGZsb3R0ZS1lYXJseS1jb21tYW5kLmxvZyAyPiYxCg== | base64 -d | bash'
|
|
||||||
|
|
||||||
d-i debian-installer/locale string de_DE.UTF-8
|
|
||||||
d-i keyboard-configuration/xkb-keymap select de
|
|
||||||
d-i keyboard-configuration/layoutcode string de
|
|
||||||
|
|
||||||
d-i netcfg/get_hostname string tuxflotte-mint-test
|
|
||||||
d-i netcfg/get_domain string unassigned-domain
|
|
||||||
|
|
||||||
d-i passwd/user-fullname string Tuxflotte
|
|
||||||
d-i passwd/username string tuxflotte
|
|
||||||
d-i passwd/user-password password test123
|
|
||||||
d-i passwd/user-password-again password test123
|
|
||||||
d-i user-setup/allow-password-weak boolean true
|
|
||||||
|
|
||||||
d-i clock-setup/utc boolean true
|
|
||||||
d-i time/zone string Europe/Berlin
|
|
||||||
d-i clock-setup/ntp boolean true
|
|
||||||
|
|
||||||
d-i partman-auto/method string regular
|
|
||||||
d-i partman-auto/choose_recipe select atomic
|
|
||||||
d-i partman-partitioning/confirm_write_new_label boolean true
|
|
||||||
d-i partman/choose_partition select finish
|
|
||||||
d-i partman/confirm boolean true
|
|
||||||
d-i partman/confirm_nooverwrite boolean true
|
|
||||||
|
|
||||||
ubiquity ubiquity/summary note
|
|
||||||
|
|
||||||
# Versuch 4: kein automatischer Reboot mehr, damit wir vor dem Neustart aus
|
|
||||||
# der Live-Sitzung heraus direkt unter /target/... nachsehen koennen, ob
|
|
||||||
# alles geschrieben wurde -- auf Vorschlag des Nutzers.
|
|
||||||
ubiquity ubiquity/reboot boolean false
|
|
||||||
|
|
||||||
# Versuch 1: wlp9s0 (Intel AX210) erkannt, aber nicht verbunden (keine
|
|
||||||
# Zugangsdaten). Versuch 2: nmcli verbindet + anode per curl erreichbar
|
|
||||||
# WAEHREND der Installation -- funktioniert. Verbindung ueberlebte den
|
|
||||||
# Reboot nicht (nmcli sprach vermutlich mit der Live-NetworkManager-Instanz,
|
|
||||||
# nicht mit einer, die ins Zielsystem schreibt). Versuch 3: direktes
|
|
||||||
# Schreiben der .nmconnection-Datei als eigener Preseed-Wert -- Datei kam
|
|
||||||
# nie an, kein Log vorhanden, vermutlich Debconf-Problem mit einem so
|
|
||||||
# langen, mehrzeiligen Preseed-Wert (lokaler Bash-Syntaxtest war fehlerfrei,
|
|
||||||
# also kein Bash-Problem). Versuch 4 (dieser): dasselbe Skript Base64-
|
|
||||||
# kodiert -- der Preseed-Wert besteht dadurch nur noch aus unkritischen
|
|
||||||
# Zeichen, keine Anfuehrungszeichen/Klammern mehr, die Debconf Probleme
|
|
||||||
# machen koennten.
|
|
||||||
ubiquity ubiquity/use_nonfree boolean true
|
|
||||||
|
|
||||||
ubiquity ubiquity/success_command string in-target bash -c 'echo IyEvYmluL2Jhc2gKewogIGVjaG8gInR1eGZsb3R0ZTogc3VjY2Vzc19jb21tYW5kIGxpZWYsICQoZGF0ZSAtdSkiCiAgZWNobyAiLS0tIG5tY2xpIGRldmljZSBzdGF0dXMgKHZvcmhlcikgLS0tIgogIG5tY2xpIGRldmljZSBzdGF0dXMgMj4mMQogIGVjaG8gIi0tLSBXTEFOIHZlcmJpbmRlbiAobGl2ZSwgZnVlciBkZW4gYW5vZGUtVGVzdCB3YWVocmVuZCBkZXIgSW5zdGFsbGF0aW9uKSAtLS0iCiAgbm1jbGkgZGV2aWNlIHdpZmkgcmVzY2FuIDI+JjEKICBzbGVlcCAzCiAgbm1jbGkgZGV2aWNlIHdpZmkgY29ubmVjdCBzdGFsbGludXggcGFzc3dvcmQgYTk4NzY1NDMyMSAyPiYxCiAgc2xlZXAgMwogIGVjaG8gIi0tLSBubWNsaSBkZXZpY2Ugc3RhdHVzIChuYWNoaGVyKSAtLS0iCiAgbm1jbGkgZGV2aWNlIHN0YXR1cyAyPiYxCiAgZWNobyAiLS0tIGN1cmwgYW5vZGUgL2hlYWx0aCAtLS0iCiAgY3VybCAtbSAxMCAtc1MgaHR0cHM6Ly9hbm9kZS50dXhmbG90dGUuZGUvaGVhbHRoIDI+JjEKICBlY2hvCiAgZWNobyAiLS0tIHNjaHJlaWJlIHBlcnNpc3RlbnRlcyBOZXR3b3JrTWFuYWdlci1Qcm9maWwgaW5zIFppZWxzeXN0ZW0gLS0tIgogIG1rZGlyIC1wIC9ldGMvTmV0d29ya01hbmFnZXIvc3lzdGVtLWNvbm5lY3Rpb25zCiAgQ09OTkZJTEU9L2V0Yy9OZXR3b3JrTWFuYWdlci9zeXN0ZW0tY29ubmVjdGlvbnMvc3RhbGxpbnV4Lm5tY29ubmVjdGlvbgogIHByaW50ZiAiJXNcbiIgIltjb25uZWN0aW9uXSIgImlkPXN0YWxsaW51eCIgInR5cGU9d2lmaSIgIlt3aWZpXSIgIm1vZGU9aW5mcmFzdHJ1Y3R1cmUiICJzc2lkPXN0YWxsaW51eCIgIlt3aWZpLXNlY3VyaXR5XSIgImtleS1tZ210PXdwYS1wc2siICJwc2s9YTk4NzY1NDMyMSIgIltpcHY0XSIgIm1ldGhvZD1hdXRvIiAiW2lwdjZdIiAibWV0aG9kPWF1dG8iID4gIiRDT05ORklMRSIKICBjaG1vZCA2MDAgIiRDT05ORklMRSIKICBjaG93biByb290OnJvb3QgIiRDT05ORklMRSIKICBlY2hvICJwZXJzaXN0ZW50ZXMgUHJvZmlsIGdlc2NocmllYmVuLiIKfSA+PiAvdmFyL2xvZy90dXhmbG90dGUtcG9zdGluc3RhbGwubG9nIDI+JjEKZWNobyAiZHZkLXdsYW4tdGVzdCAoQURSLTAwMDksIE1pbnQtRm9ydHNldHp1bmcpIC0tIHByb3Zpc2lvbmVkIGJ5IHR1eGZsb3R0ZSIgPiAvZXRjL21vdGQK | base64 -d | bash'
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
loadfont unicode
|
|
||||||
|
|
||||||
set color_normal=white/black
|
|
||||||
set color_highlight=black/light-gray
|
|
||||||
|
|
||||||
set timeout=30
|
|
||||||
|
|
||||||
menuentry "Start Linux Mint 22.3 Cinnamon 64-bit" --class linuxmint {
|
|
||||||
set gfxpayload=keep
|
|
||||||
linux /casper/vmlinuz boot=casper uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint iso-scan/filename=${iso_path} quiet splash --
|
|
||||||
initrd /casper/initrd.lz
|
|
||||||
}
|
|
||||||
menuentry "Start Linux Mint 22.3 Cinnamon 64-bit (compatibility mode)" {
|
|
||||||
linux /casper/vmlinuz boot=casper uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint iso-scan/filename=${iso_path} noapic noacpi nosplash irqpoll nomodeset --
|
|
||||||
initrd /casper/initrd.lz
|
|
||||||
}
|
|
||||||
menuentry "OEM install (for manufacturers)" {
|
|
||||||
set gfxpayload=keep
|
|
||||||
linux /casper/vmlinuz oem-config/enable=true only-ubiquity boot=casper uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint iso-scan/filename=${iso_path} quiet splash --
|
|
||||||
initrd /casper/initrd.lz
|
|
||||||
}
|
|
||||||
menuentry "Tuxflotte Auto-Install (Linux Mint 22.3 Cinnamon)" --class linuxmint {
|
|
||||||
set gfxpayload=keep
|
|
||||||
linux /casper/vmlinuz boot=casper uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint iso-scan/filename=${iso_path} file=/cdrom/preseed/tuxflotte.seed automatic-ubiquity noprompt priority=critical debian-installer/language=de keyboard-configuration/layoutcode=de quiet splash --
|
|
||||||
initrd /casper/initrd.lz
|
|
||||||
}
|
|
||||||
grub_platform
|
|
||||||
if [ "$grub_platform" = "efi" ]; then
|
|
||||||
menuentry 'Von lokaler Festplatte booten (Standard)' {
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
set default="Von lokaler Festplatte booten (Standard)"
|
|
||||||
menuentry 'UEFI Firmware Settings' {
|
|
||||||
fwsetup
|
|
||||||
}
|
|
||||||
menuentry 'Memory test' {
|
|
||||||
linux /boot/memtest.efi
|
|
||||||
}
|
|
||||||
else
|
|
||||||
menuentry 'Von lokaler Festplatte booten (Standard)' {
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
set default="Von lokaler Festplatte booten (Standard)"
|
|
||||||
fi
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
timeout 100
|
|
||||||
|
|
||||||
menu background splash.png
|
|
||||||
menu title Welcome to Linux Mint 22.3 64-bit
|
|
||||||
|
|
||||||
menu color screen 37;40 #80ffffff #00000000 std
|
|
||||||
MENU COLOR border 30;44 #40ffffff #a0000000 std
|
|
||||||
MENU COLOR title 1;36;44 #ffffffff #a0000000 std
|
|
||||||
MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all
|
|
||||||
MENU COLOR unsel 37;44 #50ffffff #a0000000 std
|
|
||||||
MENU COLOR help 37;40 #c0ffffff #a0000000 std
|
|
||||||
MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std
|
|
||||||
MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std
|
|
||||||
MENU COLOR msg07 37;40 #90ffffff #a0000000 std
|
|
||||||
MENU COLOR tabmsg 31;40 #ffDEDEDE #00000000 std
|
|
||||||
MENU WIDTH 78
|
|
||||||
MENU MARGIN 15
|
|
||||||
MENU ROWS 6
|
|
||||||
MENU VSHIFT 10
|
|
||||||
MENU TABMSGROW 12
|
|
||||||
MENU CMDLINEROW 12
|
|
||||||
MENU HELPMSGROW 16
|
|
||||||
MENU HELPMSGENDROW 29
|
|
||||||
|
|
||||||
label tuxflotte
|
|
||||||
menu label Tuxflotte Auto-Install
|
|
||||||
kernel /casper/vmlinuz
|
|
||||||
append boot=casper initrd=/casper/initrd.lz uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint file=/cdrom/preseed/tuxflotte.seed automatic-ubiquity noprompt priority=critical debian-installer/language=de keyboard-configuration/layoutcode=de quiet splash --
|
|
||||||
|
|
||||||
label live
|
|
||||||
menu label Start Linux Mint
|
|
||||||
kernel /casper/vmlinuz
|
|
||||||
append boot=casper initrd=/casper/initrd.lz uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint quiet splash --
|
|
||||||
|
|
||||||
label compat
|
|
||||||
menu label Start Linux Mint in compatibility mode
|
|
||||||
linux /casper/vmlinuz
|
|
||||||
append boot=casper initrd=/casper/initrd.lz uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint noapic noacpi nosplash irqpoll nomodeset --
|
|
||||||
|
|
||||||
label oem
|
|
||||||
menu label OEM install (for manufacturers)
|
|
||||||
linux /casper/vmlinuz
|
|
||||||
append oem-config/enable=true only-ubiquity boot=casper initrd=/casper/initrd.lz uuid=6e72f523-dc09-4880-8910-93ffa64401c5 username=mint hostname=mint quiet splash --
|
|
||||||
|
|
||||||
label hdt
|
|
||||||
menu label Hardware Detection
|
|
||||||
kernel hdt.c32
|
|
||||||
|
|
||||||
label local
|
|
||||||
menu label Boot from local drive
|
|
||||||
menu default
|
|
||||||
COM32 chain.c32
|
|
||||||
APPEND hd0
|
|
||||||
|
|
||||||
label memtest
|
|
||||||
menu label Memory test
|
|
||||||
linux /boot/memtest.bin
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
#! /bin/sh
|
|
||||||
|
|
||||||
PREREQ=""
|
|
||||||
|
|
||||||
prereqs()
|
|
||||||
{
|
|
||||||
echo "$PREREQ"
|
|
||||||
}
|
|
||||||
|
|
||||||
case $1 in
|
|
||||||
# get pre-requisites
|
|
||||||
prereqs)
|
|
||||||
prereqs
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Ueberschreibt das originale 99casperboot (macht nur `touch
|
|
||||||
# /run/.casper-boot`) statt einen zusaetzlichen, neuen Hook-Skriptnamen
|
|
||||||
# einzufuehren: initramfs-tools generiert beim ISO-Bau eine ORDER-Datei
|
|
||||||
# (scripts/casper-bottom/ORDER) mit der fest verdrahteten Aufruf-Reihenfolge
|
|
||||||
# aller casper-bottom-Skripte - real durch Extraktion des echten initrd.lz
|
|
||||||
# mit unmkinitramfs bestaetigt. Ein per Initrd-Cpio-Layer NEU hinzugefuegtes
|
|
||||||
# Skript taucht in dieser ORDER-Datei nicht auf und wird nie aufgerufen,
|
|
||||||
# selbst wenn die Datei im initramfs physisch vorhanden ist (erste Version
|
|
||||||
# dieses Fixes ist daran gescheitert). Das Ueberschreiben eines bereits in
|
|
||||||
# ORDER referenzierten Pfads funktioniert dagegen zuverlaessig (spaetere
|
|
||||||
# cpio-Schichten ueberschreiben Dateiinhalte am selben Pfad, siehe
|
|
||||||
# scripts/lib/initrd.sh - derselbe Mechanismus, der schon fuer das
|
|
||||||
# Kexec-Preseed in Phase 1 verifiziert wurde). 99casperboot ist laut ORDER
|
|
||||||
# das letzte casper-bottom-Skript ueberhaupt - garantiert nach 05mountpoints,
|
|
||||||
# das /cdrom nach /root/cdrom umhaengt und damit die Voraussetzung fuer den
|
|
||||||
# folgenden Kopiervorgang schafft.
|
|
||||||
if [ -d /root/cdrom/updates ]; then
|
|
||||||
cp -a /root/cdrom/updates/. /root/
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ubiquity stuerzt im automatic-ubiquity-GTK-Modus real reproduzierbar ab
|
|
||||||
# (TypeError: Argument 1 does not allow None as a value), sobald
|
|
||||||
# enable_download_updates(False) den Netzwerkstatus als "nicht verbunden"
|
|
||||||
# meldet: ubi-prepare.py uebergibt das Ergebnis von
|
|
||||||
# self.controller.get_string('ubiquity/text/label_download_updates_na')
|
|
||||||
# ungeprueft an GtkLabel.set_label() - fehlt fuer diese Vorlage/Sprache ein
|
|
||||||
# Uebersetzungsstring, liefert get_string() None statt eines leeren Strings,
|
|
||||||
# und GTK akzeptiert kein None als Label-Text (real ueber QEMU-Testlauf
|
|
||||||
# gefunden, siehe ADR-0023-Nachtrag). Gezielter Sed-Patch statt vollstaendigem
|
|
||||||
# Datei-Ersatz, um nicht die komplette Drittanbieter-Datei mitpflegen zu
|
|
||||||
# muessen - betrifft nur die eine Zeile, "or ''" faengt jeden None-Rueckgabewert
|
|
||||||
# ab, unabhaengig von der genauen Ursache der fehlenden Uebersetzung.
|
|
||||||
ubi_prepare="/root/usr/lib/ubiquity/plugins/ubi-prepare.py"
|
|
||||||
if [ -f "$ubi_prepare" ]; then
|
|
||||||
sed -i \
|
|
||||||
"s/self\.controller\.get_string(template))/self.controller.get_string(template) or '')/" \
|
|
||||||
"$ubi_prepare"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# AT-SPI-Bruecke fuer den Autoklicker (atspi_click.py) muss VOR dem
|
|
||||||
# GTK-Start von Ubiquity aktiv sein - siehe live-updates/usr/share/ubiquity/
|
|
||||||
# tuxflotte-atspi-env.sh (dorthin per casper-bottom-Kopie oben bereits nach
|
|
||||||
# /root/usr/share/ubiquity/ ausgeliefert). Einzeilige Einbindung direkt nach
|
|
||||||
# dem fruehen Existenz-Check von ubiquity-dm - alles Weitere, inkl.
|
|
||||||
# Paketinstallation, uebernimmt die eingebundene Datei selbst.
|
|
||||||
start_ubiquity_dm="/root/usr/share/ubiquity/start-ubiquity-dm"
|
|
||||||
if [ -f "$start_ubiquity_dm" ]; then
|
|
||||||
sed -i \
|
|
||||||
'/^test -x \/usr\/bin\/ubiquity-dm || exit 0$/a\
|
|
||||||
. /usr/share/ubiquity/tuxflotte-atspi-env.sh' \
|
|
||||||
"$start_ubiquity_dm"
|
|
||||||
fi
|
|
||||||
|
|
||||||
touch /run/.casper-boot
|
|
||||||
@ -1 +0,0 @@
|
|||||||
mint ALL=(ALL) NOPASSWD: ALL
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../tuxflotte-autoclicker.service
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Tuxflotte Autoklicker fuer Ubiquitys GTK-Installationsassistenten
|
|
||||||
# Nur bei automatisierter Installation relevant (siehe autoclicker.sh -
|
|
||||||
# prueft selbst auf das Boot-Keyword "tuxflotte-autoclick" und beendet sich
|
|
||||||
# sofort, wenn es fehlt). Bewusst kein "After=graphical.target" o.ae., da
|
|
||||||
# ubiquity-dm keine normale Display-Manager-Sitzung startet, deren
|
|
||||||
# Bereitschaft systemd auf ueblichem Weg erkennen wuerde - das Skript selbst
|
|
||||||
# wartet auf den X-Socket.
|
|
||||||
After=multi-user.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
ExecStart=/opt/tuxflotte/scripts/autoclicker.sh
|
|
||||||
Restart=no
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Name=SELinux Troubleshooter Notifier
|
|
||||||
Hidden=true
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Name=Tuxflotte Provisioning
|
|
||||||
Exec=/opt/tuxflotte/kiosk/start-kiosk.sh
|
|
||||||
X-GNOME-Autostart-enabled=true
|
|
||||||
NoDisplay=true
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Tuxflotte Provisioning</title>
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: #ffffff;
|
|
||||||
color: #3465a4;
|
|
||||||
font-family: sans-serif;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 60%;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<img src="logo.svg" alt="Tuxflotte">
|
|
||||||
<p>Kiosk-Oberfläche in Entwicklung.</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 77 KiB |
@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# German keyboard layout for the graphical session (vconsole.keymap=de on the
|
|
||||||
# kernel cmdline only covers the text console, not X11/Wayland input). Cinnamon
|
|
||||||
# ships its own parallel org.cinnamon.desktop.input-sources schema, separate
|
|
||||||
# from GNOME's org.gnome.desktop.input-sources — writing org.gnome alone had
|
|
||||||
# no effect (confirmed on real hardware), so both are set here.
|
|
||||||
dconf write /org/cinnamon/desktop/input-sources/sources "[('xkb', 'de')]"
|
|
||||||
dconf write /org/gnome/desktop/input-sources/sources "[('xkb', 'de')]"
|
|
||||||
|
|
||||||
# Suppresses Epiphany's "set as default browser?" first-run prompt (schema
|
|
||||||
# default is true, see /usr/share/glib-2.0/schemas/org.gnome.epiphany.gschema.xml).
|
|
||||||
dconf write /org/gnome/epiphany/ask-for-default false
|
|
||||||
|
|
||||||
# Kiosk cleanup: no desktop icons (Nemo). The panel itself is kept (removing
|
|
||||||
# the last panel via panels-enabled triggers Cinnamon's own "you don't have
|
|
||||||
# any panels, open panel settings?" confirmation dialog — hardcoded in
|
|
||||||
# js/ui/panel.js, no way to suppress it, so panels-enabled is deliberately
|
|
||||||
# left untouched). Instead, only the two useful applets are kept: a window
|
|
||||||
# list (to restore Epiphany if a user minimizes it) and power (shutdown/
|
|
||||||
# reboot) — everything else (menu, systray, clock, workspace switcher etc.)
|
|
||||||
# is removed. Deliberately the classic window-list@cinnamon.org, not
|
|
||||||
# grouped-window-list@cinnamon.org: the grouped variant ships default
|
|
||||||
# "pinned apps" launchers (firefox.desktop, org.gnome.Terminal.desktop,
|
|
||||||
# nemo.desktop — confirmed via its settings-schema.json) that showed up in
|
|
||||||
# the panel even with unrelated applets removed. Plain window-list has no
|
|
||||||
# pinned-apps concept at all, only ever shows actually open windows.
|
|
||||||
dconf write /org/nemo/desktop/show-desktop-icons false
|
|
||||||
dconf write /org/cinnamon/enabled-applets "['panel1:left:0:window-list@cinnamon.org:1', 'panel1:right:0:power@cinnamon.org:2']"
|
|
||||||
|
|
||||||
mkdir -p "${HOME}/.local/share/tuxflotte-kiosk"
|
|
||||||
|
|
||||||
# Auto-Modus (personalisierte Kunden-ISO, siehe scripts/build_customer_iso.sh)
|
|
||||||
# startet den Installer sofort unbeaufsichtigt statt der Kiosk-Startseite -
|
|
||||||
# config/installer.conf wird nur bei personalisierten Abbildern mitgeliefert,
|
|
||||||
# das Standard-/Testabbild bleibt unverändert interaktiv.
|
|
||||||
if grep -qE '^\s*(export\s+)?TUXFLOTTE_AUTO_MODE=true\s*$' /opt/tuxflotte/config/installer.conf 2>/dev/null; then
|
|
||||||
exec sudo /opt/tuxflotte/scripts/installer.sh
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec epiphany --profile="${HOME}/.local/share/tuxflotte-kiosk" file:///opt/tuxflotte/kiosk/index.html
|
|
||||||
@ -1,308 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Klickt den primaeren "Weiter"-Button der aktuell sichtbaren Ubiquity-Seite
|
|
||||||
per AT-SPI (Linux-Barrierefreiheits-API) - wird von autoclicker.sh wiederholt
|
|
||||||
aufgerufen (ein Prozessstart pro Klickversuch, absichtlich einfach statt
|
|
||||||
dauerhaft im Hintergrund am Accessibility-Bus zu lauschen).
|
|
||||||
|
|
||||||
Warum AT-SPI statt Bildschirmkoordinaten: der urspruengliche xdotool-Ansatz
|
|
||||||
scheiterte daran, dass Metacity mehrere ueberlappende X11-Fenster aehnlicher
|
|
||||||
Groesse erzeugt (WM-Dekorationsrahmen, Hilfsfenster) - eine "groesstes
|
|
||||||
sichtbares Fenster"-Heuristik traf wiederholt das falsche Fenster (siehe
|
|
||||||
ADR-0023-Nachtrag). AT-SPI identifiziert den Button stattdessen ueber seine
|
|
||||||
Rolle (PUSH_BUTTON) und seinen Namen im Accessibility-Baum der Anwendung
|
|
||||||
selbst - unabhaengig von Fenstergeometrie/-dekoration.
|
|
||||||
|
|
||||||
WICHTIG (real beim Testen entdeckt, 29.08.2026): trotz fest gesetztem
|
|
||||||
"debian-installer/language=de" sind NICHT alle Assistentenseiten auf
|
|
||||||
Deutsch beschriftet - die Mint-eigene "Multimedia-Codecs"-Seite zeigt reale
|
|
||||||
englische Buttons ("Quit"/"Back"/"Continue"), vermutlich dieselbe Ursache
|
|
||||||
wie der bereits gepatchte ubi-prepare.py-None-Bug (fehlender/unvollstaendiger
|
|
||||||
.mo-Uebersetzungskatalog fuer dieses Plugin auf dem Live-Medium). Eine reine
|
|
||||||
Werteliste bekannter deutscher Beschriftungen (fruehere Version dieses
|
|
||||||
Skripts) haette hier fuer immer haengen bleiben. Die Logik wurde deshalb auf
|
|
||||||
eine sprachunabhaengige Ausschluss-Strategie umgestellt: bekannte
|
|
||||||
rueckwaerts-/abbrechende Beschriftungen (zweisprachig) werden aus der
|
|
||||||
Kandidatenliste entfernt, der bzw. die verbleibenden Kandidaten werden
|
|
||||||
geklickt - siehe select_target() fuer die genaue Regel samt
|
|
||||||
Sicherheitsverhalten bei Mehrdeutigkeit.
|
|
||||||
|
|
||||||
Sprache ist durch "debian-installer/language=de" im Kexec-Append-Preseed
|
|
||||||
grundsaetzlich auf de_DE gesetzt (siehe backend.sh), s.o. aber nicht
|
|
||||||
verlaesslich fuer jede einzelne Seite.
|
|
||||||
|
|
||||||
NACHTRAG (real beim Testen entdeckt, 29.08.2026): auf der manuellen
|
|
||||||
Partitionierungsseite ("Etwas Anderes") lief der Klick auf "Jetzt
|
|
||||||
installieren" nachweislich zuverlaessig (AT-SPI meldete jedes Mal Erfolg),
|
|
||||||
aber die Seite kam nie voran - stattdessen loeste jeder Klick erneut die
|
|
||||||
Debconf-Frage "ubiquity/partman-rebuild-cache" aus
|
|
||||||
(/lib/partman/update.d/99signal_ubiquity), was ubi-partman.py zurueck auf
|
|
||||||
dieselbe Seite wirft (siehe ADR-0023-Nachtrag). Ein gezielter Fix am
|
|
||||||
Partitionsrezept (kein zusaetzliches $bootable{ } auf der Root-Partition
|
|
||||||
unter UEFI) aenderte NICHTS - das deutet auf einen zeitlichen statt
|
|
||||||
strukturellen Fehler hin: die Seite zeigt waehrend dieser Phase ein
|
|
||||||
Status-Panel "Konfiguration der Installation wird ueberprueft ..." - klickt
|
|
||||||
der Autoklicker WAEHREND dieser Hintergrundpruefung noch laeuft, koennte
|
|
||||||
er sie unterbrechen/neu anstossen, statt ihr Zeit zum Abschliessen zu
|
|
||||||
geben. Dieses Skript wartet deshalb jetzt aktiv, solange dieser Text
|
|
||||||
sichtbar ist (siehe VALIDATION_TEXT_MARKERS/find_showing_text()), und
|
|
||||||
klickt erst, wenn er verschwunden ist.
|
|
||||||
|
|
||||||
Exit-Codes: 0 = ein Button wurde gefunden und geklickt,
|
|
||||||
1 = kein eindeutiger Button gefunden (z.B. Seite im Ladezustand, oder echte
|
|
||||||
Mehrdeutigkeit - dann bewusst NICHT geklickt, siehe select_target()),
|
|
||||||
2 = pyatspi/AT-SPI-Bus nicht erreichbar (Bruecke evtl. nicht aktiv),
|
|
||||||
3 = Ubiquitys eigene Hintergrundpruefung laeuft noch sichtbar - bewusst
|
|
||||||
nicht geklickt, siehe Nachtrag oben.
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
|
|
||||||
try:
|
|
||||||
import pyatspi
|
|
||||||
except ImportError:
|
|
||||||
print("pyatspi nicht verfuegbar", file=sys.stderr)
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
# Zweisprachig, da nicht jede Seite verlaesslich deutsch beschriftet ist
|
|
||||||
# (siehe Modul-Docstring). Diese Namen werden aus der Kandidatenliste
|
|
||||||
# AUSGESCHLOSSEN, nie geklickt - unabhaengig davon, ob sie der einzige
|
|
||||||
# sichtbare Button waeren.
|
|
||||||
EXCLUDE_LABELS = {
|
|
||||||
"zurück",
|
|
||||||
"zurueck",
|
|
||||||
"back",
|
|
||||||
"abbrechen",
|
|
||||||
"abbruch",
|
|
||||||
"cancel",
|
|
||||||
"beenden",
|
|
||||||
"quit",
|
|
||||||
"exit",
|
|
||||||
"verwerfen",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Bevorzugt geklickt, falls mehrere Kandidaten nach dem Ausschluss uebrig
|
|
||||||
# bleiben (siehe select_target()). Kein Ausschlusskriterium fuer
|
|
||||||
# Kandidaten, die HIER nicht drin stehen - die duerfen trotzdem als
|
|
||||||
# alleiniger Kandidat geklickt werden (Sole-Survivor-Regel), damit auch
|
|
||||||
# unbekannte/nicht uebersetzte Beschriftungen wie das reale "Continue"
|
|
||||||
# oben nicht zum Stillstand fuehren.
|
|
||||||
PREFER_LABELS = {
|
|
||||||
"weiter",
|
|
||||||
"continue",
|
|
||||||
"next",
|
|
||||||
"jetzt installieren",
|
|
||||||
"install now",
|
|
||||||
"fortfahren",
|
|
||||||
"proceed",
|
|
||||||
"installieren",
|
|
||||||
"install",
|
|
||||||
"neu starten",
|
|
||||||
"restart now",
|
|
||||||
"schließen",
|
|
||||||
"schliessen",
|
|
||||||
"close",
|
|
||||||
"finish",
|
|
||||||
"fertigstellen",
|
|
||||||
"done",
|
|
||||||
}
|
|
||||||
|
|
||||||
MAX_DEPTH = 40
|
|
||||||
|
|
||||||
# Zweisprachig vorsichtshalber (siehe Multimedia-Codecs-Erfahrung oben),
|
|
||||||
# auch wenn dieser konkrete Text bisher immer deutsch beobachtet wurde.
|
|
||||||
VALIDATION_TEXT_MARKERS = (
|
|
||||||
"wird überprüft",
|
|
||||||
"wird ueberprueft",
|
|
||||||
"being checked",
|
|
||||||
"checking the install",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _node_text(node):
|
|
||||||
"""Sammelt allen aus einem Accessible auslesbaren Text: .name,
|
|
||||||
.description UND (falls unterstuetzt) das separate Text-Interface.
|
|
||||||
WICHTIG (real beim Testen entdeckt, 29.08.2026): ein einfaches GtkLabel
|
|
||||||
exponiert seinen sichtbaren Text ueblicherweise NICHT ueber .name/
|
|
||||||
.description (die bleiben dort oft leer) sondern ausschliesslich ueber
|
|
||||||
das AT-SPI-Text-Interface (queryText().getText(0, -1)) - ohne diesen
|
|
||||||
dritten Zugriffsweg fand find_showing_text() den sichtbar angezeigten
|
|
||||||
Status-Text nie, obwohl er laut Screenshot die ganze Zeit auf dem Schirm
|
|
||||||
stand."""
|
|
||||||
parts = []
|
|
||||||
try:
|
|
||||||
if node.name:
|
|
||||||
parts.append(node.name)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
if node.description:
|
|
||||||
parts.append(node.description)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
text_iface = node.queryText()
|
|
||||||
content = text_iface.getText(0, -1)
|
|
||||||
if content:
|
|
||||||
parts.append(content)
|
|
||||||
except NotImplementedError:
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return " ".join(parts).strip().lower()
|
|
||||||
|
|
||||||
|
|
||||||
def find_showing_text(node, needle, depth=0):
|
|
||||||
"""Sucht ein sichtbares Accessible, dessen Text (siehe _node_text()) den
|
|
||||||
Suchtext enthaelt - unabhaengig von seiner Rolle (Label/Expander/...),
|
|
||||||
da Ubiquitys Status-Panel-Text nicht zuverlaessig unter derselben Rolle
|
|
||||||
exponiert sein muss. Bei Unsicherheit ueber den Sichtbarkeitsstatus
|
|
||||||
bewusst konservativ (lieber einen Zyklus zu viel warten als einen
|
|
||||||
Klick mitten in die Pruefung setzen)."""
|
|
||||||
if node is None or depth > MAX_DEPTH:
|
|
||||||
return False
|
|
||||||
if needle in _node_text(node):
|
|
||||||
try:
|
|
||||||
state = node.getState()
|
|
||||||
return state.contains(pyatspi.STATE_SHOWING)
|
|
||||||
except Exception:
|
|
||||||
return True
|
|
||||||
try:
|
|
||||||
count = node.childCount
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
for i in range(count):
|
|
||||||
try:
|
|
||||||
child = node.getChildAtIndex(i)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
if find_showing_text(child, needle, depth + 1):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def collect_buttons(node, depth=0, acc=None):
|
|
||||||
"""Alle sichtbaren+aktivierten Push-Buttons im Baum einsammeln (Rolle,
|
|
||||||
Zustand). Absichtlich ALLE Kandidaten sammeln statt beim ersten Treffer
|
|
||||||
abzubrechen - select_target() braucht die vollstaendige Liste, um
|
|
||||||
Mehrdeutigkeit erkennen zu koennen."""
|
|
||||||
if acc is None:
|
|
||||||
acc = []
|
|
||||||
if node is None or depth > MAX_DEPTH:
|
|
||||||
return acc
|
|
||||||
try:
|
|
||||||
role = node.getRole()
|
|
||||||
except Exception:
|
|
||||||
return acc
|
|
||||||
|
|
||||||
if role == pyatspi.ROLE_PUSH_BUTTON:
|
|
||||||
try:
|
|
||||||
state = node.getState()
|
|
||||||
if state.contains(pyatspi.STATE_SENSITIVE) and state.contains(
|
|
||||||
pyatspi.STATE_SHOWING
|
|
||||||
):
|
|
||||||
acc.append(node)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return acc
|
|
||||||
|
|
||||||
try:
|
|
||||||
count = node.childCount
|
|
||||||
except Exception:
|
|
||||||
return acc
|
|
||||||
for i in range(count):
|
|
||||||
try:
|
|
||||||
child = node.getChildAtIndex(i)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
collect_buttons(child, depth + 1, acc)
|
|
||||||
return acc
|
|
||||||
|
|
||||||
|
|
||||||
def select_target(buttons):
|
|
||||||
"""Waehlt aus allen sichtbaren+aktivierten Buttons genau einen aus, der
|
|
||||||
geklickt werden darf - oder None, wenn das nicht sicher moeglich ist.
|
|
||||||
|
|
||||||
Regel (bewusst sicherheitsorientiert, siehe Modul-Docstring):
|
|
||||||
1. Bekannte rueckwaerts-/abbrechende Beschriftungen (EXCLUDE_LABELS)
|
|
||||||
werden nie geklickt, unabhaengig vom Rest.
|
|
||||||
2. Von den verbleibenden Kandidaten wird bevorzugt, wer eine bekannte
|
|
||||||
vorwaerts-/abschliessende Beschriftung traegt (PREFER_LABELS).
|
|
||||||
3. Bleibt nach 1./2. KEIN bekannter Treffer, aber genau EIN Kandidat
|
|
||||||
uebrig, wird dieser geklickt (Sole-Survivor-Regel - deckt unbekannte/
|
|
||||||
nicht uebersetzte Beschriftungen wie das real beobachtete "Continue"
|
|
||||||
ab, siehe Modul-Docstring).
|
|
||||||
4. Bleiben mehrere unbekannte Kandidaten gleichzeitig uebrig, wird NICHT
|
|
||||||
geklickt (echte Mehrdeutigkeit - ein Fehlklick waere hier riskanter
|
|
||||||
als ein uebersprungener Zyklus, die Schleife in autoclicker.sh
|
|
||||||
versucht es 2 Sekunden spaeter erneut)."""
|
|
||||||
candidates = []
|
|
||||||
for btn in buttons:
|
|
||||||
name = (btn.name or "").strip().lower()
|
|
||||||
if name in EXCLUDE_LABELS:
|
|
||||||
continue
|
|
||||||
candidates.append(btn)
|
|
||||||
|
|
||||||
if not candidates:
|
|
||||||
return None
|
|
||||||
|
|
||||||
preferred = [b for b in candidates if (b.name or "").strip().lower() in PREFER_LABELS]
|
|
||||||
if preferred:
|
|
||||||
return preferred[0]
|
|
||||||
|
|
||||||
if len(candidates) == 1:
|
|
||||||
return candidates[0]
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
try:
|
|
||||||
desktop = pyatspi.Registry.getDesktop(0)
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"AT-SPI-Desktop nicht erreichbar: {exc}", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
|
|
||||||
apps = []
|
|
||||||
for i in range(desktop.childCount):
|
|
||||||
try:
|
|
||||||
app = desktop.getChildAtIndex(i)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
if app is not None:
|
|
||||||
apps.append(app)
|
|
||||||
|
|
||||||
for marker in VALIDATION_TEXT_MARKERS:
|
|
||||||
for app in apps:
|
|
||||||
if find_showing_text(app, marker):
|
|
||||||
print(
|
|
||||||
f"Hintergrundpruefung noch sichtbar ('{marker}'), warte statt zu klicken.",
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
return 3
|
|
||||||
|
|
||||||
all_buttons = []
|
|
||||||
for app in apps:
|
|
||||||
collect_buttons(app, 0, all_buttons)
|
|
||||||
|
|
||||||
target = select_target(all_buttons)
|
|
||||||
if target is None:
|
|
||||||
names = [repr((b.name or "").strip()) for b in all_buttons]
|
|
||||||
print(
|
|
||||||
"kein eindeutiger Button gefunden, sichtbare Buttons: "
|
|
||||||
+ (", ".join(names) if names else "(keine)"),
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
try:
|
|
||||||
action = target.queryAction()
|
|
||||||
action.doAction(0)
|
|
||||||
except Exception as exc:
|
|
||||||
print(f"Klick auf '{target.name}' fehlgeschlagen: {exc}", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
print(f"geklickt: {target.name}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
### Klickt automatisiert durch Ubiquitys GTK-Installationsassistenten
|
|
||||||
### (automatic-ubiquity) - noetig, weil Ubiquitys eigenes "noninteractive"-
|
|
||||||
### Frontend die gefuehrte Partitionierung nicht zuverlaessig automatisieren
|
|
||||||
### kann (endlose choose_partition-Schleife in ubi-partman.py, real
|
|
||||||
### bestaetigt, siehe ADR-0023-Nachtrag). Der GTK-Assistent fuellt jede Seite
|
|
||||||
### bereits vollstaendig aus dem Preseed vor (siehe preseed.tpl) - es fehlt
|
|
||||||
### nur der "Weiter"/"Installieren"-Klick pro Seite.
|
|
||||||
###
|
|
||||||
### Klickt ueber AT-SPI (Barrierefreiheits-API), nicht mehr ueber
|
|
||||||
### Bildschirmkoordinaten/Enter-Taste (siehe atspi_click.py fuer die
|
|
||||||
### eigentliche Klicklogik). Der fruehere xdotool-Ansatz scheiterte real
|
|
||||||
### daran, dass Metacity mehrere ueberlappende X11-Fenster aehnlicher
|
|
||||||
### Groesse erzeugt (WM-Dekorationsrahmen, Hilfsfenster) - weder eine
|
|
||||||
### "groesstes sichtbares Fenster"-Heuristik noch die Enter-Taste (aktiviert
|
|
||||||
### auf der Partitionierungs-Uebersichtsseite einen Ausklapp-Pfeil statt des
|
|
||||||
### "Jetzt installieren"-Buttons) waren zuverlaessig genug (siehe
|
|
||||||
### ADR-0023-Nachtrag). AT-SPI identifiziert den Button stattdessen ueber
|
|
||||||
### seine Rolle+seinen Namen im Accessibility-Baum der Anwendung selbst -
|
|
||||||
### unabhaengig von Fenstergeometrie/-dekoration.
|
|
||||||
###
|
|
||||||
### Die AT-SPI-Bruecke selbst (GTK_MODULES=gail:atk-bridge, Session-/A11y-
|
|
||||||
### Bus) wird NICHT hier, sondern bereits VOR dem GTK-Start von Ubiquity in
|
|
||||||
### tuxflotte-atspi-env.sh aktiviert (eingebunden von start-ubiquity-dm,
|
|
||||||
### siehe Sed-Patch in initrd-hooks/casper-bottom/99casperboot) - eine erst
|
|
||||||
### nachtraeglich aktivierte Bruecke wird von einer bereits laufenden
|
|
||||||
### GTK-Anwendung nicht mehr nachgeladen.
|
|
||||||
###
|
|
||||||
### Wird per systemd-Service (siehe ...service im selben Ausliefer-Layer) bei
|
|
||||||
### JEDEM Boot gestartet, bricht aber sofort ab, wenn das eigene Boot-Keyword
|
|
||||||
### "tuxflotte-autoclick" nicht in /proc/cmdline steht - siehe backend_launch()
|
|
||||||
### in backends/mint/backend.sh, wo dieses Keyword gesetzt wird. Auf einem
|
|
||||||
### normalen (nicht automatisierten) Boot also wirkungslos.
|
|
||||||
set -u
|
|
||||||
|
|
||||||
LOG=/var/log/tuxflotte-autoclicker.log
|
|
||||||
log() {
|
|
||||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >>"${LOG}"
|
|
||||||
}
|
|
||||||
|
|
||||||
grep -qw tuxflotte-autoclick /proc/cmdline || exit 0
|
|
||||||
|
|
||||||
log "tuxflotte-autoclick erkannt, starte Autoklicker."
|
|
||||||
export DISPLAY=:0
|
|
||||||
|
|
||||||
is_ubiquity_running() {
|
|
||||||
pgrep -f '/usr/bin/ubiquity' >/dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
# ubiquity-dm startet Ubiquity frueh im Boot zu einem nicht exakt
|
|
||||||
# vorhersagbaren Zeitpunkt - auf den Prozess warten statt auf eine feste
|
|
||||||
# Wartezeit zu vertrauen. AT-SPI braucht anders als der fruehere
|
|
||||||
# xdotool-Ansatz keinen eigenen X-Socket-Check - der Zugriff laeuft ueber
|
|
||||||
# den Accessibility-Bus, nicht direkt ueber X11.
|
|
||||||
ubiquity_ready=0
|
|
||||||
for _ in $(seq 1 120); do
|
|
||||||
if is_ubiquity_running; then
|
|
||||||
ubiquity_ready=1
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
if [ "${ubiquity_ready}" -ne 1 ]; then
|
|
||||||
log "ubiquity-Prozess nach 120s nicht erschienen, breche ab."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
log "ubiquity-Prozess erkannt."
|
|
||||||
|
|
||||||
if ! python3 -c 'import pyatspi' >/dev/null 2>&1; then
|
|
||||||
log "pyatspi fehlt auf dem Live-Medium, installiere nach."
|
|
||||||
if ! apt-get install -y python3-pyatspi >>"${LOG}" 2>&1; then
|
|
||||||
log "pyatspi-Installation fehlgeschlagen, breche ab."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
ATSPI_CLICK=/opt/tuxflotte/scripts/atspi_click.py
|
|
||||||
|
|
||||||
log "Starte Klick-Schleife."
|
|
||||||
# Harte Obergrenze als Sicherheitsnetz (60 Minuten) - falls die Installation
|
|
||||||
# haengen bleibt, soll dieser Dienst nicht unbegrenzt weiterlaufen.
|
|
||||||
end=$((SECONDS + 3600))
|
|
||||||
clicks=0
|
|
||||||
misses=0
|
|
||||||
while [ "${SECONDS}" -lt "${end}" ]; do
|
|
||||||
if ! is_ubiquity_running; then
|
|
||||||
log "ubiquity-Prozess nicht mehr aktiv (vermutlich fertig oder abgestuerzt), beende Schleife nach ${clicks} Klick(s)."
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
out="$(python3 "${ATSPI_CLICK}" 2>&1)"
|
|
||||||
rc=$?
|
|
||||||
if [ "${rc}" -eq 0 ]; then
|
|
||||||
clicks=$((clicks + 1))
|
|
||||||
misses=0
|
|
||||||
log "Klick ${clicks}: ${out}"
|
|
||||||
elif [ "${rc}" -eq 2 ]; then
|
|
||||||
log "AT-SPI-Bus nicht erreichbar, breche ab: ${out}"
|
|
||||||
break
|
|
||||||
elif [ "${rc}" -eq 3 ]; then
|
|
||||||
# Ubiquitys eigene Hintergrundpruefung laeuft noch sichtbar (siehe
|
|
||||||
# atspi_click.py-Nachtrag) - bewusst NICHT als Fehlversuch zaehlen,
|
|
||||||
# nur abwarten. Real beim Testen entdeckt: ein Klick auf "Jetzt
|
|
||||||
# installieren" WAEHREND dieser Pruefung fuehrte wiederholt in die
|
|
||||||
# partman-rebuild-cache-Schleife statt zum eigentlichen Commit.
|
|
||||||
log "wartet auf Hintergrundpruefung: ${out}"
|
|
||||||
else
|
|
||||||
misses=$((misses + 1))
|
|
||||||
log "kein Treffer (${misses}): ${out}"
|
|
||||||
fi
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
log "Autoklicker beendet (${clicks} Klick(s) insgesamt)."
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
### Wird per Ein-Zeilen-Sed-Patch (siehe initrd-hooks/casper-bottom/99casperboot)
|
|
||||||
### GANZ AM ANFANG von start-ubiquity-dm eingebunden ("."), also BEVOR
|
|
||||||
### ubiquity-dm den X-Server startet und Ubiquity selbst exec't.
|
|
||||||
###
|
|
||||||
### Zweck: die AT-SPI-Barrierefreiheits-Bruecke (atk-bridge) fuer Ubiquitys
|
|
||||||
### GTK-Prozess aktivieren, damit atspi_click.py (siehe
|
|
||||||
### /opt/tuxflotte/scripts/) Buttons ueber ihre Rolle/ihren Namen im
|
|
||||||
### Accessibility-Baum anklicken kann statt ueber Bildschirmkoordinaten.
|
|
||||||
### Der xdotool-Koordinaten-Ansatz ist an ueberlappenden X11-Fenstern durch
|
|
||||||
### die Metacity-Fensterdekoration gescheitert (mehrere Fenster aehnlicher
|
|
||||||
### Groesse, "groesstes Fenster"-Heuristik traf wiederholt das falsche -
|
|
||||||
### siehe ADR-0023-Nachtrag).
|
|
||||||
###
|
|
||||||
### WICHTIG: das muss VOR dem GTK-Init von Ubiquity passieren - eine erst
|
|
||||||
### nachtraeglich verfuegbare Bruecke wird von einer bereits laufenden
|
|
||||||
### GTK-Anwendung nicht mehr nachgeladen (GTK_MODULES wird nur beim Start
|
|
||||||
### ausgewertet). Deshalb blockierend (kein Hintergrund-"&" fuer die
|
|
||||||
### Paketinstallation) und deshalb im Skript, das ubiquity-dm selbst
|
|
||||||
### aufruft, statt im spaeter laufenden Autoklicker-Service.
|
|
||||||
###
|
|
||||||
### Absichtlich EIN gemeinsamer apt-get-Aufruf fuer alle drei Pakete (statt
|
|
||||||
### getrennter Aufrufe je Skript) - der Autoklicker installiert xdotool
|
|
||||||
### zeitlich nah, ein zweiter paralleler apt-get/dpkg-Aufruf haette real
|
|
||||||
### beobachtete Lock-Konflikte riskiert (debconf: "config.dat is locked by
|
|
||||||
### another process").
|
|
||||||
|
|
||||||
for pkg in at-spi2-core dbus-x11 python3-pyatspi; do
|
|
||||||
dpkg -s "$pkg" >/dev/null 2>&1 && continue
|
|
||||||
if [ "${_tuxflotte_atspi_apt_updated:-}" != "1" ]; then
|
|
||||||
apt-get update -qq
|
|
||||||
_tuxflotte_atspi_apt_updated=1
|
|
||||||
fi
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y "$pkg"
|
|
||||||
done
|
|
||||||
|
|
||||||
export GTK_MODULES="gail:atk-bridge"
|
|
||||||
export NO_AT_BRIDGE=0
|
|
||||||
|
|
||||||
# Eigene Session-Bus-Instanz, falls keine geerbt wurde - die minimale
|
|
||||||
# ubiquity-dm-X-Sitzung startet anders als eine normale Desktop-Sitzung
|
|
||||||
# vermutlich keinen eigenen Session-Bus.
|
|
||||||
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ] && command -v dbus-launch >/dev/null 2>&1; then
|
|
||||||
eval "$(dbus-launch --sh-syntax)"
|
|
||||||
export DBUS_SESSION_BUS_ADDRESS DBUS_SESSION_BUS_PID
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v at-spi-bus-launcher >/dev/null 2>&1; then
|
|
||||||
at-spi-bus-launcher --launch-immediately >/var/log/tuxflotte-atspi-bus.log 2>&1 &
|
|
||||||
# Kurz abwarten, bis der a11y-Bus unter org.a11y.Bus registriert ist,
|
|
||||||
# bevor ubiquity-dm/Ubiquity (naechste Zeile in start-ubiquity-dm)
|
|
||||||
# GTK initialisiert.
|
|
||||||
sleep 1
|
|
||||||
fi
|
|
||||||
155
scripts/build.sh
155
scripts/build.sh
@ -1,6 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Nur noch fuer das Fedora-Backend (Kickstart/Anaconda) - das Mint-Backend
|
||||||
|
# ist auf das eigenstaendige, per live-build gebaute Boot-Medium umgezogen
|
||||||
|
# (siehe scripts/build_boot_medium.sh, boot-medium/). Der bisherige
|
||||||
|
# Mint-Zweig hier (Casper-Patchen einer echten Mint-Live-ISO + Ubiquity-
|
||||||
|
# Preseed) ist damit Geschichte - siehe ADR-0025 fuer die Begruendung.
|
||||||
|
|
||||||
SOURCE_ISO="${1:-}"
|
SOURCE_ISO="${1:-}"
|
||||||
BACKEND="${2:-}"
|
BACKEND="${2:-}"
|
||||||
|
|
||||||
@ -10,18 +16,8 @@ BUILD_DIR="$BASE_DIR/build"
|
|||||||
WORK_DIR="$BUILD_DIR/work"
|
WORK_DIR="$BUILD_DIR/work"
|
||||||
OUTPUT_DIR="$BUILD_DIR/output"
|
OUTPUT_DIR="$BUILD_DIR/output"
|
||||||
|
|
||||||
# shellcheck source=lib/initrd.sh
|
|
||||||
source "$REPO_DIR/scripts/lib/initrd.sh"
|
|
||||||
|
|
||||||
# Test-Device fuer den in bake_test_preseed() eingebrannten, nicht
|
|
||||||
# personalisierten Test-Preseed (Mint) - dasselbe "Default Lab"-Testgeraet,
|
|
||||||
# das schon fuer die Fedora-Verifikation genutzt wurde. Echte Personalisierung
|
|
||||||
# pro Kunde ist nicht Teil dieses Build-Skripts (siehe ADR-0011-Kontext).
|
|
||||||
TEST_DEVICE_ID="a0238a0b-d2b5-4516-a6ce-da7170041d11"
|
|
||||||
TEST_HOSTNAME="tuxflotte-mint-test"
|
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "Usage: $0 /path/to/source.iso <fedora|mint>"
|
echo "Usage: $0 /path/to/source.iso fedora"
|
||||||
}
|
}
|
||||||
|
|
||||||
check_input() {
|
check_input() {
|
||||||
@ -35,13 +31,10 @@ check_input() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$BACKEND" in
|
if [[ "$BACKEND" != "fedora" ]]; then
|
||||||
fedora|mint) ;;
|
echo "Error: unbekanntes Backend '$BACKEND' (erwartet: fedora)"
|
||||||
*)
|
exit 1
|
||||||
echo "Error: unbekanntes Backend '$BACKEND' (erwartet: fedora oder mint)"
|
fi
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
}
|
||||||
|
|
||||||
check_dependencies() {
|
check_dependencies() {
|
||||||
@ -51,15 +44,6 @@ check_dependencies() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ "$BACKEND" == "mint" ]]; then
|
|
||||||
for cmd in jq envsubst base64 cpio; do
|
|
||||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
||||||
echo "Error: missing dependency: $cmd"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare_dirs() {
|
prepare_dirs() {
|
||||||
@ -87,135 +71,39 @@ patch_grub() {
|
|||||||
# -f: xorriso extrahiert ISO-Dateien mit deren Original-Rechten, die auf
|
# -f: xorriso extrahiert ISO-Dateien mit deren Original-Rechten, die auf
|
||||||
# Live-Medien haeufig read-only (444) sind - ohne -f schlaegt cp beim
|
# Live-Medien haeufig read-only (444) sind - ohne -f schlaegt cp beim
|
||||||
# Ueberschreiben mit "Keine Berechtigung" fehl.
|
# Ueberschreiben mit "Keine Berechtigung" fehl.
|
||||||
if [[ "$BACKEND" == "fedora" ]]; then
|
cp -f "$REPO_DIR/grub/EFI-BOOT-grub.cfg" \
|
||||||
cp -f "$REPO_DIR/grub/EFI-BOOT-grub.cfg" \
|
"$WORK_DIR/EFI/BOOT/grub.cfg"
|
||||||
"$WORK_DIR/EFI/BOOT/grub.cfg"
|
|
||||||
|
|
||||||
cp -f "$REPO_DIR/grub/boot-grub2-grub.cfg" \
|
cp -f "$REPO_DIR/grub/boot-grub2-grub.cfg" \
|
||||||
"$WORK_DIR/boot/grub2/grub.cfg"
|
"$WORK_DIR/boot/grub2/grub.cfg"
|
||||||
else
|
|
||||||
# Mint hat, anders als Fedora, keine zweite ESP/FAT-Kopie des GRUB-Menüs,
|
|
||||||
# die separat gepatcht werden müsste (real gegen die vorhandene
|
|
||||||
# tuxflotte-mint-test.iso verifiziert - x86_64-efi/grub.cfg ist nur ein
|
|
||||||
# Loader-Stub, der per `source /boot/grub/grub.cfg` zurückverweist).
|
|
||||||
cp -f "$REPO_DIR/grub/mint-boot-grub.cfg" \
|
|
||||||
"$WORK_DIR/boot/grub/grub.cfg"
|
|
||||||
|
|
||||||
cp -f "$REPO_DIR/grub/mint-isolinux-live.cfg" \
|
|
||||||
"$WORK_DIR/isolinux/live.cfg"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_workdir() {
|
verify_workdir() {
|
||||||
echo "Verifying workdir..."
|
echo "Verifying workdir..."
|
||||||
|
|
||||||
if [[ "$BACKEND" == "fedora" ]]; then
|
grep -q "Tuxflotte" "$WORK_DIR/EFI/BOOT/grub.cfg"
|
||||||
grep -q "Tuxflotte" "$WORK_DIR/EFI/BOOT/grub.cfg"
|
grep -q "Tuxflotte" "$WORK_DIR/boot/grub2/grub.cfg"
|
||||||
grep -q "Tuxflotte" "$WORK_DIR/boot/grub2/grub.cfg"
|
|
||||||
else
|
|
||||||
grep -q "Tuxflotte" "$WORK_DIR/boot/grub/grub.cfg"
|
|
||||||
grep -q "Tuxflotte" "$WORK_DIR/isolinux/live.cfg"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "GRUB verification passed."
|
echo "GRUB verification passed."
|
||||||
}
|
}
|
||||||
|
|
||||||
bake_test_preseed() {
|
|
||||||
[[ "$BACKEND" == "mint" ]] || return 0
|
|
||||||
|
|
||||||
echo "Rendering test preseed (nicht personalisiert, siehe TEST_DEVICE_ID)..."
|
|
||||||
|
|
||||||
local preseed_dir="$WORK_DIR/preseed"
|
|
||||||
local runtime_blueprint_file="$WORK_DIR/runtime_blueprint.json"
|
|
||||||
local server_response_file="$WORK_DIR/response.json"
|
|
||||||
|
|
||||||
mkdir -p "$preseed_dir"
|
|
||||||
|
|
||||||
cat > "$runtime_blueprint_file" <<EOF
|
|
||||||
{
|
|
||||||
"runtime_blueprint": {
|
|
||||||
"backend_id": "mint",
|
|
||||||
"blueprints": [],
|
|
||||||
"installation_directives": {
|
|
||||||
"disk_encryption": false,
|
|
||||||
"partitioning": {"scheme": "single", "root_filesystem": "ext4"},
|
|
||||||
"secure_boot_required": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat > "$server_response_file" <<EOF
|
|
||||||
{
|
|
||||||
"device": {"hostname": "${TEST_HOSTNAME}", "id": "${TEST_DEVICE_ID}"}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# backend.sh direkt wiederverwenden statt die Templating-Logik hier zu
|
|
||||||
# duplizieren - Variablen sind bewusst nicht readonly (siehe backend.sh),
|
|
||||||
# backend_init() wird übersprungen (braucht root für --owner/--group,
|
|
||||||
# hier nicht nötig, mkdir reicht für den Build-Kontext).
|
|
||||||
# shellcheck disable=SC1091
|
|
||||||
source "$REPO_DIR/backends/mint/backend.sh"
|
|
||||||
|
|
||||||
RUNTIME_BLUEPRINT_FILE="$runtime_blueprint_file"
|
|
||||||
SERVER_RESPONSE_FILE="$server_response_file"
|
|
||||||
RUNTIME_DIR="$preseed_dir"
|
|
||||||
CONFIG_FILE="$preseed_dir/tuxflotte.seed"
|
|
||||||
|
|
||||||
backend_validate
|
|
||||||
backend_generate_config
|
|
||||||
|
|
||||||
echo "Test-Preseed erzeugt: $CONFIG_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
prepare_updates() {
|
prepare_updates() {
|
||||||
echo "Assembling live-updates payload..."
|
echo "Assembling updates payload..."
|
||||||
|
|
||||||
local updates_dir="$WORK_DIR/updates"
|
local updates_dir="$WORK_DIR/updates"
|
||||||
|
|
||||||
rm -rf "$updates_dir"
|
rm -rf "$updates_dir"
|
||||||
mkdir -p "$updates_dir/opt/tuxflotte"
|
mkdir -p "$updates_dir/opt/tuxflotte"
|
||||||
|
|
||||||
cp -a "$REPO_DIR/live-updates/." "$updates_dir/"
|
|
||||||
cp -a "$REPO_DIR/scripts" "$updates_dir/opt/tuxflotte/"
|
cp -a "$REPO_DIR/scripts" "$updates_dir/opt/tuxflotte/"
|
||||||
cp -a "$REPO_DIR/backends" "$updates_dir/opt/tuxflotte/"
|
cp -a "$REPO_DIR/backends" "$updates_dir/opt/tuxflotte/"
|
||||||
cp -a "$REPO_DIR/config" "$updates_dir/opt/tuxflotte/"
|
cp -a "$REPO_DIR/config" "$updates_dir/opt/tuxflotte/"
|
||||||
|
|
||||||
chmod 0440 "$updates_dir/etc/sudoers.d/90-tuxflotte"
|
|
||||||
}
|
|
||||||
|
|
||||||
patch_initrd() {
|
|
||||||
[[ "$BACKEND" == "mint" ]] || return 0
|
|
||||||
|
|
||||||
echo "Injecting casper-bottom updates-hook into initrd..."
|
|
||||||
|
|
||||||
inject_casper_bottom_hooks "$REPO_DIR" \
|
|
||||||
"$WORK_DIR/casper/initrd.lz" \
|
|
||||||
"$WORK_DIR/casper/initrd-tuxflotte.lz"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create_iso() {
|
create_iso() {
|
||||||
echo "Creating Tuxflotte ISO..."
|
echo "Creating Tuxflotte ISO..."
|
||||||
|
|
||||||
local output_iso
|
local output_iso="$OUTPUT_DIR/tuxflotte-provisioning-0.2.iso"
|
||||||
local -a grub_map_args
|
|
||||||
|
|
||||||
if [[ "$BACKEND" == "fedora" ]]; then
|
|
||||||
output_iso="$OUTPUT_DIR/tuxflotte-provisioning-0.2.iso"
|
|
||||||
grub_map_args=(
|
|
||||||
-map "$REPO_DIR/grub/EFI-BOOT-grub.cfg" /EFI/BOOT/grub.cfg
|
|
||||||
-map "$REPO_DIR/grub/boot-grub2-grub.cfg" /boot/grub2/grub.cfg
|
|
||||||
)
|
|
||||||
else
|
|
||||||
output_iso="$OUTPUT_DIR/tuxflotte-mint-provisioning-0.1.iso"
|
|
||||||
grub_map_args=(
|
|
||||||
-map "$REPO_DIR/grub/mint-boot-grub.cfg" /boot/grub/grub.cfg
|
|
||||||
-map "$REPO_DIR/grub/mint-isolinux-live.cfg" /isolinux/live.cfg
|
|
||||||
-map "$WORK_DIR/preseed/tuxflotte.seed" /preseed/tuxflotte.seed
|
|
||||||
-map "$WORK_DIR/casper/initrd-tuxflotte.lz" /casper/initrd.lz
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f "$output_iso"
|
rm -f "$output_iso"
|
||||||
|
|
||||||
@ -224,7 +112,8 @@ create_iso() {
|
|||||||
-outdev "$output_iso" \
|
-outdev "$output_iso" \
|
||||||
-compliance no_emul_toc \
|
-compliance no_emul_toc \
|
||||||
-volid TUXFLOTTE \
|
-volid TUXFLOTTE \
|
||||||
"${grub_map_args[@]}" \
|
-map "$REPO_DIR/grub/EFI-BOOT-grub.cfg" /EFI/BOOT/grub.cfg \
|
||||||
|
-map "$REPO_DIR/grub/boot-grub2-grub.cfg" /boot/grub2/grub.cfg \
|
||||||
-map "$WORK_DIR/updates" /updates \
|
-map "$WORK_DIR/updates" /updates \
|
||||||
-chown_r 0 /updates -- \
|
-chown_r 0 /updates -- \
|
||||||
-chgrp_r 0 /updates -- \
|
-chgrp_r 0 /updates -- \
|
||||||
@ -240,9 +129,7 @@ main() {
|
|||||||
extract_iso
|
extract_iso
|
||||||
patch_grub
|
patch_grub
|
||||||
verify_workdir
|
verify_workdir
|
||||||
bake_test_preseed
|
|
||||||
prepare_updates
|
prepare_updates
|
||||||
patch_initrd
|
|
||||||
create_iso
|
create_iso
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Wird von build.sh und build_customer_iso.sh eingebunden (source), nicht
|
|
||||||
# eigenstaendig ausgefuehrt.
|
|
||||||
|
|
||||||
# Haengt initrd-hooks/casper-bottom/* als zusaetzliche cpio-Schicht an ein
|
|
||||||
# bestehendes Casper-Initrd an - derselbe Mechanismus, den
|
|
||||||
# backend_launch() (backends/mint/backend.sh) bereits fuer das
|
|
||||||
# Kexec-Preseed nutzt und der per QEMU verifiziert ist: der Kernel haengt
|
|
||||||
# spaeter aneinandergereihte cpio-Archive additiv an ein bestehendes
|
|
||||||
# initramfs an. Noetig, weil Casper keinen automatischen /updates-Merge
|
|
||||||
# kennt (real per QEMU-Boot widerlegt) - der Hook selbst kopiert
|
|
||||||
# /cdrom/updates beim Boot auf das Root-Dateisystem, siehe
|
|
||||||
# initrd-hooks/casper-bottom/21tuxflotte_updates.
|
|
||||||
inject_casper_bottom_hooks() {
|
|
||||||
local repo_dir="$1"
|
|
||||||
local src_initrd="$2"
|
|
||||||
local dest_initrd="$3"
|
|
||||||
|
|
||||||
local hook_dir="${repo_dir}/initrd-hooks"
|
|
||||||
local layer_dir
|
|
||||||
layer_dir="$(mktemp -d)"
|
|
||||||
# Ausserhalb von layer_dir, damit die Archiv-Datei sich beim Bauen nicht
|
|
||||||
# selbst als leeren Eintrag einfaengt (find laeuft im selben Moment, in
|
|
||||||
# dem die Shell-Redirection sie bereits anlegt).
|
|
||||||
local cpio_out
|
|
||||||
cpio_out="$(mktemp)"
|
|
||||||
|
|
||||||
cp -a "${hook_dir}/casper-bottom" "${layer_dir}/scripts-casper-bottom-tmp"
|
|
||||||
mkdir -p "${layer_dir}/scripts"
|
|
||||||
mv "${layer_dir}/scripts-casper-bottom-tmp" "${layer_dir}/scripts/casper-bottom"
|
|
||||||
chmod 0755 "${layer_dir}"/scripts/casper-bottom/*
|
|
||||||
|
|
||||||
(cd "${layer_dir}" && find . -mindepth 1 | cpio -o -H newc 2>/dev/null | gzip) \
|
|
||||||
>"${cpio_out}" ||
|
|
||||||
{ echo "Error: Casper-Bottom-Hook-Schicht konnte nicht gebaut werden." >&2; rm -rf "${layer_dir}"; rm -f "${cpio_out}"; return 1; }
|
|
||||||
|
|
||||||
cat "${src_initrd}" "${cpio_out}" >"${dest_initrd}" ||
|
|
||||||
{ echo "Error: Initrd konnte nicht mit Hook-Schicht zusammengesetzt werden." >&2; rm -rf "${layer_dir}"; rm -f "${cpio_out}"; return 1; }
|
|
||||||
|
|
||||||
rm -rf "${layer_dir}"
|
|
||||||
rm -f "${cpio_out}"
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user