claw-subagent-service 0.0.139 → 0.0.141
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/command/linux/restart.sh +57 -38
- package/command/linux/stop.sh +509 -534
- package/package.json +1 -1
package/command/linux/restart.sh
CHANGED
|
@@ -187,51 +187,70 @@ restart_docker() {
|
|
|
187
187
|
# 如果正在运行,先停止(包括进程存在但端口未监听的情况)
|
|
188
188
|
if [ -n "$pid" ] || check_port "$PORT"; then
|
|
189
189
|
log_info "正在停止 OpenClaw 服务..."
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
190
|
+
|
|
191
|
+
# 第一步:优雅停止所有 openclaw 进程
|
|
192
|
+
log_info "发送 SIGTERM 到所有 openclaw 进程..."
|
|
193
|
+
pkill -15 -f "openclaw" &>/dev/null || true
|
|
194
|
+
sleep 3
|
|
195
|
+
|
|
196
|
+
# 第二步:检查是否还有进程在运行
|
|
197
|
+
local remaining_pids=""
|
|
198
|
+
if command -v pgrep &>/dev/null; then
|
|
199
|
+
remaining_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
if [ -n "$remaining_pids" ]; then
|
|
203
|
+
log_warn "进程仍在运行: $remaining_pids,发送 SIGKILL..."
|
|
204
|
+
pkill -9 -f "openclaw" &>/dev/null || true
|
|
205
|
+
sleep 3
|
|
200
206
|
fi
|
|
201
207
|
|
|
202
|
-
#
|
|
208
|
+
# 第三步:连续监控,确保所有进程都停止(防止看门狗重启)
|
|
209
|
+
log_info "进入连续监控模式,确保进程完全停止..."
|
|
203
210
|
local elapsed=0
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
current_pid=$(get_openclaw_pid)
|
|
207
|
-
if [ -z "$current_pid" ] && ! check_port "$PORT"; then
|
|
208
|
-
break
|
|
209
|
-
fi
|
|
211
|
+
local consecutive_empty=0
|
|
212
|
+
while [ $elapsed -lt 15 ]; do
|
|
210
213
|
sleep 1
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
local current_pid_after_wait
|
|
216
|
-
current_pid_after_wait=$(get_openclaw_pid)
|
|
217
|
-
if [ -n "$current_pid_after_wait" ] || check_port "$PORT"; then
|
|
218
|
-
log_warn "服务未在 10 秒内停止,正在强制停止..."
|
|
219
|
-
if [ -n "$current_pid_after_wait" ]; then
|
|
220
|
-
kill -9 "$current_pid_after_wait" &>/dev/null || true
|
|
221
|
-
fi
|
|
222
|
-
pkill -9 -f "openclaw" &>/dev/null || true
|
|
223
|
-
if command -v fuser &>/dev/null; then
|
|
224
|
-
fuser -k "${PORT}/tcp" &>/dev/null || true
|
|
214
|
+
|
|
215
|
+
local current_pids=""
|
|
216
|
+
if command -v pgrep &>/dev/null; then
|
|
217
|
+
current_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
225
218
|
fi
|
|
226
|
-
sleep 2
|
|
227
219
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
220
|
+
if [ -z "$current_pids" ] && ! check_port "$PORT"; then
|
|
221
|
+
consecutive_empty=$((consecutive_empty + 1))
|
|
222
|
+
log_info "第 $elapsed 秒: 无进程且端口未监听(连续 $consecutive_empty 次)"
|
|
223
|
+
if [ $consecutive_empty -ge 3 ]; then
|
|
224
|
+
log_info "服务已完全停止"
|
|
225
|
+
break
|
|
226
|
+
fi
|
|
227
|
+
else
|
|
228
|
+
consecutive_empty=0
|
|
229
|
+
if [ -n "$current_pids" ]; then
|
|
230
|
+
log_warn "第 $elapsed 秒: 发现进程 $current_pids,再次 kill..."
|
|
231
|
+
pkill -9 -f "openclaw" &>/dev/null || true
|
|
232
|
+
fi
|
|
233
|
+
if check_port "$PORT"; then
|
|
234
|
+
log_warn "第 $elapsed 秒: 端口仍在监听,使用 fuser..."
|
|
235
|
+
if command -v fuser &>/dev/null; then
|
|
236
|
+
fuser -k "${PORT}/tcp" &>/dev/null || true
|
|
237
|
+
fi
|
|
238
|
+
fi
|
|
234
239
|
fi
|
|
240
|
+
|
|
241
|
+
elapsed=$((elapsed + 1))
|
|
242
|
+
done
|
|
243
|
+
|
|
244
|
+
# 最终验证
|
|
245
|
+
local final_pids=""
|
|
246
|
+
if command -v pgrep &>/dev/null; then
|
|
247
|
+
final_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
if [ -n "$final_pids" ] || check_port "$PORT"; then
|
|
251
|
+
log_error "OpenClaw 服务停止失败!进程或端口仍在运行。"
|
|
252
|
+
log_error "剩余进程: $final_pids"
|
|
253
|
+
exit 1
|
|
235
254
|
fi
|
|
236
255
|
|
|
237
256
|
log_info "服务已停止"
|
package/command/linux/stop.sh
CHANGED
|
@@ -1,534 +1,509 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# OpenClaw 服务停止脚本
|
|
4
|
-
# 用法: ./stop.sh [选项]
|
|
5
|
-
# 支持 systemd 和 Docker(无 systemd)双模式
|
|
6
|
-
|
|
7
|
-
# 注意:不使用 set -e,因为我们已经实现了完善的错误处理和验证逻辑
|
|
8
|
-
# set -e 可能导致 pgrep/pidof 找不到进程时脚本意外退出
|
|
9
|
-
|
|
10
|
-
# 调试模式:记录每条执行的命令(用于排查问题)
|
|
11
|
-
# set -x
|
|
12
|
-
|
|
13
|
-
# 颜色定义
|
|
14
|
-
RED='\033[0;31m'
|
|
15
|
-
GREEN='\033[0;32m'
|
|
16
|
-
YELLOW='\033[1;33m'
|
|
17
|
-
NC='\033[0m' # No Color
|
|
18
|
-
|
|
19
|
-
# 日志函数
|
|
20
|
-
log_info() {
|
|
21
|
-
echo -e "${GREEN}[INFO]${NC} $1"
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
log_warn() {
|
|
25
|
-
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
log_error() {
|
|
29
|
-
echo -e "${RED}[ERROR]${NC} $1"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
# 服务名称
|
|
33
|
-
SERVICE_NAME="openclaw-gateway.service"
|
|
34
|
-
|
|
35
|
-
# 检测是否在 Docker 环境(无 systemd)
|
|
36
|
-
# 注意:某些 Docker 镜像安装了 systemctl 命令但无法使用
|
|
37
|
-
# 所以同时检查 systemd 是否实际运行
|
|
38
|
-
is_docker() {
|
|
39
|
-
# 方法1: 检查 systemctl 是否可用
|
|
40
|
-
if ! command -v systemctl &>/dev/null; then
|
|
41
|
-
return 0 # 无 systemctl,认为是 Docker
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
|
-
# 方法2: 即使安装了 systemctl,检查 systemd 是否实际运行
|
|
45
|
-
# 在 Docker 中,/run/systemd/system 通常不存在
|
|
46
|
-
if [ ! -d "/run/systemd/system" ] && [ ! -d "/sys/fs/cgroup/systemd" ]; then
|
|
47
|
-
return 0 # systemd 未运行,认为是 Docker
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
# 方法3: 尝试执行 systemctl status,如果失败则认为是 Docker
|
|
51
|
-
if ! systemctl status &>/dev/null; then
|
|
52
|
-
return 0 # systemctl 无法使用,认为是 Docker
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
return 1
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
# 端口号
|
|
59
|
-
PORT="18789"
|
|
60
|
-
|
|
61
|
-
# 检查端口是否监听
|
|
62
|
-
# 注意:只检查端口,不检查进程。进程存在不等于端口在监听。
|
|
63
|
-
check_port() {
|
|
64
|
-
local port=$1
|
|
65
|
-
if command -v ss &>/dev/null; then
|
|
66
|
-
ss -tln 2>/dev/null | grep -q ":$port "
|
|
67
|
-
return $?
|
|
68
|
-
elif command -v netstat &>/dev/null; then
|
|
69
|
-
netstat -tln 2>/dev/null | grep -q ":$port "
|
|
70
|
-
return $?
|
|
71
|
-
elif command -v lsof &>/dev/null; then
|
|
72
|
-
lsof -i :$port 2>/dev/null | grep -q LISTEN
|
|
73
|
-
return $?
|
|
74
|
-
elif command -v fuser &>/dev/null; then
|
|
75
|
-
fuser $port/tcp 2>/dev/null | grep -q '[0-9]'
|
|
76
|
-
return $?
|
|
77
|
-
fi
|
|
78
|
-
# 如果所有工具都不可用,无法准确检查端口,保守返回 1(端口未监听)
|
|
79
|
-
return 1
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
# 获取 openclaw 进程 PID
|
|
83
|
-
get_openclaw_pid() {
|
|
84
|
-
local port=18789
|
|
85
|
-
local pid=""
|
|
86
|
-
|
|
87
|
-
# 按优先级尝试多种工具(适配精简 Docker 镜像)
|
|
88
|
-
# 方法1: lsof(最可靠)
|
|
89
|
-
if command -v lsof &>/dev/null; then
|
|
90
|
-
pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
|
|
91
|
-
if [ -n "$pid" ]; then
|
|
92
|
-
echo "$pid"
|
|
93
|
-
return
|
|
94
|
-
fi
|
|
95
|
-
fi
|
|
96
|
-
|
|
97
|
-
# 方法2: fuser
|
|
98
|
-
if command -v fuser &>/dev/null; then
|
|
99
|
-
pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
|
|
100
|
-
if [ -n "$pid" ]; then
|
|
101
|
-
echo "$pid"
|
|
102
|
-
return
|
|
103
|
-
fi
|
|
104
|
-
fi
|
|
105
|
-
|
|
106
|
-
# 方法3: ss
|
|
107
|
-
if command -v ss &>/dev/null; then
|
|
108
|
-
pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
|
|
109
|
-
if [ -n "$pid" ]; then
|
|
110
|
-
echo "$pid"
|
|
111
|
-
return
|
|
112
|
-
fi
|
|
113
|
-
fi
|
|
114
|
-
|
|
115
|
-
# 方法4: netstat
|
|
116
|
-
if command -v netstat &>/dev/null; then
|
|
117
|
-
pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
|
|
118
|
-
if [ -n "$pid" ]; then
|
|
119
|
-
echo "$pid"
|
|
120
|
-
return
|
|
121
|
-
fi
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
|
-
# 方法5: 通过 /proc/net/tcp 查找(不需要外部工具)
|
|
125
|
-
# 端口 18789 的十六进制 = 0x4965
|
|
126
|
-
local hex_port="4965"
|
|
127
|
-
for proc_dir in /proc/[0-9]*; do
|
|
128
|
-
if [ -f "$proc_dir/net/tcp" ]; then
|
|
129
|
-
# 检查该进程是否监听目标端口
|
|
130
|
-
if grep -q "[^0-9a-fA-F]${hex_port} " "$proc_dir/net/tcp" 2>/dev/null; then
|
|
131
|
-
basename "$proc_dir"
|
|
132
|
-
return
|
|
133
|
-
fi
|
|
134
|
-
fi
|
|
135
|
-
done
|
|
136
|
-
|
|
137
|
-
# 方法6: 通过进程名查找(当服务未监听预期端口时)
|
|
138
|
-
# 使用 pgrep/pidof/ps 查找 openclaw 进程
|
|
139
|
-
if command -v pgrep &>/dev/null; then
|
|
140
|
-
pid=$(pgrep -f "openclaw" | head -1)
|
|
141
|
-
if [ -n "$pid" ]; then
|
|
142
|
-
echo "$pid"
|
|
143
|
-
return
|
|
144
|
-
fi
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
if command -v pidof &>/dev/null; then
|
|
148
|
-
pid=$(pidof openclaw | awk '{print $1}')
|
|
149
|
-
if [ -n "$pid" ]; then
|
|
150
|
-
echo "$pid"
|
|
151
|
-
return
|
|
152
|
-
fi
|
|
153
|
-
fi
|
|
154
|
-
|
|
155
|
-
# 最后尝试 ps
|
|
156
|
-
pid=$(ps aux | grep -v grep | grep "openclaw" | head -1 | awk '{print $2}')
|
|
157
|
-
if [ -n "$pid" ]; then
|
|
158
|
-
echo "$pid"
|
|
159
|
-
return
|
|
160
|
-
fi
|
|
161
|
-
|
|
162
|
-
echo ""
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
# Docker 模式:停止服务
|
|
166
|
-
# 参数: $1 = force (1=强制停止, 空=优雅停止)
|
|
167
|
-
stop_docker() {
|
|
168
|
-
local force="$1"
|
|
169
|
-
log_info "=== OpenClaw Docker 停止模式 ==="
|
|
170
|
-
log_info "检查 OpenClaw 服务状态..."
|
|
171
|
-
|
|
172
|
-
# 获取所有 openclaw 进程 PID
|
|
173
|
-
local all_pids=""
|
|
174
|
-
if command -v pgrep &>/dev/null; then
|
|
175
|
-
all_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
176
|
-
log_info "使用 pgrep 查找进程: $all_pids"
|
|
177
|
-
elif command -v pidof &>/dev/null; then
|
|
178
|
-
all_pids=$(pidof openclaw)
|
|
179
|
-
log_info "使用 pidof 查找进程: $all_pids"
|
|
180
|
-
else
|
|
181
|
-
all_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
|
|
182
|
-
log_info "使用 ps 查找进程: $all_pids"
|
|
183
|
-
fi
|
|
184
|
-
|
|
185
|
-
local pid
|
|
186
|
-
pid=$(get_openclaw_pid)
|
|
187
|
-
|
|
188
|
-
# 检查服务状态
|
|
189
|
-
if [ -z "$pid" ] && [ -z "$all_pids" ]; then
|
|
190
|
-
# 没有进程,检查端口
|
|
191
|
-
if check_port "$PORT"; then
|
|
192
|
-
log_warn "端口 $PORT 仍在监听,但无法获取 PID,尝试备选停止方案..."
|
|
193
|
-
# 尝试通过 fuser 直接通过端口杀进程
|
|
194
|
-
if command -v fuser &>/dev/null; then
|
|
195
|
-
log_info "使用 fuser 通过端口停止服务..."
|
|
196
|
-
fuser -k "${PORT}/tcp" &>/dev/null || true
|
|
197
|
-
sleep 2
|
|
198
|
-
fi
|
|
199
|
-
# 尝试通过 pkill 停止 openclaw 相关进程
|
|
200
|
-
if check_port "$PORT"; then
|
|
201
|
-
log_info "使用 pkill 停止 openclaw 进程..."
|
|
202
|
-
pkill -9 -f "openclaw" &>/dev/null || true
|
|
203
|
-
sleep 2
|
|
204
|
-
fi
|
|
205
|
-
fi
|
|
206
|
-
|
|
207
|
-
# 再次检查进程和端口
|
|
208
|
-
local final_check_pid
|
|
209
|
-
final_check_pid=$(get_openclaw_pid)
|
|
210
|
-
local final_check_port=false
|
|
211
|
-
if check_port "$PORT"; then
|
|
212
|
-
final_check_port=true
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
if [ -z "$final_check_pid" ] && [ "$final_check_port" = false ]; then
|
|
216
|
-
log_warn "OpenClaw 服务未在运行。"
|
|
217
|
-
exit 0
|
|
218
|
-
else
|
|
219
|
-
if [ -n "$final_check_pid" ]; then
|
|
220
|
-
log_error "OpenClaw 服务停止失败!进程仍然存在: $final_check_pid"
|
|
221
|
-
fi
|
|
222
|
-
if [ "$final_check_port" = true ]; then
|
|
223
|
-
log_error "OpenClaw 服务停止失败!端口 $PORT 仍在监听。"
|
|
224
|
-
fi
|
|
225
|
-
exit 1
|
|
226
|
-
fi
|
|
227
|
-
fi
|
|
228
|
-
|
|
229
|
-
log_info "发现 OpenClaw 进程: $all_pids"
|
|
230
|
-
|
|
231
|
-
# 如果不是强制模式,先尝试优雅停止
|
|
232
|
-
if [ -z "$force" ]; then
|
|
233
|
-
log_info "正在优雅停止 OpenClaw 服务..."
|
|
234
|
-
# 尝试发送 SIGTERM
|
|
235
|
-
for p in $all_pids; do
|
|
236
|
-
log_info "执行: kill -15 $p"
|
|
237
|
-
kill -15 "$p" 2>/dev/null || log_warn "kill -15 $p 失败"
|
|
238
|
-
done
|
|
239
|
-
|
|
240
|
-
# 等待 5 秒让服务优雅退出
|
|
241
|
-
log_info "等待 5 秒让服务优雅退出..."
|
|
242
|
-
sleep 5
|
|
243
|
-
|
|
244
|
-
# 检查是否已停止
|
|
245
|
-
local pid_after_graceful
|
|
246
|
-
pid_after_graceful=$(get_openclaw_pid)
|
|
247
|
-
if [ -z "$pid_after_graceful" ] && ! check_port "$PORT"; then
|
|
248
|
-
log_info "OpenClaw 服务已通过优雅停止退出。"
|
|
249
|
-
log_info "服务已成功停止。"
|
|
250
|
-
log_info "Success"
|
|
251
|
-
exit 0
|
|
252
|
-
fi
|
|
253
|
-
|
|
254
|
-
log_warn "优雅停止失败,服务仍在运行,切换到强制停止..."
|
|
255
|
-
fi
|
|
256
|
-
|
|
257
|
-
# 强制停止模式(直接发送 SIGKILL)
|
|
258
|
-
log_info "正在强制停止 OpenClaw 服务(SIGKILL)..."
|
|
259
|
-
|
|
260
|
-
# 获取最新的进程列表(因为优雅停止后可能有新进程)
|
|
261
|
-
local current_pids=""
|
|
262
|
-
if command -v pgrep &>/dev/null; then
|
|
263
|
-
current_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
264
|
-
elif command -v pidof &>/dev/null; then
|
|
265
|
-
current_pids=$(pidof openclaw)
|
|
266
|
-
else
|
|
267
|
-
current_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
|
|
268
|
-
fi
|
|
269
|
-
|
|
270
|
-
log_info "当前 openclaw 进程: $current_pids"
|
|
271
|
-
|
|
272
|
-
for p in $current_pids; do
|
|
273
|
-
log_info "执行: kill -9 $p"
|
|
274
|
-
kill -9 "$p" 2>/dev/null || log_warn "kill -9 $p 失败"
|
|
275
|
-
done
|
|
276
|
-
# 额外使用 pkill 确保所有相关进程都被停止
|
|
277
|
-
log_info "执行: pkill -9 -f openclaw"
|
|
278
|
-
pkill -9 -f "openclaw" 2>/dev/null || log_warn "pkill 失败"
|
|
279
|
-
log_info "执行: killall -9 openclaw"
|
|
280
|
-
killall -9 openclaw 2>/dev/null || log_warn "killall 失败"
|
|
281
|
-
|
|
282
|
-
# 等待 2 秒让进程退出
|
|
283
|
-
sleep 2
|
|
284
|
-
|
|
285
|
-
# 连续监控模式:每秒检查并杀死看门狗重启的进程
|
|
286
|
-
# 这样即使看门狗立即重启,也会被再次杀死
|
|
287
|
-
log_info "进入连续监控模式(最多 20 秒),防止看门狗自动重启..."
|
|
288
|
-
local elapsed=0
|
|
289
|
-
local consecutive_empty=0
|
|
290
|
-
while [ $elapsed -lt 20 ]; do
|
|
291
|
-
sleep 1
|
|
292
|
-
|
|
293
|
-
# 检查是否还有 openclaw 进程
|
|
294
|
-
local remaining_pids=""
|
|
295
|
-
if command -v pgrep &>/dev/null; then
|
|
296
|
-
remaining_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
297
|
-
elif command -v pidof &>/dev/null; then
|
|
298
|
-
remaining_pids=$(pidof openclaw)
|
|
299
|
-
else
|
|
300
|
-
remaining_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
|
|
301
|
-
fi
|
|
302
|
-
|
|
303
|
-
# 同时检查端口是否仍在监听
|
|
304
|
-
local port_listening=false
|
|
305
|
-
if check_port "$PORT"; then
|
|
306
|
-
port_listening=true
|
|
307
|
-
fi
|
|
308
|
-
|
|
309
|
-
if [ -z "$remaining_pids" ] && [ "$port_listening" = false ]; then
|
|
310
|
-
consecutive_empty=$((consecutive_empty + 1))
|
|
311
|
-
log_info "第 $elapsed 秒: 无 openclaw 进程且端口未监听(连续 $consecutive_empty 次)"
|
|
312
|
-
# 连续 3 秒没有进程且端口未监听,认为已停止
|
|
313
|
-
if [ $consecutive_empty -ge 3 ]; then
|
|
314
|
-
log_info "OpenClaw 服务已停止。(看门狗已放弃重启)"
|
|
315
|
-
log_info "服务已成功停止。"
|
|
316
|
-
log_info "Success"
|
|
317
|
-
exit 0
|
|
318
|
-
fi
|
|
319
|
-
else
|
|
320
|
-
consecutive_empty=0
|
|
321
|
-
if [ -n "$remaining_pids" ]; then
|
|
322
|
-
log_info "第 $elapsed 秒: 发现新进程 $remaining_pids,再次 kill..."
|
|
323
|
-
for rp in $remaining_pids; do
|
|
324
|
-
kill -9 "$rp" 2>/dev/null || true
|
|
325
|
-
done
|
|
326
|
-
fi
|
|
327
|
-
if [ "$port_listening" = true ]; then
|
|
328
|
-
log_info "第 $elapsed 秒: 端口 $PORT 仍在监听,尝试 fuser 终止..."
|
|
329
|
-
if command -v fuser &>/dev/null; then
|
|
330
|
-
fuser -k "${PORT}/tcp" 2>/dev/null || true
|
|
331
|
-
fi
|
|
332
|
-
fi
|
|
333
|
-
pkill -9 -f "openclaw" 2>/dev/null || true
|
|
334
|
-
killall -9 openclaw 2>/dev/null || true
|
|
335
|
-
fi
|
|
336
|
-
|
|
337
|
-
elapsed=$((elapsed + 1))
|
|
338
|
-
done
|
|
339
|
-
|
|
340
|
-
# 最终验证前,再执行一次全面清理
|
|
341
|
-
log_info "执行最终清理..."
|
|
342
|
-
pkill -9 -f "openclaw" 2>/dev/null || true
|
|
343
|
-
killall -9 openclaw 2>/dev/null || true
|
|
344
|
-
|
|
345
|
-
# 等待 2 秒
|
|
346
|
-
sleep 2
|
|
347
|
-
|
|
348
|
-
# 最终验证
|
|
349
|
-
local remaining_pids=""
|
|
350
|
-
if command -v pgrep &>/dev/null; then
|
|
351
|
-
remaining_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
352
|
-
elif command -v pidof &>/dev/null; then
|
|
353
|
-
remaining_pids=$(pidof openclaw)
|
|
354
|
-
else
|
|
355
|
-
remaining_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
|
|
356
|
-
fi
|
|
357
|
-
|
|
358
|
-
# 最终验证:显示当前所有进程
|
|
359
|
-
log_info "最终验证: 当前 openclaw 进程: $remaining_pids"
|
|
360
|
-
log_info "最终验证: 当前端口状态:"
|
|
361
|
-
if command -v netstat &>/dev/null; then
|
|
362
|
-
netstat -tlnp 2>/dev/null | grep ":${PORT} " || log_info "端口 ${PORT} 未监听"
|
|
363
|
-
elif command -v ss &>/dev/null; then
|
|
364
|
-
ss -tlnp 2>/dev/null | grep ":${PORT} " || log_info "端口 ${PORT} 未监听"
|
|
365
|
-
fi
|
|
366
|
-
|
|
367
|
-
# 最终验证:同时检查进程和端口
|
|
368
|
-
local port_listening_final=false
|
|
369
|
-
if check_port "$PORT"; then
|
|
370
|
-
port_listening_final=true
|
|
371
|
-
fi
|
|
372
|
-
|
|
373
|
-
if [ -z "$remaining_pids" ] && [ "$port_listening_final" = false ]; then
|
|
374
|
-
log_info "OpenClaw 服务已停止。"
|
|
375
|
-
log_info "服务已成功停止。"
|
|
376
|
-
log_info "Success"
|
|
377
|
-
exit 0
|
|
378
|
-
else
|
|
379
|
-
if [ -n "$remaining_pids" ]; then
|
|
380
|
-
log_error "OpenClaw 服务停止失败!进程仍然存在: $remaining_pids"
|
|
381
|
-
fi
|
|
382
|
-
if [ "$port_listening_final" = true ]; then
|
|
383
|
-
log_error "OpenClaw 服务停止失败!端口 $PORT 仍在监听。"
|
|
384
|
-
fi
|
|
385
|
-
exit 1
|
|
386
|
-
fi
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
# Systemd 模式:停止服务
|
|
390
|
-
# 参数: $1 = force (1=强制停止, 空=优雅停止)
|
|
391
|
-
stop_systemd() {
|
|
392
|
-
local force="$1"
|
|
393
|
-
|
|
394
|
-
# 检查服务是否存在
|
|
395
|
-
if ! systemctl --user list-unit-files "$SERVICE_NAME" &>/dev/null; then
|
|
396
|
-
log_error "服务 $SERVICE_NAME 不存在。"
|
|
397
|
-
exit 1
|
|
398
|
-
fi
|
|
399
|
-
|
|
400
|
-
# 检查服务状态
|
|
401
|
-
log_info "检查 OpenClaw 服务状态..."
|
|
402
|
-
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
|
403
|
-
log_info "OpenClaw 服务正在运行,准备停止..."
|
|
404
|
-
else
|
|
405
|
-
log_warn "OpenClaw 服务未在运行。"
|
|
406
|
-
exit 0
|
|
407
|
-
fi
|
|
408
|
-
|
|
409
|
-
# 停止服务
|
|
410
|
-
log_info "正在停止 OpenClaw 服务..."
|
|
411
|
-
|
|
412
|
-
if systemctl --user stop "$SERVICE_NAME"; then
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
#
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
#
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
fi
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
-f|--force)
|
|
511
|
-
force="1"
|
|
512
|
-
shift
|
|
513
|
-
;;
|
|
514
|
-
-h|--help)
|
|
515
|
-
show_help
|
|
516
|
-
exit 0
|
|
517
|
-
;;
|
|
518
|
-
*)
|
|
519
|
-
log_error "未知选项: $1"
|
|
520
|
-
show_help
|
|
521
|
-
exit 1
|
|
522
|
-
;;
|
|
523
|
-
esac
|
|
524
|
-
done
|
|
525
|
-
|
|
526
|
-
if is_docker; then
|
|
527
|
-
stop_docker "$force"
|
|
528
|
-
else
|
|
529
|
-
stop_systemd "$force"
|
|
530
|
-
fi
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
# 执行主函数
|
|
534
|
-
main "$@"
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# OpenClaw 服务停止脚本
|
|
4
|
+
# 用法: ./stop.sh [选项]
|
|
5
|
+
# 支持 systemd 和 Docker(无 systemd)双模式
|
|
6
|
+
|
|
7
|
+
# 注意:不使用 set -e,因为我们已经实现了完善的错误处理和验证逻辑
|
|
8
|
+
# set -e 可能导致 pgrep/pidof 找不到进程时脚本意外退出
|
|
9
|
+
|
|
10
|
+
# 调试模式:记录每条执行的命令(用于排查问题)
|
|
11
|
+
# set -x
|
|
12
|
+
|
|
13
|
+
# 颜色定义
|
|
14
|
+
RED='\033[0;31m'
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
YELLOW='\033[1;33m'
|
|
17
|
+
NC='\033[0m' # No Color
|
|
18
|
+
|
|
19
|
+
# 日志函数
|
|
20
|
+
log_info() {
|
|
21
|
+
echo -e "${GREEN}[INFO]${NC} $1"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
log_warn() {
|
|
25
|
+
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
log_error() {
|
|
29
|
+
echo -e "${RED}[ERROR]${NC} $1"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# 服务名称
|
|
33
|
+
SERVICE_NAME="openclaw-gateway.service"
|
|
34
|
+
|
|
35
|
+
# 检测是否在 Docker 环境(无 systemd)
|
|
36
|
+
# 注意:某些 Docker 镜像安装了 systemctl 命令但无法使用
|
|
37
|
+
# 所以同时检查 systemd 是否实际运行
|
|
38
|
+
is_docker() {
|
|
39
|
+
# 方法1: 检查 systemctl 是否可用
|
|
40
|
+
if ! command -v systemctl &>/dev/null; then
|
|
41
|
+
return 0 # 无 systemctl,认为是 Docker
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# 方法2: 即使安装了 systemctl,检查 systemd 是否实际运行
|
|
45
|
+
# 在 Docker 中,/run/systemd/system 通常不存在
|
|
46
|
+
if [ ! -d "/run/systemd/system" ] && [ ! -d "/sys/fs/cgroup/systemd" ]; then
|
|
47
|
+
return 0 # systemd 未运行,认为是 Docker
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# 方法3: 尝试执行 systemctl status,如果失败则认为是 Docker
|
|
51
|
+
if ! systemctl status &>/dev/null; then
|
|
52
|
+
return 0 # systemctl 无法使用,认为是 Docker
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
return 1
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# 端口号
|
|
59
|
+
PORT="18789"
|
|
60
|
+
|
|
61
|
+
# 检查端口是否监听
|
|
62
|
+
# 注意:只检查端口,不检查进程。进程存在不等于端口在监听。
|
|
63
|
+
check_port() {
|
|
64
|
+
local port=$1
|
|
65
|
+
if command -v ss &>/dev/null; then
|
|
66
|
+
ss -tln 2>/dev/null | grep -q ":$port "
|
|
67
|
+
return $?
|
|
68
|
+
elif command -v netstat &>/dev/null; then
|
|
69
|
+
netstat -tln 2>/dev/null | grep -q ":$port "
|
|
70
|
+
return $?
|
|
71
|
+
elif command -v lsof &>/dev/null; then
|
|
72
|
+
lsof -i :$port 2>/dev/null | grep -q LISTEN
|
|
73
|
+
return $?
|
|
74
|
+
elif command -v fuser &>/dev/null; then
|
|
75
|
+
fuser $port/tcp 2>/dev/null | grep -q '[0-9]'
|
|
76
|
+
return $?
|
|
77
|
+
fi
|
|
78
|
+
# 如果所有工具都不可用,无法准确检查端口,保守返回 1(端口未监听)
|
|
79
|
+
return 1
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# 获取 openclaw 进程 PID
|
|
83
|
+
get_openclaw_pid() {
|
|
84
|
+
local port=18789
|
|
85
|
+
local pid=""
|
|
86
|
+
|
|
87
|
+
# 按优先级尝试多种工具(适配精简 Docker 镜像)
|
|
88
|
+
# 方法1: lsof(最可靠)
|
|
89
|
+
if command -v lsof &>/dev/null; then
|
|
90
|
+
pid=$(lsof -i :${port} -t 2>/dev/null | head -1)
|
|
91
|
+
if [ -n "$pid" ]; then
|
|
92
|
+
echo "$pid"
|
|
93
|
+
return
|
|
94
|
+
fi
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# 方法2: fuser
|
|
98
|
+
if command -v fuser &>/dev/null; then
|
|
99
|
+
pid=$(fuser ${port}/tcp 2>/dev/null | tr -d ' ')
|
|
100
|
+
if [ -n "$pid" ]; then
|
|
101
|
+
echo "$pid"
|
|
102
|
+
return
|
|
103
|
+
fi
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# 方法3: ss
|
|
107
|
+
if command -v ss &>/dev/null; then
|
|
108
|
+
pid=$(ss -tlnp 2>/dev/null | grep ":${port} " | head -1 | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
|
|
109
|
+
if [ -n "$pid" ]; then
|
|
110
|
+
echo "$pid"
|
|
111
|
+
return
|
|
112
|
+
fi
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# 方法4: netstat
|
|
116
|
+
if command -v netstat &>/dev/null; then
|
|
117
|
+
pid=$(netstat -tnlp 2>/dev/null | grep ":${port} " | head -1 | awk '{print $7}' | cut -d'/' -f1)
|
|
118
|
+
if [ -n "$pid" ]; then
|
|
119
|
+
echo "$pid"
|
|
120
|
+
return
|
|
121
|
+
fi
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# 方法5: 通过 /proc/net/tcp 查找(不需要外部工具)
|
|
125
|
+
# 端口 18789 的十六进制 = 0x4965
|
|
126
|
+
local hex_port="4965"
|
|
127
|
+
for proc_dir in /proc/[0-9]*; do
|
|
128
|
+
if [ -f "$proc_dir/net/tcp" ]; then
|
|
129
|
+
# 检查该进程是否监听目标端口
|
|
130
|
+
if grep -q "[^0-9a-fA-F]${hex_port} " "$proc_dir/net/tcp" 2>/dev/null; then
|
|
131
|
+
basename "$proc_dir"
|
|
132
|
+
return
|
|
133
|
+
fi
|
|
134
|
+
fi
|
|
135
|
+
done
|
|
136
|
+
|
|
137
|
+
# 方法6: 通过进程名查找(当服务未监听预期端口时)
|
|
138
|
+
# 使用 pgrep/pidof/ps 查找 openclaw 进程
|
|
139
|
+
if command -v pgrep &>/dev/null; then
|
|
140
|
+
pid=$(pgrep -f "openclaw" | head -1)
|
|
141
|
+
if [ -n "$pid" ]; then
|
|
142
|
+
echo "$pid"
|
|
143
|
+
return
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
if command -v pidof &>/dev/null; then
|
|
148
|
+
pid=$(pidof openclaw | awk '{print $1}')
|
|
149
|
+
if [ -n "$pid" ]; then
|
|
150
|
+
echo "$pid"
|
|
151
|
+
return
|
|
152
|
+
fi
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# 最后尝试 ps
|
|
156
|
+
pid=$(ps aux | grep -v grep | grep "openclaw" | head -1 | awk '{print $2}')
|
|
157
|
+
if [ -n "$pid" ]; then
|
|
158
|
+
echo "$pid"
|
|
159
|
+
return
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
echo ""
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# Docker 模式:停止服务
|
|
166
|
+
# 参数: $1 = force (1=强制停止, 空=优雅停止)
|
|
167
|
+
stop_docker() {
|
|
168
|
+
local force="$1"
|
|
169
|
+
log_info "=== OpenClaw Docker 停止模式 ==="
|
|
170
|
+
log_info "检查 OpenClaw 服务状态..."
|
|
171
|
+
|
|
172
|
+
# 获取所有 openclaw 进程 PID
|
|
173
|
+
local all_pids=""
|
|
174
|
+
if command -v pgrep &>/dev/null; then
|
|
175
|
+
all_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
176
|
+
log_info "使用 pgrep 查找进程: $all_pids"
|
|
177
|
+
elif command -v pidof &>/dev/null; then
|
|
178
|
+
all_pids=$(pidof openclaw)
|
|
179
|
+
log_info "使用 pidof 查找进程: $all_pids"
|
|
180
|
+
else
|
|
181
|
+
all_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
|
|
182
|
+
log_info "使用 ps 查找进程: $all_pids"
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
local pid
|
|
186
|
+
pid=$(get_openclaw_pid)
|
|
187
|
+
|
|
188
|
+
# 检查服务状态
|
|
189
|
+
if [ -z "$pid" ] && [ -z "$all_pids" ]; then
|
|
190
|
+
# 没有进程,检查端口
|
|
191
|
+
if check_port "$PORT"; then
|
|
192
|
+
log_warn "端口 $PORT 仍在监听,但无法获取 PID,尝试备选停止方案..."
|
|
193
|
+
# 尝试通过 fuser 直接通过端口杀进程
|
|
194
|
+
if command -v fuser &>/dev/null; then
|
|
195
|
+
log_info "使用 fuser 通过端口停止服务..."
|
|
196
|
+
fuser -k "${PORT}/tcp" &>/dev/null || true
|
|
197
|
+
sleep 2
|
|
198
|
+
fi
|
|
199
|
+
# 尝试通过 pkill 停止 openclaw 相关进程
|
|
200
|
+
if check_port "$PORT"; then
|
|
201
|
+
log_info "使用 pkill 停止 openclaw 进程..."
|
|
202
|
+
pkill -9 -f "openclaw" &>/dev/null || true
|
|
203
|
+
sleep 2
|
|
204
|
+
fi
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
# 再次检查进程和端口
|
|
208
|
+
local final_check_pid
|
|
209
|
+
final_check_pid=$(get_openclaw_pid)
|
|
210
|
+
local final_check_port=false
|
|
211
|
+
if check_port "$PORT"; then
|
|
212
|
+
final_check_port=true
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
if [ -z "$final_check_pid" ] && [ "$final_check_port" = false ]; then
|
|
216
|
+
log_warn "OpenClaw 服务未在运行。"
|
|
217
|
+
exit 0
|
|
218
|
+
else
|
|
219
|
+
if [ -n "$final_check_pid" ]; then
|
|
220
|
+
log_error "OpenClaw 服务停止失败!进程仍然存在: $final_check_pid"
|
|
221
|
+
fi
|
|
222
|
+
if [ "$final_check_port" = true ]; then
|
|
223
|
+
log_error "OpenClaw 服务停止失败!端口 $PORT 仍在监听。"
|
|
224
|
+
fi
|
|
225
|
+
exit 1
|
|
226
|
+
fi
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
log_info "发现 OpenClaw 进程: $all_pids"
|
|
230
|
+
|
|
231
|
+
# 如果不是强制模式,先尝试优雅停止
|
|
232
|
+
if [ -z "$force" ]; then
|
|
233
|
+
log_info "正在优雅停止 OpenClaw 服务..."
|
|
234
|
+
# 尝试发送 SIGTERM
|
|
235
|
+
for p in $all_pids; do
|
|
236
|
+
log_info "执行: kill -15 $p"
|
|
237
|
+
kill -15 "$p" 2>/dev/null || log_warn "kill -15 $p 失败"
|
|
238
|
+
done
|
|
239
|
+
|
|
240
|
+
# 等待 5 秒让服务优雅退出
|
|
241
|
+
log_info "等待 5 秒让服务优雅退出..."
|
|
242
|
+
sleep 5
|
|
243
|
+
|
|
244
|
+
# 检查是否已停止
|
|
245
|
+
local pid_after_graceful
|
|
246
|
+
pid_after_graceful=$(get_openclaw_pid)
|
|
247
|
+
if [ -z "$pid_after_graceful" ] && ! check_port "$PORT"; then
|
|
248
|
+
log_info "OpenClaw 服务已通过优雅停止退出。"
|
|
249
|
+
log_info "服务已成功停止。"
|
|
250
|
+
log_info "Success"
|
|
251
|
+
exit 0
|
|
252
|
+
fi
|
|
253
|
+
|
|
254
|
+
log_warn "优雅停止失败,服务仍在运行,切换到强制停止..."
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
# 强制停止模式(直接发送 SIGKILL)
|
|
258
|
+
log_info "正在强制停止 OpenClaw 服务(SIGKILL)..."
|
|
259
|
+
|
|
260
|
+
# 获取最新的进程列表(因为优雅停止后可能有新进程)
|
|
261
|
+
local current_pids=""
|
|
262
|
+
if command -v pgrep &>/dev/null; then
|
|
263
|
+
current_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
264
|
+
elif command -v pidof &>/dev/null; then
|
|
265
|
+
current_pids=$(pidof openclaw)
|
|
266
|
+
else
|
|
267
|
+
current_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
log_info "当前 openclaw 进程: $current_pids"
|
|
271
|
+
|
|
272
|
+
for p in $current_pids; do
|
|
273
|
+
log_info "执行: kill -9 $p"
|
|
274
|
+
kill -9 "$p" 2>/dev/null || log_warn "kill -9 $p 失败"
|
|
275
|
+
done
|
|
276
|
+
# 额外使用 pkill 确保所有相关进程都被停止
|
|
277
|
+
log_info "执行: pkill -9 -f openclaw"
|
|
278
|
+
pkill -9 -f "openclaw" 2>/dev/null || log_warn "pkill 失败"
|
|
279
|
+
log_info "执行: killall -9 openclaw"
|
|
280
|
+
killall -9 openclaw 2>/dev/null || log_warn "killall 失败"
|
|
281
|
+
|
|
282
|
+
# 等待 2 秒让进程退出
|
|
283
|
+
sleep 2
|
|
284
|
+
|
|
285
|
+
# 连续监控模式:每秒检查并杀死看门狗重启的进程
|
|
286
|
+
# 这样即使看门狗立即重启,也会被再次杀死
|
|
287
|
+
log_info "进入连续监控模式(最多 20 秒),防止看门狗自动重启..."
|
|
288
|
+
local elapsed=0
|
|
289
|
+
local consecutive_empty=0
|
|
290
|
+
while [ $elapsed -lt 20 ]; do
|
|
291
|
+
sleep 1
|
|
292
|
+
|
|
293
|
+
# 检查是否还有 openclaw 进程
|
|
294
|
+
local remaining_pids=""
|
|
295
|
+
if command -v pgrep &>/dev/null; then
|
|
296
|
+
remaining_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
297
|
+
elif command -v pidof &>/dev/null; then
|
|
298
|
+
remaining_pids=$(pidof openclaw)
|
|
299
|
+
else
|
|
300
|
+
remaining_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
# 同时检查端口是否仍在监听
|
|
304
|
+
local port_listening=false
|
|
305
|
+
if check_port "$PORT"; then
|
|
306
|
+
port_listening=true
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
if [ -z "$remaining_pids" ] && [ "$port_listening" = false ]; then
|
|
310
|
+
consecutive_empty=$((consecutive_empty + 1))
|
|
311
|
+
log_info "第 $elapsed 秒: 无 openclaw 进程且端口未监听(连续 $consecutive_empty 次)"
|
|
312
|
+
# 连续 3 秒没有进程且端口未监听,认为已停止
|
|
313
|
+
if [ $consecutive_empty -ge 3 ]; then
|
|
314
|
+
log_info "OpenClaw 服务已停止。(看门狗已放弃重启)"
|
|
315
|
+
log_info "服务已成功停止。"
|
|
316
|
+
log_info "Success"
|
|
317
|
+
exit 0
|
|
318
|
+
fi
|
|
319
|
+
else
|
|
320
|
+
consecutive_empty=0
|
|
321
|
+
if [ -n "$remaining_pids" ]; then
|
|
322
|
+
log_info "第 $elapsed 秒: 发现新进程 $remaining_pids,再次 kill..."
|
|
323
|
+
for rp in $remaining_pids; do
|
|
324
|
+
kill -9 "$rp" 2>/dev/null || true
|
|
325
|
+
done
|
|
326
|
+
fi
|
|
327
|
+
if [ "$port_listening" = true ]; then
|
|
328
|
+
log_info "第 $elapsed 秒: 端口 $PORT 仍在监听,尝试 fuser 终止..."
|
|
329
|
+
if command -v fuser &>/dev/null; then
|
|
330
|
+
fuser -k "${PORT}/tcp" 2>/dev/null || true
|
|
331
|
+
fi
|
|
332
|
+
fi
|
|
333
|
+
pkill -9 -f "openclaw" 2>/dev/null || true
|
|
334
|
+
killall -9 openclaw 2>/dev/null || true
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
elapsed=$((elapsed + 1))
|
|
338
|
+
done
|
|
339
|
+
|
|
340
|
+
# 最终验证前,再执行一次全面清理
|
|
341
|
+
log_info "执行最终清理..."
|
|
342
|
+
pkill -9 -f "openclaw" 2>/dev/null || true
|
|
343
|
+
killall -9 openclaw 2>/dev/null || true
|
|
344
|
+
|
|
345
|
+
# 等待 2 秒
|
|
346
|
+
sleep 2
|
|
347
|
+
|
|
348
|
+
# 最终验证
|
|
349
|
+
local remaining_pids=""
|
|
350
|
+
if command -v pgrep &>/dev/null; then
|
|
351
|
+
remaining_pids=$(pgrep -f "openclaw" | tr '\n' ' ')
|
|
352
|
+
elif command -v pidof &>/dev/null; then
|
|
353
|
+
remaining_pids=$(pidof openclaw)
|
|
354
|
+
else
|
|
355
|
+
remaining_pids=$(ps aux | grep -v grep | grep "openclaw" | awk '{print $2}' | tr '\n' ' ')
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
# 最终验证:显示当前所有进程
|
|
359
|
+
log_info "最终验证: 当前 openclaw 进程: $remaining_pids"
|
|
360
|
+
log_info "最终验证: 当前端口状态:"
|
|
361
|
+
if command -v netstat &>/dev/null; then
|
|
362
|
+
netstat -tlnp 2>/dev/null | grep ":${PORT} " || log_info "端口 ${PORT} 未监听"
|
|
363
|
+
elif command -v ss &>/dev/null; then
|
|
364
|
+
ss -tlnp 2>/dev/null | grep ":${PORT} " || log_info "端口 ${PORT} 未监听"
|
|
365
|
+
fi
|
|
366
|
+
|
|
367
|
+
# 最终验证:同时检查进程和端口
|
|
368
|
+
local port_listening_final=false
|
|
369
|
+
if check_port "$PORT"; then
|
|
370
|
+
port_listening_final=true
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
if [ -z "$remaining_pids" ] && [ "$port_listening_final" = false ]; then
|
|
374
|
+
log_info "OpenClaw 服务已停止。"
|
|
375
|
+
log_info "服务已成功停止。"
|
|
376
|
+
log_info "Success"
|
|
377
|
+
exit 0
|
|
378
|
+
else
|
|
379
|
+
if [ -n "$remaining_pids" ]; then
|
|
380
|
+
log_error "OpenClaw 服务停止失败!进程仍然存在: $remaining_pids"
|
|
381
|
+
fi
|
|
382
|
+
if [ "$port_listening_final" = true ]; then
|
|
383
|
+
log_error "OpenClaw 服务停止失败!端口 $PORT 仍在监听。"
|
|
384
|
+
fi
|
|
385
|
+
exit 1
|
|
386
|
+
fi
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
# Systemd 模式:停止服务
|
|
390
|
+
# 参数: $1 = force (1=强制停止, 空=优雅停止)
|
|
391
|
+
stop_systemd() {
|
|
392
|
+
local force="$1"
|
|
393
|
+
|
|
394
|
+
# 检查服务是否存在
|
|
395
|
+
if ! systemctl --user list-unit-files "$SERVICE_NAME" &>/dev/null; then
|
|
396
|
+
log_error "服务 $SERVICE_NAME 不存在。"
|
|
397
|
+
exit 1
|
|
398
|
+
fi
|
|
399
|
+
|
|
400
|
+
# 检查服务状态
|
|
401
|
+
log_info "检查 OpenClaw 服务状态..."
|
|
402
|
+
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
|
403
|
+
log_info "OpenClaw 服务正在运行,准备停止..."
|
|
404
|
+
else
|
|
405
|
+
log_warn "OpenClaw 服务未在运行。"
|
|
406
|
+
exit 0
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
# 停止服务
|
|
410
|
+
log_info "正在停止 OpenClaw 服务..."
|
|
411
|
+
|
|
412
|
+
if systemctl --user stop "$SERVICE_NAME"; then
|
|
413
|
+
log_info "OpenClaw 服务停止成功!"
|
|
414
|
+
|
|
415
|
+
# 等待服务完全停止
|
|
416
|
+
sleep 2
|
|
417
|
+
|
|
418
|
+
# 验证服务状态
|
|
419
|
+
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
|
420
|
+
if [ -n "$force" ]; then
|
|
421
|
+
log_warn "服务仍在运行,执行强制停止..."
|
|
422
|
+
# 获取进程 PID 并强制终止
|
|
423
|
+
local main_pid
|
|
424
|
+
main_pid=$(systemctl --user show "$SERVICE_NAME" --property=MainPID --value)
|
|
425
|
+
if [ -n "$main_pid" ] && [ "$main_pid" != "0" ]; then
|
|
426
|
+
log_info "强制终止进程 PID: $main_pid"
|
|
427
|
+
kill -9 "$main_pid" 2>/dev/null || true
|
|
428
|
+
fi
|
|
429
|
+
# 同时清理所有 openclaw 相关进程
|
|
430
|
+
pkill -9 -f "openclaw" 2>/dev/null || true
|
|
431
|
+
killall -9 openclaw 2>/dev/null || true
|
|
432
|
+
|
|
433
|
+
sleep 2
|
|
434
|
+
|
|
435
|
+
if ! systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
|
436
|
+
log_info "服务已成功强制停止。"
|
|
437
|
+
log_info "Success"
|
|
438
|
+
exit 0
|
|
439
|
+
else
|
|
440
|
+
log_error "强制停止失败!服务仍在运行。"
|
|
441
|
+
exit 1
|
|
442
|
+
fi
|
|
443
|
+
else
|
|
444
|
+
log_warn "服务可能仍在运行,请检查进程。"
|
|
445
|
+
systemctl --user status "$SERVICE_NAME" --no-pager
|
|
446
|
+
fi
|
|
447
|
+
else
|
|
448
|
+
log_info "服务已成功停止。"
|
|
449
|
+
log_info "Success"
|
|
450
|
+
fi
|
|
451
|
+
else
|
|
452
|
+
log_error "OpenClaw 服务停止失败!"
|
|
453
|
+
exit 1
|
|
454
|
+
fi
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
# 显示帮助信息
|
|
458
|
+
show_help() {
|
|
459
|
+
echo "OpenClaw 服务停止脚本"
|
|
460
|
+
echo ""
|
|
461
|
+
echo "用法: $0 [选项]"
|
|
462
|
+
echo ""
|
|
463
|
+
echo "选项:"
|
|
464
|
+
echo " -f, --force 强制停止服务"
|
|
465
|
+
echo " -h, --help 显示此帮助信息"
|
|
466
|
+
echo ""
|
|
467
|
+
echo "示例:"
|
|
468
|
+
echo " $0 正常停止服务"
|
|
469
|
+
echo " $0 -f 强制停止服务"
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
# 主函数
|
|
473
|
+
main() {
|
|
474
|
+
local force=""
|
|
475
|
+
|
|
476
|
+
# 检查 FORCE 环境变量(来自 script-executor 的强制停止模式)
|
|
477
|
+
if [ "${FORCE:-}" = "1" ]; then
|
|
478
|
+
force="1"
|
|
479
|
+
log_info "检测到 FORCE=1 环境变量,启用强制停止模式"
|
|
480
|
+
fi
|
|
481
|
+
|
|
482
|
+
# 解析命令行参数
|
|
483
|
+
while [ $# -gt 0 ]; do
|
|
484
|
+
case $1 in
|
|
485
|
+
-f|--force)
|
|
486
|
+
force="1"
|
|
487
|
+
shift
|
|
488
|
+
;;
|
|
489
|
+
-h|--help)
|
|
490
|
+
show_help
|
|
491
|
+
exit 0
|
|
492
|
+
;;
|
|
493
|
+
*)
|
|
494
|
+
log_error "未知选项: $1"
|
|
495
|
+
show_help
|
|
496
|
+
exit 1
|
|
497
|
+
;;
|
|
498
|
+
esac
|
|
499
|
+
done
|
|
500
|
+
|
|
501
|
+
if is_docker; then
|
|
502
|
+
stop_docker "$force"
|
|
503
|
+
else
|
|
504
|
+
stop_systemd "$force"
|
|
505
|
+
fi
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
# 执行主函数
|
|
509
|
+
main "$@"
|