Subsections

8.3 Module Concept

As of version 2.0 fli4l is split into modules (packages), i.e.

With the base package fli4l acts as a pure Ethernet router. For ISDN and/or DSL the packages isdn and/or dsl have to be unpacked to the fli4l directory. The same applies for the other packages.


8.3.1 mkfli4l

Depending on the current configuration a file called rc.cfg and two archives rootfs.img and opt.img will be generated which contain all required configuration informations and files. These files are generated using mkfli4l which reads the individual package files and checks for configuration errors.

mkfli4l will accept the parameters listed in table 8.1. If omitted the default values noted in brackets are used. A complete list of all options (Table 8.1) is displayed when executing

    mkfli4l -h
.


Table: Parameters for mkfli4l
Option Meaning
-c, - -config Declaration of the directory mkfli4l will scan for package config files (default: config)
-x, - -check Declaration of the directory mkfli4l will scan for files needed for package error checking (<package>.txt, <package>.exp and <package>.ext; default: check)
-l, - -log Declaration of the log file to which mkfli4l will log error messages and warnings (default: img/mkfli4l.log)
-p, - -package Declaration of the packages to be checked, this option may be used more than once in case of a desired check for several packages in conjunction. If using -p, however, the file <check_dir>/base.exp will always be read first to provide the common regular expressions provided by the base package. Hence, this file must exist.
-i, - -info Provides information on the check (which files are read, which tests are run, which uncommon things happened during the review process)
-v, - -verbose More verbose variant of option -i
-h, - -help Displays the help text
-d, - -debug Allows for debugging the review process. This is meant to be a help for package developers wishing to know in detail how the process of package checking is working.
Debug Option Meaning
check show check process
zip-list show generation of zip list
zip-list-skipped show skipped files
zip-list-regexp show regular expressions for ziplist
opt-files check all files in opt/<package>.txt
ext-trace show trace of extended checks

8.3.2 Structure

A package can contain multiple OPTs, if it contains only one, however, it is appropriate to name the package like the OPT. Below <PACKAGE> is to be replaced by the respective package name. A package consists of the following parts:

The individual parts are described in more detail below.

8.3.3 Configuration of Packages

The user's changes to the package's configuration are made in the file config/<PACKAGE>.txt. All the OPT's variables should begin with the name of the OPT, for example:

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

An OPT should be prefixed by a header in the configuration file (see above). This increases readability, especially as a package indeed can contain multiple OPTs. Variables associated to the OPT should -- again in the interest of readability -- not be indented further. Comments and blank lines are allowed, with comments always starting in column 33. If a variable including its content has more than 32 characters, the comment should be inserted with a row offset, starting in column 33. Longer comments are spread over multiple lines, each starting at column 33. All this increases easy review of the configuration file.

All values following the equal sign must be enclosed in quotes8.1not doing so can lead to problems during system boot.

Activated variables (see below), will be transferred to rc.cfg, everything else will be ignored. The only exceptions are variables by the name of <PACKAGE>_DO_DEBUG. These are used for debugging and are transferred as is.


8.3.4 List of Files to Copy

The file opt/<PACKAGE>.txt contains instructions that describe

Based on this information mkfli4l will generate the archives needed.

Blank lines and lines beginning with ``#'' are ignored. In one of the first lines the version of the package file format should be noted as follows:

    <first column>      <second column> <third column>
    opt_format_version  1                    -

The remaining lines have the following syntax:

    <first column>  <second column> <third column> <columns following>
    Variable        Value            File           Options

  1. The first column contains the name of a variable which triggers inclusion of the file referenced in the third column depending on its value in the package's config file. The name of a variable may appear in the first column as often as needed if multiple files depend on it. Any variable that appears in the file opt/<PACKAGE>.txt is marked by mkfli4l.

    If multiple variables should be tested for the same value a list of variables (separated by commas) can be used instead. It is sufficient in this case if at least one variable contains the value required in the second column. It is important not to use spaces between the individual variables!

    In OPT variables (ie variables that begin with OPT_ and typically have the type YESNO), the prefix ``OPT_'' can be omitted. It does not matter whether variables are noted in upper- or lowercase (or mixed).

  2. The second column contains a value. If the variable in the first column is identical with this value and is activated too (see below), the file referenced in the third column will be included. If the first column contains a %-variable it will be iterated over all indices and checked whether the respective variable matches the value. If this is the case copying will be executed. In addition, the copy process based on the current value of the variable will be logged.

    It is possible to write a ``!'' in front of the value. In this case, the test is negated, meaning the file is only copied if the variable does not contain the value.

  3. In the third column a file name is referenced. The path must be given relative to the opt directory. The file must exist and be readable, otherwise an error is raised while generating the boot medium and the build process is aborted.

    If the file name is prefixed with a ``rootfs:'' the file is included in the list of files to be copied to the RootFS. The prefix will be stripped before.

    If the file is located below the current configuration directory it is added to the list of files to be copied from there, otherwise the file found below opt is taken. Those files are not allowed to have a rootfs: prefix.

    If the file to copy is a kernel module the actual kernel version may be substituted by ${KERNEL_VERSION}. mkfli4l will then pick the version from the configuration and place it there. Using this you may provide modules for several kernel versions for the package and the module matching the current kernel version will be copied to the router. For kernel modules the path may be omitted, mkfli4l will find the module using modules.dep and modules.alias, see the section ``Automatically Resolving Dependencies for Kernel Modules''.


    Table 8.2: Options for Files
    Option Meaning Default Value
    type= Type of the Entry:

    local Filesystem Object
    file File
    dir Directory
    node Device
    symlink (symbolic) Link


    This option has to be placed in front when given. The type ``local'' represents the type of an object existing in the file system and hence matches ``file'', ``dir'', ``node'' or ``symlink'' (depends). All other types except for ``file'' can create entries in the archive that do not have to exist in the local file system. This can i.e. be used to create devices files in the RootFS archive.
    local
    uid= The file owner, either numeric or as a name from passwd root
    gid= File group, either numeric or as a name from group root
    mode= Access rights Files and Devices:
    rw-r--r-- (644)
    Directories:
    rwxr-xr-x (755)
    Links:
    rwxrwxrwx (777)
    flags=
    (type=file)
    Conversions before inclusion in the archive:

    utxt Conversion to *nix format
    dtxt Conversion to DOS format
    sh Shell-Skript: Conversion to *nix format, stripping of superfluous chars
    name= Alternative name for inclusion of the entry in the archive
    devtype=
    (type=node)
    Descibes the type of the device (``c'' for character and ``b'' für block oriented devices). Has to be placed in second position.
    major=
    (type=node)
    Decribes the so-called ``Major'' number of the device file. Has to be placed in third position.
    minor=
    (type=node)
    Decribes the so-called ``Minor'' number of the device file. Has to be placed in fourth position.
    linktarget=
    (type=symlink)
    Describes the target of the symbolic link. Has to be placed in second position.

  4. the other columns may contain the options for owner, group, rights for files and conversion listed in table 8.2.

Some examples:

The files will be copied only if the above conditions are met and OPT_PACKAGE='yes' of the corresponding package is set. What OPT variable is referenced is decribed in the file check/<PACKAGE>.txt.

If a variable is referenced in a package that is not defined by the package itself, it may happen that the corresponding package is not installed. This would result in an error message from mkfli4l, as it expects that all of the variables referenced by opt/<PACKAGE>.txt are defined.

To handle this situation correctly the ``weak'' declaration has been introduced. It has the following format:

    weak        <Variable>    -

By this the variable it is defined (if not already existing) and its value is set to ``undefined''. Please note that the prefix ``OPT_" must be provided (if existing) because else a variable without this prefix will be created.

An example taken from opt/rrdtool.txt:

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

Without the weak definition mkfli4l would display an error message when using the package ``rrdtool'' while the ``openvpn'' package is not activated. By using the weak definition no error message is raised even in the case that the ``openvpn'' package does not exist.


8.3.4.1 Files adapted by Configuration

In some situations it is desired to replace original files with configuration-specific files for inclusion in the archive, i.e. host keys, own firewall scripts, ... mkfli4l supports this scenario by checking whether a file can be found in the configuration directory and, if so, including this one instead in the file list for opt.img resp. rootfs.img.

Another option to add configuration-specific files to an archive is decribed in the section Extended Checks of the Configuration.


8.3.4.2 Automatically Resolving Dependencies for Kernel Modules

Kernel modules may depend on other kernel modules. Those must be loaded before and therefore also have to be added to the archive. mkfli4l resolves this dependencies based on modules.dep and modules.alias (two files generated during the kernel build), automatically including all required modules in the archives. Thus, for example the following entry

    net_drv_%   ne2k-pci    ne2k-pci.ko

triggers that both 8390.ko and crc32.ko are included in the archive because ne2k_pci depends on both of them.

The necessary entries from modules.dep and modules.alias are included in the RootFS and can be used by modprobe for loading the drivers.


8.3.5 Checking Configuration Variables

By the help of check/<PACKAGE>.txt the content of variables can be checked for validity. In earlier version of the program mkfli4l this check was hard coded there but it was outsourced to the check files in the course of modularizing fli4l. This file contains a line for each variable in the config files. These lines consist of four to five columns which have the following functions:

  1. Variable: this column specifies the name of the configuration file variable to check. If this is an array variable, it can appear multiple times with different indices, so instead of the index number a percent sign (%) is added to the variable name. It is always used as ``_%_'' in the middle of a name resp. ``_%'' at the end of a name. The name may contain more than one percent sign allowing the use of multidimensional arrays. It is recommended (but not mandatory) to add some text between the percent signs to avoid weird names like ``FOO_%__%''.

    Often the problem occurs that certain variables describe options that are needed only in some situations. Therefore variables may be marked as optional. Optional variables are identified by the prefix ``+''. They may then exist, but do not have to. Arrays can also use a ``++'' prefix. Prefixed with a ``+'' the array can exist or be entirely absent. Prefixed with a``++'' in addition some elements of the array may be missing.

  2. OPT_VARIABLE: This column assigns the variable to a specific OPT. The variable is checked for validity only if the OPT variable is set to ``yes''. If there is no OPT variable a ``-'' indicates this. In this case, the variable must be defined in the configuration file, unless a default value is defined (see below). The name of the OPT variable may be arbitrary but should start with the prefix ``OPT_''.

    If a variable does not depend on any OPT variables, it is considered active. If it is depending on an OPT variable, it is precisely active if

    In all other cases the variable is inactive.

    Hint: Inactive OPT variables will be set back to ``no'' by mkfli4l if set to ``yes'' in the configuration file, an appropriate warning will be generated then (i.e. OPT_Y='yes' is ignored, because OPT_X='no'). For transitive dependency chains (OPT_Z depends on OPT_Y which in turn depends on OPT_X) this will only work reliable, if the names of all OPT-variables start with ``OPT_''.

  3. VARIABLE_N: If the first column contains a variable with a ``%'' in its name, it indicates the number of occurrences of the variable (the so-called N-variable). In case of a multi-dimensional variable, the occurences of the last index are specified. If the variable depends on a certain OPT, the N-variable must be dependant on the same or no OPT. If the variable does not depend on any OPT, the N-variable also shouldn't. If no N-variable exists, specify ``-'' to indicate that.

    For compatibility with future versions of fli4l the variable specified here must be identical with the variable in OPT_VARIABLE where the last ``%'' is replaced by an ``N'' and everything following is removed. An array HOST_%_IP4 must have the N-Variable HOST_N assigned and an array PF_USR_CHAIN_%_RULE_% hence the N-variable PF_USR_CHAIN_%_RULE_N, and this N-variable itself is an array variable with the corresponding N-variable PF_USR_CHAIN_N. All other namings of the N variables will be incompatible with future versions of fli4l!

  4. VALUE: This column provides the values a variable can hold. For example the following settings are possible:

    Name Meaning
    NONE No error checking will be done
    YESNO The variable must be ``yes'' or ``no''
    NOTEMPTY The variable can't be empty
    NOBLANK The variable can't contain spaces
    NUMERIC The variable must be numeric
    IPADDR The variable must be an IP address
    DIALMODE The variable must be ``on'', ``off'' or ``auto''

    I values are prefixed by ``WARN_'' an illegal content will not raise an error message and abort the build by mkfli4l, but only display a warning.

    The possible checks are defined by regular expressions in check/base.exp. This file may be extended and now contains some new checking routines, for example: HEX, NUMHEX, IP_ROUTE, DISK and PARTITION.

    The number of expressions may be extended at any time for the future needs of package developers. Provide feedback!

    In addition, regular expressions can also be directly defined in the check-files, even relations to existing expressions can be made. Instead of YESNO you could, for example also write

        RE:yes|no.
    
    This is useful if a test is performed only once and is relatively easy. For more details see the next chapter.

  5. Default Setting: In this column, an optional default value for the variables can be defined in the case that the variable is not specified in the configuration file.

    Hint: At the moment this does not work for array variables. Additionally, the variable can't be optional (no ``+'' in front of the variable name).

    Example:

        OPT_TELNETD     -      -      YESNO    "no"
    

    If OPT_TELNETD is missing in the config file, ``no'' will be assumed and written as a value to rc.cfg.

The percent sign thingie is best decribed with an example. Let's assume check/base.txt amongst others has the following content:

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

This means that depending on the value of NET_DRV_N the variables NET_DRV_N, NET_DRV_1_OPTION, NET_DRV_2_OPTION, NET_DRV_3_OPTION, a.s.o. will be checked.

8.3.6 Own Definitions for Checking the Configuration Variables

8.3.6.1 Introduction of Regular Expressions

In version 2.0 only the above mentioned value ranges for variable checks existed: NONE, NOTEMPTY, NUMERIC, IPADDR, YESNO, NOBLANK, DIALMODE. Checking was hard-coded to mkfli4l, not expandable and restricted to essential ``data types'' which could be evaluated with reasonable efforts.

As of version 2.1 this checking has been reimplemented. The aim of the new implementation is a more flexible testing of variables, that is also able to examine more complex expressions. Therefore, regular expressions are used that can be stored in one or more separate files. This on one hand makes it possible to examine variables that are not checked for the moment and on the other hand, developers of optional packages can now define own terms in order to check the configuration of their packages.

A description of regular expressions can be found via ``man 7 regex'' or i.e. here:
http://unixhelp.ed.ac.uk/CGI/man-cgi?regex+7.

8.3.6.2 Specification of Regular Expressions

Specification of regular expressions can be accomplished in two ways:

  1. Package specific exp files check/<PACKAGE>.exp

    This file can be found in the check directory and has the same name as the package containing it, i.e. check/base.exp. It contains definitions for expressions that can be referenced in the file check/<PACKAGE>.txt. check/base.exp for example at the moment contains definitions for the known tests and check/isdn.exp a definition for the variable ISDN_CIRC_x_ROUTE (the absence of this check was the trigger for the changes).

    The syntax is as follows (again, double quotes can be used if needed):

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

    The regular expressions can also include already existing definitions by a reference. These are then pasted to substitute the reference. This makes it easier to construct regular expressions. The references are inserted by '(RE: Reference)'. (See the definition of the term NETWORKS above for an appropriate example.)

    The error messages tend to be too long. Therefore, they may be displayed on multiple lines. The lines afterwards always have to start with a space or tab then. When reading the file check/<PACKAGE>.exp superfluous whitespaces are reduced to one and tabs are replaced by spaces. An entry in check/<PACKAGE>.exp could look like this:

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

  2. Regular expressions directly in the check file check/<PACKAGE>.txt

    Some expressions occur but once and are not worth defining a regular expression in a check/<PACKAGE>.exp file. You can simply write this expression to the check file for example:

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

    MOUNT_BOOT can only take the value ``ro'', ``rw'' or ``no'', everything else will be denied.

    If you want to refer to existing regular expressions, simply add a reference via `(RE:...)''. Example:

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

8.3.6.3 Expansion of Existing Regular Expressions

If an optional package adds an additional value for a variable which will be examined by a regular expression, then the regular expression has to be expanded. This is done simply by defining the new possible values by a regular expression (as described above) and complement the existing regular expression in a separate check/<PACKAGE>.exp file. That an existing expression is modified is indicated by a leading ``+''. The new expression complements the existing expression by appending the new value to the existing value(s) as an alternative. If another expression makes use of the complemented expression, the supplement is also there. The specified error message is simply appended to the end of the existing one.

Using the Ethernet driver as an example this could look like here:

Now PCMCIA drivers can be chosen in addition.

8.3.6.4 Extend Regular Expressions in Relation to YESNO Variables

If you have extended NET_DRV with the PCMCIA drivers as shown above, but the package ``pcmcia'' has been deactivated, you still could select a PCMCIA driver in config/base.txt without an error message generated when creating the archives. To prevent this, you may let the regular expression depend on a YESNO variable in the configuration. For this purpose, the name of the variable that determines whether the expression is extended is added with brackets immediately after the name of the expression. If the variable is active and has the value ``yes'', the term is extended, otherwise not.

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

If specifying OPT_PCMCIA='no' and using i.e. the PCMCIA driver xirc2ps_cs in
config/base.txt, an error message will be generated during archive build.

Hint: This does not work if the variable is not set explicitely in the configuration file but gets its value by a default setting in check/<PACKAGE>.txt. In this case the variable hence has to be set explicitely and the default setting has to be avoided if necessary.


8.3.6.5 Extending Regular Expressions Depending on other Variables

Alternatively, you may also use arbitrary values of variables as conditions, the syntax looks like this:

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

If KERNEL_VERSION matches the given regular expression (if any of the kernels of the 3.16 line is used) then the list of network driver allowed is extended with the drivers mentioned.

Hint: This does not work if the variable is not set explicitely in the configuration file but gets its value by a default setting in check/<PACKAGE>.txt. In this case the variable hence has to be set explicitely and the default setting has to be avoided if necessary.

8.3.6.6 Error Messages

If the checking process detects an error, an error message of the following kind is displayed:

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

For the first error, the term was defined in a check/<PACKAGE>.exp file and an explanation of the error is displayed. In the second case the term was specified directly in a check/<PACKAGE>.txt file, so there is no additional explanation of the error cause.

8.3.6.7 Definition of Regular Expressions

Regular expressions are defined as follows:

Regular expression: One or more alternatives, separated by '|', i.e. ``ro|rw|no''. If one option matches, the whole term matches (in this case ``ro'', ``rw'' and ``no'' are valid expressions).

An alternative is a concatenation of several sections that are simply added.

A section is an ``atom'', followed by a single ``*'', ``+'', ``?'' or ``{min, max}''. The meaning is as follows:

An ``atom'' is a

An expression in square brackets indicates the following:

8.3.6.8 Examples for regular Expressions

Let's have a look at some examples!

NUMERIC: A numeric value consists of at least one, but otherwise any number of digits. ``At least one'' is expressed with a ``+'', one digit was already in an example above. So this results in:

    NUMERIC = '[0-9]+'
or alternatively
    NUMERIC = '[[:digit:]]+'

NOBLANK: A value that does not contain spaces, is any char (except for the char ``space'') and any number of them:

    NOBLANK = '[^ ]*'

or, if the value is not allowed to be empty:

    NOBLANK = '[^ ]+'

IPADDR: Let's have a look at an example with an IP4-address. An ipv4 address consists of four ``Octets'', divided by dots (``.''). An octet is a number between 0 and 255. Let's define an octet at first. It may be

a number between 0 and 9: [0-9]
a number between 10 and 99: [1-9][0-9]
a number between 100 and 199: 1[0-9][0-9]
a number between 200 and 249: 2[0-4][0-9]
a number between 250 and 255: 25[0-5]

All are alternatives hence we concatenate them with ``|'' forming one expression: ``[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]'' and get an octet. Now we compose an IP4 address, four octets divided by dots (the dot must be masked with a backslash, because else it would represent an arbitrary char). Based on the syntax of an exp-file it would look like this:

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

8.3.6.9 Assistance for the Design of Regular Expressions

If you want to design and test regular expressions, you can use the ``regexp'' program located in the unix or windows directory of the package ``base''. It accepts the following syntax:

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

The parameters explained in short:

This may for example look like here:

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

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

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

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

8.3.7 Extended Checks of the Configuration

Sometimes it is necessary to perform more complex checks. Examples of such complex things would be i.e. dependencies between packages or conditions that must be satisfied only when variables take certain values. For example if a PCMCIA ISDN adapter is used the package ``pcmcia'' has to be installed, too.

In order to perform these checks you may write small tests to check/<PACKAGE>.ext (also called ext-script). The language consists of the following elements:

  1. Keywords:

  2. Data Types: strings, positive integers, version numbers
  3. Logical Operations: <, ==, >, !=, !, &&, ||, =~, copy_pending, samenet, subnet


8.3.7.1 Data Types

Concerning data types please note that variables, based on the associated regular expression are permanently assigned to a data type:

This means, among other things, that a variable of type ENUMERIC can not be used as an index when accessing an array variable, even if you have checked at first that it is not empty. The following code thus does not work as expected:

    # TEST should be a variable of type ENUMERIC
    if (test != "")
    then
        # Error: You can't use a non-numeric ID in a numeric
        #         context. Check type of operand.
        set i=my_array[test]
        # Error: You can't use a non-numeric ID in a numeric
        #         context. Check type of operand.
        set j=test+2
    fi

A solution for this problem is offered by split:

    if (test != "")
    then
        # all elemente of test_% are numeric
        split(test, test_%, ' ', numeric)
        # OK
        set i=my_array[test_%[1]]
        # OK
        set j=test_%[1]+2
    fi


8.3.7.2 Substitution of Strings and Variables

At various points strings are needed, such as when a Warning should be issued. In some cases described in this documentation, such a string is scanned for variables. If found, these are replaced by their contents or other attributes. This replacement is called variable substitution.

This will be illustrated by an example. Assume this configuration:

    # config/base.txt
    HOSTNAME='fli4l'
    # config/dns_dhcp.txt
    HOST_N='1' # Number of hosts
    HOST_1_NAME='client'
    HOST_1_IP4='192.168.1.1'

Then the character strings are rewritten as follows, if variable substitution is active in this context:

    "My router is called $HOSTNAME"
    # --> "My router is called fli4l"
    "HOSTNAME is part of the package %{HOSTNAME}"
    # --> "HOSTNAME is part of the package base"
    "@HOST_N is $HOST_N"
    # --> " # Number of hosts is 1"

As you can see, there are basically three options for replacement:

Hint: Elements of array variables can not be integrated into strings this way, because there is no possibility to provide an index.

In general, only constants can be used for variable substitution, strings that come from a variable remain unchanged. An example will make this clear - assume the following configuration:

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

This code:

    warning "${TEST}"

leads to the following output:

    Warning: ${HOSTNAME}

It will not display:

    Warning: fli4l

In the following sections it will be explicitly noted under which conditions strings are subject of variable substitution.

8.3.7.3 Definition of a Service with an associated Version Number: provides

For instance, an OPT may declare that it provides a Printer service or a Webserver service. Only one package can provide a certain service. This prevents i.e. that two web servers are installed in parallel, which is not possible for obvious reasons, since the two servers would both register port 80. In addition, the current version of the service is provided so that updates can be triggered. The version number consists of two or three numbers separated by dots, such as ``4.0'' or ``2.1.23''.

Services typically originate from OPTs, not from packages. For example the package ``tools'' has a number of programs that each have their own provides statement defined if activated by OPT_...='yes'.

The syntax is as follows:

    provides <Name> version <Version>

Example from package ``easycron'':

    provides cron version 3.10.0

The version number should be incemented by the OPT-developer in the third component, if only functional enhancements have been made and the OPT's interface is still. The version number should be increased in the first or second component when the interface has changed in any incompatible way (eg. due to variable renaming, path changes, missing or renamed utilities, etc.).

8.3.7.4 Definition of a Dependency to a Service with a specific Version: depends

If another service is needed to provide the own function (eg. a web server) this dependency to a specific version may be defined here. The version can be given with two (i.e. ``2.1'') or three numbers (i.e. ``2.1.11'') while the two-number version accepts all versions starting with this number and the three-number version only accepting just the specified one. A list of version numbers may also be specified if multiple versions of the service are compatible with the package.

The syntax is as follows:

    depends on <Name> version <Version>+

An example: Package ``server'' contains:

    provides server version 1.0.1

A Package ``client'' with the following depends-instruction is given:8.2

    depends on server version 1.0       # OK, '1.0' matches '1.0.1'
    depends on server version 1.0.1     # OK, '1.0.1' matches '1.0.1'
    depends on server version 1.0.2     # Error, '1.0.2' does not match with '1.0.1'
    depends on server version 1.1       # Error, '1.1' does not match with '1.0.1'
    depends on server version 1.0 1.1   # OK, '1.0' matches '1.0.1'
    depends on server version 1.0.2 1.1 # Error, neither '1.0.2' nor '1.1' are matching
                                        # '1.0.1'


8.3.7.5 Communication with the User: warning, error, fatal_error

Using these three functions users may be warned, signalized an errors or stop the test immediately. The syntax is as follows:

All strings passed to these funtions are subject of variable substitution.


8.3.7.6 Assignments

If for any reason a temporary variable is required it can be created by ``set var [= value]''. The variable can not be a configuration variable! 8.3 If you omit the ``= value'' part the variable is simply set to ``yes'' so it may be tested in an if-statement. If an assignment part is given, anything may be specified after the equal sign: normal variables, indexed variables, numbers, strings and version numbers.

Please note that by the assignment also the type of the temporary variable is defined. If a number is assigned mkfli4l ``remembers'' that the variable contains a number and later on allows calculations with it. Trying to do calculations with variables of other types will fail.
Example:

    set i=1   # OK, i is a numeric variable
    set j=i+1 # OK, j is a numeric variable and contains the value 2
    set i="1" # OK, i now is a string variable
    set j=i+1 # Error "You can't use a non-numeric ID in a numeric
              #         context. Check type of operand."
              # --> no calculations with strings!

You may also define temporary arrays (see below). Example:

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

The number of array elements is kept by mkfli4l in the variable prim_n. The code above hence leads to the following output:

    Warning: 3

If the right side of an assignment is a string constant, it is subject of variable substitution at the time of assignment. The following example demonstrates this. The code:

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

will output ``not equal'', because the variables v1 and v2 replace the content of the variable s already at the time of assignment.

Hint: A variable set in a script is visible while processing further scripts - currently there exists no such thing as local variables. Since the order of processing scripts of different packages is not defined, you should never rely on any variable having values defined by another package.

8.3.7.7 Arrays

If you want to access elements of a %-variable (of an array) you have to use the original name of the variable like mentioned in the file check/<PACKAGE>.txt and add an index for each ``%'' sign by using ``[Index]''.

Example: If you want to access the elements of variable PF_USR_CHAIN_%_RULE_% you need two indices because the variable has two ``%'' signs. All elements may be printed for example using the following code (the foreach-loop is exlained in see below):

    foreach i in pf_usr_chain_n
    do
        # only one index needed, only one '%' in the variable's name
        set j_n=pf_usr_chain_%_rule_n[i]
        # Attention: a
        # foreach j in pf_usr_chain_%_rule_n[i]
        # is not possible, hence the use of j_n!
        foreach j in j_n
        do
            # two indices needed, two '%' in the variable's name
            set rule=pf_usr_chain_%_rule_%[i][j]
            warning "Rule $i/$j: ${rule}"
        done
    done

With this sample configuration

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

the following output is printed:

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

Alternatively, you can iterate directly over all values of the array (but the exact indices of the entries are not always known, because this is not required):

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

That produces the following output with the sample configuration from above:

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

The second example nicely shows the meaning of the %<Name>-syntax: Within the string %rule is substitued by the name of the variable in question (for example PF_USR_CHAIN_1_RULE_1), while $rule is substituted by its content (i.e. ACCEPT).

8.3.7.8 Encryption of Passwords: crypt

Some variables contain passswords that should not be noted in plain text in rc.cfg. These variables can be encrypted by the use of crypt and are transferred to a format also needed on the router. Use this like here:

    crypt (<Variable>)

The crypt function is the only point at which a configuration variable can be changed.


8.3.7.9 Querying File Properties: stat

stat is used to query file properties. At the moment only file size can be accessed. If checking for files under the current configuration directory you may use the internal variable config_dir. The Syntax:

    stat (<file name>, <key>)

The command looks like this (the parameters used are only examples):

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

The example checks whether a file exists in the current configuration directory.
If OPENVPN_1_SECRET='test' is set in the configuration file, the loop in the first run checks for the existence of the file etc/openvpn/test.secret in the current configuration directory.

After the call two variables are defined:

It may for example look like this:

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

A file name passed as a string constant is subject of variable substitution.


8.3.7.10 Search files: fgrep

If you wish to search a file via ``grep''8.4 you may use the fgrep command. The syntax is:

    fgrep (<File name>, <RegEx>)

If the file <File name> does not exist mkfli4l will abort with a fatal error! If it is not sure if the file exists, test this before with stat. After calling fgrep the search result is present in an array called FGREP_MATCH_%, with its index x as usual ranging from one to FGREP_MATCH_N. FGREP_MATCH_1 points to the whole range of the line the regular expression has matched, while FGREP_MATCH_2 to FGREP_MATCH_N contain the n-1 th part in brackets.

A first example will illustrate the use. The file opt/etc/shells contains the line:

/bin/sh

The following code

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

produces this output:

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

The RegEx has (only) matched with ``/bin/'' (only this part of the line is contained in the variable FGREP_MATCH_1). The first bracketed part in the expression only matches the first char after the first ``/'', this is why only ``b'' is contained in FGREP_MATCH_2. The second bracketed part contains the rest after ``b'' up to the last ``/'', hence ``in'' is noted in variable FGREP_MATCH_3.

The following second example demonstrates an usual use of fgrep taken from check/base.ext. It will be tested if all tmpl:-references given in PF_FORWARD_x are really present.

    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

Both a filename value as well as a regular expression passed as a string constant are subject to variable substitution.


8.3.7.11 Splitting Parameters: split

Often variables can be assigned with several parameters, which then have to be split apart again in the startup scripts. If it is desired to split these previously and perform tests on them split can be used. The syntax is like this:

    split (<String>, <Array>, <Separator>)

The string can be specified by a variable or directly as a constant. mkfli4l splits it where a separator is found and generates an element of the array for each part. You may iterate over these elements later on and perform tests. If nothing is found between two separators an array element with an empty string as its value is created. The exception is `` '': Here all spaces are deleted and no empty variable is created.

If the elements generated by such a split should be in a numeric context (e.g. as indices) this has to be specified when calling split. This is done by the additional attribute ``numeric''. Such a call looks as follows:

    split (<String>, <Array>, <Separator>, numeric)

An example:

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

the output looks like this:

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

Hint: If using the ``numeric'' variant mkfli4l will not check the generated string parts for really being numeric! If you use such a non-numeric construct later in a numeric context (i.e. in an addtion) mkfli4l will raise a fatal error. Example:

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

A string constant passed to split in the first parameter is subject of variable substitution.


8.3.7.12 Adding Files to the Archives: add_to_opt

The function add_to_opt can add additional files to the Opt- or RootFS-Archives. All files under opt/ or from the configuration directory may be chosen. There is no limitation to only files from a specific package. If a file is found under opt/ as well as in the configuration directory, add_to_opt will prefer the latter. The function add_to_opt is typically used if complex logical rules decide if and what files have to be included in the archives.

The syntax looks like this:

    add_to_opt <File> [<Flags>]

Flags are optional. The defaults from table 8.2 are used if no flags are given.

See an example from the package ``sshd'':

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

stat at first checks for the file existing in the configuration directory. If it is, it will be included in the archive, if not, mkfli4l will abort with an error message.

Hint: Also for add_to_opt mkfli4l will first check if the file to be copied can be found in the configuration directory.

Filenames as well flags passed as string constants are subject of variable substitution.


8.3.7.13 Control Flow

    if (expr)
    then
            statement
    else
            statement
    fi

A classic case distinction, as we know it. If the condition is true, the then part is executed, if the condition is wrong the else part.

If you want to run tests on array variables, you have to test every single variable. The foreach loop in two variants for this.

  1. Iterate over array variables:

        foreach <control variable> in <array variable>
        do
                <instruction>
        done
    
        foreach <control variable> in <array variable-1> <array variable-2> ...
        do
                <instruction>
        done
    

    This loop iterates over all of the specified array variables, each starting with the first to the last element, the number of elements in this array is taken from the N-variable associated with this array. The control variable takes the values of the respective array variables. It should be noted that when processing optional array variables that are not present in the configuration, an empty element is generated. You may have to take this into account in the script, for example like this:

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

    As you also can see in the example, the name of the respective array variables can be determined with the %<control variable> construction.

    The instruction in the loop may be one of the above control elements or functions (if, foreach, provides, depends, ...).

    If you want to access exactly one element of an array, you can address it using the syntax <Array>[<Index>]. The index can be a normal variable, a numeric constant or again an indexed array.

  2. Iteration over N-variables:

        foreach <control variable> in <N-variable>
        do
                <instruction>
        done
    

    This loop executes from 1 to the value that is given in the N-variable. You can use the control variable to index array variables. So if you want to iterate over not only one but more array variables at the same time all controlled by the same N-variable you take this variant of the loop and use the control variable for indexing multiple array variables. Example:

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

    The resulting content of the HOST_%_NAME- and HOST_%_IP4-arrays for this example:

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

8.3.7.14 Expressions

Expressions link values and operators to a new value. Such a value can be an normal variable, an array element, or a constant (Number, string or version number). All string constants in expressions are subject to variable substitution.

Operators allow just about everything you could want from a programming language. A test for the equality of two variables could look like this:

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

It should be noted that the comparison is done depending on the type that was defined for the variable in check/<PACKAGE>.txt. If one of the two variables is numeric the comparison is made numeric-based, meaning that the strings are converted to numbers and then compared. Otherwise, the comparison is done string-based; comparing "05" == "5" gives the result ``false'', a comparison "18" < "9" ``true'' due to the lexicographical string order: the digit ``1'' precedes the digit ``9'' in the ASCII character set.

For the comparison of version numbers the construct numeric(version) is introduced, which generates the numeric value of a version number for comparison purposes. Here applies:

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

whereas ``major'' is the first component of the version number, ``minor'' the second and ``sub'' the third. If ``sub'' is missing the term in the addition above is omitted (in other words ``sub'' will be equalled to zero).

A complete list of all expressions can be found in table 8.3. ``val'' stands for any value of any type, ``number'' for a numeric value and ``string''for a string.


Table 8.3: Logical Epressions
Expression true if
id id == ``yes''
val == val values of identical type are equal
val != val values of identical type are unequal
val == number numeric value of val == number
val != number numeric value of val != number
val < number numeric value of val < number
val > number numeric value of val > number
val == version numeric(val) == numeric(version)
val < version numeric(val) < numeric(version)
val > version numeric(val) > numeric(version)
val =~ string regular expression in string matches val
( expr ) Expression in brackets is true
expr && expr both expressions are true
expr || expr at least one of both expressions is true
copy_pending(id) see description
samenet (string1, string2) string1 describes the same net as string2
subnet (string1, string2) string1 describes a subnet of string2

8.3.7.15 Match-Operator

With the match operator =~ you can check whether a regular expression matches the value of a variable. Furthermore, one can also use the operator to extract subexpressions from a variable. After successfully applying a regular expression on a variable the array MATCH_% contains the parts found. May look like this:

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

Calling mkfli4l then would lead to this output:

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

When using =~ you may take all existing regular expressions into account. If you i.e. want to check whether a PCMCIA Ethernet driver is selected without OPT_PCMCIA being set to ``yes'', it might look like this:

    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

As demonstrated in the example, it is important to anchor the regular expression with ˆ and $ if intending to apply the expression on the complete variable. Otherwise, the match-expression already returns ``true'' if only a part of the variable is covered by the regular expression, which is certainly not desired in this case.

8.3.7.16 Check if a File has been copied depending on the Value of a Variable: copy_pending

With the information gained during the checking process the function copy_pending tests if a file has been copied depending on the value of a variable or not. This can be used i.e. in order to test whether the driver specified by the user really exists and has been copied. copy_pending accepts the name to be tested in the form of a variable or a string. 8.5 In order to accomplish this copy_pending checks whether

copy_pending will return ``true'' if it detects that during the last step no file was copied, the copy process hence still is ``pending''.

A small example of the use of all these functions can be found in check/base.ext:

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

Alle elements of the array NET_DRV_% are detected for which no copy action has been done because there is no corresponding entry existing in opt/base.txt.

8.3.7.17 Comparison of Network Addresses: samenet und subnet

For testing routes from time to time a test is needed whether two networks are identical or if one is a subnet of the other. The two functions samenet and subnet are of help here.

    samenet (netz1, netz2)

returns ``true'' if both nets are identical and

    subnet (net1, net2)

returns ``true'' if ``net1'' is a subnet of ``net2''.

8.3.7.18 Expanding the Kernel Command Line

If an OPT must pass other boot parameters to the kernel, in former times the variable KERNEL_BOOT_OPTION had to be checked whether the required parameter was included, and if necessary, a warning or error message had to be displayed. With the internal variable KERNEL_BOOT_OPTION_EXT you may add a necessary but missing option directly in an ext-script. An Example taken from check/base.ext:

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

This passes ``acpi=off'' to the kernel if no or ``APM''-type power management is desired.

8.3.8 Support for Different Kernel Version Lines

Different kernel version lines often differ in some details:

These differences are mostly handled automatically by mkfli4l. To describe the available modules you can, on one hand expand tests dependant on the version (conditional regular expressions), or, on the other hand mkfli4l allows version dependant opt/<PACKAGE>.txt-files. These are then named opt/<PACKAGE>_<Kernel-Version>.txt, where the components of the kernel version are separated from each other by underscores. An example: the package ``base'' contains these files in its opt-directory:

the first file (base.txt) is always considered. Both other files are only considered if the kernel version is called ``3.16(.*)'' resp. ``3.17(.*)''. As seen here, some parts of the version may be omitted in file names, if a group of kernels should be addressed. If KERNEL_VERSION='3.16.41' is given, the following files (if existing) are considered for the package <PACKAGE>:

8.3.9 Documentation

Documentation should be placed in the files

HTML-files may be splitted, meaning one for each OPT contained. Nevertheless a file <PACKAGE>.html has to be created linking to the other files. Changes should be documented in:

The entire text documentation may not contain any tabs and has to have a line feed no later than after 79 characters. This ensures that the documentation can also be read correctly with an editor without automatic line feed.

Also a documentation in LATEX-format is possible, with HTML and PDF versions generated from it. The documentation of fli4l may serve as an example here. A documentation framework for required LATEX-macros can be found in the package ``template''. A brief description is to be found in the following subsections.

The fli4l documentation is currently available in the following languages: German (<LANGUAGE> = ``deutsch''), English (<LANGUAGE> = ``english'') and French (<LANGUAGE> = ``french''). It is the package developer's decision to document his package in any language. For the purposes of clarity it is recommended to create a documentation in German and/or English (ideally in both languages).

8.3.9.1 Prerequisites for Creating a LATEXDocumentation

To create a documentation from LATEX-sources the following requirements apply:

8.3.9.2 File Names

The documentation files are named according to the following scheme:

<PACKAGE>_main.tex:
This file contains the main part of the documentation. <PACKAGE> stands for the name of the package to be described (in lowercase letters).
<PACKAGE>_appendix.tex:
If further comments should be added to the package, they should be placed there.

The files should be stored in the directory fli4l/<PACKAGE>/doc/<SPRACHE>/tex/<PACKAGE>. For the package sshd this looks like here:

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

The Makefile is responsible for generating the documentation, the sshd.tex-file provides a framework for the actual documentation and the appendix, which is located in the other two files. See an example in the documentation of the package ``template''.

8.3.9.3 LATEX-Basics

LATEX is, just like HTML, ``Tag-based'' , only that the tags are called ``commands'' and have this format: \command resp. \begin{environment} ...\end{environment}

By the help of commands you should rather emphasize the importance of the text less the display. It is therefore of advantage to use

\warning{Please do not...}

instead of

\emph{Please do not...}

.

Each command rsp. each environment may take some more parameters noted like this: \command{parameter1}{parameter2}{parameterN}.

Some commands have optional parameters in square (instead of curly) brackets:
\command[optionalParameter]{parameter1} ... Usually only one optional parameter is used, in rare cases there may be more.

Individual paragraphs in the document are separated by blank lines. Within these paragraphs LATEX itself takes care of line breaks and hyphenation.

The following characters have special meaning in LATEX and, if occuring in normal text, must be masked prefixed by a \: # $ & _ % { }. ``~'' and ``^'' have to be written as follows: \verb?~? \verb?^?

The main LATEX-commands are explained in the documentation of the package ``template''.

8.3.10 File Formats

All text files (both documentation and scripts, which later reside on the router) should be added to the package in DOS file format, with CR/LF instead of just LF at the end of a line. This ensures that Windows users can read the documentation even with ``notepad'' and that after changing a script under Windows everything still is executable on the router.

The scripts are converted to the required format during archive creation (see the description of the flags in table 8.2).

8.3.11 Developer Documentation

If a program from the package defines a new interface that other programs can use, please store the documentation for this interface in a separate documentation in doc/dev/<PACKAGE>.txt.

8.3.12 Client Programs

If a package also provides additional client programs, please store them in the directory windows/ for Windows clients and in the directory unix/ for *nix and Linux clients.

8.3.13 Source Code

Customized programs and source code may be enclosed in the directory src/<PACKAGE>/. If the programs should be built like the rest of the fi4l programs, please have a look at the documentation of the ``src''-package .


8.3.14 More Files

All files, which will be copied to the router have to be stored under opt/etc/ and opt/files/. Be under

Scripts in opt/etc/boot.d/, opt/etc/rc.d/ and opt/etc/rc0.d/ have the following naming scheme:

    rc<number>.<name>

The number defines the order of execution, the name gives a hint on what program/package is processed by this script.



Footnotes

... quotes8.1
Both single and double quotes are valid. You can hence write FOO='bar' as well as FOO="bar". The use of double quotes should be an exception and you should previously inform about how an *nix shell uses single and double quotes.
... given:8.2
Of course only one at a time!
... variable!8.3
This is a desired restriction: Check scripts are not able to change the user configuration.
... ``grep''8.4
``grep'' is a common command on *nix-like OSes for filtering text streams.
... string.8.5
As described before the string is subject of variable substitution, i.e via a foreach-loop and a %<Name>-subsitution all elements of an array may be examined.
© 2001-2019 The fli4l-Team - 28 April 2019