procsig

package
v0.1.0-proto2h Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: MPL-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package procsig delivers signals to agent processes guarded by their start epoch (DDR-5, GAPI-DIV-016): a signal aimed at a dead process whose PID was recycled must never hit the new occupant. Linux-only by design (operator decision 2026-07-28) - delivery uses pidfd_open + pidfd_send_signal with no fallback; non-Linux builds compile but refuse to deliver.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrStaleEpoch: the process at this PID is not the one the caller
	// meant (start epoch mismatch - the PID was recycled).
	ErrStaleEpoch = errors.New("procsig: stale start epoch, refusing delivery")
	// ErrProcessGone: no process holds this PID.
	ErrProcessGone = errors.New("procsig: process gone")
	// ErrUnsupported: signal delivery is Linux-only.
	ErrUnsupported = errors.New("procsig: signal delivery is linux-only")
	// ErrPidfdUnsupported: the kernel does not offer pidfd_open, so
	// neither this package nor the os/exec handle path can pin a
	// process. Signal delivery would silently degrade to kill-by-PID,
	// which is the PID-recycling hazard itself (GAPI-DIV-016).
	ErrPidfdUnsupported = errors.New("procsig: kernel does not support pidfd_open")
)

Typed delivery failures. Errors are data; there is deliberately no retry on ErrStaleEpoch - a stale epoch means the target is gone and the orchestrator must re-resolve, not hammer a recycled PID.

Functions

func RequirePidfd

func RequirePidfd() error

RequirePidfd reports whether this kernel can pin a process with pidfd_open, by opening a pidfd on this process and closing it again.

It exists because gapi's signal safety is INHERITED rather than asserted (GAPI-DIV-016). os.Process signals through pidfd_send_signal against a handle os/exec bound at fork, which is a stronger guarantee than this package's epoch check - a handle cannot refer to a recycled PID at all. But if the kernel has no pidfd, os/exec silently falls back to a raw kill by PID and nothing anywhere notices. A supervisor that signals the wrong process because the kernel was older than it assumed should refuse to boot, not discover it during a stop.

Probing self is deliberate: it needs no target to exist and no privilege beyond signalling ourselves, so a false negative cannot come from the target rather than the kernel.

func Signal

func Signal(pid int, startEpoch uint64, sig syscall.Signal) error

Signal delivers sig to pid if and only if the process's start epoch matches. The check-pin-recheck order makes it race-free: the epoch is checked, the process is pinned with pidfd_open, and the epoch is re-checked while pinned - a PID recycled between the first check and the pin shows a different epoch on the recheck and is refused. The signal then goes to the pinned process via pidfd_send_signal, never to a raw PID.

func StartEpoch

func StartEpoch(pid int) (uint64, error)

StartEpoch returns the process's start time in clock ticks since boot (/proc/<pid>/stat field 22). Recorded at spawn, it uniquely identifies a PID incarnation on one boot of one node.

Types

type ProcessIdentity

type ProcessIdentity struct {
	Pid        int
	StartEpoch uint64
	PidNsInode uint64
}

ProcessIdentity is the mutable runtime locator of a process: the PID plus the two fields that disambiguate a PID across recycling and namespaces (DDR-3/4/5). It travels in gossip, never in Raft.

func Identify

func Identify(pid int) (ProcessIdentity, error)

Identify captures a process's runtime locator: pid, start epoch, and pid-namespace inode (/proc/<pid>/ns/pid). Nodes call this after spawning an instance and publish it over gossip.

Jump to

Keyboard shortcuts

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