1. 程式人生 > >OpenWrt新增LuCI的模組

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”,而第二與第三個引數則是用在來頁面上顯示的,比如如下所示的圖:
1.jpg

接下來需要建立與配置檔案中對應的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及之後的文章。