Table of Contents

UCI beállítások használata scriptekből

Az OpenWrt által kínált standard shell procedúrák segítségével az UCI konfigfájlok beállításai hatékonyan kiolvashatóak és kezelhetőek shell scriptekből is. Ezek a shell wrapper-ek ugyanúgy az uci parancssori segédprogramot használják, de számos funkcióval megkönnyítik a használatát.

Betöltés

Hogy használni tudd az általános UCI procedúrákat, mindenek előtt includolni kell a /etc/functions.sh fájlt a scriptedbe az alábbi paranccsal (általában ez csak egy symlink a ../lib/functions.sh-ra):

. /etc/functions.sh

Ezután a config_load name hívással tölthetsz be egy adott UCI beállításfájlt. Ez a funkció előbb ellenőrzi, hogy a name abszolút elérési út-e, és ha nem, akkor alapértelmezetten a /etc/config/ mapppából próbálja betölteni a filet (általában így szokás használni).

Direkt elérés

A UCI beállítások legegyszerűbben ezzel a módszerrel érhetők el. Ha előre ismert a section neve, akkor a benne lévő option-ok értékei szekvenciális feldolgozás alkalmazása nélkül, közvetlenül is elérhetőek. Ehhez a config_get és config_set függvényeket kell használni.

Opciók beolvasása

Konkrét opciók beolvasására a config_get használható. Két féle paramétezése van:

Használata (teljes): config_get <variable> <section> <option> [<default>]

Ekkor a config_get procedúra legalább három paramétert vár:

  1. variable - a változó neve amibe a kiolvasott érték kerülni fog
  2. section - a section ID-je amiben a kért option található
  3. option - az option neve aminek az értékét tudni szeretnénk
  4. default (opcionális) - alapértelmezett viszatérési érték, ha a keresett option nincs beállítva

Használata (egyszerűsített): config_get <section> <option>

Egyszerű elérésnél a paraméterek a következők:

  1. section - a section ID-je amiben a kért option található
  2. option - az option neve aminek az értékével a függvény visszatér

Az alábbi példaprogram a “loopback” hálózati interfész IP-címét olvassa be (ez alapesetben 127.0.0.1) előbb sikeresen, majd az option nevét elrontva sikertelenül, majd újra sikeres az egyszerűbb módszerrel:

#!/bin/sh
. /etc/functions.sh
local loopbackIP
config_load network
config_get loopbackIP loopback ipaddr
echo The loopback IP is: "$loopbackIP"
 
echo "trying to read a non-existant value results default value:"
config_get loopbackIP loopback ipaddress "invalid option"
echo The loopback IP is: "$loopbackIP"
 
echo "reading with the simple method:"
loopbackIP=$(config_get loopback ipaddr)
echo The loopback IP is: "$loopbackIP"

Eredmény:

# ./UCIgetloopbackip.sh The loopback IP is: 127.0.0.1 trying to read a non-existant value results default value: The loopback IP is: invalid option reading with the simple method: The loopback IP is: 127.0.0.1

Logikai értékek beolvasása

Ha előre tudjuk hogy egy logikai típusú option-t szeretnénk beolvasni, akkor használjuk a config_get_bool függvényt. A logikai konfigurációs értékek elég változatosak lehetnek, az igaz értéket a következők bármelyike jelölheti on, true, enabled vagy 1. A hamis érték off, false, disabled vagy 0 is lehet.

The config_get_bool leegyszerűsíti ezek kezelését azáltal, hogy sima integerré konvertálja őket (1 lesz az igaz és 0 a hamis érték).

Használata: config_get_bool <variable> <section> <option> [<default>]

A paraméterei ugyanazok mint a config_get-nek:

  1. variable - a változó neve amibe a kiolvasott érték kerülni fog
  2. section - a section ID-je amiben a kért option található
  3. option - az option neve aminek az értékét tudni szeretnénk
  4. default (opcionális) - alapértelmezett viszatérési érték, ha a keresett option nincs beállítva

Listák beolvasása

A lista típusú UCI option-ök több értéket tárolnak ugyanazzal a névvel. Például az alábbi network nevű listának két eleme van:

... list network lan list network wifi ...

A config_get használata listák eléréséhez nem mindig célravezető, mivel a lista összes elemét összefűzve, szóközzel elválasztva adja vissza. A fenti network listánál ez nem okoz különösebb gondot az eredmény: lan wifi.

Viszont kicsit bonyolultabb a helyzet ha a lista elemei maguk is tartalmaznak szóközt, például:

... list animal 'White Elephant' list animal 'Mighty Unicorn' ...

Ekkor config_get a következő értéket adja vissza: White Elephant Mighty Unicorn és ez alapján nem lehet eldönteni, hogy meddig tartott az első, és hol kezdődik a második elem.

A probléma megoldására a config_list_foreach iterátor használható. Ez hasonlóan működik, mint a config_foreach (lásd később), csak a section-ök helyett a lista értékeit kezeli. Használatához először egy saját callback függvényt kell definiálni, ami paraméterben fogja egyesével megkapni a lista értékeit, minden egyes lista értéknél újra meghívódik.

A config_list_foreach legalább három paramétert vár:

  1. Section - a section ID-je amiben a lista van
  2. Option - a lista option neve, amit fel szeretnénk dolgozni
  3. Function - a callback függvény neve, ami minden lista elemnél meghívódik
  4. További paraméterek (opcionális) - minden további paramétert változtatás nélkül megkap a callback függvény

Például

# handle list items in a callback # $config contains the ID of the section handle_animal() { local value=“$1” # do something with $value } config_list_foreach “$config” animal handle_animal

Teljes példaprogram, az NTP szerverek kiíratása:

#!/bin/sh
. /etc/functions.sh
counter=0
handle_listvalue() {
    local value="$1"
    local custom="$2"
    echo The $counter. value of the list is $value
    counter=$((counter+1))
    }
 
config_load system
echo "value of ntp.server list by using config_get:"
config_get ntp server
 
echo "values using the config_list_foreach iterator:"
config_list_foreach ntp server handle_listvalue 

Eredmény:

# ./UCIlistiterator.sh value of ntp.server list by using config_get: 0.openwrt.pool.ntp.org 1.openwrt.pool.ntp.org 2.openwrt.pool.ntp.org 3.openwrt.pool.ntp.org values using the config_list_foreach iterator: The 0. value of the list is 0.openwrt.pool.ntp.org The 1. value of the list is 1.openwrt.pool.ntp.org The 2. value of the list is 2.openwrt.pool.ntp.org The 3. value of the list is 3.openwrt.pool.ntp.org

Szekvenciális feldolgozás

A beállítások szekvenciális feldolgozására akkor van szükség,

Erre két féle módszer használható: a visszahívásos módszer (callback) és az utólagos iteráció.

Ha a callback módszert szeretnéd használni a beállítások feldolgozásához, akkor ehhez felül definiálni kell bizonyos shell függvényeket a /etc/functions.sh includolása után, de még a config_load hívása előtt.

Callback módszer

Ebben az esetben a keretscript a konfigfájlt soronként olvassa be és minden sorhoz meghívja a hozzá tartozó callback függvényt:

fut le. Ezeket nekünk kell definiálni a scriptünkben a config_load hívása előtt. (Alapesetben ezek a függvények nem csinálnak semmit, mindössze egy return 0-val visszatérnek.)

A callback függvényeken belül az aktuális section ID-je a $CONFIG_SECTION változóban érhető el.

config_cb

A feldolgozás során minden új UCI section kezdeténél (config kulcsszónál) meghívódik a config_cb függvény.

A config_cb ebben az esetben két paramétert kap:

  1. Type - a section típusa, pl. a config interface wan sor esetén az interface.
  2. Name - a section neve, pl. a config interface wan sornál a wan, vagy anonymous section-ök esetén (pl. config route) egy automatikusan generált ID lesz, pl. cfg13abef.
config_cb() { local type=“$1” local name=“$2” # echo New section - type: “$type”, name: “$name” # minden section esetén futtatandó parancsok }

A config_load függvény végén egy további, paraméterek nélküli config_cb hívás is történik, így nem csak az option-ok előtt hanem az összes section feldolgozása után is hajthatsz végre saját parancsokat. Számíts rá, hogy ilyenkor a paramétereknek nem lesz értéke, szűrd ki az esetet pl. egy if-fel.

option_cb

Hasonló módon, minden egyes új option sor elérésekor az option_cb függvény hívódik meg.

Az option_cb mindig két paramétert kap:

  1. Name - az option neve, pl. ifname az option ifname eth0 sor esetén
  2. Value - az option értéke, pl. eth0 az option ifname eth0 sor esetén
option_cb() { local name=“$1” local value=“$2” # echo new option - name: “$name”, value: “$value” # minden option esetén futtatandó parancsok }

Az option_cb függvényt a config_cb függvényből is módosíthatod a section típusa szerint. Így lehetőség van típusonként külön option_cb függvényt alkalmazva minden egyes config section-t a saját típusának megfelelően feldolgozni.

list_cb

A harmadik kulcsszó sem kivétel, minden egyes új list sor elérésekor a list_cb függvény fut le. Mivel minden egyes lista elem külön list sorban van, így a függvény elemenként egyszer lesz meghívva. Ugyanazon lista elemeinek ugyanaz a nevük.

Az list_cb mindig két paramétert kap:

  1. Name - a lista neve neve, pl. icmp_type a list icmp_type echo-reply sor esetén
  2. Value - az option értéke, pl. echo_reply a list icmp_type echo-reply sor esetén
list_cb() { local name=“$1” local value=“$2” # echo new list element - name: “$name”, value: “$value” # minden list esetén futtatandó parancsok }

Callback példa

Írassuk ki a default system configot! A teszt pillanatában nekem így nézett ki a /etc/config/system fájl:

config system option hostname OpenWrt option timezone UTC config timeserver ntp list server 0.openwrt.pool.ntp.org list server 1.openwrt.pool.ntp.org list server 2.openwrt.pool.ntp.org list server 3.openwrt.pool.ntp.org option enable_server 0

Az alábbi példaprogram segítségével élesben is tesztelheted a callback függvények működését. Mentsd el tetszőleges néven, majd adj rá futtatási jogot és indítsd el.

#!/bin/sh
. /etc/functions.sh
config_cb() {
    local type="$1"
    local name="$2"
    if [ ! -z $type ] ;then
      echo New section - type: "$type", name: "$name"
    else
      echo "End of configuration"
    fi
    }
 
option_cb() {
    local name="$1"
    local value="$2"
    echo "  new option - name:" "$name", value: "$value", in section: $CONFIG_SECTION
    }
 
list_cb() {
    local name="$1"
    local value="$2"
    echo "  new list element - name:" "$name", value: "$value", in section: $CONFIG_SECTION
    }
 
echo Loading system config:
config_load system

Eredmény

# ./UCIcallbacktest.sh Loading system config: New section - type: system, name: cfg02e48a new option - name: hostname, value: OpenWrt, in section: cfg02e48a new option - name: timezone, value: UTC, in section: cfg02e48a New section - type: timeserver, name: ntp new option - name: server_ITEM1, value: 0.openwrt.pool.ntp.org, in section: ntp new option - name: server_LENGTH, value: 1, in section: ntp new list element - name: server, value: 0.openwrt.pool.ntp.org, in section: ntp new option - name: server_ITEM2, value: 1.openwrt.pool.ntp.org, in section: ntp new option - name: server_LENGTH, value: 2, in section: ntp new list element - name: server, value: 1.openwrt.pool.ntp.org, in section: ntp new option - name: server_ITEM3, value: 2.openwrt.pool.ntp.org, in section: ntp new option - name: server_LENGTH, value: 3, in section: ntp new list element - name: server, value: 2.openwrt.pool.ntp.org, in section: ntp new option - name: server_ITEM4, value: 3.openwrt.pool.ntp.org, in section: ntp new option - name: server_LENGTH, value: 4, in section: ntp new list element - name: server, value: 3.openwrt.pool.ntp.org, in section: ntp new option - name: enable_server, value: 0, in section: ntp End of configuration

Három dolgot kell észrevenni:

  1. a system section anonymous, ezért generálódott neki egy ideiglenes név
  2. a lista típusú opciók esetén (mint a system.ntp) is meghívódik az option_cb callback. Ez valószinűleg a keretscript hibája, reméljük, hogy kijavítják valamikor.
  3. a betöltés legvégén van egy paraméterek nélküli config_cb hívás, amit a függvényen belül le kell kezelni

Iterációs módszer

A másik módszer a konfigurációs fájlok feldolgozására, hogy előbb betöltjük az egész fájlt a memóriába, majd sorban végigmegyünk a section-ökön a config_foreach procedúra használatával. Ez inkább akkor használatos, ha a section-ök típusa adott (tudjuk, hogy milyen option-ok vannak bennük), de nem tudjuk előre, hogy hány ilyen típusú section lesz. Sajnos option_foreach procedúra jelenleg nincs a rendszerben.

A config_foreach procedúrának egy kötelező és további opcionális paraméterei vannak:

  1. Function - egy előre definiált saját függvény neve, ami minden új section esetén meghívódik
  2. Type (opcionális) - ha meg van adva, akkor csak az adott típusú section-ök lesznek feldolgozva, a többit kihagyja
  3. További paraméterek (opcionális) - minden további paramétert változtatás nélkül megkap az első paraméterben hívott függvény

Az alábbi példában a saját handle_interface procedúra meghívódik minden egyes config interface section-höz a /etc/config/network fájlban. A test string extra paraméterként átadódik a saját procedúránknak.

#!/bin/sh
. /etc/functions.sh
handle_interface() {
    local config="$1"
    local custom="$2"
    echo New section: "$config", custom parameter: "$custom"
    # minden új section esetén futtatandó parancsok
    }
config_load network
config_foreach handle_interface interface test

Megszakíthatjuk az iterációt, ha nem nulla értékkel térünk vissza a függvényből (return 1).

A section-ön belül a config_get vagy a config_set függvények segítségével beolvashatók vagy megváltoztathatók az option-ok értékei.