http://wiki.openwrt.org/doc/uci
UCI是Unified Configuration Interface的縮寫,翻譯成中文就是統一配置接口,用途就是為OpenWrt提供一個集中控制的接口。OpenWrt實現的這個工具,能夠讓你的不管是Lua還是PHP程序,或者SHELL程序或C程序,只要執行命令傳輸參數就能達到修改系統參數的目的,請參考本文后面的命令行實用工具。
系統的配置應該簡單直接,UCI的設計初衷即是這樣的,它是NVRAM-based配置方法的繼承者(基於NVRAM的配置方法起源於OpenWrt的White Russian系列,該版本目前不再更新,最后發布於2007年,版本號為0.9)。UCI可以視為OpenWrt系統功能設置的主要用戶配置接口,通常來說這些配置與系統的功能關聯性較大,想像一樣我們平常所使用的路由器或嵌入式設備中的WEB界面中的那些配置項,就是路由器或嵌入式設備系統所集成了的功能。常見的例子如路由器的網絡接口設置,無線參數設置,logging設置和遠程登錄設置等。
UCI目前已經支持有一小部分應用程序,因而對這些應用程序的控制會變得更加簡單一些。這些第三方應用程序都會有自己的配置文件,不同的語法,不同的文件位置,如
/etc/network/interfaces
/etc/exports
/etc/dnsmasq.conf
或/etc/samba/samba.conf
由於UCI統一配置接口的出現,對這些第三方應用程序的配置只需要修改UCI的配置文件即可,就不必再去找不同的目錄,寫不同的語法了。當然,你安裝的大多數第三方應用程序都沒有提供UCI配置接口,很可能是因為這些應用程序本身就不需要向普通用戶提供應用程序接口,配置文件是給開發者使用的,從這個角度上來看,沒有提供UCI接口反而更好。因而,OpenWrt包維護人員只選定了一小部分必需的程序實現了UCI配置接口,下面有列出(Therefore, only a few selected programs which benefit from availability of a centralised configuration have been made UCI-compatible by the OpenWrt package maintainers (see the UCI configuration file list below))。
許多第三方程序是根據它自己對應於/etc/config下的UCI配置文件的選項去設置程序的原始配置文件,這樣就實現了程序對UCI配置的兼容,然后執行一次/etc/init.d腳本完成一次配置。因而當你啟動一個某個程序的UCI兼容的進程腳本時,該腳本應該不只是修改/etc/config下對應的UCI配置文件,同時也應該覆蓋程序自己的原配置文件。比如Samba/CIFS程序,其原配置文件是在/etc/samba/smb.conf,而對應的UCI文件是/etc/config/samba,當/etc/config/samba文件被修改了之后,需要運行一次
/etc/init.d/samba start
之后UCI文件中的設置才會更新到原配置文件中去。
除此之外,應用程序的配置文件常常是存放在RAM而不是FLASH中,因為它不需要每次修改參數之后就去寫非易性閃存了,而只在應用改變的時候它才會根據UCI文件去寫非易性閃存(原文:In addition, the application's configuration file is often stored in RAM instead of in flash, because it does not need to be stored in non-volatile memory and it is rewritten after every change, based on the UCI file.)。
OpenWrt的wiki里有一篇文章NotUCI Configuration列舉了一些與UCI不兼容的自帶程序,而其它的第三方程序,你得自己去查閱程序的說明了。
一般規則
UCI的配置文件被分割成/etc/config下的多個獨立的文件,各個文件按名字含義對應系統的不同的功能配置。你可以通過文本編譯器或者uci實用程序去修改這些配置文件,同時uci還提供了C語言/腳本/Lua等語言的應用程序接口,WEB配置頁面例如Luci就是利用了uci所提供的API而實現對UCI配置文件的修改的。
不管你是采用文本編輯器還是通過命令行的方式修改了UCI配置文件,相應的服務或應用程序不會自動更新狀態,這時你都必須調用一次/etc/init.d (re)start才能使剛剛對UCI配置文件的修改生效。許多兼容UCI的程序采用這樣的方法來應用更新:在init.d腳本執行流中去修改自己程序的配置文件。具體說來,init.d腳本先去修改自己程序的原配置文件中的信息(如/etc/samba/smb.conf),之后重啟一次應用程序,應用程序就會去讀自己的配置文件(剛剛被init.d更新過的)再啟動,這樣應用程序的狀態就更新了。僅僅重啟應用程序,而不執行init.d腳本的話,/etc/config下的UCI配置文件是不會應用於應用程序的,新配置也就不生效了。
舉個例子:
先登錄到路由器的WEB頁面把WiFi給禁用掉,這個時候你的手機搜索到你的路由器發送的SSID了,我這兒是NGTestRouter。
這時我們准備通過使用文本編輯器修改UCI再應用的方法來現使能WiFi,步驟如下:
編輯wireless文件把disabled這個項注釋掉(也就是enable WiFi了)
#vi /etc/config/wireless
然后運行一次
#/etc/init.d/network restart
這時你的手機又可以看到NGTestRouter這個熱點了!
UCI配置文件
(請參考原鏈接http://wiki.openwrt.org/doc/uci#configuration.files)
文件語法
uci配置文件通常包含有一個或多個語句。所謂段(section),包含有一個或多個option語句,這些語句定義了實際的值。
下面是一個簡單的配置文件:
package 'example' config 'example' 'test' option 'string' 'some value' option 'boolean' '1' list 'collection' 'first item' list 'collection' 'second item'
- config 'example' 'test'表示一個段的開始,其中example是段的類型,test為段的名字。段也可以沒有名字,像config 'example',但是必須要有類型,類型指示了uci程序怎么去處理后面的option內容;
- option 'string' 'some value'和option 'boolean' '1'兩個語句定義了段內的兩個標識符的值,雖然它們一個是string一個是boolean,但是在語法沒有任何區別。boolean后面可以跟'0', 'no', 'off', 'false'中的一個作為否的值,或者'1', 'yes', 'on', 'true'作為邏輯是的值;
- 后面兩行以list開頭的語句,是為某個有多種選項值的option所定義的,在同一option中的選項值,它們應該有同樣的名字,在這里的名字為collection。最張這兩個值為收納到同一個list表中,表中出現的順序即你這里所定義的;
- 標識符option和list是為了更易讀而加上的,沒有它們也是可以的;
- 如果某個option沒有但它不是必須的,那么uci處理程序會假定一個默認值;如果該option是必須的,而文件中沒有定義,那么uci會報錯或者顯現出奇怪的結果;
語句中的標識和值可不必使用引號引起,除非你的字段值含有空格或者tab鍵。如果使用引號,那你可以隨意使用單引號或者雙引號。比如這樣子:
option example value option 'example' value option example "value" option "example" 'value' option 'example' "value"
不過不能這樣子(引號混用,字段中有空格但未用引號引起來):
option 'example" "value' (quotes are unbalanced) option example some value with space (note the missing quotes around the value)
UCI的文件名和標識符(像option example value中的example即為標識符,value為option的值)可以使用a-z, 0-9和下划線_組合的任意字符串,不允許使用橫杠線-,而option的值可以傅任意字符(像空格這樣子的字段值需要用引號引起)。
命令行實用工具
修改配置的一種方法是直接去修改UCI配置文件。不過,UCI配置文件讀和寫操作都可以通過uci命令行實用工具來完成,因而如果你自己去寫一個腳本來解析或寫入UCI配置文件不是一個明智的選擇,既浪費時間又不一定寫得好。以下介紹如何使用uci命令行實用工具,並伴有一些實例參考:
在學習該工具前需要注意:uci會把先讀到UCI文件,其中不認識的所有命令參數和注釋會被刪除!所以,像uhttpd這樣安裝地有詳細注釋的文件,在使用uci操作之后其中的注釋就會被抹掉的。OpenWrt的默認WEB界面Luci就是采用了uci來寫UCI文件!
用法
執行下面命令查看
root@OpenWrt:~#uci
例子
設置一個值
把uhttpd的監聽端口從80換成8080
root@OpenWrt:~# uci set uhttpd.main.listen_http=8080
root@OpenWrt:~# uci commit uhttpd
root@OpenWrt:~# /etc/init.d/uhttpd restart
root@OpenWrt:~#
導出整體配置信息
root@OpenWrt:~# uci export httpd package 'httpd'
config 'httpd'
option 'port' '80'
option 'home' '/www'
root@OpenWrt:~#
顯示一個給定配置的樹
root@OpenWrt:~# uci show httpd
httpd.@httpd[0]=httpd
httpd.@httpd[0].port=80
httpd.@httpd[0].home=/www
root@OpenWrt:~#
顯示一個option的值
root@OpenWrt:~# uci get httpd.@httpd[0].port
80
root@OpenWrt:~#
追加list的一個條目
uci add_list system.ntp.server='0.de.pool.ntp.org'
替換一個list
uci delete system.ntp.server
uci add_list system.ntp.server='0.de.pool.ntp.org'
uci add_list system.ntp.server='1.de.pool.ntp.org'
uci add_list system.ntp.server='2.de.pool.ntp.org'
UCI路徑
假設有下面的UCI文件
# /etc/config/foo
config bar 'first'
option name 'Mr. First'
config bar
option name 'Mr. Second'
config bar 'third'
option name 'Mr. Third'
那么下面三組路徑的執行得到的值分別各自相等
# Mr. First
uci get foo.@bar[0].name
uci get foo.@bar[-0].name
uci get foo.@bar[-3].name
uci get foo.first.name
# Mr. Second
uci get foo.@bar[1].name
uci get foo.@bar[-2].name
# uci get foo.second.name 本條語句不工作,因為second沒有定義
# Mr. Third
uci get foo.@bar[2].name
uci get foo.@bar[-1].name
uci get foo.third.name
如果show,則會得到這樣的值
# uci show foo
foo.first=bar
foo.first.name=Mr. First
foo.@bar[0]=bar
foo.@bar[0].name=Mr. Second
foo.third=bar
foo.third.name=Mr. Third
執行uci show foo.@bar[0]得到
# uci show foo.@bar[0] foo.first=bar foo.first.name=Mr. First
查詢輸出
root@OpenWrt:~# uci -P/var/state show network.wan uci: Entry not found network.loopback=interface network.loopback.ifname=lo network.loopback.proto=static network.loopback.ipaddr=127.0.0.1 network.loopback.netmask=255.0.0.0 network.loopback.up=1 network.loopback.connect_time=10749 network.loopback.device=lo network.lan=interface network.lan.type=bridge network.lan.proto=static network.lan.netmask=255.255.255.0 network.lan.ipaddr=10.0.11.233 network.lan.gateway=10.0.11.254 network.lan.dns=8.8.8.8 network.lan.up=1 network.lan.connect_time=10747 network.lan.device=eth0 network.lan.ifname=br-lan
添加防火牆規則
這個例子不僅演示了如何添加TCP SSH防火牆規則,同時也演示uci的negative (-1)語法。
root@OpenWrt:~# uci add firewall rule
root@OpenWrt:~# uci set firewall.@rule[-1].src=wan
root@OpenWrt:~# uci set firewall.@rule[-1].target=ACCEPT
root@OpenWrt:~# uci set firewall.@rule[-1].proto=tcp
root@OpenWrt:~# uci set firewall.@rule[-1].dest_port=22
root@OpenWrt:~# uci commit firewall
root@OpenWrt:~# /etc/init.d/firewall restart
獲取SSID
root@OpenWrt:~# uci get wireless.@wifi-iface[-1].ssid NGTestRouter
Get WAN IP address
-
Backfire
uci -P/var/state get network.wan.ipaddr
-
The uci state vars are deprecated and not used anymore for network related information. In Trunk (not really uci) do
./lib/functions/network.sh; network_get_ipaddr ip wan; echo $ip
Get WAN interface
-
Backfire
uci -P/var/state get network.wan.ifname
-
The uci state vars are deprecated and not used anymore for network related information. In Trunk (not really uci) do
./lib/functions/network.sh; network_get_device if_wan wan; echo $if_wan
-
Or
./lib/functions/network.sh; network_get_physdev if_wan wan; echo $if_wan