mod_custom_images - Erweitern von FRITZ!OS um eigene Software-Pakete
Da das neue Skript mit dem Namen "
mod_custom_images" seinerseits selbst eher ein Framework ist als eine Modifikation mit einem konkreten Ergebnis, will und werde ich der Beschreibung dieser Erweiterung etwas mehr Platz einräumen bzw. von Beginn an versuchen, die Erklärungen wieder in die Hauptteile "Installieren", "Benutzen", "technischer Hintergrund" aufteilen, damit das etwas übersichtlicher bleibt und jeder nur die Informationen lesen muß (jedenfalls im akuten Fall, wenn es ums "Nachschlagen" geht), die er von Fall zu Fall braucht.
Was soll das überhaupt, brauche ich das?
Diese Modifikation fügt ein Shell-Skript mit dem Namen
E99-custom in das Verzeichnis mit den Start-Dateien des FRITZ!OS (
/etc/init.d) ein.
Mittels dieses Skriptes, das dann von der originalen Firmware am absoluten Ende des Start-Prozesses aufgerufen wird, kann man seine eigenen Erweiterungspakete für die Firmware - die als Dateisystem-Image vorliegen müssen, damit sie hier verwendet werden können - in das Dateisystem der FRITZ!Box einhängen lassen und über die - ebenfalls in diesen Images liegenden - zusätzlichen Shell-Skripte seine eigenen Dienste starten oder andere Veränderungen vornehmen.
Am Ende ist es nicht viel anders als der Plugin-Mechanismus, dem AVM auch bei der deutschen Labor-Firmware für die 7390 ja gerade neues Leben einhaucht - nur daß bei AVM noch eine Prüfung der Signatur solcher Plugins erfolgt, die hier (zumindest vorläufig) noch entfällt.
OK, wenn ich das nun wohl brauche bzw. "haben will", wie kriege ich das dann in mein FRITZ!OS?
Das ist natürlich zunächst mal wieder ein
modscript für
modfs ... neben der Vorlage für dieses Skript (Datei
files/E99-custom) enthält die neue Version auch noch zwei zusätzliche Programme für die Verwaltung von ext2-/ext3-Dateisystemen, die man ggf. zum Erstellen eigener Images ganz gut gebrauchen kann, wenn man das nicht auf einem "ausgewachsenen System" machen will.
Die Modifikation beschränkt sich eigentlich auf das Kopieren der Vorlage in das Zielverzeichnis im neuen Dateisystem und auf das Editieren der
rc.tail.sh in der letzten Zeile (dazu gleich mehr).
Für den Aufruf dieser Datei sorgt dann jedenfalls schon die originale AVM-Firmware, denn sie führt alle dort liegenden Dateien aus, wenn deren Namen einem bestimmten Schema entsprechen. Damit dieser Mechanismus allerdings auch für ein Skript funktioniert, dessen Name mit
E9 beginnt (das ist dann wirklich der krönende Abschluß der Initialisierung, hier ist alles andere bereits fertig, solange es sequentiell erfolgt), muß das Beenden der Abarbeitung von
/etc/init.d/rc.S in der Datei
/etc/init.d/rc.tail.sh durch die Zeile "
exit 0" am Ende verhindert werden, da alle
Exx-Dateien erst nach den
Sxx-Dateien an der Reihe sind und diese
rc.tail.sh aus der letzten zu startenden Sxx-Datei mit dem Namen
S99-tail aufgerufen wird. Das erfolgt einfach durch das
Auskommentieren dieser Zeile, damit irgendwelche anderen Skripte zumindest noch ihren "anchor" finden, falls sie nach diesem
exit suchen oder davon ausgehen, daß die letzte Zeile in dieser Datei zumindest nichts anderes mehr aufruft. Das
modscript für das Einbinden der
rc.user (
mod_rc_tail_sh) fügt seine zusätzliche Zeile z.B. vor der letzten Zeile dort ein.
Dieses Setzen der Abarbeitung ganz an das Ende des Startprozesses ist jedenfalls pure Absicht ... es gibt sicherlich je nach Verwendungszweck so einer Erweiterung bessere Punkte, wo man in die Firmware eingreifen könnte (z.B. das Ereignis, daß ein USB-Gerät verfügbar ist oder wenn die FRITZ!Box "online geht"), aber ich habe es oft genug gesehen, daß durch falsch erstellte Erweiterungen (selbst noch in der
debug.cfg, wo beim originalen Aufruf - wie ihn auch Freetz wieder reaktiviert - eben keinesfalls ein
exit vorhanden sein darf/sollte, weil dann die danach liegenden Kommandos in der
rc.tail.sh nicht mehr ausgeführt werden) am Ende schwer zu lokalisierende Folgefehler entstehen (z.B. die Abarbeitung der
rc.S nie wirklich beendet wird) und das läßt sich auf diesem Weg vermeiden. Für eine sichere Feststellung, ob die FRITZ!Box bereits online ist, ein USB-Speicher verfügbar ist oder nicht, usw., ist es ohnehin erforderlich, daß sich jede Erweiterung selbst davon überzeugt, denn durch den Ausfall eines Einwahlserver o.ä. kann das ja jederzeit genauso auftreten, wie der Benutzer einfach einen USB-Stick abgezogen haben kann und dann spielt der Zeitpunkt, wann solche Erweiterungen zum Zuge kommen, plötzlich keine Rolle mehr.
So ... jetzt habe ich das in meinem System, aber es passiert nichts. Was mache ich falsch?
Ganz kurz: nichts.
Ohne die passenden Images (oder mit passenden Einstellungen im Urlader-Environment trotz eventuell vorhandener Images) macht dieses Skript praktisch gar nichts ... und das ist auch gut so.
Erst dann, wenn man - aus welcher Quelle auch immer, zum notwendigen "Grundvertrauen" in das Gute im Menschen, wenn man Binärpakete von Fremden benutzt, habe ich oft genug meinen Senf abgegeben - eines oder mehrere passende Images irgendwo auf der Box abgelegt hat, erst dann kann die Modifikation wirklich etwas bewirken.
Dann durchsucht das Skript beim Start die Verzeichnisse, für die es konfiguriert wurde (der Standard wäre das Durchsuchen von
/wrapper und
/var/media/ftp, das läßt sich aber notfalls auch verändern, wenn man das Skript schon im seinem SquashFS-Image hat und es dort nicht mehr beschreibbar ist, dazu kommen wir später) nach Dateien, die auf die Erweiterung
.custom enden.
Kurze Anmerkung: Alles, was man ggf. vor dem Schreiben von
E99-custom in das Dateisystem beim
modfs noch anpassen könnte, kommt später ... jetzt geht es erst einmal darum, wie sich das Skript verhält, wenn man es 1:1 übernimmt.
Es wird also nach Dateien mit der Erweiterung
.custom gesucht und jede dieser Dateien wird dann als ein Dateisystem-Image angesehen und es wird versucht, sie in das Dateisystem der FRITZ!Box einzuhängen.
Gelingt das, werden alle Dateien innerhalb des Verzeichnisses etc/init.d in diesem Image gesucht und für jede dieser Dateien ein Kommando zum Aufruf in ein temporäres Shell-Skript geschrieben, das am Ende der Abarbeitung von
E99-custom dann alle zusätzlichen Start-Skripte auf einen Schlag startet und parallel abarbeitet.
Hilfe! Kann ich das auch von Fall zu Fall beeinflussen? Ich habe ein falsches Paket erstellt und nun hängt mein System bei jedem Start.
Genau für solche Notfälle berücksichtigt das Skript den Inhalt von
/proc/cmdline ... dieser wiederum kann recht einfach auch "von außen" beeinflußt werden, denn er enthält u.a. den Inhalt der Variablen "kernel_args" aus dem Urlader-Environment.
Wie man diese Variable jetzt aus einem laufenden FRITZ!OS heraus ändert (über das Schreiben in
/proc/sys/urlader/environment) oder in einer FTP-Sitzung mit dem Bootloader EVA einen neuen Wert schreibt (mittels
SETENV), ist oft genug thematisiert worden.
Die Variable, nach der das Skript dort sucht, muß mit der Zeichenkette
custom= beginnen, die möglichen Angaben dahinter werden gleich erläutert. Entscheidend für solche Notfälle wäre aber der Wert
custom=off, dessen Auftreten jede weitere Abarbeitung der
E99-custom zum frühestmöglichen Zeitpunkt beendet.
Über diese Variable kann man aber auch die Verzeichnisse neu definieren, in denen nach Erweiterungsdateien gesucht wird, einzelne Erweiterungspakete ausschließen oder - am ehesten beim Test eigener neuer Images nützlich - den Start von Erweiterungspaketen ausführlich protokollieren lassen (Option
-x beim Start der Shell).
Der grundsätzliche Aufbau des Wertes der Variablen richtet sich dabei nach folgender Notation (an EBNF angelehnt):
Code:
custom = "custom=", [ off_value | kvset ];
kvset = key_value_set, { ( ",", key_value_set ) };
key_value_set = key, "(", value, ")";
value = package_list | search_path;
package_list = package, { ( ":", package ) };
search_path = directory, { ( ":", directory ) };
package = <string>; (terminal, but limited by existing subdirectories)
filename = package, { ( "_" | option ) };
option = [ "tmpfs" | [ "rw | "ro" ] | "gz" ];
key = "path" | "ignore" | "debug";
off_value = "off";
Wenn man also die Suche nach Erweiterungsdateien auf die Verzeichnisse
/var/media/ftp und
/var/media/ftp/my_usb_stick beschränken will (was bei einer 7390 z.B. eine Möglichkeit wäre, denn auch dort funktioniert das Skript weiterhin, auch wenn man sich einen anderen Weg überlegen muß, wie man das System dazu bringt,
E99-custom zu starten - solange man das nicht einfach aus der
debug.cfg heraus selbst macht) und gleichzeitig das Initialisieren von Paketen mit den Namen
openvpn und
dropbear unterbinden will, dann sähe die Variable z.B. so aus:
Code:
custom=path(/var/media/ftp:/var/media/ftp/my_usb_stick),ignore(openvpn:dropbear)
Wer den Term
filename in der o.a. Syntax-Beschreibung sieht, stellt ja schnell fest, daß der eigentlich gar nicht dazugehört ... denn in der
custom-Variablen soll er gar nicht auftauchen, dort ist nur der Paketname gefragt. Aber diese möglichen Bestandteile eines Dateinamens für ein Erweiterungspaket bieten dann die Möglichkeit, das Einhängen (Mounten) des Dateisystems in gewissen Grenzen zu beeinflussen. Welche das sind und was man dabei beachten sollte, versuche ich bei den Erklärungen zum Aufbau und zum Erstellen eines eigenen Images zu beleuchten.
Wie baue ich mir mein eigenes Image?
So ein Image ist eigentlich schnell erstellt. Man legt sich an einer beschreibbaren Stelle im Dateisystem ein leeres Verzeichnis als Basis für das künftige Image an und kopiert in die Unterverzeichnisse (die man darin wieder anlegt) alles an Dateien (Binaries, Libraries, Einstellungen), was man so für die gewünschten Aufgaben braucht.
Zwar sind bis zu 16
loop-Devices tatsächlich nutzbar (wenn man die
dev-Nodes selbst anlegt), aber die AVM-Firmware erstellt von sich aus nur 8 Loop-Devices (
loop0 bis
loop7) und sie benötigt - je nach Box und Firmware-Version - selbst freie Loop-Devices (wo z.B. das Skript
/sbin/start_plugin.sh bei der 7390 auch nur bis 7 zählen mag) für das Mounten des root-Dateisystem, das Mounten von einem oder mehreren Plugins und das Mounten des Dateisystem-Images beim Firmware-Update. Ich würde also eher nicht hingehen und mehr als fünf Erweiterungspakete getrennt mounten lassen ... es ist ja auch relativ leicht, mehrere Software-Pakete zu einem gemeinsamen Erweiterungspaket zusammenzupacken.
Genau aus diesem Grund startet
E99-custom auch wirklich jedes Skript, das es im Unterverzeichnis
etc/init.d des neu gemounteten Images finden kann ... den Namen des Verzeichnisses habe ich der Einfachheit halber beibehalten (dann muß man nicht raten, wo die Dateien zu finden sein könnten), denn das (alte) Verzeichnis
etc/init.d kennt eigentlich jeder Linux-Benutzer aus den seligen Zeiten vor dem
systemd.
Wenn man also 5 verschiedene Software-Pakete für fünf verschiedene Daemons in ein gemeinsames Image geschoben hat, bietet sich die Verwendung von fünf getrennten Start-Skripten eher an, als diese in eine gemeinsame Datei zu überführen.
Wie so ein Start-Skript aussehen könnte (gerade auch, wenn es um die "Kommunikation" mit
E99-custom geht), habe ich in einer
Vorlage für so eine Datei mal angedeutet.
Der Grund, warum ich nicht einfach die ganzen Angaben für das Skript in Environment-Variablen übergebe, ist das Fehlen genau dieser Variablen bei späteren Aufrufen eines solchen Skriptes (z.B. bei der Rekonfiguration von Diensten oder bei der Abfrage des Status) und da anhand dieser Variablen diverse Entscheidungen getroffen werden können/müssen (z.B. eben, ob Änderungen an einem beschreibbaren Image irgendwie wieder mittels
gzip gepackt und persistent am originalen Ort gespeichert werden sollten), die auch noch bei späteren Aufrufen relevant sind, habe ich mich dazu entschlossen, diese Übergabe lieber über eine kleine Text-Datei unterhalb von
/var zu realisieren, die jedes dieser Start-Skripte (auch bei späteren Aufrufen und ohne Hinweis im Environment, zu welchem Paket man selbst gehört) leicht anhand des eigenen Dateinamens finden kann.
Der Rest in der Vorlage ist eher Standardkost ... wie das für ein eigenes Image konkret aussehen kann, kommt später noch, wenn ich das Erstellen anhand eines Beispiels genauer beschreibe.
Ist jedenfalls dieses Verzeichnis dann erst einmal komplett, packt man es entweder mit dem passenden
mksquashfsX-Programm aus dem modfs-Archiv zusammen oder man erstellt sich ein leeres
ext2/3-Image, mountet dieses irgendwo und kopiert den Verzeichnisinhalt in dieses Image (über dessen Mountpoint), bevor man das Dateisystem-Image wieder aushängt.
Hier kommt dann auch wieder der oben vertagte Aufbau des Dateinamens so einer Image-Datei ins Spiel ... dieser besteht ja aus dem Paketnamen und einigen Anweisungen, wie so ein Image beim Mounten zu behandeln ist. Diese ganzen Bestandteile werden jeweils durch Unterstriche voneinander getrennt und alles vor dem ersten Unterstrich wird dem Namen zugeschlagen. Die zusätzlichen Optionen, die in so einem Dateinamen noch enthalten sein können, wären dann:
- tmpfs -> das sorgt dafür, daß die Image-Datei nicht an ihrem Ursprungsort gemountet wird, sondern sie wird vorher ins tmpfs kopiert; das kostet nicht nur zusätzlichen Platz im tmpfs (Hauptspeicher), es bietet auch die Möglichkeit, im laufenden System diese ursprüngliche Datei einfach auszutauschen, ohne die Dienste anhalten zu müssen und das in Benutzung befindliche Dateisystem-Image erst einmal aushängen zu müssen
- gz -> zusätzlich zu tmpfs, sorgt diese Option dafür, daß beim Kopieren ins tmpfs die Datei mit gunzip dekomprimiert wird
- ro oder rw -> gibt den gewünschten Modus beim Mounten an; fehlt die Angabe, wird anhand des Dateisystem-Typs und der Zugriffsmöglichkeiten am Ort des Images entschieden, ob Schreibzugriff möglich ist, das gilt aber auch für die explizite Angabe von rw als Option, daß diese nur berücksichtigt wird, wenn es möglich ist
Auch die Quelle so eines Images sollte man bei der Entscheidung, wie es einzubinden ist, noch berücksichtigen. Liegt die Datei auf einem USB-Stick, kann es zu Problemen beim Neustart der FRITZ!Box führen, wenn man ein Image von dort einhängt. Der Code zum Stoppen des USB-Subsystems weiß nichts davon/rechnet nicht damit, daß da noch ein solches Loop-Device gemountet sein könnte (jedenfalls bisher nicht, mal sehen ob sich das mit den Plugins wieder ändert) und damit hängt dann ggf. das Stoppen des USB-Hosts schon fest. Vor diesem Problem steht man naturgemäß nicht, wenn man das Paket vor dem Mounten ins
tmpfs kopieren läßt, dann kann der USB-Speicher bleiben, wo er will.
Eine Image-Datei (
ext2) für das Paket
dropbear, die vor dem Mounten ins
tmpfs ausgepackt werden soll und danach aber (prophylaktisch) nur
r/o eingebunden werden soll, hieße also:
dropbear_gz_tmpfs_ro.custom - wobei die Reihenfolge der Optionen hinter dem Paketnamen wahlfrei ist, bei widersprüchlichen Angaben (
ro_rw_tmpfs) ist das Verhalten undefiniert.
Da der Paketname unmittelbar in den Namen des Mountpoints einfließt (
/var/$package), kann es nicht zwei Pakete mit demselben Namen geben. Hier wird ein einzelnes Paket
gewinnen, auch da ist aber das Verhalten, welches das am Ende ist, per se undefiniert und man sollte sich auf nichts verlassen.
Was sollte ich bei der Auswahl des Dateisystemtyps für mein Image berücksichtigen?
Es werden vom originalen Skript nur drei Dateisystem-Typen unterstützt:
ext2,
ext3 (das ist ja auch nur ein ext2-Dateisystem mit Journal) und das - jeweils zum Kernel passende -
squashfs. Man muß sich also irgendwie eine Image-Datei in einem passenden Format erstellen.
Welches Dateisystem man dabei wählt, hängt von einigen Faktoren ab, die vielleicht am besten mit einem kleinen Vergleich der wichtigsten Eigenschaften für den angepeilten Einsatzzweck verdeutlichen kann:
Gesichtspunkt | ext2 | ext3 | squashfs |
änderbar | ja, wenn es an einer Stelle liegt, wo generell Schreiben möglich ist | ja, wenn es an einer Stelle liegt, wo generell Schreiben möglich ist | nein |
Platzbedarf der Image-Datei | 1:1 die (vorher festgelegte) Größe der Image-Datei beim Mounten, kann optional im Dateisystem trotzdem komprimiert werden | wie ext2, zusätzlich ist aber immer etwas weniger Platz im Image frei verfügbar, weil das Journal auch Platz braucht | die Daten werden - je nach SquashFS-Version - leicht (zlib) bis richtig heavy (lzma2) komprimiert und nehmen nur den Platz ein, der tatsächlich benötigt wird |
unempfindlich gegen plötzliche Neustarts, wenn beschreibbar | nein | ja, auf Kosten von Speicherplatz und I/O-Durchsatz, weil die zusätzlichen Schreiboperationen für das Journal ja auch erfolgen müssen ... allerdings sollten Schreibzugriffe auf solche Images (egal wo die liegen mögen) eher selten passieren
| gar nicht beschreibbar, damit stellt sich die Frage nicht |
Zum Punkt
Platzbedarf noch die Anmerkung, daß ja so ein
ext2-/ext3-Image immer vor dem Erstellen des Dateisystems in dieser Image-Datei schon auf die max. mögliche Größe zugeschnitten werden muß ... wer also 30 MB zusätzlichen Platzbedarf hat, der kriegt so eine Image-Datei nicht einmal mit "gut zureden" dann noch in der
wrapper-Partition unter. Um auch an dieser Stelle eine (leichte) Kompression zu ermöglichen, bietet das Skript auch die Möglichkeit (s.o.), eine mit
gzip komprimierte Image-Datei vor dem Mounten in das
tmpfs zu kopieren und dabei zu dekomprimieren, dabei entsteht natürlich auch wieder eine Datei der ursprünglichen Größe, was im
tmpfs bei Boxen mit weniger Hauptspeicher (die 128 MB-Modelle) ja auch nicht das Gelbe von Ei ist (Ostern war erst, da ist diese Phrase noch präsent). Das ist also ein Balance-Akt zwischen freiem Platz im Hauptspeicher und freiem Platz an der Stelle, wo das Image persistent gespeichert ist.
Zusätzlich kommt dann ja noch bei einigen (denkbaren) Paketen die Notwendigkeit dazu, spezielle Einstellungen für diese Box in einer Art und Weise zu verwalten, daß die einen Neustart überleben. Nun gibt es ja in so einer Box (auch abhängig von der verbauten Hardware) jede Menge denkbare Stellen, wo man solche Einstellungen ablegen könnte.
- TFFS-Node analog zum Vorgehen von Freetz, da werden diese Daten dann nicht gesichert, aber zumindest auch nicht gelöscht beim Werksreset, wenn man unter 100 bleibt
- TFFS-Node, der von AVM als Datei in die Sicherung/Wiederherstellung einbezogen wird ... die /var/flash/calllog wäre meines Wissens so eine Möglichkeit - sie wird immer noch gesichert (getestet) und wiederhergestellt (ungetestet) - sogar netterweise als Binärdatei - und ansonsten ignoriert neuere Firmware den Inhalt dieser Datei komplett; allerdings riskiert man bei diesem Vorgehen, daß AVM bei irgendeinem Update diese Sicherung/Wiederherstellung einstellt und dann schaut man in die Röhre
- Sicherung als Datei in der yaffs2-Partition unter /var/flash - diese existiert nur bei NAND-Modellen und hat m.W. bei diesen Modellen den Namen config (auch bei der 7412, wo sie aber nicht verwendet wird); der Platz dort ist allerdings begrenzt (~ 1 MB steht zur Verfügung), jedoch ist das im Vergleich zur max. Größe im TFFS (da sind 32 KB komprimiert die Obergrenze, weil die Längen nur als 2-Byte-Integer gespeichert sind) ja immer noch riesig
- Sicherung als Datei in der yaffs2-Partition unter /wrapper - da ist einerseits viel Platz, andererseits werden sie auch dort dann nicht (mit den AVM-Dateien zusammen) gesichert (wie im Punkt zuvor natürlich auch nicht)
- Sicherung als Datei in der yaffs2-Partition, die bei allen NAND-Modellen (und der 7390) unter /var/media/ftp gemountet ist, viel Platz (mind. 15 MB gesamt, selbst bei Boxen mit nur 128 MB NAND-Flash), keine automatische Sicherung
Bei einer beschreibbaren Image-Datei kommt dann noch die Möglichkeit hinzu, die Einstellungen direkt dort in der Image-Datei zu verwalten ... allerdings setzt das voraus, daß die Image-Datei unkomprimiert an einer Stelle im Dateisystem gespeichert wurde, wo sie veränderbar ist. Zwar erfüllt der Speicher unter
/var/media/ftp diese Anforderungen, aber die dazu notwendige unkomprimierte Speicherung des Images ist eigentlich eher ein "no-go" an dieser Stelle.
Ein "Spezialfall" ist dann noch die Verwendung der
yaffs2-Partition unter
/var/flash, um dort anstelle eines char-Devices für einen TFFS-Node eine reguläre Datei anzulegen, die wieder mehrfach so groß sein kann wie ein TFFS-Node und trotzdem ganz normal bei Sichern/Wiederherstellen gelesen und geschrieben wird.
Alles in allem spricht also wirklich wenig für die dauerhafte(!) Verwendung eines beschreibbaren Images, max. noch dann, wenn es eigentlich vom USB-Speicher kommt (was dann wieder andere Probleme mit sich bringen kann, aber das ist ein anderes Thema). Ich verwende(te) das meist auch nur, wenn es um das Bauen neuer Images geht/ging, weil man eben solange ändern kann, bis es endlich alles läuft und dann kann man immer noch ein SquashFS-Image mit maximaler Platzersparnis daraus machen.
Auch bei der Verwendung von Images, die über mehrere Modelle benutzbar sein sollen, ist (und war) die Verwendung von SquashFS nicht immer der beste Weg, weil es mit dem neuen Kernel zwar einen fundamentalen Unterschied gibt (SquashFS3 vs. SquashFS4), aber auch früher schon unterschiedliche Kompressionsverfahren (NAND-Modelle nur mit
zlib, NOR-Modelle mit
lzma) verwendet wurden. Zwar konnten die NAND-Modelle (zumindest die 7490) auch die
lzma-Kompression der NOR-Modelle mounten (oder es war umgekehrt, ist halt lange her und selten genutzt, hatte ich aber auch hier mal irgendwo im IPPF geschrieben), aber die Gegenrichtung funktioniert(e) dann meist ohnehin nicht mehr und so ist ein
ext2-Image an dieser Stelle der kleinste gemeinsame Nenner, auf den sich alle (halbwegs aktuellen) Boxen automatisch verstehen. Je nach Hauptspeicherausbau und verfügbarem Platz im persistenten Speicher kann es dann immer noch Sinn machen, das fertige Image mit
gzip vor der Speicherung dort zu komprimieren, dann braucht es halt zwingend den Platz im
tmpfs für die entpackte Version, bevor da irgendetwas gemountet werden kann.
Geht das auch weniger trocken/technisch ... hast Du nicht mal ein Beispiel zur Hand?
Nun kann man das ja auch mal praktisch angehen ... wir bauen uns jetzt aus den bereits andernorts von mir veröffentlichten Komponenten (
modfs-Archiv und
modfs-Starter-Archiv, zzgl. der
Vorlage für das Start-Skript) ein funktionierendes Image zusammen, das über die
E99-custom bei jedem Systemstart wieder einen
ShellInABox-Daemon aktiviert.
Zuerst bereiten wir dazu unsere Umgebung vor ... und wir beginnen an der Stelle auch tatsächlich mit der Vertreibung aus dem Paradies, damit es eine kontrollierte Umgebung wird. Natürlich kann jeder, der sich damit auskennt, das entsprechend seinen eigenen Bedürfnissen abändern.
bold sind eigene Eingaben ... die Ausgaben weichen ggf. ab, z.B. beim Prompt zur Eingabe.
Zuerst wechseln wir in das Verzeichnis
/var/media/ftp und legen uns dort ein Verzeichnis für
modfs an, bevor wir die Dateien von yourfritz.de laden und entsprechend entpacken.
Code:
root@FB7490:~ $ [B]cd /var/media/ftp[/B]
root@FB7490:/var/media/ftp $ [B]mkdir modfs[/B]
root@FB7490:/var/media/ftp $ [B]cd modfs[/B]
root@FB7490:/var/media/ftp/modfs $ [B]wget -qO- http://yourfritz.de/modfs.tgz | gunzip -c | tar xv[/B]
bin/
bin/212
bin/VR9/
bin/VR9/e2fsck
bin/VR9/mke2fs
bin/VR9/unsquashfs4
bin/VR9/unsquashfs3
bin/VR9/mksquashfs4
bin/VR9/mksquashfs3
bin/VR9/busybox
bin/VR9/busybox.config
bin/.gitattributes
bin/193
bin/203
bin/175
bin/185
files/
files/E99-custom
files/128MB_ext3.gz
locale/
locale/en
locale/de
modscripts/
modscripts/mod_enable_telnet
modscripts/mod_leddisplay
modscripts/mod_show_vpn_on_overview
modscripts/template
modscripts/yourfritz_hooks
modscripts/mod_mount_by_label
modscripts/mod_prefer_fonnumber_name
modscripts/gui_boot_manager_v0.2
modscripts/mod_show_name
modscripts/mod_default_show_mac
modscripts/edit_rcuser
modscripts/mod_rc_tail_sh
modscripts/dectcmds.modscript
modscripts/mod_custom_images
modscripts/mod_profile
modscripts/copy_binaries
modscripts/gui_boot_manager_v0.1
README.ger.outdated
README.outdated
LICENSE
BOOTSELECTION.ger
modfs
root@FB7490:/var/media/ftp/modfs $ [B]cd ..[/B]
root@FB7490:/var/media/ftp $ [B]wget -qO- http://yourfritz.de/inject_shellinabox_vr9_nand_sqfs4.tar | tar -O -xv ./var/tmp/filesystem.image >/tmp/siab.image[/B]
root@FB7490:/var/media/ftp $ [B]modfs/bin/VR9/unsquashfs4 -dest myimage -linfo /tmp/siab.image[/B]
Filesystem on /tmp/siab.image is xz compressed (4:0)
Parallel unsquashfs: Using 2 processors
5 inodes (24 blocks) to write
drwxr-xr-x root/root 59 2015-12-16 07:28 myimage
drwxr-xr-x root/root 30 2015-12-15 10:23 myimage/bin
-rwxr-xr-x root/root 917748 2015-12-15 10:23 myimage/bin/busybox
drwxr-xr-x root/root 29 2015-12-15 13:05 myimage/etc
drwxr-xr-x root/root 32 2015-12-15 13:37 myimage/etc/init.d
-rw-r--r-- root/root 727 2015-12-15 13:37 myimage/etc/init.d/rc.custom
drwxr-xr-x root/root 47 2015-12-15 10:27 myimage/lib
-rwxr-xr-x root/root 9298 2015-12-15 10:27 myimage/lib/libprivatekeypassword.so
drwxr-xr-x root/root 26 2015-12-15 10:24 myimage/usr
drwxr-xr-x root/root 61 2015-12-15 10:24 myimage/usr/bin
-rwxr-xr-x root/root 4688 2015-12-15 10:24 myimage/usr/bin/privatekeypassword
-rwxr-xr-x root/root 339964 2015-12-16 06:57 myimage/usr/bin/shellinaboxd
created 5 files
created 7 directories
created 0 symlinks
created 0 devices
created 0 fifos
Nun entfernen wir das im modfs-Starter-Image enthaltene Start-Skript und ersetzen es durch das besser auf die Verwendung mit
E99-custom zugeschnittene aus dem GitHub-Repository. Da es ohne passendes
wget auf der FRITZ!Box nicht funktioniert, diese Datei von github.com zu laden, greifen wir wieder auf die Kopie auf yourfritz.de zurück.
Code:
root@FB7490:/var/media/ftp $ [B]rm myimage/etc/init.d/rc.custom[/B]
root@FB7490:/var/media/ftp $ [B]wget -qO- http://yourfritz.de/rc.template >myimage/etc/init.d/rc.shellinaboxd[/B]
Nachdem wir nun die Zutaten beisammen und das Gemüse ordentlich geputzt haben, geht es an die Zubereitung ... dazu ist es erst einmal notwendig, die Datei
myimage/etc/init.d/rc.shellinaboxd mit einem Editor zu bearbeiten. Wie man das macht, ist jedem selbst überlasssen, die FRITZ!Box bietet mit AVM-Firmware nur den Editor
vi dafür an. Das Ergebnis nach dem Editieren sollte dann so aussehen, die Änderungen ggü. der originalen
rc.template sind grün hervorgehoben:
Code:
#! /bin/sh
#####################################################################################
# #
# initialization script template - try to keep it independent from fixed path and #
# file names to make it reusable from different image sources and with different #
# image filesystem types #
# #
#####################################################################################
my_script="$0"
if [ ${#PKG_NAME} -gt 0 ]; then # environment variable set, no need to grab it
my_package="$PKG_NAME"
else # grab the package name from our mountpoint ... it's the part before "/etc/"
my_package="${my_script%/etc/*}"
my_package="${my_package##*/}"
fi
my_name="${my_script##*/}"
my_environ="${my_script%%/$my_package/*}/${my_package}.${my_name}.environ"
my_check="$(sed -n -e "s|^PKG_\([^_]*\)_SCRIPT=\"$my_script\"\$|\1|p" $my_environ)"
if [ x$my_check != x$my_package ]; then
echo "Unable to find environment file." 1>&2
exit 1
fi
#####################################################################################
# #
# our service definitions #
# #
#####################################################################################
my_service="[COLOR="#008000"]shellinaboxd[/COLOR]"
my_display="[COLOR="#008000"]Shell in a Box[/COLOR]"
my_executable="[COLOR="#008000"]/usr/bin/shellinaboxd[/COLOR]"
my_pidfile="[COLOR="#008000"]/var/run/${my_service}.pid[/COLOR]"
my_executable_params="[COLOR="#008000"]--user=0 --group=0[/COLOR]"
[COLOR="#008000"]my_executable_params="$my_executable_params --cert-from-box"
my_executable_params="$my_executable_params --background"
my_executable_params="$my_executable_params --pidfile=$my_pidfile"
my_executable_params="$my_executable_params --no-sni"
my_executable_params="$my_executable_params --disable-ssl-menu"
my_executable_params="$my_executable_params --service=/:0:0:\$CFG_HOMEDIR:\$CFG_LOGIN"
my_executable_params="$my_executable_params --port=\$CFG_PORT"[/COLOR]
#####################################################################################
# #
# our configuration variables, feel free to set them from other sources #
# #
#####################################################################################
[COLOR="#008000"]CFG_PORT=8010
CFG_HOMEDIR=/var/media/ftp
CFG_LOGIN=/sbin/ar7login[/COLOR]
#####################################################################################
# #
# internal subfunctions #
# #
#####################################################################################
# #
# securely read the settings from our environment file - read each single expected #
# item instead to source the file without any knowledge, what it contains #
# #
#####################################################################################
secure_load_environment_file()
{
local envfile="$1" package="$2" var val
local vars="SCRIPT IMAGE MOUNTEDIMAGE MOUNTPOINT MOUNTMODE IMAGE_FSTYPE IMAGE_IS_VOLATILE"
for var in $vars; do
val=$(sed -n -e "s|^PKG_${package}_${var}=\"\(.*\)\"\$|\1|p" $envfile)
echo "PKG_${var}=\"${val}\""
done
}
#####################################################################################
# #
# start our service(s) #
# #
#####################################################################################
start_service()
{
export LD_LIBRARY_PATH="${PKG_MOUNTPOINT}/lib:$LD_LIBRARY_PATH"
eval $PKG_MOUNTPOINT$my_executable $my_executable_params
}
#####################################################################################
# #
# stop our service(s) #
# #
#####################################################################################
stop_service()
{
local pid=$(cat $my_pidfile 2>/dev/null)
if [ ${#pid} -gt 0 ]; then
if [ -d /proc/$pid ]; then
kill $pid
wait $pid
fi
fi
rm $my_pidfile 2>/dev/null
}
#####################################################################################
# #
# end of internal subfunctions #
# #
#####################################################################################
# #
# load our environment file #
# #
#####################################################################################
tf=/tmp/$$_$(date +%s).source
secure_load_environment_file "$my_environ" "$my_package" >$tf
source $tf
rm $tf 2>/dev/null
#####################################################################################
# #
# check our parameters #
# #
#####################################################################################
op="$1"
shift
case "$op" in
start)
start_service $*
;;
stop)
stop_service $*
;;
restart)
stop_service $*
start_service $*
;;
*)
echo "Unknown parameter '$1' specified." 1>&2
exit 1
;;
esac
exit 0
EDIT: Und da mir noch aufgefallen ist, daß das neue Image unsinnigerweise auch noch eine (schon ältere) Busybox enthält (das modfs-Starter-Image ist eben schon älter und ich habe kein Interesse daran, es zu aktualisieren), sparen wir gleich noch 2/3 des Platzes, den das Image ansonsten belegen würde und entfernen vor dem Einpacken mit einem kühnen
rm myimage/bin/busybox noch dieses Relikt, bevor wir fortfahren. /EDIT
Jetzt packen wir uns das ganze Verzeichnis
myimage zu einer SquashFS-Imagedatei zusammen, diese erzeugen wir gleich in
/var/media/ftp, was ja von
E99-custom mit Standardeinstellungen auch durchsucht wird. Den Namen wählen wir dabei so, daß die Datei vor dem Mounten erst ins
tmpfs kopiert wird, damit können wir die originale Image-Datei jederzeit durch eine neue ersetzen.
Code:
root@FB7490:/var/media/ftp $ [B]modfs/bin/VR9/mksquashfs4 myimage shellinaboxd_tmpfs.custom[/B]
Parallel mksquashfs: Using 2 processors
Creating 4.0 filesystem on shellinaboxd_tmpfs.custom, block size 65536.
[===========================================================-] 24/24 100%
Exportable Squashfs 4.0 filesystem, xz compressed, data block size 65536
compressed data, compressed metadata, compressed fragments, no xattrs
duplicates are removed
Filesystem size 517.44 Kbytes (0.51 Mbytes)
41.43% of uncompressed filesystem size (1249.06 Kbytes)
Inode table size 278 bytes (0.27 Kbytes)
59.15% of uncompressed inode table size (470 bytes)
Directory table size 206 bytes (0.20 Kbytes)
76.01% of uncompressed directory table size (271 bytes)
Number of duplicate files found 0
Number of inodes 12
Number of files 5
Number of fragments 1
Number of symbolic links 0
Number of device nodes 0
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 7
Number of ids (unique uids + gids) 1
Number of uids 1
root (0)
Number of gids 1
root (0)
Das sollte bereits ausreichen, damit unsere neue Image-Datei beim nächsten Start der FRITZ!Box als Erweiterungspaket erkannt und behandelt wird. Will man die Datei lieber in der
wrapper-Partition ablegen, führt man noch die folgenden Kommandos aus:
Code:
root@FB7490:/var/media/ftp $ [B]mount -o remount,rw /wrapper/[/B]
root@FB7490:/var/media/ftp $ [B]cp -a shellinaboxd_tmpfs.custom /wrapper/[/B]
root@FB7490:/var/media/ftp $ [B]mount -o remount,ro /wrapper/[/B]
Wo sollte ich denn meine eigenen Image-Dateien am besten speichern?
Jeder der bisher von mir beschriebenen möglichen Ablageorte für Image-Dateien hat seine Vor- und Nachteile. Welchen man also am Ende wählt, hängt auch davon ab, was man erreichen will und wie man sich insgesamt seinen Umgang mit der FRITZ!Box vorstellt.
Die folgenden Plätze kommen für die Image-Dateien selbst in Frage, für die Einstellungen steht das ja schon etwas weiter vorne:
/wrapper
Das dort liegende
yaffs2-Dateisystem steht nur auf FRITZ!Boxen zur Verfügung, die ihr Dateisystem im NAND-Flash verwalten (und dann noch "hot-flash capable" sind, also zwei Systemversionen verwenden) ... also im Prinzip genau auf den Modellen, für die
modfs auch funktioniert. Allerdings sind die Partitionen von AVM mit 48 MB für das Dateisystem recht üppig dimensioniert, in einer unmodifizierten Firmware für die 7490 sind derzeit weniger als 50% dieser Partition belegt. Das bietet zwar jede Menge sinnvoll nutzbaren Speicherplatz, allerdings gibt es eben pro Systemversion eine eigene Partition und nach der Umschaltung auf das andere System oder nach einem Update (bei dem das yaffs2-Dateisystem ebenfalls neu geschrieben wird), sind die eigenen Image-Dateien dann auch wieder weg. Dafür kann man eben für jedes System eigene Versionen von Images verwenden, wenn man ab und an mal zwischen den vorhandenen Systemen umschaltet (z.B. wäre ein System mit einer VPN-Lösung über einen Anbieter oder mit Software für's Onion-Routing in der einen und mit einem "normalem" FRITZ!OS in der anderen Partition denkbar).
/var/media/ftp
Das unter diesem Pfad eingebundene
yaffs2-Dateisystem gibt es auch nur bei den NAND-Modellen bzw. bei der 7390 (wo sogar mehr Platz verfügbar ist, weil die System-Partitionen nicht im NAND-Flash liegen bei diesem Modell). Dafür steht dieses Dateisystem in beiden möglichen Systemversionen eines solchen Gerätes zur Verfügung und es wird auch bei einem Update nicht in Mitleidenschaft gezogen. Genauso wie
/wrapper bei passenden Modellen ist dieser Speicher auch immer vorhanden, egal was der Benutzer anstellt (im Gegensatz zu einem USB-Storage-Device). Jedoch wird auch hier beim nächsten Start nach dem Zurücksetzen auf Werkseinstellungen oder nach Recovery der komplette Inhalt des Dateisystems gelöscht, damit natürlich auch dort abgelegte Erweiterungspakete.
zusätzlicher Speicherplatz auf einem USB-Gerät, erreichbar unterhalb von /var/media/ftp/irgendwas
Dieser Speicher steht eben nur dann zur Verfügung, wenn das betreffende USB-Gerät auch vorhanden ist und mit etwas Pech braucht es auch länger, entsprechende USB-Volumes einzubinden. Das würde dann dazu führen, daß (die originale)
E99-custom schon abgearbeitet wurde, wenn das Volume verfügbar wird. Dagegen werden hier keinerlei Vorkehrungen getroffen, daher ist so ein USB-Speicher auch nicht unbedingt der ideale Ablageort für Erweiterungspakete, zumindest nicht als primäre Quelle. Auch erfordert die Einbeziehung solcher Verzeichnisse (deren Namen eben variieren) dann entweder bereits im Vorfeld Änderungen an der
E99-custom (Variable
custom_sources) oder man muß wirklich die
custom-Variable im Urlader-Environment bei der
kernel_args-Einstellung setzen. Dafür ist dieser Speicher dann sogar gegen Werkseinstellungen und Recovery immun.
Ich selbst benutze eine Mischung aus der Speicherung in
/wrapper und
/var/media/ftp (daher auch diese Voreinstellung in der
E99-custom, auch in dieser Reihenfolge, weil ein Paket in
/wrapper dabei vor einem in
/var/media/ftp gefunden wird und ja nur ein Paket gleichen Namens initialisiert wird), wobei eben in
/var/media/ftp die Pakete liegen, die für beide Systeme verwendet werden sollen und in
/wrapper die systemspezifischen.
Einstellungen (der Teil ist in der hier veröffentlichten Version noch recht unterentwickelt) speichere ich in einer Datei (komprimiertes tar-Archiv), die ich anstelle der Datei (bzw. des char-Devices)
/var/flash/calllog als reguläre Datei oder als Symlink auf einen passenden Speicherort einbinde (das erfordert noch eine passende Vorbereitung bei jedem Start oder eine Änderung des Startprozesses von AVM, damit das char-Device nicht mehr angelegt wird und ggf. eine reguläre Datei desselben Namens gelöscht wird).
Damit lassen sich auch die Einstellungen für Erweiterungen ganz normal über das AVM-GUI sichern, auch in weit größerem Umfang, als es bei einer Speicherung in einem TFFS-Node überhaupt möglich wäre. Auch der Export von 2 MB in einer solchen Einstellungsdatei ist kein wirkliches Problem (ergibt halt ein recht großes export-File), beim Wiederherstellen mußte ich das noch nie versuchen, weil ich es immer von Hand gemacht habe bzw. ohnehin dann nur einzelne Einstellungen dort wiederherstellen wollte und das kann natürlich das AVM-GUI dann auch nicht, das schreibt entweder alles oder gar nichts.
Eigentlich soll diese Modifikation (also
mod_custom_images gesamt) so etwas wie der nächste Schritt auf dem Weg zu einer wirklich dynamischen Verwaltung solcher Erweiterungspakete inkl. einer passenden HTML-Oberfläche für diese Zwecke sein, damit man dann nicht mehr zu einem Shell-Zugang greifen muß, um (vorgefertigte) Erweiterungspakete zu "installieren".