開發OpenWrt路由器上LuCI的模塊-參考

作者:大魔王mAysWINd

原文:http://www.cnblogs.com/mayswind/p/3468124.html?utm_source=tuicool&utm_medium=referral


【題外話】

學校裏最近改造了校園網,要求必須用iNode驗證,萬幸的是路由器能刷OpenWrt,並且OpenWrt上有好多iNode認證的開源項目,比如njit8021xclient(以下簡稱njit-client)就非常好用。雖然程序寫的好用,但是配置起來還是稍微麻煩一些的,大家通常的方法是在/etc/init.d下寫啓動腳本,把用戶名、密碼什麼的都直接填進去,但畢竟配置起來不方便,同時日後修改起來也不便。好在用Lua爲LuCI寫配置模塊很簡單,索性就自己做了一個,現在把開發的流程寫一下,方便初學的同學去做。爲njit-client做好的Web配置界面也已經開源,地址:https://github.com/mayswind/luci-app-njitclient,或者直接下載編譯好(不限平臺)的文件:http://pan.baidu.com/s/1CbPal

 

【文章索引】

  1. LuCI配置界面開發的框架
  2. 用Lua和UCI接口開發LuCI配置模塊
  3. 在Bash文件中調用UCI接口
  4. 編譯開發的程序

 

【一、LuCI配置界面開發的框架】

LuCI是OpenWrt上的Web管理界面,LuCI採用了MVC三層架構,同時其使用Lua腳本開發,所以開發LuCI的配置界面不需要編輯任何的Html代碼,除非想自己單獨去創建網頁(View層),否則我們基本上只需要修改Model層就可以了。官方也有一個如何去創建模塊的說明文檔,雖然寫的比較晦澀:http://luci.subsignal.org/trac/wiki/Documentation/ModulesHowTo

要爲LuCI增加一個新模塊,首先需要創建兩個文件,一個位於Controller(/usr/lib/lua/luci/controller/)下,定義模塊的入口;另一個位於Model(/usr/lib/lua/luci/model/cbi/)下,爲配置模塊實際的代碼。

首先我們定義模塊的入口,在/usr/lib/lua/luci/controller/下創建一個lua文件,類似如下:

module("luci.controller.控制器名", package.seeall)

function index()
        entry(路徑, 調用目標, _("顯示名稱"), 顯示順序)
        end

第一行說明了程序和模塊的名稱,比如在controller/目錄創建一個mymodule.lua,那麼就可以寫成“luci.controller.mymodule”,如果你的程序比較多,可能分爲好幾個模塊,那麼可以在controller下再常見一個子目錄,比如controller/myapp/,那麼就可以寫成“luci.controller.myapp.mymodule”。

接下來的entry表示添加一個新的模塊入口,官方給出了entry的定義,其中後兩項都是可以爲空的:

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

第一項是訪問的路徑,不過路徑是按字符串數組給定的,比如路徑按如下方式寫“{"click", "here", "now"}”,那麼就可以在瀏覽器裏訪問“http://192.168.1.1/cgi-bin/luci/click/here/now”來訪問這個腳本。而通常我們希望爲管理員菜單添加腳本,那麼我們需要按如下方式編寫“{"admin", "一級菜單名", "菜單項名"}”,系統會自動在對應的菜單中生成菜單項。比如想在“網絡”菜單下創建一個菜單項,那麼一級菜單名可以寫爲“network”。

第二項爲調用目標,調用目標分爲三種,分別是執行指定方法(Action)、訪問指定頁面(Views)以及調用CBI Module。

  • 第一種可以直接調用指定的函數,比如點擊菜單項就直接重啓路由器等等,比如寫爲“call("function_name")”,然後在lua文件下編寫名爲function_name的函數就可以調用了。
  • 第二種可以訪問指定的頁面,比如寫爲“template("myapp/mymodule")”就可以調用/usr/lib/lua/luci/view/myapp/mymodule.htm文件了。
  • 而如果要編寫配置頁面,那麼使用第三種方法無非是最方便的,比如寫爲“cbi("myapp/mymodule")”就可以調用/usr/lib/lua/luci/model/cbi/myapp/mymodule.lua文件了。

而title和order無非是針對管理員菜單來的,可以參考其他的lua文件來決定編寫的內容。

這裏我們創建/usr/lib/lua/luci/controller/njitclient.lua文件,定義我們的入口,代碼如下:

module("luci.controller.njitclient", package.seeall)

function index()
        entry({"admin", "network", "njitclient"}, cbi("njitclient"), _("NJIT Client"), 100)
        end

 

【二、用Lua和UCI接口開發LuCI配置模塊

我們要做的實際上就是希望能將用戶名、密碼等信息存儲在路由器文件中,同時路由器開機時能根據設定的配置自動運行njit-client,同時我們還希望能動態的禁用和啓用njit-client等等。所以最方便的方式就是使用CBI Module,上一節我們也添加了這個調用,那麼接下來我們就要根據上邊寫的路徑來創建/usr/lib/lua/luci/model/cbi/njitclient.lua文件。

開發LuCI的配置模塊有很多種方式,比較基本的可以用SimpleForm,就跟開發普通的Web應用類似,當然最方便的還是使用UCI(Unified Configuration Interface,統一配置接口)的方式,因爲使用UCI接口可以使得在LuCI中可以無需考慮配置文件如何存儲和讀取(這種方式也會自動創建“保存&應用”、“保存”以及“復位”三個按鈕),同時在Bash文件中也可以非常方便的存儲和讀取。

對於使用UCI的方式,我們首先需要創建對應的配置文件(如果配置文件不存在的話,訪問配置頁面將會報錯),格式即爲linux配置文件的格式,文件需要存儲在/etc/config,比如文件路徑爲“/etc/config/njitclient”,內容如下:

config login
    option username ''
    option password ''
    option ifname 'eth0'
    option domain ''

然後我們要在CBI Module的lua文件中首先需要映射與存儲文件的關係,比如:

m = Map("配置文件文件名", "配置頁面標題", "配置頁面說明")

第一個參數即爲配置文件存儲的文件名,不包含路徑,比如按上述創建的話,應該寫爲“njitclient”,而第二與第三個參數則是用在來頁面上顯示的,比如如下所示的圖:

接下來需要創建與配置文件中對應的Section,Section分爲兩種,NamedSection和TypedSection,前者根據配置文件中的Section名,而後者根據配置文件中的Section類型,這裏我們使用後者,代碼如下。同時我們設定不允許增加或刪除Section(“.addremove = false”),以及不顯示Section的名稱(“.anonymous = true”)。

s = m:section(TypedSection, "login", "")
s.addremove = false
s.anonymous = true

接下來我們需要創建Section中不同內容的交互(創建Option),常見的比如有Value(文本框)、ListValue(下拉框)、Flag(選擇框)等等,詳細的可以參考官方的文檔:http://luci.subsignal.org/trac/wiki/Documentation/CBI

創建Option的過程非常簡單,而且創建後系統會無需考慮讀取以及寫入配置文件的問題,系統都會自動處理。但是根據上述的要求,我們在應用配置以後可能希望啓用、禁用或重新啓動njit-client,所以我們還需要在頁面最後判斷用戶是否點擊了“應用”按鈕,這裏與編寫asp網頁等都是相同的,我們可以通過如下的代碼判斷是否點擊了“應用”按鈕:

複製代碼
local apply = luci.http.formvalue("cbi.apply")
if apply then
    --[[
        需要處理的代碼
    ]]--
end
複製代碼

由於剩餘的代碼都非常簡單,所以所以這部分的全部代碼見下:

複製代碼
 1 require("luci.sys")
 2 
 3 m = Map("njitclient", translate("NJIT Client"), translate("Configure NJIT 802.11x client."))
 4 
 5 s = m:section(TypedSection, "login", "")
 6 s.addremove = false
 7 s.anonymous = true
 8 
 9 enable = s:option(Flag, "enable", translate("Enable"))
10 name = s:option(Value, "username", translate("Username"))
11 pass = s:option(Value, "password", translate("Password"))
12 pass.password = true
13 domain = s:option(Value, "domain", translate("Domain"))
14 
15 ifname = s:option(ListValue, "ifname", translate("Interfaces"))
16 for k, v in ipairs(luci.sys.net.devices()) do
17     if v ~= "lo" then
18         ifname:value(v)
19     end
20 end
21 
22 local apply = luci.http.formvalue("cbi.apply")
23 if apply then
24     io.popen("/etc/init.d/njitclient restart")
25 end
26 
27 return m
複製代碼

其中Luci全部類庫的函數定義和使用說明可以參考如下地址:http://luci.subsignal.org/api/luci/index.html

 

【三、在Bash文件中調用UCI接口】

上邊我們已經完成了LuCI配置界面的開發,在配置界面中我們已經能讀取並保存配置文件了。接下來我們要編寫/etc/init.d/njitclient腳本,使程序最終能運行起來。關於UCI接口在腳本文件中的官方說明可以參考:http://wiki.openwrt.org/doc/devel/config-scripting

要使用UCI調用腳本,首先第一步需要讀取配置文件,命令爲“config_load 配置文件名”,比如我們可以這樣讀入剛纔的配置文件:

config_load njitclient

接下來要遍歷配置文件中的Section,可以使用“config_foreach 遍歷函數名 Section類型”,例如我們可以這樣:

config_foreach run_njit login

然後我們去編寫名爲“run_njit”的函數,在這個函數中,我們可以使用“config_get 變量名 Section名 Section參數名”獲取變量的值,或者使用“config_get_bool 變量名 Section名 Section參數名”獲取布爾型的值。所以全部的代碼見下:

複製代碼
 1 #!/bin/sh /etc/rc.common
 2 START=50
 3 
 4 run_njit()
 5 {
 6     local enable
 7     config_get_bool enable $1 enable
 8     
 9     if [ $enable ]; then
10         local username
11         local password
12         local domain
13         local ifname
14         
15         config_get username $1 username
16         config_get password $1 password
17         config_get domain $1 domain
18         config_get ifname $1 ifname
19         
20         if [ "$domain" != "" ]; then
21             njit-client $username@$domain $password $ifname &
22         else
23             njit-client $username $password $ifname &
24         fi
25         
26         echo "NJIT Client has started."
27     fi
28 }
29 
30 start()
31 {
32     config_load njitclient
33     config_foreach run_njit login
34 }
35 
36 stop()
37 {
38     killall njit-client
39     killall udhcpc
40     
41     echo "NJIT Client has stoped."
42 }
複製代碼

 

【四、編譯開發的程序

如果按上述內容創建好上述4個文件,那麼配置頁面和程序就能在OpenWrt上運行起來了。但是如果要想把自己寫的程序打包,還需要創建OpenWrt的Makefile來使用OpenWrt的SDK進行編譯。

關於LuCI上配置Makefile的官方說明可以見這個地址:http://luci.subsignal.org/trac/wiki/Documentation/Modules

無非就是定義包的名稱(PKG_NAME)、版本和生成次數(PKG_VERSION、PKG_RELEASE)、在menuconfig中的分類說明等(define Package/luci-app-njitclient)以及安裝時進行的操作(define Package/luci-app-njitclient/install)等等。其中安裝的文件分爲三種,分別是配置文件、可執行文件以及其他數據文件,其中配置可執行文件時,會自動加入執行權限的,所以不需要額外進行處理。Makefile全部的代碼見下:

複製代碼
 1 include $(TOPDIR)/rules.mk
 2 
 3 PKG_NAME:=luci-app-njitclient
 4 PKG_VERSION=1.0
 5 PKG_RELEASE:=1
 6 
 7 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
 8 
 9 include $(INCLUDE_DIR)/package.mk
10 
11 define Package/luci-app-njitclient
12     SECTION:=luci
13     CATEGORY:=LuCI
14     SUBMENU:=3. Applications
15     TITLE:=NJIT 802.1X Client for LuCI
16     PKGARCH:=all
17 endef
18 
19 define Package/luci-app-njitclient/description
20     This package contains LuCI configuration pages for njit8021xclient.
21 endef
22 
23 define Build/Prepare
24 endef
25 
26 define Build/Configure
27 endef
28 
29 define Build/Compile
30 endef
31 
32 define Package/luci-app-njitclient/install
33     $(INSTALL_DIR) $(1)/etc/config
34     $(INSTALL_DIR) $(1)/etc/init.d
35     $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
36     $(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
37     
38     $(INSTALL_CONF) ./files/root/etc/config/njitclient $(1)/etc/config/njitclient
39     $(INSTALL_BIN) ./files/root/etc/init.d/njitclient $(1)/etc/init.d/njitclient
40     $(INSTALL_DATA) ./files/root/usr/lib/lua/luci/model/cbi/njitclient.lua $(1)/usr/lib/lua/luci/model/cbi/njitclient.lua
41     $(INSTALL_DATA) ./files/root/usr/lib/lua/luci/controller/njitclient.lua $(1)/usr/lib/lua/luci/controller/njitclient.lua
42 endef
43 
44 $(eval $(call BuildPackage,luci-app-njitclient))
複製代碼

接下來在編譯目錄下的package目錄下創建一個文件夾,如njitclient,然後將所有的文件按目錄複製到該目錄下即可。之後配置好OpenWrt的交叉編譯環境後就可以使用OpenWrt SDK進行編譯了,由於這類文章較多,故不再贅述,可以參考相關鏈接3及之後的文章。

 

【相關鏈接】

  1. LuCI:http://luci.subsignal.org/trac/wiki
  2. LuCI界面修改實現802.1x配置界面:http://chaochaoblog.com/archives/359
  3. 【詳細教程】編譯openwrt + njit-client 1.3 通過iNode認證:http://www.7forz.com/1973/
  4. openwrt SDK, 利用SDK生成自己的ipk安裝包:http://blog.chinaunix.net/uid-27194309-id-3432651.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章