\chapter{Entwickler-Dokumentation}


\section{Inkompatibilitäten zwischen 2.0 und 3.x}

Beim Umstellen von Paketen von Version 2.0 auf 3.x sind folgende Dinge
zu beachten:

\begin{itemize}
\item 3.x verwendet ein anderes Format für die Datei
  opt/$<$package$>$.txt (in 2 Minuten angepaßt), siehe
  \jump{sec:opt_txt}{Liste der zu kopierenden Dateien}
\item uClibc statt libc5, ausführbare Programme müssen neu gebaut
  werden, siehe \jump{sec:libc}{Compilieren von Programmen}
\item  Startup-Scripte müssen jetzt rc$<$nummer$>$.$<$name$>$ genannt
  werden, sonst werden sie nicht ausgeführt, siehe
  \jump{sec:script_names}{Weitere Dateien}

\item iptables statt ipchains, Scripte, die ipchains Kommandos
  verwenden, müssen neu geschrieben werden
\item Linux 2.4.x und 2.6.x statt 2.2.x, Kernelmodule müssen ersetzt
werden
\end{itemize}
\section{Allgemeine Regeln}

Damit ein neues Paket in die OPT-Datenbank auf der FLI4L-Homepage
aufgenommen wird, müssen einige Regeln beachtet werden. Pakete die
sich nicht an diese Regeln halten, können ohne Vorwarnung aus der
Datenbank entfernt werden.

  \begin{enumerate}
    \item KEINE Kopieraktionen von Seiten des Benutzers! FLI4L bietet ein
    ausgefeiltes System, um die Daten der FLI4L-Pakete in das opt.img
    einzupacken. Alle Dateien, die auf den Router sollen, liegen in opt/

    \item Pakete richtig packen und komprimieren: Die Pakete müssen so
    aufgebaut sein, daß sie sich mühelos ins FLI4L-Verzeichnis entpacken
    lassen.

    \item Die Pakete sollen sich VOLLSTÄNDIG über die Konfigurationsdatei
    konfigurieren lassen. Ein weiteres Bearbeiten der Konfigurationsdateien
    darf nicht vom Benutzer verlangt werden. Schwierige Entscheidungen dem
    Benutzer abnehmen oder in einen erweiterten Bereich verlagern (Ans Ende
    der Config-Datei mit einem dicken Hinweis: ONLY MODIFY IF YOU KNOW WHAT
    YOU DO).

    \item Noch ein Hinweis zur Konfigurationsdatei: Anhand des Namens einer
    Variablen muß sich eindeutig erkennen lassen, zu welchem OPT sie gehört.
    So gehören z.B. zum \var{OPT\_\-HTTPD} die Variablen \var{OPT\_\-HTTPD},
    \var{HTTPD\_\-USER\_\-N}, usw.

    \item Bitte, bitte, macht kleine Binaries (Programme)! Wenn ihr sie
    selbst kompiliert habt, vergesst nicht, die Dateien zu strip-en, die
    besten Ergebnisse lassen sich mit dem folgenden Befehl erzielen:
\begin{example}
\begin{verbatim}
      strip -R .comment -R .note <Dateiname>
\end{verbatim}
\end{example}
    Manchmal hilft dieser Befehl auch bei vorkompilierten Programmen weiter.
    Nichts ist frustrierender als ein Download von 2MB, wo man nachher
    feststellt, daß 500kB gereicht hätten...

    \wichtig{Bitte auf diese Art und Weise keine Module strip-en. Sie werden
    hinterher nicht mehr funktionieren!
    Wie man Module am sinnvollsten von Ballast befreit, könnt Ihr in Eurem
    fli4l-Verzeichnis unter src, kernel-2.4 in der Datei mkkernel-strip.sh
    nachlesen.
    }

    \item Prüft euer Copyright! Wenn ihr Dateien als Vorlagen benutzt, achtet
    bitte darauf, das Copyright entsprechend zu ändern. Dies gilt besonders
    für die Config-, Check- und Opt-Textdateien. Ersetzt hier das Copyright
    durch euren eigenen Namen. Bei wortwörtlich kopierter Dokumentation muß
    natürlich das Copyright des Orginal-Autors erhalten bleiben!

    \item Bitte als Archivtypen nur verbreitete, freie Formate benutzen. Dazu
    gehören:
    \begin{itemize}
      \item ZIP  (.zip)
      \item GZIP (.tgz oder .tar.gz)
    \end{itemize}
    Folgende Formate bitte nicht verwenden:
    \begin{itemize}
      \item RAR
      \item ACE
      \item BlackHole
      \item LHA
      \item etc.
    \end{itemize}
    Auch Windows-Installer-Dateien (.msi) oder selbstextrahierende Archive und
    Installer (.exe) sind nicht zu benutzen.
  \end{enumerate}

\marklabel{sec:libc}{
  \section{Compilieren von Programmen}
}

Die für das Compilieren von Programmen erforderliche Umgebung wird in
einem separaten Archiv angeboten. Dazu bitte die Beschreibung im
src.tar.gz ansehen.
\section {Verwendung eines modifizierten Kernels --- Compilieren
eigener Module}

Möchte man die Konfiguration des 2.4er Kernel (analog für den 2.6er
Kernel) für fli4l modifizieren, kann man dazu das Script mkkernel.sh
in src/kernel-2.4 aus dem Src-Archive src.tar.gz verwenden. Dieses
automatisiert das Bauen des Kerns.

Wie das geschieht, sieht man bei einem Aufruf der Hilfe zum Script:

\begin{example}
\begin{verbatim}
    mkkernel.sh -h
\end{verbatim}
\end{example}

Will man die Konfiguration verändern, sind folgende Schritte notwendig:
\begin{enumerate}
\item
         Alle evtl. notwendigen zusätzlichen Patches werden nach
        src/kernel-2.4 kopiert. Die Namen müssen dabei wie folgt lauten:

\begin{example}
\begin{verbatim}
    patch_<patch name>_<kernel version>
\end{verbatim}
\end{example}


      \item Den Kern für das Konfigurieren vorbereiten:
\begin{example}
\begin{verbatim}
    cd src/kernel-2.4
    mkkernel.sh -fetch  # hole den kern von www.kernel.org
    mkkernel.sh -unpack # packe ihn aus
    mkkernel.sh -patch  # wende die Patches an
\end{verbatim}
\end{example}

           Hat man den Kern bereits lokal irgendwo liegen, kann man
           sich das fetch sparen und kann das Verzeichnis, in dem das
           Archiv liegt, als Option an mkkernel.sh mitgeben.


         \item Kernel konfigurieren:
\begin{example}
\begin{verbatim}
    cd linux-<kernel version>
    make xconfig #  oder config oder menuconfig
    cp .config ../dot-config
\end{verbatim}
\end{example}

           Hat man bereits eine fertige dot-config, kann man sich
           2. und 3. ganzen Schritt sparen.


         \item  Den Kern bauen:
\begin{example}
\begin{verbatim}
    mkkernel.sh -remove # lösche alte Binaries
    mkkernel.sh -kernel # bau den Kern und seine Module
    mkkernel.sh -hisax  # bau die Hisax-Module
\end{verbatim}
\end{example}

           Danach liegen der Kern und die Module in einer
fli4l-konformen Verzeichnisstruktur unter ./out.
           Sollte sich die Kernelversion geändert haben, ist
           in der Datei config/base.txt die Variable KERNEL\_VERSION
           anzupassen, da mkfli4l diese Variable verwendet, um die
           korrekte Version der Module einzupacken.

\end{enumerate}

Einige andere sinvolle Optionen zu mkkernel.sh sind die folgenden:

\begin{itemize}
\item {\bf -all} -- führt alle notwendigen Aktionen aus
\item {\bf -nohisax} -- wie all, nur ohne isdn module
\item {\bf -other} -- baut die nicht im kernel enthaltenen Module
\item {\bf -header} -- stellt die Linux-Header zur Verfügung, die man
benötigt, um externe Module zu bauen
\end{itemize}

\section{Modulkonzept}

fli4l wird seit der Version 2.0 in Module (Pakete) aufgeteilt, z.B.
\begin{itemize}
    \item fli4l-\version~~$<$--- Das Basis-Paket

    \item dns-dhcp
    \item dsl
    \item isdn
    \item sshd
\item und viele weitere...
\end{itemize}



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.


\marklabel{mkfli4l}{\subsection{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 \ref{tab:mkfli4l} angegebenen
Parameter. Fehlen sie, werden die in Klammern angegebenen
default-Werte genommen. Eine vollständige Liste der Optionen (Tabelle
\ref{tab:mkfli4l}) erhält
man, wenn man
\begin{verbatim}
    mkfli4l -h
\end{verbatim}
aufruft.

\begin{table}[htbp]
  \centering
  \begin{tabular}{|lp{2cm}|p{9cm}|}
    \hline
    \multicolumn{1}{|c|}{\bf Option} & \multicolumn{2}{c|} {\bf Bedeutung} \\
    \hline
    -c, -\,-config    & \multicolumn{2}{|p{11cm}|} { Setzen des
      Verzeichnisses, in dem mkfli4l die config-Dateien der Pakete
      sucht (default: config)} \\
    -x, -\,-check     & \multicolumn{2}{|p{11cm}|} { 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       & \multicolumn{2}{|p{11cm}|} { Setzen der Logdatei;
      mkfli4l protokolliert Fehlermeldungen und Warnungen in
      dieser Datei (default: img/mkfli4l.log)} \\
    -p, -\,-package   & \multicolumn{2}{|p{11cm}|} { 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       & \multicolumn{2}{|p{11cm}|} { 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    & \multicolumn{2}{|p{11cm}|} { Ausführlichere
      Variante von -i} \\
    -h, -\,-help       & \multicolumn{2}{|p{11cm}|} { Zeigt die Hilfe an} \\
    \html{\multirow{8}{*}{top}} \latex{\multirow{8}{*}{}}{-d, -\,-debug} &
      \multicolumn{2}{|p{11cm}|} { 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.} \\
    \cline{2-3}
    \latex{&} \multicolumn{1}{|c|}{\bf Debugoption} & \multicolumn{1}{c|}{\bf Bedeutung} \\
    \cline{2-3}
    \latex{&} \multicolumn{1}{|l|}{check} & show check process \\
    \latex{&} \multicolumn{1}{|l|}{zip-list} & show generation of zip list \\
    \latex{&} \multicolumn{1}{|l|}{zip-list-skipped} & show skipped files \\
    \latex{&} \multicolumn{1}{|l|}{zip-list-regexp} & show regular expressions for ziplist \\
    \latex{&} \multicolumn{1}{|l|}{opt-files} & check all files in opt/$<$package$>$.txt \\
    \latex{&} \multicolumn{1}{|l|}{ext-trace} & show trace of extended checks \\
    \hline
  \end{tabular}
  \caption{Parameter für mkfli4l}
  \label{tab:mkfli4l}
\end{table}


\subsection{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:

\begin{itemize}
\item Verwaltungsdateien
\item Dokumentation
\item Entwickler-Dokumentation
\item Client-Programme
\item Quellcode
\item Weitere Dateien
\end{itemize}

Die einzelnen Teile sind im folgenden näher beschrieben.


\subsection{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:

\begin{example}
\begin{verbatim}
      #-------------------------------------------------------------------
      # Optional package: TELNETD
      #-------------------------------------------------------------------
      OPT_TELNETD='no'        # install telnetd: yes or no
      TELNETD_PORT='23'       # telnet port, see also FIREWALL_DENY_PORT_x
\end{verbatim}
\end{example}

Auch hier sind Kommentare und Leerzeilen erlaubt. Alle Werte hinter
dem Gleichheitszeichen müssen in Hochkommas\footnote{Es können sowohl
  einfache Hochkommas als auch doppelte Hochkommas verwendet werden.
  Man kann also {\em FOO='bar'} oder auch \emph{FOO=''bar''}
  schreiben.  Die Verwendung von doppelten Hochkommas sollte
  allerdings die Ausnahme sein und man sollte sich vorher
  unbedingt darüber informieren, wie eine Unix-Shell mit einfachen und
  doppelten Hochkommatas umgeht.} 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.

\marklabel{sec:opt_txt}{
  \subsection{Die Liste der zu kopierenden Dateien}
}

    Die Datei opt/$<$PACKAGE$>$.txt enthält Anweisungen, die beschreiben
\begin{itemize}
\item welche Dateien zu welchem OPT gehören,
\item wann sie in das zu generierende opt.img bzw. ins rootfs
    übernommen werden sollen,
\item welche uid, gid und Rechte sie bekommen soll,
\item welche Konvertierungen vor Aufnahme ins tar file erfolgen sollen.
\end{itemize}
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:

\begin{example}
\begin{verbatim}
<erste Spalte>      <zweite Spalte> <dritte Spalte>
opt_format_version  1                    -
\end{verbatim}
\end{example}

    Die restlichen Zeilen haben folgende Syntax:

\begin{example}
\begin{verbatim}
<erste Spalte>  <zweite Spalte> <dritte Spalte> <folgende spalten>
Variable        Wert            Datei           Optionen
\end{verbatim}
\end{example}

    \begin{enumerate}
    \item
        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.

      \item 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.

      \item  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 {\tt rootfs:}- bzw {\tt
          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 dem Config-Verzeichnis zu
        übernehmen Files aufgenommen, andernfalls wird die unter opt
        liegende Datei genommen. Die Datei darf dann kein {\tt
          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
        mit auf den Router kopiert.
        Für Kernel-Module kann der Pfad auch vollkommen entfallen,
        mkfli4l findet das Modul anhand von {\em modules.dep} und {\em
          modules.alias} (\jump{subsec:automatic-dependencies}{siehe}).

        \begin{figure}[h!]
          \centering
          \begin{tabular}{|l|p{5cm}|p{7cm}|}
            \hline
            Option & Bedeutung & Standardwert \\
            \hline
            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 der Datei & 644 (rw-r--r--) für Dateien,
            755 (rwxr-xr-x) für Verzeichnisse \\
            flags= & Konvertierungen vor der Aufname ins tar-File &
            \begin{itemize}
            \item {\em utxt} - Konverierung ins Unix-Format 
            \item {\em dtxt} - Konverierung ins Dos-Format 
            \item {\em sh} - Shell-Script, Konverierung ins
              Unix-Format, entfernen überflüssiger
              Zeichen 
            \end{itemize} \\
            name= & Alternativer Name unter dem das File ins Archiv
aufgenommen wird  &  \\
            \hline
            
          \end{tabular}
          \caption{Optionen für Dateien}
          \label{fig:options}
        \end{figure}
        
      \item In den anderen Spalten können die in Abbildung
\ref{fig:options} aufgeführten Optionen für uid/gid/Rechte der Dateien
und Konvertierungen stehen.

    Einige Beispiele:
    \begin{itemize}
    \item kopiere Datei, wenn \var{OPT\_\-TELNETD}='yes', setze
uid/gid auf root und die Rechte auf 755 (rwxr-xr-x)
\begin{example}
\begin{verbatim}
telnetd     yes    files/usr/sbin/in.telnetd mode=755
\end{verbatim}
\end{example}

    \item 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
\begin{example}
\begin{verbatim}
base    yes     etc/rc0.d/rc500.killall mode=555 flags=sh
\end{verbatim}
\end{example}

         \item kopiere Datei, wenn \var{PCMCIA\_\-PCIC}='i822365', setze
uid/gid auf root und die Rechte auf 644 (rw--r--r)
\begin{example}
\begin{verbatim}
pcmcia_pcic i82365 files/lib/modules/${KERNEL_VERSION}/pcmcia/i82365.o
\end{verbatim}
\end{example}

         \item kopiere Datei, wenn eine der *\_\% Variablen mit dem zweiten Feld
           übereinstimmt , setze
uid/gid auf root und die Rechte auf 644 (rw--r--r)
\begin{example}
\begin{verbatim}
net_drv_%   3c503  3c503.o
\end{verbatim}
\end{example}

    \end{itemize}

    Die Dateien werden nur kopiert, wenn die oben genannten
    Bedingungen erfüllt sind und das dazugehörige \var{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:

\begin{example}
\begin{verbatim}
            weak        variable    -
\end{verbatim}
\end{example}

    Dadurch wird die Variable definiert, wenn sie nicht bereits
    vorhanden ist und ihr Wert wird auf 'undefined' gesetzt.

  \end{enumerate}


\marklabel{subsec:konfigspezdatei}{
\subsubsection{Konfigurations-spezifische Dateien }
}

In manchen Situationen möchte man originale Dateien durch
konfigurations-spezifische Dateien im opt.img 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.img aufzunehmenden
Dateien.

Eine weitere Möglichkeit, konfigurations-spezifische Dateien ans Opt-Archiv
anzuhängen wird im Abschnitt
\jump{subsec:addtoopt}{Erweiterte Prüfungen der Konfiguration} beschrieben.

\marklabel{subsec:automatic-dependencies}{
\subsubsection{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. {\em mkfli4l} bestimmt diese
Abhängigkeiten anhand von {\em modules.dep} und {\em modules.alias}
(zweier beim Kernel-Bauen generierter Dateien) und nimmt automatisch
alle benötigten Module in die Archive auf. So führt z.B. folgender
Eintrag 

\begin{example}
\begin{verbatim}
net_drv_%   ne2k-pci    ne2k-pci.o
\end{verbatim}
\end{example}

dazu, daß sowohl 8390.o, als auch crc32.o ins Archiv aufgenommen
werden, da ne2k\_pci von beiden abhängt. 

Die notwendigen Einträge auf {\em modules.dep} und {\em modules.alias}
werden in das rootfs mit aufgenommen und können von {\em modprobe} zum
laden der Treiber genutzt werden.

\subsection{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:

\begin{enumerate}

\item 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.


\item \var{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.


\item \var{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 \var{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.


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

  \begin{tabular}[h!]{|l|l|}
    \hline
    Name & Bedeutung \\
    \hline
    \hline
    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\\
    \hline
  \end{tabular}

  Werden die Werte mit einem {\bf 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
\begin{example}
\begin{verbatim}
        RE:yes|no
\end{verbatim}
\end{example}
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:
\begin{example}
\begin{verbatim}

      NET_DRV_N          -                  -                  NUMERIC
      NET_DRV_%          -                  NET_DRV_N          NONE
      NET_DRV_%_OPTION   -                  NET_DRV_N          NONE
\end{verbatim}
\end{example}

      Das heißt, dass je nach Wert von \var{NET\_\-DRV\_\-N} die Variablen \var{NET\_\-DRV\_\-N},
      \var{NET\_\-DRV\_\-1\_\-OPTION}, \var{NET\_\-DRV\_\-2\_\-OPTION}, \var{NET\_\-DRV\_\-3\_\-OPTION}, usw. überprüft werden.

\subsection{Eigene Definitionen zum Prüfen der Konfigurationsvariablen}

\subsubsection{Einführung regulärer Ausdrücke}

  In der Version 2.0 gab es nur 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: \altlink{http://unixhelp.ed.ac.uk/CGI/man-cgi?regex+7}.


\subsubsection{Spezifikation regulärer Ausdrücke}

  Spezifizieren kann man die Ausdrücke auf zwei Wegen:

  \begin{enumerate}
  \item 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
    \var{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:
\begin{example}
\begin{verbatim}
        Name = 'Regulärer Ausdruck' : 'Fehlermeldung'
\end{verbatim}
\end{example}
oder am Beispiel aus base.exp:
\begin{example}
\begin{verbatim}
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)' : 'no valid ip address'
EIPADDR  = '()|(RE:IPADDR)'
         : 'should be empty or contain a valid ip address'
NOBLANK  = '[^ ]+'              : 'should not contain spaces'
DIALMODE = 'auto|manual|off'    : 'only auto, manual or off are allowd'
NETWORKS = '(RE:NETWORK)([[:space:]]+(RE:NETWORK))*'
         : 'no valid network specification, should be one or more 
            network addresse(s) followed by a netmask, 
            for instance 192.168.6.0/24'
\end{verbatim}
\end{example}

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:

\begin{example}
\begin{verbatim}
NUM_HEX         = '0x[[:xdigit:]]+'
                : 'should be a hexadecimal number
                   (a number starting with "0x")'
\end{verbatim}
\end{example}

\item  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:

\begin{example}
\begin{verbatim}
        # Variable      OPT_VARIABLE    VARIABLE_N     VALUE
        MOUNT_BOOT      -               -              RE:ro|rw|no
\end{verbatim}
\end{example}

\var{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.

\end{enumerate}

\subsubsection{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:

\begin{itemize}
\item Das Basis-Paket stellt eine Menge von Ethernet-Treibern bereit
  und prüft die Variable \var{NET\_DRV\_x} mit dem regulären Ausdruck \var{NET\_DRV},
  der wie folgt spezifiert ist:

\begin{example}
\begin{verbatim}
        NET_DRV         = '3c503|3c505|3c507|...'
                        : 'invalid ethernet driver, please choose one'
                          ' of the drivers in config/base.txt'
\end{verbatim}
\end{example}
\item Das PCMCIA-Paket stellt jetzt zusätzliche Gerätetreiber bereit,
  muß also \var{NET\_DRV} ergänzen. Das sieht dann wie folgt aus:

\begin{example}
\begin{verbatim}
        PCMCIA_NET_DRV = 'pcnet_cs|xirc2ps_cs|3c574_cs|...' : ''
        +NET_DRV       = '(RE:PCMCIA_NET_DRV)' : ''
\end{verbatim}
\end{example}
\end{itemize}

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

\subsubsection{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.

\begin{example}
\begin{verbatim}
        PCMCIA_NET_DRV(OPT_PCMCIA) = 'pcnet_cs|xirc2ps_cs|3c574_cs|...' : ''
        +NET_DRV       = '(RE:PCMCIA_NET_DRV)' : ''
\end{verbatim}
\end{example}

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.


\marklabel{sec:regexp-dependencies}{
  \subsubsection{Regex in Abhängigkeit von anderen Variablen definieren}
}

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

\begin{example}
\begin{verbatim}
	+NET_DRV(KERNEL_VERSION=~'^2\.4\..*$') = ...
\end{verbatim}
\end{example}

Wenn \var{KERNEL\_VERSION} auf den angegebenen Ausdruck matched, also
irgend eine 2.4er Kernelversion genutzt wird, dann wird die Liste der
erlaubten Netzwerktreiber um die angegebenen Treiber ergänzt.


\subsubsection{Fehlermeldungen}

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

\begin{example}
\begin{verbatim}
Error: wrong value of variable HOSTNAME: '' (may not be empty)
Error: wrong value of variable MOUNT_OPT: 'rx' (user supplied regular expression)
\end{verbatim}
\end{example}

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.


\subsubsection{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:
\begin{itemize}
\item 'a*' - beliebig viele a's einschließlich kein a
\item   'a+' - mindestens ein a, sonst beliebig viele a's
\item   'a?' - kein oder ein a
\item   'a\{2,5\} - zwei bis 5 a's
\item   'a\{5\} - 5 a's
\item   'a\{2,\} - mindestens 2 a's
\end{itemize}

Ein ``Atom'' ist ein
\begin{itemize}
\item  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
        \item   ein leeres Paar Klammern steht für einen ``leeren''
          Ausdruck
        \item   ein Ausdruck mit eckigen Klammern '[]' (siehe weiter unten)
        \item ein Punkt '.', der auf irgend ein einzelnes Zeichen zutrifft,
          z.B. '.+' trifft auf eine beliebige Zeichenkette zu, die
          mindestens ein Zeichen enthält
        \item 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'
        \item ein '\$' steht für das Zeilenende
        \item ein '$\backslash$' gefolgt von einem der Sonderzeichen
          '\^\,.[\$()$|$*+?\{'` steht für genau das zweite Zeichen
          ohne seine spezielle Bedeutung
        \item  ein normales Zeichen trifft auf genau das Zeichen zu,
          z.B. 'a' trifft auf genau 'a' zu.
\end{itemize}

Ein Ausdruck mit rechteckigen Klammern bedeutet folgendes
\begin{itemize}
\item '[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

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

                \item '[:character\_class:] - trifft auf ein Zeichen der Zeichen-Klasse
                  zu. Relevante Standardzeichenklassen sind: alnum, alpha,
                  blank, digit, lower, print, punct, space, upper, xdigit.
\end{itemize}


\subsubsection{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:

\begin{example}
\begin{verbatim}
        NUMERIC = '[0-9]+' oder alternativ
        NUMERIC = '[[:digit:]]+'
\end{verbatim}
\end{example}

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

\begin{example}
\begin{verbatim}
        NOBLANK = '[^ ]*'
\end{verbatim}
\end{example}

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

\begin{example}
\begin{verbatim}
        NOBLANK = '[^ ]+'
\end{verbatim}
\end{example}


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:

\begin{example}
\begin{verbatim}
        DISK      = '(hd|sd)[a-z]'
        PARTITION = '(hd|sd)[a-z][1-8]'
\end{verbatim}
\end{example}

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

\begin{tabular}[h!]{lr}
  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]\\

\end{tabular}

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

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

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 {\em 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:

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


\subsubsection{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:

\begin{example}
\begin{verbatim}
        usage: regexp [-c <check dir>] regexp string
\end{verbatim}
\end{example}

Dabei bedeuten die Parameter folgendes:
\begin{itemize}
\item $<$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.



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


\item string ist der zu prüfende String
\end{itemize}

Das könnte z.B. wie folgt aussehen:
\begin{example}
\begin{verbatim}
        ./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)
\end{verbatim}
\end{example}

\end{enumerate}


\subsection{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:

    \begin{enumerate}
    \item Schlüsselwörter:

      \begin{itemize}
      \item Kontrollfluß:

        \begin{itemize}
        \item if (expr) then statement else statement fi
        \item foreach var in set\_var do statement done
        \item foreach var in set\_var\_1 ... set\_var\_n do statement done
        \item foreach var in var\_n do statement done
        \end{itemize}

      \item
        Abhängigkeiten:
        \begin{itemize}
        \item provides package version x.y.z
        \item depends on package version x.y[$<$regulärer ausdruck$>$]
        \end{itemize}

      \item Aktionen:
        \begin{itemize}
        \item warning ``warning''
        \item error   ``error''
        \item fatal\_error ``fatal error''
        \item set var = value
        \item stat (filename, res)
        \item crypt (variable)
        \item split (string, set\_variable, character)
        \end{itemize}
      \end{itemize}
    \item Datentypen:      strings, numerische Werte, Versionsnummern, Arrays
    \item Logische Operationen:    $<$, ==, $>$, !=, !, \&\&, $||$,
      =\verb?~?, copy\_pending, samenet, subnet
    \end{enumerate}






\subsubsection{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.

\begin{example}
\begin{verbatim}
        provides IDENTIFIER version VERSION_NUMBER
\end{verbatim}
\end{example}

\subsubsection{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).

\begin{example}
\begin{verbatim}
        depends on IDENTIFIER version VERSION
\end{verbatim}
\end{example}

\subsubsection{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:

    \begin{itemize}
    \item warning     ``text''
    \item   error       ``text''
    \item   fatal\_error ``text''
    \end{itemize}

    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.
    '\%\%'.

\subsubsection{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.

\subsubsection{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ß.

\subsubsection{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:

\begin{example}
\begin{verbatim}
    crypt (password)
\end{verbatim}
\end{example}

\marklabel{subsec:statdatei}{
\subsubsection{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):

\begin{example}
\begin{verbatim}
    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
\end{verbatim}
\end{example}

    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:

    \begin{itemize}
    \item test\_res: Resultat des Systemrufs stat() (``OK'', wenn
      Systemruf erfolgreich, sonst Fehlermeldung des Systemrufs)
    \item test\_size: Größe der Datei
    \end{itemize}

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

\begin{example}
\begin{verbatim}
        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
\end{verbatim}
\end{example}

\marklabel{subsec:fgrepdatei}{
\subsubsection{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:

     \begin{itemize}

       \item FGREP\_MATCH\_N: Hier steht die Anzahl der Treffer die im
       dem FGREP\_MATCH Array stehen.

       \item FGREP\_MATCH\_x: Wobei x von 0 bis FGREP\_MATCH\_N
       reicht. Dort stehen die einzelnen Treffer des grep.

     \end{itemize}

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

\begin{example}
\begin{verbatim}
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
\end{verbatim}
\end{example}

\subsubsection{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.

\begin{example}
\begin{verbatim}
        split (string, %_var, character)
\end{verbatim}
\end{example}

    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:
\begin{example}
\begin{verbatim}
        split (string, %_var, character, numeric)
\end{verbatim}
\end{example}

   Ein Beispiel könnte wie folgt aussehen:

\begin{example}
\begin{verbatim}
        set bar=''1.2.3.4''
        split (bar, tmp_%, '.', numeric)
        foreach i in tmp_%
        do
                warning "%i = $i"
        done
\end{verbatim}
\end{example}

\marklabel{subsec:addtoopt}{
\subsubsection{Hinzufügen von Dateien zum Opt-Archiv}
}

   Mit der Funktion {\tt add\_to\_opt()} können zusätzliche Dateien
   ans Opt-Archiv angehängt werden. Es können dabei \emph{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: {\tt add\_to\_opt $<$filename$>$
[$<$flags$>$]}. Im folgenden ein Beispiel aus dem Paket SSHD.

\begin{example}
\begin{verbatim}
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
\end{verbatim}
\end{example}

   Mit \jump{subsec:statdatei}{{\tt 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 \var{mkfli4l} mit einer
   entsprechenden Fehlermeldung ab.

   {\bf Hinweis:} Auch bei \var{add\_to\_opt}
   \jump{subsec:konfigspezdatei}{prüft} \var{mkfli4l}, ob die zu kopierende
   Datei im Konfig-Verzeichnis zu finden ist.




\subsubsection{Kontrollfluss}

\begin{example}
\begin{verbatim}
        if (expr)
        then
                statement
        else
                statement
        fi
\end{verbatim}
\end{example}


    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:

\begin{example}
\begin{verbatim}
        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
\end{verbatim}
\end{example}

    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. Zu beachten ist dabei, dass bei optionalen Variablen, die in der Konfiguration nicht vorhanden sind, eine leere Instanz generiert wird. Unter Umständen muß das im Script berücksichtigt werden, was man z.B. wie folgt tun kann:
\begin{example}
\begin{verbatim}
    foreach i in template_var_opt_%
    do
        if (i != "")
        then
            warning "%i is present (%i='$i')"
        else
            warning "%i is undefined (empty)"
        fi
    done
\end{verbatim}
\end{example}


    Das Statement in der Schleife kann 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.

\begin{example}
\begin{verbatim}
        foreach loop_var in var_n
        do
                statement
        done
\end{verbatim}
\end{example}

    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.


    \subsubsection{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:
\begin{example}
\begin{verbatim}
        var1 == var2
        "$var1" == "$var"
\end{verbatim}
\end{example}

    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
    \ref{tab:expr} zu finden.

  \begin{table}[htb]
    \begin{center}
      \begin{tabular}{ll}
        \hline
        expr &                     true if\\
        \hline
        \hline
       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  =\verb?~? 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\\
        \hline
      \end{tabular}
      \caption{Logische Ausdrücke}
      \label{tab:expr}

    \end{center}
  \end{table}

\subsubsection{Match-Operator}

Mit dem Match-Operator \verb?=~? 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:

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

Ein mkfli4l aufruf führt dann zu folgender Ausgabe:

\begin{example}
\begin{verbatim}
        Warning: match MATCH_1: foo
        Warning: match MATCH_2: bar
        Warning: match MATCH_3: 12
\end{verbatim}
\end{example}



  Bei Verwendung von \verb?=~? 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:
\begin{example}
\begin{verbatim}
        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
\end{verbatim}
\end{example}



\subsubsection{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
        \begin{itemize}
        \item die Variable aktiv ist (wenn sie von einem opt abhängt,
           muß dieses  auf 'yes' gesetzt sein)

         \item die Variable in einer opt/$<$package$>$.txt Datei referenziert
           wurde

         \item ob in Abhängigkeit vom aktuellen Wert eine Datei kopiert
           wurde.
        \end{itemize}

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

\subsubsection{Vergleich von Netzwerkadressen -- {\em samenet} und
  {\em 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 {\em samenet} und
  {\em subnet}; {\em samenet (netz1, netz2)} liefert true, wenn beide Netze
  identisch sind, {\em subnet (netz1, netz2)} liefert true, wenn
  netz1 ein Subnetz von netz2 ist.

\subsubsection{Erweitern der Kernel-Commandline im ext Script}

Ist ein Opt gezwungen, dem Kernel anderen Boot-Parameter zu uebergeben, so musste
die Variable KERNEL\_BOOT\_OPTION geprueft werden, ob der noetige Parameter enthalten war
und ggf. ein warning oder error ausgeben.
mit der internen Variable KERNEL\_BOOT\_OPTION\_EXT geht das jetzt direkt im ext-script:

\begin{example}
\begin{verbatim}
  set kernel_boot_option_ext="${kernel_boot_option_ext} nokbd"
\end{verbatim}
\end{example}

aktuell wird dieser Mechanismus von opt\_embedded benutzt, um bei den Geraeten WRAP und ALIX
den reboot-Mechanismus und die Abwesenheit eines Keyboard-Controllers bekannt zu geben.

\subsection{Unterstützung von Linux 2.4 und 2.6}

Die beiden Kernelversionen unterscheiden sich in kleinen, aber
wichtigen Details:
\begin{itemize}
\item es stehen andere Treiber zur Verfügung, einige sind weggefallen,
  andere Hinzugekommen
\item die Modul-Namen enden mit '.o' bzw. '.ko'
\item die Module heißen teilweise einfach anders
\item die Modul-Abhängigkeiten sehen anders aus
\item die Module liegen woanders
\end{itemize}

Diese Unterschiede werden zum großen Teil durch {\em mkfli4l}
automatisch behandelt. Um die zur Verfügung stehenden Module zu
beschreiben, kann man zum einen die zur Prüfung verwendeten checks in
Abhängigkit von der Version erweitern
(\jump{sec:regexp-dependencies}{bedingte reguläre Ausdrücke}) und zum
anderen erlaubt {\em mkfli4l} versionsabhängige {\em
  opt/$<$package$>$\_2\_4.txt} und {\em opt/$<$package$>$\_2\_6.txt}
Dateien, die je nach Version verschiedene Dateien ins Archiv
aufnehmen.

\subsection{Dokumentation}

    Die Dokumentation wird in den Dateien

    \begin{itemize}
    \item doc/deutsch/opt/$<$PACKAGE$>$.txt
    \item doc/deutsch/opt/$<$PACKAGE$>$.html
    \item doc/english/opt/$<$PACKAGE$>$.txt
    \item doc/english/opt/$<$PACKAGE$>$.html
    \end{itemize}

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

    \begin{itemize}
    \item doc/deutsch/opt/changes/$<$PACKAGE$>$-$<$VERSION$>$.txt
    \item  doc/english/opt/changes/$<$PACKAGE$>$-$<$VERSION$>$.txt
    \end{itemize}

    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.

\subsubsection{Voraussetzungen für die Erstellung einer \LaTeX-Dokumentation}

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

  \begin{itemize}
  \item 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)
  \item installiertes latex2html für die HTML-Version
  \item Installiertes \LaTeX (tetex unter Unix getest) mit pdftex und folgenden
    Paketen:
    \begin{itemize}
    \item aktuelles KOMA-Skript (Version 2)
    \item alle notwendigen Pakete für pdftex
    \item Ausgepacktes Dokumentationspaket für fli4l, welches die
      benötigten Makefiles und Stylefiles bereitstellt.
  \end{itemize}
  \end{itemize}


\subsubsection{Dateinamen}

Die Dateien der Dokumentation werden nach folgendem Schema benannt:

\begin{description}
\item [$<$packagename$>$\_main.tex] Enthält den Hauptteil der
  Dokumentation. $<$packagename$>$ steht hier für den Namen des
  Pakets, das beschrieben werden soll (in Kleinbuchstaben). Diese
  Namen entsprechen den Namen der einzelnen Verzeichnisse unterhalb
  fli4l.
\item[$<$packagename$>$\_appendix.tex] Sollen zu diesem Paket noch
  weitere Anmerkungen im Anhang hinzugefügt werden, so werden
  diese hier abgelegt.
\end{description}


Diese Dateien werden im Verzeichniss
\begin{tabbing}
  aaaaaaaa\=\kill
  \>fli4l/$<$packagename$>$/doc/deutsch/tex/$<$packagename$>$
\end{tabbing} abgelegt.  Für das sshd Paket sieht das z.B. wie folgt aus:
\begin{verbatim}
> ls fli4l/doc/deutsch/tex/sshd/
Makefile sshd_appendix.tex  sshd_main.tex  sshd.tex
\end{verbatim}

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.



\subsubsection{\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: \verb*?\kommando?
bzw. \verb*?\begin{umgebung}? ... \verb*?\end{umgebung}?

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

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

Optionale Paramter werden wie folgt geschrieben:
\verb*?\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 \verb*?\?
maskiert werden: \# \$ \& \_ \% \{ \}. '\verb?~?' und '\verb?^? müssen
wie folgt geschrieben werden: \verb!\verb?~?! \verb!\verb?^?!

Die wichtigsten \LaTeX-Kommandos werden in der Template-Dokumentation
verwendet und erklärt.

\subsection{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 Paket 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).

\subsection{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 {\em doc/dev/$<$PACKAGE$>$.txt} abzulegen.

\subsection{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.

\subsection{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 \jump{buildroot}{Pakets SRC} werfen.

\marklabel{sec:script_names}{
  \subsection{Weitere Dateien}
}

    Alle Dateien, die nachher auf dem Router liegen, werden unter opt/etc/
    und opt/files/ abgelegt. Dabei liegen unter:
    \begin{itemize}
    \item opt/etc/boot.d und opt/etc/rc.d Srcipte, die beim Booten des
      Systems ausgefuehrt werden sollen
    \item opt/etc/rc0.d Scripte, die beim Runterfahren des Systems
      ausgefuehrt werden
    \item opt/etc/ppp Scripte, die beim Einwählen und auflegen
      ausgeführt werden
    \item opt/files die ausführbaren Programme und sonstige Dateien
      entsprechend ihrer Positionen im *nix-Filesystem
    \end{itemize}

    Die Scripte in opt/etc/boot.d, opt/etc/rc.d und opt/etc/rc0.d
    werden wie folgt benannt:
    \begin{trivlist}
    \item rc$<$nummer$>$.$<$name$>$
    \end{trivlist}
    Die Nummer entscheidet über die
    Reihenfolge der Ausführung, die Namen geben einen Hinweis darauf,
    welches Programm/Paket von diesem Script behandelt wird.


\section{Allgemeine Skript-Erstellung auf FLI4L}


Hier folgt jetzt keine allgemeine Einführung in Shell-Scripts, das
kann jeder im Internet selber nachlesen, es wird nur auf die spezielle
Gegebenheiten bei FLI4L eingegangen. Infos dazu gibt es in den
Unix-/Linux- Manualpages. Folgende Links können als Einstiegspunkte zu
diesem Thema dienen:
\begin{itemize}
\item Einführung in Shell-Scripts:
  \begin{itemize}
  \item \altlink{http://cip.physik.uni-freiburg.de/main/howtos/sh.php}
  \end{itemize}
 \item
   Manualpages online:
   \begin{itemize}
   \item \altlink{http://www.freebsd.org/cgi/man.cgi?manpath=Red+Hat+Linux/i386+9}
   \item \altlink{http://heapsort.de/man2web}
   \item \altlink{http://man.he.net/}
   \item \altlink{http://www.linuxcommand.org/superman_pages.php}
   \end{itemize}
\end{itemize}

Für Unix-User: Leider war es wegen des begrenzten Platzes nicht möglich,
erweierte Tools, wie z.B. awk, tail, uniq, etc. einzubinden. Viele
von diesen Tools lassen sich aber mit einfachen Maßnahmen nachbilden

\subsection{Aufbau}

    In der Unix-Welt ist es nötig, ein Script mit dem Namen des Interpreters
    zu beginnen, daher steht in der ersten Zeile:
\begin{example}
\begin{verbatim}
      #!/bin/sh
\end{verbatim}
\end{example}

    Damit man später leichter erkennen kann, was ein Script macht und wer es
    geschrieben hat, sollte jetzt ein kurzer Header folgen, in etwa so:

\begin{example}
\begin{verbatim}
      #--------------------------------------------------------------------
      # /etc/rc.d/rc500.dummy - start my cool dummy server
      #
      # Creation:     19.07.2001  Tobias Gruetzmacher <fli4l@portfolio16.de>
      # Last Update:  11.11.2001  Tobias Gruetzmacher <fli4l@portfolio16.de>
      #--------------------------------------------------------------------
\end{verbatim}
\end{example}

    Nun kann das eigentliche Script folgen...



\subsection{Umgang mit Konfigurationsvariablen}


\subsubsection{Allgemeines}

    Pakete werden über die Datei $<$package$>$/config/$<$package$>$.txt
    konfiguriert. Die darin enthaltenen Variablen werden beim Erzeugen
    der Boot-Diskette in die Datei rc.cfg übernommen. Beim Booten des
    Routers wird diese Datei eingelesen, bevor irgend ein rc Script
    (scripte unter /etc/rc.d) gestartet wird. Diese Scripte können
    dadurch auf alle Konfigurationsvariablen einfach durch
    \$CONFIG\_VARIABLE zugreifen.

    Benötigt man Werte von Konfigurationsvariablen auch nach dem
    Booten noch, müssen sie separat abgelegt werden, da die rc.cfg
    Datei nach dem Hochfahren des Routers verschwindet. Üblich ist
    dabei, die Werte in eine Datei unter /var/run/$<$name$>$
    abzulegen. Von dort kann sie dann ein anderes Script wieder
    auslesen. Dabei gibt es zwei verschiedene Vorgehensweisen:
    \begin{itemize}
    \item Man schreibt die Werte einfach ein eine Datei und ließt sie
      nachher unter Beachtung des verwendeten Formats aus.
    \item Man generiert eine eigene Config-Datei, in der die Variablen
      in der Form FOO='foo' stehen.
    \end{itemize}

    Für beides gibt es im folgenden ein konkretes Beispiel.


\subsubsection{Beispiel für das Ablegen von Werten in einer Datei}

    Am Beispiel des ISDN-Paketes erklärt, würde das Auswerten der
    Konfigurationsvariablen so aussehen (/etc/rc.d/rc340.circuits):

\begin{example}
\begin{verbatim}
        # Laufvariable für % Variablen
        idx=1
        # Ist isdn überhaupt aktiv?
        if [ "$OPT_ISDN" = "yes" ]
        then
        # konfiguriere 1-n circuits
            while [ "$idx" -le "$ISDN_CIRCUITS_N" ]
            do
                # wie ist denn die Einwahlnummer des n-ten circuits
                eval eaz='$ISDN_CIRC_'$idx'_EAZ'
                # und die lokale IP Adresse für das Interface
                eval local='$ISDN_CIRC_'$idx'_LOCAL'
                # ... remote
                eval remote='$ISDN_CIRC_'$idx'_REMOTE'
                # ... netzmaske
                eval netmask='$ISDN_CIRC_'$idx'_NETMASK'

\end{verbatim}
\end{example}

    Da einige Werte später noch gebraucht werden, werden sie in einer
    Datei abgelegt:

\begin{example}
\begin{verbatim}
                # Datei erzeugen und Wert reinschreiben
                echo $local >/var/run/$dev.conf
                # Rest anhängen
                echo $remote >>/var/run/$dev.conf
                echo $netmask >>/var/run/$dev.conf
\end{verbatim}
\end{example}

    Braucht man die Werte später wieder, liest man sie einfach wieder
    aus, wie man am Beispiel von /usr/local/bin/delete-all-routes
    sehen kann:

\begin{example}
\begin{verbatim}
        # auslesen der abgelegten werte
        conf=`cat /var/run/$dev.conf`
        # die in $conf stehenden Werte werden zu $1, $2, $3, ...
        set $conf
        # Verwenden der Werte
        ip addr add $1 peer $2 dev $device
\end{verbatim}
\end{example}

    Steht nicht von vornherein fest, wieviele Werte dort drin stehen
    werden, arbeitet man mit shift, bis keine Variablen mehr da
    sind. Das könnte wie folgt aussehen:

\begin{example}
\begin{verbatim}
        # auslesen der abgelegten werte
        conf=`cat /var/run/$interface.conf`
        # die in $conf stehenden Werte werden zu $1, $2, $3, ...
        set $conf
        # Verwenden der Werte
        while [ "$1" != "" ]
        do
            ip route add -net $1 netmask $2 dev $interface
            # die beiden verbrauchten Werte wegwerfen und die nächsten
            # nach vorne holen. Dabei wird in diesem Fall $3 zu $1, $4
            # zu $2, ... , $n zu $(n-2)
            shift 2
        done
\end{verbatim}
\end{example}


\subsubsection{Beispiel für das Ablegen von Variablen im Config-Dateiformat}

    Möchte man die Variablen im Format der Config-Dateien ablegen,
    könnte das wir folgt aussehen (am Beispiel von register):
    \begin{itemize}
    \item in /etc/rc.d/rc910.register werden die
      Config-Variablen in eine Datei geschrieben:
\begin{example}
\begin{verbatim}
           {
                echo "register_server='$REGISTER_SERVER'"
                echo "register_always='$REGISTER_ALWAYS'"
           } > var/run/register.conf
\end{verbatim}
\end{example}


    \item die Werte werden dann in /usr/local/bin/register.sh einfach
        über das ``source'' Komando der Shell (kurzform ``. filename'')
        wieder eingelesen und sind dann einfach in der Umgebung des
        Scripts verfügbar:
\begin{example}
\begin{verbatim}
              . /var/run/register.conf
\end{verbatim}
\end{example}

    \end{itemize}

    Als Namenskonvention wird hier /var/run/$<$opt name$>$.conf
    festgelegt.


\subsection{Fehlersuche}

    Bei Start-Scripts ist es oft sinnvoll, diese bei Bedarf im Debug-Modus
    der Shell laufen zu lassen, um festzustellen, wo ``der Wurm drin ist''.
    Dazu wird am Anfang und am Ende folgendes eingefügt:

\begin{example}
\begin{verbatim}
      begin_script <OPT-Name> "start message"
      <script code>
      end_script
\end{verbatim}
\end{example}

Im normalen Betrieb erscheint jetzt beim Start des Scriptes der
angegebene Text und am Ende der gleiche Text mit einem vorangestellten
``finished''.

Will man die Scripte debuggen, muß man zwei Dinge tun:

\begin{enumerate}

\item Man muß \jump{DEBUGSTARTUP}{\var{DEBUG\_\-STARTUP}} auf 'yes'
  setzen.
\item Man muß das Debuggen für dieses Opt aktivieren. das tut man in
  der Regel durch den Eintrag
\begin{example}
\begin{verbatim}
      <OPT-Name>_DO_DEBUG='yes'
\end{verbatim}
\end{example}
in der Konfigurationsdatei\footnote{Manchmal werden mehrere
  Startupscripte verwendet, die dann auch verschiedene Namen für ihre
  Debug-Variablen haben. Hier hilft ein kurzer Blick in die Scripte}.
    Jetzt wird während der Ausführung am Bildschirm genau dargestellt, was
    passiert.
\end{enumerate}


\subsubsection{Weitere beim Debuggen hilfreiche Variablen}

\begin{description}

  \config{DEBUG\_ENABLE\_CORE}{DEBUG\_ENABLE\_CORE}{DEBUGENABLECORE}

  Diese Variable gestattet das Erzeugen von core-Dumps. Stürzt ein
  Programm aufgrund eines Fehlers ab, wird ein Abbild des aktuellen
  Zustandes im Filesystem abgelegt, der hinterher zur Analyse des
  Problems verwendet werden kann. Die core-Dumps werden unter
  /var/log/dumps/ abgelegt.

  \config{DEBUG\_IP}{DEBUG\_IP}{DEBUGIP}

  Wird diese Variable gesetzt, werden alle Aufrufe des Programms {\em
    ip} protokolliert.

  \config{DEBUG\_IPUP}{DEBUG\_IPUP}{DEBUGIPUP}  

  Wird diese Variable auf {\em yes} gesetzt, werden während der
  Ausführung der ip-up/-down-Scripte traces mitgezeichnet und im
  System-Log protokollier.

  \config{LOG\_BOOT\_SEQ}{LOG\_BOOT\_SEQ}{LOGBOOTSEQ}

  Wird diese Variable auf {\em yes} gesetzt, protokolliert der {\em
    bootlogd} während des Bootens alle auf die COnsole getätigten
  Ausgaben. Diese Variable hat standardmäßig den Wert {\em yes}.

  \config{DEBUG\_KEEP\_BOOTLOGD}{DEBUG\_KEEP\_BOOTLOGD}{DEBUGKEEPBOOTLOGD}

  Normalerweise wird der bootlogd am Ende des Bootvorganges
  beendet. Ein Setzten dieser Variable unterbindet das und erlaubt ein
  Protokollieren der Konsolenausgaben über den Bootvorgang hinaus.

  \config{DEBUG\_MDEV}{DEBUG\_MDEV}{DEBUGMDEV}

  Ein Setzen dieser generiert ein Protokoll des mdev-Daemons, der für
  das Anlegen der Geräte-Dateien unter /dev zuständig ist.

%  \config{}{}{}
\end{description}
\subsection{Hinweise}
\begin{itemize}
\item  Es ist {\bf immer} besser, geschweifte Klammern {} an Stelle von runden
      Klammern () zu benutzen. Allerdings muß dabei darauf geachtet werden,
      daß nach der öffnenden Klammer ein Leerzeichen oder eine neue Zeile vor
      dem nächsten Befehl kommt und vor der schließenden Klammer ein
      Semikolon oder auch eine neue Zeile kommt. Beispielsweise ist:
\begin{example}
\begin{verbatim}
        { echo "cpu"; echo "quit"; } | ...
\end{verbatim}
\end{example}

        gleichbedeutend mit:

\begin{example}
\begin{verbatim}
        {
                echo "cpu"
                echo "quit"
        } | ...
\end{verbatim}
\end{example}


      \item Ein Script kann mit ``exit'' vorzeitig beendet
        werden. Dies ist aber bei den Start-Scripts (opt/etc/boot.d,
        opt/etc/rc.d/*), den Stop-Scripts (opt/etc/rc0.d/*) und den
        ip-up/ip-down-Scripts (opt/etc/ppp/*) geradezu tödlich, da
        auch nachfolgende Scripts nicht mehr ausgeführt werden. Im
        Zweifelsfall immer weglassen.


      \item KISS - Keep it small and simple - Du willst Perl als
        Script-Sprache verwenden? Dir reichen die
        Scripting-Möglichkeiten von FLI4L nicht?  Überdenke deine
        Einstellung! Ist dein OPT wirklich nötig? FLI4L ist immer noch
        ``nur'' ein Router, ein Router sollte eigentlich keine
        Serverdienste anbieten.


      \item Die Fehlermeldung ``: not found'' heißt meistens, das das
        Script noch im DOS-Format vorliegt. Weitere Fehlerquelle: Das
        Script ist nicht ausführbar. In beiden Fällen sollte das
opt/$<$package$>$.txt File daraufhin geprüft werden, ob es die
Korekten mode/gid/uid/flags optionen enthält bzw. Wenn das Script erst
bem Booten erzeugt wurde, ein ``chmod +x $<$Scriptname$>$'' ausgeführt
werden.

      \item Für temporäre Dateien sollte der Pfad /tmp genutzt werden.
        Es ist aber unbedingt darauf zu achten, daß hier nur wenig
        Platz ist weil dies in der Rootfs-Ramdisk liegt! Wenn mehr
        Platz benötigt wird, muss man sich eine eigene Ramdisk
        erstellen und mounten. Details dazu verrät der Abschnitt ``RAM-Disks''
        dieser Dev-Doku.

    \item Damit temporäre Dateien eindeutige Namen erhalten sollte man
      grundsätzlich die aktuelle Prozess-ID an die Datei anhängen. Ein
      ``guter'' Dateiname ist also /tmp/$<$name-des-opts$>$-\$\$ statt
      /tmp/$<$name-des-opts$>$, wobei $<$name-des-opts$>$ natürlich
      nicht so stehen bleiben soll, sondern ersetzt wird durch...was
      wohl?

\end{itemize}

\section{Arbeit mit dem Paketfilter}
\subsection{Hinzufügen von eigenen Chains und Regeln}

Zur Manipulation des Paketfilters steht eine Reihe von Routinen zur
Verfügung, mit deren Hilfe man Chains und Regeln hinzufügen und wieder
löschen kann.

\begin{description}
\item [add\_chain chain, add\_nat\_chain chain] Fügt eine Chain zur
  Filter- oder Nat-Tabelle hinzu. 
\item [flush\_chain chain, flush\_nat\_chain chain] Entfernt alle
  Regeln aus einer Chain
\item [del\_chain chain, del\_nat\_chain chain] Entfernt eine Chain
  aus der Filter- oder Nat-Tabelle; Chains müssen leer sein, bevor sie
  gelöscht werden können und es darf auch keine Referenz mehr auf sie
  geben
  
\item[add\_rule, ins\_rule, del\_rule] Fügt Regeln am Ende einer Chain
  (add) bzw. in die chain (ins) ein und löscht Regeln wieder
  (del). Ein Aufruf sieht wie folgt aus:
  
\begin{verbatim}
add_rule table chain rule comment
ins_rule table chain rule position comment
del_rule table chain rule comment
\end{verbatim}
  wobei die Parameter folgende Bedeutung haben:
  \begin{description}
  \item[table] Die Tabelle, in deren Chain die Regel eingefügt werden soll
  \item[chain] Die Chain, in die die Regel eingefügt werden soll
  \item[rule] Die Regel, die eingefügt werden soll; das Format
    entspricht dem im Config-File verwendeten
  \item[position] Die Position, an der die Regel eingefügt werden soll
  \item[comment] Ein Kommentar, der zusammen mit der Regel angezeigt
    werden soll, wenn sich jemand den Paketfilter ansieht.
  \end{description}
\end{description}


\subsection{Einordnen in bestehende Regeln}

Fli4l konfiguriert den Paketfilter mit einem gewissen
Standardregelsatz. Will man eigene Regeln einfügen, wird man diese in
der Regel nach dem Standardregelsatz einfügen wollen. Ebenfalls wird
man wissen wollen, was denn die vom Nutzer gewünschte Aktion beim
Verwerfen eines Paketes ist. Diese Informationen bekommt man für die
FORWARD und INPUT Chain durch Aufruf zweier Funktionen wie im
folgenden gezeigt:

\begin{verbatim}
get_defaults FORWARD # get default actions for forward chain
get_count    FORWARD # get number of default rules for forward
\end{verbatim}

\begin{description}
\item[\$res] {\em res} enthält die Position nach den
  default rules
\item[\$drop] {\em drop} enthält die Chain, in die verzweigt werden
  soll, wenn ein Paket verworfen wird
\item[\$reject] enthält die Chain, in die verzweigt werden
  soll, wenn ein Paket abgelehnt wird
\end{description}

\subsection{Erweiterung der Paketfilter-Matches}

Fli4l verwendet in den Paketfilterregeln die Syntax {\tt
match:params}, um zusätzliche Bedingungen an die Pakete zu stellen
(siehe mac:, limit:, length:, prot:, ...). Will man zusätzliche
Matches hinzufügen, wird das folgendermassen gemacht:

\begin{enumerate}
\item Anlegen einer Datei opt/etc/rc.d/fwrules-{\em $<$name$>$}.ext.
In diesem File steht in etwa folgendes:
\begin{example}
\begin{verbatim}
# extension is available
foo_p=yes

# the actual extension, adding matches to match_opt
do_foo ()
{
        param=$1
        get_negation $param
        match_opt="$match_opt -m foo $neg_opt --fooval $param"
}
\end{verbatim}
\end{example}

\item Testen der Erweiterung
\begin{example}
\begin{verbatim}
> cd opt/etc/rc.d
> sh test-rules.sh 'foo:bar ACCEPT'
add_rule filter FORWARD 'foo:bar ACCEPT'
iptables -t filter -A FORWARD -m foo --fooval bar -s 0.0.0.0/0 \
    -d 0.0.0.0/0 -m comment --comment foo:bar ACCEPT -j ACCEPT
\end{verbatim}
\end{example}

\item Aufnahme der Erweiterung und aller noch benötigten Dateien
(iptables-Komponenten, netfilter-Kernelmodule) ins Archiv über einen
der bekannten Mechanismen.
\item Zulassen der Erweiterung in der Konfiguration durch Erweitern
von \var{PF\_GENERIC\_MATCH} in einem *.exp file, z.B. wie folgt:

\begin{example}
\begin{verbatim}
+FW_GENERIC_MATCH='foo:bar' : ''
\end{verbatim}
\end{example}
\end{enumerate}
\section{CGI-Erstellung für das httpd-Paket}

\subsection{Allgemeines zum Webserver}
Der Webserver, der bei FLI4L verwendet wird, ist der {\tt mini\_httpd} von ACME
Labs. Die Quellen und Manpages können unter
\altlink{http://www.acme.com/software/mini_httpd/} heruntergeladen werden.
Allerdings wurden für FLI4L in der aktuellen Version (1.19) ein paar Änderungen
vorgenommen, der Patch befindet sich im SRC-Paket im Verzeichnis {\tt
src/fli4l/httpd}.

\subsection{Scriptnamen}

Der Scriptname sollte möglichst vielsagend sein, damit er von
anderen Scripts leichter zu unterscheiden ist und es keine
Namensüberschneidungen bei verschiedenen OPTs gibt.

Damit die Scripts ausführbar gemacht werden und DOS-Zeilenumbrücke in
UNIX-Zeilenumbrüche umgewandelt werden, muss in der opt/euer\_opt.txt ein
entsprechender Eintrag gemacht werden (s.o.).

\subsection{Menü-Einträge}

Um eine Eintrag im Menü vorzunehmen, muß eine Eintragung in der Datei {\tt
/etc/httpd/menu} vorgenommen werden. Dieser Mechanismus erlaubt es den OPTs,
auch im laufendem Betrieb Änderungen am Menü vorzunehmen. Dies sollte nur mit
dem Script {\tt httpd-menu.sh} gemacht werden, die dieses darauf achtet, dass
das Dateiformat dieser Datei immer schlüssig ist. Um neue Menüpunkte
einzufügen, wird es folgendermaßen aufgerufen:

\begin{verbatim}
    httpd-menu.sh add [-p <priority>] <link> <name> [section] [realm]
\end{verbatim}

So wird ein Eintrag mit dem Namen {\tt $<$name$>$} in den Abschnitt {\tt [section]}
eingetragen. Wenn {\tt [section]} weggelassen wird, wird es standardmäßig in den
Abschnitt ,,Opt'' eingetragen. {\tt $<$link$>$} gibt das Ziel des neuen Links
an. {\tt $<$priority$>$} spezifiziert die Priorität eines Menüeintrags in
seinem Abschnitt. Wird sie nicht angegeben, wird die Standardpriorität 500
benutzt. Die Priorität sollte eine dreistellige Nummer sein. Je kleiner die
Priorität, desto weiter oben steht der Link in dem Abschnitt. Soll ein Eintrag
möglichst weit nach unten, so ist z.B. die Priorität 900 zu wählen. Bei
gleicher Priorität werden die Einträge nach dem Ziel des Links sortiert.
Bei {\tt [realm]} wird der Breich angegeben, für den ein eingeloggter Benutzer 
mindestens die Berechtigung \var{view} haben muss, damit der Menüpunkt angezeigt
wird. Wird {\tt [realm]} nicht angegeben, wird der Menüpunkt immer angezeigt
Siehe auch \jump{sec:rights}{Abschnitt Benutzerrechte}.

Beispiel:

\begin{verbatim}
    httpd-menu.sh add "neuedatei.cgi" "Hier klicken" "Tools" "tools"
\end{verbatim}

Dieses Beispiel erzeugt einen Link mit dem Titel ,,Hier klicken'' und dem
Link-Ziel ,,neuedatei.cgi'' im Abschnitt ,,Tools'' und legt diesen Abschnitt
bei Bedarf an.

Das Skript kann auch Einträge aus dem Menü wieder entfernen:

\begin{verbatim}
    httpd-menu.sh rem <link>
\end{verbatim}

Mit diesem Aufruf wird der Eintrag mit dem Link {\tt $<$link$>$} wieder
entfernt. Achtung: Wenn mehrere Menüeinträge auf die gleiche Datei verweisen,
werden alle diese Einträge aus dem Menü entfernt.

Da Abschnitte auch Prioritäten haben können, können diese auch manuell angelegt
werden. Wird ein Abschnitt automatisch beim Hinzufügen eines Eintrages
angelegt, erhält er automatisch die Priorität 500. Der Syntax zum Anlegen von
Abschnitten lautet:

\begin{verbatim}
    httpd-menu.sh addsec <priority> <name>
\end{verbatim}

Auch hier sollte {\tt $<$priority$>$} nur 3-stellige numerische Werte annehmen.

\wichtig{Um sinnvolle Prioritäten in Erfahrung zu bringen, lohnt es sich, auf
einem laufenden fli4l in die Datei {\tt /etc/httpd/menu} zu schauen, die
Prioritäten stehen in der 2. Spalte}

Der Vollständigkeit halber wird hier kurz auf das Dateiformat der Menüdatei
eingegangen. Wem die Funktion von {\tt httpd-menu.sh} reicht, der kann diesen
Abschnitt überspringen. Die Datei {\tt /etc/httpd/menu} hat den folgenden
Aufbau: Sie ist in vier Spalten eingeteilt. In der ersten Spalte steht ein
Kennbuchstabe, der Überschriften und Einträge unterscheidet. In der zweiten
Spalte steht die Sortierungspriorität. Die dritte Spalte enthält bei Einträgen
das Ziel des Links und bei Überschriften einen Bindestrich, da dieses Feld bei
Überschriften keine Bedeutung hat. Im Rest der Zeile steht der Text, der
nachher im Menü erscheint.

Überschriften benutzen den Kennbuchstaben 't', dann wird ein neuer Menüabschnitt
begonnen. Normale Menüeinträge benutzen den Kennbuchstaben 'e'.
Ein Beispiel:

\begin{verbatim}
    t 300 - Mein tolles OPT
    e 200 meinopt1.cgi Mach was tolles
    e 500 meinopt1.cgi?mehr=ja Mach mehr tolles
\end{verbatim}

Beim Bearbeiten dieser Datei muss man darauf achten, dass das {\tt
httpd-menu.sh}-Script die Datei immer sortiert abspeichert. Die einzelnen
Abschnitte sind sortiert und die Einträge pro Abschnitt sind in diesem
Abschnitt sortiert. Der genau Sortieralgorithmus kann aus {\tt httpd-menu.sh}
übernommen werden - besser wäre allerdings, dieses Script um mögliche neue
Funktionen zu erweitern, damit alle Menü-Bearbeitungen an zentraler Stelle
passieren.

\subsection{Aufbau eines CGI-Skriptes}

\subsubsection{Die Kopfzeilen}
Alle Skripte des Webservers sind einfache Shell-Skripte (Interpreter wie z.B.
Perl, PHP, etc. sind viel zu groß für FLI4L). Sie sollten mit dem
obligatorischen Script-Header anfangen (Verweis auf den Interpreter,
Name, Sinn des Skriptes, Autor, Lizenz).

\subsubsection{Das Hilfsskript cgi-helper}
Gleich nach den Kopfzeilen sollte dann schon das Hilfsskript cgi-helper
mit folgendem Aufruf eingebunden werden:

\begin{verbatim}
. /srv/www/include/cgi-helper
\end{verbatim}

Wichtig ist ein Leerzeichen zwischen Punkt und Slash!

Dieses Skript stellt diverse Hilfsfunktionen bereit, die das Erstellen
von CGIs für fli4l wesentlich vereinfachen sollen. Außerdem werden mit dem Einbinden
des cgi-helper auch noch Standardaufgaben ausgeführt, wie beispielsweise das
parsen von Variablen, die mit Formularen oder über die URL übergebenen wurden, oder das laden
von Sprach- und CSS-Dateien.

Hier eine kleine Funktionsübersicht:

\begin{table}[htbp]
  \begin{scriptsize}
    \begin{tabular}{ll}
      Name                         & Funktion         \\
      \hline \\
      check\_rights                & Überprüfung der Benutzerrechte \\
      http\_header                 & Ausgabe eines Standard HTTP-Headers oder eines speziellen Headers, beispielsweise zum Download von Dateien\\
      show\_html\_header           & Ausgabe des kompletten Seitenheaders (inkl. HTTP-Header, Kopfzeile und Menü)\\
      show\_html\_footer           & Ausgabe des Abschlusses der HTML-Seite \\
      show\_tab\_header            & Ausgabe eines Inhalts-Fensters mit Tabs\\
      show\_tab\_footer            & Ausgabe des Abschlusses des Inhaltsfensters\\
      show\_error                  & Ausgabe eines Fensters für Fehlermeldungen (Farbe: rot)\\
      show\_info                   & Ausgabe eines Fensters für Informationen/ Erfolgsmeldungen (Farbe: grün)\\
    \end{tabular}
  \end{scriptsize}
  \caption{Funktionen des cgi-helper Skriptes.}
\end{table}

\subsubsection{Der Inhalt}

Um ein einheitliches Design und vor allem die Kompatibilität
mit zukünftigen fli4l-Versionen zu gewährleisten, ist es sehr zu empfehlen, die
oben genannten Funktionen zu benutzen, auch wenn man in einem CGI theoretisch
alle Ausgaben selbst generieren könnte.

Eine einfaches CGI-Skript könnte wie folgt aussehen:

\begin{verbatim}
   #!/bin/sh
   # --------------------
   # Header (c) Autor Datum
   # --------------------
   # get main helper functions
   . /srv/www/include/cgi-helper

    show_html_header "Mein erstes CGI"
    echo '   <h2>Willkommen</h2>'
    echo '   <h3>Dies ist ein Beispiel-CGI-Skript</h3>'
    show_html_footer

\end{verbatim}

Der Parameter, welcher der Funktion show\_html\_header übergeben wird, wird
dabei als Titel verwendet. Sie generiert automatisch das Menü und bindet ebenso
automatisch zum Skript gehörende CSS und Sprachdateien ein. Voraussetzung dafür ist,
dass diese sich in den Verzeichnissen /srv/www/css bzw. /srv/www/lang befinden
und den selben Namen (aber natürlich eine andere Endung) wie das Skript haben.
Ein Beispiel:

\begin{verbatim}
    /srv/www/admin/OpenVPN.cgi
    /srv/www/css/OpenVPN.css
    /srv/www/lang/OpenVPN.de
\end{verbatim}

Sowohl das Benutzen von Sprachdateien als auch von CSS-Dateien ist optional. Immer
Eingebunden werden css/main.css und lang/main.xx wobei 'xx' der gewählten Sprache
entspricht.

Der Funktion show\_html\_header können aber neben dem Titel noch weitere Parameter
übergeben werden. Ein Aufruf mit allen möglichen Parametern könnte so aussehen:

\begin{verbatim}
    show_html_header "Titel der Seite" "refresh=$time;url=$url;cssfile=$cssfile;showmenu=no"
\end{verbatim}

Alle zusätzlichen Parameter müssen, wie im Beispiel gezeigt, mit Anführungszeichen
umschlossen und durch ein Semikolon getrennt werden. Eine Überprüfung der
Syntax erfolgt NICHT! Es ist also notwendig sehr genau auf die Parameterübergabe
zu achten.

Hier eine kurze Übersicht über die Funktion der Parameter:
\begin{itemize}
 \item refresh=\$time: Zeit in Sekunden in der die Seite vom Browser neu geladen werden soll.
 \item url=\$url: Die URL die bei einem Refresh neu geladen wird.
 \item cssfile=\$file: Name einer CSS-Datei, wenn diese vom Namen des CGIs abeicht.
 \item showmenu=no: Damit kann die Anzeige des Menüs und des Headers unterdrückt werden.
\end{itemize}

Sonstige Richtlinien zum Inhalt:

\begin{itemize}
 \item Fasst euch kurz :-)
 \item Schreibt sauberes HTML (SelfHTML ist da ein guter Ansatzpunkt)
 \item Verzichtet auf hochmodernen Schnick-Schnack (JavaScript ist OK, wenn es
nicht stört, sondern den Benutzer unterstützt, das ganze muß auch
ohne JavaScript funktionieren)
\end{itemize}

\subsubsection{Die show\_tab\_header Funktion}
Damit der Inhalt eurer mithilfe des CGI erzeugten Webseite auch hübsch geordnet
aussieht könnt ihr die cgi-helper Funktion show\_tab\_header nutzen.
Sie erzeugt dann anklickbare 'Tabs', so dass ihr eure Seite in mehrere Bereiche
unterteilen könnt, oder einfach nur eine Titelzeile (ein Tab).

Der show\_tab\_header Funktion werden Parameter immer in Paaren übergeben entgegen.
Der erste Wert entspricht dem Titel eines Tabs, der 2. dem Link.
Wird als Link die Zeichenkette 'no' übergeben, wird lediglich der Titel ausgegeben
und der Tab ist nicht anklickbar (und blau).

Im folgenen Beispiel wir ein 'Fenster' mit dem Titel 'Ein tolles Fenster'
erzeugt. Im Fenster steht 'foo bar':

\begin{verbatim}
    show_tab_header "Ein tolles Fenster" "no"
    echo "foo"
    echo "bar"
    show_tab_footer
\end{verbatim}

In diesem Beispiel werden zwei anklickbare Auswahltabs generiert, die dem
Skript die Variable action mit verschiedenen Werten übergibt.

\begin{verbatim}
    show_tab_header "1. Auswahltab" "$myname?action=machdas" \
                    "2. Auswahltab" "$myname?action=machdies"
    echo "foo"
    echo "bar"
    show_tab_footer
\end{verbatim}

Nun kann das Skript den Inhalt der Variablen FORM\_action (siehe weiter unten
zur Variablenauswertung) auswerten und je nach dem unterschiedliche Inhalte
bereitstellen. Damit der angeklickte Tab auch ausgewählt erscheint und nicht
mehr angeklickt werden kann, müsste der Funktion statt dem Link wie schon
erwähnt ein 'no' übergeben werden. Das geht aber auch einfacher, wenn man sich
an die Konvention in folgendem Beispiel hält:

\begin{verbatim}
    _opt_machdas="1. Auswahltab"
    _opt_machdies="2. Auswahltab"
    show_tab_header "$_opt_machdas" "$myname?action=opt_machdas" \
                    "$_opt_machdies" "$myname?action=opt_machdies"
    case $FORM_action in
        opt_machdas) echo "foo" ;;
        opt_machdies) echo "bar" ;;
    esac
    show_tab_footer
\end{verbatim}

Wird also dem Titel eine Variable übergeben, deren Namen dem Inhalt der Variablen
action mit führendem '\_' entspricht, wird der entsprechende Tab ausgewählt
dargestellt.

\subsubsection{Mehrsprachfähigkeit}

Das Hilfsskript enthält auch alles, um CGI-Skripte mehrsprachfähig zu machen.
Dazu müsst ihr 'nur' für alle Textausgaben Variablen mit führenden '\_' verwenden
und diese Variablen in den entsprechenden Sprachdateien definieren.

Beispiel:

lang/opt.de:
\begin{verbatim}
    _opt_machdas="Eine Ausgabe"
\end{verbatim}

lang/opt.en:
\begin{verbatim}
    _opt_machdas="An Output"
\end{verbatim}

admin/opt.cgi
\begin{verbatim}
    ...
    echo $_opt_machdas
    ...

\end{verbatim}


\subsubsection{Formular-Auswertung}

Um Formulare zu verarbeiten, muss man noch einige Dinge wissen. Egal ob die
Formular-Methode GET oder POST verwendet wird, die Variablen finden sich nach
dem Einbinden des cgi-helpers (welches wiederum das Hilfsprogramm proccgi aufruft)
als shell-variable wieder, mit dem Prefix ,,FORM\_''. gespeichert.
Wenn das Formularfeld also den Namen ,,eingabe'' hatte, kann im Shell-Script
mit {\tt\$FORM\_eingabe} darauf zugegriffen werden.

Weitere Infos zum Programm proccgi:
\altlink{http://www.fpx.de/fp/Software/ProcCGI.html}

\marklabel{sec:rights}{
    \subsection{Benutzerrechte}
}
Um die Rechteverwaltung zu verwenden, müssen am Anfang des Scripts folgende
Zeile eingefügt werden:

\begin{verbatim}
    check_rights "misc" "test"
\end{verbatim}

Dieses Script wird dann nur ausgeführt wenn der aktuelle Benutzer alle Rechte
({\tt HTTPD\_RIGHTS\_x='all'}), alle Rechte in diesem Bereich ({\tt
misc}) ({\tt HTTPD\_RIGHTS\_x='misc:all'}) oder das Recht hat, diese
spezielle Aktion ({\tt SEC\_ACTION}) ({\tt HTTPD\_RIGHTS\_x='misc:test'})
auszuführen.

% More examples?

\subsection{Sonstiges}

Dies und das (Ja, das ist auch noch wichtig!):

\begin{itemize}
 \item Der {\tt mini\_httpd} schützt Unterverzeichnisse nicht mit Passwort. Es muß
      für jedes Verzeichniss eine eigene {\tt .htaccess} angelegt werden oder ein
      Link auf eine andere htaccess-Datei angelegt werden.
 \item KISS - Keep it simple, stupid!
 \item Diese Angaben können sich jederzeit ohne Vorankündigung ändern!
\end{itemize}

\subsection{Fehlersuche}
Debug innerhalb des CGIs aktivieren, erstellt die Datei debug.log
unter http://fli4l/admin/debug.log. Diese enthält alle Aufrufe des CGIs.
Die Variabele ist nicht global, muss also in jeden Problem-CGI erneut gesetzt werden.

\begin{verbatim}
   set_debug="yes"
   . /srv/www/include/cgi-helper
\end{verbatim}

Curl eignet sich hervoragend zur Fehlersuche, insbesondere wenn die HTTP Kopfzeilen
nicht korrekt zusammengesetzt werden oder der Browser nur weiße Seiten anzeigt.
Auch ist das Cachingverhalten moderner Webbrowser bei der Fehlersuche hinderlich.

Bespiel: mit curl den HTTP-Header dumpen (-D) und das CGI Output admin/mein.cgi betrachten. 
Dazu den Usernamen (-u) admin verwenden.

\begin{verbatim}
   curl -D - http://fli4l/admin/mein.cgi -u admin
\end{verbatim}
Mehr zu curl (Bezugsquellen und Informationen):
\altlink{http://de.wikipedia.org/wiki/CURL}

\section{Booten, Rebooten, Einwählen und Auflegen unter fli4l}

\subsection{Bootkonzept}

fli4l 2.0 sollte eine saubere Installation auf eine Festplatte oder ein
CompactFlash(TM)-Medium bieten, aber auch eine Installation auf ein
Zip-Medium oder die Erstellung einer bootfähigen CD-ROM sollte möglich
sein. Zusätzlich sollte die Festplattenversion sich nicht grundlegend von
einer Installation von Diskette unterscheiden.

Diese Anforderungen wurden realisiert, indem die Dateien des opt.img aus der
bisherigen RAM-Disk auf eine anderes Medium verlagert werden können. Dies
kann eine
Partition auf einer Festplatte bzw. einem CF-Medium sein. Dieses zweite
Volume wird nach /opt gemountet und von dort werden nur noch symbolische
Links in das rootfs angelegt. Das entstehende Layout im
Root-Filesystem entspricht dann dem im opt-Verzeichnis der
ausgepackten Fli4l-Distribution mit einer Ausnahme -- das files-Prefix
entfällt. Die Datei opt/etc/rc ist dann also direkt unter /etc/rc zu
finden, opt/files/usr/local/bin/busybox unter
/usr/local/bin/busybox. Daß diese Dateien unter Umständen nur Links
auf ein read only gemountetes Medium sind, kann man ignorieren,
solange man die Dateien nicht modifizieren möchte. Will man dies tun,
muß man die Dateien vorher mit mk\_writable schreibbar machen.

\subsection{Start-Scripts}

Scripts, die beim booten des Systems ausgeführt werden sollen,
befinden sich unter opt/etc/\{boot.d,rc.d\} und werden auch in der
Reihenfolge ausgeführt, also erst Dateien unter boot.d und danach
unter rc.d. Da zum Ausführen dieser Scripte kein separater Prozess
erzeugt wird, dürfen sie nicht mit ``exit 0'' abgeschlossen
werden. Ein exit würde zum Abbruch des Bootvorganges führen.

\subsubsection{Hilfsfunktionen für Startupscripte}

In /etc/boot.d/base-helper werden verschiedene Funktionen
bereitgestellt, die von den Startup-Scripten verwendet werden
können. Das betrifft Dinge wie Debug-Support, Module-Behandlung,
Fehler-Meldungen oder Ersetzung symbolischer Referenzen. Zum Laden der
Module sollte aufgrund der automatischen Abhängikeitsauflösung
do\_modprobe verwendet werden. Sowohl mkfli4l als auch modprobe werten
Modulabhängigkeiten aus und fügen benötgte Module zum Archiv hinzu
bzw. laden sie beim Aufruf automatisch mit. Die do\_insmod-Funktionen
sind aus Kompatibilitätsgründen und für Spezialfälle weiterhin
vorhanden.

Die einzelnen Funktionen werden im folgenden aufgelistet und kurz
beschrieben.

\begin{description}
\item[begin\_script $<$symbol$>$ $<$message$>$] Gibt die Message aus
und aktiviert das Script debugging mittels {\em set -x}, falls
$<$symbol$>$\_DO\_DEBUG auf yes steht.
\item[end\_script] Gibt eine Abschluss-Message aus und stellt den
ursprünglichen Debug-Zustand wieder her.

\item[do\_modrobe $<$module$>$ $<$params$>$] Laden das Moduls {\em module} mit den
Parametern {\em params} bei gleichzeitiger Auflösung der Modulabhängigkeiten
\item[do\_modrobe\_if\_exists $<$module\_path$>$ $<$module$>$ $<$params$>$] Prüft, ob das
Modul (/usr/lib/$<$kernel version$>$/$<$module path$>$/module) existiert und führt bei 
vorhandensein do\_modprobe aus.
\item[do\_insmod $<$module$>$ $<$params$>$] Laden das Moduls {\em module} mit den
Parametern {\em params}
\item[do\_insmod\_once $<$module$>$ $<$params$>$] Wie do\_insmod, nur das
sichergestellt wird, daß das Module nur einmal geladen wird.
\item[do\_insmod\_if\_exists module\_path $<$module$>$ $<$params$>$] Prüft, ob das
Module existiert (/usr/lib/$<$kernel version$>$/$<$module
path$>$/module) existiert und führt wenn es da ist, do\_insmod aus.
\item[do\_insmod\_if\_exists\_once $<$module\_path$>$ $<$module$>$ $<$params$>$] Prüft, ob das
Module (/usr/lib/$<$kernel version$>$/$<$module path$>$/module) 
existiert und führt wenn es da ist und noch nicht
geladen wurde, do\_insmod aus.
\item[mk\_writable $<$file$>$] Stellt sicher, daß die Datei {\em file}
schreibbar ist. Befindet sich die Datei auf einem Read-Only
Dateisystem und ist lediglich über einen Softlink ins Dateisystem eingebunden, wird eine lokale Kopie angelegt, die dann schreibbar ist.
\item[unique $<$elements$>$] Erzeugt aus den Parametern eine Liste, in der
Elemente nur einmal auftauchen. Die Liste wird in der Variable {\em
list} zurückgegeben.
\item[log\_error $<$error message$>$] Schreibt die Fehlermeldung auf
die Konsole und nach /boot\_messages.txt. Wird keine Fehlermeldung als
Parameter übergeben, liest log\_error von stdin.
\item[set\_error $<$error\_message$>$] Gibt die Fehlermeldung aus und setzt
ein Fehlerflag, das später via is\_error geprüft werden kann.
\item[is\_error] Setzt das Error-Flag zurück und liefert wahr zurück,
falls es gesetzt war.
\item[translate\_ip\_net $<$value$>$] Ersetzt symbolische Referenzen in
Parametern. Momentan finden folgende Übersetzungen statt:
\begin{description}
\item[ip adresse, netzwerk, none, default, pppoe, dynamic, Zahl] Keine
Übersetzung
\item[any] Wird nach 0.0.0.0/0 übersetzt
\item[IP\_NET\_*] wird durch das in der Konfiguration stehende
Netzwerk ersetzt
\item[IP\_NET\_*\_IPADDR] wird durch die in der Konfiguration stehende
IP-Adresse ersetzt
\item[IP\_ROUTE\_*] wird durch die in der Konfiguration stehende
Route ersetzt
\item[@$<$host name$>$] wird durch die in der Konfiguration für den
angegeben Host spezifizierte IP-Adresse ersetzt
\end{description}

Nach dem Aufruf von translate\_ip\_net sollte man mit is\_error prüfen,
ob die Übersetzung korrekt erfolgt ist.

\end{description}

\subsubsection{opt/etc/boot.d/rc*.*}

    Dateien, die in diesem Verzeichnis liegen, werden als erstes
ausgeführt. Ihre Aufgabe ist es, das Boot-Device zu mounten, die
rc.cfg einzulesen und das opt-Archive zu entpacken. Je nach Bootyp
sind diese Scripte mehr oder weniger kompliziert und tun die folgenden
Dinge:

\begin{itemize}
\item load drivers (optional)
\item mount boot medium (optional)
\item read rc.cfg
\item extract/mount opt files (optional)
\end{itemize}

\subsubsection{opt/etc/rc.d/rc*.*}

    Befehle, die immer beim Starten des Routers ausgeführt werden müssen,
    können in Scripts im Verzeichnis opt/etc/rc.d abgelegt werden. Hierbei
    gelten folgende Konventionen:

    \begin{enumerate}
    \item Es gilt folgende Namenskonvention: rc$<$dreistellige
      Zahl$>$.$<$Name des Opt's$>$ Die Scripts werden in der Reihenfolge der
      Zahlen gestartet. Haben mehrere Scripts die gleiche Zahl,
      entscheidet die alphabetische Sortierung nach dem Punkt. Falls
      der Start eines Paketes erst nach einem anderen erfolgen darf,
      wird das durch die Zahl festgelegt.

          Hier eine ungefähre Richtlinie, welche Nummern für welche Aufgaben
          verwendet werden sollten:

          \begin{tabular}{ll}
                  \hline
                  Nummer        &       Aufgabe         \\
                  \hline
                  \hline
                  000-099       &       Grundsystem (mounten, Zeitzone) \\
                  100-199       &       Kernel-Module (Treiber) \\
                  200-299       &       externe Verbindungen (PPPoE, I4L, PPtP) \\
                  300-399       &       Netzwerk (Routing, Interfaces, Paketfilter) \\
                  400-499       &       Server (DHCP, HTTPD, Proxy, etc.) \\
                  500-900       &       Freeform, use as you wish \\
                  900-997       &       Alles, was eine Einwahl hervorrufen könnte \\
                  998-999       &       reserved (don't use) \\
                  \hline
          \end{tabular}

    \item In diesen Scripts {\em müssen} alle Funktionen, die das rootfs
      verändern hinterlegt werden. (z.B. das Anlegen eines
      Verzeichnisses /var/log/lpd)


    \item  In diesen Scripts {\em dürfen} keine Schreibzugriffe auf den
      Dateien erfolgen, die Teil des
opt-Archives sein können, da diese Dateien auf einem Read-Only-Medium
liegen können. Muß man eine solche Datei modifizieren, muß man sie
vorher via {\em mk\_writable file} schreibbar machen. Durch den Aufruf
der Funktion wird die Datei wenn nötig als schreibbare Kopie im rootfs
abgelegt. Ist die Datei bereits schreibbar, bewirkt der {\em
mk\_writable}-Aufruf nichts. \wichtig{mk\_writable muß direkt auf den
Files im rootfs angewendet werden, nicht über den Umweg /opt. Will man
also /usr/local/bin/foo modifizieren, ruft man {\em mk\_writable
/usr/local/bin/foo} auf.}

    \item Diese Scripte müssen vor der Ausführung der eigentlichen
      Befehle prüfen, ob das dazugehörige OPT auch aktiv ist. Das ist
          normalerweise durch eine einfache if-Abfrage erledigt:

\begin{example}
\begin{verbatim}
    if [ "$OPT_<OPT-Name>" = "yes" ]
    then
        ...
        # Hier OPT starten!
        ...
    fi
\end{verbatim}
\end{example}

    \item Um das debuggen zu erleichtern, sollten die Scripte mit {\em
begin\_script name message} und {\em end\_script} geklammert werden:

\begin{example}
\begin{verbatim}
    if [ "$OPT_<OPT-Name>" = "yes" ]
    then
        begin_script FOO ``configuring foo ...''
        ...
        end_script
    fi
\end{verbatim}
\end{example}

    Das debuggen einzelner Startupscripte kann man dann einfach via
FOO\_DO\_DEBUG='yes' aktivieren.

    \item Diese Scripte dürfen nicht direkt auf die Datei rc.cfg
      zugreifen. Dies ist auch nicht notwendig, weil alle Variablen
      aus dieser Datei direkt verfügbar sind bis der Startvorgang
      beendet wurde.  Im Abschnitt ``Umgang mit
      Konfigurationsvariablen'' wurde erklärt, wie man Variablen für
      eine spätere Nutzung abspeichern kann.


    \item Der Pfad /opt darf auch nicht als Speicherplatz für
      Datenbestände des Opts benutzt werden. Falls zusätzlicher
      Speicherplatz benötigt wird ist eine eigene Ramdisk zu erstellen
      oder die Angabe eines Pfades über eine Variable zu ermöglichen.

    \end{enumerate}


\subsubsection{Stop-Scripts}

Jeder Rechner muss mal heruntergefahren oder neu gestartet werden. Nun kann
es vorkommen, dass man noch etwas durchführen muss, bevor der Rechner
heruntergefahren oder neu gestartet wird. Zum herunterfahren und neu starten
sind die Befehle halt und reboot zuständig. Diese Befehle werden auch
aufgerufen, wenn man im IMONC auf die entsprechenden Buttons klickt.

Alle Stop-Scripts liegen im Verzeichnis opt/etc/rc0.d/. Ihre Dateinamen
werden analog zu den Startscripten gebildet.



\subsection{ttyI devices}

Für die ttyI devices (/dev/ttyI0 ... /dev/ttyI15), über die die ``Modem-
Emulation'' der ISDN-Karte genutzt werden kann, existiert ein Zähler, um
Konflikte zwischen verschiedenen Paketen, die diese Devices nutzen, zu
vermeiden.
Hierzu wird beim Start des Routers die Datei /var/run/next\_ttyI angelegt, die
von den verschiedenen Addons abgefragt und fortgezählt werden muß.
Mit dem folgenden Beispielscript kann dieser Wert abgefragt, um eins
erhöht und wieder für das als nächstes startende Addon exportiert werden.

\begin{example}
\begin{verbatim}
ttydev=`cat /var/run/next_ttyI`
if [ "$ttydev" -le 16 ]
then                                    # ttyI device available? yes
    ttydev=`/usr/bin/expr $ttydev + 1`  # ttyI device + 1
    echo $ttydev >/var/run/next_ttyI    # und wieder abspeichern
else                                    # ttyI device available? no
    /usr/local/bin/colecho "*** ERROR: no ttyI device for \
    <Name deines Addons> available ! ***" br x br
    sleep 10                            # wait, so user can read message
    ttydev_error=true                   # set error for later use
fi

if [ "$ttydev_error" != true ]          # start addon only if next tty device
                                        # was available to minimize error
then                                    # messages and minimize the
    ....                                # risk of uncomplete boot
fi
\end{verbatim}
\end{example}


\subsection{Scripte beim Einwählen und Auflegen}

Nach dem Herstellen einer Dial-Up-Verbindung werden die Scripte unter
/etc/ppp abgearbeitet. Hier können Opt-Pakete als Aktionen ausführen,
die nach dem Herstellen der Verbindung bzw. nach dem Auflegen nötig
sind. Benannt werden die Dateien wie folgt:

\begin{tabular}[h!]{ll}
    ip-up$<$dreistelligenummer$>$.$<$OPT-Name$>$    & Wird beim Verbinden ausgeführt \\
    ip-down$<$dreistelligenummer$>$.$<$OPT-Name$>$  & Wird {\em nach} dem Trennen der Verbindung ausgeführt\\

\end{tabular}

In den ip-down-Scripts dürfen keine Aktionen ausgeführt werden, die zu
einer erneuten Internet-Einwahl führen würden, da dadurch nur ein
Dauer-Online-Zustand erreicht wird, was für Nicht-Flatrate-Benutzer
ein teures Unterfangen ist.

Da für die einzelnen Scripte kein eigener Prozeß erzeugt wird, dürfen
auch diese Scripts {\em nicht} mit ``exit 0'' abgeschlossen werden.

Hinweis: Wenn ein Script prüfen will ob überhaupt die ip-up Skripte
ausgeführt werden kann es ab rc321 die Variable
\emph{''ip\_up\_events''} prüfen. Steht diese auf \var{''yes''} laufen die
ip-up Skripte. Steht dort ein \var{''no''} laufen keine ip-up Skripte.

\subsubsection{Variablen}

Durch das spezielle Aufrufkonzept stehen alle Variablen aus der
ip-up/ip-down auch in den einzelnen Scripts zur Verfügung. Folgende
Variablen sind verfügbar:

\begin{center}
\begin{tabular}[h!]{lp{10cm}}

    \$real\_interface   & Das aktuelle Interface, also z.B. ppp0, ippp0, ...\\
    \$interface        & Das IMOND-Interface, also pppoe, ippp0, ...\\
    \$tty              & Verbundenes Terminal... Möglicherweise leer!\\
    \$speed            & Die Verbindunggeschwindigkeit, bei ISDN Z.B. 64000\\
    \$local            & Die eigene IP-Adresse\\
    \$remote           & Die IP-Adresse des Point-To-Point-Partners\\
    \$is\_default\_route & Gibt an, ob das aktuelle ip-up/down für das Interface
                        durchgeführt wird, welches gerade die Default-Route
                        hat (kann ``yes'' oder ``no'' sein)\\
\end{tabular}
\end{center}


\subsubsection{Default-Route}

Seit Version 2.1.0 werden die ip-up/down-Scripts nicht nur für das
Default- Route-Interface gestartet, sondern für alle Verbindungen, die
die ip-up/down- Scripts aufrufen. Um das alte Verhalten zu simulieren,
muß in ip-up/down Scripts eine solche Abfrage eingefügt werden:

\begin{example}
\begin{verbatim}
        # is a default-route-interface going up?
        if [ "$is_default_route" = "yes" ]
        then
                # die eigentlichen Aktionen
        fi
\end{verbatim}
\end{example}

Natürlich darf das neue Verhalten auch für spezielle Aktionen
ausgenutzt werden.


\section{Paket TEMPLATE}

Um einiges von dem hier beschriebene  etwas zu veranschaulichen, liegt der
fli4l-Distribution das Paket TEMPLATE bei. Dieses erklärt an kleinen
Beispielen, wie:
\begin{itemize}
\item eine Konfigurationsdatei auszusehen hat (config/template.txt)
\item eine check-Datei geschrieben wird (check/template.txt)
\item die erweiterten Prüfmöglichekiten verwendet werden (check/template.ext)
\item Konfigurationsvariablen für spätere Verwendung abgelegt werden
  können \\(opt/etc/rc.d/rc999.template)
\item abgelegte Konfigurationsvariablen wieder ausgelesen werden \\(opt/files/usr/bin/template\_show\_config)
\end{itemize}



\section{Aufbau der Boot-Diskette}

Ab fli4l-Version 1.5 wird das Programm syslinux zum Booten von Diskette verwendet.
Dieses hat den Vorteil, daß ein DOS-Filesystem auf der Diskette zur Verfügung steht.

Die Boot-Diskette enthält folgende Dateien:
\begin{center}
\begin{tabular}[h!]{lp{10cm}}
ldlinux.sys             & Bootloader syslinux \\
syslinux.cfg            & Konfigurationsdatei für syslinux \\
kernel                  & Linux-Kernel\\
rootfs.img               & Root-Filesystem\\
opt.img             & Optionale Dateien: Treiber und Opt-Pakete\\
rc.cfg                  & Konfigurationsdatei mit den benutzten Variablen aus
den Dateien des config-Verzeichnisses\\
\end{tabular}
\end{center}

Durch das Script mkfli4l.sh (bzw. mkfli4l.bat) werden zunächst
die Dateien opt.img, syslinux.cfg und rc.cfg sowie das rootfs.img erzeugt.
Die dafür nötige Dateiliste erstellt das Programm mkfli4l (im unix- bzw.
windows-Unterverzeichnis). In dem bz2-Archiv (Mit Bzip2 komprimiertes Tar-Archiv)
sind die benötigten Kernel- und Opt-Module enthalten. Früher war auch die Datei
rc.cfg im opt-Archiv, diese liegt seit der fli4l-Version 2.0 direkt auf der Diskette.

Anschließend werden die Dateien kernel, rootfs.img, opt.tar.bz2 (opt.img)
und rc.cfg zusammen mit den syslinux-Dateien auf die Diskette kopiert.

Beim Booten von fli4l wird über das Script /etc/rc die rc.cfg ausgewertet und das
komprimierte Tar-Image in eine eigene Ramdisk oder auf eine schon existierende
Partition extrahiert und diese nach /opt gemountet.
Die Dateien opt/etc/* werden nun in das rootfs hineinkopiert und
für die Dateien und Verzeichnisse in opt/* werden symbolische Links im rootfs erstellt.
Danach werden die Scripte in /etc/rc.d in alphanumerischer Reihenfolge ausgeführt
und somit die Treiber geladen und die Dienste gestartet.



% \section{Dateien im root-Filesystem}
%
% Das Shell-Script "mount-rootfs.sh" im src-Verzeichnis erstellt zunächst
% eine root-Filesystem-Image-Datei namens rootfs.img, falls diese noch
% nicht existiert. Anschließend wird diese Image-Datei über das loop-Device
% (nicht zu verwechseln mit dem loopback-Device) gemounted.
% Aufruf mit ``sh mount-rootfs.sh''
% Das Script legt im aktuellen Verzeichnis ein Directory "rootfs/" an
% und mountet darin das minix-Filesystem.
%
% Wichtig: Der Kernel muß das Loop-Device und das minix-Filesystem unterstützen,
% damit der Mount funktioniert!
%
% Die im rootfs sichtbaren Dateien und Verzeichnisse sind in Tabelle
% \ref{tab:rootfs} dargestellt. Die mit (BB) gekennzeichneten Dateien
% sind Hard-Links auf das Programm usr/local/bin/busybox.
%
% Das root-Filesystem kann mit dem Befehl ``sh umount-rootfs''
% wieder abgemeldet werden.
%
% Möchte man jedoch ein neues komprimiertes root-Filesystem-Image
% ../img/rootfs.bz2 erzeugen, sollte man stattdessen das Kommando
% ``sh compact-rootfs.sh'' verwenden. Dann werden die vorhandenen Dateien
% im minix-Image in ein neues Image umkopiert. Anschließend wird das neue
% Image wieder in "rootfs.img" umbenannt.
%
% Das hat folgenden Grund:
% Durch Änderungen in den Dateien bleiben Rest-Daten im Filesystem-Image
% liegen, die nicht gelöscht, aber nicht mehr verwendet werden. Diese
% erschweren die Komprimierung beim Erstellen der Boot-Diskette. Dabei
% kann es dann passieren, dass das komprimierte Filesystem-Image dann
% nicht mehr auf die Floppy passt!
%
% Nach dem Umkopieren wird das root-Filesystem-Image komprimiert als
% ../img/rootfs.bz2 abgespeichert.
%
%
% \begin{small}
%   \begin{center}
%
%     \begin{longtable}{||l|p{10cm}||}
%       \hline \hline
%       Datei/Verzeichnis & Verwendung \\
%       \hline .profile                    &profile-Datei für ash \\
%       bin/cat                     &Anzeigen von Dateien (BB) \\
%       bin/chgrp                   &Setzen von Gruppenzugehörigkeit von
%       Dateien (BB) \\
%       bin/chmod                   &Setzen von Berechtigungen von Dateien
%       (BB) \\
%       bin/chown                   &Setzen des Eigentümers von Dateien (BB) \\
%       bin/cp                      &Kopieren von Dateien (BB) \\
%       bin/date                    &Datum ausgeben/setzen (BB) \\
%       bin/dd                      &Dateien kopieren/konvertieren (BB) \\
%       bin/df                      &Freien Plattenplatz ausgeben (BB) \\
%       bin/dir                     &Verzeichnis in Kurzform ausgeben \\
%       bin/false                   &Liefert exit-Wert 1 \\
%       bin/hostname                &Setzen/Abfragen von hostname (BB) \\
%       bin/kill                    &Prozess beenden / Signal schicken (BB) \\
%       bin/ln                      &Datei linken (BB) \\
%       bin/ls                      &Dateiverzeichnis ausgeben (BB) \\
%       bin/mkdir                   &Dateiverzeichnis anlegen (BB) \\
%       bin/mount                   &Mount-Programm (BB) \\
%       bin/mv                      &Dateien umbenennen/verschieben (BB) \\
%       bin/ping                    &Ping pong (BB) \\
%       bin/ps                      &Prozesstabelle anzeigen (BB) \\
%       bin/rm                      &Dateien löschen (BB) \\
%       bin/sh                      &Shell, gelinkt auf /usr/bin/ash \\
%       bin/sleep                   &x Sekunden warten (BB) \\
%       bin/sync                    &Synchronisieren des
%       Linux-Filesystem-Caches (BB) \\
%       bin/tar                     &tar (BB) \\
%       bin/true                    &Liefert exit-Wert 0 \\
%       bin/umount                  &Umount-Programm (BB) \\
%       dev/...                     &Device-Nodes \\
%       etc/fstab                   &Filesysteme zum Mounten \\
%       etc/group                   &Group-Datei \\
%       etc/host.conf               &Bestimmt Verfahren für
%       Hostnamen-Auflösung \\
%       etc/inittab                 &für den init-Prozess \\
%       etc/mklinks.sh              &erstellt die Softlinks nach /opt \\
%       etc/mtab                    &Leere Datei für Mount-Tabelle \\
%       etc/passwd                  &Password-Datei \\
%       etc/protocols               &protocols-Datei \\
%       etc/rc                      &Boot-Script \\
%       etc/rc0                     &Shutdown-Script \\
%       etc/services                &Services-Tabelle \\
%       etc/shadow                  &Shadow-Datei \\
%       etc/shells                  &Mögliche Shells \\
%       sbin/halt                   &Halt Linux :-) \\
%       sbin/modprobe               &Programm zum Laden von Kernel-Modulen \\
%       sbin/insmod                 &Programm zum Laden von Kernel-Modulen \\
%       sbin/lsmod                  &listet die geladenen Module auf \\
%       sbin/reboot                 &Reboot Linux (BB)  \\
%       sbin/rmmod                  &Löschen von Kernelmodulen (BB) \\
%       sbin/init                   &Busybox-init (BB) \\
%       sbin/poweroff               &??? (BB) \\
%       sbin/mkfs.minix             &zum formatieren der opt-Ramdisk (BB) \\
%       usr/bin/ash                 &Shell, bash ist viel zu groß für Floppy! \\
%       usr/bin/basename            &Basisnamen eines Dateinamens anzeigen
%       (BB) \\
%       usr/bin/cut                 &Link auf Busysbox-cut (BB) \\
%       usr/bin/dirname             &Verzeichnis eines Dateinamens anzeigen
%       (BB) \\
%       usr/bin/expr                &Mini-Rechenprogramm, zum Inkrementieren
%       (BB) \\
%       usr/bin/grep                &Link auf Busybox-grep (BB) \\
%       usr/bin/gunzip              &GNU unzip-Programm (BB) \\
%       usr/bin/killall             &Link auf Busybox-killall (BB) \\
%       usr/bin/nslookup            &Abfragen von DNS-Servern (BB) \\
%       usr/bin/sed                 &stream editor sed  \\
%       usr/bin/tr                  &Link auf Busybox-tr (BB) \\
%       usr/lib/terminfo/l/linux    &Linux-Konsole: terminfo-Datei \\
%       usr/i386-linux-uclibc/*     &Shared Libs, gegen die die Binaries
%       gelinkt wurden \\
%       usr/local/bin/busybox       &Busybox, s.a. http://busybox.lineo.org \\
%       usr/local/bin/clrhome       &Script: löscht Bildschirm, setzt Cursor
%       oben links \\
%       usr/local/bin/colecho       &echo mit Farbe :-) \\
%       usr/local/bin/dtou          &DOS to Unix: CRs aus DOS Dateien
%       eliminieren \\
%       usr/bin/zcat                &zum Entpacken gzip-komrimierter Dateien
%       (BB) \\
%       boot/               &Mountpoint für die Bootdiskette \\
%       etc/ppp/ &   \\
%       etc/rc.d/  &   \\
%       etc/rc0.d/  &   \\
%       lib/modules/        &für die Kernel-Module \\
%       mnt/                &Verzeichnis für temporären Mount \\
%       proc/               &Verzeichnis für Prozesstabellen \\
%       opt/                &Mountpoint für das entpackte opt.img \\
%       tmp/                &Verzeichnis für temporäre Dateien \\
%       usr/lib/isdn/       &Verzeichnis für ISDN-Dateien, z.B. für AVM B1 \\
%       usr/local/ens/  &   \\
%       usr/sbin/ &   \\
%       var/adm/            &Verzeichnis für tmp-Dateien (Administration) \\
%       var/lock/           &Verzeichnis für Lockfiles zur Prozessverriegelung
%       \\
%       var/log/ &   \\
%       var/run/ &   \\
%       var/spool/          &Drucker-Spool-Verz. und Mount-Point f. Ramdisk \\
%       var/tmp/            &Verzeichnis für tmp-Dateien (Administration) \\
%       dev/inet &   \\
%       \hline \hline
%   \caption{Inhalt des rootfs}
%   \label{tab:rootfs}
% \end{longtable}
%   \end{center}
% \end{small}
%
%
%


\section{Konfigurationsdateien}

Hier werden die Dateien kurz aufgeführt, die vom fli4l-Router
on-the-fly beim Booten erzeugt werden.

\begin{enumerate}
\item Konfiguration Provider
  \begin{itemize}
  \item         etc/ppp/pap-secrets

  \item         etc/ppp/chap-secrets

  \end{itemize}
\item Konfiguration DNS
  \begin{itemize}
  \item         etc/resolv.conf

  \item         etc/dnsmasq.conf

  \item         etc/dnsmasq\_dhcp.conf

  \item         etc/resolv.dnsmasq

  \end{itemize}


\item Hosts-Datei
  \begin{itemize}
  \item         etc/hosts
  \end{itemize}



\item imond-Konfiguration
  \begin{itemize}
  \item etc/imond.conf
  \end{itemize}

\end{enumerate}


\subsection{Konfiguration Provider}

Für den ausgesuchten Provider wird in etc/ppp/pap-secrets
die User-ID und das Password angepasst.

Beispiel für Provider Planet-Interkom:

\begin{verbatim}
# Secrets for authentication using PAP
# client        server  secret                  IP addresses
"anonymer"      *       "surfer"                *
\end{verbatim}

Dabei ist im Beispiel ``anonymer'' die USER-ID. Als Remote-Server wird
prinzipiell jeder erlaubt (deshalb ``*''). ``surfer'' ist das Password für
den Provider Planet-Interkom.


\subsection{Konfiguration DNS}


Man kann den fli4l-Router als DNS-Server einsetzen. Warum dies
sinnvoll - ja sogar bei Windows-Rechnern im LAN zwingend notwendig
ist, wurde bereits in ../doc erläutert.

Die Resolver-Datei etc/resolv.conf enthält den Domainnamen und den zu
verwendenten Nameserver. Sie hat folgenden Inhalt (wobei domain.de nur
ein Platzhalter für den Wert der Konfigurationsvariable
\var{DOMAIN\_\-NAME} ist):

\begin{verbatim}
        search domain.de
        nameserver 127.0.0.1
\end{verbatim}

Der Nameserver dnsmasq wird über die Datein etc/dnsmasq.conf
konfiguriert. Sie wird beim Booten vom Script rc001.base sowie
rc370.dnsmasq automatisch erzeugt und könnte wie folgt aussehen:

\begin{example}
\begin{verbatim}
user=dns
group=dns
resolv-file=/etc/resolv.dnsmasq
no-poll
no-negcache
bogus-priv
log-queries
domain-suffix=lan.fli4l
local=/lan.fli4l/
domain-needed
expand-hosts
filterwin2k
conf-file=/etc/dnsmasq_dhcp.conf
\end{verbatim}
\end{example}





\subsection{Hosts-Datei}

    Sollte jedem bekannt sein :-)
    Ist eigentlich überflüssig, wenn zusätzlich ein lokaler DNS-Server
    gestartet wird.



\subsection{imond-Konfiguration}

Die Datei etc/imond.conf wird u.a. aus den Konfigurationsvariablen
\var{CIRC\_x\_NAME}, \var{CIRC\_x\_ROUTE}, \var{CIRC\_x\_CHARGEINT}
und \var{CIRC\_x\_TIMES} konstruiert. Sie kann aus bis zu 32 Zeilen
(ohne die Kommentarzeilen) bestehen. Jede Zeile besteht aus 8 Spalten:

\begin{enumerate}
\item  Bereich Wochentag bis Wochentag
\item  Bereich Stunde bis Stunde
\item  Device (ipppX oder isdnX)
\item   Circuit mit Default-Route: yes/no
\item   Telefonnummer
\item  Name des Circuits
\item  Telefonkosten pro Minute in DM
\item  Zeittakt (charge interval) in Sekunden
\end{enumerate}

    Hier ein Beispiel:

\begin{verbatim}
#day  hour  device  defroute  phone        name        charge  ch-int
Mo-Fr 18-09 ippp0   yes       010280192306 Addcom      0.0248  60
Sa-Su 00-24 ippp0   yes       010280192306 Addcom      0.0248  60
Mo-Fr 09-18 ippp1   yes       019160       Compuserve  0.019   180
Mo-Fr 09-18 isdn2   no        0221xxxxxxx  Firma       0.08    90
Mo-Fr 18-09 isdn2   no        0221xxxxxxx  Firma       0.03    90
Sa-Su 00-24 isdn2   no        0221xxxxxxx  Firma       0.03    90
\end{verbatim}

    Weitere Erklärungen zum Least-Cost-Routing findet man in ../doc.




%
