開發(fā)OpenWrt路由器上LuCI的模塊【題外話】 學校里最近改造了校園網(wǎng),要求必須用iNode驗證,萬幸的是路由器能刷OpenWrt,并且OpenWrt上有好多iNode認證的開源項目,比如njit8021xclient(以下簡稱njit-client)就非常好用。雖然程序?qū)懙暮糜茫桥渲闷饋磉€是稍微麻煩一些的,大家通常的方法是在/etc/init.d下寫啟動腳本,把用戶名、密碼什么的都直接填進去,但畢竟配置起來不方便,同時日后修改起來也不便。好在用Lua為LuCI寫配置模塊很簡單,索性就自己做了一個,現(xiàn)在把開發(fā)的流程寫一下,方便初學的同學去做。為njit-client做好的Web配置界面也已經(jīng)開源,地址:https://github.com/mayswind/luci-app-njitclient,或者直接下載編譯好(不限平臺)的文件:http://pan.baidu.com/s/1CbPal
【文章索引】
LuCI是OpenWrt上的Web管理界面,LuCI采用了MVC三層架構(gòu),同時其使用Lua腳本開發(fā),所以開發(fā)LuCI的配置界面不需要編輯任何的Html代碼,除非想自己單獨去創(chuàng)建網(wǎng)頁(View層),否則我們基本上只需要修改Model層就可以了。官方也有一個如何去創(chuàng)建模塊的說明文檔,雖然寫的比較晦澀:http://luci./trac/wiki/Documentation/ModulesHowTo 要為LuCI增加一個新模塊,首先需要創(chuàng)建兩個文件,一個位于Controller(/usr/lib/lua/luci/controller/)下,定義模塊的入口;另一個位于Model(/usr/lib/lua/luci/model/cbi/)下,為配置模塊實際的代碼。 首先我們定義模塊的入口,在/usr/lib/lua/luci/controller/下創(chuàng)建一個lua文件,類似如下: module("luci.controller.控制器名", package.seeall) function index() entry(路徑, 調(diào)用目標, _("顯示名稱"), 顯示順序) end 第一行說明了程序和模塊的名稱,比如在controller/目錄創(chuàng)建一個mymodule.lua,那么就可以寫成“l(fā)uci.controller.mymodule”,如果你的程序比較多,可能分為好幾個模塊,那么可以在controller下再常見一個子目錄,比如controller/myapp/,那么就可以寫成“l(fā)uci.controller.myapp.mymodule”。 接下來的entry表示添加一個新的模塊入口,官方給出了entry的定義,其中后兩項都是可以為空的: entry(path, target, title=nil, order=nil) 第一項是訪問的路徑,不過路徑是按字符串數(shù)組給定的,比如路徑按如下方式寫“{"click", "here", "now"}”,那么就可以在瀏覽器里訪問“http://192.168.1.1/cgi-bin/luci/click/here/now”來訪問這個腳本。而通常我們希望為管理員菜單添加腳本,那么我們需要按如下方式編寫“{"admin", "一級菜單名", "菜單項名"}”,系統(tǒng)會自動在對應的菜單中生成菜單項。比如想在“網(wǎng)絡(luò)”菜單下創(chuàng)建一個菜單項,那么一級菜單名可以寫為“network”。 第二項為調(diào)用目標,調(diào)用目標分為三種,分別是執(zhí)行指定方法(Action)、訪問指定頁面(Views)以及調(diào)用CBI Module。
而title和order無非是針對管理員菜單來的,可以參考其他的lua文件來決定編寫的內(nèi)容。 這里我們創(chuàng)建/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
我們要做的實際上就是希望能將用戶名、密碼等信息存儲在路由器文件中,同時路由器開機時能根據(jù)設(shè)定的配置自動運行njit-client,同時我們還希望能動態(tài)的禁用和啟用njit-client等等。所以最方便的方式就是使用CBI Module,上一節(jié)我們也添加了這個調(diào)用,那么接下來我們就要根據(jù)上邊寫的路徑來創(chuàng)建/usr/lib/lua/luci/model/cbi/njitclient.lua文件。 開發(fā)LuCI的配置模塊有很多種方式,比較基本的可以用SimpleForm,就跟開發(fā)普通的Web應用類似,當然最方便的還是使用UCI(Unified Configuration Interface,統(tǒng)一配置接口)的方式,因為使用UCI接口可以使得在LuCI中可以無需考慮配置文件如何存儲和讀?。ㄟ@種方式也會自動創(chuàng)建“保存&應用”、“保存”以及“復位”三個按鈕),同時在Bash文件中也可以非常方便的存儲和讀取。 對于使用UCI的方式,我們首先需要創(chuàng)建對應的配置文件(如果配置文件不存在的話,訪問配置頁面將會報錯),格式即為linux配置文件的格式,文件需要存儲在/etc/config,比如文件路徑為“/etc/config/njitclient”,內(nèi)容如下: config login option username '' option password '' option ifname 'eth0' option domain '' 然后我們要在CBI Module的lua文件中首先需要映射與存儲文件的關(guān)系,比如: m = Map("配置文件文件名", "配置頁面標題", "配置頁面說明") 第一個參數(shù)即為配置文件存儲的文件名,不包含路徑,比如按上述創(chuàng)建的話,應該寫為“njitclient”,而第二與第三個參數(shù)則是用在來頁面上顯示的,比如如下所示的圖:
接下來需要創(chuàng)建與配置文件中對應的Section,Section分為兩種,NamedSection和TypedSection,前者根據(jù)配置文件中的Section名,而后者根據(jù)配置文件中的Section類型,這里我們使用后者,代碼如下。同時我們設(shè)定不允許增加或刪除Section(“.addremove = false”),以及不顯示Section的名稱(“.anonymous = true”)。 s = m:section(TypedSection, "login", "") s.addremove = false s.anonymous = true 接下來我們需要創(chuàng)建Section中不同內(nèi)容的交互(創(chuàng)建Option),常見的比如有Value(文本框)、ListValue(下拉框)、Flag(選擇框)等等,詳細的可以參考官方的文檔:http://luci./trac/wiki/Documentation/CBI 創(chuàng)建Option的過程非常簡單,而且創(chuàng)建后系統(tǒng)會無需考慮讀取以及寫入配置文件的問題,系統(tǒng)都會自動處理。但是根據(jù)上述的要求,我們在應用配置以后可能希望啟用、禁用或重新啟動njit-client,所以我們還需要在頁面最后判斷用戶是否點擊了“應用”按鈕,這里與編寫asp網(wǎng)頁等都是相同的,我們可以通過如下的代碼判斷是否點擊了“應用”按鈕: 由于剩余的代碼都非常簡單,所以所以這部分的全部代碼見下: 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全部類庫的函數(shù)定義和使用說明可以參考如下地址:http://luci./api/luci/index.html
上邊我們已經(jīng)完成了LuCI配置界面的開發(fā),在配置界面中我們已經(jīng)能讀取并保存配置文件了。接下來我們要編寫/etc/init.d/njitclient腳本,使程序最終能運行起來。關(guān)于UCI接口在腳本文件中的官方說明可以參考:http://wiki./doc/devel/config-scripting 要使用UCI調(diào)用腳本,首先第一步需要讀取配置文件,命令為“config_load 配置文件名”,比如我們可以這樣讀入剛才的配置文件: config_load njitclient 接下來要遍歷配置文件中的Section,可以使用“config_foreach 遍歷函數(shù)名 Section類型”,例如我們可以這樣: config_foreach run_njit login然后我們?nèi)ゾ帉懨麨椤皉un_njit”的函數(shù),在這個函數(shù)中,我們可以使用“config_get 變量名 Section名 Section參數(shù)名”獲取變量的值,或者使用“config_get_bool 變量名 Section名 Section參數(shù)名”獲取布爾型的值。所以全部的代碼見下: 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 }
如果按上述內(nèi)容創(chuàng)建好上述4個文件,那么配置頁面和程序就能在OpenWrt上運行起來了。但是如果要想把自己寫的程序打包,還需要創(chuàng)建OpenWrt的Makefile來使用OpenWrt的SDK進行編譯。 關(guān)于LuCI上配置Makefile的官方說明可以見這個地址:http://luci./trac/wiki/Documentation/Modules 無非就是定義包的名稱(PKG_NAME)、版本和生成次數(shù)(PKG_VERSION、PKG_RELEASE)、在menuconfig中的分類說明等(define Package/luci-app-njitclient)以及安裝時進行的操作(define Package/luci-app-njitclient/install)等等。其中安裝的文件分為三種,分別是配置文件、可執(zhí)行文件以及其他數(shù)據(jù)文件,其中配置可執(zhí)行文件時,會自動加入執(zhí)行權(quán)限的,所以不需要額外進行處理。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目錄下創(chuàng)建一個文件夾,如njitclient,然后將所有的文件按目錄復制到該目錄下即可。之后配置好OpenWrt的交叉編譯環(huán)境后就可以使用OpenWrt SDK進行編譯了,由于這類文章較多,故不再贅述,可以參考相關(guān)鏈接3及之后的文章。
【相關(guān)鏈接】
|
|
|