关于luci的几个问题一
最近,由于項(xiàng)目的原因,現(xiàn)在總結(jié)幾點(diǎn):
1.luci運(yùn)行的流程?
答:
首先,我們從/www/cgi-bin/文件開始,運(yùn)行luci文件中代碼:
#!/usr/bin/lua luci.dispatcher.indexcache = “tmp.luci-indexcache” luci.sgi.cgi.run()接著進(jìn)入到sgi/的cgi.lua文件中,一個函數(shù)是limitsource(handle,?limit),主要是將limit個數(shù)的字符從stdin里面。
另一個函數(shù)是run()后面的頁面生成將一直在這個函數(shù)進(jìn)行。
run()Local x = coroutine.create(luci.dispatcher.httpdispatcher)While coroutine.status(x) ~= “dead” do//運(yùn)行上面創(chuàng)建的協(xié)同程序,即運(yùn)行httpdispatcher,參數(shù)為上面local rLocal res. Id, data1, data2 = coroutine.resume(x, r)if active thenIf id == 1 then -- http.response-lineIf id == 2 then --準(zhǔn)備headerIf id == 3 then --寫header、blank,默認(rèn)到stdoutIf id == 4 then --寫bodyIf id == 5 then --EOFIf id == 6 then EndEnd End然后,進(jìn)入httpdispatcher(request,?prefix)????//參數(shù)都放在context里
<pre name="code" class="html"> <pre name="code" class="html">Local pathinfo = http.urldecode(request:getenv(“PATH_INFO”) or “”, true)For _,node in ipairs(prefix) do R[#r+1] = node --將node賦給r{}EndLocal stat, err = util.coxpcall(function() Dispatch(context.request)End, error500) 接著,進(jìn)入 dispatch(request) 函數(shù)中,A.設(shè)置語言B.創(chuàng)建node-tree節(jié)點(diǎn)樹結(jié)構(gòu) Local c = ctx.tree Local stat If not c then C = createtree() //此函數(shù)從controller下的index函數(shù)來創(chuàng)建node-tree結(jié)構(gòu) 文件 End B. createtree()函數(shù)If not index then Createindex() //此函數(shù)定義了path、suff,判斷條件然后進(jìn)入不同分支, //Createindex_fastindex(path, suff)、createindex_plain(path, suff) EndLocal track = {} -- 每一層把找到的node信息放在這個track()中 For i, s in ipairs(request) do Util.update(track, c) -- update(t, updates) t要更新的表,updates包含需要更新的值得表。C.需要顯示的部分 If (c and c.index) or not track.template then 初始化模板 定義了tpl.context.viewns = setmetatable() D.認(rèn)證 If track.sysauth then Local sauth = require “l(fā)uci.sauth” //將getcookie和sysauth屬性賦給sess If not sess then Sess = luci.http.getcookie(“sysauth”) Sess = sess and sess:match(“^[a-f0-9]*$”) Verifytoken = trueEnd//讀取session值返回它的contentLocal sdat = sauth.read(sess) If sdat then Else Local eu = http.getenv(“HTTP_AUTH_USER”) Local ep = http.getenv(“HTTP_AUTH_PASS”)End F.顯示/處理 c是createtree()的返回值tree,根據(jù)不同類型不同處理。 If c then If type(c.target) ==”function” then target = c.target Elseif type(c.target) == “table” then target = c.target.target End End 2.關(guān)于root和mini登錄的問題?
答:
Root=node()調(diào)用dispatcher.lua中node(...)函數(shù),node(...)調(diào)用_create_node(path)函數(shù),定義了最外面的節(jié)點(diǎn),也就是最上層的菜單顯示。
If not root.target then Root.target = alias(“admin”) //調(diào)用dispatcher.lua中alias(...)函數(shù),重定向到另外一個節(jié)點(diǎn),在函數(shù)將admin節(jié)點(diǎn)繼續(xù)給了req,然后dispatch(req) Root.index = true End Local page = node(“admin”) //將admin.寫到context.treecache[name]中 Page.target = firstchild() //調(diào)用dispatcher.lua中firstchild()函數(shù),return {type = “firstchild”, target = _firstchild},然后調(diào)用_firstchild(),將最低順序的node給dispatch()函數(shù)執(zhí)行。 Page.title = _(“Administration”) //標(biāo)題 Page.order = 10 //順序 Page.sysauth = “root” //認(rèn)證用戶的登錄 Page.sysauth_authenticator = “htmlauth” //調(diào)用dispatcher.lua中htmlauth()函數(shù),檢測登錄的合法性。 Page.ucidata = true Page.index = true方法:將admin文件夾拷貝一份給mini,將mini中index.lua中將page.sysauth?=?“root”和page.sysauth_authenticator?=?“htmlauth”注釋掉,那么在彈出的頁面上點(diǎn)擊User按鈕,會直接進(jìn)入到系統(tǒng)中。
3.對于entry()函數(shù)的分析?答:
3.1?entry(path,?target,?title,?order)??例如:
Entry({“admin”,?“system”},?alias(“admin”,?“system”,?“system”),?_(“System”),?30).index?=?true
?
Path:虛擬路徑
Target:目標(biāo)函數(shù)調(diào)用
Title:顯示在頁面上的標(biāo)題
Order:在同一級別下,node的順序。(可選)
?
Local?c?=?node(unpack(path))????//unpack返回path中的所有值,并傳給node做參數(shù),然后調(diào)用node兩次,將node節(jié)點(diǎn)創(chuàng)建出來
//將參數(shù)分別傳進(jìn)去
C.target?=?target???//將值傳進(jìn)去后,剛開始構(gòu)造node-tree的時候,alias(..)函數(shù)會在entry(...)函數(shù)之前調(diào)用,然后alias()函數(shù)中調(diào)用dispatch(req)。
C.title?=?title
C.order?=?order
C.module?=?getfenv(2).__NAME
3.2.?Entry({“admin”,?“system”,?“system”},?cbi(“admin_system/system”),?_(“System”),?1)
首先,cbi()函數(shù)先執(zhí)行,return?{type?=?“cbi”,?config?=?config,?model?=?model,?target?=?_cbi}這句話,開始的時候,_cbi函數(shù)不會被執(zhí)行,只有到了dispatch()函數(shù)里面才可以執(zhí)行。
Cbi函數(shù)返回四個值,type,config,model,target
當(dāng)點(diǎn)擊與cbi有關(guān)的request的時候,在dispatch()函數(shù)的顯示/處理部分,有個if判斷,
if c thenIf type(c.target) == “function” then //當(dāng)c.target類型為函數(shù)時候Target = c.targetElseif type(c.target) == “table” then //當(dāng)c.target類型為table時候Target = c.target.target //也就是把表中屬性target = “_cbi”給了target,進(jìn)行后續(xù)處理。EndEnd在_cbi(self,?...)函數(shù)中,
Local cbi = require “l(fā)uci.cbi” Local tpl = require “l(fā)uci.template” Local http = require “l(fā)uci.http”Local config = self.config or {} //將cbi()返回的第二個參數(shù)給config Local maps = cbi.load(self.model, ...) //將第三個參數(shù)給load(),然后給maps 在cbi.lua文件中,model其實(shí)就是路徑,load路徑接下來是一個for循環(huán),每一個node首先需要map畫出框架,然后一層一層的畫控件。
For i, res in ipairs(maps) doRes.flow = config //map.flowLocal cstate = res:parse() //調(diào)用Map.parse(self, readinput,...) 調(diào)用Node.parse(self, ...),(uci主要是與luci進(jìn)行數(shù)據(jù)交互的平臺。) Function Map.parse(self, readinput, ...)Formvalue(“cbi.skip”) Node.parse(self, ...)If self.save then // 如果map的保存按鈕被點(diǎn)擊,或者其他按鈕被點(diǎn)擊,都會觸發(fā)uci里面的函數(shù),來處理相關(guān)操作。 //比如:on_save, on_before_save,on_after_save, on_before_commit, on_after_commit,on_before_apply之類If self:submitstate() then //如果map的提交按鈕被點(diǎn)擊 EndMap = class(Node) //Map是Node的子類,那么map.parse會執(zhí)行Node.parse()方法,function Node.parse(self, ...)For k, child in ipairs(self.children) doChild:parse()EndEnd在此函數(shù)執(zhí)行之前,調(diào)用Node.__init__(self,?title,?description),self.children?=?{}
Local?class=?util.class???class()函數(shù)return?setmetatable({},?{__call?=?_instantiate,__index?=?base)}??當(dāng)調(diào)用的時候,調(diào)用_instantiate(class,?...),函數(shù)中調(diào)用inst:__init__(...)函數(shù)初始化具體的類,返回類。
Map = class(Node) Function Map.__init__() Function Map.fromvalue(self, key) Function Map.formvaluetable(self, key) Function Map.get_scheme() Function Map.submitstate(self) Functoin Map.chain() Function Map.state_handler() Function Map.parse() Function Map.render() Function Map.section(self, class,...) Function Map.add(self, sectiontype) Function Map.set(self, section,...) Function Map.del() Function Map.get() <pre name="code" class="html"> 3.3 m:chain(“l(fā)uci”) //向map中插入外部的config信息 S = m:section(TypedSection,””, “”) //Map:section創(chuàng)建了一個子section,其中如果是abstraction的實(shí)例,那么調(diào)用Node:append()中table.insert(self.children, obj)語句。S是類TypedSection產(chǎn)生的實(shí)例。 S.addremove = false //當(dāng)執(zhí)行TypedSection()函數(shù)的時候就會判斷這個和下面的選項(xiàng) S.anonymous = true S.tab = s:tab("general", translate("General Settings")) //定義一個tab給s,調(diào)用abstractSection : tab(tab,title,desc)函數(shù),其中self.tab_names[#self.tab_names+1] = tabself.tabs[tab] = {title = title,description = desc,childs = { }} 將將tab給了tab_names,tab的各個參數(shù)給了tabs數(shù)組。 o = s:taboption("general", DummyValue, "_systime", translate("Local Time")) //調(diào)用AbstractSection:taboptoin(),然后調(diào)用AbstractSection.option(self, ...), If instanceof(class, AbstractValue) then //如果DummyValue是AbstractSection的實(shí)例local obj = class(self.map, self, option, ...) //實(shí)例化DummyValue類self:append(obj) //返回的obj追加給AbstractSectionself.fields[option] = obj //將對象賦給fields[option]return obj //返回obj End o.template = "admin_system/clock_status" //DummyValue實(shí)例后的對象o調(diào)用template function DummyValue.__init__(self, ...)AbstractValue.__init__(self, ...)self.template = "cbi/dvalue" //這句話o.template調(diào)用template/cbi/dvalue.htm文件self.value = nil end o = s:taboption("general", Value, "hostname", translate("Hostname")) //Value類實(shí)例化的實(shí)例給了o o.datatype = "hostname" //o連接到cbi文件夾下,datatype.lua文件function o.write(self, section, value)Value.write(self, section, value) //調(diào)用AbstractValue.write()方法,調(diào)用uci:set()寫到config文件中l(wèi)uci.sys.hostname(value) //獲得或者更改當(dāng)前的hostname End 3.4 entry({"admin", "services"}, firstchild(), _("Services"), 40).index = true 調(diào)用dispatcher.lua 中的Firstchild(), function firstchild()return { type = "firstchild", target = _firstchild } //當(dāng)后期顯示部分執(zhí)行dispatch()時候,調(diào)用_firstchild()函數(shù), if node and node.nodes and next(node.nodes) thenlocal k, vfor k, v in pairs(node.nodes) doif not lowest or(v.order or 100) < (node.nodes[lowest].order or 100)thenlowest = kendendEndpath[#path+1] = lowest --將多出來的節(jié)點(diǎn)追加給pathdispatch(path) -- 最關(guān)鍵的一句代碼,調(diào)用dispatch(path),path已經(jīng)改變 3.5 entry({"admin", "logout"}, call("action_logout"), _("Logout"), 90) 調(diào)用dispatcher.lua中的call(), function call(name, ...)return {type = "call", argv = {...}, name = name, target = _call} //當(dāng)后期顯示部分執(zhí)行dispatch()時候,調(diào)用_call()函數(shù), local function _call(self, ...)local func = getfenv()[self.name] //獲取當(dāng)前函數(shù)所在文件夾路徑if #self.argv > 0 thenreturn func(unpack(self.argv), ...) elsereturn func(...) //調(diào)用當(dāng)前函數(shù)所在路徑下的對應(yīng)函數(shù)end End 3.6 entry({“admin”, “system”, “startup”},form(“admin_system/startup”),_(“Startup”), 45) Function form(model)Return {type = “cbi”, model = model, target = _form} End 當(dāng)執(zhí)行dispatch()函數(shù)時,執(zhí)行_form()函數(shù), Local maps = luci.cbi.load(self.model, ...) For i, res in ipairs(maps) do Local cstate = res:parse() If cstate and (not state or cstate < state) then State = cstateEnd End Http.header(“X-CBI-State”, state or 0) //context.headers[key:lower()] = value //Coroutine.yield(2, key, value) Tpl.render(“header”) //render(name, scope) return template(name):render(scope or //getenv(2))然后調(diào)用Template(name):reader()畫出header.htm For i, res in ipairs(maps) doRes:render() End Tpl.render(“footer”) //畫出footer.htm 3<span style="font-family: 宋體;">.7 entry({“admin”,“network”,“wireless”}, arcombine(template(“admin_network/wifi_overview”), cbi(“admin_network/wifi”)), _(“Wifi”), 15)</span> Arcombine(template, cbi)調(diào)用dispatcher.lua文件中function arcombine(trg1, trg2)Return {type = “arcombine”, env = getfenv(), target = _arcombine, targets = {trg1, trg2}}Function _arcombine(self, ...)Local argv = {...}Local target = #argv > 0 and self.targets[2] or self.targets[1] Setfenv(target.target,self.env)Target:target(unpack(argv)) //一個接著一個執(zhí)行相應(yīng)的函數(shù), End 3.8 entry({“admin”, “status”, “overview”}, template(“admin_status/index”), _(“Overview”), 1) Template()調(diào)用dispatcher.lua文件中template(name)函數(shù), Return {type = “template”, view = name, target = _template} 當(dāng)執(zhí)行dispatch()函數(shù)的時候,則執(zhí)行_template = function (self, ...) require “l(fā)uci.template”.render(self.view) ,畫出admin_status/index.htm4.formvalue怎么處理值?
答:dispatcher.lua中authenticator.htmlauth()函數(shù)中,
Local user = luci.http.formvalue(“username”) Local pass = luci.http.formvalue(“password”)這句話是從http.lua文件中調(diào)用formvalue()函數(shù)獲取值
調(diào)用function?formvalue(name,?noparse)
Return?context.request:formvalue(name,?noparse)
End
然后調(diào)用Request.formvalue(self,?name,?noparse)
If?name?then?return?self.message.params[name]???
然后返回Request中的message.params[name]
- HTTP-Message table Self.message = {Env = env,Headers = {},Params = protocol.urldecode_params(env.QUERY_STRING or “”), }將最初的“Username”傳到了params這里,然后調(diào)用http/protocol.lua文件中的
Function Urldecode_params(url, tbl) Local params = tbl or {} If url.find(“?”) then Url = url:gsub(“^.+%?([^?]+)”, “%1”) //^開頭表示匹配開始部分,+匹配1次或者多次,%?轉(zhuǎn)義問號,第三個參數(shù)表示捕獲第一個匹配字符串。^?表示非問號的部分,.+進(jìn)行的是最長匹配。 End <span style="color:#3366ff;">//由wireshark分析,Post /cgi-bin/luci HTTP/1.1(application/x-www-form-urlencoded) Url:http://192.168.1.1/cgi-bin/luci?username=root&password=admin Content-length:28form iten :”username” = “root”Key:usernameValue:rootForm item:”password” = “admin”Key: passwordValue:admin 可見:還是處理成key-value對。</span> For pair in url:gmatch(“[^&;]+”) do-- 查找key、valueLocal key = urldecode(pair:match(“^([^=]+)”) )Local val = urldecode(pair:match(“^[^=]+=(.+)$”)) //調(diào)用urldecode中pair.match()查找key、val-- 存儲值If type(key) == “string” and key:len() > 0 then //key就是第一句話傳進(jìn)去的id(username),然后val賦給params[name]If type(val) ~= “string” then val = “” endIf not params[key] then //登錄頁面?zhèn)鬟M(jìn)來的值進(jìn)入這里面Params[key] = val Elseif type(params[key] ~= “table” thenParams[key] = {params[key], val}ElseTable.insert(params[key],val) EndEnd End Return params End至此,將url中的所需的值就獲得了。然后再進(jìn)行后續(xù)處理。
5.點(diǎn)擊login按鈕后發(fā)生了什么?
答:在sysauth.htm中,
<formmthod=”post”?action=”<%=pcdata(luci.http.getenv(“REQUEST_URI”))%>”>
當(dāng)點(diǎn)擊按鈕時候,就會跳轉(zhuǎn)到action指定的url.在dispatcher.lua文件dispatch()函數(shù)中,
tpl.context.views setmetable({
.......},{__index=function(table,key)If key == “controller” then return build_url() Elseif key == “REQUEST_URI” then return build_url(unpack(ctx.requestpath)) }) 當(dāng)點(diǎn)擊登錄后,頁面會跳轉(zhuǎn)到/,接著/admin,如果需要認(rèn)證,那么接下來會彈出htmlauth.htm頁面,然后如果沒有驗(yàn)證成功,則繼續(xù)本頁面,如果成功了,那么繼續(xù)跳轉(zhuǎn)/,然后admin/,此時post來了信息,然后alias到entry.order最小的那個node,很顯然是/admin/status.lua,然后status這個node會alias到overview這個node。最主要的還是在diapatcher.lua文件中的dispatch()的認(rèn)證部分。 Apply,apply&save,reset的邏輯跟這個也是一樣的。當(dāng)點(diǎn)擊登錄后,頁面會跳轉(zhuǎn)到/,接著/admin,如果需要認(rèn)證,那么接下來會彈出htmlauth.htm頁面,然后如果沒有驗(yàn)證成功,則繼續(xù)本頁面,如果成功了,那么繼續(xù)跳轉(zhuǎn)/,然后admin/,此時post來了信息,然后alias到entry.order最小的那個node,很顯然是/admin/status.lua,然后status這個node會alias到overview這個node。最主要的還是在diapatcher.lua文件中的dispatch()的認(rèn)證部分。
Apply,apply&save,reset的邏輯跟這個也是一樣的。
總結(jié)
以上是生活随笔為你收集整理的关于luci的几个问题一的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 记录工作中第一次解决bug的小事
- 下一篇: 关于luci的几个问题二