proc

package
v0.19.2 Latest Latest
Warning

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

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

README

proc — OS process-supervision contract

internal/core/proc declares the ports, value types, and error sentinels for the SDK's OS process-supervision domain (ADR 0016). It is the shared foundation behind six public facades:

Facade Built on
pkg/v1/proc/process Process, Spec, ExitValue
pkg/v1/proc/signal Signal, Parse
pkg/v1/proc/reaper Reaper
pkg/v1/proc/rlimit Resource, LimitValue
pkg/v1/proc/cgroup Group
pkg/v1/proc/systemd/notify Listener, NotificationValue

It is an internal/ package — consumers import the pkg/v1 facades, never this package directly.

Shape

  • Ports (Process, Reaper, Group, Listener) are interfaces; implementations live in internal/service/proc/*, selected at build time by platform tag (no runtime registry).
  • Value types (Spec, ExitValue, LimitValue, NotificationValue, Signal, Resource) are immutable and carry no policy.
  • Errors: the whole domain shares one dotted-quad PP octet (0.2.6.*); every sentinel is declared in codes_gen.go (written by kit gen from the design, ADR 0164) and only wrapped downstream.

Platform

process, signal, and rlimit are Unix-wide; reaper (subreaper), cgroup, and the systemd/notify listener's credential check are Linux-only. Non-supporting platforms return a no-op plus the UNSUPPORTED_PLATFORM sentinel — they never panic and always compile.

See also

  • ADR 0016 — docs/adr/0016-sdk-process-supervision-domain.md
  • internal/core/proc/CLAUDE.md — maintainer notes

The ports — Process, Reaper, Group and Listener — are generated from design/proc.yaml into design_gen.go (ADR 0163): a port changes in the design, then kit gen.

Documentation

Overview

Package proc — range 0.2.6.* (ADR 0016 core/proc block). The whole domain owns this single PP octet: every sentinel below is declared here and only wrapped (never re-Defined) by the service implementations and pkg/v1 facades.

Package proc — declares the sentinels returned across the process-supervision domain. Each var's name equals its errs.Define Reason in SCREAMING_SNAKE form; service implementations and pkg/v1 facades wrap these, never re-Define them.

Package proc — the ExitValue value type: the outcome of a finished process.

Package proc — the Group port: a cgroup v2 control group handle.

Package proc — the LimitValue value type: a soft/hard setrlimit pair.

Package proc — the Listener port: the supervisor side of sd_notify.

Package proc — the MemoryLimitValue value type and the MemorySource enum: what a derived Go soft memory limit is, and what decided it.

Package proc — the NotificationValue value type: a parsed sd_notify datagram.

Package proc declares the OS process-supervision contract of the SDK: the ports (Process, Reaper, Group, Listener), the immutable value types (Spec, ExitValue, LimitValue, NotificationValue, Signal, Resource), and the domain's complete error-sentinel set (range 0.2.6.*). It is the sixth internal/core sibling (ADR 0016), peer of codec / writer / crypto / logger / transform.

Unlike codec and crypto, proc ships no plug-in registry: each primitive has a single canonical OS implementation chosen at build time by platform tag, not a runtime-registered scheme. Core declares the ports and value types; concrete behaviour lives in internal/service/proc/*; the pkg/v1/proc facades (process, signal, reaper, rlimit, cgroup, systemd/notify) re-export this surface.

proc is stdlib-only (os, syscall, time, strconv, strings) plus internal/kernel/errs — no golang.org/x/sys — preserving the SDK's dep-light invariant. Every error returned by the domain is one of the sentinels in errors.go; service code wraps them with errs.Wrap and never defines new codes.

Package proc — the Process port: lifetime control over a spawned process.

Package proc — the Reaper port: collection of terminated child processes.

Package proc — the Signal value type: a typed, platform-portable OS signal with Parse / String / OS bridging. The name table is platform-specific and lives in signal_unix.go / signal_other.go.

Package proc — Unix signal name table (linux/darwin/bsd).

Package proc — the Spec value type: an immutable process spawn specification.

Package proc — the StdioMode value: how a spawned child's standard streams (stdin, stdout, stderr) are connected.

Index

Constants

View Source
const CodeCgroupCreateFailed errs.Code = 0x00_02_06_10 // 0.2.6.16

CodeCgroupCreateFailed identifies a failure creating a control-group directory under the cgroup v2 hierarchy.

View Source
const CodeCgroupDeleteFailed errs.Code = 0x00_02_06_12 // 0.2.6.18

CodeCgroupDeleteFailed identifies a failure removing a control-group directory.

View Source
const CodeCgroupUnavailable errs.Code = 0x00_02_06_0F // 0.2.6.15

CodeCgroupUnavailable identifies a cgroup operation attempted where cgroup v2 is not mounted or not delegated to the caller.

View Source
const CodeCgroupWriteFailed errs.Code = 0x00_02_06_11 // 0.2.6.17

CodeCgroupWriteFailed identifies a failure writing a controller file (memory.max, cpu.max, pids.max, io.max, cgroup.procs).

View Source
const CodeCredentialMismatch errs.Code = 0x00_02_06_16 // 0.2.6.22

CodeCredentialMismatch identifies a received datagram whose kernel-verified sender credentials did not match the expected supervised process.

View Source
const CodeInvalidNotification errs.Code = 0x00_02_06_15 // 0.2.6.21

CodeInvalidNotification identifies a received sd_notify datagram that could not be parsed into NAME=value fields.

View Source
const CodeInvalidSpec errs.Code = 0x00_02_06_02 // 0.2.6.2

CodeInvalidSpec identifies a Start call whose Spec is malformed — an empty Path or a contradictory attribute combination.

View Source
const CodeListenFailed errs.Code = 0x00_02_06_14 // 0.2.6.20

CodeListenFailed identifies a failure creating or binding the supervisor-side sd_notify datagram socket.

View Source
const CodeNotifyFailed errs.Code = 0x00_02_06_13 // 0.2.6.19

CodeNotifyFailed identifies a failure sending an sd_notify datagram to $NOTIFY_SOCKET (distinct from the no-op when the variable is unset).

View Source
const CodeReapFailed errs.Code = 0x00_02_06_0C // 0.2.6.12

CodeReapFailed identifies a wait4 failure during a reap sweep other than the benign "no children" condition.

View Source
const CodeRelayFailed errs.Code = 0x00_02_06_0A // 0.2.6.10

CodeRelayFailed identifies a Relay that could not forward a received signal to its target process or group.

View Source
const CodeRlimitFailed errs.Code = 0x00_02_06_0E // 0.2.6.14

CodeRlimitFailed identifies a setrlimit(2) failure applying a per-process resource limit.

View Source
const CodeSignalFailed errs.Code = 0x00_02_06_07 // 0.2.6.7

CodeSignalFailed identifies a kill(2) failure delivering a signal to a process or process group.

View Source
const CodeSpawnFailed errs.Code = 0x00_02_06_05 // 0.2.6.5

CodeSpawnFailed identifies a fork/exec failure when starting the process — a missing binary, a permission denial, or a kernel resource fault.

View Source
const CodeStdioCaptureFailed errs.Code = 0x00_02_06_17 // 0.2.6.23

CodeStdioCaptureFailed identifies a StdioCapture spawn whose output copier could not deliver the child's stdout/stderr to the caller's writer (the writer itself returned an error). Surfaced from Wait when the process otherwise exited cleanly.

View Source
const CodeStopFailed errs.Code = 0x00_02_06_08 // 0.2.6.8

CodeStopFailed identifies a graceful-stop that could not terminate the process group even after escalating to SIGKILL.

View Source
const CodeSubreaperFailed errs.Code = 0x00_02_06_0B // 0.2.6.11

CodeSubreaperFailed identifies a prctl(PR_SET_CHILD_SUBREAPER) failure when arming subreaper semantics for a non-PID1 supervisor.

View Source
const CodeUnknownGroup errs.Code = 0x00_02_06_04 // 0.2.6.4

CodeUnknownGroup identifies a Spec naming a Group or supplementary group that does not resolve to a gid on the host.

View Source
const CodeUnknownResource errs.Code = 0x00_02_06_0D // 0.2.6.13

CodeUnknownResource identifies an Apply call naming a Resource the platform does not map to an RLIMIT_* constant.

View Source
const CodeUnknownSignal errs.Code = 0x00_02_06_09 // 0.2.6.9

CodeUnknownSignal identifies a Parse call whose name or number is not in the platform signal table.

View Source
const CodeUnknownUser errs.Code = 0x00_02_06_03 // 0.2.6.3

CodeUnknownUser identifies a Spec naming a User that does not resolve to a uid on the host.

View Source
const CodeUnsupportedPlatform errs.Code = 0x00_02_06_01 // 0.2.6.1

CodeUnsupportedPlatform identifies a primitive invoked on an operating system that cannot provide it (e.g. cgroup or subreaper off Linux). The no-op stub returns it instead of panicking.

View Source
const CodeWaitFailed errs.Code = 0x00_02_06_06 // 0.2.6.6

CodeWaitFailed identifies a wait4 failure while reaping the leader — a host fault, not a normal non-zero exit (which is reported via ExitValue).

View Source
const LimitInfinity uint64 = ^uint64(0)

LimitInfinity is the soft/hard value meaning "no limit" (RLIM_INFINITY). Use it in a LimitValue to lift a resource ceiling rather than setting a number.

Variables

View Source
var (
	// UnsupportedPlatform is returned by a primitive invoked on an operating
	// system that cannot provide it; the no-op stub returns it, never a panic.
	UnsupportedPlatform = errs.Define(CodeUnsupportedPlatform, "UNSUPPORTED_PLATFORM",
		"This operation is not supported on this platform",
		"core/proc: primitive unavailable on the current GOOS; the stub returns this instead of acting",
		errs.WithExitCode(exitUnavailable))

	// InvalidSpec is returned by Start when the Spec is malformed — an empty Path
	// or a contradictory attribute combination.
	InvalidSpec = errs.Define(CodeInvalidSpec, "INVALID_SPEC",
		"Process specification is invalid",
		"service/proc/exec.Start: Spec is malformed (empty Path or contradictory attributes)",
		errs.WithExitCode(exitUsage))

	// UnknownUser is returned when a Spec names a User that does not resolve to a
	// uid on the host.
	UnknownUser = errs.Define(CodeUnknownUser, "UNKNOWN_USER",
		"The requested user does not exist",
		"service/proc/exec.Start: Spec.User did not resolve to a uid via os/user",
		errs.WithExitCode(exitNoUser))

	// UnknownGroup is returned when a Spec names a Group or supplementary group
	// that does not resolve to a gid on the host.
	UnknownGroup = errs.Define(CodeUnknownGroup, "UNKNOWN_GROUP",
		"The requested group does not exist",
		"service/proc/exec.Start: Spec.Group/Groups did not resolve to a gid via os/user",
		errs.WithExitCode(exitNoUser))

	// SpawnFailed is returned when fork/exec fails — a missing binary, a
	// permission denial, or a kernel resource fault.
	SpawnFailed = errs.Define(CodeSpawnFailed, "SPAWN_FAILED",
		"Could not start the process",
		"service/proc/exec.Start: fork/exec failed",
		errs.WithExitCode(exitOSErr))

	// WaitFailed is returned when wait4 fails reaping the leader — a host fault,
	// not a normal non-zero exit (reported via ExitValue instead).
	WaitFailed = errs.Define(CodeWaitFailed, "WAIT_FAILED",
		"Could not wait for the process to exit",
		"service/proc/exec.Wait: wait4 returned an unexpected error",
		errs.WithExitCode(exitOSErr))

	// SignalFailed is returned when kill(2) fails delivering a signal to a
	// process or process group.
	SignalFailed = errs.Define(CodeSignalFailed, "SIGNAL_FAILED",
		"Could not deliver the signal",
		"service/proc: kill(2) failed for the target pid or process group",
		errs.WithExitCode(exitOSErr))

	// StopFailed is returned when a graceful stop cannot terminate the process
	// group even after escalating to SIGKILL.
	StopFailed = errs.Define(CodeStopFailed, "STOP_FAILED",
		"Could not stop the process",
		"service/proc/exec.Stop: process group survived signal escalation to SIGKILL",
		errs.WithExitCode(exitOSErr))

	// UnknownSignal is returned by Parse when the name or number is not in the
	// platform signal table.
	UnknownSignal = errs.Define(CodeUnknownSignal, "UNKNOWN_SIGNAL",
		"Unknown signal name or number",
		"core/proc.Parse: input is not a signal known on this platform",
		errs.WithExitCode(exitUsage))

	// RelayFailed is returned by Relay when a received signal cannot be forwarded
	// to its target process or group.
	RelayFailed = errs.Define(CodeRelayFailed, "RELAY_FAILED",
		"Could not relay the signal to the target",
		"service/proc/signal.Relay: forwarding the signal to the target failed",
		errs.WithExitCode(exitOSErr))

	// SubreaperFailed is returned when prctl(PR_SET_CHILD_SUBREAPER) fails arming
	// subreaper semantics for a non-PID1 supervisor.
	SubreaperFailed = errs.Define(CodeSubreaperFailed, "SUBREAPER_FAILED",
		"Could not enable child-subreaper mode",
		"service/proc/reaper.SetChildSubreaper: prctl(PR_SET_CHILD_SUBREAPER) failed",
		errs.WithExitCode(exitOSErr))

	// ReapFailed is returned when a reap sweep hits a wait4 error other than the
	// benign "no children" condition.
	ReapFailed = errs.Define(CodeReapFailed, "REAP_FAILED",
		"Could not reap child processes",
		"service/proc/reaper.ReapOnce: wait4 failed with an error other than ECHILD",
		errs.WithExitCode(exitOSErr))

	// UnknownResource is returned by Apply when a Resource has no RLIMIT_*
	// mapping on the platform.
	UnknownResource = errs.Define(CodeUnknownResource, "UNKNOWN_RESOURCE",
		"Unknown or unsupported resource limit",
		"service/proc/rlimit.Apply: Resource has no RLIMIT_* mapping on this platform",
		errs.WithExitCode(exitUsage))

	// RlimitFailed is returned when setrlimit(2) fails applying a per-process
	// resource limit.
	RlimitFailed = errs.Define(CodeRlimitFailed, "RLIMIT_FAILED",
		"Could not apply the resource limit",
		"service/proc/rlimit.Apply: setrlimit(2) failed",
		errs.WithExitCode(exitOSErr))

	// CgroupUnavailable is returned when a cgroup operation runs where cgroup v2
	// is not mounted or not delegated to the caller.
	CgroupUnavailable = errs.Define(CodeCgroupUnavailable, "CGROUP_UNAVAILABLE",
		"cgroup v2 is not available or not delegated",
		"service/proc/cgroup: the unified cgroup v2 hierarchy is absent or not writable by the caller",
		errs.WithExitCode(exitUnavailable))

	// CgroupCreateFailed is returned when a control-group directory cannot be
	// created under the cgroup v2 hierarchy.
	CgroupCreateFailed = errs.Define(CodeCgroupCreateFailed, "CGROUP_CREATE_FAILED",
		"Could not create the control group",
		"service/proc/cgroup.Create: mkdir under the cgroup v2 hierarchy failed",
		errs.WithExitCode(exitOSErr))

	// CgroupWriteFailed is returned when a controller file cannot be written
	// (memory.max, cpu.max, pids.max, io.max, cgroup.procs).
	CgroupWriteFailed = errs.Define(CodeCgroupWriteFailed, "CGROUP_WRITE_FAILED",
		"Could not write the cgroup controller file",
		"service/proc/cgroup: writing a controller file failed (controller disabled or value rejected)",
		errs.WithExitCode(exitOSErr))

	// CgroupDeleteFailed is returned when a control-group directory cannot be
	// removed.
	CgroupDeleteFailed = errs.Define(CodeCgroupDeleteFailed, "CGROUP_DELETE_FAILED",
		"Could not delete the control group",
		"service/proc/cgroup.Delete: rmdir of the control group failed (still populated?)",
		errs.WithExitCode(exitOSErr))

	// NotifyFailed is returned when an sd_notify datagram cannot be sent to
	// $NOTIFY_SOCKET — distinct from the no-op when the variable is unset.
	NotifyFailed = errs.Define(CodeNotifyFailed, "NOTIFY_FAILED",
		"Could not send the sd_notify datagram",
		"service/proc/systemd/notify: writing to $NOTIFY_SOCKET failed",
		errs.WithExitCode(exitOSErr))

	// ListenFailed is returned when the supervisor-side sd_notify datagram socket
	// cannot be created or bound.
	ListenFailed = errs.Define(CodeListenFailed, "LISTEN_FAILED",
		"Could not create the sd_notify listener socket",
		"service/proc/systemd/notify.Listen: creating or binding the AF_UNIX datagram socket failed",
		errs.WithExitCode(exitOSErr))

	// InvalidNotification is returned when a received sd_notify datagram cannot
	// be parsed into NAME=value fields.
	InvalidNotification = errs.Define(CodeInvalidNotification, "INVALID_NOTIFICATION",
		"Received sd_notify datagram is malformed",
		"service/proc/systemd/notify.Recv: datagram did not parse into NAME=value fields",
		errs.WithExitCode(exitDataErr))

	// CredentialMismatch is returned when a received datagram's kernel-verified
	// sender credentials do not match the expected supervised process.
	CredentialMismatch = errs.Define(CodeCredentialMismatch, "CREDENTIAL_MISMATCH",
		"Datagram sender credentials did not match",
		"service/proc/systemd/notify.Recv: SO_PASSCRED sender pid is not the expected supervised process",
		errs.WithExitCode(exitNoPerm))

	// StdioCaptureFailed is returned from Wait when a StdioCapture copier could
	// not deliver the child's stdout/stderr to the caller's writer (the writer
	// itself errored), even though the process exited cleanly.
	StdioCaptureFailed = errs.Define(CodeStdioCaptureFailed, "STDIO_CAPTURE_FAILED",
		"Could not deliver the captured output to the writer",
		"service/proc/exec: a StdioCapture copier failed writing the child's stdout/stderr to the caller's sink",
		errs.WithExitCode(exitIOErr))
)

Functions

This section is empty.

Types

type ExitValue

type ExitValue struct {
	// Code is the exit status (0–255) when the process exited normally; it is -1
	// when the process was terminated by a signal.
	Code int
	// Signal is the signal that terminated the process, or the zero Signal when
	// it exited normally.
	Signal Signal
	// Signaled reports whether the process was terminated by a signal rather
	// than exiting on its own.
	Signaled bool
	// UserTime is the CPU time spent in user mode.
	UserTime time.Duration
	// SystemTime is the CPU time spent in kernel mode on the process's behalf.
	SystemTime time.Duration
	// MaxRSS is the peak resident set size in kilobytes, as reported by wait4's
	// rusage; it is 0 when the platform does not supply it.
	MaxRSS int64
}

ExitValue is the immutable outcome of a finished process: its exit status, the signal that terminated it (if any), and resource-usage accounting. It is the value returned by Process.Wait.

func (ExitValue) Success

func (e ExitValue) Success() bool

Success reports whether the process exited normally with status zero.

type Group

type Group interface {
	// SetMemoryMax writes memory.max (systemd MemoryMax=); a negative value
	// means "max" (no limit).
	SetMemoryMax(bytes int64) error
	// SetCPUMax writes cpu.max as "quota period" (systemd CPUQuota=); a negative
	// quota means "max".
	SetCPUMax(quota, period int64) error
	// SetPidsMax writes pids.max (systemd TasksMax=); a negative value means
	// "max".
	SetPidsMax(n int64) error
	// SetIOMax writes one io.max line verbatim (systemd IO*Max=), e.g.
	// "8:0 rbps=1048576".
	SetIOMax(spec string) error
	// Add moves the process pid into this group by writing cgroup.procs.
	Add(pid int) error
	// Kill atomically SIGKILLs every process in the group by writing "1" to
	// cgroup.kill (systemd KillMode=control-group; kernel >= 5.14). Unlike a
	// process-group kill, nothing escapes — a member that called setsid is still
	// in the control group. Returns UnsupportedPlatform when cgroup.kill is absent
	// (older kernels), so callers can fall back to SignalGroup.
	Kill() error
	// Freeze quiesces the whole group by writing "1" to cgroup.freeze (kernel
	// >= 5.2), stopping every member so a consistent signal sweep or snapshot can
	// run. Returns UnsupportedPlatform when cgroup.freeze is absent. Idempotent.
	Freeze() error
	// Thaw resumes a frozen group by writing "0" to cgroup.freeze. Returns
	// UnsupportedPlatform when cgroup.freeze is absent. Idempotent.
	Thaw() error
	// Delete removes the (empty) control group; callers move processes out first.
	Delete() error
}

Group is a handle to a cgroup v2 control group used to confine a process tree: set controller limits, attach processes, and remove the group. Implementations live in internal/service/proc/*; it is Linux-only and non-Linux builds return a no-op satisfying this interface with UNSUPPORTED_PLATFORM.

type LimitValue

type LimitValue struct {
	// Soft is the enforced limit; it must not exceed Hard.
	Soft uint64
	// Hard is the ceiling for Soft; raising it typically requires privilege.
	Hard uint64
}

LimitValue is an immutable soft/hard resource-limit pair for setrlimit(2). The soft limit is the enforced value; the hard limit is the ceiling an unprivileged process may raise the soft limit to.

type Listener

type Listener interface {
	// Recv blocks until the next datagram arrives and returns it parsed, with the
	// sender PID verified by the kernel. It returns a typed error on a malformed
	// datagram or once the listener is closed.
	Recv() (NotificationValue, error)
	// Close releases the socket and unblocks any in-flight Recv.
	Close() error
}

Listener is the supervisor side of the sd_notify protocol: it owns the AF_UNIX datagram socket a supervised child writes readiness to, and yields one parsed, credential-verified NotificationValue per received datagram. Implementations live in internal/service/proc/*.

type MemoryLimitValue

type MemoryLimitValue struct {
	// Allowance is the tightest cgroup memory cap governing this process, in
	// bytes, or zero when none was read.
	Allowance int64
	// Limit is the Go soft memory limit derived from Allowance, in bytes, or
	// zero when no limit was applied.
	Limit int64
	// Source names what decided the outcome.
	Source MemorySource
}

MemoryLimitValue is an immutable record of one soft-memory-limit derivation: the cgroup allowance it read, the limit it derived, and what decided the outcome.

Allowance and Limit are zero on every outcome except MemorySourceCgroup, with one exception: MemorySourceBelowFloor carries the Allowance it read, because the reason the limit was discarded is only legible next to the cap that produced it.

func (MemoryLimitValue) Applied

func (m MemoryLimitValue) Applied() bool

Applied reports whether the derivation installed a limit. It is true for exactly one source, and it is the predicate a caller wants rather than a comparison against Limit — a zero Limit and "no limit applied" are the same state today, and tying call sites to that coincidence would be fragile.

type MemorySource

type MemorySource uint8

MemorySource names what decided the Go soft memory limit, so a caller that sees no limit applied can tell WHY without re-deriving it.

The zero value is deliberately unclaimed. A MemorySource is produced by a derivation, never chosen by a caller, so a zero reaching a log line is a value this package never minted rather than one of the four real outcomes wearing a default.

const (
	// MemorySourceOperator reports that GOMEMLIMIT carried a non-empty value.
	// The Go runtime has already applied it and the derivation stands aside:
	// overriding an operator's explicit decision would contradict them silently.
	//
	// The run starts at iota+1 so the zero stays unminted — see the type comment.
	MemorySourceOperator MemorySource = iota + 1
	// MemorySourceUnconstrained reports that no control group governing this
	// process declares a memory cap: not containerised, capped at "max", or not
	// Linux at all. There is nothing to derive a limit from.
	MemorySourceUnconstrained
	// MemorySourceBelowFloor reports that a cap was found but the limit derived
	// from it fell under the usable floor. Holding the runtime beneath it would
	// spin the collector continuously without averting the kill, so the
	// derivation declines rather than applying a limit it expects to hurt.
	MemorySourceBelowFloor
	// MemorySourceCgroup reports the only outcome that yields a limit: a real
	// cap was read from the control-group hierarchy and the derived value is
	// usable.
	MemorySourceCgroup
)

func (MemorySource) String

func (s MemorySource) String() string

String renders the source for a log line. A value this package never mints — including the zero — renders as "unknown" rather than as a number, because it is a defect worth reading as one.

type NotificationValue

type NotificationValue struct {
	// State is the raw NAME=value field set from the datagram (READY, STATUS,
	// MAINPID, WATCHDOG, RELOADING, STOPPING, …), preserved verbatim.
	State map[string]string
	// Status is the free-text STATUS= line, empty when absent.
	Status string
	// MainPID is the PID advertised by MAINPID=, or 0 when absent.
	MainPID int
	// SenderPID is the sending process's PID as verified by the kernel via
	// SO_PASSCRED, not a value the sender can forge; 0 when credentials were
	// unavailable.
	SenderPID int
}

NotificationValue is a parsed sd_notify(3) datagram received by a Listener: the raw newline-separated state fields plus the kernel-verified sender PID. It carries no policy — a supervisor decides what READY or WATCHDOG means for ordering. The lifecycle flags are read through methods so State stays the single source of truth.

func (NotificationValue) Ready

func (n NotificationValue) Ready() bool

Ready reports whether the datagram carried READY=1.

func (NotificationValue) Reloading

func (n NotificationValue) Reloading() bool

Reloading reports whether the datagram carried RELOADING=1.

func (NotificationValue) Stopping

func (n NotificationValue) Stopping() bool

Stopping reports whether the datagram carried STOPPING=1.

func (NotificationValue) Watchdog

func (n NotificationValue) Watchdog() bool

Watchdog reports whether the datagram carried WATCHDOG=1 (a keep-alive ping).

type Process

type Process interface {
	// PID reports the process identifier of the leader.
	PID() int
	// Wait blocks until the process exits and returns its ExitValue — the real
	// one even when a running Reaper collected the child first. It is safe
	// to call once; concurrent or repeated calls observe the same outcome. Under
	// StdioCapture it returns only after every captured byte has reached the
	// caller's writers; if a writer itself failed, the ExitValue still reports the
	// real exit status and the error is StdioCaptureFailed.
	Wait() (ExitValue, error)
	// Signal delivers sig to the leader process only.
	Signal(sig Signal) error
	// SignalGroup delivers sig to the leader's entire process group (kill(-pgid,
	// sig)), reaching forked grandchildren — the KillMode=control-group analogue.
	SignalGroup(sig Signal) error
	// Stop terminates the process group gracefully: it sends sig, waits up to
	// grace for exit, then escalates to SIGKILL. It returns when the process has
	// exited or ctx is cancelled.
	Stop(ctx context.Context, grace time.Duration, sig Signal) error
}

Process is a handle to a spawned process. It exposes only lifetime control — observe completion, deliver a signal to the leader or its whole group, and stop it gracefully. Implementations live in internal/service/proc/*; the pkg/v1/proc/process facade returns this interface.

type Reaper

type Reaper interface {
	// Start begins a background SIGCHLD-driven loop that reaps children as they
	// exit. It is idempotent: a second Start while running is a no-op.
	Start()
	// Stop ends the background loop after a final draining sweep, so shutdown
	// leaves no un-reaped zombies. It is safe to call without a prior Start.
	Stop()
	// ReapOnce performs a single non-blocking sweep and reports how many
	// children were reaped. It is safe to call concurrently with the loop.
	ReapOnce() (int, error)
}

Reaper collects terminated child processes so they do not linger as zombies — the core duty of any PID1 or subreaper. Implementations live in internal/service/proc/*; non-supporting platforms return a no-op reaper.

A sweep collects every exited child, including one a Process handle is waiting for; an implementation must hand such a child's exit status to that Process rather than discard it, so its Wait still reports the real exit.

type Resource

type Resource int

Resource identifies a per-process resource governed by setrlimit(2). It is an abstract, platform-portable enum; the service layer maps each value to the platform's RLIMIT_* constant. The zero value ResourceUnknown is the reserved invalid sentinel.

const (
	// ResourceUnknown is the reserved zero value: no resource selected.
	ResourceUnknown Resource = iota
	// ResourceNoFile limits the highest open file descriptor (RLIMIT_NOFILE),
	// mapping systemd's LimitNOFILE=.
	ResourceNoFile
	// ResourceNProc limits the number of processes for the real user id
	// (RLIMIT_NPROC), mapping systemd's LimitNPROC=.
	ResourceNProc
	// ResourceCore limits the size of a core dump in bytes (RLIMIT_CORE),
	// mapping systemd's LimitCORE=.
	ResourceCore
	// ResourceAS limits the process virtual address-space size (RLIMIT_AS),
	// mapping systemd's LimitAS=.
	ResourceAS
	// ResourceCPU limits CPU time in seconds (RLIMIT_CPU), mapping systemd's
	// LimitCPU=.
	ResourceCPU
	// ResourceFSize limits the largest file the process may create in bytes
	// (RLIMIT_FSIZE), mapping systemd's LimitFSIZE=.
	ResourceFSize
	// ResourceData limits the process data-segment size (RLIMIT_DATA), mapping
	// systemd's LimitDATA=.
	ResourceData
	// ResourceStack limits the process stack size (RLIMIT_STACK), mapping
	// systemd's LimitSTACK=.
	ResourceStack
	// ResourceMemLock limits bytes that may be locked into RAM (RLIMIT_MEMLOCK),
	// mapping systemd's LimitMEMLOCK=.
	ResourceMemLock
)

func (Resource) Known

func (r Resource) Known() bool

Known reports whether r is a defined resource other than the zero value.

func (Resource) String

func (r Resource) String() string

String reports the canonical lowercase name of r (e.g. "nofile"), or "unknown" for an unrecognised value. The names match the suffix of the systemd Limit* directives.

type Signal

type Signal int

Signal is a typed, platform-portable OS signal. Its underlying value is the platform signal number, so Signal(syscall.SIGTERM) and a Signal parsed from "SIGTERM" compare equal. The zero value is the reserved invalid signal.

func Parse

func Parse(name string) (sig Signal, err error)

Parse resolves a signal from its name or number into sig. It accepts the canonical "SIGTERM", the bare "TERM", and the numeric "15" (all case-insensitive), returning UnknownSignal for anything the platform table does not define.

func (Signal) Int

func (s Signal) Int() int

Int reports the raw platform signal number underlying s.

func (Signal) Known

func (s Signal) Known() bool

Known reports whether s names a signal in the platform table.

func (Signal) OS

func (s Signal) OS() os.Signal

OS bridges s to the os.Signal expected by os/signal and Process.Signal. It is a zero-cost conversion to syscall.Signal — the canonical os.Signal carrier.

func (Signal) String

func (s Signal) String() string

String reports the canonical upper-case name of s (e.g. "SIGTERM"). An unrecognised signal renders as "signal <n>" so logs stay unambiguous.

type Spec

type Spec struct {
	// Path is the executable to run. Required. A value containing a path
	// separator is a file path, taken as written (a relative one is resolved
	// against Dir). A bare name — "go", "sh" — is searched in the PATH the
	// CHILD will see: the PATH entry of Env whenever Env names one (an
	// explicitly empty PATH= searches nothing), the parent's only when Env
	// names none (including when Env is nil). os/exec's rules apply: the first
	// executable in PATH order wins, and a match found only through a relative
	// entry ("." or an empty one) is refused with exec.ErrDot.
	Path string
	// Args is the full argv including argv[0]; when empty the service uses
	// [Spec.Path] — the name as written, not the resolved file — as the sole
	// argument.
	Args []string
	// Dir is the working directory; empty means inherit the parent's.
	Dir string
	// Env is the explicit environment; nil yields an empty environment, never
	// implicit inheritance, so a spawned service starts from a known state.
	Env []string

	// User is the name or numeric uid to setuid to; empty leaves the parent's.
	User string
	// Group is the name or numeric gid to setgid to; empty leaves the parent's.
	Group string
	// Groups are supplementary group names or gids (systemd SupplementaryGroups=).
	Groups []string

	// Setpgid places the child in its own process group, enabling group-kill of
	// the whole tree (systemd KillMode=control-group without cgroups).
	Setpgid bool
	// Setsid starts the child in a new session detached from the controlling tty.
	Setsid bool

	// Umask, when non-nil, is the file-creation mask applied to the child
	// (systemd UMask=).
	Umask *int
	// Nice, when non-nil, is the scheduling-priority adjustment applied to the
	// child (systemd Nice=).
	Nice *int
	// OOMScoreAdj, when non-nil, is the /proc/<pid>/oom_score_adj value applied
	// to the child (systemd OOMScoreAdjust=).
	OOMScoreAdj *int
	// Rlimits are per-resource soft/hard limits applied post-fork, pre-exec
	// (systemd Limit*=); resources unsupported on the platform surface a typed
	// error rather than silently no-op.
	Rlimits map[Resource]LimitValue

	// CgroupPath, when non-empty, is an already-created cgroup v2 directory the
	// child is placed into BEFORE exec, so controller limits (MemoryMax/TasksMax/
	// …) apply from its first instruction — closing the unconfined window that a
	// post-spawn Group.Add(pid) leaves open (systemd places the unit's payload in
	// its cgroup the same way). The service writes the child's pid to
	// <CgroupPath>/cgroup.procs in the pre-exec trampoline. Linux-only: a non-empty
	// value on any other GOOS surfaces UnsupportedPlatform; a path that is missing,
	// not a cgroup v2 directory, or not delegated surfaces a typed cgroup error
	// rather than spawning unconfined.
	CgroupPath string

	// Stdio selects how the child's standard streams are wired: StdioInherit
	// (default — share the parent's), StdioNull (discard to the null device), or
	// StdioCapture (use the Stdout/Stderr/Stdin members below). systemd
	// StandardOutput=/StandardError=/StandardInput= is the analogue.
	Stdio StdioMode
	// Stdout, when Stdio is StdioCapture, receives the child's standard output;
	// a nil writer discards that stream to the null device.
	Stdout io.Writer
	// Stderr, when Stdio is StdioCapture, receives the child's standard error;
	// a nil writer discards that stream to the null device.
	Stderr io.Writer
	// Stdin, when Stdio is StdioCapture, is copied to the child's standard input;
	// a nil reader gives the child an immediate-EOF stdin. The reader should be
	// EOF-terminating (a buffer, file, or strings.Reader); the copier closes the
	// child's stdin at EOF and never blocks Wait.
	Stdin io.Reader

	// ExtraFiles are additional open files inherited by the child, in order,
	// starting at file descriptor 3 (after stdin/stdout/stderr). This is the
	// mechanism behind socket activation (sd_listen_fds): an activator binds
	// listening sockets and passes them here, then sets LISTEN_FDS/LISTEN_PID in
	// Env so the child finds them at fd 3..3+len-1. nil means no extra fds.
	ExtraFiles []*os.File
}

Spec is the immutable description of a process to spawn: the executable and its environment, the credentials and isolation topology to apply, and the resource/scheduling attributes to set between fork and exec. It carries no policy — restart, health, and dependency concerns belong to the caller.

Pointer fields (Umask, Nice, OOMScoreAdj) distinguish "leave at the inherited default" (nil) from "set explicitly to zero". Maps and slices are read by the service layer and never mutated.

type StdioMode

type StdioMode uint8

StdioMode selects how a spawned child's standard streams are wired. The zero value is StdioInherit, preserving the historical behaviour where the child shares the parent's stdin/stdout/stderr (systemd StandardOutput=/StandardInput= is the analogue, but the choice is generic to anything that spawns processes).

const (
	// StdioInherit shares the parent's stdin/stdout/stderr with the child — the
	// default, and the only behaviour that existed before per-process wiring.
	StdioInherit StdioMode = iota
	// StdioNull connects every stream to the platform null device: the child's
	// output is discarded and its stdin returns EOF immediately.
	StdioNull
	// StdioCapture connects the child to the Spec's Stdout/Stderr writers and
	// Stdin reader; a nil writer or reader for a given stream falls back to the
	// null device for that one stream.
	StdioCapture
)

func (StdioMode) Known

func (m StdioMode) Known() bool

Known reports whether m is one of the defined StdioInherit/StdioNull/StdioCapture modes (i.e. m <= StdioCapture).

func (StdioMode) String

func (m StdioMode) String() string

String returns the lowercase mode name ("inherit", "null", "capture"), or "stdiomode(N)" for an out-of-range value.

Directories

Path Synopsis
Package ipc — the private socket's codes, range 0.3.91.* (ADR 0148).
Package ipc — the private socket's codes, range 0.3.91.* (ADR 0148).

Jump to

Keyboard shortcuts

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