Luci 上传文件源码分析

前言

怕忘记, 在这里保存一下
所有源码都在system.luaaction_flashops方法下

源码备注

####声明变量部分

1
2
3
4
5
6
7
8
9
10
function action_flashops()
local sys = require "luci.sys"
local fs = require "luci.fs"

local upgrade_avail = nixio.fs.access("/lib/upgrade/platform.sh")
local reset_avail = os.execute([[grep '"rootfs_data"' /proc/mtd >/dev/null 2>&1]]) == 0

local restore_cmd = "tar -xzC/ >/dev/null 2>&1"
local backup_cmd = "sysupgrade --create-backup - 2>/dev/null"
local image_tmp = "/tmp/firmware.img"

这部分没啥好说, 主要是image_tmp 这个变量
image_tmp 是上传的文件保存的位置

####声明方法部分

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
local function image_supported()
-- XXX: yay...
return ( 0 == os.execute(
". /lib/functions.sh; " ..
"include /lib/upgrade; " ..
"platform_check_image %q >/dev/null"
% image_tmp
) )
end

local function image_checksum()
return (luci.sys.exec("md5sum %q" % image_tmp):match("^([^%s]+)"))
end

local function storage_size()
local size = 0
if nixio.fs.access("/proc/mtd") then
for l in io.lines("/proc/mtd") do
local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
if n == "linux" or n == "firmware" then
size = tonumber(s, 16)
break
end
end
elseif nixio.fs.access("/proc/partitions") then
for l in io.lines("/proc/partitions") do
local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)')
if b and n and not n:match('[0-9]') then
size = tonumber(b) * 1024
break
end
end
end
return size
end

####接收文件部分(setfilehandler)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local fp
luci.http.setfilehandler(
function(meta, chunk, eof)
if not fp then
if meta and meta.name == "image" then
fp = io.open(image_tmp, "w")
else
fp = io.popen(restore_cmd, "w")
end
end
if chunk then
fp:write(chunk)
end
if eof then
fp:close()
end
end
)

这部分一定要放在声明下面, 逻辑的上面, 就是说luci.http.setfilehandler上面只能有声明的代码
并且它接收完毕才会只系下面的代码, 不是异步的

逻辑部分

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
	...

elseif luci.http.formvalue("image") or luci.http.formvalue("step") then
--
-- Initiate firmware flash
--
local step = tonumber(luci.http.formvalue("step") or 1)
if step == 1 then
if image_supported() then
luci.template.render("admin_system/upgrade", {
checksum = image_checksum(),
storage = storage_size(),
size = nixio.fs.stat(image_tmp).size,
keep = (not not luci.http.formvalue("keep"))
})
else
nixio.fs.unlink(image_tmp)
luci.template.render("admin_system/flashops", {
reset_avail = reset_avail,
upgrade_avail = upgrade_avail,
image_invalid = true
})
end
--
-- Start sysupgrade flash
--
elseif step == 2 then
local keep = (luci.http.formvalue("keep") == "1") and "" or "-n"
luci.template.render("admin_system/applyreboot", {
title = luci.i18n.translate("Flashing..."),
msg = luci.i18n.translate("The system is flashing now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a few minutes before you try to reconnect. It might be necessary to renew the address of your computer to reach the device again, depending on your settings."),
addr = (#keep > 0) and "192.168.1.1" or nil
})
fork_exec("killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp })
end

...

end
end

PS. 已经注释掉无关部分
接收完文件之后, 这一句luci.http.formvalue("image")就通过了, 跟住就检查文件的正确性, 正确就渲染admin_system/upgrade.htm, 错误就渲染admin_system/flashops.htm并且删除文件nixio.fs.unlink(image_tmp)
admin_system/flashops.htm没啥代码, 主要是提交step = 2回来,
所以升级代码是这句fork_exec("killall dropbear uhttpd; sleep 1; /sbin/sysupgrade %s %q" %{ keep, image_tmp })


Arylo Yeung, Typescripter, 暂留在鹅厂
tomail:arylo.open@gmail.com

关于Entry()

关于Entry

Function 分析

1
entry(path, target, title=nil, order=nil)
  • path 是定义显示的虚拟路径
  • target 是定义相对应的处理方式, 分别有
    • alias 直接转向别的Entry
    • template 渲染某个view
    • cbi/form 调用某个model
    • call 直接调用Function
  • title 菜单的显示标题, 可以为空
    • _() 国际化文字
    • translate() 同上, 常见于model
  • order 同级下的菜单显示位置, 越小越前, 最小为1(?), 可以为空

entry()还有其他属性, 例如’entry().index=true’, 分别是:

  • index 该节点为首页
  • dependent 如果父节点丢失的话, 就不会该节点被意外调用
  • leaf 如果该节点下有子节点, 解析到该节点就不继续解析它的子节点
  • sysauth 该节点需要系统账号认证
  • i18n

E.g.

1
2
3
4
5
6
7
-- controller/admin/status.lua
entry({"admin", "status"}, alias("admin", "status", "overview"), _("Status"), 20).index = true
entry({"admin", "status", "overview"}, template("admin_status/index"), _("Overview"), 1)
entry({"admin", "status", "iptables"}, call("action_iptables"), _("Firewall"), 2).leaf = true
entry({"admin", "status", "routes"}, template("admin_status/routes"), _("Routes"), 3)

entry({"admin", "status", "processes"}, cbi("admin_status/processes"), _("Processes"), 6)

Arylo Yeung, Typescripter, 暂留在鹅厂
tomail:arylo.open@gmail.com

罗总语录

江浙泸

=> 江浙驴

三顾茅庐

=> 三顾茅驴

养牛

=> 以前很多人上山下乡, 跟着有两个知青过来, 在我们那里弄一个农村, 在哪里养牛奶

这么高大上

=> is very 高科技

我人品非常好

=> my RP is very good

比星巴克还要星巴克

=> super 星巴克

你要不要装个samba?

=> 你要不要装个38?

舌战群儒

=> 舌战群lu

刽子手

=> 汇子手

从哪里来, 滚回哪里去

=> Where are you come from, go back where


Arylo Yeung, Typescripter, 暂留在鹅厂
tomail:arylo.open@gmail.com

修改Luci默认主题

修改默认主题 (样例Theme 为 Lafite)

编辑contrib/package/luci/Makefile文件

将这行代码

1
$(eval $(call theme,bootstrap,Bootstrap Theme (Original)))

修改为:

1
$(eval $(call theme,lafite,Lafite Theme (Original)))

然后找到下面代码

1
2
3
4
$(eval $(call collection,, \
Standard OpenWrt set including full admin with ppp support and the default OpenWrt theme, \
+uhttpd+uhttpd-mod-ubus +luci-mod-admin-full +luci-theme-bootstrap+luci-app-firewall \
+luci-proto-ppp +libiwinfo-lua +luci-lib-nixio))

修改为(就是将+luci-theme-bootstrap改为+luci-theme-lafite)

1
2
3
4
$(eval $(call collection,, \
Standard OpenWrt set including full admin with ppp support and the default OpenWrt theme, \
+uhttpd+uhttpd-mod-ubus +luci-mod-admin-full +luci-theme-lafite+luci-app-firewall \
+luci-proto-ppp +libiwinfo-lua +luci-lib-nixio))

编辑modules/base/root/etc/config/luci文件

将下列代码

1
option mediaurlbase /luci-static/openwrt.org

修改为

1
option mediaurlbase /luci-static/lafite

Arylo Yeung, Typescripter, 暂留在鹅厂
tomail:arylo.open@gmail.com