Opennet Firmware
|
Die Opennet-Firmware basiert auf den Komponenten git, quilt und der openwrt-Entwicklungsumgebung. Links zur Dokumentation dieser Komponenten findest du am Ende dieses Dokuments.
Die grundlegende Struktur der Entwicklungsumgebung ist in der Struktur-Dokumentation beschrieben.
Die Einrichtung der Entwicklungsumgebung dauert nur wenige Minuten und ist untenstehend beschrieben. Der gesamte Prozess der Image-Erzeugung erfordert dagegen ca. 10 GB Festplattenplatz je Zielarchitektur und dauert - je nach Rechner - üblicherweise mindestens ein dutzend Stunden.
Debian:
apt-get install build-essential git flex gcc-multilib subversion libncurses5-dev zlib1g-dev liblzo2-dev gawk unzip python quilt
Nur-Lese-Zugriff:
git clone https://dev.opennet-initiative.de/git/on_firmware
Schreibzugriff:
git clone git@dev.opennet-initiative.de:on_firmware
Die lokale Arbeitsumgebung wird mit folgenden Kommandos abgeschlossen:
cd on_firmware make init
Die obige Aktion wird eine Weile dauern, da die openwrt-Repositories heruntergeladen werden.
Für die komfortable Verwendung des Patch-Verwaltungsystems quilt sollten ein paar quilt-Einstellungen gesetzt werden.
Als dauerhafte Lösung ist folgendes möglich - dabei werden jedoch eventuell vorhandene quilt-Einstellungen gelöscht:
cp quiltrc ~/.quiltrc
Alternativ können die Einstellungen auch zu Beginn jeder Shell-Sitzung importiert werden:
source quiltrc
Falls die obigen Einstellungen nicht gesetzt werden, wird quilt unnötige Patch-Korrekturen vornehmen, sobald sich Zeitstempel ändern.
Dies ist nicht wünschenswert.
Für das Einbringen von Änderungen in das öffentliche Firmware-Repository benötigst du einen git-Account. Diesen kannst du auf der Opennet-Firmware-Mailingliste erfragen: https://list.opennet-initiative.de/mailman/listinfo/firmware
Sobald du einen git-Account zum pushen deiner Änderungen hast, solltest du den Upstream auf die schreibfähige URL umstellen:
git remote set-url origin git@dev.opennet-initiative.de:on_firmware.git
Auf dem Server minato
ist ein automatischer Build-Dienst konfiguriert, der kurz nach einem push einen neuen Snapshot der Firmware erzeugt. Innerhalb von ca. 30 Minuten sind die ersten neuen Images dann verfügbar: http://downloads.opennet-initiative.de/openwrt/testing
Nach dem Auschecken editiere die gewünschten Dateien. Wenn du .patch-Dateien editieren will, musst du weitere Dinge beachten (siehe andere Abschnitte).
Um deine Änderungen einzuchecken, führe folgende Kommandos aus:
# lokal einchecken git commit # (Versuch) Änderungen remote einzuchecken git push # Wenn es keinen Fehler gibt, bist du fertig. Glückwunsch!
Nun kann es sein, dass andere Personen zwischendurch Änderungen gemacht haben. Wenn dem so ist, bekommst du eine Fehlermeldung mit entsprechendem Hinweis. Dieses Problem kannst du folgendermaßen lösen:
# alle vorher eingespielten Patches zurückspielen make unpatch # hole alle Änderungen von remote und wende deine Änderungen darauf an git pull --rebase # deine Änderungen remote einchecken git push
Prinzipiell ist die Arbeit mit dem opennet-Repository identisch mit dem üblichen Umgang mit git-Arbeitsumgebungen. Lediglich die quilt-Patches führen unter besonderen Bedingungen zu einem leicht geänderten Verhalten. Daher wird anstelle des üblichen git pull
folgende Abfolge empfohlen:
# lokale Patches zurücknehmen (vermeidet Konflikte, falls Patch-Dateien geändert werden) make unpatch # entfernte Änderungen git pull --rebase
Achtung: git pull --rebase
manipuliert deine lokale git-History. Falls du frische lokale Commits also bereits zu einem anderen Server übertragen haben solltest, dann führt dies zu unüberschaubaren Chaos. Verzichte in diesem Fall auf --rebase
.
Im openwrt-Repository befindet sich das Basissystem und openwrt-spezifischer Code. Externe Pakete befinden sich (in Form von Makefiles, die auf upstream-Quellen verweisen) in den separaten Paket-Repositories.
# alle Patches anwenden (falls der neue Patch am Ende angehängt werden soll) quilt push -a # einen neuen Patch beginnen quilt new IRGENDEIN_THEMA.patch # Hinweis: "quilt edit" ist identisch zu "quilt add", manuellen Änderungen und einem anschließenden "quilt refresh" quilt edit openwrt/IRGENDEINE_DATEI # Patch beschreiben quilt header -e # ausprobieren ... # falls weitere Änderungen vorgenommen wurden, dann nochmal die Patch-Datei aktualisieren quilt refresh # Patch committen git commit patches/IRGENDEIN_THEMA.patch
Im Gegensatz zum openwrt-Repository befinden sich in den Paket-Repositories lediglich Makefiles (Rezepte für den Paketbau) und openwrt-spezifische Patches. Unsere Änderungen sind üblicherweise ebenfalls Patches, die zu den openwrt-Patches hinzukommen.
quilt new PATCH_NAME.patch
quilt add packages/net/openssh/patches/042-FOO.patch
http://wiki.openwrt.org/doc/devel/patches
quilt refresh
quilt header -e
git commit patches/PATCH_NAME.patch
quilt pop -a
quilt push PATCH
quilt edit DATEI
quilt refresh
quilt header -e
make unpatch
cd openwrt; git pull; cd ..
make patch
git commit openwrt
Ziel: den Patch patches/oni-feeds.patch verändern (z.B. um einen weiteren Feed zu erweitern)
# alle Patches zurücknehmen make unpatch # alle Patches auflisten quilt series # alles bis zu dem gewünschten Patch anwenden quilt push oni-feeds.patch # die neue Feeds-Quelle eintrage vi openwrt/feeds.conf # Patch-Datei aktualisieren quilt refresh # den neuen Patch in das Repository hochladen git commit patches/oni-feeds.patch -m "andere feeds-Dinge hinzugefügt"
Für die schnelle Lösung von Build-Problemen ist es oft sinnvoll, nur das eine problematische Paket erstellen zu lassen:
make -C openwrt package/on-core/{clean,compile} V=s
Die Liste vorhandener Pakete und ihrer Einstellungen wird mit dem feeds-Skript von openwrt verwaltet. Die Feeds werden mittels des meta-Makefile vor jedem Build und vor jedem Aufruf von make menuconfig
aktualisiert. Du kannst dies jedoch auch manuell auslösen:
make feeds
Eine detaillierte Fehlerausgabe erhältst mit der make-Zugabe von V=s
:
make ar71xx V=s
Dabei erleichtert es den Überblick deutlich, wenn du parallele Build-Prozess (z.B. -j 3
) nicht verwendest. Andernfalls musst du eventuell ein paar Seiten in der Build-Ausgabe zurückblättern, um die Fehlermeldung zu finden.
Wie üblich in make-Buildumgebung kannst du manuell mehrere parallele Prozesse für den Paketbau verwenden. Als Faustregel wird üblicherweise ein Wert von Anzahl der Kerne + 1 empfohlen. Bei einem vier-Kern-Rechner wäre dies folgende Zeile:
make -j 5
In den ersten 20 Zeilen der Build-Ausgabe wirst du ein paar Fehlermeldung bezüglich -j1
finden - diese sind ein Indikator für eine openwert-spezifische Unfeinheit. Der finale Build-Prozess wird ungeachtet dieser Warnungen parallelisiert ablaufen.
Die opennet-Entwicklungsumgebung verwendet grundlegend die Abläufe der openwrt-Buildumgebung. Es gibt jedoch ein paar Besonderheiten, bzw. Komfortfunktionen.
Da die opennet-Firmware verschiedene Ziel-Plattformen (ar71xx, ixp44, x86, ...) unterstützt, müssen verschiedene Konfigurationen gepflegt werden. Zur Erleichterung der Pflege und zur Vermeidung von Doppelungen gibt es für jede Ziel-Plattform eine separate Datei (z.B. opennet/config/ar71xx), sowie eine Datei mit Einstellungen, die für alle Zielplattformen gelten (opennet/config/common). Letztere ist für den Entwicklungsprozess wohl die wichtigere.
Die plattform-spezifische config-Datei wird durch opennet/config/Makefile mit der allgemeinen config-Datei zusammengefügt. Anschließend werden folgende Ersetzungen vorgenommen:
__PKG_STATUS__
wird durch stable oder snapshots ersetzt (je nachdem, ob der aktuelle git-commit ein Versions-Tag trägt)Die finale openwrt-Konfiguration wird aus der Ziel-Plattform-Konfiguration und der allgemeinen Konfiguration erstellt und anschließend mittels make defconfig
durch openwrt auf Aufabhängigkeiten zu prüfen und mit den Standardwerten aufzufüllen.
Diese Konfiguration für eine Plattform kann beispielsweise mittels make config-arx71xx
erstellt werden. Das Ergebnis ist anschließend als openwrt/.config verfügbar.
Zur verbesserten Überschaubarkeit von Einstellungsänderungen ist in dem meta-Makefile ein Target namens diff-menuconfig
integriert. Es zeigt dir nach dem Ausführen des gewohnten make -C openwrt menuconfig
den Unterschied zwischen der vorherigen und der gespeicherten Konfiguration in Form eines diffs an.
Unter /usr/lib/opennet/
liegen mehrere Dateien, die Shell-Funktionen beinhalten. Alle Funktionen werden durch die folgende Zeile im globalen Namensraum verfügbar gemacht:
. "${IPKG_INSTROOT:-}/usr/lib/opennet/on-helper.sh"
Alle Funktionen aus den shell-Bibliotheken lassen sich folgendermaßen prüfen:
on-function get_zone_interfaces on_mesh
Dabei ist zu debug-Zwecken auch die ausführliche Ausführungsprotokollierung verfügbar:
ON_DEBUG=1 on-function get_zone_interfaces on_mesh
Die optionale strikte Fehlerbehandlung durch die Shell erleichtert das Debugging. Sie lässt sich global in der Datei /usr/lib/opennet/on-helper.sh
mit folgender Zeile aktivieren:
set -eu
In allen Skripten sollten Fehler-Traps aktiviert werden, um den Ort der Entstehung von Problemem leichter zu ermitteln. Folgende Zeile ist im Kopf von nicht-trivialen Funktionen einzutragen, wobei der Funktionsname zu ersetzen ist:
trap "error_trap NAME_DER_FUNKTION $*" $GUARD_TRAPS
Leider ist es in der ash-Shell nicht möglich, reine Fehler (siehe set -e
) abzufangen (siehe echo 'trap "echo klappt" ERR' ash
). Daher müssen wir die in der ash-Shell vorhandene allgemeinere EXIT
-trap verwenden. Dies führt leider dazu, dass wir gewünschte Fehler (z.B.: return 1
in einer Wahrheitswert-Funktion) von ungewünschten Fehlern (durch set -e
abgefangen) explizit unterscheiden müssen. In jeder Funktion, die explizit ein false
-Ergebnis zurückliefern möchte (und die traps verwendet), muss folgende Zeile anstelle von return 1
(bzw. anderen Fehlercodes) eingesetzt werden:
trap "" $GUARD_TRAPS && return 1
Im System-Log (logread
) lassen sich ausgelöste traps finden:
logread | grep trapped
Folgende Stolperfallen sind sehr beliebt bei der Verwendung des strikten Fehlermodus:
[ 1 -lt "$x" ] && echo "foo"
als letzte Zeile einer Schleife zu einem Abbruch, da die Schleife mit dieser (nicht-Abbruch-auslösenden, jedoch gleichzeitig nicht-erfolgreichen Kette) nach ihrem letzten Durchlauf als "nicht erfolgreich" gilt und somit zu einem Abbruch führtreturn 0
oder true
oder ein angehängtes | true
kann nie schadenDa der typische uci-Aufruf uci -q get CONFIG_KEY
bei nicht-existenten Schlüsseln einen Fehlercode zurückliefert, müsste hier jeder Aufruf von unübersichtlichem boilerplate-Code umgeben werden, um mit strikter Shell-Fehlerbehandlung zu funktionieren. Alternativ steht diese Funktion zur Verfügung:
uci_get CONFIG_KEY [DEFAULT_VALUE]
Das Ergebnis ist ein leerer String, falls der config-Wert nicht vorhanden oder leer ist. Andernfalls wird der Inhalt zurückgeliefert.
Außerdem ist eine verbesserte Version von uci add_list
verfügbar, die - im Unterschied zum Original - bereits vorhandene Einträge nicht erneut hinzufügt:
uci_add_list CONFIG_KEY=NEW_VALUE
Die Funktion uci_delete
entspricht ihrem Äquivalent (uci delete
) bis auf die fehlende Fehlermeldung und der Fehlercode im Falle der Löschung eines nicht vorhandenen Knotens:
uci_delete CONFIG_KEY
Die Opennet Versionsnummern entsprechen dem Commit Zähler. Ab und zu ist es nötig zu wissen, welcher konkrete Commit zu welcher Versionsnummer gehört. Hier gibt es eine einfache Kommandozeile
git log --oneline | sed -n '1!G;h;$p' | nl
Dies listet alle Commit mit entsprechender ONI Versionsnummer auf und einem Abstrakt der Commit Nachricht.
Das openwrt-Web-Interface basiert auf lua-Skripten. Der grundlegende Code sollte in den shell-Bibliotheken untergebracht werden - lediglich die für die Darstellung notwendige Logik gehört in die lua-Skripte.
Ein paar wenige Funktionen werden von mehreren lua-Skripten verwendet. Diese liegen derzeit in der Datei on-core/files/usr/lib/lua/luci/model/opennet/funcs.lua
.
Die wichtigsten Funktionen sind folgende:
Hinzu kommen ein paar Funktionen für die Erleichterung alltäglicher Dinge:
Zum Debuggen von Fehlern im Web-Interface sind folgende Kommandos sinnvoll:
killall -9 uhttpd 2>/dev/null; sleep 1; rm -rf /var/luci-*; uhttpd -h /www -p 80 -f
Denselben Effekt erreichst du mit der Funktion run_httpd_debug (blockierend - inkl. Fehler-Ausgaben des Webservers), sowie clean_luci_restart (Webserver neustarten und im Hintergrund ausführen):
on-function run_httpd_debug # Debug on-function clean_luci_restart # Daemon
openwrt verwendet procd
für die Behandlung von hotplug-Ereignissen. Skripte liegen unter /etc/hotplug.d/
. Für unsere netzwerkbasierten Ereignisse (z.B. Hinzufügen neuer olsrd-Interfaces) verwenden wir den hotplug-Typ ifcace
.
Der Aufruf von hotplug-Skripten lässt sich folgendermaßen emulieren:
ACTION=ifup hotplug-call iface
Das Skript opennet/tools/check_for_obsolete_functions.sh
gibt potentiell unbenutzte lua- und shell-Funktionen aus. Ein gelegentliches Prüfen der Ausgabe dieses Skripts hilft dabei, nicht mehr benötigte Funktionen zu beräumen.
Das Skript opennet/tools/copy-package-files-to-AP.sh
kopiert die Inhalte der lokalen Entwicklungsverzeichnisse direkt auf einen angebenen AP. Dies ist möglich, da in unseren Pakete lediglich Interpreter-Code (shell/lua) vorhanden ist und somit kein Build-Prozess erforderlich ist.
Falls auf dem Ziel-AP rsync installiert ist, wird die Übertragung deutlich beschleunigt.
opennet/tools/copy-package-files-to-AP.sh 172.16.0.1
Mehr Log-Ausgaben (debug) ins syslog (logread) schreiben:
uci set on-core.settings.debug=true
Ein Skript mit detaillierter Ausführungsverfolgung starten:
ON_DEBUG=1 on-function print_services
Fehlermeldungen des Web-Interface ausgeben:
killall -9 uhttpd 2>/dev/null; sleep 1; rm -rf /var/luci-*; uhttpd -h /www -p 80 -f
Für Performance-Optimierungen sind Daten zum Zeitbedarf der verschiedenen Funktionen sehr hilfreich.
Mit dem folgenden Kommando werden alle Shell-Skripte der opennet-Pakete derart manipuliert, dass fortan der Zeitbedarf jedes Funktionsaufrufs aufgezeichnet wird:
on-function enable_profiling
Vor der Manipulation der Shell-Skripte prüft das obige Skript, ob die notwendigen Zusatzpakete (z.B. bash) intalliert sind.
Die obige Aktion ist irreversibel. Die einzige Möglichkeit, das Profiling wieder abzuschalten, ist die erneute Installation der Pakete.
Die Ergebnisse des Profiling werden unter /var/run/on-profiling
abgelegt. Für jede Funktion wird eine Datei erzeugt. Jede Zeile in diesen Dateien entspricht dabei einem Funktionsdurchlauf. Die gemessene Zeit entspricht dem zeitlichen Abstand von Funktionseintritt und -ende. Die Verarbeitungszeit der aufgerufenen Funktionen geht also in den Zeitbedarf der sie aufrufenden Funktion ein.
Die Auswertung des Profiling ist folgendermaßen möglich:
on-function summary_profiling
Mit fortlaufendem Profiling wird das tmp-Verzeichnis (bzw. der RAM) schrittweise gefüllt. Ein Host mit aktiviertem Profiling darf also nicht im produktiven Einsatz sein, da im Verlauf von Stunden eine Speichermangel-Situation droht.
Die Übersetzungen werden mittels des luci-Übersetzungskonzepts verwaltet. In den Templates verwenden wir englische Originaltexte.
<%:This is an example.%>
luci.i18n.translate("Interface")
luci.i18n.translatef("Send an email to %s for further information.", email_address)
make translate
virtaal opennet/po/de/on-core.po
Die Versionsnummer des kommenden Release ist in der opennet/config/common
als CONFIG_VERSION_NUMBER
eingetragen.
Im Laufe der Erzeugung der config-Datei wird eventuell die git-commit-Nummer hinzugefügt (siehe Zusammensetzung einer config-Datei ).
Die opennet-relevanten Pakete (on-core u.s.w) erhalten dieselbe Versionsnummer.
Wir verwenden trac als Web-Interface und Entwicklungswerkzeug: http://dev.opennet-initiative.de/.
Das trac-Plugin bitten wird durch einen git-commit-Hook ausgelöst und regt wenige Minuten nach einem git push einen Build-Prozess auf dem Server minato an. Dieser Build-Prozess wird vom Nutzer trac-bitten-slave ausgeführt. Die Build-Schritte sind im Web-Interface definiert.
Innerhalb des Build-Prozess wird das export-Skript ausgeführt. Es kopiert das Build-Ergebnis für eine Plattform oder wahlweise die erstellte Dokumentation in das Export-Verzeichnis, welches via Webserver veröffentlicht wird (http://downloads.opennet-initiative.de/openwrt/). Das exakte Zielverzeichnis ergibt sich dabei aus der Versionsnummer (siehe Versionsnummerierung ).
Bei RAM-Mangel (erkennbar am spontanen reboot ohne Änderungen nach dem Upload der neuen Firmware-Datei) kann folgende Kommandozeile wahrscheinlich genügend Platz schaffen:
for a in collectd dnsmasq sysntpd cron; do /etc/init.d/$a stop; done
Alterantiv koennen diese Dienste via Administration -> System -> Systemstart
gestoppt (nicht deaktiviert!) werden.
Für ein Release sind lediglich folgende Schritte durchzuführen:
git tag -a v0.5.1
git push --tags
Die git-Doku befindet sich hier: http://git-scm.com/documentation
Wir verwenden das Patch-Verwaltungssystem quilt. Dies erleichtert die Erstellung und Pflege von Patch-Serien gegenüber fremden Quellen.
Das Howto von quilt ist hier zu finden: http://repo.or.cz/w/guilt.git/blob/HEAD:/Documentation/HOWTO
Die Entwicklungsdokumentation von openwrt ist hier zu finden: http://wiki.openwrt.org/doc/devel/start