Files
panel/pkg/ntp
耗子andClaude Opus 5 2fc68ed98f fix: 修复 ctx 打通改造引入的回归与遗留缺陷
对上一轮改造做了一次 max-effort 代码审查(9 个角度约 70 条候选),
本次修掉其中确有实际影响的部分。

两个改造引入的回归,均有最小复现实测:

ExecfWithTimeout 从硬超时退化成软超时。旧实现 time.After 到期即 Kill 并
立刻返回,新实现的 cmd.Run() 要等 stdout/stderr 拷贝 goroutine 结束,而
孙子进程持有管道写端时永不返回。实测 1s 超时的命令实际 30.0s 才返回,
pkg/systemctl 的全部包装都继承了这个缺陷。改为设置 WaitDelay 并按进程组
kill,同一用例现在 1.00s 返回,孙子进程不再残留。

systemctl.Status 把 ctx 取消吞成「服务未运行」且不报错。原注释「is-active
在服务未启用时会返回 3」在加 ctx 之前成立,之后不再成立。这一处制造了三类
事故:webserver.ReloadIfRunning 据此静默返回 nil(证书和站点配置写盘后
nginx 从未 reload 却报成功)、告警采集把所有服务判定为 DOWN、改面板端口时
防火墙放行被跳过而后续的保存和重启照常执行,把管理员锁在面板外。

其余修复按性质分三类:

断链位置错误。replaceFirewallRule 只保护了回滚,取消落在「删旧规则之后、
reload 之前」会让端口新旧规则都消失;备份上传的 WithoutCancel 恰好关掉了
s3sdk 里唯一正确的取消清理,且对不看 ctx 的 Local/SFTP 是空操作,改为传
.partial 再 rename;UpdatePanel 的断链罩住了本该可取消的下载与备份。

保护遗漏。FileService.Move 失败时跳过 relock,把防篡改条目永久摘出登记表;
restoreWebsite 的替换阶段被取消会同时毁掉站点和暂存的还原内容;面板每日
备份直接写最终文件名,截断包会被 FixPanel 按 mtime 选中去覆盖运行中的面板;
websiteRepo.Create 失败无回滚,留下面板看不见的活站点。

停机与长连接。taskqueue 在面板停机时把任务标记为 Failed 并跳过清理命令,
与用户手动取消的语义不一致;关停顺序让 HTTP 比调度器多活 30 秒;WS 连接
hijack 后 r.Context() 不随客户端断开取消,长连接靠心跳回收,Exec 路径补上
管道关闭以免每次会话泄漏 goroutine 与僵尸进程。

顺带修掉两个与 ctx 无关的既有缺陷:证书自动续签从不执行用户的部署脚本;
nginx 与 apache 的 reload 失败路径用配置检查的返回值覆盖了原始错误,检查
恰好通过时错误信息变成 <nil>。

判定为误报未改:Copy 不调用 Unlock 所以没有需要恢复的状态;firewalld.Status
只是转调且三层都传播错误;UpdateSystemTime 的 date -s 是原子命令没有半状态;
finishUpdate 已被上游断链覆盖。

已知无法修复:WebDAV 后端不响应 ctx,gowebdav v0.13.0 没有任何接收 ctx 的
API,且其 WriteStream 会探测 io.Seeker,包一层 Reader 会导致整个备份被读进
内存计算 Content-Length,改为给 transport 装分阶段超时。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 04:11:02 +08:00
..