掘金 后端 ( ) • 2024-04-25 17:26

theme: smartblue

Ubuntu22
immortalwrt v21.02.7
MT7620A

背景

Oui官方文档 的介绍中,我们可以很方便找到 前端代码调试的说明。可是没有说明 Lua 文件应该怎么更新。我们看到:

{
    server: {
        proxy: {
        '/oui-rpc': {
            target: 'http://openwrt.lan',
            secure: false
        }
        }
        ...
    }
}

这里使用了代理,就是把请求都转发到了我们的开发板上。所以我们推测:需要把 .lua 文件上传到开发板的特定位置。

本文假定我们的开发板网络已配置到了 192.168.8.88 ,且我们开发环境所在的虚拟机可以访问到开发板。

探索oui-httpd执行lua脚本的位置

着急的可以直接跳到下一节

我们肯定需要先定位到oui-httpd.lua文件,然后我们发现:

local function handle_rpc(con, req)
    if req.method ~= 'POST' then
        return con:send_error(http.STATUS_BAD_REQUEST)
    end

    local body, err = con:read_body()
    if not body then
        log.err('read body fail:', err)
        return con:send_error(http.STATUS_BAD_REQUEST)
    end

    local ok, msg = pcall(cjson.decode, body)
    if not ok or type(msg) ~= 'table' or type(msg.method) ~= 'string' then
        return con:send_error(http.STATUS_BAD_REQUEST)
    end

    if type(msg.params or {}) ~= 'table' then
        return con:send_error(http.STATUS_BAD_REQUEST)
    end

    if not rpc_methods[msg.method] then
        log.err('Oui: Not supported rpc method: ', msg.method)
        return con:send_error(http.STATUS_NOT_FOUND)
    end

    rpc_methods[msg.method](con, req, msg.params or {})
end

重点只有最后一行 rpc_methods[msg.method](con, req, msg.params or {}) ,在前端正常使用oui的this.$oui.call的时候,这里的msg.methodcall。也就是说,我们还是得看call是谁?大概第89行,我们可以看到rpc_methods['call'] = function(con, req, params)...,代码核心的部分如下:

rpc_methods['call'] = function(con, req, params)
    ...
    local result, err = rpc.call(mod, func, args, session)
    ...
    if result then
        local resp = cjson.encode({ result = result }):gsub('{}','[]')
        con:send(resp)
    else
        con:send('{}')
    end
end

那么 rpc 是啥呢?它来自文件 rpc.lua ,于是我们找到了M.call函数,重要代码如下:

function M.call(mod, func, args, session)
    if not lua_code_cache or not funcs[mod] then
        local script = '/usr/share/oui/rpc/' .. mod .. '.lua'
        ...
        local ok, tb = pcall(dofile, script)
        ...
        if type(tb) == 'table' then
            funcs[mod] = tb
        end
    end
    ...
    return funcs[mod][func](args, session)
end

好了,我们找到了脚本加载的地方,是 /usr/share/oui/rpc/ ,果不其然,我们查看开发板上,也能看到oui会把我们的lua文件都放在这个下面,所以我们命名上一定要注意不要重复啊!

image.png

进行sshscp的准备

好,现在我们已经得知,更新代码可以上传.lua文件到开发板的/usr/share/oui/rpc/目录下,那么我们开始写脚本进行文件上传。

开放WAN口SSH连接

一般来说,国内开发者都是在虚拟机内进行编程,然后通过开发板的WAN口和开发板进行网络通讯。所以我们在LuCI的管理界面上,添加SSH访问的 Dropbear 实例:

这里用到了让LuCI和Oui共存的知识,您可以参看《OpenWrt-同时使用LuCI和Oui》

系统->管理权->SSH访问->添加实例

添加一个WAN口可以连接的实例,保存并应用即可。

image.png

上传ssh key

由于我们会频繁访问开发板,所以我们使用ssh key的方式。首先我们生成公钥秘钥:

ssh-keygen -t ed25519 -C "[email protected]"

然后我们移动到 ~/.ssh 目录下执行:

ssh-copy-id -p 522 -i id_ed25519.pub [email protected]

在使用 ssh-copy-id 命令时,我们本地公钥会被添加到远程服务器的 ~/.ssh/authorized_keys 文件中。然后,当使用 SSH 连接到该服务器时,服务器会在客户端接受连接请求后,发送它自己的公钥。SSH会将服务器的公钥存在 ~/.ssh/known_hosts

如果你在开发过程中重新刷写了固件,记得进行移除 ~/.ssh/known_hosts 里之前的条目:

ssh-keygen -R [192.168.8.88]:522

lua文件上传脚本

我们移动到我们子项目,也就是oui-app-demo这样的目录下,然后移至固定存放 Lua 文件的目录files/rpc目录下新建一个update.sh文件,并写入:

#!/bin/bash

# 定义远程服务器地址、用户名和目标目录
REMOTE_SERVER=192.168.8.88
REMOTE_USER=root
REMOTE_DIR=/usr/share/oui/rpc/

# 找到并循环遍历当前目录下的所有.lua文件
for file in *.lua
do
    # 如果找到.lua文件,则将其复制到远程服务器
    if [ -f "$file" ]; then
        # 使用 scp 命令将文件复制到远程服务器,指定522端口
        scp -P 522 "$file" "$REMOTE_USER@$REMOTE_SERVER:$REMOTE_DIR"
        # 打印 scp 命令
        echo "scp $file $REMOTE_USER@$REMOTE_SERVER:$REMOTE_DIR"
        # 如果复制成功,则输出提示信息
        if [ $? -eq 0 ]; then
            echo "Uploaded $file to $REMOTE_SERVER:$REMOTE_DIR"
        fi
    fi
done
# 重启 oui-httpd 服务
ssh -p 522 "$REMOTE_USER@$REMOTE_SERVER" "service oui-httpd restart"

如此,我们就可以通过执行 bash 脚本更新负责 rpclua 代码了。

致谢

感谢一直陪伴我探索的大模型们,感谢oui的贡献者们。

变更记录

时间 变更内容 ...