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:
- variable - a változó neve amibe a kiolvasott érték kerülni fog
- section - a section ID-je amiben a kért option található
- option - az option neve aminek az értékét tudni szeretnénk
- 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:
- section - a section ID-je amiben a kért option található
- 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:
- variable - a változó neve amibe a kiolvasott érték kerülni fog
- section - a section ID-je amiben a kért option található
- option - az option neve aminek az értékét tudni szeretnénk
- 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:
- Section - a section ID-je amiben a lista van
- Option - a lista option neve, amit fel szeretnénk dolgozni
- Function - a callback függvény neve, ami minden lista elemnél meghívódik
- 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,
- ha egy section, vagy egy egész konfigfájl összes option-ját ki szeretnénk értékelni,
- ha nem tudjuk előre, hogy hány sectiont kell feldolgozni,
- ha az anonymous section-öket is el szeretnénk érni.
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:
configkulcsszónál aconfig_cboptionkulcsszónál azoption_cblistkulcsszó esetén pedig alist_cb
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:
- Type - a section típusa, pl. a
config interface wansor esetén azinterface. - Name - a section neve, pl. a
config interface wansornál awan, 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:
- Name - az option neve, pl.
ifnameazoption ifname eth0sor esetén - Value - az option értéke, pl.
eth0azoption ifname eth0sor 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:
- Name - a lista neve neve, pl.
icmp_typealist icmp_type echo-replysor esetén - Value - az option értéke, pl.
echo_replyalist icmp_type echo-replysor 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:
- a system section anonymous, ezért generálódott neki egy ideiglenes név
- a lista típusú opciók esetén (mint a system.ntp) is meghívódik az
option_cbcallback. Ez valószinűleg a keretscript hibája, reméljük, hogy kijavítják valamikor. - a betöltés legvégén van egy paraméterek nélküli
config_cbhí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:
- Function - egy előre definiált saját függvény neve, ami minden új section esetén meghívódik
- Type (opcionális) - ha meg van adva, akkor csak az adott típusú section-ök lesznek feldolgozva, a többit kihagyja
- 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.