Files
clash-for-linux/scripts/core/runtime.sh
T

400 lines
10 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# shellcheck source=scripts/core/common.sh
source "${PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}/scripts/core/common.sh"
# shellcheck source=scripts/core/config.sh
source "${PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}/scripts/core/config.sh"
resolve_yq() {
local arch version file url tmp_dir tmp_file
arch="$(get_arch)"
version="${YQ_VERSION:-$DEFAULT_YQ_VERSION}"
if [ -x "$(yq_bin)" ]; then
return 0
fi
case "$arch" in
amd64) file="yq_linux_amd64.tar.gz" ;;
arm64) file="yq_linux_arm64.tar.gz" ;;
armv7) file="yq_linux_arm.tar.gz" ;;
*) die "暂不支持的 yq 架构:$arch" ;;
esac
url="https://github.com/mikefarah/yq/releases/download/${version}/${file}"
tmp_dir="$(mktemp -d)"
tmp_file="$tmp_dir/$file"
download_file "$url" "$tmp_file" "yq"
tar -xzf "$tmp_file" -C "$tmp_dir"
if [ -f "$tmp_dir/yq_linux_${arch}" ]; then
install -m 0755 "$tmp_dir/yq_linux_${arch}" "$(yq_bin)"
elif [ -f "$tmp_dir/yq_linux_arm" ]; then
install -m 0755 "$tmp_dir/yq_linux_arm" "$(yq_bin)"
elif [ -f "$tmp_dir/yq" ]; then
install -m 0755 "$tmp_dir/yq" "$(yq_bin)"
else
rm -rf "$tmp_dir"
die "解压后未找到 yq 可执行文件"
fi
rm -rf "$tmp_dir"
}
resolve_mihomo() {
local arch version file url tmp_file url_base custom_url
arch="$(get_arch)"
version="${MIHOMO_VERSION:-$DEFAULT_MIHOMO_VERSION}"
url_base="${MIHOMO_DOWNLOAD_BASE:-https://github.com/MetaCubeX/mihomo/releases/download}"
custom_url="${MIHOMO_DOWNLOAD_URL:-}"
if [ -x "$(mihomo_bin)" ]; then
return 0
fi
case "$arch" in
amd64) file="mihomo-linux-amd64-compatible-${version}.gz" ;;
arm64) file="mihomo-linux-arm64-${version}.gz" ;;
armv7) file="mihomo-linux-armv7-${version}.gz" ;;
*) die "暂不支持的 Mihomo 架构:$arch" ;;
esac
if [ -n "${custom_url:-}" ]; then
url="$custom_url"
else
url="${url_base%/}/${version}/${file}"
fi
tmp_file="$(mktemp)"
download_file "$url" "$tmp_file" "mihomo(可在 .env 中设置 MIHOMO_DOWNLOAD_BASE / MIHOMO_DOWNLOAD_URL)"
gzip -dc "$tmp_file" > "$(mihomo_bin)"
chmod +x "$(mihomo_bin)"
rm -f "$tmp_file"
}
resolve_clash() {
local arch version version_no_v url_base tmp_file url downloaded="false"
local candidates file
arch="$(get_arch)"
version="${CLASH_VERSION:-$DEFAULT_CLASH_VERSION}"
version_no_v="${version#v}"
url_base="${CLASH_DOWNLOAD_BASE:-https://github.com/WindSpiritSR/clash/releases/download}"
if [ -x "$(clash_bin)" ] \
&& [ "$(read_runtime_value "KERNEL_TYPE_INSTALLED" 2>/dev/null || true)" = "clash" ] \
&& [ "$(read_runtime_value "CLASH_VERSION_INSTALLED" 2>/dev/null || true)" = "$version" ]; then
return 0
fi
case "$arch" in
amd64)
candidates="
clash-linux-amd64-${version}.gz
clash-linux-amd64-v${version_no_v}.gz
"
;;
arm64)
candidates="
clash-linux-arm64-${version}.gz
clash-linux-arm64-v${version_no_v}.gz
clash-linux-armv8-${version}.gz
clash-linux-armv8-v${version_no_v}.gz
"
;;
armv7)
candidates="
clash-linux-armv7-${version}.gz
clash-linux-armv7-v${version_no_v}.gz
"
;;
*)
die "暂不支持的 Clash 架构:$arch"
;;
esac
tmp_file="$(mktemp)"
rm -f "$tmp_file"
for file in $candidates; do
url="${url_base}/${version}/${file}"
if download_file "$url" "$tmp_file" "clash"; then
downloaded="true"
break
fi
done
[ "$downloaded" = "true" ] || {
rm -f "$tmp_file"
die "Clash 内核下载失败,请检查版本或在 .env 中覆盖 CLASH_DOWNLOAD_BASE / CLASH_VERSION"
}
gzip -dc "$tmp_file" > "$(clash_bin)"
chmod +x "$(clash_bin)"
rm -f "$tmp_file"
write_runtime_value "KERNEL_TYPE_INSTALLED" "clash"
write_runtime_value "CLASH_VERSION_INSTALLED" "$version"
}
resolve_runtime_kernel() {
case "$(runtime_kernel_type)" in
mihomo)
resolve_mihomo
write_runtime_value "KERNEL_TYPE_INSTALLED" "mihomo"
write_runtime_value "MIHOMO_VERSION_INSTALLED" "${MIHOMO_VERSION:-$DEFAULT_MIHOMO_VERSION}"
;;
clash)
resolve_clash
;;
*)
die "未知内核类型:$(runtime_kernel_type)"
;;
esac
}
subconverter_version_file_value() {
read_runtime_value "SUBCONVERTER_VERSION_INSTALLED" 2>/dev/null || true
}
mark_subconverter_version_installed() {
local version="$1"
write_runtime_value "SUBCONVERTER_VERSION_INSTALLED" "$version"
}
resolve_subconverter() {
local arch version file url tmp_dir tmp_file target_dir installed_version
arch="$(get_arch)"
version="${SUBCONVERTER_VERSION:-$DEFAULT_SUBCONVERTER_VERSION}"
target_dir="$(subconverter_home)"
installed_version="$(subconverter_version_file_value)"
if [ -x "$(subconverter_bin)" ] && [ "$installed_version" = "$version" ]; then
return 0
fi
case "$arch" in
amd64) file="subconverter_linux64.tar.gz" ;;
arm64) file="subconverter_aarch64.tar.gz" ;;
armv7) file="subconverter_armv7.tar.gz" ;;
*) die "暂不支持的 subconverter 架构:$arch" ;;
esac
url="https://github.com/tindy2013/subconverter/releases/download/${version}/${file}"
tmp_dir="$(mktemp -d)"
tmp_file="$tmp_dir/$file"
download_file "$url" "$tmp_file" "subconverter"
rm -rf "$target_dir"
mkdir -p "$target_dir"
tar -xzf "$tmp_file" -C "$target_dir"
[ -x "$(subconverter_bin)" ] || {
rm -rf "$tmp_dir"
die "解压后未找到 subconverter 可执行文件"
}
chmod +x "$(subconverter_bin)"
mark_subconverter_version_installed "$version"
rm -rf "$tmp_dir"
}
ensure_runtime_config_ready() {
local config_file last_file now reason
config_file="$RUNTIME_DIR/config.yaml"
last_file="$RUNTIME_DIR/config.last.yaml"
if [ -s "$config_file" ] && test_runtime_config "$config_file" >/dev/null 2>&1; then
write_runtime_event_value "RUNTIME_LAST_FALLBACK_USED" "false"
write_runtime_event_value "RUNTIME_LAST_FALLBACK_TIME" ""
write_runtime_event_value "RUNTIME_LAST_FALLBACK_REASON" ""
write_runtime_event_value "RUNTIME_LAST_RISK_LEVEL" "$(calculate_runtime_risk_level)"
mark_runtime_config_source "runtime"
return 0
fi
reason="当前运行配置不可用"
warn "当前运行配置不可用,尝试回退到上一次成功配置"
[ -s "$last_file" ] || die "当前配置不可用,且不存在可回退的最后成功配置:$last_file"
if test_runtime_config "$last_file" >/dev/null 2>&1; then
cp -f "$last_file" "$config_file"
now="$(now_datetime)"
write_runtime_event_value "RUNTIME_LAST_FALLBACK_USED" "true"
write_runtime_event_value "RUNTIME_LAST_FALLBACK_TIME" "$now"
write_runtime_event_value "RUNTIME_LAST_FALLBACK_REASON" "$reason"
write_runtime_event_value "RUNTIME_LAST_RISK_LEVEL" "$(calculate_runtime_risk_level)"
mark_runtime_config_source "last_good"
success "已回退到最后成功配置"
return 0
fi
die "最后成功配置也不可用:$last_file"
}
tun_effective_check() {
local config_tun_enabled auto_route controller_ok route_ok
config_tun_enabled="$(runtime_config_tun_enabled 2>/dev/null || true)"
if [ "$(tun_enabled)" != "true" ]; then
echo "disabled-in-state"
return 1
fi
if [ "${config_tun_enabled:-false}" != "true" ]; then
echo "disabled-in-runtime-config"
return 1
fi
if ! status_is_running 2>/dev/null; then
echo "runtime-not-running"
return 1
fi
controller_ok="false"
if proxy_controller_reachable 2>/dev/null; then
controller_ok="true"
fi
if [ "$controller_ok" != "true" ]; then
echo "controller-unreachable"
return 1
fi
auto_route="$(runtime_config_tun_auto_route 2>/dev/null || true)"
route_ok="unknown"
if [ "${auto_route:-false}" = "true" ]; then
if default_route_is_tun_like; then
route_ok="true"
else
route_ok="false"
fi
if [ "$route_ok" != "true" ]; then
echo "default-route-not-tun"
return 1
fi
fi
echo "ok"
return 0
}
tun_disable_check() {
local config_tun_enabled
config_tun_enabled="$(runtime_config_tun_enabled 2>/dev/null || true)"
if [ "$(tun_enabled)" = "true" ]; then
echo "state-still-enabled"
return 1
fi
if [ "${config_tun_enabled:-false}" = "true" ]; then
echo "runtime-config-still-enabled"
return 1
fi
if status_is_running 2>/dev/null; then
if ! proxy_controller_reachable 2>/dev/null; then
echo "runtime-restarted-but-controller-unreachable"
return 1
fi
fi
echo "ok"
return 0
}
start_runtime() {
local config_file="$RUNTIME_DIR/config.yaml"
resolve_runtime_kernel
ensure_runtime_config_ready
if [ -f "$RUNTIME_DIR/mihomo.pid" ]; then
local old_pid
old_pid="$(cat "$RUNTIME_DIR/mihomo.pid" 2>/dev/null || true)"
if [ -n "${old_pid:-}" ] && kill -0 "$old_pid" 2>/dev/null; then
warn "Mihomo 已在运行:pid=$old_pid"
return 0
fi
rm -f "$RUNTIME_DIR/mihomo.pid"
fi
nohup "$(runtime_kernel_bin)" -f "$config_file" -d "$RUNTIME_DIR" \
> "$LOG_DIR/mihomo.out.log" 2>&1 &
echo $! > "$RUNTIME_DIR/mihomo.pid"
success "$(runtime_kernel_name) 已启动:pid=$(cat "$RUNTIME_DIR/mihomo.pid")"
}
stop_runtime() {
local pid_file="$RUNTIME_DIR/mihomo.pid"
[ -f "$pid_file" ] || {
warn "Mihomo 当前未运行"
return 0
}
local pid
pid="$(cat "$pid_file" 2>/dev/null || true)"
if [ -n "${pid:-}" ] && kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null || true
sleep 1
kill -9 "$pid" 2>/dev/null || true
fi
rm -f "$pid_file"
success "Mihomo 已停止"
}
runtime_status_text() {
if [ -f "$RUNTIME_DIR/mihomo.pid" ]; then
local pid
pid="$(cat "$RUNTIME_DIR/mihomo.pid" 2>/dev/null || true)"
if [ -n "${pid:-}" ] && kill -0 "$pid" 2>/dev/null; then
echo "运行中"
echo "进程号:$pid"
return 0
fi
fi
echo "未运行"
}
clean_runtime_state() {
stop_subconverter || true
stop_runtime || true
clear_build_meta || true
clear_runtime_event_file || true
rm -f "$RUNTIME_DIR/config.yaml" 2>/dev/null || true
rm -f "$(runtime_meta_file)" 2>/dev/null || true
rm -f "$RUNTIME_DIR"/*.pid 2>/dev/null || true
rm -f "$RUNTIME_DIR"/*.lock 2>/dev/null || true
rm -rf "$LOG_DIR" 2>/dev/null || true
mkdir -p "$LOG_DIR"
rm -rf "$RUNTIME_DIR/profiles" 2>/dev/null || true
rm -rf "$RUNTIME_DIR/generated" 2>/dev/null || true
rm -rf "$RUNTIME_DIR/tmp" 2>/dev/null || true
rm -rf "$(runtime_dashboard_dir)" 2>/dev/null || true
clear_shell_proxy_persist_state || true
mkdir -p "$RUNTIME_DIR" "$BIN_DIR" "$LOG_DIR"
}