用户报告(2026-08-06)
点击 Enable 后,DNS mode 应该已经授权且启动了,但 UI 卡在 "Enabling...";切换页面后再回来,又变成了没启动;再点又卡第一个截图的样子。后台跑起来的 mhost-dns 我只能通过 kill -9 来杀掉。
pnpm tauri dev 验证:卡 "Enabling..." 时 ps aux | grep mhost-dns-proxy 显示 进程已启动,但 UI 永远显示 loading,再切回 Settings 显示 "Stopped"。
根因(3 个互相强化)
Bug #1 — AppleScript 脚本里 & + disown 让 proxy 跟 osascript 解耦
platform.rs::enable_dns_mode line 523-550 的 sh 脚本以 "{proxy}" --listen 53 --target ... & 启动 proxy(后台进程),然后用 do shell script ... with administrator privileges 跑这个脚本。
当用户在 osascript sudo 弹窗里点 Cancel:
networksetup -setdnsservers 那行没执行
- AppleScript 整体退出非零 →
enable_dns_mode 走 line 559 rollback(只清理 signal + original 文件)
- proxy 因为
& + disown 已经脱离 sh 进程组,sh 退出不影响它 → proxy 进入「无主」状态:没 signal 文件(被 rollback 删了)、没 SIGTERM 路径(它是 root,user态 SIGTERM 被 macOS 静默 EACCES 丢弃)
Bug #2 — kill_orphan_dns_proxies() 用裸 SIGTERM,无法杀掉 root proxy
platform.rs::enable_dns_mode line 503 的孤儿清理用了 libc::kill(pid, SIGTERM),但 proxy 是 with administrator privileges 起 root 进程,user 态发 SIGTERM 在 macOS 默认被 EACCES 丢弃。pgrep 能找到旧 proxy,kill 杀不掉。
Bug #3 — enable_dns_mode 没在 proxy 启动后注册退出陷阱(trap)
脚本没有 trap cleanup EXIT/INT/TERM。proxy 启动后脚本执行任何一步失败(proxy bind 冲突、networksetup 被拒、其他),proxy 都不会被自动 kill——& + disown 让它脱离脚本控制。
链式失败
- 用户在 osascript 弹窗点 Cancel(典型场景:密码输错、不想授权)
- AppleScript 退出非零,
enable_dns_mode 走 rollback,proxy 仍然在跑(孤儿)
- IPC Err 回到前端 → catch 跑 → truth-sync
getDnsMode 返回 false(state.dns_enabled 没设上)→ UI 显示 "Stopped" 但 proxy 实际占着 53 + 系统 DNS 已被改
- 切到其他页面再切回 Settings:
App.tsx 的 fetchDnsModeAtom 只在 App mount 时跑一次,useEffect deps 不变就不重跑 → dnsEnabled 保持 false → UI 一直 "Stopped"
- 用户再点 Enable →
set_dns_mode_enable → kill_orphan_dns_proxies pgrep 找到孤儿 → SIGTERM 失败(权限) → 新 proxy bind 53 撞 Address already in use → 5s ready timeout → exit 1 → IPC 永远 await 在 osascript → UI 永远 "Enabling..."
死循环:orphan proxy 占着 53 → 新 enable 永远失败 → 用户只能 kill -9
子 issue(子任务跟踪)
不在 scope
Acceptance criteria(每个 sub-issue 自带)
#148 合入后:
- dev 模式 enable → 在 osascript 弹窗点 Cancel →
ps aux | grep mhost-dns-proxy 无输出
- dev 模式 enable → 故意让 networksetup 失败 → proxy 必须被 kill
- dev 模式连续 enable → 第二次 enable 不应撞 53 端口冲突
#149 合入后:
- dev 模式 enable → 在弹窗出现后点 "Cancel" 按钮 → UI 立即恢复 stopped + proxy 被 kill
- dev 模式 enable → 故意让 server.start() 阻塞 60s → 点 Cancel → IPC 立即返回 + UI 恢复 + server.stop() 跑过
Repro 现场状态(用户已验证 2026-08-06)
pnpm tauri dev 启动
- 进 Settings → 点 Enable
- 卡 "Enabling..." 时
ps aux | grep mhost-dns-proxy → 进程在跑
- 切 ProfileView 再切回 Settings → 显示 "Stopped"
- 再点 Enable → 卡死循环
相关 commit / PR
用户报告(2026-08-06)
根因(3 个互相强化)
Bug #1 — AppleScript 脚本里
&+disown让 proxy 跟 osascript 解耦platform.rs::enable_dns_modeline 523-550 的 sh 脚本以"{proxy}" --listen 53 --target ... &启动 proxy(后台进程),然后用do shell script ... with administrator privileges跑这个脚本。当用户在 osascript sudo 弹窗里点 Cancel:
networksetup -setdnsservers那行没执行enable_dns_mode走 line 559 rollback(只清理 signal + original 文件)&+disown已经脱离 sh 进程组,sh 退出不影响它 → proxy 进入「无主」状态:没 signal 文件(被 rollback 删了)、没 SIGTERM 路径(它是 root,user态 SIGTERM 被 macOS 静默 EACCES 丢弃)Bug #2 —
kill_orphan_dns_proxies()用裸 SIGTERM,无法杀掉 root proxyplatform.rs::enable_dns_modeline 503 的孤儿清理用了libc::kill(pid, SIGTERM),但 proxy 是with administrator privileges起 root 进程,user 态发 SIGTERM 在 macOS 默认被 EACCES 丢弃。pgrep 能找到旧 proxy,kill 杀不掉。Bug #3 —
enable_dns_mode没在 proxy 启动后注册退出陷阱(trap)脚本没有
trap cleanup EXIT/INT/TERM。proxy 启动后脚本执行任何一步失败(proxy bind 冲突、networksetup 被拒、其他),proxy 都不会被自动 kill——&+disown让它脱离脚本控制。链式失败
enable_dns_mode走 rollback,proxy 仍然在跑(孤儿)getDnsMode返回 false(state.dns_enabled没设上)→ UI 显示 "Stopped" 但 proxy 实际占着 53 + 系统 DNS 已被改App.tsx的fetchDnsModeAtom只在 App mount 时跑一次,useEffectdeps 不变就不重跑 → dnsEnabled 保持 false → UI 一直 "Stopped"set_dns_mode_enable→kill_orphan_dns_proxiespgrep 找到孤儿 → SIGTERM 失败(权限) → 新 proxy bind 53 撞Address already in use→ 5s ready timeout → exit 1 → IPC 永远 await 在 osascript → UI 永远 "Enabling..."死循环:orphan proxy 占着 53 → 新 enable 永远失败 → 用户只能
kill -9子 issue(子任务跟踪)
platform.rs::enable_dns_modeline 503: 把kill_orphan_dns_proxies改为run_with_privileges提权kill <pid>(必须 sudo 才能杀 root proxy)trap cleanup EXIT INT TERM+cleanup函数里 killproxy_pidsetDnsModeinvoke wrapper 加AbortSignal参数toggleDnsModeAtom改造支持 cancellation + 自动 cleanup 状态set_dns_mode接受 abort token,tokio::select!包裹 enable 关键路径,cancel 时走 disable rollback不在 scope
isDnsLoadingAtom状态在 catch 后的兜底(已在 PR fix: ship mhost-dns-proxy sidecar in .app + kill orphan proxy on enable + robust Ctrl+C exit #146 commitc88423f合入,这条保留作为兜底防御)Acceptance criteria(每个 sub-issue 自带)
#148 合入后:
ps aux | grep mhost-dns-proxy无输出#149 合入后:
Repro 现场状态(用户已验证 2026-08-06)
pnpm tauri dev启动ps aux | grep mhost-dns-proxy→ 进程在跑相关 commit / PR
a2fe6e4master) — 修了"前端 catch truth-sync"兜底,但没修这层c88423f— frontend truth-sync(已合 master)5c12428— kill_orphan_dns_proxies 加到 enable 路径(此 bug 的部分缓解,但不彻底——SIGTERM 杀不掉 root)