Eine kleine Aktualisierung verursacht gerade an einigen Stellen im Raspberry Pi OS 12 Probleme – dabei war sie als Übergangslösung für eine Umstellung gedacht. Vor allem config.txt
und cmdline.txt
sind betroffen. Einige Werkzeuge verwenden sie intern. Leider rechnen nicht alle damit, dass es die alten Pfade nicht mehr geben könnte. Im Raspberry Pi OS 12 aka Bookworm ist aber genau das passiert: /boot
wurde nach /boot/firmware
verschoben. Das irritiert nicht nur einige Nutzer, die sie für erweiterte Funktionen anpassen möchten.
Hier erfährst du, warum derzeit Skripte wie rpi-clone
nicht mehr funktionieren und wir derzeit mit zwei verschiedenen Pfaden arbeiten müssen, um diese beiden Dateien zu bearbeiten. Außerdem demonstriere ich am quelloffenen rpi-clone
Projekt, wie man bei OSS mit wenigen Anpassungen eine Lösung einbauen kann.
Was machen config.txt und cmdline.txt?
Von X86-PCs sind wir ein BIOS gewohnt, das inzwischen nahezu vollständig vom Nachfolger UEFI abgelöst wurde. In beiden Fällen geht es im Kern um eine Firmware, die beim Starten des Computers weitere Software startet. Für gewöhnlich ist das ein Bootloader, der wiederum ein Betriebssystem wie eine GNU/Linux-Distribution oder Microsoft Windows startet. Jedes BIOS/UEFI hat eine Konfigurationsoberfläche, um diverse Einstellungen vornehmen zu können. Dort lassen sich Hardware-Funktionen des Mainboards aktivieren oder deaktivieren. Darunter ist z.B. die Startreihenfolge der Medien, von denen das BIOS/UEFI versuchen soll, Bootloader und Betriebssystem zu starten.
Während das UEFI im X86-Bereich Standardisiert ist, haben wir bei Einplatinencomputern leider ein Chaos mit selbst entwickelter, oft Proprietärer Firmware. Beim Raspberry Pi existiert keine Oberfläche für dessen Einstellungen, wie bei BIOS und UEFI. Stattdessen können Einstellungen über die config.txt
Datei vorgenommen werden.1 Beispielsweise lässt sich dort die Tonausgabe deaktivieren. Auch verschiedene GPIO-Schnittstellen kann man dort ein- oder ausschalten.
# Tonausgabe aktivieren
dtparam=audio=on
# I2C Bus auf den GPIO-Schnittstellen einschalten
dtparam=i2c_arm=on
Die Firmware übergibt an den Bootloader. Findet dieser entsprechend seiner Konfiguration ein Betriebssystem (z.B. auf der Speicherkarte oder angeschlossener USB-Geräte), wird vereinfacht erst der Linux-Kernel geladen, dann die restliche GNU/Linux-Distribution (z.B. Raspberry Pi OS). Wie der Name bereits vermuten lässt, ist der Kernel zentraler Bestandteil jedes Betriebssystems. Er kümmert sich um Prozesse und Dateien an unterster Stelle, d.H. mit direktem Zugriff auf die Hardware (z.B. eine Speicherkarte). Auch hier gibt es daher einige Einstellungen, die man als Parameter an den Kernel übermitteln kann. Sie werden beim Raspberry Pi in der cmdline.txt
im Format name=wert
festgelegt. Im Gegensatz zu config.txt
allerdings alle in der gleichen Zeile, getrennt durch ein Leerzeichen.
console=serial0,115200 console=tty1 root=PARTUUID=7ce26686-02 rootfstype=ext4 ...
Im Auszug wird etwa eine serielle Konsole (zu Debug-Zwecken) festlegt und ein tty für die Bildschirmausgabe. TTY steht für teletypewriter, zu Deutsch Fernschreiber. Im Kern geht es um Ein- und Ausgabe auf der Konsole, die auch aus der Ferne (tele) stattfinden kann. Darüber hinaus ist das Wurzel-Dateisystem mit eindeutiger UUID der Partition angegeben. Wie man sehen kann, ist das nach dem Start in / eingehängt worden:
$ ls -lh /dev/disk/by-partuuid/7ce26686-02
lrwxrwxrwx 1 root root 15 Feb 7 00:22 /dev/disk/by-partuuid/7ce26686-02 -> ../../mmcblk0p2
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
mmcblk0 179:0 0 59.5G 0 disk
├─mmcblk0p1 179:1 0 512M 0 part /boot/firmware
└─mmcblk0p2 179:2 0 59G 0 part /
Es finden sich in der standardmäßig vorhandenen cmdline.txt
noch weitere Parameter, die hier den Rahmen sprengen würden. Das Konzept dahinter ist nicht spezifisch für den Raspberry Pi: In jeder GNU/Linux-Distribution kann man Parameter für den Kernel setzen. Üblicherweise geschieht dies auf X86-Systemen über den Bootloader. Meist ist das Grub, dieser leitet den Inhalt der Variable GRUB_CMDLINE_LINUX_DEFAULT
in /etc/default/grub
beim Start an Linux weiter.
Was genau wurde beim Raspberry Pi OS geändert?
Beide Dateien lagen dort bis Bookworm direkt in /boot
. Unter den meisten GNU/Linux-Distributionen liegen dort einige Dateien, die notwendig sind, um Linux zu starten – oft auch die Kernel selbst. Mit dem Raspberry Pi OS 12 (Bookworm) hat sich das geändert: Die Dateien wurden in /boot/firmware
verschoben.23 Dabei handelt es sich um ein für den Raspberry Pi spezifisches Verzeichnis.4
/boot/config.txt
wurde zu/boot/firmware/config.txt
/boot/cmdline.txt
wurde zu/boot/firmware/<code>cmdline
.txt
Diese Änderung blieb lange Zeit unbemerkt, weil man als Übergangslösung symbolische Verknüpfungen (Symlinks) für beide Dateien erstellt hatte. Das ist eine Datei, die auf eine andere zeigt. Viele Programme folgen solchen symbolischen Verknüpfungen. Folgendes Beispiel legt eine Textdatei in meinem Heimverzeichnis an und erstellt /tmp/demo.txt als symbolische Verknüpfung auf diese Datei. Der Schreibvorgang mit echo
fügt den Text in die originale Datei meines Heimverzeichnisses ein:
$ touch /home/u-labs/demo.txt
$ ln -s /home/u-labs/demo.txt /tmp/demo.txt
$ l /tmp/demo.txt
Permissions Size User Group Date Modified Name
lrwxrwxrwx - u-labs u-labs 5 Feb 16:07 /tmp/demo.txt -> /home/u-labs/demo.txt
$ echo abc > /tmp/demo.txt
$ cat /home/u-labs/demo.txt
abc
Am 18.01.2024 wurde diese Übergangslösung jedoch im APT-Paket raspberrypi-sys-mods
entfernt.5 Statt der symbolischen Verknüpfung auf die jeweilige Datei im firmware
Unterverzeichnis enthalten beide einen Hinweis, man solle diese Datei nicht mehr bearbeiten und stattdessen den neuen Pfad verwenden:
$ cat /boot/config.txt
DO NOT EDIT THIS FILE
The file you are looking for has moved to /boot/firmware/config.txt
Die aktualisierte Version 20240123
des Pakets wurde Ende Dezember 2023 verteilt. Wer alle Paketquellen und Pakete per sudo apt update && sudo apt upgrade
aktualisiert hat, dem wurden die beiden symbolischen Verknüpfungen entfernt.
$ dpkg -l | grep raspberrypi-sys-mods
ii raspberrypi-sys-mods 20240123 arm64 System tweaks for the Raspberry Pi
Das sind die Folgen
Und das hat Konsequenzen: Dadurch funktioniert beispielsweise rpi-clone
nicht mehr: Seit dem aktualisierten Paket starten geklonte Laufwerke nicht mehr, sondern landen in einer BusyBox Shell. Bei rpi-clone
handelt es sich um ein beliebtes Skript, um u.a. einen kompletten Klon der Karte anfertigen zu können – etwa, um von SD-Speicherkarte auf SSD zu migrieren. Im Gegensatz zum 1:1 Klonen mit dd
weist das Skript den Partitionen auf dem Ziellaufwerk neue UUIDs zu. Sonst kann es zu Konflikten kommen, wenn beide Laufwerke verbunden sind. Außerdem bietet es erweiterte Optionen, die ansonsten ebenfalls manuelle Vorarbeit erforderlich machen. Beispielsweise das Kopieren auf Datenträger, die kleiner als die Partitionen sind (aber immer noch groß genug für den belegten Speicher). Seit der
Auch der Einrichtungsassistent von DietPi, welches auf dem Raspberry Pi OS basiert, lief durch diese Änderung auf Fehler.6 Der Aufwand einer Portierung von DietPi auf den neuen Raspberry Pi 5 ist u.a. dadurch gestiegen.7 Im Raspberry Pi Forum wurde und wird diese Änderung kontrovers diskutiert.8 Neben diesen Beispielen gibt es wohl eine größere Dunkelziffer an Programmen, die nun nicht mehr funktionieren, da sie automatisiert Änderungen an einer der beiden Dateien vornehmen. Unter Umständen sind eben so Anleitungen und Dokumentationen betroffen.
Warum wurde die symbolische Verknüpfung entfernt?
Aus Gründen der Abwärtskompatibilität schien die Verknüpfung ein sinnvoller Kompromiss zu sein. Einige fragen sich daher, warum man diese nicht schlichtweg hat bestehen lassen – dazu gibt es ein Ticket auf GitHub.9 Darin bestätigt der für die Entfernung verantwortliche Entwickler bereits zuvor geäußerte Vermutungen, dass manche Editoren die symbolische Verknüpfung nicht beachten und die Verknüpfung überschreiben, statt deren Ziel. Ich habe dies mit Vim und Nano getestet, beide haben in das Verknüpfungsziel geschrieben.
Anders sieht es dagegen beim beliebten GNU/Linux Werkzeug sed
aus: Standardmäßig legt es beim Ersetzen in Dateien mit -i
(--in-place
) im Pfad der Verknüpfung die neue Datei an. Nur wer den Parameter --follow-symlinks
kennt und benutzt, schreibt tatsächlich in das Verknüpfungsziel.
$ sed 's/a/X/' /tmp/demo.txt -i
$ cat demo.txt
abc def ghi
$ cat /tmp/demo.txt
Xbc def ghi
$ l /tmp/demo.txt
Permissions Size User Group Date Modified Name
.rw-r--r-- 12 u-labs u-labs 5 Feb 16:35 /tmp/demo.txt
Das wird schnell zum Problem, da sed als GNU-Werkzeug vorinstalliert ist – es kommt in vielen Skripten zum Einsatz. Insbesondere wenn das Skript keine vernünftige Fehlerbehandlung aufweist (wie bei etwa rpi-clone
der Fall), führt das zu schwer auffindbaren Fehlern. Bei rpi-clone
wiederum hat man mitgedacht und ruft sämtliche Pfade mit dem Werkzeug realpath
auf. Dies gibt uns den echten Pfad zurück, bei Verknüpfungen also deren Ziel:
$ realpath /tmp/demo.txt
/home/u-labs/demo.txt
Daher hat es auf dem Raspberry Pi OS 12 auch mit der Verknüpfung funktioniert. Erst die Aktualisierung zur Entfernung des Symlinks machte es unbrauchbar. So gut die Idee einer Verknüpfung auch gemeint ist – es ist nicht von der Hand zu weisen, dass dies zu Verwirrung führen kann und in gewissen Konstellationen eine fiese Fehlerquelle ist. An dieser Stelle ist ein Ende mit Schrecken wohl besser, als ein Schrecken ohne Ende. Wenngleich man über die Entscheidung, die Dateien zu verschieben, sicher streiten kann.
Wir reparieren rpi-clone
Das originale Projekt von billw2 wird leider seit 2020 nicht mehr gepflegt.10 Doch das ist in der Open Source Gemeinschaft kein Beinbruch: Es gibt andere Personen, es als Abspaltung (Fork) selbst weiterführen. Der wohl verbreitetste stammt von Jeff Geerling. Er hat bereits in der Vergangenheit neue Funktionen, Fehlerkorrekturen und andere Änderungen eingebaut. Ich habe aus diesem Grunde seine Abspaltung als Grundlage genommen:
git clone https://github.com/geerlingguy/rpi-clone.git
Da ich die Codebasis nicht kenne, habe ich im Hauptskript (rpi-clone)
schlicht nach cmdline gesucht – 11 Treffer. Auch cmdline liefert nur 25 Ergebnisse. Das ist überschaubar für eine händische Überprüfung. Zumal der Begriff an einigen Stellen in Kommentaren gefunden wurde. Die Wichtigsten, bei denen wir den Pfad ändern müssen, lauten:
cmdline_txt=$(realpath /boot/cmdline.txt)
# ...
cmdline_txt=${clone}$(realpath /boot/cmdline.txt)
# ...
cp $cmdline_txt /boot/cmdline.txt
Nun können wir allerdings nicht lediglich an diesen drei Stellen den Pfad um den Unterordner firmware
ersetzen. Schließlich betrifft die Änderung nur Raspberry Pi OS 12 Bookworm. Die Vorgängerversion 11 nutzt weiterhin die bestehenden Pfade und ist noch unterstützt. Selbst bei Bookworm können wir nicht pauschal vom neuen Pfad ausgehen. Gerade zur jetzigen Zeit hat ggf. noch nicht jeder das frische Paket installiert. Daher ist eine Weiche nötig: Ich prüfe, ob in /boot/cmdline.txt
der Hinweistext aus dem Paketupdate enthalten ist. In dem Fall müssen wir den neuen Pfad nehmen, ansonsten den alten.
# In the end of 2023, RPIOS moved cmdline.txt and config.txt from /boot to /boot/firmware:
# https://github.com/raspberrypi/documentation/issues/3089
# There was a symlink, but it got removed at the end of january 2024 and leaves a notice in /boot/cmdline.txst:
# https://github.com/RPi-Distro/raspberrypi-sys-mods/commit/c62cf1a12f4e422c855f7e2fcb39e9cadcb5459b
# This update has broken pi-clone: The cloned drive would boot into busybox.
# To fix this, get_cmdline_path() checks if /boot/cmdline.txt has moved, so it uses the new path on updated systems.
get_cmdline_path() {
if [ "$(head -1 /boot/cmdline.txt)" = "DO NOT EDIT THIS FILE" ]; then
echo "/boot/firmware/cmdline.txt"
else
echo "/boot/cmdline.txt"
fi
}
Die obigen drei Aufrufe habe ich nun durch get_cmdline_path
ersetzt:
- cmdline_txt=$(realpath /boot/cmdline.txt)
+ cmdline_txt=$(realpath $(get_cmdline_path))
- cmdline_txt=${clone}$(realpath /boot/cmdline.txt)
+ cmdline_txt=${clone}$(realpath $(get_cmdline_path))
- cp $cmdline_txt /boot/cmdline.txt
+ cp $cmdline_txt $(get_cmdline_path)
Mit dem neuen Skript erstellt startet das geklonte Abbild wieder wie das Original direkt in das Raspberry Pi OS, statt in BusyBox. Nun wäre im Sinne der OSS-Bewegung der Zeitpunkt, dem Quell-Repository diese Änderung zur Verfügung zu stellen: So profitieren alle die Geerlings Repository nutzen von den Änderungen, inklusive ihm selbst. Während ich das für diesen Beitrag vorbereite, stellte sich allerdings heraus: Er war schneller und hat kurz davor einige Änderungen (Commits) veröffentlicht. Darunter auch eine Korrektur für das Problem der verschobenen Boot-Partition.11
Somit ist die Übermittlung meines Fix nicht mehr nötig. Dies demonstriert die Macht der Open Source Community: Bereits wenige Tage nachdem das Problem bemerkt wird, arbeiten mehrere Personen an einer Lösung. Alleine hier sind es mindestens drei, da diese Code-Änderung nicht von Geerling selbst kam. Sondern von einem GNU/Linux-Nutzer aus Stuttgart.
Fazit
Kleine Änderung, große Wirkung – so könnte man die Problematik zusammen fassen. Dieser Beitrag soll ein Verständnis für das Problem schaffen und sowohl Betroffene als auch Entwickler in die richtige Richtung lotsen, nachdem die Ursache teils nur schwer auffindbar ist. Da nicht alle Nutzer ihre Software sofort aktualisieren, wird es in den nächsten Wochen und Monaten wohl sicher noch den einen oder anderen betreffen – bis alle Projekte entsprechende Weichen eingebaut sowie verteilt haben. Ich habe dies bei rpi-clone
durchgeführt, da ich es für einen geplanten Beitrag zum übertragen von SSD-Karten auf M.2 SSDs am Raspberry Pi benötige.
Ein großer Vorteil von Quelloffener Software: Wir müssen nicht zuschauen oder sind gar aufgeschmissen, weil ein Projekt eingestellt wurde – wie es mit rpi-clone
leider der Fall zu sein scheint. Stattdessen können wir per Fork unseren eigenen Zweig davon erstellen. Gerade bei vergleichsweise kleinen Änderungen wie in diesem Falle muss man kein Softwareentwickler mit 20 Jahren Erfahrung sein, um das Problem selbst lösen zu können. Bei proprietärer Software sind nun alle Nutzer darauf angewiesen, dass der Hersteller sie aktualisiert – sofern es ihn noch gibt & er die Unterstützung nicht längst eingestellt hat.
Quellen & weiterführende Informationen
- https://www.raspberrypi.com/documentation/computers/config_txt.html ↩︎
- https://github.com/raspberrypi/documentation/issues/3089 ↩︎
- https://9to5linux.com/raspberry-pi-os-is-now-based-on-debian-bookworm-supports-raspberry-pi-5-and-pipewire ↩︎
- https://askubuntu.com/a/1256823/650986 ↩︎
- https://github.com/RPi-Distro/raspberrypi-sys-mods/commit/c62cf1a12f4e422c855f7e2fcb39e9cadcb5459b ↩︎
- https://github.com/MichaIng/DietPi/issues/6747 ↩︎
- https://github.com/MichaIng/DietPi/issues/6676#issuecomment-1771124218 ↩︎
- https://forums.raspberrypi.com/viewtopic.php?t=364582 ↩︎
- https://github.com/RPi-Distro/raspberrypi-sys-mods/issues/88#issuecomment-1931626454 ↩︎
- https://github.com/billw2/rpi-clone ↩︎
- https://github.com/geerlingguy/rpi-clone/commit/63765e8e84cc978417a75bbb7e1a28746d4ca542 ↩︎