1.5 Modulkonzept ab fli4l-2.1

Unterabschnitte

- 1.5.1 mkfli4l
- 1.5.2 Aufbau
- 1.5.3 Die Konfiguration der Pakete
- 1.5.4 Die Liste der zu kopierenden Dateien
  - 1.5.4.1 Konfigurations-spezifische Dateien
- 1.5.5 Die Prüfung der Konfiguration-Variablen
- 1.5.6 Eigene Definitionen zum Prüfen der Konfigurationsvariablen
  - 1.5.6.1 Einführung regulärer Ausdrücke in 2.1
  - 1.5.6.2 Spezifikation regulärer Ausdrücke
  - 1.5.6.3 Erweiterung existierender regulärer Ausdrücke
  - 1.5.6.4 Regex anhand von YESNO Variablen definieren
  - 1.5.6.5 Fehlermeldungen
  - 1.5.6.6 Definition regulärer Ausdrücke
  - 1.5.6.7 Beispiele für Reguläre Ausdrücke
  - 1.5.6.8 Unterstützung beim Entwurf regulärer Ausdrücke
- 1.5.7 Erweiterte Prüfungen der Konfiguration
  - 1.5.7.1 Definition eines Paket-Identifiers mit einer dazugehörenden Versionsnummer: provides
  - 1.5.7.2 Definition einer Abhängigkeit von einem anderen Paket mit einer bestimmten Version: depends
  - 1.5.7.3 Kommunikation mit dem Nutzer: warning, error, fatal_error
  - 1.5.7.4 Zuweisungen
  - 1.5.7.5 Arrays
  - 1.5.7.6 Verschlüsseln eines Passwortes - crypt
  - 1.5.7.7 Abfragen von Eigenschaften einer Datei - stat
  - 1.5.7.8 Grep in einer Datei - fgrep
  - 1.5.7.9 Auseinandernehmen von Parametern - split
  - 1.5.7.10 Hinzufügen von Dateien zum Opt-Archiv
  - 1.5.7.11 Kontrollfluss
  - 1.5.7.12 Expressions
  - 1.5.7.13 Match-Operator
  - 1.5.7.14 Prüfen, ob in Abhängigkeit vom Wert einer Variable eine Datei kopiert wurde: copy_pending
  - 1.5.7.15 Vergleich von Netzwerkadressen - samenet und subnet
- 1.5.8 Dokumentation
  - 1.5.8.1 Voraussetzungen für die Erstellung einer LATEX-Dokumentation
  - 1.5.8.2 Dateinamen
  - 1.5.8.3 LATEX-Grundlagen
- 1.5.9 Dateiformate
- 1.5.10 Entwickler-Dokumentation
- 1.5.11 Client-Programme
- 1.5.12 Quellcode
- 1.5.13 Weitere Dateien

# 1.5 Modulkonzept ab fli4l-2.1

fli4l wird ab der Version 2.0 in Module (Packages) aufgeteilt, z.B.

- fli4l-2.x.x <- Das Basis-Paket

- dhcp
- dsl
- isdn
- sshd
- und viele weitere...

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

## 1.5.1 mkfli4l

Aus den Paketen wird in Abhängigkeit von der konkreten Konfiguration eine Konfigurationsdatei namens rc.cfg und ein Tar-Archiv namens opt_tar.bz2 erstellt, die alle Konfigurationsinformationen und alle benötigten Dateien enthalten. Diese beiden 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 1.1 angegebenen Parameter. Fehlen sie, werden die in Klammern angegebenen default-Werte genommen. Eine vollständige Liste der Optionen erhält man, wenn man

        mkfli4l -h

aufruft.

Tabelle 1.1: 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, indem 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äeren Ausdrücke, die vom Basis-Paket bereitgestellt werden, zur Verfügung zu stellen. Diese Datei muß 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

## 1.5.2 Aufbau

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

- Verwaltungsdateien
- Dokumentation
- Entwickler-Dokumentation
- Client-Programme
- Quellcode
- Weitere Dateien

Die einzelnen Teile sind im folgenden näher beschrieben.

## 1.5.3 Die Konfiguration der Pakete

In der Datei config/PACKAGE.txt werden vom Benutzer Änderungen an der Konfiguration des Packages 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

Auch hier sind Kommentare und Leerzeilen erlaubt. Alle Werte hinter dem Gleichheitszeichen müssen in Hochkommas1.1 eingefasst werden, da es sonst beim Booten zu Problemen kommen kann.

Variablen, die benutzt werden, werden in die rc.cfg übernommen, alles andere wird ignoriert. Einzige Ausnahme sind Variablen mit dem Namen <PACKAGE>_DO_DEBUG. Diese dienen zum Debuggen von Paketen und werden pauschal übernommen.

##
1.5.4 Die Liste der zu kopierenden Dateien

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

- welche Dateien zu welchem OPT gehören,
- wann sie in das zu generierende opt_tar.bz2 bzw. ins rootfs übernommen werden sollen,
- welche uid, gid und Rechte sie bekommen soll,
- welche Konvertierungen vor Aufnahme ins tar file erfolgen sollen.

mkfli4l generiert basierend darauf eine Liste mit allen für das Erstellen des Tarballs notwendigen Informationen.

Leere Zeilen und Zeilen, die mit '#' anfangen, werden ignoriert. In einer der ersten Zeile sollte die Version des Opt-File-Formats 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 im opt/<package>.txt File auftaucht, wird von mkfli4l markiert.

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 (d.h. falls die Variable von einer opt-Variable abhängig ist, hat diese den Wert 'yes'), 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, daß aufgrund des aktuellen Wertes der Variable eine Datei kopiert wurde.

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

   Beginnt der Dateiname mit einem `rootfs:`- bzw `rootfs:files/`-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 Config-Directories, wird es in die Liste der aus zum Config-Verzeichnis zu übernehmen Files 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 $KERNELVERSION$ ersetzen. mkfli4l liest die konkrete Version aus der Datei opt/etc/kernel-version 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 mit auf den Router kopiert.

   Abbildung 1.1: Optionen für Dateien

4. In den anderen Spalten können die in Abbildung 1.1 aufgeführten Optionen für uid/gid/Rechte der Dateien und Konvertierungen stehen.

   Einige Beispiele:

   - kopiere Datei, wenn OPT_TELNETD='yes', setze uid/gid auf root und die Rechte auf 755 (rwxr-xr-x)

         telnetd     yes    files/usr/sbin/in.telnetd mode=755

   - kopiere Datei, setze uid/gid auf root, die Rechte auf 555 (r-xr-xr-x) und konvertiere die Datei ins Unix-Format bei gleichzeitigem entfernen aller überflüssigen Zeichen

         base    yes     etc/rc0.d/rc500.killall mode=555 flags=sh

   - kopiere Datei, wenn PCMCIA_PCIC='i822365', setze uid/gid auf root und die Rechte auf 644 (rw-r-r)

         pcmcia_pcic i82365 files/lib/modules/$KERNELVERSION$/pcmcia/i82365.o

   - kopiere Datei, wenn eine der *_% Variablen mit dem zweiten Feld übereinstimmt , setze uid/gid auf root und die Rechte auf 644 (rw-r-r)

         net_drv_%   3c503  files/lib/modules/$KERNELVERSION$/net/3c503.o

   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, daß das entsprechende Paket nicht installiert ist. Das würde zu einer Fehlermeldung in mkfli4l führen, da es erwartet, daß 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 'undefined' gesetzt.

###
1.5.4.1 Konfigurations-spezifische Dateien

In manchen Situationen möchte man originale Dateien durch konfigurations-spezifische Dateien im opt_tar.bz2 ersetzen, wie z.B. Host-Keys, eigene Firewall-Scripte, ... . mkfli4l unterstützt dieses Scenario, indem es prüft, ob eine zu kopierende Datei im Konfig-Verzeichnis zu finden ist und übernimmt in diesem Falle diese Datei in die Liste der ins opt_tar.bz2 aufzunehmenden Dateien.

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

## 1.5.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 Config-Dateien eine Zeile vorhanden. Diese Zeilen bestehen aus vier Spalten, welche folgende Funktionen haben:

1. Variable - Diese Spalte gibt den Namen der zu überprüfenden Variable aus der Config-Datei an. Wenn es sich dabei um eine Variable handelt, die mehrmals mit verschiedenen Nummern 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 daß man auch mehrdimensionale Arrays realisieren kann. Dann sollte zwischen den Prozentzeichen allerdings etwas stehen, muß aber nicht, was dann allerdings so seltsamen Namen wie foo_%__% führen würde.

   Oftmals hat man das Problem, daß 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.

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. Ist die Variable mehrdimensional, wird die Häufigkeit des letzten Index beschrieben. Hängt die Variable von einem Opt ab, muß die VARIABLE_N vom selben Opt oder von keinem Opt abhängig sein. Ist die Variable von keinem Opt abhängig, darf auch die Variable_n von keinem opt abhängig sein. Gibt es keine Variable_n, ist hier ein '-' anzugeben.

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 Input von den Opt-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.

   Die Sache mit dem Prozentzeichen läßt 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.

##
1.5.6 Eigene Definitionen zum Prüfen der Konfigurationsvariablen

### 1.5.6.1 Einführung regulärer Ausdrücke in 2.1

Bisher gab es die oben angeführten 7 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änkt sich auf wesentliche ``Datentypen'', die mit vertretbarem Aufwand geprüft werden können.

Mit der Verion 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.

### 1.5.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-Direktory und trägt den gleichen Namen wie das dazugehörige Paket, Also z.B. base.exp. Es enthält Definitionen für Ausdrücke, die im *.txt Dateien referenziert werden können. So enthält das base.exp im Augenblick Definitionen für die bekannten Prüfungen und isdn.exp eine Definition für die Variable ISDN_CIRC_?_ROUTE (das Fehlen dieser Überprüfung war der Auslöser dieser Änderungen).

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

               Name = 'Regulärer Ausdruck' : 'Fehlermeldung'

   oder am Beispiel aus base.exp:

       NOTEMPTY = '.*[^ ]+.*'          : 'may not be empty'
       YESNO    = 'yes|no'             : 'only yes or no are allowed'
       NUMERIC  = '[0-9]+'             : 'should be numeric'
       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)' : 'no valid ip address'
       EIPADDR  = '()|(RE:IPADDR)'     : 'neither valid ip address nor empty'
       NOBLANK  = '[^ ]+'              : 'may not contain spaces'
       DIALMODE = 'auto|manual|off'    : 'only auto, manual or off are allowd'
       IP_ROUTE = '(RE:NETWORK)[[:space:]]+(RE:IPADDR)'
                : 'no valid route specification (network/cidr gateway)'

   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)'.

   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 Whitespace (Leerzeichen, Tabulator) beginnen. Beim Einlesen der package.exp-Datei werden überflüssige Whitespaces auf eins reduziert und Tabulatoren durch Whitespaces ersetzt. Ein Eintrag in die *.exp Datei könnte dann so aussehen:

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

### 1.5.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, muß 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. Daß 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:

- Das Basis-Paket stellt eine Menge von Ethernet-Treibern bereit und prüft die Variable NET_DRV_x mit dem regulären Ausdruck NET_DRV, der wie folgt spezifiert ist:

              NET_DRV         = '3c503|3c505|3c507|...'
                              : 'invalid ethernet driver, please choose one'
                                ' of the drivers in config/base.txt'

- Das PCMCIA-Paket stellt jetzt zusätzliche Gerätetreiber bereit, muß also NET_DRV ergänzen. Das sieht dann wie folgt aus:

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

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

### 1.5.6.4 Regex anhand von YESNO Variablen definieren

Wenn man NET_DRV wie oben um die PCMCIA Treiber erweitert hat, aber das PCMCIA Paket deaktiviert hat, könnte man dennoch einen PCMCIA Treiber in der base.txt auswählen, ohne das eine Fehlermeldung beim Erstellen der Diskette auftritt. Um das zu verhindern, kann man die Regex auch abhängig von einer YESNO Variablen in der Konfiguration machen. Dazu wird der Name der Variablen die bestimmt ob die Regex benutzt wird mit runden Klammern direkt hinter den Namen der Regex angehängt. Steht die Variable auf 'yes' wird die regex benutzt, ansonsten wird die regex nicht definiert.

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

Wird jetzt OPT_PCMCIA='no' gesetzt und in der base.txt wird z.B. der Treiber xirc2ps_cs benutzt gibt es beim Erstellen der Diskette einen Fehler.

Reguläre Ausdrücke direkt in der Check-Datei in check/*.txt

Manche Ausdrücke kommen nur einmal vor, dann lohnt es sich nicht, dafür einen regulären Ausdruck in *.exp 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.

### 1.5.6.5 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 einem exp file definiert und ein Hinweis auf den Fehler wird mit ausgegeben. Im zweiten Falle wurde der Ausdruck direkt in der *.txt Datei spezifiziert, deshalb gibt es keinen zusätzlichen Hinweis auf die Fehlerursache.

### 1.5.6.6 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:

- 'a*' - beliebig viele a's einschließlich kein a
- 'a+' - mindestens ein a, sonst beliebig viele a's
- 'a?' - kein oder ein a
- 'a{2,5} - zwei bis 5 a's
- 'a{5} - 5 a's
- 'a{2,} - mindestens 2 a's

Ein ``Atom'' ist ein

- regulärer Ausdruck eingeschlossen in Klammern, z.B. (a|b)+ trifft auf eine beliebige Zeichenkette zu, die mindestens ein a oder b enthält, sonst aber beliebig viele und in beliebiger Reihenfolge
- ein leeres Paar Klammern steht für einen ``leeren'' Ausdruck
- ein Ausdruck mit eckigen Klammern '[]' (siehe weiter unten)
- ein Punkt '.', der auf irgend ein einzelnes Zeichen zutrifft, z.B. '.+' trifft auf eine beliebige Zeichenkette zu, die mindestens ein Zeichen enthält
- ein '^' steht für den Zeilenanfang, z.B. '^a.*' trifft auf eine Zeichenkette zu, die mit einem a anfängt und in der beliebige Zeichen folgen, 'a' oder 'adkadhashdkash'
- ein '$' steht für das Zeilenende
- ein ' \' gefolgt von einem der Sonderzeichen '^.[$()|*+?{'` steht für genau das zweite Zeichen ohne seine spezielle Bedeutung
- ein normales Zeichen trifft auf genau das Zeichen zu, z.B. 'a' trifft auf genau 'a' zu.

Ein Ausdruck mit rechteckigen Klammern bedeutet folgendes

- '[x-y]' - trifft auf irgend ein Zeichen zu, das zwischen x und y liegt, z.B. '[0-9]' steht für alle Zeichen zwischen 0 und 9; '[a-zA-Z]' für alle Buchstaben, egal ob groß oder klein

- '[^x-y]' - trifft auf irgendein Zeichen zu, das nicht im angegebenen Intervall liegt

- '[:character_class:] - trifft auf ein Zeichen der Zeichen-Klasse zu. Relevante Standardzeichenklassen sind: alnum, alpha, blank, digit, lower, print, punct, space, upper, xdigit.

### 1.5.6.7 Beispiele für Reguläre Ausdrücke

Sehen wir uns das mal an einigen Beispielen an:

Numerisch: Ein numerischer Wert besteht aus mindestens einer, aber beliebig vielen Zahlen. Mindestens ein, aber beliebig viele drückt man mit '+' aus, eine Zahl 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 soll:

            NOBLANK = '[^ ]+'

Disk und Partition: Gültige Bezeichner für eine Disk beginnen mit hd bei IDE-Disks bzw sd bei SCSI-Disks. Dann folgen Buchstaben von a-z (a für die erste Disk, b für die 2., ...) und bei Partitionen die Zahlen 1-8 (1-4 für die ersten 4 Partitionen, die Primär bzw. Extended (nur eine) sein können und 5-8 für die logischen Partitionen innerhalb einer extended Partition). Die Ausdrücke sehen dann wie folgt aus:

            DISK      = '(hd|sd)[a-z]'
            PARTITION = '(hd|sd)[a-z][1-8]'

Sehen wir uns das ganze nochmal am Beispiel der IP-Addresse an. Eine IP-Adresse besteht aus 4 Octets, die mit einem '.' 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 00 und 99: | [0-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]

Da sich die ersten drei Teile stark ähneln, kann man sie zusammenfassen:

- ein Zahl zwischen 0 und 199: 1?[0-9]?[0-9] (die ersten beiden Stellen können, aber müssen nicht da sein)

Das ganze sind Alternativen, also fassen wir sie einfach mittels '|' zu einem Ausdruck zusammen: '1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]' und haben damit ein Octet. Daraus können wir nun eine IP-Adresse machen, 4 Octets mit Punkten voneinander getrennt (der Punkt muß mittels backslash gequotet 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  = '1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]'
            IPADDR = '((RE:OCTET)\.){3}(RE:OCTET)'

### 1.5.6.8 Unterstützung beim Entwurf regulärer Ausdrücke

Will man reguläre Ausdrücke entwerfen und testen, kann man dazu das regexp tool verwenden, das sich in base/{unix,window}/regexp befindet. Es akzeptiert die folgende Syntax:

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

Dabei bedeuten die Parameter folgendes:

- <check dir> ist das Verzzeichnis, das die check-Dateien und damit auch die *.exp Dateien enthält. Diese werden von regexp einglesen, damit man auf bereits definiert Ausdrücke zurückgreifen kann.

- regexp ist der reguläre Ausdruck (im Zweifelsfall immer in '' angeben)

- string ist der zu prüfende String

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

            ./regexp -c ../check '[0-9]' 0
            ./regexp -c ../check '[0-9]' a
            Error: wrong value of variable command_line_term: 'a'
                   (expression didn't match)
            ./regexp -c ../check IPADDR 192.168.0.1
            ./regexp -c ../check IPADDR 192.168.0.256
            Error: wrong value of variable command_line_term:
                   '192.168.0.256' (no valid ip address)

## 1.5.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 muß z.B. bei Auswahl eines PCMCIA-ISDN-Adapters auch das PCMCIA-Paket installiert werden.

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

1. Schlüsselwörter:

   - Kontrollfluß:

     - if (expr) then statement else statement fi
     - foreach var in set_var do statement done
     - foreach var in set_var_1 ... set_var_n do statement done
     - foreach var in var_n do statement done

   - Abhängigkeiten:
     - provides package version x.y.z
     - depends on package version x.y[<regulärer ausdruck>]

   - Aktionen:
     - warning ``warning''
     - error ``error''
     - fatal_error ``fatal error''
     - set var = value
     - stat (filename, res)
     - crypt (variable)
     - split (string, set_variable, character)
2. Datentypen: strings, numerische Werte, Versionsnummern, Arrays
3. Logische Operationen: <, ==, >, !=, !, &&, ||, =`~`, copy_pending, samenet, subnet

### 1.5.7.1 Definition eines Paket-Identifiers mit einer dazugehörenden Versionsnummer: provides

Damit kann z.B. ein Paket deklarieren, daß es einen Drucker-Dienst bereitstellt oder ein httpd-Paket, daß es einen Webserver bereitstellt. Es kann jeweils nur ein einziges Paket geben, daß einen Identifier bereitstellt. Damit kann man verhindern, daß 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 Pakets angegeben, so daß Weiterentwicklungen Rechnung getragen werden kann. Die Versionsnummer ist dreigeteilt, die ersten beiden Teile sind einstellig, der letzte dreistellig, z.B. 2.1.23.

            provides IDENTIFIER version VERSION_NUMBER

### 1.5.7.2 Definition einer Abhängigkeit von einem anderen Paket mit einer bestimmten Version: depends

Benötigt man zur Erbringung der eigenen Funktionalität ein anderes Paket (z.B. einen Webserver), kann man hiermit diese Abhängigkeit von einem Paket mit einer bestimmten Version spezifizieren. Die Version kann zweistellig (z.B. 2.1), dreistellig (z.B. 2.1.11) oder als dreistelliger regulärer Ausdruck angegeben werden (z.B. 2.1.1[12], also sowohl 2.1.11 als auch 2.1.12 sind akzeptabel).

            depends on IDENTIFIER version VERSION

### 1.5.7.3 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. Das Format sieht wie folgt aus:

- warning ``text''
- error ``text''
- fatal_error ``text''

Im Text kann man auf Variablen Bezug nehmen, indem man einfach den Namen mit einem vorangestellten '$' oder '%' Zeichen in den Text schreibt (evtl. in {} eingeschlossen, um den Variablenamen vom umgebenden Text abzugrenzen). mkfli4l versucht den darauffolgenden Text (Ziffern, Zahlen, '_') als Variablennamen zu interpretieren und setzt bei einem vorangestellten '$' den Inhalt der Variablen an dieser Stelle ein, wenn Sie definiert ist, bzw bei einem vorangestellten '%' den vollständigen Namen der aktuellen %-Variable. Sonst steht der originale Text da. Will man wirklich ein '$' oder ein '%' im Text haben, schreibt man '$$' bzw. '%%'.

### 1.5.7.4 Zuweisungen

Benötigt man aus irgend einem Grund eine temporäre Variable, kann man diese einfach mit ``set var [= value]'' anlegen. Die Variable darf kein Config-Variable sein. Läßt man den ``= value'' Teil weg, wird die Variable einfach auf ``yes'' gesetzt, so daß 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, Versionen.

### 1.5.7.5 Arrays

Will man auf einzelne Elemente einer %-Variablen (eines Arrays) zugreifen, kann man das wie gewohnt mit %_var[index] tun, wobei für jedes % Zeichen ein [ Index ] auftauchen muß.

### 1.5.7.6 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, daß auch auf dem Router benötigt wird. Verwendet wird das wie folgt:

        crypt (password)

###
1.5.7.7 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, andere Attribute sind aber leicht hinzuzufügen. Wenn man auf Dateien unterhalb des aktuellen config Verzeichnisses testen will, kann man $config$ benutzen um auf das aktuelle config Verzeichnis zuzugreifen. Abfragen sehen wie folgt aus (wobei die verwendeten Parameter nur Beispiele sind):

        stat ("unix/Makefile", test)

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

In dem Beispiel mit der Schleife wird geprüft, ob eine Datei mit einem Namen aus einer Configvariablen im aktuellen config Verzeichnis vorhanden ist. Wenn also OPENVPN_1_SECRET='test' in der Konfigurationsdatei gesetzt wird, prüft die Schleife im ersten Durchlauf ob im aktuellen config Verzeichnis die Datei etc/openvpn/test.secret vorhanden ist.

Danach sind zwei Variablen definiert:

- test_res: Resultat des Systemrufs stat() (``OK'', wenn Systemruf erfolgreich, sonst Fehlermeldung des Systemrufs)
- test_size: Größe der Datei

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 ..."
            fi

###
1.5.7.8 Grep in einer Datei - fgrep

Wenn Sie in einer Datei per grep suchen wollen steht Ihnen das fgrep() Kommando zur Verfügung. Die Syntax ist 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 wie folgt zur Verfügung:

- FGREP_MATCH_N: Hier steht die Anzahl der Treffer die im dem FGREP_MATCH Array stehen.

- FGREP_MATCH_x: Wobei x von 0 bis FGREP_MATCH_N reicht. Dort stehen die einzelnen Treffer des grep.

Das folgende Beispiel zeigt die Verwendung von fgrep() aus einem Beispiel aus der base.ext. Hier wird getestet ob alle in der FORWARD_LIST_x angegebenen tmpl: Referenzen vorhanden sind.

    foreach n in forward_list_n
    do
      set rule=forward_list_%[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 FORWARD_LIST_${n}='$rule'!"
              fi
            fi
          fi
        done
      fi
    done

### 1.5.7.9 Auseinandernehmen von Parametern - split

Oftmals werden Variablen mir mehreren Parametern belegt, die dann in Startup-Scripten erst wieder auseinandergenommen werden. Will man diese bereits vorher auseinandernehmen und Tests mit ihnen durchführen, nimmt man split.

            split (string, %_var, character)

Der String kann durch eine Variable, %-Variable oder direkt als String angegeben werden. mkfli4l zerlegt ihn an den Stellen, an denen das Trennzeichen auftaucht und erzeugt je eine Instanz der %-Variablen. Über diese kann man dann hinterher Tests laufen lassen. Steht zwischen zwei Trennzeichen nichts, wird eine Instanz mit einer leeren Zeichenkette als Wert erzeugt. Ausnahme ist ' ', hier werden alle whitespaces konsumiert und keine leeren Variablen erzeugt.

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

            split (string, %_var, character, numeric)

Ein Beispiel könnte wie folgt aussehen:

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

###
1.5.7.10 Hinzufügen von Dateien zum Opt-Archiv

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

Die Syntax sieht wie folgt aus: `add_to_opt <filename> [<flags>]`. Im folgenden ein Beispiel aus dem Package 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 ..."
         fi
       done
    fi

Mit `stat()` wird zunächst geprüft, ob die Datei im Konfig-Verzeichnis existiert. Ist die Datei vorhanden, wird sie ans Opt-Archiv angehängt, andernfalls bricht mkfli4l mit einer entsprechenden Fehlermeldung ab.

Hinweis: Auch bei add_to_opt prüft mkfli4l, ob die zu kopierende Datei im Konfig-Verzeichnis zu finden ist.

### 1.5.7.11 Kontrollfluss

            if (expr)
            then
                    statement
            else
                    statement
            fi

Eine klassische if-Konstruktion, 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 %-Variablen durchführen, muß man jede einzelne Variable testen. Dazu gibt es das foreach-Statement in zwei Varianten:

            foreach loop_var in set_var
            do
                    statement
            done

            foreach loop_var in set_var_1 set_var_2 ... set_var_n
            do
                    statement
            done

Diese Schleife iteriert über alle %-Variablen, angefangen bei eins bis zum in der dazugehörigen (in check/<PACKAGE>.txt stehenden) Variable_n stehenden Index n. Die Laufvariable loop_var nimmt dabei die jeweiligen Werte der %-Variablen an.

Das Statement kann dabei eine der oben beschriebenen Funktionen if, foreach, provides, depends, warning, error oder fatal_error sein.

Will man genau eine %-Variable testen, kann man diese mittels var_%[index] auswählen. Der index kann dabei eine normale Variable, ein String, oder wiederum ein indiziertes Array sein.

            foreach loop_var in var_n
            do
                    statement
            done

Diese Schleife läuft von 1 bis var_n. Man kann die Schleifenvariable loop_var dazu benutzen, um _% Variablen zu indizieren. Will man also nicht nur über eine _% Variable iterieren, sondern über mehrere gleichzeitig, nimmt man diese Variante der Schleife und verwendet die loop_var zum Indizieren mehrerer _% Variablen.

### 1.5.7.12 Expressions

Die Expressions erlaubt so gut wie alles, was man von einer Programmiersprache gewöhnt ist. Ein in einem Ausdruck auftauchender Wert 'val' kann eine Variable, ein String oder ein indiziertes Array sein. Variablen in Strings werden dabei wie oben beschrieben ersetzt. Ein Test auf die Gleichheit zweier Variablen könnte also so aussehen:

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

Zu beachten ist dabei, daß der Vergleich in Abhängigkeit vom Typ der Variable erfolgt, der in check/package.txt festgelegt wurde. Variablen, die als Typnamen einen mit NUM beginnenden Namen haben erhalten einen numerischen Typ. Ist einer der beiden Variablen numerisch, erfolgt der Vergleich auf numerischer Basis, d.h. die Zeichenketten werden in Zahlen umgewandelt und dann vergleichen. Sonst erfolgt der Vergleich auf String-Basis; ein Vergleich von '05' und '5' geht ergibt ungleich, ein Vergleich von '18' und '9' ergibt '18' < '9'.

Für den Vergleich von Versionen wird das Hilfkonstrukt numeric(version) eingeführt, welches den numerischen Wert für einen Versionsstring für Vergleichszwecke bestimmt. numeric(version) == sub_version + 1000 * minor_version + 10000 * major_version

Eine Vollständige Auflistung aller Ausdrücke ist in Tabelle 1.2 zu finden.

Tabelle 1.2: Logische Ausdrücke

expr | true if
id | id == 'yes'
val == val | strings/numerische Werte sind identisch
val != val | strings/numerische Werte sind verschieden
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 paßt
( 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 Subnet von string2 beschreibt

### 1.5.7.13 Match-Operator

Mit dem Match-Operator `=~` kann man prüfen, ob ein regulärer Ausdruckt auf den Wert einer Variable ``matches''. 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 daß 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

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

Mit den im Check-Prozeß 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 eines Strings. Im String werden Ersetzungen wie oben beschrieben durchgeführt, so daß man z.B. mittels einer foreach Schleife und einem ``%loop_var'' alle Inkarantionen einer %-Variable prüfen kann.

copy_pending() prüft dazu, ob

- die Variable aktiv ist (wenn sie von einem opt abhängt, muß dieses auf 'yes' gesetzt sein)

- die Variable in einer opt/package.txt Datei referenziert wurde

- ob in Abhängigkeit vom aktuellen Wert eine Datei kopiert wurde.

Ein kleines Beispiel für die Anwendung all dieser Funktionen findet man im template package.

### 1.5.7.15 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; samenet (netz1, netz2) liefert true, wenn beide Netze identisch sind, subnet (netz1, netz2) liefert true, wenn netz1 ein Subnetz von netz2 ist.

## 1.5.8 Dokumentation

Die Dokumentation wird in den Dateien

- doc/deutsch/opt/<PACKAGE>.txt
- doc/deutsch/opt/<PACKAGE>.html
- doc/english/opt/<PACKAGE>.txt
- doc/english/opt/<PACKAGE>.html

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 (siehe Package HD). Änderungen sollten folgenden Dateien dokumentiert werden:

- doc/deutsch/opt/changes/<PACKAGE>-<VERSION>.txt
- doc/english/opt/changes/<PACKAGE>-<VERSION>.txt

Die gesamte Text-Dokumentation darf keine Tabs enthalten und muß nach spätestens 79 Zeichen einen harten Zeilenumbruch haben. Die stellt sicher, daß die Dokumentation auch mit einem Editor ohne automatischen Zeilen- umbruch richtig gelesen werden kann.

Wer mag kann auch eine Dokumentation im Tex-Format erstellen und daraus dann Text, html, Postscript und PDF-Versionen 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 Template-Paket finden. Eine kurze Beschreibung ist in den folgenden Unterabschnitten zu finden.

### 1.5.8.1 Voraussetzungen für die Erstellung einer LATEX-Dokumentation

Zum Erstellen der Doku aus den LATEX-Sourcen gibt es folgende Anforderungen an die Umgebung:

- Linux/Unix-Umgebung: Zur einfachen Erzeugung gibt es ein Makefile, mit dem alle weiteren Aufrufe automatisiert sind (Cygwin müsste auch tun, ist aber imho noch nicht getestet)
- installiertes latex2html für die HTML-Version
- Installiertes LATEX(tetex unter Unix getest) mit pdftex und folgenden Paketen:
  - aktuelles KOMA-Skript (Version 2)
  - alle notwendigen Pakete für pdftex
  - Ausgepacktes Dokumentationspaket für fli4l, welches die benötigten Makefiles und Stylefiles bereitstellt.

### 1.5.8.2 Dateinamen

Die Dateien der Dokumentation werden nach folgendem Schema benannt:

<packagename>_main.tex
  Enthält den Hauptteil der Dokumentation. <packagename> steht hier für den Namen des Packages, das beschrieben werden soll (in Kleinbuchstaben). Diese Namen entsprechen den Namen der einzelnen Verzeichnisse unterhalb fli4l.
<packagename>_appendix.tex
  Sollen zu diesem Package noch weitere Anmerkungen im Anhang hinzugefügt werden, so werden diese hier abgelegt.

Diese Dateien werden im Verzeichniss

     		fli4l/<packagename>/doc/deutsch/tex/<packagename>

abgelegt. Für das sshd Paket 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, das sshd.tex File stellt einen Rahmen für die eigentliche Doku und den Anhang bereit, der sich in den anderen beiden Dateien befindet. Ansehen kann man sich das am Beispiel der Template-Dokumentation.

### 1.5.8.3 LATEX-Grundlagen

Disclaimer (Jörg): Ich arbeite selbst noch nicht so lange mit LATEX, und was ich hier von mir gebe, ist an vielen Stellen sehr vereinfacht und eingeschränkt; wir werden hier längst nicht alle Möglichkeite einsetzen, die LATEX bietet.

LATEX arbeitet ähnlich wie HTML ``tag-orientiert'', nur das die Tags hier Kommandos heissen und folgendes Format aufweisen: `\kommando` bzw. `\begin{umgebung}` ... `\end{umgebung}`

Nach Möglichkeit sollte man die Kommandos lieber logisch einsetzen als als phyische Auszeichnung missbrauchen, also z.B. `\warning{Bitte nicht ... tun}` statt `\emph{Bitte nicht ... tun}` verwenden.

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

Optionale Paramter werden wie folgt geschrieben: `\kommando[optionalerParameter]{parameter1}` ..., wobei im Normalfall nur 1 optionaler Parameter vorkommt, in bestimmten Fällen aber auch mehrere.

Einzelne Absätze werden im Dokument-Quelltext 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 Template-Dokumentation verwendet und erklärt.

## 1.5.9 Dateiformate

Alle Textdateien (sowohl Dokumentation als auch Scripte, die später auf dem Router liegen) müssen im DOS-Dateiformat, also mit CR-LF statt nur LF in das Package gelegt werden. Dadurch wird erreicht, daß Windows-User die Dokumentation auch mit dem Notepad lesen können und durch eine Änderung eines Scriptes unter Windows das ganze später auf dem Router trotzdem lauffähig bleibt. Die Scripte werden beim Bauen der Archive in das auf dem Router benötigte Format konvertiert (siehe flags in opt/<package>.txt).

## 1.5.10 Entwickler-Dokumentation

Sollte ein Programm aus dem Package 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.

## 1.5.11 Client-Programme

Sollte ein Package 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.

## 1.5.12 Quellcode

Angepasste Programme und Quellcodes können im Verzeichnis src/ beigelegt werden. Handelt es sich um mehrere Dateien, sollte zweckmäßigerweise ein entsprechendes Unterverzeichnis angelegt werden. Sollen die Programme genauso wie die restlichen fli4l-Programme gebaut werden, bitte einen Blick in die Dokumentation des Source-Packages werfen.

##
1.5.13 Weitere Dateien

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

- opt/etc/prep und opt/etc/rc.d Srcipte, die beim Booten des Systems ausgefuehrt werden sollen
- opt/etc/rc0.d Scripte, die beim Runterfahren des Systems ausgefuehrt werden
- opt/etc/ppp Scripte, die beim Einwählen und auflegen ausgeführt werden
- opt/files die ausführbaren Programme und sonstige Dateien entsprechend ihrer Positionen im *nix-Filesystem

Die Scripte in opt/etc/prep, 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, die Namen geben einen Hinweis darauf, welches Programm/Paket von diesem Script behandelt wird.

Das Fli4l-Team
23. Februar 2006