procutil

package
v0.20.0 Latest Latest
Warning

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

Go to latest
Published: Oct 4, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package procutil answers one question about a process id — is the process alive, and which incarnation of the pid is it — for the watcher's single-instance guard (plan 6.6, corrected by E0-5; package tree 7.1).

E0-5 item 1 is the reason this package exists. A SIGKILLed Claude Code process stays a ZOMBIE until whoever owns it reaps the corpse — measured at 27.6 s and 59.0 s, with no upper bound that belongs to Brigade — and for that whole time kill(pid, 0) keeps returning success, `ps -o lstart=` keeps printing the original start time, and the socket file stays on disk. A guard built on kill(pid, 0) alone therefore reports a dead session as alive. Lookup reads the process STATE from the kernel (a sysctl on darwin, /proc on linux) so a zombie reads as dead, and it reads the process's start time at the finest granularity the kernel offers so that a reused pid reads as a different process: E0-5's "unfixed limitation" — `ps -o lstart=` has 1 s resolution, and six processes were observed sharing one token — is fixed by construction on darwin, where the token carries microseconds.

The package spawns nothing and reads nothing but the kernel's own process table. Its two implementation files carry `//go:build darwin` and `//go:build linux` so a third operating system fails to build instead of compiling a guard that cannot see process state (D33).

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidPID = errors.New("procutil: pid must be positive")

ErrInvalidPID is returned by Lookup for a pid that is not positive: 0 and negative values address process groups in kill(2), and a guard that signalled a whole group by mistake would be a bug rather than a lookup.

Functions

This section is empty.

Types

type Info

type Info struct {
	// PID is the pid that was looked up, echoed so a caller holding
	// several Infos cannot confuse them.
	PID int
	// Exists reports that the pid names a process the kernel still knows
	// about: kill(pid, 0) returned nil or EPERM. A zombie EXISTS in this
	// sense — that is the whole E0-5 trap — so Exists alone never means
	// alive; read Zombie too.
	Exists bool
	// Zombie reports that the process has exited and is waiting to be
	// reaped (darwin p_stat SZOMB, linux /proc state Z). It can only be
	// true when Exists is.
	Zombie bool
	// Foreign reports that kill(pid, 0) returned EPERM: the pid belongs
	// to another user, so it cannot be the watcher this user's hook
	// spawned. The guard treats it as pid reuse.
	Foreign bool
	// StartToken identifies THIS incarnation of the pid. It is the
	// process's start time as the kernel records it, rendered as a
	// decimal string that is compared byte for byte and never parsed
	// again: darwin "<sec>.<usec>" (usec zero-padded to six digits),
	// linux the start time in clock ticks since boot (field 22 of
	// /proc/<pid>/stat). It is empty when the process does not exist or
	// its state could not be read.
	StartToken string
}

Info is what the guard needs to know about a pid at one instant.

The fields are deliberately separate rather than folded into one "alive" boolean: the pidfile guard (internal/harness/pidfile) combines them with the stored start token, and a caller diagnosing a stale pidfile wants to say WHICH condition failed.

func Lookup

func Lookup(pid int) (Info, error)

Lookup reports the state of pid.

It returns (Info{PID: pid}, nil) — Exists false and everything else zero — for a pid the kernel does not know, including a pid that was reaped a moment ago and a pid that vanished between the two kernel calls this function makes. It returns an error only for an invalid pid (ErrInvalidPID) or when the kernel refused a query it should have answered; in that case the Info alongside is partial (Exists and Foreign are set, StartToken is empty), which the pidfile guard reads as "not proven alive".

Lookup never signals anything: kill(pid, 0) delivers no signal, it only asks the kernel whether it could.

Jump to

Keyboard shortcuts

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