轉自:http://blog.chinaunix.net/uid-23780428-id-4367414.html
1. 頁面請求:
1.1. 代碼結構
在openwrt文件系統中,lua語言的代碼不要編譯,類似一種腳本語言被執行,還有一些uhttpd服務器的主目錄,它們是:
/www/index.html
cgi-bin/luci
luci-static/xxx/xx.css、js、gif
/usr/lib/lua/nixio.so、uci.so
luci/http.lua、dispatcher.lua、core…
controller/xxx.lua
model/xxx.lua
view/xxx.lua
1.2. 界面顯示
網頁請求格式基本都如下所示:http://10.10.82.238/cgi-bin/luci,說明處理都在服務器的默認網站下的/cgi-bin/luci文件進行處理。
1.2.1. /www/cgi-bin/luci
luci.dispatcher.indexcache = "/tmp/luci-indexcache"--緩存文件位置“/tmp/luci-indexcache”
luci.sgi.cgi.run()--cgi程序接下來執行程序,Luci的默認路徑是/usr/lib/lua/luci,所以luci.sgi.cgi.run()是運行/usr/lib/lua/luci/sgi/cgi.lua文件中的run函數。
1.2.2. /usr/lib/lua/luci/sgi/cgi.lua
local r = luci.http.Request(…)--把web請求放於r中(包括環境變量,web請求,出錯處理接口)
local x = coroutine.create(luci.dispatcher.httpdispatch)--創建一個協同程序
local res, id, data1, data2 = coroutine.resume(x, r)--運行上面創建的協同程序,即運行httpdispatch,參數為上面local r里的變量。
if active then
if id == 1 then
io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\r\n")
elseif id == 2 then
hcache = hcache .. data1 .. ": " .. data2 .. "\r\n"—准備header
elseif id == 3 then--寫header、blank
io.write(hcache)—默認到stdout
io.write("\r\n")
elseif id == 4 then
io.write(tostring(data1 or ""))--寫body
elseif id == 5 then
io.flush()
io.close()--EOF
active = false
elseif id == 6 then
data1:copyz(nixio.stdout, data2)
data1:close()
1.2.3. /usr/lib/lua/luci/dispatcher.lua
httpdispatch:解析請求,獲得請求節點,並調用dispatch處理請求節點,如:
Request :http://10.10.82.238/cgi-bin/luci/;stok=e10fa5c70fbb55d478eb8b8a2eaabc6f/admin/network/firewall/ get: admin network firewall
dispatch:四個部分處理請求
A.節點樹node-tree創立
if not c then
c = createtree()
B.需要顯示的部分
if (c and c.index) or not track.notemplate then
C.認證
if track.sysauth then
D.顯示/處理
ok, err = util.copcall(target, c.target, unpack(args))
1.2.4. 請求頁面network
http://10.10.82.238/cgi-bin/luci/;stok=4b77c83a89c7b9cd8f4dcc0fcbc28024/admin/network/
1.2.3中D顯示部分與entry()函數(形如entry(path,target,title,order))有關,其中定義的target方法或者target部分。在以上http請求中會根據請求路徑去訪問到/usr/lib/lua/luci/controller/admin/network.lua,調用順序如下:
ok, err = util.copcall(target, unpack(args))-- dispatcher.luaà
page.target = firstchild() -- network.luaà
function firstchild()-- dispatcher.luaà
_firstchild()-- dispatcher.luaàdispatch(path)-- 自動鏈接到它的第一個子節點,
在network.lua中定義order,Interfaces是10,為第一個子節點:
page = entry({"admin", "network", "network"}, arcombine(cbi("admin_network/network"), cbi("admin_network/ifaces")), _("Interfaces"), 10)--通過cbi方法處理admin_network/ifaces.lua和admin_network/network.lua,生成html文件
2. 頁面響應
2.1. Web請求
當點擊頁面“Save & Apply”按鈕時,瀏覽器會把每一個有name的web元素的對應值下傳,下傳form表格如下:
-----------------------------151563007122428
Content-Disposition: form-data; name="cbi.submit" 1
-----------------------------151563007122428
Content-Disposition: form-data; name="cbi.cbe.firewall.cfg02e63d.syn_flood" 1 -----------------------------151563007122428
Content-Disposition: form-data; name="cbi.cbe.firewall.cfg02e63d.drop_invalid" 1
……
……
-----------------------------151563007122428
Content-Disposition: form-data; name="cbi.apply" Save & Apply -----------------------------151563007122428—
2.2. 處理
服務器處理過程和頁面生成基本類似,也調用到/usr/lib/lua/luci/dispatcher.lua並走到顯示/處理部分,后繼處理如下:
ok, err = util.copcall(target, c.target, unpack(args)) à(target在luci/controller/firewall中被賦值為arcombine(cbi("firewall/zones"), cbi("firewall/zone-details")),即兩個cbi函數的集合)
function cbi(model, config) à
local function _cbi(self, ...) à
local cstate = res:parse()à
function Map.parse(self, readinput, ...) à
Node.parse(self, ...)
Node.parse會調用Map中的每一個子元素自身的處理
EX:
如調用Flag的處理:function Flag.parse(self, section),他會通過遍歷處理from傳下來的每一個Flag,並通過本身的write/remove來啟用和禁用這個選項。
當form保存下來cbid.firewall.cfg02e63d.syn_flood這個Network/Firewall/General Setting下的Flag標簽的值時,處理函數就會調用Flag.parse處理:調用self:formvalue來匹配標簽值,然后調用model/cbi/firewall/zones.lua的write或者remove來禁用或者啟用這個選項所控制的開關。
由於Flag = class(AbstractValue),繼承於AbstractValue類,所以其write/remove是調用的AbstractValue類的write/remove方法。
AbstractValue.write調用self.map:set即function Map.set(self, section, option, value),Map.set 再調用self.uci:set(self.config, section, option, value)來設置對應config文件,然后Map.parse 會調用self.uci:commit(config)對已修改的config逐一提交。
生效的兩種方式
1、按照固定格式設置對應選項,系統自動調用來使各個參數生效,self.uci:apply(self.parsechain) (應用剛設置的config設置服務)àfunction Cursor.apply(self, configlist, command) àreturn { "/sbin/luci-reload", unpack(configlist) };
2、self:_run_hooks("on_apply", "on_after_apply"),自己在對應的.lua文件中寫m.on_apply來啟動或者處理方式。
ps:openwrt個人理解加上前輩的blog來寫的,基本是一路打log來了解流程,若有文中問題還請指正