设备把自己当 USB 网卡挂给上游主机时会出现 usb0(驱动 configfs-gadget.g1, IP 192.168.68.1)。该链路只通向 PC,被当成出口后所有出站都 context deadline exceeded,客户端看到 socks connect failed rep=0x01。 误选原因:score_iface_as_cellular 给 usb0 与真出口 wwan0 都打 175 分。 usb0 靠名称命中 patterns 里的 usb(+80)与非默认路由(+40);wwan0 的 bam-dmux 驱动不在白名单里,白丢 +100。detect_cellular_iface 用 -gt 比较, list_ifaces 按名排序让 usb0 先入选,平分下无法被顶替。 改动: - lib.sh 新增 iface_is_usb_gadget(),按驱动名与 gadget 总线路径识别, 并并入 is_virtual_or_skip_iface(usb0 评分 175 -> 0) - 驱动白名单补 bam-dmux/bam_dmux/qcom-ipa/ipa_wan;名称权重 80 -> 40, 确保驱动证据始终压过名称猜测 - 新增 detect_cellular_iface_via_mm(),把 ModemManager bearer 的 interface: 作为第 0 步权威来源 - detect_cellular_iface / resolve_cellular 全链路拒绝 gadget 网卡, 取不到真出口时按 REQUIRE_CELLULAR_IFACE 直接失败而不是绑错 - generate/install/upgrade/watch/detect/verify 各入口独立设闸, verify.sh 发现出口是 gadget 时直接 exit 4 并给出修复命令 - 默认 CELLULAR_IFACE_PATTERNS 去掉 usb/enx;upgrade.sh 与根 install.sh 就地迁移已有 settings.conf,旧机器升级即修复 - 新增 tests/detect-gadget.sh:18 条离线断言,无需真机
456 lines
17 KiB
Bash
Executable File
456 lines
17 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 增量升级:保留 settings/密钥/网卡绑定,默认不重下 sing-box、不重探测、不强制 verify
|
||
# 用法:
|
||
# sudo ./scripts/upgrade.sh
|
||
# sudo ./scripts/upgrade.sh --ui-only
|
||
# sudo ./scripts/upgrade.sh --force-binary
|
||
# sudo ./scripts/upgrade.sh --rebind
|
||
# sudo ./scripts/upgrade.sh --verify
|
||
set -euo pipefail
|
||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
# shellcheck source=lib.sh
|
||
source "$ROOT_DIR/scripts/lib.sh"
|
||
|
||
UI_ONLY=false
|
||
FORCE_BINARY=false
|
||
DO_REBIND=false
|
||
DO_VERIFY=false
|
||
SKIP_START=false
|
||
|
||
usage() {
|
||
cat <<'H'
|
||
cellular-proxy 增量升级
|
||
|
||
保留: settings.conf / PANEL_SECRET / PROXY_* / CELLULAR_IFACE
|
||
默认: 不重新探测网卡、不重下 sing-box(已存在且可执行则跳过)、不强制出口验证
|
||
|
||
选项:
|
||
--ui-only 只更新 UI 面板
|
||
--force-binary 强制重下/重装 sing-box
|
||
--rebind 升级时重新探测数据网卡并写回配置
|
||
--verify 升级后跑出口验证
|
||
--skip-start 只落盘,不 restart 服务
|
||
-h, --help
|
||
H
|
||
}
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--ui-only) UI_ONLY=true; shift ;;
|
||
--force-binary) FORCE_BINARY=true; shift ;;
|
||
--rebind) DO_REBIND=true; shift ;;
|
||
--verify) DO_VERIFY=true; shift ;;
|
||
--skip-start) SKIP_START=true; shift ;;
|
||
-h|--help) usage; exit 0 ;;
|
||
*) die "未知参数: $1" ;;
|
||
esac
|
||
done
|
||
|
||
need_root
|
||
|
||
# 优先用已安装目录的配置
|
||
INST_CANDIDATES=(
|
||
"${INSTALL_DIR:-}"
|
||
"/opt/cellular-proxy"
|
||
"$ROOT_DIR"
|
||
)
|
||
EXISTING=""
|
||
for d in "${INST_CANDIDATES[@]}"; do
|
||
[[ -n "$d" ]] || continue
|
||
if [[ -f "$d/etc/settings.conf" ]]; then
|
||
EXISTING="$d"
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [[ -z "$EXISTING" ]]; then
|
||
die "未检测到已安装实例(缺少 \$INSTALL_DIR/etc/settings.conf)。
|
||
请先全量安装:
|
||
curl -fsSL 'https://gitea.chickliu.fun/Hermes/cellular-proxy/raw/branch/main/install.sh' | sudo bash"
|
||
fi
|
||
|
||
export CONFIG_FILE="$EXISTING/etc/settings.conf"
|
||
load_config
|
||
INSTALL_DIR="${INSTALL_DIR:-$EXISTING}"
|
||
# 若 settings 里 INSTALL_DIR 与探测到的不一致,以配置为准;配置空则用探测目录
|
||
if [[ -z "${INSTALL_DIR:-}" || ! -d "$INSTALL_DIR" ]]; then
|
||
INSTALL_DIR="$EXISTING"
|
||
fi
|
||
export INSTALL_DIR
|
||
|
||
info "======== cellular-proxy 增量升级 ========"
|
||
info "安装目录: $INSTALL_DIR"
|
||
info "配置文件: $CONFIG_FILE(将保留密钥/网卡/账密)"
|
||
|
||
# --- 迁移:老配置里的 usb/enx 关键词会把 USB gadget 网卡(usb0)当数据出口 ---
|
||
# 升级保留 settings.conf,因此必须在这里就地清理,否则旧机器升级后仍会误绑。
|
||
migrate_iface_patterns() {
|
||
local conf="$1" cur cleaned tmp
|
||
[[ -f "$conf" ]] || return 0
|
||
cur="$(awk -F= '/^CELLULAR_IFACE_PATTERNS=/{print substr($0,index($0,"=")+1); exit}' "$conf" 2>/dev/null || true)"
|
||
[[ -n "$cur" ]] || return 0
|
||
cleaned="$(printf '%s' "$cur" | tr ',' '\n' | sed 's/[[:space:]]//g' \
|
||
| grep -vxE 'usb|enx' | paste -sd, - 2>/dev/null || true)"
|
||
[[ -n "$cleaned" && "$cleaned" != "$cur" ]] || return 0
|
||
tmp="$(mktemp)"
|
||
awk -v v="$cleaned" '
|
||
/^CELLULAR_IFACE_PATTERNS=/ { print "CELLULAR_IFACE_PATTERNS=" v; next }
|
||
{ print }
|
||
' "$conf" > "$tmp"
|
||
mv "$tmp" "$conf"
|
||
warn "已从 CELLULAR_IFACE_PATTERNS 移除 usb/enx(会误命中 USB gadget 网卡): $cur -> $cleaned"
|
||
CELLULAR_IFACE_PATTERNS="$cleaned"
|
||
}
|
||
migrate_iface_patterns "$CONFIG_FILE"
|
||
|
||
# 迁移:已写死 gadget 网卡的旧配置,清空后走重新探测
|
||
if [[ -n "${CELLULAR_IFACE:-}" ]] && iface_is_usb_gadget "${CELLULAR_IFACE:-}"; then
|
||
warn "旧配置 CELLULAR_IFACE=$CELLULAR_IFACE 是 USB gadget 网卡,将重新探测数据出口"
|
||
CELLULAR_IFACE=""
|
||
fi
|
||
|
||
ensure_dirs
|
||
mkdir -p /var/lib/cellular-proxy "$INSTALL_DIR/bin" "$INSTALL_DIR/etc" "$INSTALL_DIR/ui" "$INSTALL_DIR/scripts" "$INSTALL_DIR/generated"
|
||
|
||
# --- UI only fast path ---
|
||
if [[ "$UI_ONLY" == "true" ]]; then
|
||
info "模式: 仅更新 UI"
|
||
if [[ ! -f "$ROOT_DIR/ui/index.html" ]]; then
|
||
die "源码缺少 ui/index.html"
|
||
fi
|
||
cp -a "$ROOT_DIR/ui/." "$INSTALL_DIR/ui/"
|
||
info "UI 已更新 -> $INSTALL_DIR/ui"
|
||
# 静态文件无需重启代理;admin 也不必
|
||
echo
|
||
info "完成(ui-only)。浏览器强刷: http://<LAN-IP>:${PANEL_PORT}/ui/"
|
||
exit 0
|
||
fi
|
||
|
||
# --- 可选 rebind ---
|
||
if [[ "$DO_REBIND" == "true" ]]; then
|
||
info "重新探测数据网卡…"
|
||
cell="$(detect_cellular_iface "" || true)"
|
||
[[ -n "$cell" ]] || die "未能探测数据网卡"
|
||
! iface_is_usb_gadget "$cell" || die "探测到的 $cell 是 USB gadget 网卡(面向上游主机),不能作为数据出口"
|
||
src="$(detect_source_ip "$cell" || true)"
|
||
persist_cellular_to_settings "$CONFIG_FILE" "$cell" "$src"
|
||
load_config
|
||
CELLULAR_IFACE="$cell"
|
||
CELLULAR_SOURCE_IP="${src:-}"
|
||
info "已 rebind: $CELLULAR_IFACE ${CELLULAR_SOURCE_IP:-}"
|
||
fi
|
||
|
||
cell="${CELLULAR_IFACE:-}"
|
||
if [[ -z "$cell" ]] || ! iface_exists "$cell" || iface_is_usb_gadget "$cell"; then
|
||
if [[ -n "$cell" ]] && iface_is_usb_gadget "$cell"; then
|
||
warn "配置中的 CELLULAR_IFACE=$cell 是 USB gadget 网卡,不能作为出口,重新探测"
|
||
else
|
||
warn "配置中的 CELLULAR_IFACE=${cell:-空} 无效,尝试自动探测(仅本次)"
|
||
fi
|
||
cell="$(detect_cellular_iface "" || true)"
|
||
if [[ -n "$cell" ]] && ! iface_is_usb_gadget "$cell"; then
|
||
src="$(detect_source_ip "$cell" || true)"
|
||
persist_cellular_to_settings "$CONFIG_FILE" "$cell" "$src"
|
||
load_config
|
||
CELLULAR_IFACE="$cell"
|
||
CELLULAR_SOURCE_IP="${src:-$CELLULAR_SOURCE_IP}"
|
||
else
|
||
die "无有效数据网卡。请: cpxy rebind 或 --rebind / --iface"
|
||
fi
|
||
fi
|
||
src_ip="${CELLULAR_SOURCE_IP:-$(detect_source_ip "$cell" || true)}"
|
||
|
||
# --- binary ---
|
||
need_binary=true
|
||
if [[ "$FORCE_BINARY" != "true" && -x "$INSTALL_DIR/bin/sing-box" ]]; then
|
||
if "$INSTALL_DIR/bin/sing-box" version >/dev/null 2>&1; then
|
||
cur="$("$INSTALL_DIR/bin/sing-box" version 2>/dev/null | head -1 || true)"
|
||
info "复用已有 sing-box: $cur"
|
||
need_binary=false
|
||
fi
|
||
fi
|
||
|
||
if [[ "$need_binary" == "true" ]]; then
|
||
info "安装/更新 sing-box 二进制"
|
||
FORCE_DOWNLOAD="${FORCE_DOWNLOAD:-}"
|
||
if [[ "$FORCE_BINARY" == "true" ]]; then
|
||
export FORCE_DOWNLOAD=1
|
||
fi
|
||
# fetch 会同时拷 UI;后面还会再拷一遍 scripts/ui 没关系
|
||
export CONFIG_FILE
|
||
"$ROOT_DIR/scripts/fetch-binaries.sh"
|
||
else
|
||
info "跳过二进制下载(需要强制时加 --force-binary)"
|
||
mkdir -p "$INSTALL_DIR/ui"
|
||
if [[ -f "$ROOT_DIR/ui/index.html" ]]; then
|
||
cp -a "$ROOT_DIR/ui/." "$INSTALL_DIR/ui/"
|
||
info "已更新 UI"
|
||
fi
|
||
fi
|
||
|
||
# --- scripts / cpxy / generate config ---
|
||
info "更新 scripts / 配置生成 / systemd"
|
||
# 同步新脚本到安装目录,但不要覆盖 etc/settings
|
||
if command -v rsync >/dev/null 2>&1; then
|
||
rsync -a --delete \
|
||
--exclude 'settings.conf' \
|
||
"$ROOT_DIR/scripts/" "$INSTALL_DIR/scripts/"
|
||
else
|
||
mkdir -p "$INSTALL_DIR/scripts"
|
||
cp -a "$ROOT_DIR/scripts/." "$INSTALL_DIR/scripts/"
|
||
fi
|
||
chmod +x "$INSTALL_DIR/scripts/"*.sh "$INSTALL_DIR/scripts/"*.py 2>/dev/null || true
|
||
|
||
# 生成配置(用现有 settings,不改 secret)
|
||
export CONFIG_FILE
|
||
"$ROOT_DIR/scripts/generate.sh" "$INSTALL_DIR/generated"
|
||
install -m 0644 "$INSTALL_DIR/generated/config.json" "$INSTALL_DIR/etc/config.json"
|
||
install -m 0644 "$INSTALL_DIR/generated/runtime.env" "$INSTALL_DIR/etc/runtime.env"
|
||
# 保留 settings:只确保 INSTALL_DIR 字段正确(CONFIG_FILE 已是 live 路径时勿 self-copy)
|
||
if ! grep -qE '^INSTALL_DIR=' "$CONFIG_FILE" 2>/dev/null; then
|
||
printf 'INSTALL_DIR=%s\n' "$INSTALL_DIR" >> "$CONFIG_FILE"
|
||
fi
|
||
live_settings="$INSTALL_DIR/etc/settings.conf"
|
||
if [[ "$(readlink -f "$CONFIG_FILE" 2>/dev/null || echo "$CONFIG_FILE")" != "$(readlink -f "$live_settings" 2>/dev/null || echo "$live_settings")" ]]; then
|
||
install -m 0644 "$CONFIG_FILE" "$live_settings"
|
||
fi
|
||
|
||
if ! "$INSTALL_DIR/bin/sing-box" check -c "$INSTALL_DIR/etc/config.json"; then
|
||
die "sing-box 配置校验失败"
|
||
fi
|
||
|
||
# 校验 bind
|
||
if ! python3 - "$INSTALL_DIR/etc/config.json" "$cell" <<'PY'
|
||
import json,sys
|
||
c=json.load(open(sys.argv[1]))
|
||
cell=sys.argv[2]
|
||
ok=any(o.get("tag")=="cellular" and o.get("bind_interface")==cell for o in c.get("outbounds",[]))
|
||
sys.exit(0 if ok else 1)
|
||
PY
|
||
then
|
||
die "配置未正确绑定数据网卡 $cell"
|
||
fi
|
||
info "配置已确认 bind_interface=$cell"
|
||
|
||
# 安装 cpxy 包装器(与 install.sh 一致,含 upgrade 子命令)
|
||
install -m 0755 /dev/stdin "$INSTALL_DIR/bin/cpxy" <<'EOF'
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
# 经 /usr/local/bin/cpxy 符号链接调用时,$0 仍是链接路径;必须 resolve 真实路径
|
||
_self="${BASH_SOURCE[0]:-$0}"
|
||
if command -v readlink >/dev/null 2>&1; then
|
||
_resolved="$(readlink -f "$_self" 2>/dev/null || true)"
|
||
[[ -n "$_resolved" ]] && _self="$_resolved"
|
||
fi
|
||
BASE="$(cd "$(dirname "$_self")/.." && pwd)"
|
||
if [[ ! -d "$BASE/scripts" && -d /opt/cellular-proxy/scripts ]]; then
|
||
BASE="/opt/cellular-proxy"
|
||
fi
|
||
export CONFIG_FILE="${CONFIG_FILE:-$BASE/etc/settings.conf}"
|
||
cmd="${1:-help}"
|
||
shift || true
|
||
case "$cmd" in
|
||
detect) exec "$BASE/scripts/detect.sh" "$@" ;;
|
||
generate)
|
||
# shellcheck source=/dev/null
|
||
source "$BASE/scripts/lib.sh"
|
||
load_config
|
||
if [[ -z "${CELLULAR_IFACE:-}" ]] || ! iface_exists "${CELLULAR_IFACE:-}" || iface_is_usb_gadget "${CELLULAR_IFACE:-}"; then
|
||
if [[ -n "${CELLULAR_IFACE:-}" ]] && iface_is_usb_gadget "${CELLULAR_IFACE:-}"; then
|
||
warn "CELLULAR_IFACE=$CELLULAR_IFACE 是 USB gadget 网卡,重新探测"
|
||
fi
|
||
cell="$(detect_cellular_iface "" || true)"
|
||
if [[ -n "$cell" ]] && ! iface_is_usb_gadget "$cell"; then
|
||
src="$(detect_source_ip "$cell" || true)"
|
||
persist_cellular_to_settings "$CONFIG_FILE" "$cell" "$src"
|
||
CELLULAR_IFACE="$cell"
|
||
CELLULAR_SOURCE_IP="${src:-}"
|
||
fi
|
||
elif [[ -n "${CELLULAR_IFACE:-}" ]]; then
|
||
src="$(detect_source_ip "$CELLULAR_IFACE" || true)"
|
||
if [[ -n "$src" && "${CELLULAR_SOURCE_IP:-}" != "$src" ]]; then
|
||
persist_cellular_to_settings "$CONFIG_FILE" "$CELLULAR_IFACE" "$src"
|
||
CELLULAR_SOURCE_IP="$src"
|
||
fi
|
||
fi
|
||
"$BASE/scripts/generate.sh" "$BASE/generated"
|
||
install -m 0644 "$BASE/generated/config.json" "$BASE/etc/config.json"
|
||
install -m 0644 "$BASE/generated/runtime.env" "$BASE/etc/runtime.env"
|
||
if [[ "$(readlink -f "$CONFIG_FILE" 2>/dev/null || echo "$CONFIG_FILE")" != "$(readlink -f "$BASE/etc/settings.conf" 2>/dev/null || echo "$BASE/etc/settings.conf")" ]]; then
|
||
install -m 0644 "$CONFIG_FILE" "$BASE/etc/settings.conf" 2>/dev/null || true
|
||
fi
|
||
"$BASE/bin/sing-box" check -c "$BASE/etc/config.json"
|
||
systemctl restart cellular-proxy 2>/dev/null || true
|
||
systemctl restart cellular-proxy-admin 2>/dev/null || true
|
||
;;
|
||
rebind)
|
||
# shellcheck source=/dev/null
|
||
source "$BASE/scripts/lib.sh"
|
||
load_config
|
||
cell="$(detect_cellular_iface "" || true)"
|
||
[[ -n "$cell" ]] || die "未能探测数据网卡"
|
||
! iface_is_usb_gadget "$cell" || die "探测到的 $cell 是 USB gadget 网卡,不能作为数据出口"
|
||
src="$(detect_source_ip "$cell" || true)"
|
||
persist_cellular_to_settings "$CONFIG_FILE" "$cell" "$src"
|
||
info "已重新绑定 $cell src=${src:-}"
|
||
exec "$0" generate
|
||
;;
|
||
upgrade)
|
||
# 一条命令在线升级:拉最新 install.sh 再 --upgrade
|
||
if [[ -x "$BASE/scripts/online-upgrade.sh" ]]; then
|
||
exec "$BASE/scripts/online-upgrade.sh" "$@"
|
||
fi
|
||
url="${CELLULAR_PROXY_INSTALL_URL:-https://gitea.chickliu.fun/Hermes/cellular-proxy/raw/branch/main/install.sh}"
|
||
exec bash -c 'curl -fsSL "$1" | bash -s -- --upgrade --install-dir "$2" "${@:3}"' _ "$url" "$BASE" "$@"
|
||
;;
|
||
start) systemctl start cellular-proxy cellular-proxy-admin 2>/dev/null || systemctl start cellular-proxy ;;
|
||
stop) systemctl stop cellular-proxy-admin 2>/dev/null || true; systemctl stop cellular-proxy ;;
|
||
restart) systemctl restart cellular-proxy; systemctl restart cellular-proxy-admin 2>/dev/null || true ;;
|
||
status)
|
||
systemctl status cellular-proxy --no-pager || true
|
||
systemctl status cellular-proxy-admin --no-pager 2>/dev/null || true
|
||
;;
|
||
verify) exec "$BASE/scripts/verify.sh" "$@" ;;
|
||
auth)
|
||
exec "$BASE/scripts/apply-proxy-auth.sh" "$@"
|
||
;;
|
||
watch)
|
||
case "${1:-status}" in
|
||
once) exec "$BASE/scripts/watch-cellular.sh" --once ;;
|
||
status)
|
||
systemctl status cellular-proxy-watch.service --no-pager 2>/dev/null || true
|
||
systemctl status cellular-proxy-watch.timer --no-pager 2>/dev/null || true
|
||
;;
|
||
start|enable)
|
||
systemctl enable --now cellular-proxy-watch.service cellular-proxy-watch.timer 2>/dev/null || true
|
||
;;
|
||
stop|disable)
|
||
systemctl disable --now cellular-proxy-watch.service cellular-proxy-watch.timer 2>/dev/null || true
|
||
;;
|
||
logs) journalctl -u cellular-proxy-watch -n "${2:-80}" --no-pager ;;
|
||
*) exec "$BASE/scripts/watch-cellular.sh" "$@" ;;
|
||
esac
|
||
;;
|
||
logs) journalctl -u cellular-proxy -n "${1:-80}" -f ;;
|
||
help|*)
|
||
cat <<H
|
||
cpxy — cellular-proxy
|
||
语义: 走代理的连接一律从数据网卡出口
|
||
cpxy detect | rebind | generate | start | stop | restart | status
|
||
cpxy verify | logs [N]
|
||
cpxy auth --user U --pass P | --clear | --show
|
||
cpxy watch [status|once|start|stop|logs] # WWAN 自动监控
|
||
cpxy upgrade # 一条命令在线增量升级
|
||
cpxy upgrade --ui-only | --force-binary | --verify
|
||
H
|
||
;;
|
||
esac
|
||
EOF
|
||
ln -sfn "$INSTALL_DIR/bin/cpxy" /usr/local/bin/cpxy
|
||
|
||
# systemd
|
||
MEM="${MEMORY_MAX_MB:-96}"
|
||
if [[ -f "$ROOT_DIR/systemd/cellular-proxy.service" ]]; then
|
||
install -m 0644 "$ROOT_DIR/systemd/cellular-proxy.service" /etc/systemd/system/cellular-proxy.service
|
||
sed -i "s|@INSTALL_DIR@|$INSTALL_DIR|g" /etc/systemd/system/cellular-proxy.service
|
||
sed -i "s|@LOG_DIR@|$LOG_DIR|g" /etc/systemd/system/cellular-proxy.service
|
||
if [[ "$MEM" != "0" && -n "$MEM" ]]; then
|
||
sed -i "s|@MEMORY_MAX@|${MEM}M|g" /etc/systemd/system/cellular-proxy.service
|
||
else
|
||
sed -i '/MemoryMax=@MEMORY_MAX@/d' /etc/systemd/system/cellular-proxy.service
|
||
fi
|
||
fi
|
||
if [[ -f "$ROOT_DIR/systemd/cellular-proxy-admin.service" ]]; then
|
||
install -m 0644 "$ROOT_DIR/systemd/cellular-proxy-admin.service" /etc/systemd/system/cellular-proxy-admin.service
|
||
sed -i "s|@INSTALL_DIR@|$INSTALL_DIR|g" /etc/systemd/system/cellular-proxy-admin.service
|
||
fi
|
||
# WWAN 自动监控
|
||
for u in cellular-proxy-watch.service cellular-proxy-watch-once.service cellular-proxy-watch.timer; do
|
||
if [[ -f "$ROOT_DIR/systemd/$u" ]]; then
|
||
install -m 0644 "$ROOT_DIR/systemd/$u" "/etc/systemd/system/$u"
|
||
sed -i "s|@INSTALL_DIR@|$INSTALL_DIR|g" "/etc/systemd/system/$u"
|
||
sed -i "s|@LOG_DIR@|${LOG_DIR}|g" "/etc/systemd/system/$u"
|
||
fi
|
||
done
|
||
chmod +x "$INSTALL_DIR/scripts/watch-cellular.sh" 2>/dev/null || true
|
||
systemctl daemon-reload
|
||
|
||
export SYSTEMD_PAGER=cat
|
||
export SYSTEMD_COLORS=0
|
||
if [[ "$SKIP_START" == "true" ]]; then
|
||
systemctl enable cellular-proxy.service 2>/dev/null || true
|
||
systemctl enable cellular-proxy-admin.service 2>/dev/null || true
|
||
info "已跳过 restart(--skip-start)"
|
||
else
|
||
systemctl enable cellular-proxy.service 2>/dev/null || true
|
||
systemctl enable cellular-proxy-admin.service 2>/dev/null || true
|
||
systemctl restart cellular-proxy.service
|
||
systemctl restart cellular-proxy-admin.service 2>/dev/null || true
|
||
ok=0
|
||
for _ in 1 2 3 4 5 6 7 8; do
|
||
if systemctl is-active --quiet cellular-proxy.service; then
|
||
ok=1
|
||
break
|
||
fi
|
||
sleep 1
|
||
done
|
||
active_state="$(systemctl is-active cellular-proxy.service 2>/dev/null || echo unknown)"
|
||
info "服务状态: $active_state"
|
||
if [[ "$ok" -ne 1 ]]; then
|
||
err "服务未 active"
|
||
journalctl -u cellular-proxy -n 30 --no-pager 2>/dev/null || true
|
||
die "升级后启动失败"
|
||
fi
|
||
fi
|
||
|
||
watch_flag="$(echo "${ENABLE_CELLULAR_WATCH:-true}" | tr '[:upper:]' '[:lower:]')"
|
||
if [[ "$watch_flag" == "false" || "$watch_flag" == "0" || "$watch_flag" == "no" ]]; then
|
||
systemctl disable --now cellular-proxy-watch.service cellular-proxy-watch.timer 2>/dev/null || true
|
||
info "ENABLE_CELLULAR_WATCH=false,未启用自动监控"
|
||
else
|
||
systemctl enable cellular-proxy-watch.service 2>/dev/null || true
|
||
systemctl enable cellular-proxy-watch.timer 2>/dev/null || true
|
||
if [[ "$SKIP_START" != "true" ]]; then
|
||
systemctl restart cellular-proxy-watch.service 2>/dev/null || true
|
||
systemctl restart cellular-proxy-watch.timer 2>/dev/null || true
|
||
fi
|
||
info "已启用 cellular-proxy-watch(网卡/源 IP 变化自动 rebind)"
|
||
fi
|
||
|
||
if [[ "$DO_VERIFY" == "true" && "$SKIP_START" != "true" ]]; then
|
||
info "出口验证…"
|
||
set +e
|
||
VERIFY_QUICK=1 EGRESS_TIMEOUT="${EGRESS_TIMEOUT:-8}" "$INSTALL_DIR/scripts/verify.sh"
|
||
vr=$?
|
||
set -e
|
||
if [[ "$vr" -eq 0 ]]; then
|
||
info "出口验证通过"
|
||
else
|
||
warn "验证未通过(exit=$vr),可稍后: cpxy verify"
|
||
fi
|
||
else
|
||
info "已跳过出口验证(需要时: --verify 或 cpxy verify)"
|
||
fi
|
||
|
||
# shellcheck disable=SC1090
|
||
source "$INSTALL_DIR/etc/settings.conf" 2>/dev/null || true
|
||
lan="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}' || true)"
|
||
|
||
cat <<EOM
|
||
|
||
========== 增量升级完成 ==========
|
||
安装目录: $INSTALL_DIR
|
||
数据网卡: ${CELLULAR_IFACE:-$cell} (${src_ip:-})
|
||
保留: PANEL_SECRET / PROXY_USER / 端口等 settings
|
||
|
||
代理: ${lan:-127.0.0.1}:${PROXY_MIXED_PORT:-7890}
|
||
面板: http://${lan:-<LAN-IP>}:${PANEL_PORT:-9090}/ui/
|
||
管理: :9091(账密配置)
|
||
|
||
下次升级:
|
||
sudo cpxy upgrade
|
||
sudo cpxy upgrade --ui-only
|
||
sudo cpxy upgrade --force-binary
|
||
EOM
|
||
exit 0
|