Skip to content

fix: DNS mode enable leaves orphan mhost-dns-proxy when osascript is cancelled — UI/backend state desync (#148) #147

Description

@flyhigher139

用户报告(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 #2kill_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 #3enable_dns_mode 没在 proxy 启动后注册退出陷阱(trap)

脚本没有 trap cleanup EXIT/INT/TERM。proxy 启动后脚本执行任何一步失败(proxy bind 冲突、networksetup 被拒、其他),proxy 都不会被自动 kill——& + disown 让它脱离脚本控制。

链式失败

  1. 用户在 osascript 弹窗点 Cancel(典型场景:密码输错、不想授权)
  2. AppleScript 退出非零,enable_dns_mode 走 rollback,proxy 仍然在跑(孤儿)
  3. IPC Err 回到前端 → catch 跑 → truth-sync getDnsMode 返回 false(state.dns_enabled 没设上)→ UI 显示 "Stopped" 但 proxy 实际占着 53 + 系统 DNS 已被改
  4. 切到其他页面再切回 Settings:App.tsxfetchDnsModeAtom 只在 App mount 时跑一次,useEffect deps 不变就不重跑 → dnsEnabled 保持 false → UI 一直 "Stopped"
  5. 用户再点 Enable → set_dns_mode_enablekill_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 合入后:

  1. dev 模式 enable → 在 osascript 弹窗点 Cancel → ps aux | grep mhost-dns-proxy 无输出
  2. dev 模式 enable → 故意让 networksetup 失败 → proxy 必须被 kill
  3. dev 模式连续 enable → 第二次 enable 不应撞 53 端口冲突

#149 合入后:

  1. dev 模式 enable → 在弹窗出现后点 "Cancel" 按钮 → UI 立即恢复 stopped + proxy 被 kill
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdns-modeDNS mode (本地 DNS server) 相关问题

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions