Openwrt開發(fā)與Luci介紹

摘要:Lua作為一門方便嵌入(其它應(yīng)用程序)并可擴展的輕量級腳本語言來設(shè)計的,因此她一直遵從著簡單、小巧、可移植、快速的原則,官方實現(xiàn)完全采用ANSI C編寫,能以C程序庫的形式嵌入到宿主程序中。Lua的每個版本都保持著開放源碼的傳統(tǒng),不過各版采用的許可協(xié)議并不相同,自5.0版(最新版是5.1)開始她采用的是著名的MIT許可協(xié)議。正由于上述特點,所以Lua在游戲開發(fā)、機器人控制、分布式應(yīng)用、圖像處理、生物信息學(xué)等各種各樣的領(lǐng)域中得到了越來越廣泛的應(yīng)用。


Luci介紹

Luci是 Lua Con?gurationInterface的簡稱,意在OpenWrt整個系統(tǒng)的配置集中化。見鏈接:
http://wiki.openwrt.org/zh-cn/doc/uci

Luci 的啟動--uhttpd

uhttpd是一個簡單的web服務(wù)器程序,主要就是cgi的處理,openwrt是利用uhttpd作為web服務(wù)器,實現(xiàn)客戶端web頁面配置功能。對于request處理方式,采用的是cgi,而所用的cgi程序就是luci。

Luci 的啟動--luci

在web server中的cgi-bin目錄下,運行 luci 文件(權(quán)限一般是 755 ),luci的代碼如下:

  #!/usr/bin/lua      --cgi的執(zhí)行命令的路徑 
 
  require"luci.cacheloader"    --導(dǎo)入cacheloader包 

  require"luci.sgi.cgi"         --導(dǎo)入sgi.cgi包  

  luci.dispatcher.indexcache = "/tmp/luci-indexcache"  --cache緩存路徑地址 

  luci.sgi.cgi.run()  --執(zhí)行run,此方法位于*/luci/sgi/cgi.lua中

Luci-- Web

  a.登錄

  輸入: http://x.x.x.x/ 登錄LuCI.

  Calling /www/cgi-bin/luci.

  b. 進入主菜單‘status’

  輸入: http://x.x.x.x/cgi-bin/luci/admin/status/即可訪問status頁面。Luci則會calling /luci/admin/目錄下的status.lua腳本:

  module("luci.controller.admin.status", package.seeall)

  /usr/lib/lua/luci/controller/admin/status.lua->index()

以status模塊為例進行說明

模塊入口文件status.lua在目錄lua\luci\controller\admin下在index()函數(shù)中,使用entry函數(shù)來完成每個模塊函數(shù)的注冊:

entry(path, target, title=nil, order=nil)

entry()函數(shù)

  第一個參數(shù)是定義菜單的顯示(Virtual path)。

  第二個參數(shù)定義相應(yīng)的處理方式(target)。

  alias是指向別的entry的別名,from調(diào)用的某一個view,cbi調(diào)用某一個model,call直接調(diào)用函數(shù)。

  第三個參數(shù)是菜單的文本,_(“string”),國際化。

  第四個參數(shù)是是同級菜單下,此菜單項的位置,從大到小

target主要分為三類:call,template 和cbi

call用來調(diào)用函數(shù)。即語句

entry({"admin", "status", "iptables"}, call("action_iptables"), _("Firewall"), 2)
Firewall模塊調(diào)用了action_iptables函數(shù)

template調(diào)用

template用來調(diào)用已有的htm模版,模版目錄在lua\luci\view目錄下。即語句

entry({"admin","status","overview"},template("admin_status/index"),_("Overview"), 1)
調(diào)用lua\luci\view\admin_status\index.htm文件來顯示。

CBI調(diào)用

a. CBI了解 –- Configuration Binding Interface

CBI模型是Lua文件描述UCI配置文件的結(jié)構(gòu)和由此產(chǎn)生的HTML表單來評估CBI解析器,所有CBI luci.cbi.Map類型的模型文件必須返回一個map對象,在cbi模塊中定義各種控件,Luci系統(tǒng)會自動執(zhí)行大部分處理工作。其鏈接目錄在lua\luci\model\cbi

entry({"admin", "status", "processes"}, cbi("admin_status/processes"), _("Processes"), 6)
調(diào)用\lua\luci\model\cbi\admin_status\processes.lua來實現(xiàn)模塊。

Luci API的使用

官方文檔介紹:http://luci.subsignal.org/api/luci/

比如:luci.sys luci.sys.net等對應(yīng)的解析,由Luci源碼結(jié)構(gòu)中的/luci-0.11/libs/sys/luasrc/sys.lua完成。

OpenWrt的UCI系統(tǒng)

UCI是Unified Configuration Interface的縮寫,翻譯成中文就是統(tǒng)一配置接口,用途就是為OpenWrt提供一個集中控制的接口。OpenWrt實現(xiàn)的這個工具,能夠讓你的不管是Lua還是PHP程序,或者SHELL程序或C程序,只要執(zhí)行命令傳輸參數(shù)就能達到修改系統(tǒng)參數(shù)的目的,請參考本文后面的命令行實用工具。

UCI系統(tǒng)的優(yōu)勢

系統(tǒng)的配置應(yīng)該簡單直接,UCI的設(shè)計初衷即是這樣的,它是NVRAM-based配置方法的繼承者(基于NVRAM的配置方法起源于OpenWrt的White Russian系列,該版本目前不再更新,最后發(fā)布于2007年,版本號為0.9)。UCI可以視為OpenWrt系統(tǒng)功能設(shè)置的主要用戶配置接口,通常來說這些配置與系統(tǒng)的功能關(guān)聯(lián)性較大,想像一樣我們平常所使用的路由器或嵌入式設(shè)備中的WEB界面中的那些配置項,就是路由器或嵌入式設(shè)備系統(tǒng)所集成了的功能。常見的例子如路由器的網(wǎng)絡(luò)接口設(shè)置,無線參數(shù)設(shè)置,logging設(shè)置和遠程登錄設(shè)置等。

UCI統(tǒng)一標(biāo)準

UCI目前已經(jīng)支持有一小部分應(yīng)用程序,因而對這些應(yīng)用程序的控制會變得更加簡單一些。這些第三方應(yīng)用程序都會有自己的配置文件,不同的語法,不同的文件位置,如

/etc/network/interfaces
/etc/exports
/etc/dnsmasq.conf
或/etc/samba/samba.conf

由于UCI統(tǒng)一配置接口的出現(xiàn),對這些第三方應(yīng)用程序的配置只需要修改UCI的配置文件即可,就不必再去找不同的目錄,寫不同的語法了。當(dāng)然,你安裝的大多數(shù)第三方應(yīng)用程序都沒有提供UCI配置接口,很可能是因為這些應(yīng)用程序本身就不需要向普通用戶提供應(yīng)用程序接口,配置文件是給開發(fā)者使用的,從這個角度上來看,沒有提供UCI接口反而更好。因而,OpenWrt包維護人員只選定了一小部分必需的程序?qū)崿F(xiàn)了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))。

許多第三方程序是根據(jù)它自己對應(yīng)于/etc/config下的UCI配置文件的選項去設(shè)置程序的原始配置文件,這樣就實現(xiàn)了程序?qū)CI配置的兼容,然后執(zhí)行一次/etc/init.d腳本完成一次配置。因而當(dāng)你啟動一個某個程序的UCI兼容的進程腳本時,該腳本應(yīng)該不只是修改/etc/config下對應(yīng)的UCI配置文件,同時也應(yīng)該覆蓋程序自己的原配置文件。比如Samba/CIFS程序,其原配置文件是在/etc/samba/smb.conf,而對應(yīng)的UCI文件是/etc/config/samba,當(dāng)/etc/config/samba文件被修改了之后,需要運行一次

/etc/init.d/samba start

之后UCI文件中的設(shè)置才會更新到原配置文件中去。

UCI統(tǒng)一標(biāo)準方法
UCI統(tǒng)一標(biāo)準方法

除此之外,應(yīng)用程序的配置文件常常是存放在RAM而不是FLASH中,因為它不需要每次修改參數(shù)之后就去寫非易性閃存了,而只在應(yīng)用改變的時候它才會根據(jù)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不兼容的自帶程序,而其它的第三方程序,你得自己去查閱程序的說明了。

一般規(guī)則

UCI的配置文件被分割成/etc/config下的多個獨立的文件,各個文件按名字含義對應(yīng)系統(tǒng)的不同的功能配置。你可以通過文本編譯器或者uci實用程序去修改這些配置文件,同時uci還提供了C語言/腳本/Lua等語言的應(yīng)用程序接口,WEB配置頁面例如Luci就是利用了uci所提供的API而實現(xiàn)對UCI配置文件的修改的。

不管你是采用文本編輯器還是通過命令行的方式修改了UCI配置文件,相應(yīng)的服務(wù)或應(yīng)用程序不會自動更新狀態(tài),這時你都必須調(diào)用一次/etc/init.d (re)start才能使剛剛對UCI配置文件的修改生效。許多兼容UCI的程序采用這樣的方法來應(yīng)用更新:在init.d腳本執(zhí)行流中去修改自己程序的配置文件。具體說來,init.d腳本先去修改自己程序的原配置文件中的信息(如/etc/samba/smb.conf),之后重啟一次應(yīng)用程序,應(yīng)用程序就會去讀自己的配置文件(剛剛被init.d更新過的)再啟動,這樣應(yīng)用程序的狀態(tài)就更新了。僅僅重啟應(yīng)用程序,而不執(zhí)行init.d腳本的話,/etc/config下的UCI配置文件是不會應(yīng)用于應(yīng)用程序的,新配置也就不生效了。
舉個例子:

先登錄到路由器的WEB頁面把WiFi給禁用掉,這個時候你的手機搜索到你的路由器發(fā)送的SSID了,我這兒是NGTestRouter。


UCI統(tǒng)一標(biāo)準方法
UCI統(tǒng)一標(biāo)準方法

這時我們準備通過使用文本編輯器修改UCI再應(yīng)用的方法來現(xiàn)使能WiFi,步驟如下:

編輯wireless文件把disabled這個項注釋掉(也就是enable WiFi了)

#vi /etc/config/wireless

然后運行一次

#/etc/init.d/network restart

這時你的手機又可以看到NGTestRouter這個熱點了!


UCI統(tǒng)一標(biāo)準方法
UCI統(tǒng)一標(biāo)準方法

文件語法

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內(nèi)容;
option 'string' 'some value'option 'boolean' '1'兩個語句定義了段內(nèi)的兩個標(biāo)識符的值,雖然它們一個是string一個是boolean,但是在語法沒有任何區(qū)別。boolean后面可以跟'0', 'no', 'off', 'false'中的一個作為否的值,或者'1', 'yes', 'on', 'true'作為邏輯是的值;
后面兩行以list開頭的語句,是為某個有多種選項值的option所定義的,在同一option中的選項值,它們應(yīng)該有同樣的名字,在這里的名字為collection。最張這兩個值為收納到同一個list表中,表中出現(xiàn)的順序即你這里所定義的;
標(biāo)識符optionlist是為了更易讀而加上的,沒有它們也是可以的;
如果某個option沒有但它不是必須的,那么uci處理程序會假定一個默認值;如果該option是必須的,而文件中沒有定義,那么uci會報錯或者顯現(xiàn)出奇怪的結(jié)果;
語句中的標(biāo)識和值可不必使用引號引起,除非你的字段值含有空格或者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的文件名和標(biāo)識符(像option example value中的example即為標(biāo)識符,value為option的值)可以使用a-z, 0-9和下劃線_組合的任意字符串,不允許使用橫杠線-,而option的值可以傅任意字符(像空格這樣子的字段值需要用引號引起)。

命令行實用工具

修改配置的一種方法是直接去修改UCI配置文件。不過,UCI配置文件讀和寫操作都可以通過uci命令行實用工具來完成,因而如果你自己去寫一個腳本來解析或?qū)懭險CI配置文件不是一個明智的選擇,既浪費時間又不一定寫得好。以下介紹如何使用uci命令行實用工具,并伴有一些實例參考:

在學(xué)習(xí)該工具前需要注意:uci會把先讀到UCI文件,其中不認識的所有命令參數(shù)和注釋會被刪除!所以,像uhttpd這樣安裝地有詳細注釋的文件,在使用uci操作之后其中的注釋就會被抹掉的。OpenWrt的默認WEB界面Luci就是采用了uci來寫UCI文件!

Uci的使用

Uci命令的使用

Usage: uci [<options>] <command> [<arguments>]

Commands:
        batch
        export     [<config>]
        import     [<config>]
        changes    [<config>]
        commit     [<config>]
        add        <config> <section-type>
        add_list   <config>.<section>.<option>=<string>
        del_list   <config>.<section>.<option>=<string>
        show       [<config>[.<section>[.<option>]]]
        get        <config>.<section>[.<option>]
        set        <config>.<section>[.<option>]=<value>
        delete     <config>[.<section>[[.<option>][=<id>]]]
        rename     <config>.<section>[.<option>]=<name>
        revert     <config>[.<section>[.<option>]]
        reorder    <config>.<section>=<position>

Options:
        -c <path>  set the search path for config files (default: /etc/config)
        -d <str>   set the delimiter for list values in uci show
        -f <file>  use <file> as input instead of stdin
        -m         when importing, merge data into an existing package
        -n         name unnamed sections on export (default)
        -N         don't name unnamed sections
        -p <path>  add a search path for config change files
        -P <path>  add a search path for config change files and use as default
        -q         quiet mode (don't print error messages)
        -s         force strict mode (stop on parser errors, default)
        -S         disable strict mode
        -X         do not use extended syntax on 'show'

例子

設(shè)置一個值

#把uhttpd的監(jiān)聽端口從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:~#

#導(dǎo)出整體配置信息
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路徑
*假設(shè)有下面的UCI文件
*/etc/config/foo 
**/

config bar 'first' 
    option name 'Mr. First' 
config bar 
    option name 'Mr. Second' 
config bar 'third' 
    option name 'Mr. Third'
那么下面三組路徑的執(zhí)行得到的值分別各自相等

# 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
執(zhí)行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

添加防火墻規(guī)則
這個例子不僅演示了如何添加TCP SSH防火墻規(guī)則,同時也演示uci的negative (-1)語法。

Uci c API的使用

在腳本中使用uci config文件:http://wiki.openwrt.org/doc/devel/config-scripting

總結(jié)一下Luci、Lua、Uci、CBI的關(guān)系圖,如下圖:


luci關(guān)系圖
luci關(guān)系圖

以上為最近研究Luci開發(fā)的相關(guān)資料整理,同時自己也動手做了幾個測試頁面并通過luci.sys.call實現(xiàn)了腳本、系統(tǒng)程序的調(diào)用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容