Documentation
¶
Overview ¶
Package procguard keeps forge from signalling the processes it is running inside.
forge stops processes it started: `forge env down <env>`, `forge env down --all`, the `forge env up` pre-flight that replaces a running stack, host-infra shutdown and `forge debug stop`. Each selects its targets by forge's ownership markers, and those markers are ENVIRONMENT VARIABLES — inherited by every descendant of a process forge started. A forge-started server that itself runs work (an agent server hosting sessions, a shell, an IDE task runner) therefore hands the markers to everything it spawns, including a `forge env down` typed inside it. That command used to select the very server hosting it, SIGTERM the tree, and end its own session along with every sibling session running in it.
The rule here is structural, not a heuristic: a forge command never signals its own LINEAGE — itself and every ancestor up to init. Lineage is read from the kernel's parent links, so no environment variable, process name or configuration can widen or narrow it, and the same process table always produces the same decision.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrLineage = errors.New("process is in this command's own lineage")
ErrLineage is wrapped by every refusal to signal a member of this command's own lineage. Callers test for it with errors.Is.
Functions ¶
This section is empty.
Types ¶
type Entry ¶
type Entry struct {
// PPID is the parent pid. 0 means "no trustworthy parent" and ends the
// walk (on Windows a recorded parent that is dead or younger than its
// child is reported as 0 — see trustedParent).
PPID int
// PGID is the process-group id, or 0 on a platform whose process groups
// a signal cannot target (Windows).
PGID int
}
Entry is what a lineage walk reads about one process.
type Lineage ¶
type Lineage struct {
// contains filtered or unexported fields
}
Lineage is one process and every ancestor above it, nearest first.
func Self ¶
func Self() Lineage
Self returns this process's lineage, read from the operating system.
It is read fresh on every call rather than cached: forge also runs embedded in long-lived servers, and a process whose parent dies is re-parented, so a cached chain could go on protecting a pid the OS has since recycled. A walk is a handful of syscalls.
func Walk ¶
Walk builds the lineage of self from lookup. self is always a member, even when lookup knows nothing about it. The walk ends at a pid <= 0, a process that is its own parent, a pid already seen (a cycle), or a pid lookup cannot answer for. Pure: the decision is a function of the table it is handed.
func (Lineage) ContainsGroup ¶
ContainsGroup reports whether any member of the lineage belongs to process group pgid — so a group-wide signal to it would reach this command.
type LineageError ¶
type LineageError struct {
PID int
// Command names the process for the reader ("" when unreadable).
Command string
}
LineageError is the refusal for one pid.
func (*LineageError) Error ¶
func (e *LineageError) Error() string
func (*LineageError) Unwrap ¶
func (e *LineageError) Unwrap() error