subproc

package
v1.2.9 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package subproc is the one place a child process gets its bound.

A short-lived child (git, gh, ssh, sops, tailscale, go tooling, ps) is started through Command: it runs under a context whose deadline is the caller's own when the caller has one sooner, and otherwise the named default of its kind (Kind.Budget), and it carries WaitDelay so that a killed child cannot hang its caller on a pipe. Without WaitDelay the kill is not enough: Output and CombinedOutput read until the pipe closes, and a grandchild (an ssh under git, a credential helper, a pager) that inherited the pipe holds it open after the child is gone.

A long-lived child (a harness run, a member's native child, a server) is started through Long: it runs under the caller's cancellable context and has no deadline, because how long it runs is not this package's call. WaitDelay is set there too, so the wait after its cancellation or exit is bounded, never the run itself.

Index

Constants

View Source
const (
	GitBudget = 60 * time.Second
	// GitLongBudget is a git that moves a whole repository or tree: clone, fetch, pull,
	// push, ls-remote (any network git), and a status, add, checkout, reset or clean of a
	// large worktree.
	GitLongBudget = 300 * time.Second
	GHBudget      = 120 * time.Second
	SSHBudget     = 300 * time.Second
	GoBudget      = 300 * time.Second
	ToolBudget    = 60 * time.Second
)

Budgets by kind. A caller with its own context deadline sooner than the kind's budget keeps it; a caller that needs longer passes a context with no deadline to Long, or sets the option its own runner exposes.

View Source
const WaitDelay = 5 * time.Second

WaitDelay is how long a killed or exited child has to close its pipes before Wait closes them and returns.

Variables

This section is empty.

Functions

func BoundFor

func BoundFor(ctx context.Context, budget time.Duration) (context.Context, context.CancelFunc)

BoundFor narrows ctx to budget. A context that already ends sooner is kept as it is. A nil ctx is context.Background. The cancel function is always non-nil and is called when the child has been waited for.

func BudgetOf

func BudgetOf(name string, args []string) time.Duration

BudgetOf is the default budget of a program run by name with these arguments: its kind's, and for git the command line's (GitBudgetFor).

func Command

func Command(ctx context.Context, k Kind, name string, args ...string) (*exec.Cmd, context.CancelFunc)

Command is exec.CommandContext under Bound(ctx, k) with WaitDelay set. The returned cancel is called when the child has been waited for (defer it).

func CommandFor

func CommandFor(ctx context.Context, budget time.Duration, name string, args ...string) (*exec.Cmd, context.CancelFunc)

CommandFor is Command with an explicit budget.

func Context

func Context(ctx context.Context, name string, args ...string) *exec.Cmd

Context is exec.CommandContext under the caller's own context, unchanged, with WaitDelay set: for a caller whose context already carries the deadline it wants (a release build's budget, a probe's timeout) and only needs the pipe not to hang after the kill.

func Expired

func Expired(ctx context.Context) bool

Expired reports whether a deadline, and not the caller's cancellation, ended ctx.

func GitBudgetFor

func GitBudgetFor(args []string) time.Duration

GitBudgetFor is the budget of one git command line: GitLongBudget for a subcommand that goes to the network (clone, fetch, pull, push, ls-remote, submodule), GitBudget for the rest. Leading options are skipped, and one that takes a path or a config assignment as its value (-C dir, -c key=value, --git-dir dir, --work-tree dir, in the separated form; the equals form is a plain flag) consumes that value, so a repository path is never read as the subcommand.

func Long

func Long(ctx context.Context, name string, args ...string) *exec.Cmd

Long is the constructor of a long-lived child: Context under the caller's cancellable context, no deadline. A nil ctx is context.Background, which never ends the child.

Types

type Bounded

type Bounded struct {
	Cmd *exec.Cmd
	// Ctx is the bound context; Expired(Ctx) says the deadline ended the child.
	Ctx context.Context
	// Budget is the time the child was given, zero when the caller's own deadline was
	// the sooner one, so a message never names a budget that did not apply.
	Budget time.Duration
	// Cancel is called once the child has been waited for.
	Cancel context.CancelFunc
}

Bounded is one child built under a bound, not yet started.

func Prepare

func Prepare(ctx context.Context, budget time.Duration, name string, args ...string) Bounded

Prepare builds a child under BoundFor(ctx, budget) with WaitDelay set.

func (Bounded) Wrap

func (b Bounded) Wrap(what string, err error) error

Wrap returns err as a *TimeoutError when the deadline ended the child, and err unchanged otherwise (including nil).

type Kind

type Kind int

Kind names the default budget of a short-lived child.

const (
	// Git is git in any form: rev-parse, ls-tree, config, grep, show, diff.
	Git Kind = iota
	// GH is the GitHub CLI: one API round trip or a short listing.
	GH
	// SSH is ssh, scp and rsync: a connection, then a file or a short remote command.
	SSH
	// Go is go tooling run as a one-shot: env, list, tool.
	Go
	// Tool is every other one-shot: sops, age-keygen, tailscale, ps, lsof, sysctl,
	// diskutil, stty, taskkill and the sandbox's own check.
	Tool
)

func KindOf

func KindOf(name string) Kind

KindOf is the kind of a program by its base name, for a caller that runs whatever name it is handed (a test seam's real implementation).

func (Kind) Budget

func (k Kind) Budget() time.Duration

Budget is the named default of the kind.

type TimeoutError

type TimeoutError struct {
	// What is the command line, for the message.
	What string
	// Budget is the time the child was given, when the caller knows it (zero otherwise).
	Budget time.Duration
	// Err is what the wait returned, usually "signal: killed".
	Err error
}

TimeoutError is what a child killed at its deadline is reported as.

func (*TimeoutError) Error

func (e *TimeoutError) Error() string

func (*TimeoutError) Unwrap

func (e *TimeoutError) Unwrap() error

Jump to

Keyboard shortcuts

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