diff --git a/clashctl b/clashctl index 4d26e2e..e7f02ea 100755 --- a/clashctl +++ b/clashctl @@ -35,7 +35,9 @@ PID_FILE="$CLASH_HOME/temp/clash.pid" SUBSCRIPTION_FILE="$CLASH_HOME/conf/subscriptions.list" use_systemd() { - command -v systemctl >/dev/null 2>&1 + command -v systemctl >/dev/null 2>&1 || return 1 + systemctl show --property=Version --value >/dev/null 2>&1 || return 1 + return 0 } action_with_systemd() { @@ -78,10 +80,11 @@ set_env_var() { echo "[ERROR] 未找到 .env 文件: $ENV_FILE" >&2 exit 1 fi - local escaped + local escaped escaped_sed escaped=$(printf "%s" "$value" | sed "s/'/'\"'\"'/g") + escaped_sed=$(printf "%s" "$escaped" | sed 's/[\\&@]/\\&/g') if grep -q "^export ${key}=" "$ENV_FILE"; then - sed -i "s@^export ${key}=.*@export ${key}='${escaped}'@" "$ENV_FILE" + sed -i "s@^export ${key}=.*@export ${key}='${escaped_sed}'@" "$ENV_FILE" else echo "export ${key}='${escaped}'" >> "$ENV_FILE" fi diff --git a/install.sh b/install.sh index f397e45..3b7aef9 100755 --- a/install.sh +++ b/install.sh @@ -351,35 +351,57 @@ read_secret_from_config() { printf '%s' "$s" } +# 判断 systemd 是否可用(仅有 systemctl 命令但 PID 1 不是 systemd 时视为不可用) +systemd_ready() { + command -v systemctl >/dev/null 2>&1 || return 1 + systemctl show --property=Version --value >/dev/null 2>&1 || return 1 + return 0 +} + # ========================= # systemd 安装与启动 # ========================= Service_Enabled="unknown" Service_Started="unknown" +Systemd_Usable="false" -if command -v systemctl >/dev/null 2>&1; then - CLASH_SERVICE_USER="$Service_User" CLASH_SERVICE_GROUP="$Service_Group" "$Install_Dir/scripts/install_systemd.sh" +if systemd_ready; then + Systemd_Usable="true" +fi - if [ "${CLASH_ENABLE_SERVICE:-true}" = "true" ]; then - systemctl enable "${Service_Name}.service" >/dev/null 2>&1 || true - fi - if [ "${CLASH_START_SERVICE:-true}" = "true" ]; then - systemctl start "${Service_Name}.service" >/dev/null 2>&1 || true - fi +if [ "$Systemd_Usable" = "true" ]; then + if [ "${CLASH_ENABLE_SERVICE:-true}" = "true" ] || [ "${CLASH_START_SERVICE:-true}" = "true" ]; then + CLASH_SERVICE_USER="$Service_User" CLASH_SERVICE_GROUP="$Service_Group" "$Install_Dir/scripts/install_systemd.sh" - if systemctl is-enabled --quiet "${Service_Name}.service" 2>/dev/null; then - Service_Enabled="enabled" + if [ "${CLASH_ENABLE_SERVICE:-true}" = "true" ]; then + systemctl enable "${Service_Name}.service" >/dev/null 2>&1 || true + fi + if [ "${CLASH_START_SERVICE:-true}" = "true" ]; then + systemctl start "${Service_Name}.service" >/dev/null 2>&1 || true + fi + + if systemctl is-enabled --quiet "${Service_Name}.service" 2>/dev/null; then + Service_Enabled="enabled" + else + Service_Enabled="disabled" + fi + + if systemctl is-active --quiet "${Service_Name}.service" 2>/dev/null; then + Service_Started="active" + else + Service_Started="inactive" + fi else + info "已按配置跳过 systemd 服务安装与启动(CLASH_ENABLE_SERVICE=false 且 CLASH_START_SERVICE=false)" Service_Enabled="disabled" - fi - - if systemctl is-active --quiet "${Service_Name}.service" 2>/dev/null; then - Service_Started="active" - else Service_Started="inactive" fi else - warn "未检测到 systemd,已跳过服务单元生成" + if command -v systemctl >/dev/null 2>&1; then + warn "检测到 systemctl 命令,但当前环境不可用 systemd(常见于 Docker 容器),已跳过服务单元生成" + else + warn "未检测到 systemd,已跳过服务单元生成" + fi fi # ========================= @@ -394,7 +416,7 @@ install_profiled() { [ "$http_port" = "auto" ] && http_port="7890" # 只写 IPv4 loopback,避免某些环境 ::1 解析问题 - sudo tee "$PROFILED_FILE" >/dev/null </dev/null </dev/null 2>&1; then +if [ "$Systemd_Usable" = "true" ]; then section "服务状态" se="${Service_Enabled:-unknown}" @@ -464,6 +486,11 @@ if command -v systemctl >/dev/null 2>&1; then log "${C_BOLD}常用命令:${C_NC}" log " $(cmd "sudo systemctl status ${Service_Name}.service")" log " $(cmd "sudo systemctl restart ${Service_Name}.service")" +else + section "服务状态" + warn "当前环境未启用 systemd(如 Docker 容器),请使用 clashctl 管理进程" + log " $(cmd "sudo clashctl start")" + log " $(cmd "sudo clashctl restart")" fi # ========================= @@ -514,7 +541,11 @@ else log " $(cmd "sudo bash -c 'echo \"CLASH_URL=<订阅地址>\" > ${ENV_FILE}'")" log "" log "配置完成后重启服务:" - log " $(cmd "sudo systemctl restart ${Service_Name}.service")" + if [ "$Systemd_Usable" = "true" ]; then + log " $(cmd "sudo systemctl restart ${Service_Name}.service")" + else + log " $(cmd "sudo clashctl restart")" + fi fi # ========================= @@ -535,9 +566,9 @@ fi # 启动后快速诊断 # ========================= sleep 1 -if command -v journalctl >/dev/null 2>&1; then +if [ "$Systemd_Usable" = "true" ] && command -v journalctl >/dev/null 2>&1; then if journalctl -u "${Service_Name}.service" -n 50 --no-pager 2>/dev/null \ | grep -q "Clash订阅地址不可访问"; then warn "服务启动异常:订阅不可用,请检查 CLASH_URL(可能过期 / 404 / 被墙)。" fi -fi \ No newline at end of file +fi diff --git a/start.sh b/start.sh index 80b45f2..9bd352d 100644 --- a/start.sh +++ b/start.sh @@ -88,6 +88,7 @@ URL="${CLASH_URL:-}" # 清理可能的 CRLF(Windows 写 .env 很常见) URL="$(printf '%s' "$URL" | tr -d '\r')" +URL="$(printf '%s' "$URL" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')" #让 bash 子进程能拿到 export CLASH_URL="$URL" @@ -97,6 +98,10 @@ if [ -z "$URL" ] && [ "${SYSTEMD_MODE:-false}" != "true" ]; then echo "[ERR] CLASH_URL 为空(未配置订阅地址)" exit 2 fi +if [ -n "$URL" ] && ! printf '%s' "$URL" | grep -Eq '^https?://'; then + echo "[ERR] CLASH_URL 格式无效:必须以 http:// 或 https:// 开头" >&2 + exit 2 +fi # 获取 CLASH_SECRET 值:优先 .env;其次读取旧 config;占位符视为无效;最后生成随机值 Secret="${CLASH_SECRET:-}"