🗽 Sync 2026-04-04 08:46:19

This commit is contained in:
github-actions[bot] 2026-04-04 08:46:19 +08:00
parent cc48a1d6ad
commit 874bea09f4
10 changed files with 258 additions and 105 deletions

View File

@ -4,6 +4,20 @@
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
## [2.0.3] - 2026-04-03
### 修复
- **微信插件安装失败 (exit: 127)**: 当 Gateway 未运行或路径检测失败时,`npx` 命令指向不存在的路径导致安装失败
- 根因: `get_actual_install_path()` 依赖 Gateway 进程运行时返回正确路径Gateway 未启动时返回默认值 `/opt/openclaw`,但实际安装路径可能是 `/mnt/data/openclaw` 等自定义路径
- 修复: `action_wechat_install()``action_wechat_upgrade_plugin()` 新增多层路径校验逻辑:
1. 先检查默认检测到的 `npx` 是否存在
2. 不存在时回退到 UCI 配置的 `install_path`
3. 最后遍历常见备用路径 (`/mnt/data/openclaw`, `/opt/openclaw`, `/overlay/upper/opt/openclaw`)
4. 所有路径都无效时返回明确的错误提示,而非模糊的 exit 127
---
## [2.0.2] - 2026-03-31
### 新增功能

View File

@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-openclaw
PKG_VERSION:=$(strip $(shell cat $(CURDIR)/VERSION 2>/dev/null || echo "1.0.0"))
PKG_RELEASE:=8
PKG_RELEASE:=9
PKG_MAINTAINER:=10000ge10000 <10000ge10000@users.noreply.github.com>
PKG_LICENSE:=GPL-3.0

View File

@ -1 +1 @@
2.0.2
2.0.3

View File

@ -52,62 +52,90 @@ function index()
entry({"admin", "services", "openclaw", "wechat_status"}, call("action_wechat_status"), nil).leaf = true
-- 微信插件安装 API (后台安装)
entry({"admin", "services", "openclaw", "wechat_install"}, call("action_wechat_install"), nil).leaf = true
entry({"admin", "services", "openclaw", "wechat_install"}, post("action_wechat_install"), nil).leaf = true
-- 微信安装日志轮询 API
entry({"admin", "services", "openclaw", "wechat_install_log"}, call("action_wechat_install_log"), nil).leaf = true
-- 微信登录 API (启动登录流程)
entry({"admin", "services", "openclaw", "wechat_login"}, call("action_wechat_login"), nil).leaf = true
entry({"admin", "services", "openclaw", "wechat_login"}, post("action_wechat_login"), nil).leaf = true
-- 微信登录状态/二维码 API
entry({"admin", "services", "openclaw", "wechat_login_status"}, call("action_wechat_login_status"), nil).leaf = true
-- 微信插件卸载 API
entry({"admin", "services", "openclaw", "wechat_uninstall"}, call("action_wechat_uninstall"), nil).leaf = true
entry({"admin", "services", "openclaw", "wechat_uninstall"}, post("action_wechat_uninstall"), nil).leaf = true
-- 微信插件检测升级 API
entry({"admin", "services", "openclaw", "wechat_check_upgrade"}, call("action_wechat_check_upgrade"), nil).leaf = true
-- 微信插件升级 API
entry({"admin", "services", "openclaw", "wechat_upgrade_plugin"}, call("action_wechat_upgrade_plugin"), nil).leaf = true
entry({"admin", "services", "openclaw", "wechat_upgrade_plugin"}, post("action_wechat_upgrade_plugin"), nil).leaf = true
-- 微信退出/删除账号 API
entry({"admin", "services", "openclaw", "wechat_logout"}, call("action_wechat_logout"), nil).leaf = true
entry({"admin", "services", "openclaw", "wechat_logout"}, post("action_wechat_logout"), nil).leaf = true
end-- ═══════════════════════════════════════════
-- 获取实际安装路径 (辅助函数)
-- 获取安装路径 (唯一权威来源: UCI 配置)
-- ═══════════════════════════════════════════
local function get_actual_install_path()
local sys = require "luci.sys"
-- 核心原则: 用户在安装时输入的路径是唯一的安装位置
-- - UCI install_path 存储用户输入的基础路径 (如 /mnt/data 或 /opt)
-- - 实际安装路径为 ${install_path}/openclaw
-- - 此函数始终返回 UCI 配置的路径,不做任何"智能"回退
-- ═══════════════════════════════════════════
local function get_install_path()
local uci = require "luci.model.uci".cursor()
-- 从 UCI 读取用户配置的基础路径,默认 /opt
local base_path = uci:get("openclaw", "main", "install_path") or "/opt"
-- 返回完整安装路径
return base_path .. "/openclaw"
end
-- 从 UCI 读取用户配置的基础路径
local install_path_uci = uci:get("openclaw", "main", "install_path") or "/opt"
local uci_install_path = install_path_uci .. "/openclaw"
-- 确保网关端口可用:检测占用并尝试优雅停止或强制杀死占用进程
local function ensure_port_free(port)
local sys = require "luci.sys"
if not port or port == "" then return end
-- 优先尝试使用 openclaw 自身的 stop 命令(如果已安装)
sys.exec("openclaw gateway stop >/dev/null 2>&1 || true")
-- 方法1: 从运行中的 gateway 进程检测实际路径
local actual_path = sys.exec("ps w 2>/dev/null | grep -E 'openclaw.*gateway|openclaw-gateway' | grep -v grep | head -1"):gsub("%s+", "")
if actual_path and actual_path ~= "" then
local matched_path = actual_path:match("(/[^ ]+)/data/")
if matched_path and nixio.fs.stat(matched_path .. "/data/.openclaw/openclaw.json", "type") then
return matched_path
-- 查询占用端口的行
local check_cmd = ""
if os.execute("command -v ss >/dev/null 2>&1") == 0 then
check_cmd = string.format("ss -tulnp 2>/dev/null | grep -E ':%%s ' || true", port)
else
check_cmd = string.format("netstat -tulnp 2>/dev/null | grep -E ':%%s ' || true", port)
end
local out = sys.exec(check_cmd)
out = out or ""
if out:match("%S") then
-- 尝试解析 pid
local pid = out:match("pid=(%d+)") or out:match(" (%d+)/") or out:match("/(%d+)")
pid = pid and pid:gsub("%s+", "") or nil
if pid and pid ~= "" then
-- 再次尝试优雅停止
sys.exec("openclaw gateway stop >/dev/null 2>&1 || true")
-- 发送 SIGTERM
sys.exec("kill -TERM " .. pid .. " >/dev/null 2>&1 || true")
-- 等待释放,最多等待 5 次(每次 1s
for i = 1,5 do
local still = sys.exec(check_cmd) or ""
if not still:match("%S") then break end
os.execute("sleep 1")
end
-- 如果仍然存在则强杀
local still2 = sys.exec(check_cmd) or ""
if still2:match("%S") then
sys.exec("kill -9 " .. pid .. " >/dev/null 2>&1 || true")
end
else
-- 未能解析 PID则尝试批量杀死关键进程名
sys.exec("pgrep -f openclaw-gateway 2>/dev/null | xargs -r kill -TERM 2>/dev/null || true")
os.execute("sleep 1")
local still3 = sys.exec(check_cmd) or ""
if still3:match("%S") then
sys.exec("pgrep -f openclaw-gateway 2>/dev/null | xargs -r kill -9 2>/dev/null || true")
end
end
end
-- 方法2: 检查常见安装位置 (优先检查实际存在的配置)
local possible_paths = {
uci_install_path, -- UCI 配置的路径
"/mnt/data/openclaw", -- 常见的外置存储路径
"/opt/openclaw", -- 默认路径
}
for _, p in ipairs(possible_paths) do
if nixio.fs.stat(p .. "/data/.openclaw/openclaw.json", "type") then
return p
end
end
-- 方法3: 如果都没找到,返回 UCI 配置的路径 (即使不存在,安装时会创建)
return uci_install_path
end
-- ═══════════════════════════════════════════
@ -122,8 +150,8 @@ function action_status()
local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
local enabled = uci:get("openclaw", "main", "enabled") or "0"
-- 使用 get_actual_install_path 获取实际安装路径
local install_path = get_actual_install_path()
-- 使用 get_install_path 获取安装路径 (唯一来源: UCI 配置)
local install_path = get_install_path()
-- 验证端口值为纯数字,防止命令注入
if not port:match("^%d+$") then port = "18789" end
@ -467,8 +495,19 @@ function action_uninstall()
-- 实际安装路径
local install_path = install_path_uci .. "/openclaw"
-- 停止服务
-- 1. 停止服务 (通过 init.d 正常流程)
sys.exec("/etc/init.d/openclaw stop >/dev/null 2>&1")
-- 2. 获取配置端口,确保在清理前端口对应的进程都被杀掉
local port = uci:get("openclaw", "main", "port") or "18789"
local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
-- 3. 终极无感化清理僵尸进程
sys.exec("for p in " .. port .. " " .. pty_port .. "; do pid=$(netstat -tulnp 2>/dev/null | grep \":$p \" | awk '{print $7}' | cut -d'/' -f1); [ -n \"$pid\" ] && kill -9 \"$pid\" 2>/dev/null; done")
sys.exec("ss -tulnp 2>/dev/null | awk '/:" .. port .. " |:" .. pty_port .. " /{print $NF}' | awk -F',' '{print $2}' | awk -F'=' '{print $2}' | xargs -r kill -9 2>/dev/null")
sys.exec("pgrep -f 'openclaw-gateway|web-pty.js' 2>/dev/null | xargs -r kill -9 2>/dev/null")
sys.exec("pgrep -u openclaw 2>/dev/null | xargs -r kill -9 2>/dev/null")
-- 禁用开机启动
sys.exec("/etc/init.d/openclaw disable 2>/dev/null")
-- 设置 UCI enabled=0
@ -1028,32 +1067,8 @@ function action_wechat_status()
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
-- 获取安装路径 (优先从运行环境检测)
local install_path_uci = uci:get("openclaw", "main", "install_path") or "/opt"
local install_path = install_path_uci .. "/openclaw"
-- 检测实际运行的安装路径 (通过查找 gateway 进程)
local actual_path = sys.exec("ps w 2>/dev/null | grep -E 'openclaw.*gateway|openclaw-gateway' | grep -v grep | head -1"):gsub("%s+", "")
if actual_path and actual_path ~= "" then
-- 从进程命令行提取路径
local matched_path = actual_path:match("(/[^ ]+)/data/")
if matched_path and nixio.fs.stat(matched_path, "type") then
install_path = matched_path
end
end
-- 也检查常见安装位置
local possible_paths = {
install_path,
"/mnt/data/openclaw",
"/opt/openclaw",
}
for _, p in ipairs(possible_paths) do
if nixio.fs.stat(p .. "/data/.openclaw/openclaw.json", "type") then
install_path = p
break
end
end
-- 使用统一的路径获取函数 (唯一来源: UCI 配置)
local install_path = get_install_path()
local result = {
plugin_installed = false,
@ -1135,8 +1150,10 @@ end
function action_wechat_install()
local http = require "luci.http"
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
local install_path = get_actual_install_path()
-- 使用统一的路径获取函数 (唯一来源: UCI 配置)
local install_path = get_install_path()
local node_bin = install_path .. "/node/bin/node"
local npx_bin = install_path .. "/node/bin/npx"
local oc_data = install_path .. "/data"
@ -1144,11 +1161,40 @@ function action_wechat_install()
-- 清理旧日志和状态
sys.exec("rm -f /tmp/openclaw-wechat-install.log /tmp/openclaw-wechat-install.pid /tmp/openclaw-wechat-install.exit")
-- 后台执行安装
-- 校验: 确保 npx 存在 (运行环境已安装)
if not nixio.fs.stat(npx_bin, "type") then
-- npx 不存在,运行环境未安装或路径配置错误
local log_content = "开始安装微信插件...\n" ..
"安装路径: " .. install_path .. "\n" ..
"❌ 错误: npx 命令不存在 (" .. npx_bin .. ")\n" ..
"请先在「基本设置」页面安装运行环境。\n" ..
"UCI install_path=" .. (uci:get("openclaw", "main", "install_path") or "未设置")
local f = io.open("/tmp/openclaw-wechat-install.log", "w")
if f then
f:write(log_content)
f:close()
end
local ef = io.open("/tmp/openclaw-wechat-install.exit", "w")
if ef then
ef:write("127")
ef:close()
end
http.prepare_content("application/json")
http.write_json({ status = "ok", message = "微信插件安装已在后台启动..." })
return
end
-- 后台执行安装
-- 在启动安装前,确保网关端口可用(自动清理残留 gateway 进程)
local port = uci:get("openclaw", "main", "port") or "18789"
ensure_port_free(port)
local install_cmd = string.format(
"( " ..
"echo '开始安装微信插件...' > /tmp/openclaw-wechat-install.log; " ..
"echo '安装路径: %s' >> /tmp/openclaw-wechat-install.log; " ..
"echo 'npx 路径: %s' >> /tmp/openclaw-wechat-install.log; " ..
-- 修复 npm 缓存目录权限 (避免 root 创建的缓存导致 openclaw 用户写入失败)
"chown -R openclaw:openclaw %s/.npm 2>/dev/null; " ..
"cd %s && " ..
"su -s /bin/sh openclaw -c 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw " ..
"PATH=%s/node/bin:%s/global/bin:$PATH " ..
@ -1157,7 +1203,7 @@ function action_wechat_install()
"if [ $RC -eq 0 ]; then echo '✅ 微信插件安装成功!' >> /tmp/openclaw-wechat-install.log; " ..
"else echo '❌ 安装失败 (exit: '$RC')' >> /tmp/openclaw-wechat-install.log; fi " ..
") & echo $! > /tmp/openclaw-wechat-install.pid",
install_path, oc_data, oc_data, oc_data, oc_data, install_path, install_path, npx_bin
install_path, npx_bin, oc_data, install_path, oc_data, oc_data, oc_data, install_path, install_path, npx_bin
) sys.exec(install_cmd)
http.prepare_content("application/json")
@ -1225,7 +1271,7 @@ function action_wechat_login()
local http = require "luci.http"
local sys = require "luci.sys"
local install_path = get_actual_install_path()
local install_path = get_install_path()
local node_bin = install_path .. "/node/bin/node"
local oc_data = install_path .. "/data"
local oc_entry = ""
@ -1353,13 +1399,10 @@ function action_wechat_uninstall()
local http = require "luci.http"
local sys = require "luci.sys"
local install_path = get_actual_install_path()
local install_path = get_install_path()
-- 删除微信插件目录
local wechat_ext_dir = install_path .. "/data/.openclaw/extensions/openclaw-weixin"
if nixio.fs.stat(wechat_ext_dir, "type") then
sys.exec("rm -rf " .. wechat_ext_dir)
end
-- 从配置中删除微信相关配置
local config_file = install_path .. "/data/.openclaw/openclaw.json"
@ -1398,7 +1441,7 @@ function action_wechat_check_upgrade()
local http = require "luci.http"
local sys = require "luci.sys"
local install_path = get_actual_install_path()
local install_path = get_install_path()
local npx_bin = install_path .. "/node/bin/npx"
local oc_data = install_path .. "/data"
@ -1445,19 +1488,51 @@ end
function action_wechat_upgrade_plugin()
local http = require "luci.http"
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
local install_path = get_actual_install_path()
-- 使用统一的路径获取函数 (唯一来源: UCI 配置)
local install_path = get_install_path()
local node_bin = install_path .. "/node/bin/node"
local npx_bin = install_path .. "/node/bin/npx"
local oc_data = install_path .. "/data"
-- 清理旧日志和状态
sys.exec("rm -f /tmp/openclaw-wechat-install.log /tmp/openclaw-wechat-install.pid /tmp/openclaw-wechat-install.exit")
-- 校验: 确保 npx 存在 (运行环境已安装)
if not nixio.fs.stat(npx_bin, "type") then
local log_content = "正在升级微信插件...\n" ..
"安装路径: " .. install_path .. "\n" ..
"❌ 错误: npx 命令不存在 (" .. npx_bin .. ")\n" ..
"请先检查运行环境是否正常安装。\n" ..
"UCI install_path=" .. (uci:get("openclaw", "main", "install_path") or "未设置")
local f = io.open("/tmp/openclaw-wechat-install.log", "w")
if f then
f:write(log_content)
f:close()
end
local ef = io.open("/tmp/openclaw-wechat-install.exit", "w")
if ef then
ef:write("127")
ef:close()
end
http.prepare_content("application/json")
http.write_json({ status = "ok", message = "微信插件升级已在后台启动..." })
return
end
-- 后台执行升级 (其实就是重新安装最新版)
local upgrade_cmd = string.format(
-- 在启动升级前,确保网关端口可用(自动清理残留 gateway 进程)
local port = uci:get("openclaw", "main", "port") or "18789"
ensure_port_free(port)
local upgrade_cmd = string.format(
"( " ..
"echo '正在升级微信插件...' > /tmp/openclaw-wechat-install.log; " ..
"echo '安装路径: %s' >> /tmp/openclaw-wechat-install.log; " ..
"echo 'npx 路径: %s' >> /tmp/openclaw-wechat-install.log; " ..
-- 修复 npm 缓存目录权限 (避免 root 创建的缓存导致 openclaw 用户写入失败)
"chown -R openclaw:openclaw %s/.npm 2>/dev/null; " ..
"cd %s && " ..
"su -s /bin/sh openclaw -c 'HOME=%s OPENCLAW_HOME=%s OPENCLAW_STATE_DIR=%s/.openclaw " ..
"PATH=%s/node/bin:%s/global/bin:$PATH " ..
@ -1466,7 +1541,7 @@ function action_wechat_upgrade_plugin()
"if [ $RC -eq 0 ]; then echo '✅ 微信插件升级成功!' >> /tmp/openclaw-wechat-install.log; " ..
"else echo '❌ 升级失败 (exit: '$RC')' >> /tmp/openclaw-wechat-install.log; fi " ..
") & echo $! > /tmp/openclaw-wechat-install.pid",
install_path, oc_data, oc_data, oc_data, oc_data, install_path, install_path, npx_bin
install_path, npx_bin, oc_data, install_path, oc_data, oc_data, oc_data, install_path, install_path, npx_bin
) sys.exec(upgrade_cmd)
http.prepare_content("application/json")
@ -1487,7 +1562,7 @@ function action_wechat_logout()
return
end
local install_path = get_actual_install_path()
local install_path = get_install_path()
local node_bin = install_path .. "/node/bin/node"
local oc_data = install_path .. "/data"
local oc_entry = ""

View File

@ -412,6 +412,7 @@ local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
<script type="text/javascript">
//<![CDATA[
(function() {
var csrfToken = '<%=token%>';
var statusUrl = '<%=luci.dispatcher.build_url("admin", "services", "openclaw", "status_api")%>';
var wechatStatusUrl = '<%=luci.dispatcher.build_url("admin", "services", "openclaw", "wechat_status")%>';
var wechatInstallUrl = '<%=luci.dispatcher.build_url("admin", "services", "openclaw", "wechat_install")%>';
@ -522,7 +523,7 @@ local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
document.getElementById('oc-install-log').textContent = '正在启动安装...\n';
// 启动安装
(new XHR()).post(wechatInstallUrl, null, function(x) {
(new XHR()).post(wechatInstallUrl, { token: csrfToken }, function(x) {
try {
var d = JSON.parse(x.responseText);
if (d.status === 'ok') {
@ -632,7 +633,7 @@ local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
document.getElementById('oc-login-result').style.display = 'none';
// 启动登录流程
(new XHR()).post(wechatLoginUrl, null, function(x) {
(new XHR()).post(wechatLoginUrl, { token: csrfToken }, function(x) {
try {
var d = JSON.parse(x.responseText);
if (d.status === 'ok') {
@ -653,7 +654,7 @@ local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
btn.textContent = '退出中...';
btn.disabled = true;
(new XHR()).post(wechatLogoutUrl + '?account=' + encodeURIComponent(accountId), null, function(x) {
(new XHR()).post(wechatLogoutUrl + '?account=' + encodeURIComponent(accountId), { token: csrfToken }, function(x) {
btn.textContent = oldText;
btn.disabled = false;
ocRefreshWechatStatus();
@ -833,7 +834,7 @@ local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
var upgradeUrl = '<%=luci.dispatcher.build_url("admin", "services", "openclaw", "wechat_upgrade_plugin")%>';
(new XHR()).post(upgradeUrl, null, function(x) {
(new XHR()).post(upgradeUrl, { token: csrfToken }, function(x) {
try {
var d = JSON.parse(x.responseText);
if (d.status === 'ok') {
@ -905,7 +906,7 @@ local pty_port = uci:get("openclaw", "main", "pty_port") or "18793"
btn.disabled = true;
btn.textContent = '⏳ 卸载中...';
(new XHR()).post('<%=luci.dispatcher.build_url("admin", "services", "openclaw", "wechat_uninstall")%>', null, function(x) {
(new XHR()).post('<%=luci.dispatcher.build_url("admin", "services", "openclaw", "wechat_uninstall")%>', { token: csrfToken }, function(x) {
try {
var d = JSON.parse(x.responseText);
if (d.status === 'ok') {

View File

@ -297,7 +297,7 @@ _ensure_port_free() {
if command -v ss >/dev/null 2>&1; then
port_pid=$(ss -tulnp 2>/dev/null | grep ":${p} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)
else
port_pid=$(netstat -tulnp 2>/dev/null | grep ":${p} " | sed -n 's|.* \([0-9]*\)/.*|\1|p' | head -1)
port_pid=$(netstat -tulnp 2>/dev/null | grep ":${p} " | awk '{print $7}' | cut -d'/' -f1 | grep -E '^[0-9]+$' | head -n 1)
fi
[ -n "$port_pid" ] && kill -9 "$port_pid" 2>/dev/null && usleep 300000 2>/dev/null
return 0
@ -392,17 +392,36 @@ done
# 4) 如果端口仍被占用,强制 SIGKILL
if [ $wait_count -ge $max_wait ]; then
local port_pid=""
if command -v ss >/dev/null 2>&1; then
port_pid=$(ss -tulnp 2>/dev/null | grep ":${port} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)
else
port_pid=$(netstat -tulnp 2>/dev/null | grep ":${port} " | sed -n 's|.* \([0-9]*\)/.*|\1|p' | head -1)
fi
if [ -n "$port_pid" ]; then
kill -9 "$port_pid" 2>/dev/null
# 等待内核回收 (缩短到 0.5 秒)
usleep 500000 2>/dev/null || sleep 0.5
fi
local port_pid=""
if command -v ss >/dev/null 2>&1; then
port_pid=$(ss -tulnp 2>/dev/null | grep ":${port} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)
else
port_pid=$(netstat -tulnp 2>/dev/null | grep ":${port} " | awk '{print $7}' | cut -d'/' -f1 | grep -E '^[0-9]+$' | head -n 1)
fi
if [ -n "$port_pid" ]; then
kill -9 "$port_pid" 2>/dev/null
# 等待内核回收 (缩短到 0.5 秒)
usleep 500000 2>/dev/null || sleep 0.5
fi
fi
# 5) 终极保险:使用暴力匹配端口杀死该名下任何相关占用进程
local _pty_port=$(uci -q get openclaw.main.pty_port || echo "18793")
for _p in "$port" "$_pty_port"; do
local _spid=""
if command -v ss >/dev/null 2>&1; then
_spid=$(ss -tulnp 2>/dev/null | grep ":${_p} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)
else
_spid=$(netstat -tulnp 2>/dev/null | grep ":${_p} " | awk '{print $7}' | cut -d'/' -f1 | grep -E '^[0-9]+$' | head -n 1)
fi
[ -n "$_spid" ] && kill -9 "$_spid" 2>/dev/null || true
done
# 6) 清理该特定系统用户下任何悬挂或离群孤儿进程
if getent passwd openclaw >/dev/null 2>&1 || grep -q '^openclaw:' /etc/passwd 2>/dev/null; then
for _opcup in $(pgrep -u openclaw 2>/dev/null); do
kill -9 "$_opcup" 2>/dev/null || true
done
fi
}

View File

@ -999,13 +999,14 @@ add_firewall_rule() {
$ipt_m -N PSW_RULE
$ipt_m -A PSW_RULE -j CONNMARK --restore-mark
$ipt_m -A PSW_RULE -m mark --mark ${FWMARK} -j RETURN
$ipt_m -A PSW_RULE -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j MARK --set-xmark ${FWMARK}
$ipt_m -A PSW_RULE -p udp -m conntrack --ctstate NEW -j MARK --set-xmark ${FWMARK}
$ipt_m -A PSW_RULE -p tcp -m tcp --syn -j MARK --set-xmark ${FWMARK}
$ipt_m -A PSW_RULE -p udp -m conntrack --ctstate NEW,RELATED -j MARK --set-xmark ${FWMARK}
$ipt_m -A PSW_RULE -j CONNMARK --save-mark
$ipt_m -N PSW
$ipt_m -A PSW $(dst $IPSET_LAN) -j RETURN
$ipt_m -A PSW $(dst $IPSET_VPS) -j RETURN
$ipt_m -A PSW -m conntrack --ctdir REPLY -j RETURN
[ ! -z "${WAN_IP}" ] && {
ipset -F $IPSET_WAN
@ -1024,7 +1025,6 @@ add_firewall_rule() {
$ipt_m -N PSW_OUTPUT
$ipt_m -A PSW_OUTPUT $(dst $IPSET_LAN) -j RETURN
$ipt_m -A PSW_OUTPUT $(dst $IPSET_VPS) -j RETURN
[ -n "$IPT_APPEND_DNS" ] && {
local local_dns dns_address dns_port
for local_dns in $(echo $IPT_APPEND_DNS | tr ',' ' '); do
@ -1044,6 +1044,7 @@ add_firewall_rule() {
[ "${USE_BLOCK_LIST}" = "1" ] && $ipt_m -A PSW_OUTPUT $(dst $IPSET_BLOCK) -j DROP
[ "${USE_DIRECT_LIST}" = "1" ] && $ipt_m -A PSW_OUTPUT $(dst $IPSET_WHITE) -j RETURN
$ipt_m -A PSW_OUTPUT -m conntrack --ctdir REPLY -j RETURN
$ipt_m -A PSW_OUTPUT -m mark --mark 255 -j RETURN
ip rule add fwmark ${FWMARK} lookup 999 priority 999
@ -1077,13 +1078,14 @@ add_firewall_rule() {
$ip6t_m -N PSW_RULE
$ip6t_m -A PSW_RULE -j CONNMARK --restore-mark
$ip6t_m -A PSW_RULE -m mark --mark ${FWMARK} -j RETURN
$ip6t_m -A PSW_RULE -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j MARK --set-xmark ${FWMARK}
$ip6t_m -A PSW_RULE -p udp -m conntrack --ctstate NEW -j MARK --set-xmark ${FWMARK}
$ip6t_m -A PSW_RULE -p tcp -m tcp --syn -j MARK --set-xmark ${FWMARK}
$ip6t_m -A PSW_RULE -p udp -m conntrack --ctstate NEW,RELATED -j MARK --set-xmark ${FWMARK}
$ip6t_m -A PSW_RULE -j CONNMARK --save-mark
$ip6t_m -N PSW
$ip6t_m -A PSW $(dst $IPSET_LAN6) -j RETURN
$ip6t_m -A PSW $(dst $IPSET_VPS6) -j RETURN
$ip6t_m -A PSW -m conntrack --ctdir REPLY -j RETURN
WAN6_IP=$(get_wan_ips ip6)
[ ! -z "${WAN6_IP}" ] && {
@ -1106,6 +1108,7 @@ add_firewall_rule() {
$ip6t_m -A PSW_OUTPUT $(dst $IPSET_VPS6) -j RETURN
[ "${USE_BLOCK_LIST}" = "1" ] && $ip6t_m -A PSW_OUTPUT $(dst $IPSET_BLOCK6) -j DROP
[ "${USE_DIRECT_LIST}" = "1" ] && $ip6t_m -A PSW_OUTPUT $(dst $IPSET_WHITE6) -j RETURN
$ip6t_m -A PSW_OUTPUT -m conntrack --ctdir REPLY -j RETURN
ip -6 rule add fwmark ${FWMARK} table 999 priority 999
ip -6 route add local ::/0 dev lo table 999

View File

@ -1037,14 +1037,15 @@ add_firewall_rule() {
nft "flush chain $NFTABLE_NAME PSW_MANGLE"
nft "add rule $NFTABLE_NAME PSW_MANGLE ip daddr @$NFTSET_LAN counter return"
nft "add rule $NFTABLE_NAME PSW_MANGLE ip daddr @$NFTSET_VPS counter return"
nft "add rule $NFTABLE_NAME PSW_MANGLE ct direction reply counter return"
nft "add chain $NFTABLE_NAME PSW_OUTPUT_MANGLE"
nft "flush chain $NFTABLE_NAME PSW_OUTPUT_MANGLE"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_LAN counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_VPS counter return"
[ "${USE_BLOCK_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_BLOCK counter drop"
[ "${USE_DIRECT_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_WHITE counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ct direction reply counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE meta mark 255 counter return"
# jump chains
@ -1105,6 +1106,7 @@ add_firewall_rule() {
nft "flush chain $NFTABLE_NAME PSW_MANGLE_V6"
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ip6 daddr @$NFTSET_LAN6 counter return"
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return"
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ct direction reply counter return"
nft "add chain $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6"
nft "flush chain $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6"
@ -1112,6 +1114,7 @@ add_firewall_rule() {
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return"
[ "${USE_BLOCK_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_BLOCK6 counter drop"
[ "${USE_DIRECT_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_WHITE6 counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ct direction reply counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta mark 255 counter return"
[ -n "$IPT_APPEND_DNS" ] && {

View File

@ -744,7 +744,7 @@ add_firewall_rule() {
}
$ipt_m -A PSW2_OUTPUT -m mark --mark 255 -j RETURN
ip rule add fwmark ${FWMARK} lookup 999 priority 999
ip rule add fwmark ${FWMARK} table 999 priority 999
ip route add local 0.0.0.0/0 dev lo table 999
[ "$accept_icmpv6" = "1" ] && {
@ -953,10 +953,10 @@ del_firewall_rule() {
done
done
ip rule del fwmark ${FWMARK} lookup 999 priority 999 2>/dev/null
ip rule del fwmark ${FWMARK} 2>/dev/null
ip route del local 0.0.0.0/0 dev lo table 999 2>/dev/null
ip -6 rule del fwmark ${FWMARK} table 999 priority 999 2>/dev/null
ip -6 rule del fwmark ${FWMARK} 2>/dev/null
ip -6 route del local ::/0 dev lo table 999 2>/dev/null
log_i18n 0 "Delete %s rules is complete." "iptables"

View File

@ -642,9 +642,35 @@ filter_direct_node_list() {
done
}
del_script_mwan3() {
[ -s "/etc/init.d/mwan3" ] && sed -i "/${CONFIG}/d" /etc/init.d/mwan3 >/dev/null 2>&1
}
add_script_mwan3() {
del_script_mwan3
[ -s "/etc/init.d/mwan3" ] && {
sed -i '/start_service()/,/}/ s/^}/ \/usr\/share\/passwall2\/nftables.sh mwan3_start\n}/' /etc/init.d/mwan3
sed -i '/stop_service().*{/a \ \/usr\/share\/passwall2\/nftables.sh mwan3_stop' /etc/init.d/mwan3
}
}
mwan3_stop() {
local handles=$(nft -a list chain ip mangle mwan3_hook 2>/dev/null | grep "${FWMARK}" | awk -F '# handle ' '{print$2}')
for handle in $handles; do
nft delete rule ip mangle mwan3_hook handle ${handle} 2>/dev/null
done
}
mwan3_start() {
mwan3_stop
nft list chain ip mangle mwan3_hook >/dev/null 2>&1 && nft insert rule ip mangle mwan3_hook ct mark ${FWMARK} counter return >/dev/null 2>&1
}
add_firewall_rule() {
log_i18n 0 "Starting to load %s firewall rules..." "nftables"
gen_nft_tables
add_script_mwan3
mwan3_start
gen_nftset $NFTSET_WAN ipv4_addr 0 "-1"
gen_nftset $NFTSET_LOCAL ipv4_addr 0 "-1"
gen_nftset $NFTSET_LAN ipv4_addr 0 "-1" $(gen_lanlist)
@ -798,7 +824,7 @@ add_firewall_rule() {
}
unset WAN_IP
ip rule add fwmark ${FWMARK} lookup 999 priority 999
ip rule add fwmark ${FWMARK} table 999 priority 999
ip route add local 0.0.0.0/0 dev lo table 999
#ipv6 tproxy mode and udp
@ -974,10 +1000,10 @@ del_firewall_rule() {
# Need to be removed at the end, otherwise it will show "Resource busy"
nft delete chain $NFTABLE_NAME handle $(nft -a list chains | grep -E "PSW2_RULE" | awk -F '# handle ' '{print$2}') 2>/dev/null
ip rule del fwmark ${FWMARK} lookup 999 priority 999 2>/dev/null
ip rule del fwmark ${FWMARK} 2>/dev/null
ip route del local 0.0.0.0/0 dev lo table 999 2>/dev/null
ip -6 rule del fwmark ${FWMARK} table 999 priority 999 2>/dev/null
ip -6 rule del fwmark ${FWMARK} 2>/dev/null
ip -6 route del local ::/0 dev lo table 999 2>/dev/null
destroy_nftset $NFTSET_LOCAL
@ -990,6 +1016,8 @@ del_firewall_rule() {
destroy_nftset $NFTSET_LAN6
destroy_nftset $NFTSET_VPS6
del_script_mwan3
log_i18n 0 "Delete %s rules is complete." "nftables"
}
@ -1071,6 +1099,12 @@ insert_nftset)
filter_direct_node_list)
filter_direct_node_list
;;
mwan3_start)
mwan3_start
;;
mwan3_stop)
mwan3_stop
;;
stop)
stop
;;
@ -1079,3 +1113,7 @@ start)
;;
*) ;;
esac
reload_service() {
restart
}