最近,由于项目的原因,现在总结几点:

1.luci运行的流程?

答:

首先,我们从/www/cgi-bin/文件开始,运行luci文件中代码:

#!/usr/bin/lua
luci.dispatcher.indexcache = “tmp.luci-indexcache”
luci.sgi.cgi.run()

接着进入到sgi/的cgi.lua文件中,一个函数是limitsource(handle, limit),主要是将limit个数的字符从stdin里面。

另一个函数是run()后面的页面生成将一直在这个函数进行。

run()Local x = coroutine.create(luci.dispatcher.httpdispatcher)While coroutine.status(x)  ~= “dead” do//运行上面创建的协同程序,即运行httpdispatcher,参数为上面local rLocal res. Id, data1, data2 = coroutine.resume(x, r)if active thenIf id == 1 then     -- http.response-lineIf id == 2 then     --准备headerIf id == 3 then     --写header、blank,默认到stdoutIf id == 4 then     --写bodyIf id == 5 then     --EOFIf id == 6 then     EndEnd
End

然后,进入httpdispatcher(request, prefix)    //参数都放在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)

接着,进入 dispatch(request) 函数中,

A.设置语言B.创建node-tree节点树结构
Local c = ctx.tree
Local stat
If not c then
C = createtree()   //此函数从controller下的index函数来创建node-tree结构              文件
End
B. createtree()函数If not index then
Createindex()  //此函数定义了path、suff,判断条件然后进入不同分支,
//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.认证
If track.sysauth then
Local sauth = require “luci.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,根据不同类型不同处理。
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.关于root和mini登录的问题?

答:

Root=node()调用dispatcher.lua中node(...)函数,node(...)调用_create_node(path)函数,定义了最外面的节点,也就是最上层的菜单显示。

If not root.target then
Root.target = alias(“admin”)     //调用dispatcher.lua中alias(...)函数,重定向到另外一个节点,在函数将admin节点继续给了req,然后dispatch(req)
Root.index = true
End
Local page = node(“admin”)    //将admin.写到context.treecache[name]中
Page.target = firstchild()     //调用dispatcher.lua中firstchild()函数,return {type = “firstchild”, target = _firstchild},然后调用_firstchild(),将最低顺序的node给dispatch()函数执行。
Page.title = _(“Administration”)  //标题
Page.order = 10      //顺序
Page.sysauth = “root”  //认证用户的登录
Page.sysauth_authenticator = “htmlauth”   //调用dispatcher.lua中htmlauth()函数,检测登录的合法性。
Page.ucidata = true
Page.index = true

方法:将admin文件夹拷贝一份给mini,将mini中index.lua中将page.sysauth = “root”和page.sysauth_authenticator = “htmlauth”注释掉,那么在弹出的页面上点击User按钮,会直接进入到系统中。

3.对于entry()函数的分析?

答:

3.1 entry(path, target, title, order)  例如:

Entry({“admin”, “system”}, alias(“admin”, “system”, “system”), _(“System”), 30).index = true

Path:虚拟路径

Target:目标函数调用

Title:显示在页面上的标题

Order:在同一级别下,node的顺序。(可选)

Local c = node(unpack(path))    //unpack返回path中的所有值,并传给node做参数,然后调用node两次,将node节点创建出来

//将参数分别传进去

C.target = target   //将值传进去后,刚开始构造node-tree的时候,alias(..)函数会在entry(...)函数之前调用,然后alias()函数中调用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()函数先执行,return {type = “cbi”, config = config, model = model, target = _cbi}这句话,开始的时候,_cbi函数不会被执行,只有到了dispatch()函数里面才可以执行。

Cbi函数返回四个值,type,config,model,target

当点击与cbi有关的request的时候,在dispatch()函数的显示/处理部分,有个if判断,

if c thenIf type(c.target) == “function” then   //当c.target类型为函数时候Target = c.targetElseif type(c.target) == “table” then  //当c.target类型为table时候Target = c.target.target        //也就是把表中属性target = “_cbi”给了target,进行后续处理。EndEnd

在_cbi(self, ...)函数中,

Local cbi = require “luci.cbi”
Local tpl = require “luci.template”
Local http = require “luci.http”Local config = self.config or {}     //将cbi()返回的第二个参数给config
Local maps = cbi.load(self.model, ...)  //将第三个参数给load(),然后给maps
在cbi.lua文件中,model其实就是路径,load路径

接下来是一个for循环,每一个node首先需要map画出框架,然后一层一层的画控件。

For i, res in ipairs(maps)  doRes.flow = config    //map.flowLocal cstate = res:parse()    //调用Map.parse(self, readinput,...)
调用Node.parse(self, ...),(uci主要是与luci进行数据交互的平台。)
Function Map.parse(self, readinput, ...)Formvalue(“cbi.skip”) Node.parse(self, ...)If self.save then     // 如果map的保存按钮被点击,或者其他按钮被点击,都会触发uci里面的函数,来处理相关操作。
                              //比如:on_save, on_before_save,on_after_save, on_before_commit, on_after_commit,on_before_apply之类If self:submitstate() then  //如果map的提交按钮被点击
EndMap = class(Node)   //Map是Node的子类,那么map.parse会执行Node.parse()方法,function Node.parse(self, ...)For k, child in ipairs(self.children) doChild:parse()End

End在此函数执行之前,调用Node.__init__(self, title, description),self.children = {}

Local class= util.class   class()函数return setmetatable({}, {__call = _instantiate,__index = base)}  当调用的时候,调用_instantiate(class, ...),函数中调用inst:__init__(...)函数初始化具体的类,返回类。

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(“luci”)   //向map中插入外部的config信息

S = m:section(TypedSection,””, “”)  //Map:section创建了一个子section,其中如果是abstraction的实例,那么调用Node:append()中table.insert(self.children, obj)语句。S是类TypedSection产生的实例。
S.addremove = false //当执行TypedSection()函数的时候就会判断这个和下面的选项
S.anonymous = true S.tab = s:tab("general",  translate("General Settings"))  //定义一个tab给s,调用abstractSection : tab(tab,title,desc)函数,其中self.tab_names[#self.tab_names+1] = tabself.tabs[tab] = {title       = title,description = desc,childs      = { }}
将将tab给了tab_names,tab的各个参数给了tabs数组。
o = s:taboption("general", DummyValue, "_systime", translate("Local Time"))
//调用AbstractSection:taboptoin(),然后调用AbstractSection.option(self, ...),
If  instanceof(class, AbstractValue) then      //如果DummyValue是AbstractSection的实例local obj  = class(self.map, self, option, ...)   //实例化DummyValue类self:append(obj)        //返回的obj追加给AbstractSectionself.fields[option] = obj    //将对象赋给fields[option]return obj     //返回obj
End
o.template = "admin_system/clock_status" //DummyValue实例后的对象o调用template
function DummyValue.__init__(self, ...)AbstractValue.__init__(self, ...)self.template = "cbi/dvalue"   //这句话o.template调用template/cbi/dvalue.htm文件self.value = nil
end
o = s:taboption("general", Value, "hostname", translate("Hostname"))  //Value类实例化的实例给了o
o.datatype = "hostname"
//o连接到cbi文件夹下,datatype.lua文件function o.write(self, section, value)Value.write(self, section, value)    //调用AbstractValue.write()方法,调用uci:set()写到config文件中luci.sys.hostname(value)       //获得或者更改当前的hostname
End

3.4 entry({"admin", "services"}, firstchild(), _("Services"), 40).index = true
调用dispatcher.lua 中的Firstchild(),
function firstchild()return { type = "firstchild", target = _firstchild }   //当后期显示部分执行dispatch()时候,调用_firstchild()函数,
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 --将多出来的节点追加给pathdispatch(path)    -- 最关键的一句代码,调用dispatch(path),path已经改变

3.5 entry({"admin", "logout"}, call("action_logout"), _("Logout"), 90)
调用dispatcher.lua中的call(),
function call(name, ...)return {type = "call", argv = {...}, name = name, target = _call}  //当后期显示部分执行dispatch()时候,调用_call()函数,
local function _call(self, ...)local func = getfenv()[self.name]     //获取当前函数所在文件夹路径if #self.argv > 0 thenreturn func(unpack(self.argv), ...)   elsereturn func(...)    //调用当前函数所在路径下的对应函数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
当执行dispatch()函数时,执行_form()函数,
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))然后调用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)调用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))   //一个接着一个执行相应的函数,
End

3.8 entry({“admin”, “status”, “overview”}, template(“admin_status/index”), _(“Overview”), 1)
Template()调用dispatcher.lua文件中template(name)函数,
Return {type = “template”, view = name, target = _template}
当执行dispatch()函数的时候,则执行_template = function (self, ...) require “luci.template”.render(self.view) ,画出admin_status/index.htm

4.formvalue怎么处理值?

答:dispatcher.lua中authenticator.htmlauth()函数中,

Local user = luci.http.formvalue(“username”)
Local pass = luci.http.formvalue(“password”)

这句话是从http.lua文件中调用formvalue()函数获取值

调用function formvalue(name, noparse)

Return context.request:formvalue(name, noparse)

End

然后调用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这里,然后调用http/protocol.lua文件中的

Function Urldecode_params(url, tbl)
Local params = tbl or {}
If url.find(“?”) then
Url = url:gsub(“^.+%?([^?]+)”, “%1”)  //^开头表示匹配开始部分,+匹配1次或者多次,%?转义问号,第三个参数表示捕获第一个匹配字符串。^?表示非问号的部分,.+进行的是最长匹配。
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(“^[^=]+=(.+)$”))
//调用urldecode中pair.match()查找key、val-- 存储值If type(key) == “string” and key:len() > 0 then    //key就是第一句话传进去的id(username),然后val赋给params[name]If type(val) ~= “string” then val = “” endIf not params[key] then    //登录页面传进来的值进入这里面Params[key] = val      Elseif type(params[key] ~= “table” thenParams[key] = {params[key], val}ElseTable.insert(params[key],val)  EndEnd
End
Return params
End

至此,将url中的所需的值就获得了。然后再进行后续处理。

5.点击login按钮后发生了什么?

答:在sysauth.htm中,

<formmthod=”post” action=”<%=pcdata(luci.http.getenv(“REQUEST_URI”))%>”>

当点击按钮时候,就会跳转到action指定的url.在dispatcher.lua文件dispatch()函数中,

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))
})
当点击登录后,页面会跳转到/,接着/admin,如果需要认证,那么接下来会弹出htmlauth.htm页面,然后如果没有验证成功,则继续本页面,如果成功了,那么继续跳转/,然后admin/,此时post来了信息,然后alias到entry.order最小的那个node,很显然是/admin/status.lua,然后status这个node会alias到overview这个node。最主要的还是在diapatcher.lua文件中的dispatch()的认证部分。
Apply,apply&save,reset的逻辑跟这个也是一样的。

当点击登录后,页面会跳转到/,接着/admin,如果需要认证,那么接下来会弹出htmlauth.htm页面,然后如果没有验证成功,则继续本页面,如果成功了,那么继续跳转/,然后admin/,此时post来了信息,然后alias到entry.order最小的那个node,很显然是/admin/status.lua,然后status这个node会alias到overview这个node。最主要的还是在diapatcher.lua文件中的dispatch()的认证部分。

Apply,apply&save,reset的逻辑跟这个也是一样的。

关于luci的几个问题一相关推荐

  1. luci网页shell_openwrt luci web分析

    openwrt luci web分析 www/cbi-bin/luci run方法的主要任务就是在安全的环境中打开开始页面(登录页面),在run中,最主要的功能还是在dispatch.lua中完成. ...

  2. Openwrt 刷机后配置WAN口,安装luci和设置中文、安装挂载USB存储。

    官方版本的ROM编译时时没有把luci和uhttpd打包进去的,所以,要ssh登录到路由器后手动安装,默认用户名root,密码是空. 如果你的路由器是挂载在其他路由下面的,DHCP可以获取到IP,能正 ...

  3. 【OpenWRT之旅】LuCI探究

    1. 多语言 1)检查: opkg list | grep luci-i18n- 2)安装语言包: opkg install luci-i18n-hungarian 2.uhttpd 这个是LuCI所 ...

  4. api文档 luci_研究LuCI - 技术手札 - OSCHINA - 中文开源技术交流社区

    OpenWrt里有微型的 http 服务器,叫 uhttpd. 可执行文件在 /usr/sbin/uhttpd,对应的配置文件是 /etc/config/uhttpd.打开这个文件: 里面指定的网页的 ...

  5. 完整适配LUCI界面的Openwrt中EC20的QMI拨号

    目前4G模块应用已经非常普及,跟之前的3G不同,3G基本使用ppp拨号,usbserial驱动,Linux内核自带支持,此应用非常简单. 4G模块由于速率较高,usbserial驱动性能满足不了,因此 ...

  6. 关于luci的几个问题二

    1.Luci/sgi/cgi.lua和http.lua和dispatcher.lua关系? 答: 对每一个node,最重要的属性是target,也是dispatch()流程最后要执行的方法.各个方法a ...

  7. Openwrt中luci配置页面cbi小记

    先看看network的配置文件: [html] view plaincopy config interface 'loopback' option ifname 'lo' option proto ' ...

  8. 智能路由器-OpenWRT 系列三 (OpenWRT安装LuCI网络配置)

    OpenWRT 安装 LUCI 每次ssh登陆OpenWRT安装新软件时,都必须更新opkg opkg update 安装LUCI opkg install luci 安装luci中文语言包, 不同O ...

  9. CC版本添加 LUCI

    BB版本的时候,在 feeds/luci/contrib/package/luci下的Makefile文件添加上需要编译的 luci-application即可. 在CC版里改为:Feeds/luci ...

最新文章

  1. 虚拟函数是否应该被声明仅为private/protected?
  2. 干货|NLP 的四张技术路线图,带你系统设计学习路径
  3. Java技术学习心得
  4. 我一定要找到它FreeEIM
  5. python redis分布式锁_Python 使用 Redis 实现分布式锁
  6. 据说IE7.0不支持跨域名脚本,那网页计数器不是要失效啦?
  7. 原生javascript开发仿微信打飞机小游戏
  8. 从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结
  9. 计算机硬件基础与计算机组装知识总结
  10. 软件测试10年,如果再给我一次机会,我可能···
  11. 并发和并行的区别(图解)
  12. 移动磁盘显示数据错误循环冗余检查数据怎样恢复
  13. Mac 系统下java端口占用
  14. 4gl 的内建函数和操作符简介
  15. 信号量优先级反转问题记录(总是遗忘)
  16. getElementById的使用方法
  17. [k8s]一步一步学习k8syaml
  18. 数据结构与算法经典程序:农夫过河问题讲解
  19. windows10局域网内打印机共享
  20. 文件批量下载工具(自己用QT编写)

热门文章

  1. 深度学习在自然语言处理的应用(Version 0.76)
  2. 【最后测试点超时】1063 Set Similarity (25 分)_22行代码AC
  3. 【图示,简单明了】HttpServlet中getAttribute和getParameter的区别——【javaweb系列学习笔记】
  4. python多分类混淆矩阵代码_深度学习自学记录(3)——两种多分类混淆矩阵的Python实现(含代码)...
  5. ie浏览器跳转谷歌浏览器_微软IE浏览器的命运:加速死亡
  6. xp系统怎么关dhcp服务器,怎样解决Win XP操作系统DHCP故障:获取未使用的IP地址
  7. 如何构建GFS分布式存储平台?理论+实操!
  8. python笔记之序列(set的基本使用和常用操作)
  9. centos下搭建网站服务器,Centos7搭建web服务器
  10. m3u8地址_「波波带你手动提取网页视频」04讲 Network和Elements提取m3u8链接