Documentation
¶
Overview ¶
Package bgjob 远程后台作业 marker 协议原语(tools#106):经宿主**既有** SSH 会话在远端起「脱离会话的长任务」,用退出码 marker 文件做探测与收尾。
协议(四条实弹坑全部协议化,jumpserver-exec 背书) ¶
- 退出码走 marker 文件(输出路径 + ".rc." + 任务身份):输出文件早就 在场,光看它存在与否判断不了「还在跑」——marker 才是终态的唯一凭证。 Status 据此区分 running(输出在场、marker 未落)/ finished(marker 已落,含退出码)/ removed(双缺,已收尾)。
- 命令在内层 shell 跑、marker 由**外层**写:把 `; echo $? > marker` 拼进 用户命令,任务里任何 `exit` 都会让 shell 先死、marker 永远不写,任务 看起来永远 RUNNING。Start 的启动命令是双层结构——内层 shell 只跑用户 命令,外层 wrapper 在内层死后照常把 `$?` 落进 marker。
- marker 路径单源拼接:marker = 输出路径 + ".rc." + 本次 Start 的任务 身份(markerPathOf 在 Go 侧一次算定,Job.MarkerPath),启动/探测/清理 命令对它整段单引号包裹(shellQuote 单源)——「.rc 拼进引号内变成 p.rc.rc、探测永远 RUNNING」的 P0 活虫在协议层不可能复发;路径含空格/ 单引号逐字抵达远端(回归测试钉住命令原文与真 shell 行为)。身份进 文件名是同路径复用下「在飞旧任务写回」的防线(tools#161):旧 wrapper 的 `echo $?` 落旧路径,新任务的探测/清理只认自己的路径——启动链的 rm -f 只清 Start 时刻已落盘的残留,挡不住 Start 之后才执行的迟到写回。
- 清理自证:Cleanup 先探测——RUNNING 拒绝(任务还在写输出,且文件 原样);FINISHED 后 rm -f 输出与 marker,删完 [ -e ] **自证** REMOVED 才报「已清理」;REMOVED 幂等(重复收尾无物可清,不报错)。
边界(提案正文声明) ¶
- 不做进程树管理(本地子进程是 procx 的领域);不做日志轮转——输出 重定向目标由调用方经 WithOutputPath 给定(必填、远端绝对路径);
- 启动/探测/清理复用宿主既有会话(Session 小接口:Exec(string) (stdout, code, err)),本包不做连接管理、不做重试——连接层的断连 与重连是宿主会话的职责,探测/清理失败如实上抛;
- 远端契约:POSIX sh + setsid + nohup(Linux 堡垒机/生产主机缺省满足)。
以上契约(marker 判据、外层写、路径单源、REMOVED 自证)是本原语的 **验收项**而非实现细节,回归测试逐条钉死。
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Job ¶
type Job struct {
// OutputPath 输出文件(远端绝对路径,调用方经 WithOutputPath 给定):
// 任务的 stdout/stderr 落此文件。
OutputPath string
// MarkerPath 退出码 marker = OutputPath + ".rc." + 任务身份(Go 侧单源
// 一次算定,契约见 doc.go 第 3 条)。身份每次 Start 唯一(tools#161):
// 同路径复用时上一轮**在飞**任务的 wrapper 在新 Start 之后才写回,固定
// 路径下旧退出码会落成新任务的终态凭证——身份分开后旧写回落旧路径,
// 本任务的探测/清理只认自己的路径。
MarkerPath string
// contains filtered or unexported fields
}
Job 一个远程后台作业(Start 构造;零值不可用)。
func Start ¶
func Start(sess Session, cmd string, opts ...StartOption) (*Job, error)
Start 经既有会话在远端起脱离任务(setsid + nohup + 内层 shell + 外层 marker),命令立即返回——等待/轮询是调用方对 Status 的事。
结构即协议(doc.go 契约 2):
umask 077 && mkdir -p 'DIR' && : > 'OUT' && { setsid nohup sh -c 'INNER' > 'OUT' 2>&1 </dev/null & }
INNER = sh -c 'USERCMD'; echo $? > 'MARKER'
- setsid + nohup + </dev/null:新会话、抗 HUP、stdin 摘钩——SSH 会话关闭
不带走任务(脱离会话的判据,宿主断连不影响任务存活);
- 用户命令在内层 shell(sh -c 'USERCMD')里跑,marker 由**外层** wrapper
写——内层任何 exit 只死自己,$? 照常落 marker(裸 exit 不会永远 RUNNING);
- `: > 'OUT'` 同步建输出文件:Start 返回即在场——探测的 REMOVED 判据
(输出+marker 双缺)不会在任务尚未起来时误报;
- mkdir/建文件与 spawn 同链但 spawn 花括号后台化:前置步骤失败时 Exec
退出码非零,Start 如实报错(若整链后台化,失败会被吞成启动成功);
- umask 077:输出与 marker 0600 起(共享主机上的路径卫生)。
用户命令与路径经 shellQuote 单源包裹后拼进命令串,**不做**任何转义改写: cmd 在远端 shell 里逐字执行(含管道/重定向/引号——那是调用方的命令本体)。
type Session ¶
type Session interface {
// Exec 在远端 shell 执行 command,返回 stdout 与退出码。stderr 由宿主
// 自行处置——本原语的启动/探测/清理输出全部走 stdout。
Exec(command string) (stdout string, code int, err error)
}
Session 宿主既有 SSH 会话的最小收窄(tools#106):bgjob 只需要「跑一条 命令、拿 stdout 与退出码」。宿主各自适配(jumpserver-exec 的 session、 golang.org/x/crypto/ssh 的 Session 等各自包一层);本包不做连接管理。
type StartOption ¶
type StartOption func(*startConfig)
StartOption 启动选项(XxxOption 命名纪律,AGENTS.md)。
func WithOutputPath ¶
func WithOutputPath(path string) StartOption
WithOutputPath 指定远端输出文件路径(**必填**、远端绝对路径):任务的 stdout/stderr 落此文件,退出码 marker 为其 + ".rc." + 本次任务身份 (markerPathOf 单源,tools#161)。输出重定向由调用方给——本原语不做 日志轮转、不替调用方选路径(提案边界声明)。