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程序的啟動入口,代碼解析如下

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請求參數

//  /usr/lib/lua/luci/dispatch.lua

function 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。

-- 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)認證
	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函數會生成頁面的菜單欄並定義各個頁面的調用方法
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

  

  

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM