Subsections

8.3 Modulkonzept

fli4l wird seit der Version 2.0 in Module (Pakete) aufgeteilt, z.B.

Mit dem Basis-Paket ist fli4l ein reiner Ethernet-Router. Für ISDN und/oder DSL ist das Paket isdn und/oder dsl in dem fli4l-Verzeichnis auszupacken. Entsprechendes gilt für die anderen Pakete.


8.3.1 mkfli4l

Aus den Paketen wird in Abhängigkeit von der konkreten Konfiguration eine Konfigurationsdatei namens rc.cfg und zwei Archive namens rootfs.img und opt.img erstellt, die alle Konfigurationsinformationen und alle benötigten Dateien enthalten. Diese Dateien werden mit Hilfe von mkfli4l erzeugt, welches die einzelnen Pakete einliest und auf Fehler in der Konfiguration prüft.

mkfli4l akzeptiert die in Tabelle 8.1 angegebenen Parameter. Fehlen sie, werden die in Klammern angegebenen default-Werte genommen. Eine vollständige Liste der Optionen (Tabelle 8.1) erhält man, wenn man

    mkfli4l -h
aufruft.


Table: Parameter für mkfli4l
Option Bedeutung
-c, - -config Setzen des Verzeichnisses, in dem mkfli4l die config-Dateien der Pakete sucht (default: config)
-x, - -check Setzen des Verzeichnisses, in dem mkfli4l die zum Prüfen der Pakete benötigten Dateien sucht (<package>.txt, <package>.exp und <package>.ext; default: check)
-l, - -log Setzen der Logdatei; mkfli4l protokolliert Fehlermeldungen und Warnungen in dieser Datei (default: img/mkfli4l.log)
-p, - -package Angabe der Pakete, die geprüft werden sollen, diese Option kann mehrmals angegeben werden, wenn man mehrere Pakete im Zusammenhang prüfen will. Bei Verwendung von -p wird allerdings grundsätzlich zuerst die Datei <check_dir>/base.exp eingelesen, um die allgemeinen regulären Ausdrücke, die vom Basis-Paket bereitgestellt werden, zur Verfügung zu stellen. Diese Datei muss also existieren.
-i, - -info Gibt Auskunft über den Verlauf der Prüfung (welche Dateien werden gelesen, welche Prüfungen werden durchgeführt, welche besonderen Dinge traten während des Prüfprozesses auf)
-v, - -verbose Ausführlichere Variante von -i
-h, - -help Zeigt die Hilfe an
-d, - -debug Gestattet das Debuggen des Prüfprozesses. Dies ist als Hilfe für Paketentwickler gedacht, die etwas genauer wissen möchten, wie die Prüfung des Paketes abläuft.
Debugoption Bedeutung
check show check process
zip-list show generation of zip list
zip-list-skipped show skipped files
zip-list-regexp show regular expressions for ziplist
opt-files check all files in opt/<package>.txt
ext-trace show trace of extended checks

8.3.2 Aufbau

Ein Paket kann mehrere OPTs enthalten, wenn es aber nur eins enthält, ist es allerdings zweckmäßig, das Paket genauso wie das OPT zu nennen. Im Folgenden ist <PACKAGE> durch den jeweiligen Paket-Namen zu ersetzen. Ein Paket besteht aus folgenden Teilen:

Die einzelnen Teile sind im Folgenden näher beschrieben.

8.3.3 Die Konfiguration der Pakete

In der Datei config/<PACKAGE>.txt werden vom Benutzer Änderungen an der Konfiguration des Pakets vorgenommen. Alle Variablen eines OPTs sollten einheitlich mit dem Namen des OPTs beginnen, also zum Beispiel:

    #-------------------------------------------------------------------
    # Optional package: TELNETD
    #-------------------------------------------------------------------
    OPT_TELNETD='no'        # install telnetd: yes or no
    TELNETD_PORT='23'       # telnet port, see also FIREWALL_DENY_PORT_x

Ein OPT sollte in der Konfigurationsdatei durch einen Header (siehe oben) entsprechend abgegrenzt werden. Dies erhöht die Übersichtlichkeit, zumal ein Paket ja auch mehrere OPTs enthalten kann. Die dem OPT zugehörigen Variablen sollten -- ebenfalls im Interesse der Übersichtlichkeit -- nicht weiter eingerückt werden. Kommentare und Leerzeilen sind erlaubt, wobei Kommentare einheitlich in Spalte 33 beginnen sollen. Ist eine Variable inklusive ihrer Belegung länger als 32 Zeichen, ist der Kommentar eine Zeile versetzt ab Spalte 33 einzufügen. Längere Kommentare werden jeweils ab Spalte 33 beginnend auf mehrere Zeilen aufgeteilt. Diese Maßnahmen sollen die Lesbarkeit der Konfigurationsdatei erhöhen.

Alle Werte hinter dem Gleichheitszeichen müssen in Hochkommata8.1 eingefasst werden, da es sonst beim Booten zu Problemen kommen kann.

Variablen, die aktiv sind (s.u.), werden in die rc.cfg übernommen, alles andere wird ignoriert. Einzige Ausnahme sind Variablen mit dem Namen <PACKAGE>_DO_DEBUG. Diese dienen zur Fehlersuche in Paketen und werden pauschal übernommen.


8.3.4 Die Liste der zu kopierenden Dateien

Die Datei opt/<PACKAGE>.txt enthält Anweisungen, die beschreiben

mkfli4l generiert darauf basierend die erforderlichen Archive.

Leere Zeilen und Zeilen, die mit "`#"' anfangen, werden ignoriert. In einer der ersten Zeilen sollte die Version des Paket-Dateiformats wie folgt stehen:

    <erste Spalte>      <zweite Spalte> <dritte Spalte>
    opt_format_version  1                    -

Die restlichen Zeilen haben folgende Syntax:

    <erste Spalte>  <zweite Spalte> <dritte Spalte> <folgende spalten>
    Variable        Wert            Datei           Optionen

  1. In der ersten Spalte steht der Name einer Variable, von deren Wert das Übernehmen der in der dritten Spalte stehenden Datei abhängt. Der Name einer Variable kann beliebig oft in der ersten Spalte auftauchen, falls mehrere Dateien von ihr abhängen. Jede Variable, die in der opt/<PACKAGE>.txt-Datei auftaucht, wird von mkfli4l markiert.

    Falls mehrere Variablen auf denselben Wert geprüft werden sollen, kann auch eine Liste von Variablen (durch Kommata getrennt) verwendet werden. In diesem Falle reicht es aus, wenn mindestens eine Variable den in der zweiten Spalte geforderten Wert enthält. Wichtig ist dabei, dass zwischen den einzelnen Variablen keine Leerzeichen stehen!

    Bei OPT-Variablen (also Variablen, die mit OPT_ beginnen und typischerweise den Typ YESNO haben) kann das Präfix "`OPT_"' weggelassen werden. Des Weiteren ist es unwichtig, ob Variablen in Groß- oder in Kleinbuchstaben (oder beliebig gemischt) notiert werden.

  2. In der zweiten Spalte steht ein Wert. Stimmt die in der ersten Spalte stehende Variable mit diesem Wert überein und ist die Variable aktiv (s.u.), wird die Datei in der dritten Spalte übernommen. Steht eine %-Variable in der ersten Spalte, wird über alle Indizes iteriert und geprüft, ob die jeweilige Variable mit dem Wert übereinstimmt. Ist das der Fall, wird kopiert. Zusätzlich wird vermerkt, dass aufgrund des aktuellen Wertes der Variable eine Datei kopiert wurde.

    Es ist möglich, vor den Wert ein "`!"' zu schreiben. In diesem Falle wird die Prüfung negiert, d.h. die Datei wird genau dann kopiert, wenn die Variable diesen Wert nicht enthält.

  3. In der dritten Spalte steht der Name einer Datei. Die Pfadangabe erfolgt relativ zum opt-Verzeichnis. Die Datei muss existieren und lesbar sein, sonst gibt es beim Generieren des Bootmediums einen Fehler und die Generierung wird abgebrochen.

    Beginnt der Dateiname mit einem "`rootfs:"'-Präfix, wird die Datei in die Liste der ins RootFS aufzunehmenden Dateien übernommen. Der Präfix wird vorher entfernt.

    Liegt die Datei unterhalb der aktuellen Konfigurationsverzeichnisses, wird sie in die Liste der aus dem Konfigurationsverzeichnis zu übernehmenden Dateien aufgenommen, andernfalls wird die unter opt/ liegende Datei genommen. Die Datei darf dann kein rootfs:-Präfix haben.

    Ist die zu kopierende Datei ein Kernel-Modul, kann man die konkrete Kernel-Version durch ${KERNEL_VERSION} ersetzen. mkfli4l nimmt dann die Version aus der Konfiguration und setzt sie hier ein. Dadurch kann man einem Paket Module für verschiedene Kern-Versionen mitgeben, und es wird immer die für den Kern richtige Version auf den Router kopiert. Für Kernel-Module kann der Pfad auch vollkommen entfallen, mkfli4l findet das Modul anhand von modules.dep und modules.alias, siehe den Abschnitt "`Automatische Auflösung von Abhängigkeiten für Kernel-Module"'.


    Table 8.2: Optionen für Dateien
    Option Bedeutung Standardwert
    type= Der Typ des Eintrags:

    local Dateisystem-Objekt
    file Datei
    dir Verzeichnis
    node Gerät
    symlink (symbolische) Verknüpfung


    Wenn vorhanden, muss diese Option an erster Stelle stehen. Der Typ "`local"' steht hierbei für den Typ eines im Dateisystem existierenden Objekts und entspricht somit "`file"', "`dir"', "`node"' oder "`symlink"' (je nachdem). Die anderen Typen mit Ausnahme von "`file"' können Einträge im Archiv erzeugen, die nicht im lokalen Dateisystem vorliegen müssen. Das wird z.B. benutzt, um Gerätedateien im RootFS-Archiv anzulegen.
    local
    uid= Der Eigentümer der Datei, entweder numerisch oder als Name aus passwd root
    gid= Die Gruppe der Datei, entweder numerisch oder als Name aus group root
    mode= Die Zugriffsrechte Dateien und Geräte:
    rw-r--r-- (644)
    Verzeichnisse:
    rwxr-xr-x (755)
    Verknüpfungen:
    rwxrwxrwx (777)
    flags=
    (type=file)
    Konvertierungen vor der Aufnahme ins Archiv:

    utxt Konvertierung ins Unix-Format
    dtxt Konvertierung ins DOS-Format
    sh Shell-Skript: Konvertierung ins Unix-Format, Entfernen überflüssiger Zeichen
    name= Alternativer Name, unter dem der Eintrag ins Archiv aufgenommen wird
    devtype=
    (type=node)
    Beschreibt den Typ des Geräts ("`c"' für zeichenorientierte und "`b"' für blockorientierte Geräte). Muss an zweiter Stelle stehen.
    major=
    (type=node)
    Beschreibt die so genannte "`Major"'-Nummer der Gerätedatei. Muss an dritter Stelle stehen.
    minor=
    (type=node)
    Beschreibt die so genannte "`Minor"'-Nummer der Gerätedatei. Muss an vierter Stelle stehen.
    linktarget=
    (type=symlink)
    Beschreibt das Ziel der symbolischen Verknüpfung. Muss an zweiter Stelle stehen.

  4. In den anderen Spalten können die in Tabelle 8.2 aufgeführten Optionen für den Eigentümer, die Gruppe, die Rechte der Dateien und Konvertierungen stehen.

Einige Beispiele:

Die Dateien werden nur kopiert, wenn die oben genannten Bedingungen erfüllt sind und das dazugehörige OPT_PACKAGE='yes' gesetzt ist. Welche OPT-Variable dazugehört, wird über die Datei check/<PACKAGE>.txt beschrieben.

Wenn im Paket eine Variable referenziert wird, die nicht vom Paket selbst definiert wird, kann es passieren, dass das entsprechende Paket nicht installiert ist. Das würde zu einer Fehlermeldung in mkfli4l führen, da es erwartet, dass alle von opt/<PACKAGE>.txt referenzierten Variablen definiert sind.

Um diese Situation korrekt handhaben zu können, wurde die "`weak"'-Deklaration eingeführt. Sie hat das folgende Format:

    weak        <Variable>    -

Dadurch wird die Variable definiert, wenn sie nicht bereits vorhanden ist und ihr Wert wird auf "`undefiniert"' gesetzt. Dabei ist jedoch zu beachten, dass hier das "`OPT_"'-Präfix nicht weggelassen werden darf (falls es existiert), weil sonst eine Variable ohne dieses Präfix definiert wird.

Ein Beispiel aus der opt/rrdtool.txt:

    weak opt_openvpn -
    [...]
    openvpn    yes    files/usr/lib/collectd/openvpn.so

Ohne die weak-Definition würde mkfli4l bei der Nutzung des Pakets "`rrdtool"' eine Fehlermeldung anzeigen, wenn das "`openvpn"'-Paket nicht ebenfalls vorliegt. Mit Hilfe der weak-Definition kommt auch in dem Fall, dass das "`openvpn"'-Paket nicht vorliegt, keine Fehlermeldung.


8.3.4.1 Konfigurations-spezifische Dateien

In manchen Situationen möchte man originale Dateien im Archiv durch konfigurationsspezifische Dateien wie z.B. Host-Keys, eigene Firewall-Skripte, ... ersetzen. mkfli4l unterstützt dieses Szenario, indem es prüft, ob eine zu kopierende Datei im Konfigurationsverzeichnis zu finden ist und übernimmt in diesem Falle diese Datei in die Liste der ins opt.img bzw. rootfs.img aufzunehmenden Dateien.

Eine weitere Möglichkeit, konfigurationsspezifische Dateien ans Archiv anzuhängen wird im Abschnitt Erweiterte Prüfungen der Konfiguration beschrieben.


8.3.4.2 Automatische Auflösung von Abhängigkeiten für Kernel-Module

Kernel-Module bauen unter Umständen auf anderen Kernel-Modulen auf. Diese müssen vor ihnen geladen werden und daher gleichfalls in das Archiv aufgenommen werden. mkfli4l bestimmt diese Abhängigkeiten anhand von modules.dep und modules.alias (zweier beim Kernel-Bau generierter Dateien) und nimmt automatisch alle benötigten Module in die Archive auf. So führt z.B. folgender Eintrag

    net_drv_%   ne2k-pci    ne2k-pci.ko

dazu, dass sowohl 8390.ko als auch crc32.ko ins Archiv aufgenommen werden, da ne2k_pci von beiden abhängt.

Die notwendigen Einträge in modules.dep und modules.alias werden in das RootFS mit aufgenommen und können von modprobe zum Laden der Treiber genutzt werden.


8.3.5 Die Prüfung der Konfiguration-Variablen

Mit Hilfe der Datei check/<PACKAGE>.txt können die Inhalte der Variablen auf Gültigkeit überprüft werden. Diese Überprüfung war in früheren Versionen fest im Programm mkfli4l eingebaut, wurde aber im Zuge der Modularisierung von fli4l in die Check-Dateien ausgelagert. In dieser Datei ist für jede Variable aus den Konfigurationsdateien eine Zeile vorhanden. Diese Zeilen bestehen aus vier bis fünf Spalten, welche folgende Funktionen haben:

  1. Variable: Diese Spalte gibt den Namen der zu überprüfenden Variable aus der Konfigurationsdatei an. Wenn es sich dabei um eine so genannte Array-Variable handelt, die mehrmals mit verschiedenen Indizes auftauchen kann, wird an Stelle der Nummer ein Prozentzeichen (%) in den Variablenname eingefügt. Dieses wird immer als "`_%_"' in der Mitte eines Namens bzw. "`_%"' am Ende eines Namens verwendet. Der Name kann dabei mehrere Prozentzeichen enthalten, so dass man auch mehrdimensionale Arrays realisieren kann. Dann sollte zwischen den Prozentzeichen allerdings etwas stehen, muss aber nicht, was dann allerdings zu so seltsamen Namen wie "`FOO_%__%"' führt.

    Oftmals hat man das Problem, dass bestimmte Variablen Optionen beschreiben, die man nur in bestimmten Situationen benötigt. Deshalb können Variablen als optional markiert werden. Optionale Variablen werden mit einem vorangestellten "`+"' gekennzeichnet. Sie können dann da sein, müssen aber nicht. Arrays können auch mit einem "`++"' Präfix versehen werden. Steht ein "`+"' davor, kann das Array da sein oder ganz fehlen. Steht "`++"' davor, können zusätzlich auch noch einzelne Elemente des Arrays fehlen.

  2. OPT_VARIABLE: Diese Spalte teilt die Variable einem bestimmten OPT zu. Die Variable wird nur auf Gültigkeit überprüft, wenn die hier angegebene Variable auf "`yes"' steht. Gibt es keine OPT-Variable, ist hier ein "`-"' anzugeben. In diesem Fall muss die Variable in der Konfigurationsdatei definiert werden, es sei denn, es wird eine Standard-Belegung definiert (s.u.). Der Name der OPT-Variable kann beliebig sein, er sollte jedoch mit dem Präfix "`OPT_"' beginnen.

    Falls eine Variable von keiner OPT-Variablen abhängt, gilt sie als aktiv. Falls sie von einer OPT-Variablen abhängig ist, ist sie genau dann aktiv, wenn

    Andernfalls ist die Variable inaktiv.

    Hinweis: Inaktive OPT-Variablen werden, wenn sie in der Konfiguration mit "`yes"' belegt werden, auf den Wert "`no"' zurückgesetzt; dies wird von mkfli4l auch mit einer entsprechenden Warnmeldung (bspw. "`OPT_Y='yes' ignored, because OPT_X='no'"') kommentiert. Bei transitiven Abhängigkeitsketten (OPT_Z hängt von OPT_Y ab, das wiederum von OPT_X abhängt) funktioniert dies aber nur dann zuverlässig, wenn die Namen aller OPT-Variablen mit "`OPT_"' beginnen.

  3. VARIABLE_N: Steht in der ersten Spalte eine Variable mit einem % im Namen, wird hier die Variable angegeben, die die Häufigkeit des Auftretens der Variable beschreibt (die so genannte N-Variable). Ist die Variable mehrdimensional, wird die Häufigkeit des letzten Index beschrieben. Hängt die Variable von einem OPT ab, muss die N-Variable vom selben OPT oder von keinem OPT abhängig sein. Ist die Variable von keinem OPT abhängig, darf auch die N-Variable von keinem OPT abhängig sein. Gibt es keine N-Variable, ist hier ein "`-"' anzugeben.

    Aus Kompatibilitätsgründen mit zukünftigen fli4l-Versionen muss die hier angegebene Variable identisch sein mit der Variable in OPT_VARIABLE, wobei das letzte "`%"' durch ein "`N"' ersetzt und alles dahinter entfernt wurde. Ein Array HOST_%_IP4 bekommt also zwingend die N-Variable HOST_N zugewiesen und ein Array PF_USR_CHAIN_%_RULE_% also die N-Variable PF_USR_CHAIN_%_RULE_N, und diese N-Variable ist selbst wieder eine Array-Variable mit der zugehörigen N-Variable PF_USR_CHAIN_N. Alle anderen Benennungen der N-Variable werden mit zukünftigen fli4l-Versionen inkompatibel sein!

  4. VALUE: Diese Spalte gibt an, welche Werte für diese Variable eingegeben werden können. Es sind dabei z.B. folgende Angaben möglich:

    Name Bedeutung
    NONE Es wird keine Überprüfung vorgenommen
    YESNO Die Variable muss "`yes"' oder "`no"' sein
    NOTEMPTY Die Variable darf nicht leer sein
    NOBLANK Die Variable darf kein Leerzeichen enthalten
    NUMERIC Die Variable muss numerisch sein
    IPADDR Die Variable muss eine IP-Adresse sein
    DIALMODE Die Variable muss "`on"', "`off"' oder "`auto"' sein

    Werden die Werte mit einem "`WARN_"'-Präfix versehen, so führt ein illegaler Wert nicht zu einer Fehlermeldung und damit zu einem Abbruch von mkfli4l, sondern nur zur Ausgabe einer Warnung.

    Die möglichen Prüfungen werden durch reguläre Ausdrücke in check/base.exp definiert. Diese Datei kann erweitert werden und enhält neuerdings z.B. zusätzlich folgende Prüfungen: HEX, NUMHEX, IP_ROUTE, DISK und PARTITION.

    Die Anzahl der Ausdrücke kann jederzeit erweitert werden, hier ist Rückmeldung von den Paket-Entwicklern erforderlich.

    Zusätzlich können reguläre Ausdrücke auch direkt in den Check-Dateien angegeben werden, wobei man auch Bezug auf existierende Ausdrücke nehmen kann. Statt YESNO könnte man z.B. auch

        RE:yes|no
    
    schreiben. Sinnvoll ist es dann, wenn ein Test nur ein einziges Mal ausgeführt wird und relativ einfach ist. Für genauere Informationen siehe nächstes Kapitel.

  5. Standard-Belegung: In dieser Spalte kann optional ein Standard-Wert für die Variable stehen, falls die Variable nicht in der Konfiguration steht.

    Hinweis: Dies funktioniert zur Zeit jedoch nicht für Array-Variablen. Auch darf die Variable nicht optional sein, es darf also kein "`+"' vor dem Variablennamen stehen.

    Beispiel:

        OPT_TELNETD     -      -      YESNO    "no"
    

    Fehlt OPT_TELNETD nun in der Konfigurationsdatei, wird "`no"' angenommen und dieser Wert auch in die rc.cfg geschrieben.

Die Sache mit dem Prozentzeichen lässt sich am Besten mit einem Beispiel erklären. Nehmen wir an, in der check/base.txt steht:

    NET_DRV_N          -                  -                  NUMERIC
    NET_DRV_%          -                  NET_DRV_N          NONE
    NET_DRV_%_OPTION   -                  NET_DRV_N          NONE

Das heißt, dass je nach Wert von NET_DRV_N die Variablen NET_DRV_N, NET_DRV_1_OPTION, NET_DRV_2_OPTION, NET_DRV_3_OPTION, usw. überprüft werden.

8.3.6 Eigene Definitionen zum Prüfen der Konfigurationsvariablen

8.3.6.1 Einführung regulärer Ausdrücke

In der Version 2.0 gab es nur die oben angeführten sieben Werte-Bereiche, auf die Variablen geprüft werden können: NONE, NOTEMPTY, NUMERIC, IPADDR, YESNO, NOBLANK, DIALMODE. Die Überprüfung war in mkfli4l fest eingebaut, nicht erweiterbar und beschränkte sich auf wesentliche "`Datentypen"', die mit vertretbarem Aufwand geprüft werden können.

Mit der Version 2.1 wurde diese Prüfung neu implementiert. Ziel der neuen Implementierung ist eine flexiblere Prüfung der Variablen, die auch in der Lage ist, komplexere Ausdrücke zu prüfen. Deshalb werden reguläre Ausdrücke verwendet, die in einem oder mehreren separaten Dateien abgespeichert werden. Dadurch wird es zum einen möglich, Variablen zu prüfen, die im Augenblick noch nicht geprüft werden, und zum anderen können Entwickler optionaler Pakete eigene Ausdrücke definieren, um die Konfiguration ihrer Pakete prüfen zu lassen.

Eine Beschreibung regulärer Ausdrücke findet man via "`man 7 regex"' oder z.B. hier: http://unixhelp.ed.ac.uk/CGI/man-cgi?regex+7.

8.3.6.2 Spezifikation regulärer Ausdrücke

Spezifizieren kann man die Ausdrücke auf zwei Wegen:

  1. Paketspezifische exp-Datei check/<PACKAGE>.exp

    Diese Datei liegt im check-Verzeichnis und trägt den gleichen Namen wie das dazugehörige Paket, also z.B. check/base.exp. Sie enthält Definitionen für Ausdrücke, die in der Datei check/<PACKAGE>.txt referenziert werden können. So enthält check/base.exp im Augenblick Definitionen für die bekannten Prüfungen und check/isdn.exp eine Definition für die Variable ISDN_CIRC_x_ROUTE (das Fehlen dieser Überprüfung war der Auslöser dieser Änderungen).

    Die Syntax lautet wie folgt, wobei man auch hier bei Bedarf doppelte Hochkommata verwenden kann:

        <Name> = '<Regulärer Ausdruck>' : '<Fehlermeldung>'
    
    oder am Beispiel aus check/base.exp:
        NOTEMPTY = '.*[^ ]+.*'          : 'should not be empty'
        YESNO    = 'yes|no'             : 'only yes or no are allowed'
        NUMERIC  = '0|[1-9][0-9]*'      : 'should be numeric (decimal)'
        OCTET    = '1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]'
                 : 'should be a value between 0 and 255'
        IPADDR   = '((RE:OCTET)\.){3}(RE:OCTET)' : 'invalid ipv4 address'
        EIPADDR  = '()|(RE:IPADDR)'
                 : 'should be empty or contain a valid ipv4 address'
        NOBLANK  = '[^ ]+'              : 'should not contain spaces'
        DIALMODE = 'auto|manual|off'    : 'only auto, manual or off are allowed'
        NETWORKS = '(RE:NETWORK)([[:space:]]+(RE:NETWORK))*'
                 : 'no valid network specification, should be one or more
                    network address(es) followed by a netmask,
                    for instance 192.168.6.0/24'
    

    In den regulären Ausdrücken können auch Referenzen auf bereits existierende Definitionen enthalten sein. Diese werden dann einfach an der Stelle eingefügt. Dadurch ist es einfacher, reguläre Ausdrücke zu konstruieren. Eingefügt werden die Referenzen einfach durch '(RE:Referenz)'. (Siehe die Definition des Ausdrucks NETWORKS oben für ein entsprechendes Beispiel.)

    Die Fehlermeldungen tendieren dazu, zu lang zu werden. Daher besteht die Möglichkeit, sie über mehrere Zeilen zu verteilen. Die folgenden Zeilen müssen dann immer mit einem Leerzeichen oder Tabulator beginnen. Beim Einlesen der check/<PACKAGE>.exp-Datei werden überflüssige Leerzeichen auf eins reduziert und Tabulatoren durch Leerzeichen ersetzt. Ein Eintrag in der check/<PACKAGE>.exp könnte dann so aussehen:

        NUM_HEX         = '0x[[:xdigit:]]+'
                        : 'should be a hexadecimal number
                           (a number starting with "0x")'
    

  2. Reguläre Ausdrücke direkt in der Check-Datei check/<PACKAGE>.txt

    Manche Ausdrücke kommen nur einmal vor, dann lohnt es sich nicht, dafür einen regulären Ausdruck in einer check/<PACKAGE>.exp-Datei zu definieren. Dann kann man diesen Ausdruck einfach in die Check-Datei schreiben, z.B.:

        # Variable      OPT_VARIABLE    VARIABLE_N     VALUE
        MOUNT_BOOT      -               -              RE:ro|rw|no
    

    MOUNT_BOOT kann lediglich die Werte "`ro"', "`rw"' oder "`no"' annehmen, alles andere wird abgelehnt.

    Will man Bezug auf existierende reguläre Ausdrücke nehmen, fügt man einfach eine Referenz via "`(RE:...)"' ein. Beispiel:

        # Variable      OPT_VARIABLE    VARIABLE_N     VALUE
        LOGIP_LOGDIR    OPT_LOGIP       -              RE:(RE:ABS_PATH)|auto
    

8.3.6.3 Erweiterung existierender regulärer Ausdrücke

Fügt ein optionales Paket einen zusätzlichen Wert für eine Variable hinzu, die von einem regulären Ausdruck geprüft wird, muss der reguläre Ausdruck erweitert werden. Dies geschieht einfach durch Definition der neuen möglichen Werte durch einen regulären Ausdruck (wie oben beschrieben) und Ergänzung des bestehenden regulären Ausdrucks in einer eigenen check/<PACKAGE>.exp-Datei. Dass ein bestehender Ausdruck modifiziert werden soll, kennzeichnet ein führendes "`+"'. Der neue Ausdruck ergänzt den bestehenden Ausdruck, indem der neue Wert als Alternative an den bestehenden Wert angehängt wird. Verwendet ein anderer Ausdruck den ergänzten Ausdruck, gilt auch dort die Ergänzung. Die angegebene Fehlermeldung wird einfach an die vorhandene hinten angehängt.

Am Beispiel der Ethernet-Treiber könnte das wie folgt aussehen:

Nun kann man zusätzlich auch noch PCMCIA-Treiber auswählen.

8.3.6.4 Regulären Ausdruck in Abhängigkeit von YESNO-Variablen erweitern

Wenn man NET_DRV wie oben um die PCMCIA-Treiber erweitert hat, aber das Paket "`pcmcia"' deaktiviert hat, könnte man dennoch einen PCMCIA-Treiber in der config/base.txt auswählen, ohne dass eine Fehlermeldung beim Erstellen der Archive auftritt. Um das zu verhindern, kann man den regulären Ausdruck auch abhängig von einer YESNO-Variablen in der Konfiguration erweitern. Dazu wird der Name der Variablen, die bestimmt ob der Ausdruck erweitert wird, mit runden Klammern direkt hinter den Namen des Ausdrucks gehängt. Ist die Variable aktiv und hat den Wert "`yes"', wird der Ausdruck erweitert, sonst nicht.

    PCMCIA_NET_DRV       = 'pcnet_cs|xirc2ps_cs|3c574_cs|...' : ''
    +NET_DRV(OPT_PCMCIA) = '(RE:PCMCIA_NET_DRV)' : ''

Wird jetzt OPT_PCMCIA='no' gesetzt, und in der config/base.txt wird z.B. der PCMCIA-Treiber xirc2ps_cs benutzt, gibt es beim Erstellen der Archive eine Fehlermeldung.

Hinweis: Dies funktioniert nicht, wenn die Variable nicht explizit in der Konfigurationsdatei gesetzt wird, sondern ihren Wert über eine Standard-Belegung in der check/<PACKAGE>.txt erhält. In diesem Fall muss man also in der Konfigurationsdatei die Variable explizit setzen und ggf. auf die Standard-Belegung verzichten.


8.3.6.5 Regulären Ausdruck in Abhängigkeit von anderen Variablen erweitern

Alternativ kann man auch beliebige Werte von Variablen als Bedingung verwenden, die Syntax sieht dann wie folgt aus:

    +NET_DRV(KERNEL_VERSION=~'^3\.16\..*$') = ...

Wenn KERNEL_VERSION zu dem angegebenen regulären Ausdruck passt, also irgendein Kernel aus der 3.16er Versionsreihe genutzt wird, dann wird die Liste der erlaubten Netzwerktreiber um die angegebenen Treiber ergänzt.

Hinweis: Dies funktioniert nicht, wenn die Variable nicht explizit in der Konfigurationsdatei gesetzt wird, sondern ihren Wert über eine Standard-Belegung in der check/<PACKAGE>.txt erhält. In diesem Fall muss man also in der Konfigurationsdatei die Variable explizit setzen und ggf. auf die Standard-Belegung verzichten.

8.3.6.6 Fehlermeldungen

Findet die Prüfung einen Fehler, erscheint eine Fehlermeldung der folgenden Art:

    Error: wrong value of variable HOSTNAME: '' (may not be empty)
    Error: wrong value of variable MOUNT_OPT: 'rx' (user supplied regular expression)

Beim ersten Fehler wurde der Ausdruck in einer check/<PACKAGE>.exp-Datei definiert und ein Hinweis auf den Fehler wird mit ausgegeben. Im zweiten Falle wurde der Ausdruck direkt in einer check/<PACKAGE>.txt-Datei spezifiziert, deshalb gibt es keinen zusätzlichen Hinweis auf die Fehlerursache.

8.3.6.7 Definition regulärer Ausdrücke

Reguläre Ausdrücke sind wie folgt definiert:

Regulärer Ausdruck: Eine oder mehrere Alternativen, getrennt durch '|', z.B. "`ro|rw|no"'. Trifft eine der Alternativen zu, trifft der ganze Ausdruck zu (hier wären "`ro"', "`rw"' und "`no"' gültige Ausdrücke).

Eine Alternative ist eine Verkettung mehrerer Teilstücke, die einfach aneinandergereiht werden.

Ein Teilstück ist ein "`Atom"', gefolgt von einem einzelnen "`*"', "`+"', "`?"' oder "`{min, max}"'. Die Bedeutung ist wie folgt:

Ein "`Atom"' ist ein

Ein Ausdruck in rechteckigen Klammern bedeutet Folgendes:

8.3.6.8 Beispiele für reguläre Ausdrücke

Sehen wir uns das mal an einigen Beispielen an!

NUMERIC: Ein numerischer Wert besteht aus mindestens einer, aber ansonsten beliebig vielen Ziffern. "`Mindestens ein"' drückt man mit "`+"' aus, eine Ziffer hatten wir schon als Beispiel. Zusammengesetzt ergibt das:

    NUMERIC = '[0-9]+'
oder alternativ
    NUMERIC = '[[:digit:]]+'

NOBLANK: Ein Wert, der keine Leerzeichen enthält, ist ein beliebiges Zeichen (außer dem Leerzeichen) und davon beliebig viele:

    NOBLANK = '[^ ]*'

bzw. wenn der Wert zusätzlich auch nicht leer sein darf:

    NOBLANK = '[^ ]+'

IPADDR: Sehen wir uns das Ganze nochmal am Beispiel der IPv4-Addresse an. Eine IPv4-Adresse besteht aus vier "`Octets"', die durch einen Punkt ("`."') voneinander getrennt sind. Ein Octet kann eine Zahl zwischen 0 und 255 sein. Definieren wir als erstes ein Octet. Es kann

eine Zahl zwischen 0 und 9 sein: [0-9]
eine Zahl zwischen 10 und 99: [1-9][0-9]
eine Zahl zwischen 100 und 199: 1[0-9][0-9]
eine Zahl zwischen 200 und 249: 2[0-4][0-9]
eine Zahl zwischen 250 und 255 sein: 25[0-5]

Das Ganze sind Alternativen, also fassen wir sie einfach mittels "`|"' zu einem Ausdruck zusammen: "`[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]"' und haben damit ein Octet. Daraus können wir nun eine IPv4-Adresse machen, vier Octets mit Punkten voneinander getrennt (der Punkt muss mittels eines Backslashs maskiert werden, da er sonst für ein beliebiges Zeichen steht). Basierend auf der Syntax der exp-Dateien sieht das Ganze dann wie folgt aus:

    OCTET  = '[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]'
    IPADDR = '((RE:OCTET)\.){3}(RE:OCTET)'

8.3.6.9 Unterstützung beim Entwurf regulärer Ausdrücke

Will man reguläre Ausdrücke entwerfen und testen, kann man dazu das "`regexp"'-Programm verwenden, das sich in dem Verzeichnis unix bzw. windows des Pakets "`base"' befindet. Es akzeptiert die folgende Syntax:

    usage: regexp [-c <check dir>] <regexp> <string>

Dabei bedeuten die Parameter Folgendes:

Das könnte z.B. wie folgt aussehen:

./i586-linux-regexp -c ../check '[0-9]' 0
adding user defined regular expression='[0-9]' ('^([0-9])$')
checking '0' against regexp '[0-9]' ('^([0-9])$')
'[0-9]' matches '0'

./i586-linux-regexp -c ../check '[0-9]' a
adding user defined regular expression='[0-9]' ('^([0-9])$')
checking 'a' against regexp '[0-9]' ('^([0-9])$')
regex error 1 (No match) for value 'a' and regexp '[0-9]' ('^([0-9])$')

./i586-linux-regexp -c ../check IPADDR 192.168.0.1
using predefined regular expression from base.exp
adding IPADDR='((RE:OCTET)\.){3}(RE:OCTET)'
 ('^(((1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]))$')
'IPADDR' matches '192.168.0.1'

./i586-linux-regexp -c ../check IPADDR 192.168.0.256
using predefined regular expression from base.exp
adding IPADDR='((RE:OCTET)\.){3}(RE:OCTET)'
 ('^(((1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]))$')
regex error 1 (No match) for value '192.168.0.256' and regexp
 '((RE:OCTET)\.){3}(RE:OCTET)'
(unknown:-1) wrong value of variable cmd_var: '192.168.0.256' (invalid ipv4 address)

8.3.7 Erweiterte Prüfungen der Konfiguration

Manchmal ist es notwendig, komplexere Überprüfungen durchzuführen. Beispiele für solche komplexeren Dinge wären z.B. Abhängigkeiten zwischen Paketen oder Bedingungen, die nur erfüllt sein müssen, wenn Variablen bestimmte Werte annehmen. So muss z.B. bei Auswahl eines PCMCIA-ISDN-Adapters auch das Paket "`pcmcia"' installiert werden.

Um diese Überprüfungen durchführen zu können, kann man in check/<PACKAGE>.ext (auch ext-Skript genannt) kleinere Tests schreiben. Die Sprache besteht aus folgenden Elementen:

  1. Schlüsselwörter:

  2. Datentypen: Zeichenketten, positive ganze Zahlen, Versionsnummern
  3. Logische Operationen: <, ==, >, !=, !, &&, ||, =~, copy_pending, samenet, subnet


8.3.7.1 Datentypen

Zu den Datentypen ist zu sagen, dass Variablen auf Grund des zugehörigen regulären Ausdrucks fest einem Datentyp zugeordnet werden:

Das bedeutet unter anderem, dass eine Variable vom Typ ENUMERIC nicht als Index beim Zugriff auf eine Array-Variable benutzt werden kann, auch wenn man sich vorher vergewissert hat, dass sie nicht leer ist. Der folgende Code funktioniert somit nicht:

    # sei TEST eine Variable vom Typ ENUMERIC
    if (test != "")
    then
        # Fehler: You can't use a non-numeric ID in a numeric 
        #         context. Check type of operand.
        set i=my_array[test]
        # Fehler: You can't use a non-numeric ID in a numeric 
        #         context. Check type of operand.
        set j=test+2
    fi

Eine Lösung für dieses Problem bietet split:

    if (test != "")
    then
        # alle Elemente von test_% sind numerisch
        split(test, test_%, ' ', numeric)
        # OK
        set i=my_array[test_%[1]]
        # OK
        set j=test_%[1]+2
    fi


8.3.7.2 Zeichenketten und Variablenersetzung

An verschiedenen Stellen werden Zeichenketten benötigt, etwa wenn eine Warnung ausgegeben werden soll. In einigen Fällen, die in dieser Dokumentation beschrieben werden, wird eine solche Zeichenkette dabei nach Variablen durchsucht; werden welche gefunden, werden diese durch ihren Inhalt oder andere Attribute ersetzt. Diese Ersetzung wird Variablenersetzung genannt.

Dies soll an einem Beispiel verdeutlicht werden. Es gelte die Konfiguration:

    # config/base.txt
    HOSTNAME='fli4l'
    # config/dns_dhcp.txt
    HOST_N='1' # Anzahl der Hosts
    HOST_1_NAME='client'
    HOST_1_IP4='192.168.1.1'

Dann werden die Zeichenketten wie folgt umgeschrieben, wenn die Variablenersetzung in dem jeweiligen Kontext aktiv ist:

    "Mein Router heißt $HOSTNAME"
    # --> "Mein Router heißt fli4l"
    "HOSTNAME ist Teil des Pakets %{HOSTNAME}"
    # --> "HOSTNAME ist Teil des Pakets base"
    "@HOST_N ist $HOST_N"
    # --> " # Anzahl der Hosts ist 1"

Wie man sehen kann, gibt es prinzipiell drei Möglichkeiten der Ersetzung:

Will man ein "`$"', "`@"' oder "`%"' im Text haben, schreibt man "`$$"', "`@@"' bzw. "`%%"'.

Hinweis: Elemente von Array-Variablen können auf diese Weise nicht in Zeichenketten integriert werden, weil es keine Möglichkeit gibt, einen Index anzugeben.

Generell unterliegen nur Konstanten der Variablenersetzung; Zeichenketten, die über eine Variable hereinkommen, bleiben unverändert. Ein Beispiel soll dies verdeutlichen - es sei die folgende Konfiguration gegeben:

    HOSTNAME='fli4l'
    TEST='${HOSTNAME}'

Dann führt der Code:

    warning "${TEST}"

zur Ausgabe von:

    Warning: ${HOSTNAME}

und nicht zur Ausgabe von:

    Warning: fli4l

In den folgenden Abschnitten wird explizit darauf hingewiesen, unter welchen Umständen Zeichenketten der Variablenersetzung unterliegen.

8.3.7.3 Definition eines Dienstes mit einer dazugehörenden Versionsnummer: provides

Damit kann z.B. ein OPT deklarieren, dass es einen Drucker-Dienst oder einen Webserver-Dienst bereitstellt. Es kann jeweils nur ein einziges Paket geben, dass einen Dienst bereitstellt. Damit kann man verhindern, dass z.B. zwei Webserver parallel installiert werden, was naheliegenderweise nicht gehen würde, da sich die beiden Server um den Port 80 streiten würden. Zusätzlich wird die aktuelle Version des Dienstes angegeben, so dass Weiterentwicklungen Rechnung getragen werden kann. Die Versionsnummer besteht aus zwei- oder drei Zahlen, die durch Punkte voneinander getrennt sind, etwa "`4.0"' oder "`2.1.23"'.

Typischerweise werden Dienste auf OPTs, nicht auf ganze Pakete abgebildet. So besitzt etwa das Paket "`tools"' eine ganze Reihe von Programmen, die jeweils ihre eigene provides-Anweisung definieren, so sie denn via OPT_...='yes' aktiviert sind.

Die Syntax lautet:

    provides <Name> version <Version>

Beispiel aus dem Paket "`easycron"':

    provides cron version 3.10.0

Die Versionsnummer sollte vom OPT-Entwickler in der dritten Komponente angehoben werden, wenn lediglich Funktionserweiterungen vorgenommen wurden und die Schnittstelle zum OPT kompatibel geblieben ist. Die Versionsnummer sollte in der ersten oder zweiten Komponente angehoben werden, wenn sich die Schnittstelle in irgendeiner Weise inkompatibel verändert hat (z.B. auf Grund von Variablenumbenennungen, Pfad-Änderungen, verschwundenen oder umbenannten Dienstprogrammen etc.).

8.3.7.4 Definition einer Abhängigkeit zu einem Dienst mit einer bestimmten Version: depends

Benötigt man zur Erbringung der eigenen Funktionalität einen anderen Dienst (z.B. einen Webserver), kann man hiermit diese Abhängigkeit zu einem Dienst mit einer bestimmten Version spezifizieren. Die Version kann zweistellig (z.B. "`2.1"') oder dreistellig (z.B. "`2.1.11"') angegeben werden, wobei die zweistellige Variante alle Versionen akzeptiert, die ebenfalls so beginnen, während die dreistellige Version nur genau diese angegebene Version akzeptiert. Des Weiteren kann eine Liste von solchen Versionsnummern angegeben werden, falls mehrere Versionen des Dienstes kompatibel mit dem Paket sind.

Die Syntax lautet:

    depends on <Name> version <Version>+

Ein Beispiel: Paket "`server"' enthalte:

    provides server version 1.0.1

Sei ein Paket "`client"' gegeben. Darin seien folgende depends-Anweisungen beispielhaft enthalten:8.2

    depends on server version 1.0       # OK, '1.0' passt zu '1.0.1'
    depends on server version 1.0.1     # OK, '1.0.1' passt zu '1.0.1'
    depends on server version 1.0.2     # Fehler, '1.0.2' passt nicht zu '1.0.1'
    depends on server version 1.1       # Fehler, '1.1' passt nicht zu '1.0.1'
    depends on server version 1.0 1.1   # OK, '1.0' passt zu '1.0.1'
    depends on server version 1.0.2 1.1 # Fehler, weder '1.0.2' noch '1.1' passen
                                        # zu '1.0.1'


8.3.7.5 Kommunikation mit dem Nutzer: warning, error, fatal_error

Mit Hilfe dieser drei Funktionen kann man Nutzer warnen, einen Fehler signalisieren oder die Prüfung sofort abbrechen. Die Syntax sieht wie folgt aus:

Alle an diese Funktionen übergebenen Zeichenketten-Konstanten unterliegen der Variablenersetzung.


8.3.7.6 Zuweisungen

Benötigt man aus irgendeinem Grund eine temporäre Variable, kann man diese einfach mit "`set var [= value]"' anlegen. Die Variable darf kein Konfigurationsvariable sein!8.3 Lässt man den "`= value"' Teil weg, wird die Variable einfach auf "`yes"' gesetzt, so dass man sie hinterher einfach in einer if-Anweisung testen kann. Wird ein Zuweisungsteil angegeben, kann hinter dem Gleichheitszeichen alles stehen: normale Variablen, indizierte Variablen, Zahlen, Zeichenketten, Versionsnummern.

Zu beachten ist, dass durch diese Zuweisung gleichzeitig der Typ der temporären Variablen festgelegt wird. Wird eine Zahl zugewiesen, "`merkt"' mkfli4l sich, dass diese Variable eine Zahl enthält, und erlaubt später das Rechnen damit. Versucht man, mit einer anders getypten Variable zu rechnen, wird dies fehlschlagen. Beispiel:

    set i=1   # OK, i ist eine numerische Variable
    set j=i+1 # OK, j ist eine numerische Variable und enthält den Wert 2
    set i="1" # OK, i ist nun eine Zeichenketten-Variable
    set j=i+1 # Fehler "You can't use a non-numeric ID in a numeric 
              #         context. Check type of operand."
              # --> mit Zeichenketten kann man nicht rechnen!

Man kann auch temporäre Arrays (siehe unten) anlegen. Beispiel:

    set prim_%[1]=2
    set prim_%[2]=3
    set prim_%[3]=5
    warning "${prim_n}"

Dabei wird die Anzahl der Elemente in dem Array in der Variable prim_n von mkfli4l verwaltet. Der obige Code führt somit zu folgender Ausgabe:

    Warning: 3

Wenn auf der rechten Seite einer Zuweisung eine Zeichenketten-Konstante steht, unterliegt sie zum Zeitpunkt der Zuweisung der Variablenersetzung. Dies wird im folgenden Beispiel demonstriert. Der Code:

    set s="a"
    set v1="$s" # v1="a"
    set s="b"
    set v2="$s" # v2="b"
    if (v1 == v2)
    then
      warning "gleich"
    else
      warning "ungleich"
    fi

produziert die Ausgabe "`ungleich"', weil die Variablen v1 und v2 bereits während der Zuweisung den aktuellen Inhalt der Variablen s ersetzen.

Hinweis: Eine in einem Skript gesetzte Variable ist bei der Abarbeitung weiterer Skripte sichtbar - es existiert zur Zeit kein Lokalitätsprinzip für derart eingeführte Variablen. Da die Reihenfolge, in der die Skripte verschiedener Pakete abgearbeitet wird, nicht definiert ist, sollte man sich nie darauf verlassen, dass Variablen irgendwelche Werte besitzen bzw. von einem anderen Paket übernommen haben.

8.3.7.7 Arrays

Will man auf einzelne Elemente einer %-Variablen (eines Arrays) zugreifen, muss man den Original-Namen der Variable, wie er in der check/<PACKAGE>.txt-Datei steht, verwenden, und dabei für jedes "`%"'-Zeichen einen Index mit Hilfe von "`[Index]"' anhängen.

Beispiel: Will man auf die Elemente der Variable PF_USR_CHAIN_%_RULE_% zugreifen, benötigt man zwei Indizes, weil die Variable zwei "`%"'-Zeichen besitzt. Alle Elemente ausgeben kann man z.B. mit Hilfe des folgenden Codes (die foreach-Schleife wird weiter unten erläutert):

    foreach i in pf_usr_chain_n
    do
        # nur ein Index nötig, da nur ein '%' im Variablennamen
        set j_n=pf_usr_chain_%_rule_n[i]
        # Achtung: ein
        # foreach j in pf_usr_chain_%_rule_n[i]
        # ist leider nicht möglich, deshalb der Umweg über j_n!
        foreach j in j_n
        do
            # zwei Indizes nötig, da zwei '%' im Variablennamen
            set rule=pf_usr_chain_%_rule_%[i][j]
            warning "Rule $i/$j: ${rule}"
        done
    done

Mit der folgenden Beispiel-Konfiguration

    PF_USR_CHAIN_N='2'
    PF_USR_CHAIN_1_NAME='usr-chain_a'
    PF_USR_CHAIN_1_RULE_N='2'
    PF_USR_CHAIN_1_RULE_1='ACCEPT'
    PF_USR_CHAIN_1_RULE_2='REJECT'
    PF_USR_CHAIN_2_NAME='usr-chain_b'
    PF_USR_CHAIN_2_RULE_N='1'
    PF_USR_CHAIN_2_RULE_1='DROP'

gibt es dann die folgenden Ausgaben:

    Warning: Rule 1/1: ACCEPT
    Warning: Rule 1/2: REJECT
    Warning: Rule 2/1: DROP

Alternativ kann man direkt über alle Werte des Arrays iterieren, kennt dann allerdings nicht die exakten Indizes der Einträge (was auch nicht immer erforderlich ist):

    foreach rule in pf_usr_chain_%_rule_%
    do
        warning "Rule %{rule}='${rule}'"
    done

Das produziert mit der Beispiel-Konfiguration von oben die folgenden Ausgaben:

    Warning: Rule PF_USR_CHAIN_1_RULE_1='ACCEPT'
    Warning: Rule PF_USR_CHAIN_1_RULE_2='REJECT'
    Warning: Rule PF_USR_CHAIN_2_RULE_1='DROP'

An dem zweiten Beispiel sieht man auch schön die Bedeutung der %<Name>-Syntax: Innerhalb der Zeichenkette wird %rule durch den Namen der betrachteten Variable ersetzt (also z.B. PF_USR_CHAIN_1_RULE_1), während $rule durch dessen Inhalt (also z.B. ACCEPT) ersetzt wird.

8.3.7.8 Verschlüsseln eines Passwortes: crypt

Einige Variablen enthalten Passwörter, die nicht im Klartext in der rc.cfg stehen sollen. Diese Variablen können mittels crypt verschlüsselt werden und werden damit in das Format überführt, dass auch auf dem Router benötigt wird. Verwendet wird das wie folgt:

    crypt (<Variable>)

Die crypt-Funktion ist die einzige Stelle, an der eine Konfigurationsvariable verändert werden kann.


8.3.7.9 Abfragen von Eigenschaften einer Datei: stat

stat ermöglicht es, Eigenschaften einer Datei abzufragen. Zur Verfügung gestellt wird im Augenblick lediglich die Größe einer Datei. Wenn man auf Dateien unterhalb des aktuellen Konfigurationsverzeichnisses testen will, kann man die interne Variable config_dir benutzen. Die Syntax lautet:

    stat (<Dateiname>, <Schlüssel>)

Der Aufruf sieht wie folgt aus (wobei die verwendeten Parameter nur Beispiele sind):

    foreach i in openvpn_%_secret
    do
       stat("${config_dir}/etc/openvpn/$i.secret", keyfile)
       if (keyfile_res != "OK")
       then
          error "OpenVPN: missing secretfile <config>/etc/openvpn/$i.secret"
       fi
    done

In dem Beispiel wird geprüft, ob eine Datei im aktuellen Konfigurationsverzeichnis vorhanden ist. Wenn also OPENVPN_1_SECRET='test' in der Konfigurationsdatei gesetzt wird, prüft die Schleife im ersten Durchlauf, ob im aktuellen Konfigurationsverzeichnis die Datei etc/openvpn/test.secret vorhanden ist.

Nach dem Aufruf sind zwei Variablen definiert:

Das könnte dann z.B. so aussehen:

    stat ("unix/Makefile", test)
    if ("$test_res" == "OK")
    then
            warning "test_size = $test_size"
    else
            error "Error '$test_res' while trying to get size of 'unix/Makefile'"
    fi

Ein als Zeichenketten-Konstante übergebener Dateiname unterliegt der Variablenersetzung.


8.3.7.10 Durchsuchen von Dateien: fgrep

Wenn Sie in einer Datei per "`grep"'8.4 suchen wollen, steht Ihnen das fgrep-Kommando zur Verfügung. Die Syntax lautet:

    fgrep (<Dateiname>, <RegEx>)

Wenn die Datei <Dateiname> nicht existiert wird mkfli4l mit einem fatalen Fehler beendet! Wenn Sie also nicht sicher sind, ob die Datei immer vorhanden ist, testen Sie die Existenz von <Dateiname> vorher mit stat ab. Nach dem Aufruf von fgrep steht Ihnen das Suchresultat in dem Array FGREP_MATCH_% zur Verfügung, wobei der Index x wie üblich von eins bis FGREP_MATCH_N reicht. FGREP_MATCH_1 verweist dabei auf den gesamten Bereich der Zeile, auf den der reguläre Ausdruck gepasst hat, während FGREP_MATCH_2 bis FGREP_MATCH_N den jeweils n-1-ten geklammerten Teil beinhalten.

Ein erstes einfaches Beispiel soll die Verwendung demonstrieren. Die Datei opt/etc/shells enthält die Zeile:

/bin/sh

Der folgende Code

    fgrep("opt/etc/shells", "^/(.)(.*)/")
    foreach v in FGREP_MATCH_%
    do
      warning "%v='$v'"
    done

produziert die folgende Ausgabe:

    Warning: FGREP_MATCH_1='/bin/'
    Warning: FGREP_MATCH_2='b'
    Warning: FGREP_MATCH_3='in'

Der reguläre Ausdruck hat (nur) auf "`/bin/"' gepasst, deshalb steht auch (nur) dieser Teil der Zeile in der Variable FGREP_MATCH_1. Der erste geklammerte Teil im Ausdruck passt auf das erste Zeichen hinter dem ersten "`/"', deshalb steht auch nur "`b"' in FGREP_MATCH_2. Der zweite geklammerte Teil umfasst den Rest hinter den "`b"' bis zum letzten "`/"', somit steht "`in"' in der Variable FGREP_MATCH_3.

Das folgende zweite Beispiel demonstriert eine praxisnahe Verwendung von fgrep an einem Beispiel aus der check/base.ext. Hier wird getestet, ob alle in der PF_FORWARD_x angegebenen tmpl:-Referenzen vorhanden sind:

    foreach n in pf_forward_n
    do
      set rule=pf_forward_%[n]
      if (rule =~ "tmpl:([^[:space:]]+)")
      then
        foreach m in match_%
        do
          stat("$config_dir/etc/fwrules.tmpl/$m", tmplfile)
          if(tmplfile_res == "OK")
          then
            add_to_opt "etc/fwrules.tmpl/$m"
          else
            stat("opt/etc/fwrules.tmpl/$m", tmplfile)
            if(tmplfile_res == "OK")
            then
              add_to_opt "etc/fwrules.tmpl/$m"
            else
              fgrep("opt/etc/fwrules.tmpl/templates", "^$m[[:space:]]+")
              if (fgrep_match_n == 0)
              then
                error "Can't find tmpl:$m for PF_FORWARD_${n}='$rule'!"
              fi
            fi
          fi
        done
      fi
    done

Sowohl ein als Zeichenketten-Konstante übergebener Dateiname als auch als Zeichenketten-Konstante übergebener regulärer Ausdruck unterliegen der Variablenersetzung.


8.3.7.11 Auseinandernehmen von Parametern: split

Oftmals werden Variablen mit mehreren Parametern belegt, die dann in Startup-Skripten erst wieder auseinandergenommen werden. Will man diese bereits vorher auseinandernehmen und Tests auf ihnen durchführen, nimmt man split. Die Syntax lautet:

    split (<Zeichenkette>, <Array>, <Trennzeichen>)

Die Zeichenkette kann durch eine Variable oder direkt als Konstante angegeben werden. mkfli4l zerlegt ihn an den Stellen, an denen das Trennzeichen auftaucht, und erzeugt pro Teil ein Element des Arrays. Über diese Elemente kann man dann hinterher iterieren und Prüfungen vornehmen. Steht zwischen zwei Trennzeichen nichts, wird ein Array-Element mit einer leeren Zeichenkette als Wert erzeugt. Ausnahme ist "` "': Hier werden alle Leerzeichen konsumiert und keine leeren Variablen erzeugt.

Sollen die bei der Zerlegung entstandenen Elemente in einem numerischen Kontext verwendet werden (z.B. als Indizes), muss das beim Aufruf von split spezifiert werden. Das geschieht durch das zusätzliche Attribut "`numeric"'. Der Aufruf sieht dann wie folgt aus:

    split (<Zeichenkette>, <Array>, <Trennzeichen>, numeric)

Ein Beispiel:

    set bar="1.2.3.4"
    split (bar, tmp_%, '.', numeric)
    foreach i in tmp_%
    do
            warning "%i = $i"
    done

Die produzierte Ausgabe ist:

    Warning: TMP_1 = 1
    Warning: TMP_2 = 2
    Warning: TMP_3 = 3
    Warning: TMP_4 = 4

Hinweis: Wenn die "`numeric"'-Variante verwendet wird, dann prüft mkfli4l zum Zeitpunkt der Zerlegung nicht, ob die Teil-Zeichenketten auch wirklich numerisch sind! Bei einer späteren Verwendung in einem numerischen Kontext (etwa beim Addieren) löst mkfli4l jedoch einen fatalen Fehler aus, wenn eine solche Variable doch nicht numerisch ist. Beispiel:

    set bar="a.b.c.d"
    split (bar, tmp_%, '.', numeric)
    # Fehler: invalid number 'a'
    set i=tmp_%[1]+1

Eine an split im ersten Parameter übergebene Zeichenketten-Konstante unterliegt der Variablenersetzung.


8.3.7.12 Hinzufügen von Dateien zum Archiv: add_to_opt

Mit der Funktion add_to_opt können zusätzliche Dateien ans Opt- oder RootFS-Archiv angehängt werden. Es können dabei alle Dateien unterhalb von opt/ oder aus dem Konfigurationsverzeichnis ausgewählt werden. Eine Beschränkung nur auf die Dateien, die mit einem bestimmten Paket geliefert werden, gibt es nicht. Liegt eine Datei sowohl unter opt/ als auch im Konfigurationsverzeichnis im gleichen Pfad, bevorzugt add_to_opt die Dateien aus dem Konfigurationsverzeichnis. Die Funktion add_to_opt wird in der Regel dann eingesetzt, wenn komplexe logische Regeln darüber entscheiden, ob und welche Dateien in das Archiv aufgenommen werden müssen.

Die Syntax sieht wie folgt aus:

    add_to_opt <Datei> [<Flags>]

Die Flags sind optional. Es gelten die in Tabelle 8.2 aufgeführten Standard-Werte, falls keine Flags angegeben sind.

Es folgt ein Beispiel aus dem Paket "`sshd"':

    if (opt_sshd)
    then
       foreach pkf in sshd_public_keyfile_%
       do
         stat("$config_dir/etc/ssh/$pkf", publickeyfile)
         if(publickeyfile_res == "OK")
         then
             add_to_opt "etc/ssh/$pkf" "mode=400 flags=utxt"
         else
             error "sshd: missing public keyfile %pkf=$pkf"
         fi
       done
    fi

Mit stat wird zunächst geprüft, ob die Datei im Konfigurationsverzeichnis existiert. Ist die Datei vorhanden, wird sie ans Archiv angehängt, andernfalls bricht mkfli4l mit einer entsprechenden Fehlermeldung ab.

Hinweis: Auch bei add_to_opt prüft mkfli4l zuerst, ob die zu kopierende Datei im Konfigurationsverzeichnis zu finden ist.

Sowohl ein als Zeichenketten-Konstante übergebener Dateiname als auch als Zeichenketten-Konstante übergebene Flags unterliegen der Variablenersetzung.


8.3.7.13 Kontrollfluss

    if (expr)
    then
            statement
    else
            statement
    fi

Eine klassische Fallunterscheidung, wie man sie kennt. Ist die Bedingung wahr, wird der then-Teil ausgeführt, ist die Bedingung falsch, wird der else-Teil ausgeführt.

Will man Tests über Array-Variablen durchführen, muss man jede einzelne Variable testen. Dazu gibt es die foreach-Schleife in zwei Varianten.

  1. Iterieren über Array-Variablen:

        foreach <Laufvariable> in <Array-Variable>
        do
                <Anweisung>
        done
    
        foreach <Laufvariable> in <Array-Variable-1> <Array-Variable-2> ...
        do
                <Anweisung>
        done
    

    Diese Schleife iteriert über alle angegebenen Array-Variablen, jeweils angefangen beim ersten Element bis hin zum letzten; die Anzahl der Elemente im Array wird dabei der dem Array zugeordneten N-Variable entnommen. Die Laufvariable nimmt dabei die jeweiligen Werte der Array-Variablen an. Zu beachten ist dabei, dass bei optionalen Array-Variablen, die in der Konfiguration nicht vorhanden sind, ein leeres Element generiert wird. Unter Umständen muss das im Skript berücksichtigt werden, was man z.B. wie folgt tun kann:

        foreach i in template_var_opt_%
        do
            if (i != "")
            then
                warning "%i is present (%i='$i')"
            else
                warning "%i is undefined (empty)"
            fi
        done
    

    Wie man auch am Beispiel erkennen kann, lässt sich der Name der jeweiligen Array-Variablen durch die %<Laufvariable>-Konstruktion ermitteln.

    Die Anweisung in der Schleife kann eine der oben beschriebenen Kontrollelemente oder Funktionen (if, foreach, provides, depends, ...) sein.

    Will man auf genau ein Element eines Arrays zugreifen, kann man dieses mittels der Syntax <Array>[<Index>] ansprechen. Der Index kann dabei eine normale Variable, eine Zahlenkonstante oder wiederum ein indiziertes Array sein.

  2. Iterieren über N-Variablen:

        foreach <Laufvariable> in <N-Variable>
        do
                <Anweisung>
        done
    

    Diese Schleife läuft von 1 bis zum Wert, der in der N-Variable steht. Man kann die Laufvariable dazu benutzen, um Array-Variablen zu indizieren. Will man also nicht nur über eine Array-Variable iterieren, sondern über mehrere gleichzeitig, die alle durch dieselben N-Variable kontrolliert werden, nimmt man diese Variante der Schleife und verwendet die Laufvariable zum Indizieren mehrerer Array-Variablen. Beispiel:

        foreach i in host_n
        do
            set name=host_%_name[i]
            set ip4=host_%_ip4[i]
            warning "$i: name=$name ip4=$ip4"
        done
    

    Das ergibt bei entsprechend gefüllten HOST_%_NAME- und HOST_%_IP4-Arrays beispielsweise:

        Warning: 1: name=berry ip4=192.168.11.226
        Warning: 2: name=fence ip4=192.168.11.254
        Warning: 3: name=sandbox ip4=192.168.12.254
    

8.3.7.14 Ausdrücke

Ausdrücke verknüpfen Werte und Operatoren zu einem neuen Wert. Ein Wert kann dabei eine gewöhnliche Variable, ein Array-Element oder eine Konstante (Zahl, Zeichenkette oder Versionsnummer) sein. Alle Zeichenketten-Konstanten, die in Ausdrücken auftreten, unterliegen der Variablenersetzung.

Operatoren erlauben so gut wie alles, was man von einer Programmiersprache gewöhnt ist. Ein Test auf die Gleichheit zweier Variablen könnte also so aussehen:

    var1 == var2
    "$var1" == "$var2"

Zu beachten ist dabei, dass der Vergleich in Abhängigkeit vom Typ der Variable erfolgt, der in check/<PACKAGE>.txt festgelegt wurde. Ist eine der beiden Variablen numerisch, erfolgt der Vergleich auf numerischer Basis, d.h. die Zeichenketten werden in Zahlen umgewandelt und dann verglichen. Sonst erfolgt der Vergleich auf Zeichenketten-Basis; ein Vergleich "05" == "5" ergibt "`falsch"', ein Vergleich "18" < "9" ergibt "`wahr"' auf Grund der lexikographischen Ordnung auf Zeichenketten: die Ziffer "`1"' liegt vor der Ziffer "`9"' im zugrunde liegenden ASCII-Zeichensatz.

Für den Vergleich von Versionen wird das Hilfskonstrukt numeric(version) eingeführt, welches den numerischen Wert für eine Versionsnummer für Vergleichszwecke bestimmt. Dabei gilt:

    numeric(version) := major * 10000 + minor * 1000 + sub

wobei "`major"' die erste Komponente der Versionsnummer darstellt, "`minor"' die zweite und "`sub"' die dritte; fehlt "`sub"', entfällt der Term in der obigen Summe einfach (oder anders ausgedrückt, für "`sub"' wird null angenommen).

Eine vollständige Auflistung aller Ausdrücke ist in Tabelle 8.3 zu finden. Dabei steht "`val"' für einen beliebig getypten Wert, "`number"' für einen numerischen Wert und "`string"' für eine Zeichenkette.


Table 8.3: Logische Ausdrücke
Ausdruck wahr wenn
id id == "`yes"'
val == val identisch getypte Werte sind gleich
val != val identisch getypte Werte sind ungleich
val == number numerischer Wert von val == number
val != number numerischer Wert von val != number
val < number numerischer Wert von val < number
val > number numerischer Wert von val > number
val == version numeric(val) == numeric(version)
val < version numeric(val) < numeric(version)
val > version numeric(val) > numeric(version)
val =~ string regulärer Ausdruck in string auf val passt
( expr ) Ausdruck in Klammern ist wahr
expr && expr beide Ausdrücke sind wahr
expr || expr mind. einer der beiden Audrücke ist wahr
copy_pending(id) siehe Beschreibung
samenet (string1, string2) string1 das gleiche netz wie string2 beschreibt
subnet (string1, string2) string1 ein Subnetz von string2 beschreibt

8.3.7.15 Match-Operator

Mit dem Match-Operator =~ kann man prüfen, ob ein regulärer Ausdruck auf den Wert einer Variable passt. Weiterhin kann man den Operator auch nutzen, um Teilausdrücke aus einer Variablen zu extrahieren. Nach erfolgreichem Anwenden eines regulären Ausdrucks auf eine Variable enthält das Array MATCH_% die gefundenen Teile. Das könnte z.B. wie folgt aussehen:

    set foo="foobar12"
    if ( foo =~ "(foo)(bar)([0-9]*)" )
    then
            foreach i in match_%
            do
                    warning "match %i: $i"
            done
    fi

Ein mkfli4l-Aufruf führt dann zu folgender Ausgabe:

    Warning: match MATCH_1: foo
    Warning: match MATCH_2: bar
    Warning: match MATCH_3: 12

Bei Verwendung von =~ kann Bezug auf alle existierenden regulären Ausdrücke genommen werden. Will man z.B. prüfen, ob ein PCMCIA-Ethernet-Treiber ausgewählt wurde, ohne dass OPT_PCMCIA auf "`yes"' gesetzt wurde, könnte das wie folgt aussehen:

    if (!opt_pcmcia)
    then
        foreach i in net_drv_%
        do
           if (i =~ "^(RE:PCMCIA_NET_DRV)$")
           then
               error "If you want to use ..."
           fi
        done
    fi

Wie in dem Beispiel demonstriert wird, ist es wichtig, den regulären Ausdruck mit Hilfe von ˆ und $ zu verankern, wenn man den Ausdruck auf die gesamte Variable anwenden will. Ansonsten liefert der Match-Ausdruck schon "`wahr"', wenn nur ein Teil der Variable vom regulären Ausdruck abgedeckt wird, was in diesem Fall sicherlich nicht erwünscht ist.

8.3.7.16 Prüfen, ob in Abhängigkeit vom Wert einer Variable eine Datei kopiert wurde: copy_pending

Mit den im Check-Prozess gewonnenen Informationen prüft die Funktion copy_pending, ob in Abhängigkeit vom Wert einer Variable eine Datei kopiert wurde oder nicht. Das kann man verwenden, um z.B. zu testen, ob der vom Nutzer angegebene Treiber auch wirklich existiert und kopiert wurde. copy_pending akzeptiert den zu prüfenden Namen in Form einer Variablen oder einer Zeichenkette.8.5 copy_pending prüft dazu, ob

Dabei liefert copy_pending "`wahr"' zurück, wenn im letzten Schritt festgestellt wurde, dass keine Datei kopiert wurde, das Kopieren also somit noch aussteht (also "`pending"' ist).

Ein kleines Beispiel für die Anwendung all dieser Funktionen findet man in check/base.ext:

    foreach i in net_drv_%
    do
        if (copy_pending("%i"))
        then
            error "No network driver found for %i='$i', check config/base.txt"
        fi
    done

Hier werden alle Elemente des Arrays NET_DRV_% angemeckert, für die keine Kopieraktion vorgenommen wurde, für die also in der opt/base.txt kein entsprechender Eintrag existiert.

8.3.7.17 Vergleich von Netzwerkadressen: samenet und subnet

Zum Prüfen von Routen benötigt man ab und zu einen Test, ob zwei Netzwerke identisch sind oder eines ein Subnetz eines anderen ist. Dazu gibt es die beiden Funktionen samenet und subnet. Dabei liefert

    samenet (netz1, netz2)

"`wahr"', wenn beide Netze identisch sind, und

    subnet (netz1, netz2)

gibt "`wahr"' zurück, wenn "`netz1"' ein Subnetz von "`netz2"' ist.

8.3.7.18 Erweitern der Kernel-Kommandozeile

Ist ein OPT gezwungen, dem Kernel andere Boot-Parameter zu übergeben, so musste früher die Variable KERNEL_BOOT_OPTION geprüft werden, ob der nötige Parameter enthalten war, und ggf. eine Warnung oder eine Fehlermeldung ausgegeben werden. Mit der internen Variable KERNEL_BOOT_OPTION_EXT kann man nötige, aber fehlende Optionen direkt im ext-Skript ergänzen. Ein Beispiel aus der check/base.ext:

    if (powermanagement =~ "apm.*|none")
    then
        if ( ! kernel_boot_option =~ "acpi=off")
        then
            set kernel_boot_option_ext="${kernel_boot_option_ext} acpi=off"
        fi
    fi

Damit wird "`acpi=off"' an den Kernel übergeben, falls keine Energieverwaltung oder welche vom Typ "`APM"' gewünscht ist.

8.3.8 Unterstützung verschiedener Kernelversionslinien

Verschiedene Kernelversionslinien unterscheiden sich häufig in einigen Details:

Diese Unterschiede werden zum großen Teil durch mkfli4l automatisch behandelt. Um die zur Verfügung stehenden Module zu beschreiben, kann man zum einen die zur Prüfung verwendeten Prüfungen in Abhängigkit von der Version erweitern (bedingte reguläre Ausdrücke), und zum anderen erlaubt mkfli4l versionsabhängige opt/<PACKAGE>.txt-Dateien. Dies heißen dann opt/<PACKAGE>_<Kernel-Version>.txt, wobei die Komponenten der Kernel-Version durch Unterstriche voneinander getrennt werden. Ein Beispiel: Das Paket "`base"' enthält die folgenden Dateien im opt-Verzeichnis:

Die erste Datei (base.txt) wird immer verarbeitet. Die anderen beiden Dateien werden nur verarbeitet, wenn die Kernelversion "`3.16(.*)"' bzw. "`3.17(.*)"' lautet. Wie man sieht, können Versionskomponenten im Dateinamen weggelassen werden, wenn man eine ganze Gruppe von Kerneln "`erschlagen"' möchte. Unter Annahme von KERNEL_VERSION='3.16.41' werden für ein Paket <PACKAGE> die folgenden Dateien (sofern vorhanden) eingelesen und verarbeitet:

8.3.9 Dokumentation

Die Dokumentation wird in den Dateien

abgelegt. Die HTML-Dateien können auch aufgeteilt werden, d.h. für jedes enthaltene OPT eine. Dann muss trotzdem eine <PACKAGE>.html angelegt werden, die auf die anderen Dateien verweist. Änderungen sollten in folgenden Dateien dokumentiert werden:

Die gesamte Text-Dokumentation darf keine Tabulatoren enthalten und muss nach spätestens 79 Zeichen einen harten Zeilenumbruch haben. Die stellt sicher, dass die Dokumentation auch mit einem Editor ohne automatischen Zeilenumbruch richtig gelesen werden kann.

Wer mag kann auch eine Dokumentation im LATEX-Format erstellen und daraus dann HTML- und PDF-Fassungen erzeugen. Als Beispiel kann die Dokumentation von fli4l dienen. Einen Rahmen für die Dokumentation und die minimal benötigten LATEX-Macros kann man im Paket "`template"' finden. Eine kurze Beschreibung ist in den folgenden Unterabschnitten zu finden.

Die fli4l-Dokumentation steht zur Zeit in den folgenden Sprachen zur Verfügung: deutsch, englisch (<SPRACHE> = "`english"') und französisch (<SPRACHE> = "`french"'). Es steht einem Paket-Entwickler jedoch frei, sein Paket in beliebigen Sprachen zu dokumentieren. Im Sinne der Verständlichkeit wird jedoch empfohlen, eine Dokumentation in deutsch und/oder englisch (idealerweise in beiden Sprachen) anzufertigen.

8.3.9.1 Voraussetzungen für die Erstellung einer LATEX-Dokumentation

Zum Erstellen der Dokumentation aus LATEX-Quellen gibt es folgende Anforderungen an die Umgebung:

8.3.9.2 Dateinamen

Die Dateien der Dokumentation werden nach folgendem Schema benannt:

<PACKAGE>_main.tex:
Diese Datei enthält den Hauptteil der Dokumentation. <PACKAGE> steht hier für den Namen des Pakets, das beschrieben werden soll (in Kleinbuchstaben).
<PACKAGE>_appendix.tex:
Sollen zu diesem Paket noch weitere Anmerkungen im Anhang hinzugefügt werden, so werden diese hier abgelegt.

Diese Dateien werden im Verzeichnis fli4l/<PACKAGE>/doc/<SPRACHE>/tex/<PACKAGE> abgelegt. Für das Paket "`sshd"' sieht das z.B. wie folgt aus:

    $ ls fli4l/doc/deutsch/tex/sshd/
    Makefile sshd_appendix.tex  sshd_main.tex  sshd.tex

Das Makefile ist für die Generierung der Dokumentation verantwortlich, die sshd.tex-Datei stellt einen Rahmen für die eigentliche Dokumentation und den Anhang bereit, der sich in den anderen beiden Dateien befindet. Ansehen kann man sich das am Beispiel der Dokumentation des "`template"'-Pakets.

8.3.9.3 LATEX-Grundlagen

LATEX arbeitet ähnlich wie HTML "`Tag-orientiert"', nur dass die Tags hier "`Kommandos"' heißen und folgendes Format aufweisen: \kommando bzw. \begin{umgebung} ...\end{umgebung}

Nach Möglichkeit sollte man mit Hilfe von Kommandos eher die Bedeutung des jeweiligen Textes auszeichnen und weniger dessen Darstellung. Es ist also vorteilhaft, z.B.

\warning{Bitte nicht ... tun}

statt

\emph{Bitte nicht ... tun}

zu verwenden.

Jedes Kommando bzw. jede Umgebung kann noch weitere Parameter aufnehmen, die mit \kommando{parameter1}{paramter2}{paramterN} geschrieben werden.

Manche Kommandos haben optionale Parameter, die in eckigen (statt geschweiften) Klammern stehen: \kommando[optionalerParameter]{parameter1} ... Dabei kommt im Normalfall nur ein optionaler Parameter vor, in seltenen Fällen aber auch mehrere.

Einzelne Absätze werden im Dokument durch Leerzeilen getrennt. Innerhalb dieser Absätze nimmt LATEX selbst den Zeilenumbruch und die Worttrennung vor.

Folgende Buchstaben haben eine spezielle Bedeutung in LATEX und müssen, sollten sie in normalem Text vorkommen, mit einem vorangestellten \ maskiert werden: # $ & _ % { }. "`~"' und "`^"' müssen wie folgt geschrieben werden: \verb?~? \verb?^?

Die wichtigsten LATEX-Kommandos werden in der Dokumentation des "`template"'-Pakets verwendet und erklärt.

8.3.10 Dateiformate

Alle Textdateien (sowohl Dokumentation als auch Skripte, die später auf dem Router liegen) müssen im DOS-Dateiformat, also mit CR/LF statt nur LF am Zeilenende in das Paket gelegt werden. Dadurch wird erreicht, dass Windows-Nutzer die Dokumentation auch mit "`notepad"' lesen können und durch eine Änderung eines Skripts unter Windows das Ganze später auf dem Router trotzdem lauffähig bleibt. Die Skripte werden beim Bauen der Archive in das auf dem Router benötigte Format konvertiert (siehe die Beschreibung der Flags in Tabelle 8.2).

8.3.11 Entwickler-Dokumentation

Sollte ein Programm aus dem Paket eine neue Schnittstelle definieren, die andere Programme nutzen können, so ist die Dokumentation dieser Schnittstelle in einer separaten Dokumentation unter doc/dev/<PACKAGE>.txt abzulegen.

8.3.12 Client-Programme

Sollte ein Paket zusätzliche Client-Programme mitliefern, so sind diese im Verzeichnis windows/ für Windows-Clients und im Verzeichnis unix/ für Unix- und Linux-Clients abzulegen.

8.3.13 Quellcode

Angepasste Programme und Quellcodes können im Verzeichnis src/<PACKAGE>/ beigelegt werden. Sollen die Programme genauso wie die restlichen fli4l-Programme gebaut werden, bitte einen Blick in die Dokumentation des "`src"'-Pakets werfen.


8.3.14 Weitere Dateien

Alle Dateien, die nachher auf dem Router liegen, werden unter opt/etc/ und opt/files/ abgelegt. Dabei liegen unter:

Die Skripte in opt/etc/boot.d/, opt/etc/rc.d/ und opt/etc/rc0.d/ werden wie folgt benannt:

    rc<nummer>.<name>

Die Nummer entscheidet über die Reihenfolge der Ausführung, der Name gibt einen Hinweis darauf, welches Programm/Paket von diesem Skript behandelt wird.



Footnotes

... Hochkommata8.1
Es können sowohl einfache Hochkommata als auch doppelte Hochkommata verwendet werden. Man kann also FOO='bar' oder auch FOO="bar" schreiben. Die Verwendung von doppelten Hochkommata sollte allerdings die Ausnahme sein und man sollte sich vorher unbedingt darüber informieren, wie eine Unix-Shell mit einfachen und doppelten Hochkommata umgeht.
... enthalten:8.2
Natürlich nur jeweils eine zur selben Zeit!
... sein!8.3
Dies ist eine bewusste Entscheidung: Durch check-Skripte lässt sich die Benutzerkonfiguration nicht verändern.
... "`grep"'8.4
"`grep"' ist ein auf Unix-Betriebsystemen verbreitetes Kommando zum Filtern von Textströmen.
... Zeichenkette.8.5
Wie eingangs beschrieben unterliegt die Zeichenkette der Variablenersetzung, so dass man z.B. mittels einer foreach-Schleife und der %<Name>-Ersetzung alle Elemente eines Arrays prüfen kann.
© 2001-2019 Das fli4l-Team - April 28, 2019