From afd10467e5e04329f63a9bf10efe7668d48ee057 Mon Sep 17 00:00:00 2001 From: Lihatoo <1747565629@gmail.com> Date: Fri, 13 Mar 2026 16:51:06 +0800 Subject: [PATCH] Improve mh routing, diagnostics, and MCP setup --- README.md | 38 +++++- mh | 391 +++++++++++++++++++++++++++++++++++++++++++++++++---- test_mh.sh | 197 +++++++++++++++++++++++++++ 3 files changed, 590 insertions(+), 36 deletions(-) create mode 100755 test_mh.sh diff --git a/README.md b/README.md index 248f195..6969aed 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ mihomo: https://github.com/mihomo/mihomo sudo chmod +x mh # 给予执行权限 sudo setcap cap_net_admin,cap_net_raw+ep /home/lht/bfile/mihomo/mihomo # 给予网络tun权限 ``` -然后在任何终端都可以使用`mh`命令 +然后在任何终端都可以使用`mh`命令。 # 使用 @@ -47,10 +47,39 @@ mh name # 搜索并列表选择节点 ## 其他操作 ```bash -mh tun on/off # 开启/关闭tun +mh tun on/off # 开启/关闭tun,可能需要sudo权限 +mh env # 输出当前 shell 可用的代理 export 片段 +mh mode rule/global/direct # 切换运行模式 +mh rules show/reset # 查看或重置内建基础规则 mh -h # 帮助 mh # 快速预览信息 mh db # 一些测试,debug专用 +sudo -E apt install ...#如果下载没有网络,则是环境没有导入 +``` + +如果要让当前 shell 立刻走代理,不能只执行 `mh`,因为它是子进程,没法反向修改你的父 shell 环境。要这样用: +```bash +eval "$(mh env)" +``` +默认输出的是 `127.0.0.1:7897`,和常见 GUI 代理端口保持一致。也可以把 `mh env` 的输出写进 `~/.bashrc` / `~/.zshrc`。 + +## 基础规则 + +现在新建的 profile 默认走一套很简单的规则: + +- 局域网、本地回环直连 +- 其他流量走 `Final` +- `Final` 默认选择 `Proxy` + +可以直接调整: + +```bash +mh mode rule # 规则模式 +mh mode global # 全局代理 +mh mode direct # 全局直连 + +mh rules show # 查看当前规则 +mh rules reset # 重置成内建基础规则 ``` # 错误 @@ -67,8 +96,3 @@ mh db # 一些测试,debug专用 - - - - - diff --git a/mh b/mh index 645d1e7..6503e22 100755 --- a/mh +++ b/mh @@ -1,14 +1,15 @@ #!/usr/bin/env bash set -euo pipefail shopt -s nullglob + MIHOMO_ROOT=/home/lht/bfile/mihomo MIHOMO_BIN=${MIHOMO_ROOT}/mihomo # 指向二进制文件 PID_FILE=${MIHOMO_ROOT}/run.pid # 固定pid文件 API_HOST="127.0.0.1" API_PORT="9090" -MIXED_PORT="7890" # 默认main -SOCK_PORT="7891" +MIXED_PORT="7897" # 默认main +SOCK_PORT="7898" API_BASE="http://${API_HOST}:${API_PORT}" API_SECRET="" # 这是密钥,用于验证;对应yaml中设置的secret字段,保持一致即可;如果不设置,默认空字符串,表示不验证 @@ -79,6 +80,20 @@ api_put() { fi } +sync_global_group() { + local pjson + pjson="$(api_get "/proxies" 2>/dev/null)" || return 1 + + jq -e '.proxies["GLOBAL"]?.all? | index("Proxy") != null' >/dev/null <<<"$pjson" || return 0 + + local now + now="$(jq -r '.proxies["GLOBAL"].now // empty' <<<"$pjson")" + if [[ "$now" != "Proxy" ]]; then + api_put "/proxies/GLOBAL" '{"name":"Proxy"}' >/dev/null + info "Synced global selector [GLOBAL] -> [Proxy]" + fi +} + api_delay() { local name="$1" @@ -116,6 +131,21 @@ need_root() { fi return 0 } + +has_mihomo_cap_net_admin() { + command -v getcap >/dev/null 2>&1 || return 1 + getcap "$MIHOMO_BIN" 2>/dev/null | grep -q 'cap_net_admin' +} + +disable_dns_fallback_geoip() { + local cfg="$1" + [[ -f "$cfg" ]] || return 1 + yq -y -i ' + .dns.enable = false | + .dns.fallback = [] | + .dns."fallback-filter" = null + ' "$cfg" +} #===============测试小工具,上====================================== @@ -218,7 +248,7 @@ socks-port: ${SOCK_PORT} allow-lan: false bind-address: ${API_HOST} -mode: global +mode: rule log-level: info external-controller: ${API_HOST}:${API_PORT} @@ -236,6 +266,25 @@ proxy-groups: type: select use: - ${name} + - name: Final + type: select + proxies: + - Proxy + - DIRECT + +rules: + - DOMAIN-SUFFIX,localhost,DIRECT + - DOMAIN-SUFFIX,local,DIRECT + - IP-CIDR,127.0.0.0/8,DIRECT,no-resolve + - IP-CIDR,10.0.0.0/8,DIRECT,no-resolve + - IP-CIDR,172.16.0.0/12,DIRECT,no-resolve + - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve + - IP-CIDR,100.64.0.0/10,DIRECT,no-resolve + - IP-CIDR,224.0.0.0/4,DIRECT,no-resolve + - IP-CIDR6,::1/128,DIRECT,no-resolve + - IP-CIDR6,fc00::/7,DIRECT,no-resolve + - IP-CIDR6,fe80::/10,DIRECT,no-resolve + - MATCH,Final YAML } @@ -272,22 +321,92 @@ print_mihomo_status() { echo "✅ Now: ${now}" } +port_open_local() { + local port="$1" + ss -lnt 2>/dev/null | awk '{print $4}' | grep -qE "(:|\\.)${port}\$" +} + +show_runtime_routing_status() { + local cfg pjson + cfg="$(api_get "/configs" 2>/dev/null)" || { + echo "Routing: API unavailable" + return 0 + } + pjson="$(api_get "/proxies" 2>/dev/null)" || { + echo "Routing: /proxies unavailable" + return 0 + } + + local mode global_now final_now proxy_now + mode="$(jq -r '.mode // "-"' <<<"$cfg")" + global_now="$(jq -r '.proxies["GLOBAL"].now // empty' <<<"$pjson")" + final_now="$(jq -r '.proxies["Final"].now // empty' <<<"$pjson")" + proxy_now="$(jq -r '.proxies["Proxy"].now // empty' <<<"$pjson")" + + echo "Mode: ${mode}" + [[ -n "$global_now" ]] && echo "GLOBAL -> ${global_now}" + [[ -n "$final_now" ]] && echo "Final -> ${final_now}" + [[ -n "$proxy_now" ]] && echo "Proxy -> ${proxy_now}" +} + +extract_proxy_port() { + local value="${1:-}" + [[ -n "$value" ]] || return 1 + sed -E 's#^[[:alpha:]][[:alnum:]+.-]*://[^:/]+:([0-9]+)/*$#\1#' <<<"$value" +} + +proxy_env_status_line() { + local name="$1" + local value="$2" + + if [[ -z "$value" ]]; then + echo "${name}: OFF" + return 0 + fi + + local port + port="$(extract_proxy_port "$value" 2>/dev/null || true)" + if [[ -n "$port" ]] && port_open_local "$port"; then + echo "${name}: ON (${value}, port ${port} reachable)" + elif [[ -n "$port" ]]; then + echo "${name}: STALE (${value}, port ${port} not listening)" + else + echo "${name}: ON (${value})" + fi +} + start_end(){ # start/end name/none 文件 - local start0Rend=$1 + local start0Rend="${1:-}" local name_file=${2:-} local cfg=$MIHOMO_ROOT/$name_file + [[ -n "$start0Rend" ]] || die "usage: $0 start|end" if [[ "$start0Rend" == "start" ]]; then start_end "end" # 先停止当前 - if [[ -d $cfg ]];then - # 进入的前提配置没有问题,可以直接运行 - info "start mihomo with config=$cfg/config.yaml data=$cfg" - # 前台跑不方便,这里后台启动并记录 pid - test_port "$API_PORT" "$MIXED_PORT" "$SOCK_PORT" - "$MIHOMO_BIN" -d "$cfg" -f "$cfg/config.yaml" >"$cfg/mihomo.log" 2>&1 & # 每次启动情况记录 - echo $! >"$PID_FILE" # 保存到定点位置 + [[ -d "$cfg" ]] || die "profile not found: $cfg" + [[ -f "$cfg/config.yaml" ]] || die "missing config: $cfg/config.yaml" + local provider_name + provider_name="$(yq -r '."proxy-providers" | keys | .[0] // empty' "$cfg/config.yaml" 2>/dev/null || true)" + if [[ -n "$provider_name" ]]; then + yq -y -i " + .\"proxy-providers\".\"${provider_name}\".path = \"${cfg}/providers/${provider_name}.yaml\" + " "$cfg/config.yaml" fi + yq -y -i " + .[\"mixed-port\"] = ${MIXED_PORT} | + .[\"socks-port\"] = ${SOCK_PORT} | + .[\"external-controller\"] = \"${API_HOST}:${API_PORT}\" + " "$cfg/config.yaml" + if [[ "$(yq -r '.tun.enable // false' "$cfg/config.yaml" 2>/dev/null || echo false)" != "true" ]]; then + disable_dns_fallback_geoip "$cfg/config.yaml" + fi + # 进入的前提配置没有问题,可以直接运行 + info "start mihomo with config=$cfg/config.yaml data=$cfg" + # 前台跑不方便,这里后台启动并记录 pid + test_port "$API_PORT" "$MIXED_PORT" "$SOCK_PORT" + "$MIHOMO_BIN" -d "$cfg" -f "$cfg/config.yaml" >"$cfg/mihomo.log" 2>&1 & # 每次启动情况记录 + echo $! >"$PID_FILE" # 保存到定点位置 elif [[ "$start0Rend" == "end" ]]; then if [[ -f "$PID_FILE" ]]; then # 存在就终止 local pid @@ -316,8 +435,8 @@ cfg_add() { # add name src if [[ "$1" == "add" ]]; then shift # 去掉 "add" 参数 fi - local name="$1" - local src="$2" + local name="${1:-}" + local src="${2:-}" local cfg_root="${MIHOMO_ROOT}/${name}" [[ -n "$name" ]] || die "usage: $0 add " [[ -n "$src" ]] || die "usage: $0 add " @@ -356,7 +475,7 @@ get_group_nodes_json() { help() { cat <" -l | --list | list @@ -465,6 +608,21 @@ Examples: # 停止 mihomo mh end + # 输出代理环境变量 + mh env + + # 切换模式 + mh mode rule + mh mode global + mh mode direct + + # 查看/重置基础规则 + mh rules show + mh rules reset + + # 一键诊断 + mh doctor + # 查看节点列表 mh -l @@ -479,6 +637,7 @@ Examples: Notes: - API 不通时,很多功能会失败:确保 mihomo 正在运行且 external-controller 为 ${API_HOST}:${API_PORT} - 如果刚启动就查询失败,wait_api 会稍等 mihomo API 就绪(避免 race) + - `mh` 是子进程,不能直接修改你当前 shell 的代理环境;需要 `eval "\$(mh env)"` 或写入 rc 文件 EOF } @@ -520,6 +679,7 @@ select_one(){ # select name echo "$name" > "${MIHOMO_ROOT}/current.profile" start_end "start" "$name" wait_api 5 || warn "mihomo API not ready yet" + sync_global_group || warn "failed to sync GLOBAL -> Proxy" speedtest_now 1000 } @@ -531,6 +691,97 @@ current_profile() { cat "${MIHOMO_ROOT}/current.profile" } +current_profile_cfg() { + local prof + prof="$(current_profile)" || return 1 + printf '%s/%s/config.yaml\n' "$MIHOMO_ROOT" "$prof" +} + +reset_basic_rules() { + local cfg="$1" + [[ -f "$cfg" ]] || return 1 + + local provider_name + provider_name="$(yq -r '."proxy-providers" | keys | .[0] // empty' "$cfg" 2>/dev/null)" + [[ -n "$provider_name" ]] || { warn "cannot detect proxy-provider name from $cfg"; return 1; } + + yq -y -i " + .mode = \"rule\" | + .[\"proxy-groups\"] = [ + {\"name\":\"Proxy\",\"type\":\"select\",\"use\":[\"${provider_name}\"]}, + {\"name\":\"Final\",\"type\":\"select\",\"proxies\":[\"Proxy\",\"DIRECT\"]} + ] | + .rules = [ + \"DOMAIN-SUFFIX,localhost,DIRECT\", + \"DOMAIN-SUFFIX,local,DIRECT\", + \"IP-CIDR,127.0.0.0/8,DIRECT,no-resolve\", + \"IP-CIDR,10.0.0.0/8,DIRECT,no-resolve\", + \"IP-CIDR,172.16.0.0/12,DIRECT,no-resolve\", + \"IP-CIDR,192.168.0.0/16,DIRECT,no-resolve\", + \"IP-CIDR,100.64.0.0/10,DIRECT,no-resolve\", + \"IP-CIDR,224.0.0.0/4,DIRECT,no-resolve\", + \"IP-CIDR6,::1/128,DIRECT,no-resolve\", + \"IP-CIDR6,fc00::/7,DIRECT,no-resolve\", + \"IP-CIDR6,fe80::/10,DIRECT,no-resolve\", + \"MATCH,Final\" + ] + " "$cfg" +} + +mode_set() { + local next_mode="${1:-}" + case "$next_mode" in + rule|global|direct) ;; + *) die "usage: $0 mode rule|global|direct" ;; + esac + + local prof cfg + prof="$(current_profile)" || die "no current profile" + cfg="${MIHOMO_ROOT}/${prof}/config.yaml" + [[ -f "$cfg" ]] || die "missing config: $cfg" + + if [[ "$next_mode" == "rule" ]] && ! yq -e '.rules | length > 0' "$cfg" >/dev/null 2>&1; then + info "No rules found in profile=${prof}, applying basic rules first..." + reset_basic_rules "$cfg" || die "failed to initialize basic rules" + fi + + yq -y -i ".mode = \"${next_mode}\"" "$cfg" + info "Mode set to ${next_mode} (profile=${prof}), restarting..." + start_end end + start_end start "$prof" + wait_api 5 || warn "mihomo API not ready" + if [[ "$next_mode" == "global" ]]; then + sync_global_group || warn "failed to sync GLOBAL -> Proxy" + fi + print_mihomo_status || true +} + +rules_cmd() { + local action="${1:-show}" + local cfg prof + prof="$(current_profile)" || die "no current profile" + cfg="${MIHOMO_ROOT}/${prof}/config.yaml" + [[ -f "$cfg" ]] || die "missing config: $cfg" + + case "$action" in + show) + echo "Rules (profile=${prof}):" + yq -r '.rules[]?' "$cfg" + ;; + reset) + reset_basic_rules "$cfg" || die "failed to reset basic rules" + info "Basic rules reset (profile=${prof}), restarting..." + start_end end + start_end start "$prof" + wait_api 5 || warn "mihomo API not ready" + print_mihomo_status || true + ;; + *) + die "usage: $0 rules show|reset" + ;; + esac +} + tun() { # 用法:sudo mh tun on|off|toggle local action="${1:-toggle}" @@ -550,8 +801,9 @@ tun() { toggle) [[ "$cur" == "true" ]] && next=false || next=true ;; *) warn "usage: mh tun on|off|toggle"; return 1 ;; esac - if [[ "$next" == "true" ]]; then # 只有“要开启 tun”才提示/要求 sudo - need_root "sudo -E \"$0\" tun ${action}" || return 1 + if [[ "$next" == "true" && "${EUID:-$(id -u)}" -ne 0 ]] && ! has_mihomo_cap_net_admin; then + warn "开启 TUN 通常需要 root,或提前给 mihomo 设置 cap_net_admin/cap_net_raw" + warn "继续尝试启动;若失败,请执行:sudo setcap cap_net_admin,cap_net_raw+ep \"$MIHOMO_BIN\"" fi yq -y -i " .tun.enable = ${next} | @@ -567,13 +819,19 @@ tun() { .dns.listen = (.dns.listen // \"127.0.0.1:1053\") | .dns[\"enhanced-mode\"] = \"redir-host\" | .dns.nameserver = (.dns.nameserver // [\"223.5.5.5\",\"119.29.29.29\"]) | - .dns.fallback = (.dns.fallback // [\"1.1.1.1\",\"8.8.8.8\"]) + .dns.fallback = (.dns.fallback // [\"1.1.1.1\",\"8.8.8.8\"]) | + .dns[\"fallback-filter\"].geoip = false " "$cfg" + if [[ "$next" != "true" ]]; then + disable_dns_fallback_geoip "$cfg" + fi + info "TUN set to ${next} (profile=${prof}), restarting..." start_end end start_end start "$prof" wait_api 5 || warn "mihomo API not ready" + sync_global_group || warn "failed to sync GLOBAL -> Proxy" print_mihomo_status 1200 || true } @@ -749,11 +1007,24 @@ tun_status() { proxy_env_status() { - if [[ -n "${HTTP_PROXY:-}" || -n "${HTTPS_PROXY:-}" ]]; then - echo "Proxy ENV: ON (HTTP_PROXY=${HTTP_PROXY:-} HTTPS_PROXY=${HTTPS_PROXY:-})" - else - echo "Proxy ENV: OFF" - fi + proxy_env_status_line "HTTP_PROXY" "${HTTP_PROXY:-}" + proxy_env_status_line "HTTPS_PROXY" "${HTTPS_PROXY:-}" + proxy_env_status_line "ALL_PROXY" "${ALL_PROXY:-}" +} + +show_proxy_exports() { + cat <" + [[ -n "$cfg" ]] && echo "config: ${cfg}" + echo + + echo "== capability ==" + if command -v getcap >/dev/null 2>&1; then + getcap "$MIHOMO_BIN" 2>/dev/null || true + else + echo "getcap not found" + fi + echo + + echo "== ports ==" + ss -lnt 2>/dev/null | grep -E ":(9090|7897|7898|1053)\\b" || echo "no mh ports listening" + echo + + echo "== runtime ==" + tun_status + show_runtime_routing_status + proxy_env_status + echo + + echo "== api ==" + api_get "/configs" 2>/dev/null | jq -r '{mode, "mixed-port": ."mixed-port", "socks-port": ."socks-port", "external-controller": ."external-controller"}' 2>/dev/null || echo "api unavailable" + echo + + echo "== route ==" + ip route show default 2>/dev/null || true + echo + + echo "== dns ==" + resolvectl status 2>/dev/null | sed -n '1,120p' || cat /etc/resolv.conf + echo + + echo "== log tail ==" + if [[ -n "$prof" && -f "${MIHOMO_ROOT}/${prof}/mihomo.log" ]]; then + tail -n 40 "${MIHOMO_ROOT}/${prof}/mihomo.log" + else + echo "no log file" + fi +} + proxy_on() { export http_proxy="http://127.0.0.1:${MIXED_PORT}" export https_proxy="http://127.0.0.1:${MIXED_PORT}" @@ -936,24 +1259,34 @@ proxy_off() { #=========================debug,上=============================== main() { - proxy_on - # unset http_proxy https_proxy all_proxy HTTP_PROXY # 停止一些用法,终止一些env if [[ -z "${1:-}" ]]; then tun_status + show_runtime_routing_status proxy_env_status - speedtest_now 1000 + speedtest_now 1000 || true # print_mihomo_status exit 0 fi case "${1:-}" in add) cfg_add "$@";; - start|end) + start) + [[ -n "${2:-}" ]] || die "usage: $0 start " + select_one "select" "${2}";; + end) start_end "$@";; select) select_one "$@";; db) debug_one "$@";; + doctor) + doctor ;; + env) + show_proxy_exports ;; + mode) + shift; mode_set "${1:-}" ;; + rules) + shift; rules_cmd "${1:-show}" ;; help|-h|--help) help ;; -l|--list|list) list_nodes ;; tun) shift; tun "${1:-toggle}" ;; diff --git a/test_mh.sh b/test_mh.sh new file mode 100755 index 0000000..c10c2a5 --- /dev/null +++ b/test_mh.sh @@ -0,0 +1,197 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +MH_BIN="${ROOT_DIR}/mh" +PROFILE="${1:-tnt}" +TIMEOUT="${TIMEOUT:-10}" +API_PORT="${API_PORT:-9090}" +MIXED_PORT="${MIXED_PORT:-7897}" +SOCKS_PORT="${SOCKS_PORT:-7898}" +OLD_PROXY_PORT="${OLD_PROXY_PORT:-}" +RUN_PID_FILE="${ROOT_DIR}/run.pid" +LOG_FILE="${ROOT_DIR}/${PROFILE}/mihomo.log" + +PASS_COUNT=0 +FAIL_COUNT=0 + +log() { + printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*" +} + +pass() { + PASS_COUNT=$((PASS_COUNT + 1)) + log "PASS $*" +} + +fail() { + FAIL_COUNT=$((FAIL_COUNT + 1)) + log "FAIL $*" +} + +run_check() { + local label="$1" + shift + if "$@"; then + pass "$label" + else + fail "$label" + fi +} + +print_section() { + printf '\n== %s ==\n' "$1" +} + +cleanup_env() { + unset http_proxy https_proxy all_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY no_proxy || true +} + +port_listen_check() { + local port="$1" + ss -lnt 2>/dev/null | awk '{print $4}' | grep -qE "(:|\\.)${port}\$" +} + +curl_proxy_head() { + local url="$1" + curl -I -m "$TIMEOUT" -x "http://127.0.0.1:${MIXED_PORT}" "$url" >/dev/null 2>&1 +} + +curl_env_head() { + local url="$1" + curl -I -m "$TIMEOUT" "$url" >/dev/null 2>&1 +} + +print_recent_log() { + if [[ -f "$LOG_FILE" ]]; then + print_section "Recent mihomo.log" + tail -n 40 "$LOG_FILE" || true + else + print_section "Recent mihomo.log" + echo "missing log file: $LOG_FILE" + fi +} + +print_context() { + print_section "Context" + echo "root_dir=${ROOT_DIR}" + echo "profile=${PROFILE}" + echo "api_port=${API_PORT}" + echo "mixed_port=${MIXED_PORT}" + echo "socks_port=${SOCKS_PORT}" + echo "old_proxy_port=${OLD_PROXY_PORT}" + echo "timeout=${TIMEOUT}" +} + +print_env_snapshot() { + print_section "Proxy Env Before Test" + env | grep -iE 'http_proxy|https_proxy|all_proxy|no_proxy' || true +} + +print_port_snapshot() { + print_section "Port Snapshot" + ss -lnt | grep -E ":(7897|7898|9090)\\b" || true +} + +print_pid_snapshot() { + print_section "PID Snapshot" + if [[ -f "$RUN_PID_FILE" ]]; then + local pid + pid="$(cat "$RUN_PID_FILE" 2>/dev/null || true)" + echo "run.pid=${pid:-}" + if [[ -n "${pid:-}" ]]; then + ps -fp "$pid" -o pid,ppid,cmd || true + fi + else + echo "run.pid missing" + fi +} + +api_json() { + local path="$1" + curl -fsS --noproxy '*' "http://127.0.0.1:${API_PORT}${path}" +} + +api_selector_now() { + local selector="$1" + api_json "/proxies" | jq -r --arg selector "$selector" '.proxies[$selector].now // empty' +} + +selector_equals() { + local selector="$1" + local expected="$2" + [[ "$(api_selector_now "$selector")" == "$expected" ]] +} + +selector_non_empty() { + local selector="$1" + [[ -n "$(api_selector_now "$selector")" ]] +} + +main() { + [[ -x "$MH_BIN" ]] || { echo "missing executable: $MH_BIN"; exit 1; } + + print_context + print_env_snapshot + print_port_snapshot + + if [[ -n "$OLD_PROXY_PORT" ]] && port_listen_check "$OLD_PROXY_PORT"; then + echo + echo "旧代理端口 ${OLD_PROXY_PORT} 仍在监听。先手动关闭它,再重新运行此脚本。" + exit 2 + fi + + cleanup_env + + print_section "Start Profile" + "$MH_BIN" end >/dev/null 2>&1 || true + "$MH_BIN" start "$PROFILE" + + run_check "API port ${API_PORT} is listening" port_listen_check "$API_PORT" + run_check "mixed port ${MIXED_PORT} is listening" port_listen_check "$MIXED_PORT" + run_check "socks port ${SOCKS_PORT} is listening" port_listen_check "$SOCKS_PORT" + run_check "run.pid exists" test -f "$RUN_PID_FILE" + + if [[ -f "$RUN_PID_FILE" ]]; then + pid="$(cat "$RUN_PID_FILE" 2>/dev/null || true)" + run_check "run.pid process is alive" test -n "${pid:-}" + if [[ -n "${pid:-}" ]]; then + run_check "PID ${pid} exists" kill -0 "$pid" + fi + fi + + print_pid_snapshot + + print_section "API Checks" + run_check "GET /configs" api_json "/configs" + run_check "GET /proxies" api_json "/proxies" + run_check "GLOBAL selector points to Proxy" selector_equals "GLOBAL" "Proxy" + run_check "Proxy selector has current node" selector_non_empty "Proxy" + + print_section "Shell Proxy Checks" + eval "$("$MH_BIN" env)" + run_check "HTTP_PROXY points to ${MIXED_PORT}" bash -lc '[[ "${HTTP_PROXY:-}" == "http://127.0.0.1:'"${MIXED_PORT}"'" ]]' + run_check "HTTPS_PROXY points to ${MIXED_PORT}" bash -lc '[[ "${HTTPS_PROXY:-}" == "http://127.0.0.1:'"${MIXED_PORT}"'" ]]' + + print_section "Network Through Proxy" + run_check "proxy -> baidu" curl_proxy_head "https://www.baidu.com" + run_check "proxy -> github" curl_proxy_head "https://www.github.com" + run_check "proxy -> google" curl_proxy_head "https://www.google.com" + + print_section "Network Through Current Shell" + run_check "env -> baidu" curl_env_head "https://www.baidu.com" + run_check "env -> github" curl_env_head "https://www.github.com" + run_check "env -> google" curl_env_head "https://www.google.com" + + print_recent_log + + print_section "Summary" + echo "PASS=${PASS_COUNT}" + echo "FAIL=${FAIL_COUNT}" + + if (( FAIL_COUNT > 0 )); then + exit 1 + fi +} + +main "$@"