luci启动的流程

1、uhttpd Web server的根目录在/etc/config/uhttpd文件中指定为www,主页面为/www/index.html,

2、index.html中指定cgi程序启动脚本为/cgi-bin/luci

3、/cgi-bin/luci脚本,指定缓存路径为/tmp/luci-indexcache,指定cgi启动接口为/usr/lib/lua/luci/sgi/cgi.lua的run()函数

 

注:可以rm -rf /tmp/luci* 来删除luci的备份文件,这样可以清除缓存,执行修改后的操作。

cgi启动流程

run()函数作为CGI程序的启动入口,代码解析如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
function run()
-- 获取web请求,放于变量r中(包括环境变量,请求数据,出错处理接口)
    local r = luci.http.Request(
        luci.sys.getenv(),
        limitsource(io.stdin, tonumber(luci.sys.getenv("CONTENT_LENGTH"))),
        ltn12.sink.file(io.stderr)
    )
    --创建一个协同程序
    local x = coroutine.create(luci.dispatcher.httpdispatch)
    local hcache = ""
    local active = true
    --查看协同程序x的协同状态
    while coroutine.status(x) ~= "dead" do
        local res, id, data1, data2 = coroutine.resume(x, r)
 
        if not res then
            print("Status: 500 Internal Server Error")
            print("Content-Type: text/plain\n")
            print(id)
            break;
        end
                -- HTTP的响应报文通过io.write()方式写在stdout上,
        -- 在由uhttpd架构将这些数据传递给父进程,
        -- 通过tcp连接返回给client端。
        if active then
            if id == 1 then
                        -- 填写HTTP响应状态行
                io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\r\n")
            elseif id == 2 then
                       -- 准备报文头header
                hcache = hcache .. data1 .. ": " .. data2 .. "\r\n"
            elseif id == 3 then
                      -- 填写header、blank到stdout上
                io.write(hcache)
                io.write("\r\n")
            elseif id == 4 then
                          -- 填写body
                io.write(tostring(data1 or ""))
            elseif id == 5 then
                          -- 关闭io接口,EOF
                io.flush()
                io.close()
                active = false
            elseif id == 6 then
                data1:copyz(nixio.stdout, data2)
                data1:close()
            end
        end
    end
end                                

dispatcher启动流程

在上述run()函数中,创建了一个协同程序,调用httpdispatch()函数,而这个函数位于dispatcher.lua中。通过后续的介绍也可以发现,luci真正的主体部分都在dispatcher.lua脚本里,本小节主要对httpdispatch()和dispatch()函数进行介绍。(注:在版本机dispatcher.lua的同目录下有一个dispatcher.luadoc文件,里面介绍dispatcher.lua的参数说明,仅供参考。)

1、httpdispatch():解析请求,获得HTTP request请求参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//  /usr/lib/lua/luci/dispatch.luafunction httpdispatch(request, prefix)
    http.context.request = request
 
    local r = {}
    context.request = r
      --解析HTTP request,获取请求路径,并传入dispatch()函数进行处理
    local pathinfo = http.urldecode(request:getenv("PATH_INFO") or "", true)
 
    if prefix then
        for _, node in ipairs(prefix) do
            r[#r+1] = node
        end
    end
 
    local node
    for node in pathinfo:gmatch("[^/%z]+") do
        r[#r+1] = node
    end
 
    determine_request_language()
 
    local stat, err = util.coxpcall(function()
        dispatch(context.request)
    end, error500)
 
    http.close()
 
    --context._disable_memtrace()
end

2、dispatch():解析请求节点,调度网页显示,分为以下四个部分:

(1)创建节点树node-tree,解析请求路径,获取节点树节点

createtree()函数主要从controller目录下寻找.lua文件,并且调用每个lua文件中的index()函数。这些index函数通过entry(path,target,title,order)函数定义了菜单栏的每个子菜单选项,包括子菜单的节点位置path、调度行为target、页面标题title以及节点顺序order。当解析完index()下的node节点,对应生成一个node-tree。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- Build the index before if it does not exist yet.
function createtree()
    if not index then
        createindex()
    end
 
    local ctx  = context
    local tree = {nodes={}, inreq=true}
 
    ctx.treecache = setmetatable({}, {__mode="v"})
    ctx.tree = tree
 
    local scope = setmetatable({}, {__index = luci.dispatcher})
 
    for k, v in pairs(index) do
        scope._NAME = k
        setfenv(v, scope)
        v()
    end
 
    return tree
end
(2)认证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
if type(auth) == "table" and type(auth.methods) == "table" and #auth.methods > 0 then
    local sid, sdat, sacl
    for _, method in ipairs(auth.methods) do
        sid, sdat, sacl = check_authentication(method)
 
        if sid and sdat and sacl then
            break
        end
    end
 
    if not (sid and sdat and sacl) and auth.login then
        local user = http.getenv("HTTP_AUTH_USER")
        local pass = http.getenv("HTTP_AUTH_PASS")
 
        if user == nil and pass == nil then
            user = http.formvalue("luci_username")
            pass = http.formvalue("luci_password")
        end
 
        if user and pass then
            sid, sdat, sacl = session_setup(user, pass)
        end
 
        if not sid then
            context.path = {}
 
            http.status(403, "Forbidden")
            http.header("X-LuCI-Login-Required", "yes")
 
            local scope = { duser = "root", fuser = user }
            local ok, res = util.copcall(tpl.render_string, [[<% include("themes/" .. theme .. "/sysauth") %>]], scope)
            if ok then
                return res
            end
            return tpl.render("sysauth", scope)
        end
 
        http.header("Set-Cookie", 'sysauth=%s; path=%s; SameSite=Strict; HttpOnly%s' %{
            sid, build_url(), http.getenv("HTTPS") == "on" and "; secure" or ""
        })
 
        http.redirect(build_url(unpack(ctx.requestpath)))
        return
    end
 
    if not sid or not sdat or not sacl then
        http.status(403, "Forbidden")
        http.header("X-LuCI-Login-Required", "yes")
        return
    end
 
    ctx.authsession = sid
    ctx.authtoken = sdat.token
    ctx.authuser = sdat.username
    ctx.authacl = sacl
end
 
if #required_path_acls > 0 then
    local perm = check_acl_depends(required_path_acls, ctx.authacl and ctx.authacl["access-group"])
    if perm == nil then
        http.status(403, "Forbidden")
        return
    end
 
    page.readonly = not perm
end
 
local action = (page and type(page.action) == "table") and page.action or {}
 
if action.type == "arcombine" then
    action = (#requested_path_args > 0) and action.targets[2] or action.targets[1]
end
 
if cors and http.getenv("REQUEST_METHOD") == "OPTIONS" then
    luci.http.status(200, "OK")
    luci.http.header("Access-Control-Allow-Origin", http.getenv("HTTP_ORIGIN") or "*")
    luci.http.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
    return
end
 
if require_post_security(action) then
    if not test_post_security() then
        return
    end
end
  (3)会根据action.type处理相应的动作,每个controller下的lua文件的index函数会生成页面的菜单栏并定义各个页面的调用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
if action.type == "view" then
        tpl.render("view", { view = action.path })
 
    elseif action.type == "call" then
        local ok, mod = util.copcall(require, action.module)
        if not ok then
            error500(mod)
            return
        end
...
elseif action.type == "alias" then
...
elseif action.type == "rewrite" then
...
elseif action.type == "template" then
        tpl.render(action.path, getfenv(1))
 
    elseif action.type == "cbi" then
        _cbi({ config = action.config, model = action.path }, unpack(requested_path_args))
 
    elseif action.type == "form" then
        _form({ model = action.path }, unpack(requested_path_args))
else
        local root = find_subnode(menu, {}, true)
        if not root then
            error404("No root node was registered, this usually happens if no module was installed.\n" ..
                     "Install luci-mod-admin-full and retry. " ..
                     "If the module is already installed, try removing the /tmp/luci-indexcache file.")
        else
            error404("No page is registered at '/" .. table.concat(requested_path_full, "/") .. "'.\n" ..
                     "If this url belongs to an extension, make sure it is properly installed.\n" ..
                     "If the extension was recently installed, try removing the /tmp/luci-indexcache file.")
        end
    end
end

  

  

 

 

 

 

 

 

posted @   轻轻的吻  阅读(2918)  评论(1编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示