OpenWrt新增LuCI的模組
【一、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做好的Web配置介面也已經開源,地址:https://github.com/mayswind/luci-app-njitclient.
我們要做的實際上就是希望能將使用者名稱、密碼等資訊儲存在路由器檔案中,同時路由器開機時能根據設定的配置自動執行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
由於剩餘的程式碼都非常簡單,所以所以這部分的全部程式碼見下:
require("luci.sys")
m = Map("njitclient", translate("NJIT Client"), translate("Configure NJIT 802.11x client."))
s = m:section(TypedSection, "login", "")
s.addremove = false
s.anonymous = true
enable = s:option(Flag, "enable", translate("Enable"))
name = s:option(Value, "username", translate("Username"))
pass = s:option(Value, "password", translate("Password"))
pass.password = true
domain = s:option(Value, "domain", translate("Domain"))
ifname = s:option(ListValue, "ifname", translate("Interfaces"))
for k, v in ipairs(luci.sys.net.devices()) do
if v ~= "lo" then
ifname:value(v)
end
end
local apply = luci.http.formvalue("cbi.apply")
if apply then
io.popen("/etc/init.d/njitclient restart")
end
return m
【三、在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引數名”獲取布林型的值。所以全部的程式碼見下:
#!/bin/sh /etc/rc.common
START=50
run_njit()
{
local enable
config_get_bool enable $1 enable
if [ $enable ]; then
local username
local password
local domain
local ifname
config_get username $1 username
config_get password $1 password
config_get domain $1 domain
config_get ifname $1 ifname
if [ "$domain" != "" ]; then
njit-client $username@$domain $password $ifname &
else
njit-client $username $password $ifname &
fi
echo "NJIT Client has started."
fi
}
start()
{
config_load njitclient
config_foreach run_njit login
}
stop()
{
killall njit-client
killall udhcpc
echo "NJIT Client has stoped."
}
【四、 編譯開發的程式 】
如果按上述內容建立好上述4個檔案,那麼配置頁面和程式就能在OpenWrt上執行起來了。但是如果要想把自己寫的程式打包,還需要建立OpenWrt的Makefile來使用OpenWrt的SDK進行編譯。
無非就是定義包的名稱(PKG_NAME)、版本和生成次數(PKG_VERSION、PKG_RELEASE)、在menuconfig中的分類說明等(define Package/luci-app-njitclient)以及安裝時進行的操作(define Package/luci-app-njitclient/install)等等。其中安裝的檔案分為三種,分別是配置檔案、可執行檔案以及其他資料檔案,其中配置可執行檔案時,會自動加入執行許可權的,所以不需要額外進行處理。Makefile全部的程式碼見下:
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-njitclient
PKG_VERSION=1.0
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/luci-app-njitclient
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
TITLE:=NJIT 802.1X Client for LuCI
PKGARCH:=all
endef
define Package/luci-app-njitclient/description
This package contains LuCI configuration pages for njit8021xclient.
endef
define Build/Prepare
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/luci-app-njitclient/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
$(INSTALL_CONF) ./files/root/etc/config/njitclient $(1)/etc/config/njitclient
$(INSTALL_BIN) ./files/root/etc/init.d/njitclient $(1)/etc/init.d/njitclient
$(INSTALL_DATA) ./files/root/usr/lib/lua/luci/model/cbi/njitclient.lua $(1)/usr/lib/lua/luci/model/cbi/njitclient.lua
$(INSTALL_DATA) ./files/root/usr/lib/lua/luci/controller/njitclient.lua $(1)/usr/lib/lua/luci/controller/njitclient.lua
endef
$(eval $(call BuildPackage,luci-app-njitclient))
接下來在編譯目錄下的package目錄下建立一個資料夾,如njitclient,然後將所有的檔案按目錄複製到該目錄下即可。之後配置好OpenWrt的交叉編譯環境後就可以使用OpenWrt SDK進行編譯了,由於這類文章較多,故不再贅述,可以參考相關連結3及之後的文章。