bgjob

package
v0.14.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 14, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package bgjob 远程后台作业 marker 协议原语(tools#106):经宿主**既有** SSH 会话在远端起「脱离会话的长任务」,用退出码 marker 文件做探测与收尾。

协议(四条实弹坑全部协议化,jumpserver-exec 背书)

  1. 退出码走 marker 文件(输出路径 + ".rc." + 任务身份):输出文件早就 在场,光看它存在与否判断不了「还在跑」——marker 才是终态的唯一凭证。 Status 据此区分 running(输出在场、marker 未落)/ finished(marker 已落,含退出码)/ removed(双缺,已收尾)。
  2. 命令在内层 shell 跑、marker 由**外层**写:把 `; echo $? > marker` 拼进 用户命令,任务里任何 `exit` 都会让 shell 先死、marker 永远不写,任务 看起来永远 RUNNING。Start 的启动命令是双层结构——内层 shell 只跑用户 命令,外层 wrapper 在内层死后照常把 `$?` 落进 marker。
  3. marker 路径单源拼接:marker = 输出路径 + ".rc." + 本次 Start 的任务 身份(markerPathOf 在 Go 侧一次算定,Job.MarkerPath),启动/探测/清理 命令对它整段单引号包裹(shellQuote 单源)——「.rc 拼进引号内变成 p.rc.rc、探测永远 RUNNING」的 P0 活虫在协议层不可能复发;路径含空格/ 单引号逐字抵达远端(回归测试钉住命令原文与真 shell 行为)。身份进 文件名是同路径复用下「在飞旧任务写回」的防线(tools#161):旧 wrapper 的 `echo $?` 落旧路径,新任务的探测/清理只认自己的路径——启动链的 rm -f 只清 Start 时刻已落盘的残留,挡不住 Start 之后才执行的迟到写回。
  4. 清理自证: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 里逐字执行(含管道/重定向/引号——那是调用方的命令本体)。

func (*Job) Cleanup

func (j *Job) Cleanup() (removed bool, err error)

Cleanup 就地收尾:RUNNING 拒绝(任务还在写输出,且文件原样——契约 4 的 验收项);FINISHED 后 rm -f 输出与 marker,删完 [ -e ] 自证 REMOVED 才报 (true, nil);REMOVED 幂等((false, nil))。自证不过(rm 后文件仍在)如实 报错——「报已清理」必须是事实。

Status→rm 之间存在固有的窄竞态(探测后任务恰好结束):拒绝保护的是 「RUNNING 强删」的常态事故,终态边界上的一次性收尾不在此防线内。

func (*Job) Status

func (j *Job) Status() (state State, exitCode string, err error)

Status 探测任务状态:marker 在场 = finished(+ 退出码);否则输出文件在场 = running;双缺 = removed。探测失败(连接错/退出码非零/输出不可识别)如实 上抛——状态判不了就是判不了,不猜。

退出码仅在 finished 时非空(marker 内容首字段,`echo $?` 的十进制形态; 信号杀死为 128+N)。

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)。输出重定向由调用方给——本原语不做 日志轮转、不替调用方选路径(提案边界声明)。

type State

type State string

State 任务状态(探测结论,marker 协议的三态)。

const (
	// StateRunning 任务在跑:marker 未落、输出文件在场(输出文件由 Start
	// 同步建出,任务未起不会误报 running)。
	StateRunning State = "running"
	// StateFinished 任务已结束:marker 已落,退出码见 Status 的第二返回值。
	StateFinished State = "finished"
	// StateRemoved 已收尾:输出与 marker 均不在(Cleanup 之后的稳态)。
	StateRemoved State = "removed"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL