detach

package
v1.61.0 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package detach runs a program that outlives the cockpit. The process is put into a session of its own and writes into files instead of pipes, so nothing it does depends on the server that started it: a restart, a self update or a crash leaves it running, and whoever comes next picks it up again.

What ties the two processes together is a lock file, not a process number. An exclusive flock is taken before anything starts and travels into the child as an inherited descriptor, so there is no moment where the program runs and the lock is free, and the lock dies with the last process that holds it. Whether that file can be locked is therefore whether the program still runs. No start times, no process table, the same code on every unix.

The program does not run directly: it runs under a hold process, a copy of this binary whose one job is to hold the lock, enforce the timeout and write down the exit code, because the exit code of a process this server did not start is lost to it (nobody can wait for a process that is not their child).

Index

Constants

This section is empty.

Variables

View Source
var HoldArgs = []string{"run-detached"}

HoldArgs is the argv prefix that reaches Hold in the real binary. A test binary has no command tree in front of it and recognizes the same token itself, so the caller builds one argv and both binaries read it the same way.

Functions

func Alive

func Alive(pid int, lock string) bool

Alive reports whether a run this server did not start is still going. The lock is the truth. The wait is only hygiene: after a self update the runs of the previous image are this image's children, and a child nobody collects would sit around as a zombie for as long as this server lives. Not our child answers with an error, which is the normal case after a full restart and costs nothing.

func Hold

func Hold(args []string) int

Hold runs the program named in args and waits it out. It is the process behind `dev-cockpit run-detached`, and its job is to hold the run's lock, inherited as an open descriptor, for as long as the program runs: the program cannot drop it by closing its own descriptors, because this process keeps its copy. Standard output and error are already the files of the run, wired up by the server before this process started, and the program's exit code is returned as this process's own.

func Kill

func Kill(pid int, lock string)

Kill ends the whole process group of a run. The lock is checked first: a run that already ended must not be signalled, its process number may belong to somebody else by now.

func Result

func Result(path string) (int, bool)

Result reads back the exit code a finished run wrote down. A run whose hold process was killed outright never wrote one, and that is what the second return value says: the run ended, but not by its own decision.

func TimedOut

func TimedOut(code int) bool

TimedOut reports whether a code is the one a run killed for taking too long answers with.

Types

type Options

type Options struct {
	// Command is the program and its arguments, never a shell line. Whatever
	// travels in here stays an argument and can never become a command.
	Command []string
	// Dir is the working directory of the program.
	Dir string
	// Env replaces the environment of the program when it is set; nil inherits
	// this process's own.
	Env []string
	// Out is the file the program's standard output is written to. Err is the
	// one for standard error; leaving it empty puts both into Out, which is
	// what a caller asking for the combined output wants.
	Out string
	Err string
	// Lock is the file whose exclusive lock says the run is still going.
	Lock string
	// Result is where the hold process writes the exit code. Leaving it empty
	// means nobody asks about it, and the run reports through its output alone.
	Result string
	// Timeout bounds the run. It is enforced by the hold process, not by the
	// caller: the server that asked for the run may be gone long before the
	// run is, and a context here would die with it.
	Timeout time.Duration
}

Options describe one detached run.

type Process

type Process struct {
	// contains filtered or unexported fields
}

Process is a started run: what it takes to find it again, and how to tell whether it is still going.

func Adopt

func Adopt(pid int, lock string) Process

Adopt is a run this server did not start, named by what an earlier one wrote down about it.

func Start

func Start(opts Options) (Process, error)

Start launches one run detached from this server: its own session, its output straight into a file, no pipe between the two. That is what lets it keep running while the cockpit restarts, and it is why nothing here may hold a handle the child depends on.

func (Process) Alive

func (p Process) Alive() bool

Alive reports whether the run is still going.

func (Process) Kill

func (p Process) Kill()

Kill ends the whole process group. A program spawns helpers, and killing only the leader would leave them writing into a file nobody reads.

func (Process) PID

func (p Process) PID() int

PID is the hold process, the one that a kill signals and a self update reaps. Whether the run is still going is decided by the lock alone.

func (Process) Wait

func (p Process) Wait()

Wait blocks until the run has ended. A run this server started is waited for, one it adopted is watched through its lock.

Jump to

Keyboard shortcuts

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