diff --git a/uninstall.sh b/uninstall.sh index 4b61277..811cda9 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -1,152 +1,105 @@ #!/usr/bin/env bash set -euo pipefail -# More accurate uninstall for clash-for-linux SERVICE_NAME="clash-for-linux" UNIT_PATH="/etc/systemd/system/${SERVICE_NAME}.service" +INSTALL_DIR="${CLASH_INSTALL_DIR:-/opt/clash-for-linux}" -RED='\033[31m' -GREEN='\033[32m' -YELLOW='\033[33m' -NC='\033[0m' -info() { echo -e "${GREEN}[INFO]${NC} $*"; } -ok() { echo -e "${GREEN}[OK]${NC} $*"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } -err() { echo -e "${RED}[ERROR]${NC} $*"; } +CLASHCTL_LINK="/usr/local/bin/clashctl" +PROFILED_FILE="/etc/profile.d/clash-for-linux.sh" + +PURGE=false + +for arg in "$@"; do + case "$arg" in + --purge) + PURGE=true + ;; + *) + echo "[ERROR] Unknown arg: $arg" >&2 + echo "Usage: uninstall.sh [--purge]" >&2 + exit 2 + ;; + esac +done + +log() { printf "%b\n" "$*"; } +ok() { log "\033[32m[OK]\033[0m $*"; } +warn() { log "\033[33m[WARN]\033[0m $*"; } +err() { log "\033[31m[ERROR]\033[0m $*"; } if [ "$(id -u)" -ne 0 ]; then - err "需要 root 权限执行卸载脚本(请使用 sudo bash uninstall.sh)" + err "需要 root 权限执行卸载" exit 1 fi -# Candidate install dirs: -# 1) explicit CLASH_INSTALL_DIR -# 2) working directory if it looks like clash-for-linux -# 3) service WorkingDirectory / ExecStart path inferred from unit -# 4) common defaults -candidates=() -[ -n "${CLASH_INSTALL_DIR:-}" ] && candidates+=("${CLASH_INSTALL_DIR}") -PWD_BASENAME="$(basename "${PWD}")" -if [ "$PWD_BASENAME" = "clash-for-linux" ] && [ -f "${PWD}/start.sh" ]; then - candidates+=("${PWD}") -fi - -if [ -f "$UNIT_PATH" ]; then - wd="$(sed -nE 's#^WorkingDirectory=(.*)#\1#p' "$UNIT_PATH" | head -n1 || true)" - [ -n "$wd" ] && candidates+=("$wd") - - exec_path="$(sed -nE 's#^ExecStart=/bin/bash[[:space:]]+([^[:space:]]+/start\.sh).*#\1#p' "$UNIT_PATH" | head -n1 || true)" - if [ -n "$exec_path" ]; then - candidates+=("$(dirname "$exec_path")") - fi -fi - -candidates+=("/root/clash-for-linux" "/opt/clash-for-linux") - -# normalize + uniq + choose first existing dir containing start.sh or shutdown.sh -INSTALL_DIR="" -declare -A seen -for d in "${candidates[@]}"; do - [ -n "$d" ] || continue - d="${d%/}" - [ -n "$d" ] || continue - if [ -z "${seen[$d]:-}" ]; then - seen[$d]=1 - if [ -d "$d" ] && { [ -f "$d/start.sh" ] || [ -f "$d/shutdown.sh" ] || [ -d "$d/conf" ]; }; then - INSTALL_DIR="$d" - break - fi - fi -done - -if [ -z "$INSTALL_DIR" ]; then - warn "未能自动识别安装目录,将按候选路径继续清理 systemd / 环境文件。" -else - info "识别到安装目录: $INSTALL_DIR" -fi - -info "开始卸载 ${SERVICE_NAME} ..." - -# 1) graceful stop -if [ -n "$INSTALL_DIR" ] && [ -f "${INSTALL_DIR}/shutdown.sh" ]; then - info "执行 shutdown.sh(优雅停止)..." - bash "${INSTALL_DIR}/shutdown.sh" >/dev/null 2>&1 || true -fi +echo "[INFO] uninstalling clash-for-linux..." +# ========================= +# 停止服务 +# ========================= if command -v systemctl >/dev/null 2>&1; then - info "停止并禁用 systemd 服务..." systemctl stop "${SERVICE_NAME}.service" >/dev/null 2>&1 || true systemctl disable "${SERVICE_NAME}.service" >/dev/null 2>&1 || true fi -# 2) stop process by pid file from all likely dirs -for d in "/root/clash-for-linux" "/opt/clash-for-linux" "${INSTALL_DIR:-}"; do - [ -n "$d" ] || continue - PID_FILE="$d/runtime/clash.pid" - if [ -f "$PID_FILE" ]; then - PID="$(cat "$PID_FILE" 2>/dev/null || true)" - if [ -n "${PID:-}" ] && kill -0 "$PID" 2>/dev/null; then - info "检测到 PID=${PID}(来自 $PID_FILE),尝试停止..." - kill "$PID" 2>/dev/null || true - sleep 1 - if kill -0 "$PID" 2>/dev/null; then - warn "进程仍在运行,强制 kill -9 ${PID}" - kill -9 "$PID" 2>/dev/null || true - fi +# ========================= +# 停止进程(仅当前项目) +# ========================= +PID_FILE="${INSTALL_DIR}/runtime/clash.pid" + +if [ -f "$PID_FILE" ]; then + PID="$(cat "$PID_FILE" 2>/dev/null || true)" + + if [ -n "${PID:-}" ] && kill -0 "$PID" 2>/dev/null; then + echo "[INFO] stopping pid=$PID" + kill "$PID" 2>/dev/null || true + sleep 1 + + if kill -0 "$PID" 2>/dev/null; then + echo "[WARN] force kill -9 $PID" + kill -9 "$PID" 2>/dev/null || true fi - rm -f "$PID_FILE" || true fi -done -# 兜底:按完整路径匹配,避免误杀其他 clash -pkill -f '/clash-for-linux/.*/clash' >/dev/null 2>&1 || true -pkill -f '/clash-for-linux/.*/mihomo' >/dev/null 2>&1 || true -sleep 1 -pkill -9 -f '/clash-for-linux/.*/clash' >/dev/null 2>&1 || true -pkill -9 -f '/clash-for-linux/.*/mihomo' >/dev/null 2>&1 || true + rm -f "$PID_FILE" +fi -# 3) remove unit and related files +# ========================= +# 删除 systemd +# ========================= if [ -f "$UNIT_PATH" ]; then rm -f "$UNIT_PATH" - ok "已移除 systemd 单元: ${UNIT_PATH}" -fi -if [ -d "/etc/systemd/system/${SERVICE_NAME}.service.d" ]; then - rm -rf "/etc/systemd/system/${SERVICE_NAME}.service.d" - ok "已移除 drop-in: /etc/systemd/system/${SERVICE_NAME}.service.d" + ok "removed systemd unit" fi + if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload >/dev/null 2>&1 || true systemctl reset-failed >/dev/null 2>&1 || true fi -# 4) cleanup env / command entry -rm -f "/etc/default/${SERVICE_NAME}" >/dev/null 2>&1 || true -rm -f "/etc/profile.d/clash-for-linux.sh" >/dev/null 2>&1 || true -rm -f "/usr/local/bin/clashctl" >/dev/null 2>&1 || true -for d in "/root/clash-for-linux" "/opt/clash-for-linux" "${INSTALL_DIR:-}"; do - [ -n "$d" ] || continue - rm -f "$d/temp/clash-for-linux.sh" >/dev/null 2>&1 || true -done +# ========================= +# 删除命令入口 +# ========================= +rm -f "$CLASHCTL_LINK" >/dev/null 2>&1 || true +rm -f "$PROFILED_FILE" >/dev/null 2>&1 || true -# 5) remove install dirs -removed_any=false -for d in "${INSTALL_DIR:-}" "/root/clash-for-linux" "/opt/clash-for-linux"; do - [ -n "$d" ] || continue - if [ -d "$d" ] && { [ -f "$d/start.sh" ] || [ -d "$d/conf" ] || [ "$d" = "$INSTALL_DIR" ]; }; then - rm -rf "$d" - ok "已移除安装目录: $d" - removed_any=true +ok "removed command + env" + +# ========================= +# 删除安装目录 +# ========================= +if [ "$PURGE" = true ]; then + if [ -d "$INSTALL_DIR" ]; then + rm -rf "$INSTALL_DIR" + ok "removed install dir: $INSTALL_DIR" + else + warn "install dir not found: $INSTALL_DIR" fi -done - -if [ "$removed_any" = false ]; then - warn "未发现可删除的安装目录" +else + warn "install dir preserved: $INSTALL_DIR" + echo "run with --purge to remove it" fi echo -warn "如果你曾执行 proxy_on,当前终端可能仍保留代理环境变量。可执行:" -echo " unset http_proxy https_proxy no_proxy HTTP_PROXY HTTPS_PROXY NO_PROXY" -echo " # 或关闭终端重新打开" - -echo -ok "卸载完成 ✅" +ok "uninstall complete" \ No newline at end of file