Howtos: Entwicklung
- Entpacken und Packen von Firmware-Images
- Patches in Freetz einspielen
- Example 3: NZBGet
- Example 2: par2cmdline
- Example 1: Httptunnel
- Ablauf eines Firmware-Updates
- Eigene Programme kompilieren
- Dynamische Bandbreitenanzeige per SVG
- Platz sparen im Dateisystem der FritzBox
- Flash-Partitionen im laufenden Betrieb sichern
- Release Management
- First steps - How to start your first freetz package
- Kernel konfigurieren und kompilieren
- Menükonfiguration pflegen
- ADAM2-Bootloader
- Einstellungen speichern im Urlader-Environment
- Busybox konfigurieren und kompilieren
- Wie baue ich ein eigenes Paket für Freetz?
- Firmware-Image-Namen analysieren und interpretieren
- Developer Information
- Addon Paket installieren
- Paketverwaltung für Freetz
- Wie die FritzBox Manipulationen erkennt
- Shell Coding Conventions
- Package Development
- Erstellen einer GUI für Pakete in Freetz
- Flash Partitionierung
- Trac Hooks
- Package Developing - Advanced Topics
- Eigene Dateien in die Firmware integrieren
- Freetz Build-Prozeß
- Flash-Partitionen von außen mit FTP sichern
- libmodcgi.sh
- Cross-Compiler / Toolchain erstellen
- Eigene Download-Toolchain erstellen
- Target/Native-Compiler-Toolchain erstellen
Einstellungen speichern im Urlader-Environment
Vorwort und Motivation
Es kann von Vorteil sein, während des Boot-Prozesses oder evtl. auch danach noch gewisse Schalter (engl. flags) abfragen zu können, ohne deswegen gleich auf /var/flash/debug.cfg zugreifen zu müssen. Dafür gibt es folgende Gründe:
- Die Character Devices unter /var/flash sind erst nach Ablauf von /etc/init.d/rc.S verfügbar, weil sie in diesem Skript erst mittels mknod erzeugt werden - speziell debug.cfg übrigens erst separat am Ende des Skripts.
- Gewisse Funktionalitäten würden wir gerne aufrufen, bevor rc.S läuft. Bestes Beispiel sind Root-Mounts, z.B. das Freetz-Paket mini_fo, dessen Init-Skript sich in /etc/inittab vor rc.S einträgt. Würden wir jetzt gerne einen Schalter haben, der das Laden von mini_fo verhindern kann, hätten wir ein Problem.
Lösungsmöglichkeiten
Kein Problem ohne Lösung. Wir könnten innerhalb von rc.mini_fo selbst mknod verwenden, um debug.cfg temporär zugreifbar zu machen und nach Abfragen der gewünschten Information wieder aufräumen mit rm, damit später rc.S nicht irritiert wird beim erneuten Erzeugen des Nodes. Es gibt tatsächlich ein Paket (NFS-Root), welches diese Technik verwendet, um auf AVM-Konfigurationsdaten aus dem TFFS zuzugreifen, zusätzlich aber auch noch Informationen von woanders einholt, und zwar aus dem sog. Bootloader Environment (Urlader-Umgebungsvariablen). Schon seit 15.0 greifen auch zwei (noch) undokumentierte Logging-Werkzeuge des DS-Mod, Inotify-Tools und Dmesg-Recording, auf diese Umgebungsvariablen zu, die beiden Letzteren sogar über ein kleines, funktional auf bestimmte Anwendungsfälle eingegrenztes Shell-API, das ich mir habe einfallen lassen. Dazu später mehr.
Bootloader Environment
Der Urlader (engl. bootloader), je nach Version auch bekannt unter ADAM2 oder EVA, besitzt ein sog. Environment, also einen kleinen Speicherbereich für globale Einstellungen, welche absolut erforderlich sind, damit die Box überhaupt funktioniert. Das ist ja bekannt, aber zur Auffrischung nochmals die (mit "#" anonymisierte) Ausgabe, generiert auf meiner 7170 mit Kernel 2.6:
$ cat /proc/sys/urlader/environment HWRevision 94.1.1.0 ProductID Fritz_Box_7170 SerialNumber 0000000000000000 annex B autoload yes bootloaderVersion 1.153 bootserport tty0 bluetooth ##:##:##:##:##:## cpufrequency 211968000 firstfreeaddress 0x946AE530 firmware_version 1und1 firmware_info 29.04.29 flashsize 0x00800000 maca ##:##:##:##:##:## macb ##:##:##:##:##:## macwlan ##:##:##:##:##:## macdsl ##:##:##:##:##:## memsize 0x02000000 modetty0 38400,n,8,1,hw modetty1 38400,n,8,1,hw mtd0 0x90000000,0x90000000 mtd1 0x90010000,0x90780000 mtd2 0x90000000,0x90010000 mtd3 0x90780000,0x907C0000 mtd4 0x907C0000,0x90800000 my_ipaddress 192.168.178.1 prompt AVM_Ar7 ptest reserved ##:##:##:##:##:## req_fullrate_freq 125000000 sysfrequency 125000000 urlader-version 1153 usb_board_mac ##:##:##:##:##:## usb_rndis_mac ##:##:##:##:##:## usb_device_id 0x3D00 usb_revision_id 0x0200 usb_device_name USB DSL Device usb_manufacturer_name AVM wlan_key ################ wlan_cal ####,####,####,####,####,####,####,####,####
Das Schöne an diesem Environment ist, dass es nicht nur lesbar, sondern auch (teilweise) beschreibbar ist. Das wird gern verwendet, um Anpassungen am Annex oder am Branding vorzunehmen, insbesondere bei OEM-Boxen, deren Besitzer gern eine vollwertige FritzBox daraus machen möchten, um die entsprechenden Original-Firmware oder Freetz darauf zu installieren, bzw. auch, um eine deutsche Box im Ausland lauffähig zu machen oder umgekehrt.
Es ist bei den allermeisten Werten im Environment absolut nicht ratsam, sie für andere Zwecke zu missbrauchen, aber es gibt eine oben gar nicht sichtbare Variable, die dafür geschaffen wurde, dem Linux-Kernel Parameter für den Boot-Vorgang mitzugeben. Diese Parameter werden später weitergereicht an die Startskripte, aber auch gleichzeitig persistent gespeichert und sind somit ideal geeignet, um Werte von dort abzufragen.
Variable "kernel_args"
Die Variable, von der hier die Rede ist, heißt kernel_args, fasst maximal 64 Zeichen an Informationen und sollte daher mit Bedacht verwendet werden. Man kann folgendermaßen etwas hinein schreiben:
echo "kernel_args tea=Darjeeling quality=FTGFOP1" > /proc/sys/urlader/environment
Damit würden wir ein eigens dafür entworfenes (leider fiktives) Startskript der FritzBox anweisen, beim Hochfahren der Box Tee zu kochen, und zwar Darjeeling der Qualitätsstufe FTGFOP1 (Finest Tippy Golden Flowery Orange Pekoe 1).
Wenn man sich jetzt das Environment nochmals anzeigen lässt, findet man plötzlich dort die Variable kernel_args vor:
$ cat /proc/sys/urlader/environment | grep kernel_args kernel_args tea=Darjeeling quality=FTGFOP1
Mit ein bisschen Zeichenketten-Manipulation können wir den Wert von kernel_args isolieren und dann weiter zerlegen in unsere beiden Schlüssel-Werte-Paare. Darauf will ich an dieser Stelle nicht weiter eingehen, das sind Grundlagen der Shell-Programmierung.
Jedoch wichtig zu wissen ist, wie man während des Boot-Vorgangs an diese Variable heran kommt. Die Antwort hängt davon ab, zu welchem Zeitpunkt man den Zugriff benötigt. Sofern das virtuelle Dateisystem unter /proc bereits zugreifbar, das sog. procfs also bereits ins Root-Dateisystem per mount eingehängt wurde, können wir so vorgehen wie oben gezeigt. Andernfalls müssen wir zunächst mittels
[ -e /proc/mounts ] || mount proc
procfs selbst einhängen, falls es noch nicht da ist. Nach Benutzung loswerden können wir es entsprechend über umount proc
Kernel_Args-API
Für einfache, auf Debugging oder Logging ausgerichtete Anwendungsfälle, die innerhalb von kernel_args auskommen mit den Werten
- aktiv/ja,
- inaktiv/nein,
- Countdown-Zähler > 0,
gibt es das Shell-Skript kernel_args, welches man mit . /usr/bin/kernel_args in ein laufendes Skript inkludieren und daraufhin auf verschiedene vorgefertigte Funktionen zur Manipulation von innerhalb der Bootloader-Variablen kernel_args gespeicherten Schlüssel-Werte-Paaren zugreifen kann. Das Skript ist in den enthaltenen Kommentarzeilen gut dokumentiert, daher hier nur eine kurze Auflistung der aktuell (ds26-15.2) verfügbaren Funktionen:
- ka_mountProc: /proc mounten, falls notwendig
- ka_getArgs: kernel_args komplett auslesen
- ka_getKeyValuePair: Schlüssel-Wert-Paar zu geg. Schlüssel ermitteln
- ka_isValidName: Schlüsselnamen auf Validität prüfen
- ka_isValidValue: Wert auf Validität (y, n, Zahl > 0) prüfen
- ka_getValue: Wert zu einem Schlüssel ermitteln
- ka_setValue: Wer zu einem Schlüssel setzen
- ka_removeVariable: Schlüssel-Wert-Paar löschen
- ka_removeVariableNoUpdate: wie oben, aber nur neuen Wert von kernel_args nach angenommener Entfernung eines Paares anzeigen, nicht direkt ins Environment schreiben
- ka_isPositiveInteger: Hilfsfunktion zum Prüfen numerischer Werte
- ka_isActiveVariable: Prüfen, ob Wert > 0 oder "y" (aktiv)
- ka_decreaseValue: Positiven Ganzzahlwert um 1 vermindern. Falls er 0 werden würde, Wert durch "n" (inaktiv) ersetzen
Countdown-Trick
Gerade die letzten beiden Aufrufe ermöglichen ein hilfreiches Konstrukt beim Entwickeln von Startskripten: Man kann eine Variable z.B. auf 5 setzen und bei jedem Startvorgang um 1 vermindern, bis sie nach fünf Durchläufen auf "n" (inaktiv) gesetzt wird. Abhängig davon könnte man den weiteren Verlauf eines Skripts beeinflussen, es also fortsetzen oder vorzeitig beenden. Sollte im weiteren Verlauf des Skripts also ein Fehler auftauchen, der den Startvorgang der Box torpediert, so daß man nicht mehr an sie heran kommt ohne Recover, wäre dieser Countdown eine praktische Hilfe, denn spätestens beim sechsten Anlauf würde ja die fehlerhafte Funktion nicht mehr aktiviert sein und die Box normal weiter gestartet werden. Wir retten uns hiermit also vor uns selbst und ziehen uns an den eigenen Haaren aus dem Sumpf!
Grenzen des kernel_args-API
Sobald wir andere Arten von Werten in kernel_args speichern wollen, z.B. etwas wie my_path=/usr/bin/my_script, versagt das API in der momentanen Version seinen Dienst, weil es ja nur die Werte "y", "n", positive Ganzzahl zulässt. Aber oben steht ja, wie man auch damit umgehen kann durch Direktzugriff. Eines Tages erweitere ich vielleicht auch das API.
Mögliche Anwendungsfälle
Root-Mounts: Dienste wie mini_fo zur virtuellen Überlagerung des Root-Dateisystems durch eine RAM-Disk oder einen externen Speicher, um Schreibzugriffe zu ermöglichen oder NFS-Root, also der vollständige Ersatz des Root-Dateisystems durch einen voll beschreibbaren und größenmäßig quasi unbegrenzten Netzwerk-Mount könnten von Schaltern im Bootloader Environment profitieren, weil man sie bei Bedarf ein- und ausschalten könnte. (Anm.: NFS-Root zum [De-]Aktivieren tatsächlich einen Eintrag in kernel_args, allerdings ohne API. Zusätzlich wird der zu mountende NFS-Pfad in einer anderen Bootloader-Variablen Namens nfsroot abgelegt, die der Linux-Kernel sowieso kennt und die wir quasi missbrauchen.)
Debugging/Logging: Bei Bedarf zuschaltebare Funktionen, um Dateizugriffe beim Booten zu protokollieren, um z.B. festzustellen, welchen Binaries beim Starten nicht angerührt werden und die man deshalb via Downloader-CGI auslagern könnte, um Platz für mehr früher benötigte DS-Mod-Pakete zu schaffen, oder um das Kernel-Log in eine Datei zu sichern, bevor der Ringpuffer überläuft und der Anfang verloren geht, sind Beispiele für weitere sinnvolle Anwendungsbereiche von kernel_args, ob nun mit oder ohne API. Der Entwickler braucht keine Debug-Version seiner FW zu flashen, um etwas zu probieren, sondern er baut die notwendigen Dinge fest in seine FW ein, macht den Start aber abhängig von einem oder mehreren Schaltern (Berücksichtigung in den Init-Skripten). Sehr bequem!
Weitere Schweinereien überlasse ich Eurer geschätzten Phantasie.
Diskussionen zum Thema bitte unter http://www.ip-phone-forum.de/showthread.php?t=134976, wo zu Beginn noch die Rede davon ist, die Variable SerialNumber zu verwenden, um Werte dort zu speichern. Allerdings hat sich später herausgestellt, daß man diese Variable zwar dem Anschein nach ändern kann, die Änderungen aber einen Neustart der Box nicht überleben. Also bitte nicht verwirren lassen, "state of the art" ist momentan kernel_args.