Improve mh routing, diagnostics, and MCP setup
This commit is contained in:
parent
6feea6391e
commit
afd10467e5
3 changed files with 590 additions and 36 deletions
38
README.md
38
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专用
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
391
mh
391
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 <name> <url|file>"
|
||||
[[ -n "$src" ]] || die "usage: $0 add <name> <url|file>"
|
||||
|
|
@ -356,7 +475,7 @@ get_group_nodes_json() {
|
|||
|
||||
help() {
|
||||
cat <<EOF
|
||||
✅ 复制粘贴到你的 shell 启动文件(见下方“该改哪个文件”):
|
||||
✅ 复制粘贴到你的 shell 启动文件(见下方“该改哪个文件”),或直接执行 mh env:
|
||||
|
||||
# ====== MIHOMO PROXY ENV BEGIN ======
|
||||
export http_proxy="http://127.0.0.1:${MIXED_PORT}"
|
||||
|
|
@ -427,9 +546,33 @@ Commands:
|
|||
end
|
||||
停止当前 mihomo(通过 ${PID_FILE} 记录的 pid)
|
||||
|
||||
env
|
||||
输出代理环境变量 export 片段
|
||||
用法示例:
|
||||
eval "\$(mh env)"
|
||||
|
||||
mode [rule|global|direct]
|
||||
切换当前 profile 的运行模式,并重启
|
||||
- rule 使用内建基础规则
|
||||
- global 全部走代理
|
||||
- direct 全部直连
|
||||
|
||||
rules [show|reset]
|
||||
查看或重置当前 profile 的基础规则
|
||||
- show 显示当前规则
|
||||
- reset 写回一套最简规则,并切回 rule 模式
|
||||
|
||||
doctor
|
||||
输出当前 mh/mihomo 的诊断信息
|
||||
- capability
|
||||
- 端口监听
|
||||
- TUN/模式/代理环境
|
||||
- API / 路由 / DNS
|
||||
- 最近日志
|
||||
|
||||
tun [on|off|toggle]
|
||||
开关 TUN(会写入当前 profile 的 config.yaml 并重启)
|
||||
- 开启 TUN 通常需要 sudo/root 或给 mihomo 加能力:
|
||||
- 开启 TUN 通常需要 sudo/root,或给 mihomo 加能力:
|
||||
sudo setcap cap_net_admin,cap_net_raw+ep "<mihomo_bin>"
|
||||
|
||||
-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:-<unset>} HTTPS_PROXY=${HTTPS_PROXY:-<unset>})"
|
||||
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 <<EOF
|
||||
export http_proxy="http://127.0.0.1:${MIXED_PORT}"
|
||||
export https_proxy="http://127.0.0.1:${MIXED_PORT}"
|
||||
export all_proxy="socks5h://127.0.0.1:${MIXED_PORT}"
|
||||
|
||||
export HTTP_PROXY="\$http_proxy"
|
||||
export HTTPS_PROXY="\$https_proxy"
|
||||
export ALL_PROXY="\$all_proxy"
|
||||
|
||||
export NO_PROXY="127.0.0.1,localhost,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
|
||||
export no_proxy="\$NO_PROXY"
|
||||
EOF
|
||||
}
|
||||
|
||||
net_test() {
|
||||
|
|
@ -819,7 +1090,7 @@ debug_one() {
|
|||
shift
|
||||
|
||||
local -a debug_code=()
|
||||
debug_code[1]="ss -lntp | grep -E ':(9090|9091|7890|7891)'|| echo \"❌ 未找到代理服务\""
|
||||
debug_code[1]="ss -lntp | grep -E ':(9090|9091|7897|7898|7890|7891)'|| echo \"❌ 未找到代理服务\""
|
||||
debug_code[2]="ps -fp \${1:-} -o pid,ppid,cmd"
|
||||
debug_code[3]="net_test"
|
||||
debug_code[4]="net_test_double"
|
||||
|
|
@ -828,7 +1099,7 @@ debug_one() {
|
|||
debug_code[7]="resolvectl status 2>/dev/null | sed -n '1,120p' || cat /etc/resolv.conf"
|
||||
|
||||
local -a debug_desc=()
|
||||
debug_desc[1]="检查 9090/9091/7890 端口监听:"
|
||||
debug_desc[1]="检查 9090/9091/7897/7898 端口监听:"
|
||||
debug_desc[2]="查看指定 PID 的进程信息:"
|
||||
debug_desc[3]="测试一下网络(仅proxy)"
|
||||
debug_desc[4]="测试一下网络(直连+proxy)"
|
||||
|
|
@ -856,6 +1127,58 @@ debug_one() {
|
|||
return 1
|
||||
}
|
||||
|
||||
doctor() {
|
||||
local prof cfg
|
||||
prof="$(current_profile 2>/dev/null || true)"
|
||||
cfg=""
|
||||
[[ -n "$prof" ]] && cfg="${MIHOMO_ROOT}/${prof}/config.yaml"
|
||||
|
||||
echo "== mh doctor =="
|
||||
echo "root: ${MIHOMO_ROOT}"
|
||||
echo "bin: ${MIHOMO_BIN}"
|
||||
echo "pid: ${PID_FILE}"
|
||||
[[ -n "$prof" ]] && echo "profile: ${prof}" || echo "profile: <none>"
|
||||
[[ -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 <name>"
|
||||
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}" ;;
|
||||
|
|
|
|||
197
test_mh.sh
Executable file
197
test_mh.sh
Executable file
|
|
@ -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:-<empty>}"
|
||||
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 "$@"
|
||||
Loading…
Add table
Add a link
Reference in a new issue