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:
config
kulcsszónál aconfig_cb
option
kulcsszónál azoption_cb
list
kulcsszó 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 wan
sor esetén azinterface
. - Name - a section neve, pl. a
config interface wan
sorná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.
ifname
azoption ifname eth0
sor esetén - Value - az option értéke, pl.
eth0
azoption 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:
- Name - a lista neve neve, pl.
icmp_type
alist icmp_type echo-reply
sor esetén - Value - az option értéke, pl.
echo_reply
alist 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:
- 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_cb
callback. 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_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:
- 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.