Openwrt開發與Luci介紹
分享到 評論
- LUCI
- Openwrt
- 開發
文章目錄
- 1. Luci介紹
- 1.1. Luci 的啟動—uhttpd
- 1.2. Luci 的啟動—luci
- 1.3. Luci— Web
- 1.4. 以status子產品為例進行說明
- 1.5. entry()函數
- 1.6. target主要分為三類:call,template 和cbi。
- 1.6.1. call用來調用函數。即語句
- 1.6.2. template調用
- 1.7. CBI調用
- 1.8. Luci API的使用
- 2. OpenWrt的UCI系統
- 2.1. UCI系統的優勢
- 2.2. UCI統一标準
- 2.3. 一般規則
- 2.4. 檔案文法
- 2.5. 指令行實用工具
- 2.6. Uci的使用
- 2.6.1. 例子
- 2.7. Uci c API的使用
摘要: Lua作為一門友善嵌入(其它應用程式)并可擴充的輕量級腳本語言來設計的,是以她一直遵從着簡單、小巧、可移植、快速的原則,官方實作完全采用ANSI C編寫,能以C程式庫的形式嵌入到宿主程式中。Lua的每個版本都保持着開放源碼的傳統,不過各版采用的許可協定并不相同,自5.0版(最新版是5.1)開始她采用的是著名的MIT許可協定。正由于上述特點,是以Lua在遊戲開發、機器人控制、分布式應用、圖像處理、生物資訊學等各種各樣的領域中得到了越來越廣泛的應用。
Luci介紹
Luci是 Lua ConfigurationInterface的簡稱,意在OpenWrt整個系統的配置集中化。見連結:
http://wiki.openwrt.org/zh-cn/doc/uci
Luci 的啟動—uhttpd
uhttpd是一個簡單的web伺服器程式,主要就是cgi的處理,openwrt是利用uhttpd作為web伺服器,實作用戶端web頁面配置功能。對于request處理方式,采用的是cgi,而所用的cgi程式就是luci。
Luci 的啟動—luci
在web server中的cgi-bin目錄下,運作 luci 檔案(權限一般是 755 ),luci的代碼如下:
#!/usr/bin/lua --cgi的執行指令的路徑
require"luci.cacheloader" --導入cacheloader包
require"luci.sgi.cgi" --導入sgi.cgi包
luci.dispatcher.indexcache = "/tmp/luci-indexcache" --cache緩存路徑位址
luci.sgi.cgi.run() --執行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()函數中,使用entry函數來完成每個子產品函數的注冊:
entry(path, target, title=nil, order=nil)
entry()函數
第一個參數是定義菜單的顯示(Virtual path)。
第二個參數定義相應的處理方式(target)。
alias是指向别的entry的别名,from調用的某一個view,cbi調用某一個model,call直接調用函數。
第三個參數是菜單的文本,_(“string”),國際化。
第四個參數是是同級菜單下,此菜單項的位置,從大到小
target主要分為三類: call,template 和cbi
。
call,template 和cbi
call用來調用函數。即語句
| |
template調用
template用來調用已有的htm模版,模版目錄在lua\luci\view目錄下。即語句
| |
CBI調用
a. CBI了解 –- Configuration Binding Interface
CBI模型是Lua檔案描述UCI配置檔案的結構和由此産生的HTML表單來評估CBI解析器,所有CBI luci.cbi.Map類型的模型檔案必須傳回一個map對象,在cbi子產品中定義各種控件,Luci系統會自動執行大部分處理工作。其連結目錄在
lua\luci\model\cbi
下
| |
Luci API的使用
官方文檔介紹:http://luci.subsignal.org/api/luci/
比如:luci.sys luci.sys.net等對應的解析,由Luci源碼結構中的
/luci-0.11/libs/sys/luasrc/sys.lua
完成。
OpenWrt的UCI系統
UCI是Unified Configuration Interface的縮寫,翻譯成中文就是統一配置接口,用途就是為OpenWrt提供一個集中控制的接口。OpenWrt實作的這個工具,能夠讓你的不管是Lua還是PHP程式,或者SHELL程式或C程式,隻要執行指令傳輸參數就能達到修改系統參數的目的,請參考本文後面的指令行實用工具。
UCI系統的優勢
系統的配置應該簡單直接,UCI的設計初衷即是這樣的,它是NVRAM-based配置方法的繼承者(基于NVRAM的配置方法起源于OpenWrt的White Russian系列,該版本目前不再更新,最後釋出于2007年,版本号為0.9)。UCI可以視為OpenWrt系統功能設定的主要使用者配置接口,通常來說這些配置與系統的功能關聯性較大,想像一樣我們平常所使用的路由器或嵌入式裝置中的WEB界面中的那些配置項,就是路由器或嵌入式裝置系統所內建了的功能。常見的例子如路由器的網絡接口設定,無線參數設定,logging設定和遠端登入設定等。
UCI統一标準
UCI目前已經支援有一小部分應用程式,因而對這些應用程式的控制會變得更加簡單一些。這些第三方應用程式都會有自己的配置檔案,不同的文法,不同的檔案位置,如
| |
由于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檔案被修改了之後,需要運作一次
| |
之後UCI檔案中的設定才會更新到原配置檔案中去。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuETSDV1Lc12bj5ibkVXaulWcuMncvR3bs1SZyVHdjlGcvw1LcpDc0RHaiojIsJye.png)
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統一标準方法
這時我們準備通過使用文本編輯器修改UCI再應用的方法來現使能WiFi,步驟如下:
編輯wireless檔案把disabled這個項注釋掉(也就是enable WiFi了)
| |
然後運作一次
| |
這時你的手機又可以看到NGTestRouter這個熱點了!
UCI統一标準方法
檔案文法
uci配置檔案通常包含有一個或多個語句。所謂段(section),包含有一個或多個option語句,這些語句定義了實際的值。
下面是一個簡單的配置檔案:
| |
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鍵。如果使用引号,那你可以随意使用單引号或者雙引号。比如這樣子:
| |
不過不能這樣子(引号混用,字段中有空格但未用引号引起來):
| |
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檔案!
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'
例子
設定一個值
#把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 [email protected][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 [email protected][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)文法。
Uci c API的使用
在腳本中使用uci config檔案:http://wiki.openwrt.org/doc/devel/config-scripting
總結一下Luci、Lua、Uci、CBI的關系圖,如下圖:
luci關系圖
以上為最近研究Luci開發的相關資料整理,同時自己也動手做了幾個測試頁面并通過luci.sys.call實作了腳本、系統程式的調用。