Skip to content

Commit bef68e9

Browse files
authored
Merge pull request #670 from lyjjl/main
feat(script): 优化了脚本对各种Ctrl+C退出的处理能力
2 parents 6330181 + a9b15cc commit bef68e9

File tree

1 file changed

+140
-30
lines changed

1 file changed

+140
-30
lines changed

script/start-linux.sh

Lines changed: 140 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,114 @@
22

33
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
44
export PATH=$PATH:/usr/bin:/usr/local/bin
5+
56
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
67

8+
CLEANING=0
9+
PMHQ_PID=""
10+
USE_XVFB=0
11+
DISTRO=""
12+
713
log() { echo -e "${GREEN}>>> $1${NC}"; }
814
warn() { echo -e "${YELLOW}>>> $1${NC}"; }
915
error() { echo -e "${RED}错误: $1${NC}"; exit 1; }
1016

17+
check_sudo() {
18+
log "验证 Sudo 权限..."
19+
if ! sudo -v; then
20+
error "Sudo 验证失败或被取消,脚本终止。"
21+
fi
22+
}
23+
24+
cleanup() {
25+
# 防止用户对 Ctrl+C 突然产生某种异样的迷恋然后狂按 Ctrl+C
26+
[ "$CLEANING" -eq 1 ] && return
27+
CLEANING=1
28+
29+
# 防止日志挡交互
30+
if [ -n "$PMHQ_PID" ] && kill -0 "$PMHQ_PID" 2>/dev/null; then
31+
kill -STOP -"$PMHQ_PID" 2>/dev/null
32+
fi
33+
34+
echo ""
35+
warn "收到退出信号 (进程已挂起) <<<"
36+
37+
local kill_qq=1
38+
local choice=""
39+
40+
if [ -n "$PMHQ_PID" ]; then
41+
if read -t 5 -n 1 -r -p "是否关闭 QQ 及相关进程? [Y/n] (5秒后默认关闭): " choice < /dev/tty; then
42+
echo ""
43+
else
44+
echo ""
45+
log "等待超时,执行默认关闭操作。"
46+
fi
47+
48+
if [[ "$choice" == "n" || "$choice" == "N" ]]; then
49+
kill_qq=0
50+
fi
51+
fi
52+
53+
if [ $kill_qq -eq 1 ]; then
54+
warn "正在停止服务..."
55+
56+
kill -CONT -"$PMHQ_PID" 2>/dev/null
57+
58+
kill -TERM -"$PMHQ_PID" 2>/dev/null
59+
pkill -15 -f "$PMHQ_BIN" 2>/dev/null
60+
pkill -15 -f "/opt/QQ/qq" 2>/dev/null
61+
62+
local wait_count=0
63+
64+
while kill -0 "$PMHQ_PID" 2>/dev/null || \
65+
pgrep -f "$PMHQ_BIN" > /dev/null || \
66+
pgrep -f "/opt/QQ/qq" > /dev/null; do
67+
sleep 0.5
68+
((wait_count++))
69+
if [ $wait_count -ge 6 ]; then
70+
warn "检测到残留进程,执行强制清理..."
71+
# 气死我了,总有点关不掉进程的毛病,累了,跟我 pkill -9 说去吧
72+
# 期待大手子修复
73+
kill -KILL -"$PMHQ_PID" 2>/dev/null
74+
pkill -9 -f "$PMHQ_BIN" 2>/dev/null
75+
pkill -9 -f "/opt/QQ/qq" 2>/dev/null
76+
break
77+
fi
78+
done
79+
80+
# 环境清理
81+
if [ "$DISTRO" != "arch" ] && [ "$USE_XVFB" -eq 0 ]; then
82+
if command -v xhost &> /dev/null; then
83+
xhost -local:$(whoami) > /dev/null 2>&1
84+
fi
85+
fi
86+
log "所有进程已清理完毕。"
87+
else
88+
# 选择不关闭时必须恢复进程运行
89+
kill -CONT -"$PMHQ_PID" 2>/dev/null
90+
log "已恢复后台进程运行 (PGID: $PMHQ_PID)"
91+
warn "注意:它们已脱离脚本控制,后续请手动管理"
92+
fi
93+
94+
exit 0
95+
}
96+
97+
trap cleanup SIGINT SIGTERM
98+
1199
confirm() {
12100
read -n 1 -s -r -p "$1 (Y/n) " key
13101
echo ""
14102
[[ "$key" == "Y" || "$key" == "y" || "$key" == "" ]]
15103
}
16104

17105
find_port() {
18-
# 让系统自动分配可用端口
106+
# 优先尝试让系统自动分配
19107
local port=$(python3 -c 'import socket; s=socket.socket(); s.bind(("",0)); print(s.getsockname()[1]); s.close()' 2>/dev/null)
20108
if [ -n "$port" ]; then
21109
echo $port
22110
return 0
23111
fi
24-
# 回退方案:从指定端口开始查找
112+
# 回退方案:扫描可用端口
25113
local port=$1
26114
while [ $port -lt 65535 ]; do
27115
if ! ss -tuln 2>/dev/null | grep -q ":$port " && ! netstat -tuln 2>/dev/null | grep -q ":$port "; then
@@ -38,19 +126,20 @@ if command -v pacman &> /dev/null; then
38126
elif command -v apt &> /dev/null; then
39127
DISTRO="debian"
40128
else
41-
error "只支持 apt 或 pacman 包管理器"
129+
error "当前只支持 apt 或 pacman 包管理器"
42130
fi
43131
log "检测到系统: $DISTRO"
44132

45133
install_arch() {
134+
check_sudo
46135
log "检查 Arch 依赖..."
47-
sudo pacman -S --needed --noconfirm base-devel git ffmpeg xorg-server-xvfb libvips imagemagick dbus xorg-xhost fcitx5-im wget
136+
sudo pacman -S --needed --noconfirm base-devel git ffmpeg xorg-server-xvfb libvips imagemagick dbus xorg-xhost fcitx5-im wget || error "基础依赖安装失败"
48137

49138
if [ ! -f "/opt/QQ/qq" ] && confirm "未检测到 QQ,是否通过 AUR 安装?"; then
50139
if ! command -v yay &> /dev/null; then
51140
warn "未检测到 yay,尝试安装..."
52141
if ! sudo pacman -S --needed --noconfirm yay 2>/dev/null; then
53-
warn "pacman 安装失败,切换源码编译..."
142+
warn "pacman 安装 yay 失败,切换源码编译..."
54143
rm -rf /tmp/yay_install && git clone https://aur.archlinux.org/yay.git /tmp/yay_install
55144
(cd /tmp/yay_install && makepkg -si --noconfirm) || error "yay 编译失败"
56145
rm -rf /tmp/yay_install
@@ -61,6 +150,7 @@ install_arch() {
61150
}
62151

63152
install_debian() {
153+
check_sudo
64154
local MACHINE=$(uname -m)
65155
local ARCH=""
66156
case "$MACHINE" in
@@ -71,78 +161,88 @@ install_debian() {
71161

72162
if [ ! -f "/opt/QQ/qq" ] && confirm "未检测到 QQ,是否安装?"; then
73163
log "下载并安装 QQ ($ARCH)..."
74-
sudo apt-get update && sudo apt-get install -y wget
164+
sudo apt-get update && sudo apt-get install -y wget || error "apt update 或 wget 安装失败"
165+
75166
local DEB="/tmp/qq.deb"
76-
wget -O "$DEB" "https://dldir1v6.qq.com/qqfile/qq/QQNT/ab90fdfa/linuxqq_3.2.20-40768_$ARCH.deb" || error "下载失败"
167+
wget -O "$DEB" "https://dldir1v6.qq.com/qqfile/qq/QQNT/ab90fdfa/linuxqq_3.2.20-40768_$ARCH.deb" || error "QQ 安装包下载失败"
77168

78-
# 依赖判断 (新版 Ubuntu 24.04+ 用 libasound2t64,旧版用 libasound2)
79169
local LIB_SND="libasound2"
80170
if apt-cache show libasound2t64 &>/dev/null; then
81171
LIB_SND="libasound2t64"
82172
fi
173+
log "使用 ALSA 库包名: $LIB_SND"
83174

84-
echo "使用 ALSA 库包: $LIB_SND"
85-
86-
sudo apt install -y "$DEB" x11-utils libgtk-3-0 libxcb-xinerama0 libgl1-mesa-dri libnotify4 libnss3 xdg-utils libsecret-1-0 libappindicator3-1 libgbm1 $LIB_SND fonts-noto-cjk libxss1
175+
sudo apt install -y "$DEB" x11-utils libgtk-3-0 libxcb-xinerama0 libgl1-mesa-dri libnotify4 libnss3 xdg-utils libsecret-1-0 libappindicator3-1 libgbm1 $LIB_SND fonts-noto-cjk libxss1 || error "QQ 依赖安装失败"
87176
rm -f "$DEB"
88177
fi
89178

179+
# 检查其他工具
180+
local missing_pkgs=""
90181
for pkg in ffmpeg xvfb; do
91-
command -v $pkg &> /dev/null || sudo apt-get install -y $pkg
182+
if ! command -v $pkg &> /dev/null; then
183+
missing_pkgs="$missing_pkgs $pkg"
184+
fi
92185
done
186+
187+
if [ -n "$missing_pkgs" ]; then
188+
log "安装缺失工具: $missing_pkgs"
189+
sudo apt-get install -y $missing_pkgs || error "工具安装失败"
190+
fi
93191
}
94192

95-
[ "$DISTRO" == "arch" ] && install_arch || install_debian
193+
# 执行安装
194+
if [ "$DISTRO" == "arch" ]; then
195+
install_arch
196+
else
197+
install_debian
198+
fi
96199

97200
chmod +x "$SCRIPT_DIR/llbot/node" "$SCRIPT_DIR/llbot/pmhq" 2>/dev/null
98-
[ "$DISTRO" == "arch" ] && sudo chown -R $(whoami):$(whoami) "$SCRIPT_DIR/llbot"
201+
202+
sudo chown -R $(whoami):$(whoami) "$SCRIPT_DIR/llbot" 2>/dev/null
99203

100204
PORT=$(find_port 13000)
101205
[ -z "$PORT" ] && error "无法找到可用端口"
102-
log "使用端口: $PORT"
103-
104-
HAS_DISPLAY=0
105-
[[ -n "$DISPLAY" || -n "$WAYLAND_DISPLAY" ]] && HAS_DISPLAY=1
206+
log "分配端口: $PORT"
106207

107208
echo "------------------------------------------------"
108-
echo "1) GUI 模式"
109-
echo "2) Shell 模式 (默认)"
209+
echo "1) GUI 模式 (有界面)"
210+
echo "2) Shell 模式 (无界面/Headless,默认)"
110211
echo "------------------------------------------------"
111212

112213
MODE_CHOICE=""
113214
TIMEOUT=5
114215
while [ $TIMEOUT -gt 0 ]; do
115-
printf "\r请选择 [1/2] (${TIMEOUT}秒后默认选择 Shell): "
216+
printf "\r请选择 [1/2] (${TIMEOUT}秒后自动选择 Shell): "
116217
if read -t 1 -n 1 MODE_CHOICE; then
117218
echo ""
118219
break
119220
fi
120221
((TIMEOUT--))
121222
done
122-
[ $TIMEOUT -eq 0 ] && echo "" && log "超时,使用默认 Shell 模式"
223+
[ $TIMEOUT -eq 0 ] && echo "" && log "超时,自动选择 Shell 模式"
224+
123225
MODE_CHOICE=${MODE_CHOICE:-2}
124226
USE_XVFB=$([ "$MODE_CHOICE" == "2" ] && echo 1 || echo 0)
125227

126-
# 授权 X11
127228
if [ $USE_XVFB -eq 0 ]; then
128229
if command -v xauth &> /dev/null; then
129230
export XAUTHORITY=${XAUTHORITY:-$HOME/.Xauthority}
130231
else
131232
warn "未检测到 xauth,使用临时 xhost 授权"
132233
xhost +local:$(whoami) > /dev/null 2>&1
133-
trap "xhost -local:$(whoami) > /dev/null 2>&1" EXIT
134234
fi
135235
fi
136236

137237
IM_ENV=""
138238
EXTRA_FLAGS=""
139239

140240
if [[ "$XDG_SESSION_TYPE" == "wayland" || -n "$WAYLAND_DISPLAY" ]]; then
141-
log "环境: Wayland"
241+
log "检测到 Wayland 环境"
142242
IM_ENV="XMODIFIERS=@im=fcitx"
143243
EXTRA_FLAGS="--enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime"
144244
else
145-
log "环境: X11"
245+
log "检测到 X11 环境"
146246
IM_ENV="GTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx XMODIFIERS=@im=fcitx SDL_IM_MODULE=fcitx GLFW_IM_MODULE=ibus"
147247
fi
148248

@@ -151,21 +251,31 @@ LLBOT_JS="$SCRIPT_DIR/llbot/llbot.js"
151251
PMHQ_BIN="$SCRIPT_DIR/llbot/pmhq"
152252

153253
run_llbot() {
254+
set -m
255+
154256
if [ "$DISTRO" == "arch" ]; then
155257
export LD_PRELOAD="/usr/lib/libstdc++.so.6:/usr/lib/libgcc_s.so.1"
156258
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
157259
fi
158260

159261
local sub_cmd="$NODE_BIN --enable-source-maps $LLBOT_JS -- --pmhq-port=$PORT --no-sandbox $EXTRA_FLAGS"
160262

161-
log "启动中... (模式: $([ $USE_XVFB -eq 1 ] && echo "Headless" || echo "GUI"))"
263+
log "正在启动 LLBot... (模式: $([ $USE_XVFB -eq 1 ] && echo "Headless" || echo "GUI"))"
264+
log "按 Ctrl+C 可停止运行"
162265

163266
if [ $USE_XVFB -eq 1 ]; then
164-
env $IM_ENV xvfb-run -a "$PMHQ_BIN" --port="$PORT" --sub-cmd="$sub_cmd"
267+
env $IM_ENV xvfb-run -a "$PMHQ_BIN" --port="$PORT" --sub-cmd="$sub_cmd" &
165268
else
166-
[ "$DISTRO" != "arch" ] && xhost +local:$(whoami) > /dev/null 2>&1
167-
env $IM_ENV "$PMHQ_BIN" --port="$PORT" --sub-cmd="$sub_cmd"
269+
if [ "$DISTRO" != "arch" ]; then
270+
xhost +local:$(whoami) > /dev/null 2>&1
271+
fi
272+
env $IM_ENV "$PMHQ_BIN" --port="$PORT" --sub-cmd="$sub_cmd" &
168273
fi
274+
275+
PMHQ_PID=$!
276+
277+
# 阻塞等待进程结束
278+
wait "$PMHQ_PID" || true
169279
}
170280

171281
run_llbot

0 commit comments

Comments
 (0)